@cohesivesystems/presentation-react 0.1.0-alpha.1

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.
Files changed (252) hide show
  1. package/LICENSE +55 -0
  2. package/dist/collection-chrome-slot-renderer-registry.d.ts +12 -0
  3. package/dist/collection-chrome-slot-renderer-registry.d.ts.map +1 -0
  4. package/dist/collection-chrome-slot-renderer-registry.js +12 -0
  5. package/dist/collection-chrome-slot-renderer-registry.js.map +1 -0
  6. package/dist/collection-selection-state-context.d.ts +6 -0
  7. package/dist/collection-selection-state-context.d.ts.map +1 -0
  8. package/dist/collection-selection-state-context.js +25 -0
  9. package/dist/collection-selection-state-context.js.map +1 -0
  10. package/dist/collection-selection-state.d.ts +3 -0
  11. package/dist/collection-selection-state.d.ts.map +1 -0
  12. package/dist/collection-selection-state.js +52 -0
  13. package/dist/collection-selection-state.js.map +1 -0
  14. package/dist/document-workspace-runtime.d.ts +262 -0
  15. package/dist/document-workspace-runtime.d.ts.map +1 -0
  16. package/dist/document-workspace-runtime.js +699 -0
  17. package/dist/document-workspace-runtime.js.map +1 -0
  18. package/dist/index.d.ts +51 -0
  19. package/dist/index.d.ts.map +1 -0
  20. package/dist/index.js +51 -0
  21. package/dist/index.js.map +1 -0
  22. package/dist/input-form-endpoint-runtime.d.ts +140 -0
  23. package/dist/input-form-endpoint-runtime.d.ts.map +1 -0
  24. package/dist/input-form-endpoint-runtime.js +280 -0
  25. package/dist/input-form-endpoint-runtime.js.map +1 -0
  26. package/dist/local-flow-state-data-source-projection.d.ts +21 -0
  27. package/dist/local-flow-state-data-source-projection.d.ts.map +1 -0
  28. package/dist/local-flow-state-data-source-projection.js +65 -0
  29. package/dist/local-flow-state-data-source-projection.js.map +1 -0
  30. package/dist/navigation-runtime-context.d.ts +22 -0
  31. package/dist/navigation-runtime-context.d.ts.map +1 -0
  32. package/dist/navigation-runtime-context.js +23 -0
  33. package/dist/navigation-runtime-context.js.map +1 -0
  34. package/dist/navigation-shell-frame-renderer-registry.d.ts +20 -0
  35. package/dist/navigation-shell-frame-renderer-registry.d.ts.map +1 -0
  36. package/dist/navigation-shell-frame-renderer-registry.js +9 -0
  37. package/dist/navigation-shell-frame-renderer-registry.js.map +1 -0
  38. package/dist/navigation-shell-region-component-registry.d.ts +50 -0
  39. package/dist/navigation-shell-region-component-registry.d.ts.map +1 -0
  40. package/dist/navigation-shell-region-component-registry.js +44 -0
  41. package/dist/navigation-shell-region-component-registry.js.map +1 -0
  42. package/dist/navigation-shell-slot-renderer-registry.d.ts +22 -0
  43. package/dist/navigation-shell-slot-renderer-registry.d.ts.map +1 -0
  44. package/dist/navigation-shell-slot-renderer-registry.js +9 -0
  45. package/dist/navigation-shell-slot-renderer-registry.js.map +1 -0
  46. package/dist/page-host-projection.d.ts +127 -0
  47. package/dist/page-host-projection.d.ts.map +1 -0
  48. package/dist/page-host-projection.js +59 -0
  49. package/dist/page-host-projection.js.map +1 -0
  50. package/dist/page-host-renderer-registry.d.ts +72 -0
  51. package/dist/page-host-renderer-registry.d.ts.map +1 -0
  52. package/dist/page-host-renderer-registry.js +63 -0
  53. package/dist/page-host-renderer-registry.js.map +1 -0
  54. package/dist/presentation-action-group-runtime-options.d.ts +36 -0
  55. package/dist/presentation-action-group-runtime-options.d.ts.map +1 -0
  56. package/dist/presentation-action-group-runtime-options.js +50 -0
  57. package/dist/presentation-action-group-runtime-options.js.map +1 -0
  58. package/dist/presentation-action-runtime.d.ts +52 -0
  59. package/dist/presentation-action-runtime.d.ts.map +1 -0
  60. package/dist/presentation-action-runtime.js +78 -0
  61. package/dist/presentation-action-runtime.js.map +1 -0
  62. package/dist/presentation-data-source-binding-runtime.d.ts +3 -0
  63. package/dist/presentation-data-source-binding-runtime.d.ts.map +1 -0
  64. package/dist/presentation-data-source-binding-runtime.js +65 -0
  65. package/dist/presentation-data-source-binding-runtime.js.map +1 -0
  66. package/dist/presentation-data-source-binding.d.ts +12 -0
  67. package/dist/presentation-data-source-binding.d.ts.map +1 -0
  68. package/dist/presentation-data-source-binding.js +13 -0
  69. package/dist/presentation-data-source-binding.js.map +1 -0
  70. package/dist/presentation-flow-runtime.d.ts +139 -0
  71. package/dist/presentation-flow-runtime.d.ts.map +1 -0
  72. package/dist/presentation-flow-runtime.js +514 -0
  73. package/dist/presentation-flow-runtime.js.map +1 -0
  74. package/dist/presentation-module-context.d.ts +4 -0
  75. package/dist/presentation-module-context.d.ts.map +1 -0
  76. package/dist/presentation-module-context.js +6 -0
  77. package/dist/presentation-module-context.js.map +1 -0
  78. package/dist/presentation-module-provider.d.ts +7 -0
  79. package/dist/presentation-module-provider.d.ts.map +1 -0
  80. package/dist/presentation-module-provider.js +7 -0
  81. package/dist/presentation-module-provider.js.map +1 -0
  82. package/dist/presentation-projection-diagnostics-context.d.ts +8 -0
  83. package/dist/presentation-projection-diagnostics-context.d.ts.map +1 -0
  84. package/dist/presentation-projection-diagnostics-context.js +3 -0
  85. package/dist/presentation-projection-diagnostics-context.js.map +1 -0
  86. package/dist/presentation-projection-diagnostics-hooks.d.ts +4 -0
  87. package/dist/presentation-projection-diagnostics-hooks.d.ts.map +1 -0
  88. package/dist/presentation-projection-diagnostics-hooks.js +17 -0
  89. package/dist/presentation-projection-diagnostics-hooks.js.map +1 -0
  90. package/dist/presentation-projection-diagnostics-runtime.d.ts +12 -0
  91. package/dist/presentation-projection-diagnostics-runtime.d.ts.map +1 -0
  92. package/dist/presentation-projection-diagnostics-runtime.js +54 -0
  93. package/dist/presentation-projection-diagnostics-runtime.js.map +1 -0
  94. package/dist/presentation-projection-trace.d.ts +27 -0
  95. package/dist/presentation-projection-trace.d.ts.map +1 -0
  96. package/dist/presentation-projection-trace.js +54 -0
  97. package/dist/presentation-projection-trace.js.map +1 -0
  98. package/dist/presentation-query-form-runtime.d.ts +30 -0
  99. package/dist/presentation-query-form-runtime.d.ts.map +1 -0
  100. package/dist/presentation-query-form-runtime.js +75 -0
  101. package/dist/presentation-query-form-runtime.js.map +1 -0
  102. package/dist/presentation-query-form-state-adapter-registry.d.ts +4 -0
  103. package/dist/presentation-query-form-state-adapter-registry.d.ts.map +1 -0
  104. package/dist/presentation-query-form-state-adapter-registry.js +13 -0
  105. package/dist/presentation-query-form-state-adapter-registry.js.map +1 -0
  106. package/dist/presentation-query-form-state-context.d.ts +20 -0
  107. package/dist/presentation-query-form-state-context.d.ts.map +1 -0
  108. package/dist/presentation-query-form-state-context.js +20 -0
  109. package/dist/presentation-query-form-state-context.js.map +1 -0
  110. package/dist/presentation-query-form-state.d.ts +29 -0
  111. package/dist/presentation-query-form-state.d.ts.map +1 -0
  112. package/dist/presentation-query-form-state.js +99 -0
  113. package/dist/presentation-query-form-state.js.map +1 -0
  114. package/dist/presentation-renderer-registry.d.ts +109 -0
  115. package/dist/presentation-renderer-registry.d.ts.map +1 -0
  116. package/dist/presentation-renderer-registry.js +25 -0
  117. package/dist/presentation-renderer-registry.js.map +1 -0
  118. package/dist/presentation-standards-context.d.ts +4 -0
  119. package/dist/presentation-standards-context.d.ts.map +1 -0
  120. package/dist/presentation-standards-context.js +12 -0
  121. package/dist/presentation-standards-context.js.map +1 -0
  122. package/dist/presentation-standards-provider.d.ts +14 -0
  123. package/dist/presentation-standards-provider.d.ts.map +1 -0
  124. package/dist/presentation-standards-provider.js +16 -0
  125. package/dist/presentation-standards-provider.js.map +1 -0
  126. package/dist/projected-document-action-runtime-bindings.d.ts +20 -0
  127. package/dist/projected-document-action-runtime-bindings.d.ts.map +1 -0
  128. package/dist/projected-document-action-runtime-bindings.js +88 -0
  129. package/dist/projected-document-action-runtime-bindings.js.map +1 -0
  130. package/dist/projected-document-action-runtime.d.ts +76 -0
  131. package/dist/projected-document-action-runtime.d.ts.map +1 -0
  132. package/dist/projected-document-action-runtime.js +224 -0
  133. package/dist/projected-document-action-runtime.js.map +1 -0
  134. package/dist/projected-document-process-preview-action-runtime-bindings.d.ts +26 -0
  135. package/dist/projected-document-process-preview-action-runtime-bindings.d.ts.map +1 -0
  136. package/dist/projected-document-process-preview-action-runtime-bindings.js +106 -0
  137. package/dist/projected-document-process-preview-action-runtime-bindings.js.map +1 -0
  138. package/dist/projected-document-process-preview-action-runtime.d.ts +321 -0
  139. package/dist/projected-document-process-preview-action-runtime.d.ts.map +1 -0
  140. package/dist/projected-document-process-preview-action-runtime.js +729 -0
  141. package/dist/projected-document-process-preview-action-runtime.js.map +1 -0
  142. package/dist/projected-document-process-preview-capability-runtime.d.ts +47 -0
  143. package/dist/projected-document-process-preview-capability-runtime.d.ts.map +1 -0
  144. package/dist/projected-document-process-preview-capability-runtime.js +49 -0
  145. package/dist/projected-document-process-preview-capability-runtime.js.map +1 -0
  146. package/dist/projected-document-workspace-action-runtime.d.ts +87 -0
  147. package/dist/projected-document-workspace-action-runtime.d.ts.map +1 -0
  148. package/dist/projected-document-workspace-action-runtime.js +150 -0
  149. package/dist/projected-document-workspace-action-runtime.js.map +1 -0
  150. package/dist/projected-document-workspace-projection-renderer-diagnostics.d.ts +18 -0
  151. package/dist/projected-document-workspace-projection-renderer-diagnostics.d.ts.map +1 -0
  152. package/dist/projected-document-workspace-projection-renderer-diagnostics.js +207 -0
  153. package/dist/projected-document-workspace-projection-renderer-diagnostics.js.map +1 -0
  154. package/dist/projected-document-workspace-runtime-binding-diagnostics.d.ts +19 -0
  155. package/dist/projected-document-workspace-runtime-binding-diagnostics.d.ts.map +1 -0
  156. package/dist/projected-document-workspace-runtime-binding-diagnostics.js +124 -0
  157. package/dist/projected-document-workspace-runtime-binding-diagnostics.js.map +1 -0
  158. package/dist/projected-navigation-provider.d.ts +16 -0
  159. package/dist/projected-navigation-provider.d.ts.map +1 -0
  160. package/dist/projected-navigation-provider.js +51 -0
  161. package/dist/projected-navigation-provider.js.map +1 -0
  162. package/dist/projected-presentation-flow-host.d.ts +16 -0
  163. package/dist/projected-presentation-flow-host.d.ts.map +1 -0
  164. package/dist/projected-presentation-flow-host.js +14 -0
  165. package/dist/projected-presentation-flow-host.js.map +1 -0
  166. package/dist/projected-query-form-state-adapter.d.ts +12 -0
  167. package/dist/projected-query-form-state-adapter.d.ts.map +1 -0
  168. package/dist/projected-query-form-state-adapter.js +361 -0
  169. package/dist/projected-query-form-state-adapter.js.map +1 -0
  170. package/dist/react-router-binding.d.ts +133 -0
  171. package/dist/react-router-binding.d.ts.map +1 -0
  172. package/dist/react-router-binding.js +76 -0
  173. package/dist/react-router-binding.js.map +1 -0
  174. package/dist/react-router-path.d.ts +4 -0
  175. package/dist/react-router-path.d.ts.map +1 -0
  176. package/dist/react-router-path.js +11 -0
  177. package/dist/react-router-path.js.map +1 -0
  178. package/dist/scope-selection-context.d.ts +28 -0
  179. package/dist/scope-selection-context.d.ts.map +1 -0
  180. package/dist/scope-selection-context.js +13 -0
  181. package/dist/scope-selection-context.js.map +1 -0
  182. package/dist/scope-selection-provider.d.ts +33 -0
  183. package/dist/scope-selection-provider.d.ts.map +1 -0
  184. package/dist/scope-selection-provider.js +162 -0
  185. package/dist/scope-selection-provider.js.map +1 -0
  186. package/dist/scope-selection-request.d.ts +6 -0
  187. package/dist/scope-selection-request.d.ts.map +1 -0
  188. package/dist/scope-selection-request.js +12 -0
  189. package/dist/scope-selection-request.js.map +1 -0
  190. package/dist/standard-navigation-shell-frame-renderers.d.ts +15 -0
  191. package/dist/standard-navigation-shell-frame-renderers.d.ts.map +1 -0
  192. package/dist/standard-navigation-shell-frame-renderers.js +22 -0
  193. package/dist/standard-navigation-shell-frame-renderers.js.map +1 -0
  194. package/dist/standard-navigation-shell-slot-renderers.d.ts +59 -0
  195. package/dist/standard-navigation-shell-slot-renderers.d.ts.map +1 -0
  196. package/dist/standard-navigation-shell-slot-renderers.js +67 -0
  197. package/dist/standard-navigation-shell-slot-renderers.js.map +1 -0
  198. package/dist/view-chrome-slot-renderer-registry.d.ts +12 -0
  199. package/dist/view-chrome-slot-renderer-registry.d.ts.map +1 -0
  200. package/dist/view-chrome-slot-renderer-registry.js +12 -0
  201. package/dist/view-chrome-slot-renderer-registry.js.map +1 -0
  202. package/package.json +60 -0
  203. package/src/collection-chrome-slot-renderer-registry.ts +66 -0
  204. package/src/collection-selection-state-context.ts +51 -0
  205. package/src/collection-selection-state.tsx +101 -0
  206. package/src/document-workspace-runtime.tsx +1505 -0
  207. package/src/index.ts +50 -0
  208. package/src/input-form-endpoint-runtime.ts +632 -0
  209. package/src/local-flow-state-data-source-projection.ts +133 -0
  210. package/src/navigation-runtime-context.ts +40 -0
  211. package/src/navigation-shell-frame-renderer-registry.ts +54 -0
  212. package/src/navigation-shell-region-component-registry.tsx +127 -0
  213. package/src/navigation-shell-slot-renderer-registry.ts +88 -0
  214. package/src/page-host-projection.ts +356 -0
  215. package/src/page-host-renderer-registry.tsx +353 -0
  216. package/src/presentation-action-group-runtime-options.ts +121 -0
  217. package/src/presentation-action-runtime.ts +190 -0
  218. package/src/presentation-data-source-binding-runtime.ts +109 -0
  219. package/src/presentation-data-source-binding.tsx +24 -0
  220. package/src/presentation-flow-runtime.tsx +835 -0
  221. package/src/presentation-module-context.ts +10 -0
  222. package/src/presentation-module-provider.tsx +19 -0
  223. package/src/presentation-projection-diagnostics-context.ts +17 -0
  224. package/src/presentation-projection-diagnostics-hooks.ts +29 -0
  225. package/src/presentation-projection-diagnostics-runtime.tsx +95 -0
  226. package/src/presentation-projection-trace.ts +117 -0
  227. package/src/presentation-query-form-runtime.ts +148 -0
  228. package/src/presentation-query-form-state-adapter-registry.ts +25 -0
  229. package/src/presentation-query-form-state-context.ts +56 -0
  230. package/src/presentation-query-form-state.tsx +250 -0
  231. package/src/presentation-renderer-registry.ts +214 -0
  232. package/src/presentation-standards-context.ts +21 -0
  233. package/src/presentation-standards-provider.tsx +44 -0
  234. package/src/projected-document-action-runtime-bindings.ts +132 -0
  235. package/src/projected-document-action-runtime.ts +414 -0
  236. package/src/projected-document-process-preview-action-runtime-bindings.ts +231 -0
  237. package/src/projected-document-process-preview-action-runtime.ts +1725 -0
  238. package/src/projected-document-process-preview-capability-runtime.ts +216 -0
  239. package/src/projected-document-workspace-action-runtime.ts +402 -0
  240. package/src/projected-document-workspace-projection-renderer-diagnostics.ts +342 -0
  241. package/src/projected-document-workspace-runtime-binding-diagnostics.ts +208 -0
  242. package/src/projected-navigation-provider.tsx +104 -0
  243. package/src/projected-presentation-flow-host.tsx +46 -0
  244. package/src/projected-query-form-state-adapter.ts +578 -0
  245. package/src/react-router-binding.tsx +417 -0
  246. package/src/react-router-path.ts +16 -0
  247. package/src/scope-selection-context.ts +58 -0
  248. package/src/scope-selection-provider.tsx +324 -0
  249. package/src/scope-selection-request.ts +21 -0
  250. package/src/standard-navigation-shell-frame-renderers.tsx +106 -0
  251. package/src/standard-navigation-shell-slot-renderers.tsx +289 -0
  252. package/src/view-chrome-slot-renderer-registry.ts +66 -0
