@every-app/sdk 0.3.0-rc.1 → 0.3.0-rc.11
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/LICENSE +21 -0
- package/README.md +62 -64
- package/dist/cloudflare/catalogSettings.generated.d.ts +84 -0
- package/dist/cloudflare/catalogSettings.generated.d.ts.map +1 -0
- package/dist/cloudflare/catalogSettings.generated.js +175 -0
- package/dist/cloudflare/index.d.ts +3 -2
- package/dist/cloudflare/index.d.ts.map +1 -1
- package/dist/cloudflare/index.js +4 -1
- package/dist/cloudflare/sentinel.d.ts +60 -2
- package/dist/cloudflare/sentinel.d.ts.map +1 -1
- package/dist/cloudflare/sentinel.js +37 -2
- package/dist/cloudflare/worker.d.ts +391 -63
- package/dist/cloudflare/worker.d.ts.map +1 -1
- package/dist/cloudflare/worker.js +394 -108
- package/dist/dev/index.d.ts +1 -1
- package/dist/dev/index.d.ts.map +1 -1
- package/dist/dev/index.js +1 -1
- package/dist/dev/keypair.d.ts +2 -0
- package/dist/dev/keypair.d.ts.map +1 -1
- package/dist/dev/keypair.js +2 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/internal/aiDefault.d.ts +205 -0
- package/dist/internal/aiDefault.d.ts.map +1 -0
- package/dist/internal/aiDefault.js +335 -0
- package/dist/internal/aiProviders.d.ts +7 -2
- package/dist/internal/aiProviders.d.ts.map +1 -1
- package/dist/internal/aiProviders.js +19 -6
- package/dist/internal/appSettings.d.ts +82 -4
- package/dist/internal/appSettings.d.ts.map +1 -1
- package/dist/internal/appSettings.js +204 -29
- package/dist/internal/audit.d.ts +108 -0
- package/dist/internal/audit.d.ts.map +1 -0
- package/dist/internal/audit.js +235 -0
- package/dist/internal/capabilities.d.ts +2261 -815
- package/dist/internal/capabilities.d.ts.map +1 -1
- package/dist/internal/capabilities.js +122 -1034
- package/dist/internal/catalog/commerce.d.ts +394 -0
- package/dist/internal/catalog/commerce.d.ts.map +1 -0
- package/dist/internal/catalog/commerce.js +477 -0
- package/dist/internal/catalog/comms.d.ts +375 -0
- package/dist/internal/catalog/comms.d.ts.map +1 -0
- package/dist/internal/catalog/comms.js +520 -0
- package/dist/internal/catalog/index.d.ts +2008 -0
- package/dist/internal/catalog/index.d.ts.map +1 -0
- package/dist/internal/catalog/index.js +19 -0
- package/dist/internal/catalog/inference.d.ts +393 -0
- package/dist/internal/catalog/inference.d.ts.map +1 -0
- package/dist/internal/catalog/inference.js +460 -0
- package/dist/internal/catalog/infra.d.ts +458 -0
- package/dist/internal/catalog/infra.d.ts.map +1 -0
- package/dist/internal/catalog/infra.js +608 -0
- package/dist/internal/catalog/search.d.ts +400 -0
- package/dist/internal/catalog/search.d.ts.map +1 -0
- package/dist/internal/catalog/search.js +463 -0
- package/dist/internal/devProviders.d.ts +2 -2
- package/dist/internal/devProviders.d.ts.map +1 -1
- package/dist/internal/devProviders.js +19 -1
- package/dist/internal/events.d.ts +65 -3
- package/dist/internal/events.d.ts.map +1 -1
- package/dist/internal/events.js +110 -5
- package/dist/internal/gatewaySettings.d.ts +3 -2
- package/dist/internal/gatewaySettings.d.ts.map +1 -1
- package/dist/internal/gatewaySettings.js +12 -6
- package/dist/internal/identity.d.ts +41 -4
- package/dist/internal/identity.d.ts.map +1 -1
- package/dist/internal/identity.js +48 -10
- package/dist/internal/index.d.ts +6 -1
- package/dist/internal/index.d.ts.map +1 -1
- package/dist/internal/index.js +6 -1
- package/dist/internal/members.d.ts +3 -2
- package/dist/internal/members.d.ts.map +1 -1
- package/dist/internal/oauthBuiltIns.d.ts +101 -0
- package/dist/internal/oauthBuiltIns.d.ts.map +1 -0
- package/dist/internal/oauthBuiltIns.js +122 -0
- package/dist/internal/oauthClient.d.ts +8 -50
- package/dist/internal/oauthClient.d.ts.map +1 -1
- package/dist/internal/oauthClient.js +2 -177
- package/dist/internal/oauthPresets.d.ts +32 -0
- package/dist/internal/oauthPresets.d.ts.map +1 -0
- package/dist/internal/oauthPresets.js +253 -0
- package/dist/internal/oauthScopes.d.ts +43 -100
- package/dist/internal/oauthScopes.d.ts.map +1 -1
- package/dist/internal/oauthScopes.js +83 -382
- package/dist/internal/oauthSpec.d.ts +103 -0
- package/dist/internal/oauthSpec.d.ts.map +1 -0
- package/dist/internal/oauthSpec.js +520 -0
- package/dist/internal/protocol.d.ts +97 -5
- package/dist/internal/protocol.d.ts.map +1 -1
- package/dist/internal/protocol.js +89 -1
- package/dist/internal/providerRegistry.d.ts +17 -6
- package/dist/internal/providerRegistry.d.ts.map +1 -1
- package/dist/internal/providerRegistry.js +71 -8
- package/dist/internal/providerSpec.d.ts +18 -7
- package/dist/internal/providerSpec.d.ts.map +1 -1
- package/dist/internal/providerSpec.js +420 -34
- package/dist/internal/remoteMcp.d.ts.map +1 -1
- package/dist/internal/remoteMcp.js +1 -5
- package/dist/internal/signInProviders.d.ts +104 -0
- package/dist/internal/signInProviders.d.ts.map +1 -0
- package/dist/internal/signInProviders.js +245 -0
- package/dist/internal/tools.d.ts +5 -4
- package/dist/internal/tools.d.ts.map +1 -1
- package/dist/internal/tools.js +4 -3
- package/dist/manifest.d.ts +267 -0
- package/dist/manifest.d.ts.map +1 -0
- package/dist/manifest.js +30 -0
- package/dist/server/ai.d.ts +111 -0
- package/dist/server/ai.d.ts.map +1 -0
- package/dist/server/ai.js +145 -0
- package/dist/server/aiBinding.d.ts +68 -0
- package/dist/server/aiBinding.d.ts.map +1 -0
- package/dist/server/aiBinding.js +294 -0
- package/dist/server/attribution.d.ts +18 -5
- package/dist/server/attribution.d.ts.map +1 -1
- package/dist/server/attribution.js +19 -5
- package/dist/server/connectUrl.d.ts +16 -0
- package/dist/server/connectUrl.d.ts.map +1 -0
- package/dist/server/connectUrl.js +40 -0
- package/dist/server/context.d.ts +68 -0
- package/dist/server/context.d.ts.map +1 -0
- package/dist/server/context.js +117 -0
- package/dist/server/emailBinding.d.ts +72 -0
- package/dist/server/emailBinding.d.ts.map +1 -0
- package/dist/server/emailBinding.js +124 -0
- package/dist/server/emit.d.ts +1 -1
- package/dist/server/emit.d.ts.map +1 -1
- package/dist/server/emit.js +7 -7
- package/dist/server/env.d.ts +44 -0
- package/dist/server/env.d.ts.map +1 -0
- package/dist/server/env.js +1 -0
- package/dist/server/eventsEndpoint.d.ts +48 -0
- package/dist/server/eventsEndpoint.d.ts.map +1 -0
- package/dist/server/eventsEndpoint.js +145 -0
- package/dist/server/everyApp.d.ts +40 -11
- package/dist/server/everyApp.d.ts.map +1 -1
- package/dist/server/everyApp.js +116 -6
- package/dist/server/fromMcpTools.d.ts +89 -0
- package/dist/server/fromMcpTools.d.ts.map +1 -0
- package/dist/server/fromMcpTools.js +192 -0
- package/dist/server/gatewayFetch.d.ts +42 -18
- package/dist/server/gatewayFetch.d.ts.map +1 -1
- package/dist/server/gatewayFetch.js +138 -19
- package/dist/server/gatewayUrl.d.ts +13 -0
- package/dist/server/gatewayUrl.d.ts.map +1 -0
- package/dist/server/gatewayUrl.js +34 -0
- package/dist/server/index.d.ts +16 -4
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +13 -3
- package/dist/server/integrationFetch.d.ts +92 -73
- package/dist/server/integrationFetch.d.ts.map +1 -1
- package/dist/server/integrationFetch.js +206 -319
- package/dist/server/listMembers.d.ts +1 -1
- package/dist/server/listMembers.d.ts.map +1 -1
- package/dist/server/listMembers.js +8 -11
- package/dist/server/refusal.d.ts +55 -0
- package/dist/server/refusal.d.ts.map +1 -0
- package/dist/server/refusal.js +193 -0
- package/dist/server/sendEmail.d.ts +1 -1
- package/dist/server/sendEmail.d.ts.map +1 -1
- package/dist/server/sendEmail.js +7 -7
- package/dist/server/tools.d.ts +7 -2
- package/dist/server/tools.d.ts.map +1 -1
- package/dist/server/tools.js +2 -1
- package/dist/server/toolsEndpoint.d.ts +3 -1
- package/dist/server/toolsEndpoint.d.ts.map +1 -1
- package/dist/server/toolsEndpoint.js +19 -7
- package/dist/server/webhooks.d.ts +56 -0
- package/dist/server/webhooks.d.ts.map +1 -0
- package/dist/server/webhooks.js +205 -0
- package/dist/tanstack-start/index.d.ts +9 -0
- package/dist/tanstack-start/index.d.ts.map +1 -0
- package/dist/tanstack-start/index.js +29 -0
- package/dist/testing/index.d.ts +93 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js +156 -0
- package/package.json +35 -14
- package/dist/server/authorizeUrl.d.ts +0 -43
- package/dist/server/authorizeUrl.d.ts.map +0 -1
- package/dist/server/authorizeUrl.js +0 -110
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `verifyWebhook()`: check an inbound provider webhook on a route the
|
|
3
|
+
* manifest declares `public`, the way the provider signs it.
|
|
4
|
+
*
|
|
5
|
+
* The Gateway forwards a public route's body and headers untouched and
|
|
6
|
+
* verifies nothing (landing-page/content/agent/manifest.md, "Why a route is
|
|
7
|
+
* public"): the app checks the provider's signature itself, with the signing
|
|
8
|
+
* secret it declares in `secrets`. Every app that takes a webhook would
|
|
9
|
+
* otherwise write this HMAC code again, and the easy mistakes (a `===`
|
|
10
|
+
* compare, no timestamp window, verifying a re-serialized body, reading an
|
|
11
|
+
* unbounded body first) are the ones that matter. Proposed in
|
|
12
|
+
* docs/design/2026-11-birds-forcing-function.md (limitation 6).
|
|
13
|
+
*
|
|
14
|
+
* Supported signing schemes:
|
|
15
|
+
* - `slack`: `X-Slack-Signature: v0=<hex>` over `v0:<timestamp>:<body>`,
|
|
16
|
+
* with `X-Slack-Request-Timestamp` inside the tolerance;
|
|
17
|
+
* - `github`: `X-Hub-Signature-256: sha256=<hex>` over the body. GitHub
|
|
18
|
+
* signs no timestamp, so a replay is the receiver's to drop: dedupe on
|
|
19
|
+
* `deliveryId` (`X-GitHub-Delivery`);
|
|
20
|
+
* - `everyapp`: a Gateway's own event delivery, `X-EveryApp-Signature:
|
|
21
|
+
* t=<seconds>,v1=<hex>` over `<t>.<body>` (the same check as the
|
|
22
|
+
* Gateway's `verifyEventSignature`), deduped on `X-EveryApp-Event-Id`.
|
|
23
|
+
*
|
|
24
|
+
* WebCrypto only. The compare is `crypto.subtle.verify`, constant time.
|
|
25
|
+
* Several secrets may be given, so a secret rotates without a gap.
|
|
26
|
+
*/
|
|
27
|
+
import { EVENT_HEADERS, EVENT_SIGNATURE_TOLERANCE_SECONDS, eventSigningInput, parseEventSignature, } from "../internal/events.js";
|
|
28
|
+
/** The default body limit: well above Slack's payloads and most of GitHub's (whose cap is 25 MB). */
|
|
29
|
+
export const WEBHOOK_MAX_BYTES_DEFAULT = 5 * 1024 * 1024;
|
|
30
|
+
const HEX_SHA256 = /^[0-9a-f]{64}$/i;
|
|
31
|
+
const MAX_EVENT_SIGNATURES = 4;
|
|
32
|
+
const encoder = new TextEncoder();
|
|
33
|
+
/**
|
|
34
|
+
* Verify `request` as a `provider` webhook signed with `secret`. Reads the
|
|
35
|
+
* body (at most `maxBytes`) and returns it on success; never throws for a
|
|
36
|
+
* bad delivery, so a handler answers `401` on `{ ok: false }`. Throws a
|
|
37
|
+
* `TypeError` only for a programming error: an unknown `provider`, or a
|
|
38
|
+
* `toleranceSeconds` or `maxBytes` that is negative, NaN or infinite.
|
|
39
|
+
*
|
|
40
|
+
* ```ts
|
|
41
|
+
* const check = await verifyWebhook(request, { provider: "slack", secret: env.SLACK_SIGNING_SECRET });
|
|
42
|
+
* if (!check.ok) return new Response(null, { status: 401 });
|
|
43
|
+
* const params = new URLSearchParams(check.body);
|
|
44
|
+
* ```
|
|
45
|
+
*/
|
|
46
|
+
export async function verifyWebhook(request, options) {
|
|
47
|
+
const secrets = (typeof options.secret === "string" ? [options.secret] : options.secret).filter((secret) => typeof secret === "string" && secret.length > 0);
|
|
48
|
+
if (secrets.length === 0)
|
|
49
|
+
return refused("no_secret");
|
|
50
|
+
// A NaN or infinite tolerance or limit would quietly turn the window or the
|
|
51
|
+
// read bound off (every comparison with NaN is false): refuse the call.
|
|
52
|
+
const tolerance = checkedOption("toleranceSeconds", options.toleranceSeconds, EVENT_SIGNATURE_TOLERANCE_SECONDS);
|
|
53
|
+
const maxBytes = checkedOption("maxBytes", options.maxBytes, WEBHOOK_MAX_BYTES_DEFAULT);
|
|
54
|
+
const signed = readSignature(request.headers, options.provider);
|
|
55
|
+
if (!signed.ok)
|
|
56
|
+
return signed;
|
|
57
|
+
if (signed.timestamp !== null) {
|
|
58
|
+
const nowSeconds = Math.floor((options.now ?? new Date()).getTime() / 1000);
|
|
59
|
+
if (Math.abs(nowSeconds - signed.timestamp) > tolerance) {
|
|
60
|
+
return refused("expired");
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
const bytes = await readBody(request, maxBytes);
|
|
64
|
+
if (bytes === "unreadable")
|
|
65
|
+
return refused("unreadable");
|
|
66
|
+
if (bytes === null)
|
|
67
|
+
return refused("too_large");
|
|
68
|
+
// The HMAC runs over the bytes received, never a decoded and re-encoded copy.
|
|
69
|
+
const prefix = encoder.encode(signed.prefix);
|
|
70
|
+
const data = new Uint8Array(new ArrayBuffer(prefix.byteLength + bytes.byteLength));
|
|
71
|
+
data.set(prefix, 0);
|
|
72
|
+
data.set(bytes, prefix.byteLength);
|
|
73
|
+
for (const secret of secrets) {
|
|
74
|
+
const key = await crypto.subtle.importKey("raw", encoder.encode(secret), { name: "HMAC", hash: "SHA-256" }, false, ["verify"]);
|
|
75
|
+
for (const candidate of signed.digests) {
|
|
76
|
+
if (await crypto.subtle.verify("HMAC", key, fromHex(candidate), data)) {
|
|
77
|
+
return {
|
|
78
|
+
ok: true,
|
|
79
|
+
body: new TextDecoder().decode(bytes),
|
|
80
|
+
deliveryId: signed.deliveryId,
|
|
81
|
+
timestamp: signed.timestamp,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return refused("mismatch");
|
|
87
|
+
}
|
|
88
|
+
function readSignature(headers, provider) {
|
|
89
|
+
switch (provider) {
|
|
90
|
+
case "slack": {
|
|
91
|
+
const header = headers.get("x-slack-signature");
|
|
92
|
+
if (!header)
|
|
93
|
+
return refused("missing_signature");
|
|
94
|
+
const rawTimestamp = headers.get("x-slack-request-timestamp");
|
|
95
|
+
const digest = header.startsWith("v0=") ? header.slice(3) : "";
|
|
96
|
+
if (!HEX_SHA256.test(digest) ||
|
|
97
|
+
!rawTimestamp ||
|
|
98
|
+
!/^\d{1,12}$/.test(rawTimestamp)) {
|
|
99
|
+
return refused("malformed");
|
|
100
|
+
}
|
|
101
|
+
return {
|
|
102
|
+
ok: true,
|
|
103
|
+
digests: [digest.toLowerCase()],
|
|
104
|
+
timestamp: Number(rawTimestamp),
|
|
105
|
+
deliveryId: null,
|
|
106
|
+
prefix: `v0:${rawTimestamp}:`,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
case "github": {
|
|
110
|
+
const header = headers.get("x-hub-signature-256");
|
|
111
|
+
if (!header)
|
|
112
|
+
return refused("missing_signature");
|
|
113
|
+
const digest = header.startsWith("sha256=") ? header.slice(7) : "";
|
|
114
|
+
if (!HEX_SHA256.test(digest))
|
|
115
|
+
return refused("malformed");
|
|
116
|
+
return {
|
|
117
|
+
ok: true,
|
|
118
|
+
digests: [digest.toLowerCase()],
|
|
119
|
+
timestamp: null,
|
|
120
|
+
deliveryId: headers.get("x-github-delivery"),
|
|
121
|
+
prefix: "",
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
case "everyapp": {
|
|
125
|
+
const header = headers.get(EVENT_HEADERS.signature);
|
|
126
|
+
if (!header)
|
|
127
|
+
return refused("missing_signature");
|
|
128
|
+
const parsed = parseEventSignature(header);
|
|
129
|
+
// The Gateway sends one v1 (two while a secret rotates): a header with
|
|
130
|
+
// many isn't one it sent, and each would cost an HMAC per secret.
|
|
131
|
+
if (!parsed || parsed.v1.length > MAX_EVENT_SIGNATURES) {
|
|
132
|
+
return refused("malformed");
|
|
133
|
+
}
|
|
134
|
+
return {
|
|
135
|
+
ok: true,
|
|
136
|
+
digests: parsed.v1,
|
|
137
|
+
timestamp: parsed.timestamp,
|
|
138
|
+
deliveryId: headers.get(EVENT_HEADERS.eventId),
|
|
139
|
+
prefix: eventSigningInput(parsed.timestamp, ""),
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
default: {
|
|
143
|
+
const unknown = provider;
|
|
144
|
+
throw new TypeError(`verifyWebhook: unknown provider "${String(unknown)}"`);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
/** A finite, non-negative option, or its default when not given. */
|
|
149
|
+
function checkedOption(name, value, fallback) {
|
|
150
|
+
if (value === undefined)
|
|
151
|
+
return fallback;
|
|
152
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value < 0) {
|
|
153
|
+
throw new TypeError(`verifyWebhook: ${name} must be a finite number of at least 0`);
|
|
154
|
+
}
|
|
155
|
+
return value;
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* The body's bytes; null once it passes `maxBytes` (read no further);
|
|
159
|
+
* "unreadable" when it was already consumed or its stream fails.
|
|
160
|
+
*/
|
|
161
|
+
async function readBody(request, maxBytes) {
|
|
162
|
+
const declared = Number(request.headers.get("content-length"));
|
|
163
|
+
if (Number.isFinite(declared) && declared > maxBytes)
|
|
164
|
+
return null;
|
|
165
|
+
if (request.bodyUsed)
|
|
166
|
+
return "unreadable";
|
|
167
|
+
if (!request.body)
|
|
168
|
+
return new Uint8Array(new ArrayBuffer(0));
|
|
169
|
+
const chunks = [];
|
|
170
|
+
let total = 0;
|
|
171
|
+
try {
|
|
172
|
+
const reader = request.body.getReader();
|
|
173
|
+
for (;;) {
|
|
174
|
+
const { done, value } = await reader.read();
|
|
175
|
+
if (done)
|
|
176
|
+
break;
|
|
177
|
+
total += value.byteLength;
|
|
178
|
+
if (total > maxBytes) {
|
|
179
|
+
await reader.cancel().catch(() => undefined);
|
|
180
|
+
return null;
|
|
181
|
+
}
|
|
182
|
+
chunks.push(value);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
catch {
|
|
186
|
+
return "unreadable";
|
|
187
|
+
}
|
|
188
|
+
const bytes = new Uint8Array(new ArrayBuffer(total));
|
|
189
|
+
let offset = 0;
|
|
190
|
+
for (const chunk of chunks) {
|
|
191
|
+
bytes.set(chunk, offset);
|
|
192
|
+
offset += chunk.byteLength;
|
|
193
|
+
}
|
|
194
|
+
return bytes;
|
|
195
|
+
}
|
|
196
|
+
function fromHex(hex) {
|
|
197
|
+
const bytes = new Uint8Array(new ArrayBuffer(hex.length / 2));
|
|
198
|
+
for (let index = 0; index < bytes.length; index += 1) {
|
|
199
|
+
bytes[index] = Number.parseInt(hex.slice(index * 2, index * 2 + 2), 16);
|
|
200
|
+
}
|
|
201
|
+
return bytes;
|
|
202
|
+
}
|
|
203
|
+
function refused(reason) {
|
|
204
|
+
return { ok: false, reason };
|
|
205
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Puts the person the Gateway signed in on `context.user` (an
|
|
3
|
+
* `EveryAppUser`), verified from the identity the Gateway sent; throws the
|
|
4
|
+
* 401 Response when there is none (a public route, anonymous).
|
|
5
|
+
*/
|
|
6
|
+
export declare const ensureUserMiddleware: import("@tanstack/react-start").FunctionMiddlewareAfterServer<{}, unknown, undefined, {
|
|
7
|
+
user: import("../internal/protocol.js").EveryAppUser;
|
|
8
|
+
}, undefined, undefined, undefined>;
|
|
9
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tanstack-start/index.ts"],"names":[],"mappings":"AAmBA;;;;GAIG;AACH,eAAO,MAAM,oBAAoB;;mCAK/B,CAAC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @every-app/sdk/tanstack-start — identity for a TanStack Start app's server
|
|
3
|
+
* functions:
|
|
4
|
+
*
|
|
5
|
+
* import { ensureUserMiddleware } from "@every-app/sdk/tanstack-start";
|
|
6
|
+
*
|
|
7
|
+
* export const getTodos = createServerFn()
|
|
8
|
+
* .middleware([ensureUserMiddleware])
|
|
9
|
+
* .handler(({ context: { user } }) => listTodos(user.id));
|
|
10
|
+
*
|
|
11
|
+
* The SDK lists `@tanstack/react-start` as a peer dependency, so the Start
|
|
12
|
+
* plugin compiles this module like the app's own: the `.server()` part never
|
|
13
|
+
* reaches the browser.
|
|
14
|
+
*/
|
|
15
|
+
import { createMiddleware } from "@tanstack/react-start";
|
|
16
|
+
import { getRequest } from "@tanstack/react-start/server";
|
|
17
|
+
import { env } from "cloudflare:workers";
|
|
18
|
+
import { requireEveryAppUser } from "../server/everyApp.js";
|
|
19
|
+
/**
|
|
20
|
+
* Puts the person the Gateway signed in on `context.user` (an
|
|
21
|
+
* `EveryAppUser`), verified from the identity the Gateway sent; throws the
|
|
22
|
+
* 401 Response when there is none (a public route, anonymous).
|
|
23
|
+
*/
|
|
24
|
+
export const ensureUserMiddleware = createMiddleware({
|
|
25
|
+
type: "function",
|
|
26
|
+
}).server(async ({ next }) => {
|
|
27
|
+
const user = await requireEveryAppUser(getRequest(), env);
|
|
28
|
+
return next({ context: { user } });
|
|
29
|
+
});
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { IDENTITY_HEADER, type IdentityChannel, type ToolCallResultV1, type ToolsCallResponseV1, type ToolsListResponseV1 } from "../internal/index.js";
|
|
2
|
+
import type { ToolSet } from "../server/tools.js";
|
|
3
|
+
export { IDENTITY_HEADER };
|
|
4
|
+
export type { ToolCallResultV1 as ToolCallResult, ToolsCallResponseV1 as ToolCallResponse, ToolsListResponseV1 as ToolsListResponse, };
|
|
5
|
+
/** The person a test signs in; each field left out takes {@link TEST_USER}'s. */
|
|
6
|
+
export interface TestUser {
|
|
7
|
+
id?: string;
|
|
8
|
+
email?: string;
|
|
9
|
+
orgId?: string;
|
|
10
|
+
/** `owner`, `admin` or `member`, as the Gateway signs it. */
|
|
11
|
+
orgRole?: string;
|
|
12
|
+
}
|
|
13
|
+
/** The person a test signs in by default: the owner of `org_test`. */
|
|
14
|
+
export declare const TEST_USER: Readonly<Required<TestUser>>;
|
|
15
|
+
/** The app id {@link testEnv} uses when none is given. */
|
|
16
|
+
export declare const TEST_APP_ID = "test-app";
|
|
17
|
+
/** What an identity is minted for, beside the person. */
|
|
18
|
+
export interface TestIdentityOptions {
|
|
19
|
+
user?: TestUser;
|
|
20
|
+
/** The app the identity is for (the token's audience). Default: {@link TEST_APP_ID}. */
|
|
21
|
+
appId?: string;
|
|
22
|
+
/** Default `["*"]`: a browser session, which holds every scope. */
|
|
23
|
+
scopes?: string[];
|
|
24
|
+
/** Default `"web"`. */
|
|
25
|
+
channel?: IdentityChannel;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* The Worker env `everyApp()` needs to trust test identities: the app id,
|
|
29
|
+
* the dev issuer and key, `EVERYAPP_DEV`, and a `GATEWAY_URL`. `overrides`
|
|
30
|
+
* adds the app's own bindings (a fake `DB`, a secret) or replaces any of
|
|
31
|
+
* these.
|
|
32
|
+
*/
|
|
33
|
+
export declare function testEnv<T extends object = Record<never, never>>(overrides?: {
|
|
34
|
+
appId?: string;
|
|
35
|
+
} & T): {
|
|
36
|
+
EVERYAPP_APP_ID: string;
|
|
37
|
+
EVERYAPP_DEV: string;
|
|
38
|
+
EVERYAPP_IDENTITY_ISSUER: string;
|
|
39
|
+
EVERYAPP_IDENTITY_PUBLIC_KEYS: string;
|
|
40
|
+
GATEWAY_URL: string;
|
|
41
|
+
} & Omit<T, "appId">;
|
|
42
|
+
/**
|
|
43
|
+
* An `ExecutionContext` for calling a Worker's `fetch` in a test:
|
|
44
|
+
* `waitUntil` keeps each promise in `waited`, so a test can await them.
|
|
45
|
+
*/
|
|
46
|
+
export declare function testContext(): ExecutionContext & {
|
|
47
|
+
waited: Promise<unknown>[];
|
|
48
|
+
};
|
|
49
|
+
/** An identity token for `user`, as the Gateway sends in the identity header. */
|
|
50
|
+
export declare function mintTestIdentity(options?: TestIdentityOptions & {
|
|
51
|
+
toolCall?: {
|
|
52
|
+
id: string;
|
|
53
|
+
name: string;
|
|
54
|
+
};
|
|
55
|
+
}): Promise<string>;
|
|
56
|
+
/**
|
|
57
|
+
* A request as the Gateway forwards it: `user` signed in (the default
|
|
58
|
+
* person when left out), or nobody with `user: null`, as on a public route.
|
|
59
|
+
*/
|
|
60
|
+
export declare function testRequest(input: string | URL, init?: RequestInit & Omit<TestIdentityOptions, "user"> & {
|
|
61
|
+
user?: TestUser | null;
|
|
62
|
+
}): Promise<Request>;
|
|
63
|
+
/** A Worker as `everyApp()` returns it. */
|
|
64
|
+
export interface TestWorker {
|
|
65
|
+
fetch(request: Request, env: never, ctx: ExecutionContext): Response | Promise<Response>;
|
|
66
|
+
}
|
|
67
|
+
export interface CallToolOptions {
|
|
68
|
+
user?: TestUser;
|
|
69
|
+
/**
|
|
70
|
+
* The caller's scopes. Default: `mcp:read`, `mcp:write` and
|
|
71
|
+
* `mcp:destructive`, so every tool may be called; pass fewer to test a
|
|
72
|
+
* refusal.
|
|
73
|
+
*/
|
|
74
|
+
scopes?: string[];
|
|
75
|
+
/** Default: {@link testEnv}() for the worker's app. */
|
|
76
|
+
env?: object;
|
|
77
|
+
ctx?: ExecutionContext;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Call one tool as the Gateway does for an AI client: a `chan: "mcp"`
|
|
81
|
+
* identity minted for this call, through the app's tools endpoint, so the
|
|
82
|
+
* SDK checks the scope, validates input and output and maps errors as it
|
|
83
|
+
* does deployed. `app` is the Worker `everyApp()` returned, or the
|
|
84
|
+
* `defineTools()` set alone. The answer is `{ result }`, or `{ error }`
|
|
85
|
+
* when the SDK refused the call.
|
|
86
|
+
*/
|
|
87
|
+
export declare function callTool(app: TestWorker | ToolSet, name: string, args?: Record<string, unknown>, options?: CallToolOptions): Promise<ToolsCallResponseV1>;
|
|
88
|
+
/** The tool catalog, read as the Gateway reads it when the app deploys. */
|
|
89
|
+
export declare function listTools(app: TestWorker | ToolSet, options?: {
|
|
90
|
+
env?: object;
|
|
91
|
+
ctx?: ExecutionContext;
|
|
92
|
+
}): Promise<ToolsListResponseV1>;
|
|
93
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/testing/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @every-app/sdk/testing — an app's tests, without a Gateway: a signed-in
|
|
3
|
+
* person for a request, and the tools called the way the Gateway calls them.
|
|
4
|
+
*
|
|
5
|
+
* import { callTool, testContext, testEnv, testRequest } from "@every-app/sdk/testing";
|
|
6
|
+
*
|
|
7
|
+
* const worker = everyApp(handler, { tools });
|
|
8
|
+
* const response = await worker.fetch(
|
|
9
|
+
* await testRequest("https://todo.test/api/todos", { user: { id: "ana" } }),
|
|
10
|
+
* testEnv(),
|
|
11
|
+
* testContext(),
|
|
12
|
+
* );
|
|
13
|
+
* const result = await callTool(worker, "list_todos", {}, { user: { id: "ana" } });
|
|
14
|
+
*
|
|
15
|
+
* Identities are signed with the well-known dev keypair, which `everyApp()`
|
|
16
|
+
* trusts only with `EVERYAPP_DEV` set, as {@link testEnv} sets it: never in a
|
|
17
|
+
* deployed Worker. For tests only; a Worker never imports this module.
|
|
18
|
+
*/
|
|
19
|
+
import { DEV_ISSUER, DEV_PRIVATE_KEY_PEM, DEV_PUBLIC_KEY_PEM, } from "../dev/keypair.js";
|
|
20
|
+
import { APP_ID_ENV, AUDIENCE_ENV, IDENTITY_DEV_KEY_ID, IDENTITY_HEADER, MCP_DESTRUCTIVE_SCOPE, MCP_READ_SCOPE, MCP_WRITE_SCOPE, SYSTEM_HEADER, TOOLS_CALL_PATH, TOOLS_LIST_PATH, mintIdentityJwt, mintSystemJwt, } from "../internal/index.js";
|
|
21
|
+
import { everyApp } from "../server/everyApp.js";
|
|
22
|
+
export { IDENTITY_HEADER };
|
|
23
|
+
/** The person a test signs in by default: the owner of `org_test`. */
|
|
24
|
+
export const TEST_USER = Object.freeze({
|
|
25
|
+
id: "user_test",
|
|
26
|
+
email: "test@example.com",
|
|
27
|
+
orgId: "org_test",
|
|
28
|
+
orgRole: "owner",
|
|
29
|
+
});
|
|
30
|
+
/** The app id {@link testEnv} uses when none is given. */
|
|
31
|
+
export const TEST_APP_ID = "test-app";
|
|
32
|
+
/**
|
|
33
|
+
* The Worker env `everyApp()` needs to trust test identities: the app id,
|
|
34
|
+
* the dev issuer and key, `EVERYAPP_DEV`, and a `GATEWAY_URL`. `overrides`
|
|
35
|
+
* adds the app's own bindings (a fake `DB`, a secret) or replaces any of
|
|
36
|
+
* these.
|
|
37
|
+
*/
|
|
38
|
+
export function testEnv(overrides = {}) {
|
|
39
|
+
const { appId = TEST_APP_ID, ...rest } = overrides;
|
|
40
|
+
return {
|
|
41
|
+
EVERYAPP_APP_ID: appId,
|
|
42
|
+
EVERYAPP_DEV: "1",
|
|
43
|
+
EVERYAPP_IDENTITY_ISSUER: DEV_ISSUER,
|
|
44
|
+
EVERYAPP_IDENTITY_PUBLIC_KEYS: JSON.stringify([DEV_PUBLIC_KEY_PEM]),
|
|
45
|
+
GATEWAY_URL: "https://gateway.test",
|
|
46
|
+
...rest,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* An `ExecutionContext` for calling a Worker's `fetch` in a test:
|
|
51
|
+
* `waitUntil` keeps each promise in `waited`, so a test can await them.
|
|
52
|
+
*/
|
|
53
|
+
export function testContext() {
|
|
54
|
+
const waited = [];
|
|
55
|
+
return {
|
|
56
|
+
waited,
|
|
57
|
+
waitUntil: (promise) => void waited.push(promise),
|
|
58
|
+
passThroughOnException: () => { },
|
|
59
|
+
props: {},
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
/** An identity token for `user`, as the Gateway sends in the identity header. */
|
|
63
|
+
export async function mintTestIdentity(options = {}) {
|
|
64
|
+
const user = { ...TEST_USER, ...options.user };
|
|
65
|
+
return mintIdentityJwt(DEV_PRIVATE_KEY_PEM, {
|
|
66
|
+
subject: {
|
|
67
|
+
sub: user.id,
|
|
68
|
+
email: user.email,
|
|
69
|
+
orgId: user.orgId,
|
|
70
|
+
orgRole: user.orgRole,
|
|
71
|
+
},
|
|
72
|
+
audience: options.appId ?? TEST_APP_ID,
|
|
73
|
+
issuer: DEV_ISSUER,
|
|
74
|
+
keyId: IDENTITY_DEV_KEY_ID,
|
|
75
|
+
channel: options.channel,
|
|
76
|
+
scopes: options.scopes,
|
|
77
|
+
toolCall: options.toolCall,
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* A request as the Gateway forwards it: `user` signed in (the default
|
|
82
|
+
* person when left out), or nobody with `user: null`, as on a public route.
|
|
83
|
+
*/
|
|
84
|
+
export async function testRequest(input, init = {}) {
|
|
85
|
+
const { user, appId, scopes, channel, ...requestInit } = init;
|
|
86
|
+
const headers = new Headers(requestInit.headers);
|
|
87
|
+
if (user !== null) {
|
|
88
|
+
headers.set(IDENTITY_HEADER, await mintTestIdentity({ user, appId, scopes, channel }));
|
|
89
|
+
}
|
|
90
|
+
return new Request(input, { ...requestInit, headers });
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Call one tool as the Gateway does for an AI client: a `chan: "mcp"`
|
|
94
|
+
* identity minted for this call, through the app's tools endpoint, so the
|
|
95
|
+
* SDK checks the scope, validates input and output and maps errors as it
|
|
96
|
+
* does deployed. `app` is the Worker `everyApp()` returned, or the
|
|
97
|
+
* `defineTools()` set alone. The answer is `{ result }`, or `{ error }`
|
|
98
|
+
* when the SDK refused the call.
|
|
99
|
+
*/
|
|
100
|
+
export async function callTool(app, name, args = {}, options = {}) {
|
|
101
|
+
const env = options.env ?? testEnv();
|
|
102
|
+
const callId = crypto.randomUUID();
|
|
103
|
+
const identity = await mintTestIdentity({
|
|
104
|
+
user: options.user,
|
|
105
|
+
appId: audienceOf(env),
|
|
106
|
+
channel: "mcp",
|
|
107
|
+
scopes: options.scopes ?? [
|
|
108
|
+
MCP_READ_SCOPE,
|
|
109
|
+
MCP_WRITE_SCOPE,
|
|
110
|
+
MCP_DESTRUCTIVE_SCOPE,
|
|
111
|
+
],
|
|
112
|
+
toolCall: { id: callId, name },
|
|
113
|
+
});
|
|
114
|
+
const response = await workerOf(app).fetch(new Request(`https://app.test${TOOLS_CALL_PATH}`, {
|
|
115
|
+
method: "POST",
|
|
116
|
+
headers: { [IDENTITY_HEADER]: identity },
|
|
117
|
+
body: JSON.stringify({ v: 1, name, arguments: args, callId }),
|
|
118
|
+
}), env, options.ctx ?? testContext());
|
|
119
|
+
return (await response.json());
|
|
120
|
+
}
|
|
121
|
+
/** The tool catalog, read as the Gateway reads it when the app deploys. */
|
|
122
|
+
export async function listTools(app, options = {}) {
|
|
123
|
+
const env = options.env ?? testEnv();
|
|
124
|
+
const token = await mintSystemJwt(DEV_PRIVATE_KEY_PEM, {
|
|
125
|
+
audience: audienceOf(env),
|
|
126
|
+
issuer: DEV_ISSUER,
|
|
127
|
+
purpose: "tools.list",
|
|
128
|
+
keyId: IDENTITY_DEV_KEY_ID,
|
|
129
|
+
});
|
|
130
|
+
const response = await workerOf(app).fetch(new Request(`https://app.test${TOOLS_LIST_PATH}`, {
|
|
131
|
+
method: "POST",
|
|
132
|
+
headers: { [SYSTEM_HEADER]: token },
|
|
133
|
+
body: JSON.stringify({ v: 1 }),
|
|
134
|
+
}), env, options.ctx ?? testContext());
|
|
135
|
+
if (!response.ok) {
|
|
136
|
+
throw new Error(`the tools list answered ${response.status}: ${await response.text()}`);
|
|
137
|
+
}
|
|
138
|
+
return (await response.json());
|
|
139
|
+
}
|
|
140
|
+
/** Who the app's identities are minted for: the deployed audience, else the app id. */
|
|
141
|
+
function audienceOf(env) {
|
|
142
|
+
const values = env;
|
|
143
|
+
for (const key of [AUDIENCE_ENV, APP_ID_ENV]) {
|
|
144
|
+
const value = values[key];
|
|
145
|
+
if (typeof value === "string" && value)
|
|
146
|
+
return value;
|
|
147
|
+
}
|
|
148
|
+
return TEST_APP_ID;
|
|
149
|
+
}
|
|
150
|
+
function workerOf(app) {
|
|
151
|
+
if ("fetch" in app)
|
|
152
|
+
return app;
|
|
153
|
+
return everyApp(() => new Response("Not found", { status: 404 }), {
|
|
154
|
+
tools: app,
|
|
155
|
+
});
|
|
156
|
+
}
|
package/package.json
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@every-app/sdk",
|
|
3
|
-
"version": "0.3.0-rc.
|
|
3
|
+
"version": "0.3.0-rc.11",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
7
|
+
".": {
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"default": "./dist/index.js"
|
|
10
|
+
},
|
|
7
11
|
"./server": {
|
|
8
12
|
"types": "./dist/server/index.d.ts",
|
|
9
13
|
"default": "./dist/server/index.js"
|
|
@@ -24,34 +28,51 @@
|
|
|
24
28
|
"types": "./dist/dev/index.d.ts",
|
|
25
29
|
"default": "./dist/dev/index.js"
|
|
26
30
|
},
|
|
31
|
+
"./tanstack-start": {
|
|
32
|
+
"types": "./dist/tanstack-start/index.d.ts",
|
|
33
|
+
"default": "./dist/tanstack-start/index.js"
|
|
34
|
+
},
|
|
35
|
+
"./testing": {
|
|
36
|
+
"types": "./dist/testing/index.d.ts",
|
|
37
|
+
"default": "./dist/testing/index.js"
|
|
38
|
+
},
|
|
27
39
|
"./package.json": "./package.json"
|
|
28
40
|
},
|
|
29
41
|
"files": [
|
|
30
42
|
"dist",
|
|
31
43
|
"README.md"
|
|
32
44
|
],
|
|
33
|
-
"
|
|
34
|
-
"
|
|
35
|
-
"build": "tsc -p tsconfig.build.json",
|
|
36
|
-
"types:check": "tsc --noEmit",
|
|
37
|
-
"format:check": "prettier --check .",
|
|
38
|
-
"format:write": "prettier . --write",
|
|
39
|
-
"test": "vitest run --silent",
|
|
40
|
-
"test:watch": "vitest",
|
|
41
|
-
"check:esm-relative-imports": "node ./scripts/check-esm-relative-imports.mjs"
|
|
45
|
+
"dependencies": {
|
|
46
|
+
"jose": "^6.2.5"
|
|
42
47
|
},
|
|
43
48
|
"peerDependencies": {
|
|
44
|
-
"
|
|
49
|
+
"@tanstack/react-start": "^1.168.61"
|
|
45
50
|
},
|
|
46
51
|
"devDependencies": {
|
|
47
52
|
"@cloudflare/workers-types": "^4.20251014.0",
|
|
53
|
+
"@tanstack/react-start": "1.168.61",
|
|
48
54
|
"@types/node": "^22.18.13",
|
|
49
|
-
"jose": "^6.2.12",
|
|
50
55
|
"prettier": "^3.6.2",
|
|
51
56
|
"typescript": "^5.9.3",
|
|
52
|
-
"vite": "^7.
|
|
57
|
+
"vite": "^7.3.7",
|
|
53
58
|
"vite-tsconfig-paths": "^5.1.4",
|
|
54
59
|
"vitest": "^3.2.6",
|
|
55
60
|
"zod": "^4.2.0"
|
|
61
|
+
},
|
|
62
|
+
"peerDependenciesMeta": {
|
|
63
|
+
"@tanstack/react-start": {
|
|
64
|
+
"optional": true
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
"scripts": {
|
|
68
|
+
"prebuild": "node -e \"require('node:fs').rmSync('dist', { recursive: true, force: true })\"",
|
|
69
|
+
"build": "tsc -p tsconfig.build.json",
|
|
70
|
+
"types:check": "tsc --noEmit",
|
|
71
|
+
"format:check": "prettier --check .",
|
|
72
|
+
"format:write": "prettier . --write",
|
|
73
|
+
"test": "vitest run --silent",
|
|
74
|
+
"test:watch": "vitest",
|
|
75
|
+
"check:esm-relative-imports": "node ./scripts/check-esm-relative-imports.mjs",
|
|
76
|
+
"catalog:update": "UPDATE_CATALOG=1 vitest run src/cloudflare/catalogSettings.test.ts --silent"
|
|
56
77
|
}
|
|
57
|
-
}
|
|
78
|
+
}
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
import { type OAuthClientCapabilityId } from "../internal/index.js";
|
|
2
|
-
import type { OAuthClientIntegrationStatus } from "./integrationFetch.js";
|
|
3
|
-
export interface AuthorizeUrlOptions {
|
|
4
|
-
/** The OAuth client, as listed in the manifest's `capabilities`. */
|
|
5
|
-
provider: OAuthClientCapabilityId;
|
|
6
|
-
/** What `integrationStatus()` answered for it, ready and with its `clientId`. */
|
|
7
|
-
status: OAuthClientIntegrationStatus;
|
|
8
|
-
/** One of the redirect URIs the operator registered on the client. */
|
|
9
|
-
redirectUri: string;
|
|
10
|
-
/** The app's own anti-forgery value, checked when the person comes back. */
|
|
11
|
-
state: string;
|
|
12
|
-
/** PKCE: the S256 challenge of the verifier the app keeps for the exchange. */
|
|
13
|
-
codeChallenge?: string;
|
|
14
|
-
/**
|
|
15
|
-
* The provider's extras (`prompt`, `login_hint`, Google's `hd`). Never
|
|
16
|
-
* one of the parameters this helper sets (PKCE's and the provider's own
|
|
17
|
-
* included), nor Google's `include_granted_scopes`.
|
|
18
|
-
*/
|
|
19
|
-
params?: Record<string, string>;
|
|
20
|
-
}
|
|
21
|
-
/**
|
|
22
|
-
* The provider's authorize URL to send a person to, for a client with
|
|
23
|
-
* scope sets (`google-workspace`, `microsoft-365`, `slack`), built from the
|
|
24
|
-
* capability's status: the registry's authorize endpoint (or the instance
|
|
25
|
-
* the status names), `client_id`, `response_type=code`, `redirect_uri`,
|
|
26
|
-
* `state`, and the scopes the status allows this app, which are exactly
|
|
27
|
-
* what the gateway will let the token carry. Each provider's own needs
|
|
28
|
-
* are added: Google's `access_type=offline` (a refresh token), Microsoft's
|
|
29
|
-
* `response_mode=query`, Slack's comma-joined `scope` and its `user_scope`
|
|
30
|
-
* (the status's `userScopes`, when the app's sets include a user set). With
|
|
31
|
-
* `codeChallenge`, PKCE as S256. The app keeps its `state` and verifier
|
|
32
|
-
* for the callback.
|
|
33
|
-
*
|
|
34
|
-
* A client without scope sets (LinkedIn, Mastodon, ...) is refused: the
|
|
35
|
-
* registry's checklist for one isn't what the gateway holds a token to,
|
|
36
|
-
* so an app builds that URL itself from the status's `clientId`.
|
|
37
|
-
*
|
|
38
|
-
* Throws a `TypeError` naming the fix when the client has no scope sets,
|
|
39
|
-
* when the status isn't ready or has no `clientId`, when the status lacks
|
|
40
|
-
* `scopes`, or when `params` names a parameter the helper sets.
|
|
41
|
-
*/
|
|
42
|
-
export declare function authorizeUrl({ provider, status, redirectUri, state, codeChallenge, params, }: AuthorizeUrlOptions): URL;
|
|
43
|
-
//# sourceMappingURL=authorizeUrl.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"authorizeUrl.d.ts","sourceRoot":"","sources":["../../src/server/authorizeUrl.ts"],"names":[],"mappings":"AAAA,OAAO,EAKL,KAAK,uBAAuB,EAC7B,MAAM,sBAAsB,CAAC;AAC9B,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,uBAAuB,CAAC;AAE1E,MAAM,WAAW,mBAAmB;IAClC,oEAAoE;IACpE,QAAQ,EAAE,uBAAuB,CAAC;IAClC,iFAAiF;IACjF,MAAM,EAAE,4BAA4B,CAAC;IACrC,sEAAsE;IACtE,WAAW,EAAE,MAAM,CAAC;IACpB,4EAA4E;IAC5E,KAAK,EAAE,MAAM,CAAC;IACd,+EAA+E;IAC/E,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACjC;AAgCD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,YAAY,CAAC,EAC3B,QAAQ,EACR,MAAM,EACN,WAAW,EACX,KAAK,EACL,aAAa,EACb,MAAM,GACP,EAAE,mBAAmB,GAAG,GAAG,CA2D3B"}
|