@every-app/sdk 0.3.0-rc.8 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (146) hide show
  1. package/README.md +75 -74
  2. package/dist/cloudflare/catalogSettings.generated.d.ts +20 -6
  3. package/dist/cloudflare/catalogSettings.generated.d.ts.map +1 -1
  4. package/dist/cloudflare/catalogSettings.generated.js +45 -14
  5. package/dist/cloudflare/index.d.ts +1 -1
  6. package/dist/cloudflare/index.d.ts.map +1 -1
  7. package/dist/cloudflare/sentinel.d.ts +3 -0
  8. package/dist/cloudflare/sentinel.d.ts.map +1 -1
  9. package/dist/cloudflare/sentinel.js +10 -0
  10. package/dist/cloudflare/worker.d.ts +67 -23
  11. package/dist/cloudflare/worker.d.ts.map +1 -1
  12. package/dist/cloudflare/worker.js +68 -32
  13. package/dist/index.d.ts +1 -1
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/internal/aiDefault.d.ts +36 -79
  16. package/dist/internal/aiDefault.d.ts.map +1 -1
  17. package/dist/internal/aiDefault.js +47 -149
  18. package/dist/internal/aiProviders.d.ts +1 -1
  19. package/dist/internal/aiProviders.d.ts.map +1 -1
  20. package/dist/internal/aiProviders.js +2 -2
  21. package/dist/internal/appSettings.d.ts +140 -89
  22. package/dist/internal/appSettings.d.ts.map +1 -1
  23. package/dist/internal/appSettings.js +264 -156
  24. package/dist/internal/capabilities.d.ts +145 -503
  25. package/dist/internal/capabilities.d.ts.map +1 -1
  26. package/dist/internal/capabilities.js +29 -550
  27. package/dist/internal/catalog/commerce.js +1 -1
  28. package/dist/internal/catalog/comms.d.ts +37 -0
  29. package/dist/internal/catalog/comms.d.ts.map +1 -1
  30. package/dist/internal/catalog/comms.js +43 -0
  31. package/dist/internal/catalog/index.d.ts +72 -0
  32. package/dist/internal/catalog/index.d.ts.map +1 -1
  33. package/dist/internal/catalog/infra.d.ts +35 -0
  34. package/dist/internal/catalog/infra.d.ts.map +1 -1
  35. package/dist/internal/catalog/infra.js +65 -0
  36. package/dist/internal/devProviders.d.ts +1 -19
  37. package/dist/internal/devProviders.d.ts.map +1 -1
  38. package/dist/internal/devProviders.js +7 -1
  39. package/dist/internal/events.d.ts +52 -1
  40. package/dist/internal/events.d.ts.map +1 -1
  41. package/dist/internal/events.js +66 -2
  42. package/dist/internal/gatewaySettings.d.ts +6 -16
  43. package/dist/internal/gatewaySettings.d.ts.map +1 -1
  44. package/dist/internal/gatewaySettings.js +6 -24
  45. package/dist/internal/index.d.ts +1 -0
  46. package/dist/internal/index.d.ts.map +1 -1
  47. package/dist/internal/index.js +1 -0
  48. package/dist/internal/mcpOAuth.d.ts +210 -0
  49. package/dist/internal/mcpOAuth.d.ts.map +1 -0
  50. package/dist/internal/mcpOAuth.js +735 -0
  51. package/dist/internal/oauthClient.d.ts +7 -47
  52. package/dist/internal/oauthClient.d.ts.map +1 -1
  53. package/dist/internal/oauthClient.js +1 -115
  54. package/dist/internal/oauthPresets.d.ts +356 -20
  55. package/dist/internal/oauthPresets.d.ts.map +1 -1
  56. package/dist/internal/oauthPresets.js +183 -861
  57. package/dist/internal/oauthScopes.d.ts +43 -155
  58. package/dist/internal/oauthScopes.d.ts.map +1 -1
  59. package/dist/internal/oauthScopes.js +82 -475
  60. package/dist/internal/oauthSpec.d.ts +28 -71
  61. package/dist/internal/oauthSpec.d.ts.map +1 -1
  62. package/dist/internal/oauthSpec.js +172 -448
  63. package/dist/internal/protocol.d.ts +39 -15
  64. package/dist/internal/protocol.d.ts.map +1 -1
  65. package/dist/internal/protocol.js +40 -7
  66. package/dist/internal/providerApps.d.ts +2 -2
  67. package/dist/internal/providerApps.js +2 -2
  68. package/dist/internal/providerRegistry.d.ts +5 -64
  69. package/dist/internal/providerRegistry.d.ts.map +1 -1
  70. package/dist/internal/providerRegistry.js +19 -118
  71. package/dist/internal/providerSpec.d.ts.map +1 -1
  72. package/dist/internal/providerSpec.js +14 -36
  73. package/dist/internal/remoteMcp.d.ts +191 -47
  74. package/dist/internal/remoteMcp.d.ts.map +1 -1
  75. package/dist/internal/remoteMcp.js +338 -133
  76. package/dist/internal/signInProviders.d.ts +1 -1
  77. package/dist/internal/signInProviders.d.ts.map +1 -1
  78. package/dist/internal/tools.d.ts +15 -8
  79. package/dist/internal/tools.d.ts.map +1 -1
  80. package/dist/internal/tools.js +9 -8
  81. package/dist/manifest.d.ts +96 -28
  82. package/dist/manifest.d.ts.map +1 -1
  83. package/dist/manifest.js +1 -1
  84. package/dist/server/ai.d.ts +1 -93
  85. package/dist/server/ai.d.ts.map +1 -1
  86. package/dist/server/ai.js +17 -111
  87. package/dist/server/attribution.d.ts +10 -1
  88. package/dist/server/attribution.d.ts.map +1 -1
  89. package/dist/server/attribution.js +7 -1
  90. package/dist/server/connectUrl.d.ts +4 -9
  91. package/dist/server/connectUrl.d.ts.map +1 -1
  92. package/dist/server/connectUrl.js +17 -14
  93. package/dist/server/context.d.ts +12 -14
  94. package/dist/server/context.d.ts.map +1 -1
  95. package/dist/server/context.js +23 -25
  96. package/dist/server/emit.d.ts.map +1 -1
  97. package/dist/server/emit.js +2 -2
  98. package/dist/server/env.d.ts +3 -0
  99. package/dist/server/env.d.ts.map +1 -1
  100. package/dist/server/eventsEndpoint.d.ts +48 -0
  101. package/dist/server/eventsEndpoint.d.ts.map +1 -0
  102. package/dist/server/eventsEndpoint.js +145 -0
  103. package/dist/server/everyApp.d.ts +16 -26
  104. package/dist/server/everyApp.d.ts.map +1 -1
  105. package/dist/server/everyApp.js +22 -46
  106. package/dist/server/gatewayFetch.d.ts +19 -1
  107. package/dist/server/gatewayFetch.d.ts.map +1 -1
  108. package/dist/server/gatewayFetch.js +45 -3
  109. package/dist/server/index.d.ts +6 -9
  110. package/dist/server/index.d.ts.map +1 -1
  111. package/dist/server/index.js +4 -8
  112. package/dist/server/integrationFetch.d.ts +60 -72
  113. package/dist/server/integrationFetch.d.ts.map +1 -1
  114. package/dist/server/integrationFetch.js +124 -397
  115. package/dist/server/listMembers.d.ts.map +1 -1
  116. package/dist/server/listMembers.js +2 -2
  117. package/dist/server/refusal.d.ts +0 -9
  118. package/dist/server/refusal.d.ts.map +1 -1
  119. package/dist/server/refusal.js +0 -22
  120. package/dist/server/sendEmail.d.ts.map +1 -1
  121. package/dist/server/sendEmail.js +2 -2
  122. package/dist/server/tools.d.ts +17 -6
  123. package/dist/server/tools.d.ts.map +1 -1
  124. package/dist/server/tools.js +14 -4
  125. package/dist/server/toolsEndpoint.d.ts +2 -1
  126. package/dist/server/toolsEndpoint.d.ts.map +1 -1
  127. package/dist/server/toolsEndpoint.js +4 -3
  128. package/dist/server/webhooks.d.ts +56 -0
  129. package/dist/server/webhooks.d.ts.map +1 -0
  130. package/dist/server/webhooks.js +205 -0
  131. package/dist/testing/index.d.ts +2 -3
  132. package/dist/testing/index.d.ts.map +1 -1
  133. package/dist/testing/index.js +2 -6
  134. package/package.json +6 -1
  135. package/dist/server/aiBinding.d.ts +0 -68
  136. package/dist/server/aiBinding.d.ts.map +0 -1
  137. package/dist/server/aiBinding.js +0 -294
  138. package/dist/server/authorizeUrl.d.ts +0 -43
  139. package/dist/server/authorizeUrl.d.ts.map +0 -1
  140. package/dist/server/authorizeUrl.js +0 -106
  141. package/dist/server/emailBinding.d.ts +0 -72
  142. package/dist/server/emailBinding.d.ts.map +0 -1
  143. package/dist/server/emailBinding.js +0 -124
  144. package/dist/server/fromMcpTools.d.ts +0 -89
  145. package/dist/server/fromMcpTools.d.ts.map +0 -1
  146. package/dist/server/fromMcpTools.js +0 -192
