@nexartis/sentinel-sdk 1.6.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 (55) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +215 -0
  3. package/dist/core/auth-utils.d.ts +31 -0
  4. package/dist/core/auth-utils.d.ts.map +1 -0
  5. package/dist/core/auth-utils.js +128 -0
  6. package/dist/core/auth-utils.js.map +1 -0
  7. package/dist/core/client.d.ts +22 -0
  8. package/dist/core/client.d.ts.map +1 -0
  9. package/dist/core/client.js +137 -0
  10. package/dist/core/client.js.map +1 -0
  11. package/dist/core/errors.d.ts +47 -0
  12. package/dist/core/errors.d.ts.map +1 -0
  13. package/dist/core/errors.js +69 -0
  14. package/dist/core/errors.js.map +1 -0
  15. package/dist/core/index.d.ts +8 -0
  16. package/dist/core/index.d.ts.map +1 -0
  17. package/dist/core/index.js +6 -0
  18. package/dist/core/index.js.map +1 -0
  19. package/dist/core/logger.d.ts +30 -0
  20. package/dist/core/logger.d.ts.map +1 -0
  21. package/dist/core/logger.js +54 -0
  22. package/dist/core/logger.js.map +1 -0
  23. package/dist/core/types.d.ts +269 -0
  24. package/dist/core/types.d.ts.map +1 -0
  25. package/dist/core/types.js +2 -0
  26. package/dist/core/types.js.map +1 -0
  27. package/dist/sveltekit/client/index.d.ts +36 -0
  28. package/dist/sveltekit/client/index.d.ts.map +1 -0
  29. package/dist/sveltekit/client/index.js +121 -0
  30. package/dist/sveltekit/client/index.js.map +1 -0
  31. package/dist/sveltekit/cookies.d.ts +54 -0
  32. package/dist/sveltekit/cookies.d.ts.map +1 -0
  33. package/dist/sveltekit/cookies.js +64 -0
  34. package/dist/sveltekit/cookies.js.map +1 -0
  35. package/dist/sveltekit/handle.d.ts +20 -0
  36. package/dist/sveltekit/handle.d.ts.map +1 -0
  37. package/dist/sveltekit/handle.js +146 -0
  38. package/dist/sveltekit/handle.js.map +1 -0
  39. package/dist/sveltekit/handlers/index.d.ts +29 -0
  40. package/dist/sveltekit/handlers/index.d.ts.map +1 -0
  41. package/dist/sveltekit/handlers/index.js +536 -0
  42. package/dist/sveltekit/handlers/index.js.map +1 -0
  43. package/dist/sveltekit/index.d.ts +6 -0
  44. package/dist/sveltekit/index.d.ts.map +1 -0
  45. package/dist/sveltekit/index.js +5 -0
  46. package/dist/sveltekit/index.js.map +1 -0
  47. package/dist/sveltekit/pages/index.d.ts +68 -0
  48. package/dist/sveltekit/pages/index.d.ts.map +1 -0
  49. package/dist/sveltekit/pages/index.js +207 -0
  50. package/dist/sveltekit/pages/index.js.map +1 -0
  51. package/dist/sveltekit/utils.d.ts +17 -0
  52. package/dist/sveltekit/utils.d.ts.map +1 -0
  53. package/dist/sveltekit/utils.js +34 -0
  54. package/dist/sveltekit/utils.js.map +1 -0
  55. package/package.json +99 -0
