comes 0.0.3 → 0.0.4

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/CLAUDE.md ADDED
@@ -0,0 +1,35 @@
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
+
5
+ ## Project Overview
6
+
7
+ **comes** is a TypeScript event system library for simple pub/sub communication. It provides an `EventSystem` class with address-based events, lazy loaders, and interceptors.
8
+
9
+ ## Commands
10
+
11
+ - **Build:** `npm run build` (cleans `build/` then runs `tsc`)
12
+ - **Test:** `npm test` (runs vitest with coverage enabled by default)
13
+ - **Run single test:** `npx vitest run -t "Test name"` or `npx vitest run test/simple-emit.test.ts`
14
+
15
+ ## Architecture
16
+
17
+ Single-file library (`src/index.ts`) exporting:
18
+
19
+ - **`EventSystem`** class — the core. Manages a `data` map (keyed by string "address") where each address holds an `ES_ValueType` containing the last value, listeners array, optional loader, and loader error handler.
20
+ - **`es`** — a default singleton `EventSystem` instance exported for convenience.
21
+
22
+ Key concepts:
23
+ - **Addresses** are string keys identifying event channels.
24
+ - **`send(id, event)`** — async; runs interceptors (chain of responsibility), then notifies all listeners. Both interceptors and listeners are awaited sequentially.
25
+ - **`listen(id, listener)`** — registers a listener. If a value was already sent (has a `date`), the listener is immediately called with the cached `last` value. If no value exists but a loader is configured, the loader is triggered. Returns an unregister function.
26
+ - **Interceptors** (`addInter`) — transform/validate events before they reach listeners. The empty string `""` address matches all events. Throwing in an interceptor stops the chain and prevents listener notification.
27
+ - **Loaders** (`setLoader`) — lazy data fetchers invoked when the first listener registers on an address that has no value yet. `loaderCatch` handles loader errors and can optionally return a fallback value.
28
+
29
+ ## Build Output
30
+
31
+ TypeScript compiles from `src/` to `build/` (ES2017, Node16 modules). Declarations (`.d.ts`) are generated. Entry point: `build/index.js`.
32
+
33
+ ## Test Structure
34
+
35
+ Tests are in `test/` using vitest. A single test file (`test/simple-emit.test.ts`) covers: send/get, listen before/after emit, unregistering, listener errors, loader lifecycle (with/without error handlers), and interceptor chains.
package/README.md CHANGED
@@ -1,3 +1,272 @@
1
1
  # comes - Communication Event System
2
2
 
