@mondaydotcomorg/atp-providers 0.17.14

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 (58) hide show
  1. package/README.md +430 -0
  2. package/__tests__/oauth-integration.test.ts +457 -0
  3. package/__tests__/scope-checkers.test.ts +560 -0
  4. package/__tests__/token-expiration.test.ts +308 -0
  5. package/dist/audit/jsonl.d.ts +29 -0
  6. package/dist/audit/jsonl.d.ts.map +1 -0
  7. package/dist/audit/jsonl.js +163 -0
  8. package/dist/audit/jsonl.js.map +1 -0
  9. package/dist/audit/opentelemetry.d.ts +23 -0
  10. package/dist/audit/opentelemetry.d.ts.map +1 -0
  11. package/dist/audit/opentelemetry.js +240 -0
  12. package/dist/audit/opentelemetry.js.map +1 -0
  13. package/dist/audit/otel-metrics.d.ts +111 -0
  14. package/dist/audit/otel-metrics.d.ts.map +1 -0
  15. package/dist/audit/otel-metrics.js +115 -0
  16. package/dist/audit/otel-metrics.js.map +1 -0
  17. package/dist/auth/env.d.ts +21 -0
  18. package/dist/auth/env.d.ts.map +1 -0
  19. package/dist/auth/env.js +48 -0
  20. package/dist/auth/env.js.map +1 -0
  21. package/dist/cache/file.d.ts +47 -0
  22. package/dist/cache/file.d.ts.map +1 -0
  23. package/dist/cache/file.js +262 -0
  24. package/dist/cache/file.js.map +1 -0
  25. package/dist/cache/memory.d.ts +30 -0
  26. package/dist/cache/memory.d.ts.map +1 -0
  27. package/dist/cache/memory.js +81 -0
  28. package/dist/cache/memory.js.map +1 -0
  29. package/dist/cache/redis.d.ts +28 -0
  30. package/dist/cache/redis.d.ts.map +1 -0
  31. package/dist/cache/redis.js +124 -0
  32. package/dist/cache/redis.js.map +1 -0
  33. package/dist/index.d.ts +10 -0
  34. package/dist/index.d.ts.map +1 -0
  35. package/dist/index.js +9 -0
  36. package/dist/index.js.map +1 -0
  37. package/dist/oauth/index.d.ts +2 -0
  38. package/dist/oauth/index.d.ts.map +1 -0
  39. package/dist/oauth/index.js +2 -0
  40. package/dist/oauth/index.js.map +1 -0
  41. package/dist/oauth/scope-checkers.d.ts +61 -0
  42. package/dist/oauth/scope-checkers.d.ts.map +1 -0
  43. package/dist/oauth/scope-checkers.js +166 -0
  44. package/dist/oauth/scope-checkers.js.map +1 -0
  45. package/package.json +26 -0
  46. package/project.json +31 -0
  47. package/src/audit/jsonl.ts +189 -0
  48. package/src/audit/opentelemetry.ts +286 -0
  49. package/src/audit/otel-metrics.ts +122 -0
  50. package/src/auth/env.ts +65 -0
  51. package/src/cache/file.ts +330 -0
  52. package/src/cache/memory.ts +105 -0
  53. package/src/cache/redis.ts +160 -0
  54. package/src/index.ts +32 -0
  55. package/src/oauth/index.ts +1 -0
  56. package/src/oauth/scope-checkers.ts +196 -0
  57. package/tsconfig.json +14 -0
  58. package/tsconfig.tsbuildinfo +1 -0
