@bernierllc/email-manager 0.4.0 → 0.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/README.md +653 -0
  2. package/dist/capabilities/degradation-logger.d.ts +59 -0
  3. package/dist/capabilities/degradation-logger.d.ts.map +1 -0
  4. package/dist/capabilities/degradation-logger.js +106 -0
  5. package/dist/capabilities/degradation-logger.js.map +1 -0
  6. package/dist/capabilities/feature-router.d.ts +55 -0
  7. package/dist/capabilities/feature-router.d.ts.map +1 -0
  8. package/dist/capabilities/feature-router.js +143 -0
  9. package/dist/capabilities/feature-router.js.map +1 -0
  10. package/dist/capabilities/in-memory-resolver.d.ts +27 -0
  11. package/dist/capabilities/in-memory-resolver.d.ts.map +1 -0
  12. package/dist/capabilities/in-memory-resolver.js +79 -0
  13. package/dist/capabilities/in-memory-resolver.js.map +1 -0
  14. package/dist/capabilities/index.d.ts +13 -0
  15. package/dist/capabilities/index.d.ts.map +1 -0
  16. package/dist/capabilities/index.js +21 -0
  17. package/dist/capabilities/index.js.map +1 -0
  18. package/dist/capabilities/matrix.d.ts +66 -0
  19. package/dist/capabilities/matrix.d.ts.map +1 -0
  20. package/dist/capabilities/matrix.js +247 -0
  21. package/dist/capabilities/matrix.js.map +1 -0
  22. package/dist/capabilities/redis-resolver.d.ts +95 -0
  23. package/dist/capabilities/redis-resolver.d.ts.map +1 -0
  24. package/dist/capabilities/redis-resolver.js +227 -0
  25. package/dist/capabilities/redis-resolver.js.map +1 -0
  26. package/dist/capabilities/resolver-factory.d.ts +30 -0
  27. package/dist/capabilities/resolver-factory.d.ts.map +1 -0
  28. package/dist/capabilities/resolver-factory.js +70 -0
  29. package/dist/capabilities/resolver-factory.js.map +1 -0
  30. package/dist/capabilities/resolver.d.ts +40 -0
  31. package/dist/capabilities/resolver.d.ts.map +1 -0
  32. package/dist/capabilities/resolver.js +18 -0
  33. package/dist/capabilities/resolver.js.map +1 -0
  34. package/dist/capabilities/routing-metadata.d.ts +16 -0
  35. package/dist/capabilities/routing-metadata.d.ts.map +1 -0
  36. package/dist/capabilities/routing-metadata.js +17 -0
  37. package/dist/capabilities/routing-metadata.js.map +1 -0
  38. package/dist/capabilities/safe-resolver.d.ts +24 -0
  39. package/dist/capabilities/safe-resolver.d.ts.map +1 -0
  40. package/dist/capabilities/safe-resolver.js +48 -0
  41. package/dist/capabilities/safe-resolver.js.map +1 -0
  42. package/dist/config/schema.d.ts +99 -4
  43. package/dist/config/schema.d.ts.map +1 -1
  44. package/dist/config/schema.js +17 -0
  45. package/dist/config/schema.js.map +1 -1
  46. package/dist/email-manager.d.ts.map +1 -1
  47. package/dist/email-manager.js +28 -1
  48. package/dist/email-manager.js.map +1 -1
  49. package/dist/enhanced-email-manager.d.ts +163 -1
  50. package/dist/enhanced-email-manager.d.ts.map +1 -1
  51. package/dist/enhanced-email-manager.js +412 -8
  52. package/dist/enhanced-email-manager.js.map +1 -1
  53. package/dist/errors.d.ts +11 -0
  54. package/dist/errors.d.ts.map +1 -1
  55. package/dist/errors.js +13 -0
  56. package/dist/errors.js.map +1 -1
  57. package/dist/index.d.ts +13 -1
  58. package/dist/index.d.ts.map +1 -1
  59. package/dist/index.js +7 -1
  60. package/dist/index.js.map +1 -1
  61. package/dist/managers/provider-manager.d.ts +43 -0
  62. package/dist/managers/provider-manager.d.ts.map +1 -1
  63. package/dist/managers/provider-manager.js +102 -4
  64. package/dist/managers/provider-manager.js.map +1 -1
  65. package/dist/types.d.ts +66 -0
  66. package/dist/types.d.ts.map +1 -1
  67. package/dist/types.js.map +1 -1
  68. package/package.json +38 -25
