@ylsoo/core 1.1.0 → 2.0.1
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/README.md +50 -67
- package/index.d.ts +100 -49
- package/package.json +12 -8
- package/src/core/analytics.js +66 -0
- package/src/core/cache.js +31 -0
- package/src/core/i18n.js +42 -0
- package/src/core/logger.js +39 -0
- package/src/core/state.js +67 -0
- package/src/core/storage.js +99 -0
- package/src/engine/config.js +54 -0
- package/src/engine/features.js +59 -0
- package/src/events/bus.js +69 -0
- package/src/http/client.js +49 -0
- package/src/index.js +68 -0
- package/src/resilience/breaker.js +73 -0
- package/src/router/domRouter.js +131 -0
- package/src/security/crypto.js +67 -0
- package/src/utils/helpers.js +43 -0
- package/src/utils/queue.js +50 -0
- package/src/utils/time.js +46 -0
- package/src/validation/schema.js +53 -0
- package/index.js +0 -202
package/README.md
CHANGED
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
<div align="center">
|
|
2
|
-
<h1>@ylsoo/core</h1>
|
|
3
|
-
<p><b>The
|
|
2
|
+
<h1>@ylsoo/core (v2.2 Enterprise)</h1>
|
|
3
|
+
<p><b>The absolute standard in enterprise SDK architecture.</b></p>
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/@ylsoo/core)
|
|
6
6
|
[](https://opensource.org/licenses/MIT)
|
|
7
7
|
[](https://www.typescriptlang.org/)
|
|
8
|
+
[](#)
|
|
8
9
|
</div>
|
|
9
10
|
|
|
10
11
|
<hr>
|
|
11
12
|
|
|
12
13
|
## 🚀 Overview
|
|
13
14
|
|
|
14
|
-
`@ylsoo/core`
|
|
15
|
+
`@ylsoo/core` v2.2 introduces tier-1 routing capabilities and highly rigorous A/B target evaluation. Everything operates entirely on native standard technologies, avoiding the devastating payload bloat of common frameworks.
|
|
15
16
|
|
|
16
17
|
## 📦 Installation
|
|
17
18
|
|
|
@@ -19,88 +20,70 @@
|
|
|
19
20
|
npm i @ylsoo/core
|
|
20
21
|
```
|
|
21
22
|
|
|
22
|
-
|
|
23
|
+
## 🛠️ V2.2 Enterprise Architecture Models
|
|
23
24
|
|
|
24
|
-
|
|
25
|
+
### 1. 🛣️ Professional DOM Router (`ylsoo.router`)
|
|
26
|
+
An incredibly capable HTML5 Frontend matcher mimicking heavily advanced Vue/React dynamics.
|
|
25
27
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
+
- **Dynamic Constraints**: Extracts `/:id/` params cleanly into variables.
|
|
29
|
+
- **Middleware Control**: Provides `beforeEach()` to easily intercept and blockade navigations dynamically.
|
|
30
|
+
- **Click Hijacking**: Automatically binds to HTML5 PushState and catches `data-route` click events!
|
|
28
31
|
|
|
29
32
|
```javascript
|
|
30
|
-
|
|
33
|
+
// 1. Build an Authentication Middleware Guard
|
|
34
|
+
ylsoo.router.beforeEach((to, from, next) => {
|
|
35
|
+
if (to.includes('admin') && !loggedIn) next(false); // Halt routing
|
|
36
|
+
});
|
|
31
37
|
|
|
32
|
-
|
|
33
|
-
ylsoo.
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
### 2. Ylsoo Request Engine (`http`)
|
|
39
|
-
A robust wrapper around the native `fetch` API. It automatically handles stringifying bodies, applying correct headers, parsing JSON, and safely throwing formatted errors.
|
|
40
|
-
|
|
41
|
-
```javascript
|
|
42
|
-
const ylsoo = require('@ylsoo/core');
|
|
43
|
-
|
|
44
|
-
async function fetchUserData() {
|
|
45
|
-
try {
|
|
46
|
-
const data = await ylsoo.http.get('https://api.ylsoo.com/v1/user/me');
|
|
47
|
-
console.log(data);
|
|
48
|
-
} catch (error) {
|
|
49
|
-
ylsoo.logger.error('Failed to fetch user', error);
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
### 3. Ylsoo Memory Cache (`cache`)
|
|
55
|
-
A fast, volatile in-memory key/value store with Time-To-Live (TTL) functionality.
|
|
56
|
-
|
|
57
|
-
```javascript
|
|
58
|
-
const ylsoo = require('@ylsoo/core');
|
|
59
|
-
|
|
60
|
-
// Cache a complex object for 5 seconds (5000 ms)
|
|
61
|
-
ylsoo.cache.set('session_token', { id: 123 }, 5000);
|
|
38
|
+
// 2. Configure Dynamic Deep Routes
|
|
39
|
+
ylsoo.router.add('/users/:id/billing', (route) => {
|
|
40
|
+
console.log('Loading Billing For User:', route.params.id);
|
|
41
|
+
});
|
|
62
42
|
|
|
63
|
-
|
|
64
|
-
const session = ylsoo.cache.get('session_token');
|
|
65
|
-
|
|
66
|
-
// Will return null after 5 seconds
|
|
67
|
-
setTimeout(() => {
|
|
68
|
-
console.log(ylsoo.cache.get('session_token')); // null
|
|
69
|
-
}, 5100);
|
|
43
|
+
ylsoo.router.start();
|
|
70
44
|
```
|
|
71
45
|
|
|
72
|
-
###
|
|
46
|
+
### 2. 🎛️ Deterministic A/B Flags (`ylsoo.feature`)
|
|
47
|
+
Not just a switch. A true matrix evaluation engine utilizing cryptographic hashing to guarantee specific users continuously load the exact same "random" feature buckets.
|
|
73
48
|
|
|
74
|
-
#### `sleep(ms)`
|
|
75
|
-
Promise-based execution halter.
|
|
76
49
|
```javascript
|
|
77
|
-
|
|
50
|
+
// Force the dashboard to turn on ONLY for:
|
|
51
|
+
// 1) Exactly 25% of traffic
|
|
52
|
+
// 2) Users whose attributes dictate they live in the UK
|
|
53
|
+
ylsoo.feature.setFlag('beta_dashboard', {
|
|
54
|
+
enabled: true,
|
|
55
|
+
rolloutPercentage: 25,
|
|
56
|
+
conditions: { country: 'UK' }
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
// Execution check during login
|
|
60
|
+
const userContext = { userId: "uuid-123", attributes: { country: 'UK' }};
|
|
61
|
+
|
|
62
|
+
if (await ylsoo.feature.evaluate('beta_dashboard', userContext)) {
|
|
63
|
+
console.log('User Granted Beta Access');
|
|
64
|
+
}
|
|
78
65
|
```
|
|
79
66
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
const copy = ylsoo.deepClone(complexObject);
|
|
84
|
-
```
|
|
67
|
+
### 3. 🛡️ Resilience Core (`ylsoo.resilience`)
|
|
68
|
+
- **`withRetry(fn)`**: Wraps flaky API calls and repeats them on an exponential delay loop (e.g. 1s... 2s... 4s...) rather than immediately panicking.
|
|
69
|
+
- **`createBreaker(fn)`**: Operates a Circuit Breaker algorithm to instantly cut network traffic down if an upstream service goes offline, protecting your node instances from dead-locking.
|
|
85
70
|
|
|
86
|
-
|
|
87
|
-
|
|
71
|
+
### 4. 🕐 Precise Time Formatter (`ylsoo.time`)
|
|
72
|
+
Completely strips the need for massive `moment.js` dependency packets. Quickly parses standard intervals.
|
|
88
73
|
```javascript
|
|
89
|
-
|
|
74
|
+
ylsoo.time.format(Date.now(), "YYYY-MM-DD HH:mm:ss");
|
|
75
|
+
ylsoo.time.timeAgo(oldDate); // "5 minutes ago"
|
|
90
76
|
```
|
|
91
77
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
```javascript
|
|
95
|
-
ylsoo.isEmpty({}); // true
|
|
96
|
-
ylsoo.isEmpty([]); // true
|
|
97
|
-
ylsoo.isEmpty("hello"); // false
|
|
98
|
-
```
|
|
78
|
+
### 5. 🗄️ Strict Config Matrix (`ylsoo.config`)
|
|
79
|
+
A deep-merging configurations orchestrator. Implements `.freeze()` architecture to physically lock constants into memory, violently blocking any injected bad code downstream from maliciously altering your `API_KEYS`.
|
|
99
80
|
|
|
100
|
-
|
|
101
|
-
|
|
81
|
+
### 6. 🗃️ Concurrency Queue (`ylsoo.createQueue`)
|
|
82
|
+
Need thousands of promises to compute, but executing them simultaneously throttles your CPU? Assign them to a limit.
|
|
102
83
|
```javascript
|
|
103
|
-
ylsoo.
|
|
84
|
+
const q = ylsoo.createQueue(5); // Throttle process to exactly 5 lanes
|
|
85
|
+
q.add(() => fetchBigData1());
|
|
86
|
+
q.add(() => fetchBigData2());
|
|
104
87
|
```
|
|
105
88
|
|
|
106
89
|
---
|
package/index.d.ts
CHANGED
|
@@ -1,89 +1,140 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @ylsoo/core Type Definitions
|
|
2
|
+
* @ylsoo/core v2.2.0 Type Definitions
|
|
3
|
+
* Enterprise Cross-Platform SDK
|
|
3
4
|
*/
|
|
4
5
|
|
|
5
6
|
declare module '@ylsoo/core' {
|
|
6
7
|
|
|
8
|
+
// --- Base Systems ---
|
|
7
9
|
export class YlsooLogger {
|
|
8
|
-
/** Logs an informational message */
|
|
9
10
|
info(message: string | any): void;
|
|
10
|
-
/** Logs a success message */
|
|
11
11
|
success(message: string | any): void;
|
|
12
|
-
/** Logs a warning message */
|
|
13
12
|
warn(message: string | any): void;
|
|
14
|
-
/** Logs an error message */
|
|
15
13
|
error(message: string | any): void;
|
|
16
14
|
}
|
|
17
15
|
|
|
18
16
|
export class YlsooCache {
|
|
19
|
-
/**
|
|
20
|
-
* Set a key-value pair in memory
|
|
21
|
-
* @param key String identifier
|
|
22
|
-
* @param value The raw value to store
|
|
23
|
-
* @param ttlMs Time to live in milliseconds (0 for infinite)
|
|
24
|
-
*/
|
|
25
17
|
set(key: string, value: any, ttlMs?: number): void;
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Retrieve a value from the cache. Returns null if expired or missing.
|
|
29
|
-
*/
|
|
30
18
|
get<T = any>(key: string): T | null;
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Delete a specific key from the cache
|
|
34
|
-
*/
|
|
35
19
|
delete(key: string): boolean;
|
|
20
|
+
clear(): void;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export class YlsooHttp {
|
|
24
|
+
request<T = any>(endpoint: string, options?: any): Promise<T>;
|
|
25
|
+
get<T = any>(endpoint: string, headers?: any): Promise<T>;
|
|
26
|
+
post<T = any>(endpoint: string, body: any, headers?: any): Promise<T>;
|
|
27
|
+
}
|
|
36
28
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
29
|
+
// --- Security & Process ---
|
|
30
|
+
export class YlsooCrypto {
|
|
31
|
+
encodeBase64(str: string): string;
|
|
32
|
+
decodeBase64(base64: string): string;
|
|
33
|
+
hash(str: string): Promise<string>;
|
|
34
|
+
uuid(): string;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export class YlsooEventBus {
|
|
38
|
+
on(event: string, callback: (data?: any) => void): () => void;
|
|
39
|
+
off(event: string, callback: (data?: any) => void): void;
|
|
40
|
+
emit(event: string, data?: any): void;
|
|
41
|
+
once(event: string, callback: (data?: any) => void): void;
|
|
40
42
|
clear(): void;
|
|
41
43
|
}
|
|
42
44
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
45
|
+
// --- State & Config ---
|
|
46
|
+
export class YlsooState {
|
|
47
|
+
setup(key: string, initialValue?: any): void;
|
|
48
|
+
get<T = any>(key: string): T;
|
|
49
|
+
set(key: string, value: any): void;
|
|
50
|
+
subscribe<T = any>(key: string, callback: (val: T) => void): () => void;
|
|
48
51
|
}
|
|
49
52
|
|
|
50
|
-
export class
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
53
|
+
export class YlsooStorage {
|
|
54
|
+
set(key: string, value: any): void;
|
|
55
|
+
get<T = any>(key: string): T | null;
|
|
56
|
+
remove(key: string): void;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export class YlsooConfig {
|
|
60
|
+
setDefault(defaults: Record<string, any>): void;
|
|
61
|
+
applyEnvironment(envOverrides: Record<string, any>): void;
|
|
62
|
+
freeze(): void;
|
|
63
|
+
get<T = any>(key?: string | null): T;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export class YlsooFeatureFlags {
|
|
67
|
+
setFlag(flagName: string, rules: { enabled?: boolean, rolloutPercentage?: number, conditions?: Record<string, any> }): void;
|
|
68
|
+
evaluate(flagName: string, context?: { userId?: string, attributes?: Record<string, any> }): Promise<boolean>;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// --- Flow & Routers ---
|
|
72
|
+
export class YlsooRouter {
|
|
73
|
+
beforeEach(hook: (to: string, from: string, next: (confirm?: boolean) => void) => void): void;
|
|
74
|
+
add(path: string, handler: (route: { params: any, query: any, path: string }) => void): void;
|
|
75
|
+
setFallback(handler: (route: { path: string, query: any }) => void): void;
|
|
76
|
+
start(): void;
|
|
77
|
+
push(path: string): void;
|
|
59
78
|
}
|
|
60
79
|
|
|
80
|
+
export class YlsooResilience {
|
|
81
|
+
withRetry<T>(asyncFunction: () => Promise<T>, maxRetries?: number, baseDelayMs?: number): Promise<T>;
|
|
82
|
+
createBreaker<T>(asyncFunction: (...args: any[]) => Promise<T>, options?: any): (...args: any[]) => Promise<T>;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export class YlsooTaskQueue {
|
|
86
|
+
constructor(concurrencyLimit?: number);
|
|
87
|
+
add<T>(asyncTask: () => Promise<T>): Promise<T>;
|
|
88
|
+
get remaining(): number;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// --- Micro Utils ---
|
|
92
|
+
export class YlsooAnalytics {
|
|
93
|
+
configure(options: any): void;
|
|
94
|
+
track(eventName: string, metadata?: any): void;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export class Ylsoo18n {
|
|
98
|
+
load(locale: string, dict: Record<string, string>): void;
|
|
99
|
+
setLocale(locale: string): void;
|
|
100
|
+
t(key: string, variables?: Record<string, string>): string;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export class YlsooTime {
|
|
104
|
+
format(date: Date | number | string, formatStr?: string): string | null;
|
|
105
|
+
timeAgo(date: Date | number | string): string;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// --- Main Export ---
|
|
61
109
|
export class YlsooCore {
|
|
62
110
|
version: string;
|
|
63
111
|
|
|
64
|
-
|
|
65
|
-
|
|
112
|
+
crypto: YlsooCrypto;
|
|
113
|
+
config: YlsooConfig;
|
|
114
|
+
router: YlsooRouter;
|
|
115
|
+
feature: YlsooFeatureFlags;
|
|
66
116
|
|
|
67
|
-
|
|
117
|
+
state: YlsooState;
|
|
68
118
|
cache: YlsooCache;
|
|
119
|
+
storage: YlsooStorage;
|
|
69
120
|
|
|
70
|
-
/** The official Ylsoo request engine */
|
|
71
121
|
http: YlsooHttp;
|
|
122
|
+
events: YlsooEventBus;
|
|
123
|
+
resilience: YlsooResilience;
|
|
124
|
+
analytics: YlsooAnalytics;
|
|
125
|
+
|
|
126
|
+
logger: YlsooLogger;
|
|
127
|
+
time: YlsooTime;
|
|
128
|
+
i18n: Ylsoo18n;
|
|
72
129
|
|
|
73
|
-
/** Halt execution for a set duration */
|
|
74
130
|
sleep(ms: number): Promise<void>;
|
|
75
|
-
|
|
76
|
-
/** Safely deep clone an object */
|
|
77
131
|
deepClone<T>(obj: T): T;
|
|
78
|
-
|
|
79
|
-
/** Creates a debounced version of a function */
|
|
80
132
|
debounce<T extends (...args: any[]) => any>(func: T, wait: number): (...args: Parameters<T>) => void;
|
|
81
|
-
|
|
82
|
-
/** Check if an array, string, object, map, or set is semantically empty */
|
|
83
133
|
isEmpty(value: any): boolean;
|
|
84
|
-
|
|
85
|
-
/** Capitalizes the first letter of a string */
|
|
86
134
|
capitalize(str: string): string;
|
|
135
|
+
|
|
136
|
+
validate(data: any, schema: Record<string, any>): boolean;
|
|
137
|
+
createQueue(limit?: number): YlsooTaskQueue;
|
|
87
138
|
}
|
|
88
139
|
|
|
89
140
|
const ylsoo: YlsooCore;
|
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ylsoo/core",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "
|
|
5
|
-
"main": "index.js",
|
|
3
|
+
"version": "2.0.1",
|
|
4
|
+
"description": "Enterprise cross-platform SDK for Ylsoo projects",
|
|
5
|
+
"main": "src/index.js",
|
|
6
6
|
"types": "index.d.ts",
|
|
7
7
|
"files": [
|
|
8
|
-
"
|
|
8
|
+
"src/",
|
|
9
9
|
"index.d.ts",
|
|
10
10
|
"README.md"
|
|
11
11
|
],
|
|
@@ -15,11 +15,15 @@
|
|
|
15
15
|
"keywords": [
|
|
16
16
|
"ylsoo",
|
|
17
17
|
"core",
|
|
18
|
-
"
|
|
18
|
+
"enterprise",
|
|
19
19
|
"sdk",
|
|
20
|
-
"
|
|
21
|
-
"
|
|
22
|
-
"
|
|
20
|
+
"router",
|
|
21
|
+
"feature-flags",
|
|
22
|
+
"ab-testing",
|
|
23
|
+
"circuit-breaker",
|
|
24
|
+
"retry",
|
|
25
|
+
"queue",
|
|
26
|
+
"time"
|
|
23
27
|
],
|
|
24
28
|
"author": "Ylsoo",
|
|
25
29
|
"license": "MIT",
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
class YlsooAnalytics {
|
|
2
|
+
constructor(httpEngine) {
|
|
3
|
+
this.http = httpEngine;
|
|
4
|
+
this.queue = [];
|
|
5
|
+
this.config = {
|
|
6
|
+
endpoint: null, // User must set this via configure()
|
|
7
|
+
batchSize: 10, // Flush after 10 events
|
|
8
|
+
flushInterval: 5000 // Or flush every 5 seconds
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
this.timer = null;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
configure(options) {
|
|
15
|
+
this.config = { ...this.config, ...options };
|
|
16
|
+
this._startTimer();
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Silently track an event
|
|
21
|
+
* @param {string} eventName
|
|
22
|
+
* @param {object} metadata
|
|
23
|
+
*/
|
|
24
|
+
track(eventName, metadata = {}) {
|
|
25
|
+
this.queue.push({
|
|
26
|
+
event: eventName,
|
|
27
|
+
data: metadata,
|
|
28
|
+
timestamp: new Date().toISOString()
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
if (this.queue.length >= this.config.batchSize) {
|
|
32
|
+
this.flush();
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async flush() {
|
|
37
|
+
if (this.queue.length === 0 || !this.config.endpoint) return;
|
|
38
|
+
|
|
39
|
+
const payload = [...this.queue];
|
|
40
|
+
this.queue = []; // clear early to prevent race conditions
|
|
41
|
+
|
|
42
|
+
try {
|
|
43
|
+
// Intentionally suppress errors to stay silent
|
|
44
|
+
await this.http.post(this.config.endpoint, { events: payload });
|
|
45
|
+
} catch(err) {
|
|
46
|
+
// Re-queue on fail
|
|
47
|
+
this.queue = [...payload, ...this.queue];
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
_startTimer() {
|
|
52
|
+
if (this.timer) clearInterval(this.timer);
|
|
53
|
+
|
|
54
|
+
// In Node or Browser, setInterval is standard
|
|
55
|
+
this.timer = setInterval(() => {
|
|
56
|
+
this.flush();
|
|
57
|
+
}, this.config.flushInterval);
|
|
58
|
+
|
|
59
|
+
// In node, unref the timer to prevent keeping the process alive
|
|
60
|
+
if (typeof this.timer.unref === 'function') {
|
|
61
|
+
this.timer.unref();
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
module.exports = { YlsooAnalytics };
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
class YlsooCache {
|
|
2
|
+
constructor() {
|
|
3
|
+
this.store = new Map();
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
set(key, value, ttlMs = 0) {
|
|
7
|
+
const expiresAt = ttlMs > 0 ? Date.now() + ttlMs : null;
|
|
8
|
+
this.store.set(key, { value, expiresAt });
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
get(key) {
|
|
12
|
+
const item = this.store.get(key);
|
|
13
|
+
if (!item) return null;
|
|
14
|
+
|
|
15
|
+
if (item.expiresAt && Date.now() > item.expiresAt) {
|
|
16
|
+
this.store.delete(key);
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
return item.value;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
delete(key) {
|
|
23
|
+
return this.store.delete(key);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
clear() {
|
|
27
|
+
this.store.clear();
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
module.exports = { YlsooCache };
|
package/src/core/i18n.js
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
class Ylsoo18n {
|
|
2
|
+
constructor() {
|
|
3
|
+
this.dictionary = {};
|
|
4
|
+
this.locale = 'en';
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Load a language dictionary into memory
|
|
9
|
+
* @param {string} locale
|
|
10
|
+
* @param {object} dict
|
|
11
|
+
*/
|
|
12
|
+
load(locale, dict) {
|
|
13
|
+
this.dictionary[locale] = dict;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
setLocale(locale) {
|
|
17
|
+
this.locale = locale;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Translate a key and interpolate variables
|
|
22
|
+
* @param {string} key
|
|
23
|
+
* @param {object} variables
|
|
24
|
+
* @returns {string}
|
|
25
|
+
*/
|
|
26
|
+
t(key, variables = {}) {
|
|
27
|
+
const dict = this.dictionary[this.locale];
|
|
28
|
+
if (!dict || !dict[key]) return key; // Fallback to raw key
|
|
29
|
+
|
|
30
|
+
let translation = dict[key];
|
|
31
|
+
|
|
32
|
+
// Interpolate: "Welcome {name}" -> "Welcome Admin"
|
|
33
|
+
Object.keys(variables).forEach(varKey => {
|
|
34
|
+
const regex = new RegExp(`{${varKey}}`, 'g');
|
|
35
|
+
translation = translation.replace(regex, variables[varKey]);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
return translation;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
module.exports = { Ylsoo18n };
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
class YlsooLogger {
|
|
2
|
+
constructor() {
|
|
3
|
+
this.colors = {
|
|
4
|
+
reset: "\x1b[0m",
|
|
5
|
+
info: "\x1b[36m", // Cyan
|
|
6
|
+
success: "\x1b[32m", // Green
|
|
7
|
+
warn: "\x1b[33m", // Yellow
|
|
8
|
+
error: "\x1b[31m", // Red
|
|
9
|
+
ylsoo: "\x1b[35m" // Magenta
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
_format(level, message, color) {
|
|
14
|
+
// Only use colors in Node.js, not browsers. Check process briefly.
|
|
15
|
+
const isNode = typeof process !== 'undefined' && process.versions && process.versions.node;
|
|
16
|
+
if (isNode) {
|
|
17
|
+
return `${this.colors.ylsoo}[Ylsoo]${this.colors.reset} ${color}[${level.toUpperCase()}]${this.colors.reset} ${message}`;
|
|
18
|
+
}
|
|
19
|
+
return `[Ylsoo] [${level.toUpperCase()}] ${message}`;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
info(message) {
|
|
23
|
+
console.log(this._format('info', message, this.colors.info));
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
success(message) {
|
|
27
|
+
console.log(this._format('success', message, this.colors.success));
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
warn(message) {
|
|
31
|
+
console.warn(this._format('warn', message, this.colors.warn));
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
error(message) {
|
|
35
|
+
console.error(this._format('error', message, this.colors.error));
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
module.exports = { YlsooLogger };
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
class YlsooState {
|
|
2
|
+
constructor() {
|
|
3
|
+
this.state = {};
|
|
4
|
+
this.listeners = new Map();
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Initializes or updates a specific state key
|
|
9
|
+
* @param {string} key
|
|
10
|
+
* @param {*} initialValue
|
|
11
|
+
*/
|
|
12
|
+
setup(key, initialValue = null) {
|
|
13
|
+
if (!(key in this.state)) {
|
|
14
|
+
this.state[key] = initialValue;
|
|
15
|
+
this.listeners.set(key, new Set());
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Retrieves the current value of a state key
|
|
21
|
+
* @param {string} key
|
|
22
|
+
*/
|
|
23
|
+
get(key) {
|
|
24
|
+
return this.state[key];
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Set a new value and trigger all subscribers
|
|
29
|
+
* @param {string} key
|
|
30
|
+
* @param {*} value
|
|
31
|
+
*/
|
|
32
|
+
set(key, value) {
|
|
33
|
+
this.setup(key); // Ensure setup
|
|
34
|
+
|
|
35
|
+
// Only trigger if actually changed to prevent render loops
|
|
36
|
+
if (this.state[key] !== value) {
|
|
37
|
+
this.state[key] = value;
|
|
38
|
+
this.listeners.get(key).forEach(callback => {
|
|
39
|
+
try {
|
|
40
|
+
callback(value);
|
|
41
|
+
} catch (e) {
|
|
42
|
+
console.error(`[Ylsoo State] Error in listener for ${key}`, e);
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Subscribe to changes on a specific key
|
|
50
|
+
* @param {string} key
|
|
51
|
+
* @param {Function} callback
|
|
52
|
+
* @returns {Function} Unsubscribe method
|
|
53
|
+
*/
|
|
54
|
+
subscribe(key, callback) {
|
|
55
|
+
this.setup(key);
|
|
56
|
+
this.listeners.get(key).add(callback);
|
|
57
|
+
|
|
58
|
+
// Provide initial state immediately upon subscription
|
|
59
|
+
callback(this.state[key]);
|
|
60
|
+
|
|
61
|
+
return () => {
|
|
62
|
+
this.listeners.get(key).delete(callback);
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
module.exports = { YlsooState };
|