@actdim/dynstruct 1.2.8 → 1.2.9

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 CHANGED
@@ -2,11 +2,35 @@
2
2
 
3
3
  Build scalable applications with dynamic structured components, explicit wiring, and decoupled message flow. Keep architecture clean and modular.
4
4
 
5
- [![npm version](https://img.shields.io/npm/v/@actdim/dynstruct.svg)](https://www.npmjs.com/package/@actdim/dynstruct)
6
- [![TypeScript](https://img.shields.io/badge/TypeScript-5.9+-blue.svg)](https://www.typescriptlang.org/)
7
- [![License: Proprietary](https://img.shields.io/badge/License-Proprietary-red.svg)](LICENSE)
8
-
9
- ## Overview
5
+ [![npm version](https://img.shields.io/npm/v/@actdim/dynstruct.svg)](https://www.npmjs.com/package/@actdim/dynstruct)
6
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5.9+-blue.svg)](https://www.typescriptlang.org/)
7
+ [![License: Proprietary](https://img.shields.io/badge/License-Proprietary-red.svg)](LICENSE)
8
+
9
+ ## Table of Contents
10
+
11
+ - [Overview](#overview)
12
+ - [Framework Support](#framework-support)
13
+ - [Features](#features)
14
+ - [Quick Start](#quick-start)
15
+ - [How It Works](#how-it-works)
16
+ - [Installation](#installation)
17
+ - [Getting Started (React)](#getting-started-react)
18
+ - [Key Advantages (React Examples)](#key-advantages-react-examples)
19
+ - [Core Concepts](#core-concepts)
20
+ - [More Examples (React)](#more-examples-react)
21
+ - [Architecture](#architecture)
22
+ - [API Reference](#api-reference)
23
+ - [Storybook Examples](#storybook-examples)
24
+ - [Development](#development)
25
+ - [Package Management](#package-management)
26
+ - [Contributing](#contributing)
27
+ - [License](#license)
28
+ - [Author](#author)
29
+ - [Repository](#repository)
30
+ - [Issues](#issues)
31
+ - [Keywords](#keywords)
32
+
33
+ ## Overview
10
34
 
11
35
  **@actdim/dynstruct** is a TypeScript-based component system and architectural framework for building large-scale, modular applications. It provides a structure-first, declarative approach to component design with:
12
36
 
@@ -73,13 +97,14 @@ The core pattern in dynstruct is **structure-first composition** where parent co
73
97
  npm install @actdim/dynstruct
74
98
  ```
75
99
 
76
- ### Peer Dependencies
77
-
78
- This package requires the following peer dependencies:
79
-
80
- ```bash
81
- npm install react react-dom mobx mobx-react-lite mobx-utils \
82
- @actdim/msgmesh @actdim/utico react-router react-router-dom \
100
+ ### Peer Dependencies
101
+
102
+ This package requires the following peer dependencies:
103
+ For message bus functionality, install [@actdim/msgmesh](https://www.npmjs.com/package/@actdim/msgmesh).
104
+
105
+ ```bash
106
+ npm install react react-dom mobx mobx-react-lite mobx-utils \
107
+ @actdim/msgmesh @actdim/utico react-router react-router-dom \
83
108
  rxjs uuid path-to-regexp jwt-decode http-status
84
109
  ```
85
110
 
@@ -92,7 +117,7 @@ pnpm add @actdim/dynstruct @actdim/msgmesh @actdim/utico \
92
117
  jwt-decode http-status
93
118
  ```
94
119
 
95
- ## Quick Start (React)
120
+ ## Getting Started (React)
96
121
 
97
122
  > **Note:** All examples below are for the **React** implementation. SolidJS and Vue.js versions will have similar structure with framework-specific adapters.
98
123
 
@@ -824,7 +849,7 @@ type Struct = ComponentStruct<
824
849
 
825
850
  // List of effect names that will be available in this component.
826
851
  // Effect implementations are defined in ComponentDef (see below).
827
- effects: ['loadData', 'syncState'];
852
+ effects: ['computeSummary', 'trackCounter'];
828
853
  }
829
854
  >;
830
855
  ```
@@ -870,23 +895,24 @@ const useMyComponent = (params: ComponentParams<Struct>) => {
870
895
  },
871
896
 
872
897
  effects: {
873
- // Effect implementations. Effects are methods similar to actions
874
- // (or they simply call actions), but they run automatically as
875
- // soon as any property accessed within the effect implementation
876
- // changes.
898
+ // Effect implementations. Effects are auto-tracking reactive
899
+ // functions that re-run automatically whenever any reactive
900
+ // property accessed inside them changes.
877
901
  //
878
902
  // Effects are accessed on the component instance by name via
879
- // the `effects` property (e.g. c.effects.loadData).
903
+ // the `effects` property (e.g. c.effects.computeSummary).
880
904
  //
881
905
  // An effect runs immediately when the component is created and
882
906
  // can later be manually paused, resumed, or stopped entirely.
883
- loadData: (component) => {
884
- console.log('Items count:', m.items.length);
907
+ computeSummary: (component) => {
908
+ // Re-runs whenever m.items changes
909
+ m.message = `Total items: ${m.items.length}`;
885
910
  // Return an optional cleanup function
886
911
  return () => { /* cleanup */ };
887
912
  },
888
- syncState: (component) => {
889
- console.log('Counter is', m.counter);
913
+ trackCounter: (component) => {
914
+ // Re-runs whenever m.counter changes
915
+ if (m.counter > 100) m.message = 'Counter is high!';
890
916
  },
891
917
  },
892
918
 
@@ -1719,18 +1745,18 @@ const useForm = (params: ComponentParams<FormStruct>) => {
1719
1745
  isValid: false
1720
1746
  },
1721
1747
  events: {
1722
- // Validate email when it changes
1723
- onChangeEmail: (oldValue, newValue) => {
1748
+ // Validate email after it changes — onChange receives only the new value
1749
+ onChangeEmail: (value) => {
1724
1750
  const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
1725
- m.isValid = emailRegex.test(newValue) && m.password.length >= 6;
1751
+ m.isValid = emailRegex.test(value) && m.password.length >= 6;
1726
1752
  },
1727
1753
 
1728
- // Validate password when it changes
1729
- onChangePassword: (oldValue, newValue) => {
1730
- m.isValid = m.email.includes('@') && newValue.length >= 6;
1754
+ // Validate password after it changes
1755
+ onChangePassword: (value) => {
1756
+ m.isValid = m.email.includes('@') && value.length >= 6;
1731
1757
  },
1732
1758
 
1733
- // Sanitize input before setting
1759
+ // Sanitize input before setting — onChanging receives (oldValue, newValue)
1734
1760
  onChangingEmail: (oldValue, newValue) => {
1735
1761
  return newValue.toLowerCase().trim();
1736
1762
  }
@@ -1854,302 +1880,128 @@ const useEffectDemo = (params: ComponentParams<Struct>) => {
1854
1880
 
1855
1881
  In this example the `trackNameChanges` effect accesses `m.firstName` and `m.lastName`, so it re-runs whenever either changes. Clicking **Pause** calls `c.effects.trackNameChanges.pause()`, which suspends the auto-tracking — edits to the name fields no longer update `fullName` until **Resume** is clicked.
1856
1882
 
1857
- ## Examples (React)
1883
+ ## More Examples (React)
1858
1884
 
1859
- > **Note:** All examples below are for the **React** implementation.
1885
+ > **Note:** For basic examples (simple component, parent-child, bindings, events, effects) see the [Getting Started](#getting-started-react) and [Core Concepts](#core-concepts) sections above.
1860
1886
 
1861
- ### Example 1: Simple Counter Component
1887
+ ### Service Integration (API Calls)
1862
1888
 
1863
- ```typescript
1864
- // React implementation
1865
- import { ComponentStruct, ComponentDef, ComponentParams } from '@actdim/dynstruct/componentModel/contracts';
1866
- import { useComponent, toReact } from '@actdim/dynstruct/componentModel/react';
1867
- import { AppMsgStruct } from '@actdim/dynstruct/appDomain/appContracts';
1889
+ dynstruct integrates with the **service adapter** system from [@actdim/msgmesh](https://github.com/actdim/msgmesh/?tab=readme-ov-file#service-adapters). Adapters automatically register any service class (e.g. an API client) as a message bus provider — channel names, payload types, and return types are all derived from the service class at compile time.
1868
1890
 
1869
- type CounterStruct = ComponentStruct<AppMsgStruct, {
1870
- props: { count: number };
1871
- actions: { increment: () => void; decrement: () => void };
1872
- }>;
1891
+ #### 1. Define an API client
1873
1892
 
1874
- const useCounter = (params: ComponentParams<CounterStruct>) => {
1875
- const def: ComponentDef<CounterStruct> = {
1876
- props: { count: params.count ?? 0 },
1877
- actions: {
1878
- increment: () => { c.model.count++; },
1879
- decrement: () => { c.model.count--; }
1880
- },
1881
- view: (_, c) => (
1882
- <div>
1883
- <h2>Counter: {c.model.count}</h2>
1884
- <button onClick={c.actions.increment}>+</button>
1885
- <button onClick={c.actions.decrement}>-</button>
1886
- </div>
1887
- )
1888
- };
1893
+ ```typescript
1894
+ export type DataItem = { id: number; name: string };
1889
1895
 
1890
- const c = useComponent(def, params);
1891
- return c;
1892
- };
1896
+ export class TestApiClient {
1897
+ static readonly name = 'TestApiClient' as const;
1898
+ readonly name = 'TestApiClient' as const;
1893
1899
 
1894
- export const Counter = toReact(useCounter);
1900
+ getDataItems(param1: number[], param2: string[]): Promise<DataItem[]> {
1901
+ return fetch('/api/data').then(r => r.json());
1902
+ }
1903
+ }
1895
1904
  ```
1896
1905
 
1897
- ### Example 2: Component with Children
1906
+ #### 2. Set up adapters and service provider
1907
+
1908
+ Type utilities from [`@actdim/msgmesh/adapters`](https://www.npmjs.com/package/@actdim/msgmesh) transform the service class into a typed bus structure. Each public method becomes a channel (e.g. `getDataItems` → `API.TEST.GETDATAITEMS`). See [@actdim/msgmesh — Service Adapters](https://github.com/actdim/msgmesh/?tab=readme-ov-file#service-adapters) for details on how the type transformation works.
1898
1909
 
1899
1910
  ```typescript
1900
- // React implementation
1901
- import { ComponentStruct, ComponentDef, ComponentParams } from '@actdim/dynstruct/componentModel/contracts';
1902
- import { useComponent, toReact } from '@actdim/dynstruct/componentModel/react';
1903
- import { AppMsgStruct } from '@actdim/dynstruct/appDomain/appContracts';
1911
+ import {
1912
+ BaseServiceSuffix, getMsgChannelSelector, MsgProviderAdapter,
1913
+ ToMsgChannelPrefix, ToMsgStruct,
1914
+ } from '@actdim/msgmesh/adapters';
1915
+ import { ServiceProvider } from '@actdim/dynstruct/services/react/ServiceProvider';
1916
+
1917
+ // "TestApiClient" → remove suffix "Client" → uppercase → "API.TEST."
1918
+ type ApiPrefix = 'API';
1919
+ type TestApiChannelPrefix = ToMsgChannelPrefix<
1920
+ typeof TestApiClient.name, ApiPrefix, BaseServiceSuffix
1921
+ >;
1904
1922
 
1905
- // Child component
1906
- type ButtonStruct = ComponentStruct<AppMsgStruct, {
1907
- props: { label: string; onClick: () => void };
1908
- }>;
1923
+ // Transform service methods into a bus struct
1924
+ type ApiMsgStruct = ToMsgStruct<TestApiClient, TestApiChannelPrefix>;
1909
1925
 
1910
- const useButton = (params: ComponentParams<ButtonStruct>) => {
1911
- const def: ComponentDef<ButtonStruct> = {
1912
- props: {
1913
- label: params.label ?? 'Click',
1914
- onClick: params.onClick ?? (() => {})
1915
- },
1916
- view: (_, c) => (
1917
- <button onClick={c.model.onClick}>{c.model.label}</button>
1918
- )
1919
- };
1920
- return useComponent(def, params);
1926
+ // Create adapter instances
1927
+ const services: Record<TestApiChannelPrefix, any> = {
1928
+ 'API.TEST.': new TestApiClient(),
1921
1929
  };
1922
1930
 
1923
- // Parent component with children
1924
- type PanelStruct = ComponentStruct<AppMsgStruct, {
1925
- props: { title: string; clickCount: number };
1926
- children: {
1927
- okButton: ButtonStruct;
1928
- cancelButton: ButtonStruct;
1929
- };
1930
- }>;
1931
-
1932
- const usePanel = (params: ComponentParams<PanelStruct>) => {
1933
- let c: Component<PanelStruct>;
1934
- let m: ComponentModel<PanelStruct>;
1935
-
1936
- const def: ComponentDef<PanelStruct> = {
1937
- props: {
1938
- title: params.title ?? 'Panel',
1939
- clickCount: 0
1940
- },
1941
- children: {
1942
- okButton: useButton({
1943
- label: 'OK',
1944
- onClick: () => {
1945
- m.clickCount++; // Reactive property update
1946
- console.log('OK clicked');
1947
- }
1948
- }),
1949
- cancelButton: useButton({
1950
- label: 'Cancel',
1951
- onClick: () => console.log('Cancel clicked')
1952
- })
1953
- },
1954
- view: (_, c) => (
1955
- <div className="panel">
1956
- <h3>{m.title}</h3>
1957
- <p>Clicks: {m.clickCount}</p>
1958
- <div className="buttons">
1959
- <c.children.okButton.View />
1960
- <c.children.cancelButton.View />
1961
- </div>
1962
- </div>
1963
- )
1964
- };
1965
-
1966
- c = useComponent(def, params);
1967
- m = c.model;
1968
- return c;
1969
- };
1931
+ const msgProviderAdapters = Object.entries(services).map(
1932
+ ([_, service]) => ({
1933
+ service,
1934
+ channelSelector: getMsgChannelSelector(services),
1935
+ }) as MsgProviderAdapter,
1936
+ );
1970
1937
 
1971
- export const Panel = toReact(usePanel);
1938
+ // React provider component — wraps children with registered adapters
1939
+ export const ApiServiceProvider = () => ServiceProvider({ adapters: msgProviderAdapters });
1972
1940
  ```
1973
1941
 
1974
- ### Example 3: Message Bus Producer/Consumer
1975
-
1976
- ```typescript
1977
- // React implementation
1978
- import { ComponentStruct, ComponentDef, ComponentParams, Component, ComponentModel } from '@actdim/dynstruct/componentModel/contracts';
1979
- import { useComponent, toReact } from '@actdim/dynstruct/componentModel/react';
1980
- import { AppMsgStruct } from '@actdim/dynstruct/appDomain/appContracts';
1942
+ #### 3. Use in a component
1981
1943
 
1982
- // Producer Component
1983
- type ProducerStruct = ComponentStruct<AppMsgStruct, {
1984
- msgScope: {
1985
- provide: {
1986
- 'EVENT-FIRED': { timestamp: number; data: string };
1987
- };
1988
- };
1989
- }>;
1944
+ Data loading is a plain async function — it has nothing to do with effects. Call it from an event handler (`onReady`) or directly from a button click.
1990
1945
 
1991
- const useProducer = (params: ComponentParams<ProducerStruct>) => {
1992
- const def: ComponentDef<ProducerStruct> = {
1993
- // msgBroker is part of ComponentDef
1994
- msgBroker: {
1995
- provide: {
1996
- 'EVENT-FIRED': {
1997
- callback: () => ({
1998
- timestamp: Date.now(),
1999
- data: 'Event fired from producer'
2000
- })
2001
- }
2002
- }
2003
- },
2004
- view: (_, c) => (
2005
- <button onClick={() => {
2006
- // Use component's msgBus to send
2007
- c.msgBus.send({
2008
- channel: 'EVENT-FIRED',
2009
- payload: {}
2010
- });
2011
- }}>
2012
- Fire Event
2013
- </button>
2014
- )
1946
+ ```typescript
1947
+ type Struct = ComponentStruct<ApiMsgStruct, {
1948
+ props: {
1949
+ dataItems: DataItem[];
2015
1950
  };
2016
- return useComponent(def, params);
2017
- };
2018
-
2019
- export const Producer = toReact(useProducer);
2020
-
2021
- // Consumer Component
2022
- type ConsumerStruct = ComponentStruct<AppMsgStruct, {
2023
- props: { lastEvent: string };
2024
1951
  msgScope: {
2025
- subscribe: {
2026
- 'EVENT-FIRED': { timestamp: number; data: string };
2027
- };
1952
+ subscribe: ComponentMsgChannels<'API.TEST.GETDATAITEMS'>;
1953
+ publish: ComponentMsgChannels<'API.TEST.GETDATAITEMS'>;
2028
1954
  };
2029
1955
  }>;
2030
1956
 
2031
- const useConsumer = (params: ComponentParams<ConsumerStruct>) => {
2032
- let c: Component<ConsumerStruct>;
2033
- let m: ComponentModel<ConsumerStruct>;
2034
-
2035
- const def: ComponentDef<ConsumerStruct> = {
2036
- props: { lastEvent: 'No events yet' },
2037
- // msgBroker subscribes to messages
2038
- msgBroker: {
2039
- subscribe: {
2040
- 'EVENT-FIRED': {
2041
- callback: (msg) => {
2042
- // Update reactive property
2043
- m.lastEvent = `${msg.payload.data} at ${new Date(msg.payload.timestamp).toLocaleTimeString()}`;
2044
- }
2045
- }
2046
- }
2047
- },
2048
- view: (_, c) => (
2049
- <div>
2050
- <p>Last Event: {m.lastEvent}</p>
2051
- </div>
2052
- )
2053
- };
2054
-
2055
- c = useComponent(def, params);
2056
- m = c.model; // Properties are now reactive
2057
- return c;
2058
- };
2059
-
2060
- export const Consumer = toReact(useConsumer);
2061
- ```
2062
-
2063
- ### Example 4: Service Integration (API Calls)
2064
-
2065
- ```typescript
2066
- // React implementation
2067
- import { ComponentStruct, ComponentDef, ComponentParams, Component, ComponentModel } from '@actdim/dynstruct/componentModel/contracts';
2068
- import { useComponent, toReact } from '@actdim/dynstruct/componentModel/react';
2069
- import { AppMsgStruct } from '@actdim/dynstruct/appDomain/appContracts';
2070
- import { ClientBase } from '@actdim/dynstruct/net/client';
2071
- import { MsgProviderAdapter } from '@actdim/dynstruct/componentModel/adapters';
2072
- import { ServiceProvider } from '@actdim/dynstruct/services/ServiceProvider';
2073
-
2074
- // Define API client
2075
- class UserApiClient extends ClientBase {
2076
- constructor() {
2077
- super({ baseUrl: 'https://api.example.com' });
2078
- }
2079
-
2080
- async getUsers() {
2081
- return this.get<User[]>('/users');
2082
- }
1957
+ const useApiCallExample = (params: ComponentParams<Struct>) => {
1958
+ let c: Component<Struct>;
1959
+ let m: ComponentModel<Struct>;
2083
1960
 
2084
- async createUser(data: CreateUserDto) {
2085
- return this.post<User>('/users', data);
1961
+ // Plain async function not an effect, not an action
1962
+ async function loadData() {
1963
+ const msg = await c.msgBus.request({
1964
+ channel: 'API.TEST.GETDATAITEMS',
1965
+ payloadFn: (fn) => fn([1, 2], ['first', 'second']),
1966
+ });
1967
+ m.dataItems = msg.payload;
2086
1968
  }
2087
1969
 
2088
- async deleteUser(id: string) {
2089
- return this.delete(`/users/${id}`);
1970
+ async function clear() {
1971
+ m.dataItems.length = 0;
2090
1972
  }
2091
- }
2092
-
2093
- // Create adapter
2094
- const userApiAdapter: MsgProviderAdapter<UserApiClient> = {
2095
- service: new UserApiClient(),
2096
- channelSelector: (service, method) => `API.USERS.${method.toUpperCase()}`
2097
- };
2098
-
2099
- // Register in app provider
2100
- export const ApiServiceProvider = () =>
2101
- ServiceProvider({ adapters: [userApiAdapter] });
2102
-
2103
- // Use in component
2104
- type AppStruct = ComponentStruct<AppMsgStruct, {
2105
- props: { users: User[]; loading: boolean };
2106
- }>;
2107
-
2108
- const useApp = (params: ComponentParams<AppStruct>) => {
2109
- let c: Component<AppStruct>;
2110
- let m: ComponentModel<AppStruct>;
2111
1973
 
2112
- const def: ComponentDef<AppStruct> = {
1974
+ const def: ComponentDef<Struct> = {
1975
+ regType: 'ApiCallExample',
2113
1976
  props: {
2114
- users: [],
2115
- loading: false
1977
+ dataItems: [],
2116
1978
  },
2117
- effects: {
2118
- 'loadUsers': async (component) => {
2119
- m.loading = true; // Reactive update
2120
- const response = await component.msgBus.request({
2121
- channel: 'API.USERS.GETUSERS',
2122
- payload: {}
2123
- });
2124
- m.users = response.payload; // Reactive update
2125
- m.loading = false;
2126
- }
1979
+ events: {
1980
+ // Load data when the component is ready
1981
+ onReady: () => { loadData(); },
2127
1982
  },
2128
1983
  view: (_, c) => (
2129
- <div>
2130
- <h2>Users</h2>
2131
- {m.loading ? (
2132
- <p>Loading...</p>
2133
- ) : (
2134
- <ul>
2135
- {m.users.map(user => (
2136
- <li key={user.id}>{user.name}</li>
2137
- ))}
2138
- </ul>
2139
- )}
1984
+ <div id={c.id}>
1985
+ <button onClick={loadData}>Load data</button>
1986
+ <button onClick={clear}>Clear</button>
1987
+ <ul>
1988
+ {m.dataItems.map((item) => (
1989
+ <li key={item.id}>{item.id}: {item.name}</li>
1990
+ ))}
1991
+ </ul>
2140
1992
  </div>
2141
- )
1993
+ ),
2142
1994
  };
2143
1995
 
2144
1996
  c = useComponent(def, params);
2145
- m = c.model; // Properties are reactive after useComponent
1997
+ m = c.model;
2146
1998
  return c;
2147
1999
  };
2148
2000
 
2149
- export const App = toReact(useApp);
2001
+ export const ApiCallExample = toReact(useApiCallExample);
2150
2002
  ```
2151
2003
 
2152
- ### Example 5: Navigation
2004
+ ### Navigation
2153
2005
 
2154
2006
  ```typescript
2155
2007
  // React implementation
@@ -2196,7 +2048,7 @@ const usePage = (params: ComponentParams<PageStruct>) => {
2196
2048
  export const Page = toReact(usePage);
2197
2049
  ```
2198
2050
 
2199
- ### Example 6: Authentication & Security
2051
+ ### Authentication & Security
2200
2052
 
2201
2053
  ```typescript
2202
2054
  // React implementation
@@ -2321,9 +2173,11 @@ Messages can be filtered by source using `ComponentMsgFilter`:
2321
2173
  msgBroker: {
2322
2174
  subscribe: {
2323
2175
  'MY-EVENT': {
2324
- filter: { FromAncestors: true },
2325
- callback: (msg) => {
2326
- // Only triggered by parent components
2176
+ in: {
2177
+ callback: (msg, component) => {
2178
+ // Only triggered by parent/ancestor components
2179
+ },
2180
+ componentFilter: ComponentMsgFilter.FromAncestors
2327
2181
  }
2328
2182
  }
2329
2183
  }
@@ -1,5 +1,5 @@
1
1
  import { BaseAppMsgStruct } from '../appDomain/appContracts';
2
- import { MsgProviderAdapter } from '../componentModel/adapters';
2
+ import { MsgProviderAdapter } from '@actdim/msgmesh/adapters';
3
3
  import { MsgBus } from '@actdim/msgmesh/contracts';
4
4
  export declare function createServiceProvider(msgBus: MsgBus<BaseAppMsgStruct>, adapters?: MsgProviderAdapter[], abortSignal?: AbortSignal): {
5
5
  adapters: MsgProviderAdapter[];
@@ -1 +1 @@
1
- {"version":3,"file":"ServiceProvider.d.ts","sourceRoot":"","sources":["../../src/services/ServiceProvider.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,EAAE,kBAAkB,EAAoB,MAAM,4BAA4B,CAAC;AAClF,OAAO,EAAE,MAAM,EAAE,MAAM,2BAA2B,CAAC;AAEnD,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,CAAC,gBAAgB,CAAC,EAAE,QAAQ,CAAC,EAAE,kBAAkB,EAAE,EAAE,WAAW,CAAC,EAAE,WAAW;;;EAUjI"}
1
+ {"version":3,"file":"ServiceProvider.d.ts","sourceRoot":"","sources":["../../src/services/ServiceProvider.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,EAAE,kBAAkB,EAAoB,MAAM,0BAA0B,CAAC;AAChF,OAAO,EAAE,MAAM,EAAE,MAAM,2BAA2B,CAAC;AAEnD,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,CAAC,gBAAgB,CAAC,EAAE,QAAQ,CAAC,EAAE,kBAAkB,EAAE,EAAE,WAAW,CAAC,EAAE,WAAW;;;EAUjI"}
@@ -1,4 +1,4 @@
1
- import { registerAdapters as n } from "../componentModel/adapters.es.js";
1
+ import { registerAdapters as n } from "@actdim/msgmesh/adapters";
2
2
  function i(o, e, r) {
3
3
  const t = new AbortController();
4
4
  return r && (r = AbortSignal.any([r, t.signal])), n(o, e, r), {
@@ -1,5 +1,5 @@
1
+ import { MsgProviderAdapter } from '@actdim/msgmesh/adapters';
1
2
  import { PropsWithChildren, default as React } from 'react';
2
- import { MsgProviderAdapter } from '../../componentModel/adapters';
3
3
  export declare function ServiceProvider(props: PropsWithChildren<{
4
4
  adapters?: MsgProviderAdapter[];
5
5
  abortSignal?: AbortSignal;
@@ -1 +1 @@
1
- {"version":3,"file":"ServiceProvider.d.ts","sourceRoot":"","sources":["../../../src/services/react/ServiceProvider.tsx"],"names":[],"mappings":"AACA,OAAO,EAAiB,iBAAiB,EAAmB,MAAM,OAAO,CAAC;AAC1E,OAAO,EAAE,kBAAkB,EAAoB,MAAM,+BAA+B,CAAC;AACrF,OAAO,KAAK,MAAM,OAAO,CAAC;AAO1B,wBAAgB,eAAe,CAC3B,KAAK,EAAE,iBAAiB,CAAC;IACrB,QAAQ,CAAC,EAAE,kBAAkB,EAAE,CAAC;IAChC,WAAW,CAAC,EAAE,WAAW,CAAC;CAC7B,CAAC,qBAyBL"}
1
+ {"version":3,"file":"ServiceProvider.d.ts","sourceRoot":"","sources":["../../../src/services/react/ServiceProvider.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAoB,MAAM,0BAA0B,CAAC;AAChF,OAAO,EAAiB,iBAAiB,EAAmB,MAAM,OAAO,CAAC;AAC1E,OAAO,KAAK,MAAM,OAAO,CAAC;AAO1B,wBAAgB,eAAe,CAC3B,KAAK,EAAE,iBAAiB,CAAC;IACrB,QAAQ,CAAC,EAAE,kBAAkB,EAAE,CAAC;IAChC,WAAW,CAAC,EAAE,WAAW,CAAC;CAC7B,CAAC,qBAyBL"}
@@ -1,13 +1,13 @@
1
1
  import { useComponentContext as a } from "../../componentModel/componentContext.es.js";
2
- import n, { createContext as i, useLayoutEffect as c } from "react";
3
- import { registerAdapters as l } from "../../componentModel/adapters.es.js";
4
- const s = i(void 0);
2
+ import { registerAdapters as n } from "@actdim/msgmesh/adapters";
3
+ import i, { createContext as c, useLayoutEffect as l } from "react";
4
+ const s = c(void 0);
5
5
  function f(t) {
6
6
  const r = a(), o = new AbortController();
7
7
  let e = t.abortSignal;
8
- return e && (e = AbortSignal.any([e, o.signal])), c(() => (l(r.msgBus, t.adapters, e), () => {
8
+ return e && (e = AbortSignal.any([e, o.signal])), l(() => (n(r.msgBus, t.adapters, e), () => {
9
9
  o.abort();
10
- }), [r.msgBus, t.adapters]), /* @__PURE__ */ n.createElement(
10
+ }), [r.msgBus, t.adapters]), /* @__PURE__ */ i.createElement(
11
11
  s.Provider,
12
12
  {
13
13
  value: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@actdim/dynstruct",
3
- "version": "1.2.8",
3
+ "version": "1.2.9",
4
4
  "description": "A type-safe component system for large-scale apps: explicit dependencies, message bus communication, and structure-first, declarative design",
5
5
  "author": "Pavel Borodaev",
6
6
  "license": "Proprietary",
@@ -71,7 +71,7 @@
71
71
  "typecheck": "tsc -p tsconfig.json --noEmit"
72
72
  },
73
73
  "peerDependencies": {
74
- "@actdim/msgmesh": "^1.2.7",
74
+ "@actdim/msgmesh": "^1.2.9",
75
75
  "@actdim/utico": "^1.1.2",
76
76
  "http-status": "^2.1.0",
77
77
  "jwt-decode": "^4.0.0",
@@ -1,20 +0,0 @@
1
- import { MsgBus, MsgStructFactory } from '@actdim/msgmesh/contracts';
2
- import { AddPrefix, Filter, Func, RemoveSuffix, Skip, ToUpper } from '@actdim/utico/typeCore';
3
- export type MsgProviderAdapter = {
4
- service: any;
5
- channelSelector: (service: any, methodName: string) => string;
6
- };
7
- export declare function registerAdapters(msgBus: MsgBus<any>, adapters: MsgProviderAdapter[], abortSignal?: AbortSignal): void;
8
- export type BaseServiceSuffix = 'CLIENT' | 'API' | 'SERVICE' | 'FETCHER' | 'CONTROLLER' | 'LOADER' | 'REPOSITORY' | 'PROVIDER';
9
- export type BaseWordSeparator = ".";
10
- export type ToMsgChannelPrefix<TServiceName extends string, Prefix extends string, Suffix extends string = BaseServiceSuffix, WordSeparator extends string = BaseWordSeparator> = `${Prefix}${WordSeparator}${RemoveSuffix<Uppercase<TServiceName>, Suffix>}${WordSeparator}`;
11
- type ToMsgStructSource<TService, TPrefix extends string, TSkip extends keyof TService = never> = Filter<ToUpper<AddPrefix<Skip<TService, TSkip>, TPrefix>>, Func>;
12
- export type ToMsgStruct<TService, TPrefix extends string, TSkip extends keyof TService = never, TMsgStructSource = ToMsgStructSource<TService, TPrefix, TSkip>> = MsgStructFactory<{
13
- [K in keyof TMsgStructSource as TMsgStructSource[K] extends Func ? (Uppercase<K extends string ? K : never>) : never]: {
14
- in: TMsgStructSource[K] extends Func ? Parameters<TMsgStructSource[K]> : never;
15
- out: TMsgStructSource[K] extends Func ? ReturnType<TMsgStructSource[K]> : never;
16
- };
17
- }>;
18
- export declare function getMsgChannelSelector<TTPrefix extends string>(services: Record<TTPrefix, any>): (service: any, methodName: string) => string;
19
- export {};
20
- //# sourceMappingURL=adapters.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"adapters.d.ts","sourceRoot":"","sources":["../../src/componentModel/adapters.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AACrE,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AAY9F,MAAM,MAAM,kBAAkB,GAAG;IAC7B,OAAO,EAAE,GAAG,CAAC;IAEb,eAAe,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,UAAU,EAAE,MAAM,KAAK,MAAM,CAAC;CACjE,CAAC;AAEF,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE,QAAQ,EAAE,kBAAkB,EAAE,EAAE,WAAW,CAAC,EAAE,WAAW,QAwB9G;AAED,MAAM,MAAM,iBAAiB,GAAG,QAAQ,GAAG,KAAK,GAAG,SAAS,GAAG,SAAS,GAAG,YAAY,GAAG,QAAQ,GAAG,YAAY,GAAG,UAAU,CAAC;AAC/H,MAAM,MAAM,iBAAiB,GAAG,GAAG,CAAC;AAIpC,MAAM,MAAM,kBAAkB,CAC1B,YAAY,SAAS,MAAM,EAC3B,MAAM,SAAS,MAAM,EACrB,MAAM,SAAS,MAAM,GAAG,iBAAiB,EACzC,aAAa,SAAS,MAAM,GAAG,iBAAiB,IAChD,GAAG,MAAM,GAAG,aAAa,GAAG,YAAY,CAAC,SAAS,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC,GAAG,aAAa,EAAE,CAAC;AAEhG,KAAK,iBAAiB,CAAC,QAAQ,EAAE,OAAO,SAAS,MAAM,EAAE,KAAK,SAAS,MAAM,QAAQ,GAAG,KAAK,IAAI,MAAM,CACnG,OAAO,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,EAAE,KAAK,CAAC,EAAE,OAAO,CAAC,CAAC,EAClD,IAAI,CACP,CAAC;AAEF,MAAM,MAAM,WAAW,CAAC,QAAQ,EAAE,OAAO,SAAS,MAAM,EAAE,KAAK,SAAS,MAAM,QAAQ,GAAG,KAAK,EAAE,gBAAgB,GAAG,iBAAiB,CAAC,QAAQ,EAAE,OAAO,EAAE,KAAK,CAAC,IAAI,gBAAgB,CAAC;KAC9K,CAAC,IAAI,MAAM,gBAAgB,IAAI,gBAAgB,CAAC,CAAC,CAAC,SAAS,IAAI,GAAG,CAAC,SAAS,CAAC,CAAC,SAAS,MAAM,GAAG,CAAC,GAAG,KAAK,CAAC,CAAC,GAAG,KAAK,GAAG;QACnH,EAAE,EAAE,gBAAgB,CAAC,CAAC,CAAC,SAAS,IAAI,GAAG,UAAU,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC;QAC/E,GAAG,EAAE,gBAAgB,CAAC,CAAC,CAAC,SAAS,IAAI,GAAG,UAAU,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC;KACnF;CACJ,CAAC,CAAC;AAEH,wBAAgB,qBAAqB,CAAC,QAAQ,SAAS,MAAM,EACzD,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,GAAG,CAAC,IAEvB,SAAS,GAAG,EAAE,YAAY,MAAM,YAO3C"}
@@ -1,33 +0,0 @@
1
- const s = (t) => Object.getOwnPropertyNames(t).filter(
2
- (e) => e !== "constructor" && typeof t[e] == "function"
3
- );
4
- function l(t, e, n) {
5
- if (e)
6
- for (const o of e) {
7
- const { service: r, channelSelector: c } = o;
8
- if (!r || !c)
9
- throw new Error("Service and channelSelector are required for an adapter");
10
- for (const a of s(Object.getPrototypeOf(r))) {
11
- const f = c?.(r, a);
12
- f && t.provide({
13
- channel: f,
14
- topic: "/.*/",
15
- callback: (i) => r[a](...i.payload || []),
16
- options: {
17
- abortSignal: n
18
- }
19
- });
20
- }
21
- }
22
- }
23
- function p(t) {
24
- return (e, n) => {
25
- const o = Object.entries(t).find((r) => r[1] === e);
26
- return o ? `${o[0]}${n.toUpperCase()}` : null;
27
- };
28
- }
29
- export {
30
- p as getMsgChannelSelector,
31
- l as registerAdapters
32
- };
33
- //# sourceMappingURL=adapters.es.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"adapters.es.js","sources":["../../src/componentModel/adapters.ts"],"sourcesContent":null,"names":["getMethodNames","client","name","registerAdapters","msgBus","adapters","abortSignal","adapter","service","channelSelector","methodName","channel","msg","getMsgChannelSelector","services","entry"],"mappings":"AAIA,MAAMA,IAAiB,CAACC,MAEb,OAAO,oBAAoBA,CAAM,EAAE;AAAA,EACtC,CAACC,MAASA,MAAS,iBAAiB,OAAOD,EAAOC,CAAI,KAAM;AAAA;AAa7D,SAASC,EAAiBC,GAAqBC,GAAgCC,GAA2B;AAC7G,MAAID;AACA,eAAWE,KAAWF,GAAU;AAC5B,YAAM,EAAE,SAAAG,GAAS,iBAAAC,EAAA,IAAoBF;AACrC,UAAI,CAACC,KAAW,CAACC;AACb,cAAM,IAAI,MAAM,yDAAyD;AAE7E,iBAAWC,KAAcV,EAAe,OAAO,eAAeQ,CAAO,CAAC,GAAG;AACrE,cAAMG,IAAUF,IAAkBD,GAASE,CAAU;AACrD,QAAIC,KACAP,EAAO,QAAQ;AAAA,UACX,SAAAO;AAAA,UACA,OAAO;AAAA,UACP,UAAU,CAACC,MACCJ,EAAQE,CAAU,EAAW,GAAKE,EAAI,WAAW,CAAA,CAAa;AAAA,UAE1E,SAAS;AAAA,YACL,aAAAN;AAAA,UAAA;AAAA,QACJ,CACH;AAAA,MAET;AAAA,IACJ;AAER;AA0BO,SAASO,EACZC,GACF;AACE,SAAO,CAACN,GAAcE,MAAuB;AACzC,UAAMK,IAAQ,OAAO,QAAQD,CAAQ,EAAE,KAAK,CAACC,MAAUA,EAAM,CAAC,MAAMP,CAAO;AAC3E,WAAKO,IAGE,GAAGA,EAAM,CAAC,CAAC,GAAGL,EAAW,aAAa,KAFlC;AAAA,EAGf;AACJ;"}