mcp-twake-mail 0.1.0 → 1.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 (69) hide show
  1. package/README.md +77 -48
  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/mcp/server.d.ts +5 -3
  44. package/build/mcp/server.js +11 -5
  45. package/build/mcp/server.js.map +1 -1
  46. package/build/mcp/tools/email-sending.d.ts +10 -1
  47. package/build/mcp/tools/email-sending.js +60 -15
  48. package/build/mcp/tools/email-sending.js.map +1 -1
  49. package/build/mcp/tools/index.d.ts +10 -1
  50. package/build/mcp/tools/index.js +4 -3
  51. package/build/mcp/tools/index.js.map +1 -1
  52. package/build/signature/converter.d.ts +2 -0
  53. package/build/signature/converter.js +23 -0
  54. package/build/signature/converter.js.map +1 -0
  55. package/build/signature/converter.test.d.ts +1 -0
  56. package/build/signature/converter.test.js +84 -0
  57. package/build/signature/converter.test.js.map +1 -0
  58. package/build/signature/index.d.ts +2 -0
  59. package/build/signature/index.js +3 -0
  60. package/build/signature/index.js.map +1 -0
  61. package/build/signature/loader.d.ts +6 -0
  62. package/build/signature/loader.js +31 -0
  63. package/build/signature/loader.js.map +1 -0
  64. package/build/signature/loader.test.d.ts +1 -0
  65. package/build/signature/loader.test.js +85 -0
  66. package/build/signature/loader.test.js.map +1 -0
  67. package/docs/auto-discovery.md +210 -0
  68. package/docs/oidc-configuration.md +261 -0
  69. package/package.json +3 -1