3
+ A simple, lightweight event system for TypeScript/JavaScript applications. It provides address-based pub/sub communication with built-in value caching, lazy loaders, and interceptors.
3
4
 
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install comes
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ```ts
14
+ import { es } from 'comes';
15
+
16
+ // Send a value to an address
17
+ await es.send('user/name', 'Alice');
18
+
19
+ // Listen for values on an address
20
+ const unregister = es.listen('user/name', (value) => {
21
+ console.log(value); // "Alice" (immediately, from cache)
22
+ });
23
+
24
+ // Stop listening
25
+ unregister();
26
+ ```
27
+
28
+ ## Core Concepts
29
+
30
+ - **Address**: A string key that identifies an event channel (e.g. `'user/name'`, `'app/status'`).
31
+ - **Value caching**: The last value sent to each address is cached. When a new listener registers, it immediately receives the cached value.
32
+ - **Interceptors**: Middleware functions that can transform, validate, or monitor values before they reach listeners.
33
+ - **Loaders**: Lazy data fetchers that run automatically when the first listener registers on an address that has no value yet.
34
+
35
+ ## API
36
+
37
+ ### Importing
38
+
39
+ ```ts
40
+ // Use the default singleton instance
41
+ import { es } from 'comes';
42
+
43
+ // Or create your own isolated instance
44
+ import { EventSystem } from 'comes';
45
+ const es = new EventSystem();
46
+ ```
47
+
48
+ ---
49
+
50
+ ### `send<T>(id: string, event: T): Promise<T>`
51
+
52
+ Sends a value to all listeners of the given address. Interceptors are executed first (in order), then each listener is called with the resulting value. The final value (after interceptors) is cached.
53
+
54
+ Returns the value after interceptor transformations.
55
+
56
+ ```ts
57
+ await es.send('counter', 1);
58
+ await es.send('user/profile', { name: 'Alice', age: 30 });
59
+ ```
60
+
61
+ If an interceptor or listener throws an error, `send` will throw that error.
62
+
63
+ ---
64
+
65
+ ### `listen(id: string, listener: EventListenerType): () => void`
66
+
67
+ Registers a listener on the given address. Returns an **unregister function** to remove the listener later.
68
+
69
+ **Behavior on registration:**
70
+ - If a value was already sent to this address, the listener is **immediately called** with the cached value.
71
+ - If no value exists but a **loader** is configured, the loader is triggered to fetch the initial value.
72
+ - If neither exists, the listener simply waits for the next `send`.
73
+
74
+ ```ts
75
+ // Basic usage
76
+ const unregister = es.listen('counter', (value) => {
77
+ console.log('Counter:', value);
78
+ });
79
+
80
+ // Later, stop listening
81
+ unregister();
82
+ ```
83
+
84
+ ---
85
+
86
+ ### `unlisten(id: string, listener: EventListenerType): void`
87
+
88
+ Removes a specific listener from the given address. This is useful when you need to unregister **inside** the listener itself, where the unregister function returned by `listen` may not be assigned yet.
89
+
90
+ ```ts
91
+ const listener = (value: string) => {
92
+ es.unlisten('my-event', listener); // safe to call here
93
+ console.log('Received once:', value);
94
+ };
95
+ es.listen('my-event', listener);
96
+ ```
97
+
98
+ ---
99
+
100
+ ### `get(id: string): ES_ValueType`
101
+
102
+ Returns a reference to the internal state object for the given address. Useful for inspecting the current cached value, the listener list, or the loader state.
103
+
104
+ ```ts
105
+ await es.send('status', 'online');
106
+
107
+ const data = es.get('status');
108
+ console.log(data.last); // "online"
109
+ console.log(data.date); // Date when last value was sent
110
+ console.log(data.listeners); // Array of registered listeners
111
+ ```
112
+
113
+ **`ES_ValueType` fields:**
114
+
115
+ | Field | Type | Description |
116
+ |---|---|---|
117
+ | `last` | `any` | The last value sent to this address |
118
+ | `listeners` | `EventListenerType[]` | Array of registered listeners |
119
+ | `date` | `Date \| undefined` | Timestamp of the last `send`. `undefined` means no value was sent yet |
120
+ | `loader` | `Function \| undefined` | The loader function, if configured |
121
+ | `loaderCatch` | `Function \| undefined` | The loader error handler, if configured |
122
+ | `loaderProm` | `Promise \| undefined` | The loader promise, while the loader is running |
123
+
124
+ ---
125
+
126
+ ### `addInter(id: string, interceptor: EventInterceptorType): () => void`
127
+
128
+ Adds an interceptor for the given address. Interceptors are called **before** listeners, in the order they were added. Each interceptor receives the value, can transform it, and must return the (possibly modified) value for the next interceptor in the chain.
129
+
130
+ Returns an **unregister function** to remove the interceptor.
131
+
132
+ **Special address `""`:** An interceptor registered with an empty string `""` as the address is called for **every** event on the `EventSystem`, regardless of address.
133
+
134
+ ```ts
135
+ // Transform values before listeners receive them
136
+ es.addInter('counter', async (id, event, es) => {
137
+ return event * 2; // double the value
138
+ });
139
+
140
+ es.listen('counter', (value) => {
141
+ console.log(value); // 20
142
+ });
143
+
144
+ await es.send('counter', 10);
145
+ ```
146
+
147
+ **Interceptor chain:**
148
+
149
+ ```ts
150
+ es.addInter('price', async (id, event, es) => {
151
+ return event + 1; // first: add 1
152
+ });
153
+
154
+ es.addInter('price', async (id, event, es) => {
155
+ return event * 10; // second: multiply by 10
156
+ });
157
+
158
+ await es.send('price', 5); // (5 + 1) * 10 = 60
159
+ ```
160
+
161
+ **Global interceptor (logging example):**
162
+
163
+ ```ts
164
+ es.addInter('', async (id, event, es) => {
165
+ console.log(`[${id}]`, event);
166
+ return event; // pass through unchanged
167
+ });
168
+ ```
169
+
170
+ If an interceptor throws an error, the chain stops and listeners are **not** called.
171
+
172
+ ---
173
+
174
+ ### `removeInter(id: string, interceptor: EventInterceptorType): void`
175
+
176
+ Removes a specific interceptor from the given address.
177
+
178
+ ```ts
179
+ const interceptor = async (id: string, event: number, es: EventSystem) => {
180
+ return event + 1;
181
+ };
182
+
183
+ es.addInter('counter', interceptor);
184
+ es.removeInter('counter', interceptor);
185
+ ```
186
+
187
+ ---
188
+
189
+ ### `setLoader(id: string, loader, loaderCatch?): void`
190
+
191
+ Configures a **loader** function for the given address. The loader is a lazy data fetcher that runs automatically when:
192
+
193
+ 1. The first listener registers on an address, **and**
194
+ 2. No value has been sent to that address yet.
195
+
196
+ If a value was already sent before the first listener, the loader is **never** called.
197
+
198
+ The loader function receives the address `id` as the first argument, and should call `es.send(id, value)` to deliver the loaded value.
199
+
200
+ ```ts
201
+ es.setLoader('user/profile', async (id) => {
202
+ const response = await fetch('/api/profile');
203
+ const data = await response.json();
204
+ es.send(id, data);
205
+ });
206
+
207
+ // The loader runs automatically when the first listener registers
208
+ es.listen('user/profile', (profile) => {
209
+ console.log(profile); // data from API
210
+ });
211
+ ```
212
+
213
+ **With error handler (`loaderCatch`):**
214
+
215
+ The optional third argument is an error handler invoked when the loader throws. It has three possible behaviors:
216
+
217
+ - **Return a value**: The loader resolves successfully with that value (sent to listeners).
218
+ - **Return `undefined`** (or no return): The loader rejects with the original error.
219
+ - **Throw a new error**: The loader rejects with the new error, and the original error is attached as `error.loaderEx`.
220
+
221
+ ```ts
222
+ es.setLoader(
223
+ 'user/profile',
224
+ async (id) => {
225
+ throw new Error('Network error');
226
+ },
227
+ async (id, error) => {
228
+ console.warn('Loader failed:', error.message);
229
+ return { name: 'Guest' }; // fallback value sent to listeners
230
+ }
231
+ );
232
+ ```
233
+
234
+ ---
235
+
236
+ ### `setLoaderCatch(id: string, loaderCatch): void`
237
+
238
+ Sets (or replaces) the error handler for the loader of a given address, independently from `setLoader`.
239
+
240
+ ```ts
241
+ es.setLoaderCatch('user/profile', async (id, error) => {
242
+ return { name: 'Default' }; // fallback value
243
+ });
244
+ ```
245
+
246
+ ---
247
+
248
+ ### `load<T>(id: string, ...args: any[]): Promise<T>`
249
+
250
+ Manually triggers the loader for the given address. Extra arguments are forwarded to the loader function.
251
+
252
+ If no loader is configured, returns the current cached value immediately.
253
+
254
+ ```ts
255
+ es.setLoader('data', async (id, page, limit) => {
256
+ const res = await fetch(`/api/data?page=${page}&limit=${limit}`);
257
+ es.send(id, await res.json());
258
+ });
259
+
260
+ // Manually trigger with parameters
261
+ await es.load('data', 1, 20);
262
+ ```
263
+
264
+ ## Types
265
+
266
+ ```ts
267
+ // Listener function signature
268
+ type EventListenerType<T = any> = (event: T) => void;
269
+
270
+ // Interceptor function signature
271
+ type EventInterceptorType<T = any> = (id: string, event: T, es: EventSystem) => T;
272
+ ```
package/build/index.d.ts CHANGED
@@ -18,7 +18,7 @@ export type ES_ValueType = {
18
18
  * If this value is "null" or "undefined" so no event/value was emitted yet.
19
19
  * In this case, the "last" field will be "null" or "undefined" also.
20
20
  */
21
- date?: Date;
21
+ date?: number;
22
22
  /**
23
23
  * Loader to run when the first listeners is registered and no event was
24
24
  * emitted yet.
package/build/index.js CHANGED
@@ -8,13 +8,9 @@ exports.es = exports.EventSystem = void 0;
8
8
  * @param list Array where to find the item
9
9
  */
10
10
  function deleteFromArray(item, list) {
11
- for (let i = list.length; i > -1; i--) {
12
- let it = list[i];
13
- if (it === item) {
14
- list.splice(i, 1);
15
- break;
16
- }
17
- }
11
+ const i = list.indexOf(item);
12
+ if (i !== -1)
13
+ list.splice(i, 1);
18
14
  }
19
15
  /**
20
16
  * Class that creates the field through which communication will occur.
@@ -53,7 +49,7 @@ class EventSystem {
53
49
  * @param event The value to send to listeners
54
50
  */
55
51
  async send(id, event) {
56
- let inters = [...(this.inters[''] || []), ...(this.inters[id] || [])];
52
+ let inters = (this.inters[''] || []).concat(this.inters[id] || []);
57
53
  let newValue = event;
58
54
  try {
59
55
  for (let inte of inters) {
@@ -61,18 +57,18 @@ class EventSystem {
61
57
  }
62
58
  }
63
59
  catch (err) {
64
- console.error(`error on interceptor of ${id}`, err);
60
+ console.error(`inter:${id}`, err);
65
61
  throw err;
66
62
  }
67
63
  let esData = this.get(id);
68
64
  esData.last = newValue;
69
- esData.date = new Date();
65
+ esData.date = Date.now();
70
66
  for (let func of esData.listeners) {
71
67
  try {
72
68
  await func(newValue);
73
69
  }
74
70
  catch (err) {
75
- console.error(`Error on listener of ${id}, ${func}`, err);
71
+ console.error(`listen:${id}`, err);
76
72
  throw err;
77
73
  }
78
74
  }
package/nul ADDED
@@ -0,0 +1,2 @@
1
+ ERRO: Argumento/op��o inv�lido - 'F:/'.
2
+ Digite "TASKKILL /?" para obter detalhes sobre o uso.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "comes",
3
- "version": "0.0.3",
3
+ "version": "0.0.4",
4
4
  "description": "Simple Event System Communication",
5
5
  "keywords": [
6
6
  "event",