@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
package/src/index.ts ADDED
@@ -0,0 +1,50 @@
1
+ export const cohesivePresentationReactAdapterId = 'cohesive.presentation.react'
2
+ export * from './collection-chrome-slot-renderer-registry'
3
+ export * from './collection-selection-state'
4
+ export * from './collection-selection-state-context'
5
+ export * from './document-workspace-runtime'
6
+ export * from './input-form-endpoint-runtime'
7
+ export * from './local-flow-state-data-source-projection'
8
+ export * from './navigation-runtime-context'
9
+ export * from './navigation-shell-frame-renderer-registry'
10
+ export * from './navigation-shell-region-component-registry'
11
+ export * from './navigation-shell-slot-renderer-registry'
12
+ export * from './page-host-projection'
13
+ export * from './page-host-renderer-registry'
14
+ export * from './presentation-action-group-runtime-options'
15
+ export * from './presentation-action-runtime'
16
+ export * from './presentation-data-source-binding'
17
+ export * from './presentation-data-source-binding-runtime'
18
+ export * from './presentation-flow-runtime'
19
+ export * from './presentation-module-context'
20
+ export * from './presentation-module-provider'
21
+ export * from './presentation-projection-diagnostics-context'
22
+ export * from './presentation-projection-diagnostics-hooks'
23
+ export * from './presentation-projection-diagnostics-runtime'
24
+ export * from './presentation-projection-trace'
25
+ export * from './presentation-query-form-runtime'
26
+ export * from './presentation-query-form-state'
27
+ export * from './presentation-query-form-state-adapter-registry'
28
+ export * from './presentation-query-form-state-context'
29
+ export * from './presentation-renderer-registry'
30
+ export * from './presentation-standards-context'
31
+ export * from './presentation-standards-provider'
32
+ export * from './projected-document-action-runtime'
33
+ export * from './projected-document-action-runtime-bindings'
34
+ export * from './projected-document-process-preview-action-runtime'
35
+ export * from './projected-document-process-preview-action-runtime-bindings'
36
+ export * from './projected-document-process-preview-capability-runtime'
37
+ export * from './projected-document-workspace-action-runtime'
38
+ export * from './projected-document-workspace-projection-renderer-diagnostics'
39
+ export * from './projected-document-workspace-runtime-binding-diagnostics'
40
+ export * from './projected-navigation-provider'
41
+ export * from './projected-presentation-flow-host'
42
+ export * from './projected-query-form-state-adapter'
43
+ export * from './react-router-binding'
44
+ export * from './react-router-path'
45
+ export * from './scope-selection-context'
46
+ export * from './scope-selection-provider'
47
+ export * from './scope-selection-request'
48
+ export * from './standard-navigation-shell-frame-renderers'
49
+ export * from './standard-navigation-shell-slot-renderers'
50
+ export * from './view-chrome-slot-renderer-registry'
@@ -0,0 +1,632 @@
1
+ import { useMutation, useQueryClient, type QueryClient } from '@tanstack/react-query'
2
+ import { useMemo, useState } from 'react'
3
+
4
+ import {
5
+ findPresentationAction,
6
+ type ActionDefinition,
7
+ type InputFormDefinition,
8
+ type PresentationValueDefinition,
9
+ type PresentationModuleDefinition,
10
+ resolvePresentationActionEndpointBinding,
11
+ readObjectPath,
12
+ type ProjectedInputFormActionContext,
13
+ type ProjectedInputFormRuntime,
14
+ writeObjectPath,
15
+ } from '@cohesivesystems/presentation-core'
16
+ import type {
17
+ PresentationActionEndpointExecutionRequest,
18
+ PresentationActionEndpointExecutor,
19
+ } from './presentation-action-runtime'
20
+ import {
21
+ inputFormTargetKinds,
22
+ presentationValueKinds,
23
+ } from '@cohesivesystems/presentation-contracts'
24
+
25
+ type InputFormEndpointRequestOption<TValue extends object, TOption> =
26
+ | TOption
27
+ | ((context: ProjectedInputFormEndpointRequestContext<TValue>) => TOption)
28
+
29
+ /**
30
+ * Request-time context used to lower a projected input-form value into a concrete
31
+ * endpoint execution request.
32
+ *
33
+ * @typeParam TValue - Shape of the projected input form draft value.
34
+ */
35
+ export interface ProjectedInputFormEndpointRequestContext<TValue extends object = object> {
36
+ /** Presentation action resolved from the submitted form placement, when available. */
37
+ readonly action: ActionDefinition | null
38
+
39
+ /** Full action context emitted by ProjectedInputForm when the user submits the form. */
40
+ readonly actionContext: ProjectedInputFormActionContext<TValue>
41
+
42
+ /** Endpoint selected from the action target binding or the input-form target. */
43
+ readonly endpointId: string
44
+
45
+ /** Backend-declared input form being submitted. */
46
+ readonly inputForm: InputFormDefinition
47
+
48
+ /** Presentation module that owns the form, actions, and target bindings. */
49
+ readonly module: PresentationModuleDefinition | null
50
+
51
+ /** Current draft value owned by the projected form runtime. */
52
+ readonly value: TValue
53
+ }
54
+
55
+ /**
56
+ * Success-time context supplied after an endpoint-backed input form completes.
57
+ *
58
+ * @typeParam TValue - Shape of the submitted form value.
59
+ * @typeParam TResult - Result returned by the endpoint executor.
60
+ */
61
+ export interface ProjectedInputFormEndpointSuccessContext<
62
+ TValue extends object = object,
63
+ TResult = unknown,
64
+ > extends ProjectedInputFormEndpointRequestContext<TValue> {
65
+ /** React Query client used by the runtime for optional cache writes or invalidation. */
66
+ readonly queryClient: QueryClient
67
+
68
+ /** Concrete request sent to the endpoint executor. */
69
+ readonly request: PresentationActionEndpointExecutionRequest
70
+
71
+ /** Endpoint result returned by the application API adapter. */
72
+ readonly result: TResult
73
+ }
74
+
75
+ /**
76
+ * Options for projecting an InputFormDefinition with an EndpointRequest target
77
+ * into mutable React state plus an executable ProjectedInputFormRuntime.
78
+ *
79
+ * @typeParam TValue - Shape of the form draft and lowered endpoint body.
80
+ * @typeParam TResult - Result returned by the endpoint executor.
81
+ */
82
+ export interface UseProjectedInputFormEndpointRuntimeOptions<
83
+ TValue extends object = object,
84
+ TResult = unknown,
85
+ > {
86
+ /** Presentation target component set used when resolving action endpoint bindings. */
87
+ readonly componentSet?: string | null
88
+
89
+ /** Optional data source specialization used when resolving an action endpoint binding. */
90
+ readonly dataSourceId?: string | null
91
+
92
+ /** Maps presentation data source ids to query keys for cache invalidation. */
93
+ readonly dataSourceQueryKey?: (dataSourceId: string) => readonly unknown[]
94
+
95
+ /** Optional baseline body merged before form fields are lowered into the request body. */
96
+ readonly defaultBody?: InputFormEndpointRequestOption<TValue, Readonly<Record<string, unknown>>>
97
+
98
+ /** Initial form value; field DefaultValue declarations are applied over this value. */
99
+ readonly defaultValue?: TValue | (() => TValue)
100
+
101
+ /** Runtime context used by form-level DefaultValues bindings, such as resource or route data. */
102
+ readonly defaultValueSources?: Readonly<Record<string, unknown>>
103
+
104
+ /** Application API adapter used to execute the resolved endpoint. */
105
+ readonly executeEndpoint: PresentationActionEndpointExecutor
106
+
107
+ /** Input form to project. Null yields a null runtime. */
108
+ readonly inputForm: InputFormDefinition | null
109
+
110
+ /** Data source ids to invalidate after success; defaults to action or form target policy. */
111
+ readonly invalidateDataSourceIds?:
112
+ | readonly string[]
113
+ | ((context: ProjectedInputFormEndpointSuccessContext<TValue, TResult>) => readonly string[])
114
+
115
+ /** Presentation module that owns form definitions, actions, and target bindings. */
116
+ readonly module: PresentationModuleDefinition | null
117
+
118
+ /**
119
+ * Optional full request preparer. When omitted, the runtime lowers the form
120
+ * value into request.body and resolves routeParameters from options.
121
+ */
122
+ readonly prepareRequest?: (
123
+ context: ProjectedInputFormEndpointRequestContext<TValue>,
124
+ ) => PresentationActionEndpointExecutionRequest
125
+
126
+ /** Optional result side effect that runs before cache invalidation and onSuccess. */
127
+ readonly processResult?: (context: ProjectedInputFormEndpointSuccessContext<TValue, TResult>) => void
128
+
129
+ /** Route parameters passed to the endpoint executor, such as entity ids. */
130
+ readonly routeParameters?: InputFormEndpointRequestOption<
131
+ TValue,
132
+ Readonly<Record<string, string | null | undefined>>
133
+ >
134
+
135
+ /** Optional direct cache write for endpoint results before invalidation runs. */
136
+ readonly setResultQueryData?: (
137
+ context: ProjectedInputFormEndpointSuccessContext<TValue, TResult>,
138
+ ) => void
139
+
140
+ /** Identity of the current form state instance; changing it resets draft value. */
141
+ readonly stateKey?: string | null
142
+
143
+ /** Final success callback after result handling and invalidation. */
144
+ readonly onSuccess?: (context: ProjectedInputFormEndpointSuccessContext<TValue, TResult>) => void
145
+ }
146
+
147
+ /**
148
+ * Runtime state returned by useProjectedInputFormEndpointRuntime.
149
+ *
150
+ * @typeParam TValue - Shape of the form draft and lowered endpoint body.
151
+ * @typeParam TResult - Result returned by the endpoint executor.
152
+ */
153
+ export interface ProjectedInputFormEndpointRuntime<
154
+ TValue extends object = object,
155
+ TResult = unknown,
156
+ > {
157
+ /** Endpoint currently resolved for the form's primary action, when available. */
158
+ readonly endpointId: string | null
159
+
160
+ /** Last endpoint execution error, if any. */
161
+ readonly error: unknown
162
+
163
+ /** Whether the endpoint request is currently in flight. */
164
+ readonly isPending: boolean
165
+
166
+ /** Whether the most recent endpoint request completed successfully. */
167
+ readonly isSuccess: boolean
168
+
169
+ /** Clears endpoint state and resets the form draft to its initial value. */
170
+ readonly reset: () => void
171
+
172
+ /** Clears only the last endpoint execution state, preserving the current draft value. */
173
+ readonly resetExecution: () => void
174
+
175
+ /** Resets only the form draft value. */
176
+ readonly resetValue: () => void
177
+
178
+ /** Most recent endpoint result. */
179
+ readonly result: TResult | undefined
180
+
181
+ /** Runtime consumed directly by ProjectedInputForm; null when the form is unsupported. */
182
+ readonly runtime: ProjectedInputFormRuntime<TValue> | null
183
+
184
+ /** Current draft form value. */
185
+ readonly value: TValue
186
+ }
187
+
188
+ interface InputFormEndpointMutationResult<TValue extends object, TResult> {
189
+ readonly context: ProjectedInputFormEndpointRequestContext<TValue>
190
+ readonly request: PresentationActionEndpointExecutionRequest
191
+ readonly result: TResult
192
+ }
193
+
194
+ /**
195
+ * Creates a ProjectedInputFormRuntime for input forms whose target kind is
196
+ * EndpointRequest.
197
+ *
198
+ * The hook is intentionally target-generic: it resolves the concrete endpoint
199
+ * from action bindings or the input-form target, owns keyed draft state, lowers
200
+ * fields by ValuePath into an endpoint body, executes through the provided API
201
+ * adapter, and applies optional cache/result policies.
202
+ *
203
+ * @typeParam TValue - Shape of the form draft and lowered endpoint body.
204
+ * @typeParam TResult - Result returned by the endpoint executor.
205
+ */
206
+ export function useProjectedInputFormEndpointRuntime<
207
+ TValue extends object = object,
208
+ TResult = unknown,
209
+ >({
210
+ componentSet = 'cohesive.presentation.react',
211
+ dataSourceId,
212
+ dataSourceQueryKey,
213
+ defaultBody,
214
+ defaultValue,
215
+ defaultValueSources,
216
+ executeEndpoint,
217
+ inputForm,
218
+ invalidateDataSourceIds,
219
+ module,
220
+ onSuccess,
221
+ prepareRequest,
222
+ processResult,
223
+ routeParameters,
224
+ setResultQueryData,
225
+ stateKey,
226
+ }: UseProjectedInputFormEndpointRuntimeOptions<TValue, TResult>): ProjectedInputFormEndpointRuntime<TValue, TResult> {
227
+ const queryClient = useQueryClient()
228
+ const initialValue = useMemo(
229
+ () =>
230
+ createInitialInputFormValue({
231
+ defaultValue: resolveInitialValue(defaultValue),
232
+ inputForm,
233
+ sources: defaultValueSources,
234
+ }),
235
+ [defaultValue, defaultValueSources, inputForm],
236
+ )
237
+ const effectiveStateKey = stateKey ?? inputForm?.Id ?? ''
238
+ const [state, setState] = useState<{
239
+ readonly key: string
240
+ readonly value: TValue
241
+ }>(() => ({
242
+ key: effectiveStateKey,
243
+ value: initialValue,
244
+ }))
245
+ const value = state.key === effectiveStateKey ? state.value : initialValue
246
+ const setRuntimeValue: ProjectedInputFormRuntime<TValue>['setValue'] = (update) => {
247
+ setState((current) => {
248
+ const currentValue = current.key === effectiveStateKey ? current.value : initialValue
249
+ const nextValue = typeof update === 'function'
250
+ ? (update as (value: TValue) => TValue)(currentValue)
251
+ : update
252
+
253
+ return {
254
+ key: effectiveStateKey,
255
+ value: nextValue,
256
+ }
257
+ })
258
+ }
259
+
260
+ const endpointId = useMemo(
261
+ () =>
262
+ inputForm
263
+ ? resolveInputFormEndpointId({
264
+ actionId: inputForm.Actions[0]?.ActionId ?? null,
265
+ componentSet,
266
+ dataSourceId: dataSourceId ?? inputForm.Target.DataSourceId,
267
+ inputForm,
268
+ module,
269
+ })
270
+ : null,
271
+ [componentSet, dataSourceId, inputForm, module],
272
+ )
273
+
274
+ const mutation = useMutation<
275
+ InputFormEndpointMutationResult<TValue, TResult>,
276
+ unknown,
277
+ ProjectedInputFormActionContext<TValue>
278
+ >({
279
+ mutationFn: async (actionContext) => {
280
+ if (!inputForm || !isEndpointRequestInputForm(inputForm)) {
281
+ throw new Error('The projected input form is not bound to an endpoint request target.')
282
+ }
283
+
284
+ const resolvedEndpointId = resolveInputFormEndpointId({
285
+ actionId: actionContext.placement.ActionId,
286
+ componentSet,
287
+ dataSourceId: dataSourceId ?? inputForm.Target.DataSourceId,
288
+ inputForm,
289
+ module,
290
+ })
291
+ if (!resolvedEndpointId) {
292
+ throw new Error(`No endpoint binding is registered for input form '${inputForm.Id}'.`)
293
+ }
294
+
295
+ const action = findPresentationAction<ActionDefinition>(
296
+ module,
297
+ actionContext.placement.ActionId,
298
+ )
299
+ const context = {
300
+ action,
301
+ actionContext,
302
+ endpointId: resolvedEndpointId,
303
+ inputForm,
304
+ module,
305
+ value: actionContext.value,
306
+ } satisfies ProjectedInputFormEndpointRequestContext<TValue>
307
+ const request = prepareRequest?.(context) ?? createDefaultEndpointRequest({
308
+ context,
309
+ defaultBody,
310
+ routeParameters,
311
+ })
312
+ const result = await executeEndpoint<TResult>(resolvedEndpointId, request)
313
+
314
+ return {
315
+ context,
316
+ request,
317
+ result,
318
+ }
319
+ },
320
+ onSuccess: async ({ context, request, result }) => {
321
+ const successContext = {
322
+ ...context,
323
+ queryClient,
324
+ request,
325
+ result,
326
+ } satisfies ProjectedInputFormEndpointSuccessContext<TValue, TResult>
327
+
328
+ setResultQueryData?.(successContext)
329
+ processResult?.(successContext)
330
+ await invalidateInputFormDataSources({
331
+ context: successContext,
332
+ dataSourceQueryKey,
333
+ inputForm,
334
+ invalidateDataSourceIds,
335
+ queryClient,
336
+ })
337
+ onSuccess?.(successContext)
338
+ },
339
+ })
340
+
341
+ const runtime = inputForm && isEndpointRequestInputForm(inputForm)
342
+ ? {
343
+ invokeAction: (context) => {
344
+ if (!mutation.isPending) {
345
+ mutation.mutate(context)
346
+ }
347
+ },
348
+ setValue: setRuntimeValue,
349
+ value,
350
+ } satisfies ProjectedInputFormRuntime<TValue>
351
+ : null
352
+
353
+ function resetValue() {
354
+ setState({
355
+ key: effectiveStateKey,
356
+ value: initialValue,
357
+ })
358
+ }
359
+
360
+ return {
361
+ endpointId,
362
+ error: mutation.error,
363
+ isPending: mutation.isPending,
364
+ isSuccess: mutation.isSuccess,
365
+ reset: () => {
366
+ mutation.reset()
367
+ resetValue()
368
+ },
369
+ resetExecution: mutation.reset,
370
+ resetValue,
371
+ result: mutation.data?.result,
372
+ runtime,
373
+ value,
374
+ }
375
+ }
376
+
377
+ /**
378
+ * Lowers a projected input-form value into an endpoint request body.
379
+ *
380
+ * Each field writes the value found at its ValuePath into the same target path.
381
+ * Missing, null, or empty values are replaced by the field's DefaultValue when
382
+ * present. The optional defaultBody is copied first, so host-specific request
383
+ * defaults such as concurrency tokens can be supplied outside the form surface.
384
+ *
385
+ * @typeParam TValue - Shape of the source form value.
386
+ */
387
+ export function createInputFormEndpointRequestBody<TValue extends object>({
388
+ defaultBody,
389
+ inputForm,
390
+ value,
391
+ }: {
392
+ readonly defaultBody?: Readonly<Record<string, unknown>>
393
+ readonly inputForm: InputFormDefinition
394
+ readonly value: TValue
395
+ }) {
396
+ const body: Record<string, unknown> = {
397
+ ...(defaultBody ?? {}),
398
+ }
399
+
400
+ for (const field of inputForm.Fields) {
401
+ const fieldValue = readInputFormEndpointFieldValue(value, field.ValuePath, field.DefaultValue)
402
+ if (fieldValue !== undefined) {
403
+ writeObjectPath(body, field.ValuePath, fieldValue)
404
+ }
405
+ }
406
+
407
+ return body
408
+ }
409
+
410
+ function createDefaultEndpointRequest<TValue extends object>({
411
+ context,
412
+ defaultBody,
413
+ routeParameters,
414
+ }: {
415
+ readonly context: ProjectedInputFormEndpointRequestContext<TValue>
416
+ readonly defaultBody?: InputFormEndpointRequestOption<TValue, Readonly<Record<string, unknown>>>
417
+ readonly routeParameters?: InputFormEndpointRequestOption<
418
+ TValue,
419
+ Readonly<Record<string, string | null | undefined>>
420
+ >
421
+ }): PresentationActionEndpointExecutionRequest {
422
+ return {
423
+ body: createInputFormEndpointRequestBody({
424
+ defaultBody: resolveOption(defaultBody, context),
425
+ inputForm: context.inputForm,
426
+ value: context.value,
427
+ }),
428
+ routeParameters: resolveOption(routeParameters, context),
429
+ }
430
+ }
431
+
432
+ function resolveInputFormEndpointId({
433
+ actionId,
434
+ componentSet,
435
+ dataSourceId,
436
+ inputForm,
437
+ module,
438
+ }: {
439
+ readonly actionId: string | null
440
+ readonly componentSet: string | null
441
+ readonly dataSourceId?: string | null
442
+ readonly inputForm: InputFormDefinition
443
+ readonly module: PresentationModuleDefinition | null
444
+ }) {
445
+ const actionEndpointId = actionId
446
+ ? resolvePresentationActionEndpointBinding({
447
+ actionId,
448
+ componentSet,
449
+ dataSourceId,
450
+ module,
451
+ })?.EndpointId
452
+ : null
453
+
454
+ return actionEndpointId ?? inputForm.Target.EndpointId ?? null
455
+ }
456
+
457
+ function createInitialInputFormValue<TValue extends object>({
458
+ defaultValue,
459
+ inputForm,
460
+ sources,
461
+ }: {
462
+ readonly defaultValue: TValue
463
+ readonly inputForm: InputFormDefinition | null
464
+ readonly sources?: Readonly<Record<string, unknown>>
465
+ }): TValue {
466
+ if (!inputForm) {
467
+ return defaultValue
468
+ }
469
+
470
+ const value = createInputFormEndpointRequestBody({
471
+ defaultBody: defaultValue as Readonly<Record<string, unknown>>,
472
+ inputForm,
473
+ value: defaultValue,
474
+ }) as TValue
475
+
476
+ return applyInputFormDefaultValueBindings({
477
+ inputForm,
478
+ sources,
479
+ value,
480
+ })
481
+ }
482
+
483
+ function resolveInitialValue<TValue extends object>(
484
+ defaultValue: TValue | (() => TValue) | undefined,
485
+ ): TValue {
486
+ if (typeof defaultValue === 'function') {
487
+ return (defaultValue as () => TValue)()
488
+ }
489
+
490
+ return (defaultValue ?? {}) as TValue
491
+ }
492
+
493
+ function readInputFormEndpointFieldValue(
494
+ value: object,
495
+ path: string,
496
+ defaultValue: string | null | undefined,
497
+ ) {
498
+ const valueAtPath = readObjectPath(value, path)
499
+ if (valueAtPath === undefined || valueAtPath === null || valueAtPath === '') {
500
+ return parseInputFormDefaultValue(defaultValue)
501
+ }
502
+
503
+ return valueAtPath
504
+ }
505
+
506
+ function parseInputFormDefaultValue(value: string | null | undefined) {
507
+ if (value === null || value === undefined) {
508
+ return undefined
509
+ }
510
+
511
+ if (value === 'null') {
512
+ return null
513
+ }
514
+
515
+ if (value === 'true') {
516
+ return true
517
+ }
518
+
519
+ if (value === 'false') {
520
+ return false
521
+ }
522
+
523
+ const numberValue = Number(value)
524
+ return Number.isFinite(numberValue) && value.trim() !== '' ? numberValue : value
525
+ }
526
+
527
+ function applyInputFormDefaultValueBindings<TValue extends object>({
528
+ inputForm,
529
+ sources,
530
+ value,
531
+ }: {
532
+ readonly inputForm: InputFormDefinition
533
+ readonly sources?: Readonly<Record<string, unknown>>
534
+ readonly value: TValue
535
+ }): TValue {
536
+ const defaultValues = inputForm.DefaultValues ?? []
537
+ if (defaultValues.length === 0) {
538
+ return value
539
+ }
540
+
541
+ const target = { ...(value as Readonly<Record<string, unknown>>) }
542
+ for (const binding of defaultValues) {
543
+ const resolved = resolveInputFormDefaultValue(binding.Source, sources)
544
+ if ((resolved === null || resolved === undefined) && binding.OmitWhenNull) {
545
+ continue
546
+ }
547
+
548
+ writeObjectPath(target, binding.TargetPath, resolved)
549
+ }
550
+
551
+ return target as TValue
552
+ }
553
+
554
+ function resolveInputFormDefaultValue(
555
+ value: PresentationValueDefinition,
556
+ sources: Readonly<Record<string, unknown>> | undefined,
557
+ ) {
558
+ if (isPresentationValueKind(value.Kind, presentationValueKinds.literal, 'literal')) {
559
+ return parseInputFormDefaultValue(value.Literal)
560
+ }
561
+
562
+ if (isPresentationValueKind(value.Kind, presentationValueKinds.field, 'field')) {
563
+ return value.Field ? readObjectPath(sources ?? {}, value.Field) : undefined
564
+ }
565
+
566
+ if (isPresentationValueKind(value.Kind, presentationValueKinds.state, 'state')) {
567
+ return value.StateId ? sources?.[value.StateId] : undefined
568
+ }
569
+
570
+ if (isPresentationValueKind(value.Kind, presentationValueKinds.expression, 'expression')) {
571
+ throw new Error('Input form default expression values are not supported yet.')
572
+ }
573
+
574
+ return undefined
575
+ }
576
+
577
+ function isPresentationValueKind(
578
+ value: unknown,
579
+ numericKind: number,
580
+ stringKind: string,
581
+ ) {
582
+ return value === numericKind ||
583
+ (typeof value === 'string' && value.toLowerCase() === stringKind.toLowerCase())
584
+ }
585
+
586
+ function isEndpointRequestInputForm(inputForm: InputFormDefinition) {
587
+ return inputForm.Target.Kind === inputFormTargetKinds.endpointRequest ||
588
+ String(inputForm.Target.Kind).toLowerCase() === 'endpointrequest'
589
+ }
590
+
591
+ function resolveOption<TValue extends object, TOption>(
592
+ option: InputFormEndpointRequestOption<TValue, TOption> | undefined,
593
+ context: ProjectedInputFormEndpointRequestContext<TValue>,
594
+ ) {
595
+ return typeof option === 'function'
596
+ ? (option as (context: ProjectedInputFormEndpointRequestContext<TValue>) => TOption)(context)
597
+ : option
598
+ }
599
+
600
+ async function invalidateInputFormDataSources<TValue extends object, TResult>({
601
+ context,
602
+ dataSourceQueryKey,
603
+ inputForm,
604
+ invalidateDataSourceIds,
605
+ queryClient,
606
+ }: {
607
+ readonly context: ProjectedInputFormEndpointSuccessContext<TValue, TResult>
608
+ readonly dataSourceQueryKey?: (dataSourceId: string) => readonly unknown[]
609
+ readonly inputForm: InputFormDefinition | null
610
+ readonly invalidateDataSourceIds?:
611
+ | readonly string[]
612
+ | ((context: ProjectedInputFormEndpointSuccessContext<TValue, TResult>) => readonly string[])
613
+ readonly queryClient: QueryClient
614
+ }) {
615
+ if (!dataSourceQueryKey) {
616
+ return
617
+ }
618
+
619
+ const dataSourceIds = typeof invalidateDataSourceIds === 'function'
620
+ ? invalidateDataSourceIds(context)
621
+ : (
622
+ invalidateDataSourceIds ??
623
+ context.action?.Result?.InvalidateDataSourceIds ??
624
+ (inputForm?.Target.DataSourceId ? [inputForm.Target.DataSourceId] : [])
625
+ )
626
+
627
+ await Promise.all(
628
+ Array.from(new Set(dataSourceIds)).map((dataSourceId) =>
629
+ queryClient.invalidateQueries({ queryKey: dataSourceQueryKey(dataSourceId) }),
630
+ ),
631
+ )
632
+ }