@@ -0,0 +1,59 @@
1
+ export type LogLevel = 'silent' | 'info' | 'warn' | 'error';
2
+ export interface DegradationLoggerConfig {
3
+ logger: {
4
+ warn: Function;
5
+ info: Function;
6
+ error: Function;
7
+ };
8
+ logLevel: LogLevel;
9
+ includeDocLinks: boolean;
10
+ docsBaseUrl?: string;
11
+ }
12
+ export interface DegradationEvent {
13
+ provider: string;
14
+ feature: string;
15
+ strategy: string;
16
+ description: string;
17
+ docUrl: string;
18
+ }
19
+ export interface UnsupportedEvent {
20
+ provider: string;
21
+ feature: string;
22
+ message: string;
23
+ }
24
+ /**
25
+ * DegradationLogger provides structured logging for feature degradation
26
+ * and unsupported feature events across email providers. It respects
27
+ * configurable log levels and optionally includes documentation links.
28
+ */
29
+ export declare class DegradationLogger {
30
+ private readonly config;
31
+ constructor(config: DegradationLoggerConfig);
32
+ /**
33
+ * Log a degradation event when a provider does not natively support a feature
34
+ * and a fallback strategy is being used.
35
+ */
36
+ logDegradation(event: DegradationEvent): void;
37
+ /**
38
+ * Log an unsupported feature event. These are always logged at error level
39
+ * (unless log level is silent).
40
+ */
41
+ logUnsupported(event: UnsupportedEvent): void;
42
+ /**
43
+ * Check whether a message at the given severity should be logged
44
+ * based on the configured log level.
45
+ *
46
+ * The configured logLevel sets the minimum severity that gets logged:
47
+ * - 'silent': nothing is logged
48
+ * - 'error': only error-level messages
49
+ * - 'warn': error + warn messages
50
+ * - 'info': everything (error + warn + info)
51
+ */
52
+ private shouldLog;
53
+ /**
54
+ * Resolve a doc URL, prepending the docsBaseUrl if the path is relative
55
+ * and includeDocLinks is enabled.
56
+ */
57
+ private resolveDocUrl;
58
+ }
59
+ //# sourceMappingURL=degradation-logger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"degradation-logger.d.ts","sourceRoot":"","sources":["../../src/capabilities/degradation-logger.ts"],"names":[],"mappings":"AAQA,MAAM,MAAM,QAAQ,GAAG,QAAQ,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;AAE5D,MAAM,WAAW,uBAAuB;IACtC,MAAM,EAAE;QAAE,IAAI,EAAE,QAAQ,CAAC;QAAC,IAAI,EAAE,QAAQ,CAAC;QAAC,KAAK,EAAE,QAAQ,CAAA;KAAE,CAAC;IAC5D,QAAQ,EAAE,QAAQ,CAAC;IACnB,eAAe,EAAE,OAAO,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;CACjB;AAaD;;;;GAIG;AACH,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA0B;gBAErC,MAAM,EAAE,uBAAuB;IAI3C;;;OAGG;IACH,cAAc,CAAC,KAAK,EAAE,gBAAgB,GAAG,IAAI;IAwB7C;;;OAGG;IACH,cAAc,CAAC,KAAK,EAAE,gBAAgB,GAAG,IAAI;IAgB7C;;;;;;;;;OASG;IACH,OAAO,CAAC,SAAS;IAUjB;;;OAGG;IACH,OAAO,CAAC,aAAa;CAqBtB"}
@@ -0,0 +1,106 @@
1
+ /*
2
+ Copyright (c) 2025 Bernier LLC
3
+
4
+ This file is licensed to the client under a limited-use license.
5
+ The client may use and modify this code *only within the scope of the project it was delivered for*.
6
+ Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.
7
+ */
8
+ /**
9
+ * Severity ranking for log levels, used for filtering.
10
+ * Higher numbers mean more severe / less filtered.
11
+ */
12
+ const LOG_LEVEL_SEVERITY = {
13
+ silent: -1,
14
+ info: 0,
15
+ warn: 1,
16
+ error: 2,
17
+ };
18
+ /**
19
+ * DegradationLogger provides structured logging for feature degradation
20
+ * and unsupported feature events across email providers. It respects
21
+ * configurable log levels and optionally includes documentation links.
22
+ */
23
+ export class DegradationLogger {
24
+ constructor(config) {
25
+ this.config = config;
26
+ }
27
+ /**
28
+ * Log a degradation event when a provider does not natively support a feature
29
+ * and a fallback strategy is being used.
30
+ */
31
+ logDegradation(event) {
32
+ if (!this.shouldLog('warn')) {
33
+ return;
34
+ }
35
+ const fullDocUrl = this.resolveDocUrl(event.docUrl);
36
+ const message = `${event.provider.toUpperCase()} does not support native ${event.feature}. ` +
37
+ `Using ${event.strategy}: ${event.description}. ` +
38
+ `Docs: ${fullDocUrl}. ` +
39
+ `Override: set featureOverrides.${event.feature}.behavior = 'error' to disable degradation.`;
40
+ const metadata = {
41
+ provider: event.provider,
42
+ feature: event.feature,
43
+ strategy: event.strategy,
44
+ description: event.description,
45
+ docUrl: fullDocUrl,
46
+ };
47
+ this.config.logger.warn(message, metadata);
48
+ }
49
+ /**
50
+ * Log an unsupported feature event. These are always logged at error level
51
+ * (unless log level is silent).
52
+ */
53
+ logUnsupported(event) {
54
+ if (!this.shouldLog('error')) {
55
+ return;
56
+ }
57
+ const message = `${event.provider.toUpperCase()} does not support ${event.feature}: ${event.message}`;
58
+ const metadata = {
59
+ provider: event.provider,
60
+ feature: event.feature,
61
+ };
62
+ this.config.logger.error(message, metadata);
63
+ }
64
+ /**
65
+ * Check whether a message at the given severity should be logged
66
+ * based on the configured log level.
67
+ *
68
+ * The configured logLevel sets the minimum severity that gets logged:
69
+ * - 'silent': nothing is logged
70
+ * - 'error': only error-level messages
71
+ * - 'warn': error + warn messages
72
+ * - 'info': everything (error + warn + info)
73
+ */
74
+ shouldLog(messageSeverity) {
75
+ const configuredLevel = this.config.logLevel;
76
+ if (configuredLevel === 'silent') {
77
+ return false;
78
+ }
79
+ // The configured level is the minimum severity we show.
80
+ // A message is shown if its severity >= the configured threshold.
81
+ return LOG_LEVEL_SEVERITY[messageSeverity] >= LOG_LEVEL_SEVERITY[configuredLevel];
82
+ }
83
+ /**
84
+ * Resolve a doc URL, prepending the docsBaseUrl if the path is relative
85
+ * and includeDocLinks is enabled.
86
+ */
87
+ resolveDocUrl(docUrl) {
88
+ if (!this.config.includeDocLinks) {
89
+ return docUrl;
90
+ }
91
+ // If it's already an absolute URL, return as-is
92
+ if (docUrl.startsWith('http://') || docUrl.startsWith('https://')) {
93
+ return docUrl;
94
+ }
95
+ // Prepend docsBaseUrl for relative paths
96
+ if (this.config.docsBaseUrl) {
97
+ const base = this.config.docsBaseUrl.endsWith('/')
98
+ ? this.config.docsBaseUrl.slice(0, -1)
99
+ : this.config.docsBaseUrl;
100
+ const path = docUrl.startsWith('/') ? docUrl : `/${docUrl}`;
101
+ return `${base}${path}`;
102
+ }
103
+ return docUrl;
104
+ }
105
+ }
106
+ //# sourceMappingURL=degradation-logger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"degradation-logger.js","sourceRoot":"","sources":["../../src/capabilities/degradation-logger.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,55 @@
1
+ import type { CapabilityResolver } from './resolver.js';
2
+ export interface ProviderInfo {
3
+ id: string;
4
+ name: string;
5
+ type: 'sendgrid' | 'mailgun' | 'postmark' | 'ses' | 'smtp';
6
+ isActive: boolean;
7
+ priority: number;
8
+ }
9
+ export interface RoutingResult {
10
+ provider: ProviderInfo;
11
+ source: 'provider' | 'platform' | 'enhanced' | 'unsupported';
12
+ degraded: boolean;
13
+ degradation: {
14
+ strategy: string;
15
+ description: string;
16
+ docUrl: string;
17
+ } | null;
18
+ attemptedProviders: string[];
19
+ }
20
+ export interface FeatureOverride {
21
+ behavior: 'native-first' | 'platform-always' | 'error';
22
+ }
23
+ export interface FeatureRouterConfig {
24
+ resolver: CapabilityResolver;
25
+ fallbackResolver?: CapabilityResolver;
26
+ featureOverrides?: Record<string, FeatureOverride>;
27
+ }
28
+ /**
29
+ * Smart routing engine that decides which provider handles a feature request.
30
+ *
31
+ * The router inspects the capability matrix (via a {@link CapabilityResolver})
32
+ * to find the best active provider for a given feature, respecting priority
33
+ * ordering and feature overrides.
34
+ */
35
+ export declare class FeatureRouter {
36
+ private readonly resolver;
37
+ private readonly fallbackResolver;
38
+ private readonly featureOverrides;
39
+ constructor(config: FeatureRouterConfig);
40
+ /**
41
+ * Find the best provider for a given feature.
42
+ *
43
+ * @throws {FeatureUnsupportedError} when no provider can handle the feature
44
+ */
45
+ findProviderForFeature(feature: string, providers: ProviderInfo[]): Promise<RoutingResult>;
46
+ private handlePlatformAlways;
47
+ private handleErrorOverride;
48
+ private handleNativeFirst;
49
+ /**
50
+ * Resolve a capability entry, falling back to the fallback resolver on error.
51
+ * Returns `undefined` when the capability cannot be resolved at all.
52
+ */
53
+ private resolveCapability;
54
+ }
55
+ //# sourceMappingURL=feature-router.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"feature-router.d.ts","sourceRoot":"","sources":["../../src/capabilities/feature-router.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAMxD,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,UAAU,GAAG,SAAS,GAAG,UAAU,GAAG,KAAK,GAAG,MAAM,CAAC;IAC3D,QAAQ,EAAE,OAAO,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,YAAY,CAAC;IACvB,MAAM,EAAE,UAAU,GAAG,UAAU,GAAG,UAAU,GAAG,aAAa,CAAC;IAC7D,QAAQ,EAAE,OAAO,CAAC;IAClB,WAAW,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAC9E,kBAAkB,EAAE,MAAM,EAAE,CAAC;CAC9B;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,cAAc,GAAG,iBAAiB,GAAG,OAAO,CAAC;CACxD;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,kBAAkB,CAAC;IAC7B,gBAAgB,CAAC,EAAE,kBAAkB,CAAC;IACtC,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;CACpD;AAMD;;;;;;GAMG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAqB;IAC9C,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAiC;IAClE,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAkC;gBAEvD,MAAM,EAAE,mBAAmB;IAMvC;;;;OAIG;IACG,sBAAsB,CAC1B,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,YAAY,EAAE,GACxB,OAAO,CAAC,aAAa,CAAC;YAiCX,oBAAoB;YAgBpB,mBAAmB;YA4BnB,iBAAiB;IAuD/B;;;OAGG;YACW,iBAAiB;CAmBhC"}
@@ -0,0 +1,143 @@
1
+ /*
2
+ Copyright (c) 2025 Bernier LLC
3
+
4
+ This file is licensed to the client under a limited-use license.
5
+ The client may use and modify this code *only within the scope of the project it was delivered for*.
6
+ Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.
7
+ */
8
+ import { FeatureUnsupportedError } from '../errors.js';
9
+ // ---------------------------------------------------------------------------
10
+ // FeatureRouter
11
+ // ---------------------------------------------------------------------------
12
+ /**
13
+ * Smart routing engine that decides which provider handles a feature request.
14
+ *
15
+ * The router inspects the capability matrix (via a {@link CapabilityResolver})
16
+ * to find the best active provider for a given feature, respecting priority
17
+ * ordering and feature overrides.
18
+ */
19
+ export class FeatureRouter {
20
+ constructor(config) {
21
+ this.resolver = config.resolver;
22
+ this.fallbackResolver = config.fallbackResolver;
23
+ this.featureOverrides = config.featureOverrides ?? {};
24
+ }
25
+ /**
26
+ * Find the best provider for a given feature.
27
+ *
28
+ * @throws {FeatureUnsupportedError} when no provider can handle the feature
29
+ */
30
+ async findProviderForFeature(feature, providers) {
31
+ // 1. Filter to active providers only
32
+ const activeProviders = providers.filter((p) => p.isActive);
33
+ if (activeProviders.length === 0) {
34
+ throw new FeatureUnsupportedError(feature, providers.map((p) => p.id));
35
+ }
36
+ // 2. Sort by priority (lowest number = highest priority)
37
+ const sorted = [...activeProviders].sort((a, b) => a.priority - b.priority);
38
+ // 3. Check for feature override
39
+ const override = this.featureOverrides[feature];
40
+ if (override?.behavior === 'platform-always') {
41
+ return this.handlePlatformAlways(feature, sorted);
42
+ }
43
+ if (override?.behavior === 'error') {
44
+ return this.handleErrorOverride(feature, sorted, providers);
45
+ }
46
+ // 4. Default: native-first -- iterate providers in priority order
47
+ return this.handleNativeFirst(feature, sorted, providers);
48
+ }
49
+ // -------------------------------------------------------------------------
50
+ // Override handlers
51
+ // -------------------------------------------------------------------------
52
+ async handlePlatformAlways(feature, sorted) {
53
+ const first = sorted[0];
54
+ const capability = await this.resolveCapability(first.type, feature);
55
+ return {
56
+ provider: first,
57
+ source: 'platform',
58
+ degraded: true,
59
+ degradation: capability?.degradation ?? null,
60
+ attemptedProviders: [first.id],
61
+ };
62
+ }
63
+ async handleErrorOverride(feature, sorted, allProviders) {
64
+ const attemptedProviders = [];
65
+ for (const provider of sorted) {
66
+ attemptedProviders.push(provider.id);
67
+ const capability = await this.resolveCapability(provider.type, feature);
68
+ if (capability && (capability.source === 'provider' || capability.source === 'enhanced')) {
69
+ return {
70
+ provider,
71
+ source: capability.source,
72
+ degraded: false,
73
+ degradation: null,
74
+ attemptedProviders,
75
+ };
76
+ }
77
+ }
78
+ throw new FeatureUnsupportedError(feature, allProviders.map((p) => p.id));
79
+ }
80
+ async handleNativeFirst(feature, sorted, allProviders) {
81
+ const attemptedProviders = [];
82
+ let platformFallback;
83
+ for (const provider of sorted) {
84
+ attemptedProviders.push(provider.id);
85
+ const capability = await this.resolveCapability(provider.type, feature);
86
+ if (!capability) {
87
+ continue;
88
+ }
89
+ // Native or enhanced -- return immediately
90
+ if (capability.source === 'provider' || capability.source === 'enhanced') {
91
+ return {
92
+ provider,
93
+ source: capability.source,
94
+ degraded: false,
95
+ degradation: null,
96
+ attemptedProviders,
97
+ };
98
+ }
99
+ // Track first platform fallback
100
+ if (capability.source === 'platform' && !platformFallback) {
101
+ platformFallback = { provider, capability };
102
+ }
103
+ }
104
+ // 5. No native/enhanced found -- try platform degradation
105
+ if (platformFallback) {
106
+ return {
107
+ provider: platformFallback.provider,
108
+ source: 'platform',
109
+ degraded: true,
110
+ degradation: platformFallback.capability.degradation ?? null,
111
+ attemptedProviders,
112
+ };
113
+ }
114
+ // 6. Completely unsupported
115
+ throw new FeatureUnsupportedError(feature, allProviders.map((p) => p.id));
116
+ }
117
+ // -------------------------------------------------------------------------
118
+ // Resolver helper
119
+ // -------------------------------------------------------------------------
120
+ /**
121
+ * Resolve a capability entry, falling back to the fallback resolver on error.
122
+ * Returns `undefined` when the capability cannot be resolved at all.
123
+ */
124
+ async resolveCapability(providerType, feature) {
125
+ try {
126
+ return await this.resolver.getCapability(providerType, feature);
127
+ }
128
+ catch (primaryError) {
129
+ if (this.fallbackResolver) {
130
+ try {
131
+ return await this.fallbackResolver.getCapability(providerType, feature);
132
+ }
133
+ catch {
134
+ // Both resolvers failed -- treat as unresolvable
135
+ return undefined;
136
+ }
137
+ }
138
+ // No fallback -- propagate error
139
+ throw primaryError;
140
+ }
141
+ }
142
+ }
143
+ //# sourceMappingURL=feature-router.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"feature-router.js","sourceRoot":"","sources":["../../src/capabilities/feature-router.ts"],"names":[],"mappings":"AAAA;;;;;;EAME;AAIF,OAAO,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAC;AAkCvD,8EAA8E;AAC9E,gBAAgB;AAChB,8EAA8E;AAE9E;;;;;;GAMG;AACH,MAAM,OAAO,aAAa;IAKxB,YAAY,MAA2B;QACrC,IAAI,CAAC,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC;QAChC,IAAI,CAAC,gBAAgB,GAAG,MAAM,CAAC,gBAAgB,CAAC;QAChD,IAAI,CAAC,gBAAgB,GAAG,MAAM,CAAC,gBAAgB,IAAI,EAAE,CAAC;IACxD,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,sBAAsB,CAC1B,OAAe,EACf,SAAyB;QAEzB,qCAAqC;QACrC,MAAM,eAAe,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;QAE5D,IAAI,eAAe,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACjC,MAAM,IAAI,uBAAuB,CAC/B,OAAO,EACP,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAC3B,CAAC;QACJ,CAAC;QAED,yDAAyD;QACzD,MAAM,MAAM,GAAG,CAAC,GAAG,eAAe,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,GAAG,CAAC,CAAC,QAAQ,CAAC,CAAC;QAE5E,gCAAgC;QAChC,MAAM,QAAQ,GAAG,IAAI,CAAC,gBAAgB,CAAC,OAAO,CAAC,CAAC;QAEhD,IAAI,QAAQ,EAAE,QAAQ,KAAK,iBAAiB,EAAE,CAAC;YAC7C,OAAO,IAAI,CAAC,oBAAoB,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QACpD,CAAC;QAED,IAAI,QAAQ,EAAE,QAAQ,KAAK,OAAO,EAAE,CAAC;YACnC,OAAO,IAAI,CAAC,mBAAmB,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,CAAC,CAAC;QAC9D,CAAC;QAED,kEAAkE;QAClE,OAAO,IAAI,CAAC,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,CAAC,CAAC;IAC5D,CAAC;IAED,4EAA4E;IAC5E,oBAAoB;IACpB,4EAA4E;IAEpE,KAAK,CAAC,oBAAoB,CAChC,OAAe,EACf,MAAsB;QAEtB,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;QACxB,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,iBAAiB,CAAC,KAAK,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QAErE,OAAO;YACL,QAAQ,EAAE,KAAK;YACf,MAAM,EAAE,UAAU;YAClB,QAAQ,EAAE,IAAI;YACd,WAAW,EAAE,UAAU,EAAE,WAAW,IAAI,IAAI;YAC5C,kBAAkB,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC;SAC/B,CAAC;IACJ,CAAC;IAEO,KAAK,CAAC,mBAAmB,CAC/B,OAAe,EACf,MAAsB,EACtB,YAA4B;QAE5B,MAAM,kBAAkB,GAAa,EAAE,CAAC;QAExC,KAAK,MAAM,QAAQ,IAAI,MAAM,EAAE,CAAC;YAC9B,kBAAkB,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;YACrC,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,iBAAiB,CAAC,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;YAExE,IAAI,UAAU,IAAI,CAAC,UAAU,CAAC,MAAM,KAAK,UAAU,IAAI,UAAU,CAAC,MAAM,KAAK,UAAU,CAAC,EAAE,CAAC;gBACzF,OAAO;oBACL,QAAQ;oBACR,MAAM,EAAE,UAAU,CAAC,MAAM;oBACzB,QAAQ,EAAE,KAAK;oBACf,WAAW,EAAE,IAAI;oBACjB,kBAAkB;iBACnB,CAAC;YACJ,CAAC;QACH,CAAC;QAED,MAAM,IAAI,uBAAuB,CAC/B,OAAO,EACP,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAC9B,CAAC;IACJ,CAAC;IAEO,KAAK,CAAC,iBAAiB,CAC7B,OAAe,EACf,MAAsB,EACtB,YAA4B;QAE5B,MAAM,kBAAkB,GAAa,EAAE,CAAC;QACxC,IAAI,gBAAqF,CAAC;QAE1F,KAAK,MAAM,QAAQ,IAAI,MAAM,EAAE,CAAC;YAC9B,kBAAkB,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;YACrC,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,iBAAiB,CAAC,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;YAExE,IAAI,CAAC,UAAU,EAAE,CAAC;gBAChB,SAAS;YACX,CAAC;YAED,2CAA2C;YAC3C,IAAI,UAAU,CAAC,MAAM,KAAK,UAAU,IAAI,UAAU,CAAC,MAAM,KAAK,UAAU,EAAE,CAAC;gBACzE,OAAO;oBACL,QAAQ;oBACR,MAAM,EAAE,UAAU,CAAC,MAAM;oBACzB,QAAQ,EAAE,KAAK;oBACf,WAAW,EAAE,IAAI;oBACjB,kBAAkB;iBACnB,CAAC;YACJ,CAAC;YAED,gCAAgC;YAChC,IAAI,UAAU,CAAC,MAAM,KAAK,UAAU,IAAI,CAAC,gBAAgB,EAAE,CAAC;gBAC1D,gBAAgB,GAAG,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAC;YAC9C,CAAC;QACH,CAAC;QAED,0DAA0D;QAC1D,IAAI,gBAAgB,EAAE,CAAC;YACrB,OAAO;gBACL,QAAQ,EAAE,gBAAgB,CAAC,QAAQ;gBACnC,MAAM,EAAE,UAAU;gBAClB,QAAQ,EAAE,IAAI;gBACd,WAAW,EAAE,gBAAgB,CAAC,UAAU,CAAC,WAAW,IAAI,IAAI;gBAC5D,kBAAkB;aACnB,CAAC;QACJ,CAAC;QAED,4BAA4B;QAC5B,MAAM,IAAI,uBAAuB,CAC/B,OAAO,EACP,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAC9B,CAAC;IACJ,CAAC;IAED,4EAA4E;IAC5E,kBAAkB;IAClB,4EAA4E;IAE5E;;;OAGG;IACK,KAAK,CAAC,iBAAiB,CAC7B,YAAoB,EACpB,OAAe;QAEf,IAAI,CAAC;YACH,OAAO,MAAM,IAAI,CAAC,QAAQ,CAAC,aAAa,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;QAClE,CAAC;QAAC,OAAO,YAAY,EAAE,CAAC;YACtB,IAAI,IAAI,CAAC,gBAAgB,EAAE,CAAC;gBAC1B,IAAI,CAAC;oBACH,OAAO,MAAM,IAAI,CAAC,gBAAgB,CAAC,aAAa,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;gBAC1E,CAAC;gBAAC,MAAM,CAAC;oBACP,iDAAiD;oBACjD,OAAO,SAAS,CAAC;gBACnB,CAAC;YACH,CAAC;YACD,iCAAiC;YACjC,MAAM,YAAY,CAAC;QACrB,CAAC;IACH,CAAC;CACF"}
@@ -0,0 +1,27 @@
1
+ import type { CapabilityEntry } from '@bernierllc/email-sender';
2
+ import type { CapabilityResolverAdmin } from './resolver.js';
3
+ /**
4
+ * In-memory implementation of {@link CapabilityResolverAdmin}.
5
+ *
6
+ * Initialised with a deep clone of the canonical {@link CAPABILITY_MATRIX} so
7
+ * that mutations made through the admin interface never affect the original
8
+ * static data.
9
+ */
10
+ export declare class InMemoryCapabilityResolver implements CapabilityResolverAdmin {
11
+ private readonly data;
12
+ constructor();
13
+ getCapability(provider: string, feature: string): Promise<CapabilityEntry>;
14
+ getProviderCapabilities(provider: string): Promise<Record<string, CapabilityEntry>>;
15
+ setCapability(provider: string, feature: string, entry: CapabilityEntry): Promise<void>;
16
+ setProviderCapabilities(provider: string, capabilities: Record<string, CapabilityEntry>): Promise<void>;
17
+ /**
18
+ * Resolves the capability map for a known provider or throws.
19
+ */
20
+ private resolveProvider;
21
+ /**
22
+ * Validates a capability entry against the Zod schema, throwing a
23
+ * {@link CapabilityResolverError} with code `INVALID_ENTRY` on failure.
24
+ */
25
+ private validateEntry;
26
+ }
27
+ //# sourceMappingURL=in-memory-resolver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"in-memory-resolver.d.ts","sourceRoot":"","sources":["../../src/capabilities/in-memory-resolver.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAKhE,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;AAE7D;;;;;;GAMG;AACH,qBAAa,0BAA2B,YAAW,uBAAuB;IACxE,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAuB;;IAUtC,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC;IAY1E,uBAAuB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;IAQnF,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC;IAWvF,uBAAuB,CAC3B,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,GAC5C,OAAO,CAAC,IAAI,CAAC;IAahB;;OAEG;IACH,OAAO,CAAC,eAAe;IAWvB;;;OAGG;IACH,OAAO,CAAC,aAAa;CAUtB"}
@@ -0,0 +1,79 @@
1
+ /*
2
+ Copyright (c) 2025 Bernier LLC
3
+
4
+ This file is licensed to the client under a limited-use license.
5
+ The client may use and modify this code *only within the scope of the project it was delivered for*.
6
+ Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.
7
+ */
8
+ import { capabilityEntrySchema } from '@bernierllc/email-sender';
9
+ import { CAPABILITY_MATRIX } from './matrix.js';
10
+ import { CapabilityResolverError } from './resolver.js';
11
+ /**
12
+ * In-memory implementation of {@link CapabilityResolverAdmin}.
13
+ *
14
+ * Initialised with a deep clone of the canonical {@link CAPABILITY_MATRIX} so
15
+ * that mutations made through the admin interface never affect the original
16
+ * static data.
17
+ */
18
+ export class InMemoryCapabilityResolver {
19
+ constructor() {
20
+ this.data = structuredClone(CAPABILITY_MATRIX);
21
+ }
22
+ // -----------------------------------------------------------------------
23
+ // Read operations
24
+ // -----------------------------------------------------------------------
25
+ async getCapability(provider, feature) {
26
+ const providerCaps = this.resolveProvider(provider);
27
+ const entry = providerCaps[feature];
28
+ if (!entry) {
29
+ throw new CapabilityResolverError(`Unknown feature "${feature}" for provider "${provider}"`, 'UNKNOWN_FEATURE');
30
+ }
31
+ return entry;
32
+ }
33
+ async getProviderCapabilities(provider) {
34
+ return { ...this.resolveProvider(provider) };
35
+ }
36
+ // -----------------------------------------------------------------------
37
+ // Write operations
38
+ // -----------------------------------------------------------------------
39
+ async setCapability(provider, feature, entry) {
40
+ this.validateEntry(entry);
41
+ // Ensure provider bucket exists (create on demand for admin use-cases)
42
+ if (!this.data.providers[provider]) {
43
+ this.data.providers[provider] = {};
44
+ }
45
+ this.data.providers[provider][feature] = entry;
46
+ }
47
+ async setProviderCapabilities(provider, capabilities) {
48
+ // Validate every entry before committing any of them
49
+ for (const [feature, entry] of Object.entries(capabilities)) {
50
+ this.validateEntry(entry, feature);
51
+ }
52
+ this.data.providers[provider] = { ...capabilities };
53
+ }
54
+ // -----------------------------------------------------------------------
55
+ // Internal helpers
56
+ // -----------------------------------------------------------------------
57
+ /**
58
+ * Resolves the capability map for a known provider or throws.
59
+ */
60
+ resolveProvider(provider) {
61
+ const caps = this.data.providers[provider];
62
+ if (!caps) {
63
+ throw new CapabilityResolverError(`Unknown provider "${provider}"`, 'UNKNOWN_PROVIDER');
64
+ }
65
+ return caps;
66
+ }
67
+ /**
68
+ * Validates a capability entry against the Zod schema, throwing a
69
+ * {@link CapabilityResolverError} with code `INVALID_ENTRY` on failure.
70
+ */
71
+ validateEntry(entry, label) {
72
+ const result = capabilityEntrySchema.safeParse(entry);
73
+ if (!result.success) {
74
+ const prefix = label ? `Invalid entry for "${label}": ` : 'Invalid capability entry: ';
75
+ throw new CapabilityResolverError(prefix + result.error.issues.map((i) => i.message).join('; '), 'INVALID_ENTRY');
76
+ }
77
+ }
78
+ }
79
+ //# sourceMappingURL=in-memory-resolver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"in-memory-resolver.js","sourceRoot":"","sources":["../../src/capabilities/in-memory-resolver.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ export { FEATURE_DEFINITIONS, FEATURE_NAMES, PROVIDER_NAMES, CAPABILITY_MATRIX, getCapabilityMatrix, } from './matrix.js';
2
+ export type { FeatureName, ProviderName, ProviderCapabilityMap, CapabilityMatrixData, } from './matrix.js';
3
+ export { CapabilityResolverError } from './resolver.js';
4
+ export type { CapabilityResolver, CapabilityResolverAdmin } from './resolver.js';
5
+ export { InMemoryCapabilityResolver } from './in-memory-resolver.js';
6
+ export { RedisCapabilityResolver } from './redis-resolver.js';
7
+ export type { RedisLike, RedisPipelineLike, RedisCapabilityResolverOptions, } from './redis-resolver.js';
8
+ export { SafeCapabilityResolver } from './safe-resolver.js';
9
+ export { createCapabilityResolver } from './resolver-factory.js';
10
+ export type { ResolverFactoryOptions, ResolverFactoryLogger } from './resolver-factory.js';
11
+ export { FeatureRouter } from './feature-router.js';
12
+ export type { ProviderInfo, RoutingResult, FeatureOverride, FeatureRouterConfig, } from './feature-router.js';
13
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/capabilities/index.ts"],"names":[],"mappings":"AAQA,OAAO,EACL,mBAAmB,EACnB,aAAa,EACb,cAAc,EACd,iBAAiB,EACjB,mBAAmB,GACpB,MAAM,aAAa,CAAC;AAErB,YAAY,EACV,WAAW,EACX,YAAY,EACZ,qBAAqB,EACrB,oBAAoB,GACrB,MAAM,aAAa,CAAC;AAGrB,OAAO,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;AACxD,YAAY,EAAE,kBAAkB,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;AAGjF,OAAO,EAAE,0BAA0B,EAAE,MAAM,yBAAyB,CAAC;AAGrE,OAAO,EAAE,uBAAuB,EAAE,MAAM,qBAAqB,CAAC;AAC9D,YAAY,EACV,SAAS,EACT,iBAAiB,EACjB,8BAA8B,GAC/B,MAAM,qBAAqB,CAAC;AAG7B,OAAO,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAC;AAG5D,OAAO,EAAE,wBAAwB,EAAE,MAAM,uBAAuB,CAAC;AACjE,YAAY,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAG3F,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,YAAY,EACV,YAAY,EACZ,aAAa,EACb,eAAe,EACf,mBAAmB,GACpB,MAAM,qBAAqB,CAAC"}
@@ -0,0 +1,21 @@
1
+ /*
2
+ Copyright (c) 2025 Bernier LLC
3
+
4
+ This file is licensed to the client under a limited-use license.
5
+ The client may use and modify this code *only within the scope of the project it was delivered for*.
6
+ Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.
7
+ */
8
+ export { FEATURE_DEFINITIONS, FEATURE_NAMES, PROVIDER_NAMES, CAPABILITY_MATRIX, getCapabilityMatrix, } from './matrix.js';
9
+ // Capability resolver interfaces and error class
10
+ export { CapabilityResolverError } from './resolver.js';
11
+ // In-memory resolver implementation
12
+ export { InMemoryCapabilityResolver } from './in-memory-resolver.js';
13
+ // Redis-backed resolver implementation
14
+ export { RedisCapabilityResolver } from './redis-resolver.js';
15
+ // Safe fallback wrapper
16
+ export { SafeCapabilityResolver } from './safe-resolver.js';
17
+ // Factory function for auto-detecting resolver
18
+ export { createCapabilityResolver } from './resolver-factory.js';
19
+ // Feature router
20
+ export { FeatureRouter } from './feature-router.js';
21
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/capabilities/index.ts"],"names":[],"mappings":"AAAA;;;;;;EAME;AAEF,OAAO,EACL,mBAAmB,EACnB,aAAa,EACb,cAAc,EACd,iBAAiB,EACjB,mBAAmB,GACpB,MAAM,aAAa,CAAC;AASrB,iDAAiD;AACjD,OAAO,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;AAGxD,oCAAoC;AACpC,OAAO,EAAE,0BAA0B,EAAE,MAAM,yBAAyB,CAAC;AAErE,uCAAuC;AACvC,OAAO,EAAE,uBAAuB,EAAE,MAAM,qBAAqB,CAAC;AAO9D,wBAAwB;AACxB,OAAO,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAC;AAE5D,+CAA+C;AAC/C,OAAO,EAAE,wBAAwB,EAAE,MAAM,uBAAuB,CAAC;AAGjE,iBAAiB;AACjB,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC"}
@@ -0,0 +1,66 @@
1
+ import type { CapabilityEntry } from '@bernierllc/email-sender';
2
+ export declare const FEATURE_DEFINITIONS: {
3
+ readonly sendEmail: "Send a single email through a provider";
4
+ readonly batchSend: "Send multiple emails in a single operation";
5
+ readonly providerTemplates: "Use templates stored on the provider (server-side)";
6
+ readonly localTemplates: "Render templates locally before sending";
7
+ readonly calendarInvites: "Attach iCalendar (.ics) invites to outgoing emails";
8
+ readonly calendarEventMgmt: "Create, update, and cancel calendar events programmatically";
9
+ readonly webhooksReceive: "Receive webhook callbacks from the provider for email events";
10
+ readonly webhookNormalization: "Normalize provider-specific webhook payloads into a common format";
11
+ readonly deliveryTracking: "Track delivery status of sent emails (delivered, bounced, etc.)";
12
+ readonly inboundEmailParsing: "Parse inbound emails forwarded by the provider";
13
+ /**
14
+ * subscriptionMgmt -- Manage subscriber lists, preference centres, and
15
+ * opt-in / opt-out lifecycle. This is distinct from suppressionLists which
16
+ * deals with bounce/complaint suppression at the provider level.
17
+ */
18
+ readonly subscriptionMgmt: "Manage subscriber lists, preferences, and opt-in/opt-out lifecycle";
19
+ /**
20
+ * suppressionLists -- Provider-level bounce and complaint suppression.
21
+ * Automatically maintained by the provider to prevent sending to addresses
22
+ * that have bounced or filed complaints. Distinct from subscriptionMgmt
23
+ * which handles user-facing subscription preferences.
24
+ */
25
+ readonly suppressionLists: "Provider-level bounce and complaint suppression lists";
26
+ readonly unsubscribeUrlGeneration: "Generate one-click unsubscribe URLs for email headers";
27
+ readonly scheduledSend: "Schedule emails for future delivery";
28
+ readonly openClickTracking: "Track email opens and link clicks";
29
+ readonly emailContentParsing: "Parse and transform email content (HTML/text)";
30
+ readonly emailHeaderMgmt: "Set and manage custom email headers";
31
+ readonly attachments: "Attach files to outgoing emails";
32
+ readonly advancedAttachmentProcessing: "Process, validate, and transform attachments (resize, compress, etc.)";
33
+ readonly dkimSpf: "Programmatic DKIM/SPF authentication configuration";
34
+ readonly linkBranding: "Custom branded tracking domains for links";
35
+ readonly retryResilience: "Automatic retry with backoff on transient failures";
36
+ readonly multiProviderFailover: "Automatic failover to alternate providers on failure";
37
+ };
38
+ /**
39
+ * Union of every recognised feature name, derived from FEATURE_DEFINITIONS.
40
+ */
41
+ export type FeatureName = keyof typeof FEATURE_DEFINITIONS;
42
+ /**
43
+ * Ordered array of every feature name (derived from FEATURE_DEFINITIONS keys).
44
+ */
45
+ export declare const FEATURE_NAMES: readonly FeatureName[];
46
+ export type ProviderName = 'sendgrid' | 'mailgun' | 'postmark' | 'ses' | 'smtp';
47
+ export declare const PROVIDER_NAMES: readonly ProviderName[];
48
+ /**
49
+ * A complete capability map for a single provider -- one entry per feature.
50
+ */
51
+ export type ProviderCapabilityMap = Record<FeatureName, CapabilityEntry>;
52
+ /**
53
+ * Top-level structure for the capability matrix.
54
+ */
55
+ export interface CapabilityMatrixData {
56
+ version: string;
57
+ lastUpdated: string;
58
+ providers: Record<ProviderName, ProviderCapabilityMap>;
59
+ }
60
+ export declare const CAPABILITY_MATRIX: CapabilityMatrixData;
61
+ /**
62
+ * Returns a deep clone of the capability matrix to prevent mutation of the
63
+ * canonical data.
64
+ */
65
+ export declare function getCapabilityMatrix(): CapabilityMatrixData;
66
+ //# sourceMappingURL=matrix.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"matrix.d.ts","sourceRoot":"","sources":["../../src/capabilities/matrix.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAUhE,eAAO,MAAM,mBAAmB;;;;;;;;;;;IAW9B;;;;OAIG;;IAEH;;;;;OAKG;;;;;;;;;;;;;CAaK,CAAC;AAEX;;GAEG;AACH,MAAM,MAAM,WAAW,GAAG,MAAM,OAAO,mBAAmB,CAAC;AAE3D;;GAEG;AACH,eAAO,MAAM,aAAa,EAAE,SAAS,WAAW,EAE9B,CAAC;AAMnB,MAAM,MAAM,YAAY,GAAG,UAAU,GAAG,SAAS,GAAG,UAAU,GAAG,KAAK,GAAG,MAAM,CAAC;AAEhF,eAAO,MAAM,cAAc,EAAE,SAAS,YAAY,EAMxC,CAAC;AAMX;;GAEG;AACH,MAAM,MAAM,qBAAqB,GAAG,MAAM,CAAC,WAAW,EAAE,eAAe,CAAC,CAAC;AAEzE;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC,YAAY,EAAE,qBAAqB,CAAC,CAAC;CACxD;AAuND,eAAO,MAAM,iBAAiB,EAAE,oBAU/B,CAAC;AAEF;;;GAGG;AACH,wBAAgB,mBAAmB,IAAI,oBAAoB,CAE1D"}