create-fluxstack 1.0.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.
Files changed (101) hide show
  1. package/.env +30 -0
  2. package/LICENSE +21 -0
  3. package/README.md +214 -0
  4. package/app/client/README.md +69 -0
  5. package/app/client/frontend-only.ts +12 -0
  6. package/app/client/index.html +13 -0
  7. package/app/client/public/vite.svg +1 -0
  8. package/app/client/src/App.css +883 -0
  9. package/app/client/src/App.tsx +669 -0
  10. package/app/client/src/assets/react.svg +1 -0
  11. package/app/client/src/components/TestPage.tsx +453 -0
  12. package/app/client/src/index.css +51 -0
  13. package/app/client/src/lib/eden-api.ts +110 -0
  14. package/app/client/src/main.tsx +10 -0
  15. package/app/client/src/vite-env.d.ts +1 -0
  16. package/app/client/tsconfig.app.json +43 -0
  17. package/app/client/tsconfig.json +7 -0
  18. package/app/client/tsconfig.node.json +25 -0
  19. package/app/server/app.ts +10 -0
  20. package/app/server/backend-only.ts +15 -0
  21. package/app/server/controllers/users.controller.ts +69 -0
  22. package/app/server/index.ts +104 -0
  23. package/app/server/routes/index.ts +25 -0
  24. package/app/server/routes/users.routes.ts +121 -0
  25. package/app/server/types/index.ts +1 -0
  26. package/app/shared/types/index.ts +18 -0
  27. package/bun.lock +1053 -0
  28. package/core/__tests__/integration.test.ts +227 -0
  29. package/core/build/index.ts +186 -0
  30. package/core/cli/command-registry.ts +334 -0
  31. package/core/cli/index.ts +394 -0
  32. package/core/cli/plugin-discovery.ts +200 -0
  33. package/core/client/standalone.ts +57 -0
  34. package/core/config/__tests__/config-loader.test.ts +591 -0
  35. package/core/config/__tests__/config-merger.test.ts +657 -0
  36. package/core/config/__tests__/env-converter.test.ts +372 -0
  37. package/core/config/__tests__/env-processor.test.ts +431 -0
  38. package/core/config/__tests__/env.test.ts +452 -0
  39. package/core/config/__tests__/integration.test.ts +418 -0
  40. package/core/config/__tests__/loader.test.ts +331 -0
  41. package/core/config/__tests__/schema.test.ts +129 -0
  42. package/core/config/__tests__/validator.test.ts +318 -0
  43. package/core/config/env-dynamic.ts +326 -0
  44. package/core/config/env.ts +597 -0
  45. package/core/config/index.ts +317 -0
  46. package/core/config/loader.ts +546 -0
  47. package/core/config/runtime-config.ts +322 -0
  48. package/core/config/schema.ts +694 -0
  49. package/core/config/validator.ts +540 -0
  50. package/core/framework/__tests__/server.test.ts +233 -0
  51. package/core/framework/client.ts +132 -0
  52. package/core/framework/index.ts +8 -0
  53. package/core/framework/server.ts +501 -0
  54. package/core/framework/types.ts +63 -0
  55. package/core/plugins/__tests__/built-in.test.ts.disabled +366 -0
  56. package/core/plugins/__tests__/manager.test.ts +398 -0
  57. package/core/plugins/__tests__/monitoring.test.ts +401 -0
  58. package/core/plugins/__tests__/registry.test.ts +335 -0
  59. package/core/plugins/built-in/index.ts +142 -0
  60. package/core/plugins/built-in/logger/index.ts +180 -0
  61. package/core/plugins/built-in/monitoring/README.md +193 -0
  62. package/core/plugins/built-in/monitoring/index.ts +912 -0
  63. package/core/plugins/built-in/static/index.ts +289 -0
  64. package/core/plugins/built-in/swagger/index.ts +229 -0
  65. package/core/plugins/built-in/vite/index.ts +316 -0
  66. package/core/plugins/config.ts +348 -0
  67. package/core/plugins/discovery.ts +350 -0
  68. package/core/plugins/executor.ts +351 -0
  69. package/core/plugins/index.ts +195 -0
  70. package/core/plugins/manager.ts +583 -0
  71. package/core/plugins/registry.ts +424 -0
  72. package/core/plugins/types.ts +254 -0
  73. package/core/server/framework.ts +123 -0
  74. package/core/server/index.ts +8 -0
  75. package/core/server/plugins/database.ts +182 -0
  76. package/core/server/plugins/logger.ts +47 -0
  77. package/core/server/plugins/swagger.ts +34 -0
  78. package/core/server/standalone.ts +91 -0
  79. package/core/templates/create-project.ts +455 -0
  80. package/core/types/api.ts +169 -0
  81. package/core/types/build.ts +174 -0
  82. package/core/types/config.ts +68 -0
  83. package/core/types/index.ts +127 -0
  84. package/core/types/plugin.ts +94 -0
  85. package/core/utils/__tests__/errors.test.ts +139 -0
  86. package/core/utils/__tests__/helpers.test.ts +297 -0
  87. package/core/utils/__tests__/logger.test.ts +141 -0
  88. package/core/utils/env-runtime-v2.ts +232 -0
  89. package/core/utils/env-runtime.ts +252 -0
  90. package/core/utils/errors/codes.ts +115 -0
  91. package/core/utils/errors/handlers.ts +63 -0
  92. package/core/utils/errors/index.ts +81 -0
  93. package/core/utils/helpers.ts +180 -0
  94. package/core/utils/index.ts +18 -0
  95. package/core/utils/logger/index.ts +161 -0
  96. package/core/utils/logger.ts +106 -0
  97. package/core/utils/monitoring/index.ts +212 -0
  98. package/create-fluxstack.ts +231 -0
  99. package/package.json +43 -0
  100. package/tsconfig.json +51 -0
  101. package/vite.config.ts +42 -0
