mcp-twake-mail 0.1.0 → 2.0.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 (86) hide show
  1. package/README.md +168 -74
  2. package/build/cli/commands/setup.js +54 -12
  3. package/build/cli/commands/setup.js.map +1 -1
  4. package/build/cli/prompts/setup-wizard.d.ts +33 -1
  5. package/build/cli/prompts/setup-wizard.js +134 -2
  6. package/build/cli/prompts/setup-wizard.js.map +1 -1
  7. package/build/cli/prompts/setup-wizard.test.d.ts +1 -0
  8. package/build/cli/prompts/setup-wizard.test.js +161 -0
  9. package/build/cli/prompts/setup-wizard.test.js.map +1 -0
  10. package/build/config/schema.d.ts +2 -0
  11. package/build/config/schema.js +3 -0
  12. package/build/config/schema.js.map +1 -1
  13. package/build/discovery/dns-srv.d.ts +18 -0
  14. package/build/discovery/dns-srv.js +60 -0
  15. package/build/discovery/dns-srv.js.map +1 -0
  16. package/build/discovery/dns-srv.test.d.ts +4 -0
  17. package/build/discovery/dns-srv.test.js +79 -0
  18. package/build/discovery/dns-srv.test.js.map +1 -0
  19. package/build/discovery/index.d.ts +9 -0
  20. package/build/discovery/index.js +13 -0
  21. package/build/discovery/index.js.map +1 -0
  22. package/build/discovery/oauth-discovery.d.ts +34 -0
  23. package/build/discovery/oauth-discovery.js +160 -0
  24. package/build/discovery/oauth-discovery.js.map +1 -0
  25. package/build/discovery/oauth-discovery.test.d.ts +1 -0
  26. package/build/discovery/oauth-discovery.test.js +198 -0
  27. package/build/discovery/oauth-discovery.test.js.map +1 -0
  28. package/build/discovery/orchestrator.d.ts +31 -0
  29. package/build/discovery/orchestrator.js +87 -0
  30. package/build/discovery/orchestrator.js.map +1 -0
  31. package/build/discovery/orchestrator.test.d.ts +4 -0
  32. package/build/discovery/orchestrator.test.js +242 -0
  33. package/build/discovery/orchestrator.test.js.map +1 -0
  34. package/build/discovery/types.d.ts +18 -0
  35. package/build/discovery/types.js +15 -0
  36. package/build/discovery/types.js.map +1 -0
  37. package/build/discovery/well-known.d.ts +21 -0
  38. package/build/discovery/well-known.js +52 -0
  39. package/build/discovery/well-known.js.map +1 -0
  40. package/build/discovery/well-known.test.d.ts +4 -0
  41. package/build/discovery/well-known.test.js +120 -0
  42. package/build/discovery/well-known.test.js.map +1 -0
  43. package/build/jmap/client.js +1 -1
  44. package/build/jmap/client.js.map +1 -1
  45. package/build/mcp/server.d.ts +5 -3
  46. package/build/mcp/server.js +11 -5
  47. package/build/mcp/server.js.map +1 -1
  48. package/build/mcp/tools/batch-operations.d.ts +10 -0
  49. package/build/mcp/tools/batch-operations.js +609 -0
  50. package/build/mcp/tools/batch-operations.js.map +1 -0
  51. package/build/mcp/tools/batch-operations.test.d.ts +1 -0
  52. package/build/mcp/tools/batch-operations.test.js +875 -0
  53. package/build/mcp/tools/batch-operations.test.js.map +1 -0
  54. package/build/mcp/tools/email-operations.js +355 -1
  55. package/build/mcp/tools/email-operations.js.map +1 -1
  56. package/build/mcp/tools/email-operations.test.js +5 -3
  57. package/build/mcp/tools/email-operations.test.js.map +1 -1
  58. package/build/mcp/tools/email-sending.d.ts +10 -1
  59. package/build/mcp/tools/email-sending.js +481 -14
  60. package/build/mcp/tools/email-sending.js.map +1 -1
  61. package/build/mcp/tools/index.d.ts +10 -1
  62. package/build/mcp/tools/index.js +7 -3
  63. package/build/mcp/tools/index.js.map +1 -1
  64. package/build/mcp/tools/mailbox.js +433 -1
  65. package/build/mcp/tools/mailbox.js.map +1 -1
  66. package/build/mcp/tools/mailbox.test.js +5 -2
  67. package/build/mcp/tools/mailbox.test.js.map +1 -1
  68. package/build/signature/converter.d.ts +2 -0
  69. package/build/signature/converter.js +23 -0
  70. package/build/signature/converter.js.map +1 -0
  71. package/build/signature/converter.test.d.ts +1 -0
  72. package/build/signature/converter.test.js +84 -0
  73. package/build/signature/converter.test.js.map +1 -0
  74. package/build/signature/index.d.ts +2 -0
  75. package/build/signature/index.js +3 -0
  76. package/build/signature/index.js.map +1 -0
  77. package/build/signature/loader.d.ts +6 -0
  78. package/build/signature/loader.js +31 -0
  79. package/build/signature/loader.js.map +1 -0
  80. package/build/signature/loader.test.d.ts +1 -0
  81. package/build/signature/loader.test.js +85 -0
  82. package/build/signature/loader.test.js.map +1 -0
  83. package/build/types/jmap.d.ts +23 -0
  84. package/docs/auto-discovery.md +210 -0
  85. package/docs/oidc-configuration.md +261 -0
  86. package/package.json +3 -1
