@actdim/dynstruct 1.2.8 → 1.3.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 +145 -282
- package/dist/appDomain/appContracts.d.ts +3 -3
- package/dist/appDomain/appContracts.d.ts.map +1 -1
- package/dist/appDomain/security/securityContracts.d.ts +4 -4
- package/dist/appDomain/security/securityContracts.d.ts.map +1 -1
- package/dist/componentModel/DynamicContent.es.js +3 -3
- package/dist/componentModel/DynamicContent.es.js.map +1 -1
- package/dist/componentModel/contracts.d.ts +5 -5
- package/dist/componentModel/contracts.d.ts.map +1 -1
- package/dist/componentModel/contracts.es.js.map +1 -1
- package/dist/componentModel/core.d.ts +2 -2
- package/dist/componentModel/core.d.ts.map +1 -1
- package/dist/componentModel/core.es.js +102 -110
- package/dist/componentModel/core.es.js.map +1 -1
- package/dist/componentModel/react.d.ts.map +1 -1
- package/dist/componentModel/react.es.js +118 -113
- package/dist/componentModel/react.es.js.map +1 -1
- package/dist/services/ServiceProvider.d.ts +1 -1
- package/dist/services/ServiceProvider.d.ts.map +1 -1
- package/dist/services/ServiceProvider.es.js +1 -1
- package/dist/services/react/ServiceProvider.d.ts +1 -1
- package/dist/services/react/ServiceProvider.d.ts.map +1 -1
- package/dist/services/react/ServiceProvider.es.js +5 -5
- package/package.json +2 -2
- package/dist/componentModel/adapters.d.ts +0 -20
- package/dist/componentModel/adapters.d.ts.map +0 -1
- package/dist/componentModel/adapters.es.js +0 -33
- package/dist/componentModel/adapters.es.js.map +0 -1
package/README.md
CHANGED
|
@@ -6,6 +6,30 @@ Build scalable applications with dynamic structured components, explicit wiring,
|
|
|
6
6
|
[](https://www.typescriptlang.org/)
|
|
7
7
|
[](LICENSE)
|
|
8
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
|
+
|
|
9
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:
|
|
@@ -76,6 +100,7 @@ npm install @actdim/dynstruct
|
|
|
76
100
|
### Peer Dependencies
|
|
77
101
|
|
|
78
102
|
This package requires the following peer dependencies:
|
|
103
|
+
For message bus functionality, install [@actdim/msgmesh](https://www.npmjs.com/package/@actdim/msgmesh).
|
|
79
104
|
|
|
80
105
|
```bash
|
|
81
106
|
npm install react react-dom mobx mobx-react-lite mobx-utils \
|
|
@@ -92,7 +117,7 @@ pnpm add @actdim/dynstruct @actdim/msgmesh @actdim/utico \
|
|
|
92
117
|
jwt-decode http-status
|
|
93
118
|
```
|
|
94
119
|
|
|
95
|
-
##
|
|
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: ['
|
|
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
|
|
874
|
-
//
|
|
875
|
-
//
|
|
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.
|
|
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
|
-
|
|
884
|
-
|
|
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
|
-
|
|
889
|
-
|
|
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
|
|
|
@@ -953,8 +979,15 @@ const useMyComponent = (params: ComponentParams<Struct>) => {
|
|
|
953
979
|
// (accessed via component.children.*.View).
|
|
954
980
|
// This function is intended to be compact since all wiring
|
|
955
981
|
// and initialization code is distributed across other
|
|
956
|
-
// definition areas. Inline
|
|
957
|
-
//
|
|
982
|
+
// definition areas. Inline props should be used only in a
|
|
983
|
+
// functional wrapper component created via toReact (or a
|
|
984
|
+
// similar adapter) to integrate dynstruct into regular
|
|
985
|
+
// components (for example, at the app root level). Using such
|
|
986
|
+
// components directly inside dynstruct view functions
|
|
987
|
+
// keeps child parameter wiring mixed into render code instead
|
|
988
|
+
// of a dedicated children block, reduces compactness and
|
|
989
|
+
// readability, increases side-effect risk, and harms
|
|
990
|
+
// predictable reactivity.
|
|
958
991
|
view: (_, c) => (
|
|
959
992
|
<div>
|
|
960
993
|
<h3>{m.message}</h3>
|
|
@@ -980,8 +1013,8 @@ const useMyComponent = (params: ComponentParams<Struct>) => {
|
|
|
980
1013
|
| `effects` | Effect implementations — methods that run automatically when any property accessed within them changes. An effect runs on component creation and can be paused, resumed, or stopped via `c.effects.<name>`. Returns an optional cleanup function. |
|
|
981
1014
|
| `children` | Child component instances created via hook-constructors (`use*`). Properties can be initialized with values or bindings; external event handlers can be assigned. |
|
|
982
1015
|
| `events` | Component event handlers (lifecycle, property changes). See [Component Events](#component-events). |
|
|
983
|
-
| `msgBroker` | Message bus handlers for channels declared in the structure. Contains `provide` (response providers) and `subscribe` (message handlers) sections. |
|
|
984
|
-
| `msgBus` | Explicit message bus instance. If omitted, the bus from the component model context is used. Must be compatible with the declared message structure. |
|
|
1016
|
+
| `msgBroker` | Message bus handlers for channels declared in the structure. Contains `provide` (response providers) and `subscribe` (message handlers) sections. Handlers are registered through the component-scoped `msgBus`, so unmount cleanup semantics are applied to broker channels as well. |
|
|
1017
|
+
| `msgBus` | Explicit message bus instance. If omitted, the bus from the component model context is used. Must be compatible with the declared message structure. The component uses a lifecycle-scoped `msgBus` wrapper: on unmount, subscriptions are automatically canceled and pending requests are aborted via `AbortSignal`. |
|
|
985
1018
|
| `view` | Render function producing the component's JSX view. Child components are rendered via `c.children.<name>.View`. Intended to be compact — logic is distributed across other definition areas. |
|
|
986
1019
|
|
|
987
1020
|
### Reactive Properties
|
|
@@ -1049,13 +1082,13 @@ dynstruct integrates with **[@actdim/msgmesh](https://www.npmjs.com/package/@act
|
|
|
1049
1082
|
First, declare message channels at the application (or domain) level with full typing:
|
|
1050
1083
|
|
|
1051
1084
|
```typescript
|
|
1052
|
-
import {
|
|
1085
|
+
import { MsgStruct, MsgBus } from '@actdim/msgmesh/contracts';
|
|
1053
1086
|
import { createMsgBus } from '@actdim/msgmesh/core';
|
|
1054
1087
|
import { BaseAppMsgStruct } from '@actdim/dynstruct/appDomain/appContracts';
|
|
1055
1088
|
|
|
1056
1089
|
// Define your application's message structure
|
|
1057
1090
|
export type AppMsgStruct = BaseAppMsgStruct<AppRoutes> &
|
|
1058
|
-
|
|
1091
|
+
MsgStruct<{
|
|
1059
1092
|
// Event message (fire-and-forget)
|
|
1060
1093
|
'USER-CLICKED': {
|
|
1061
1094
|
in: { buttonId: string; timestamp: number };
|
|
@@ -1268,6 +1301,8 @@ const validationResult = await c.msgBus.request(
|
|
|
1268
1301
|
);
|
|
1269
1302
|
```
|
|
1270
1303
|
|
|
1304
|
+
> **Unmount safety:** `c.msgBus` is bound to component lifecycle. When the component is unmounted, active subscriptions are automatically unsubscribed and pending `request(...)` calls are aborted. This prevents updating state of an already destroyed component from late async responses.
|
|
1305
|
+
|
|
1271
1306
|
#### Message Filtering
|
|
1272
1307
|
|
|
1273
1308
|
Use **ComponentMsgFilter** to control which components can send messages to your handlers:
|
|
@@ -1573,7 +1608,7 @@ The full set of supported events is defined by the `ComponentEvents<TStruct>` ty
|
|
|
1573
1608
|
| `onLayoutReady` | mount | The HTML representation is ready and inserted into the DOM tree, but the frame has not been painted yet. |
|
|
1574
1609
|
| `onReady` | postMount | The HTML representation has already been rendered and is visible to the user. The component is fully ready for interaction. |
|
|
1575
1610
|
| `onLayoutDestroy` | preUnmount | The component's HTML representation is about to be removed from the DOM. |
|
|
1576
|
-
| `onDestroy` | unmount | The component is destroyed. All resources should be released. |
|
|
1611
|
+
| `onDestroy` | unmount | The component is destroyed. All resources should be released. The component-scoped `msgBus` abort signal is triggered, which safely terminates broker subscriptions/providers and pending `request(...)` operations. |
|
|
1577
1612
|
| `onError` | — | An error occurred during component operation. Receives the error object and optional info. |
|
|
1578
1613
|
|
|
1579
1614
|
```typescript
|
|
@@ -1719,18 +1754,18 @@ const useForm = (params: ComponentParams<FormStruct>) => {
|
|
|
1719
1754
|
isValid: false
|
|
1720
1755
|
},
|
|
1721
1756
|
events: {
|
|
1722
|
-
// Validate email
|
|
1723
|
-
onChangeEmail: (
|
|
1757
|
+
// Validate email after it changes — onChange receives only the new value
|
|
1758
|
+
onChangeEmail: (value) => {
|
|
1724
1759
|
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
1725
|
-
m.isValid = emailRegex.test(
|
|
1760
|
+
m.isValid = emailRegex.test(value) && m.password.length >= 6;
|
|
1726
1761
|
},
|
|
1727
1762
|
|
|
1728
|
-
// Validate password
|
|
1729
|
-
onChangePassword: (
|
|
1730
|
-
m.isValid = m.email.includes('@') &&
|
|
1763
|
+
// Validate password after it changes
|
|
1764
|
+
onChangePassword: (value) => {
|
|
1765
|
+
m.isValid = m.email.includes('@') && value.length >= 6;
|
|
1731
1766
|
},
|
|
1732
1767
|
|
|
1733
|
-
// Sanitize input before setting
|
|
1768
|
+
// Sanitize input before setting — onChanging receives (oldValue, newValue)
|
|
1734
1769
|
onChangingEmail: (oldValue, newValue) => {
|
|
1735
1770
|
return newValue.toLowerCase().trim();
|
|
1736
1771
|
}
|
|
@@ -1854,302 +1889,128 @@ const useEffectDemo = (params: ComponentParams<Struct>) => {
|
|
|
1854
1889
|
|
|
1855
1890
|
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
1891
|
|
|
1857
|
-
## Examples (React)
|
|
1892
|
+
## More Examples (React)
|
|
1858
1893
|
|
|
1859
|
-
> **Note:**
|
|
1894
|
+
> **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
1895
|
|
|
1861
|
-
###
|
|
1896
|
+
### Service Integration (API Calls)
|
|
1862
1897
|
|
|
1863
|
-
|
|
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';
|
|
1898
|
+
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
1899
|
|
|
1869
|
-
|
|
1870
|
-
props: { count: number };
|
|
1871
|
-
actions: { increment: () => void; decrement: () => void };
|
|
1872
|
-
}>;
|
|
1900
|
+
#### 1. Define an API client
|
|
1873
1901
|
|
|
1874
|
-
|
|
1875
|
-
|
|
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
|
-
};
|
|
1902
|
+
```typescript
|
|
1903
|
+
export type DataItem = { id: number; name: string };
|
|
1889
1904
|
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1905
|
+
export class TestApiClient {
|
|
1906
|
+
static readonly name = 'TestApiClient' as const;
|
|
1907
|
+
readonly name = 'TestApiClient' as const;
|
|
1893
1908
|
|
|
1894
|
-
|
|
1909
|
+
getDataItems(param1: number[], param2: string[]): Promise<DataItem[]> {
|
|
1910
|
+
return fetch('/api/data').then(r => r.json());
|
|
1911
|
+
}
|
|
1912
|
+
}
|
|
1895
1913
|
```
|
|
1896
1914
|
|
|
1897
|
-
|
|
1915
|
+
#### 2. Set up adapters and service provider
|
|
1916
|
+
|
|
1917
|
+
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
1918
|
|
|
1899
1919
|
```typescript
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1920
|
+
import {
|
|
1921
|
+
BaseServiceSuffix, getMsgChannelSelector, MsgProviderAdapter,
|
|
1922
|
+
ToMsgChannelPrefix, ToMsgStruct,
|
|
1923
|
+
} from '@actdim/msgmesh/adapters';
|
|
1924
|
+
import { ServiceProvider } from '@actdim/dynstruct/services/react/ServiceProvider';
|
|
1925
|
+
|
|
1926
|
+
// "TestApiClient" → remove suffix "Client" → uppercase → "API.TEST."
|
|
1927
|
+
type ApiPrefix = 'API';
|
|
1928
|
+
type TestApiChannelPrefix = ToMsgChannelPrefix<
|
|
1929
|
+
typeof TestApiClient.name, ApiPrefix, BaseServiceSuffix
|
|
1930
|
+
>;
|
|
1904
1931
|
|
|
1905
|
-
//
|
|
1906
|
-
type
|
|
1907
|
-
props: { label: string; onClick: () => void };
|
|
1908
|
-
}>;
|
|
1932
|
+
// Transform service methods into a bus struct
|
|
1933
|
+
type ApiMsgStruct = ToMsgStruct<TestApiClient, TestApiChannelPrefix>;
|
|
1909
1934
|
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
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);
|
|
1935
|
+
// Create adapter instances
|
|
1936
|
+
const services: Record<TestApiChannelPrefix, any> = {
|
|
1937
|
+
'API.TEST.': new TestApiClient(),
|
|
1921
1938
|
};
|
|
1922
1939
|
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
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
|
-
};
|
|
1940
|
+
const msgProviderAdapters = Object.entries(services).map(
|
|
1941
|
+
([_, service]) => ({
|
|
1942
|
+
service,
|
|
1943
|
+
channelSelector: getMsgChannelSelector(services),
|
|
1944
|
+
}) as MsgProviderAdapter,
|
|
1945
|
+
);
|
|
1970
1946
|
|
|
1971
|
-
|
|
1947
|
+
// React provider component — wraps children with registered adapters
|
|
1948
|
+
export const ApiServiceProvider = () => ServiceProvider({ adapters: msgProviderAdapters });
|
|
1972
1949
|
```
|
|
1973
1950
|
|
|
1974
|
-
|
|
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';
|
|
1951
|
+
#### 3. Use in a component
|
|
1981
1952
|
|
|
1982
|
-
|
|
1983
|
-
type ProducerStruct = ComponentStruct<AppMsgStruct, {
|
|
1984
|
-
msgScope: {
|
|
1985
|
-
provide: {
|
|
1986
|
-
'EVENT-FIRED': { timestamp: number; data: string };
|
|
1987
|
-
};
|
|
1988
|
-
};
|
|
1989
|
-
}>;
|
|
1953
|
+
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
1954
|
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
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
|
-
)
|
|
1955
|
+
```typescript
|
|
1956
|
+
type Struct = ComponentStruct<ApiMsgStruct, {
|
|
1957
|
+
props: {
|
|
1958
|
+
dataItems: DataItem[];
|
|
2015
1959
|
};
|
|
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
1960
|
msgScope: {
|
|
2025
|
-
subscribe:
|
|
2026
|
-
|
|
2027
|
-
};
|
|
1961
|
+
subscribe: ComponentMsgChannels<'API.TEST.GETDATAITEMS'>;
|
|
1962
|
+
publish: ComponentMsgChannels<'API.TEST.GETDATAITEMS'>;
|
|
2028
1963
|
};
|
|
2029
1964
|
}>;
|
|
2030
1965
|
|
|
2031
|
-
const
|
|
2032
|
-
let c: Component<
|
|
2033
|
-
let m: ComponentModel<
|
|
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
|
-
}
|
|
1966
|
+
const useApiCallExample = (params: ComponentParams<Struct>) => {
|
|
1967
|
+
let c: Component<Struct>;
|
|
1968
|
+
let m: ComponentModel<Struct>;
|
|
2083
1969
|
|
|
2084
|
-
async
|
|
2085
|
-
|
|
1970
|
+
// Plain async function — not an effect, not an action
|
|
1971
|
+
async function loadData() {
|
|
1972
|
+
const msg = await c.msgBus.request({
|
|
1973
|
+
channel: 'API.TEST.GETDATAITEMS',
|
|
1974
|
+
payloadFn: (fn) => fn([1, 2], ['first', 'second']),
|
|
1975
|
+
});
|
|
1976
|
+
m.dataItems = msg.payload;
|
|
2086
1977
|
}
|
|
2087
1978
|
|
|
2088
|
-
async
|
|
2089
|
-
|
|
1979
|
+
async function clear() {
|
|
1980
|
+
m.dataItems.length = 0;
|
|
2090
1981
|
}
|
|
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
1982
|
|
|
2112
|
-
const def: ComponentDef<
|
|
1983
|
+
const def: ComponentDef<Struct> = {
|
|
1984
|
+
regType: 'ApiCallExample',
|
|
2113
1985
|
props: {
|
|
2114
|
-
|
|
2115
|
-
loading: false
|
|
1986
|
+
dataItems: [],
|
|
2116
1987
|
},
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
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
|
-
}
|
|
1988
|
+
events: {
|
|
1989
|
+
// Load data when the component is ready
|
|
1990
|
+
onReady: () => { loadData(); },
|
|
2127
1991
|
},
|
|
2128
1992
|
view: (_, c) => (
|
|
2129
|
-
<div>
|
|
2130
|
-
<
|
|
2131
|
-
{
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
))}
|
|
2138
|
-
</ul>
|
|
2139
|
-
)}
|
|
1993
|
+
<div id={c.id}>
|
|
1994
|
+
<button onClick={loadData}>Load data</button>
|
|
1995
|
+
<button onClick={clear}>Clear</button>
|
|
1996
|
+
<ul>
|
|
1997
|
+
{m.dataItems.map((item) => (
|
|
1998
|
+
<li key={item.id}>{item.id}: {item.name}</li>
|
|
1999
|
+
))}
|
|
2000
|
+
</ul>
|
|
2140
2001
|
</div>
|
|
2141
|
-
)
|
|
2002
|
+
),
|
|
2142
2003
|
};
|
|
2143
2004
|
|
|
2144
2005
|
c = useComponent(def, params);
|
|
2145
|
-
m = c.model;
|
|
2006
|
+
m = c.model;
|
|
2146
2007
|
return c;
|
|
2147
2008
|
};
|
|
2148
2009
|
|
|
2149
|
-
export const
|
|
2010
|
+
export const ApiCallExample = toReact(useApiCallExample);
|
|
2150
2011
|
```
|
|
2151
2012
|
|
|
2152
|
-
###
|
|
2013
|
+
### Navigation
|
|
2153
2014
|
|
|
2154
2015
|
```typescript
|
|
2155
2016
|
// React implementation
|
|
@@ -2196,7 +2057,7 @@ const usePage = (params: ComponentParams<PageStruct>) => {
|
|
|
2196
2057
|
export const Page = toReact(usePage);
|
|
2197
2058
|
```
|
|
2198
2059
|
|
|
2199
|
-
###
|
|
2060
|
+
### Authentication & Security
|
|
2200
2061
|
|
|
2201
2062
|
```typescript
|
|
2202
2063
|
// React implementation
|
|
@@ -2321,9 +2182,11 @@ Messages can be filtered by source using `ComponentMsgFilter`:
|
|
|
2321
2182
|
msgBroker: {
|
|
2322
2183
|
subscribe: {
|
|
2323
2184
|
'MY-EVENT': {
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2185
|
+
in: {
|
|
2186
|
+
callback: (msg, component) => {
|
|
2187
|
+
// Only triggered by parent/ancestor components
|
|
2188
|
+
},
|
|
2189
|
+
componentFilter: ComponentMsgFilter.FromAncestors
|
|
2327
2190
|
}
|
|
2328
2191
|
}
|
|
2329
2192
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { MsgStruct, MsgStructBase } from '@actdim/msgmesh/contracts';
|
|
2
2
|
import { BaseSecurityDomainConfig as BaseSecurityDomainConfig, BaseSecurityMsgStruct } from './security/securityContracts';
|
|
3
3
|
import { ReactNode } from 'react';
|
|
4
4
|
import { KeysOf } from '@actdim/utico/typeCore';
|
|
@@ -60,10 +60,10 @@ export type AppError = {
|
|
|
60
60
|
properties?: Record<string | number, any>;
|
|
61
61
|
[$isAppError]: true;
|
|
62
62
|
};
|
|
63
|
-
export type BaseApiMsgStruct = BaseSecurityMsgStruct &
|
|
63
|
+
export type BaseApiMsgStruct = BaseSecurityMsgStruct & MsgStruct<{}>;
|
|
64
64
|
export type Importance = "critical" | "high" | "normal" | "low" | undefined;
|
|
65
65
|
export type Severity = 'emergency' | 'alert' | 'error' | 'warn' | 'notice' | 'info' | 'success' | 'hint' | 'debug' | undefined;
|
|
66
|
-
export type BaseAppMsgStruct<TNavRoutes extends NavRoutes = NavRoutes> = BaseApiMsgStruct &
|
|
66
|
+
export type BaseAppMsgStruct<TNavRoutes extends NavRoutes = NavRoutes> = BaseApiMsgStruct & MsgStruct<{
|
|
67
67
|
[$NOTICE]: {
|
|
68
68
|
in: {
|
|
69
69
|
text: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"appContracts.d.ts","sourceRoot":"","sources":["../../src/appDomain/appContracts.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,
|
|
1
|
+
{"version":3,"file":"appContracts.d.ts","sourceRoot":"","sources":["../../src/appDomain/appContracts.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { MsgStruct
|
|
2
|
-
import {
|
|
1
|
+
import { MsgStruct } from '@actdim/msgmesh/contracts';
|
|
2
|
+
import { Skip } from '@actdim/utico/typeCore';
|
|
3
3
|
export declare enum AccessLevel {
|
|
4
4
|
None = 0,
|
|
5
5
|
Inherited = 1,
|
|
@@ -29,7 +29,7 @@ export type SecurityTokens = {
|
|
|
29
29
|
accessToken?: string;
|
|
30
30
|
refreshToken?: string;
|
|
31
31
|
};
|
|
32
|
-
export type BaseSecurityMsgStruct =
|
|
32
|
+
export type BaseSecurityMsgStruct = MsgStruct<{
|
|
33
33
|
[$AUTH_SIGNIN]: {
|
|
34
34
|
in: UserCredentials;
|
|
35
35
|
out: SecurityContext;
|
|
@@ -68,7 +68,7 @@ export type BaseSecurityMsgStruct = RequireExtends<{
|
|
|
68
68
|
in: void;
|
|
69
69
|
out: BaseSecurityDomainConfig;
|
|
70
70
|
};
|
|
71
|
-
}
|
|
71
|
+
}>;
|
|
72
72
|
export type ISecurable = {
|
|
73
73
|
id: string;
|
|
74
74
|
};
|