@@ -0,0 +1,137 @@
1
+ import { SentinelTimeoutError, isRetryableError, isRetryableStatus } from './errors.js';
2
+ import { createLogger } from './logger.js';
3
+ const DEFAULT_TIMEOUT_MS = 8_000;
4
+ const DEFAULT_MAX_RETRIES = 1;
5
+ export class SentinelClient {
6
+ config;
7
+ fetch;
8
+ timeoutMs;
9
+ maxRetries;
10
+ log;
11
+ constructor(config, fetchFn = fetch) {
12
+ this.config = config;
13
+ this.fetch = fetchFn;
14
+ this.timeoutMs = config.timeoutMs ?? DEFAULT_TIMEOUT_MS;
15
+ this.maxRetries = config.maxRetries ?? DEFAULT_MAX_RETRIES;
16
+ this.log = config.logger ?? createLogger('sentinel-client');
17
+ }
18
+ async request(endpoint, options = {}) {
19
+ const url = `${this.config.apiUrl}${endpoint}`;
20
+ const headers = {
21
+ 'Content-Type': 'application/json',
22
+ ...options.headers
23
+ };
24
+ let lastError;
25
+ for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
26
+ try {
27
+ // Apply per-request timeout via AbortController
28
+ const controller = new AbortController();
29
+ const timeoutId = setTimeout(() => controller.abort(), this.timeoutMs);
30
+ try {
31
+ const response = await this.fetch(url, {
32
+ ...options,
33
+ headers,
34
+ signal: controller.signal
35
+ });
36
+ clearTimeout(timeoutId);
37
+ // If the response status is retryable and we have retries left, retry
38
+ if (isRetryableStatus(response.status) && attempt < this.maxRetries) {
39
+ lastError = new Error(`HTTP ${response.status} from ${url}`);
40
+ await this.backoff(attempt);
41
+ continue;
42
+ }
43
+ return await response.json();
44
+ }
45
+ catch (fetchError) {
46
+ clearTimeout(timeoutId);
47
+ // Convert AbortError to SentinelTimeoutError
48
+ if (fetchError instanceof DOMException && fetchError.name === 'AbortError') {
49
+ throw new SentinelTimeoutError(url, this.timeoutMs);
50
+ }
51
+ throw fetchError;
52
+ }
53
+ }
54
+ catch (error) {
55
+ lastError = error;
56
+ // Only retry retryable errors
57
+ if (attempt < this.maxRetries && isRetryableError(error)) {
58
+ await this.backoff(attempt);
59
+ continue;
60
+ }
61
+ throw error;
62
+ }
63
+ }
64
+ // Should not reach here, but just in case
65
+ throw lastError;
66
+ }
67
+ /** Exponential backoff: 1s, 2s, 4s, ... */
68
+ backoff(attempt) {
69
+ const delay = Math.min(1000 * Math.pow(2, attempt), 8000);
70
+ return new Promise(resolve => setTimeout(resolve, delay));
71
+ }
72
+ async startLogin(email) {
73
+ return this.request('/auth/login-passwordless', {
74
+ method: 'POST',
75
+ body: JSON.stringify({
76
+ email,
77
+ email_endpoint: this.config.baseUrl
78
+ })
79
+ });
80
+ }
81
+ async pollStatus(transactionId) {
82
+ return this.request('/auth/poll', {
83
+ method: 'POST',
84
+ body: JSON.stringify({ transactionId })
85
+ });
86
+ }
87
+ async claimTokens(transactionId, clientToken) {
88
+ return this.request('/auth/claim', {
89
+ method: 'POST',
90
+ body: JSON.stringify({ transactionId, clientToken })
91
+ });
92
+ }
93
+ async register(email) {
94
+ return this.request('/auth/register', {
95
+ method: 'POST',
96
+ body: JSON.stringify({
97
+ email,
98
+ email_endpoint: this.config.baseUrl
99
+ })
100
+ });
101
+ }
102
+ async verifyEmail(token) {
103
+ return this.request('/auth/verify', {
104
+ method: 'POST',
105
+ body: JSON.stringify({ verification_token: token })
106
+ });
107
+ }
108
+ async completePasswordless(magicToken) {
109
+ return this.request('/auth/complete-passwordless', {
110
+ method: 'POST',
111
+ body: JSON.stringify({ magic_token: magicToken })
112
+ });
113
+ }
114
+ async refreshToken(refreshToken) {
115
+ return this.request('/auth/refresh', {
116
+ method: 'POST',
117
+ body: JSON.stringify({ refresh_token: refreshToken })
118
+ });
119
+ }
120
+ async validateToken(accessToken) {
121
+ return this.request('/auth/validate', {
122
+ method: 'POST',
123
+ headers: {
124
+ 'Authorization': `Bearer ${accessToken}`
125
+ }
126
+ });
127
+ }
128
+ async getCurrentUser(accessToken) {
129
+ return this.request('/users/me', {
130
+ method: 'GET',
131
+ headers: {
132
+ 'Authorization': `Bearer ${accessToken}`
133
+ }
134
+ });
135
+ }
136
+ }
137
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/core/client.ts"],"names":[],"mappings":"AAaA,OAAO,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AACxF,OAAO,EAAE,YAAY,EAAuB,MAAM,aAAa,CAAC;AAEhE,MAAM,kBAAkB,GAAG,KAAK,CAAC;AACjC,MAAM,mBAAmB,GAAG,CAAC,CAAC;AAE9B,MAAM,OAAO,cAAc;IAClB,MAAM,CAAiB;IACvB,KAAK,CAA0B;IAC/B,SAAS,CAAS;IAClB,UAAU,CAAS;IACnB,GAAG,CAAiB;IAE5B,YAAY,MAAsB,EAAE,UAAmC,KAAK;QAC3E,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,KAAK,GAAG,OAAO,CAAC;QACrB,IAAI,CAAC,SAAS,GAAG,MAAM,CAAC,SAAS,IAAI,kBAAkB,CAAC;QACxD,IAAI,CAAC,UAAU,GAAG,MAAM,CAAC,UAAU,IAAI,mBAAmB,CAAC;QAC3D,IAAI,CAAC,GAAG,GAAG,MAAM,CAAC,MAAM,IAAI,YAAY,CAAC,iBAAiB,CAAC,CAAC;IAC7D,CAAC;IAEO,KAAK,CAAC,OAAO,CAAI,QAAgB,EAAE,UAAuB,EAAE;QACnE,MAAM,GAAG,GAAG,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,GAAG,QAAQ,EAAE,CAAC;QAC/C,MAAM,OAAO,GAAG;YACf,cAAc,EAAE,kBAAkB;YAClC,GAAG,OAAO,CAAC,OAAO;SAClB,CAAC;QAEF,IAAI,SAAkB,CAAC;QAEvB,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,IAAI,IAAI,CAAC,UAAU,EAAE,OAAO,EAAE,EAAE,CAAC;YAC7D,IAAI,CAAC;gBACJ,gDAAgD;gBAChD,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;gBACzC,MAAM,SAAS,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;gBAEvE,IAAI,CAAC;oBACJ,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE;wBACtC,GAAG,OAAO;wBACV,OAAO;wBACP,MAAM,EAAE,UAAU,CAAC,MAAM;qBACzB,CAAC,CAAC;oBAEH,YAAY,CAAC,SAAS,CAAC,CAAC;oBAExB,sEAAsE;oBACtE,IAAI,iBAAiB,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,OAAO,GAAG,IAAI,CAAC,UAAU,EAAE,CAAC;wBACrE,SAAS,GAAG,IAAI,KAAK,CAAC,QAAQ,QAAQ,CAAC,MAAM,SAAS,GAAG,EAAE,CAAC,CAAC;wBAC7D,MAAM,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;wBAC5B,SAAS;oBACV,CAAC;oBAED,OAAO,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;gBAC9B,CAAC;gBAAC,OAAO,UAAU,EAAE,CAAC;oBACrB,YAAY,CAAC,SAAS,CAAC,CAAC;oBAExB,6CAA6C;oBAC7C,IAAI,UAAU,YAAY,YAAY,IAAI,UAAU,CAAC,IAAI,KAAK,YAAY,EAAE,CAAC;wBAC5E,MAAM,IAAI,oBAAoB,CAAC,GAAG,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;oBACrD,CAAC;oBACD,MAAM,UAAU,CAAC;gBAClB,CAAC;YACF,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBAChB,SAAS,GAAG,KAAK,CAAC;gBAElB,8BAA8B;gBAC9B,IAAI,OAAO,GAAG,IAAI,CAAC,UAAU,IAAI,gBAAgB,CAAC,KAAK,CAAC,EAAE,CAAC;oBAC1D,MAAM,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;oBAC5B,SAAS;gBACV,CAAC;gBAED,MAAM,KAAK,CAAC;YACb,CAAC;QACF,CAAC;QAED,0CAA0C;QAC1C,MAAM,SAAS,CAAC;IACjB,CAAC;IAED,2CAA2C;IACnC,OAAO,CAAC,OAAe;QAC9B,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,CAAC,EAAE,IAAI,CAAC,CAAC;QAC1D,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC;IAC3D,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,KAAa;QAC7B,OAAO,IAAI,CAAC,OAAO,CAAqB,0BAA0B,EAAE;YACnE,MAAM,EAAE,MAAM;YACd,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;gBACpB,KAAK;gBACL,cAAc,EAAE,IAAI,CAAC,MAAM,CAAC,OAAO;aACnC,CAAC;SACF,CAAC,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,aAAqB;QACrC,OAAO,IAAI,CAAC,OAAO,CAAe,YAAY,EAAE;YAC/C,MAAM,EAAE,MAAM;YACd,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,aAAa,EAAE,CAAC;SACvC,CAAC,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,WAAW,CAAC,aAAqB,EAAE,WAAmB;QAC3D,OAAO,IAAI,CAAC,OAAO,CAAgB,aAAa,EAAE;YACjD,MAAM,EAAE,MAAM;YACd,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,aAAa,EAAE,WAAW,EAAE,CAAC;SACpD,CAAC,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,KAAa;QAC3B,OAAO,IAAI,CAAC,OAAO,CAAmB,gBAAgB,EAAE;YACvD,MAAM,EAAE,MAAM;YACd,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;gBACpB,KAAK;gBACL,cAAc,EAAE,IAAI,CAAC,MAAM,CAAC,OAAO;aACnC,CAAC;SACF,CAAC,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,WAAW,CAAC,KAAa;QAC9B,OAAO,IAAI,CAAC,OAAO,CAAiB,cAAc,EAAE;YACnD,MAAM,EAAE,MAAM;YACd,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,kBAAkB,EAAE,KAAK,EAAE,CAAC;SACnD,CAAC,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,oBAAoB,CAAC,UAAkB;QAC5C,OAAO,IAAI,CAAC,OAAO,CAA+B,6BAA6B,EAAE;YAChF,MAAM,EAAE,MAAM;YACd,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,WAAW,EAAE,UAAU,EAAE,CAAC;SACjD,CAAC,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,YAAY,CAAC,YAAoB;QACtC,OAAO,IAAI,CAAC,OAAO,CAAkB,eAAe,EAAE;YACrD,MAAM,EAAE,MAAM;YACd,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,aAAa,EAAE,YAAY,EAAE,CAAC;SACrD,CAAC,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,aAAa,CAAC,WAAmB;QACtC,OAAO,IAAI,CAAC,OAAO,CAAmB,gBAAgB,EAAE;YACvD,MAAM,EAAE,MAAM;YACd,OAAO,EAAE;gBACR,eAAe,EAAE,UAAU,WAAW,EAAE;aACxC;SACD,CAAC,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,cAAc,CAAC,WAAmB;QACvC,OAAO,IAAI,CAAC,OAAO,CAAsB,WAAW,EAAE;YACrD,MAAM,EAAE,KAAK;YACb,OAAO,EAAE;gBACR,eAAe,EAAE,UAAU,WAAW,EAAE;aACxC;SACD,CAAC,CAAC;IACJ,CAAC;CACD","sourcesContent":["// src/core/client.ts\nimport type {\n\tSentinelConfig,\n\tStartLoginResponse,\n\tPollResponse,\n\tClaimResponse,\n\tRefreshResponse,\n\tValidateResponse,\n\tCurrentUserResponse,\n\tRegisterResponse,\n\tVerifyResponse,\n\tCompletePasswordlessResponse\n} from './types.js';\nimport { SentinelTimeoutError, isRetryableError, isRetryableStatus } from './errors.js';\nimport { createLogger, type SentinelLogger } from './logger.js';\n\nconst DEFAULT_TIMEOUT_MS = 8_000;\nconst DEFAULT_MAX_RETRIES = 1;\n\nexport class SentinelClient {\n\tprivate config: SentinelConfig;\n\tprivate fetch: typeof globalThis.fetch;\n\tprivate timeoutMs: number;\n\tprivate maxRetries: number;\n\tprivate log: SentinelLogger;\n\n\tconstructor(config: SentinelConfig, fetchFn: typeof globalThis.fetch = fetch) {\n\t\tthis.config = config;\n\t\tthis.fetch = fetchFn;\n\t\tthis.timeoutMs = config.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n\t\tthis.maxRetries = config.maxRetries ?? DEFAULT_MAX_RETRIES;\n\t\tthis.log = config.logger ?? createLogger('sentinel-client');\n\t}\n\n\tprivate async request<T>(endpoint: string, options: RequestInit = {}): Promise<T> {\n\t\tconst url = `${this.config.apiUrl}${endpoint}`;\n\t\tconst headers = {\n\t\t\t'Content-Type': 'application/json',\n\t\t\t...options.headers\n\t\t};\n\n\t\tlet lastError: unknown;\n\n\t\tfor (let attempt = 0; attempt <= this.maxRetries; attempt++) {\n\t\t\ttry {\n\t\t\t\t// Apply per-request timeout via AbortController\n\t\t\t\tconst controller = new AbortController();\n\t\t\t\tconst timeoutId = setTimeout(() => controller.abort(), this.timeoutMs);\n\n\t\t\t\ttry {\n\t\t\t\t\tconst response = await this.fetch(url, {\n\t\t\t\t\t\t...options,\n\t\t\t\t\t\theaders,\n\t\t\t\t\t\tsignal: controller.signal\n\t\t\t\t\t});\n\n\t\t\t\t\tclearTimeout(timeoutId);\n\n\t\t\t\t\t// If the response status is retryable and we have retries left, retry\n\t\t\t\t\tif (isRetryableStatus(response.status) && attempt < this.maxRetries) {\n\t\t\t\t\t\tlastError = new Error(`HTTP ${response.status} from ${url}`);\n\t\t\t\t\t\tawait this.backoff(attempt);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\treturn await response.json();\n\t\t\t\t} catch (fetchError) {\n\t\t\t\t\tclearTimeout(timeoutId);\n\n\t\t\t\t\t// Convert AbortError to SentinelTimeoutError\n\t\t\t\t\tif (fetchError instanceof DOMException && fetchError.name === 'AbortError') {\n\t\t\t\t\t\tthrow new SentinelTimeoutError(url, this.timeoutMs);\n\t\t\t\t\t}\n\t\t\t\t\tthrow fetchError;\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tlastError = error;\n\n\t\t\t\t// Only retry retryable errors\n\t\t\t\tif (attempt < this.maxRetries && isRetryableError(error)) {\n\t\t\t\t\tawait this.backoff(attempt);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t}\n\n\t\t// Should not reach here, but just in case\n\t\tthrow lastError;\n\t}\n\n\t/** Exponential backoff: 1s, 2s, 4s, ... */\n\tprivate backoff(attempt: number): Promise<void> {\n\t\tconst delay = Math.min(1000 * Math.pow(2, attempt), 8000);\n\t\treturn new Promise(resolve => setTimeout(resolve, delay));\n\t}\n\n\tasync startLogin(email: string): Promise<StartLoginResponse> {\n\t\treturn this.request<StartLoginResponse>('/auth/login-passwordless', {\n\t\t\tmethod: 'POST',\n\t\t\tbody: JSON.stringify({\n\t\t\t\temail,\n\t\t\t\temail_endpoint: this.config.baseUrl\n\t\t\t})\n\t\t});\n\t}\n\n\tasync pollStatus(transactionId: string): Promise<PollResponse> {\n\t\treturn this.request<PollResponse>('/auth/poll', {\n\t\t\tmethod: 'POST',\n\t\t\tbody: JSON.stringify({ transactionId })\n\t\t});\n\t}\n\n\tasync claimTokens(transactionId: string, clientToken: string): Promise<ClaimResponse> {\n\t\treturn this.request<ClaimResponse>('/auth/claim', {\n\t\t\tmethod: 'POST',\n\t\t\tbody: JSON.stringify({ transactionId, clientToken })\n\t\t});\n\t}\n\n\tasync register(email: string): Promise<RegisterResponse> {\n\t\treturn this.request<RegisterResponse>('/auth/register', {\n\t\t\tmethod: 'POST',\n\t\t\tbody: JSON.stringify({\n\t\t\t\temail,\n\t\t\t\temail_endpoint: this.config.baseUrl\n\t\t\t})\n\t\t});\n\t}\n\n\tasync verifyEmail(token: string): Promise<VerifyResponse> {\n\t\treturn this.request<VerifyResponse>('/auth/verify', {\n\t\t\tmethod: 'POST',\n\t\t\tbody: JSON.stringify({ verification_token: token })\n\t\t});\n\t}\n\n\tasync completePasswordless(magicToken: string): Promise<CompletePasswordlessResponse> {\n\t\treturn this.request<CompletePasswordlessResponse>('/auth/complete-passwordless', {\n\t\t\tmethod: 'POST',\n\t\t\tbody: JSON.stringify({ magic_token: magicToken })\n\t\t});\n\t}\n\n\tasync refreshToken(refreshToken: string): Promise<RefreshResponse> {\n\t\treturn this.request<RefreshResponse>('/auth/refresh', {\n\t\t\tmethod: 'POST',\n\t\t\tbody: JSON.stringify({ refresh_token: refreshToken })\n\t\t});\n\t}\n\n\tasync validateToken(accessToken: string): Promise<ValidateResponse> {\n\t\treturn this.request<ValidateResponse>('/auth/validate', {\n\t\t\tmethod: 'POST',\n\t\t\theaders: {\n\t\t\t\t'Authorization': `Bearer ${accessToken}`\n\t\t\t}\n\t\t});\n\t}\n\n\tasync getCurrentUser(accessToken: string): Promise<CurrentUserResponse> {\n\t\treturn this.request<CurrentUserResponse>('/users/me', {\n\t\t\tmethod: 'GET',\n\t\t\theaders: {\n\t\t\t\t'Authorization': `Bearer ${accessToken}`\n\t\t\t}\n\t\t});\n\t}\n}\n\n"]}
@@ -0,0 +1,47 @@
1
+ export declare enum ErrorCode {
2
+ USER_NOT_FOUND = "USER_NOT_FOUND",
3
+ TOKEN_EXPIRED = "TOKEN_EXPIRED",
4
+ INVALID_TOKEN = "INVALID_TOKEN",
5
+ NETWORK_ERROR = "NETWORK_ERROR",
6
+ NO_REFRESH_TOKEN = "NO_REFRESH_TOKEN",
7
+ CLAIM_FAILED = "CLAIM_FAILED",
8
+ CONFIGURATION_ERROR = "CONFIGURATION_ERROR"
9
+ }
10
+ /**
11
+ * Sentinel backend numeric error codes.
12
+ * These are emitted in the `code` field of Sentinel API responses.
13
+ * Use these constants instead of magic numbers when checking result codes.
14
+ *
15
+ * Source: Sentinel backend `ErrorCode` enum (Hono / Cloudflare Worker).
16
+ */
17
+ export declare const SentinelCode: {
18
+ readonly SUCCESS: 0;
19
+ readonly VALIDATION_ERROR: 1001;
20
+ readonly ACCOUNT_NOT_VERIFIED: 2001;
21
+ readonly ACCOUNT_DISABLED: 2002;
22
+ readonly INVALID_TOKEN: 2003;
23
+ readonly EXPIRED_TOKEN: 2004;
24
+ readonly TOKEN_REUSE_DETECTED: 2005;
25
+ readonly TOKEN_REVOKED: 2006;
26
+ readonly ACCOUNT_NOT_EXIST: 2009;
27
+ readonly INVALID_TRANSACTION: 2011;
28
+ };
29
+ export type SentinelCodeValue = (typeof SentinelCode)[keyof typeof SentinelCode];
30
+ export declare class SentinelError extends Error {
31
+ code: ErrorCode;
32
+ originalError?: unknown | undefined;
33
+ constructor(code: ErrorCode, message: string, originalError?: unknown | undefined);
34
+ }
35
+ /**
36
+ * Thrown when a fetch to the Sentinel API times out.
37
+ */
38
+ export declare class SentinelTimeoutError extends SentinelError {
39
+ constructor(url: string, timeoutMs: number);
40
+ }
41
+ /** HTTP status codes that are safe to retry (transient server errors). */
42
+ export declare const RETRYABLE_HTTP_STATUSES: Set<number>;
43
+ /** Check whether an error is retryable (network failure or transient HTTP error). */
44
+ export declare function isRetryableError(error: unknown): boolean;
45
+ /** Check whether an HTTP status is retryable. */
46
+ export declare function isRetryableStatus(status: number): boolean;
47
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/core/errors.ts"],"names":[],"mappings":"AAEA,oBAAY,SAAS;IACpB,cAAc,mBAAmB;IACjC,aAAa,kBAAkB;IAC/B,aAAa,kBAAkB;IAC/B,aAAa,kBAAkB;IAC/B,gBAAgB,qBAAqB;IACrC,YAAY,iBAAiB;IAC7B,mBAAmB,wBAAwB;CAC3C;AAED;;;;;;GAMG;AACH,eAAO,MAAM,YAAY;;;;;;;;;;;CAWf,CAAC;AAEX,MAAM,MAAM,iBAAiB,GAAG,CAAC,OAAO,YAAY,CAAC,CAAC,MAAM,OAAO,YAAY,CAAC,CAAC;AAEjF,qBAAa,aAAc,SAAQ,KAAK;IAE/B,IAAI,EAAE,SAAS;IAEf,aAAa,CAAC,EAAE,OAAO;gBAFvB,IAAI,EAAE,SAAS,EACtB,OAAO,EAAE,MAAM,EACR,aAAa,CAAC,EAAE,OAAO,YAAA;CAK/B;AAED;;GAEG;AACH,qBAAa,oBAAqB,SAAQ,aAAa;gBAC1C,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM;CAO1C;AAED,0EAA0E;AAC1E,eAAO,MAAM,uBAAuB,aAA2B,CAAC;AAEhE,qFAAqF;AACrF,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAQxD;AAED,iDAAiD;AACjD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAEzD"}
@@ -0,0 +1,69 @@
1
+ // src/core/errors.ts
2
+ export var ErrorCode;
3
+ (function (ErrorCode) {
4
+ ErrorCode["USER_NOT_FOUND"] = "USER_NOT_FOUND";
5
+ ErrorCode["TOKEN_EXPIRED"] = "TOKEN_EXPIRED";
6
+ ErrorCode["INVALID_TOKEN"] = "INVALID_TOKEN";
7
+ ErrorCode["NETWORK_ERROR"] = "NETWORK_ERROR";
8
+ ErrorCode["NO_REFRESH_TOKEN"] = "NO_REFRESH_TOKEN";
9
+ ErrorCode["CLAIM_FAILED"] = "CLAIM_FAILED";
10
+ ErrorCode["CONFIGURATION_ERROR"] = "CONFIGURATION_ERROR";
11
+ })(ErrorCode || (ErrorCode = {}));
12
+ /**
13
+ * Sentinel backend numeric error codes.
14
+ * These are emitted in the `code` field of Sentinel API responses.
15
+ * Use these constants instead of magic numbers when checking result codes.
16
+ *
17
+ * Source: Sentinel backend `ErrorCode` enum (Hono / Cloudflare Worker).
18
+ */
19
+ export const SentinelCode = {
20
+ SUCCESS: 0,
21
+ VALIDATION_ERROR: 1001,
22
+ ACCOUNT_NOT_VERIFIED: 2001,
23
+ ACCOUNT_DISABLED: 2002,
24
+ INVALID_TOKEN: 2003,
25
+ EXPIRED_TOKEN: 2004,
26
+ TOKEN_REUSE_DETECTED: 2005, // One-time token already consumed (e.g., email client prefetch)
27
+ TOKEN_REVOKED: 2006,
28
+ ACCOUNT_NOT_EXIST: 2009,
29
+ INVALID_TRANSACTION: 2011
30
+ };
31
+ export class SentinelError extends Error {
32
+ code;
33
+ originalError;
34
+ constructor(code, message, originalError) {
35
+ super(message);
36
+ this.code = code;
37
+ this.originalError = originalError;
38
+ this.name = 'SentinelError';
39
+ }
40
+ }
41
+ /**
42
+ * Thrown when a fetch to the Sentinel API times out.
43
+ */
44
+ export class SentinelTimeoutError extends SentinelError {
45
+ constructor(url, timeoutMs) {
46
+ super(ErrorCode.NETWORK_ERROR, `Sentinel API request to ${url} timed out after ${timeoutMs}ms`);
47
+ this.name = 'SentinelTimeoutError';
48
+ }
49
+ }
50
+ /** HTTP status codes that are safe to retry (transient server errors). */
51
+ export const RETRYABLE_HTTP_STATUSES = new Set([502, 503, 504]);
52
+ /** Check whether an error is retryable (network failure or transient HTTP error). */
53
+ export function isRetryableError(error) {
54
+ // Timeout errors are retryable
55
+ if (error instanceof SentinelTimeoutError)
56
+ return true;
57
+ // Network errors (Failed to fetch, etc.)
58
+ if (error instanceof TypeError)
59
+ return true;
60
+ // DOMException from AbortController
61
+ if (error instanceof DOMException && error.name === 'AbortError')
62
+ return true;
63
+ return false;
64
+ }
65
+ /** Check whether an HTTP status is retryable. */
66
+ export function isRetryableStatus(status) {
67
+ return RETRYABLE_HTTP_STATUSES.has(status);
68
+ }
69
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/core/errors.ts"],"names":[],"mappings":"AAAA,qBAAqB;AAErB,MAAM,CAAN,IAAY,SAQX;AARD,WAAY,SAAS;IACpB,8CAAiC,CAAA;IACjC,4CAA+B,CAAA;IAC/B,4CAA+B,CAAA;IAC/B,4CAA+B,CAAA;IAC/B,kDAAqC,CAAA;IACrC,0CAA6B,CAAA;IAC7B,wDAA2C,CAAA;AAC5C,CAAC,EARW,SAAS,KAAT,SAAS,QAQpB;AAED;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG;IAC3B,OAAO,EAAE,CAAC;IACV,gBAAgB,EAAE,IAAI;IACtB,oBAAoB,EAAE,IAAI;IAC1B,gBAAgB,EAAE,IAAI;IACtB,aAAa,EAAE,IAAI;IACnB,aAAa,EAAE,IAAI;IACnB,oBAAoB,EAAE,IAAI,EAAE,gEAAgE;IAC5F,aAAa,EAAE,IAAI;IACnB,iBAAiB,EAAE,IAAI;IACvB,mBAAmB,EAAE,IAAI;CAChB,CAAC;AAIX,MAAM,OAAO,aAAc,SAAQ,KAAK;IAE/B;IAEA;IAHR,YACQ,IAAe,EACtB,OAAe,EACR,aAAuB;QAE9B,KAAK,CAAC,OAAO,CAAC,CAAC;QAJR,SAAI,GAAJ,IAAI,CAAW;QAEf,kBAAa,GAAb,aAAa,CAAU;QAG9B,IAAI,CAAC,IAAI,GAAG,eAAe,CAAC;IAC7B,CAAC;CACD;AAED;;GAEG;AACH,MAAM,OAAO,oBAAqB,SAAQ,aAAa;IACtD,YAAY,GAAW,EAAE,SAAiB;QACzC,KAAK,CACJ,SAAS,CAAC,aAAa,EACvB,2BAA2B,GAAG,oBAAoB,SAAS,IAAI,CAC/D,CAAC;QACF,IAAI,CAAC,IAAI,GAAG,sBAAsB,CAAC;IACpC,CAAC;CACD;AAED,0EAA0E;AAC1E,MAAM,CAAC,MAAM,uBAAuB,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC;AAEhE,qFAAqF;AACrF,MAAM,UAAU,gBAAgB,CAAC,KAAc;IAC9C,+BAA+B;IAC/B,IAAI,KAAK,YAAY,oBAAoB;QAAE,OAAO,IAAI,CAAC;IACvD,yCAAyC;IACzC,IAAI,KAAK,YAAY,SAAS;QAAE,OAAO,IAAI,CAAC;IAC5C,oCAAoC;IACpC,IAAI,KAAK,YAAY,YAAY,IAAI,KAAK,CAAC,IAAI,KAAK,YAAY;QAAE,OAAO,IAAI,CAAC;IAC9E,OAAO,KAAK,CAAC;AACd,CAAC;AAED,iDAAiD;AACjD,MAAM,UAAU,iBAAiB,CAAC,MAAc;IAC/C,OAAO,uBAAuB,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;AAC5C,CAAC","sourcesContent":["// src/core/errors.ts\n\nexport enum ErrorCode {\n\tUSER_NOT_FOUND = 'USER_NOT_FOUND',\n\tTOKEN_EXPIRED = 'TOKEN_EXPIRED',\n\tINVALID_TOKEN = 'INVALID_TOKEN',\n\tNETWORK_ERROR = 'NETWORK_ERROR',\n\tNO_REFRESH_TOKEN = 'NO_REFRESH_TOKEN',\n\tCLAIM_FAILED = 'CLAIM_FAILED',\n\tCONFIGURATION_ERROR = 'CONFIGURATION_ERROR'\n}\n\n/**\n * Sentinel backend numeric error codes.\n * These are emitted in the `code` field of Sentinel API responses.\n * Use these constants instead of magic numbers when checking result codes.\n *\n * Source: Sentinel backend `ErrorCode` enum (Hono / Cloudflare Worker).\n */\nexport const SentinelCode = {\n\tSUCCESS: 0,\n\tVALIDATION_ERROR: 1001,\n\tACCOUNT_NOT_VERIFIED: 2001,\n\tACCOUNT_DISABLED: 2002,\n\tINVALID_TOKEN: 2003,\n\tEXPIRED_TOKEN: 2004,\n\tTOKEN_REUSE_DETECTED: 2005, // One-time token already consumed (e.g., email client prefetch)\n\tTOKEN_REVOKED: 2006,\n\tACCOUNT_NOT_EXIST: 2009,\n\tINVALID_TRANSACTION: 2011\n} as const;\n\nexport type SentinelCodeValue = (typeof SentinelCode)[keyof typeof SentinelCode];\n\nexport class SentinelError extends Error {\n\tconstructor(\n\t\tpublic code: ErrorCode,\n\t\tmessage: string,\n\t\tpublic originalError?: unknown\n\t) {\n\t\tsuper(message);\n\t\tthis.name = 'SentinelError';\n\t}\n}\n\n/**\n * Thrown when a fetch to the Sentinel API times out.\n */\nexport class SentinelTimeoutError extends SentinelError {\n\tconstructor(url: string, timeoutMs: number) {\n\t\tsuper(\n\t\t\tErrorCode.NETWORK_ERROR,\n\t\t\t`Sentinel API request to ${url} timed out after ${timeoutMs}ms`\n\t\t);\n\t\tthis.name = 'SentinelTimeoutError';\n\t}\n}\n\n/** HTTP status codes that are safe to retry (transient server errors). */\nexport const RETRYABLE_HTTP_STATUSES = new Set([502, 503, 504]);\n\n/** Check whether an error is retryable (network failure or transient HTTP error). */\nexport function isRetryableError(error: unknown): boolean {\n\t// Timeout errors are retryable\n\tif (error instanceof SentinelTimeoutError) return true;\n\t// Network errors (Failed to fetch, etc.)\n\tif (error instanceof TypeError) return true;\n\t// DOMException from AbortController\n\tif (error instanceof DOMException && error.name === 'AbortError') return true;\n\treturn false;\n}\n\n/** Check whether an HTTP status is retryable. */\nexport function isRetryableStatus(status: number): boolean {\n\treturn RETRYABLE_HTTP_STATUSES.has(status);\n}\n\n"]}
@@ -0,0 +1,8 @@
1
+ export { SentinelClient } from './client.js';
2
+ export { SentinelError, SentinelTimeoutError, ErrorCode, SentinelCode, isRetryableError, isRetryableStatus } from './errors.js';
3
+ export type { SentinelCodeValue } from './errors.js';
4
+ export { validateToken, isTokenExpired, decodeTokenLocally } from './auth-utils.js';
5
+ export { createLogger, noopLogger } from './logger.js';
6
+ export type { SentinelLogger, LogLevel, LogEntry } from './logger.js';
7
+ export type { SentinelConfig, SentinelResponse, SentinelResponseBase, StartLoginResponse, PollResponse, ClaimResponse, RefreshResponse, ValidateResponse, RegisterResponse, VerifyResponse, CompletePasswordlessResponse, DecodedToken, UserLocals, SentinelHandleOptions, AuthHandlerOptions, BeforeLoginContext, BeforeLoginReject, AfterLoginContext, AfterClaimContext, ErrorContext, ResolveRedirectContext, LogoutRedirectContext } from './types.js';
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,oBAAoB,EAAE,SAAS,EAAE,YAAY,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAChI,YAAY,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AACpF,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACvD,YAAY,EAAE,cAAc,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACtE,YAAY,EACX,cAAc,EACd,gBAAgB,EAChB,oBAAoB,EACpB,kBAAkB,EAClB,YAAY,EACZ,aAAa,EACb,eAAe,EACf,gBAAgB,EAChB,gBAAgB,EAChB,cAAc,EACd,4BAA4B,EAC5B,YAAY,EACZ,UAAU,EACV,qBAAqB,EACrB,kBAAkB,EAClB,kBAAkB,EAClB,iBAAiB,EACjB,iBAAiB,EACjB,iBAAiB,EACjB,YAAY,EACZ,sBAAsB,EACtB,qBAAqB,EACrB,MAAM,YAAY,CAAC"}
@@ -0,0 +1,6 @@
1
+ // src/core/index.ts
2
+ export { SentinelClient } from './client.js';
3
+ export { SentinelError, SentinelTimeoutError, ErrorCode, SentinelCode, isRetryableError, isRetryableStatus } from './errors.js';
4
+ export { validateToken, isTokenExpired, decodeTokenLocally } from './auth-utils.js';
5
+ export { createLogger, noopLogger } from './logger.js';
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA,oBAAoB;AACpB,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,oBAAoB,EAAE,SAAS,EAAE,YAAY,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAEhI,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AACpF,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC","sourcesContent":["// src/core/index.ts\nexport { SentinelClient } from './client.js';\nexport { SentinelError, SentinelTimeoutError, ErrorCode, SentinelCode, isRetryableError, isRetryableStatus } from './errors.js';\nexport type { SentinelCodeValue } from './errors.js';\nexport { validateToken, isTokenExpired, decodeTokenLocally } from './auth-utils.js';\nexport { createLogger, noopLogger } from './logger.js';\nexport type { SentinelLogger, LogLevel, LogEntry } from './logger.js';\nexport type {\n\tSentinelConfig,\n\tSentinelResponse,\n\tSentinelResponseBase,\n\tStartLoginResponse,\n\tPollResponse,\n\tClaimResponse,\n\tRefreshResponse,\n\tValidateResponse,\n\tRegisterResponse,\n\tVerifyResponse,\n\tCompletePasswordlessResponse,\n\tDecodedToken,\n\tUserLocals,\n\tSentinelHandleOptions,\n\tAuthHandlerOptions,\n\tBeforeLoginContext,\n\tBeforeLoginReject,\n\tAfterLoginContext,\n\tAfterClaimContext,\n\tErrorContext,\n\tResolveRedirectContext,\n\tLogoutRedirectContext\n} from './types.js';\n\n"]}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Lightweight structured logger for Cloudflare Workers observability.
3
+ *
4
+ * Outputs JSON-structured logs with level, component, message, and optional data.
5
+ * Designed for CF Workers where console.log is captured by Logpush / Tail Workers.
6
+ */
7
+ export type LogLevel = 'debug' | 'info' | 'warn' | 'error';
8
+ export interface LogEntry {
9
+ level: LogLevel;
10
+ component: string;
11
+ message: string;
12
+ data?: Record<string, unknown>;
13
+ timestamp: string;
14
+ }
15
+ export interface SentinelLogger {
16
+ debug(message: string, data?: Record<string, unknown>): void;
17
+ info(message: string, data?: Record<string, unknown>): void;
18
+ warn(message: string, data?: Record<string, unknown>): void;
19
+ error(message: string, data?: Record<string, unknown>): void;
20
+ }
21
+ /**
22
+ * Create a structured logger for a specific component.
23
+ *
24
+ * @param component - Component name (e.g., 'sentinel-client', 'sentinel-handle')
25
+ * @param minLevel - Minimum log level to emit. Default: 'info' in production, 'debug' otherwise.
26
+ */
27
+ export declare function createLogger(component: string, minLevel?: LogLevel): SentinelLogger;
28
+ /** Default no-op logger (silences all output). */
29
+ export declare const noopLogger: SentinelLogger;
30
+ //# sourceMappingURL=logger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/core/logger.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AAEH,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;AAS3D,MAAM,WAAW,QAAQ;IACxB,KAAK,EAAE,QAAQ,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,SAAS,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,cAAc;IAC9B,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAC7D,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAC5D,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAC5D,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;CAC7D;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,GAAE,QAAiB,GAAG,cAAc,CAmC3F;AAED,kDAAkD;AAClD,eAAO,MAAM,UAAU,EAAE,cAKxB,CAAC"}
@@ -0,0 +1,54 @@
1
+ // src/core/logger.ts
2
+ const LEVEL_ORDER = {
3
+ debug: 0,
4
+ info: 1,
5
+ warn: 2,
6
+ error: 3
7
+ };
8
+ /**
9
+ * Create a structured logger for a specific component.
10
+ *
11
+ * @param component - Component name (e.g., 'sentinel-client', 'sentinel-handle')
12
+ * @param minLevel - Minimum log level to emit. Default: 'info' in production, 'debug' otherwise.
13
+ */
14
+ export function createLogger(component, minLevel = 'info') {
15
+ const minOrder = LEVEL_ORDER[minLevel];
16
+ function log(level, message, data) {
17
+ if (LEVEL_ORDER[level] < minOrder)
18
+ return;
19
+ const entry = {
20
+ level,
21
+ component,
22
+ message,
23
+ timestamp: new Date().toISOString(),
24
+ ...(data && Object.keys(data).length > 0 ? { data } : {})
25
+ };
26
+ switch (level) {
27
+ case 'error':
28
+ console.error(JSON.stringify(entry));
29
+ break;
30
+ case 'warn':
31
+ console.warn(JSON.stringify(entry));
32
+ break;
33
+ case 'debug':
34
+ console.debug(JSON.stringify(entry));
35
+ break;
36
+ default:
37
+ console.log(JSON.stringify(entry));
38
+ }
39
+ }
40
+ return {
41
+ debug: (msg, data) => log('debug', msg, data),
42
+ info: (msg, data) => log('info', msg, data),
43
+ warn: (msg, data) => log('warn', msg, data),
44
+ error: (msg, data) => log('error', msg, data)
45
+ };
46
+ }
47
+ /** Default no-op logger (silences all output). */
48
+ export const noopLogger = {
49
+ debug: () => { },
50
+ info: () => { },
51
+ warn: () => { },
52
+ error: () => { }
53
+ };
54
+ //# sourceMappingURL=logger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/core/logger.ts"],"names":[],"mappings":"AAAA,qBAAqB;AAWrB,MAAM,WAAW,GAA6B;IAC7C,KAAK,EAAE,CAAC;IACR,IAAI,EAAE,CAAC;IACP,IAAI,EAAE,CAAC;IACP,KAAK,EAAE,CAAC;CACR,CAAC;AAiBF;;;;;GAKG;AACH,MAAM,UAAU,YAAY,CAAC,SAAiB,EAAE,WAAqB,MAAM;IAC1E,MAAM,QAAQ,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAC;IAEvC,SAAS,GAAG,CAAC,KAAe,EAAE,OAAe,EAAE,IAA8B;QAC5E,IAAI,WAAW,CAAC,KAAK,CAAC,GAAG,QAAQ;YAAE,OAAO;QAE1C,MAAM,KAAK,GAAa;YACvB,KAAK;YACL,SAAS;YACT,OAAO;YACP,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YACnC,GAAG,CAAC,IAAI,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SACzD,CAAC;QAEF,QAAQ,KAAK,EAAE,CAAC;YACf,KAAK,OAAO;gBACX,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC;gBACrC,MAAM;YACP,KAAK,MAAM;gBACV,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC;gBACpC,MAAM;YACP,KAAK,OAAO;gBACX,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC;gBACrC,MAAM;YACP;gBACC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC;QACrC,CAAC;IACF,CAAC;IAED,OAAO;QACN,KAAK,EAAE,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,EAAE,IAAI,CAAC;QAC7C,IAAI,EAAE,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,EAAE,IAAI,CAAC;QAC3C,IAAI,EAAE,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,EAAE,IAAI,CAAC;QAC3C,KAAK,EAAE,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,EAAE,IAAI,CAAC;KAC7C,CAAC;AACH,CAAC;AAED,kDAAkD;AAClD,MAAM,CAAC,MAAM,UAAU,GAAmB;IACzC,KAAK,EAAE,GAAG,EAAE,GAAE,CAAC;IACf,IAAI,EAAE,GAAG,EAAE,GAAE,CAAC;IACd,IAAI,EAAE,GAAG,EAAE,GAAE,CAAC;IACd,KAAK,EAAE,GAAG,EAAE,GAAE,CAAC;CACf,CAAC","sourcesContent":["// src/core/logger.ts\n\n/**\n * Lightweight structured logger for Cloudflare Workers observability.\n *\n * Outputs JSON-structured logs with level, component, message, and optional data.\n * Designed for CF Workers where console.log is captured by Logpush / Tail Workers.\n */\n\nexport type LogLevel = 'debug' | 'info' | 'warn' | 'error';\n\nconst LEVEL_ORDER: Record<LogLevel, number> = {\n\tdebug: 0,\n\tinfo: 1,\n\twarn: 2,\n\terror: 3\n};\n\nexport interface LogEntry {\n\tlevel: LogLevel;\n\tcomponent: string;\n\tmessage: string;\n\tdata?: Record<string, unknown>;\n\ttimestamp: string;\n}\n\nexport interface SentinelLogger {\n\tdebug(message: string, data?: Record<string, unknown>): void;\n\tinfo(message: string, data?: Record<string, unknown>): void;\n\twarn(message: string, data?: Record<string, unknown>): void;\n\terror(message: string, data?: Record<string, unknown>): void;\n}\n\n/**\n * Create a structured logger for a specific component.\n *\n * @param component - Component name (e.g., 'sentinel-client', 'sentinel-handle')\n * @param minLevel - Minimum log level to emit. Default: 'info' in production, 'debug' otherwise.\n */\nexport function createLogger(component: string, minLevel: LogLevel = 'info'): SentinelLogger {\n\tconst minOrder = LEVEL_ORDER[minLevel];\n\n\tfunction log(level: LogLevel, message: string, data?: Record<string, unknown>): void {\n\t\tif (LEVEL_ORDER[level] < minOrder) return;\n\n\t\tconst entry: LogEntry = {\n\t\t\tlevel,\n\t\t\tcomponent,\n\t\t\tmessage,\n\t\t\ttimestamp: new Date().toISOString(),\n\t\t\t...(data && Object.keys(data).length > 0 ? { data } : {})\n\t\t};\n\n\t\tswitch (level) {\n\t\t\tcase 'error':\n\t\t\t\tconsole.error(JSON.stringify(entry));\n\t\t\t\tbreak;\n\t\t\tcase 'warn':\n\t\t\t\tconsole.warn(JSON.stringify(entry));\n\t\t\t\tbreak;\n\t\t\tcase 'debug':\n\t\t\t\tconsole.debug(JSON.stringify(entry));\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tconsole.log(JSON.stringify(entry));\n\t\t}\n\t}\n\n\treturn {\n\t\tdebug: (msg, data) => log('debug', msg, data),\n\t\tinfo: (msg, data) => log('info', msg, data),\n\t\twarn: (msg, data) => log('warn', msg, data),\n\t\terror: (msg, data) => log('error', msg, data)\n\t};\n}\n\n/** Default no-op logger (silences all output). */\nexport const noopLogger: SentinelLogger = {\n\tdebug: () => {},\n\tinfo: () => {},\n\twarn: () => {},\n\terror: () => {}\n};\n"]}