@coopenomics/notifications 2025.11.8-3

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 (43) hide show
  1. package/.cursor/rules/notifications.mdc +20 -0
  2. package/.env-example +2 -0
  3. package/README.md +218 -0
  4. package/build.config.ts +13 -0
  5. package/dist/index.cjs +3027 -0
  6. package/dist/index.d.cts +792 -0
  7. package/dist/index.d.mts +792 -0
  8. package/dist/index.d.ts +792 -0
  9. package/dist/index.mjs +3016 -0
  10. package/package.json +55 -0
  11. package/src/base/defaults.ts +66 -0
  12. package/src/base/workflow-builder.ts +128 -0
  13. package/src/index.ts +9 -0
  14. package/src/sync/README.md +54 -0
  15. package/src/sync/novu-sync.service.ts +246 -0
  16. package/src/sync/sync-runner.ts +154 -0
  17. package/src/types/index.ts +99 -0
  18. package/src/utils/index.ts +1 -0
  19. package/src/utils/slugify/builtinReplacements.ts +2065 -0
  20. package/src/utils/slugify/slugify.ts +284 -0
  21. package/src/utils/slugify/transliterate.ts +48 -0
  22. package/src/workflows/approval-request/index.ts +52 -0
  23. package/src/workflows/approval-response/index.ts +54 -0
  24. package/src/workflows/decision-approved/index.ts +50 -0
  25. package/src/workflows/email-verification/index.ts +35 -0
  26. package/src/workflows/incoming-transfer/index.ts +43 -0
  27. package/src/workflows/index.ts +74 -0
  28. package/src/workflows/invite/index.ts +34 -0
  29. package/src/workflows/meet-ended/index.ts +51 -0
  30. package/src/workflows/meet-initial/index.ts +53 -0
  31. package/src/workflows/meet-reminder-end/index.ts +52 -0
  32. package/src/workflows/meet-reminder-start/index.ts +51 -0
  33. package/src/workflows/meet-restart/index.ts +53 -0
  34. package/src/workflows/meet-started/index.ts +51 -0
  35. package/src/workflows/new-agenda-item/index.ts +51 -0
  36. package/src/workflows/new-deposit-payment-request/index.ts +50 -0
  37. package/src/workflows/new-initial-payment-request/index.ts +50 -0
  38. package/src/workflows/payment-cancelled/index.ts +51 -0
  39. package/src/workflows/payment-paid/index.ts +50 -0
  40. package/src/workflows/reset-key/index.ts +36 -0
  41. package/src/workflows/server-provisioned/index.ts +45 -0
  42. package/src/workflows/welcome/index.ts +43 -0
  43. package/tsconfig.json +18 -0
