@codeleap/debug 5.8.9 → 5.8.11

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codeleap/debug",
3
- "version": "5.8.9",
3
+ "version": "5.8.11",
4
4
  "main": "src/index.ts",
5
5
  "license": "UNLICENSED",
6
6
  "repository": {
@@ -9,7 +9,7 @@
9
9
  "directory": "packages/debug"
10
10
  },
11
11
  "devDependencies": {
12
- "@codeleap/config": "5.8.9",
12
+ "@codeleap/config": "5.8.11",
13
13
  "ts-node-dev": "1.1.8"
14
14
  },
15
15
  "scripts": {
package/package.json.bak CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codeleap/debug",
3
- "version": "5.8.9",
3
+ "version": "5.8.11",
4
4
  "main": "src/index.ts",
5
5
  "license": "UNLICENSED",
6
6
  "repository": {
@@ -0,0 +1,139 @@
1
+ import { globalState } from '@codeleap/store'
2
+ import { Keyof, StringRecord, TypeGuards } from '@codeleap/types'
3
+ import { capitalize, deepEqual } from '@codeleap/utils'
4
+ import { useState, useMemo } from 'react'
5
+ import { EnvironmentStore, EnvironmentManagerConfig, Editor } from './types'
6
+ import { EDITORS } from './const'
7
+
8
+ export class EnvironmentManager<T extends StringRecord> {
9
+ private store: ReturnType<typeof globalState<EnvironmentStore<T>>>
10
+
11
+ private initialStoreValue: EnvironmentStore<T>
12
+
13
+ readonly editors = EDITORS
14
+
15
+ get environments() {
16
+ return this.config.environments
17
+ }
18
+
19
+ get defaultConfig() {
20
+ return {
21
+ environment: this.config.defaultEnvironment,
22
+ customUrl: '',
23
+ enabledBy: null,
24
+ } as EnvironmentStore<T>
25
+ }
26
+
27
+ constructor(private config: EnvironmentManagerConfig<T>) {
28
+ this.store = globalState<EnvironmentStore<T>>(
29
+ this.defaultConfig,
30
+ { persistKey: config?.persistKey ?? 'environment-manager' }
31
+ )
32
+
33
+ this.initialStoreValue = this.store.get()
34
+ }
35
+
36
+ testUrl(url: string) {
37
+ const urlValidator: RegExp = /^https?:\/\/([a-zA-Z0-9.-]+|localhost|(\d{1,3}\.){3}\d{1,3})(?::\d+)?\/$/
38
+ return urlValidator.test(url)
39
+ }
40
+
41
+ get env(): EnvironmentStore<T> {
42
+ const value = this.store.get() ?? this.defaultConfig
43
+
44
+ if (value.enabledBy) {
45
+ if (value.environment === this.config.customEnvironment && !this.testUrl(value.customUrl)) {
46
+ value.environment = this.defaultConfig.environment
47
+ }
48
+
49
+ return value
50
+ }
51
+
52
+ return this.defaultConfig
53
+ }
54
+
55
+ setEnabled(by: Editor | false = false) {
56
+ const disable = by === false || TypeGuards.isNil(by)
57
+ this.store.set({ enabledBy: disable ? null : by })
58
+ return !disable
59
+ }
60
+
61
+ get isEnabled(): boolean {
62
+ return !TypeGuards.isNil(this.isEnabledBy)
63
+ }
64
+
65
+ get isEnabledBy(): Editor {
66
+ return this.store.get(s => s?.enabledBy)
67
+ }
68
+
69
+ get isEnabledBySystem(): boolean {
70
+ return this.isEnabledBy === EDITORS.SYSTEM
71
+ }
72
+
73
+ get isEnabledByUser(): boolean {
74
+ return this.isEnabledBy === EDITORS.USER
75
+ }
76
+
77
+ get is(): Record<Capitalize<Keyof<T> extends string ? Keyof<T> : string>, boolean> {
78
+ const environment = this.store.get(s => s?.environment ?? this.defaultConfig.environment)
79
+
80
+ return Object.entries(this.environments).reduce((acc, [key, value]) => {
81
+ acc[capitalize(key) as Keyof<T>] = environment === key
82
+ return acc
83
+ }, {} as Record<Keyof<T>, boolean>)
84
+ }
85
+
86
+ get serverUrl() {
87
+ const env = this.env
88
+
89
+ if (env.environment === this.config.customEnvironment) {
90
+ if (!this.testUrl(env.customUrl)) {
91
+ return this.environments[this.defaultConfig.environment]
92
+ }
93
+
94
+ return env.customUrl
95
+ }
96
+
97
+ return this.environments[env.environment] ?? this.environments[this.defaultConfig.environment]
98
+ }
99
+
100
+ setEnvironment(environment: Keyof<T>) {
101
+ if (!this.isEnabled) return
102
+ this.store.set({ environment })
103
+ }
104
+
105
+ isEnvironment(env: Keyof<T>): boolean {
106
+ const current = this.env
107
+ return current.environment === env
108
+ }
109
+
110
+ setCustomUrl(url: string) {
111
+ if (!url.endsWith('/')) {
112
+ url += '/'
113
+ }
114
+
115
+ if (!this.testUrl(url)) return
116
+
117
+ this.store.set({ customUrl: url })
118
+ }
119
+
120
+ use() {
121
+ const value = this.store.use()
122
+
123
+ const { enabledBy, ...config } = value ?? this.defaultConfig
124
+
125
+ const [customUrl, setCustomUrl] = useState(config?.customUrl)
126
+
127
+ const changed = useMemo(() => !deepEqual(value, this.initialStoreValue), [value])
128
+
129
+ return {
130
+ enabled: !!enabledBy,
131
+ enabledByUser: enabledBy === EDITORS.USER,
132
+ enabledBySystem: enabledBy === EDITORS.SYSTEM,
133
+ config,
134
+ changed,
135
+ customUrl,
136
+ setCustomUrl,
137
+ }
138
+ }
139
+ }
@@ -0,0 +1,4 @@
1
+ export const EDITORS = {
2
+ SYSTEM: 'system',
3
+ USER: 'user',
4
+ } as const
@@ -0,0 +1,7 @@
1
+ import { StringRecord } from '@codeleap/types'
2
+ import { EnvironmentManager } from './class'
3
+ import { EnvironmentManagerConfig } from './types'
4
+
5
+ export function createEnvironmentManager<T extends StringRecord>(config: EnvironmentManagerConfig<T>) {
6
+ return new EnvironmentManager<T>(config)
7
+ }
@@ -0,0 +1,3 @@
1
+ export * from './factor'
2
+ export * from './class'
3
+ export * from './types'
@@ -0,0 +1,330 @@
1
+ import { describe, test, expect, beforeEach } from 'bun:test'
2
+ import { createEnvironmentManager } from './factor'
3
+ import { EnvironmentManager } from './class'
4
+ import { EDITORS } from './const'
5
+
6
+ type TestEnvironments = {
7
+ development: string
8
+ staging: string
9
+ production: string
10
+ custom: string
11
+ }
12
+
13
+ describe('EnvironmentManager', () => {
14
+ let manager: EnvironmentManager<TestEnvironments>
15
+
16
+ const envs = {
17
+ development: 'http://localhost:3000/',
18
+ staging: 'https://staging.example.com/',
19
+ production: 'https://api.example.com/',
20
+ custom: '',
21
+ }
22
+
23
+ const testConfig = {
24
+ environments: envs,
25
+ defaultEnvironment: 'development' as const,
26
+ customEnvironment: 'custom' as const,
27
+ persistKey: 'test-env-manager',
28
+ }
29
+
30
+ beforeEach(() => {
31
+ manager = createEnvironmentManager<TestEnvironments>(testConfig)
32
+ })
33
+
34
+ describe('Initialization', () => {
35
+ test('should create instance with correct config', () => {
36
+ expect(manager).toBeInstanceOf(EnvironmentManager)
37
+ expect(manager.environments).toEqual(testConfig.environments)
38
+ expect(manager.editors).toEqual(EDITORS)
39
+ })
40
+
41
+ test('should have correct default config', () => {
42
+ expect(manager.defaultConfig).toEqual({
43
+ environment: 'development',
44
+ customUrl: '',
45
+ enabledBy: null,
46
+ })
47
+ })
48
+ })
49
+
50
+ describe('URL Validation', () => {
51
+ test('should validate correct HTTP URLs', () => {
52
+ expect(manager.testUrl(envs.development)).toBe(true)
53
+ expect(manager.testUrl('http://example.com/')).toBe(true)
54
+ expect(manager.testUrl('http://192.168.1.1:8080/')).toBe(true)
55
+ })
56
+
57
+ test('should validate correct HTTPS URLs', () => {
58
+ expect(manager.testUrl('https://example.com/')).toBe(true)
59
+ expect(manager.testUrl(envs.production)).toBe(true)
60
+ expect(manager.testUrl('https://sub.domain.example.com:443/')).toBe(true)
61
+ })
62
+
63
+ test('should reject invalid URLs', () => {
64
+ expect(manager.testUrl('ftp://example.com/')).toBe(false)
65
+ expect(manager.testUrl('http://example.com')).toBe(false)
66
+ expect(manager.testUrl('example.com/')).toBe(false)
67
+ expect(manager.testUrl('http:/example.com/')).toBe(false)
68
+ expect(manager.testUrl('')).toBe(false)
69
+ })
70
+ })
71
+
72
+ describe('Enable/Disable functionality', () => {
73
+ test('should start disabled by default', () => {
74
+ expect(manager.isEnabled).toBe(false)
75
+ expect(manager.isEnabledBy).toBeNull()
76
+ })
77
+
78
+ test('should enable by user', () => {
79
+ const result = manager.setEnabled(EDITORS.USER)
80
+ expect(result).toBe(true)
81
+ expect(manager.isEnabled).toBe(true)
82
+ expect(manager.isEnabledByUser).toBe(true)
83
+ expect(manager.isEnabledBySystem).toBe(false)
84
+ })
85
+
86
+ test('should enable by system', () => {
87
+ manager.setEnabled(EDITORS.SYSTEM)
88
+ expect(manager.isEnabled).toBe(true)
89
+ expect(manager.isEnabledBySystem).toBe(true)
90
+ expect(manager.isEnabledByUser).toBe(false)
91
+ })
92
+
93
+ test('should disable when setting null', () => {
94
+ manager.setEnabled(EDITORS.USER)
95
+ expect(manager.isEnabled).toBe(true)
96
+
97
+ const result = manager.setEnabled(false)
98
+ expect(result).toBe(false)
99
+ expect(manager.isEnabled).toBe(false)
100
+ })
101
+ })
102
+
103
+ describe('Environment management', () => {
104
+ beforeEach(() => {
105
+ manager.setEnabled(EDITORS.USER)
106
+ })
107
+
108
+ test('should return default environment when disabled', () => {
109
+ manager.setEnabled(false)
110
+ const env = manager.env
111
+ expect(env.environment).toBe('development')
112
+ })
113
+
114
+ test('should set environment when enabled', () => {
115
+ manager.setEnvironment('staging')
116
+ const env = manager.env
117
+ expect(env.environment).toBe('staging')
118
+ })
119
+
120
+ test('should not set environment when disabled', () => {
121
+ manager.setEnabled(false)
122
+ manager.setEnvironment('production')
123
+ expect(manager.isEnvironment('development')).toBe(true)
124
+ })
125
+
126
+ test('should check current environment correctly', () => {
127
+ manager.setEnvironment('production')
128
+ expect(manager.isEnvironment('production')).toBe(true)
129
+ expect(manager.isEnvironment('staging')).toBe(false)
130
+ })
131
+ })
132
+
133
+ describe('Environment checker (is)', () => {
134
+ beforeEach(() => {
135
+ manager.setEnabled(EDITORS.USER)
136
+ })
137
+
138
+ test('should have correct environment flags for development', () => {
139
+ manager.setEnvironment('development')
140
+ expect(manager.is.Development).toBe(true)
141
+ expect(manager.is.Staging).toBe(false)
142
+ expect(manager.is.Production).toBe(false)
143
+ })
144
+
145
+ test('should have correct environment flags for production', () => {
146
+ manager.setEnvironment('production')
147
+ expect(manager.is.Production).toBe(true)
148
+ expect(manager.is.Development).toBe(false)
149
+ expect(manager.is.Staging).toBe(false)
150
+ })
151
+
152
+ test('should have correct environment flags for staging', () => {
153
+ manager.setEnvironment('staging')
154
+ expect(manager.is.Staging).toBe(true)
155
+ expect(manager.is.Development).toBe(false)
156
+ expect(manager.is.Production).toBe(false)
157
+ })
158
+ })
159
+
160
+ describe('Server URL', () => {
161
+ beforeEach(() => {
162
+ manager.setEnabled(EDITORS.USER)
163
+ })
164
+
165
+ test('should return correct URL for each environment', () => {
166
+ manager.setEnvironment('development')
167
+ expect(manager.serverUrl).toBe(envs.development)
168
+
169
+ manager.setEnvironment('staging')
170
+ expect(manager.serverUrl).toBe(envs.staging)
171
+
172
+ manager.setEnvironment('production')
173
+ expect(manager.serverUrl).toBe(envs.production)
174
+ })
175
+
176
+ test('should return default URL when disabled', () => {
177
+ manager.setEnabled(false)
178
+ expect(manager.serverUrl).toBe(envs.development)
179
+ })
180
+
181
+ test('should return custom URL when set and valid', () => {
182
+ manager.setCustomUrl('https://custom.example.com/')
183
+ manager.setEnvironment('custom')
184
+ expect(manager.serverUrl).toBe('https://custom.example.com/')
185
+ })
186
+ })
187
+
188
+ describe('Custom URL', () => {
189
+ beforeEach(() => {
190
+ manager.setEnabled(EDITORS.USER)
191
+ })
192
+
193
+ test('should set valid custom URL', () => {
194
+ manager.setCustomUrl('https://custom.example.com/')
195
+ manager.setEnvironment('custom')
196
+ expect(manager.serverUrl).toBe('https://custom.example.com/')
197
+ })
198
+
199
+ test('should add trailing slash if missing', () => {
200
+ manager.setCustomUrl('https://custom.example.com')
201
+ manager.setEnvironment('custom')
202
+ expect(manager.serverUrl).toBe('https://custom.example.com/')
203
+ })
204
+
205
+ test('should not set invalid custom URL', () => {
206
+ const initialUrl = manager.env.customUrl
207
+ manager.setCustomUrl('invalid-url')
208
+ expect(manager.env.customUrl).toBe(initialUrl)
209
+ })
210
+
211
+ test('should handle localhost custom URLs', () => {
212
+ manager.setCustomUrl('http://localhost:4000/')
213
+ manager.setEnvironment('custom')
214
+ expect(manager.serverUrl).toBe('http://localhost:4000/')
215
+ })
216
+
217
+ test('should handle IP address custom URLs', () => {
218
+ manager.setCustomUrl('http://192.168.1.100:8080/')
219
+ manager.setEnvironment('custom')
220
+ expect(manager.serverUrl).toBe('http://192.168.1.100:8080/')
221
+ })
222
+ })
223
+
224
+ describe('Factory function', () => {
225
+ test('should create manager instance', () => {
226
+ const newManager = createEnvironmentManager<TestEnvironments>(testConfig)
227
+ expect(newManager).toBeInstanceOf(EnvironmentManager)
228
+ })
229
+
230
+ test('should create independent instances', () => {
231
+ const manager1 = createEnvironmentManager<TestEnvironments>({
232
+ ...testConfig,
233
+ persistKey: 'manager-1',
234
+ })
235
+ const manager2 = createEnvironmentManager<TestEnvironments>({
236
+ ...testConfig,
237
+ persistKey: 'manager-2',
238
+ })
239
+
240
+ manager1.setEnabled(EDITORS.USER)
241
+ manager1.setEnvironment('production')
242
+
243
+ manager2.setEnabled(EDITORS.SYSTEM)
244
+ manager2.setEnvironment('staging')
245
+
246
+ expect(manager1.isEnabledByUser).toBe(true)
247
+ expect(manager2.isEnabledBySystem).toBe(true)
248
+ })
249
+ })
250
+
251
+ describe('EDITORS constant', () => {
252
+ test('should have correct editor values', () => {
253
+ expect(EDITORS.SYSTEM).toBe('system')
254
+ expect(EDITORS.USER).toBe('user')
255
+ })
256
+ })
257
+
258
+ describe('Edge cases', () => {
259
+ test('should handle repeated setEnvironment calls', () => {
260
+ manager.setEnabled(EDITORS.USER)
261
+ manager.setEnvironment('development')
262
+ manager.setEnvironment('staging')
263
+ manager.setEnvironment('production')
264
+
265
+ expect(manager.isEnvironment('production')).toBe(true)
266
+ })
267
+
268
+ test('should handle empty custom URL', () => {
269
+ manager.setEnabled(EDITORS.USER)
270
+ manager.setCustomUrl('')
271
+ expect(manager.testUrl('')).toBe(false)
272
+ })
273
+
274
+ test('should handle switching between enabled states', () => {
275
+ manager.setEnabled(EDITORS.USER)
276
+ expect(manager.isEnabledByUser).toBe(true)
277
+
278
+ manager.setEnabled(EDITORS.SYSTEM)
279
+ expect(manager.isEnabledBySystem).toBe(true)
280
+ expect(manager.isEnabledByUser).toBe(false)
281
+ })
282
+
283
+ test('should handle multiple custom URL changes', () => {
284
+ manager.setEnabled(EDITORS.USER)
285
+
286
+ manager.setCustomUrl('https://custom1.example.com/')
287
+ manager.setEnvironment('custom')
288
+ expect(manager.serverUrl).toBe('https://custom1.example.com/')
289
+
290
+ manager.setCustomUrl('https://custom2.example.com/')
291
+ expect(manager.serverUrl).toBe('https://custom2.example.com/')
292
+ })
293
+ })
294
+
295
+ describe('Integration tests', () => {
296
+ test('should handle full workflow: enable, set env, get URL', () => {
297
+ manager.setEnabled(null)
298
+ expect(manager.isEnabled).toBe(false)
299
+
300
+ manager.setEnabled(EDITORS.USER)
301
+ expect(manager.isEnabled).toBe(true)
302
+
303
+ manager.setEnvironment('production')
304
+ expect(manager.isEnvironment('production')).toBe(true)
305
+
306
+ expect(manager.serverUrl).toBe(envs.production)
307
+ })
308
+
309
+ test('should handle custom URL workflow', () => {
310
+ manager.setEnabled(EDITORS.USER)
311
+
312
+ manager.setCustomUrl('https://my-api.example.com/')
313
+ expect(manager.testUrl('https://my-api.example.com/')).toBe(true)
314
+
315
+ manager.setEnvironment('custom')
316
+ expect(manager.isEnvironment('custom')).toBe(true)
317
+ expect(manager.serverUrl).toBe('https://my-api.example.com/')
318
+ })
319
+
320
+ test('should fallback correctly on invalid custom URL', () => {
321
+ manager.setEnabled(EDITORS.USER)
322
+
323
+ manager.setEnvironment('custom')
324
+ manager.setCustomUrl('https://my-api.example.com/')
325
+ manager.setCustomUrl('not-a-valid-url')
326
+
327
+ expect(manager.serverUrl).toBe('https://my-api.example.com/')
328
+ })
329
+ })
330
+ })
@@ -0,0 +1,17 @@
1
+ import { Keyof, StringRecord, ValueOf } from '@codeleap/types'
2
+ import { EDITORS } from './const'
3
+
4
+ export type Editor = ValueOf<typeof EDITORS> | null
5
+
6
+ export type EnvironmentManagerConfig<T extends StringRecord> = {
7
+ environments: T
8
+ defaultEnvironment: Keyof<T>
9
+ customEnvironment: Keyof<T>
10
+ persistKey?: string
11
+ }
12
+
13
+ export type EnvironmentStore<T extends StringRecord> = {
14
+ environment: Keyof<T>
15
+ customUrl?: string
16
+ enabledBy: Editor
17
+ }
package/src/faker.ts ADDED
@@ -0,0 +1,34 @@
1
+ const names = [
2
+ 'James', 'John', 'Robert', 'Michael', 'William', 'David', 'Richard', 'Joseph', 'Thomas',
3
+ 'Charles', 'Christopher', 'Daniel', 'Matthew', 'Anthony', 'Donald', 'Mark', 'Paul', 'Steven',
4
+ 'Andrew', 'Kenneth', 'Joshua', 'Kevin', 'Brian', 'George', 'Edward', 'Ronald', 'Timothy',
5
+ 'Jason', 'Jeffrey', 'Ryan', 'Gary', 'Jacob'
6
+ ]
7
+
8
+ const surnames = [
9
+ 'Smith', 'Johnson', 'Williams', 'Brown', 'Jones', 'Garcia', 'Miller', 'Davis',
10
+ 'Lopez', 'Wilson', 'Anderson', 'Thomas', 'Taylor', 'Moore',
11
+ 'Jackson', 'Martin', 'Lee', 'Perez', 'Thompson', 'White', 'Harris', 'Sanchez', 'Clark',
12
+ 'Ramirez', 'Lewis', 'Robinson', 'Walker', 'Young', 'Allen', 'King', 'Wright', 'Scott', 'Torres',
13
+ ]
14
+
15
+ const animals = [
16
+ 'lion', 'tiger', 'zebra', 'panda', 'koala', 'bear',
17
+ 'wolf', 'fox', 'rabbit', 'bat', 'spider', 'frog', 'shark'
18
+ ]
19
+
20
+ function getRandom(list: Array<string>) {
21
+ return list[Math.floor(Math.random() * list.length)]
22
+ }
23
+
24
+ function number(min: number = 0, max: number = 100) {
25
+ return Math.floor(Math.random() * (max - min + 1)) + min
26
+ }
27
+
28
+ export const faker = {
29
+ lastName: () => getRandom(surnames),
30
+ firstName: () => getRandom(names),
31
+ animal: () => getRandom(animals),
32
+ number,
33
+ name: () => `${getRandom(names)} ${getRandom(surnames)}`
34
+ }
package/src/index.ts CHANGED
@@ -1 +1,3 @@
1
1
  export * from './FakeRestApi'
2
+ export * from './EnvironmentManager'
3
+ export * from './faker'