@@ -0,0 +1,121 @@
1
+ import type { ReactNode } from 'react'
2
+
3
+ import type {
4
+ ActionDefinition,
5
+ ActionPlacementDefinition,
6
+ PresentationDataSourceResolver,
7
+ PresentationModuleDefinition,
8
+ ViewDefinition,
9
+ } from '@cohesivesystems/presentation-core'
10
+ import type {
11
+ PresentationActionRuntimeRegistry,
12
+ } from '@cohesivesystems/presentation-core'
13
+
14
+ export interface PresentationActionRenderState {
15
+ readonly disabledReason?: ReactNode
16
+ readonly isDisabled?: boolean
17
+ readonly isHidden?: boolean
18
+ readonly isPending?: boolean
19
+ readonly label?: ReactNode
20
+ }
21
+
22
+ export interface PresentationActionGroupRenderContext<TContext> {
23
+ readonly context: TContext
24
+ readonly dataSourceResolver: PresentationDataSourceResolver
25
+ readonly module: Pick<PresentationModuleDefinition, 'Actions' | 'Targets'>
26
+ readonly view: ViewDefinition
27
+ }
28
+
29
+ export interface PresentationActionRenderContext<TContext>
30
+ extends PresentationActionGroupRenderContext<TContext> {
31
+ readonly action: ActionDefinition | null
32
+ readonly invalidatedDataSourceIds: readonly string[]
33
+ readonly isFetching: boolean
34
+ readonly placement: ActionPlacementDefinition
35
+ }
36
+
37
+ export interface PresentationActionGroupOptions<TContext> {
38
+ readonly canExecuteAction?: (context: PresentationActionRenderContext<TContext>) => boolean
39
+ readonly executeAction?: (context: PresentationActionRenderContext<TContext>) => Promise<void> | void
40
+ readonly renderActionLabel?: (context: PresentationActionRenderContext<TContext>) => ReactNode
41
+ readonly resolveActionState?: (
42
+ context: PresentationActionRenderContext<TContext>,
43
+ ) => PresentationActionRenderState | null | undefined
44
+ }
45
+
46
+ export interface PresentationActionGroupRuntimeOptions<TContext>
47
+ extends PresentationActionGroupOptions<TContext> {
48
+ readonly runtimes?: PresentationActionRuntimeRegistry<
49
+ PresentationActionRenderContext<TContext>,
50
+ ReactNode
51
+ >
52
+ }
53
+
54
+ /**
55
+ * Creates standard action-group options from a semantic action runtime registry.
56
+ */
57
+ export function createPresentationActionGroupRuntimeOptions<TContext>({
58
+ canExecuteAction,
59
+ executeAction,
60
+ renderActionLabel,
61
+ resolveActionState,
62
+ runtimes,
63
+ ...options
64
+ }: PresentationActionGroupRuntimeOptions<TContext> = {}): PresentationActionGroupOptions<TContext> {
65
+ const resolvedExecuteAction =
66
+ runtimes || executeAction
67
+ ? (context: PresentationActionRenderContext<TContext>) => {
68
+ const runtime = resolveRuntime(runtimes, context)
69
+ if (
70
+ runtime?.execute &&
71
+ !runtime.isDisabled &&
72
+ !runtime.isHidden &&
73
+ (runtime.canExecute?.(context) ?? true)
74
+ ) {
75
+ return runtime.execute(context)
76
+ }
77
+
78
+ return executeAction?.(context)
79
+ }
80
+ : undefined
81
+
82
+ return {
83
+ ...options,
84
+ canExecuteAction: (context) => {
85
+ const runtime = resolveRuntime(runtimes, context)
86
+ if (runtime?.isDisabled || runtime?.isHidden) {
87
+ return false
88
+ }
89
+
90
+ if (runtime?.canExecute && !runtime.canExecute(context)) {
91
+ return false
92
+ }
93
+
94
+ return canExecuteAction?.(context) ?? true
95
+ },
96
+ executeAction: resolvedExecuteAction,
97
+ renderActionLabel,
98
+ resolveActionState: (context) => {
99
+ const runtime = resolveRuntime(runtimes, context)
100
+ const baseState = resolveActionState?.(context)
101
+ return {
102
+ ...baseState,
103
+ disabledReason: runtime?.disabledReason ?? baseState?.disabledReason,
104
+ isDisabled: Boolean(baseState?.isDisabled || runtime?.isDisabled),
105
+ isHidden: Boolean(baseState?.isHidden || runtime?.isHidden),
106
+ isPending: Boolean(baseState?.isPending || runtime?.isPending),
107
+ label:
108
+ runtime?.isPending && runtime.pendingLabel
109
+ ? runtime.pendingLabel
110
+ : (runtime?.label ?? baseState?.label),
111
+ }
112
+ },
113
+ }
114
+ }
115
+
116
+ function resolveRuntime<TContext>(
117
+ runtimes: PresentationActionGroupRuntimeOptions<TContext>['runtimes'] | undefined,
118
+ context: PresentationActionRenderContext<TContext>,
119
+ ) {
120
+ return runtimes?.[context.placement.ActionId] ?? null
121
+ }
@@ -0,0 +1,190 @@
1
+ import { useMutation, useQueryClient, type QueryClient } from '@tanstack/react-query'
2
+ import { useMemo } from 'react'
3
+
4
+ import type {
5
+ PresentationActionEndpointExecutionRequest,
6
+ } from '@cohesivesystems/presentation-core'
7
+ import {
8
+ findPresentationAction,
9
+ type ActionDefinition,
10
+ type PresentationBindingDefinition,
11
+ type PresentationModuleDefinition,
12
+ resolvePresentationActionEndpointBinding,
13
+ } from '@cohesivesystems/presentation-core'
14
+
15
+ export type { PresentationActionEndpointExecutionRequest } from '@cohesivesystems/presentation-core'
16
+
17
+ export type PresentationActionEndpointExecutor = <TResult = unknown>(
18
+ endpointId: string,
19
+ request: PresentationActionEndpointExecutionRequest,
20
+ ) => Promise<TResult>
21
+
22
+ export interface PresentationActionRequestPreparationContext<TInput> {
23
+ readonly action: ActionDefinition | null
24
+ readonly actionId: string
25
+ readonly binding: PresentationBindingDefinition | null
26
+ readonly endpointId: string
27
+ readonly input: TInput
28
+ readonly module: PresentationModuleDefinition | null
29
+ }
30
+
31
+ export interface PresentationActionSuccessContext<TInput, TResult> {
32
+ readonly action: ActionDefinition | null
33
+ readonly actionId: string
34
+ readonly endpointId: string
35
+ readonly input: TInput
36
+ readonly queryClient: QueryClient
37
+ readonly result: TResult
38
+ }
39
+
40
+ export interface UsePresentationActionExecutorOptions<TInput, TResult> {
41
+ readonly actionId: string
42
+ readonly componentSet?: string | null
43
+ readonly dataSourceId?: string | null
44
+ readonly dataSourceQueryKey?: (dataSourceId: string) => readonly unknown[]
45
+ readonly executeEndpoint: PresentationActionEndpointExecutor
46
+ readonly invalidateDataSourceIds?:
47
+ | readonly string[]
48
+ | ((context: PresentationActionSuccessContext<TInput, TResult>) => readonly string[])
49
+ readonly module: PresentationModuleDefinition | null
50
+ readonly prepareRequest: (
51
+ context: PresentationActionRequestPreparationContext<TInput>,
52
+ ) => PresentationActionEndpointExecutionRequest
53
+ readonly processResult?: (context: PresentationActionSuccessContext<TInput, TResult>) => void
54
+ readonly setResultQueryData?: (context: PresentationActionSuccessContext<TInput, TResult>) => void
55
+ readonly onSuccess?: (context: PresentationActionSuccessContext<TInput, TResult>) => void
56
+ }
57
+
58
+ export interface PresentationActionExecutor<TInput, TResult> {
59
+ readonly action: ActionDefinition | null
60
+ readonly endpointId: string | null
61
+ readonly error: unknown
62
+ readonly execute: (input: TInput) => void
63
+ readonly executeAsync: (input: TInput) => Promise<TResult>
64
+ readonly isPending: boolean
65
+ readonly isSuccess: boolean
66
+ readonly reset: () => void
67
+ }
68
+
69
+ /**
70
+ * Runtime projection for endpoint-backed presentation actions. It resolves the
71
+ * endpoint from the action definition/target bindings, prepares an endpoint
72
+ * request from the caller's semantic input, executes it through the app's API
73
+ * adapter, and applies result/query invalidation policy.
74
+ */
75
+ export function usePresentationActionExecutor<TInput = void, TResult = unknown>({
76
+ actionId,
77
+ componentSet = 'cohesive.presentation.react',
78
+ dataSourceId,
79
+ dataSourceQueryKey,
80
+ executeEndpoint,
81
+ invalidateDataSourceIds,
82
+ module,
83
+ onSuccess,
84
+ prepareRequest,
85
+ processResult,
86
+ setResultQueryData,
87
+ }: UsePresentationActionExecutorOptions<TInput, TResult>): PresentationActionExecutor<TInput, TResult> {
88
+ const queryClient = useQueryClient()
89
+ const action = useMemo(
90
+ () => findPresentationAction<ActionDefinition>(module, actionId),
91
+ [actionId, module],
92
+ )
93
+ const binding = useMemo(
94
+ () =>
95
+ resolvePresentationActionEndpointBinding({
96
+ actionId,
97
+ componentSet,
98
+ dataSourceId,
99
+ module,
100
+ }),
101
+ [actionId, componentSet, dataSourceId, module],
102
+ )
103
+ const endpointId = binding?.EndpointId ?? null
104
+ const mutation = useMutation<TResult, unknown, TInput>({
105
+ mutationFn: async (input) => {
106
+ if (!endpointId) {
107
+ throw new Error(`No endpoint binding is registered for action '${actionId}'.`)
108
+ }
109
+
110
+ return await executeEndpoint<TResult>(
111
+ endpointId,
112
+ prepareRequest({
113
+ action,
114
+ actionId,
115
+ binding,
116
+ endpointId,
117
+ input,
118
+ module,
119
+ }),
120
+ )
121
+ },
122
+ onSuccess: async (result, input) => {
123
+ if (!endpointId) {
124
+ return
125
+ }
126
+
127
+ const context = {
128
+ action,
129
+ actionId,
130
+ endpointId,
131
+ input,
132
+ queryClient,
133
+ result,
134
+ } satisfies PresentationActionSuccessContext<TInput, TResult>
135
+
136
+ setResultQueryData?.(context)
137
+ processResult?.(context)
138
+ await invalidatePresentationActionDataSources({
139
+ action,
140
+ context,
141
+ dataSourceQueryKey,
142
+ invalidateDataSourceIds,
143
+ queryClient,
144
+ })
145
+ onSuccess?.(context)
146
+ },
147
+ })
148
+
149
+ return {
150
+ action,
151
+ endpointId,
152
+ error: mutation.error,
153
+ execute: mutation.mutate,
154
+ executeAsync: mutation.mutateAsync,
155
+ isPending: mutation.isPending,
156
+ isSuccess: mutation.isSuccess,
157
+ reset: mutation.reset,
158
+ }
159
+ }
160
+
161
+ async function invalidatePresentationActionDataSources<TInput, TResult>({
162
+ action,
163
+ context,
164
+ dataSourceQueryKey,
165
+ invalidateDataSourceIds,
166
+ queryClient,
167
+ }: {
168
+ readonly action: ActionDefinition | null
169
+ readonly context: PresentationActionSuccessContext<TInput, TResult>
170
+ readonly dataSourceQueryKey?: (dataSourceId: string) => readonly unknown[]
171
+ readonly invalidateDataSourceIds?:
172
+ | readonly string[]
173
+ | ((context: PresentationActionSuccessContext<TInput, TResult>) => readonly string[])
174
+ readonly queryClient: QueryClient
175
+ }) {
176
+ if (!dataSourceQueryKey) {
177
+ return
178
+ }
179
+
180
+ const dataSourceIds =
181
+ typeof invalidateDataSourceIds === 'function'
182
+ ? invalidateDataSourceIds(context)
183
+ : (invalidateDataSourceIds ?? action?.Result?.InvalidateDataSourceIds ?? [])
184
+
185
+ await Promise.all(
186
+ Array.from(new Set(dataSourceIds)).map((dataSourceId) =>
187
+ queryClient.invalidateQueries({ queryKey: dataSourceQueryKey(dataSourceId) }),
188
+ ),
189
+ )
190
+ }
@@ -0,0 +1,109 @@
1
+ import { useQueries } from '@tanstack/react-query'
2
+ import { useMemo } from 'react'
3
+
4
+ import {
5
+ findPresentationDataSource,
6
+ isTanStackQueryBinding,
7
+ resolveDataSourceAuthorization,
8
+ type DataSourceDefinition,
9
+ type PresentationDataSourceBinding,
10
+ type PresentationDataSourceState,
11
+ type PresentationDataSourceStateMap,
12
+ type PresentationLocalValueDataSourceBinding,
13
+ type PresentationTanStackQueryDataSourceBinding,
14
+ } from '@cohesivesystems/presentation-core'
15
+ import { usePresentationModule } from './presentation-module-context'
16
+
17
+ interface PresentationAsyncDataSourceResult {
18
+ readonly data: unknown
19
+ readonly error: unknown
20
+ readonly isFetching: boolean
21
+ readonly isPending: boolean
22
+ readonly refetch: () => Promise<unknown>
23
+ }
24
+
25
+ export function usePresentationDataSources(
26
+ bindings: readonly PresentationDataSourceBinding[],
27
+ ) {
28
+ const module = usePresentationModule()
29
+ const queryBindings = useMemo(
30
+ () => bindings.filter(isTanStackQueryBinding),
31
+ [bindings],
32
+ )
33
+ const queryResults = useQueries({
34
+ queries: queryBindings.map((binding) => {
35
+ const authorization = resolveDataSourceAuthorization(binding)
36
+ return {
37
+ enabled: (binding.enabled ?? true) && !authorization.isBlocked,
38
+ queryFn: binding.queryFn,
39
+ queryKey: binding.queryKey,
40
+ refetchInterval: binding.refetchInterval,
41
+ retry: binding.retry,
42
+ staleTime: binding.staleTime,
43
+ }
44
+ }),
45
+ })
46
+ const dataSources = useMemo<PresentationDataSourceStateMap>(() => {
47
+ const definitionsById = new Map(
48
+ module?.DataSources.map((definition) => [definition.Id, definition] as const) ?? [],
49
+ )
50
+ const asyncResultsByDataSourceId = new Map(
51
+ queryBindings.map((binding, index) => [binding.dataSourceId, queryResults[index]] as const),
52
+ )
53
+ return Object.fromEntries(
54
+ bindings.map((binding) => {
55
+ const definition =
56
+ definitionsById.get(binding.dataSourceId) ??
57
+ findPresentationDataSource(module, binding.dataSourceId)
58
+ const state = isTanStackQueryBinding(binding)
59
+ ? createAsyncDataSourceState(
60
+ binding,
61
+ asyncResultsByDataSourceId.get(binding.dataSourceId) as PresentationAsyncDataSourceResult,
62
+ definition,
63
+ )
64
+ : createLocalValueDataSourceState(binding, definition)
65
+ return [binding.dataSourceId, state]
66
+ }),
67
+ )
68
+ }, [bindings, module, queryBindings, queryResults])
69
+ return dataSources
70
+ }
71
+
72
+ function createAsyncDataSourceState(
73
+ binding: PresentationTanStackQueryDataSourceBinding,
74
+ result: PresentationAsyncDataSourceResult,
75
+ definition: DataSourceDefinition | null,
76
+ ): PresentationDataSourceState {
77
+ const authorization = resolveDataSourceAuthorization(binding)
78
+ return {
79
+ blockedLabel: authorization.blockedLabel,
80
+ data: result.data === undefined ? binding.fallbackData : result.data,
81
+ definition,
82
+ emptyMessage: binding.emptyMessage,
83
+ error: result.error,
84
+ isBlocked: authorization.isBlocked,
85
+ isFetching: result.isFetching,
86
+ isPending: result.isPending,
87
+ pendingLabel: binding.pendingLabel,
88
+ refetch: result.refetch,
89
+ }
90
+ }
91
+
92
+ function createLocalValueDataSourceState(
93
+ binding: PresentationLocalValueDataSourceBinding,
94
+ definition: DataSourceDefinition | null,
95
+ ): PresentationDataSourceState {
96
+ const authorization = resolveDataSourceAuthorization(binding)
97
+ return {
98
+ blockedLabel: authorization.blockedLabel,
99
+ data: binding.data,
100
+ definition,
101
+ emptyMessage: binding.emptyMessage,
102
+ error: binding.error,
103
+ isBlocked: authorization.isBlocked,
104
+ isFetching: binding.isFetching,
105
+ isPending: binding.isPending,
106
+ pendingLabel: binding.pendingLabel,
107
+ refetch: binding.refetch,
108
+ }
109
+ }
@@ -0,0 +1,24 @@
1
+ import { type ReactNode } from 'react'
2
+
3
+ import {
4
+ type PresentationDataSourceBinding,
5
+ type PresentationDataSourceStateMap,
6
+ } from '@cohesivesystems/presentation-core'
7
+ import { usePresentationDataSources } from './presentation-data-source-binding-runtime'
8
+
9
+ export interface PresentationDataSourceBinderProps {
10
+ readonly bindings: readonly PresentationDataSourceBinding[]
11
+ readonly children: (dataSources: PresentationDataSourceStateMap) => ReactNode
12
+ }
13
+
14
+ /**
15
+ * Binds declared presentation data sources to runtime state for projected React
16
+ * components.
17
+ */
18
+ export function PresentationDataSourceBinder({
19
+ bindings,
20
+ children,
21
+ }: PresentationDataSourceBinderProps) {
22
+ const dataSources = usePresentationDataSources(bindings)
23
+ return <>{children(dataSources)}</>
24
+ }