@forumone/throughline 2.2.0 → 2.3.0-pr-315-e1a501e

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 (71) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/audit/types.d.ts +1 -1
  3. package/dist/audit/types.d.ts.map +1 -1
  4. package/dist/audit/types.js +2 -0
  5. package/dist/audit/types.js.map +1 -1
  6. package/dist/editorial/palette/ReportsNav.d.ts +3 -1
  7. package/dist/editorial/palette/ReportsNav.d.ts.map +1 -1
  8. package/dist/editorial/palette/ReportsNav.js +3 -3
  9. package/dist/editorial/palette/ReportsNav.js.map +1 -1
  10. package/dist/exports/rsc.d.ts +1 -0
  11. package/dist/exports/rsc.d.ts.map +1 -1
  12. package/dist/exports/rsc.js +2 -0
  13. package/dist/exports/rsc.js.map +1 -1
  14. package/dist/mcp-oauth/admin/McpClientActions.d.ts +15 -0
  15. package/dist/mcp-oauth/admin/McpClientActions.d.ts.map +1 -0
  16. package/dist/mcp-oauth/admin/McpClientActions.js +79 -0
  17. package/dist/mcp-oauth/admin/McpClientActions.js.map +1 -0
  18. package/dist/mcp-oauth/admin/styles.d.ts +2 -0
  19. package/dist/mcp-oauth/admin/styles.d.ts.map +1 -0
  20. package/dist/mcp-oauth/admin/styles.js +27 -0
  21. package/dist/mcp-oauth/admin/styles.js.map +1 -0
  22. package/dist/mcp-oauth/admin/views.d.ts +4 -0
  23. package/dist/mcp-oauth/admin/views.d.ts.map +1 -0
  24. package/dist/mcp-oauth/admin/views.js +99 -0
  25. package/dist/mcp-oauth/admin/views.js.map +1 -0
  26. package/dist/mcp-oauth/auth.d.ts +12 -0
  27. package/dist/mcp-oauth/auth.d.ts.map +1 -0
  28. package/dist/mcp-oauth/auth.js +56 -0
  29. package/dist/mcp-oauth/auth.js.map +1 -0
  30. package/dist/mcp-oauth/cimd.d.ts +13 -0
  31. package/dist/mcp-oauth/cimd.d.ts.map +1 -0
  32. package/dist/mcp-oauth/cimd.js +93 -0
  33. package/dist/mcp-oauth/cimd.js.map +1 -0
  34. package/dist/mcp-oauth/clients.d.ts +42 -0
  35. package/dist/mcp-oauth/clients.d.ts.map +1 -0
  36. package/dist/mcp-oauth/clients.js +151 -0
  37. package/dist/mcp-oauth/clients.js.map +1 -0
  38. package/dist/mcp-oauth/collections.d.ts +10 -0
  39. package/dist/mcp-oauth/collections.d.ts.map +1 -0
  40. package/dist/mcp-oauth/collections.js +236 -0
  41. package/dist/mcp-oauth/collections.js.map +1 -0
  42. package/dist/mcp-oauth/crypto.d.ts +29 -0
  43. package/dist/mcp-oauth/crypto.d.ts.map +1 -0
  44. package/dist/mcp-oauth/crypto.js +91 -0
  45. package/dist/mcp-oauth/crypto.js.map +1 -0
  46. package/dist/mcp-oauth/endpoints.d.ts +11 -0
  47. package/dist/mcp-oauth/endpoints.d.ts.map +1 -0
  48. package/dist/mcp-oauth/endpoints.js +365 -0
  49. package/dist/mcp-oauth/endpoints.js.map +1 -0
  50. package/dist/mcp-oauth/index.d.ts +10 -0
  51. package/dist/mcp-oauth/index.d.ts.map +1 -0
  52. package/dist/mcp-oauth/index.js +6 -0
  53. package/dist/mcp-oauth/index.js.map +1 -0
  54. package/dist/mcp-oauth/metadata.d.ts +45 -0
  55. package/dist/mcp-oauth/metadata.d.ts.map +1 -0
  56. package/dist/mcp-oauth/metadata.js +84 -0
  57. package/dist/mcp-oauth/metadata.js.map +1 -0
  58. package/dist/mcp-oauth/plugin.d.ts +37 -0
  59. package/dist/mcp-oauth/plugin.d.ts.map +1 -0
  60. package/dist/mcp-oauth/plugin.js +90 -0
  61. package/dist/mcp-oauth/plugin.js.map +1 -0
  62. package/dist/mcp-oauth/runtime.d.ts +124 -0
  63. package/dist/mcp-oauth/runtime.d.ts.map +1 -0
  64. package/dist/mcp-oauth/runtime.js +111 -0
  65. package/dist/mcp-oauth/runtime.js.map +1 -0
  66. package/dist/mcp-oauth/store.d.ts +75 -0
  67. package/dist/mcp-oauth/store.d.ts.map +1 -0
  68. package/dist/mcp-oauth/store.js +408 -0
  69. package/dist/mcp-oauth/store.js.map +1 -0
  70. package/dist/migrate/exports.json +29 -0
  71. package/package.json +7 -3