@@ -2,7 +2,7 @@
2
2
  * Whom a call through the gateway is for, which decides the organization
3
3
  * it counts against. Left out, it comes from the request context: the
4
4
  * request `everyApp()` is serving (its person, while the identity is good),
5
- * or the organization `withOrganization()` named. Given explicitly:
5
+ * or the organization the SDK's event delivery runs under. Given explicitly:
6
6
  *
7
7
  * - the inbound `Request`: its person, by the identity the gateway put on
8
8
  * it (forwarded as is);
@@ -12,10 +12,19 @@
12
12
  * checks is active and has a live member. An organization's own app may
13
13
  * leave it out (the gateway knows its organization) and may only name
14
14
  * its own.
15
+ * - `{ background: true, organizationId, userId }`: a call to an OAuth
16
+ * provider's API (`integrationFetch`) on the connection member `userId`
17
+ * made in this app, at any time, even while serving another person's
18
+ * request: the call carries that member (`x-everyapp-user`) and never
19
+ * the identity of the person being served. Only an organization's own
20
+ * app may name a member, and only one who may still open the app. No
21
+ * identity for the member reaches the app. Other gateway calls ignore
22
+ * `userId`.
15
23
  */
16
24
  export type GatewayCallFor = Request | {
17
25
  background: true;
18
26
  organizationId?: string;
27
+ userId?: string;
19
28
  };