@@ -0,0 +1,330 @@
1
+ import type { CacheProvider } from '@mondaydotcomorg/atp-protocol';
2
+ import { promises as fs } from 'fs';
3
+ import path from 'path';
4
+ import os from 'os';
5
+
6
+ interface CacheEntry {
7
+ value: unknown;
8
+ expiresAt: number;
9
+ }
10
+
11
+ export interface FileCacheOptions {
12
+ cacheDir?: string;
13
+ maxKeys?: number;
14
+ defaultTTL?: number;
15
+ cleanupInterval?: number;
16
+ }
17
+
18
+ /**
19
+ * File-based cache provider for persistent local caching
20
+ * Good for single-server deployments that need persistence across restarts
21
+ * Supports cross-pod scenarios when using a shared filesystem (NFS, EFS, etc.)
22
+ */
23
+ export class FileCache implements CacheProvider {
24
+ name = 'file';
25
+ private cacheDir: string;
26
+ private maxKeys: number;
27
+ private defaultTTL: number;
28
+ private cleanupInterval: number;
29
+ private cleanupTimer?: NodeJS.Timeout;
30
+ private initPromise?: Promise<void>;
31
+
32
+ constructor(options: FileCacheOptions = {}) {
33
+ this.cacheDir = options.cacheDir || path.join(os.tmpdir(), 'atp-cache');
34
+ this.maxKeys = options.maxKeys || 1000;
35
+ this.defaultTTL = options.defaultTTL || 3600;
36
+ this.cleanupInterval = options.cleanupInterval || 300; // 5 minutes
37
+
38
+ // Initialize asynchronously
39
+ this.initPromise = this.initialize();
40
+ }
41
+
42
+ private async initialize(): Promise<void> {
43
+ try {
44
+ await fs.mkdir(this.cacheDir, { recursive: true });
45
+ // Start periodic cleanup
46
+ this.startCleanup();
47
+ } catch (error) {
48
+ console.error(
49
+ '[FileCache] Failed to initialize cache directory:',
50
+ error instanceof Error ? error.message : error
51
+ );
52
+ }
53
+ }
54
+
55
+ private async ensureInitialized(): Promise<void> {
56
+ if (this.initPromise) {
57
+ await this.initPromise;
58
+ }
59
+ }
60
+
61
+ private getFilePath(key: string): string {
62
+ // Sanitize key to be filesystem-safe
63
+ const sanitizedKey = key.replace(/[^a-zA-Z0-9_-]/g, '_');
64
+ return path.join(this.cacheDir, `${sanitizedKey}.json`);
65
+ }
66
+
67
+ private startCleanup(): void {
68
+ if (this.cleanupInterval > 0) {
69
+ this.cleanupTimer = setInterval(() => {
70
+ this.cleanExpired().catch((error) => {
71
+ console.error('[FileCache] Cleanup error:', error);
72
+ });
73
+ }, this.cleanupInterval * 1000);
74
+
75
+ // Don't prevent process exit
76
+ if (this.cleanupTimer.unref) {
77
+ this.cleanupTimer.unref();
78
+ }
79
+ }
80
+ }
81
+
82
+ private async cleanExpired(): Promise<void> {
83
+ try {
84
+ await this.ensureInitialized();
85
+ const files = await fs.readdir(this.cacheDir);
86
+ const now = Date.now();
87
+
88
+ for (const file of files) {
89
+ if (!file.endsWith('.json')) continue;
90
+
91
+ const filePath = path.join(this.cacheDir, file);
92
+ try {
93
+ const content = await fs.readFile(filePath, 'utf-8');
94
+ const entry: CacheEntry = JSON.parse(content);
95
+
96
+ if (entry.expiresAt !== -1 && now > entry.expiresAt) {
97
+ await fs.unlink(filePath);
98
+ }
99
+ } catch {
100
+ // If we can't read or parse a file, delete it
101
+ try {
102
+ await fs.unlink(filePath);
103
+ } catch {
104
+ // Ignore deletion errors
105
+ }
106
+ }
107
+ }
108
+
109
+ // Enforce max keys limit
110
+ await this.enforceMaxKeys();
111
+ } catch (error) {
112
+ console.error(
113
+ '[FileCache] Failed to clean expired entries:',
114
+ error instanceof Error ? error.message : error
115
+ );
116
+ }
117
+ }
118
+
119
+ private async enforceMaxKeys(): Promise<void> {
120
+ try {
121
+ const files = await fs.readdir(this.cacheDir);
122
+ const jsonFiles = files.filter((f) => f.endsWith('.json'));
123
+
124
+ if (jsonFiles.length > this.maxKeys) {
125
+ // Sort by modification time and remove oldest
126
+ const fileStats = await Promise.all(
127
+ jsonFiles.map(async (file) => {
128
+ const filePath = path.join(this.cacheDir, file);
129
+ const stats = await fs.stat(filePath);
130
+ return { file, mtime: stats.mtime.getTime() };
131
+ })
132
+ );
133
+
134
+ fileStats.sort((a, b) => a.mtime - b.mtime);
135
+
136
+ const toDelete = fileStats.slice(0, jsonFiles.length - this.maxKeys);
137
+ await Promise.all(
138
+ toDelete.map((item) => {
139
+ const filePath = path.join(this.cacheDir, item.file);
140
+ return fs.unlink(filePath).catch(() => {
141
+ // Ignore errors
142
+ });
143
+ })
144
+ );
145
+ }
146
+ } catch (error) {
147
+ console.error(
148
+ '[FileCache] Failed to enforce max keys:',
149
+ error instanceof Error ? error.message : error
150
+ );
151
+ }
152
+ }
153
+
154
+ async get<T>(key: string): Promise<T | null> {
155
+ try {
156
+ await this.ensureInitialized();
157
+ const filePath = this.getFilePath(key);
158
+ const content = await fs.readFile(filePath, 'utf-8');
159
+ const entry: CacheEntry = JSON.parse(content);
160
+
161
+ if (entry.expiresAt !== -1 && Date.now() > entry.expiresAt) {
162
+ await this.delete(key);
163
+ return null;
164
+ }
165
+
166
+ return entry.value as T;
167
+ } catch (error: any) {
168
+ if (error.code === 'ENOENT') {
169
+ return null;
170
+ }
171
+ console.error(
172
+ '[FileCache] Failed to get key:',
173
+ key,
174
+ error instanceof Error ? error.message : error
175
+ );
176
+ return null;
177
+ }
178
+ }
179
+
180
+ async set(key: string, value: unknown, ttl?: number): Promise<void> {
181
+ try {
182
+ await this.ensureInitialized();
183
+ await this.enforceMaxKeys();
184
+
185
+ const expiresAt = ttl
186
+ ? Date.now() + ttl * 1000
187
+ : this.defaultTTL > 0
188
+ ? Date.now() + this.defaultTTL * 1000
189
+ : -1;
190
+
191
+ const entry: CacheEntry = { value, expiresAt };
192
+ const filePath = this.getFilePath(key);
193
+
194
+ await fs.writeFile(filePath, JSON.stringify(entry), 'utf-8');
195
+ } catch (error) {
196
+ console.error(
197
+ '[FileCache] Failed to set key:',
198
+ key,
199
+ error instanceof Error ? error.message : error
200
+ );
201
+ }
202
+ }
203
+
204
+ async delete(key: string): Promise<void> {
205
+ try {
206
+ await this.ensureInitialized();
207
+ const filePath = this.getFilePath(key);
208
+ await fs.unlink(filePath);
209
+ } catch (error: any) {
210
+ if (error.code !== 'ENOENT') {
211
+ console.error(
212
+ '[FileCache] Failed to delete key:',
213
+ key,
214
+ error instanceof Error ? error.message : error
215
+ );
216
+ }
217
+ }
218
+ }
219
+
220
+ async has(key: string): Promise<boolean> {
221
+ const value = await this.get(key);
222
+ return value !== null;
223
+ }
224
+
225
+ async clear(pattern?: string): Promise<void> {
226
+ try {
227
+ await this.ensureInitialized();
228
+ const files = await fs.readdir(this.cacheDir);
229
+
230
+ if (!pattern) {
231
+ // Clear all cache files
232
+ await Promise.all(
233
+ files
234
+ .filter((f) => f.endsWith('.json'))
235
+ .map((file) => {
236
+ const filePath = path.join(this.cacheDir, file);
237
+ return fs.unlink(filePath).catch(() => {
238
+ // Ignore errors
239
+ });
240
+ })
241
+ );
242
+ return;
243
+ }
244
+
245
+ // Convert glob pattern to regex
246
+ const regex = new RegExp('^' + pattern.replace(/\*/g, '.*') + '$');
247
+
248
+ // Read all files and check if they match the pattern
249
+ for (const file of files) {
250
+ if (!file.endsWith('.json')) continue;
251
+
252
+ // Extract original key from filename (reverse sanitization is approximate)
253
+ const keyBase = file.replace('.json', '');
254
+
255
+ // We need to read the file to get the original key
256
+ // For now, use a simple pattern match on the sanitized filename
257
+ if (regex.test(keyBase)) {
258
+ const filePath = path.join(this.cacheDir, file);
259
+ await fs.unlink(filePath).catch(() => {
260
+ // Ignore errors
261
+ });
262
+ }
263
+ }
264
+ } catch (error) {
265
+ console.error(
266
+ '[FileCache] Failed to clear cache:',
267
+ error instanceof Error ? error.message : error
268
+ );
269
+ }
270
+ }
271
+
272
+ async mget(keys: string[]): Promise<Array<unknown | null>> {
273
+ return Promise.all(keys.map((key) => this.get(key)));
274
+ }
275
+
276
+ async mset(entries: Array<[string, unknown, number?]>): Promise<void> {
277
+ await Promise.all(entries.map(([key, value, ttl]) => this.set(key, value, ttl)));
278
+ }
279
+
280
+ async disconnect(): Promise<void> {
281
+ if (this.cleanupTimer) {
282
+ clearInterval(this.cleanupTimer);
283
+ this.cleanupTimer = undefined;
284
+ }
285
+ // Don't delete cache files on disconnect - they should persist
286
+ }
287
+
288
+ /** Get cache statistics */
289
+ async getStats() {
290
+ try {
291
+ await this.ensureInitialized();
292
+ const files = await fs.readdir(this.cacheDir);
293
+ const jsonFiles = files.filter((f) => f.endsWith('.json'));
294
+
295
+ // Calculate total size
296
+ let totalSize = 0;
297
+ for (const file of jsonFiles) {
298
+ const filePath = path.join(this.cacheDir, file);
299
+ try {
300
+ const stats = await fs.stat(filePath);
301
+ totalSize += stats.size;
302
+ } catch {
303
+ // Ignore errors
304
+ }
305
+ }
306
+
307
+ return {
308
+ keys: jsonFiles.length,
309
+ maxKeys: this.maxKeys,
310
+ utilization: (jsonFiles.length / this.maxKeys) * 100,
311
+ sizeBytes: totalSize,
312
+ cacheDir: this.cacheDir,
313
+ };
314
+ } catch (error) {
315
+ console.error('[FileCache] Failed to get stats:', error);
316
+ return {
317
+ keys: 0,
318
+ maxKeys: this.maxKeys,
319
+ utilization: 0,
320
+ sizeBytes: 0,
321
+ cacheDir: this.cacheDir,
322
+ };
323
+ }
324
+ }
325
+
326
+ /** Manually trigger cleanup of expired entries */
327
+ async cleanup(): Promise<void> {
328
+ await this.cleanExpired();
329
+ }
330
+ }
@@ -0,0 +1,105 @@
1
+ import type { CacheProvider } from '@mondaydotcomorg/atp-protocol';
2
+
3
+ interface CacheEntry {
4
+ value: unknown;
5
+ expiresAt: number;
6
+ }
7
+
8
+ /**
9
+ * In-memory cache provider with LRU eviction
10
+ * Good for development and single-server deployments
11
+ */
12
+ export class MemoryCache implements CacheProvider {
13
+ name = 'memory';
14
+ private cache: Map<string, CacheEntry>;
15
+ private maxKeys: number;
16
+ private defaultTTL: number;
17
+
18
+ constructor(options: { maxKeys?: number; defaultTTL?: number } = {}) {
19
+ this.cache = new Map();
20
+ this.maxKeys = options.maxKeys || 1000;
21
+ this.defaultTTL = options.defaultTTL || 3600;
22
+ }
23
+
24
+ async get<T>(key: string): Promise<T | null> {
25
+ const entry = this.cache.get(key);
26
+
27
+ if (!entry) {
28
+ return null;
29
+ }
30
+
31
+ if (entry.expiresAt !== -1 && Date.now() > entry.expiresAt) {
32
+ this.cache.delete(key);
33
+ return null;
34
+ }
35
+
36
+ this.cache.delete(key);
37
+ this.cache.set(key, entry);
38
+
39
+ return entry.value as T;
40
+ }
41
+
42
+ async set(key: string, value: unknown, ttl?: number): Promise<void> {
43
+ if (this.cache.size >= this.maxKeys && !this.cache.has(key)) {
44
+ const firstKey = this.cache.keys().next().value;
45
+ if (firstKey) {
46
+ this.cache.delete(firstKey);
47
+ }
48
+ }
49
+
50
+ const expiresAt = ttl
51
+ ? Date.now() + ttl * 1000
52
+ : this.defaultTTL > 0
53
+ ? Date.now() + this.defaultTTL * 1000
54
+ : -1;
55
+
56
+ this.cache.set(key, { value, expiresAt });
57
+ }
58
+
59
+ async delete(key: string): Promise<void> {
60
+ this.cache.delete(key);
61
+ }
62
+
63
+ async has(key: string): Promise<boolean> {
64
+ const value = await this.get(key);
65
+ return value !== null;
66
+ }
67
+
68
+ async clear(pattern?: string): Promise<void> {
69
+ if (!pattern) {
70
+ this.cache.clear();
71
+ return;
72
+ }
73
+
74
+ const regex = new RegExp('^' + pattern.replace(/\*/g, '.*') + '$');
75
+
76
+ for (const key of this.cache.keys()) {
77
+ if (regex.test(key)) {
78
+ this.cache.delete(key);
79
+ }
80
+ }
81
+ }
82
+
83
+ async mget(keys: string[]): Promise<Array<unknown | null>> {
84
+ return Promise.all(keys.map((key) => this.get(key)));
85
+ }
86
+
87
+ async mset(entries: Array<[string, unknown, number?]>): Promise<void> {
88
+ for (const [key, value, ttl] of entries) {
89
+ await this.set(key, value, ttl);
90
+ }
91
+ }
92
+
93
+ async disconnect(): Promise<void> {
94
+ this.cache.clear();
95
+ }
96
+
97
+ /** Get cache statistics */
98
+ getStats() {
99
+ return {
100
+ keys: this.cache.size,
101
+ maxKeys: this.maxKeys,
102
+ utilization: (this.cache.size / this.maxKeys) * 100,
103
+ };
104
+ }
105
+ }
@@ -0,0 +1,160 @@
1
+ import type { CacheProvider } from '@mondaydotcomorg/atp-protocol';
2
+ import type Redis from 'ioredis';
3
+
4
+ export interface RedisCacheOptions {
5
+ redis: Redis;
6
+ keyPrefix?: string;
7
+ defaultTTL?: number;
8
+ }
9
+
10
+ /**
11
+ * Redis-backed cache provider for distributed systems
12
+ * Enables cross-pod state sharing and resume capabilities
13
+ */
14
+ export class RedisCache implements CacheProvider {
15
+ name = 'redis';
16
+ private redis: Redis;
17
+ private keyPrefix: string;
18
+ private defaultTTL?: number;
19
+
20
+ constructor(options: RedisCacheOptions) {
21
+ this.redis = options.redis;
22
+ this.keyPrefix = options.keyPrefix || 'atp:cache:';
23
+ this.defaultTTL = options.defaultTTL;
24
+ }
25
+
26
+ private getFullKey(key: string): string {
27
+ return `${this.keyPrefix}${key}`;
28
+ }
29
+
30
+ async get<T>(key: string): Promise<T | null> {
31
+ try {
32
+ const value = await this.redis.get(this.getFullKey(key));
33
+ if (!value) return null;
34
+ return JSON.parse(value) as T;
35
+ } catch (error) {
36
+ console.error(
37
+ '[RedisCache] Failed to get key:',
38
+ key,
39
+ error instanceof Error ? error.message : error
40
+ );
41
+ return null;
42
+ }
43
+ }
44
+
45
+ async set(key: string, value: unknown, ttl?: number): Promise<void> {
46
+ try {
47
+ const serialized = JSON.stringify(value);
48
+ const fullKey = this.getFullKey(key);
49
+ const effectiveTTL = ttl ?? this.defaultTTL;
50
+
51
+ if (effectiveTTL) {
52
+ await this.redis.setex(fullKey, effectiveTTL, serialized);
53
+ } else {
54
+ await this.redis.set(fullKey, serialized);
55
+ }
56
+ } catch (error) {
57
+ console.error(
58
+ '[RedisCache] Failed to set key:',
59
+ key,
60
+ error instanceof Error ? error.message : error
61
+ );
62
+ }
63
+ }
64
+
65
+ async delete(key: string): Promise<void> {
66
+ try {
67
+ await this.redis.del(this.getFullKey(key));
68
+ } catch (error) {
69
+ console.error(
70
+ '[RedisCache] Failed to delete key:',
71
+ key,
72
+ error instanceof Error ? error.message : error
73
+ );
74
+ }
75
+ }
76
+
77
+ async has(key: string): Promise<boolean> {
78
+ try {
79
+ const exists = await this.redis.exists(this.getFullKey(key));
80
+ return exists === 1;
81
+ } catch (error) {
82
+ console.error(
83
+ '[RedisCache] Failed to check key:',
84
+ key,
85
+ error instanceof Error ? error.message : error
86
+ );
87
+ return false;
88
+ }
89
+ }
90
+
91
+ async clear(pattern?: string): Promise<void> {
92
+ try {
93
+ if (pattern) {
94
+ const fullPattern = this.getFullKey(pattern);
95
+ const keys = await this.redis.keys(fullPattern);
96
+ if (keys.length > 0) {
97
+ await this.redis.del(...keys);
98
+ }
99
+ } else {
100
+ const keys = await this.redis.keys(this.getFullKey('*'));
101
+ if (keys.length > 0) {
102
+ await this.redis.del(...keys);
103
+ }
104
+ }
105
+ } catch (error) {
106
+ console.error(
107
+ '[RedisCache] Failed to clear cache:',
108
+ error instanceof Error ? error.message : error
109
+ );
110
+ }
111
+ }
112
+
113
+ async mget(keys: string[]): Promise<Array<unknown | null>> {
114
+ try {
115
+ const fullKeys = keys.map((key) => this.getFullKey(key));
116
+ const values = await this.redis.mget(...fullKeys);
117
+ return values.map((value) => (value ? JSON.parse(value) : null));
118
+ } catch (error) {
119
+ console.error(
120
+ '[RedisCache] Failed to mget keys:',
121
+ error instanceof Error ? error.message : error
122
+ );
123
+ return keys.map(() => null);
124
+ }
125
+ }
126
+
127
+ async mset(entries: Array<[string, unknown, number?]>): Promise<void> {
128
+ try {
129
+ const pipeline = this.redis.pipeline();
130
+ for (const [key, value, ttl] of entries) {
131
+ const serialized = JSON.stringify(value);
132
+ const fullKey = this.getFullKey(key);
133
+ const effectiveTTL = ttl ?? this.defaultTTL;
134
+
135
+ if (effectiveTTL) {
136
+ pipeline.setex(fullKey, effectiveTTL, serialized);
137
+ } else {
138
+ pipeline.set(fullKey, serialized);
139
+ }
140
+ }
141
+ await pipeline.exec();
142
+ } catch (error) {
143
+ console.error(
144
+ '[RedisCache] Failed to mset entries:',
145
+ error instanceof Error ? error.message : error
146
+ );
147
+ }
148
+ }
149
+
150
+ async disconnect(): Promise<void> {
151
+ try {
152
+ await this.redis.quit();
153
+ } catch (error) {
154
+ console.error(
155
+ '[RedisCache] Failed to disconnect:',
156
+ error instanceof Error ? error.message : error
157
+ );
158
+ }
159
+ }
160
+ }
package/src/index.ts ADDED
@@ -0,0 +1,32 @@
1
+ export { MemoryCache } from './cache/memory.js';
2
+ export { RedisCache } from './cache/redis.js';
3
+ export { FileCache } from './cache/file.js';
4
+
5
+ export { EnvAuthProvider } from './auth/env.js';
6
+
7
+ export * from './oauth/index.js';
8
+
9
+ export { JSONLAuditSink } from './audit/jsonl.js';
10
+ export { OpenTelemetryAuditSink } from './audit/opentelemetry.js';
11
+
12
+ export {
13
+ OTelCounter,
14
+ OTelHistogram,
15
+ OTelSpan,
16
+ OTelAttribute,
17
+ METRIC_CONFIGS,
18
+ OTEL_SERVICE_NAME,
19
+ OTEL_TRACER_NAME,
20
+ OTEL_METER_NAME,
21
+ ATTRIBUTE_PREFIX_TOOL,
22
+ ATTRIBUTE_PREFIX_METADATA,
23
+ } from './audit/otel-metrics.js';
24
+
25
+ export type {
26
+ CacheProvider,
27
+ AuthProvider,
28
+ AuditSink,
29
+ AuditEvent,
30
+ AuditFilter,
31
+ UserCredentialData,
32
+ } from '@mondaydotcomorg/atp-protocol';
@@ -0,0 +1 @@
1
+ export { ScopeCheckerRegistry } from './scope-checkers.js';