@tmjeee/w-lib 0.0.1 → 0.1.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.
package/CHANGELOG.md CHANGED
@@ -5,18 +5,16 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
- ## [0.0.1] - 2026-05-27
8
+ ## [0.1.0] - 2026-05-29
9
+ - added initial version of
10
+ - event emitter
11
+ - state management
12
+ - finite state machine
13
+ - loading states
9
14
 
10
- ### Added
15
+ ## [0.0.1] - 2026-05-27
11
16
  - Initial library skeleton for npm publishing
12
- - `helloWorld()` sample function exported as the public API
13
- - Full ESM + CJS dual package support via tsdown
17
+ - Full ESM package support via tsdown
14
18
  - TypeScript strict configuration
15
19
  - Vitest test suite with coverage
16
- - `examples/basic.ts` runnable usage example
17
- - Complete publish-ready `package.json` (exports map, files, sideEffects, publishConfig)
18
20
  - MIT license and minimal conventional changelog
19
-
20
- ### Notes
21
- - This is the bootstrap release of the w-lib skeleton.
22
- - Ready for `npm publish --access public`.
package/README.md CHANGED
@@ -3,9 +3,12 @@
3
3
  [![npm version](https://img.shields.io/npm/v/@tmjeee/w-lib.svg)](https://www.npmjs.com/package/@tmjeee/w-lib)
4
4
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
5
 
6
- A minimal, modern TypeScript library skeleton ready for publishing to npmjs.com.
6
+ Disclaimer: Contains codes from [jyoung4242](https://github.com/jyoung4242/Game-Dev-Library).
7
7
 
8
- This package demonstrates current (2026) best practices for shipping small, tree-shakeable, dual-format (ESM + CJS) libraries with excellent TypeScript support.
8
+ ## Development
9
+ See [here](./README_DEV.md) for more info.
10
+
11
+ A minimal, Angular and TypeScript library.
9
12
 
10
13
  ## Installation
11
14
 
@@ -17,164 +20,120 @@ npm install @tmjeee/w-lib
17
20
 
18
21
  This package is **ESM-only**.
19
22
 
20
- ```ts
21
- import { helloWorld } from '@tmjeee/w-lib';
22
-
23
- console.log(helloWorld()); // "Hello, world!"
24
- ```
25
-
26
- > **Note**: CommonJS (`require()`) is not supported.
27
- > If you are in a CommonJS environment, use dynamic import instead:
28
- > ```js
29
- > const { helloWorld } = await import('@tmjeee/w-lib');
30
- > ```
31
-
32
- For more information, see the [Node.js documentation on publishing ESM packages](https://nodejs.org/en/learn/modules/publishing-a-package).
33
-
34
- ## API
35
-
36
- ### `helloWorld(): string`
37
-
38
- Returns the classic greeting.
39
-
40
- ```ts
41
- helloWorld(); // "Hello, world!"
42
- ```
43
-
44
- ## Development
45
-
46
- ```bash
47
- # Install dependencies
48
- npm install
49
-
50
- # Type check
51
- npm run typecheck
52
-
53
- # Run tests (once, exits after completion)
54
- npm test
55
-
56
- # Run tests in watch mode (for development)
57
- npm run test:watch
58
-
59
- # Run tests with coverage
60
- npm run test:coverage
61
-
62
- # Build (outputs to dist/)
63
- npm run build
64
-
65
- # Watch mode during development
66
- npm run dev
67
- ```
68
-
69
- ### Project Structure
70
-
71
- ```
72
- src/index.ts # Source (helloWorld)
73
- test/index.test.ts # Vitest tests
74
- examples/basic.ts # Runnable usage demo
75
- dist/ # Build output (generated)
76
- ```
77
-
78
- ## Publishing
79
-
80
- This package is configured for immediate publishing.
81
23
 
82
- ### Recommended: One-command publish
24
+ ### loading-state
25
+ ```typescript
26
+ import {createLoadingState} from '@tmjeee/w-lib';
83
27
 
84
- ```bash
85
- # Bump version first (patch / minor / major)
86
- npm version patch
28
+ const loadingState = createLoadingState();
29
+ loadingState.withLoading('assets', async ()=>{
30
+ // xhr calls, long running calls
31
+ // loadingState.is('asset'); will be true while this function is running else false
32
+ });
87
33
 
88
- # Dry-run first (highly recommended)
89
- npm run publish:npm:dry
34
+ const isLoading = loadingState.is('assets'); // signal<boolean> - true when loading else false
90
35
 
91
- # Then publish for real
92
- npm run publish:npm
36
+ loadingState.set('assets', false); // explicitly mark 'asset' as false
93
37
  ```
94
38
 
95
- ### Manual / step-by-step (with safety checks)
96
-
97
- ```bash
98
- # 1. Make sure everything is clean
99
- npm run typecheck && npm test && npm run build
100
- # (npm test now runs once and exits automatically)
101
-
102
- # 2. Dry-run the publish (strongly recommended)
103
- npm publish --dry-run --access public
104
-
105
- # 3. Bump version
106
- npm version patch
107
-
108
- # 4. Publish
109
- npm publish --access public
39
+ ### Finite state machine
40
+ ```typescript
41
+ import {FsmState, Fsm} from '@tmjeee/w-lib';
42
+
43
+ class State1 extends FsmState {
44
+ enter(prevState: FsmState | null, ...params: any) {
45
+ console.log(`[State1] enter`);
46
+ }
47
+ exit(nextState: FsmState | null, ...params: any) {
48
+ console.log(`[State1] exit`);
49
+ }
50
+ update(...params: any) {
51
+ console.log(`[State1] update`);
52
+ }
53
+ }
54
+
55
+
56
+ class State2 extends FsmState {
57
+ enter(prevState: FsmState | null, ...params: any) {
58
+ console.log(`[State2] enter`);
59
+ }
60
+ exit(nextState: FsmState | null, ...params: any) {
61
+ console.log(`[State2] exit`);
62
+ }
63
+ update(...params: any) {
64
+ console.log(`[State2] update`);
65
+ }
66
+ }
67
+
68
+ const fsm = new Fsm();
69
+ fsm.register(
70
+ new State1(`state1`),
71
+ new State2(`state2`),
72
+ );
73
+
74
+ fsm.set('state1', {}); // state1.enter(...)
75
+ fsm.update(); // state1.update({})
76
+ fsm.set('state2'); // state1.exit(...) then state2.enter(...)
77
+ fsm.update(); // state2.update({});
78
+ fsm.get(); // return current state -> state2
110
79
  ```
111
80
 
112
- The `prepublishOnly` hook (and both `publish:npm*` scripts) ensure that `build` + `test` run before anything is published.
113
-
114
- ### What gets published?
115
-
116
- Only the contents of the `dist/` folder plus `package.json`, `README.md`, `LICENSE`, and `CHANGELOG.md` (controlled via the `"files"` field).
117
-
118
- ### Automated publishing with GitHub Actions
119
-
120
- This repository includes two GitHub Actions workflows:
121
-
122
- - **`.github/workflows/ci.yml`** — Runs type checking and tests on every push and pull request. Can also be triggered manually from the Actions tab.
123
- - **`.github/workflows/publish.yml`** — Publishes to npmjs.com when you create a new GitHub Release. Can also be triggered manually from the Actions tab (use with caution).
124
81
 
125
- #### Setup steps (one time)
126
-
127
- 1. Create an npm access token:
128
- - Go to https://www.npmjs.com/settings/~tokens
129
- - Generate a new **Automation** token (or **Granular** with publish permission for `@tmjeee/w-lib`)
130
- 2. Add the token as a repository secret:
131
- - In your GitHub repo → **Settings → Secrets and variables → Actions**
132
- - Create a new secret named `NPM_TOKEN` with the value from step 1
133
- 3. (Optional but recommended) Enable npm **Trusted Publishing** (OIDC) for passwordless publishing:
134
- - See the comments in `publish.yml` and https://docs.npmjs.com/trusted-publishers
135
-
136
- #### How to release
137
-
138
- ```bash
139
- # 1. Update CHANGELOG.md
140
- # 2. Commit everything
141
- git add .
142
- git commit -m "chore: prepare v0.0.2"
143
-
144
- # 3. Create a version tag + GitHub Release
145
- npm version patch
146
- git push && git push --tags
147
-
148
- # 4. Go to GitHub Releases → "Draft a new release"
149
- # - Choose the tag you just pushed
150
- # - Publish the release
151
-
152
- # The publish workflow will run automatically and publish to npm
153
-
154
- **Alternative**: You can also manually trigger the publish workflow from the GitHub repo → **Actions** tab → **"Publish to npmjs"** → **"Run workflow"**.
82
+ ### State Management
83
+ ```typescript
84
+ const store = createStateStore<State>({
85
+ name: 'jim',
86
+ age: 12,
87
+ address: {
88
+ address1: '1 Kent Street',
89
+ address2: 'Sydney',
90
+ postcode: '2000',
91
+ }
92
+ });
93
+
94
+
95
+ const address1 = store.get('address.address1');
96
+ store.on('change', (change) => {
97
+ const state = change.state;
98
+ const previousState = change.previousState;
99
+ const value = change.value;
100
+ const previousValue = change.previousValue;
101
+
102
+ console.log(`change`, state, previousState, value, previousValue);
103
+ });
104
+
105
+ // all the following will trigger 'change' event
106
+ store.update('address', (address) => ({address1: 'new address1', address2: 'new address2', postcode: 'new postcode'}));
107
+ store.update('name', (name) => `name-${new Date()}`);
108
+ store.set('name', 'name1');
109
+ store.patch({age: 2, name: 'test'});
110
+ store.batch((ctx) => {
111
+ ctx.set('age', 1);
112
+ ctx.update('age', (age)=>age + 1);
113
+ ctx.patch({age: 2, name: 'test'});
114
+ });
155
115
  ```
156
116
 
157
- ## Tooling
158
-
159
- - **Build**: [tsdown](https://tsdown.dev/) (Rolldown + Oxc)
160
- - **Testing**: [Vitest](https://vitest.dev/)
161
- - **TypeScript**: Strict mode, ES2022 target, bundler resolution
162
-
163
- ### Optional: Add oxlint + oxfmt
164
117
 
165
- For consistency with other `@tmjeee` projects you can add:
166
-
167
- ```bash
168
- npm install -D oxlint oxfmt
118
+ ### Event Emitter
119
+ ```typescript
120
+ const emitter1 = new JsEventEmitter();
121
+ const emitter2 = new JsEventEmitter();
122
+ emitter1.on('test',()=>{
123
+ console.log(`[Emitter1] Receive test event`);
124
+ });
125
+ emitter2.once('test', ()=>{
126
+ console.log(`[Emitter2] Receive test event - once only`);
127
+ });
128
+ emitter1.emit('test', {test: 'test1'});
129
+ emitter1.emit('test', {test: 'test2'});
130
+ emitter2.emit('test', {test: 'test1'});
131
+ emitter2.emit('test', {test: 'test2'});
132
+ emitter1.off('test'); // turn off listening
133
+ emitter1.emit('test', {test: 'test3'});
169
134
  ```
170
135
 
171
- Then add scripts:
172
136
 
173
- ```json
174
- "lint": "oxlint",
175
- "format": "oxfmt --check",
176
- "format:fix": "oxfmt --write"
177
- ```
178
137
 
179
138
  ## License
180
139
 
package/dist/index.d.ts CHANGED
@@ -1,17 +1,144 @@
1
- //#region src/index.d.ts
1
+ import { Signal } from "@angular/core";
2
+ import { Subject } from "rxjs";
3
+
4
+ //#region src/loading-state.d.ts
2
5
  /**
3
- * Classic hello world sample for the w-lib skeleton.
6
+ * A lightweight utility for managing per-action loading states using signals.
4
7
  *
5
- * @returns The canonical greeting string
8
+ * Philosophy:
9
+ * - Most errors should propagate to GlobalErrorHandler.
10
+ * - This utility helps manage UI loading states cleanly without forcing try/catch everywhere.
11
+ * - Use it when you need to disable buttons / show spinners during async actions.
6
12
  *
7
- * @example
8
- * ```ts
9
- * import { helloWorld } from '@tmjeee/w-lib';
13
+ * Recommended usage:
10
14
  *
11
- * console.log(helloWorld()); // "Hello, world!"
12
- * ```
15
+ * const loading = createLoadingState<'creatingBoard' | 'deletingBoard'>();
16
+ *
17
+ * async createBoard() {
18
+ * const title = await openDialog();
19
+ * if (!title) return;
20
+ *
21
+ * await loading.withLoading('creatingBoard', async () => {
22
+ * const board = await this.boardService.createBoard(...);
23
+ * this.router.navigate(...);
24
+ * });
25
+ * }
26
+ *
27
+ * In template:
28
+ * <button [disabled]="loading.is('creatingBoard')()">
29
+ * {{ loading.is('creatingBoard')() ? 'Creating...' : 'Create Board' }}
30
+ * </button>
31
+ */
32
+ declare function createLoadingState<T extends string = string>(): {
33
+ is: (key: T) => Signal<boolean>;
34
+ set: (key: T, value: boolean) => void;
35
+ withLoading: <R>(key: T, fn: () => Promise<R>) => Promise<R>;
36
+ states: Signal<Partial<Record<T, boolean>>>;
37
+ };
38
+ /**
39
+ * Type helper for action loading keys.
40
+ * Example: type UserAction = 'addingToWorkspace' | 'removingFromWorkspace';
13
41
  */
14
- declare function helloWorld(): string;
42
+ type LoadingKey = string;
43
+ //#endregion
44
+ //#region src/event-emitter.d.ts
45
+ interface EventEmitterHandler<T> {
46
+ (change: T): void;
47
+ }
48
+ interface EventEmitterSubscription {
49
+ unsubscribe: () => void;
50
+ }
51
+ interface EventEmitter<T> {
52
+ emit(event: string, payload: T): void;
53
+ on(event: string, handler: EventEmitterHandler<T>): void;
54
+ off(event: string, handler?: EventEmitterHandler<T>): void;
55
+ once(event: string, handler: EventEmitterHandler<T>): void;
56
+ }
57
+ declare class RxEventEmitter<T> implements EventEmitter<T> {
58
+ _listeners: Record<string, Subject<T>>;
59
+ emit(event: string, payload: T): void;
60
+ on(event: string, handler: EventEmitterHandler<T>): void;
61
+ off(event: string, handler?: EventEmitterHandler<T>): void;
62
+ once(event: string, handler: EventEmitterHandler<T>): void;
63
+ }
64
+ declare class JsEventEmitter<T> implements EventEmitter<T> {
65
+ _listeners: Record<string, EventEmitterHandler<T>[]>;
66
+ emit(event: string, payload: T): void;
67
+ on(event: string, handler: EventEmitterHandler<T>): void;
68
+ off(event: string, handler?: EventEmitterHandler<T>): void;
69
+ once(event: string, handler: EventEmitterHandler<T>): void;
70
+ }
71
+ //#endregion
72
+ //#region src/state-management.d.ts
73
+ type Primitive = string | number | boolean | null | undefined;
74
+ type PathsOf<T, Prefix extends string = ""> = T extends Primitive ? never : T extends Array<infer _> ? never : { [K in keyof T & string]: Prefix extends "" ? K | (T[K] extends Primitive ? never : PathsOf<T[K], K>) : `${Prefix}.${K}` | (T[K] extends Primitive ? never : PathsOf<T[K], `${Prefix}.${K}`>) }[keyof T & string];
75
+ type Path<T> = PathsOf<T>;
76
+ type PathValue<T, P extends string> = P extends keyof T ? T[P] : P extends `${infer K}.${infer Rest}` ? K extends keyof T ? Rest extends Path<T[K]> ? PathValue<T[K], Rest> : never : never : never;
77
+ interface ChangePayload<State, V = unknown> {
78
+ path: string;
79
+ previousValue: V;
80
+ value: V;
81
+ previousState: Readonly<State>;
82
+ state: Readonly<State>;
83
+ }
84
+ type ChangeEventMap<State> = {
85
+ change: ChangePayload<State>;
86
+ [key: `change:${string}`]: ChangePayload<State>;
87
+ };
88
+ interface StateStore<State extends object> {
89
+ get(): Readonly<State>;
90
+ get<K extends Path<State>>(path: K): Readonly<PathValue<State, K>>;
91
+ select<T>(selector: (state: Readonly<State>) => T): Readonly<T>;
92
+ set<K extends Path<State>>(path: K, value: PathValue<State, K>): void;
93
+ update<K extends Path<State>>(path: K, fn: (current: PathValue<State, K>) => PathValue<State, K>): void;
94
+ patch(partial: DeepPartial<State>): void;
95
+ batch(fn: (store: BatchContext<State>) => void): void;
96
+ on<E extends keyof ChangeEventMap<State>>(event: E, handler: (payload: ChangeEventMap<State>[E]) => void): void;
97
+ off<E extends keyof ChangeEventMap<State>>(event: E, handler: (payload: ChangeEventMap<State>[E]) => void): void;
98
+ subscribe<K extends Path<State>>(path: K, handler: (payload: ChangePayload<State, PathValue<State, K>>) => void): () => void;
99
+ serialize(): string;
100
+ reset(): void;
101
+ }
102
+ interface BatchContext<State extends object> {
103
+ set<K extends Path<State>>(path: K, value: PathValue<State, K>): void;
104
+ update<K extends Path<State>>(path: K, fn: (current: PathValue<State, K>) => PathValue<State, K>): void;
105
+ patch(partial: DeepPartial<State>): void;
106
+ }
107
+ type DeepPartial<T> = T extends Primitive ? T : { [K in keyof T]?: DeepPartial<T[K]> };
108
+ declare function createStateStore<State extends object>(initialState: State, eventEmitter?: EventEmitter<ChangePayload<State>>): StateStore<State>;
109
+ //#endregion
110
+ //#region src/finite-state-machine.d.ts
111
+ type FsmResult<T = void> = {
112
+ success: boolean;
113
+ message?: string;
114
+ value?: T;
115
+ };
116
+ declare class Fsm {
117
+ states: Map<string, FsmState>;
118
+ current: FsmState | null;
119
+ currentParams: any[];
120
+ currentTime: number;
121
+ constructor();
122
+ /** Register new states, returns array of registered ExState */
123
+ register(...types: (string | FsmState)[]): FsmResult<FsmState[]>;
124
+ /** Change current state */
125
+ set(state: string | FsmState, ...params: any[]): FsmResult<FsmState> | Promise<FsmResult<FsmState>>;
126
+ /** Check if a state is registered */
127
+ has(state: string | FsmState): boolean;
128
+ /** Get current state */
129
+ get(): FsmResult<FsmState>;
130
+ /** Reset FSM */
131
+ reset(): FsmResult<void>;
132
+ /** Update current state */
133
+ update(): FsmResult<void> | Promise<FsmResult<void>>;
134
+ }
135
+ declare class FsmState {
136
+ name: string;
137
+ constructor(name: string);
138
+ enter(_previous: FsmState | null, ..._params: any): void | Promise<void>;
139
+ exit(_next: FsmState | null, ..._params: any): void | Promise<void>;
140
+ update(..._params: any): void | Promise<void>;
141
+ }
15
142
  //#endregion
16
- export { helloWorld };
143
+ export { BatchContext, ChangePayload, EventEmitter, EventEmitterHandler, EventEmitterSubscription, Fsm, FsmResult, FsmState, JsEventEmitter, LoadingKey, Path, PathValue, RxEventEmitter, StateStore, createLoadingState, createStateStore };
17
144
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../src/index.ts"],"mappings":";;AAYA;;;;;;;;;;;iBAAgB,UAAA,CAAA"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../src/loading-state.ts","../src/event-emitter.ts","../src/state-management.ts","../src/finite-state-machine.ts"],"mappings":"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"}
package/dist/index.js CHANGED
@@ -1,20 +1,428 @@
1
- //#region src/index.ts
1
+ import { computed, signal } from "@angular/core";
2
+ import { Subject } from "rxjs";
3
+ import { take } from "rxjs/operators";
4
+
5
+ //#region src/loading-state.ts
2
6
  /**
3
- * Classic hello world sample for the w-lib skeleton.
7
+ * A lightweight utility for managing per-action loading states using signals.
8
+ *
9
+ * Philosophy:
10
+ * - Most errors should propagate to GlobalErrorHandler.
11
+ * - This utility helps manage UI loading states cleanly without forcing try/catch everywhere.
12
+ * - Use it when you need to disable buttons / show spinners during async actions.
13
+ *
14
+ * Recommended usage:
4
15
  *
5
- * @returns The canonical greeting string
16
+ * const loading = createLoadingState<'creatingBoard' | 'deletingBoard'>();
6
17
  *
7
- * @example
8
- * ```ts
9
- * import { helloWorld } from '@tmjeee/w-lib';
18
+ * async createBoard() {
19
+ * const title = await openDialog();
20
+ * if (!title) return;
10
21
  *
11
- * console.log(helloWorld()); // "Hello, world!"
12
- * ```
22
+ * await loading.withLoading('creatingBoard', async () => {
23
+ * const board = await this.boardService.createBoard(...);
24
+ * this.router.navigate(...);
25
+ * });
26
+ * }
27
+ *
28
+ * In template:
29
+ * <button [disabled]="loading.is('creatingBoard')()">
30
+ * {{ loading.is('creatingBoard')() ? 'Creating...' : 'Create Board' }}
31
+ * </button>
13
32
  */
14
- function helloWorld() {
15
- return "Hello, world!";
33
+ function createLoadingState() {
34
+ const loadingMap = signal({});
35
+ const signalCache = /* @__PURE__ */ new Map();
36
+ /**
37
+ * Returns a reactive signal indicating if a specific action is loading.
38
+ * The returned signal is stable (same reference) for the same key.
39
+ */
40
+ const is = (key) => {
41
+ if (!signalCache.has(key)) signalCache.set(key, computed(() => loadingMap()[key] ?? false));
42
+ return signalCache.get(key);
43
+ };
44
+ /**
45
+ * Manually set the loading state for a key.
46
+ */
47
+ const set = (key, value) => {
48
+ loadingMap.update((map) => ({
49
+ ...map,
50
+ [key]: value
51
+ }));
52
+ };
53
+ /**
54
+ * Runs an async function while automatically managing the loading state.
55
+ * Errors are allowed to propagate (as per project convention).
56
+ */
57
+ const withLoading = async (key, fn) => {
58
+ set(key, true);
59
+ try {
60
+ return await fn();
61
+ } finally {
62
+ set(key, false);
63
+ }
64
+ };
65
+ return {
66
+ is,
67
+ set,
68
+ withLoading,
69
+ states: loadingMap.asReadonly()
70
+ };
71
+ }
72
+
73
+ //#endregion
74
+ //#region src/event-emitter.ts
75
+ var RxEventEmitter = class {
76
+ _listeners = {};
77
+ emit(event, payload) {
78
+ const handlers = this._listeners[event];
79
+ if (handlers) handlers.next(payload);
80
+ }
81
+ on(event, handler) {
82
+ if (!this._listeners[event]) this._listeners[event] = new Subject();
83
+ this._listeners[event].subscribe(handler);
84
+ }
85
+ off(event, handler) {
86
+ if (this._listeners[event]) {
87
+ this._listeners[event].complete();
88
+ delete this._listeners[event];
89
+ }
90
+ }
91
+ once(event, handler) {
92
+ if (!this._listeners[event]) this._listeners[event] = new Subject();
93
+ this._listeners[event].pipe(take(1)).subscribe(handler);
94
+ }
95
+ };
96
+ var JsEventEmitter = class {
97
+ _listeners = {};
98
+ emit(event, payload) {
99
+ const handlers = this._listeners[event];
100
+ if (handlers && handlers.length) handlers.forEach((h) => {
101
+ h(payload);
102
+ });
103
+ }
104
+ on(event, handler) {
105
+ if (!this._listeners[event]) this._listeners[event] = [];
106
+ if (!this._listeners[event].find((h) => h == handler)) this._listeners[event].push(handler);
107
+ }
108
+ off(event, handler) {
109
+ if (!this._listeners[event]) this._listeners[event] = [];
110
+ if (handler) {
111
+ const idx = this._listeners[event].findIndex((h) => h == handler);
112
+ if (idx >= 0) this._listeners[event].splice(idx, 1);
113
+ } else delete this._listeners[event];
114
+ }
115
+ once(event, handler) {
116
+ if (!this._listeners[event]) this._listeners[event] = [];
117
+ const onceOnlyHandler = (evt) => {
118
+ try {
119
+ handler(evt);
120
+ } finally {
121
+ this.off(event, onceOnlyHandler);
122
+ }
123
+ };
124
+ this._listeners[event].push(onceOnlyHandler);
125
+ }
126
+ };
127
+
128
+ //#endregion
129
+ //#region src/state-management.ts
130
+ function parsePath(path) {
131
+ return path.split(".");
16
132
  }
133
+ function deepFreeze(obj) {
134
+ if (obj === null || typeof obj !== "object") return obj;
135
+ Object.getOwnPropertyNames(obj).forEach((name) => {
136
+ const val = obj[name];
137
+ if (val && typeof val === "object") deepFreeze(val);
138
+ });
139
+ return Object.freeze(obj);
140
+ }
141
+ function deepClone(obj) {
142
+ if (obj === null || typeof obj !== "object") return obj;
143
+ if (Array.isArray(obj)) return obj.map(deepClone);
144
+ const result = {};
145
+ for (const key of Object.keys(obj)) result[key] = deepClone(obj[key]);
146
+ return result;
147
+ }
148
+ function getAtPath(obj, segments) {
149
+ let current = obj;
150
+ for (const seg of segments) {
151
+ if (current === null || typeof current !== "object") throw new Error(`Invalid path segment "${seg}": not an object`);
152
+ current = current[seg];
153
+ }
154
+ return current;
155
+ }
156
+ function setAtPath(obj, segments, value) {
157
+ if (segments.length === 0) return value;
158
+ const [head, ...tail] = segments;
159
+ if (typeof obj !== "object" || obj === null) throw new Error(`Cannot set path on non-object at segment "${head}"`);
160
+ const record = obj;
161
+ const updated = { ...record };
162
+ if (tail.length === 0) updated[head] = value;
163
+ else {
164
+ const child = record[head];
165
+ if (child === null || typeof child !== "object") throw new Error(`Path segment "${head}" is not an object`);
166
+ updated[head] = setAtPath(child, tail, value);
167
+ }
168
+ return updated;
169
+ }
170
+ function shallowEqual(a, b) {
171
+ if (a === b) return true;
172
+ if (a === null || b === null) return false;
173
+ if (typeof a !== "object" || typeof b !== "object") return false;
174
+ const keysA = Object.keys(a);
175
+ const keysB = Object.keys(b);
176
+ if (keysA.length !== keysB.length) return false;
177
+ for (const k of keysA) if (a[k] !== b[k]) return false;
178
+ return true;
179
+ }
180
+ function deepMerge(base, partial) {
181
+ const result = { ...base };
182
+ for (const key of Object.keys(partial)) {
183
+ const pVal = partial[key];
184
+ const bVal = base[key];
185
+ if (pVal !== void 0 && pVal !== null && typeof pVal === "object" && !Array.isArray(pVal) && bVal !== null && typeof bVal === "object") result[key] = deepMerge(bVal, pVal);
186
+ else if (pVal !== void 0) result[key] = pVal;
187
+ }
188
+ return result;
189
+ }
190
+ function createStateStore(initialState, eventEmitter = new JsEventEmitter()) {
191
+ const _initial = deepFreeze(deepClone(initialState));
192
+ let _state = _initial;
193
+ const _emitter = eventEmitter;
194
+ function _emit(path, previousValue, value, previousState) {
195
+ const payload = {
196
+ path,
197
+ previousValue,
198
+ value,
199
+ previousState,
200
+ state: _state
201
+ };
202
+ _emitter.emit("change", payload);
203
+ _emitter.emit(`change:${path}`, payload);
204
+ }
205
+ function _applySet(currentState, segments, value) {
206
+ return deepFreeze(setAtPath(deepClone(currentState), segments, value));
207
+ }
208
+ function get(path) {
209
+ if (path === void 0) return _state;
210
+ const segments = parsePath(path);
211
+ return deepFreeze(deepClone(getAtPath(_state, segments)));
212
+ }
213
+ function select(selector) {
214
+ const result = selector(_state);
215
+ if (result !== null && typeof result === "object") return deepFreeze(deepClone(result));
216
+ return result;
217
+ }
218
+ function set(path, value) {
219
+ const segments = parsePath(path);
220
+ const previousValue = getAtPath(_state, segments);
221
+ if (shallowEqual(previousValue, value)) return;
222
+ const previousState = _state;
223
+ _state = _applySet(_state, segments, value);
224
+ _emit(path, previousValue, value, previousState);
225
+ }
226
+ function update(path, fn) {
227
+ const segments = parsePath(path);
228
+ set(path, fn(deepClone(getAtPath(_state, segments))));
229
+ }
230
+ function patch(partial) {
231
+ const nextState = deepFreeze(deepMerge(_state, partial));
232
+ const changedPaths = [];
233
+ collectChangedPaths("", _state, nextState, changedPaths);
234
+ if (changedPaths.length === 0) return;
235
+ const previousState = _state;
236
+ _state = nextState;
237
+ for (const entry of changedPaths) _emit(entry.path, entry.prev, entry.next, previousState);
238
+ }
239
+ function collectChangedPaths(prefix, prev, next, out) {
240
+ if (prev === next) return;
241
+ if (prev !== null && next !== null && typeof prev === "object" && typeof next === "object" && !Array.isArray(prev) && !Array.isArray(next)) {
242
+ const allKeys = new Set([...Object.keys(prev), ...Object.keys(next)]);
243
+ for (const k of allKeys) {
244
+ const pv = prev[k];
245
+ const nv = next[k];
246
+ collectChangedPaths(prefix ? `${prefix}.${k}` : k, pv, nv, out);
247
+ }
248
+ } else if (!shallowEqual(prev, next)) out.push({
249
+ path: prefix,
250
+ prev,
251
+ next
252
+ });
253
+ }
254
+ function batch(fn) {
255
+ let pendingState = _state;
256
+ const changes = [];
257
+ const ctx = {
258
+ set(path, value) {
259
+ const segments = parsePath(path);
260
+ const previousValue = getAtPath(pendingState, segments);
261
+ if (shallowEqual(previousValue, value)) return;
262
+ const prevState = pendingState;
263
+ pendingState = deepFreeze(setAtPath(deepClone(pendingState), segments, value));
264
+ changes.push({
265
+ path,
266
+ prev: previousValue,
267
+ next: value,
268
+ prevState
269
+ });
270
+ },
271
+ update(path, fn2) {
272
+ const segments = parsePath(path);
273
+ const next = fn2(deepClone(getAtPath(pendingState, segments)));
274
+ ctx.set(path, next);
275
+ },
276
+ patch(partial) {
277
+ const merged = deepFreeze(deepMerge(pendingState, partial));
278
+ const changedPaths = [];
279
+ collectChangedPaths("", pendingState, merged, changedPaths);
280
+ if (changedPaths.length === 0) return;
281
+ const prevState = pendingState;
282
+ pendingState = merged;
283
+ for (const entry of changedPaths) changes.push({
284
+ path: entry.path,
285
+ prev: entry.prev,
286
+ next: entry.next,
287
+ prevState
288
+ });
289
+ }
290
+ };
291
+ fn(ctx);
292
+ if (pendingState === _state) return;
293
+ _state = pendingState;
294
+ for (const change of changes) _emit(change.path, change.prev, change.next, change.prevState);
295
+ }
296
+ function on(event, handler) {
297
+ _emitter.on(event, handler);
298
+ }
299
+ function off(event, handler) {
300
+ _emitter.off(event, handler);
301
+ }
302
+ function subscribe(path, handler) {
303
+ const eventName = `change:${path}`;
304
+ const wrapped = (payload) => handler(payload);
305
+ _emitter.on(eventName, wrapped);
306
+ return () => _emitter.off(eventName, wrapped);
307
+ }
308
+ function serialize() {
309
+ return JSON.stringify(_state);
310
+ }
311
+ function reset() {
312
+ const previousState = _state;
313
+ _state = _initial;
314
+ const changes = [];
315
+ collectChangedPaths("", previousState, _state, changes);
316
+ for (const change of changes) _emit(change.path, change.prev, change.next, previousState);
317
+ }
318
+ return {
319
+ get,
320
+ select,
321
+ set,
322
+ update,
323
+ patch,
324
+ batch,
325
+ on,
326
+ off,
327
+ subscribe,
328
+ serialize,
329
+ reset
330
+ };
331
+ }
332
+
333
+ //#endregion
334
+ //#region src/finite-state-machine.ts
335
+ var Fsm = class {
336
+ states = /* @__PURE__ */ new Map();
337
+ current = null;
338
+ currentParams = [];
339
+ currentTime = 0;
340
+ constructor() {}
341
+ /** Register new states, returns array of registered ExState */
342
+ register(...types) {
343
+ const generatedStates = [];
344
+ for (const state of types) {
345
+ const newState = typeof state === "string" ? new FsmState(state) : state;
346
+ if (this.states.has(newState.name)) console.warn(`State "${newState.name}" is already registered and will be overwritten.`);
347
+ this.states.set(newState.name, newState);
348
+ generatedStates.push(newState);
349
+ }
350
+ return {
351
+ success: true,
352
+ value: generatedStates
353
+ };
354
+ }
355
+ /** Change current state */
356
+ set(state, ...params) {
357
+ const name = typeof state === "string" ? state : state.name;
358
+ const next = this.states.get(name);
359
+ if (!next) return {
360
+ success: false,
361
+ message: `State "${name}" not found`
362
+ };
363
+ const finalize = () => {
364
+ this.current = next;
365
+ this.currentParams = params;
366
+ this.currentTime = Date.now();
367
+ return {
368
+ success: true,
369
+ value: next
370
+ };
371
+ };
372
+ if (!this.current) {
373
+ const entering = next.enter(this.current, ...params);
374
+ return entering instanceof Promise ? entering.then(finalize) : finalize();
375
+ }
376
+ const leaving = this.current.exit(next, ...params);
377
+ if (leaving instanceof Promise) return leaving.then(() => {
378
+ const entering = next.enter(this.current, ...params);
379
+ return entering instanceof Promise ? entering.then(finalize) : finalize();
380
+ });
381
+ const entering = next.enter(this.current, ...params);
382
+ return entering instanceof Promise ? entering.then(finalize) : finalize();
383
+ }
384
+ /** Check if a state is registered */
385
+ has(state) {
386
+ const name = typeof state === "string" ? state : state.name;
387
+ return this.states.has(name);
388
+ }
389
+ /** Get current state */
390
+ get() {
391
+ if (!this.current) return {
392
+ success: false,
393
+ message: "No state set"
394
+ };
395
+ return {
396
+ success: true,
397
+ value: this.current
398
+ };
399
+ }
400
+ /** Reset FSM */
401
+ reset() {
402
+ this.current = null;
403
+ this.currentParams = [];
404
+ this.currentTime = 0;
405
+ return { success: true };
406
+ }
407
+ /** Update current state */
408
+ update() {
409
+ if (!this.current) return {
410
+ success: false,
411
+ message: "No current state"
412
+ };
413
+ const result = this.current.update(...this.currentParams);
414
+ return result instanceof Promise ? result.then(() => ({ success: true })) : { success: true };
415
+ }
416
+ };
417
+ var FsmState = class {
418
+ constructor(name) {
419
+ this.name = name;
420
+ }
421
+ enter(_previous, ..._params) {}
422
+ exit(_next, ..._params) {}
423
+ update(..._params) {}
424
+ };
17
425
 
18
426
  //#endregion
19
- export { helloWorld };
427
+ export { Fsm, FsmState, JsEventEmitter, RxEventEmitter, createLoadingState, createStateStore };
20
428
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["/**\n * Classic hello world sample for the w-lib skeleton.\n *\n * @returns The canonical greeting string\n *\n * @example\n * ```ts\n * import { helloWorld } from '@tmjeee/w-lib';\n *\n * console.log(helloWorld()); // \"Hello, world!\"\n * ```\n */\nexport function helloWorld(): string {\n return 'Hello, world!';\n}\n"],"mappings":";;;;;;;;;;;;;AAYA,SAAgB,aAAqB;AACnC,QAAO"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/loading-state.ts","../src/event-emitter.ts","../src/state-management.ts","../src/finite-state-machine.ts"],"sourcesContent":["import { computed, signal, Signal } from '@angular/core';\n\n/**\n * A lightweight utility for managing per-action loading states using signals.\n *\n * Philosophy:\n * - Most errors should propagate to GlobalErrorHandler.\n * - This utility helps manage UI loading states cleanly without forcing try/catch everywhere.\n * - Use it when you need to disable buttons / show spinners during async actions.\n *\n * Recommended usage:\n *\n * const loading = createLoadingState<'creatingBoard' | 'deletingBoard'>();\n *\n * async createBoard() {\n * const title = await openDialog();\n * if (!title) return;\n *\n * await loading.withLoading('creatingBoard', async () => {\n * const board = await this.boardService.createBoard(...);\n * this.router.navigate(...);\n * });\n * }\n *\n * In template:\n * <button [disabled]=\"loading.is('creatingBoard')()\">\n * {{ loading.is('creatingBoard')() ? 'Creating...' : 'Create Board' }}\n * </button>\n */\nexport function createLoadingState<T extends string = string>() {\n const loadingMap = signal<Partial<Record<T, boolean>>>({});\n const signalCache = new Map<T, Signal<boolean>>();\n\n /**\n * Returns a reactive signal indicating if a specific action is loading.\n * The returned signal is stable (same reference) for the same key.\n */\n const is = (key: T): Signal<boolean> => {\n if (!signalCache.has(key)) {\n signalCache.set(\n key,\n computed(() => loadingMap()[key] ?? false)\n );\n }\n return signalCache.get(key)!;\n };\n\n /**\n * Manually set the loading state for a key.\n */\n const set = (key: T, value: boolean): void => {\n loadingMap.update((map) => ({ ...map, [key]: value }));\n };\n\n /**\n * Runs an async function while automatically managing the loading state.\n * Errors are allowed to propagate (as per project convention).\n */\n const withLoading = async <R>(key: T, fn: () => Promise<R>): Promise<R> => {\n set(key, true);\n try {\n return await fn();\n } finally {\n set(key, false);\n }\n };\n\n /**\n * Raw access to the loading map (rarely needed).\n */\n const states = loadingMap.asReadonly();\n\n return {\n is,\n set,\n withLoading,\n states,\n };\n}\n\n/**\n * Type helper for action loading keys.\n * Example: type UserAction = 'addingToWorkspace' | 'removingFromWorkspace';\n */\nexport type LoadingKey = string;\n","\nexport interface EventEmitterHandler<T> {\n (change:T):void;\n}\n\nexport interface EventEmitterSubscription {\n unsubscribe:()=>void;\n}\n\n\nexport interface EventEmitter<T> {\n emit(event: string, payload: T): void;\n on(event: string, handler: EventEmitterHandler<T>): void;\n off(event: string, handler?: EventEmitterHandler<T>): void;\n once(event: string, handler: EventEmitterHandler<T>): void;\n}\n\n// =============================================================\n// ============== RxJs =========================================\n// =============================================================\n\nimport {Subject} from 'rxjs';\nimport {take} from 'rxjs/operators';\n\nexport class RxEventEmitter<T> implements EventEmitter<T> {\n\n _listeners: Record<string, Subject<T>> = {};\n\n emit(event: string, payload: T): void {\n const handlers = this._listeners[event];\n if (handlers) {\n handlers.next(payload);\n }\n }\n on(event: string, handler: EventEmitterHandler<T>): void {\n if (!this._listeners[event]) {\n this._listeners[event] = new Subject();\n }\n this._listeners[event].subscribe(handler)\n }\n off(event: string, handler?: EventEmitterHandler<T>): void {\n const s = this._listeners[event];\n if (s) {\n this._listeners[event].complete();\n delete this._listeners[event];\n }\n }\n once(event: string, handler: EventEmitterHandler<T>): void {\n if (!this._listeners[event]) {\n this._listeners[event] = new Subject();\n }\n this._listeners[event].pipe(\n take(1)\n ).subscribe(handler);\n }\n}\n\n\n// =============================================================\n// ============== Js ===========================================\n// =============================================================\n\nexport class JsEventEmitter<T> implements EventEmitter<T> {\n\n _listeners: Record<string, EventEmitterHandler<T>[]> = {};\n\n emit(event: string, payload: T): void {\n const handlers = this._listeners[event];\n if (handlers && handlers.length) {\n handlers.forEach(h => {\n h(payload);\n })\n }\n }\n on(event: string, handler: EventEmitterHandler<T>): void {\n if (!this._listeners[event]) {\n this._listeners[event] = [];\n }\n const exists = this._listeners[event].find(h => h == handler);\n if (!exists) {\n this._listeners[event].push(handler);\n }\n }\n off(event: string, handler?: EventEmitterHandler<T>): void {\n if (!this._listeners[event]) {\n this._listeners[event] = [];\n }\n if (handler) {\n const idx = this._listeners[event].findIndex(h => h == handler);\n if (idx >= 0) {\n this._listeners[event].splice(idx, 1);\n }\n } else {\n delete this._listeners[event];\n }\n }\n once(event: string, handler:EventEmitterHandler<T>): void {\n if (!this._listeners[event]) {\n this._listeners[event] = [];\n }\n const onceOnlyHandler = (evt: T) => {\n try {\n handler(evt);\n } finally {\n this.off(event, onceOnlyHandler);\n }\n };\n this._listeners[event].push(onceOnlyHandler);\n }\n}","// ─── Path Typing ────────────────────────────────────────────────────────────\nimport {EventEmitter, JsEventEmitter} from './event-emitter';\n\ntype Primitive = string | number | boolean | null | undefined;\n\ntype PathsOf<T, Prefix extends string = \"\"> = T extends Primitive\n ? never\n : T extends Array<infer _>\n ? never\n : {\n [K in keyof T & string]: Prefix extends \"\"\n ? K | (T[K] extends Primitive ? never : PathsOf<T[K], K>)\n : `${Prefix}.${K}` | (T[K] extends Primitive ? never : PathsOf<T[K], `${Prefix}.${K}`>);\n }[keyof T & string];\n\nexport type Path<T> = PathsOf<T>;\n\nexport type PathValue<T, P extends string> = P extends keyof T\n ? T[P]\n : P extends `${infer K}.${infer Rest}`\n ? K extends keyof T\n ? Rest extends Path<T[K]>\n ? PathValue<T[K], Rest>\n : never\n : never\n : never;\n\n// ─── Event Types ─────────────────────────────────────────────────────────────\n\nexport interface ChangePayload<State, V = unknown> {\n path: string;\n previousValue: V;\n value: V;\n previousState: Readonly<State>;\n state: Readonly<State>;\n}\n\ntype ChangeEventMap<State> = {\n change: ChangePayload<State>;\n [key: `change:${string}`]: ChangePayload<State>;\n};\n\n// ─── StateStore Interface ────────────────────────────────────────────────────\n\nexport interface StateStore<State extends object> {\n get(): Readonly<State>;\n get<K extends Path<State>>(path: K): Readonly<PathValue<State, K>>;\n select<T>(selector: (state: Readonly<State>) => T): Readonly<T>;\n\n set<K extends Path<State>>(path: K, value: PathValue<State, K>): void;\n update<K extends Path<State>>(path: K, fn: (current: PathValue<State, K>) => PathValue<State, K>): void;\n patch(partial: DeepPartial<State>): void;\n batch(fn: (store: BatchContext<State>) => void): void;\n\n on<E extends keyof ChangeEventMap<State>>(event: E, handler: (payload: ChangeEventMap<State>[E]) => void): void;\n off<E extends keyof ChangeEventMap<State>>(event: E, handler: (payload: ChangeEventMap<State>[E]) => void): void;\n\n subscribe<K extends Path<State>>(path: K, handler: (payload: ChangePayload<State, PathValue<State, K>>) => void): () => void;\n\n serialize(): string;\n reset(): void;\n}\n\nexport interface BatchContext<State extends object> {\n set<K extends Path<State>>(path: K, value: PathValue<State, K>): void;\n update<K extends Path<State>>(path: K, fn: (current: PathValue<State, K>) => PathValue<State, K>): void;\n patch(partial: DeepPartial<State>): void;\n}\n\ntype DeepPartial<T> = T extends Primitive ? T : { [K in keyof T]?: DeepPartial<T[K]> };\n\n// ─── Utility Helpers ─────────────────────────────────────────────────────────\n\nfunction parsePath(path: string): string[] {\n return path.split(\".\");\n}\n\nfunction deepFreeze<T>(obj: T): Readonly<T> {\n if (obj === null || typeof obj !== \"object\") return obj;\n Object.getOwnPropertyNames(obj).forEach(name => {\n const val = (obj as Record<string, unknown>)[name];\n if (val && typeof val === \"object\") deepFreeze(val);\n });\n return Object.freeze(obj);\n}\n\nfunction deepClone<T>(obj: T): T {\n if (obj === null || typeof obj !== \"object\") return obj;\n if (Array.isArray(obj)) return (obj as unknown[]).map(deepClone) as unknown as T;\n const result = {} as Record<string, unknown>;\n for (const key of Object.keys(obj as object)) {\n result[key] = deepClone((obj as Record<string, unknown>)[key]);\n }\n return result as T;\n}\n\nfunction getAtPath<T>(obj: T, segments: string[]): unknown {\n let current: unknown = obj;\n for (const seg of segments) {\n if (current === null || typeof current !== \"object\") {\n throw new Error(`Invalid path segment \"${seg}\": not an object`);\n }\n current = (current as Record<string, unknown>)[seg];\n }\n return current;\n}\n\nfunction setAtPath<T extends object>(obj: T, segments: string[], value: unknown): T {\n if (segments.length === 0) return value as T;\n const [head, ...tail] = segments;\n if (typeof obj !== \"object\" || obj === null) {\n throw new Error(`Cannot set path on non-object at segment \"${head}\"`);\n }\n const record = obj as Record<string, unknown>;\n const updated = { ...record };\n if (tail.length === 0) {\n updated[head] = value;\n } else {\n const child = record[head];\n if (child === null || typeof child !== \"object\") {\n throw new Error(`Path segment \"${head}\" is not an object`);\n }\n updated[head] = setAtPath(child as object, tail, value);\n }\n return updated as T;\n}\n\nfunction shallowEqual(a: unknown, b: unknown): boolean {\n if (a === b) return true;\n if (a === null || b === null) return false;\n if (typeof a !== \"object\" || typeof b !== \"object\") return false;\n const keysA = Object.keys(a as object);\n const keysB = Object.keys(b as object);\n if (keysA.length !== keysB.length) return false;\n for (const k of keysA) {\n if ((a as Record<string, unknown>)[k] !== (b as Record<string, unknown>)[k]) return false;\n }\n return true;\n}\n\nfunction deepMerge<T extends object>(base: T, partial: DeepPartial<T>): T {\n const result = { ...base } as Record<string, unknown>;\n for (const key of Object.keys(partial as object)) {\n const pVal = (partial as Record<string, unknown>)[key];\n const bVal = (base as Record<string, unknown>)[key];\n if (\n pVal !== undefined &&\n pVal !== null &&\n typeof pVal === \"object\" &&\n !Array.isArray(pVal) &&\n bVal !== null &&\n typeof bVal === \"object\"\n ) {\n result[key] = deepMerge(bVal as object, pVal as DeepPartial<object>);\n } else if (pVal !== undefined) {\n result[key] = pVal;\n }\n }\n return result as T;\n}\n\n// ─── Factory ─────────────────────────────────────────────────────────────────\n\nexport function createStateStore<State extends object>(\n initialState: State, \n eventEmitter: EventEmitter<ChangePayload<State>> = new JsEventEmitter<ChangePayload<State>>()\n): StateStore<State> {\n\n const _initial: Readonly<State> = deepFreeze(deepClone(initialState));\n let _state: Readonly<State> = _initial;\n\n const _emitter = eventEmitter;\n\n function _emit(path: string, previousValue: unknown, value: unknown, previousState: Readonly<State>): void {\n const payload: ChangePayload<State> = {\n path,\n previousValue,\n value,\n previousState,\n state: _state,\n };\n _emitter.emit(\"change\", payload);\n _emitter.emit(`change:${path}` as `change:${string}`, payload);\n }\n\n function _applySet(currentState: Readonly<State>, segments: string[], value: unknown): Readonly<State> {\n return deepFreeze(setAtPath(deepClone(currentState), segments, value));\n }\n\n // Overloaded get\n function get(): Readonly<State>;\n function get<K extends Path<State>>(path: K): Readonly<PathValue<State, K>>;\n function get<K extends Path<State>>(path?: K): Readonly<State> | Readonly<PathValue<State, K>> {\n if (path === undefined) return _state;\n const segments = parsePath(path);\n const val = getAtPath(_state, segments);\n return deepFreeze(deepClone(val)) as Readonly<PathValue<State, K>>;\n }\n\n function select<T>(selector: (state: Readonly<State>) => T): Readonly<T> {\n const result = selector(_state);\n if (result !== null && typeof result === \"object\") {\n return deepFreeze(deepClone(result)) as Readonly<T>;\n }\n return result as Readonly<T>;\n }\n\n function set<K extends Path<State>>(path: K, value: PathValue<State, K>): void {\n const segments = parsePath(path);\n const previousValue = getAtPath(_state, segments);\n if (shallowEqual(previousValue, value)) return;\n const previousState = _state;\n _state = _applySet(_state, segments, value);\n _emit(path, previousValue, value, previousState);\n }\n\n function update<K extends Path<State>>(path: K, fn: (current: PathValue<State, K>) => PathValue<State, K>): void {\n const segments = parsePath(path);\n const current = getAtPath(_state, segments) as PathValue<State, K>;\n const next = fn(deepClone(current));\n set(path, next);\n }\n\n function patch(partial: DeepPartial<State>): void {\n const merged = deepMerge(_state as State, partial);\n const nextState = deepFreeze(merged);\n const changedPaths: Array<{ path: string; prev: unknown; next: unknown }> = [];\n collectChangedPaths(\"\", _state, nextState, changedPaths);\n if (changedPaths.length === 0) return;\n const previousState = _state;\n _state = nextState;\n for (const entry of changedPaths) {\n _emit(entry.path, entry.prev, entry.next, previousState);\n }\n }\n\n function collectChangedPaths(\n prefix: string,\n prev: unknown,\n next: unknown,\n out: Array<{ path: string; prev: unknown; next: unknown }>,\n ): void {\n if (prev === next) return;\n if (\n prev !== null &&\n next !== null &&\n typeof prev === \"object\" &&\n typeof next === \"object\" &&\n !Array.isArray(prev) &&\n !Array.isArray(next)\n ) {\n const allKeys = new Set([...Object.keys(prev as object), ...Object.keys(next as object)]);\n for (const k of allKeys) {\n const pv = (prev as Record<string, unknown>)[k];\n const nv = (next as Record<string, unknown>)[k];\n const childPath = prefix ? `${prefix}.${k}` : k;\n collectChangedPaths(childPath, pv, nv, out);\n }\n } else if (!shallowEqual(prev, next)) {\n out.push({ path: prefix, prev, next });\n }\n }\n\n function batch(fn: (ctx: BatchContext<State>) => void): void {\n let pendingState = _state;\n const changes: Array<{ path: string; prev: unknown; next: unknown; prevState: Readonly<State> }> = [];\n\n const ctx: BatchContext<State> = {\n set<K extends Path<State>>(path: K, value: PathValue<State, K>): void {\n const segments = parsePath(path);\n const previousValue = getAtPath(pendingState, segments);\n if (shallowEqual(previousValue, value)) return;\n const prevState = pendingState;\n pendingState = deepFreeze(setAtPath(deepClone(pendingState), segments, value));\n changes.push({ path, prev: previousValue, next: value, prevState });\n },\n update<K extends Path<State>>(path: K, fn2: (current: PathValue<State, K>) => PathValue<State, K>): void {\n const segments = parsePath(path);\n const current = getAtPath(pendingState, segments) as PathValue<State, K>;\n const next = fn2(deepClone(current));\n ctx.set(path, next);\n },\n patch(partial: DeepPartial<State>): void {\n const merged = deepFreeze(deepMerge(pendingState as State, partial));\n const changedPaths: Array<{ path: string; prev: unknown; next: unknown }> = [];\n collectChangedPaths(\"\", pendingState, merged, changedPaths);\n if (changedPaths.length === 0) return;\n const prevState = pendingState;\n pendingState = merged;\n for (const entry of changedPaths) {\n changes.push({ path: entry.path, prev: entry.prev, next: entry.next, prevState });\n }\n },\n };\n\n fn(ctx);\n\n if (pendingState === _state) return;\n _state = pendingState;\n\n for (const change of changes) {\n _emit(change.path, change.prev, change.next, change.prevState);\n }\n }\n\n function on<E extends keyof ChangeEventMap<State>>(event: E, handler: (payload: ChangeEventMap<State>[E]) => void): void {\n _emitter.on(event, handler as (payload: ChangeEventMap<State>[E]) => void);\n }\n\n function off<E extends keyof ChangeEventMap<State>>(event: E, handler: (payload: ChangeEventMap<State>[E]) => void): void {\n _emitter.off(event, handler as (payload: ChangeEventMap<State>[E]) => void);\n }\n\n function subscribe<K extends Path<State>>(\n path: K,\n handler: (payload: ChangePayload<State, PathValue<State, K>>) => void,\n ): () => void {\n const eventName = `change:${path}` as `change:${string}`;\n const wrapped = (payload: ChangePayload<State>) => handler(payload as unknown as ChangePayload<State, PathValue<State, K>>);\n _emitter.on(eventName, wrapped);\n return () => _emitter.off(eventName, wrapped);\n }\n\n function serialize(): string {\n return JSON.stringify(_state);\n }\n\n function reset(): void {\n const previousState = _state;\n _state = _initial;\n const changes: Array<{ path: string; prev: unknown; next: unknown }> = [];\n collectChangedPaths(\"\", previousState, _state, changes);\n for (const change of changes) {\n _emit(change.path, change.prev, change.next, previousState);\n }\n }\n\n return { get, select, set, update, patch, batch, on, off, subscribe, serialize, reset };\n}","export type FsmResult<T = void> = {\n success: boolean;\n message?: string;\n value?: T;\n};\n\nexport class Fsm {\n public states = new Map<string, FsmState>();\n public current: FsmState | null = null;\n public currentParams: any[] = [];\n public currentTime: number = 0;\n\n constructor() {}\n\n /** Register new states, returns array of registered ExState */\n register(...types: (string | FsmState)[]): FsmResult<FsmState[]> {\n const generatedStates: FsmState[] = [];\n\n for (const state of types) {\n const newState = typeof state === \"string\" ? new FsmState(state) : state;\n\n if (this.states.has(newState.name)) {\n console.warn(`State \"${newState.name}\" is already registered and will be overwritten.`);\n }\n\n this.states.set(newState.name, newState);\n generatedStates.push(newState);\n }\n\n return { success: true, value: generatedStates };\n }\n\n /** Change current state */\n set(state: string | FsmState, ...params: any[]): FsmResult<FsmState> | Promise<FsmResult<FsmState>> {\n const name = typeof state === \"string\" ? state : state.name;\n const next = this.states.get(name);\n\n if (!next) return { success: false, message: `State \"${name}\" not found` };\n\n const finalize = () => {\n this.current = next;\n this.currentParams = params;\n this.currentTime = Date.now();\n return { success: true, value: next };\n };\n\n // No current state, only enter next\n if (!this.current) {\n const entering = next.enter(this.current, ...params);\n return entering instanceof Promise ? entering.then(finalize) : finalize();\n }\n\n // Handle current exit\n const leaving = this.current.exit(next, ...params);\n if (leaving instanceof Promise) {\n return leaving.then(() => {\n const entering = next.enter(this.current, ...params);\n return entering instanceof Promise ? entering.then(finalize) : finalize();\n });\n }\n\n const entering = next.enter(this.current, ...params);\n return entering instanceof Promise ? entering.then(finalize) : finalize();\n }\n\n /** Check if a state is registered */\n has(state: string | FsmState): boolean {\n const name = typeof state === \"string\" ? state : state.name;\n return this.states.has(name);\n }\n\n /** Get current state */\n get(): FsmResult<FsmState> {\n if (!this.current) return { success: false, message: \"No state set\" };\n return { success: true, value: this.current };\n }\n\n /** Reset FSM */\n reset(): FsmResult<void> {\n this.current = null;\n this.currentParams = [];\n this.currentTime = 0;\n return { success: true };\n }\n\n /** Update current state */\n update(): FsmResult<void> | Promise<FsmResult<void>> {\n if (!this.current) return { success: false, message: \"No current state\" };\n\n const result = this.current.update(...this.currentParams);\n return result instanceof Promise ? result.then(() => ({ success: true })) : { success: true };\n }\n}\n\nexport class FsmState {\n constructor(public name: string) {}\n\n enter(_previous: FsmState | null, ..._params: any): void | Promise<void> {}\n exit(_next: FsmState | null, ..._params: any): void | Promise<void> {}\n update(..._params: any): void | Promise<void> {}\n}"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tmjeee/w-lib",
3
- "version": "0.0.1",
3
+ "version": "0.1.0",
4
4
  "description": "A minimal, modern TypeScript library skeleton ready for publishing to npmjs.com. Includes helloWorld sample.",
5
5
  "keywords": [
6
6
  "typescript",
@@ -52,6 +52,18 @@
52
52
  "publish:npm": "npm run build && npm run test && npm publish --access public",
53
53
  "publish:npm:dry": "npm run build && npm run test && npm publish --access public --dry-run"
54
54
  },
55
+ "dependencies": {
56
+ "@angular/animations": "^21.2.13",
57
+ "@angular/cdk": "^21.2.11",
58
+ "@angular/common": "^21.2.0",
59
+ "@angular/compiler": "^21.2.0",
60
+ "@angular/core": "^21.2.0",
61
+ "@angular/forms": "^21.2.0",
62
+ "@angular/material": "^21.2.11",
63
+ "@angular/platform-browser": "^21.2.0",
64
+ "@angular/router": "^21.2.0",
65
+ "rxjs": "^7.8.2"
66
+ },
55
67
  "devDependencies": {
56
68
  "@types/node": "^22.15.0",
57
69
  "tsdown": "^0.20.3",