@every-app/sdk 0.3.0-rc.1 → 0.3.0-rc.10
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 +1 -1
- package/dist/cloudflare/catalogSettings.generated.d.ts +81 -0
- package/dist/cloudflare/catalogSettings.generated.d.ts.map +1 -0
- package/dist/cloudflare/catalogSettings.generated.js +174 -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 +357 -63
- package/dist/cloudflare/worker.d.ts.map +1 -1
- package/dist/cloudflare/worker.js +363 -104
- 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 +204 -0
- package/dist/internal/aiDefault.d.ts.map +1 -0
- package/dist/internal/aiDefault.js +334 -0
- package/dist/internal/aiProviders.d.ts +7 -2
- package/dist/internal/aiProviders.d.ts.map +1 -1
- package/dist/internal/aiProviders.js +17 -4
- 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 +2268 -438
- package/dist/internal/capabilities.d.ts.map +1 -1
- package/dist/internal/capabilities.js +128 -539
- 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 +1973 -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 +423 -0
- package/dist/internal/catalog/infra.d.ts.map +1 -0
- package/dist/internal/catalog/infra.js +543 -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 +13 -2
- package/dist/internal/events.d.ts.map +1 -1
- package/dist/internal/events.js +47 -4
- 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/oauthClient.d.ts +10 -12
- package/dist/internal/oauthClient.d.ts.map +1 -1
- package/dist/internal/oauthClient.js +20 -81
- package/dist/internal/oauthPresets.d.ts +33 -0
- package/dist/internal/oauthPresets.d.ts.map +1 -0
- package/dist/internal/oauthPresets.js +1064 -0
- package/dist/internal/oauthScopes.d.ts +56 -1
- package/dist/internal/oauthScopes.d.ts.map +1 -1
- package/dist/internal/oauthScopes.js +98 -4
- package/dist/internal/oauthSpec.d.ts +102 -0
- package/dist/internal/oauthSpec.d.ts.map +1 -0
- package/dist/internal/oauthSpec.js +789 -0
- package/dist/internal/protocol.d.ts +64 -1
- package/dist/internal/protocol.d.ts.map +1 -1
- package/dist/internal/protocol.js +51 -1
- package/dist/internal/providerRegistry.d.ts +77 -6
- package/dist/internal/providerRegistry.d.ts.map +1 -1
- package/dist/internal/providerRegistry.js +178 -8
- package/dist/internal/providerSpec.d.ts +18 -7
- package/dist/internal/providerSpec.d.ts.map +1 -1
- package/dist/internal/providerSpec.js +442 -34
- 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/manifest.d.ts +238 -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 +9 -5
- package/dist/server/attribution.d.ts.map +1 -1
- package/dist/server/attribution.js +12 -4
- package/dist/server/authorizeUrl.d.ts +1 -1
- package/dist/server/authorizeUrl.d.ts.map +1 -1
- package/dist/server/authorizeUrl.js +2 -6
- package/dist/server/connectUrl.d.ts +21 -0
- package/dist/server/connectUrl.d.ts.map +1 -0
- package/dist/server/connectUrl.js +37 -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/everyApp.d.ts +27 -7
- package/dist/server/everyApp.d.ts.map +1 -1
- package/dist/server/everyApp.js +106 -5
- 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 +15 -4
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +12 -2
- package/dist/server/integrationFetch.d.ts +70 -39
- package/dist/server/integrationFetch.d.ts.map +1 -1
- package/dist/server/integrationFetch.js +244 -82
- 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/toolsEndpoint.d.ts.map +1 -1
- package/dist/server/toolsEndpoint.js +11 -5
- 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
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { IDENTITY_HEADER, ORG_HEADER, SHARED_ENV } from "../internal/index.js";
|
|
2
|
+
import { attributionFromContext } from "./context.js";
|
|
2
3
|
/**
|
|
3
4
|
* A gateway call that doesn't say whom it is for where it must: from a
|
|
4
5
|
* shared app's Worker, with neither the request nor an organization.
|
|
@@ -11,11 +12,18 @@ export class AttributionError extends Error {
|
|
|
11
12
|
}
|
|
12
13
|
}
|
|
13
14
|
/**
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
15
|
+
* Whom the call is for, checked before any call is made: `target` as the
|
|
16
|
+
* app passed it, else the request context's answer
|
|
17
|
+
* ({@link attributionFromContext}; a `RequestContextError` outside every
|
|
18
|
+
* context). A shared app's Worker must end up with the request or an
|
|
19
|
+
* organization, and anything passed must be one of the two shapes.
|
|
17
20
|
*/
|
|
18
|
-
export function
|
|
21
|
+
export function attributionFor(env, target, what) {
|
|
22
|
+
const resolved = target === undefined ? attributionFromContext(what) : target;
|
|
23
|
+
checkAttribution(env, resolved, what);
|
|
24
|
+
return resolved;
|
|
25
|
+
}
|
|
26
|
+
function checkAttribution(env, target, what) {
|
|
19
27
|
if (target !== undefined && !isRequest(target)) {
|
|
20
28
|
if (target === null || typeof target !== "object" || !target.background) {
|
|
21
29
|
throw new AttributionError(`${what}: \`for\` is the inbound Request or { background: true, organizationId }.`);
|
|
@@ -31,7 +31,7 @@ export interface AuthorizeUrlOptions {
|
|
|
31
31
|
* `codeChallenge`, PKCE as S256. The app keeps its `state` and verifier
|
|
32
32
|
* for the callback.
|
|
33
33
|
*
|
|
34
|
-
* A client without scope sets
|
|
34
|
+
* A client without scope sets is refused: the
|
|
35
35
|
* registry's checklist for one isn't what the gateway holds a token to,
|
|
36
36
|
* so an app builds that URL itself from the status's `clientId`.
|
|
37
37
|
*
|
|
@@ -1 +1 @@
|
|
|
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,
|
|
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,CAqD3B"}
|
|
@@ -40,7 +40,7 @@ function scopedClientIds() {
|
|
|
40
40
|
* `codeChallenge`, PKCE as S256. The app keeps its `state` and verifier
|
|
41
41
|
* for the callback.
|
|
42
42
|
*
|
|
43
|
-
* A client without scope sets
|
|
43
|
+
* A client without scope sets is refused: the
|
|
44
44
|
* registry's checklist for one isn't what the gateway holds a token to,
|
|
45
45
|
* so an app builds that URL itself from the status's `clientId`.
|
|
46
46
|
*
|
|
@@ -66,11 +66,7 @@ export function authorizeUrl({ provider, status, redirectUri, state, codeChallen
|
|
|
66
66
|
? `authorizeUrl(${JSON.stringify(provider)}): include_granted_scopes would widen the token to every scope the person granted this client, which the gateway refuses (integration_scope_exceeded); leave it out of params.`
|
|
67
67
|
: `authorizeUrl(${JSON.stringify(provider)}) sets ${name} itself; leave it out of params.`);
|
|
68
68
|
}
|
|
69
|
-
const
|
|
70
|
-
if (!/^https:/.test(capability.authorize.url) && !base) {
|
|
71
|
-
throw new TypeError(`authorizeUrl(${JSON.stringify(provider)}) needs the instance ${capability.label} is on: the status carries it as origin once ${capability.hostSetting} is set.`);
|
|
72
|
-
}
|
|
73
|
-
const url = new URL(capability.authorize.url, base);
|
|
69
|
+
const url = new URL(capability.authorize.url);
|
|
74
70
|
// The status speaks for the gateway: its scopes are the token's ceiling.
|
|
75
71
|
const scopes = status.scopes;
|
|
76
72
|
const slack = url.host === "slack.com";
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { ConnectionAccount } from "../internal/index.js";
|
|
2
|
+
export interface ConnectUrlOptions {
|
|
3
|
+
/** The Worker env: `GATEWAY_URL`, which every deployed Worker and `everyapp dev` set. */
|
|
4
|
+
env: object;
|
|
5
|
+
/** The OAuth provider's id (`google-workspace`, `github`). */
|
|
6
|
+
provider: string;
|
|
7
|
+
/**
|
|
8
|
+
* Where to send the person afterwards: the app's own URL (absolute) or
|
|
9
|
+
* a path on the Gateway. Absent: Settings → Connected accounts.
|
|
10
|
+
*/
|
|
11
|
+
returnTo?: string;
|
|
12
|
+
/** Whose connection to make: the person's (default) or the organization's (owner or admin). */
|
|
13
|
+
account?: ConnectionAccount;
|
|
14
|
+
/** Scope sets to ask for; absent: the union every active app that lists the provider declares. */
|
|
15
|
+
sets?: readonly string[];
|
|
16
|
+
}
|
|
17
|
+
/** The path of the connect page for `provider` on the Gateway. */
|
|
18
|
+
export declare function connectPath(provider: string): string;
|
|
19
|
+
/** The absolute URL that starts the Gateway-owned flow for `provider`. */
|
|
20
|
+
export declare function connectUrl({ env, provider, returnTo, account, sets, }: ConnectUrlOptions): string;
|
|
21
|
+
//# sourceMappingURL=connectUrl.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connectUrl.d.ts","sourceRoot":"","sources":["../../src/server/connectUrl.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAE9D,MAAM,WAAW,iBAAiB;IAChC,yFAAyF;IACzF,GAAG,EAAE,MAAM,CAAC;IACZ,8DAA8D;IAC9D,QAAQ,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,+FAA+F;IAC/F,OAAO,CAAC,EAAE,iBAAiB,CAAC;IAC5B,kGAAkG;IAClG,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAC1B;AAED,kEAAkE;AAClE,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEpD;AAED,0EAA0E;AAC1E,wBAAgB,UAAU,CAAC,EACzB,GAAG,EACH,QAAQ,EACR,QAAQ,EACR,OAAO,EACP,IAAI,GACL,EAAE,iBAAiB,GAAG,MAAM,CAmB5B"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where an app sends a person to connect an OAuth provider the Gateway
|
|
3
|
+
* runs the flow for (docs/design/2026-10-oauth-providers.md section 5):
|
|
4
|
+
* `<gateway>/connect/<id>?return=<back here>&account=<user|organization>`.
|
|
5
|
+
* The Gateway checks the session, asks the provider for the union of the
|
|
6
|
+
* sets every active app declares, finishes on its own callback and sends
|
|
7
|
+
* the browser back to `returnTo`, which must be a path on the Gateway or
|
|
8
|
+
* an https URL on one of its app hosts (in local dev also localhost).
|
|
9
|
+
*/
|
|
10
|
+
import { ConfigurationError } from "../internal/index.js";
|
|
11
|
+
/** The path of the connect page for `provider` on the Gateway. */
|
|
12
|
+
export function connectPath(provider) {
|
|
13
|
+
return `/connect/${encodeURIComponent(provider)}`;
|
|
14
|
+
}
|
|
15
|
+
/** The absolute URL that starts the Gateway-owned flow for `provider`. */
|
|
16
|
+
export function connectUrl({ env, provider, returnTo, account, sets, }) {
|
|
17
|
+
const values = env;
|
|
18
|
+
const gateway = values["GATEWAY_URL"];
|
|
19
|
+
if (typeof gateway !== "string" || !gateway.trim()) {
|
|
20
|
+
throw new ConfigurationError("connectUrl() needs GATEWAY_URL in the Worker env: the deploy sets it, and everyapp dev sets it to the local gateway.");
|
|
21
|
+
}
|
|
22
|
+
let base;
|
|
23
|
+
try {
|
|
24
|
+
base = new URL(gateway.trim());
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
throw new ConfigurationError(`GATEWAY_URL isn't a URL: ${gateway}`);
|
|
28
|
+
}
|
|
29
|
+
const url = new URL(connectPath(provider), base.origin);
|
|
30
|
+
if (returnTo)
|
|
31
|
+
url.searchParams.set("return", returnTo);
|
|
32
|
+
if (account)
|
|
33
|
+
url.searchParams.set("account", account);
|
|
34
|
+
if (sets && sets.length > 0)
|
|
35
|
+
url.searchParams.set("sets", sets.join(","));
|
|
36
|
+
return url.toString();
|
|
37
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type { EveryAppUser } from "../internal/index.js";
|
|
2
|
+
/** What `everyApp()` keeps while it serves one request. */
|
|
3
|
+
export interface ServedRequest {
|
|
4
|
+
request: Request;
|
|
5
|
+
/** The verified user, or null on a public route. */
|
|
6
|
+
user: EveryAppUser | null;
|
|
7
|
+
/** When the forwarded identity expires (epoch ms); null without one. */
|
|
8
|
+
expiresAt: number | null;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* A forwarded identity lives 120 seconds. One this close to its expiry could
|
|
12
|
+
* expire before the Gateway checks it (a 401 the call can't recover from),
|
|
13
|
+
* so work still running then goes on as the organization's background work.
|
|
14
|
+
*/
|
|
15
|
+
export declare const IDENTITY_EXPIRY_MARGIN_MS = 15000;
|
|
16
|
+
/**
|
|
17
|
+
* Code `request_context_missing`: `currentUser()` or a Gateway call with
|
|
18
|
+
* no `for`, outside any request `everyApp()` serves and outside
|
|
19
|
+
* `withOrganization()`. Work no request started names its organization.
|
|
20
|
+
*/
|
|
21
|
+
export declare class RequestContextError extends Error {
|
|
22
|
+
readonly code = "request_context_missing";
|
|
23
|
+
constructor(message: string);
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Code `attribution_organization_mismatch`: `withOrganization()` named
|
|
27
|
+
* another organization than the request being served belongs to.
|
|
28
|
+
*/
|
|
29
|
+
export declare class OrganizationMismatchError extends Error {
|
|
30
|
+
readonly code = "attribution_organization_mismatch";
|
|
31
|
+
constructor(message: string);
|
|
32
|
+
}
|
|
33
|
+
/** Serve `fn` for one request: `everyApp()` calls this; apps don't. */
|
|
34
|
+
export declare function serveRequest<T>(served: ServedRequest, fn: () => T): T;
|
|
35
|
+
/**
|
|
36
|
+
* Run `fn` as `organizationId`'s background work: every Gateway call made
|
|
37
|
+
* inside it (and in what it awaits or schedules) is that organization's,
|
|
38
|
+
* with no person behind it. For a Workflow step, a cron, a queue consumer or
|
|
39
|
+
* a Durable Object alarm, which no request started. An organization's own
|
|
40
|
+
* app names its own (`env.EVERY_APP_ORG_ID`); a shared app's Worker names
|
|
41
|
+
* the one the work is for. Inside a request another organization's person
|
|
42
|
+
* is driving, it throws {@link OrganizationMismatchError}; inside that
|
|
43
|
+
* organization's own request, the person stays attributed while their
|
|
44
|
+
* identity is good.
|
|
45
|
+
*/
|
|
46
|
+
export declare function withOrganization<T>(organizationId: string, fn: () => T): T;
|
|
47
|
+
/**
|
|
48
|
+
* The verified user of the request being served: an `EveryAppUser`, or
|
|
49
|
+
* `null` on a public route and inside `withOrganization()`'s background
|
|
50
|
+
* work. Throws {@link RequestContextError} outside both: there is no one
|
|
51
|
+
* to be, and a handler that isn't under `everyApp()` is a bug.
|
|
52
|
+
*/
|
|
53
|
+
export declare function currentUser(): EveryAppUser | null;
|
|
54
|
+
/**
|
|
55
|
+
* Whom a Gateway call made now is for, from the context:
|
|
56
|
+
*
|
|
57
|
+
* - the request being served, while its identity is good;
|
|
58
|
+
* - else that organization's background work (the identity is about to
|
|
59
|
+
* expire, or `withOrganization()` named it);
|
|
60
|
+
* - `undefined` on a public route of an organization's own app, which the
|
|
61
|
+
* Gateway attributes to the app's organization; and
|
|
62
|
+
* - a {@link RequestContextError} outside every context.
|
|
63
|
+
*/
|
|
64
|
+
export declare function attributionFromContext(what: string, now?: number): Request | {
|
|
65
|
+
background: true;
|
|
66
|
+
organizationId?: string;
|
|
67
|
+
} | undefined;
|
|
68
|
+
//# sourceMappingURL=context.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/server/context.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAEzD,2DAA2D;AAC3D,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,OAAO,CAAC;IACjB,oDAAoD;IACpD,IAAI,EAAE,YAAY,GAAG,IAAI,CAAC;IAC1B,wEAAwE;IACxE,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAUD;;;;GAIG;AACH,eAAO,MAAM,yBAAyB,QAAS,CAAC;AAEhD;;;;GAIG;AACH,qBAAa,mBAAoB,SAAQ,KAAK;IAC5C,QAAQ,CAAC,IAAI,6BAA6B;gBAC9B,OAAO,EAAE,MAAM;CAI5B;AAED;;;GAGG;AACH,qBAAa,yBAA0B,SAAQ,KAAK;IAClD,QAAQ,CAAC,IAAI,uCAAuC;gBACxC,OAAO,EAAE,MAAM;CAI5B;AAED,uEAAuE;AACvE,wBAAgB,YAAY,CAAC,CAAC,EAAE,MAAM,EAAE,aAAa,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,CAErE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,cAAc,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,CAc1E;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,IAAI,YAAY,GAAG,IAAI,CAQjD;AAED;;;;;;;;;GASG;AACH,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,MAAM,EACZ,GAAG,GAAE,MAAmB,GACvB,OAAO,GAAG;IAAE,UAAU,EAAE,IAAI,CAAC;IAAC,cAAc,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAoBrE"}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The request context: whom the app is serving, kept for the whole of a
|
|
3
|
+
* request's work (every `await`, `ctx.waitUntil()`, a streaming body) in an
|
|
4
|
+
* `AsyncLocalStorage`, which every Every App Worker has (`nodejs_compat`).
|
|
5
|
+
*
|
|
6
|
+
* `everyApp()` opens it around the handler and around each tool call, with
|
|
7
|
+
* the verified user and the request the Gateway forwarded. Deeper in the
|
|
8
|
+
* app, `currentUser()` reads the user without the request, and every call
|
|
9
|
+
* back to the Gateway (`integrationFetch`, `ai`, `sendEmail`, `emit`, ...)
|
|
10
|
+
* is attributed to that person without a `for`.
|
|
11
|
+
*
|
|
12
|
+
* Work no request started (a cron, a Workflow step, a queue consumer, a
|
|
13
|
+
* Durable Object alarm) has no context of its own: it runs under
|
|
14
|
+
* `withOrganization(organizationId, fn)`, which makes every Gateway call
|
|
15
|
+
* inside it that organization's background work. A Gateway call with
|
|
16
|
+
* neither is refused before it is made ({@link RequestContextError}): the
|
|
17
|
+
* SDK never acts as nobody.
|
|
18
|
+
*/
|
|
19
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
20
|
+
const scopes = new AsyncLocalStorage();
|
|
21
|
+
/**
|
|
22
|
+
* A forwarded identity lives 120 seconds. One this close to its expiry could
|
|
23
|
+
* expire before the Gateway checks it (a 401 the call can't recover from),
|
|
24
|
+
* so work still running then goes on as the organization's background work.
|
|
25
|
+
*/
|
|
26
|
+
export const IDENTITY_EXPIRY_MARGIN_MS = 15_000;
|
|
27
|
+
/**
|
|
28
|
+
* Code `request_context_missing`: `currentUser()` or a Gateway call with
|
|
29
|
+
* no `for`, outside any request `everyApp()` serves and outside
|
|
30
|
+
* `withOrganization()`. Work no request started names its organization.
|
|
31
|
+
*/
|
|
32
|
+
export class RequestContextError extends Error {
|
|
33
|
+
code = "request_context_missing";
|
|
34
|
+
constructor(message) {
|
|
35
|
+
super(message);
|
|
36
|
+
this.name = "RequestContextError";
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Code `attribution_organization_mismatch`: `withOrganization()` named
|
|
41
|
+
* another organization than the request being served belongs to.
|
|
42
|
+
*/
|
|
43
|
+
export class OrganizationMismatchError extends Error {
|
|
44
|
+
code = "attribution_organization_mismatch";
|
|
45
|
+
constructor(message) {
|
|
46
|
+
super(message);
|
|
47
|
+
this.name = "OrganizationMismatchError";
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/** Serve `fn` for one request: `everyApp()` calls this; apps don't. */
|
|
51
|
+
export function serveRequest(served, fn) {
|
|
52
|
+
return scopes.run({ served }, fn);
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Run `fn` as `organizationId`'s background work: every Gateway call made
|
|
56
|
+
* inside it (and in what it awaits or schedules) is that organization's,
|
|
57
|
+
* with no person behind it. For a Workflow step, a cron, a queue consumer or
|
|
58
|
+
* a Durable Object alarm, which no request started. An organization's own
|
|
59
|
+
* app names its own (`env.EVERY_APP_ORG_ID`); a shared app's Worker names
|
|
60
|
+
* the one the work is for. Inside a request another organization's person
|
|
61
|
+
* is driving, it throws {@link OrganizationMismatchError}; inside that
|
|
62
|
+
* organization's own request, the person stays attributed while their
|
|
63
|
+
* identity is good.
|
|
64
|
+
*/
|
|
65
|
+
export function withOrganization(organizationId, fn) {
|
|
66
|
+
if (typeof organizationId !== "string" || organizationId.trim() === "") {
|
|
67
|
+
throw new TypeError("withOrganization(organizationId, fn) needs a non-empty organization id (an organization's own app passes env.EVERY_APP_ORG_ID).");
|
|
68
|
+
}
|
|
69
|
+
const parent = scopes.getStore();
|
|
70
|
+
const served = parent?.served;
|
|
71
|
+
if (served?.user && served.user.orgId !== organizationId) {
|
|
72
|
+
throw new OrganizationMismatchError(`withOrganization("${organizationId}") was called while serving a request of organization "${served.user.orgId}".`);
|
|
73
|
+
}
|
|
74
|
+
return scopes.run({ ...parent, organizationId }, fn);
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* The verified user of the request being served: an `EveryAppUser`, or
|
|
78
|
+
* `null` on a public route and inside `withOrganization()`'s background
|
|
79
|
+
* work. Throws {@link RequestContextError} outside both: there is no one
|
|
80
|
+
* to be, and a handler that isn't under `everyApp()` is a bug.
|
|
81
|
+
*/
|
|
82
|
+
export function currentUser() {
|
|
83
|
+
const scope = scopes.getStore();
|
|
84
|
+
if (!scope) {
|
|
85
|
+
throw new RequestContextError("currentUser() was called outside a request everyApp() serves and outside withOrganization(): there is no user here.");
|
|
86
|
+
}
|
|
87
|
+
return scope.served?.user ?? null;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Whom a Gateway call made now is for, from the context:
|
|
91
|
+
*
|
|
92
|
+
* - the request being served, while its identity is good;
|
|
93
|
+
* - else that organization's background work (the identity is about to
|
|
94
|
+
* expire, or `withOrganization()` named it);
|
|
95
|
+
* - `undefined` on a public route of an organization's own app, which the
|
|
96
|
+
* Gateway attributes to the app's organization; and
|
|
97
|
+
* - a {@link RequestContextError} outside every context.
|
|
98
|
+
*/
|
|
99
|
+
export function attributionFromContext(what, now = Date.now()) {
|
|
100
|
+
const scope = scopes.getStore();
|
|
101
|
+
if (!scope) {
|
|
102
|
+
throw new RequestContextError(`${what} was called with no \`for\`, outside a request everyApp() serves and outside withOrganization(): the call would be nobody's. Pass for: request, or run the work under withOrganization(organizationId, fn).`);
|
|
103
|
+
}
|
|
104
|
+
const served = scope.served;
|
|
105
|
+
if (served?.user) {
|
|
106
|
+
const fresh = served.expiresAt === null ||
|
|
107
|
+
now < served.expiresAt - IDENTITY_EXPIRY_MARGIN_MS;
|
|
108
|
+
// withOrganization() refused any other organization than the request's.
|
|
109
|
+
if (fresh)
|
|
110
|
+
return served.request;
|
|
111
|
+
return { background: true, organizationId: served.user.orgId };
|
|
112
|
+
}
|
|
113
|
+
if (scope.organizationId) {
|
|
114
|
+
return { background: true, organizationId: scope.organizationId };
|
|
115
|
+
}
|
|
116
|
+
return undefined;
|
|
117
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `gatewayEmailBinding(env)`: the Gateway's `email` capability as a
|
|
3
|
+
* `send_email` binding, for code written against `env.EMAIL.send(message)`.
|
|
4
|
+
*
|
|
5
|
+
* An app on Every App binds no `send_email` of its own: the Gateway sends
|
|
6
|
+
* with its sender, checks `from` against the installation's senders and
|
|
7
|
+
* keeps the mail budgets. Code that already calls `env.EMAIL.send()` gets
|
|
8
|
+
* this object in its place:
|
|
9
|
+
*
|
|
10
|
+
* const EMAIL = gatewayEmailBinding(env, { for: request });
|
|
11
|
+
* await EMAIL.send({ from, to, subject, text });
|
|
12
|
+
*
|
|
13
|
+
* Each `send()` is a `sendEmail()` (the manifest lists `capabilities: {
|
|
14
|
+
* email: { access: "write" } }`), so local development works the same way:
|
|
15
|
+
* checked and printed, or sent with the developer's own Email Sending
|
|
16
|
+
* account (EMAIL_REST_API_TOKEN, CLOUDFLARE_ACCOUNT_ID, EMAIL_FROM).
|
|
17
|
+
*/
|
|
18
|
+
import { type SendEmailMessage } from "../internal/index.js";
|
|
19
|
+
import type { GatewayCallFor } from "./attribution.js";
|
|
20
|
+
export interface GatewayEmailBindingOptions {
|
|
21
|
+
/**
|
|
22
|
+
* Whom the mail is for: the inbound request, or `{ background: true,
|
|
23
|
+
* organizationId }` (sendEmail's `for`).
|
|
24
|
+
*/
|
|
25
|
+
for?: GatewayCallFor;
|
|
26
|
+
}
|
|
27
|
+
/** `name <email>` or a bare address, as the binding takes them. */
|
|
28
|
+
type BindingAddress = string | {
|
|
29
|
+
email: string;
|
|
30
|
+
name?: string;
|
|
31
|
+
};
|
|
32
|
+
type BindingRecipients = BindingAddress | BindingAddress[];
|
|
33
|
+
/** The structured message `send_email`'s `send()` takes (`EmailMessageBuilder`). */
|
|
34
|
+
export interface GatewayEmailBindingMessage {
|
|
35
|
+
from: BindingAddress;
|
|
36
|
+
to?: BindingRecipients;
|
|
37
|
+
cc?: BindingRecipients;
|
|
38
|
+
bcc?: BindingRecipients;
|
|
39
|
+
subject: string;
|
|
40
|
+
replyTo?: BindingAddress;
|
|
41
|
+
headers?: Record<string, string>;
|
|
42
|
+
text?: string;
|
|
43
|
+
html?: string;
|
|
44
|
+
attachments?: {
|
|
45
|
+
disposition?: "inline" | "attachment";
|
|
46
|
+
contentId?: string;
|
|
47
|
+
filename: string;
|
|
48
|
+
type: string;
|
|
49
|
+
content: string | ArrayBuffer | ArrayBufferView;
|
|
50
|
+
}[];
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* A `send_email` binding (`SendEmail`) over the Gateway: pass it where code
|
|
54
|
+
* takes `env.EMAIL`. `send()` takes the structured message (`from`, `to`,
|
|
55
|
+
* `cc`, `bcc`, `subject`, `text`, `html`, `replyTo`, `headers`,
|
|
56
|
+
* `attachments`) and resolves `{ messageId }`. A raw MIME `EmailMessage`
|
|
57
|
+
* (`new EmailMessage(from, to, raw)`) is refused with `GatewayRefusalError`
|
|
58
|
+
* `email_invalid_message`: the Gateway takes structured mail only.
|
|
59
|
+
*
|
|
60
|
+
* Errors are sendEmail()'s: `GatewayRefusalError` with the Gateway's code
|
|
61
|
+
* (`email_sender_not_allowed`, `email_budget_exhausted`,
|
|
62
|
+
* `integration_not_configured`, …) and `ConfigurationError` with neither a
|
|
63
|
+
* binding nor `everyapp dev`. Nothing is checked when the binding is made.
|
|
64
|
+
*/
|
|
65
|
+
export declare function gatewayEmailBinding(env: object, options?: GatewayEmailBindingOptions): SendEmail;
|
|
66
|
+
/**
|
|
67
|
+
* The binding's message as sendEmail()'s: recipients as bare addresses,
|
|
68
|
+
* `from` and `replyTo` with their display names, attachments base64.
|
|
69
|
+
*/
|
|
70
|
+
export declare function toSendEmailMessage(message: unknown): SendEmailMessage;
|
|
71
|
+
export {};
|
|
72
|
+
//# sourceMappingURL=emailBinding.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"emailBinding.d.ts","sourceRoot":"","sources":["../../src/server/emailBinding.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,EAGL,KAAK,gBAAgB,EACtB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAIvD,MAAM,WAAW,0BAA0B;IACzC;;;OAGG;IACH,GAAG,CAAC,EAAE,cAAc,CAAC;CACtB;AAED,mEAAmE;AACnE,KAAK,cAAc,GAAG,MAAM,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAChE,KAAK,iBAAiB,GAAG,cAAc,GAAG,cAAc,EAAE,CAAC;AAE3D,oFAAoF;AACpF,MAAM,WAAW,0BAA0B;IACzC,IAAI,EAAE,cAAc,CAAC;IACrB,EAAE,CAAC,EAAE,iBAAiB,CAAC;IACvB,EAAE,CAAC,EAAE,iBAAiB,CAAC;IACvB,GAAG,CAAC,EAAE,iBAAiB,CAAC;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,cAAc,CAAC;IACzB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE;QACZ,WAAW,CAAC,EAAE,QAAQ,GAAG,YAAY,CAAC;QACtC,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,QAAQ,EAAE,MAAM,CAAC;QACjB,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,MAAM,GAAG,WAAW,GAAG,eAAe,CAAC;KACjD,EAAE,CAAC;CACL;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,mBAAmB,CACjC,GAAG,EAAE,MAAM,EACX,OAAO,GAAE,0BAA+B,GACvC,SAAS,CAcX;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,OAAO,GAAG,gBAAgB,CA6BrE"}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `gatewayEmailBinding(env)`: the Gateway's `email` capability as a
|
|
3
|
+
* `send_email` binding, for code written against `env.EMAIL.send(message)`.
|
|
4
|
+
*
|
|
5
|
+
* An app on Every App binds no `send_email` of its own: the Gateway sends
|
|
6
|
+
* with its sender, checks `from` against the installation's senders and
|
|
7
|
+
* keeps the mail budgets. Code that already calls `env.EMAIL.send()` gets
|
|
8
|
+
* this object in its place:
|
|
9
|
+
*
|
|
10
|
+
* const EMAIL = gatewayEmailBinding(env, { for: request });
|
|
11
|
+
* await EMAIL.send({ from, to, subject, text });
|
|
12
|
+
*
|
|
13
|
+
* Each `send()` is a `sendEmail()` (the manifest lists `capabilities: {
|
|
14
|
+
* email: { access: "write" } }`), so local development works the same way:
|
|
15
|
+
* checked and printed, or sent with the developer's own Email Sending
|
|
16
|
+
* account (EMAIL_REST_API_TOKEN, CLOUDFLARE_ACCOUNT_ID, EMAIL_FROM).
|
|
17
|
+
*/
|
|
18
|
+
import { ConfigurationError, } from "../internal/index.js";
|
|
19
|
+
import { GatewayRefusalError } from "./refusal.js";
|
|
20
|
+
import { sendEmail } from "./sendEmail.js";
|
|
21
|
+
/**
|
|
22
|
+
* A `send_email` binding (`SendEmail`) over the Gateway: pass it where code
|
|
23
|
+
* takes `env.EMAIL`. `send()` takes the structured message (`from`, `to`,
|
|
24
|
+
* `cc`, `bcc`, `subject`, `text`, `html`, `replyTo`, `headers`,
|
|
25
|
+
* `attachments`) and resolves `{ messageId }`. A raw MIME `EmailMessage`
|
|
26
|
+
* (`new EmailMessage(from, to, raw)`) is refused with `GatewayRefusalError`
|
|
27
|
+
* `email_invalid_message`: the Gateway takes structured mail only.
|
|
28
|
+
*
|
|
29
|
+
* Errors are sendEmail()'s: `GatewayRefusalError` with the Gateway's code
|
|
30
|
+
* (`email_sender_not_allowed`, `email_budget_exhausted`,
|
|
31
|
+
* `integration_not_configured`, …) and `ConfigurationError` with neither a
|
|
32
|
+
* binding nor `everyapp dev`. Nothing is checked when the binding is made.
|
|
33
|
+
*/
|
|
34
|
+
export function gatewayEmailBinding(env, options = {}) {
|
|
35
|
+
const binding = {
|
|
36
|
+
async send(message) {
|
|
37
|
+
const result = await sendEmail({
|
|
38
|
+
env,
|
|
39
|
+
...(options.for === undefined ? {} : { for: options.for }),
|
|
40
|
+
message: toSendEmailMessage(message),
|
|
41
|
+
});
|
|
42
|
+
return { messageId: result.messageId ?? "" };
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
// `SendEmail` is the app's own Workers types' interface, whose `send()`
|
|
46
|
+
// overloads change with the version; this `send()` takes both forms.
|
|
47
|
+
return binding;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* The binding's message as sendEmail()'s: recipients as bare addresses,
|
|
51
|
+
* `from` and `replyTo` with their display names, attachments base64.
|
|
52
|
+
*/
|
|
53
|
+
export function toSendEmailMessage(message) {
|
|
54
|
+
if (!message || typeof message !== "object") {
|
|
55
|
+
throw new ConfigurationError("EMAIL.send() takes a message object.");
|
|
56
|
+
}
|
|
57
|
+
if (!("subject" in message)) {
|
|
58
|
+
throw new GatewayRefusalError({
|
|
59
|
+
status: 400,
|
|
60
|
+
code: "email_invalid_message",
|
|
61
|
+
message: "Raw MIME messages can't be sent through the Every App gateway; send a structured message ({ from, to, subject, text or html }).",
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
const m = message;
|
|
65
|
+
return {
|
|
66
|
+
to: addresses(m.to) ?? [],
|
|
67
|
+
...(m.cc === undefined ? {} : { cc: addresses(m.cc) }),
|
|
68
|
+
...(m.bcc === undefined ? {} : { bcc: addresses(m.bcc) }),
|
|
69
|
+
subject: m.subject,
|
|
70
|
+
...(m.text ? { text: m.text } : {}),
|
|
71
|
+
...(m.html ? { html: m.html } : {}),
|
|
72
|
+
from: named(m.from),
|
|
73
|
+
...(m.replyTo ? { replyTo: named(m.replyTo) } : {}),
|
|
74
|
+
...(m.headers && Object.keys(m.headers).length > 0
|
|
75
|
+
? { headers: m.headers }
|
|
76
|
+
: {}),
|
|
77
|
+
...(m.attachments?.length
|
|
78
|
+
? { attachments: m.attachments.map(attachment) }
|
|
79
|
+
: {}),
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* One attachment in the Gateway's shape. An inline part without a content
|
|
84
|
+
* id can't be referenced (and the Gateway refuses it), so it goes as an
|
|
85
|
+
* ordinary attachment.
|
|
86
|
+
*/
|
|
87
|
+
function attachment(a) {
|
|
88
|
+
const contentId = a.contentId?.trim();
|
|
89
|
+
return {
|
|
90
|
+
filename: a.filename,
|
|
91
|
+
contentType: a.type,
|
|
92
|
+
content: base64(a.content),
|
|
93
|
+
...(a.disposition === "inline" && contentId
|
|
94
|
+
? { disposition: "inline", contentId }
|
|
95
|
+
: {}),
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
function named(address) {
|
|
99
|
+
if (typeof address === "string")
|
|
100
|
+
return { address };
|
|
101
|
+
return {
|
|
102
|
+
address: address.email,
|
|
103
|
+
...(address.name?.trim() ? { name: address.name } : {}),
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
function addresses(recipients) {
|
|
107
|
+
if (recipients === undefined)
|
|
108
|
+
return undefined;
|
|
109
|
+
const of = (a) => (typeof a === "string" ? a : a.email);
|
|
110
|
+
return Array.isArray(recipients) ? recipients.map(of) : of(recipients);
|
|
111
|
+
}
|
|
112
|
+
/** Strings are taken as base64 already, as the binding takes them; bytes are encoded. */
|
|
113
|
+
function base64(content) {
|
|
114
|
+
if (typeof content === "string")
|
|
115
|
+
return content;
|
|
116
|
+
const bytes = content instanceof ArrayBuffer
|
|
117
|
+
? new Uint8Array(content)
|
|
118
|
+
: new Uint8Array(content.buffer, content.byteOffset, content.byteLength);
|
|
119
|
+
let binary = "";
|
|
120
|
+
for (let i = 0; i < bytes.length; i += 0x8000) {
|
|
121
|
+
binary += String.fromCharCode(...bytes.subarray(i, i + 0x8000));
|
|
122
|
+
}
|
|
123
|
+
return btoa(binary);
|
|
124
|
+
}
|
package/dist/server/emit.d.ts
CHANGED
|
@@ -59,5 +59,5 @@ export interface EmitOptions {
|
|
|
59
59
|
* the gateway checks it, printed, and answered `{ id: "dev-…", type }`;
|
|
60
60
|
* nothing is delivered.
|
|
61
61
|
*/
|
|
62
|
-
export declare function emit({ env, for:
|
|
62
|
+
export declare function emit({ env, for: explicitTarget, event, data, }: EmitOptions): Promise<EmitEventResult>;
|
|
63
63
|
//# sourceMappingURL=emit.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"emit.d.ts","sourceRoot":"","sources":["../../src/server/emit.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EASL,KAAK,eAAe,EACrB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAGL,KAAK,cAAc,EACpB,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"emit.d.ts","sourceRoot":"","sources":["../../src/server/emit.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EASL,KAAK,eAAe,EACrB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAGL,KAAK,cAAc,EACpB,MAAM,kBAAkB,CAAC;AAQ1B,MAAM,WAAW,WAAW;IAC1B,8FAA8F;IAC9F,GAAG,EAAE,MAAM,CAAC;IACZ;;;;;;OAMG;IACH,GAAG,CAAC,EAAE,cAAc,CAAC;IACrB;;;;OAIG;IACH,KAAK,EAAE,MAAM,CAAC;IACd;;;;;OAKG;IACH,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC/B;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAsB,IAAI,CAAC,EACzB,GAAG,EACH,GAAG,EAAE,cAAc,EACnB,KAAK,EACL,IAAI,GACL,EAAE,WAAW,GAAG,OAAO,CAAC,eAAe,CAAC,CAkBxC"}
|
package/dist/server/emit.js
CHANGED
|
@@ -9,9 +9,9 @@
|
|
|
9
9
|
* two can't drift.
|
|
10
10
|
*/
|
|
11
11
|
import { APP_EVENT_DATA_MAX_BYTES, APP_ID_ENV, ConfigurationError, EVENTS_EMIT_PATH, appEventNameProblem, appEventType, checkEventData, } from "../internal/index.js";
|
|
12
|
-
import { applyAttribution,
|
|
13
|
-
import { BINDING_ORIGIN } from "./gatewayFetch.js";
|
|
14
|
-
import { GatewayRefusalError, gatewayRefusal } from "./
|
|
12
|
+
import { applyAttribution, attributionFor, } from "./attribution.js";
|
|
13
|
+
import { BINDING_ORIGIN, gatewayBinding, } from "./gatewayFetch.js";
|
|
14
|
+
import { GatewayRefusalError, gatewayRefusal } from "./refusal.js";
|
|
15
15
|
/**
|
|
16
16
|
* Put an event on the gateway's event bus, for the organization's webhook
|
|
17
17
|
* and email subscribers. The gateway names it `app.<app id>.<event>`, so an
|
|
@@ -36,10 +36,10 @@ import { GatewayRefusalError, gatewayRefusal } from "./integrationFetch.js";
|
|
|
36
36
|
* the gateway checks it, printed, and answered `{ id: "dev-…", type }`;
|
|
37
37
|
* nothing is delivered.
|
|
38
38
|
*/
|
|
39
|
-
export async function emit({ env, for:
|
|
40
|
-
|
|
39
|
+
export async function emit({ env, for: explicitTarget, event, data, }) {
|
|
40
|
+
const target = attributionFor(env, explicitTarget, "emit");
|
|
41
41
|
const values = env;
|
|
42
|
-
const binding =
|
|
42
|
+
const binding = gatewayBinding(env);
|
|
43
43
|
if (binding) {
|
|
44
44
|
if (typeof binding.fetch !== "function") {
|
|
45
45
|
throw new ConfigurationError("EVERY_APP_GATEWAY is configured but is not a valid service binding.");
|
|
@@ -66,7 +66,7 @@ async function emitThroughGateway(binding, target, body) {
|
|
|
66
66
|
throw new GatewayRefusalError({
|
|
67
67
|
status: response.status,
|
|
68
68
|
code: "event_emit_failed",
|
|
69
|
-
message: `The
|
|
69
|
+
message: `The EveryApp gateway answered ${response.status} to the event.`,
|
|
70
70
|
});
|
|
71
71
|
}
|
|
72
72
|
return (await response.json());
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `EveryAppEnv`: what Every App puts in an app Worker's `env`, for an app
|
|
3
|
+
* that types its env by hand. An app built with `everyAppWorker()` needs
|
|
4
|
+
* none of it: `cf workers types` writes the same names into `Env` from the
|
|
5
|
+
* Worker's definition.
|
|
6
|
+
*/
|
|
7
|
+
import type { EveryAppManifest } from "../manifest.js";
|
|
8
|
+
/** The `env` keys Every App sets on an app's Worker, deployed and under `everyapp dev`. */
|
|
9
|
+
export interface EveryAppEnv<M extends EveryAppManifest = EveryAppManifest> {
|
|
10
|
+
/** The manifest's `id`. */
|
|
11
|
+
EVERYAPP_APP_ID: string;
|
|
12
|
+
/**
|
|
13
|
+
* The manifest (`everyapp.config.ts`) as JSON: what the app asked for,
|
|
14
|
+
* for a page that shows it. `EveryAppEnv<typeof manifest>` types it as
|
|
15
|
+
* the app's own.
|
|
16
|
+
*/
|
|
17
|
+
EVERYAPP_MANIFEST: M;
|
|
18
|
+
/** The Gateway's URL; links back to it go through `gatewayUrl(env)`. */
|
|
19
|
+
GATEWAY_URL: string;
|
|
20
|
+
/** The identity tokens' issuer: the Gateway's URL, or the dev issuer. */
|
|
21
|
+
EVERYAPP_IDENTITY_ISSUER: string;
|
|
22
|
+
/** The public keys (JSON array of PEMs) `everyApp()` verifies identities with. */
|
|
23
|
+
EVERYAPP_IDENTITY_PUBLIC_KEYS: string;
|
|
24
|
+
/** Deployed: the Worker's name, the identity tokens' audience. */
|
|
25
|
+
EVERYAPP_AUDIENCE?: string;
|
|
26
|
+
/** Deployed: the organization the app is installed for. */
|
|
27
|
+
EVERY_APP_ORG_ID?: string;
|
|
28
|
+
/** Deployed, on an app installed for every organization: `"1"`. */
|
|
29
|
+
EVERYAPP_SHARED?: string;
|
|
30
|
+
/** Deployed: the service binding to the Gateway, which capabilities, AI and email go through. */
|
|
31
|
+
EVERY_APP_GATEWAY?: Fetcher;
|
|
32
|
+
/** `"1"` under `everyapp dev`, absent when deployed. */
|
|
33
|
+
EVERYAPP_DEV?: string;
|
|
34
|
+
/** Under `everyapp dev`: the manifest's `capabilities`, as JSON, for the SDK's dev fallbacks. */
|
|
35
|
+
EVERYAPP_DEV_CAPABILITIES?: string;
|
|
36
|
+
/** Under `everyapp dev`: the app's `.everyapp/providers/*.json`, as JSON. */
|
|
37
|
+
EVERYAPP_DEV_PROVIDERS?: string;
|
|
38
|
+
/** Under `everyapp dev`, from `.dev.vars`: the senders standing in for `everyapp email set --sender`. */
|
|
39
|
+
EVERYAPP_EMAIL_SENDERS?: string;
|
|
40
|
+
/** Under `everyapp dev --gateway` (linked dev): the gateway-lite's forwarder, which the SDK's services call the real Gateway through, and the run's nonce. */
|
|
41
|
+
EVERYAPP_LINKED_DEV_URL?: string;
|
|
42
|
+
EVERYAPP_LINKED_DEV_NONCE?: string;
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=env.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"env.d.ts","sourceRoot":"","sources":["../../src/server/env.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAEvD,2FAA2F;AAC3F,MAAM,WAAW,WAAW,CAAC,CAAC,SAAS,gBAAgB,GAAG,gBAAgB;IACxE,2BAA2B;IAC3B,eAAe,EAAE,MAAM,CAAC;IACxB;;;;OAIG;IACH,iBAAiB,EAAE,CAAC,CAAC;IACrB,wEAAwE;IACxE,WAAW,EAAE,MAAM,CAAC;IACpB,yEAAyE;IACzE,wBAAwB,EAAE,MAAM,CAAC;IACjC,kFAAkF;IAClF,6BAA6B,EAAE,MAAM,CAAC;IACtC,kEAAkE;IAClE,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,2DAA2D;IAC3D,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,mEAAmE;IACnE,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,iGAAiG;IACjG,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,wDAAwD;IACxD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,iGAAiG;IACjG,yBAAyB,CAAC,EAAE,MAAM,CAAC;IACnC,6EAA6E;IAC7E,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,yGAAyG;IACzG,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,8JAA8J;IAC9J,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,yBAAyB,CAAC,EAAE,MAAM,CAAC;CACpC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|