@@ -0,0 +1,161 @@
1
+ /**
2
+ * Tests for setup wizard prompts.
3
+ */
4
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
5
+ import { confirm, input } from '@inquirer/prompts';
6
+ import { access } from 'node:fs/promises';
7
+ import { promptDefaultFrom, promptSignaturePath } from './setup-wizard.js';
8
+ // Mock @inquirer/prompts
9
+ vi.mock('@inquirer/prompts', () => ({
10
+ confirm: vi.fn(),
11
+ input: vi.fn(),
12
+ select: vi.fn(),
13
+ password: vi.fn(),
14
+ }));
15
+ // Mock node:fs/promises
16
+ vi.mock('node:fs/promises', () => ({
17
+ access: vi.fn(),
18
+ constants: {
19
+ R_OK: 4,
20
+ },
21
+ }));
22
+ // Mock node:os
23
+ vi.mock('node:os', () => ({
24
+ homedir: vi.fn(() => '/home/testuser'),
25
+ }));
26
+ describe('promptDefaultFrom', () => {
27
+ beforeEach(() => {
28
+ vi.clearAllMocks();
29
+ });
30
+ it('returns undefined when user declines to configure', async () => {
31
+ vi.mocked(confirm).mockResolvedValueOnce(false);
32
+ const result = await promptDefaultFrom();
33
+ expect(result).toBeUndefined();
34
+ expect(confirm).toHaveBeenCalledWith({
35
+ message: 'Configure default sender email address?',
36
+ default: true,
37
+ });
38
+ expect(input).not.toHaveBeenCalled();
39
+ });
40
+ it('returns email when user configures and provides valid email', async () => {
41
+ vi.mocked(confirm).mockResolvedValueOnce(true);
42
+ vi.mocked(input).mockResolvedValueOnce('user@example.com');
43
+ const result = await promptDefaultFrom();
44
+ expect(result).toBe('user@example.com');
45
+ expect(confirm).toHaveBeenCalledOnce();
46
+ expect(input).toHaveBeenCalledWith({
47
+ message: 'Default "from" email address:',
48
+ validate: expect.any(Function),
49
+ });
50
+ });
51
+ it('validates email format - rejects invalid emails', async () => {
52
+ vi.mocked(confirm).mockResolvedValueOnce(true);
53
+ // Mock input to get the validate function
54
+ let validateFn;
55
+ vi.mocked(input).mockImplementationOnce((options) => {
56
+ validateFn = options.validate;
57
+ return Promise.resolve('valid@example.com');
58
+ });
59
+ await promptDefaultFrom();
60
+ expect(validateFn).toBeDefined();
61
+ if (validateFn) {
62
+ // Test invalid emails
63
+ expect(validateFn('invalid')).toBe('Please enter a valid email address');
64
+ expect(validateFn('invalid@')).toBe('Please enter a valid email address');
65
+ expect(validateFn('@example.com')).toBe('Please enter a valid email address');
66
+ expect(validateFn('user@example')).toBe('Please enter a valid email address');
67
+ // Test valid emails
68
+ expect(validateFn('user@example.com')).toBe(true);
69
+ expect(validateFn('test.user+tag@sub.example.org')).toBe(true);
70
+ }
71
+ });
72
+ });
73
+ describe('promptSignaturePath', () => {
74
+ beforeEach(() => {
75
+ vi.clearAllMocks();
76
+ });
77
+ it('returns undefined when user declines to configure', async () => {
78
+ vi.mocked(confirm).mockResolvedValueOnce(false);
79
+ const result = await promptSignaturePath();
80
+ expect(result).toBeUndefined();
81
+ expect(confirm).toHaveBeenCalledWith({
82
+ message: 'Configure email signature file?',
83
+ default: false,
84
+ });
85
+ expect(input).not.toHaveBeenCalled();
86
+ });
87
+ it('returns path when user configures and file exists', async () => {
88
+ vi.mocked(confirm).mockResolvedValueOnce(true);
89
+ vi.mocked(access).mockResolvedValueOnce(undefined);
90
+ vi.mocked(input).mockResolvedValueOnce('/path/to/signature.md');
91
+ const result = await promptSignaturePath();
92
+ expect(result).toBe('/path/to/signature.md');
93
+ expect(confirm).toHaveBeenCalledOnce();
94
+ expect(input).toHaveBeenCalledWith({
95
+ message: 'Path to signature file (Markdown format):',
96
+ default: '~/.mcp-twake-mail/signature.md',
97
+ validate: expect.any(Function),
98
+ });
99
+ });
100
+ it('validates file exists - shows error for missing file', async () => {
101
+ vi.mocked(confirm).mockResolvedValueOnce(true);
102
+ // Mock input to capture the validate function and test it separately
103
+ let validateFn;
104
+ vi.mocked(input).mockImplementationOnce((options) => {
105
+ validateFn = options.validate;
106
+ return Promise.resolve('/valid/path.md');
107
+ });
108
+ await promptSignaturePath();
109
+ expect(validateFn).toBeDefined();
110
+ if (validateFn) {
111
+ // Set up mock to reject for missing file
112
+ vi.mocked(access).mockReset();
113
+ vi.mocked(access).mockRejectedValue(new Error('ENOENT'));
114
+ // Test missing file
115
+ const result = await validateFn('/missing/file.md');
116
+ expect(typeof result).toBe('string');
117
+ expect(result).toMatch(/File not found or not readable/);
118
+ expect(access).toHaveBeenCalledTimes(1);
119
+ expect(access).toHaveBeenCalledWith('/missing/file.md', 4); // R_OK = 4
120
+ }
121
+ });
122
+ it('validates empty path is rejected', async () => {
123
+ vi.mocked(confirm).mockResolvedValueOnce(true);
124
+ // Mock input to get the validate function
125
+ let validateFn;
126
+ vi.mocked(input).mockImplementationOnce((options) => {
127
+ validateFn = options.validate;
128
+ return Promise.resolve('valid.md');
129
+ });
130
+ await promptSignaturePath();
131
+ expect(validateFn).toBeDefined();
132
+ if (validateFn) {
133
+ const result = await validateFn('');
134
+ expect(result).toBe('Signature path cannot be empty');
135
+ expect(access).not.toHaveBeenCalled();
136
+ }
137
+ });
138
+ it('expands ~ in path for validation', async () => {
139
+ vi.mocked(confirm).mockResolvedValueOnce(true);
140
+ // Mock input to capture the validate function
141
+ let validateFn;
142
+ vi.mocked(input).mockImplementationOnce((options) => {
143
+ validateFn = options.validate;
144
+ return Promise.resolve('~/signature.md');
145
+ });
146
+ await promptSignaturePath();
147
+ expect(validateFn).toBeDefined();
148
+ if (validateFn) {
149
+ // Clear previous calls and set up mock for the validation call
150
+ vi.mocked(access).mockClear();
151
+ vi.mocked(access).mockResolvedValueOnce(undefined);
152
+ await validateFn('~/signature.md');
153
+ // Verify access was called with expanded path (not with ~)
154
+ expect(access).toHaveBeenCalledTimes(1);
155
+ const calledPath = vi.mocked(access).mock.calls[0][0];
156
+ expect(calledPath).not.toContain('~');
157
+ expect(calledPath).toMatch(/^\/.*\/signature\.md$/);
158
+ }
159
+ });
160
+ });
161
+ //# sourceMappingURL=setup-wizard.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"setup-wizard.test.js","sourceRoot":"","sources":["../../../src/cli/prompts/setup-wizard.test.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAC9D,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC1C,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAE3E,yBAAyB;AACzB,EAAE,CAAC,IAAI,CAAC,mBAAmB,EAAE,GAAG,EAAE,CAAC,CAAC;IAClC,OAAO,EAAE,EAAE,CAAC,EAAE,EAAE;IAChB,KAAK,EAAE,EAAE,CAAC,EAAE,EAAE;IACd,MAAM,EAAE,EAAE,CAAC,EAAE,EAAE;IACf,QAAQ,EAAE,EAAE,CAAC,EAAE,EAAE;CAClB,CAAC,CAAC,CAAC;AAEJ,wBAAwB;AACxB,EAAE,CAAC,IAAI,CAAC,kBAAkB,EAAE,GAAG,EAAE,CAAC,CAAC;IACjC,MAAM,EAAE,EAAE,CAAC,EAAE,EAAE;IACf,SAAS,EAAE;QACT,IAAI,EAAE,CAAC;KACR;CACF,CAAC,CAAC,CAAC;AAEJ,eAAe;AACf,EAAE,CAAC,IAAI,CAAC,SAAS,EAAE,GAAG,EAAE,CAAC,CAAC;IACxB,OAAO,EAAE,EAAE,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,gBAAgB,CAAC;CACvC,CAAC,CAAC,CAAC;AAEJ,QAAQ,CAAC,mBAAmB,EAAE,GAAG,EAAE;IACjC,UAAU,CAAC,GAAG,EAAE;QACd,EAAE,CAAC,aAAa,EAAE,CAAC;IACrB,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mDAAmD,EAAE,KAAK,IAAI,EAAE;QACjE,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,qBAAqB,CAAC,KAAK,CAAC,CAAC;QAEhD,MAAM,MAAM,GAAG,MAAM,iBAAiB,EAAE,CAAC;QAEzC,MAAM,CAAC,MAAM,CAAC,CAAC,aAAa,EAAE,CAAC;QAC/B,MAAM,CAAC,OAAO,CAAC,CAAC,oBAAoB,CAAC;YACnC,OAAO,EAAE,yCAAyC;YAClD,OAAO,EAAE,IAAI;SACd,CAAC,CAAC;QACH,MAAM,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;IACvC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6DAA6D,EAAE,KAAK,IAAI,EAAE;QAC3E,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,qBAAqB,CAAC,IAAI,CAAC,CAAC;QAC/C,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,qBAAqB,CAAC,kBAAkB,CAAC,CAAC;QAE3D,MAAM,MAAM,GAAG,MAAM,iBAAiB,EAAE,CAAC;QAEzC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC;QACxC,MAAM,CAAC,OAAO,CAAC,CAAC,oBAAoB,EAAE,CAAC;QACvC,MAAM,CAAC,KAAK,CAAC,CAAC,oBAAoB,CAAC;YACjC,OAAO,EAAE,+BAA+B;YACxC,QAAQ,EAAE,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;SAC/B,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iDAAiD,EAAE,KAAK,IAAI,EAAE;QAC/D,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,qBAAqB,CAAC,IAAI,CAAC,CAAC;QAE/C,0CAA0C;QAC1C,IAAI,UAA6D,CAAC;QAClE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,sBAAsB,CAAC,CAAC,OAAO,EAAE,EAAE;YAClD,UAAU,GAAG,OAAO,CAAC,QAA+C,CAAC;YACrE,OAAO,OAAO,CAAC,OAAO,CAAC,mBAAmB,CAAC,CAAC;QAC9C,CAAC,CAAC,CAAC;QAEH,MAAM,iBAAiB,EAAE,CAAC;QAE1B,MAAM,CAAC,UAAU,CAAC,CAAC,WAAW,EAAE,CAAC;QACjC,IAAI,UAAU,EAAE,CAAC;YACf,sBAAsB;YACtB,MAAM,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,oCAAoC,CAAC,CAAC;YACzE,MAAM,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,oCAAoC,CAAC,CAAC;YAC1E,MAAM,CAAC,UAAU,CAAC,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,oCAAoC,CAAC,CAAC;YAC9E,MAAM,CAAC,UAAU,CAAC,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,oCAAoC,CAAC,CAAC;YAE9E,oBAAoB;YACpB,MAAM,CAAC,UAAU,CAAC,kBAAkB,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAClD,MAAM,CAAC,UAAU,CAAC,+BAA+B,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACjE,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,qBAAqB,EAAE,GAAG,EAAE;IACnC,UAAU,CAAC,GAAG,EAAE;QACd,EAAE,CAAC,aAAa,EAAE,CAAC;IACrB,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mDAAmD,EAAE,KAAK,IAAI,EAAE;QACjE,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,qBAAqB,CAAC,KAAK,CAAC,CAAC;QAEhD,MAAM,MAAM,GAAG,MAAM,mBAAmB,EAAE,CAAC;QAE3C,MAAM,CAAC,MAAM,CAAC,CAAC,aAAa,EAAE,CAAC;QAC/B,MAAM,CAAC,OAAO,CAAC,CAAC,oBAAoB,CAAC;YACnC,OAAO,EAAE,iCAAiC;YAC1C,OAAO,EAAE,KAAK;SACf,CAAC,CAAC;QACH,MAAM,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;IACvC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mDAAmD,EAAE,KAAK,IAAI,EAAE;QACjE,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,qBAAqB,CAAC,IAAI,CAAC,CAAC;QAC/C,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,qBAAqB,CAAC,SAAS,CAAC,CAAC;QACnD,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,qBAAqB,CAAC,uBAAuB,CAAC,CAAC;QAEhE,MAAM,MAAM,GAAG,MAAM,mBAAmB,EAAE,CAAC;QAE3C,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAC;QAC7C,MAAM,CAAC,OAAO,CAAC,CAAC,oBAAoB,EAAE,CAAC;QACvC,MAAM,CAAC,KAAK,CAAC,CAAC,oBAAoB,CAAC;YACjC,OAAO,EAAE,2CAA2C;YACpD,OAAO,EAAE,gCAAgC;YACzC,QAAQ,EAAE,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;SAC/B,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sDAAsD,EAAE,KAAK,IAAI,EAAE;QACpE,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,qBAAqB,CAAC,IAAI,CAAC,CAAC;QAE/C,qEAAqE;QACrE,IAAI,UAAsE,CAAC;QAC3E,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,sBAAsB,CAAC,CAAC,OAAO,EAAE,EAAE;YAClD,UAAU,GAAG,OAAO,CAAC,QAAwD,CAAC;YAC9E,OAAO,OAAO,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC;QAC3C,CAAC,CAAC,CAAC;QAEH,MAAM,mBAAmB,EAAE,CAAC;QAE5B,MAAM,CAAC,UAAU,CAAC,CAAC,WAAW,EAAE,CAAC;QACjC,IAAI,UAAU,EAAE,CAAC;YACf,yCAAyC;YACzC,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,EAAE,CAAC;YAC9B,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,iBAAiB,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC;YAEzD,oBAAoB;YACpB,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,kBAAkB,CAAC,CAAC;YACpD,MAAM,CAAC,OAAO,MAAM,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YACrC,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,gCAAgC,CAAC,CAAC;YACzD,MAAM,CAAC,MAAM,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC;YACxC,MAAM,CAAC,MAAM,CAAC,CAAC,oBAAoB,CAAC,kBAAkB,EAAE,CAAC,CAAC,CAAC,CAAC,WAAW;QACzE,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kCAAkC,EAAE,KAAK,IAAI,EAAE;QAChD,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,qBAAqB,CAAC,IAAI,CAAC,CAAC;QAE/C,0CAA0C;QAC1C,IAAI,UAAsE,CAAC;QAC3E,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,sBAAsB,CAAC,CAAC,OAAO,EAAE,EAAE;YAClD,UAAU,GAAG,OAAO,CAAC,QAAwD,CAAC;YAC9E,OAAO,OAAO,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;QACrC,CAAC,CAAC,CAAC;QAEH,MAAM,mBAAmB,EAAE,CAAC;QAE5B,MAAM,CAAC,UAAU,CAAC,CAAC,WAAW,EAAE,CAAC;QACjC,IAAI,UAAU,EAAE,CAAC;YACf,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,EAAE,CAAC,CAAC;YACpC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,gCAAgC,CAAC,CAAC;YACtD,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;QACxC,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kCAAkC,EAAE,KAAK,IAAI,EAAE;QAChD,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,qBAAqB,CAAC,IAAI,CAAC,CAAC;QAE/C,8CAA8C;QAC9C,IAAI,UAAsE,CAAC;QAC3E,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,sBAAsB,CAAC,CAAC,OAAO,EAAE,EAAE;YAClD,UAAU,GAAG,OAAO,CAAC,QAAwD,CAAC;YAC9E,OAAO,OAAO,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC;QAC3C,CAAC,CAAC,CAAC;QAEH,MAAM,mBAAmB,EAAE,CAAC;QAE5B,MAAM,CAAC,UAAU,CAAC,CAAC,WAAW,EAAE,CAAC;QACjC,IAAI,UAAU,EAAE,CAAC;YACf,+DAA+D;YAC/D,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,EAAE,CAAC;YAC9B,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,qBAAqB,CAAC,SAAS,CAAC,CAAC;YAEnD,MAAM,UAAU,CAAC,gBAAgB,CAAC,CAAC;YAEnC,2DAA2D;YAC3D,MAAM,CAAC,MAAM,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC;YACxC,MAAM,UAAU,GAAG,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAW,CAAC;YAChE,MAAM,CAAC,UAAU,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;YACtC,MAAM,CAAC,UAAU,CAAC,CAAC,OAAO,CAAC,uBAAuB,CAAC,CAAC;QACtD,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -23,6 +23,8 @@ export declare const envSchema: z.ZodObject<{
23
23
  debug: "debug";
