@every-app/sdk 0.1.14 → 0.3.0-rc.1

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 (167) hide show
  1. package/README.md +608 -2
  2. package/dist/cloudflare/index.d.ts +21 -1
  3. package/dist/cloudflare/index.d.ts.map +1 -1
  4. package/dist/cloudflare/index.js +21 -1
  5. package/dist/cloudflare/sentinel.d.ts +69 -0
  6. package/dist/cloudflare/sentinel.d.ts.map +1 -0
  7. package/dist/cloudflare/sentinel.js +50 -0
  8. package/dist/cloudflare/worker.d.ts +511 -0
  9. package/dist/cloudflare/worker.d.ts.map +1 -0
  10. package/dist/cloudflare/worker.js +738 -0
  11. package/dist/dev/index.d.ts +8 -0
  12. package/dist/dev/index.d.ts.map +1 -0
  13. package/dist/dev/index.js +7 -0
  14. package/dist/dev/keypair.d.ts +24 -0
  15. package/dist/dev/keypair.d.ts.map +1 -0
  16. package/dist/dev/keypair.js +60 -0
  17. package/dist/internal/aiProviders.d.ts +67 -0
  18. package/dist/internal/aiProviders.d.ts.map +1 -0
  19. package/dist/internal/aiProviders.js +220 -0
  20. package/dist/internal/appSettings.d.ts +237 -0
  21. package/dist/internal/appSettings.d.ts.map +1 -0
  22. package/dist/internal/appSettings.js +374 -0
  23. package/dist/internal/capabilities.d.ts +1344 -0
  24. package/dist/internal/capabilities.d.ts.map +1 -0
  25. package/dist/internal/capabilities.js +1521 -0
  26. package/dist/internal/devProviders.d.ts +49 -0
  27. package/dist/internal/devProviders.d.ts.map +1 -0
  28. package/dist/internal/devProviders.js +62 -0
  29. package/dist/internal/email.d.ts +280 -0
  30. package/dist/internal/email.d.ts.map +1 -0
  31. package/dist/internal/email.js +613 -0
  32. package/dist/internal/events.d.ts +182 -0
  33. package/dist/internal/events.d.ts.map +1 -0
  34. package/dist/internal/events.js +324 -0
  35. package/dist/internal/gatewaySettings.d.ts +82 -0
  36. package/dist/internal/gatewaySettings.d.ts.map +1 -0
  37. package/dist/internal/gatewaySettings.js +209 -0
  38. package/dist/internal/identity.d.ts +88 -0
  39. package/dist/internal/identity.d.ts.map +1 -0
  40. package/dist/internal/identity.js +231 -0
  41. package/dist/internal/index.d.ts +19 -0
  42. package/dist/internal/index.d.ts.map +1 -0
  43. package/dist/internal/index.js +18 -0
  44. package/dist/internal/members.d.ts +35 -0
  45. package/dist/internal/members.d.ts.map +1 -0
  46. package/dist/internal/members.js +7 -0
  47. package/dist/internal/oauthClient.d.ts +63 -0
  48. package/dist/internal/oauthClient.d.ts.map +1 -0
  49. package/dist/internal/oauthClient.js +194 -0
  50. package/dist/internal/oauthScopes.d.ts +117 -0
  51. package/dist/internal/oauthScopes.d.ts.map +1 -0
  52. package/dist/internal/oauthScopes.js +416 -0
  53. package/dist/internal/organizationName.d.ts +18 -0
  54. package/dist/internal/organizationName.d.ts.map +1 -0
  55. package/dist/internal/organizationName.js +24 -0
  56. package/dist/internal/protocol.d.ts +195 -0
  57. package/dist/internal/protocol.d.ts.map +1 -0
  58. package/dist/internal/protocol.js +250 -0
  59. package/dist/internal/providerApps.d.ts +75 -0
  60. package/dist/internal/providerApps.d.ts.map +1 -0
  61. package/dist/internal/providerApps.js +59 -0
  62. package/dist/internal/providerRegistry.d.ts +147 -0
  63. package/dist/internal/providerRegistry.d.ts.map +1 -0
  64. package/dist/internal/providerRegistry.js +262 -0
  65. package/dist/internal/providerSpec.d.ts +55 -0
  66. package/dist/internal/providerSpec.d.ts.map +1 -0
  67. package/dist/internal/providerSpec.js +746 -0
  68. package/dist/internal/remoteMcp.d.ts +188 -0
  69. package/dist/internal/remoteMcp.d.ts.map +1 -0
  70. package/dist/internal/remoteMcp.js +543 -0
  71. package/dist/internal/tools.d.ts +115 -0
  72. package/dist/internal/tools.d.ts.map +1 -0
  73. package/dist/internal/tools.js +80 -0
  74. package/dist/server/attribution.d.ts +39 -0
  75. package/dist/server/attribution.d.ts.map +1 -0
  76. package/dist/server/attribution.js +67 -0
  77. package/dist/server/authorizeUrl.d.ts +43 -0
  78. package/dist/server/authorizeUrl.d.ts.map +1 -0
  79. package/dist/server/authorizeUrl.js +110 -0
  80. package/dist/server/emit.d.ts +63 -0
  81. package/dist/server/emit.d.ts.map +1 -0
  82. package/dist/server/emit.js +101 -0
  83. package/dist/server/everyApp.d.ts +81 -0
  84. package/dist/server/everyApp.d.ts.map +1 -0
  85. package/dist/server/everyApp.js +185 -0
  86. package/dist/server/gatewayFetch.d.ts +37 -0
  87. package/dist/server/gatewayFetch.d.ts.map +1 -0
  88. package/dist/server/gatewayFetch.js +115 -0
  89. package/dist/server/index.d.ts +18 -0
  90. package/dist/server/index.d.ts.map +1 -0
  91. package/dist/server/index.js +15 -0
  92. package/dist/server/integrationFetch.d.ts +131 -0
  93. package/dist/server/integrationFetch.d.ts.map +1 -0
  94. package/dist/server/integrationFetch.js +526 -0
  95. package/dist/server/listMembers.d.ts +32 -0
  96. package/dist/server/listMembers.d.ts.map +1 -0
  97. package/dist/server/listMembers.js +74 -0
  98. package/dist/server/nodeServer.d.ts +48 -0
  99. package/dist/server/nodeServer.d.ts.map +1 -0
  100. package/dist/server/nodeServer.js +45 -0
  101. package/dist/server/sendEmail.d.ts +39 -0
  102. package/dist/server/sendEmail.d.ts.map +1 -0
  103. package/dist/server/sendEmail.js +114 -0
  104. package/dist/server/tools.d.ts +184 -0
  105. package/dist/server/tools.d.ts.map +1 -0
  106. package/dist/server/tools.js +347 -0
  107. package/dist/server/toolsEndpoint.d.ts +32 -0
  108. package/dist/server/toolsEndpoint.d.ts.map +1 -0
  109. package/dist/server/toolsEndpoint.js +231 -0
  110. package/package.json +21 -31
  111. package/dist/cloudflare/getLocalD1Url.d.ts +0 -2
  112. package/dist/cloudflare/getLocalD1Url.d.ts.map +0 -1
  113. package/dist/cloudflare/getLocalD1Url.js +0 -40
  114. package/dist/cloudflare/lazyInit.d.ts +0 -22
  115. package/dist/cloudflare/lazyInit.d.ts.map +0 -1
  116. package/dist/cloudflare/lazyInit.js +0 -65
  117. package/dist/cloudflare/server/gateway.d.ts +0 -29
  118. package/dist/cloudflare/server/gateway.d.ts.map +0 -1
  119. package/dist/cloudflare/server/gateway.js +0 -69
  120. package/dist/cloudflare/server/index.d.ts +0 -3
  121. package/dist/cloudflare/server/index.d.ts.map +0 -1
  122. package/dist/cloudflare/server/index.js +0 -2
  123. package/dist/core/authenticatedFetch.d.ts +0 -9
  124. package/dist/core/authenticatedFetch.d.ts.map +0 -1
  125. package/dist/core/authenticatedFetch.js +0 -31
  126. package/dist/core/index.d.ts +0 -4
  127. package/dist/core/index.d.ts.map +0 -1
  128. package/dist/core/index.js +0 -2
  129. package/dist/core/sessionManager.d.ts +0 -58
  130. package/dist/core/sessionManager.d.ts.map +0 -1
  131. package/dist/core/sessionManager.js +0 -357
  132. package/dist/shared/bypassGatewayLocalOnly.d.ts +0 -15
  133. package/dist/shared/bypassGatewayLocalOnly.d.ts.map +0 -1
  134. package/dist/shared/bypassGatewayLocalOnly.js +0 -45
  135. package/dist/shared/parseMessagePayload.d.ts +0 -3
  136. package/dist/shared/parseMessagePayload.d.ts.map +0 -1
  137. package/dist/shared/parseMessagePayload.js +0 -18
  138. package/dist/tanstack/EmbeddedAppProvider.d.ts +0 -30
  139. package/dist/tanstack/EmbeddedAppProvider.d.ts.map +0 -1
  140. package/dist/tanstack/EmbeddedAppProvider.js +0 -57
  141. package/dist/tanstack/GatewayRequiredError.d.ts +0 -20
  142. package/dist/tanstack/GatewayRequiredError.d.ts.map +0 -1
  143. package/dist/tanstack/GatewayRequiredError.js +0 -91
  144. package/dist/tanstack/_internal/useEveryAppSession.d.ts +0 -18
  145. package/dist/tanstack/_internal/useEveryAppSession.d.ts.map +0 -1
  146. package/dist/tanstack/_internal/useEveryAppSession.js +0 -47
  147. package/dist/tanstack/index.d.ts +0 -4
  148. package/dist/tanstack/index.d.ts.map +0 -1
  149. package/dist/tanstack/index.js +0 -3
  150. package/dist/tanstack/server/authConfig.d.ts +0 -3
  151. package/dist/tanstack/server/authConfig.d.ts.map +0 -1
  152. package/dist/tanstack/server/authConfig.js +0 -13
  153. package/dist/tanstack/server/authenticateRequest.d.ts +0 -31
  154. package/dist/tanstack/server/authenticateRequest.d.ts.map +0 -1
  155. package/dist/tanstack/server/authenticateRequest.js +0 -154
  156. package/dist/tanstack/server/index.d.ts +0 -4
  157. package/dist/tanstack/server/index.d.ts.map +0 -1
  158. package/dist/tanstack/server/index.js +0 -2
  159. package/dist/tanstack/server/types.d.ts +0 -5
  160. package/dist/tanstack/server/types.d.ts.map +0 -1
  161. package/dist/tanstack/server/types.js +0 -1
  162. package/dist/tanstack/useEveryAppRouter.d.ts +0 -7
  163. package/dist/tanstack/useEveryAppRouter.d.ts.map +0 -1
  164. package/dist/tanstack/useEveryAppRouter.js +0 -66
  165. package/dist/tanstack/useSessionTokenClientMiddleware.d.ts +0 -2
  166. package/dist/tanstack/useSessionTokenClientMiddleware.d.ts.map +0 -1
  167. package/dist/tanstack/useSessionTokenClientMiddleware.js +0 -29