20
29
  /**
21
30
  * A gateway call that doesn't say whom it is for where it must: from a
@@ -1 +1 @@
1
- {"version":3,"file":"attribution.d.ts","sourceRoot":"","sources":["../../src/server/attribution.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;GAcG;AACH,MAAM,MAAM,cAAc,GACtB,OAAO,GACP;IAAE,UAAU,EAAE,IAAI,CAAC;IAAC,cAAc,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAElD;;;GAGG;AACH,qBAAa,gBAAiB,SAAQ,KAAK;IACzC,QAAQ,CAAC,IAAI,0BAA0B;gBAC3B,OAAO,EAAE,MAAM;CAI5B;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAC5B,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,cAAc,GAAG,SAAS,EAClC,IAAI,EAAE,MAAM,GACX,cAAc,GAAG,SAAS,CAI5B;AA8BD;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,cAAc,GAAG,SAAS,GACjC,IAAI,CAUN;AASD,0EAA0E;AAC1E,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,OAAO,CAO1D"}
1
+ {"version":3,"file":"attribution.d.ts","sourceRoot":"","sources":["../../src/server/attribution.ts"],"names":[],"mappings":"AAQA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,MAAM,cAAc,GACtB,OAAO,GACP;IAAE,UAAU,EAAE,IAAI,CAAC;IAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAEnE;;;GAGG;AACH,qBAAa,gBAAiB,SAAQ,KAAK;IACzC,QAAQ,CAAC,IAAI,0BAA0B;gBAC3B,OAAO,EAAE,MAAM;CAI5B;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAC5B,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,cAAc,GAAG,SAAS,EAClC,IAAI,EAAE,MAAM,GACX,cAAc,GAAG,SAAS,CAI5B;AAmCD;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,cAAc,GAAG,SAAS,GACjC,IAAI,CAYN;AASD,0EAA0E;AAC1E,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,OAAO,CAO1D"}
@@ -1,4 +1,4 @@
1
- import { IDENTITY_HEADER, ORG_HEADER, SHARED_ENV } from "../internal/index.js";
1
+ import { IDENTITY_HEADER, ORG_HEADER, SHARED_ENV, USER_HEADER, } from "../internal/index.js";
2
2
  import { attributionFromContext } from "./context.js";
3
3
  /**
4
4
  * A gateway call that doesn't say whom it is for where it must: from a
@@ -28,6 +28,9 @@ function checkAttribution(env, target, what) {
28
28
  if (target === null || typeof target !== "object" || !target.background) {
29
29
  throw new AttributionError(`${what}: \`for\` is the inbound Request or { background: true, organizationId }.`);
30
30
  }
31
+ if (target.userId !== undefined && !nonEmpty(target.userId)) {
32
+ throw new AttributionError(`${what}: { background: true, userId } needs a non-empty user id.`);
33
+ }
31
34
  if (target.organizationId !== undefined &&
32
35
  !nonEmpty(target.organizationId)) {
33
36
  throw new AttributionError(`${what}: { background: true, organizationId } needs a non-empty organization id.`);
@@ -46,6 +49,7 @@ function checkAttribution(env, target, what) {
46
49
  export function applyAttribution(headers, target) {
47
50
  headers.delete(IDENTITY_HEADER);
48
51
  headers.delete(ORG_HEADER);
52
+ headers.delete(USER_HEADER);
49
53
  if (target === undefined)
50
54
  return;
51
55
  if (isRequest(target)) {
@@ -56,6 +60,8 @@ export function applyAttribution(headers, target) {
56
60
  }
57
61
  if (target.organizationId)
58
62
  headers.set(ORG_HEADER, target.organizationId);
63
+ if (target.userId)
64
+ headers.set(USER_HEADER, target.userId.trim());
59
65
  }
60
66
  function isSharedWorker(env) {
61
67
  const value = env[SHARED_ENV];
@@ -1,6 +1,5 @@
1
- import type { ConnectionAccount } from "../internal/index.js";
2
1
  export interface ConnectUrlOptions {
3
- /** The Worker env: `GATEWAY_URL`, which every deployed Worker and `everyapp dev` set. */
2
+ /** The Worker env: `GATEWAY_URL` and the app's id, which every deployed Worker and `everyapp dev` set. */
4
3
  env: object;
5
4
  /** The OAuth provider's id (`google-workspace`, `github`). */
6
5
  provider: string;
@@ -9,13 +8,9 @@ export interface ConnectUrlOptions {
9
8
  * a path on the Gateway. Absent: Settings → Connected accounts.
10
9
  */
11
10
  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
11
  }
17
- /** The path of the connect page for `provider` on the Gateway. */
12
+ /** The path of the connect page for `provider` on the Gateway, without the app. */
18
13
  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;
