@atlas-id/contracts 0.1.0 → 0.2.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.
- package/dist/esm/index.js +399 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/index.d.cts +911 -0
- package/dist/index.js +457 -0
- package/dist/index.js.map +1 -0
- package/package.json +1 -1
- package/src/schemas/index.ts +8 -1
- package/src/versioning.test.ts +0 -1
|
@@ -0,0 +1,399 @@
|
|
|
1
|
+
// src/events.ts
|
|
2
|
+
var NOTIFICATION_EVENT_TYPES = {
|
|
3
|
+
REQUESTED: "notifications.requested",
|
|
4
|
+
DISPATCHED: "notifications.dispatched",
|
|
5
|
+
DELIVERED: "notifications.delivered",
|
|
6
|
+
FAILED: "notifications.failed"
|
|
7
|
+
};
|
|
8
|
+
var OAUTH_CONNECTION_EVENT_TYPES = {
|
|
9
|
+
LINKED: "oauth.connection.linked",
|
|
10
|
+
TOKEN_REFRESHED: "oauth.connection.token_refreshed",
|
|
11
|
+
REVOKED: "oauth.connection.revoked"
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
// src/notifications.ts
|
|
15
|
+
function isEmailNotificationRequest(request) {
|
|
16
|
+
return request.channel === "email";
|
|
17
|
+
}
|
|
18
|
+
function isSmsNotificationRequest(request) {
|
|
19
|
+
return request.channel === "sms";
|
|
20
|
+
}
|
|
21
|
+
function isWebhookNotificationRequest(request) {
|
|
22
|
+
return request.channel === "webhook";
|
|
23
|
+
}
|
|
24
|
+
var notificationEvents = {
|
|
25
|
+
requested: {
|
|
26
|
+
type: NOTIFICATION_EVENT_TYPES.REQUESTED,
|
|
27
|
+
version: "1.0.0",
|
|
28
|
+
schema: "notifications.requested@1.0.0",
|
|
29
|
+
example: {
|
|
30
|
+
id: "evt_123",
|
|
31
|
+
type: NOTIFICATION_EVENT_TYPES.REQUESTED,
|
|
32
|
+
version: "1.0.0",
|
|
33
|
+
occurredAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
34
|
+
payload: {
|
|
35
|
+
notification: {
|
|
36
|
+
id: "ntf_123",
|
|
37
|
+
request: {
|
|
38
|
+
channel: "email",
|
|
39
|
+
projectId: "proj_123",
|
|
40
|
+
to: "user@example.com",
|
|
41
|
+
templateId: "tmpl_welcome"
|
|
42
|
+
},
|
|
43
|
+
status: "requested",
|
|
44
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
45
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
meta: {
|
|
49
|
+
source: "notifications-service"
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
dispatched: {
|
|
54
|
+
type: NOTIFICATION_EVENT_TYPES.DISPATCHED,
|
|
55
|
+
version: "1.0.0",
|
|
56
|
+
schema: "notifications.dispatched@1.0.0",
|
|
57
|
+
example: {
|
|
58
|
+
id: "evt_124",
|
|
59
|
+
type: NOTIFICATION_EVENT_TYPES.DISPATCHED,
|
|
60
|
+
version: "1.0.0",
|
|
61
|
+
occurredAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
62
|
+
payload: {
|
|
63
|
+
notificationId: "ntf_123",
|
|
64
|
+
channel: "email",
|
|
65
|
+
provider: "postmark",
|
|
66
|
+
providerMessageId: "msg_abc"
|
|
67
|
+
},
|
|
68
|
+
meta: {
|
|
69
|
+
source: "notifications-service"
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
delivered: {
|
|
74
|
+
type: NOTIFICATION_EVENT_TYPES.DELIVERED,
|
|
75
|
+
version: "1.0.0",
|
|
76
|
+
schema: "notifications.delivered@1.0.0",
|
|
77
|
+
example: {
|
|
78
|
+
id: "evt_125",
|
|
79
|
+
type: NOTIFICATION_EVENT_TYPES.DELIVERED,
|
|
80
|
+
version: "1.0.0",
|
|
81
|
+
occurredAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
82
|
+
payload: {
|
|
83
|
+
notificationId: "ntf_123",
|
|
84
|
+
deliveredAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
85
|
+
},
|
|
86
|
+
meta: {
|
|
87
|
+
source: "notifications-service"
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
failed: {
|
|
92
|
+
type: NOTIFICATION_EVENT_TYPES.FAILED,
|
|
93
|
+
version: "1.0.0",
|
|
94
|
+
schema: "notifications.failed@1.0.0",
|
|
95
|
+
example: {
|
|
96
|
+
id: "evt_126",
|
|
97
|
+
type: NOTIFICATION_EVENT_TYPES.FAILED,
|
|
98
|
+
version: "1.0.0",
|
|
99
|
+
occurredAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
100
|
+
payload: {
|
|
101
|
+
notificationId: "ntf_123",
|
|
102
|
+
failedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
103
|
+
errorCode: "bounce",
|
|
104
|
+
message: "Mailbox unreachable",
|
|
105
|
+
retriable: false
|
|
106
|
+
},
|
|
107
|
+
meta: {
|
|
108
|
+
source: "notifications-service"
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
// src/oauth-connections.ts
|
|
115
|
+
function isActiveConnection(connection) {
|
|
116
|
+
return connection.status === "active";
|
|
117
|
+
}
|
|
118
|
+
function isRevokedConnection(connection) {
|
|
119
|
+
return connection.status === "revoked";
|
|
120
|
+
}
|
|
121
|
+
function isExpiredConnection(connection) {
|
|
122
|
+
if (connection.status === "expired") return true;
|
|
123
|
+
if (!connection.expiresAt) return false;
|
|
124
|
+
return new Date(connection.expiresAt) < /* @__PURE__ */ new Date();
|
|
125
|
+
}
|
|
126
|
+
var oauthConnectionEvents = {
|
|
127
|
+
linked: {
|
|
128
|
+
type: OAUTH_CONNECTION_EVENT_TYPES.LINKED,
|
|
129
|
+
version: "1.0.0",
|
|
130
|
+
schema: "oauth.connection.linked@1.0.0",
|
|
131
|
+
example: {
|
|
132
|
+
id: "evt_conn_1",
|
|
133
|
+
type: OAUTH_CONNECTION_EVENT_TYPES.LINKED,
|
|
134
|
+
version: "1.0.0",
|
|
135
|
+
occurredAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
136
|
+
payload: {
|
|
137
|
+
connection: {
|
|
138
|
+
id: "conn_123",
|
|
139
|
+
providerId: "google",
|
|
140
|
+
projectId: "proj_123",
|
|
141
|
+
userId: "user_123",
|
|
142
|
+
scope: ["calendar", "email"],
|
|
143
|
+
status: "active",
|
|
144
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
145
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
146
|
+
},
|
|
147
|
+
tokenSet: {
|
|
148
|
+
accessToken: "access-token",
|
|
149
|
+
refreshToken: "refresh-token",
|
|
150
|
+
issuedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
151
|
+
expiresIn: 3600
|
|
152
|
+
}
|
|
153
|
+
},
|
|
154
|
+
meta: {
|
|
155
|
+
source: "oauth-connections-service"
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
},
|
|
159
|
+
tokenRefreshed: {
|
|
160
|
+
type: OAUTH_CONNECTION_EVENT_TYPES.TOKEN_REFRESHED,
|
|
161
|
+
version: "1.0.0",
|
|
162
|
+
schema: "oauth.connection.token_refreshed@1.0.0",
|
|
163
|
+
example: {
|
|
164
|
+
id: "evt_conn_2",
|
|
165
|
+
type: OAUTH_CONNECTION_EVENT_TYPES.TOKEN_REFRESHED,
|
|
166
|
+
version: "1.0.0",
|
|
167
|
+
occurredAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
168
|
+
payload: {
|
|
169
|
+
connectionId: "conn_123",
|
|
170
|
+
tokenSet: {
|
|
171
|
+
accessToken: "access-token-2",
|
|
172
|
+
refreshToken: "refresh-token",
|
|
173
|
+
issuedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
174
|
+
expiresIn: 3600
|
|
175
|
+
}
|
|
176
|
+
},
|
|
177
|
+
meta: {
|
|
178
|
+
source: "oauth-connections-service"
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
},
|
|
182
|
+
revoked: {
|
|
183
|
+
type: OAUTH_CONNECTION_EVENT_TYPES.REVOKED,
|
|
184
|
+
version: "1.0.0",
|
|
185
|
+
schema: "oauth.connection.revoked@1.0.0",
|
|
186
|
+
example: {
|
|
187
|
+
id: "evt_conn_3",
|
|
188
|
+
type: OAUTH_CONNECTION_EVENT_TYPES.REVOKED,
|
|
189
|
+
version: "1.0.0",
|
|
190
|
+
occurredAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
191
|
+
payload: {
|
|
192
|
+
connectionId: "conn_123",
|
|
193
|
+
reason: "security"
|
|
194
|
+
},
|
|
195
|
+
meta: {
|
|
196
|
+
source: "oauth-connections-service"
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
// src/versioning.ts
|
|
203
|
+
function isValidSemanticVersion(version) {
|
|
204
|
+
return /^\d+\.\d+\.\d+$/.test(version);
|
|
205
|
+
}
|
|
206
|
+
function parseSemanticVersion(version) {
|
|
207
|
+
const match = version.match(/^(\d+)\.(\d+)\.(\d+)$/);
|
|
208
|
+
if (!match) return null;
|
|
209
|
+
return {
|
|
210
|
+
major: parseInt(match[1], 10),
|
|
211
|
+
minor: parseInt(match[2], 10),
|
|
212
|
+
patch: parseInt(match[3], 10)
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
function compareSemanticVersions(version1, version2) {
|
|
216
|
+
const v1 = parseSemanticVersion(version1);
|
|
217
|
+
const v2 = parseSemanticVersion(version2);
|
|
218
|
+
if (!v1 || !v2) {
|
|
219
|
+
throw new Error(`Invalid semantic version: ${!v1 ? version1 : version2}`);
|
|
220
|
+
}
|
|
221
|
+
if (v1.major !== v2.major) {
|
|
222
|
+
return v1.major - v2.major;
|
|
223
|
+
}
|
|
224
|
+
if (v1.minor !== v2.minor) {
|
|
225
|
+
return v1.minor - v2.minor;
|
|
226
|
+
}
|
|
227
|
+
return v1.patch - v2.patch;
|
|
228
|
+
}
|
|
229
|
+
function isVersionGreaterThan(version1, version2) {
|
|
230
|
+
return compareSemanticVersions(version1, version2) > 0;
|
|
231
|
+
}
|
|
232
|
+
function isVersionLessThan(version1, version2) {
|
|
233
|
+
return compareSemanticVersions(version1, version2) < 0;
|
|
234
|
+
}
|
|
235
|
+
function isVersionGreaterOrEqual(version1, version2) {
|
|
236
|
+
return compareSemanticVersions(version1, version2) >= 0;
|
|
237
|
+
}
|
|
238
|
+
function isVersionLessOrEqual(version1, version2) {
|
|
239
|
+
return compareSemanticVersions(version1, version2) <= 0;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// src/utils/validation.ts
|
|
243
|
+
var UUID_V4_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
244
|
+
var EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
245
|
+
var E164_PHONE_REGEX = /^\+[1-9]\d{6,14}$/;
|
|
246
|
+
function isValidUuid(value) {
|
|
247
|
+
return UUID_V4_REGEX.test(value);
|
|
248
|
+
}
|
|
249
|
+
function isValidEmail(value) {
|
|
250
|
+
return EMAIL_REGEX.test(value);
|
|
251
|
+
}
|
|
252
|
+
function isValidUrl(value) {
|
|
253
|
+
try {
|
|
254
|
+
const URLConstructor = globalThis.URL;
|
|
255
|
+
if (URLConstructor) {
|
|
256
|
+
new URLConstructor(value);
|
|
257
|
+
return true;
|
|
258
|
+
}
|
|
259
|
+
return /^https?:\/\/.+\..+/.test(value);
|
|
260
|
+
} catch {
|
|
261
|
+
return false;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
function isValidE164Phone(value) {
|
|
265
|
+
return E164_PHONE_REGEX.test(value);
|
|
266
|
+
}
|
|
267
|
+
function isValidIso8601(value) {
|
|
268
|
+
const date = new Date(value);
|
|
269
|
+
return !isNaN(date.getTime()) && value === date.toISOString();
|
|
270
|
+
}
|
|
271
|
+
function hasRequiredKeys(obj, requiredKeys) {
|
|
272
|
+
return requiredKeys.every((key) => key in obj);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// src/schemas/index.ts
|
|
276
|
+
import { z } from "zod";
|
|
277
|
+
var semanticVersionSchema = z.string().refine(
|
|
278
|
+
isValidSemanticVersion,
|
|
279
|
+
{ message: "Debe ser una versi\xF3n sem\xE1ntica v\xE1lida (MAJOR.MINOR.PATCH)" }
|
|
280
|
+
);
|
|
281
|
+
var eventMetaSchema = z.object({
|
|
282
|
+
traceId: z.string().optional(),
|
|
283
|
+
spanId: z.string().optional(),
|
|
284
|
+
correlationId: z.string().optional(),
|
|
285
|
+
causationId: z.string().optional(),
|
|
286
|
+
tenantId: z.string().optional(),
|
|
287
|
+
projectId: z.string().optional(),
|
|
288
|
+
source: z.string().min(1)
|
|
289
|
+
});
|
|
290
|
+
function createEventEnvelopeSchema(payloadSchema) {
|
|
291
|
+
return z.object({
|
|
292
|
+
id: z.string().min(1),
|
|
293
|
+
type: z.string().min(1),
|
|
294
|
+
version: semanticVersionSchema,
|
|
295
|
+
occurredAt: z.string().datetime(),
|
|
296
|
+
payload: payloadSchema,
|
|
297
|
+
meta: eventMetaSchema
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
var emailNotificationRequestSchema = z.object({
|
|
301
|
+
channel: z.literal("email"),
|
|
302
|
+
projectId: z.string().min(1),
|
|
303
|
+
tenantId: z.string().optional(),
|
|
304
|
+
locale: z.string().optional(),
|
|
305
|
+
deduplicationKey: z.string().optional(),
|
|
306
|
+
expiresAt: z.string().datetime().optional(),
|
|
307
|
+
metadata: z.record(z.string()).optional(),
|
|
308
|
+
to: z.string().email(),
|
|
309
|
+
templateId: z.string().min(1),
|
|
310
|
+
variables: z.record(z.union([z.string(), z.number(), z.boolean(), z.null()])).optional(),
|
|
311
|
+
cc: z.array(z.string().email()).optional(),
|
|
312
|
+
bcc: z.array(z.string().email()).optional(),
|
|
313
|
+
replyTo: z.string().email().optional()
|
|
314
|
+
});
|
|
315
|
+
var smsNotificationRequestSchema = z.object({
|
|
316
|
+
channel: z.literal("sms"),
|
|
317
|
+
projectId: z.string().min(1),
|
|
318
|
+
tenantId: z.string().optional(),
|
|
319
|
+
locale: z.string().optional(),
|
|
320
|
+
deduplicationKey: z.string().optional(),
|
|
321
|
+
expiresAt: z.string().datetime().optional(),
|
|
322
|
+
metadata: z.record(z.string()).optional(),
|
|
323
|
+
to: z.string().min(1),
|
|
324
|
+
templateId: z.string().min(1),
|
|
325
|
+
variables: z.record(z.union([z.string(), z.number(), z.boolean(), z.null()])).optional()
|
|
326
|
+
});
|
|
327
|
+
var webhookNotificationRequestSchema = z.object({
|
|
328
|
+
channel: z.literal("webhook"),
|
|
329
|
+
projectId: z.string().min(1),
|
|
330
|
+
tenantId: z.string().optional(),
|
|
331
|
+
locale: z.string().optional(),
|
|
332
|
+
deduplicationKey: z.string().optional(),
|
|
333
|
+
expiresAt: z.string().datetime().optional(),
|
|
334
|
+
metadata: z.record(z.string()).optional(),
|
|
335
|
+
url: z.string().url(),
|
|
336
|
+
signatureVersion: semanticVersionSchema,
|
|
337
|
+
body: z.unknown()
|
|
338
|
+
});
|
|
339
|
+
var notificationRequestSchema = z.discriminatedUnion("channel", [
|
|
340
|
+
emailNotificationRequestSchema,
|
|
341
|
+
smsNotificationRequestSchema,
|
|
342
|
+
webhookNotificationRequestSchema
|
|
343
|
+
]);
|
|
344
|
+
var oauthConnectionSchema = z.object({
|
|
345
|
+
id: z.string().min(1),
|
|
346
|
+
providerId: z.string().min(1),
|
|
347
|
+
projectId: z.string().min(1),
|
|
348
|
+
tenantId: z.string().optional(),
|
|
349
|
+
userId: z.string().min(1),
|
|
350
|
+
scope: z.array(z.string()),
|
|
351
|
+
expiresAt: z.string().datetime().optional(),
|
|
352
|
+
createdAt: z.string().datetime(),
|
|
353
|
+
updatedAt: z.string().datetime(),
|
|
354
|
+
status: z.enum(["active", "refreshing", "revoked", "expired"])
|
|
355
|
+
});
|
|
356
|
+
var tokenSetSchema = z.object({
|
|
357
|
+
accessToken: z.string().min(1),
|
|
358
|
+
refreshToken: z.string().optional(),
|
|
359
|
+
expiresIn: z.number().positive().optional(),
|
|
360
|
+
tokenType: z.string().optional(),
|
|
361
|
+
issuedAt: z.string().datetime(),
|
|
362
|
+
expiresAt: z.string().datetime().optional(),
|
|
363
|
+
idToken: z.string().optional()
|
|
364
|
+
});
|
|
365
|
+
export {
|
|
366
|
+
NOTIFICATION_EVENT_TYPES,
|
|
367
|
+
OAUTH_CONNECTION_EVENT_TYPES,
|
|
368
|
+
compareSemanticVersions,
|
|
369
|
+
createEventEnvelopeSchema,
|
|
370
|
+
emailNotificationRequestSchema,
|
|
371
|
+
eventMetaSchema,
|
|
372
|
+
hasRequiredKeys,
|
|
373
|
+
isActiveConnection,
|
|
374
|
+
isEmailNotificationRequest,
|
|
375
|
+
isExpiredConnection,
|
|
376
|
+
isRevokedConnection,
|
|
377
|
+
isSmsNotificationRequest,
|
|
378
|
+
isValidE164Phone,
|
|
379
|
+
isValidEmail,
|
|
380
|
+
isValidIso8601,
|
|
381
|
+
isValidSemanticVersion,
|
|
382
|
+
isValidUrl,
|
|
383
|
+
isValidUuid,
|
|
384
|
+
isVersionGreaterOrEqual,
|
|
385
|
+
isVersionGreaterThan,
|
|
386
|
+
isVersionLessOrEqual,
|
|
387
|
+
isVersionLessThan,
|
|
388
|
+
isWebhookNotificationRequest,
|
|
389
|
+
notificationEvents,
|
|
390
|
+
notificationRequestSchema,
|
|
391
|
+
oauthConnectionEvents,
|
|
392
|
+
oauthConnectionSchema,
|
|
393
|
+
parseSemanticVersion,
|
|
394
|
+
semanticVersionSchema,
|
|
395
|
+
smsNotificationRequestSchema,
|
|
396
|
+
tokenSetSchema,
|
|
397
|
+
webhookNotificationRequestSchema
|
|
398
|
+
};
|
|
399
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/events.ts","../../src/notifications.ts","../../src/oauth-connections.ts","../../src/versioning.ts","../../src/utils/validation.ts","../../src/schemas/index.ts"],"sourcesContent":["import { SemanticVersion } from './versioning';\n\n/**\n * Constantes para tipos de eventos del sistema de notificaciones.\n * Centraliza los nombres de eventos para evitar errores de tipeo.\n */\nexport const NOTIFICATION_EVENT_TYPES = {\n REQUESTED: 'notifications.requested',\n DISPATCHED: 'notifications.dispatched',\n DELIVERED: 'notifications.delivered',\n FAILED: 'notifications.failed',\n} as const;\n\n/**\n * Constantes para tipos de eventos de conexiones OAuth.\n */\nexport const OAUTH_CONNECTION_EVENT_TYPES = {\n LINKED: 'oauth.connection.linked',\n TOKEN_REFRESHED: 'oauth.connection.token_refreshed',\n REVOKED: 'oauth.connection.revoked',\n} as const;\n\n/**\n * Metadata asociada a un evento para trazabilidad y correlación en sistemas distribuidos.\n * \n * @property traceId - ID de traza para distributed tracing (OpenTelemetry, etc.)\n * @property spanId - ID de span dentro de la traza\n * @property correlationId - ID para correlacionar eventos relacionados en un flujo de negocio\n * @property causationId - ID del evento que causó este evento (event sourcing)\n * @property tenantId - ID del tenant en arquitecturas multi-tenant\n * @property projectId - ID del proyecto asociado\n * @property source - Nombre del servicio o componente que originó el evento\n */\nexport type EventMeta = {\n traceId?: string;\n spanId?: string;\n correlationId?: string;\n causationId?: string;\n tenantId?: string;\n projectId?: string;\n source: string;\n};\n\n/**\n * Envelope que envuelve un evento con toda su metadata y información de versionado.\n * Esta estructura permite la serialización, transmisión y procesamiento de eventos\n * en sistemas distribuidos con garantías de trazabilidad.\n * \n * @template TPayload - Tipo del payload específico del evento\n * @template TMeta - Tipo de metadata (por defecto EventMeta)\n * \n * @property id - Identificador único del evento (recomendado: UUID v4)\n * @property type - Tipo del evento (ej: 'notifications.requested')\n * @property version - Versión semántica del contrato del evento\n * @property occurredAt - Timestamp ISO 8601 de cuándo ocurrió el evento\n * @property payload - Datos específicos del evento\n * @property meta - Metadata de trazabilidad y contexto\n */\nexport type EventEnvelope<TPayload, TMeta extends EventMeta = EventMeta> = {\n id: string;\n type: string;\n version: SemanticVersion;\n occurredAt: string;\n payload: TPayload;\n meta: TMeta;\n};\n\n/**\n * Contrato que define la estructura y versión de un tipo de evento.\n * Incluye un ejemplo para documentación y validación.\n * \n * @template TPayload - Tipo del payload del evento\n * @template TMeta - Tipo de metadata del evento\n * \n * @property type - Tipo del evento\n * @property version - Versión semántica del contrato\n * @property schema - Identificador del esquema (formato: tipo@version)\n * @property example - Ejemplo completo de un evento de este tipo\n */\nexport type EventContract<TPayload, TMeta extends EventMeta = EventMeta> = {\n type: string;\n version: SemanticVersion;\n schema: string;\n example: EventEnvelope<TPayload, TMeta>;\n};\n","import { EventContract, EventEnvelope, NOTIFICATION_EVENT_TYPES } from './events';\nimport { SemanticVersion } from './versioning';\n\n/**\n * Canales disponibles para el envío de notificaciones.\n */\nexport type NotificationChannel = 'email' | 'sms' | 'webhook';\n\n/**\n * Base común para todas las solicitudes de notificación.\n * Contiene campos compartidos entre todos los tipos de canales.\n * \n * @property channel - Canal de notificación a utilizar\n * @property projectId - ID del proyecto que solicita la notificación\n * @property tenantId - ID del tenant (opcional, para multi-tenancy)\n * @property locale - Código de idioma para localización (ej: 'es-ES', 'en-US')\n * @property deduplicationKey - Clave opcional para prevenir duplicados\n * @property expiresAt - Timestamp ISO 8601 de expiración de la notificación\n * @property metadata - Metadatos adicionales como pares clave-valor\n */\nexport type BaseNotificationRequest = {\n channel: NotificationChannel;\n projectId: string;\n tenantId?: string;\n locale?: string;\n deduplicationKey?: string;\n expiresAt?: string;\n metadata?: Record<string, string>;\n};\n\n/**\n * Solicitud de notificación por correo electrónico.\n * \n * @property channel - Debe ser 'email'\n * @property to - Dirección de correo electrónico del destinatario\n * @property templateId - ID del template de email a utilizar\n * @property variables - Variables para reemplazar en el template\n * @property cc - Lista opcional de direcciones en copia\n * @property bcc - Lista opcional de direcciones en copia oculta\n * @property replyTo - Dirección de respuesta opcional\n */\nexport type EmailNotificationRequest = BaseNotificationRequest & {\n channel: 'email';\n to: string;\n templateId: string;\n variables?: Record<string, string | number | boolean | null>;\n cc?: string[];\n bcc?: string[];\n replyTo?: string;\n};\n\n/**\n * Solicitud de notificación por SMS.\n * \n * @property channel - Debe ser 'sms'\n * @property to - Número de teléfono del destinatario (formato E.164 recomendado)\n * @property templateId - ID del template de SMS a utilizar\n * @property variables - Variables para reemplazar en el template\n */\nexport type SmsNotificationRequest = BaseNotificationRequest & {\n channel: 'sms';\n to: string;\n templateId: string;\n variables?: Record<string, string | number | boolean | null>;\n};\n\n/**\n * Solicitud de notificación por webhook.\n * \n * @property channel - Debe ser 'webhook'\n * @property url - URL del endpoint que recibirá el webhook\n * @property signatureVersion - Versión del algoritmo de firma para verificación\n * @property body - Cuerpo del webhook (estructura libre)\n */\nexport type WebhookNotificationRequest = BaseNotificationRequest & {\n channel: 'webhook';\n url: string;\n signatureVersion: SemanticVersion;\n body: unknown;\n};\n\n/**\n * Unión de todos los tipos de solicitudes de notificación.\n * Utiliza discriminated union basado en el campo 'channel'.\n */\nexport type NotificationRequest =\n | EmailNotificationRequest\n | SmsNotificationRequest\n | WebhookNotificationRequest;\n\n/**\n * Estados posibles de una notificación en su ciclo de vida.\n * \n * - requested: Solicitud creada, pendiente de procesamiento\n * - scheduled: Programada para envío futuro\n * - dispatched: Enviada al proveedor externo\n * - delivered: Confirmada como entregada por el proveedor\n * - failed: Falló el envío o entrega\n * - cancelled: Cancelada antes de ser enviada\n */\nexport type NotificationStatus =\n | 'requested'\n | 'scheduled'\n | 'dispatched'\n | 'delivered'\n | 'failed'\n | 'cancelled';\n\n/**\n * Envelope que contiene una notificación con su estado y metadata.\n * \n * @property id - Identificador único de la notificación\n * @property request - Solicitud original de la notificación\n * @property status - Estado actual de la notificación\n * @property createdAt - Timestamp ISO 8601 de creación\n * @property updatedAt - Timestamp ISO 8601 de última actualización\n * @property lastError - Información del último error si el estado es 'failed'\n * @property providerResponseId - ID de respuesta del proveedor externo\n */\nexport type NotificationEnvelope = {\n id: string;\n request: NotificationRequest;\n status: NotificationStatus;\n createdAt: string;\n updatedAt: string;\n lastError?: {\n code: string;\n message: string;\n retriable: boolean;\n };\n providerResponseId?: string;\n};\n\n/**\n * Type guard para verificar si una solicitud es de tipo email.\n * \n * @param request - Solicitud a verificar\n * @returns true si es EmailNotificationRequest\n */\nexport function isEmailNotificationRequest(\n request: NotificationRequest\n): request is EmailNotificationRequest {\n return request.channel === 'email';\n}\n\n/**\n * Type guard para verificar si una solicitud es de tipo SMS.\n * \n * @param request - Solicitud a verificar\n * @returns true si es SmsNotificationRequest\n */\nexport function isSmsNotificationRequest(\n request: NotificationRequest\n): request is SmsNotificationRequest {\n return request.channel === 'sms';\n}\n\n/**\n * Type guard para verificar si una solicitud es de tipo webhook.\n * \n * @param request - Solicitud a verificar\n * @returns true si es WebhookNotificationRequest\n */\nexport function isWebhookNotificationRequest(\n request: NotificationRequest\n): request is WebhookNotificationRequest {\n return request.channel === 'webhook';\n}\n\ntype RequestedPayload = {\n notification: NotificationEnvelope;\n};\n\ntype DispatchedPayload = {\n notificationId: string;\n channel: NotificationChannel;\n provider: string;\n providerMessageId?: string;\n};\n\ntype DeliveredPayload = {\n notificationId: string;\n deliveredAt: string;\n};\n\ntype FailedPayload = {\n notificationId: string;\n failedAt: string;\n errorCode: string;\n message: string;\n retriable: boolean;\n};\n\n/**\n * Contratos de eventos para el sistema de notificaciones.\n * Define los eventos que se emiten durante el ciclo de vida de una notificación.\n */\nexport const notificationEvents: Record<\n 'requested' | 'dispatched' | 'delivered' | 'failed',\n EventContract<RequestedPayload | DispatchedPayload | DeliveredPayload | FailedPayload>\n> = {\n requested: {\n type: NOTIFICATION_EVENT_TYPES.REQUESTED,\n version: '1.0.0',\n schema: 'notifications.requested@1.0.0',\n example: {\n id: 'evt_123',\n type: NOTIFICATION_EVENT_TYPES.REQUESTED,\n version: '1.0.0',\n occurredAt: new Date().toISOString(),\n payload: {\n notification: {\n id: 'ntf_123',\n request: {\n channel: 'email',\n projectId: 'proj_123',\n to: 'user@example.com',\n templateId: 'tmpl_welcome',\n },\n status: 'requested',\n createdAt: new Date().toISOString(),\n updatedAt: new Date().toISOString(),\n },\n },\n meta: {\n source: 'notifications-service',\n },\n },\n },\n dispatched: {\n type: NOTIFICATION_EVENT_TYPES.DISPATCHED,\n version: '1.0.0',\n schema: 'notifications.dispatched@1.0.0',\n example: {\n id: 'evt_124',\n type: NOTIFICATION_EVENT_TYPES.DISPATCHED,\n version: '1.0.0',\n occurredAt: new Date().toISOString(),\n payload: {\n notificationId: 'ntf_123',\n channel: 'email',\n provider: 'postmark',\n providerMessageId: 'msg_abc',\n },\n meta: {\n source: 'notifications-service',\n },\n },\n },\n delivered: {\n type: NOTIFICATION_EVENT_TYPES.DELIVERED,\n version: '1.0.0',\n schema: 'notifications.delivered@1.0.0',\n example: {\n id: 'evt_125',\n type: NOTIFICATION_EVENT_TYPES.DELIVERED,\n version: '1.0.0',\n occurredAt: new Date().toISOString(),\n payload: {\n notificationId: 'ntf_123',\n deliveredAt: new Date().toISOString(),\n },\n meta: {\n source: 'notifications-service',\n },\n },\n },\n failed: {\n type: NOTIFICATION_EVENT_TYPES.FAILED,\n version: '1.0.0',\n schema: 'notifications.failed@1.0.0',\n example: {\n id: 'evt_126',\n type: NOTIFICATION_EVENT_TYPES.FAILED,\n version: '1.0.0',\n occurredAt: new Date().toISOString(),\n payload: {\n notificationId: 'ntf_123',\n failedAt: new Date().toISOString(),\n errorCode: 'bounce',\n message: 'Mailbox unreachable',\n retriable: false,\n },\n meta: {\n source: 'notifications-service',\n },\n },\n },\n};\n\n/**\n * Unión de todos los tipos de eventos de notificaciones.\n */\nexport type NotificationEvent =\n | EventEnvelope<RequestedPayload>\n | EventEnvelope<DispatchedPayload>\n | EventEnvelope<DeliveredPayload>\n | EventEnvelope<FailedPayload>;\n","import { EventContract, EventEnvelope, OAUTH_CONNECTION_EVENT_TYPES } from './events';\n\n/**\n * Categorías de proveedores OAuth soportados.\n * \n * - oauth2: OAuth 2.0 estándar\n * - oidc: OpenID Connect (extensión de OAuth 2.0)\n * - saml: Security Assertion Markup Language\n */\nexport type OAuthProviderCategory = 'oauth2' | 'oidc' | 'saml';\n\n/**\n * Definición de un proveedor OAuth configurado en el sistema.\n * \n * @property id - Identificador único del proveedor (ej: 'google', 'github')\n * @property name - Nombre legible del proveedor\n * @property category - Categoría del protocolo OAuth\n * @property authorizationUrl - URL del endpoint de autorización\n * @property tokenUrl - URL del endpoint de intercambio de tokens\n * @property scopes - Lista de scopes soportados por el proveedor\n * @property supportsPkce - Indica si el proveedor soporta PKCE (Proof Key for Code Exchange)\n * @property requiresWebhookForRefresh - Indica si requiere webhook para refrescar tokens\n */\nexport type OAuthProvider = {\n id: string;\n name: string;\n category: OAuthProviderCategory;\n authorizationUrl: string;\n tokenUrl: string;\n scopes: string[];\n supportsPkce: boolean;\n requiresWebhookForRefresh?: boolean;\n};\n\n/**\n * Conexión OAuth establecida entre un usuario y un proveedor.\n * Representa la autorización activa de un usuario para acceder a recursos del proveedor.\n * \n * @property id - Identificador único de la conexión\n * @property providerId - ID del proveedor OAuth\n * @property projectId - ID del proyecto asociado\n * @property tenantId - ID del tenant (opcional, para multi-tenancy)\n * @property userId - ID del usuario que autorizó la conexión\n * @property scope - Lista de scopes autorizados en esta conexión\n * @property expiresAt - Timestamp ISO 8601 de expiración de la conexión\n * @property createdAt - Timestamp ISO 8601 de creación\n * @property updatedAt - Timestamp ISO 8601 de última actualización\n * @property status - Estado actual de la conexión\n */\nexport type OAuthConnection = {\n id: string;\n providerId: string;\n projectId: string;\n tenantId?: string;\n userId: string;\n scope: string[];\n expiresAt?: string;\n createdAt: string;\n updatedAt: string;\n status: 'active' | 'refreshing' | 'revoked' | 'expired';\n};\n\n/**\n * Conjunto de tokens OAuth obtenidos de un proveedor.\n * \n * @property accessToken - Token de acceso para autenticación\n * @property refreshToken - Token para refrescar el access token (opcional)\n * @property expiresIn - Tiempo de expiración en segundos (opcional)\n * @property tokenType - Tipo de token (generalmente 'Bearer')\n * @property issuedAt - Timestamp ISO 8601 de emisión\n * @property expiresAt - Timestamp ISO 8601 de expiración calculado\n * @property idToken - ID Token para OIDC (opcional)\n */\nexport type TokenSet = {\n accessToken: string;\n refreshToken?: string;\n expiresIn?: number;\n tokenType?: string;\n issuedAt: string;\n expiresAt?: string;\n idToken?: string;\n};\n\n/**\n * Type guard para verificar si una conexión está activa.\n * \n * @param connection - Conexión a verificar\n * @returns true si el estado es 'active'\n */\nexport function isActiveConnection(connection: OAuthConnection): boolean {\n return connection.status === 'active';\n}\n\n/**\n * Type guard para verificar si una conexión está revocada.\n * \n * @param connection - Conexión a verificar\n * @returns true si el estado es 'revoked'\n */\nexport function isRevokedConnection(connection: OAuthConnection): boolean {\n return connection.status === 'revoked';\n}\n\n/**\n * Type guard para verificar si una conexión está expirada.\n * \n * @param connection - Conexión a verificar\n * @returns true si el estado es 'expired' o si expiresAt está en el pasado\n */\nexport function isExpiredConnection(connection: OAuthConnection): boolean {\n if (connection.status === 'expired') return true;\n if (!connection.expiresAt) return false;\n return new Date(connection.expiresAt) < new Date();\n}\n\ntype ConnectionLinkedPayload = {\n connection: OAuthConnection;\n tokenSet: TokenSet;\n};\n\ntype TokenRefreshedPayload = {\n connectionId: string;\n tokenSet: TokenSet;\n};\n\ntype ConnectionRevokedPayload = {\n connectionId: string;\n reason: 'user' | 'provider' | 'security';\n};\n\n/**\n * Contratos de eventos para el sistema de conexiones OAuth.\n * Define los eventos que se emiten durante el ciclo de vida de una conexión OAuth.\n */\nexport const oauthConnectionEvents: Record<\n 'linked' | 'tokenRefreshed' | 'revoked',\n EventContract<ConnectionLinkedPayload | TokenRefreshedPayload | ConnectionRevokedPayload>\n> = {\n linked: {\n type: OAUTH_CONNECTION_EVENT_TYPES.LINKED,\n version: '1.0.0',\n schema: 'oauth.connection.linked@1.0.0',\n example: {\n id: 'evt_conn_1',\n type: OAUTH_CONNECTION_EVENT_TYPES.LINKED,\n version: '1.0.0',\n occurredAt: new Date().toISOString(),\n payload: {\n connection: {\n id: 'conn_123',\n providerId: 'google',\n projectId: 'proj_123',\n userId: 'user_123',\n scope: ['calendar', 'email'],\n status: 'active',\n createdAt: new Date().toISOString(),\n updatedAt: new Date().toISOString(),\n },\n tokenSet: {\n accessToken: 'access-token',\n refreshToken: 'refresh-token',\n issuedAt: new Date().toISOString(),\n expiresIn: 3600,\n },\n },\n meta: {\n source: 'oauth-connections-service',\n },\n },\n },\n tokenRefreshed: {\n type: OAUTH_CONNECTION_EVENT_TYPES.TOKEN_REFRESHED,\n version: '1.0.0',\n schema: 'oauth.connection.token_refreshed@1.0.0',\n example: {\n id: 'evt_conn_2',\n type: OAUTH_CONNECTION_EVENT_TYPES.TOKEN_REFRESHED,\n version: '1.0.0',\n occurredAt: new Date().toISOString(),\n payload: {\n connectionId: 'conn_123',\n tokenSet: {\n accessToken: 'access-token-2',\n refreshToken: 'refresh-token',\n issuedAt: new Date().toISOString(),\n expiresIn: 3600,\n },\n },\n meta: {\n source: 'oauth-connections-service',\n },\n },\n },\n revoked: {\n type: OAUTH_CONNECTION_EVENT_TYPES.REVOKED,\n version: '1.0.0',\n schema: 'oauth.connection.revoked@1.0.0',\n example: {\n id: 'evt_conn_3',\n type: OAUTH_CONNECTION_EVENT_TYPES.REVOKED,\n version: '1.0.0',\n occurredAt: new Date().toISOString(),\n payload: {\n connectionId: 'conn_123',\n reason: 'security',\n },\n meta: {\n source: 'oauth-connections-service',\n },\n },\n },\n};\n\n/**\n * Unión de todos los tipos de eventos de conexiones OAuth.\n */\nexport type OAuthConnectionEvent =\n | EventEnvelope<ConnectionLinkedPayload>\n | EventEnvelope<TokenRefreshedPayload>\n | EventEnvelope<ConnectionRevokedPayload>;\n","/**\n * Representa una versión semántica en formato MAJOR.MINOR.PATCH\n * \n * @example\n * ```typescript\n * const version: SemanticVersion = '1.2.3';\n * ```\n */\nexport type SemanticVersion = `${number}.${number}.${number}`;\n\n/**\n * Estructura que contiene un payload con su versión asociada.\n * Útil para versionado de datos y compatibilidad entre versiones.\n * \n * @template TPayload - Tipo del payload versionado\n */\nexport type Versioned<TPayload> = {\n version: SemanticVersion;\n payload: TPayload;\n};\n\n/**\n * Valida si una cadena de texto cumple con el formato de versión semántica.\n * \n * @param version - Cadena a validar\n * @returns true si es una versión semántica válida, false en caso contrario\n * \n * @example\n * ```typescript\n * isValidSemanticVersion('1.2.3'); // true\n * isValidSemanticVersion('1.2'); // false\n * isValidSemanticVersion('invalid'); // false\n * ```\n */\nexport function isValidSemanticVersion(version: string): version is SemanticVersion {\n return /^\\d+\\.\\d+\\.\\d+$/.test(version);\n}\n\n/**\n * Parsea una versión semántica y retorna sus componentes numéricos.\n * \n * @param version - Versión semántica a parsear\n * @returns Objeto con major, minor y patch, o null si la versión es inválida\n * \n * @example\n * ```typescript\n * parseSemanticVersion('1.2.3'); // { major: 1, minor: 2, patch: 3 }\n * parseSemanticVersion('invalid'); // null\n * ```\n */\nexport function parseSemanticVersion(version: string): {\n major: number;\n minor: number;\n patch: number;\n} | null {\n const match = version.match(/^(\\d+)\\.(\\d+)\\.(\\d+)$/);\n if (!match) return null;\n \n return {\n major: parseInt(match[1], 10),\n minor: parseInt(match[2], 10),\n patch: parseInt(match[3], 10),\n };\n}\n\n/**\n * Compara dos versiones semánticas.\n * \n * @param version1 - Primera versión a comparar\n * @param version2 - Segunda versión a comparar\n * @returns -1 si version1 < version2, 0 si son iguales, 1 si version1 > version2\n * @throws Error si alguna de las versiones es inválida\n * \n * @example\n * ```typescript\n * compareSemanticVersions('1.2.3', '1.2.4'); // -1\n * compareSemanticVersions('2.0.0', '1.9.9'); // 1\n * compareSemanticVersions('1.0.0', '1.0.0'); // 0\n * ```\n */\nexport function compareSemanticVersions(\n version1: string,\n version2: string\n): number {\n const v1 = parseSemanticVersion(version1);\n const v2 = parseSemanticVersion(version2);\n \n if (!v1 || !v2) {\n throw new Error(`Invalid semantic version: ${!v1 ? version1 : version2}`);\n }\n \n if (v1.major !== v2.major) {\n return v1.major - v2.major;\n }\n \n if (v1.minor !== v2.minor) {\n return v1.minor - v2.minor;\n }\n \n return v1.patch - v2.patch;\n}\n\n/**\n * Verifica si una versión es mayor que otra.\n * \n * @param version1 - Versión a comparar\n * @param version2 - Versión de referencia\n * @returns true si version1 > version2\n * \n * @example\n * ```typescript\n * isVersionGreaterThan('2.0.0', '1.9.9'); // true\n * isVersionGreaterThan('1.0.0', '1.0.0'); // false\n * ```\n */\nexport function isVersionGreaterThan(version1: string, version2: string): boolean {\n return compareSemanticVersions(version1, version2) > 0;\n}\n\n/**\n * Verifica si una versión es menor que otra.\n * \n * @param version1 - Versión a comparar\n * @param version2 - Versión de referencia\n * @returns true si version1 < version2\n * \n * @example\n * ```typescript\n * isVersionLessThan('1.0.0', '2.0.0'); // true\n * ```\n */\nexport function isVersionLessThan(version1: string, version2: string): boolean {\n return compareSemanticVersions(version1, version2) < 0;\n}\n\n/**\n * Verifica si una versión es mayor o igual que otra.\n * \n * @param version1 - Versión a comparar\n * @param version2 - Versión de referencia\n * @returns true si version1 >= version2\n */\nexport function isVersionGreaterOrEqual(version1: string, version2: string): boolean {\n return compareSemanticVersions(version1, version2) >= 0;\n}\n\n/**\n * Verifica si una versión es menor o igual que otra.\n * \n * @param version1 - Versión a comparar\n * @param version2 - Versión de referencia\n * @returns true si version1 <= version2\n */\nexport function isVersionLessOrEqual(version1: string, version2: string): boolean {\n return compareSemanticVersions(version1, version2) <= 0;\n}\n","/**\n * Utilidades de validación para formatos comunes utilizados en los contratos.\n */\n\n/**\n * Expresión regular para validar formato UUID v4.\n */\nconst UUID_V4_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;\n\n/**\n * Expresión regular básica para validar formato de email.\n * Nota: Esta es una validación básica. Para validación estricta, usar una biblioteca especializada.\n */\nconst EMAIL_REGEX = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;\n\n/**\n * Expresión regular para validar formato de teléfono E.164.\n * E.164 requiere: + seguido de 1-3 dígitos del código de país, luego 1-14 dígitos adicionales.\n * Mínimo total: + seguido de al menos 7 dígitos (código de país + número local).\n */\nconst E164_PHONE_REGEX = /^\\+[1-9]\\d{6,14}$/;\n\n/**\n * Valida si una cadena tiene formato UUID v4.\n * \n * @param value - Cadena a validar\n * @returns true si es un UUID v4 válido\n * \n * @example\n * ```typescript\n * isValidUuid('550e8400-e29b-41d4-a716-446655440000'); // true\n * isValidUuid('invalid'); // false\n * ```\n */\nexport function isValidUuid(value: string): boolean {\n return UUID_V4_REGEX.test(value);\n}\n\n/**\n * Valida si una cadena tiene formato de email básico.\n * \n * @param value - Cadena a validar\n * @returns true si tiene formato de email válido\n * \n * @example\n * ```typescript\n * isValidEmail('user@example.com'); // true\n * isValidEmail('invalid'); // false\n * ```\n */\nexport function isValidEmail(value: string): boolean {\n return EMAIL_REGEX.test(value);\n}\n\n/**\n * Valida si una cadena tiene formato de URL válido.\n * \n * @param value - Cadena a validar\n * @returns true si es una URL válida\n * \n * @example\n * ```typescript\n * isValidUrl('https://example.com'); // true\n * isValidUrl('not-a-url'); // false\n * ```\n */\nexport function isValidUrl(value: string): boolean {\n try {\n // URL está disponible en Node.js 18+ y navegadores modernos\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call\n const URLConstructor = (globalThis as unknown as { URL?: new (url: string) => unknown }).URL;\n \n if (URLConstructor) {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-call\n new URLConstructor(value);\n return true;\n }\n // Fallback para entornos sin URL global\n return /^https?:\\/\\/.+\\..+/.test(value);\n } catch {\n return false;\n }\n}\n\n/**\n * Valida si una cadena tiene formato de teléfono E.164.\n * \n * @param value - Cadena a validar\n * @returns true si tiene formato E.164 válido\n * \n * @example\n * ```typescript\n * isValidE164Phone('+1234567890'); // true\n * isValidE164Phone('1234567890'); // false\n * ```\n */\nexport function isValidE164Phone(value: string): boolean {\n return E164_PHONE_REGEX.test(value);\n}\n\n/**\n * Valida si una cadena es un timestamp ISO 8601 válido.\n * \n * @param value - Cadena a validar\n * @returns true si es un timestamp ISO 8601 válido\n * \n * @example\n * ```typescript\n * isValidIso8601('2024-01-01T00:00:00.000Z'); // true\n * isValidIso8601('invalid'); // false\n * ```\n */\nexport function isValidIso8601(value: string): boolean {\n const date = new Date(value);\n return !isNaN(date.getTime()) && value === date.toISOString();\n}\n\n/**\n * Valida si un objeto tiene todas las propiedades requeridas.\n * \n * @param obj - Objeto a validar\n * @param requiredKeys - Array de claves requeridas\n * @returns true si todas las claves están presentes\n * \n * @example\n * ```typescript\n * hasRequiredKeys({ a: 1, b: 2 }, ['a', 'b']); // true\n * hasRequiredKeys({ a: 1 }, ['a', 'b']); // false\n * ```\n */\nexport function hasRequiredKeys<T extends Record<string, unknown>>(\n obj: T,\n requiredKeys: (keyof T)[]\n): boolean {\n return requiredKeys.every((key) => key in obj);\n}\n","/**\n * Esquemas de validación en runtime usando Zod.\n * Estos esquemas permiten validar datos en tiempo de ejecución,\n * no solo en tiempo de compilación con TypeScript.\n * \n * Nota: Requiere instalar zod como dependencia.\n */\n\nimport { z } from 'zod';\nimport { isValidSemanticVersion } from '../versioning';\n\n/**\n * Esquema para validar versiones semánticas.\n */\nexport const semanticVersionSchema = z.string().refine(\n isValidSemanticVersion,\n { message: 'Debe ser una versión semántica válida (MAJOR.MINOR.PATCH)' }\n);\n\n/**\n * Esquema para validar metadata de eventos.\n */\nexport const eventMetaSchema = z.object({\n traceId: z.string().optional(),\n spanId: z.string().optional(),\n correlationId: z.string().optional(),\n causationId: z.string().optional(),\n tenantId: z.string().optional(),\n projectId: z.string().optional(),\n source: z.string().min(1),\n});\n\n/**\n * Esquema genérico para validar envelopes de eventos.\n * \n * @template TPayload - Tipo del payload (debe ser un esquema Zod)\n */\nexport function createEventEnvelopeSchema<TPayload extends z.ZodTypeAny>(\n payloadSchema: TPayload\n): z.ZodObject<{\n id: z.ZodString;\n type: z.ZodString;\n version: typeof semanticVersionSchema;\n occurredAt: z.ZodString;\n payload: TPayload;\n meta: typeof eventMetaSchema;\n}> {\n return z.object({\n id: z.string().min(1),\n type: z.string().min(1),\n version: semanticVersionSchema,\n occurredAt: z.string().datetime(),\n payload: payloadSchema,\n meta: eventMetaSchema,\n });\n}\n\n/**\n * Esquema para validar solicitudes de notificación por email.\n */\nexport const emailNotificationRequestSchema = z.object({\n channel: z.literal('email'),\n projectId: z.string().min(1),\n tenantId: z.string().optional(),\n locale: z.string().optional(),\n deduplicationKey: z.string().optional(),\n expiresAt: z.string().datetime().optional(),\n metadata: z.record(z.string()).optional(),\n to: z.string().email(),\n templateId: z.string().min(1),\n variables: z.record(z.union([z.string(), z.number(), z.boolean(), z.null()])).optional(),\n cc: z.array(z.string().email()).optional(),\n bcc: z.array(z.string().email()).optional(),\n replyTo: z.string().email().optional(),\n});\n\n/**\n * Esquema para validar solicitudes de notificación por SMS.\n */\nexport const smsNotificationRequestSchema = z.object({\n channel: z.literal('sms'),\n projectId: z.string().min(1),\n tenantId: z.string().optional(),\n locale: z.string().optional(),\n deduplicationKey: z.string().optional(),\n expiresAt: z.string().datetime().optional(),\n metadata: z.record(z.string()).optional(),\n to: z.string().min(1),\n templateId: z.string().min(1),\n variables: z.record(z.union([z.string(), z.number(), z.boolean(), z.null()])).optional(),\n});\n\n/**\n * Esquema para validar solicitudes de notificación por webhook.\n */\nexport const webhookNotificationRequestSchema = z.object({\n channel: z.literal('webhook'),\n projectId: z.string().min(1),\n tenantId: z.string().optional(),\n locale: z.string().optional(),\n deduplicationKey: z.string().optional(),\n expiresAt: z.string().datetime().optional(),\n metadata: z.record(z.string()).optional(),\n url: z.string().url(),\n signatureVersion: semanticVersionSchema,\n body: z.unknown(),\n});\n\n/**\n * Esquema para validar cualquier solicitud de notificación.\n */\nexport const notificationRequestSchema = z.discriminatedUnion('channel', [\n emailNotificationRequestSchema,\n smsNotificationRequestSchema,\n webhookNotificationRequestSchema,\n]);\n\n/**\n * Esquema para validar conexiones OAuth.\n */\nexport const oauthConnectionSchema = z.object({\n id: z.string().min(1),\n providerId: z.string().min(1),\n projectId: z.string().min(1),\n tenantId: z.string().optional(),\n userId: z.string().min(1),\n scope: z.array(z.string()),\n expiresAt: z.string().datetime().optional(),\n createdAt: z.string().datetime(),\n updatedAt: z.string().datetime(),\n status: z.enum(['active', 'refreshing', 'revoked', 'expired']),\n});\n\n/**\n * Esquema para validar conjuntos de tokens OAuth.\n */\nexport const tokenSetSchema = z.object({\n accessToken: z.string().min(1),\n refreshToken: z.string().optional(),\n expiresIn: z.number().positive().optional(),\n tokenType: z.string().optional(),\n issuedAt: z.string().datetime(),\n expiresAt: z.string().datetime().optional(),\n idToken: z.string().optional(),\n});\n"],"mappings":"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","names":[]}
|