@@ -0,0 +1,372 @@
1
+ /**
2
+ * Tests for Environment Variable Type Conversion Utilities
3
+ * Tests edge cases and type conversion robustness
4
+ */
5
+
6
+ import { describe, test, expect } from 'vitest'
7
+ import { EnvConverter } from '../env'
8
+
9
+ describe('EnvConverter Edge Cases and Robustness', () => {
10
+ describe('toNumber edge cases', () => {
11
+ test('handles various number formats correctly', () => {
12
+ // Basic cases
13
+ expect(EnvConverter.toNumber('42', 0)).toBe(42)
14
+ expect(EnvConverter.toNumber('-123', 0)).toBe(-123)
15
+ expect(EnvConverter.toNumber('0', 42)).toBe(0)
16
+
17
+ // Edge cases
18
+ expect(EnvConverter.toNumber('00000123', 0)).toBe(123)
19
+ expect(EnvConverter.toNumber('+456', 0)).toBe(456)
20
+ expect(EnvConverter.toNumber(' 789 ', 0)).toBe(789) // With whitespace
21
+
22
+ // Hex and octal (parseInt with base 10 behavior)
23
+ expect(EnvConverter.toNumber('0x10', 0)).toBe(0) // parseInt with base 10 doesn't parse hex
24
+ expect(EnvConverter.toNumber('0o10', 0)).toBe(0) // parseInt doesn't handle 0o prefix
25
+ expect(EnvConverter.toNumber('010', 0)).toBe(10) // Treated as decimal, not octal
26
+
27
+ // Invalid cases
28
+ expect(EnvConverter.toNumber('abc', 42)).toBe(42)
29
+ expect(EnvConverter.toNumber('123abc', 42)).toBe(123) // parseInt stops at first non-digit
30
+ expect(EnvConverter.toNumber('', 42)).toBe(42)
31
+ expect(EnvConverter.toNumber(' ', 42)).toBe(42)
32
+ expect(EnvConverter.toNumber('Infinity', 42)).toBe(42)
33
+ expect(EnvConverter.toNumber('NaN', 42)).toBe(42)
34
+ expect(EnvConverter.toNumber(undefined, 42)).toBe(42)
35
+ })
36
+
37
+ test('handles floating point strings (parseInt behavior)', () => {
38
+ // parseInt truncates to integer
39
+ expect(EnvConverter.toNumber('12.34', 0)).toBe(12)
40
+ expect(EnvConverter.toNumber('56.78', 0)).toBe(56)
41
+ expect(EnvConverter.toNumber('.123', 0)).toBe(0) // parseInt behavior with leading dot
42
+ expect(EnvConverter.toNumber('9.', 0)).toBe(9)
43
+ })
44
+
45
+ test('handles extreme values', () => {
46
+ expect(EnvConverter.toNumber('999999999999999', 0)).toBe(999999999999999)
47
+ expect(EnvConverter.toNumber('-999999999999999', 0)).toBe(-999999999999999)
48
+ expect(EnvConverter.toNumber('1e10', 0)).toBe(1) // parseInt stops at 'e'
49
+ })
50
+ })
51
+
52
+ describe('toBoolean edge cases', () => {
53
+ test('handles various truthy representations', () => {
54
+ // Standard truthy values
55
+ expect(EnvConverter.toBoolean('true', false)).toBe(true)
56
+ expect(EnvConverter.toBoolean('1', false)).toBe(true)
57
+ expect(EnvConverter.toBoolean('yes', false)).toBe(true)
58
+ expect(EnvConverter.toBoolean('on', false)).toBe(true)
59
+
60
+ // Case variations
61
+ expect(EnvConverter.toBoolean('TRUE', false)).toBe(true)
62
+ expect(EnvConverter.toBoolean('True', false)).toBe(true)
63
+ expect(EnvConverter.toBoolean('YES', false)).toBe(true)
64
+ expect(EnvConverter.toBoolean('Yes', false)).toBe(true)
65
+ expect(EnvConverter.toBoolean('ON', false)).toBe(true)
66
+ expect(EnvConverter.toBoolean('On', false)).toBe(true)
67
+
68
+ // With whitespace
69
+ expect(EnvConverter.toBoolean(' true ', false)).toBe(false) // No trim in current implementation
70
+ expect(EnvConverter.toBoolean(' 1 ', false)).toBe(false)
71
+ })
72
+
73
+ test('handles various falsy representations', () => {
74
+ // Standard falsy values
75
+ expect(EnvConverter.toBoolean('false', true)).toBe(false)
76
+ expect(EnvConverter.toBoolean('0', true)).toBe(false)
77
+ expect(EnvConverter.toBoolean('no', true)).toBe(false)
78
+ expect(EnvConverter.toBoolean('off', true)).toBe(false)
79
+
80
+ // Any other string is falsy
81
+ expect(EnvConverter.toBoolean('maybe', true)).toBe(false)
82
+ expect(EnvConverter.toBoolean('2', true)).toBe(false)
83
+ expect(EnvConverter.toBoolean('anything', true)).toBe(false)
84
+ expect(EnvConverter.toBoolean('', true)).toBe(false)
85
+ })
86
+
87
+ test('handles edge cases', () => {
88
+ expect(EnvConverter.toBoolean(undefined, true)).toBe(true)
89
+ expect(EnvConverter.toBoolean(undefined, false)).toBe(false)
90
+ })
91
+ })
92
+
93
+ describe('toArray edge cases', () => {
94
+ test('handles various delimiter scenarios', () => {
95
+ // Basic cases
96
+ expect(EnvConverter.toArray('a,b,c')).toEqual(['a', 'b', 'c'])
97
+ expect(EnvConverter.toArray('single')).toEqual(['single'])
98
+
99
+ // Whitespace handling
100
+ expect(EnvConverter.toArray('a, b , c')).toEqual(['a', 'b', 'c'])
101
+ expect(EnvConverter.toArray(' a , b , c ')).toEqual(['a', 'b', 'c'])
102
+
103
+ // Empty values
104
+ expect(EnvConverter.toArray('a,,c')).toEqual(['a', 'c'])
105
+ expect(EnvConverter.toArray(',a,b,')).toEqual(['a', 'b'])
106
+ expect(EnvConverter.toArray(',,,')).toEqual([])
107
+
108
+ // Special characters
109
+ expect(EnvConverter.toArray('a\\,b,c')).toEqual(['a\\', 'b', 'c']) // No escape handling
110
+ expect(EnvConverter.toArray('a"b,c')).toEqual(['a"b', 'c'])
111
+ expect(EnvConverter.toArray("a'b,c")).toEqual(["a'b", 'c'])
112
+ })
113
+
114
+ test('handles empty and undefined inputs', () => {
115
+ expect(EnvConverter.toArray('')).toEqual([])
116
+ expect(EnvConverter.toArray(undefined)).toEqual([])
117
+ expect(EnvConverter.toArray(undefined, ['default'])).toEqual(['default'])
118
+ })
119
+
120
+ test('handles URLs and complex strings', () => {
121
+ const urls = 'http://localhost:3000,https://example.com:8080,ftp://ftp.example.com'
122
+ expect(EnvConverter.toArray(urls)).toEqual([
123
+ 'http://localhost:3000',
124
+ 'https://example.com:8080',
125
+ 'ftp://ftp.example.com'
126
+ ])
127
+
128
+ const methods = 'GET,POST,PUT,DELETE,PATCH,OPTIONS,HEAD'
129
+ expect(EnvConverter.toArray(methods)).toEqual([
130
+ 'GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS', 'HEAD'
131
+ ])
132
+ })
133
+ })
134
+
135
+ describe('toLogLevel edge cases', () => {
136
+ test('handles case variations', () => {
137
+ expect(EnvConverter.toLogLevel('DEBUG', 'info')).toBe('debug')
138
+ expect(EnvConverter.toLogLevel('Info', 'debug')).toBe('info')
139
+ expect(EnvConverter.toLogLevel('WARN', 'info')).toBe('warn')
140
+ expect(EnvConverter.toLogLevel('Error', 'info')).toBe('error')
141
+ })
142
+
143
+ test('handles invalid levels', () => {
144
+ expect(EnvConverter.toLogLevel('verbose', 'info')).toBe('info')
145
+ expect(EnvConverter.toLogLevel('trace', 'warn')).toBe('warn')
146
+ expect(EnvConverter.toLogLevel('critical', 'error')).toBe('error')
147
+ expect(EnvConverter.toLogLevel('', 'debug')).toBe('debug')
148
+ expect(EnvConverter.toLogLevel('123', 'info')).toBe('info')
149
+ })
150
+
151
+ test('handles whitespace and special characters', () => {
152
+ expect(EnvConverter.toLogLevel(' debug ', 'info')).toBe('info') // No trim
153
+ expect(EnvConverter.toLogLevel('debug\n', 'info')).toBe('info')
154
+ expect(EnvConverter.toLogLevel('de-bug', 'info')).toBe('info')
155
+ })
156
+ })
157
+
158
+ describe('toBuildTarget edge cases', () => {
159
+ test('handles case variations', () => {
160
+ expect(EnvConverter.toBuildTarget('BUN', 'node')).toBe('bun')
161
+ expect(EnvConverter.toBuildTarget('Node', 'bun')).toBe('node')
162
+ expect(EnvConverter.toBuildTarget('DOCKER', 'bun')).toBe('docker')
163
+ })
164
+
165
+ test('handles invalid targets', () => {
166
+ expect(EnvConverter.toBuildTarget('webpack', 'bun')).toBe('bun')
167
+ expect(EnvConverter.toBuildTarget('rollup', 'node')).toBe('node')
168
+ expect(EnvConverter.toBuildTarget('vite', 'docker')).toBe('docker')
169
+ expect(EnvConverter.toBuildTarget('', 'bun')).toBe('bun')
170
+ })
171
+
172
+ test('handles similar but invalid targets', () => {
173
+ expect(EnvConverter.toBuildTarget('nodejs', 'bun')).toBe('bun')
174
+ expect(EnvConverter.toBuildTarget('bunjs', 'node')).toBe('node')
175
+ expect(EnvConverter.toBuildTarget('container', 'bun')).toBe('bun')
176
+ })
177
+ })
178
+
179
+ describe('toLogFormat edge cases', () => {
180
+ test('handles case variations', () => {
181
+ expect(EnvConverter.toLogFormat('JSON', 'pretty')).toBe('json')
182
+ expect(EnvConverter.toLogFormat('Pretty', 'json')).toBe('pretty')
183
+ expect(EnvConverter.toLogFormat('PRETTY', 'json')).toBe('pretty')
184
+ })
185
+
186
+ test('handles invalid formats', () => {
187
+ expect(EnvConverter.toLogFormat('text', 'json')).toBe('json')
188
+ expect(EnvConverter.toLogFormat('yaml', 'pretty')).toBe('pretty')
189
+ expect(EnvConverter.toLogFormat('structured', 'json')).toBe('json')
190
+ expect(EnvConverter.toLogFormat('', 'pretty')).toBe('pretty')
191
+ })
192
+ })
193
+
194
+ describe('toObject edge cases', () => {
195
+ test('handles valid JSON variations', () => {
196
+ expect(EnvConverter.toObject('{"key":"value"}', {})).toEqual({ key: 'value' })
197
+ expect(EnvConverter.toObject('{"nested":{"key":"value"}}', {})).toEqual({
198
+ nested: { key: 'value' }
199
+ })
200
+ expect(EnvConverter.toObject('[1,2,3]', {})).toEqual([1, 2, 3])
201
+ expect(EnvConverter.toObject('null', {})).toBe(null)
202
+ expect(EnvConverter.toObject('true', {})).toBe(true)
203
+ expect(EnvConverter.toObject('42', {})).toBe(42)
204
+ expect(EnvConverter.toObject('"string"', {})).toBe('string')
205
+ })
206
+
207
+ test('handles malformed JSON', () => {
208
+ const defaultValue = { default: true }
209
+
210
+ expect(EnvConverter.toObject('{key:"value"}', defaultValue)).toBe(defaultValue) // Missing quotes
211
+ expect(EnvConverter.toObject('{"key":value}', defaultValue)).toBe(defaultValue) // Unquoted value
212
+ expect(EnvConverter.toObject('{key:value}', defaultValue)).toBe(defaultValue) // No quotes
213
+ expect(EnvConverter.toObject('{"key":"value",}', defaultValue)).toBe(defaultValue) // Trailing comma
214
+ expect(EnvConverter.toObject('{', defaultValue)).toBe(defaultValue) // Incomplete
215
+ expect(EnvConverter.toObject('not json at all', defaultValue)).toBe(defaultValue)
216
+ })
217
+
218
+ test('handles edge JSON cases', () => {
219
+ expect(EnvConverter.toObject('{}', { default: true })).toEqual({})
220
+ expect(EnvConverter.toObject('[]', { default: true })).toEqual([])
221
+ expect(EnvConverter.toObject('""', { default: true })).toBe('')
222
+ expect(EnvConverter.toObject('0', { default: true })).toBe(0)
223
+ expect(EnvConverter.toObject('false', { default: true })).toBe(false)
224
+ })
225
+
226
+ test('handles complex nested objects', () => {
227
+ const complexJson = JSON.stringify({
228
+ server: {
229
+ port: 3000,
230
+ host: 'localhost',
231
+ options: {
232
+ cors: true,
233
+ compression: false,
234
+ middleware: ['auth', 'logging']
235
+ }
236
+ },
237
+ database: {
238
+ url: 'postgresql://localhost:5432/db',
239
+ pool: { min: 2, max: 10 }
240
+ }
241
+ })
242
+
243
+ const result = EnvConverter.toObject(complexJson, {})
244
+ expect(result).toEqual({
245
+ server: {
246
+ port: 3000,
247
+ host: 'localhost',
248
+ options: {
249
+ cors: true,
250
+ compression: false,
251
+ middleware: ['auth', 'logging']
252
+ }
253
+ },
254
+ database: {
255
+ url: 'postgresql://localhost:5432/db',
256
+ pool: { min: 2, max: 10 }
257
+ }
258
+ })
259
+ })
260
+
261
+ test('handles undefined and empty inputs', () => {
262
+ const defaultValue = { default: true }
263
+ expect(EnvConverter.toObject(undefined, defaultValue)).toBe(defaultValue)
264
+ expect(EnvConverter.toObject('', defaultValue)).toBe(defaultValue)
265
+ })
266
+ })
267
+
268
+ describe('Conversion consistency', () => {
269
+ test('maintains type safety across conversions', () => {
270
+ // Number conversions should always return numbers or defaults
271
+ const numResult = EnvConverter.toNumber('invalid', 42)
272
+ expect(typeof numResult).toBe('number')
273
+ expect(numResult).toBe(42)
274
+
275
+ // Boolean conversions should always return booleans
276
+ const boolResult = EnvConverter.toBoolean('maybe', true)
277
+ expect(typeof boolResult).toBe('boolean')
278
+
279
+ // Array conversions should always return arrays
280
+ const arrayResult = EnvConverter.toArray('a,b,c')
281
+ expect(Array.isArray(arrayResult)).toBe(true)
282
+
283
+ // String enums should return valid enum values or defaults
284
+ const logLevelResult = EnvConverter.toLogLevel('invalid', 'info')
285
+ expect(['debug', 'info', 'warn', 'error']).toContain(logLevelResult)
286
+ })
287
+
288
+ test('handles null and undefined consistently', () => {
289
+ expect(EnvConverter.toNumber(undefined, 42)).toBe(42)
290
+ expect(EnvConverter.toBoolean(undefined, true)).toBe(true)
291
+ expect(EnvConverter.toArray(undefined)).toEqual([])
292
+ expect(EnvConverter.toLogLevel(undefined, 'info')).toBe('info')
293
+ expect(EnvConverter.toBuildTarget(undefined, 'bun')).toBe('bun')
294
+ expect(EnvConverter.toLogFormat(undefined, 'pretty')).toBe('pretty')
295
+ expect(EnvConverter.toObject(undefined, {})).toEqual({})
296
+ })
297
+ })
298
+
299
+ describe('Real-world environment variable scenarios', () => {
300
+ test('handles PORT environment variable edge cases', () => {
301
+ // Common port scenarios
302
+ expect(EnvConverter.toNumber('3000', 8080)).toBe(3000)
303
+ expect(EnvConverter.toNumber('0', 8080)).toBe(0) // Dynamic port
304
+ expect(EnvConverter.toNumber('80', 8080)).toBe(80)
305
+ expect(EnvConverter.toNumber('443', 8080)).toBe(443)
306
+ expect(EnvConverter.toNumber('8080', 3000)).toBe(8080)
307
+
308
+ // Invalid port values
309
+ expect(EnvConverter.toNumber('port', 3000)).toBe(3000)
310
+ expect(EnvConverter.toNumber('-1', 3000)).toBe(-1) // Negative (invalid but parsed)
311
+ expect(EnvConverter.toNumber('70000', 3000)).toBe(70000) // Out of range but parsed
312
+ })
313
+
314
+ test('handles CORS_ORIGINS environment variable', () => {
315
+ // Single origin
316
+ expect(EnvConverter.toArray('http://localhost:3000')).toEqual(['http://localhost:3000'])
317
+
318
+ // Multiple origins
319
+ expect(EnvConverter.toArray('http://localhost:3000,https://example.com')).toEqual([
320
+ 'http://localhost:3000',
321
+ 'https://example.com'
322
+ ])
323
+
324
+ // With ports and paths
325
+ expect(EnvConverter.toArray('http://localhost:3000,https://example.com:8080/app')).toEqual([
326
+ 'http://localhost:3000',
327
+ 'https://example.com:8080/app'
328
+ ])
329
+
330
+ // Wildcard
331
+ expect(EnvConverter.toArray('*')).toEqual(['*'])
332
+ })
333
+
334
+ test('handles boolean flags from different tools', () => {
335
+ // Docker-style
336
+ expect(EnvConverter.toBoolean('true', false)).toBe(true)
337
+ expect(EnvConverter.toBoolean('false', true)).toBe(false)
338
+
339
+ // Shell-style
340
+ expect(EnvConverter.toBoolean('1', false)).toBe(true)
341
+ expect(EnvConverter.toBoolean('0', true)).toBe(false)
342
+
343
+ // Human-readable
344
+ expect(EnvConverter.toBoolean('yes', false)).toBe(true)
345
+ expect(EnvConverter.toBoolean('no', true)).toBe(false)
346
+ expect(EnvConverter.toBoolean('on', false)).toBe(true)
347
+ expect(EnvConverter.toBoolean('off', true)).toBe(false)
348
+ })
349
+
350
+ test('handles complex JSON configurations', () => {
351
+ // Plugin configuration
352
+ const pluginConfig = JSON.stringify({
353
+ enabled: true,
354
+ options: {
355
+ timeout: 5000,
356
+ retries: 3,
357
+ endpoints: ['api', 'webhook']
358
+ }
359
+ })
360
+
361
+ const result = EnvConverter.toObject(pluginConfig, {})
362
+ expect(result).toEqual({
363
+ enabled: true,
364
+ options: {
365
+ timeout: 5000,
366
+ retries: 3,
367
+ endpoints: ['api', 'webhook']
368
+ }
369
+ })
370
+ })
371
+ })
372
+ })