14
+ /** The absolute URL that connects the person to `provider` in this app. */
15
+ export declare function connectUrl({ env, provider, returnTo, }: ConnectUrlOptions): string;
21
16
  //# sourceMappingURL=connectUrl.d.ts.map
@@ -1 +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"}
1
+ {"version":3,"file":"connectUrl.d.ts","sourceRoot":"","sources":["../../src/server/connectUrl.ts"],"names":[],"mappings":"AAaA,MAAM,WAAW,iBAAiB;IAChC,0GAA0G;IAC1G,GAAG,EAAE,MAAM,CAAC;IACZ,8DAA8D;IAC9D,QAAQ,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,mFAAmF;AACnF,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEpD;AAED,2EAA2E;AAC3E,wBAAgB,UAAU,CAAC,EACzB,GAAG,EACH,QAAQ,EACR,QAAQ,GACT,EAAE,iBAAiB,GAAG,MAAM,CAwB5B"}
@@ -1,19 +1,21 @@
1
1
  /**
2
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).
3
+ * runs the flow for (docs/design/2026-11-oauth-simplify.md):
4
+ * `<gateway>/connect/<id>?app=<this app>&return=<back here>`. A
5
+ * connection belongs to one person and one app: the Gateway checks the
6
+ * session and that the person may open the app, asks the provider for
7
+ * exactly the scopes the app's `everyapp.config.ts` lists (plus the ones
8
+ * that name the account), finishes on its own callback and sends the
9
+ * browser back to `returnTo`, which must be a path on the Gateway or an
10
+ * https URL on one of its app hosts (in local dev also localhost).
9
11
  */
10
- import { ConfigurationError } from "../internal/index.js";
11
- /** The path of the connect page for `provider` on the Gateway. */
12
+ import { APP_ID_ENV, ConfigurationError } from "../internal/index.js";
13
+ /** The path of the connect page for `provider` on the Gateway, without the app. */
12
14
  export function connectPath(provider) {
13
15
  return `/connect/${encodeURIComponent(provider)}`;
14
16
  }
