@classytic/payroll 2.7.5 → 2.8.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.
@@ -0,0 +1,1962 @@
1
+ import { LRUCache } from 'lru-cache';
2
+ import crypto from 'crypto';
3
+ import { Types } from 'mongoose';
4
+
5
+ // src/core/result.ts
6
+ function ok(value) {
7
+ return { ok: true, value };
8
+ }
9
+ function err(error) {
10
+ return { ok: false, error };
11
+ }
12
+ function isOk(result) {
13
+ return result.ok === true;
14
+ }
15
+ function isErr(result) {
16
+ return result.ok === false;
17
+ }
18
+ function unwrap(result) {
19
+ if (isOk(result)) {
20
+ return result.value;
21
+ }
22
+ throw result.error;
23
+ }
24
+ function unwrapOr(result, defaultValue) {
25
+ if (isOk(result)) {
26
+ return result.value;
27
+ }
28
+ return defaultValue;
29
+ }
30
+ function unwrapOrElse(result, fn) {
31
+ if (isOk(result)) {
32
+ return result.value;
33
+ }
34
+ return fn(result.error);
35
+ }
36
+ function map(result, fn) {
37
+ if (isOk(result)) {
38
+ return ok(fn(result.value));
39
+ }
40
+ return result;
41
+ }
42
+ function mapErr(result, fn) {
43
+ if (isErr(result)) {
44
+ return err(fn(result.error));
45
+ }
46
+ return result;
47
+ }
48
+ function flatMap(result, fn) {
49
+ if (isOk(result)) {
50
+ return fn(result.value);
51
+ }
52
+ return result;
53
+ }
54
+ async function tryCatch(fn, errorTransform) {
55
+ try {
56
+ const value = await fn();
57
+ return ok(value);
58
+ } catch (error) {
59
+ if (errorTransform) {
60
+ return err(errorTransform(error));
61
+ }
62
+ return err(error);
63
+ }
64
+ }
65
+ function tryCatchSync(fn, errorTransform) {
66
+ try {
67
+ const value = fn();
68
+ return ok(value);
69
+ } catch (error) {
70
+ if (errorTransform) {
71
+ return err(errorTransform(error));
72
+ }
73
+ return err(error);
74
+ }
75
+ }
76
+ function all(results) {
77
+ const values = [];
78
+ for (const result of results) {
79
+ if (isErr(result)) {
80
+ return result;
81
+ }
82
+ values.push(result.value);
83
+ }
84
+ return ok(values);
85
+ }
86
+ function match(result, handlers) {
87
+ if (isOk(result)) {
88
+ return handlers.ok(result.value);
89
+ }
90
+ return handlers.err(result.error);
91
+ }
92
+ async function fromPromise(promise, errorTransform) {
93
+ return tryCatch(() => promise, errorTransform);
94
+ }
95
+ function fromNullable(value, error) {
96
+ if (value === null || value === void 0) {
97
+ return err(error);
98
+ }
99
+ return ok(value);
100
+ }
101
+ var ResultClass = class _ResultClass {
102
+ constructor(result) {
103
+ this.result = result;
104
+ }
105
+ static ok(value) {
106
+ return new _ResultClass(ok(value));
107
+ }
108
+ static err(error) {
109
+ return new _ResultClass(err(error));
110
+ }
111
+ static async fromAsync(fn, errorTransform) {
112
+ const result = await tryCatch(fn, errorTransform);
113
+ return new _ResultClass(result);
114
+ }
115
+ isOk() {
116
+ return isOk(this.result);
117
+ }
118
+ isErr() {
119
+ return isErr(this.result);
120
+ }
121
+ unwrap() {
122
+ return unwrap(this.result);
123
+ }
124
+ unwrapOr(defaultValue) {
125
+ return unwrapOr(this.result, defaultValue);
126
+ }
127
+ map(fn) {
128
+ return new _ResultClass(map(this.result, fn));
129
+ }
130
+ mapErr(fn) {
131
+ return new _ResultClass(mapErr(this.result, fn));
132
+ }
133
+ flatMap(fn) {
134
+ return new _ResultClass(flatMap(this.result, fn));
135
+ }
136
+ match(handlers) {
137
+ return match(this.result, handlers);
138
+ }
139
+ toResult() {
140
+ return this.result;
141
+ }
142
+ };
143
+ var Result = {
144
+ ok,
145
+ err,
146
+ isOk,
147
+ isErr,
148
+ unwrap,
149
+ unwrapOr,
150
+ unwrapOrElse,
151
+ map,
152
+ mapErr,
153
+ flatMap,
154
+ tryCatch,
155
+ tryCatchSync,
156
+ all,
157
+ match,
158
+ fromPromise,
159
+ fromNullable
160
+ };
161
+
162
+ // src/utils/logger.ts
163
+ var createConsoleLogger = () => ({
164
+ info: (message, meta) => {
165
+ if (meta) {
166
+ console.log(`[Payroll] INFO: ${message}`, meta);
167
+ } else {
168
+ console.log(`[Payroll] INFO: ${message}`);
169
+ }
170
+ },
171
+ error: (message, meta) => {
172
+ if (meta) {
173
+ console.error(`[Payroll] ERROR: ${message}`, meta);
174
+ } else {
175
+ console.error(`[Payroll] ERROR: ${message}`);
176
+ }
177
+ },
178
+ warn: (message, meta) => {
179
+ if (meta) {
180
+ console.warn(`[Payroll] WARN: ${message}`, meta);
181
+ } else {
182
+ console.warn(`[Payroll] WARN: ${message}`);
183
+ }
184
+ },
185
+ debug: (message, meta) => {
186
+ if (process.env.NODE_ENV !== "production") {
187
+ if (meta) {
188
+ console.log(`[Payroll] DEBUG: ${message}`, meta);
189
+ } else {
190
+ console.log(`[Payroll] DEBUG: ${message}`);
191
+ }
192
+ }
193
+ }
194
+ });
195
+ var currentLogger = createConsoleLogger();
196
+ function getLogger() {
197
+ return {
198
+ info: (message, meta) => {
199
+ currentLogger.info(message, meta);
200
+ },
201
+ error: (message, meta) => {
202
+ currentLogger.error(message, meta);
203
+ },
204
+ warn: (message, meta) => {
205
+ currentLogger.warn(message, meta);
206
+ },
207
+ debug: (message, meta) => {
208
+ currentLogger.debug(message, meta);
209
+ }
210
+ };
211
+ }
212
+
213
+ // src/core/events.ts
214
+ var EventBus = class {
215
+ handlers = /* @__PURE__ */ new Map();
216
+ /**
217
+ * Register an event handler
218
+ */
219
+ on(event, handler) {
220
+ if (!this.handlers.has(event)) {
221
+ this.handlers.set(event, /* @__PURE__ */ new Set());
222
+ }
223
+ this.handlers.get(event).add(handler);
224
+ return () => this.off(event, handler);
225
+ }
226
+ /**
227
+ * Register a one-time event handler
228
+ */
229
+ once(event, handler) {
230
+ const wrappedHandler = async (payload) => {
231
+ this.off(event, wrappedHandler);
232
+ await handler(payload);
233
+ };
234
+ return this.on(event, wrappedHandler);
235
+ }
236
+ /**
237
+ * Remove an event handler
238
+ */
239
+ off(event, handler) {
240
+ const eventHandlers = this.handlers.get(event);
241
+ if (eventHandlers) {
242
+ eventHandlers.delete(handler);
243
+ }
244
+ }
245
+ /**
246
+ * Emit an event
247
+ */
248
+ async emit(event, payload) {
249
+ const eventHandlers = this.handlers.get(event);
250
+ if (!eventHandlers || eventHandlers.size === 0) {
251
+ return;
252
+ }
253
+ const handlers = Array.from(eventHandlers);
254
+ await Promise.all(
255
+ handlers.map(async (handler) => {
256
+ try {
257
+ await handler(payload);
258
+ } catch (error) {
259
+ getLogger().error(`Event handler error for ${event}`, {
260
+ error: error instanceof Error ? error.message : String(error)
261
+ });
262
+ }
263
+ })
264
+ );
265
+ }
266
+ /**
267
+ * Emit event synchronously (fire-and-forget)
268
+ */
269
+ emitSync(event, payload) {
270
+ void this.emit(event, payload);
271
+ }
272
+ /**
273
+ * Remove all handlers for an event
274
+ */
275
+ removeAllListeners(event) {
276
+ if (event) {
277
+ this.handlers.delete(event);
278
+ } else {
279
+ this.handlers.clear();
280
+ }
281
+ }
282
+ /**
283
+ * Get listener count for an event
284
+ */
285
+ listenerCount(event) {
286
+ return this.handlers.get(event)?.size ?? 0;
287
+ }
288
+ /**
289
+ * Get all registered events
290
+ */
291
+ eventNames() {
292
+ return Array.from(this.handlers.keys());
293
+ }
294
+ };
295
+ var defaultEventBus = null;
296
+ function getEventBus() {
297
+ if (!defaultEventBus) {
298
+ defaultEventBus = new EventBus();
299
+ }
300
+ return defaultEventBus;
301
+ }
302
+ function createEventBus() {
303
+ return new EventBus();
304
+ }
305
+ function resetEventBus() {
306
+ if (defaultEventBus) {
307
+ defaultEventBus.removeAllListeners();
308
+ }
309
+ defaultEventBus = null;
310
+ }
311
+ function onEmployeeHired(handler) {
312
+ return getEventBus().on("employee:hired", handler);
313
+ }
314
+ function onSalaryProcessed(handler) {
315
+ return getEventBus().on("salary:processed", handler);
316
+ }
317
+ function onPayrollCompleted(handler) {
318
+ return getEventBus().on("payroll:completed", handler);
319
+ }
320
+ function onMilestoneAchieved(handler) {
321
+ return getEventBus().on("milestone:achieved", handler);
322
+ }
323
+ var IdempotencyManager = class _IdempotencyManager {
324
+ cache;
325
+ static hasLoggedWarning = false;
326
+ constructor(options = {}) {
327
+ this.cache = new LRUCache({
328
+ max: options.max || 1e4,
329
+ // Store 10k keys
330
+ ttl: options.ttl || 1e3 * 60 * 60 * 24
331
+ // 24 hours default
332
+ });
333
+ if (!options.suppressWarning && !_IdempotencyManager.hasLoggedWarning && process.env.NODE_ENV === "production") {
334
+ _IdempotencyManager.hasLoggedWarning = true;
335
+ getLogger().warn(
336
+ "IdempotencyManager: Using in-memory cache. For horizontal scaling, implement database-backed idempotency. See @classytic/payroll documentation for implementation guidance.",
337
+ { cacheMax: options.max || 1e4, cacheTTL: options.ttl || 864e5 }
338
+ );
339
+ }
340
+ }
341
+ /**
342
+ * Check if key exists and return cached result
343
+ */
344
+ get(key) {
345
+ const cached = this.cache.get(key);
346
+ if (!cached) return null;
347
+ return {
348
+ value: cached.value,
349
+ cached: true,
350
+ createdAt: cached.createdAt
351
+ };
352
+ }
353
+ /**
354
+ * Store result for idempotency key
355
+ */
356
+ set(key, value) {
357
+ this.cache.set(key, {
358
+ value,
359
+ createdAt: /* @__PURE__ */ new Date()
360
+ });
361
+ }
362
+ /**
363
+ * Execute function with idempotency protection
364
+ */
365
+ async execute(key, fn) {
366
+ const cached = this.get(key);
367
+ if (cached) {
368
+ return cached;
369
+ }
370
+ const value = await fn();
371
+ this.set(key, value);
372
+ return {
373
+ value,
374
+ cached: false,
375
+ createdAt: /* @__PURE__ */ new Date()
376
+ };
377
+ }
378
+ /**
379
+ * Clear a specific key
380
+ */
381
+ delete(key) {
382
+ this.cache.delete(key);
383
+ }
384
+ /**
385
+ * Clear all keys
386
+ */
387
+ clear() {
388
+ this.cache.clear();
389
+ }
390
+ /**
391
+ * Get cache stats
392
+ */
393
+ stats() {
394
+ return {
395
+ size: this.cache.size,
396
+ max: this.cache.max
397
+ };
398
+ }
399
+ };
400
+ function generatePayrollIdempotencyKey(organizationId, employeeId, month, year, payrollRunType = "regular", periodStartDate) {
401
+ if (periodStartDate) {
402
+ const startDateStr = periodStartDate.toISOString().split("T")[0];
403
+ return `payroll:${organizationId}:${employeeId}:${year}-${month}:${startDateStr}:${payrollRunType}`;
404
+ }
405
+ return `payroll:${organizationId}:${employeeId}:${year}-${month}:${payrollRunType}`;
406
+ }
407
+ var WebhookManager = class {
408
+ webhooks = [];
409
+ deliveryLog = [];
410
+ maxLogSize;
411
+ storePayloads;
412
+ constructor(options) {
413
+ this.maxLogSize = options?.maxLogSize ?? 1e3;
414
+ this.storePayloads = options?.storePayloads ?? false;
415
+ }
416
+ /**
417
+ * Register a webhook
418
+ */
419
+ register(config) {
420
+ this.webhooks.push({
421
+ retries: 3,
422
+ timeout: 3e4,
423
+ ...config
424
+ });
425
+ }
426
+ /**
427
+ * Remove a webhook
428
+ */
429
+ unregister(url) {
430
+ this.webhooks = this.webhooks.filter((w) => w.url !== url);
431
+ }
432
+ /**
433
+ * Send webhook for event
434
+ */
435
+ async send(event, payload) {
436
+ const matchingWebhooks = this.webhooks.filter((w) => w.events.includes(event));
437
+ const deliveries = matchingWebhooks.map(
438
+ (webhook) => this.deliver(webhook, event, payload)
439
+ );
440
+ await Promise.allSettled(deliveries);
441
+ }
442
+ /**
443
+ * Deliver webhook with retries
444
+ */
445
+ async deliver(webhook, event, payload) {
446
+ const deliveryId = `${Date.now()}-${Math.random().toString(36)}`;
447
+ const delivery = {
448
+ id: deliveryId,
449
+ event,
450
+ url: webhook.url,
451
+ payload: this.storePayloads ? payload : void 0,
452
+ attempt: 0,
453
+ status: "pending"
454
+ };
455
+ this.deliveryLog.push(delivery);
456
+ this.pruneLog();
457
+ const maxRetries = webhook.retries || 3;
458
+ for (let attempt = 1; attempt <= maxRetries; attempt++) {
459
+ delivery.attempt = attempt;
460
+ try {
461
+ const controller = new AbortController();
462
+ const timeout = setTimeout(() => controller.abort(), webhook.timeout || 3e4);
463
+ const timestamp = Math.floor(Date.now() / 1e3);
464
+ const deliveredAt = (/* @__PURE__ */ new Date()).toISOString();
465
+ const requestBody = {
466
+ event,
467
+ payload,
468
+ deliveredAt
469
+ };
470
+ const headers = {
471
+ "Content-Type": "application/json",
472
+ "X-Payroll-Event": event,
473
+ "X-Payroll-Delivery": deliveryId,
474
+ "X-Payroll-Timestamp": timestamp.toString(),
475
+ ...webhook.headers
476
+ };
477
+ if (webhook.secret) {
478
+ headers["X-Payroll-Signature"] = this.generateSignature(requestBody, webhook.secret, timestamp);
479
+ }
480
+ const response = await fetch(webhook.url, {
481
+ method: "POST",
482
+ headers,
483
+ body: JSON.stringify(requestBody),
484
+ signal: controller.signal
485
+ });
486
+ clearTimeout(timeout);
487
+ delivery.response = {
488
+ status: response.status,
489
+ body: await response.text()
490
+ };
491
+ delivery.sentAt = /* @__PURE__ */ new Date();
492
+ if (response.ok) {
493
+ delivery.status = "sent";
494
+ return delivery;
495
+ }
496
+ const shouldRetry = response.status >= 500 || response.status === 429 || response.status === 408;
497
+ if (shouldRetry && attempt < maxRetries) {
498
+ const backoff = Math.pow(2, attempt) * 1e3;
499
+ const jitter = Math.random() * 1e3;
500
+ await this.sleep(backoff + jitter);
501
+ continue;
502
+ }
503
+ delivery.status = "failed";
504
+ delivery.error = `HTTP ${response.status}`;
505
+ return delivery;
506
+ } catch (error) {
507
+ delivery.error = error.message;
508
+ if (attempt < maxRetries) {
509
+ await this.sleep(Math.pow(2, attempt) * 1e3);
510
+ continue;
511
+ }
512
+ delivery.status = "failed";
513
+ return delivery;
514
+ }
515
+ }
516
+ return delivery;
517
+ }
518
+ /**
519
+ * Generate HMAC-SHA256 signature for webhook (Stripe-style)
520
+ *
521
+ * Format: t=<timestamp>,v1=<hmac_signature>
522
+ *
523
+ * The signed payload is: timestamp.JSON(requestBody)
524
+ * where requestBody = { event, payload, deliveredAt }
525
+ *
526
+ * Consumers should verify:
527
+ * 1. Timestamp is within tolerance (e.g., 5 minutes)
528
+ * 2. HMAC signature matches
529
+ *
530
+ * @example Verify signature (consumer side)
531
+ * ```typescript
532
+ * import crypto from 'crypto';
533
+ *
534
+ * const signature = req.headers['x-payroll-signature'];
535
+ * const timestamp = req.headers['x-payroll-timestamp'];
536
+ * const requestBody = req.body; // { event, payload, deliveredAt }
537
+ *
538
+ * // Check timestamp (replay protection)
539
+ * const now = Math.floor(Date.now() / 1000);
540
+ * if (Math.abs(now - parseInt(timestamp)) > 300) {
541
+ * throw new Error('Signature expired');
542
+ * }
543
+ *
544
+ * // Verify signature
545
+ * const signedPayload = `${timestamp}.${JSON.stringify(requestBody)}`;
546
+ * const expectedSignature = crypto
547
+ * .createHmac('sha256', secret)
548
+ * .update(signedPayload)
549
+ * .digest('hex');
550
+ *
551
+ * const parts = signature.split(',');
552
+ * const providedSignature = parts.find(p => p.startsWith('v1='))?.split('=')[1];
553
+ *
554
+ * if (providedSignature !== expectedSignature) {
555
+ * throw new Error('Invalid signature');
556
+ * }
557
+ * ```
558
+ */
559
+ generateSignature(requestBody, secret, timestamp) {
560
+ const data = JSON.stringify(requestBody);
561
+ const signedPayload = `${timestamp}.${data}`;
562
+ const hmac = crypto.createHmac("sha256", secret);
563
+ hmac.update(signedPayload);
564
+ const signature = hmac.digest("hex");
565
+ return `t=${timestamp},v1=${signature}`;
566
+ }
567
+ /**
568
+ * Prune delivery log to stay within maxLogSize.
569
+ * Removes oldest entries first.
570
+ */
571
+ pruneLog() {
572
+ if (this.deliveryLog.length > this.maxLogSize) {
573
+ this.deliveryLog = this.deliveryLog.slice(-this.maxLogSize);
574
+ }
575
+ }
576
+ /**
577
+ * Sleep for ms
578
+ */
579
+ sleep(ms) {
580
+ return new Promise((resolve) => setTimeout(resolve, ms));
581
+ }
582
+ /**
583
+ * Get delivery log
584
+ */
585
+ getDeliveries(options) {
586
+ let results = this.deliveryLog;
587
+ if (options?.event) {
588
+ results = results.filter((d) => d.event === options.event);
589
+ }
590
+ if (options?.status) {
591
+ results = results.filter((d) => d.status === options.status);
592
+ }
593
+ if (options?.limit) {
594
+ results = results.slice(-options.limit);
595
+ }
596
+ return results;
597
+ }
598
+ /**
599
+ * Clear delivery log
600
+ */
601
+ clearLog() {
602
+ this.deliveryLog = [];
603
+ }
604
+ /**
605
+ * Get all registered webhooks
606
+ */
607
+ getWebhooks() {
608
+ return [...this.webhooks];
609
+ }
610
+ };
611
+
612
+ // src/core/plugin.ts
613
+ var PluginManager = class {
614
+ constructor(context) {
615
+ this.context = context;
616
+ }
617
+ plugins = /* @__PURE__ */ new Map();
618
+ hooks = /* @__PURE__ */ new Map();
619
+ /**
620
+ * Register a plugin
621
+ */
622
+ async register(plugin) {
623
+ if (this.plugins.has(plugin.name)) {
624
+ throw new Error(`Plugin "${plugin.name}" is already registered`);
625
+ }
626
+ if (plugin.hooks) {
627
+ for (const [hookName, handler] of Object.entries(plugin.hooks)) {
628
+ if (handler) {
629
+ this.addHook(hookName, handler);
630
+ }
631
+ }
632
+ }
633
+ if (plugin.init) {
634
+ await plugin.init(this.context);
635
+ }
636
+ this.plugins.set(plugin.name, plugin);
637
+ this.context.logger.debug(`Plugin "${plugin.name}" registered`);
638
+ }
639
+ /**
640
+ * Unregister a plugin
641
+ */
642
+ async unregister(name) {
643
+ const plugin = this.plugins.get(name);
644
+ if (!plugin) {
645
+ return;
646
+ }
647
+ if (plugin.destroy) {
648
+ await plugin.destroy();
649
+ }
650
+ this.plugins.delete(name);
651
+ this.context.logger.debug(`Plugin "${name}" unregistered`);
652
+ }
653
+ /**
654
+ * Add a hook handler
655
+ */
656
+ addHook(hookName, handler) {
657
+ if (!this.hooks.has(hookName)) {
658
+ this.hooks.set(hookName, []);
659
+ }
660
+ this.hooks.get(hookName).push(handler);
661
+ }
662
+ /**
663
+ * Execute hooks for a given event
664
+ */
665
+ async executeHooks(hookName, ...args) {
666
+ const handlers = this.hooks.get(hookName);
667
+ if (!handlers || handlers.length === 0) {
668
+ return;
669
+ }
670
+ for (const handler of handlers) {
671
+ try {
672
+ await handler(...args);
673
+ } catch (error) {
674
+ this.context.logger.error(`Hook "${hookName}" error:`, { error });
675
+ const errorHandlers = this.hooks.get("onError");
676
+ if (errorHandlers) {
677
+ for (const errorHandler of errorHandlers) {
678
+ try {
679
+ await errorHandler(error, hookName);
680
+ } catch (handlerError) {
681
+ getLogger().debug("Error handler threw an error", {
682
+ hook: hookName,
683
+ handlerError: handlerError instanceof Error ? handlerError.message : String(handlerError)
684
+ });
685
+ }
686
+ }
687
+ }
688
+ }
689
+ }
690
+ }
691
+ /**
692
+ * Get registered plugin names
693
+ */
694
+ getPluginNames() {
695
+ return Array.from(this.plugins.keys());
696
+ }
697
+ /**
698
+ * Check if plugin is registered
699
+ */
700
+ hasPlugin(name) {
701
+ return this.plugins.has(name);
702
+ }
703
+ };
704
+ function definePlugin(definition) {
705
+ return definition;
706
+ }
707
+ var loggingPlugin = definePlugin({
708
+ name: "logging",
709
+ version: "1.0.0",
710
+ init: (context) => {
711
+ context.addHook("employee:hired", (payload) => {
712
+ context.logger.info("Employee hired", {
713
+ employeeId: payload.employee.employeeId,
714
+ position: payload.employee.position
715
+ });
716
+ });
717
+ context.addHook("salary:processed", (payload) => {
718
+ context.logger.info("Salary processed", {
719
+ employeeId: payload.employee.employeeId,
720
+ amount: payload.payroll.netAmount,
721
+ period: payload.payroll.period
722
+ });
723
+ });
724
+ context.addHook("employee:terminated", (payload) => {
725
+ context.logger.info("Employee terminated", {
726
+ employeeId: payload.employee.employeeId,
727
+ reason: payload.reason
728
+ });
729
+ });
730
+ },
731
+ hooks: {
732
+ onError: (error, context) => {
733
+ getLogger().error(`[Payroll Error] ${context}:`, { error: error.message });
734
+ }
735
+ }
736
+ });
737
+ var metricsPlugin = definePlugin({
738
+ name: "metrics",
739
+ version: "1.0.0",
740
+ init: (context) => {
741
+ const metrics = {
742
+ employeesHired: 0,
743
+ employeesTerminated: 0,
744
+ salariesProcessed: 0,
745
+ totalPaid: 0,
746
+ errors: 0
747
+ };
748
+ context.addHook("employee:hired", () => {
749
+ metrics.employeesHired++;
750
+ });
751
+ context.addHook("employee:terminated", () => {
752
+ metrics.employeesTerminated++;
753
+ });
754
+ context.addHook("salary:processed", (payload) => {
755
+ metrics.salariesProcessed++;
756
+ metrics.totalPaid += payload.payroll.netAmount;
757
+ });
758
+ context.payroll.metrics = metrics;
759
+ },
760
+ hooks: {
761
+ onError: (error, context) => {
762
+ }
763
+ }
764
+ });
765
+ function createNotificationPlugin(options) {
766
+ return definePlugin({
767
+ name: "notification",
768
+ version: "1.0.0",
769
+ init: (context) => {
770
+ if (options.onHired) {
771
+ context.addHook("employee:hired", async (payload) => {
772
+ await options.onHired({
773
+ id: payload.employee.id,
774
+ name: payload.employee.position
775
+ });
776
+ });
777
+ }
778
+ if (options.onTerminated) {
779
+ context.addHook("employee:terminated", async (payload) => {
780
+ await options.onTerminated({
781
+ id: payload.employee.id,
782
+ name: payload.employee.name
783
+ });
784
+ });
785
+ }
786
+ if (options.onSalaryProcessed) {
787
+ context.addHook("salary:processed", async (payload) => {
788
+ await options.onSalaryProcessed({
789
+ employee: {
790
+ id: payload.employee.id,
791
+ name: payload.employee.name
792
+ },
793
+ amount: payload.payroll.netAmount
794
+ });
795
+ });
796
+ }
797
+ if (options.onMilestone) {
798
+ context.addHook("milestone:achieved", async (payload) => {
799
+ await options.onMilestone({
800
+ employee: {
801
+ id: payload.employee.id,
802
+ name: payload.employee.name
803
+ },
804
+ milestone: payload.milestone.message
805
+ });
806
+ });
807
+ }
808
+ }
809
+ });
810
+ }
811
+ var notificationPlugin = createNotificationPlugin({});
812
+ function toObjectId(id) {
813
+ if (id instanceof Types.ObjectId) return id;
814
+ return new Types.ObjectId(id);
815
+ }
816
+
817
+ // src/core/repository-plugins.ts
818
+ function multiTenantPlugin(organizationId) {
819
+ return {
820
+ name: "multi-tenant",
821
+ apply(repo) {
822
+ if (!organizationId) return;
823
+ const orgId = toObjectId(organizationId);
824
+ repo.on("before:create", async (context) => {
825
+ if (context.data) {
826
+ if (Array.isArray(context.data)) {
827
+ context.data = context.data.map((item) => ({
828
+ ...item,
829
+ organizationId: orgId
830
+ // CRITICAL: Force override - never allow caller to set different orgId
831
+ }));
832
+ } else {
833
+ context.data = {
834
+ ...context.data,
835
+ organizationId: orgId
836
+ // CRITICAL: Force override - never allow caller to set different orgId
837
+ };
838
+ }
839
+ }
840
+ });
841
+ repo.on("before:getAll", async (context) => {
842
+ context.filters = {
843
+ ...context.filters || {},
844
+ organizationId: orgId
845
+ };
846
+ });
847
+ repo.on("before:getById", async (context) => {
848
+ context.filters = {
849
+ ...context.filters || {},
850
+ organizationId: orgId
851
+ };
852
+ });
853
+ repo.on("before:getByQuery", async (context) => {
854
+ if (!context.query) {
855
+ context.query = {};
856
+ }
857
+ context.query.organizationId = orgId;
858
+ });
859
+ repo.on("before:update", async (context) => {
860
+ context.filters = {
861
+ ...context.filters || {},
862
+ organizationId: orgId
863
+ };
864
+ });
865
+ repo.on("before:delete", async (context) => {
866
+ context.filters = {
867
+ ...context.filters || {},
868
+ organizationId: orgId
869
+ };
870
+ });
871
+ }
872
+ };
873
+ }
874
+
875
+ // src/utils/money.ts
876
+ function roundMoney(value, decimals = 2) {
877
+ const multiplier = Math.pow(10, decimals);
878
+ const scaled = value * multiplier;
879
+ const fraction = scaled - Math.floor(scaled);
880
+ if (Math.abs(fraction - 0.5) < 1e-10) {
881
+ const floor = Math.floor(scaled);
882
+ const rounded = floor % 2 === 0 ? floor : floor + 1;
883
+ return rounded / multiplier;
884
+ }
885
+ return Math.round(scaled) / multiplier;
886
+ }
887
+ function percentageOf(amount, percentage, decimals = 2) {
888
+ return roundMoney(amount * percentage / 100, decimals);
889
+ }
890
+ function prorateAmount(amount, ratio, decimals = 2) {
891
+ return roundMoney(amount * ratio, decimals);
892
+ }
893
+
894
+ // src/config.ts
895
+ var HRM_CONFIG = {
896
+ dataRetention: {
897
+ /**
898
+ * Default retention period for payroll records in seconds
899
+ *
900
+ * STANDARD APPROACH: expireAt field + configurable TTL index
901
+ *
902
+ * ## How It Works:
903
+ * 1. Set expireAt date on each payroll record
904
+ * 2. Call PayrollRecord.configureRetention() at app startup
905
+ * 3. MongoDB deletes documents when expireAt is reached
906
+ *
907
+ * ## Usage:
908
+ *
909
+ * @example Configure at initialization
910
+ * ```typescript
911
+ * await payroll.init({ ... });
912
+ * await PayrollRecord.configureRetention(0); // 0 = delete when expireAt reached
913
+ * ```
914
+ *
915
+ * @example Set expireAt per record
916
+ * ```typescript
917
+ * const expireAt = PayrollRecord.calculateExpireAt(7); // 7 years
918
+ * await PayrollRecord.updateOne({ _id }, { expireAt });
919
+ * ```
920
+ *
921
+ * ## Jurisdiction Requirements:
922
+ * - USA: 7 years → 220752000 seconds
923
+ * - EU/UK: 6 years → 189216000 seconds
924
+ * - Germany: 10 years → 315360000 seconds
925
+ * - India: 8 years → 252288000 seconds
926
+ *
927
+ * Set to 0 to disable TTL
928
+ */
929
+ payrollRecordsTTL: 63072e3,
930
+ // 2 years - adjust per jurisdiction
931
+ exportWarningDays: 30,
932
+ archiveBeforeDeletion: true
933
+ },
934
+ payroll: {
935
+ defaultCurrency: "USD",
936
+ allowProRating: true,
937
+ attendanceIntegration: true,
938
+ autoDeductions: true,
939
+ overtimeEnabled: false,
940
+ overtimeMultiplier: 1.5
941
+ },
942
+ salary: {
943
+ minimumWage: 0,
944
+ maximumAllowances: 10,
945
+ maximumDeductions: 10,
946
+ defaultFrequency: "monthly"
947
+ },
948
+ employment: {
949
+ defaultProbationMonths: 3,
950
+ maxProbationMonths: 6,
951
+ allowReHiring: true,
952
+ trackEmploymentHistory: true
953
+ },
954
+ validation: {
955
+ requireBankDetails: false,
956
+ requireUserId: false,
957
+ // Modern: Allow guest employees by default
958
+ identityMode: "employeeId",
959
+ // Modern: Use human-readable IDs as primary
960
+ identityFallbacks: ["email", "userId"]
961
+ // Smart fallback chain
962
+ }
963
+ };
964
+ var ORG_ROLES = {
965
+ OWNER: {
966
+ key: "owner",
967
+ label: "Owner",
968
+ description: "Full organization access (set by Organization model)"
969
+ },
970
+ MANAGER: {
971
+ key: "manager",
972
+ label: "Manager",
973
+ description: "Management and administrative features"
974
+ },
975
+ TRAINER: {
976
+ key: "trainer",
977
+ label: "Trainer",
978
+ description: "Training and coaching features"
979
+ },
980
+ STAFF: {
981
+ key: "staff",
982
+ label: "Staff",
983
+ description: "General staff access to basic features"
984
+ },
985
+ INTERN: {
986
+ key: "intern",
987
+ label: "Intern",
988
+ description: "Limited access for interns"
989
+ },
990
+ CONSULTANT: {
991
+ key: "consultant",
992
+ label: "Consultant",
993
+ description: "Project-based consultant access"
994
+ }
995
+ };
996
+ Object.values(ORG_ROLES).map((role) => role.key);
997
+ function getPayPeriodsPerYear(frequency) {
998
+ const periodsMap = {
999
+ monthly: 12,
1000
+ bi_weekly: 26,
1001
+ weekly: 52,
1002
+ daily: 365,
1003
+ hourly: 2080
1004
+ // Assuming 40 hours/week * 52 weeks
1005
+ };
1006
+ return periodsMap[frequency];
1007
+ }
1008
+ function mergeConfig(customConfig) {
1009
+ if (!customConfig) return HRM_CONFIG;
1010
+ return {
1011
+ dataRetention: { ...HRM_CONFIG.dataRetention, ...customConfig.dataRetention },
1012
+ payroll: { ...HRM_CONFIG.payroll, ...customConfig.payroll },
1013
+ salary: { ...HRM_CONFIG.salary, ...customConfig.salary },
1014
+ employment: { ...HRM_CONFIG.employment, ...customConfig.employment },
1015
+ validation: {
1016
+ ...HRM_CONFIG.validation,
1017
+ ...customConfig.validation,
1018
+ // Ensure fallbacks is always EmployeeIdentityMode[]
1019
+ identityFallbacks: customConfig.validation?.identityFallbacks ?? HRM_CONFIG.validation.identityFallbacks
1020
+ }
1021
+ };
1022
+ }
1023
+
1024
+ // src/core/container.ts
1025
+ var Container = class {
1026
+ _models = null;
1027
+ _config = HRM_CONFIG;
1028
+ _singleTenant = null;
1029
+ _logger;
1030
+ _initialized = false;
1031
+ constructor() {
1032
+ this._logger = getLogger();
1033
+ }
1034
+ /**
1035
+ * Initialize container with configuration
1036
+ */
1037
+ initialize(config) {
1038
+ if (this._initialized) {
1039
+ this._logger.warn("Container already initialized, re-initializing");
1040
+ }
1041
+ this._models = config.models;
1042
+ this._config = mergeConfig(config.config);
1043
+ this._singleTenant = config.singleTenant ?? null;
1044
+ if (config.logger) {
1045
+ this._logger = config.logger;
1046
+ }
1047
+ this._initialized = true;
1048
+ this._logger.info("Container initialized", {
1049
+ hasEmployeeModel: !!this._models.EmployeeModel,
1050
+ hasPayrollRecordModel: !!this._models.PayrollRecordModel,
1051
+ hasTransactionModel: !!this._models.TransactionModel,
1052
+ hasAttendanceModel: !!this._models.AttendanceModel,
1053
+ hasLeaveRequestModel: !!this._models.LeaveRequestModel,
1054
+ hasTaxWithholdingModel: !!this._models.TaxWithholdingModel,
1055
+ isSingleTenant: !!this._singleTenant
1056
+ });
1057
+ }
1058
+ /**
1059
+ * Check if container is initialized
1060
+ */
1061
+ isInitialized() {
1062
+ return this._initialized;
1063
+ }
1064
+ /**
1065
+ * Reset container (useful for testing)
1066
+ */
1067
+ reset() {
1068
+ this._models = null;
1069
+ this._config = HRM_CONFIG;
1070
+ this._singleTenant = null;
1071
+ this._initialized = false;
1072
+ this._logger.info("Container reset");
1073
+ }
1074
+ /**
1075
+ * Ensure container is initialized
1076
+ */
1077
+ ensureInitialized() {
1078
+ if (!this._initialized || !this._models) {
1079
+ throw new Error(
1080
+ "Payroll not initialized. Call Payroll.initialize() first."
1081
+ );
1082
+ }
1083
+ }
1084
+ /**
1085
+ * Get models container (strongly typed)
1086
+ */
1087
+ getModels() {
1088
+ this.ensureInitialized();
1089
+ return this._models;
1090
+ }
1091
+ /**
1092
+ * Get Employee model (strongly typed)
1093
+ */
1094
+ getEmployeeModel() {
1095
+ this.ensureInitialized();
1096
+ return this._models.EmployeeModel;
1097
+ }
1098
+ /**
1099
+ * Get PayrollRecord model (strongly typed)
1100
+ */
1101
+ getPayrollRecordModel() {
1102
+ this.ensureInitialized();
1103
+ return this._models.PayrollRecordModel;
1104
+ }
1105
+ /**
1106
+ * Get Transaction model (strongly typed)
1107
+ */
1108
+ getTransactionModel() {
1109
+ this.ensureInitialized();
1110
+ return this._models.TransactionModel;
1111
+ }
1112
+ /**
1113
+ * Get Attendance model (optional, strongly typed)
1114
+ */
1115
+ getAttendanceModel() {
1116
+ this.ensureInitialized();
1117
+ return this._models.AttendanceModel ?? null;
1118
+ }
1119
+ /**
1120
+ * Get LeaveRequest model (optional, strongly typed)
1121
+ */
1122
+ getLeaveRequestModel() {
1123
+ this.ensureInitialized();
1124
+ return this._models.LeaveRequestModel ?? null;
1125
+ }
1126
+ /**
1127
+ * Get TaxWithholding model (optional, strongly typed)
1128
+ */
1129
+ getTaxWithholdingModel() {
1130
+ this.ensureInitialized();
1131
+ return this._models.TaxWithholdingModel ?? null;
1132
+ }
1133
+ /**
1134
+ * Get configuration
1135
+ */
1136
+ getConfig() {
1137
+ return this._config;
1138
+ }
1139
+ /**
1140
+ * Get specific config section
1141
+ */
1142
+ getConfigSection(section) {
1143
+ return this._config[section];
1144
+ }
1145
+ /**
1146
+ * Check if single-tenant mode
1147
+ */
1148
+ isSingleTenant() {
1149
+ return !!this._singleTenant;
1150
+ }
1151
+ /**
1152
+ * Get single-tenant config
1153
+ */
1154
+ getSingleTenantConfig() {
1155
+ return this._singleTenant;
1156
+ }
1157
+ /**
1158
+ * Get organization ID (for single-tenant mode)
1159
+ */
1160
+ getOrganizationId() {
1161
+ if (!this._singleTenant || !this._singleTenant.organizationId) return null;
1162
+ return typeof this._singleTenant.organizationId === "string" ? this._singleTenant.organizationId : this._singleTenant.organizationId.toString();
1163
+ }
1164
+ /**
1165
+ * Get logger
1166
+ */
1167
+ getLogger() {
1168
+ return this._logger;
1169
+ }
1170
+ /**
1171
+ * Set logger
1172
+ */
1173
+ setLogger(logger) {
1174
+ this._logger = logger;
1175
+ }
1176
+ /**
1177
+ * Has attendance integration
1178
+ */
1179
+ hasAttendanceIntegration() {
1180
+ return !!this._models?.AttendanceModel && this._config.payroll.attendanceIntegration;
1181
+ }
1182
+ /**
1183
+ * Create operation context with defaults
1184
+ *
1185
+ * In single-tenant mode with autoInject enabled (default), automatically
1186
+ * injects the configured organizationId into the context.
1187
+ *
1188
+ * @throws Error if autoInject is enabled but no organizationId is configured
1189
+ */
1190
+ createOperationContext(overrides) {
1191
+ const context = {};
1192
+ const isSingleTenant2 = !!this._singleTenant;
1193
+ const autoInjectEnabled = isSingleTenant2 && this._singleTenant?.autoInject !== false;
1194
+ if (autoInjectEnabled && !overrides?.organizationId) {
1195
+ const orgId = this.getOrganizationId();
1196
+ if (orgId) {
1197
+ context.organizationId = orgId;
1198
+ } else {
1199
+ throw new Error(
1200
+ "Single-tenant mode with autoInject enabled requires organizationId in configuration. Configure it via forSingleTenant({ organizationId: YOUR_ORG_ID }) or provide it explicitly."
1201
+ );
1202
+ }
1203
+ }
1204
+ return { ...context, ...overrides };
1205
+ }
1206
+ };
1207
+ var defaultContainer = null;
1208
+ function getContainer() {
1209
+ if (!defaultContainer) {
1210
+ defaultContainer = new Container();
1211
+ }
1212
+ return defaultContainer;
1213
+ }
1214
+ function initializeContainer(config) {
1215
+ getContainer().initialize(config);
1216
+ }
1217
+ function isContainerInitialized() {
1218
+ return defaultContainer?.isInitialized() ?? false;
1219
+ }
1220
+ function getModels() {
1221
+ return getContainer().getModels();
1222
+ }
1223
+ function getConfig() {
1224
+ return getContainer().getConfig();
1225
+ }
1226
+ function isSingleTenant() {
1227
+ return getContainer().isSingleTenant();
1228
+ }
1229
+
1230
+ // src/utils/date.ts
1231
+ function toUTCDateString(date) {
1232
+ const d = new Date(date);
1233
+ d.setHours(0, 0, 0, 0);
1234
+ return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
1235
+ }
1236
+ function isEffectiveForPeriod(item, periodStart, periodEnd) {
1237
+ const effectiveFrom = item.effectiveFrom ? new Date(item.effectiveFrom) : /* @__PURE__ */ new Date(0);
1238
+ const effectiveTo = item.effectiveTo ? new Date(item.effectiveTo) : /* @__PURE__ */ new Date("2099-12-31");
1239
+ return effectiveFrom <= periodEnd && effectiveTo >= periodStart;
1240
+ }
1241
+
1242
+ // src/core/config.ts
1243
+ var DEFAULT_WORK_SCHEDULE = {
1244
+ workingDays: [1, 2, 3, 4, 5],
1245
+ // Monday to Friday
1246
+ hoursPerDay: 8
1247
+ };
1248
+ function countWorkingDays(startDate, endDate, options = {}) {
1249
+ const workDays = options.workingDays || DEFAULT_WORK_SCHEDULE.workingDays;
1250
+ const holidaySet = new Set(
1251
+ (options.holidays || []).map((d) => toUTCDateString(d))
1252
+ );
1253
+ let totalDays = 0;
1254
+ let workingDays = 0;
1255
+ let holidays = 0;
1256
+ let weekends = 0;
1257
+ const current = new Date(startDate);
1258
+ current.setHours(0, 0, 0, 0);
1259
+ const end = new Date(endDate);
1260
+ end.setHours(0, 0, 0, 0);
1261
+ while (current <= end) {
1262
+ totalDays++;
1263
+ const isHoliday = holidaySet.has(toUTCDateString(current));
1264
+ const isWorkDay = workDays.includes(current.getDay());
1265
+ if (isHoliday) {
1266
+ holidays++;
1267
+ } else if (isWorkDay) {
1268
+ workingDays++;
1269
+ } else {
1270
+ weekends++;
1271
+ }
1272
+ current.setDate(current.getDate() + 1);
1273
+ }
1274
+ return { totalDays, workingDays, weekends, holidays };
1275
+ }
1276
+ function calculateProration(hireDate, terminationDate, periodStart, periodEnd) {
1277
+ const hire = new Date(hireDate);
1278
+ hire.setHours(0, 0, 0, 0);
1279
+ const term = terminationDate ? new Date(terminationDate) : null;
1280
+ if (term) term.setHours(0, 0, 0, 0);
1281
+ const start = new Date(periodStart);
1282
+ start.setHours(0, 0, 0, 0);
1283
+ const end = new Date(periodEnd);
1284
+ end.setHours(0, 0, 0, 0);
1285
+ if (hire > end || term && term < start) {
1286
+ return { ratio: 0, reason: "not_active", isProrated: true };
1287
+ }
1288
+ const effectiveStart = hire > start ? hire : start;
1289
+ const effectiveEnd = term && term < end ? term : end;
1290
+ const totalDays = Math.ceil((end.getTime() - start.getTime()) / 864e5) + 1;
1291
+ const actualDays = Math.ceil((effectiveEnd.getTime() - effectiveStart.getTime()) / 864e5) + 1;
1292
+ const ratio = Math.min(1, Math.max(0, actualDays / totalDays));
1293
+ const isNewHire = hire > start;
1294
+ const isTermination = term !== null && term < end;
1295
+ let reason = "full";
1296
+ if (isNewHire && isTermination) {
1297
+ reason = "both";
1298
+ } else if (isNewHire) {
1299
+ reason = "new_hire";
1300
+ } else if (isTermination) {
1301
+ reason = "termination";
1302
+ }
1303
+ return { ratio, reason, isProrated: ratio < 1 };
1304
+ }
1305
+ function getPayPeriod(month, year, payDay = 28) {
1306
+ const startDate = new Date(year, month - 1, 1);
1307
+ const endDate = new Date(year, month, 0);
1308
+ const payDate = new Date(year, month - 1, Math.min(payDay, endDate.getDate()));
1309
+ return { startDate, endDate, payDate };
1310
+ }
1311
+
1312
+ // src/calculators/attendance.calculator.ts
1313
+ function calculateAttendanceDeduction(input) {
1314
+ const { expectedWorkingDays, actualWorkingDays, dailyRate } = input;
1315
+ const expected = Math.max(0, expectedWorkingDays);
1316
+ const actual = Math.max(0, actualWorkingDays);
1317
+ const rate = Math.max(0, dailyRate);
1318
+ const absentDays = Math.max(0, expected - actual);
1319
+ const deductionAmount = roundMoney(absentDays * rate, 2);
1320
+ return {
1321
+ absentDays,
1322
+ deductionAmount,
1323
+ dailyRate: rate,
1324
+ hasDeduction: deductionAmount > 0
1325
+ };
1326
+ }
1327
+ function calculateDailyRate(monthlySalary, workingDays) {
1328
+ if (workingDays <= 0) return 0;
1329
+ return roundMoney(monthlySalary / workingDays, 2);
1330
+ }
1331
+
1332
+ // src/utils/calculation.ts
1333
+ function sumBy(items, getter) {
1334
+ return items.reduce((total, item) => total + getter(item), 0);
1335
+ }
1336
+ function sumAllowances(allowances) {
1337
+ return sumBy(allowances, (a) => a.amount);
1338
+ }
1339
+ function sumDeductions(deductions) {
1340
+ return sumBy(deductions, (d) => d.amount);
1341
+ }
1342
+ function calculateGross(baseAmount, allowances) {
1343
+ return baseAmount + sumAllowances(allowances);
1344
+ }
1345
+ function calculateNet(gross, deductions) {
1346
+ return Math.max(0, gross - sumDeductions(deductions));
1347
+ }
1348
+ function applyTaxBrackets(amount, brackets) {
1349
+ let tax = 0;
1350
+ for (const bracket of brackets) {
1351
+ if (amount > bracket.min) {
1352
+ const taxableAmount = Math.min(amount, bracket.max) - bracket.min;
1353
+ tax += taxableAmount * bracket.rate;
1354
+ }
1355
+ }
1356
+ return roundMoney(tax);
1357
+ }
1358
+
1359
+ // src/calculators/prorating.calculator.ts
1360
+ function calculateProRating(input) {
1361
+ const { hireDate, terminationDate, periodStart, periodEnd, workingDays, holidays = [] } = input;
1362
+ const hire = new Date(hireDate);
1363
+ const termination = terminationDate ? new Date(terminationDate) : null;
1364
+ const effectiveStart = hire > periodStart ? hire : periodStart;
1365
+ const effectiveEnd = termination && termination < periodEnd ? termination : periodEnd;
1366
+ if (effectiveStart > periodEnd || termination && termination < periodStart) {
1367
+ const periodWorkingDays2 = countWorkingDays(periodStart, periodEnd, { workingDays, holidays }).workingDays;
1368
+ return {
1369
+ isProRated: true,
1370
+ ratio: 0,
1371
+ periodWorkingDays: periodWorkingDays2,
1372
+ effectiveWorkingDays: 0,
1373
+ effectiveStart: periodStart,
1374
+ effectiveEnd: periodStart
1375
+ // Effectively zero days
1376
+ };
1377
+ }
1378
+ const periodWorkingDays = countWorkingDays(periodStart, periodEnd, { workingDays, holidays }).workingDays;
1379
+ const effectiveWorkingDays = countWorkingDays(effectiveStart, effectiveEnd, { workingDays, holidays }).workingDays;
1380
+ const ratio = periodWorkingDays > 0 ? Math.min(1, Math.max(0, effectiveWorkingDays / periodWorkingDays)) : 0;
1381
+ const isProRated = ratio < 1;
1382
+ return {
1383
+ isProRated,
1384
+ ratio,
1385
+ periodWorkingDays,
1386
+ effectiveWorkingDays,
1387
+ effectiveStart,
1388
+ effectiveEnd
1389
+ };
1390
+ }
1391
+
1392
+ // src/calculators/salary.calculator.ts
1393
+ function calculateSalaryBreakdown(input) {
1394
+ const { employee, period, attendance, options = {}, config, taxBrackets, taxOptions, jurisdictionTaxConfig } = input;
1395
+ const comp = employee.compensation;
1396
+ const originalBaseAmount = comp.baseAmount;
1397
+ const proRating = calculateProRatingForSalary(
1398
+ employee.hireDate,
1399
+ employee.terminationDate || null,
1400
+ period.startDate,
1401
+ period.endDate,
1402
+ options,
1403
+ employee.workSchedule
1404
+ );
1405
+ let baseAmount = originalBaseAmount;
1406
+ if (proRating.isProRated && config.allowProRating && !options.skipProration) {
1407
+ baseAmount = prorateAmount(baseAmount, proRating.ratio);
1408
+ }
1409
+ const effectiveAllowances = (comp.allowances || []).filter((a) => isEffectiveForPeriod(a, period.startDate, period.endDate));
1410
+ const effectiveDeductions = (comp.deductions || []).filter((d) => isEffectiveForPeriod(d, period.startDate, period.endDate)).filter((d) => d.auto || d.recurring);
1411
+ const allowances = processAllowances(effectiveAllowances, originalBaseAmount, proRating, config, options.skipProration);
1412
+ const deductions = processDeductions(effectiveDeductions, originalBaseAmount, proRating, config, options.skipProration);
1413
+ if (!options.skipAttendance && config.attendanceIntegration && attendance) {
1414
+ const attendanceDeductionResult = calculateAttendanceDeductionFromData(
1415
+ attendance,
1416
+ baseAmount,
1417
+ proRating.effectiveWorkingDays
1418
+ );
1419
+ if (attendanceDeductionResult.hasDeduction) {
1420
+ deductions.push({
1421
+ type: "absence",
1422
+ amount: attendanceDeductionResult.deductionAmount,
1423
+ description: `Unpaid leave deduction (${attendanceDeductionResult.absentDays} days)`
1424
+ });
1425
+ }
1426
+ }
1427
+ const grossSalary = calculateGross(baseAmount, allowances);
1428
+ const taxableAllowances = allowances.filter((a) => a.taxable);
1429
+ let taxableAmount = baseAmount + sumAllowances(taxableAllowances);
1430
+ const preTaxDeductionAmount = calculatePreTaxDeductions(
1431
+ effectiveDeductions,
1432
+ deductions,
1433
+ taxOptions,
1434
+ jurisdictionTaxConfig
1435
+ );
1436
+ taxableAmount = Math.max(0, taxableAmount - preTaxDeductionAmount);
1437
+ const frequency = employee.compensation?.frequency || "monthly";
1438
+ let taxAmount = 0;
1439
+ if (!options.skipTax && taxBrackets.length > 0 && config.autoDeductions) {
1440
+ taxAmount = calculateEnhancedTax(
1441
+ taxableAmount,
1442
+ taxBrackets,
1443
+ taxOptions,
1444
+ jurisdictionTaxConfig,
1445
+ frequency
1446
+ );
1447
+ }
1448
+ if (taxAmount > 0) {
1449
+ deductions.push({
1450
+ type: "tax",
1451
+ amount: taxAmount,
1452
+ description: "Income tax"
1453
+ });
1454
+ }
1455
+ const netSalary = calculateNet(grossSalary, deductions);
1456
+ return {
1457
+ baseAmount,
1458
+ allowances,
1459
+ deductions,
1460
+ grossSalary,
1461
+ netSalary,
1462
+ taxableAmount,
1463
+ taxAmount,
1464
+ workingDays: proRating.periodWorkingDays,
1465
+ actualDays: proRating.effectiveWorkingDays,
1466
+ proRatedAmount: proRating.isProRated && !options.skipProration ? baseAmount : 0,
1467
+ attendanceDeduction: attendance ? deductions.find((d) => d.type === "absence")?.amount || 0 : 0
1468
+ };
1469
+ }
1470
+ function calculateProRatingForSalary(hireDate, terminationDate, periodStart, periodEnd, options, employeeWorkSchedule) {
1471
+ const workingDays = options?.workSchedule?.workingDays || employeeWorkSchedule?.workingDays || [1, 2, 3, 4, 5];
1472
+ const holidays = options?.holidays || [];
1473
+ return calculateProRating({
1474
+ hireDate,
1475
+ terminationDate,
1476
+ periodStart,
1477
+ periodEnd,
1478
+ workingDays,
1479
+ holidays
1480
+ });
1481
+ }
1482
+ function processAllowances(allowances, originalBaseAmount, proRating, config, skipProration) {
1483
+ return allowances.map((a) => {
1484
+ let amount = a.isPercentage && a.value !== void 0 ? percentageOf(originalBaseAmount, a.value) : a.amount;
1485
+ const originalAmount = amount;
1486
+ if (proRating.isProRated && config.allowProRating && !skipProration) {
1487
+ amount = prorateAmount(amount, proRating.ratio);
1488
+ }
1489
+ return {
1490
+ type: a.type,
1491
+ amount,
1492
+ taxable: a.taxable ?? true,
1493
+ originalAmount,
1494
+ isPercentage: a.isPercentage,
1495
+ value: a.value
1496
+ };
1497
+ });
1498
+ }
1499
+ function processDeductions(deductions, originalBaseAmount, proRating, config, skipProration) {
1500
+ return deductions.map((d) => {
1501
+ let amount = d.isPercentage && d.value !== void 0 ? percentageOf(originalBaseAmount, d.value) : d.amount;
1502
+ const originalAmount = amount;
1503
+ if (proRating.isProRated && config.allowProRating && !skipProration) {
1504
+ amount = prorateAmount(amount, proRating.ratio);
1505
+ }
1506
+ return {
1507
+ type: d.type,
1508
+ amount,
1509
+ description: d.description,
1510
+ originalAmount,
1511
+ isPercentage: d.isPercentage,
1512
+ value: d.value
1513
+ };
1514
+ });
1515
+ }
1516
+ function calculateAttendanceDeductionFromData(attendance, baseAmount, effectiveWorkingDays) {
1517
+ const expectedDays = attendance.expectedDays ?? effectiveWorkingDays;
1518
+ const actualDays = attendance.actualDays;
1519
+ if (actualDays === void 0) {
1520
+ return { hasDeduction: false, deductionAmount: 0, absentDays: 0 };
1521
+ }
1522
+ const dailyRate = calculateDailyRate(baseAmount, expectedDays);
1523
+ const result = calculateAttendanceDeduction({
1524
+ expectedWorkingDays: expectedDays,
1525
+ actualWorkingDays: actualDays,
1526
+ dailyRate
1527
+ });
1528
+ return {
1529
+ hasDeduction: result.hasDeduction,
1530
+ deductionAmount: result.deductionAmount,
1531
+ absentDays: result.absentDays
1532
+ };
1533
+ }
1534
+ function calculatePreTaxDeductions(effectiveDeductions, processedDeductions, taxOptions, jurisdictionTaxConfig) {
1535
+ let totalPreTax = 0;
1536
+ for (let i = 0; i < effectiveDeductions.length; i++) {
1537
+ const original = effectiveDeductions[i];
1538
+ const processed = processedDeductions[i];
1539
+ if (original.reducesTaxableIncome) {
1540
+ totalPreTax += processed?.amount || 0;
1541
+ }
1542
+ }
1543
+ if (jurisdictionTaxConfig?.preTaxDeductionTypes?.length) {
1544
+ const preTaxTypes = new Set(jurisdictionTaxConfig.preTaxDeductionTypes);
1545
+ for (let i = 0; i < effectiveDeductions.length; i++) {
1546
+ const original = effectiveDeductions[i];
1547
+ const processed = processedDeductions[i];
1548
+ if (original.reducesTaxableIncome) continue;
1549
+ if (preTaxTypes.has(original.type)) {
1550
+ totalPreTax += processed?.amount || 0;
1551
+ }
1552
+ }
1553
+ }
1554
+ if (taxOptions?.preTaxDeductions?.length) {
1555
+ for (const deduction of taxOptions.preTaxDeductions) {
1556
+ totalPreTax += deduction.amount;
1557
+ }
1558
+ }
1559
+ return roundMoney(totalPreTax);
1560
+ }
1561
+ function calculateEnhancedTax(periodTaxable, taxBrackets, taxOptions, jurisdictionTaxConfig, frequency = "monthly") {
1562
+ const periodsPerYear = getPayPeriodsPerYear(frequency);
1563
+ let annualTaxable = periodTaxable * periodsPerYear;
1564
+ const threshold = getApplicableThreshold(taxOptions, jurisdictionTaxConfig);
1565
+ if (threshold > 0) {
1566
+ annualTaxable = Math.max(0, annualTaxable - threshold);
1567
+ }
1568
+ let annualTax = applyTaxBrackets(annualTaxable, taxBrackets);
1569
+ if (taxOptions?.taxCredits?.length && annualTax > 0) {
1570
+ annualTax = applyTaxCredits(annualTax, taxOptions.taxCredits);
1571
+ }
1572
+ return roundMoney(annualTax / periodsPerYear);
1573
+ }
1574
+ function getApplicableThreshold(taxOptions, jurisdictionTaxConfig) {
1575
+ if (taxOptions?.standardDeductionOverride !== void 0) {
1576
+ return taxOptions.standardDeductionOverride;
1577
+ }
1578
+ if (taxOptions?.taxpayerCategory) {
1579
+ const category = taxOptions.taxpayerCategory;
1580
+ if (taxOptions.thresholdOverrides?.[category] !== void 0) {
1581
+ return taxOptions.thresholdOverrides[category];
1582
+ }
1583
+ if (jurisdictionTaxConfig?.thresholdsByCategory?.[category] !== void 0) {
1584
+ return jurisdictionTaxConfig.thresholdsByCategory[category];
1585
+ }
1586
+ }
1587
+ if (taxOptions?.applyStandardDeduction && jurisdictionTaxConfig?.standardDeduction) {
1588
+ return jurisdictionTaxConfig.standardDeduction;
1589
+ }
1590
+ return 0;
1591
+ }
1592
+ function applyTaxCredits(annualTax, taxCredits) {
1593
+ let remainingTax = annualTax;
1594
+ for (const credit of taxCredits) {
1595
+ if (remainingTax <= 0) break;
1596
+ let creditAmount = credit.amount;
1597
+ if (credit.maxPercent !== void 0 && credit.maxPercent > 0) {
1598
+ const maxCredit = annualTax * credit.maxPercent;
1599
+ creditAmount = Math.min(creditAmount, maxCredit);
1600
+ }
1601
+ creditAmount = Math.min(creditAmount, remainingTax);
1602
+ remainingTax -= creditAmount;
1603
+ }
1604
+ return Math.max(0, remainingTax);
1605
+ }
1606
+
1607
+ // src/core/state-machine.ts
1608
+ var StateMachine = class {
1609
+ constructor(config) {
1610
+ this.config = config;
1611
+ this.validTransitions = /* @__PURE__ */ new Map();
1612
+ for (const state of config.states) {
1613
+ this.validTransitions.set(state, /* @__PURE__ */ new Set());
1614
+ }
1615
+ for (const transition of config.transitions) {
1616
+ const fromStates = Array.isArray(transition.from) ? transition.from : [transition.from];
1617
+ for (const from of fromStates) {
1618
+ this.validTransitions.get(from)?.add(transition.to);
1619
+ }
1620
+ }
1621
+ this.terminalStates = new Set(config.terminal || []);
1622
+ }
1623
+ validTransitions;
1624
+ terminalStates;
1625
+ /**
1626
+ * Get the initial state
1627
+ */
1628
+ get initial() {
1629
+ return this.config.initial;
1630
+ }
1631
+ /**
1632
+ * Get all valid states
1633
+ */
1634
+ get states() {
1635
+ return this.config.states;
1636
+ }
1637
+ /**
1638
+ * Check if a state is valid
1639
+ */
1640
+ isValidState(state) {
1641
+ return this.config.states.includes(state);
1642
+ }
1643
+ /**
1644
+ * Check if a state is terminal (no outgoing transitions)
1645
+ */
1646
+ isTerminal(state) {
1647
+ return this.terminalStates.has(state);
1648
+ }
1649
+ /**
1650
+ * Check if transition from one state to another is valid
1651
+ */
1652
+ canTransition(from, to) {
1653
+ return this.validTransitions.get(from)?.has(to) ?? false;
1654
+ }
1655
+ /**
1656
+ * Get all valid next states from current state
1657
+ */
1658
+ getNextStates(from) {
1659
+ return Array.from(this.validTransitions.get(from) || []);
1660
+ }
1661
+ /**
1662
+ * Validate a transition and return result
1663
+ */
1664
+ validateTransition(from, to) {
1665
+ if (!this.isValidState(from)) {
1666
+ return {
1667
+ success: false,
1668
+ from,
1669
+ to,
1670
+ error: `Invalid current state: '${from}'`
1671
+ };
1672
+ }
1673
+ if (!this.isValidState(to)) {
1674
+ return {
1675
+ success: false,
1676
+ from,
1677
+ to,
1678
+ error: `Invalid target state: '${to}'`
1679
+ };
1680
+ }
1681
+ if (this.isTerminal(from)) {
1682
+ return {
1683
+ success: false,
1684
+ from,
1685
+ to,
1686
+ error: `Cannot transition from terminal state '${from}'`
1687
+ };
1688
+ }
1689
+ if (!this.canTransition(from, to)) {
1690
+ const validNext = this.getNextStates(from);
1691
+ return {
1692
+ success: false,
1693
+ from,
1694
+ to,
1695
+ error: `Invalid transition: '${from}' \u2192 '${to}'. Valid transitions from '${from}': [${validNext.join(", ")}]`
1696
+ };
1697
+ }
1698
+ return { success: true, from, to };
1699
+ }
1700
+ /**
1701
+ * Assert a transition is valid, throw if not
1702
+ */
1703
+ assertTransition(from, to) {
1704
+ const result = this.validateTransition(from, to);
1705
+ if (!result.success) {
1706
+ throw new Error(result.error);
1707
+ }
1708
+ }
1709
+ };
1710
+ function createStateMachine(config) {
1711
+ return new StateMachine(config);
1712
+ }
1713
+
1714
+ // src/core/payroll-states.ts
1715
+ var PayrollStatusMachine = createStateMachine({
1716
+ states: ["pending", "processing", "paid", "failed", "voided", "reversed"],
1717
+ initial: "pending",
1718
+ transitions: [
1719
+ // Normal flow
1720
+ { from: "pending", to: "processing" },
1721
+ { from: "processing", to: "paid" },
1722
+ // Direct payment (skip processing for single salary)
1723
+ { from: "pending", to: "paid" },
1724
+ // Failure handling
1725
+ { from: "processing", to: "failed" },
1726
+ { from: "failed", to: "pending" },
1727
+ // Retry
1728
+ // Void (unpaid only - pending, processing, or failed)
1729
+ { from: ["pending", "processing", "failed"], to: "voided" },
1730
+ // Reversal (paid only)
1731
+ { from: "paid", to: "reversed" },
1732
+ // Restore voided (back to pending for re-processing)
1733
+ { from: "voided", to: "pending" }
1734
+ ],
1735
+ terminal: ["reversed"]
1736
+ // Only reversed is truly terminal
1737
+ });
1738
+ var TaxStatusMachine = createStateMachine({
1739
+ states: ["pending", "submitted", "paid", "cancelled"],
1740
+ initial: "pending",
1741
+ transitions: [
1742
+ { from: "pending", to: "submitted" },
1743
+ { from: "submitted", to: "paid" },
1744
+ // Direct payment (some jurisdictions)
1745
+ { from: "pending", to: "paid" },
1746
+ // Cancellation (from any non-terminal state)
1747
+ { from: ["pending", "submitted"], to: "cancelled" }
1748
+ ],
1749
+ terminal: ["paid", "cancelled"]
1750
+ });
1751
+ var LeaveRequestStatusMachine = createStateMachine({
1752
+ states: ["pending", "approved", "rejected", "cancelled"],
1753
+ initial: "pending",
1754
+ transitions: [
1755
+ { from: "pending", to: "approved" },
1756
+ { from: "pending", to: "rejected" },
1757
+ { from: "pending", to: "cancelled" },
1758
+ // Cancel approved leave (before it starts)
1759
+ { from: "approved", to: "cancelled" }
1760
+ ],
1761
+ terminal: ["rejected", "cancelled"]
1762
+ });
1763
+ var EmployeeStatusMachine = createStateMachine({
1764
+ states: ["active", "on_leave", "suspended", "terminated"],
1765
+ initial: "active",
1766
+ transitions: [
1767
+ // Leave management
1768
+ { from: "active", to: "on_leave" },
1769
+ { from: "on_leave", to: "active" },
1770
+ // Suspension
1771
+ { from: ["active", "on_leave"], to: "suspended" },
1772
+ { from: "suspended", to: "active" },
1773
+ // Termination (from any state)
1774
+ { from: ["active", "on_leave", "suspended"], to: "terminated" },
1775
+ // Re-hire (back to active)
1776
+ { from: "terminated", to: "active" }
1777
+ ],
1778
+ terminal: []
1779
+ // No terminal states (re-hire possible)
1780
+ });
1781
+
1782
+ // src/core/timeline-audit.ts
1783
+ var PAYROLL_EVENTS = {
1784
+ /**
1785
+ * Employee lifecycle events
1786
+ */
1787
+ EMPLOYEE: {
1788
+ /** Employee was hired */
1789
+ HIRED: "employee.hired",
1790
+ /** Employee was terminated */
1791
+ TERMINATED: "employee.terminated",
1792
+ /** Employee was re-hired after termination */
1793
+ REHIRED: "employee.rehired",
1794
+ /** Employee status changed (active, on_leave, suspended) */
1795
+ STATUS_CHANGED: "employee.status_changed",
1796
+ /** Employee department/position changed */
1797
+ ROLE_CHANGED: "employee.role_changed",
1798
+ /** Employee probation ended */
1799
+ PROBATION_ENDED: "employee.probation_ended",
1800
+ /** Recommended event limits for employee timeline */
1801
+ limits: {
1802
+ "employee.status_changed": 50,
1803
+ "employee.role_changed": 20
1804
+ }
1805
+ },
1806
+ /**
1807
+ * Compensation events
1808
+ */
1809
+ COMPENSATION: {
1810
+ /** Base salary was updated */
1811
+ SALARY_UPDATED: "compensation.salary_updated",
1812
+ /** Allowance was added */
1813
+ ALLOWANCE_ADDED: "compensation.allowance_added",
1814
+ /** Allowance was removed */
1815
+ ALLOWANCE_REMOVED: "compensation.allowance_removed",
1816
+ /** Deduction was added */
1817
+ DEDUCTION_ADDED: "compensation.deduction_added",
1818
+ /** Deduction was removed */
1819
+ DEDUCTION_REMOVED: "compensation.deduction_removed",
1820
+ /** Bank details were updated */
1821
+ BANK_UPDATED: "compensation.bank_updated",
1822
+ /** Recommended event limits */
1823
+ limits: {
1824
+ "compensation.salary_updated": 24,
1825
+ // 2 years of monthly updates
1826
+ "compensation.allowance_added": 20,
1827
+ "compensation.allowance_removed": 20,
1828
+ "compensation.deduction_added": 20,
1829
+ "compensation.deduction_removed": 20,
1830
+ "compensation.bank_updated": 10
1831
+ }
1832
+ },
1833
+ /**
1834
+ * Payroll processing events
1835
+ */
1836
+ PAYROLL: {
1837
+ /** Salary was processed */
1838
+ PROCESSED: "payroll.processed",
1839
+ /** Payroll was voided (before payment) */
1840
+ VOIDED: "payroll.voided",
1841
+ /** Payroll was reversed (after payment) */
1842
+ REVERSED: "payroll.reversed",
1843
+ /** Payroll was restored from voided state */
1844
+ RESTORED: "payroll.restored",
1845
+ /** Payroll export was generated */
1846
+ EXPORTED: "payroll.exported",
1847
+ /** Recommended event limits */
1848
+ limits: {
1849
+ "payroll.processed": 36,
1850
+ // 3 years of monthly payroll
1851
+ "payroll.voided": 10,
1852
+ "payroll.reversed": 10,
1853
+ "payroll.restored": 5,
1854
+ "payroll.exported": 20
1855
+ }
1856
+ },
1857
+ /**
1858
+ * Tax withholding events
1859
+ */
1860
+ TAX: {
1861
+ /** Tax was withheld */
1862
+ WITHHELD: "tax.withheld",
1863
+ /** Tax was submitted to authorities */
1864
+ SUBMITTED: "tax.submitted",
1865
+ /** Tax payment was made */
1866
+ PAID: "tax.paid",
1867
+ /** Tax withholding was cancelled */
1868
+ CANCELLED: "tax.cancelled",
1869
+ /** Recommended event limits */
1870
+ limits: {
1871
+ "tax.withheld": 36,
1872
+ "tax.submitted": 12,
1873
+ "tax.paid": 12,
1874
+ "tax.cancelled": 10
1875
+ }
1876
+ },
1877
+ /**
1878
+ * Leave management events
1879
+ */
1880
+ LEAVE: {
1881
+ /** Leave was requested */
1882
+ REQUESTED: "leave.requested",
1883
+ /** Leave was approved */
1884
+ APPROVED: "leave.approved",
1885
+ /** Leave was rejected */
1886
+ REJECTED: "leave.rejected",
1887
+ /** Leave was cancelled */
1888
+ CANCELLED: "leave.cancelled",
1889
+ /** Leave balance was accrued */
1890
+ ACCRUED: "leave.accrued",
1891
+ /** Annual leave was reset */
1892
+ RESET: "leave.reset",
1893
+ /** Recommended event limits */
1894
+ limits: {
1895
+ "leave.requested": 50,
1896
+ "leave.approved": 50,
1897
+ "leave.rejected": 20,
1898
+ "leave.cancelled": 20,
1899
+ "leave.accrued": 12,
1900
+ "leave.reset": 5
1901
+ }
1902
+ }
1903
+ };
1904
+ var EMPLOYEE_TIMELINE_CONFIG = {
1905
+ ownerField: "organizationId",
1906
+ fieldName: "timeline",
1907
+ hideByDefault: true,
1908
+ // Don't include timeline in normal queries
1909
+ eventLimits: {
1910
+ ...PAYROLL_EVENTS.EMPLOYEE.limits,
1911
+ ...PAYROLL_EVENTS.COMPENSATION.limits
1912
+ }
1913
+ };
1914
+ var PAYROLL_RECORD_TIMELINE_CONFIG = {
1915
+ ownerField: "organizationId",
1916
+ fieldName: "timeline",
1917
+ hideByDefault: true,
1918
+ eventLimits: PAYROLL_EVENTS.PAYROLL.limits
1919
+ };
1920
+ var LEAVE_REQUEST_TIMELINE_CONFIG = {
1921
+ ownerField: "organizationId",
1922
+ fieldName: "timeline",
1923
+ hideByDefault: true,
1924
+ eventLimits: PAYROLL_EVENTS.LEAVE.limits
1925
+ };
1926
+ function buildTimelineMetadata(context) {
1927
+ if (!context) return {};
1928
+ const result = {};
1929
+ if (context.userId) {
1930
+ result.performedByUserId = String(context.userId);
1931
+ }
1932
+ if (context.userName) {
1933
+ result.performedByName = context.userName;
1934
+ }
1935
+ if (context.userRole) {
1936
+ result.performedByRole = context.userRole;
1937
+ }
1938
+ if (context.organizationId) {
1939
+ result.organizationId = String(context.organizationId);
1940
+ }
1941
+ return result;
1942
+ }
1943
+ function buildRequestContext(request) {
1944
+ if (!request) return void 0;
1945
+ const getHeader = (name) => {
1946
+ if (request.get) return request.get(name);
1947
+ if (request.headers) {
1948
+ const value = request.headers[name.toLowerCase()];
1949
+ return Array.isArray(value) ? value[0] : value;
1950
+ }
1951
+ return void 0;
1952
+ };
1953
+ return {
1954
+ ip: request.ip || getHeader("x-forwarded-for"),
1955
+ userAgent: getHeader("user-agent"),
1956
+ origin: getHeader("origin")
1957
+ };
1958
+ }
1959
+
1960
+ export { Container, DEFAULT_WORK_SCHEDULE, EMPLOYEE_TIMELINE_CONFIG, EmployeeStatusMachine, EventBus, IdempotencyManager, LEAVE_REQUEST_TIMELINE_CONFIG, LeaveRequestStatusMachine, PAYROLL_EVENTS, PAYROLL_RECORD_TIMELINE_CONFIG, PayrollStatusMachine, PluginManager, Result, ResultClass, StateMachine, TaxStatusMachine, WebhookManager, all, buildRequestContext, buildTimelineMetadata, calculateAttendanceDeduction, calculateProration, calculateSalaryBreakdown, countWorkingDays, createEventBus, createNotificationPlugin, createStateMachine, definePlugin, err, flatMap, fromNullable, fromPromise, generatePayrollIdempotencyKey, getConfig, getContainer, getEventBus, getModels, getPayPeriod, initializeContainer, isContainerInitialized, isErr, isOk, isSingleTenant, loggingPlugin, map, mapErr, match, metricsPlugin, multiTenantPlugin, notificationPlugin, ok, onEmployeeHired, onMilestoneAchieved, onPayrollCompleted, onSalaryProcessed, resetEventBus, tryCatch, tryCatchSync, unwrap, unwrapOr, unwrapOrElse };
1961
+ //# sourceMappingURL=index.js.map
1962
+ //# sourceMappingURL=index.js.map