@@ -0,0 +1,160 @@
1
+ /**
2
+ * OAuth/OIDC discovery from JMAP resource server
3
+ * Implements RFC 9728 Protected Resource Metadata discovery
4
+ */
5
+ /**
6
+ * Parse WWW-Authenticate header to extract OAuth metadata.
7
+ * Supports Bearer scheme with realm, scope, and issuer parameters.
8
+ *
9
+ * Example header:
10
+ * Bearer realm="example", scope="openid email", issuer="https://auth.example.com"
11
+ *
12
+ * @param header WWW-Authenticate header value
13
+ * @returns Parsed metadata or null if not Bearer auth
14
+ */
15
+ export function parseWwwAuthenticate(header) {
16
+ // Check if header starts with "Bearer " (case-insensitive)
17
+ if (!header.match(/^Bearer\s+/i)) {
18
+ return null;
19
+ }
20
+ // Extract the part after "Bearer "
21
+ const params = header.replace(/^Bearer\s+/i, '');
22
+ // Parse key="value" pairs using regex
23
+ const paramRegex = /(\w+)="([^"]+)"/g;
24
+ const result = {};
25
+ let match;
26
+ while ((match = paramRegex.exec(params)) !== null) {
27
+ const [, key, value] = match;
28
+ if (key === 'issuer' || key === 'realm' || key === 'scope') {
29
+ result[key] = value;
30
+ }
31
+ }
32
+ // Return null if no metadata found
33
+ if (Object.keys(result).length === 0) {
34
+ return null;
35
+ }
36
+ return result;
37
+ }
38
+ /**
39
+ * Try common SSO subdomain patterns for OIDC discovery.
40
+ * Many organizations use predictable subdomain patterns for their SSO.
41
+ *
42
+ * @param domain The base domain to check
43
+ * @param timeout Request timeout in ms
44
+ * @returns OidcDiscoveryResult or null if no valid OIDC endpoint found
45
+ */
46
+ async function tryCommonSsoPatterns(domain, timeout) {
47
+ // Extract base domain (remove subdomain like 'jmap.' or 'mail.')
48
+ const parts = domain.split('.');
49
+ const baseDomain = parts.length > 2 ? parts.slice(-2).join('.') : domain;
50
+ // Common SSO subdomain patterns
51
+ const ssoPatterns = ['sso', 'auth', 'login', 'id', 'accounts'];
52
+ for (const pattern of ssoPatterns) {
53
+ const issuerUrl = `https://${pattern}.${baseDomain}`;
54
+ const discoveryUrl = `${issuerUrl}/.well-known/openid-configuration`;
55
+ try {
56
+ const controller = new AbortController();
57
+ const timeoutId = setTimeout(() => controller.abort(), timeout);
58
+ const response = await fetch(discoveryUrl, {
59
+ signal: controller.signal,
60
+ });
61
+ clearTimeout(timeoutId);
62
+ if (response.ok) {
63
+ const config = await response.json();
64
+ // Verify it's a valid OIDC config with issuer field
65
+ if (config.issuer) {
66
+ return {
67
+ issuer: config.issuer,
68
+ method: 'well-known-oidc',
69
+ };
70
+ }
71
+ }
72
+ }
73
+ catch {
74
+ // This pattern didn't work, try next
75
+ }
76
+ }
77
+ return null;
78
+ }
79
+ /**
80
+ * Discover OAuth authorization server from JMAP resource URL.
81
+ * Implements RFC 9728 Protected Resource Metadata discovery with fallbacks.
82
+ *
83
+ * Discovery order:
84
+ * 1. Try /.well-known/oauth-protected-resource at resource origin
85
+ * 2. If that fails, try fetching JMAP URL to trigger 401 with WWW-Authenticate
86
+ * 3. If that fails, try common SSO subdomain patterns (sso., auth., login.)
87
+ *
88
+ * @param jmapUrl The JMAP session or API URL
89
+ * @param timeout Request timeout in ms (default 10000)
90
+ * @returns OidcDiscoveryResult or null if no OAuth info found
91
+ */
92
+ export async function discoverOAuthFromResource(jmapUrl, timeout = 10000) {
93
+ try {
94
+ // Parse jmapUrl to get origin
95
+ const url = new URL(jmapUrl);
96
+ const origin = `${url.protocol}//${url.host}`;
97
+ // Try RFC 9728 Protected Resource Metadata first
98
+ const metadataUrl = `${origin}/.well-known/oauth-protected-resource`;
99
+ try {
100
+ const controller = new AbortController();
101
+ const timeoutId = setTimeout(() => controller.abort(), timeout);
102
+ const metadataResponse = await fetch(metadataUrl, {
103
+ signal: controller.signal,
104
+ });
105
+ clearTimeout(timeoutId);
106
+ if (metadataResponse.ok) {
107
+ const metadata = await metadataResponse.json();
108
+ // Extract issuer from authorization_servers array
109
+ if (metadata.authorization_servers &&
110
+ Array.isArray(metadata.authorization_servers) &&
111
+ metadata.authorization_servers.length > 0) {
112
+ return {
113
+ issuer: metadata.authorization_servers[0],
114
+ method: 'protected-resource',
115
+ };
116
+ }
117
+ }
118
+ }
119
+ catch {
120
+ // Protected resource metadata failed, try WWW-Authenticate fallback
121
+ }
122
+ // Fallback 1: Try fetching JMAP URL directly to get 401 with WWW-Authenticate
123
+ try {
124
+ const controller = new AbortController();
125
+ const timeoutId = setTimeout(() => controller.abort(), timeout);
126
+ const jmapResponse = await fetch(jmapUrl, {
127
+ signal: controller.signal,
128
+ });
129
+ clearTimeout(timeoutId);
130
+ // Check for 401 response with WWW-Authenticate header
131
+ if (jmapResponse.status === 401) {
132
+ const wwwAuth = jmapResponse.headers.get('WWW-Authenticate');
133
+ if (wwwAuth) {
134
+ const parsed = parseWwwAuthenticate(wwwAuth);
135
+ if (parsed?.issuer) {
136
+ return {
137
+ issuer: parsed.issuer,
138
+ method: 'www-authenticate',
139
+ };
140
+ }
141
+ }
142
+ }
143
+ }
144
+ catch {
145
+ // WWW-Authenticate fallback also failed
146
+ }
147
+ // Fallback 2: Try common SSO subdomain patterns
148
+ const ssoResult = await tryCommonSsoPatterns(url.hostname, timeout);
149
+ if (ssoResult) {
150
+ return ssoResult;
151
+ }
152
+ // No OAuth info found
153
+ return null;
154
+ }
155
+ catch {
156
+ // URL parsing or other error
157
+ return null;
158
+ }
159
+ }
160
+ //# sourceMappingURL=oauth-discovery.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauth-discovery.js","sourceRoot":"","sources":["../../src/discovery/oauth-discovery.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,198 @@
1
+ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
2
+ import { parseWwwAuthenticate, discoverOAuthFromResource, } from './oauth-discovery.js';
3
+ describe('parseWwwAuthenticate', () => {
4
+ it('should parse valid Bearer header with issuer', () => {
5
+ const header = 'Bearer issuer="https://auth.example.com"';
6
+ const result = parseWwwAuthenticate(header);
7
+ expect(result).toEqual({
8
+ issuer: 'https://auth.example.com',
9
+ });
10
+ });
11
+ it('should parse Bearer header with multiple parameters', () => {
12
+ const header = 'Bearer realm="example", scope="openid email", issuer="https://auth.example.com"';
13
+ const result = parseWwwAuthenticate(header);
14
+ expect(result).toEqual({
15
+ realm: 'example',
16
+ scope: 'openid email',
17
+ issuer: 'https://auth.example.com',
18
+ });
19
+ });
20
+ it('should be case-insensitive for Bearer scheme', () => {
21
+ const header = 'bearer issuer="https://auth.example.com"';
22
+ const result = parseWwwAuthenticate(header);
23
+ expect(result).toEqual({
24
+ issuer: 'https://auth.example.com',
25
+ });
26
+ });
27
+ it('should return null for non-Bearer header', () => {
28
+ const header = 'Basic realm="example"';
29
+ const result = parseWwwAuthenticate(header);
30
+ expect(result).toBeNull();
31
+ });
32
+ it('should return null for malformed header without parameters', () => {
33
+ const header = 'Bearer';
34
+ const result = parseWwwAuthenticate(header);
35
+ expect(result).toBeNull();
36
+ });
37
+ it('should ignore unknown parameters', () => {
38
+ const header = 'Bearer issuer="https://auth.example.com", unknown="value"';
39
+ const result = parseWwwAuthenticate(header);
40
+ expect(result).toEqual({
41
+ issuer: 'https://auth.example.com',
42
+ });
43
+ });
44
+ });
45
+ describe('discoverOAuthFromResource', () => {
46
+ const mockFetch = vi.fn();
47
+ beforeEach(() => {
48
+ global.fetch = mockFetch;
49
+ vi.clearAllMocks();
50
+ });
51
+ afterEach(() => {
52
+ vi.restoreAllMocks();
53
+ });
54
+ it('should discover issuer from protected-resource metadata', async () => {
55
+ mockFetch.mockResolvedValueOnce({
56
+ ok: true,
57
+ json: async () => ({
58
+ authorization_servers: ['https://auth.example.com'],
59
+ }),
60
+ });
61
+ const result = await discoverOAuthFromResource('https://jmap.example.com/api');
62
+ expect(result).toEqual({
63
+ issuer: 'https://auth.example.com',
64
+ method: 'protected-resource',
65
+ });
66
+ expect(mockFetch).toHaveBeenCalledWith('https://jmap.example.com/.well-known/oauth-protected-resource', expect.objectContaining({ signal: expect.any(AbortSignal) }));
67
+ });
68
+ it('should fallback to WWW-Authenticate on 401 response', async () => {
69
+ // First call: protected-resource fails
70
+ mockFetch.mockResolvedValueOnce({
71
+ ok: false,
72
+ status: 404,
73
+ });
74
+ // Second call: JMAP URL returns 401 with WWW-Authenticate
75
+ mockFetch.mockResolvedValueOnce({
76
+ ok: false,
77
+ status: 401,
78
+ headers: {
79
+ get: (name) => {
80
+ if (name === 'WWW-Authenticate') {
81
+ return 'Bearer issuer="https://auth.example.com"';
82
+ }
83
+ return null;
84
+ },
85
+ },
86
+ });
87
+ const result = await discoverOAuthFromResource('https://jmap.example.com/api');
88
+ expect(result).toEqual({
89
+ issuer: 'https://auth.example.com',
90
+ method: 'www-authenticate',
91
+ });
92
+ expect(mockFetch).toHaveBeenCalledTimes(2);
93
+ });
94
+ it('should return null when no OAuth info available', async () => {
95
+ // First call: protected-resource fails
96
+ mockFetch.mockResolvedValueOnce({
97
+ ok: false,
98
+ status: 404,
99
+ });
100
+ // Second call: JMAP URL returns 200 (no auth required)
101
+ mockFetch.mockResolvedValueOnce({
102
+ ok: true,
103
+ status: 200,
104
+ });
105
+ const result = await discoverOAuthFromResource('https://jmap.example.com/api');
106
+ expect(result).toBeNull();
107
+ });
108
+ it('should return null when WWW-Authenticate has no issuer', async () => {
109
+ // First call: protected-resource fails
110
+ mockFetch.mockResolvedValueOnce({
111
+ ok: false,
112
+ status: 404,
113
+ });
114
+ // Second call: JMAP URL returns 401 with WWW-Authenticate but no issuer
115
+ mockFetch.mockResolvedValueOnce({
116
+ ok: false,
117
+ status: 401,
118
+ headers: {
119
+ get: (name) => {
120
+ if (name === 'WWW-Authenticate') {
121
+ return 'Bearer realm="example"';
122
+ }
123
+ return null;
124
+ },
125
+ },
126
+ });
127
+ const result = await discoverOAuthFromResource('https://jmap.example.com/api');
128
+ expect(result).toBeNull();
129
+ });
130
+ it('should handle network errors gracefully', async () => {
131
+ mockFetch.mockRejectedValue(new Error('Network error'));
132
+ const result = await discoverOAuthFromResource('https://jmap.example.com/api');
133
+ expect(result).toBeNull();
134
+ });
135
+ it('should handle invalid URL gracefully', async () => {
136
+ const result = await discoverOAuthFromResource('not-a-url');
137
+ expect(result).toBeNull();
138
+ expect(mockFetch).not.toHaveBeenCalled();
139
+ });
140
+ it('should respect custom timeout', async () => {
141
+ const abortError = new Error('Aborted');
142
+ abortError.name = 'AbortError';
143
+ mockFetch.mockRejectedValue(abortError);
144
+ const result = await discoverOAuthFromResource('https://jmap.example.com/api', 100);
145
+ expect(result).toBeNull();
146
+ });
147
+ it('should handle protected-resource with empty authorization_servers', async () => {
148
+ mockFetch.mockResolvedValueOnce({
149
+ ok: true,
150
+ json: async () => ({
151
+ authorization_servers: [],
152
+ }),
153
+ });
154
+ // Second call: fallback to WWW-Authenticate
155
+ mockFetch.mockResolvedValueOnce({
156
+ ok: false,
157
+ status: 401,
158
+ headers: {
159
+ get: (name) => {
160
+ if (name === 'WWW-Authenticate') {
161
+ return 'Bearer issuer="https://auth.example.com"';
162
+ }
163
+ return null;
164
+ },
165
+ },
166
+ });
167
+ const result = await discoverOAuthFromResource('https://jmap.example.com/api');
168
+ expect(result).toEqual({
169
+ issuer: 'https://auth.example.com',
170
+ method: 'www-authenticate',
171
+ });
172
+ });
173
+ it('should handle protected-resource with missing authorization_servers', async () => {
174
+ mockFetch.mockResolvedValueOnce({
175
+ ok: true,
176
+ json: async () => ({}),
177
+ });
178
+ // Second call: fallback to WWW-Authenticate
179
+ mockFetch.mockResolvedValueOnce({
180
+ ok: false,
181
+ status: 401,
182
+ headers: {
183
+ get: (name) => {
184
+ if (name === 'WWW-Authenticate') {
185
+ return 'Bearer issuer="https://auth.example.com"';
186
+ }
187
+ return null;
188
+ },
189
+ },
190
+ });
191
+ const result = await discoverOAuthFromResource('https://jmap.example.com/api');
192
+ expect(result).toEqual({
193
+ issuer: 'https://auth.example.com',
194
+ method: 'www-authenticate',
195
+ });
196
+ });
197
+ });
198
+ //# sourceMappingURL=oauth-discovery.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauth-discovery.test.js","sourceRoot":"","sources":["../../src/discovery/oauth-discovery.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Discovery orchestrator - chains DNS SRV, well-known, and OAuth discovery
3
+ * Provides high-level API to discover JMAP and OIDC settings from email address
4
+ */
5
+ import { JmapDiscoveryResult, OidcDiscoveryResult } from './types.js';
6
+ export interface FullDiscoveryResult {
7
+ jmap: JmapDiscoveryResult;
8
+ oidc?: OidcDiscoveryResult;
9
+ email: string;
10
+ domain: string;
11
+ }
12
+ /**
13
+ * Extract domain from email address.
14
+ * @throws Error if email format invalid
15
+ */
16
+ export declare function extractDomain(email: string): string;
17
+ /**
18
+ * Discover JMAP and OIDC settings from an email address.
19
+ *
20
+ * Discovery stages:
21
+ * 1. Extract domain from email
22
+ * 2. Try DNS SRV lookup for _jmap._tcp.{domain}
23
+ * 3. If SRV found, construct URL and verify it works
24
+ * 4. If SRV fails, try .well-known/jmap on domain
25
+ * 5. If JMAP found, attempt OAuth discovery on that URL
26
+ *
27
+ * @param email User's email address (e.g., "user@example.com")
28
+ * @returns Full discovery result with JMAP and optional OIDC settings
29
+ * @throws DiscoveryError if JMAP server cannot be discovered
30
+ */
31
+ export declare function discoverFromEmail(email: string): Promise<FullDiscoveryResult>;
@@ -0,0 +1,87 @@
1
+ /**
2
+ * Discovery orchestrator - chains DNS SRV, well-known, and OAuth discovery
3
+ * Provides high-level API to discover JMAP and OIDC settings from email address
4
+ */
5
+ import { resolveSrvRecord } from './dns-srv.js';
6
+ import { fetchWellKnownJmap, verifyJmapUrl } from './well-known.js';
7
+ import { discoverOAuthFromResource } from './oauth-discovery.js';
8
+ import { DiscoveryError, } from './types.js';
9
+ /**
10
+ * Extract domain from email address.
11
+ * @throws Error if email format invalid
12
+ */
13
+ export function extractDomain(email) {
14
+ // Split on '@' and take second part
15
+ const parts = email.split('@');
16
+ if (parts.length !== 2) {
17
+ throw new Error('Invalid email format');
18
+ }
19
+ const domain = parts[1];
20
+ // Validate domain has at least one '.'
21
+ if (!domain.includes('.')) {
22
+ throw new Error('Invalid email format');
23
+ }
24
+ return domain;
25
+ }
26
+ /**
27
+ * Discover JMAP and OIDC settings from an email address.
28
+ *
29
+ * Discovery stages:
30
+ * 1. Extract domain from email
31
+ * 2. Try DNS SRV lookup for _jmap._tcp.{domain}
32
+ * 3. If SRV found, construct URL and verify it works
33
+ * 4. If SRV fails, try .well-known/jmap on domain
34
+ * 5. If JMAP found, attempt OAuth discovery on that URL
35
+ *
36
+ * @param email User's email address (e.g., "user@example.com")
37
+ * @returns Full discovery result with JMAP and optional OIDC settings
38
+ * @throws DiscoveryError if JMAP server cannot be discovered
39
+ */
40
+ export async function discoverFromEmail(email) {
41
+ // Extract domain from email
42
+ const domain = extractDomain(email);
43
+ // Stage 1 - DNS SRV discovery
44
+ const srv = await resolveSrvRecord(domain);
45
+ if (srv) {
46
+ // Construct URL from SRV record
47
+ const url = srv.port === 443
48
+ ? `https://${srv.hostname}/.well-known/jmap`
49
+ : `https://${srv.hostname}:${srv.port}/.well-known/jmap`;
50
+ // Verify the URL works
51
+ const verified = await verifyJmapUrl(url);
52
+ if (verified) {
53
+ const jmap = {
54
+ sessionUrl: verified,
55
+ method: 'dns-srv',
56
+ };
57
+ // Stage 4 - OAuth discovery
58
+ const oidc = await discoverOAuthFromResource(verified);
59
+ return {
60
+ jmap,
61
+ oidc: oidc ?? undefined,
62
+ email,
63
+ domain,
64
+ };
65
+ }
66
+ }
67
+ // Stage 2 - Well-known fallback
68
+ const wellKnown = await fetchWellKnownJmap(domain);
69
+ if (wellKnown) {
70
+ const jmap = {
71
+ sessionUrl: wellKnown,
72
+ method: 'well-known-direct',
73
+ };
74
+ // Stage 4 - OAuth discovery
75
+ const oidc = await discoverOAuthFromResource(wellKnown);
76
+ return {
77
+ jmap,
78
+ oidc: oidc ?? undefined,
79
+ email,
80
+ domain,
81
+ };
82
+ }
83
+ // Stage 3 - Failure
84
+ throw new DiscoveryError(`Could not discover JMAP server for domain "${domain}". ` +
85
+ 'The domain does not have a JMAP SRV record or .well-known/jmap endpoint.', domain, 'well-known');
86
+ }
87
+ //# sourceMappingURL=orchestrator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"orchestrator.js","sourceRoot":"","sources":["../../src/discovery/orchestrator.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AACpE,OAAO,EAAE,yBAAyB,EAAE,MAAM,sBAAsB,CAAC;AACjE,OAAO,EAGL,cAAc,GACf,MAAM,YAAY,CAAC;AASpB;;;GAGG;AACH,MAAM,UAAU,aAAa,CAAC,KAAa;IACzC,oCAAoC;IACpC,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC/B,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CAAC,sBAAsB,CAAC,CAAC;IAC1C,CAAC;IAED,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;IAExB,uCAAuC;IACvC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QAC1B,MAAM,IAAI,KAAK,CAAC,sBAAsB,CAAC,CAAC;IAC1C,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,KAAa;IAEb,4BAA4B;IAC5B,MAAM,MAAM,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;IAEpC,8BAA8B;IAC9B,MAAM,GAAG,GAAG,MAAM,gBAAgB,CAAC,MAAM,CAAC,CAAC;IAC3C,IAAI,GAAG,EAAE,CAAC;QACR,gCAAgC;QAChC,MAAM,GAAG,GACP,GAAG,CAAC,IAAI,KAAK,GAAG;YACd,CAAC,CAAC,WAAW,GAAG,CAAC,QAAQ,mBAAmB;YAC5C,CAAC,CAAC,WAAW,GAAG,CAAC,QAAQ,IAAI,GAAG,CAAC,IAAI,mBAAmB,CAAC;QAE7D,uBAAuB;QACvB,MAAM,QAAQ,GAAG,MAAM,aAAa,CAAC,GAAG,CAAC,CAAC;QAC1C,IAAI,QAAQ,EAAE,CAAC;YACb,MAAM,IAAI,GAAwB;gBAChC,UAAU,EAAE,QAAQ;gBACpB,MAAM,EAAE,SAAS;aAClB,CAAC;YAEF,4BAA4B;YAC5B,MAAM,IAAI,GAAG,MAAM,yBAAyB,CAAC,QAAQ,CAAC,CAAC;YAEvD,OAAO;gBACL,IAAI;gBACJ,IAAI,EAAE,IAAI,IAAI,SAAS;gBACvB,KAAK;gBACL,MAAM;aACP,CAAC;QACJ,CAAC;IACH,CAAC;IAED,gCAAgC;IAChC,MAAM,SAAS,GAAG,MAAM,kBAAkB,CAAC,MAAM,CAAC,CAAC;IACnD,IAAI,SAAS,EAAE,CAAC;QACd,MAAM,IAAI,GAAwB;YAChC,UAAU,EAAE,SAAS;YACrB,MAAM,EAAE,mBAAmB;SAC5B,CAAC;QAEF,4BAA4B;QAC5B,MAAM,IAAI,GAAG,MAAM,yBAAyB,CAAC,SAAS,CAAC,CAAC;QAExD,OAAO;YACL,IAAI;YACJ,IAAI,EAAE,IAAI,IAAI,SAAS;YACvB,KAAK;YACL,MAAM;SACP,CAAC;IACJ,CAAC;IAED,oBAAoB;IACpB,MAAM,IAAI,cAAc,CACtB,8CAA8C,MAAM,KAAK;QACvD,0EAA0E,EAC5E,MAAM,EACN,YAAY,CACb,CAAC;AACJ,CAAC"}
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Tests for discovery orchestrator
3
+ */
4
+ export {};