15
- /** The absolute URL that starts the Gateway-owned flow for `provider`. */
16
- export function connectUrl({ env, provider, returnTo, account, sets, }) {
17
+ /** The absolute URL that connects the person to `provider` in this app. */
18
+ export function connectUrl({ env, provider, returnTo, }) {
17
19
  const values = env;
18
20
  const gateway = values["GATEWAY_URL"];
19
21
  if (typeof gateway !== "string" || !gateway.trim()) {
@@ -26,12 +28,13 @@ export function connectUrl({ env, provider, returnTo, account, sets, }) {
26
28
  catch {
27
29
  throw new ConfigurationError(`GATEWAY_URL isn't a URL: ${gateway}`);
28
30
  }
31
+ const app = values[APP_ID_ENV];
32
+ if (typeof app !== "string" || !app.trim()) {
33
+ throw new ConfigurationError(`connectUrl() needs ${APP_ID_ENV} in the Worker env: a connection belongs to one app, and the deploy and everyapp dev set it.`);
34
+ }
29
35
  const url = new URL(connectPath(provider), base.origin);
36
+ url.searchParams.set("app", app.trim());
30
37
  if (returnTo)
31
38
  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
39
  return url.toString();
37
40
  }
@@ -14,9 +14,9 @@ export interface ServedRequest {
14
14
  */
15
15
  export declare const IDENTITY_EXPIRY_MARGIN_MS = 15000;
16
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.
17
+ * Code `request_context_missing`: a Gateway call with no `for`, outside
18
+ * any request `everyApp()` serves. Work no request started passes
19
+ * `for: { background: true }`.
20
20
  */
21
21
  export declare class RequestContextError extends Error {
22
22
  readonly code = "request_context_missing";
@@ -35,20 +35,18 @@ export declare function serveRequest<T>(served: ServedRequest, fn: () => T): T;
35
35
  /**
36
36
  * Run `fn` as `organizationId`'s background work: every Gateway call made
37
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.
38
+ * with no person behind it. The SDK's event delivery (eventsEndpoint.ts)
39
+ * runs the app's `events.on` under it; apps pass `for` instead. Inside a
40
+ * request another organization's person is driving, it throws
41
+ * {@link OrganizationMismatchError}; inside that organization's own
42
+ * request, the person stays attributed while their identity is good.
45
43
  */
46
44
  export declare function withOrganization<T>(organizationId: string, fn: () => T): T;
47
45
  /**
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.
46
+ * The verified user of the request being served (`everyApp()` reads it to
47
+ * reuse one verification): an `EveryAppUser`, or `null` on a public route
48
+ * and inside `withOrganization()`'s background work. Throws
49
+ * {@link RequestContextError} outside both.
52
50
  */
53
51
  export declare function currentUser(): EveryAppUser | null;
54
52
  /**
@@ -1 +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"}
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;;;;;;;;GAQG;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"}
@@ -4,17 +4,17 @@
4
4
  * `AsyncLocalStorage`, which every Every App Worker has (`nodejs_compat`).
5
5
  *
6
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`.
7
+ * the verified user and the request the Gateway forwarded. Every call back
8
+ * to the Gateway made inside it (`integrationFetch`, `sendEmail`, `emit`,
9
+ * ...) is attributed to that person without a `for`.
11
10
  *
12
11
  * 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.
12
+ * Durable Object alarm) has no context of its own: it says whom each call
13
+ * is for, `for: { background: true }` (a shared app's Worker adds the
14
+ * `organizationId`). The SDK's own event delivery runs its handler under
15
+ * `withOrganization()`, which does the same for every call inside it. A
16
+ * Gateway call with neither is refused before it is made
17
+ * ({@link RequestContextError}): the SDK never acts as nobody.
18
18
  */
19
19
  import { AsyncLocalStorage } from "node:async_hooks";
20
20
  const scopes = new AsyncLocalStorage();
@@ -25,9 +25,9 @@ const scopes = new AsyncLocalStorage();
25
25
  */
26
26
  export const IDENTITY_EXPIRY_MARGIN_MS = 15_000;
27
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.
28
+ * Code `request_context_missing`: a Gateway call with no `for`, outside
29
+ * any request `everyApp()` serves. Work no request started passes
30
+ * `for: { background: true }`.
31
31
  */
32
32
  export class RequestContextError extends Error {
33
33
  code = "request_context_missing";
@@ -54,13 +54,11 @@ export function serveRequest(served, fn) {
54
54
  /**
55
55
  * Run `fn` as `organizationId`'s background work: every Gateway call made
56
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.
57
+ * with no person behind it. The SDK's event delivery (eventsEndpoint.ts)
58
+ * runs the app's `events.on` under it; apps pass `for` instead. Inside a
59
+ * request another organization's person is driving, it throws
60
+ * {@link OrganizationMismatchError}; inside that organization's own
61
+ * request, the person stays attributed while their identity is good.
64
62
  */
65
63
  export function withOrganization(organizationId, fn) {
66
64
  if (typeof organizationId !== "string" || organizationId.trim() === "") {
@@ -74,15 +72,15 @@ export function withOrganization(organizationId, fn) {
74
72
  return scopes.run({ ...parent, organizationId }, fn);
75
73
  }
76
74
  /**
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.
75
+ * The verified user of the request being served (`everyApp()` reads it to
76
+ * reuse one verification): an `EveryAppUser`, or `null` on a public route
77
+ * and inside `withOrganization()`'s background work. Throws
78
+ * {@link RequestContextError} outside both.
81
79
  */
82
80
  export function currentUser() {
83
81
  const scope = scopes.getStore();
84
82
  if (!scope) {
85
- throw new RequestContextError("currentUser() was called outside a request everyApp() serves and outside withOrganization(): there is no user here.");
83
+ throw new RequestContextError("currentUser() was called outside a request everyApp() serves: there is no user here.");
86
84
  }
87
85
  return scope.served?.user ?? null;
88
86
  }
@@ -99,7 +97,7 @@ export function currentUser() {
99
97
  export function attributionFromContext(what, now = Date.now()) {
100
98
  const scope = scopes.getStore();
101
99
  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).`);
100
+ throw new RequestContextError(`${what} was called with no \`for\`, outside a request everyApp() serves: the call would be nobody's. Pass for: request, or for: { background: true } for work no request started (a cron, a Workflow step, a queue consumer).`);
103
101
  }
104
102
  const served = scope.served;
105
103
  if (served?.user) {
@@ -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;AAI1B,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,CAoBxC"}
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"}
@@ -10,7 +10,7 @@
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
12
  import { applyAttribution, attributionFor, } from "./attribution.js";
13
- import { BINDING_ORIGIN } from "./gatewayFetch.js";
13
+ import { BINDING_ORIGIN, gatewayBinding, } from "./gatewayFetch.js";
14
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
@@ -39,7 +39,7 @@ import { GatewayRefusalError, gatewayRefusal } from "./refusal.js";
39
39
  export async function emit({ env, for: explicitTarget, event, data, }) {
40
40
  const target = attributionFor(env, explicitTarget, "emit");
41
41
  const values = env;
42
- const binding = values["EVERY_APP_GATEWAY"];
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.");
@@ -37,5 +37,8 @@ export interface EveryAppEnv<M extends EveryAppManifest = EveryAppManifest> {
37
37
  EVERYAPP_DEV_PROVIDERS?: string;
38
38
  /** Under `everyapp dev`, from `.dev.vars`: the senders standing in for `everyapp email set --sender`. */
39
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;
40
43
  }
41
44
  //# sourceMappingURL=env.d.ts.map
@@ -1 +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;CACjC"}
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,48 @@
1
+ /**
2
+ * The app side of the `app` event sink (docs/design/2026-11-birds-decisions/app-event-sink.md).
3
+ *
4
+ * `everyApp()` answers the reserved `POST /__everyapp/events` here, before
5
+ * any app code runs and whether or not the app passed `events`:
6
+ *
7
+ * - the request needs the Gateway's system token for this app (purpose
8
+ * `events.deliver`), whose `bh` claim is the SHA-256 of this exact body,
9
+ * so a token opens one delivery and nothing else;
10
+ * - the envelope must name the organization this Worker serves
11
+ * (`EVERY_APP_ORG_ID`), when it is pinned;
12
+ * - only then is `events.on` called, under `withOrganization()`, so every
13
+ * Gateway call it makes is that organization's background work.
14
+ *
15
+ * The perimeter refuses the path from outside and strips inbound
16
+ * `x-everyapp-*` headers, so only the Gateway's event bus reaches it, over
17
+ * the service binding.
18
+ */
19
+ import { type AppEventEnvelope, type RequestIdentityOptions } from "../internal/index.js";
20
+ import type { ExecutionContextLike } from "./everyApp.js";
21
+ /** One event the Gateway delivered: the webhook envelope, plus the delivery's id and attempt. */
22
+ export interface AppEvent extends AppEventEnvelope {
23
+ delivery: {
24
+ id: string;
25
+ attempt: number;
26
+ };
27
+ }
28
+ export type AppEventHandler<TEnv = unknown> = (event: AppEvent, env: TEnv, ctx: ExecutionContextLike) => unknown | Promise<unknown>;
29
+ /**
30
+ * `everyApp(handler, { events: { on } })`. `on` is one function for every
31
+ * event, or one per exact type (`"member.removed"`). An event with no
32
+ * handler is acknowledged and dropped. A handler that throws answers 500,
33
+ * and the Gateway retries the delivery with its usual backoff.
34
+ */
35
+ export interface AppEventsOptions<TEnv = unknown> {
36
+ on: AppEventHandler<TEnv> | Partial<Record<string, AppEventHandler<TEnv>>>;
37
+ }
38
+ export interface EventsEndpointOptions {
39
+ events?: AppEventsOptions<never>;
40
+ /** How to verify the Gateway's token (keys, issuer, audience, dev opt-in). */
41
+ identity: RequestIdentityOptions;
42
+ }
43
+ /**
44
+ * Answer a request to the reserved events path, or return null for any
45
+ * other path so the app's own handler runs.
46
+ */
47
+ export declare function handleEventsRequest(request: Request, env: unknown, ctx: ExecutionContextLike, options: EventsEndpointOptions): Promise<Response | null>;
48
+ //# sourceMappingURL=eventsEndpoint.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"eventsEndpoint.d.ts","sourceRoot":"","sources":["../../src/server/eventsEndpoint.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AACH,OAAO,EASL,KAAK,gBAAgB,EACrB,KAAK,sBAAsB,EAC5B,MAAM,sBAAsB,CAAC;AAE9B,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAE1D,iGAAiG;AACjG,MAAM,WAAW,QAAS,SAAQ,gBAAgB;IAChD,QAAQ,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;CAC3C;AAED,MAAM,MAAM,eAAe,CAAC,IAAI,GAAG,OAAO,IAAI,CAC5C,KAAK,EAAE,QAAQ,EACf,GAAG,EAAE,IAAI,EACT,GAAG,EAAE,oBAAoB,KACtB,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;AAEhC;;;;;GAKG;AACH,MAAM,WAAW,gBAAgB,CAAC,IAAI,GAAG,OAAO;IAC9C,EAAE,EAAE,eAAe,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;CAC5E;AAED,MAAM,WAAW,qBAAqB;IACpC,MAAM,CAAC,EAAE,gBAAgB,CAAC,KAAK,CAAC,CAAC;IACjC,8EAA8E;IAC9E,QAAQ,EAAE,sBAAsB,CAAC;CAClC;AAwCD;;;GAGG;AACH,wBAAsB,mBAAmB,CACvC,OAAO,EAAE,OAAO,EAChB,GAAG,EAAE,OAAO,EACZ,GAAG,EAAE,oBAAoB,EACzB,OAAO,EAAE,qBAAqB,GAC7B,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CA4F1B"}
@@ -0,0 +1,145 @@
1
+ /**
2
+ * The app side of the `app` event sink (docs/design/2026-11-birds-decisions/app-event-sink.md).
3
+ *
4
+ * `everyApp()` answers the reserved `POST /__everyapp/events` here, before
5
+ * any app code runs and whether or not the app passed `events`:
6
+ *
7
+ * - the request needs the Gateway's system token for this app (purpose
8
+ * `events.deliver`), whose `bh` claim is the SHA-256 of this exact body,
9
+ * so a token opens one delivery and nothing else;
10
+ * - the envelope must name the organization this Worker serves
11
+ * (`EVERY_APP_ORG_ID`), when it is pinned;
12
+ * - only then is `events.on` called, under `withOrganization()`, so every
13
+ * Gateway call it makes is that organization's background work.
14
+ *
15
+ * The perimeter refuses the path from outside and strips inbound
16
+ * `x-everyapp-*` headers, so only the Gateway's event bus reaches it, over
17
+ * the service binding.
18
+ */
19
+ import { APP_EVENTS_PATH, ConfigurationError, EVENT_HEADERS, IdentityError, SYSTEM_HEADER, bodyHash, resolveVerifyOptions, verifySystemJwt, } from "../internal/index.js";
20
+ import { withOrganization } from "./context.js";
21
+ /** The most body the endpoint reads: an envelope's data is at most 16 KiB. */
22
+ const MAX_BODY_BYTES = 64 * 1024;
23
+ function answer(status, body) {
24
+ return Response.json(body, {
25
+ status,
26
+ headers: { "cache-control": "no-store" },
27
+ });
28
+ }
29
+ function isEnvelope(value) {
30
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
31
+ return false;
32
+ }
33
+ const v = value;
34
+ const org = v.organization;
35
+ return (typeof v.id === "string" &&
36
+ typeof v.type === "string" &&
37
+ typeof v.version === "number" &&
38
+ typeof v.time === "string" &&
39
+ !!org &&
40
+ typeof org === "object" &&
41
+ typeof org.id === "string" &&
42
+ !!v.data &&
43
+ typeof v.data === "object" &&
44
+ !Array.isArray(v.data));
45
+ }
46
+ function handlerFor(on, type) {
47
+ if (typeof on === "function")
48
+ return on;
49
+ return Object.prototype.hasOwnProperty.call(on, type) ? on[type] : undefined;
50
+ }
51
+ /**
52
+ * Answer a request to the reserved events path, or return null for any
53
+ * other path so the app's own handler runs.
54
+ */
55
+ export async function handleEventsRequest(request, env, ctx, options) {
56
+ const { pathname } = new URL(request.url);
57
+ if (pathname !== APP_EVENTS_PATH)
58
+ return null;
59
+ if (request.method !== "POST") {
60
+ return answer(405, { error: "method_not_allowed" });
61
+ }
62
+ let verify;
63
+ try {
64
+ verify = resolveVerifyOptions(options.identity);
65
+ }
66
+ catch (error) {
67
+ if (error instanceof ConfigurationError) {
68
+ console.error(error);
69
+ return answer(500, { error: "misconfigured", message: error.message });
70
+ }
71
+ throw error;
72
+ }
73
+ let claims;
74
+ try {
75
+ claims = await verifySystemJwt(request.headers.get(SYSTEM_HEADER) ?? "", {
76
+ ...verify,
77
+ purpose: "events.deliver",
78
+ });
79
+ }
80
+ catch (error) {
81
+ if (error instanceof IdentityError) {
82
+ return answer(401, { error: "unauthenticated", message: error.message });
83
+ }
84
+ throw error;
85
+ }
86
+ const declared = Number(request.headers.get("content-length") ?? "0");
87
+ if (declared > MAX_BODY_BYTES) {
88
+ return answer(413, { error: "too_large" });
89
+ }
90
+ const body = await request.text();
91
+ if (body.length > MAX_BODY_BYTES) {
92
+ return answer(413, { error: "too_large" });
93
+ }
94
+ // The token was minted for this exact body: another body, even one the
95
+ // Gateway sent, is refused.
96
+ if (claims.bh !== (await bodyHash(body))) {
97
+ return answer(401, {
98
+ error: "unauthenticated",
99
+ message: "the token was minted for another delivery",
100
+ });
101
+ }
102
+ let parsed;
103
+ try {
104
+ parsed = JSON.parse(body);
105
+ }
106
+ catch {
107
+ parsed = undefined;
108
+ }
109
+ if (!isEnvelope(parsed)) {
110
+ return answer(400, { error: "invalid_request" });
111
+ }
112
+ const pinned = options.identity.organizationId;
113
+ if (pinned && parsed.organization.id !== pinned) {
114
+ return answer(403, {
115
+ error: "forbidden",
116
+ message: `the event is for organization "${parsed.organization.id}", and this Worker serves "${pinned}"`,
117
+ });
118
+ }
119
+ if (!options.events) {
120
+ // The manifest subscribes, but the Worker passes no `events` to
121
+ // everyApp(): say so, and let the Gateway retry once it does.
122
+ return answer(404, {
123
+ error: "no_handler",
124
+ message: "everyApp() was given no events: { on }",
125
+ });
126
+ }
127
+ const handler = handlerFor(options.events.on, parsed.type);
128
+ if (!handler)
129
+ return answer(200, { ok: true, handled: false });
130
+ const event = {
131
+ ...parsed,
132
+ delivery: {
133
+ id: request.headers.get(EVENT_HEADERS.delivery) ?? "",
134
+ attempt: Number(request.headers.get(EVENT_HEADERS.attempt) ?? "0") || 0,
135
+ },
136
+ };
137
+ try {
138
+ await withOrganization(parsed.organization.id, () => handler(event, env, ctx));
139
+ }
140
+ catch (error) {
141
+ console.error(error);
142
+ return answer(500, { error: "handler_failed" });
143
+ }
144
+ return answer(200, { ok: true, handled: true });
145
+ }
@@ -13,21 +13,22 @@
13
13
  * `everyapp.config.ts` (the Cloudflare Vite plugin hides every file
14
14
  * `cloudflare.config.ts` imports from the Worker in dev).
15
15
  * It also answers the gateway's internal tools contract
16
- * (`/__everyapp/tools/*`) for the app's `defineTools()` catalog.
16
+ * (`/__everyapp/tools/*`) for the app's `defineTools()` catalog, and the
17
+ * event sink's deliveries (`/__everyapp/events`) for `events: { on }`.
17
18
  *
18
19
  * Around the handler and around each tool call it opens the request
19
- * context (context.ts): `currentUser()` reads the user anywhere below, and
20
- * every call back to the Gateway is attributed to the request without a
21
- * `for`. A deployed Worker is pinned to its organization
20
+ * context (context.ts): every call back to the Gateway is attributed to
21
+ * the request without a `for`. A deployed Worker is pinned to its organization
22
22
  * (`EVERY_APP_ORG_ID`), and an identity for any other is refused on both
23
23
  * paths. The response leaves without `Set-Cookie`: the perimeter strips
24
24
  * every `Cookie` before a request reaches the app, so a cookie the app's
25
25
  * framework sets could never come back, and one named like the Gateway's
26
26
  * session cookie would lock the person out of the app host.
27
27
  */
28
- import { ConfigurationError, IdentityError, isAdmin, type EveryAppUser, type IdentityResult, type OrgRole } from "../internal/index.js";
28
+ import { ConfigurationError, isAdmin, type EveryAppUser, type IdentityResult, type OrgRole } from "../internal/index.js";
29
29
  import type { ToolSet } from "./tools.js";
30
- export { ConfigurationError, IdentityError, isAdmin };
30
+ import { type AppEventsOptions } from "./eventsEndpoint.js";
31
+ export { ConfigurationError, isAdmin };
31
32
  export type { EveryAppUser, IdentityResult, OrgRole };
32
33
  /**
33
34
  * The runtime's `ExecutionContext`, which `everyApp()` passes to the handler
@@ -36,11 +37,7 @@ export type { EveryAppUser, IdentityResult, OrgRole };
36
37
  * `fetch` themselves pass a stub cast to it.
37
38
  */
38
39
  export type ExecutionContextLike = ExecutionContext;
39
- export interface EveryAppManifestLike {
40
- id: string;
41
- [key: string]: unknown;
42
- }
43
- export interface EveryAppOptions {
40
+ export interface EveryAppOptions<TEnv = Record<string, unknown>> {
44
41
  /** Optional expected issuer (gateway URL). Defaults to env.EVERYAPP_IDENTITY_ISSUER when set. */
45
42
  issuer?: string;
46
43
  /** Env var holding the public key set. Defaults to EVERYAPP_IDENTITY_PUBLIC_KEYS. */
@@ -57,6 +54,13 @@ export interface EveryAppOptions {
57
54
  * and serves them to MCP clients; see `defineTools`.
58
55
  */
59
56
  tools?: ToolSet;
57
+ /**
58
+ * The Gateway's events this app subscribes to (`events.subscribe` in its
59
+ * manifest), delivered to `on` over the service binding: one function, or
60
+ * one per type. The SDK verifies the Gateway's `events.deliver` token
61
+ * before `on` runs.
62
+ */
63
+ events?: AppEventsOptions<TEnv>;
60
64
  /** Override for tests. */
61
65
  now?: number;
62
66
  }
@@ -74,7 +78,6 @@ interface EveryAppExport<TEnv, TCtx extends ExecutionContextLike> {
74
78
  * (a framework that hands out headers alone needs no made-up Request).
75
79
  * Answers from the request context or the per-request cache first, then
76
80
  * verifies the identity headers from the audience and public keys in env.
77
- * Inside a handler `everyApp()` serves, `currentUser()` is the shorter way.
78
81
  */
79
82
  export declare function getEveryAppUser(request: Request | Headers, env: object): Promise<EveryAppUser | null>;
80
83
  /**
@@ -82,20 +85,7 @@ export declare function getEveryAppUser(request: Request | Headers, env: object)
82
85
  * everyApp() when the request is public or unauthenticated.
83
86
  */
84
87
  export declare function requireEveryAppUser(request: Request | Headers, env: object): Promise<EveryAppUser>;
85
- /**
86
- * The user of the request being served (`currentUser()`), or throw the
87
- * same 401 JSON Response as `requireEveryAppUser()` on a public route.
88
- * Outside a request (`withOrganization()` work, a handler not under
89
- * `everyApp()`) it throws `RequestContextError`, as `currentUser()` does.
90
- */
91
- export declare function requireCurrentUser(): EveryAppUser;
92
88
  export declare function hasScope(user: EveryAppUser, scope: string): boolean;
93
89
  /** Wrap a Worker fetch handler with Every App identity verification. */
94
- export declare function everyApp<TEnv = Record<string, unknown>, TCtx extends ExecutionContextLike = ExecutionContext>(handler: EveryAppHandler<TEnv, TCtx>, options?: EveryAppOptions): EveryAppExport<TEnv, TCtx>;
95
- /**
96
- * @deprecated Drop the manifest: the app id now comes from
97
- * `env.EVERYAPP_APP_ID` and the audience from `env.EVERYAPP_AUDIENCE`.
98
- * When passed, it must match the app id.
99
- */
100
- export declare function everyApp<TEnv = Record<string, unknown>, TCtx extends ExecutionContextLike = ExecutionContext>(handler: EveryAppHandler<TEnv, TCtx>, manifest: EveryAppManifestLike | undefined, options?: EveryAppOptions): EveryAppExport<TEnv, TCtx>;
90
+ export declare function everyApp<TEnv = Record<string, unknown>, TCtx extends ExecutionContextLike = ExecutionContext>(handler: EveryAppHandler<TEnv, TCtx>, options?: EveryAppOptions<TEnv>): EveryAppExport<TEnv, TCtx>;
101
91
  //# sourceMappingURL=everyApp.d.ts.map