@@ -0,0 +1,84 @@
1
+ /*
2
+ The two discovery documents, and the exact values clients check.
3
+
4
+ A client finds everything from the `401` on `/api/mcp`: its `WWW-Authenticate`
5
+ points at the protected-resource metadata (RFC 9728), which names this site as
6
+ the authorization server, whose own metadata (RFC 8414) lists the endpoints.
7
+
8
+ What each client is strict about, from their documentation:
9
+
10
+ - Claude uses only the **first** `authorization_servers` entry, and requires
11
+ `resource` to equal the URL the person typed, path included.
12
+ - Claude picks a client ID metadata document (CIMD) only when **both**
13
+ `client_id_metadata_document_supported` and `"none"` among the token
14
+ endpoint's auth methods are present; otherwise it registers (DCR).
15
+ - ChatGPT uses its stable callback only when the authorization response carries
16
+ `iss` (RFC 9207), and compares it to `issuer` byte for byte.
17
+ - Both append `offline_access` to the scopes when it is listed, which is what
18
+ gets them a refresh token.
19
+ */
20
+ export const MCP_SCOPE = 'mcp';
21
+ export function authorizationServerMetadata(runtime) {
22
+ return {
23
+ issuer: runtime.issuer,
24
+ authorization_endpoint: runtime.endpoint('authorize'),
25
+ token_endpoint: runtime.endpoint('token'),
26
+ registration_endpoint: runtime.endpoint('register'),
27
+ revocation_endpoint: runtime.endpoint('revoke'),
28
+ response_types_supported: ['code'],
29
+ response_modes_supported: ['query'],
30
+ grant_types_supported: ['authorization_code', 'refresh_token'],
31
+ token_endpoint_auth_methods_supported: ['none'],
32
+ revocation_endpoint_auth_methods_supported: ['none'],
33
+ code_challenge_methods_supported: ['S256'],
34
+ scopes_supported: [MCP_SCOPE, 'offline_access'],
35
+ client_id_metadata_document_supported: true,
36
+ authorization_response_iss_parameter_supported: true,
37
+ };
38
+ }
39
+ export function protectedResourceMetadata(runtime) {
40
+ return {
41
+ resource: runtime.resource,
42
+ authorization_servers: [runtime.issuer],
43
+ bearer_methods_supported: ['header'],
44
+ scopes_supported: [MCP_SCOPE],
45
+ resource_name: runtime.serverName,
46
+ };
47
+ }
48
+ /** Where the protected-resource metadata is served. */
49
+ export function protectedResourceMetadataUrl(runtime) {
50
+ return `${runtime.origin}/.well-known/oauth-protected-resource${new URL(runtime.resource).pathname}`;
51
+ }
52
+ /**
53
+ * Rewrites for `next.config`, so the well-known paths at the root reach the
54
+ * endpoints Payload serves under its API route. Payload only routes under
55
+ * `/api`; RFC 8414 and RFC 9728 put discovery at `/.well-known`.
56
+ *
57
+ * ```js
58
+ * async rewrites() {
59
+ * return mcpOAuthRewrites()
60
+ * }
61
+ * ```
62
+ */
63
+ export function mcpOAuthRewrites({ apiRoute = '/api' } = {}) {
64
+ const api = apiRoute.replace(/\/+$/, '');
65
+ return [
66
+ {
67
+ source: '/.well-known/oauth-protected-resource',
68
+ destination: `${api}/oauth/protected-resource`,
69
+ },
70
+ {
71
+ source: '/.well-known/oauth-protected-resource/:path*',
72
+ destination: `${api}/oauth/protected-resource`,
73
+ },
74
+ {
75
+ source: '/.well-known/oauth-authorization-server',
76
+ destination: `${api}/oauth/metadata`,
77
+ },
78
+ {
79
+ source: '/.well-known/oauth-authorization-server/:path*',
80
+ destination: `${api}/oauth/metadata`,
81
+ },
82
+ ];
83
+ }
84
+ //# sourceMappingURL=metadata.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"metadata.js","sourceRoot":"","sources":["../../src/mcp-oauth/metadata.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;EAkBE;AAEF,MAAM,CAAC,MAAM,SAAS,GAAG,KAAK,CAAA;AAE9B,MAAM,UAAU,2BAA2B,CAAC,OAAwB;IAClE,OAAO;QACL,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,sBAAsB,EAAE,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC;QACrD,cAAc,EAAE,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC;QACzC,qBAAqB,EAAE,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC;QACnD,mBAAmB,EAAE,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC;QAC/C,wBAAwB,EAAE,CAAC,MAAM,CAAC;QAClC,wBAAwB,EAAE,CAAC,OAAO,CAAC;QACnC,qBAAqB,EAAE,CAAC,oBAAoB,EAAE,eAAe,CAAC;QAC9D,qCAAqC,EAAE,CAAC,MAAM,CAAC;QAC/C,0CAA0C,EAAE,CAAC,MAAM,CAAC;QACpD,gCAAgC,EAAE,CAAC,MAAM,CAAC;QAC1C,gBAAgB,EAAE,CAAC,SAAS,EAAE,gBAAgB,CAAC;QAC/C,qCAAqC,EAAE,IAAI;QAC3C,8CAA8C,EAAE,IAAI;KACrD,CAAA;AACH,CAAC;AAED,MAAM,UAAU,yBAAyB,CAAC,OAAwB;IAChE,OAAO;QACL,QAAQ,EAAE,OAAO,CAAC,QAAQ;QAC1B,qBAAqB,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC;QACvC,wBAAwB,EAAE,CAAC,QAAQ,CAAC;QACpC,gBAAgB,EAAE,CAAC,SAAS,CAAC;QAC7B,aAAa,EAAE,OAAO,CAAC,UAAU;KAClC,CAAA;AACH,CAAC;AAED,uDAAuD;AACvD,MAAM,UAAU,4BAA4B,CAAC,OAAwB;IACnE,OAAO,GAAG,OAAO,CAAC,MAAM,wCAAwC,IAAI,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,QAAQ,EAAE,CAAA;AACtG,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,gBAAgB,CAAC,EAAE,QAAQ,GAAG,MAAM,KAA4B,EAAE;IAChF,MAAM,GAAG,GAAG,QAAQ,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAA;IACxC,OAAO;QACL;YACE,MAAM,EAAE,uCAAuC;YAC/C,WAAW,EAAE,GAAG,GAAG,2BAA2B;SAC/C;QACD;YACE,MAAM,EAAE,8CAA8C;YACtD,WAAW,EAAE,GAAG,GAAG,2BAA2B;SAC/C;QACD;YACE,MAAM,EAAE,yCAAyC;YACjD,WAAW,EAAE,GAAG,GAAG,iBAAiB;SACrC;QACD;YACE,MAAM,EAAE,gDAAgD;YACxD,WAAW,EAAE,GAAG,GAAG,iBAAiB;SACrC;KACF,CAAA;AACH,CAAC"}
@@ -0,0 +1,37 @@
1
+ import type { Plugin } from 'payload';
2
+ import { type OverrideAuth } from './auth.js';
3
+ import { type EndpointOptions } from './endpoints.js';
4
+ import { type McpOAuthOptions, type McpOAuthRuntime } from './runtime.js';
5
+ export declare const CONNECT_MCP_VIEW_PATH = "@forumone/throughline/rsc#ConnectMcpView";
6
+ export declare const MCP_AUTHORIZE_VIEW_PATH = "@forumone/throughline/rsc#McpAuthorizeView";
7
+ export interface McpOAuth {
8
+ /** Registers the endpoints, the collections, the policy and the admin pages. */
9
+ plugin: Plugin;
10
+ /** For `mcpPlugin({ overrideAuth })`. */
11
+ overrideAuth: OverrideAuth;
12
+ runtime: McpOAuthRuntime;
13
+ }
14
+ /**
15
+ * OAuth sign-in for `/api/mcp`: a person adds the server's URL to Claude or
16
+ * ChatGPT, signs in to the admin as themselves, and allows the connection. No
17
+ * key to copy.
18
+ *
19
+ * ```ts
20
+ * const oauth = mcpOAuth({ serverURL, serverName: 'Example', tools: mcpTools, prompts: mcpPrompts,
21
+ * isAdmin, eligible })
22
+ *
23
+ * plugins: [
24
+ * ...throughline plugins, // declare the tools first
25
+ * oauth.plugin,
26
+ * mcpPlugin({ mcp: { tools: mcpTools, prompts: mcpPrompts }, overrideAuth: oauth.overrideAuth }),
27
+ * ]
28
+ * ```
29
+ *
30
+ * And in `next.config`, `rewrites: async () => mcpOAuthRewrites()`, so the
31
+ * discovery documents are found at `/.well-known`.
32
+ *
33
+ * API keys keep working beside it: a bearer token that is not one of these is
34
+ * handed to the plugin's own key lookup.
35
+ */
36
+ export declare function mcpOAuth(options: McpOAuthOptions, endpointOptions?: EndpointOptions): McpOAuth;
37
+ //# sourceMappingURL=plugin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../src/mcp-oauth/plugin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAU,MAAM,EAAE,MAAM,SAAS,CAAA;AAE7C,OAAO,EAAsB,KAAK,YAAY,EAAE,MAAM,WAAW,CAAA;AAEjE,OAAO,EAAkB,KAAK,eAAe,EAAE,MAAM,gBAAgB,CAAA;AACrE,OAAO,EAGL,KAAK,eAAe,EACpB,KAAK,eAAe,EACrB,MAAM,cAAc,CAAA;AAErB,eAAO,MAAM,qBAAqB,6CAA6C,CAAA;AAC/E,eAAO,MAAM,uBAAuB,+CAA+C,CAAA;AAEnF,MAAM,WAAW,QAAQ;IACvB,gFAAgF;IAChF,MAAM,EAAE,MAAM,CAAA;IACd,yCAAyC;IACzC,YAAY,EAAE,YAAY,CAAA;IAC1B,OAAO,EAAE,eAAe,CAAA;CACzB;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,QAAQ,CACtB,OAAO,EAAE,eAAe,EACxB,eAAe,GAAE,eAAoB,GACpC,QAAQ,CAiEV"}
@@ -0,0 +1,90 @@
1
+ import { REPORTS_NAV_PATH } from '../editorial/plugin.js';
2
+ import { createOverrideAuth } from './auth.js';
3
+ import { accessGlobal, clientsCollection, grantsCollection } from './collections.js';
4
+ import { oauthEndpoints } from './endpoints.js';
5
+ import { createRuntime, MCP_OAUTH_CUSTOM_KEY, } from './runtime.js';
6
+ export const CONNECT_MCP_VIEW_PATH = '@forumone/throughline/rsc#ConnectMcpView';
7
+ export const MCP_AUTHORIZE_VIEW_PATH = '@forumone/throughline/rsc#McpAuthorizeView';
8
+ /**
9
+ * OAuth sign-in for `/api/mcp`: a person adds the server's URL to Claude or
10
+ * ChatGPT, signs in to the admin as themselves, and allows the connection. No
11
+ * key to copy.
12
+ *
13
+ * ```ts
14
+ * const oauth = mcpOAuth({ serverURL, serverName: 'Example', tools: mcpTools, prompts: mcpPrompts,
15
+ * isAdmin, eligible })
16
+ *
17
+ * plugins: [
18
+ * ...throughline plugins, // declare the tools first
19
+ * oauth.plugin,
20
+ * mcpPlugin({ mcp: { tools: mcpTools, prompts: mcpPrompts }, overrideAuth: oauth.overrideAuth }),
21
+ * ]
22
+ * ```
23
+ *
24
+ * And in `next.config`, `rewrites: async () => mcpOAuthRewrites()`, so the
25
+ * discovery documents are found at `/.well-known`.
26
+ *
27
+ * API keys keep working beside it: a bearer token that is not one of these is
28
+ * handed to the plugin's own key lookup.
29
+ */
30
+ export function mcpOAuth(options, endpointOptions = {}) {
31
+ const runtime = createRuntime(options);
32
+ const plugin = (incoming) => {
33
+ /*
34
+ The tool names are read now, not when `mcpOAuth` was called: the modules
35
+ declare theirs while the config is built, so this plugin has to come after
36
+ them, and an empty list here means it did not.
37
+ */
38
+ if (runtime.tools.length === 0) {
39
+ throw new Error('mcpOAuth(): no MCP tools had been declared when its plugin ran. Register it after the plugins that declare tools (throughline()), and hand it the same array mcpPlugin serves.');
40
+ }
41
+ const served = new Set(runtime.tools.map((tool) => tool.name));
42
+ runtime.defaults.editorTools = runtime.defaults.editorTools.filter((name) => served.has(name));
43
+ const group = options.adminGroup ?? 'Admin';
44
+ const views = incoming.admin?.components?.views ?? {};
45
+ return {
46
+ ...incoming,
47
+ collections: [
48
+ ...(incoming.collections ?? []),
49
+ clientsCollection(runtime, group),
50
+ grantsCollection(runtime, group),
51
+ ],
52
+ globals: [...(incoming.globals ?? []), accessGlobal(runtime, group)],
53
+ endpoints: [...(incoming.endpoints ?? []), ...oauthEndpoints(runtime, endpointOptions)],
54
+ admin: {
55
+ ...incoming.admin,
56
+ components: {
57
+ ...incoming.admin?.components,
58
+ views: {
59
+ ...views,
60
+ connectMcp: {
61
+ Component: CONNECT_MCP_VIEW_PATH,
62
+ path: runtime.connectPath,
63
+ exact: true,
64
+ meta: { title: 'Connect MCP' },
65
+ },
66
+ mcpAuthorize: {
67
+ Component: MCP_AUTHORIZE_VIEW_PATH,
68
+ path: `${runtime.connectPath}/authorize`,
69
+ exact: true,
70
+ meta: { title: `Connect an app to ${runtime.serverName}` },
71
+ },
72
+ },
73
+ afterNavLinks: [
74
+ ...(incoming.admin?.components?.afterNavLinks ?? []),
75
+ {
76
+ path: REPORTS_NAV_PATH,
77
+ clientProps: {
78
+ label: 'MCP',
79
+ reports: [{ path: runtime.connectPath, label: 'Connect MCP' }],
80
+ },
81
+ },
82
+ ],
83
+ },
84
+ },
85
+ custom: { ...incoming.custom, [MCP_OAUTH_CUSTOM_KEY]: runtime },
86
+ };
87
+ };
88
+ return { plugin, overrideAuth: createOverrideAuth(runtime), runtime };
89
+ }
90
+ //# sourceMappingURL=plugin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.js","sourceRoot":"","sources":["../../src/mcp-oauth/plugin.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,124 @@
1
+ import type { Payload } from 'payload';
2
+ import { type McpClientKind } from './clients.js';
3
+ export declare const MCP_OAUTH_CLIENTS_SLUG = "mcp-oauth-clients";
4
+ export declare const MCP_OAUTH_GRANTS_SLUG = "mcp-oauth-grants";
5
+ export declare const MCP_ACCESS_SLUG = "mcp-access";
6
+ /** The strategy name a person signed in through an OAuth token carries. */
7
+ export declare const MCP_OAUTH_STRATEGY = "mcp-oauth";
8
+ type UserDoc = Record<string, unknown>;
9
+ export interface McpOAuthOptions {
10
+ /**
11
+ * The site's public origin, e.g. `https://www.example.com`. The issuer, the
12
+ * MCP resource and every endpoint are built from it, and a client compares
13
+ * them byte for byte, so it must be the host people actually use: not an
14
+ * alias that redirects, and not a deployment URL.
15
+ */
16
+ serverURL: string;
17
+ /** The site's name, as the consent screen and the Connect MCP page say it. */
18
+ serverName: string;
19
+ /** The tools `plugin-mcp` serves: the same array the host hands `mcpPlugin`. */
20
+ tools: readonly {
21
+ name: string;
22
+ }[];
23
+ /** The prompts it serves. Everybody who may connect is offered all of them. */
24
+ prompts?: readonly {
25
+ name: string;
26
+ }[];
27
+ /** An administrator: chooses the policy, sees every connection, revokes any. */
28
+ isAdmin: (user: UserDoc) => boolean;
29
+ /**
30
+ * Who may connect at all: `true`, or the reason they may not. Asked at
31
+ * consent, at every token refresh and on every MCP request, so somebody who
32
+ * loses the role loses the connection with it.
33
+ */
34
+ eligible: (user: UserDoc) => true | string;
35
+ /**
36
+ * The tools a non-administrator is offered until an administrator changes
37
+ * it. Defaults to the authoring tools.
38
+ */
39
+ editorTools?: readonly string[];
40
+ /** Which apps may connect until an administrator changes it. Each defaults on. */
41
+ clients?: Partial<Record<McpClientKind, boolean>>;
42
+ /**
43
+ * How people add the server to Claude. `organization` when a Team or
44
+ * Enterprise Owner has added it for everybody, so members only click
45
+ * Connect; `personal` (the default) when each person adds it themselves, and
46
+ * the page links to Claude's add-connector form with the URL filled in.
47
+ */
48
+ claudeConnector?: 'organization' | 'personal';
49
+ /**
50
+ * The name Claude Code gives the server in the command the Connect MCP page
51
+ * shows. Defaults to `serverName`, lowercased and hyphenated.
52
+ */
53
+ claudeCodeName?: string;
54
+ /** Default `/api`. */
55
+ apiRoute?: string;
56
+ /** Where `plugin-mcp` serves, under `apiRoute`. Default `/mcp`. */
57
+ mcpPath?: string;
58
+ /** Default `users`. */
59
+ users?: string;
60
+ /** The admin sidebar group for the two collections. Default `Admin`. */
61
+ adminGroup?: string;
62
+ /** The Connect MCP page, under the admin route. Default `/connect-mcp`. */
63
+ connectPath?: string;
64
+ /** Seconds. Default an hour. */
65
+ accessTokenTtl?: number;
66
+ /** Seconds since last use. Default thirty days. */
67
+ refreshTokenTtl?: number;
68
+ /**
69
+ * Also ask `eligible` of the person behind an API key, on every request.
70
+ * Default true: an API key's holder is otherwise checked only when the key is
71
+ * made.
72
+ */
73
+ recheckApiKeyHolders?: boolean;
74
+ }
75
+ export interface McpOAuthRuntime {
76
+ serverName: string;
77
+ claudeConnector: 'organization' | 'personal';
78
+ claudeCodeName: string;
79
+ origin: string;
80
+ issuer: string;
81
+ resource: string;
82
+ apiRoute: string;
83
+ users: string;
84
+ connectPath: string;
85
+ accessTokenTtl: number;
86
+ refreshTokenTtl: number;
87
+ recheckApiKeyHolders: boolean;
88
+ isAdmin: (user: UserDoc) => boolean;
89
+ eligible: (user: UserDoc) => true | string;
90
+ defaults: {
91
+ clients: Record<McpClientKind, boolean>;
92
+ editorTools: readonly string[];
93
+ };
94
+ tools: readonly {
95
+ name: string;
96
+ }[];
97
+ prompts: readonly {
98
+ name: string;
99
+ }[];
100
+ endpoint: (name: string) => string;
101
+ }
102
+ export declare const MCP_OAUTH_CUSTOM_KEY = "throughline:mcp-oauth";
103
+ export declare function createRuntime(options: McpOAuthOptions): McpOAuthRuntime;
104
+ /** The runtime `mcpOAuth` stored on the config, for the views. */
105
+ export declare function mcpOAuthRuntime(config: {
106
+ custom?: Record<string, unknown>;
107
+ }): McpOAuthRuntime;
108
+ export declare function keys(payload: Pick<Payload, 'secret'>): {
109
+ token: Buffer<ArrayBufferLike>;
110
+ request: Buffer<ArrayBufferLike>;
111
+ };
112
+ export interface McpAccessPolicy {
113
+ clients: Record<McpClientKind, boolean>;
114
+ editorTools: string[];
115
+ adminAllTools: boolean;
116
+ adminTools: string[];
117
+ }
118
+ export declare function readPolicy(payload: Payload, runtime: McpOAuthRuntime): Promise<McpAccessPolicy>;
119
+ /** The tools a person is offered under a policy, by served name. */
120
+ export declare function toolsFor(user: UserDoc, policy: McpAccessPolicy, runtime: Pick<McpOAuthRuntime, 'isAdmin' | 'tools'>): string[];
121
+ /** `plugin-mcp`'s key for a tool or prompt name. Its own `toCamelCase`. */
122
+ export declare function toCamelCase(value: string): string;
123
+ export {};
124
+ //# sourceMappingURL=runtime.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../src/mcp-oauth/runtime.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,111 @@
1
+ import { AUTHORING_TOOL_DESCRIPTORS } from '../authoring/descriptors.js';
2
+ import { MCP_CLIENT_KINDS } from './clients.js';
3
+ import { deriveKey } from './crypto.js';
4
+ export const MCP_OAUTH_CLIENTS_SLUG = 'mcp-oauth-clients';
5
+ export const MCP_OAUTH_GRANTS_SLUG = 'mcp-oauth-grants';
6
+ export const MCP_ACCESS_SLUG = 'mcp-access';
7
+ /** The strategy name a person signed in through an OAuth token carries. */
8
+ export const MCP_OAUTH_STRATEGY = 'mcp-oauth';
9
+ export const MCP_OAUTH_CUSTOM_KEY = 'throughline:mcp-oauth';
10
+ function slug(name) {
11
+ return (name
12
+ .toLowerCase()
13
+ .replace(/[^a-z0-9]+/g, '-')
14
+ .replace(/^-+|-+$/g, '') || 'site');
15
+ }
16
+ export function createRuntime(options) {
17
+ let origin;
18
+ try {
19
+ origin = new URL(options.serverURL).origin;
20
+ }
21
+ catch {
22
+ throw new Error(`mcpOAuth(): serverURL "${options.serverURL}" is not a URL.`);
23
+ }
24
+ const apiRoute = (options.apiRoute ?? '/api').replace(/\/+$/, '');
25
+ const mcpPath = options.mcpPath ?? '/mcp';
26
+ const served = new Set(options.tools.map((tool) => tool.name));
27
+ const authoring = AUTHORING_TOOL_DESCRIPTORS.map((tool) => tool.name);
28
+ return {
29
+ serverName: options.serverName,
30
+ claudeConnector: options.claudeConnector ?? 'personal',
31
+ claudeCodeName: options.claudeCodeName ?? slug(options.serverName),
32
+ origin,
33
+ issuer: origin,
34
+ resource: `${origin}${apiRoute}${mcpPath}`,
35
+ apiRoute,
36
+ users: options.users ?? 'users',
37
+ connectPath: options.connectPath ?? '/connect-mcp',
38
+ accessTokenTtl: options.accessTokenTtl ?? 60 * 60,
39
+ refreshTokenTtl: options.refreshTokenTtl ?? 30 * 24 * 60 * 60,
40
+ recheckApiKeyHolders: options.recheckApiKeyHolders ?? true,
41
+ isAdmin: options.isAdmin,
42
+ eligible: options.eligible,
43
+ defaults: {
44
+ clients: Object.fromEntries(MCP_CLIENT_KINDS.map((kind) => [kind, options.clients?.[kind] ?? true])),
45
+ editorTools: options.editorTools ?? authoring.filter((name) => served.size === 0 || served.has(name)),
46
+ },
47
+ tools: options.tools,
48
+ prompts: options.prompts ?? [],
49
+ endpoint: (name) => `${origin}${apiRoute}/oauth/${name}`,
50
+ };
51
+ }
52
+ /** The runtime `mcpOAuth` stored on the config, for the views. */
53
+ export function mcpOAuthRuntime(config) {
54
+ const runtime = config.custom?.[MCP_OAUTH_CUSTOM_KEY];
55
+ if (!runtime)
56
+ throw new Error('mcpOAuth() is not registered in this Payload config.');
57
+ return runtime;
58
+ }
59
+ export function keys(payload) {
60
+ return {
61
+ token: deriveKey(payload.secret, 'token'),
62
+ request: deriveKey(payload.secret, 'request'),
63
+ };
64
+ }
65
+ export async function readPolicy(payload, runtime) {
66
+ let doc;
67
+ try {
68
+ doc = (await payload.findGlobal({
69
+ slug: MCP_ACCESS_SLUG,
70
+ depth: 0,
71
+ overrideAccess: true,
72
+ }));
73
+ }
74
+ catch {
75
+ doc = {};
76
+ }
77
+ const clients = (doc['clients'] ?? {});
78
+ const editors = (doc['editors'] ?? {});
79
+ const admins = (doc['admins'] ?? {});
80
+ const names = (value, fallback) => Array.isArray(value)
81
+ ? value.filter((each) => typeof each === 'string')
82
+ : [...fallback];
83
+ return {
84
+ clients: Object.fromEntries(MCP_CLIENT_KINDS.map((kind) => [
85
+ kind,
86
+ typeof clients[kind] === 'boolean'
87
+ ? clients[kind]
88
+ : runtime.defaults.clients[kind],
89
+ ])),
90
+ editorTools: names(editors['tools'], runtime.defaults.editorTools),
91
+ adminAllTools: typeof admins['allTools'] === 'boolean' ? admins['allTools'] : true,
92
+ adminTools: names(admins['tools'], []),
93
+ };
94
+ }
95
+ /** The tools a person is offered under a policy, by served name. */
96
+ export function toolsFor(user, policy, runtime) {
97
+ const served = runtime.tools.map((tool) => tool.name);
98
+ if (runtime.isAdmin(user)) {
99
+ if (policy.adminAllTools)
100
+ return served;
101
+ return served.filter((name) => policy.adminTools.includes(name));
102
+ }
103
+ return served.filter((name) => policy.editorTools.includes(name));
104
+ }
105
+ /** `plugin-mcp`'s key for a tool or prompt name. Its own `toCamelCase`. */
106
+ export function toCamelCase(value) {
107
+ return value
108
+ .replace(/[-_\s]+(.)?/g, (_, chr) => (chr ? chr.toUpperCase() : ''))
109
+ .replace(/^(.)/, (_, chr) => chr.toLowerCase());
110
+ }
111
+ //# sourceMappingURL=runtime.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime.js","sourceRoot":"","sources":["../../src/mcp-oauth/runtime.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,0BAA0B,EAAE,MAAM,6BAA6B,CAAA;AACxE,OAAO,EAAE,gBAAgB,EAAsB,MAAM,cAAc,CAAA;AACnE,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AAEvC,MAAM,CAAC,MAAM,sBAAsB,GAAG,mBAAmB,CAAA;AACzD,MAAM,CAAC,MAAM,qBAAqB,GAAG,kBAAkB,CAAA;AACvD,MAAM,CAAC,MAAM,eAAe,GAAG,YAAY,CAAA;AAE3C,2EAA2E;AAC3E,MAAM,CAAC,MAAM,kBAAkB,GAAG,WAAW,CAAA;AAwF7C,MAAM,CAAC,MAAM,oBAAoB,GAAG,uBAAuB,CAAA;AAE3D,SAAS,IAAI,CAAC,IAAY;IACxB,OAAO,CACL,IAAI;SACD,WAAW,EAAE;SACb,OAAO,CAAC,aAAa,EAAE,GAAG,CAAC;SAC3B,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,IAAI,MAAM,CACrC,CAAA;AACH,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,OAAwB;IACpD,IAAI,MAAc,CAAA;IAClB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,MAAM,CAAA;IAC5C,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,KAAK,CAAC,0BAA0B,OAAO,CAAC,SAAS,iBAAiB,CAAC,CAAA;IAC/E,CAAC;IACD,MAAM,QAAQ,GAAG,CAAC,OAAO,CAAC,QAAQ,IAAI,MAAM,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAA;IACjE,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,MAAM,CAAA;IACzC,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAA;IAC9D,MAAM,SAAS,GAAG,0BAA0B,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IAErE,OAAO;QACL,UAAU,EAAE,OAAO,CAAC,UAAU;QAC9B,eAAe,EAAE,OAAO,CAAC,eAAe,IAAI,UAAU;QACtD,cAAc,EAAE,OAAO,CAAC,cAAc,IAAI,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC;QAClE,MAAM;QACN,MAAM,EAAE,MAAM;QACd,QAAQ,EAAE,GAAG,MAAM,GAAG,QAAQ,GAAG,OAAO,EAAE;QAC1C,QAAQ;QACR,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,OAAO;QAC/B,WAAW,EAAE,OAAO,CAAC,WAAW,IAAI,cAAc;QAClD,cAAc,EAAE,OAAO,CAAC,cAAc,IAAI,EAAE,GAAG,EAAE;QACjD,eAAe,EAAE,OAAO,CAAC,eAAe,IAAI,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE;QAC7D,oBAAoB,EAAE,OAAO,CAAC,oBAAoB,IAAI,IAAI;QAC1D,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,QAAQ,EAAE,OAAO,CAAC,QAAQ;QAC1B,QAAQ,EAAE;YACR,OAAO,EAAE,MAAM,CAAC,WAAW,CACzB,gBAAgB,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,OAAO,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,CAAC,CACtC;YACnC,WAAW,EACT,OAAO,CAAC,WAAW,IAAI,SAAS,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;SAC3F;QACD,KAAK,EAAE,OAAO,CAAC,KAAK;QACpB,OAAO,EAAE,OAAO,CAAC,OAAO,IAAI,EAAE;QAC9B,QAAQ,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,GAAG,MAAM,GAAG,QAAQ,UAAU,IAAI,EAAE;KACzD,CAAA;AACH,CAAC;AAED,kEAAkE;AAClE,MAAM,UAAU,eAAe,CAAC,MAA4C;IAC1E,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,oBAAoB,CAAgC,CAAA;IACpF,IAAI,CAAC,OAAO;QAAE,MAAM,IAAI,KAAK,CAAC,sDAAsD,CAAC,CAAA;IACrF,OAAO,OAAO,CAAA;AAChB,CAAC;AAED,MAAM,UAAU,IAAI,CAAC,OAAgC;IACnD,OAAO;QACL,KAAK,EAAE,SAAS,CAAC,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC;QACzC,OAAO,EAAE,SAAS,CAAC,OAAO,CAAC,MAAM,EAAE,SAAS,CAAC;KAC9C,CAAA;AACH,CAAC;AAkBD,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,OAAgB,EAChB,OAAwB;IAExB,IAAI,GAA4B,CAAA;IAChC,IAAI,CAAC;QACH,GAAG,GAAG,CAAC,MAAM,OAAO,CAAC,UAAU,CAAC;YAC9B,IAAI,EAAE,eAAwB;YAC9B,KAAK,EAAE,CAAC;YACR,cAAc,EAAE,IAAI;SACrB,CAAC,CAAuC,CAAA;IAC3C,CAAC;IAAC,MAAM,CAAC;QACP,GAAG,GAAG,EAAE,CAAA;IACV,CAAC;IACD,MAAM,OAAO,GAAG,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,EAAE,CAA4B,CAAA;IACjE,MAAM,OAAO,GAAG,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,EAAE,CAA4B,CAAA;IACjE,MAAM,MAAM,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,EAAE,CAA4B,CAAA;IAC/D,MAAM,KAAK,GAAG,CAAC,KAAc,EAAE,QAA2B,EAAE,EAAE,CAC5D,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAClB,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAkB,EAAE,CAAC,OAAO,IAAI,KAAK,QAAQ,CAAC;QAClE,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAC,CAAA;IAEnB,OAAO;QACL,OAAO,EAAE,MAAM,CAAC,WAAW,CACzB,gBAAgB,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC;YAC7B,IAAI;YACJ,OAAO,OAAO,CAAC,IAAI,CAAC,KAAK,SAAS;gBAChC,CAAC,CAAE,OAAO,CAAC,IAAI,CAAa;gBAC5B,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC;SACnC,CAAC,CAC+B;QACnC,WAAW,EAAE,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC;QAClE,aAAa,EAAE,OAAO,MAAM,CAAC,UAAU,CAAC,KAAK,SAAS,CAAC,CAAC,CAAE,MAAM,CAAC,UAAU,CAAa,CAAC,CAAC,CAAC,IAAI;QAC/F,UAAU,EAAE,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,EAAE,CAAC;KACvC,CAAA;AACH,CAAC;AAED,oEAAoE;AACpE,MAAM,UAAU,QAAQ,CACtB,IAAa,EACb,MAAuB,EACvB,OAAmD;IAEnD,MAAM,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IACrD,IAAI,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;QAC1B,IAAI,MAAM,CAAC,aAAa;YAAE,OAAO,MAAM,CAAA;QACvC,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAA;IAClE,CAAC;IACD,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,WAAW,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAA;AACnE,CAAC;AAED,2EAA2E;AAC3E,MAAM,UAAU,WAAW,CAAC,KAAa;IACvC,OAAO,KAAK;SACT,OAAO,CAAC,cAAc,EAAE,CAAC,CAAC,EAAE,GAAuB,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;SACvF,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,EAAE,GAAW,EAAE,EAAE,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC,CAAA;AAC3D,CAAC"}
@@ -0,0 +1,75 @@
1
+ import type { Payload } from 'payload';
2
+ import { type FetchLike } from './cimd.js';
3
+ import { type McpClientKind } from './clients.js';
4
+ import { type McpAccessPolicy, type McpOAuthRuntime } from './runtime.js';
5
+ type Doc = Record<string, unknown>;
6
+ export interface ClientRecord {
7
+ id: number | string;
8
+ clientId: string;
9
+ name?: string;
10
+ kind: McpClientKind;
11
+ method: 'dcr' | 'cimd';
12
+ redirectUris: string[];
13
+ }
14
+ /**
15
+ * A registration request's client, registered — or the one already registered
16
+ * with the same name and redirects. Registering is unauthenticated by design
17
+ * (RFC 7591), so identical requests share a row rather than each adding one.
18
+ */
19
+ export declare function registerClient(payload: Payload, policy: McpAccessPolicy, request: {
20
+ redirectUris: unknown[];
21
+ name?: string;
22
+ }): Promise<ClientRecord | {
23
+ error: string;
24
+ }>;
25
+ /**
26
+ * The client a `client_id` names: a registered one, or a metadata document,
27
+ * fetched (and cached for a day) and held to the same allowlist.
28
+ */
29
+ export declare function resolveClient(payload: Payload, policy: McpAccessPolicy, clientId: unknown, fetchImpl?: FetchLike): Promise<ClientRecord | {
30
+ error: string;
31
+ }>;
32
+ export interface PendingRequest {
33
+ clientId: string;
34
+ redirectUri: string;
35
+ codeChallenge: string;
36
+ state?: string;
37
+ }
38
+ /** Consent: a pending grant and the code that completes it. */
39
+ export declare function createPendingGrant(payload: Payload, user: Doc, client: ClientRecord, request: PendingRequest): Promise<string>;
40
+ export interface IssuedTokens {
41
+ access_token: string;
42
+ token_type: 'Bearer';
43
+ expires_in: number;
44
+ refresh_token: string;
45
+ scope: string;
46
+ }
47
+ export type TokenError = {
48
+ error: 'invalid_grant' | 'invalid_request' | 'invalid_target';
49
+ error_description: string;
50
+ };
51
+ /** Ends a grant: its tokens stop working at once. */
52
+ export declare function revokeGrant(payload: Payload, grant: Doc, reason: string): Promise<void>;
53
+ /** `grant_type=authorization_code`. */
54
+ export declare function exchangeCode(payload: Payload, runtime: McpOAuthRuntime, policy: McpAccessPolicy, params: {
55
+ code?: string;
56
+ clientId?: string;
57
+ redirectUri?: string;
58
+ codeVerifier?: string;
59
+ }): Promise<IssuedTokens | TokenError>;
60
+ /** `grant_type=refresh_token`. */
61
+ export declare function refresh(payload: Payload, runtime: McpOAuthRuntime, policy: McpAccessPolicy, params: {
62
+ refreshToken?: string;
63
+ clientId?: string;
64
+ }): Promise<IssuedTokens | TokenError>;
65
+ /** RFC 7009. Unknown tokens are not an error. */
66
+ export declare function revokeToken(payload: Payload, token: unknown): Promise<void>;
67
+ /** An access token's grant and person, if it is live and still allowed. */
68
+ export declare function authenticateAccessToken(payload: Payload, runtime: McpOAuthRuntime, policy: McpAccessPolicy, token: string): Promise<{
69
+ user: Doc;
70
+ grant: Doc;
71
+ } | {
72
+ reason: string;
73
+ }>;
74
+ export {};
75
+ //# sourceMappingURL=store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/mcp-oauth/store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AAEtC,OAAO,EAA8C,KAAK,SAAS,EAAE,MAAM,WAAW,CAAA;AACtF,OAAO,EAAuC,KAAK,aAAa,EAAE,MAAM,cAAc,CAAA;AAEtF,OAAO,EAIL,KAAK,eAAe,EACpB,KAAK,eAAe,EACrB,MAAM,cAAc,CAAA;AAWrB,KAAK,GAAG,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;AAElC,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,GAAG,MAAM,CAAA;IACnB,QAAQ,EAAE,MAAM,CAAA;IAChB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,aAAa,CAAA;IACnB,MAAM,EAAE,KAAK,GAAG,MAAM,CAAA;IACtB,YAAY,EAAE,MAAM,EAAE,CAAA;CACvB;AA2BD;;;;GAIG;AACH,wBAAsB,cAAc,CAClC,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,eAAe,EACvB,OAAO,EAAE;IAAE,YAAY,EAAE,OAAO,EAAE,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,GAClD,OAAO,CAAC,YAAY,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAsC3C;AAED;;;GAGG;AACH,wBAAsB,aAAa,CACjC,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,eAAe,EACvB,QAAQ,EAAE,OAAO,EACjB,SAAS,CAAC,EAAE,SAAS,GACpB,OAAO,CAAC,YAAY,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAwD3C;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,MAAM,CAAA;IAChB,WAAW,EAAE,MAAM,CAAA;IACnB,aAAa,EAAE,MAAM,CAAA;IACrB,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED,+DAA+D;AAC/D,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,GAAG,EACT,MAAM,EAAE,YAAY,EACpB,OAAO,EAAE,cAAc,GACtB,OAAO,CAAC,MAAM,CAAC,CAkCjB;AAED,MAAM,WAAW,YAAY;IAC3B,YAAY,EAAE,MAAM,CAAA;IACpB,UAAU,EAAE,QAAQ,CAAA;IACpB,UAAU,EAAE,MAAM,CAAA;IAClB,aAAa,EAAE,MAAM,CAAA;IACrB,KAAK,EAAE,MAAM,CAAA;CACd;AAED,MAAM,MAAM,UAAU,GAAG;IACvB,KAAK,EAAE,eAAe,GAAG,iBAAiB,GAAG,gBAAgB,CAAA;IAC7D,iBAAiB,EAAE,MAAM,CAAA;CAC1B,CAAA;AAmCD,qDAAqD;AACrD,wBAAsB,WAAW,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAc7F;AAqDD,uCAAuC;AACvC,wBAAsB,YAAY,CAChC,OAAO,EAAE,OAAO,EAChB,OAAO,EAAE,eAAe,EACxB,MAAM,EAAE,eAAe,EACvB,MAAM,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAA;CAAE,GACxF,OAAO,CAAC,YAAY,GAAG,UAAU,CAAC,CA+DpC;AAWD,kCAAkC;AAClC,wBAAsB,OAAO,CAC3B,OAAO,EAAE,OAAO,EAChB,OAAO,EAAE,eAAe,EACxB,MAAM,EAAE,eAAe,EACvB,MAAM,EAAE;IAAE,YAAY,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,GACnD,OAAO,CAAC,YAAY,GAAG,UAAU,CAAC,CAiDpC;AAED,iDAAiD;AACjD,wBAAsB,WAAW,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CASjF;AAED,2EAA2E;AAC3E,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,OAAO,EAChB,OAAO,EAAE,eAAe,EACxB,MAAM,EAAE,eAAe,EACvB,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC;IAAE,IAAI,EAAE,GAAG,CAAC;IAAC,KAAK,EAAE,GAAG,CAAA;CAAE,GAAG;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CA0BzD"}