@@ -0,0 +1,80 @@
1
+ /**
2
+ * The internal tools contract (v1) between the gateway's ToolBus and an app's
3
+ * `everyApp()` wrapper. Two reserved endpoints, reached only over the
4
+ * gateway's service binding and never public:
5
+ *
6
+ * POST /__everyapp/tools/list x-everyapp-system JWT (purpose "tools.list")
7
+ * POST /__everyapp/tools/call x-everyapp-identity JWT, chan "mcp" + tc claim
8
+ *
9
+ * MCP itself (protocol eras, headers, envelopes) stays in the gateway; apps
10
+ * only ever speak this small versioned JSON shape. Both sides import the
11
+ * shapes, paths and the catalog hash from here so they cannot drift.
12
+ */
13
+ export const TOOLS_PROTOCOL_VERSION = 1;
14
+ export const TOOLS_PATH_PREFIX = "/__everyapp/tools";
15
+ export const TOOLS_LIST_PATH = `${TOOLS_PATH_PREFIX}/list`;
16
+ export const TOOLS_CALL_PATH = `${TOOLS_PATH_PREFIX}/call`;
17
+ export const TOOL_RISKS = ["read", "write", "destructive"];
18
+ /** Scopes the gateway mints on `chan: "mcp"` identities, one per risk. */
19
+ export const MCP_READ_SCOPE = "mcp:read";
20
+ export const MCP_WRITE_SCOPE = "mcp:write";
21
+ /** Never minted in v1: destructive tools are hidden until approvals exist. */
22
+ export const MCP_DESTRUCTIVE_SCOPE = "mcp:destructive";
23
+ /** Local tool names: lowercase snake_case, at most 48 characters. */
24
+ export const TOOL_NAME_PATTERN = /^[a-z][a-z0-9_]{0,47}$/;
25
+ export function isToolRisk(value) {
26
+ return (typeof value === "string" &&
27
+ TOOL_RISKS.includes(value));
28
+ }
29
+ /** The exact scope a `chan: "mcp"` identity must carry to call a tool. */
30
+ export function requiredScopeForRisk(risk) {
31
+ switch (risk) {
32
+ case "read":
33
+ return MCP_READ_SCOPE;
34
+ case "write":
35
+ return MCP_WRITE_SCOPE;
36
+ case "destructive":
37
+ return MCP_DESTRUCTIVE_SCOPE;
38
+ }
39
+ }
40
+ /**
41
+ * JSON with object keys sorted at every depth, so equal values always
42
+ * serialize identically. `undefined` members are dropped, as JSON.stringify
43
+ * does.
44
+ */
45
+ export function canonicalJson(value) {
46
+ return JSON.stringify(sortKeys(value));
47
+ }
48
+ function sortKeys(value) {
49
+ if (Array.isArray(value))
50
+ return value.map(sortKeys);
51
+ if (!value || typeof value !== "object")
52
+ return value;
53
+ const sorted = {};
54
+ for (const key of Object.keys(value).sort()) {
55
+ const member = value[key];
56
+ if (member !== undefined)
57
+ sorted[key] = sortKeys(member);
58
+ }
59
+ return sorted;
60
+ }
61
+ /**
62
+ * `sha256:<hex>` over the canonical JSON of a catalog. The SDK reports it with
63
+ * every list and call; the gateway stores it at sync and re-syncs when a call
64
+ * reports a different one.
65
+ */
66
+ export async function catalogHash(catalog) {
67
+ const canonical = canonicalJson({
68
+ instructions: catalog.instructions ?? null,
69
+ tools: catalog.tools,
70
+ });
71
+ const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(canonical));
72
+ return `sha256:${toHex(digest)}`;
73
+ }
74
+ function toHex(buffer) {
75
+ let out = "";
76
+ for (const byte of new Uint8Array(buffer)) {
77
+ out += byte.toString(16).padStart(2, "0");
78
+ }
79
+ return out;
80
+ }
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Whom a call through the gateway is for, which decides the organization
3
+ * it counts against:
4
+ *
5
+ * - the inbound `Request`: its person, by the identity the gateway put on
6
+ * it (forwarded as is);
7
+ * - `{ background: true, organizationId }`: work no request started (a
8
+ * cron, a Workflow step, a queue). A shared app's Worker serves every
9
+ * organization, so it names the one the work is for, which the gateway
10
+ * checks is active and has a live member. An organization's own app may
11
+ * leave it out (the gateway knows its organization) and may only name
12
+ * its own.
13
+ */
14
+ export type GatewayCallFor = Request | {
15
+ background: true;
16
+ organizationId?: string;
17
+ };
18
+ /**
19
+ * A gateway call that doesn't say whom it is for where it must: from a
20
+ * shared app's Worker, with neither the request nor an organization.
21
+ */
22
+ export declare class AttributionError extends Error {
23
+ readonly code = "attribution_required";
24
+ constructor(message: string);
25
+ }
26
+ /**
27
+ * Check `target` before any call is made: a shared app's Worker must pass
28
+ * the request or name the organization, and anything else passed must be
29
+ * one of the two shapes.
30
+ */
31
+ export declare function checkAttribution(env: object, target: GatewayCallFor | undefined, what: string): void;
32
+ /**
33
+ * Put the call's attribution on its headers: the request's identity, or the
34
+ * background work's organization, and never one the app set itself.
35
+ */
36
+ export declare function applyAttribution(headers: Headers, target: GatewayCallFor | undefined): void;
37
+ /** Whether `for` is the inbound request (rather than background work). */
38
+ export declare function isRequest(value: unknown): value is Request;
39
+ //# sourceMappingURL=attribution.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attribution.d.ts","sourceRoot":"","sources":["../../src/server/attribution.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;GAYG;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;;;;GAIG;AACH,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,cAAc,GAAG,SAAS,EAClC,IAAI,EAAE,MAAM,GACX,IAAI,CAsBN;AAED;;;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"}
@@ -0,0 +1,67 @@
1
+ import { IDENTITY_HEADER, ORG_HEADER, SHARED_ENV } from "../internal/index.js";
2
+ /**
3
+ * A gateway call that doesn't say whom it is for where it must: from a
4
+ * shared app's Worker, with neither the request nor an organization.
5
+ */
6
+ export class AttributionError extends Error {
7
+ code = "attribution_required";
8
+ constructor(message) {
9
+ super(message);
10
+ this.name = "AttributionError";
11
+ }
12
+ }
13
+ /**
14
+ * Check `target` before any call is made: a shared app's Worker must pass
15
+ * the request or name the organization, and anything else passed must be
16
+ * one of the two shapes.
17
+ */
18
+ export function checkAttribution(env, target, what) {
19
+ if (target !== undefined && !isRequest(target)) {
20
+ if (target === null || typeof target !== "object" || !target.background) {
21
+ throw new AttributionError(`${what}: \`for\` is the inbound Request or { background: true, organizationId }.`);
22
+ }
23
+ if (target.organizationId !== undefined &&
24
+ !nonEmpty(target.organizationId)) {
25
+ throw new AttributionError(`${what}: { background: true, organizationId } needs a non-empty organization id.`);
26
+ }
27
+ }
28
+ if (!isSharedWorker(env))
29
+ return;
30
+ if (target === undefined || (!isRequest(target) && !target.organizationId)) {
31
+ throw new AttributionError(`${what} in a shared app's Worker serves every organization, so it must say whom the call is for: for: request, or for: { background: true, organizationId } for work no request started.`);
32
+ }
33
+ }
34
+ /**
35
+ * Put the call's attribution on its headers: the request's identity, or the
36
+ * background work's organization, and never one the app set itself.
37
+ */
38
+ export function applyAttribution(headers, target) {
39
+ headers.delete(IDENTITY_HEADER);
40
+ headers.delete(ORG_HEADER);
41
+ if (target === undefined)
42
+ return;
43
+ if (isRequest(target)) {
44
+ const identity = target.headers.get(IDENTITY_HEADER);
45
+ if (identity)
46
+ headers.set(IDENTITY_HEADER, identity);
47
+ return;
48
+ }
49
+ if (target.organizationId)
50
+ headers.set(ORG_HEADER, target.organizationId);
51
+ }
52
+ function isSharedWorker(env) {
53
+ const value = env[SHARED_ENV];
54
+ return value === "1" || value === "true" || value === true;
55
+ }
56
+ // Duck-typed: a Request from another realm (a test runner, a polyfill) is
57
+ // still the request.
58
+ /** Whether `for` is the inbound request (rather than background work). */
59
+ export function isRequest(value) {
60
+ return (typeof value === "object" &&
61
+ value !== null &&
62
+ "headers" in value &&
63
+ typeof value.headers?.get === "function");
64
+ }
65
+ function nonEmpty(value) {
66
+ return typeof value === "string" && value.trim().length > 0;
67
+ }
@@ -0,0 +1,43 @@
1
+ import { type OAuthClientCapabilityId } from "../internal/index.js";
2
+ import type { OAuthClientIntegrationStatus } from "./integrationFetch.js";
3
+ export interface AuthorizeUrlOptions {
4
+ /** The OAuth client, as listed in the manifest's `capabilities`. */
5
+ provider: OAuthClientCapabilityId;
6
+ /** What `integrationStatus()` answered for it, ready and with its `clientId`. */
7
+ status: OAuthClientIntegrationStatus;
8
+ /** One of the redirect URIs the operator registered on the client. */
9
+ redirectUri: string;
10
+ /** The app's own anti-forgery value, checked when the person comes back. */
11
+ state: string;
12
+ /** PKCE: the S256 challenge of the verifier the app keeps for the exchange. */
13
+ codeChallenge?: string;
14
+ /**
15
+ * The provider's extras (`prompt`, `login_hint`, Google's `hd`). Never
16
+ * one of the parameters this helper sets (PKCE's and the provider's own
17
+ * included), nor Google's `include_granted_scopes`.
18
+ */
19
+ params?: Record<string, string>;
20
+ }
21
+ /**
22
+ * The provider's authorize URL to send a person to, for a client with
23
+ * scope sets (`google-workspace`, `microsoft-365`, `slack`), built from the
24
+ * capability's status: the registry's authorize endpoint (or the instance
25
+ * the status names), `client_id`, `response_type=code`, `redirect_uri`,
26
+ * `state`, and the scopes the status allows this app, which are exactly
27
+ * what the gateway will let the token carry. Each provider's own needs
28
+ * are added: Google's `access_type=offline` (a refresh token), Microsoft's
29
+ * `response_mode=query`, Slack's comma-joined `scope` and its `user_scope`
30
+ * (the status's `userScopes`, when the app's sets include a user set). With
31
+ * `codeChallenge`, PKCE as S256. The app keeps its `state` and verifier
32
+ * for the callback.
33
+ *
34
+ * A client without scope sets (LinkedIn, Mastodon, ...) is refused: the
35
+ * registry's checklist for one isn't what the gateway holds a token to,
36
+ * so an app builds that URL itself from the status's `clientId`.
37
+ *
38
+ * Throws a `TypeError` naming the fix when the client has no scope sets,
39
+ * when the status isn't ready or has no `clientId`, when the status lacks
40
+ * `scopes`, or when `params` names a parameter the helper sets.
41
+ */
42
+ export declare function authorizeUrl({ provider, status, redirectUri, state, codeChallenge, params, }: AuthorizeUrlOptions): URL;
43
+ //# sourceMappingURL=authorizeUrl.d.ts.map
@@ -0,0 +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,CA2D3B"}
@@ -0,0 +1,110 @@
1
+ import { CAPABILITIES, ConfigurationError, } from "../internal/index.js";
2
+ /**
3
+ * Parameters the helper owns: the status decides the scopes, the caller's
4
+ * other arguments decide the rest (PKCE from `codeChallenge`, always as
5
+ * S256), each provider's own are fixed (Google's `access_type=offline` is
6
+ * what gets a refresh token, Microsoft's `response_mode=query` is what
7
+ * the callback reads), and Google's `include_granted_scopes` would widen
8
+ * the token to every scope the person ever granted this client, which the
9
+ * gateway refuses (`integration_scope_exceeded`).
10
+ */
11
+ const RESERVED_PARAMS = [
12
+ "scope",
13
+ "user_scope",
14
+ "client_id",
15
+ "redirect_uri",
16
+ "state",
17
+ "response_type",
18
+ "code_challenge",
19
+ "code_challenge_method",
20
+ "access_type",
21
+ "response_mode",
22
+ "include_granted_scopes",
23
+ ];
24
+ /** The OAuth clients with scope sets: the ones this helper builds for. */
25
+ function scopedClientIds() {
26
+ return Object.entries(CAPABILITIES)
27
+ .filter(([, found]) => found.mode === "oauth_client" && found.scopePolicy)
28
+ .map(([id]) => id);
29
+ }
30
+ /**
31
+ * The provider's authorize URL to send a person to, for a client with
32
+ * scope sets (`google-workspace`, `microsoft-365`, `slack`), built from the
33
+ * capability's status: the registry's authorize endpoint (or the instance
34
+ * the status names), `client_id`, `response_type=code`, `redirect_uri`,
35
+ * `state`, and the scopes the status allows this app, which are exactly
36
+ * what the gateway will let the token carry. Each provider's own needs
37
+ * are added: Google's `access_type=offline` (a refresh token), Microsoft's
38
+ * `response_mode=query`, Slack's comma-joined `scope` and its `user_scope`
39
+ * (the status's `userScopes`, when the app's sets include a user set). With
40
+ * `codeChallenge`, PKCE as S256. The app keeps its `state` and verifier
41
+ * for the callback.
42
+ *
43
+ * A client without scope sets (LinkedIn, Mastodon, ...) is refused: the
44
+ * registry's checklist for one isn't what the gateway holds a token to,
45
+ * so an app builds that URL itself from the status's `clientId`.
46
+ *
47
+ * Throws a `TypeError` naming the fix when the client has no scope sets,
48
+ * when the status isn't ready or has no `clientId`, when the status lacks
49
+ * `scopes`, or when `params` names a parameter the helper sets.
50
+ */
51
+ export function authorizeUrl({ provider, status, redirectUri, state, codeChallenge, params, }) {
52
+ const capability = oauthClientCapability(provider);
53
+ if (!capability.scopePolicy) {
54
+ throw new TypeError(`authorizeUrl() builds URLs for clients with scope sets (${scopedClientIds().join(", ")}); build ${capability.label}'s authorize URL yourself from the status's clientId.`);
55
+ }
56
+ if (!status.ready || !status.clientId) {
57
+ throw new TypeError(`authorizeUrl(${JSON.stringify(provider)}) needs a ready status with a clientId, and integrationStatus() answered ready: ${String(status.ready)}. Show ${capability.label} as not available instead: its operator sets the client up on the gateway, and an app lists the scope sets it uses in everyapp.config.ts.`);
58
+ }
59
+ if (!status.scopes) {
60
+ throw new TypeError(`authorizeUrl(${JSON.stringify(provider)}) takes ${capability.label}'s scopes from integrationStatus().scopes, which this status lacks: pass the status as the gateway (or everyapp dev) answered it.`);
61
+ }
62
+ for (const name of Object.keys(params ?? {})) {
63
+ if (!RESERVED_PARAMS.includes(name))
64
+ continue;
65
+ throw new TypeError(name === "include_granted_scopes"
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
+ : `authorizeUrl(${JSON.stringify(provider)}) sets ${name} itself; leave it out of params.`);
68
+ }
69
+ const base = status.origin ?? capability.defaultOrigin;
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);
74
+ // The status speaks for the gateway: its scopes are the token's ceiling.
75
+ const scopes = status.scopes;
76
+ const slack = url.host === "slack.com";
77
+ url.searchParams.set("client_id", status.clientId);
78
+ url.searchParams.set("response_type", "code");
79
+ url.searchParams.set("redirect_uri", redirectUri);
80
+ url.searchParams.set("state", state);
81
+ url.searchParams.set("scope", scopes.join(slack ? "," : " "));
82
+ if (status.userScopes && status.userScopes.length > 0) {
83
+ url.searchParams.set("user_scope", status.userScopes.join(","));
84
+ }
85
+ if (codeChallenge) {
86
+ url.searchParams.set("code_challenge", codeChallenge);
87
+ url.searchParams.set("code_challenge_method", "S256");
88
+ }
89
+ if (url.host === "accounts.google.com") {
90
+ // Without it Google issues no refresh token.
91
+ url.searchParams.set("access_type", "offline");
92
+ }
93
+ else if (url.host === "login.microsoftonline.com") {
94
+ url.searchParams.set("response_mode", "query");
95
+ }
96
+ for (const [name, value] of Object.entries(params ?? {})) {
97
+ url.searchParams.set(name, value);
98
+ }
99
+ return url;
100
+ }
101
+ /** The OAuth client `provider` names, from plain JavaScript or a stale type too. */
102
+ function oauthClientCapability(provider) {
103
+ const found = Object.hasOwn(CAPABILITIES, provider)
104
+ ? CAPABILITIES[provider]
105
+ : undefined;
106
+ if (found?.mode !== "oauth_client") {
107
+ throw new ConfigurationError(`${JSON.stringify(String(provider))} isn't an OAuth client the gateway holds.`);
108
+ }
109
+ return found;
110
+ }
@@ -0,0 +1,63 @@
1
+ /**
2
+ * `emit()`: an app's own event onto the gateway's event bus
3
+ * (docs/design/2026-10-events-out.md, section 7). It is shaped like
4
+ * `sendEmail()` on purpose: the same `{ env, for }` attribution, the same
5
+ * route over the EVERY_APP_GATEWAY binding, the same refusals thrown as
6
+ * `GatewayRefusalError`, and the same local-dev twin that checks what the
7
+ * gateway would check and prints instead of sending. The name and data
8
+ * rules live in `internal/events.ts`, which the gateway reads too, so the
9
+ * two can't drift.
10
+ */
11
+ import { type EmitEventResult } from "../internal/index.js";
12
+ import { type GatewayCallFor } from "./attribution.js";
13
+ export interface EmitOptions {
14
+ /** The Worker env: the EVERY_APP_GATEWAY binding when deployed; EVERYAPP_DEV in local dev. */
15
+ env: object;
16
+ /**
17
+ * Whom the event is about: the inbound request (its user becomes the
18
+ * event's actor), or `{ background: true, organizationId }` for work no
19
+ * person started (a cron, a queue, a provider's webhook). An
20
+ * organization's own app may omit it for background work; a shared app's
21
+ * Worker must pass one or the other ({@link AttributionError}).
22
+ */
23
+ for?: GatewayCallFor;
24
+ /**
25
+ * The app's own name for what happened: lowercase letters, digits and
26
+ * underscores in dotted segments (`invoice.paid`, `todo.completed`), at
27
+ * most 64 characters. The gateway delivers it as `app.<app id>.<event>`.
28
+ */
29
+ event: string;
30
+ /**
31
+ * What a subscriber needs to act on it: a JSON object, at most 8 KiB
32
+ * serialized, nested at most 8 levels. Ids, names, amounts and states;
33
+ * never a credential. A key named like one at any depth (`token`,
34
+ * `secret`, `password`, `apiKey`, `session`, `cookie`, ...) is refused.
35
+ */
36
+ data: Record<string, unknown>;
37
+ }
38
+ /**
39
+ * Put an event on the gateway's event bus, for the organization's webhook
40
+ * and email subscribers. The gateway names it `app.<app id>.<event>`, so an
41
+ * app can never emit one of the gateway's own types (`member.joined`);
42
+ * owners and admins choose `app.<app id>.*` (or one name) in Manage
43
+ * workspace, Events, or with `everyapp events add`. Nothing is declared in
44
+ * the manifest. Delivery happens after the call returns: a subscriber that
45
+ * is down doesn't slow or fail the app.
46
+ *
47
+ * `data` goes, as written, to every URL a subscriber chose (Slack, Zapier,
48
+ * an agent): put in it what a person or a workflow reads, never a
49
+ * credential, a session or a person's private content they didn't expect
50
+ * to leave the app. The gateway refuses a key named like a credential.
51
+ *
52
+ * Throws {@link GatewayRefusalError} when the gateway won't take it
53
+ * (`event_name_invalid`, `event_data_invalid`, `event_data_refused`,
54
+ * `event_data_too_large`, `event_rate_limited` past 600 an hour, ...), with
55
+ * the code and a message to show. An emit that must never fail the work
56
+ * around it goes under `ctx.waitUntil()` or a `.catch()`.
57
+ *
58
+ * In local dev (`everyapp dev`, no binding) the event is checked the way
59
+ * the gateway checks it, printed, and answered `{ id: "dev-…", type }`;
60
+ * nothing is delivered.
61
+ */
62
+ export declare function emit({ env, for: target, event, data, }: EmitOptions): Promise<EmitEventResult>;
63
+ //# sourceMappingURL=emit.d.ts.map
@@ -0,0 +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,MAAM,EACX,KAAK,EACL,IAAI,GACL,EAAE,WAAW,GAAG,OAAO,CAAC,eAAe,CAAC,CAoBxC"}
@@ -0,0 +1,101 @@
1
+ /**
2
+ * `emit()`: an app's own event onto the gateway's event bus
3
+ * (docs/design/2026-10-events-out.md, section 7). It is shaped like
4
+ * `sendEmail()` on purpose: the same `{ env, for }` attribution, the same
5
+ * route over the EVERY_APP_GATEWAY binding, the same refusals thrown as
6
+ * `GatewayRefusalError`, and the same local-dev twin that checks what the
7
+ * gateway would check and prints instead of sending. The name and data
8
+ * rules live in `internal/events.ts`, which the gateway reads too, so the
9
+ * two can't drift.
10
+ */
11
+ import { APP_EVENT_DATA_MAX_BYTES, APP_ID_ENV, ConfigurationError, EVENTS_EMIT_PATH, appEventNameProblem, appEventType, checkEventData, } from "../internal/index.js";
12
+ import { applyAttribution, checkAttribution, } from "./attribution.js";
13
+ import { BINDING_ORIGIN } from "./gatewayFetch.js";
14
+ import { GatewayRefusalError, gatewayRefusal } from "./integrationFetch.js";
15
+ /**
16
+ * Put an event on the gateway's event bus, for the organization's webhook
17
+ * and email subscribers. The gateway names it `app.<app id>.<event>`, so an
18
+ * app can never emit one of the gateway's own types (`member.joined`);
19
+ * owners and admins choose `app.<app id>.*` (or one name) in Manage
20
+ * workspace, Events, or with `everyapp events add`. Nothing is declared in
21
+ * the manifest. Delivery happens after the call returns: a subscriber that
22
+ * is down doesn't slow or fail the app.
23
+ *
24
+ * `data` goes, as written, to every URL a subscriber chose (Slack, Zapier,
25
+ * an agent): put in it what a person or a workflow reads, never a
26
+ * credential, a session or a person's private content they didn't expect
27
+ * to leave the app. The gateway refuses a key named like a credential.
28
+ *
29
+ * Throws {@link GatewayRefusalError} when the gateway won't take it
30
+ * (`event_name_invalid`, `event_data_invalid`, `event_data_refused`,
31
+ * `event_data_too_large`, `event_rate_limited` past 600 an hour, ...), with
32
+ * the code and a message to show. An emit that must never fail the work
33
+ * around it goes under `ctx.waitUntil()` or a `.catch()`.
34
+ *
35
+ * In local dev (`everyapp dev`, no binding) the event is checked the way
36
+ * the gateway checks it, printed, and answered `{ id: "dev-…", type }`;
37
+ * nothing is delivered.
38
+ */
39
+ export async function emit({ env, for: target, event, data, }) {
40
+ checkAttribution(env, target, "emit");
41
+ const values = env;
42
+ const binding = values["EVERY_APP_GATEWAY"];
43
+ if (binding) {
44
+ if (typeof binding.fetch !== "function") {
45
+ throw new ConfigurationError("EVERY_APP_GATEWAY is configured but is not a valid service binding.");
46
+ }
47
+ return emitThroughGateway(binding, target, { event, data });
48
+ }
49
+ if (!values["EVERYAPP_DEV"]) {
50
+ throw new ConfigurationError("Events are unavailable: deploy with the EVERY_APP_GATEWAY binding (everyapp deploy), or run under everyapp dev.");
51
+ }
52
+ return emitInDev(values, { event, data });
53
+ }
54
+ async function emitThroughGateway(binding, target, body) {
55
+ const headers = new Headers({ "content-type": "application/json" });
56
+ applyAttribution(headers, target);
57
+ const response = await binding.fetch(new Request(`${BINDING_ORIGIN}${EVENTS_EMIT_PATH}`, {
58
+ method: "POST",
59
+ headers,
60
+ body: JSON.stringify(body),
61
+ }));
62
+ const refusal = await gatewayRefusal(response);
63
+ if (refusal)
64
+ throw new GatewayRefusalError(refusal);
65
+ if (!response.ok) {
66
+ throw new GatewayRefusalError({
67
+ status: response.status,
68
+ code: "event_emit_failed",
69
+ message: `The Every App gateway answered ${response.status} to the event.`,
70
+ });
71
+ }
72
+ return (await response.json());
73
+ }
74
+ /**
75
+ * What the gateway would do, with no gateway: the same name and data
76
+ * rules, then one line on the console. The data isn't printed: it may hold
77
+ * a person's content, and the line is for "it fired", not for its payload.
78
+ */
79
+ function emitInDev(values, { event, data }) {
80
+ const nameProblem = appEventNameProblem(event);
81
+ if (nameProblem) {
82
+ throw new GatewayRefusalError({
83
+ status: 400,
84
+ code: "event_name_invalid",
85
+ message: nameProblem,
86
+ });
87
+ }
88
+ const checked = checkEventData(data, APP_EVENT_DATA_MAX_BYTES);
89
+ if (!checked.ok) {
90
+ throw new GatewayRefusalError({
91
+ status: checked.code === "event_data_too_large" ? 413 : 400,
92
+ code: checked.code,
93
+ message: checked.message,
94
+ });
95
+ }
96
+ const appId = values[APP_ID_ENV];
97
+ // The gateway prefixes the manifest id; a dev env without it still shows the shape.
98
+ const type = appEventType(typeof appId === "string" && appId.trim() ? appId.trim() : "app", event);
99
+ console.info(`[everyapp dev] event ${type} (not delivered: no Gateway)`);
100
+ return { id: `dev-${crypto.randomUUID()}`, type };
101
+ }
@@ -0,0 +1,81 @@
1
+ /**
2
+ * `everyApp()` — the app-side identity boundary.
3
+ *
4
+ * export default everyApp(handler, { tools });
5
+ *
6
+ * The wrapper verifies the gateway-injected identity before app code runs,
7
+ * stores the result on the original Request, and then delegates to the
8
+ * worker-style handler. The expected audience is the Worker's own name in
9
+ * `env.EVERYAPP_AUDIENCE`, which `everyAppWorker()` sets at deploy (the
10
+ * gateway stamps every token it mints for the app with it); only in
11
+ * development, where `everyapp dev` mints for the app id, does the app id in
12
+ * `env.EVERYAPP_APP_ID` stand in. The Worker never imports
13
+ * `everyapp.config.ts` (the Cloudflare Vite plugin hides every file
14
+ * `cloudflare.config.ts` imports from the Worker in dev).
15
+ * It also answers the gateway's internal tools contract
16
+ * (`/__everyapp/tools/*`) for the app's `defineTools()` catalog.
17
+ */
18
+ import { ConfigurationError, IdentityError, type EveryAppUser, type IdentityResult } from "../internal/index.js";
19
+ import type { ToolSet } from "./tools.js";
20
+ export { ConfigurationError, IdentityError };
21
+ export type { EveryAppUser, IdentityResult };
22
+ /**
23
+ * The runtime's `ExecutionContext`, which `everyApp()` passes to the handler
24
+ * as the Worker received it, so it goes on to Hono's `app.fetch(request,
25
+ * env, ctx)` or React Router's request handler as is. Tests that call
26
+ * `fetch` themselves pass a stub cast to it.
27
+ */
28
+ export type ExecutionContextLike = ExecutionContext;
29
+ export interface EveryAppManifestLike {
30
+ id: string;
31
+ [key: string]: unknown;
32
+ }
33
+ export interface EveryAppOptions {
34
+ /** Optional expected issuer (gateway URL). Defaults to env.EVERYAPP_IDENTITY_ISSUER when set. */
35
+ issuer?: string;
36
+ /** Env var holding the public key set. Defaults to EVERYAPP_IDENTITY_PUBLIC_KEYS. */
37
+ keysEnvVar?: string;
38
+ /** Provide keys directly (tests / non-standard delivery). */
39
+ publicKeys?: string[];
40
+ /**
41
+ * Accept dev identity tokens. Defaults to reading EVERYAPP_DEV from env.
42
+ * Never enable this in production.
43
+ */
44
+ allowDevIdentities?: boolean;
45
+ /**
46
+ * Agent tools from `defineTools()`. The gateway snapshots them at deploy
47
+ * and serves them to MCP clients; see `defineTools`.
48
+ */
49
+ tools?: ToolSet;
50
+ /** Override for tests. */
51
+ now?: number;
52
+ }
53
+ /**
54
+ * The handler accepted by everyApp, with the verified user as a 4th argument.
55
+ * `TCtx` narrows the context, e.g. to `ExecutionContext<Props>`.
56
+ */
57
+ export type EveryAppHandler<TEnv = Record<string, unknown>, TCtx extends ExecutionContextLike = ExecutionContext> = (request: Request, env: TEnv, ctx: TCtx, user: EveryAppUser | null) => Response | Promise<Response>;
58
+ interface EveryAppExport<TEnv, TCtx extends ExecutionContextLike> {
59
+ fetch(request: Request, env: TEnv, ctx: TCtx): Promise<Response>;
60
+ }
61
+ /**
62
+ * Return the verified Every App user for this request, or null for a declared
63
+ * public request. Uses the request cache first, then verifies the identity
64
+ * headers from the app id and public keys configured in env.
65
+ */
66
+ export declare function getEveryAppUser(request: Request, env: object): Promise<EveryAppUser | null>;
67
+ /**
68
+ * Return the verified user, or throw the same 401 JSON Response shape as
69
+ * everyApp() when the request is public or unauthenticated.
70
+ */
71
+ export declare function requireEveryAppUser(request: Request, env: object): Promise<EveryAppUser>;
72
+ export declare function hasScope(user: EveryAppUser, scope: string): boolean;
73
+ /** Wrap a Worker fetch handler with Every App identity verification. */
74
+ export declare function everyApp<TEnv = Record<string, unknown>, TCtx extends ExecutionContextLike = ExecutionContext>(handler: EveryAppHandler<TEnv, TCtx>, options?: EveryAppOptions): EveryAppExport<TEnv, TCtx>;
75
+ /**
76
+ * @deprecated Drop the manifest: the app id now comes from
77
+ * `env.EVERYAPP_APP_ID` and the audience from `env.EVERYAPP_AUDIENCE`.
78
+ * When passed, it must match the app id.
79
+ */
80
+ export declare function everyApp<TEnv = Record<string, unknown>, TCtx extends ExecutionContextLike = ExecutionContext>(handler: EveryAppHandler<TEnv, TCtx>, manifest: EveryAppManifestLike | undefined, options?: EveryAppOptions): EveryAppExport<TEnv, TCtx>;
81
+ //# sourceMappingURL=everyApp.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"everyApp.d.ts","sourceRoot":"","sources":["../../src/server/everyApp.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,EAGL,kBAAkB,EAElB,aAAa,EAIb,KAAK,YAAY,EACjB,KAAK,cAAc,EAEpB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAG1C,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAE,CAAC;AAC7C,YAAY,EAAE,YAAY,EAAE,cAAc,EAAE,CAAC;AAE7C;;;;;GAKG;AACH,MAAM,MAAM,oBAAoB,GAAG,gBAAgB,CAAC;AAEpD,MAAM,WAAW,oBAAoB;IACnC,EAAE,EAAE,MAAM,CAAC;IACX,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,eAAe;IAC9B,iGAAiG;IACjG,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,qFAAqF;IACrF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,6DAA6D;IAC7D,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB;;;OAGG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B;;;OAGG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,0BAA0B;IAC1B,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED;;;GAGG;AACH,MAAM,MAAM,eAAe,CACzB,IAAI,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,IAAI,SAAS,oBAAoB,GAAG,gBAAgB,IAClD,CACF,OAAO,EAAE,OAAO,EAChB,GAAG,EAAE,IAAI,EACT,GAAG,EAAE,IAAI,EACT,IAAI,EAAE,YAAY,GAAG,IAAI,KACtB,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;AAElC,UAAU,cAAc,CAAC,IAAI,EAAE,IAAI,SAAS,oBAAoB;IAC9D,KAAK,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;CAClE;AAoHD;;;;GAIG;AACH,wBAAsB,eAAe,CACnC,OAAO,EAAE,OAAO,EAChB,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAS9B;AAED;;;GAGG;AACH,wBAAsB,mBAAmB,CACvC,OAAO,EAAE,OAAO,EAChB,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,YAAY,CAAC,CAevB;AAED,wBAAgB,QAAQ,CAAC,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAEnE;AAUD,wEAAwE;AACxE,wBAAgB,QAAQ,CACtB,IAAI,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,IAAI,SAAS,oBAAoB,GAAG,gBAAgB,EAEpD,OAAO,EAAE,eAAe,CAAC,IAAI,EAAE,IAAI,CAAC,EACpC,OAAO,CAAC,EAAE,eAAe,GACxB,cAAc,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;AAC9B;;;;GAIG;AACH,wBAAgB,QAAQ,CACtB,IAAI,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,IAAI,SAAS,oBAAoB,GAAG,gBAAgB,EAEpD,OAAO,EAAE,eAAe,CAAC,IAAI,EAAE,IAAI,CAAC,EACpC,QAAQ,EAAE,oBAAoB,GAAG,SAAS,EAC1C,OAAO,CAAC,EAAE,eAAe,GACxB,cAAc,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC"}