@rplx/angular 0.2.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/README.md ADDED
@@ -0,0 +1,78 @@
1
+ # @rplx/angular
2
+
3
+ Angular bindings for Ripple state management library using Angular Signals.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @rplx/angular @rplx/core
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ### 1. Setup Store Provider
14
+
15
+ In your `main.ts` or module:
16
+
17
+ ```typescript
18
+ import { bootstrapApplication } from '@angular/platform-browser';
19
+ import { provideRippleStore } from '@rplx/angular';
20
+ import { createStore } from '@rplx/core';
21
+ import { AppComponent } from './app.component';
22
+
23
+ const store = createStore<AppState>({
24
+ initialState: { /* ... */ }
25
+ });
26
+
27
+ bootstrapApplication(AppComponent, {
28
+ providers: [
29
+ provideRippleStore(store)
30
+ ]
31
+ });
32
+ ```
33
+
34
+ ### 2. Use in Components
35
+
36
+ ```typescript
37
+ import { Component, computed } from '@angular/core';
38
+ import { injectStoreState, injectDispatch } from '@rplx/angular';
39
+
40
+ @Component({
41
+ selector: 'app-counter',
42
+ template: `
43
+ <div>
44
+ <p>Count: {{ count() }}</p>
45
+ <button (click)="increment()">Increment</button>
46
+ </div>
47
+ `
48
+ })
49
+ export class CounterComponent {
50
+ private dispatch = injectDispatch();
51
+ private state = injectStoreState<AppState>();
52
+
53
+ count = computed(() => this.state().counter.count);
54
+
55
+ increment() {
56
+ this.dispatch('counter/increment');
57
+ }
58
+ }
59
+ ```
60
+
61
+ ## API
62
+
63
+ ### `provideRippleStore<State>(store: StoreAPI<State>)`
64
+
65
+ Provider function to configure the Ripple store for dependency injection. The store should be created using `createStore()` from `@rplx/core`.
66
+
67
+ ### `injectStoreState<State>()`
68
+
69
+ Returns a signal containing the current store state.
70
+
71
+ ### `injectDispatch()`
72
+
73
+ Returns the dispatch function to send events to the store.
74
+
75
+ ### `RippleStoreService<State>`
76
+
77
+ The underlying service that wraps the store. You can inject this directly if needed.
78
+
@@ -0,0 +1,93 @@
1
+ import { Signal, Provider, InjectionToken } from '@angular/core';
2
+ import { StoreAPI } from '@rplx/core';
3
+
4
+ /**
5
+ * Service that wraps a Ripple Store and exposes state as an Angular Signal
6
+ * Note: This class is not decorated with @Injectable() because it's provided
7
+ * via an InjectionToken using a factory function, which avoids JIT compilation.
8
+ */
9
+ declare class RippleStoreService<State, Cofx = {}> {
10
+ private store;
11
+ private _state;
12
+ private unsubscribe?;
13
+ constructor(store: StoreAPI<State, Cofx>);
14
+ /**
15
+ * Get the current state as a Signal
16
+ */
17
+ get state(): Signal<State>;
18
+ /**
19
+ * Dispatch an event to the store
20
+ */
21
+ dispatch<Payload = any>(eventKey: string, payload?: Payload): Promise<void>;
22
+ /**
23
+ * Get the underlying store instance
24
+ */
25
+ getStore(): StoreAPI<State, Cofx>;
26
+ /**
27
+ * Cleanup: unsubscribe from state changes
28
+ */
29
+ ngOnDestroy(): void;
30
+ }
31
+
32
+ /**
33
+ * Injection token for RippleStoreService
34
+ */
35
+ declare const RIPPLE_STORE_SERVICE: InjectionToken<RippleStoreService<any, {}>>;
36
+ /**
37
+ * Provider function to configure Ripple store for dependency injection
38
+ *
39
+ * @param store The Ripple Store instance (created with createStore())
40
+ * @returns Provider configuration
41
+ */
42
+ declare function provideRippleStore<State, Cofx = {}>(store: StoreAPI<State, Cofx>): Provider;
43
+
44
+ /**
45
+ * Injection function to get the store state as a Signal
46
+ *
47
+ * @returns Signal containing the current store state
48
+ */
49
+ declare function injectStoreState<State>(): Signal<State>;
50
+
51
+ /**
52
+ * Injection function to get the dispatch function
53
+ *
54
+ * @returns Dispatch function to send events to the store
55
+ */
56
+ declare function injectDispatch<State = any>(): <Payload = any>(eventKey: string, payload?: Payload) => Promise<void>;
57
+
58
+ /**
59
+ * Injection function to subscribe to a Ripple subscription and get the value as a Signal
60
+ *
61
+ * This leverages the core subscription system which provides:
62
+ * - Shared computation across components (same subscription key+params share computation)
63
+ * - Dependency graphs (subscriptions can depend on other subscriptions)
64
+ * - Memoization and caching (only recomputes when dependencies change)
65
+ * - Selective updates (components only update when their subscribed values change)
66
+ *
67
+ * @param subscriptionKey The subscription key to subscribe to
68
+ * @param params Optional parameters for the subscription
69
+ * @returns Signal containing the subscription value
70
+ *
71
+ * @example
72
+ * ```typescript
73
+ * // Simple subscription without params
74
+ * const count = injectSubscription<number>('counter/count');
75
+ *
76
+ * // Subscription with params
77
+ * const todo = injectSubscription<Todo | undefined, [string]>('todos/byId', todoId);
78
+ *
79
+ * // Use in template
80
+ * <div>{{ count() }}</div>
81
+ * ```
82
+ */
83
+ declare function injectSubscription<Result, Params extends any[] = []>(subscriptionKey: string, ...params: Params): Signal<Result>;
84
+
85
+ /**
86
+ * Injection function to get the store instance directly
87
+ * Use this when you need direct access to the store API (e.g., for query, registerTraceCallback, etc.)
88
+ *
89
+ * @returns The underlying store instance
90
+ */
91
+ declare function injectStore<State = any, Cofx = {}>(): StoreAPI<State, Cofx>;
92
+
93
+ export { RIPPLE_STORE_SERVICE, RippleStoreService, injectDispatch, injectStore, injectStoreState, injectSubscription, provideRippleStore };
@@ -0,0 +1,93 @@
1
+ import { Signal, Provider, InjectionToken } from '@angular/core';
2
+ import { StoreAPI } from '@rplx/core';
3
+
4
+ /**
5
+ * Service that wraps a Ripple Store and exposes state as an Angular Signal
6
+ * Note: This class is not decorated with @Injectable() because it's provided
7
+ * via an InjectionToken using a factory function, which avoids JIT compilation.
8
+ */
9
+ declare class RippleStoreService<State, Cofx = {}> {
10
+ private store;
11
+ private _state;
12
+ private unsubscribe?;
13
+ constructor(store: StoreAPI<State, Cofx>);
14
+ /**
15
+ * Get the current state as a Signal
16
+ */
17
+ get state(): Signal<State>;
18
+ /**
19
+ * Dispatch an event to the store
20
+ */
21
+ dispatch<Payload = any>(eventKey: string, payload?: Payload): Promise<void>;
22
+ /**
23
+ * Get the underlying store instance
24
+ */
25
+ getStore(): StoreAPI<State, Cofx>;
26
+ /**
27
+ * Cleanup: unsubscribe from state changes
28
+ */
29
+ ngOnDestroy(): void;
30
+ }
31
+
32
+ /**
33
+ * Injection token for RippleStoreService
34
+ */
35
+ declare const RIPPLE_STORE_SERVICE: InjectionToken<RippleStoreService<any, {}>>;
36
+ /**
37
+ * Provider function to configure Ripple store for dependency injection
38
+ *
39
+ * @param store The Ripple Store instance (created with createStore())
40
+ * @returns Provider configuration
41
+ */
42
+ declare function provideRippleStore<State, Cofx = {}>(store: StoreAPI<State, Cofx>): Provider;
43
+
44
+ /**
45
+ * Injection function to get the store state as a Signal
46
+ *
47
+ * @returns Signal containing the current store state
48
+ */
49
+ declare function injectStoreState<State>(): Signal<State>;
50
+
51
+ /**
52
+ * Injection function to get the dispatch function
53
+ *
54
+ * @returns Dispatch function to send events to the store
55
+ */
56
+ declare function injectDispatch<State = any>(): <Payload = any>(eventKey: string, payload?: Payload) => Promise<void>;
57
+
58
+ /**
59
+ * Injection function to subscribe to a Ripple subscription and get the value as a Signal
60
+ *
61
+ * This leverages the core subscription system which provides:
62
+ * - Shared computation across components (same subscription key+params share computation)
63
+ * - Dependency graphs (subscriptions can depend on other subscriptions)
64
+ * - Memoization and caching (only recomputes when dependencies change)
65
+ * - Selective updates (components only update when their subscribed values change)
66
+ *
67
+ * @param subscriptionKey The subscription key to subscribe to
68
+ * @param params Optional parameters for the subscription
69
+ * @returns Signal containing the subscription value
70
+ *
71
+ * @example
72
+ * ```typescript
73
+ * // Simple subscription without params
74
+ * const count = injectSubscription<number>('counter/count');
75
+ *
76
+ * // Subscription with params
77
+ * const todo = injectSubscription<Todo | undefined, [string]>('todos/byId', todoId);
78
+ *
79
+ * // Use in template
80
+ * <div>{{ count() }}</div>
81
+ * ```
82
+ */
83
+ declare function injectSubscription<Result, Params extends any[] = []>(subscriptionKey: string, ...params: Params): Signal<Result>;
84
+
85
+ /**
86
+ * Injection function to get the store instance directly
87
+ * Use this when you need direct access to the store API (e.g., for query, registerTraceCallback, etc.)
88
+ *
89
+ * @returns The underlying store instance
90
+ */
91
+ declare function injectStore<State = any, Cofx = {}>(): StoreAPI<State, Cofx>;
92
+
93
+ export { RIPPLE_STORE_SERVICE, RippleStoreService, injectDispatch, injectStore, injectStoreState, injectSubscription, provideRippleStore };
package/dist/index.js ADDED
@@ -0,0 +1,148 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ RIPPLE_STORE_SERVICE: () => RIPPLE_STORE_SERVICE,
24
+ injectDispatch: () => injectDispatch,
25
+ injectStore: () => injectStore,
26
+ injectStoreState: () => injectStoreState,
27
+ injectSubscription: () => injectSubscription,
28
+ provideRippleStore: () => provideRippleStore
29
+ });
30
+ module.exports = __toCommonJS(index_exports);
31
+
32
+ // src/provideRippleStore.ts
33
+ var import_core2 = require("@angular/core");
34
+
35
+ // src/RippleStoreService.ts
36
+ var import_core = require("@angular/core");
37
+ var RippleStoreService = class {
38
+ constructor(store) {
39
+ this.store = store;
40
+ this._state = (0, import_core.signal)(this.store.getState());
41
+ const subscriptionKey = "__ripple_angular_state_tracker__";
42
+ try {
43
+ this.store.registerSubscription(subscriptionKey, {
44
+ compute: (state) => state
45
+ });
46
+ } catch (error) {
47
+ }
48
+ this.unsubscribe = this.store.subscribe(
49
+ subscriptionKey,
50
+ [],
51
+ (newState) => {
52
+ this._state.set(newState);
53
+ }
54
+ );
55
+ }
56
+ _state;
57
+ unsubscribe;
58
+ /**
59
+ * Get the current state as a Signal
60
+ */
61
+ get state() {
62
+ return this._state.asReadonly();
63
+ }
64
+ /**
65
+ * Dispatch an event to the store
66
+ */
67
+ dispatch(eventKey, payload) {
68
+ return this.store.dispatch(eventKey, payload);
69
+ }
70
+ /**
71
+ * Get the underlying store instance
72
+ */
73
+ getStore() {
74
+ return this.store;
75
+ }
76
+ /**
77
+ * Cleanup: unsubscribe from state changes
78
+ */
79
+ ngOnDestroy() {
80
+ if (this.unsubscribe) {
81
+ this.unsubscribe();
82
+ }
83
+ }
84
+ };
85
+
86
+ // src/provideRippleStore.ts
87
+ var RIPPLE_STORE_SERVICE = new import_core2.InjectionToken("RippleStoreService");
88
+ function provideRippleStore(store) {
89
+ return {
90
+ provide: RIPPLE_STORE_SERVICE,
91
+ useFactory: () => new RippleStoreService(store)
92
+ };
93
+ }
94
+
95
+ // src/injectStoreState.ts
96
+ var import_core3 = require("@angular/core");
97
+ function injectStoreState() {
98
+ const storeService = (0, import_core3.inject)(RIPPLE_STORE_SERVICE);
99
+ return storeService.state;
100
+ }
101
+
102
+ // src/injectDispatch.ts
103
+ var import_core4 = require("@angular/core");
104
+ function injectDispatch() {
105
+ const storeService = (0, import_core4.inject)(RIPPLE_STORE_SERVICE);
106
+ return (eventKey, payload) => storeService.dispatch(eventKey, payload);
107
+ }
108
+
109
+ // src/injectSubscription.ts
110
+ var import_core5 = require("@angular/core");
111
+ function injectSubscription(subscriptionKey, ...params) {
112
+ const storeService = (0, import_core5.inject)(RIPPLE_STORE_SERVICE);
113
+ const store = storeService.getStore();
114
+ const destroyRef = (0, import_core5.inject)(import_core5.DestroyRef);
115
+ const ngZone = (0, import_core5.inject)(import_core5.NgZone);
116
+ const subscriptionSignal = (0, import_core5.signal)(
117
+ store.query(subscriptionKey, params)
118
+ );
119
+ const unsubscribe = store.subscribe(
120
+ subscriptionKey,
121
+ params,
122
+ (result) => {
123
+ ngZone.run(() => {
124
+ subscriptionSignal.set(result);
125
+ });
126
+ }
127
+ );
128
+ destroyRef.onDestroy(() => {
129
+ unsubscribe();
130
+ });
131
+ return subscriptionSignal.asReadonly();
132
+ }
133
+
134
+ // src/injectStore.ts
135
+ var import_core6 = require("@angular/core");
136
+ function injectStore() {
137
+ const storeService = (0, import_core6.inject)(RIPPLE_STORE_SERVICE);
138
+ return storeService.getStore();
139
+ }
140
+ // Annotate the CommonJS export names for ESM import in node:
141
+ 0 && (module.exports = {
142
+ RIPPLE_STORE_SERVICE,
143
+ injectDispatch,
144
+ injectStore,
145
+ injectStoreState,
146
+ injectSubscription,
147
+ provideRippleStore
148
+ });
package/dist/index.mjs ADDED
@@ -0,0 +1,116 @@
1
+ // src/provideRippleStore.ts
2
+ import { InjectionToken } from "@angular/core";
3
+
4
+ // src/RippleStoreService.ts
5
+ import { signal } from "@angular/core";
6
+ var RippleStoreService = class {
7
+ constructor(store) {
8
+ this.store = store;
9
+ this._state = signal(this.store.getState());
10
+ const subscriptionKey = "__ripple_angular_state_tracker__";
11
+ try {
12
+ this.store.registerSubscription(subscriptionKey, {
13
+ compute: (state) => state
14
+ });
15
+ } catch (error) {
16
+ }
17
+ this.unsubscribe = this.store.subscribe(
18
+ subscriptionKey,
19
+ [],
20
+ (newState) => {
21
+ this._state.set(newState);
22
+ }
23
+ );
24
+ }
25
+ _state;
26
+ unsubscribe;
27
+ /**
28
+ * Get the current state as a Signal
29
+ */
30
+ get state() {
31
+ return this._state.asReadonly();
32
+ }
33
+ /**
34
+ * Dispatch an event to the store
35
+ */
36
+ dispatch(eventKey, payload) {
37
+ return this.store.dispatch(eventKey, payload);
38
+ }
39
+ /**
40
+ * Get the underlying store instance
41
+ */
42
+ getStore() {
43
+ return this.store;
44
+ }
45
+ /**
46
+ * Cleanup: unsubscribe from state changes
47
+ */
48
+ ngOnDestroy() {
49
+ if (this.unsubscribe) {
50
+ this.unsubscribe();
51
+ }
52
+ }
53
+ };
54
+
55
+ // src/provideRippleStore.ts
56
+ var RIPPLE_STORE_SERVICE = new InjectionToken("RippleStoreService");
57
+ function provideRippleStore(store) {
58
+ return {
59
+ provide: RIPPLE_STORE_SERVICE,
60
+ useFactory: () => new RippleStoreService(store)
61
+ };
62
+ }
63
+
64
+ // src/injectStoreState.ts
65
+ import { inject } from "@angular/core";
66
+ function injectStoreState() {
67
+ const storeService = inject(RIPPLE_STORE_SERVICE);
68
+ return storeService.state;
69
+ }
70
+
71
+ // src/injectDispatch.ts
72
+ import { inject as inject2 } from "@angular/core";
73
+ function injectDispatch() {
74
+ const storeService = inject2(RIPPLE_STORE_SERVICE);
75
+ return (eventKey, payload) => storeService.dispatch(eventKey, payload);
76
+ }
77
+
78
+ // src/injectSubscription.ts
79
+ import { inject as inject3, signal as signal2, DestroyRef, NgZone } from "@angular/core";
80
+ function injectSubscription(subscriptionKey, ...params) {
81
+ const storeService = inject3(RIPPLE_STORE_SERVICE);
82
+ const store = storeService.getStore();
83
+ const destroyRef = inject3(DestroyRef);
84
+ const ngZone = inject3(NgZone);
85
+ const subscriptionSignal = signal2(
86
+ store.query(subscriptionKey, params)
87
+ );
88
+ const unsubscribe = store.subscribe(
89
+ subscriptionKey,
90
+ params,
91
+ (result) => {
92
+ ngZone.run(() => {
93
+ subscriptionSignal.set(result);
94
+ });
95
+ }
96
+ );
97
+ destroyRef.onDestroy(() => {
98
+ unsubscribe();
99
+ });
100
+ return subscriptionSignal.asReadonly();
101
+ }
102
+
103
+ // src/injectStore.ts
104
+ import { inject as inject4 } from "@angular/core";
105
+ function injectStore() {
106
+ const storeService = inject4(RIPPLE_STORE_SERVICE);
107
+ return storeService.getStore();
108
+ }
109
+ export {
110
+ RIPPLE_STORE_SERVICE,
111
+ injectDispatch,
112
+ injectStore,
113
+ injectStoreState,
114
+ injectSubscription,
115
+ provideRippleStore
116
+ };
package/package.json ADDED
@@ -0,0 +1,55 @@
1
+ {
2
+ "name": "@rplx/angular",
3
+ "version": "0.2.0",
4
+ "description": "Angular bindings for Ripple state management",
5
+ "main": "./dist/index.js",
6
+ "module": "./dist/index.mjs",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./dist/index.mjs",
11
+ "require": "./dist/index.js",
12
+ "types": "./dist/index.d.ts"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist",
17
+ "README.md"
18
+ ],
19
+ "scripts": {
20
+ "build": "tsup src/index.ts --format cjs,esm --dts",
21
+ "dev": "tsup src/index.ts --format cjs,esm --dts --watch",
22
+ "clean": "rm -rf dist"
23
+ },
24
+ "peerDependencies": {
25
+ "@angular/core": ">=16.0.0"
26
+ },
27
+ "dependencies": {
28
+ "@rplx/core": "^0.2.0"
29
+ },
30
+ "devDependencies": {
31
+ "@angular/core": "^17.0.0",
32
+ "tsup": "^8.0.0",
33
+ "typescript": "^5.2.2"
34
+ },
35
+ "keywords": [
36
+ "state-management",
37
+ "angular",
38
+ "signals",
39
+ "re-frame",
40
+ "ripple"
41
+ ],
42
+ "license": "MIT",
43
+ "repository": {
44
+ "type": "git",
45
+ "url": "https://github.com/anonymeye/ripple.git",
46
+ "directory": "packages/ripple-angular"
47
+ },
48
+ "bugs": {
49
+ "url": "https://github.com/anonymeye/ripple/issues"
50
+ },
51
+ "homepage": "https://github.com/anonymeye/ripple#readme",
52
+ "publishConfig": {
53
+ "access": "public"
54
+ }
55
+ }