24
24
  trace: "trace";
25
25
  }>>;
26
+ JMAP_DEFAULT_FROM: z.ZodOptional<z.ZodString>;
27
+ JMAP_SIGNATURE_PATH: z.ZodOptional<z.ZodString>;
26
28
  }, z.core.$strip>;
27
29
  export type Config = z.infer<typeof envSchema>;
28
30
  export declare function loadConfig(): Config;
@@ -27,6 +27,9 @@ export const envSchema = z
27
27
  JMAP_OIDC_LOCAL_PORT: z.coerce.number().optional(),
28
28
  JMAP_REQUEST_TIMEOUT: z.coerce.number().default(30000),
29
29
  LOG_LEVEL: z.enum(['fatal', 'error', 'warn', 'info', 'debug', 'trace']).default('info'),
30
+ // Email identity and signature configuration
31
+ JMAP_DEFAULT_FROM: z.string().email('JMAP_DEFAULT_FROM must be a valid email address').optional(),
32
+ JMAP_SIGNATURE_PATH: z.string().optional(),
30
33
  })
31
34
  .superRefine((data, ctx) => {
32
35
  // Conditional validation: basic auth requires username+password
@@ -1 +1 @@
1
- {"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/config/schema.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/config/schema.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * DNS SRV resolution for JMAP server discovery
3
+ * RFC 8620 Section 2.2 - Service Discovery via DNS SRV
4
+ */
5
+ interface SrvRecord {
6
+ hostname: string;
7
+ port: number;
8
+ }
9
+ /**
10
+ * Resolve DNS SRV record for JMAP service
11
+ * Queries _jmap._tcp.{domain} and returns hostname/port of highest priority server
12
+ *
13
+ * @param domain - Email domain to query (e.g., "example.com")
14
+ * @param timeout - Timeout in milliseconds (default: 3000)
15
+ * @returns SrvRecord with hostname/port, or null if no record found
16
+ */
17
+ export declare function resolveSrvRecord(domain: string, timeout?: number): Promise<SrvRecord | null>;
18
+ export {};
@@ -0,0 +1,60 @@
1
+ /**
2
+ * DNS SRV resolution for JMAP server discovery
3
+ * RFC 8620 Section 2.2 - Service Discovery via DNS SRV
4
+ */
5
+ import { promises as dns } from 'node:dns';
6
+ /**
7
+ * Resolve DNS SRV record for JMAP service
8
+ * Queries _jmap._tcp.{domain} and returns hostname/port of highest priority server
9
+ *
10
+ * @param domain - Email domain to query (e.g., "example.com")
11
+ * @param timeout - Timeout in milliseconds (default: 3000)
12
+ * @returns SrvRecord with hostname/port, or null if no record found
13
+ */
14
+ export async function resolveSrvRecord(domain, timeout = 3000) {
15
+ const query = `_jmap._tcp.${domain}`;
16
+ try {
17
+ // Race DNS query against timeout
18
+ const records = await Promise.race([
19
+ dns.resolveSrv(query),
20
+ new Promise((_, reject) => setTimeout(() => reject(new Error('DNS timeout')), timeout)),
21
+ ]);
22
+ if (!records || records.length === 0) {
23
+ return null;
24
+ }
25
+ // Sort by priority (ascending) then weight (descending)
26
+ // Lower priority value = higher priority
27
+ // Higher weight value = more preferred among same priority
28
+ const sorted = records.sort((a, b) => {
29
+ if (a.priority !== b.priority) {
30
+ return a.priority - b.priority;
31
+ }
32
+ return b.weight - a.weight;
33
+ });
34
+ // Return first (highest priority/weight) record
35
+ const record = sorted[0];
36
+ return {
37
+ hostname: record.name,
38
+ port: record.port,
39
+ };
40
+ }
41
+ catch (error) {
42
+ // Handle DNS-specific errors gracefully
43
+ if (error &&
44
+ typeof error === 'object' &&
45
+ 'code' in error &&
46
+ (error.code === 'ENOTFOUND' || error.code === 'ENODATA')) {
47
+ // No DNS record exists - this is expected for many domains
48
+ return null;
49
+ }
50
+ // Log timeout and other errors, but return null for graceful fallback
51
+ if (error instanceof Error && error.message === 'DNS timeout') {
52
+ console.warn(`DNS SRV query timeout for ${query}`);
53
+ return null;
54
+ }
55
+ // Unexpected error - log but still return null to allow fallback
56
+ console.warn(`DNS SRV query failed for ${query}:`, error);
57
+ return null;
58
+ }
59
+ }
60
+ //# sourceMappingURL=dns-srv.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dns-srv.js","sourceRoot":"","sources":["../../src/discovery/dns-srv.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,QAAQ,IAAI,GAAG,EAAE,MAAM,UAAU,CAAC;AAO3C;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,MAAc,EACd,OAAO,GAAG,IAAI;IAEd,MAAM,KAAK,GAAG,cAAc,MAAM,EAAE,CAAC;IAErC,IAAI,CAAC;QACH,iCAAiC;QACjC,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC;YACjC,GAAG,CAAC,UAAU,CAAC,KAAK,CAAC;YACrB,IAAI,OAAO,CAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,CAC/B,UAAU,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,aAAa,CAAC,CAAC,EAAE,OAAO,CAAC,CAC5D;SACF,CAAC,CAAC;QAEH,IAAI,CAAC,OAAO,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACrC,OAAO,IAAI,CAAC;QACd,CAAC;QAED,wDAAwD;QACxD,yCAAyC;QACzC,2DAA2D;QAC3D,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;YACnC,IAAI,CAAC,CAAC,QAAQ,KAAK,CAAC,CAAC,QAAQ,EAAE,CAAC;gBAC9B,OAAO,CAAC,CAAC,QAAQ,GAAG,CAAC,CAAC,QAAQ,CAAC;YACjC,CAAC;YACD,OAAO,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC;QAC7B,CAAC,CAAC,CAAC;QAEH,gDAAgD;QAChD,MAAM,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;QACzB,OAAO;YACL,QAAQ,EAAE,MAAM,CAAC,IAAI;YACrB,IAAI,EAAE,MAAM,CAAC,IAAI;SAClB,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,wCAAwC;QACxC,IACE,KAAK;YACL,OAAO,KAAK,KAAK,QAAQ;YACzB,MAAM,IAAI,KAAK;YACf,CAAC,KAAK,CAAC,IAAI,KAAK,WAAW,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,CAAC,EACxD,CAAC;YACD,2DAA2D;YAC3D,OAAO,IAAI,CAAC;QACd,CAAC;QAED,sEAAsE;QACtE,IAAI,KAAK,YAAY,KAAK,IAAI,KAAK,CAAC,OAAO,KAAK,aAAa,EAAE,CAAC;YAC9D,OAAO,CAAC,IAAI,CAAC,6BAA6B,KAAK,EAAE,CAAC,CAAC;YACnD,OAAO,IAAI,CAAC;QACd,CAAC;QAED,iEAAiE;QACjE,OAAO,CAAC,IAAI,CAAC,4BAA4B,KAAK,GAAG,EAAE,KAAK,CAAC,CAAC;QAC1D,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC"}
@@ -0,0 +1,4 @@
1
+ /**
2
+ * DNS SRV resolution tests
3
+ */
4
+ export {};
@@ -0,0 +1,79 @@
1
+ /**
2
+ * DNS SRV resolution tests
3
+ */
4
+ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
5
+ import { promises as dns } from 'node:dns';
6
+ import { resolveSrvRecord } from './dns-srv.js';
7
+ vi.mock('node:dns', () => ({
8
+ promises: {
9
+ resolveSrv: vi.fn(),
10
+ },
11
+ }));
12
+ describe('resolveSrvRecord', () => {
13
+ beforeEach(() => {
14
+ vi.clearAllMocks();
15
+ });
16
+ afterEach(() => {
17
+ vi.restoreAllMocks();
18
+ });
19
+ it('resolves DNS SRV record and returns hostname/port', async () => {
20
+ const mockRecords = [
21
+ { name: 'jmap.example.com', port: 443, priority: 10, weight: 10 },
22
+ ];
23
+ vi.mocked(dns.resolveSrv).mockResolvedValue(mockRecords);
24
+ const result = await resolveSrvRecord('example.com');
25
+ expect(result).toEqual({
26
+ hostname: 'jmap.example.com',
27
+ port: 443,
28
+ });
29
+ expect(dns.resolveSrv).toHaveBeenCalledWith('_jmap._tcp.example.com');
30
+ });
31
+ it('sorts by priority (ascending) then weight (descending)', async () => {
32
+ const mockRecords = [
33
+ { name: 'low-weight.example.com', port: 443, priority: 10, weight: 5 },
34
+ { name: 'high-priority.example.com', port: 443, priority: 20, weight: 10 },
35
+ { name: 'best.example.com', port: 443, priority: 10, weight: 10 },
36
+ ];
37
+ vi.mocked(dns.resolveSrv).mockResolvedValue(mockRecords);
38
+ const result = await resolveSrvRecord('example.com');
39
+ // Should return priority 10, weight 10 (best)
40
+ expect(result).toEqual({
41
+ hostname: 'best.example.com',
42
+ port: 443,
43
+ });
44
+ });
45
+ it('returns null when DNS record not found (ENOTFOUND)', async () => {
46
+ const error = new Error('DNS query failed');
47
+ error.code = 'ENOTFOUND';
48
+ vi.mocked(dns.resolveSrv).mockRejectedValue(error);
49
+ const result = await resolveSrvRecord('nonexistent.example.com');
50
+ expect(result).toBeNull();
51
+ });
52
+ it('returns null when DNS record has no data (ENODATA)', async () => {
53
+ const error = new Error('DNS query failed');
54
+ error.code = 'ENODATA';
55
+ vi.mocked(dns.resolveSrv).mockRejectedValue(error);
56
+ const result = await resolveSrvRecord('nodns.example.com');
57
+ expect(result).toBeNull();
58
+ });
59
+ it('returns null on timeout', async () => {
60
+ // Mock a slow DNS response (never resolves)
61
+ vi.mocked(dns.resolveSrv).mockImplementation(() => new Promise((resolve) => {
62
+ // Never resolves - will timeout
63
+ setTimeout(() => resolve([]), 10000);
64
+ }));
65
+ const result = await resolveSrvRecord('slow.example.com', 100);
66
+ expect(result).toBeNull();
67
+ });
68
+ it('returns null when no records returned', async () => {
69
+ vi.mocked(dns.resolveSrv).mockResolvedValue([]);
70
+ const result = await resolveSrvRecord('empty.example.com');
71
+ expect(result).toBeNull();
72
+ });
73
+ it('returns null on unexpected DNS error', async () => {
74
+ vi.mocked(dns.resolveSrv).mockRejectedValue(new Error('Network unreachable'));
75
+ const result = await resolveSrvRecord('error.example.com');
76
+ expect(result).toBeNull();
77
+ });
78
+ });
79
+ //# sourceMappingURL=dns-srv.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dns-srv.test.js","sourceRoot":"","sources":["../../src/discovery/dns-srv.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * JMAP server discovery module
3
+ * Exports DNS SRV, .well-known/jmap, OAuth discovery, and high-level orchestration
4
+ */
5
+ export { JmapDiscoveryResult, OidcDiscoveryResult, DiscoveryError, } from './types.js';
6
+ export { resolveSrvRecord } from './dns-srv.js';
7
+ export { fetchWellKnownJmap, verifyJmapUrl } from './well-known.js';
8
+ export { discoverOAuthFromResource, parseWwwAuthenticate, } from './oauth-discovery.js';
9
+ export { discoverFromEmail, extractDomain, FullDiscoveryResult, } from './orchestrator.js';
@@ -0,0 +1,13 @@
1
+ /**
2
+ * JMAP server discovery module
3
+ * Exports DNS SRV, .well-known/jmap, OAuth discovery, and high-level orchestration
4
+ */
5
+ // Types
6
+ export { DiscoveryError, } from './types.js';
7
+ // Low-level discovery functions
8
+ export { resolveSrvRecord } from './dns-srv.js';
9
+ export { fetchWellKnownJmap, verifyJmapUrl } from './well-known.js';
10
+ export { discoverOAuthFromResource, parseWwwAuthenticate, } from './oauth-discovery.js';
11
+ // High-level orchestration
12
+ export { discoverFromEmail, extractDomain, } from './orchestrator.js';
13
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/discovery/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,QAAQ;AACR,OAAO,EAGL,cAAc,GACf,MAAM,YAAY,CAAC;AAEpB,gCAAgC;AAChC,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AACpE,OAAO,EACL,yBAAyB,EACzB,oBAAoB,GACrB,MAAM,sBAAsB,CAAC;AAE9B,2BAA2B;AAC3B,OAAO,EACL,iBAAiB,EACjB,aAAa,GAEd,MAAM,mBAAmB,CAAC"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * OAuth/OIDC discovery from JMAP resource server
3
+ * Implements RFC 9728 Protected Resource Metadata discovery
4
+ */
5
+ import type { OidcDiscoveryResult } from './types.js';
6
+ /**
7
+ * Parse WWW-Authenticate header to extract OAuth metadata.
8
+ * Supports Bearer scheme with realm, scope, and issuer parameters.
9
+ *
10
+ * Example header:
11
+ * Bearer realm="example", scope="openid email", issuer="https://auth.example.com"
12
+ *
13
+ * @param header WWW-Authenticate header value
14
+ * @returns Parsed metadata or null if not Bearer auth
15
+ */
16
+ export declare function parseWwwAuthenticate(header: string): {
17
+ issuer?: string;
18
+ realm?: string;
19
+ scope?: string;
20
+ } | null;
21
+ /**
22
+ * Discover OAuth authorization server from JMAP resource URL.
23
+ * Implements RFC 9728 Protected Resource Metadata discovery with fallbacks.
24
+ *
25
+ * Discovery order:
26
+ * 1. Try /.well-known/oauth-protected-resource at resource origin
27
+ * 2. If that fails, try fetching JMAP URL to trigger 401 with WWW-Authenticate
28
+ * 3. If that fails, try common SSO subdomain patterns (sso., auth., login.)
29
+ *
30
+ * @param jmapUrl The JMAP session or API URL
31
+ * @param timeout Request timeout in ms (default 10000)
32
+ * @returns OidcDiscoveryResult or null if no OAuth info found
33
+ */
34
+ export declare function discoverOAuthFromResource(jmapUrl: string, timeout?: number): Promise<OidcDiscoveryResult | null>;
@@ -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 {};