@@ -0,0 +1,154 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { NovuSyncService } from './novu-sync.service';
4
+ import { existsSync } from 'fs';
5
+ import { join, dirname } from 'path';
6
+ import { watch } from 'chokidar';
7
+ import dotenv from 'dotenv';
8
+ import { fileURLToPath } from 'url';
9
+
10
+ dotenv.config();
11
+
12
+ // Конфигурация из переменных окружения
13
+ const config = {
14
+ apiKey: process.env.NOVU_API_KEY || '',
15
+ apiUrl: process.env.NOVU_API_URL || '',
16
+ };
17
+
18
+ async function runSync() {
19
+ console.log('🔄 Запуск синхронизации воркфлоу...');
20
+
21
+ try {
22
+ // Проверяем конфигурацию
23
+ if (!config.apiKey) {
24
+ throw new Error('❌ NOVU_API_KEY не установлен в переменных окружения');
25
+ }
26
+
27
+ if (!config.apiUrl) {
28
+ throw new Error('❌ NOVU_API_URL не установлен в переменных окружения');
29
+ }
30
+
31
+ const syncService = new NovuSyncService(config);
32
+ await syncService.upsertAllWorkflows();
33
+ console.log('✅ Синхронизация завершена успешно');
34
+ return true;
35
+ } catch (error: any) {
36
+ console.error('❌ Ошибка синхронизации:', error.message);
37
+
38
+ return false;
39
+ }
40
+ }
41
+
42
+ async function main() {
43
+ const args = process.argv.slice(2);
44
+ const isDev = args.includes('--dev') || process.env.NODE_ENV === 'development';
45
+
46
+ if (isDev) {
47
+ console.log('📡 Режим разработки: отслеживаем изменения...');
48
+
49
+ // Запускаем синхронизацию сразу
50
+ const initialSyncSuccess = await runSync();
51
+
52
+ if (!initialSyncSuccess) {
53
+ console.error('❌ Первоначальная синхронизация не удалась');
54
+ process.exit(1);
55
+ }
56
+
57
+ // Отслеживаем изменения в файлах воркфлоу и типов
58
+ const __dirname = dirname(fileURLToPath(import.meta.url));
59
+ const workflowsPath = join(__dirname, '../workflows');
60
+ const typesPath = join(__dirname, '../types');
61
+
62
+ const watchPaths = [];
63
+ if (existsSync(workflowsPath)) {
64
+ watchPaths.push(workflowsPath);
65
+ }
66
+ if (existsSync(typesPath)) {
67
+ watchPaths.push(typesPath);
68
+ }
69
+
70
+ if (watchPaths.length > 0) {
71
+ console.log('👀 Отслеживаем изменения в:', watchPaths.join(', '));
72
+
73
+ let syncTimeout: NodeJS.Timeout | null = null;
74
+
75
+ const watcher = watch(watchPaths, {
76
+ ignored: /node_modules/,
77
+ persistent: true,
78
+ ignoreInitial: true,
79
+ });
80
+
81
+ watcher.on('change', (path: string) => {
82
+ console.log(`📝 Изменен файл: ${path}`);
83
+
84
+ // Дебаунс для избежания множественных запусков
85
+ if (syncTimeout) {
86
+ clearTimeout(syncTimeout);
87
+ }
88
+
89
+ syncTimeout = setTimeout(async () => {
90
+ console.log('⏰ Запускаем синхронизацию...');
91
+ const success = await runSync();
92
+ if (!success) {
93
+ console.error('⚠️ Синхронизация не удалась, но продолжаем отслеживание...');
94
+ }
95
+ }, 1000);
96
+ });
97
+
98
+ watcher.on('add', (path: string) => {
99
+ console.log(`➕ Добавлен файл: ${path}`);
100
+ if (syncTimeout) {
101
+ clearTimeout(syncTimeout);
102
+ }
103
+ syncTimeout = setTimeout(async () => {
104
+ console.log('⏰ Запускаем синхронизацию...');
105
+ const success = await runSync();
106
+ if (!success) {
107
+ console.error('⚠️ Синхронизация не удалась, но продолжаем отслеживание...');
108
+ }
109
+ }, 1000);
110
+ });
111
+
112
+ watcher.on('unlink', (path: string) => {
113
+ console.log(`➖ Удален файл: ${path}`);
114
+ if (syncTimeout) {
115
+ clearTimeout(syncTimeout);
116
+ }
117
+ syncTimeout = setTimeout(async () => {
118
+ console.log('⏰ Запускаем синхронизацию...');
119
+ const success = await runSync();
120
+ if (!success) {
121
+ console.error('⚠️ Синхронизация не удалась, но продолжаем отслеживание...');
122
+ }
123
+ }, 1000);
124
+ });
125
+
126
+ console.log('💡 Нажмите Ctrl+C для выхода');
127
+
128
+ // Держим процесс живым
129
+ process.on('SIGINT', () => {
130
+ console.log('\n👋 Выход из режима разработки');
131
+ watcher.close();
132
+ process.exit(0);
133
+ });
134
+ } else {
135
+ console.log('⚠️ Папки для отслеживания не найдены');
136
+ process.exit(1);
137
+ }
138
+
139
+ } else {
140
+ // Production режим - запускаем один раз
141
+ const success = await runSync();
142
+ process.exit(success ? 0 : 1);
143
+ }
144
+ }
145
+
146
+ // Проверяем, запущен ли файл как основной модуль
147
+ if (import.meta.url === `file://${process.argv[1]}`) {
148
+ main().catch((error) => {
149
+ console.error('❌ Критическая ошибка:', error);
150
+ process.exit(1);
151
+ });
152
+ }
153
+
154
+ export { runSync, NovuSyncService };
@@ -0,0 +1,99 @@
1
+ import { z } from 'zod';
2
+
3
+ // Базовые типы для каналов уведомлений
4
+ export interface ChannelConfig {
5
+ enabled: boolean;
6
+ readOnly?: boolean;
7
+ }
8
+
9
+ export interface ChannelsConfig {
10
+ email: ChannelConfig;
11
+ sms: ChannelConfig;
12
+ in_app: ChannelConfig;
13
+ push: ChannelConfig;
14
+ chat: ChannelConfig;
15
+ }
16
+
17
+ export interface PreferencesConfig {
18
+ user: {
19
+ all: ChannelConfig;
20
+ channels: ChannelsConfig;
21
+ };
22
+ workflow: {
23
+ all: ChannelConfig;
24
+ channels: ChannelsConfig;
25
+ };
26
+ }
27
+
28
+ // Базовые типы для шагов воркфлоу
29
+ export interface StepControlValues {
30
+ subject?: string;
31
+ body?: string;
32
+ title?: string;
33
+ content?: string;
34
+ avatar?: string;
35
+ editorType?: 'html' | 'text';
36
+ [key: string]: any;
37
+ }
38
+
39
+ export interface WorkflowStep {
40
+ name: string;
41
+ type: 'email' | 'sms' | 'in_app' | 'push' | 'chat' | 'delay' | 'digest';
42
+ controlValues: StepControlValues;
43
+ }
44
+
45
+ // Базовый интерфейс для payload воркфлоу
46
+ export interface BaseWorkflowPayload {
47
+ [key: string]: any;
48
+ }
49
+
50
+ // Интерфейс для схемы payload (JSON Schema)
51
+ export interface PayloadSchema {
52
+ type: string;
53
+ properties: Record<string, any>;
54
+ required: string[];
55
+ }
56
+
57
+ // Допустимые значения для origin в Novu
58
+ export type NovuOrigin = 'novu-cloud' | 'novu-cloud-v1' | 'external';
59
+
60
+ // Главный интерфейс для воркфлоу
61
+ export interface WorkflowDefinition<T extends BaseWorkflowPayload = BaseWorkflowPayload> {
62
+ name: string;
63
+ workflowId: string;
64
+ description?: string;
65
+ payloadSchema: PayloadSchema;
66
+ steps: WorkflowStep[];
67
+ preferences: PreferencesConfig;
68
+ origin?: NovuOrigin; // Делаем optional
69
+ tags?: string[]; // Теги для группировки воркфлоу
70
+ // Типизированная схема для валидации payload
71
+ payloadZodSchema: z.ZodSchema<T>;
72
+ }
73
+
74
+ // Интерфейс для создания воркфлоу в Novu (без origin для создания)
75
+ export interface NovuWorkflowData {
76
+ name: string;
77
+ workflowId: string;
78
+ description?: string;
79
+ payloadSchema: PayloadSchema;
80
+ steps: WorkflowStep[];
81
+ preferences: PreferencesConfig;
82
+ origin?: NovuOrigin; // Optional - только для обновлений
83
+ tags?: string[]; // Теги для группировки воркфлоу
84
+ }
85
+
86
+ // Интерфейс для триггера воркфлоу
87
+ export interface WorkflowTriggerData<T extends BaseWorkflowPayload> {
88
+ workflowId: string;
89
+ subscriberId: string;
90
+ payload: T;
91
+ actor?: {
92
+ subscriberId: string;
93
+ email?: string;
94
+ };
95
+ }
96
+
97
+ // Утилитарные типы
98
+ export type WorkflowStepType = WorkflowStep['type'];
99
+ export type ChannelType = keyof ChannelsConfig;
@@ -0,0 +1 @@
1
+ export { slugify } from './slugify/slugify';