@cratis/scene.components 3.4.0 → 3.6.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.
Files changed (188) hide show
  1. package/README.md +87 -3
  2. package/bindings/ArcBoundComponents.tsx +40 -0
  3. package/bindings/BoundConstructor.ts +5 -5
  4. package/bindings/bindingRegistry.ts +52 -4
  5. package/bindings/for_bindingRegistry/when_registering_query_identities.ts +81 -0
  6. package/bindings/for_requireArcProxy/NativeProxies.ts +28 -0
  7. package/bindings/for_requireArcProxy/when_mounting_bound_adapters.tsx +73 -0
  8. package/bindings/for_requireArcProxy/when_previewing_without_arc.tsx +37 -0
  9. package/bindings/for_requireArcProxy/when_validating_a_proxy.ts +44 -0
  10. package/bindings/requireArcProxy.ts +24 -0
  11. package/cratisComponents.ts +4 -1
  12. package/cratisComponentsPackage.ts +3 -1
  13. package/data/SceneDataTable.tsx +1 -1
  14. package/data/SceneObservableDataTable.tsx +1 -1
  15. package/data/SceneSingleResult.tsx +29 -0
  16. package/data/SingleResultRuntime.tsx +103 -0
  17. package/data/SingleResultStatus.tsx +13 -0
  18. package/data/for_SceneDataTable/when_forwarding_query_arguments.tsx +23 -2
  19. package/data/for_SceneSingleResult/when_executing_a_native_query.tsx +290 -0
  20. package/data/for_SceneSingleResult/when_gating_the_lazy_runtime.tsx +60 -0
  21. package/data/index.ts +1 -0
  22. package/dialogs/SceneCommandDialog.tsx +1 -1
  23. package/dialogs/SceneStepperCommandDialog.tsx +1 -1
  24. package/dist/cjs/bindings/ArcBoundComponents.js +55 -0
  25. package/dist/cjs/bindings/ArcBoundComponents.js.map +1 -0
  26. package/dist/cjs/bindings/bindingRegistry.js +52 -4
  27. package/dist/cjs/bindings/bindingRegistry.js.map +1 -1
  28. package/dist/cjs/bindings/requireArcProxy.js +25 -0
  29. package/dist/cjs/bindings/requireArcProxy.js.map +1 -0
  30. package/dist/cjs/cratisComponents.js +4 -0
  31. package/dist/cjs/cratisComponents.js.map +1 -1
  32. package/dist/cjs/cratisComponentsPackage.js +3 -1
  33. package/dist/cjs/cratisComponentsPackage.js.map +1 -1
  34. package/dist/cjs/data/SceneDataTable.js +1 -1
  35. package/dist/cjs/data/SceneDataTable.js.map +1 -1
  36. package/dist/cjs/data/SceneObservableDataTable.js +1 -1
  37. package/dist/cjs/data/SceneObservableDataTable.js.map +1 -1
  38. package/dist/cjs/data/SceneSingleResult.js +41 -0
  39. package/dist/cjs/data/SceneSingleResult.js.map +1 -0
  40. package/dist/cjs/data/SingleResultRuntime.js +137 -0
  41. package/dist/cjs/data/SingleResultRuntime.js.map +1 -0
  42. package/dist/cjs/data/SingleResultStatus.js +23 -0
  43. package/dist/cjs/data/SingleResultStatus.js.map +1 -0
  44. package/dist/cjs/dialogs/SceneCommandDialog.js +1 -1
  45. package/dist/cjs/dialogs/SceneCommandDialog.js.map +1 -1
  46. package/dist/cjs/dialogs/SceneStepperCommandDialog.js +1 -1
  47. package/dist/cjs/dialogs/SceneStepperCommandDialog.js.map +1 -1
  48. package/dist/cjs/forms/SceneQueryInputForm.js +141 -0
  49. package/dist/cjs/forms/SceneQueryInputForm.js.map +1 -0
  50. package/dist/cjs/forms/fields/SceneRadioButtonField.js +4 -1
  51. package/dist/cjs/forms/fields/SceneRadioButtonField.js.map +1 -1
  52. package/dist/cjs/forms/queryInputs.js +50 -0
  53. package/dist/cjs/forms/queryInputs.js.map +1 -0
  54. package/dist/cjs/index.js +8 -0
  55. package/dist/cjs/index.js.map +1 -1
  56. package/dist/cjs/pages/SceneDataPage.js +1 -1
  57. package/dist/cjs/pages/SceneDataPage.js.map +1 -1
  58. package/dist/esm/bindings/ArcBoundComponents.d.ts +18 -0
  59. package/dist/esm/bindings/ArcBoundComponents.d.ts.map +1 -0
  60. package/dist/esm/bindings/ArcBoundComponents.js +49 -0
  61. package/dist/esm/bindings/ArcBoundComponents.js.map +1 -0
  62. package/dist/esm/bindings/bindingRegistry.d.ts +4 -0
  63. package/dist/esm/bindings/bindingRegistry.d.ts.map +1 -1
  64. package/dist/esm/bindings/bindingRegistry.js +49 -5
  65. package/dist/esm/bindings/bindingRegistry.js.map +1 -1
  66. package/dist/esm/bindings/for_bindingRegistry/when_registering_query_identities.d.ts +2 -0
  67. package/dist/esm/bindings/for_bindingRegistry/when_registering_query_identities.d.ts.map +1 -0
  68. package/dist/esm/bindings/for_bindingRegistry/when_registering_query_identities.js +73 -0
  69. package/dist/esm/bindings/for_bindingRegistry/when_registering_query_identities.js.map +1 -0
  70. package/dist/esm/bindings/for_requireArcProxy/NativeProxies.d.ts +25 -0
  71. package/dist/esm/bindings/for_requireArcProxy/NativeProxies.d.ts.map +1 -0
  72. package/dist/esm/bindings/for_requireArcProxy/NativeProxies.js +23 -0
  73. package/dist/esm/bindings/for_requireArcProxy/NativeProxies.js.map +1 -0
  74. package/dist/esm/bindings/for_requireArcProxy/when_mounting_bound_adapters.d.ts +2 -0
  75. package/dist/esm/bindings/for_requireArcProxy/when_mounting_bound_adapters.d.ts.map +1 -0
  76. package/dist/esm/bindings/for_requireArcProxy/when_mounting_bound_adapters.js +56 -0
  77. package/dist/esm/bindings/for_requireArcProxy/when_mounting_bound_adapters.js.map +1 -0
  78. package/dist/esm/bindings/for_requireArcProxy/when_previewing_without_arc.d.ts +2 -0
  79. package/dist/esm/bindings/for_requireArcProxy/when_previewing_without_arc.d.ts.map +1 -0
  80. package/dist/esm/bindings/for_requireArcProxy/when_previewing_without_arc.js +32 -0
  81. package/dist/esm/bindings/for_requireArcProxy/when_previewing_without_arc.js.map +1 -0
  82. package/dist/esm/bindings/for_requireArcProxy/when_validating_a_proxy.d.ts +2 -0
  83. package/dist/esm/bindings/for_requireArcProxy/when_validating_a_proxy.d.ts.map +1 -0
  84. package/dist/esm/bindings/for_requireArcProxy/when_validating_a_proxy.js +45 -0
  85. package/dist/esm/bindings/for_requireArcProxy/when_validating_a_proxy.js.map +1 -0
  86. package/dist/esm/bindings/requireArcProxy.d.ts +5 -0
  87. package/dist/esm/bindings/requireArcProxy.d.ts.map +1 -0
  88. package/dist/esm/bindings/requireArcProxy.js +23 -0
  89. package/dist/esm/bindings/requireArcProxy.js.map +1 -0
  90. package/dist/esm/cratisComponents.d.ts.map +1 -1
  91. package/dist/esm/cratisComponents.js +4 -0
  92. package/dist/esm/cratisComponents.js.map +1 -1
  93. package/dist/esm/cratisComponentsPackage.d.ts.map +1 -1
  94. package/dist/esm/cratisComponentsPackage.js +3 -1
  95. package/dist/esm/cratisComponentsPackage.js.map +1 -1
  96. package/dist/esm/data/SceneDataTable.js +1 -1
  97. package/dist/esm/data/SceneDataTable.js.map +1 -1
  98. package/dist/esm/data/SceneObservableDataTable.js +1 -1
  99. package/dist/esm/data/SceneObservableDataTable.js.map +1 -1
  100. package/dist/esm/data/SceneSingleResult.d.ts +3 -0
  101. package/dist/esm/data/SceneSingleResult.d.ts.map +1 -0
  102. package/dist/esm/data/SceneSingleResult.js +39 -0
  103. package/dist/esm/data/SceneSingleResult.js.map +1 -0
  104. package/dist/esm/data/SingleResultRuntime.d.ts +10 -0
  105. package/dist/esm/data/SingleResultRuntime.d.ts.map +1 -0
  106. package/dist/esm/data/SingleResultRuntime.js +133 -0
  107. package/dist/esm/data/SingleResultRuntime.js.map +1 -0
  108. package/dist/esm/data/SingleResultStatus.d.ts +7 -0
  109. package/dist/esm/data/SingleResultStatus.d.ts.map +1 -0
  110. package/dist/esm/data/SingleResultStatus.js +21 -0
  111. package/dist/esm/data/SingleResultStatus.js.map +1 -0
  112. package/dist/esm/data/for_SceneDataTable/when_forwarding_query_arguments.js +21 -4
  113. package/dist/esm/data/for_SceneDataTable/when_forwarding_query_arguments.js.map +1 -1
  114. package/dist/esm/data/for_SceneSingleResult/when_executing_a_native_query.d.ts +2 -0
  115. package/dist/esm/data/for_SceneSingleResult/when_executing_a_native_query.d.ts.map +1 -0
  116. package/dist/esm/data/for_SceneSingleResult/when_executing_a_native_query.js +255 -0
  117. package/dist/esm/data/for_SceneSingleResult/when_executing_a_native_query.js.map +1 -0
  118. package/dist/esm/data/for_SceneSingleResult/when_gating_the_lazy_runtime.d.ts +2 -0
  119. package/dist/esm/data/for_SceneSingleResult/when_gating_the_lazy_runtime.d.ts.map +1 -0
  120. package/dist/esm/data/for_SceneSingleResult/when_gating_the_lazy_runtime.js +54 -0
  121. package/dist/esm/data/for_SceneSingleResult/when_gating_the_lazy_runtime.js.map +1 -0
  122. package/dist/esm/data/index.d.ts +1 -0
  123. package/dist/esm/data/index.d.ts.map +1 -1
  124. package/dist/esm/data/index.js +1 -0
  125. package/dist/esm/data/index.js.map +1 -1
  126. package/dist/esm/dialogs/SceneCommandDialog.js +1 -1
  127. package/dist/esm/dialogs/SceneCommandDialog.js.map +1 -1
  128. package/dist/esm/dialogs/SceneStepperCommandDialog.js +1 -1
  129. package/dist/esm/dialogs/SceneStepperCommandDialog.js.map +1 -1
  130. package/dist/esm/for_cratisComponentsPackage/when_describing_query_input_form.d.ts +2 -0
  131. package/dist/esm/for_cratisComponentsPackage/when_describing_query_input_form.d.ts.map +1 -0
  132. package/dist/esm/for_cratisComponentsPackage/when_describing_query_input_form.js +14 -0
  133. package/dist/esm/for_cratisComponentsPackage/when_describing_query_input_form.js.map +1 -0
  134. package/dist/esm/for_cratisComponentsPackage/when_describing_single_result.d.ts +2 -0
  135. package/dist/esm/for_cratisComponentsPackage/when_describing_single_result.d.ts.map +1 -0
  136. package/dist/esm/for_cratisComponentsPackage/when_describing_single_result.js +16 -0
  137. package/dist/esm/for_cratisComponentsPackage/when_describing_single_result.js.map +1 -0
  138. package/dist/esm/forms/SceneQueryInputForm.d.ts +3 -0
  139. package/dist/esm/forms/SceneQueryInputForm.d.ts.map +1 -0
  140. package/dist/esm/forms/SceneQueryInputForm.js +139 -0
  141. package/dist/esm/forms/SceneQueryInputForm.js.map +1 -0
  142. package/dist/esm/forms/fields/SceneRadioButtonField.d.ts.map +1 -1
  143. package/dist/esm/forms/fields/SceneRadioButtonField.js +4 -1
  144. package/dist/esm/forms/fields/SceneRadioButtonField.js.map +1 -1
  145. package/dist/esm/forms/fields/for_SceneRadioButtonField/when_mapping_the_group_name.d.ts +2 -0
  146. package/dist/esm/forms/fields/for_SceneRadioButtonField/when_mapping_the_group_name.d.ts.map +1 -0
  147. package/dist/esm/forms/fields/for_SceneRadioButtonField/when_mapping_the_group_name.js +38 -0
  148. package/dist/esm/forms/fields/for_SceneRadioButtonField/when_mapping_the_group_name.js.map +1 -0
  149. package/dist/esm/forms/fields/for_resolveFieldBinding/when_resolving_a_field_binding.js +4 -0
  150. package/dist/esm/forms/fields/for_resolveFieldBinding/when_resolving_a_field_binding.js.map +1 -1
  151. package/dist/esm/forms/for_SceneQueryInputForm/ProjectLookup.d.ts +26 -0
  152. package/dist/esm/forms/for_SceneQueryInputForm/ProjectLookup.d.ts.map +1 -0
  153. package/dist/esm/forms/for_SceneQueryInputForm/ProjectLookup.js +44 -0
  154. package/dist/esm/forms/for_SceneQueryInputForm/ProjectLookup.js.map +1 -0
  155. package/dist/esm/forms/for_SceneQueryInputForm/when_submitting_generated_guid_parameters.d.ts +2 -0
  156. package/dist/esm/forms/for_SceneQueryInputForm/when_submitting_generated_guid_parameters.d.ts.map +1 -0
  157. package/dist/esm/forms/for_SceneQueryInputForm/when_submitting_generated_guid_parameters.js +175 -0
  158. package/dist/esm/forms/for_SceneQueryInputForm/when_submitting_generated_guid_parameters.js.map +1 -0
  159. package/dist/esm/forms/for_SceneQueryInputForm/when_submitting_native_queries.d.ts +2 -0
  160. package/dist/esm/forms/for_SceneQueryInputForm/when_submitting_native_queries.d.ts.map +1 -0
  161. package/dist/esm/forms/for_SceneQueryInputForm/when_submitting_native_queries.js +413 -0
  162. package/dist/esm/forms/for_SceneQueryInputForm/when_submitting_native_queries.js.map +1 -0
  163. package/dist/esm/forms/index.d.ts +2 -0
  164. package/dist/esm/forms/index.d.ts.map +1 -1
  165. package/dist/esm/forms/index.js +1 -0
  166. package/dist/esm/forms/index.js.map +1 -1
  167. package/dist/esm/forms/queryInputs.d.ts +10 -0
  168. package/dist/esm/forms/queryInputs.d.ts.map +1 -0
  169. package/dist/esm/forms/queryInputs.js +47 -0
  170. package/dist/esm/forms/queryInputs.js.map +1 -0
  171. package/dist/esm/index.js +3 -1
  172. package/dist/esm/index.js.map +1 -1
  173. package/dist/esm/pages/SceneDataPage.js +1 -1
  174. package/dist/esm/pages/SceneDataPage.js.map +1 -1
  175. package/dist/esm/tsconfig.tsbuildinfo +1 -1
  176. package/for_cratisComponentsPackage/when_describing_query_input_form.ts +17 -0
  177. package/for_cratisComponentsPackage/when_describing_single_result.ts +20 -0
  178. package/forms/SceneQueryInputForm.tsx +79 -0
  179. package/forms/fields/SceneRadioButtonField.tsx +4 -1
  180. package/forms/fields/for_SceneRadioButtonField/when_mapping_the_group_name.tsx +45 -0
  181. package/forms/fields/for_resolveFieldBinding/when_resolving_a_field_binding.ts +6 -0
  182. package/forms/for_SceneQueryInputForm/ProjectLookup.ts +54 -0
  183. package/forms/for_SceneQueryInputForm/when_submitting_generated_guid_parameters.tsx +188 -0
  184. package/forms/for_SceneQueryInputForm/when_submitting_native_queries.tsx +423 -0
  185. package/forms/index.ts +2 -0
  186. package/forms/queryInputs.ts +50 -0
  187. package/package.json +23 -7
  188. package/pages/SceneDataPage.tsx +1 -1
@@ -0,0 +1,29 @@
1
+ // Copyright (c) Cratis. All rights reserved.
2
+ // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
+
4
+ import { lazy } from 'react';
5
+ import { RegisteredComponentProps } from '@cratis/scene.react';
6
+ import { ArcRuntimeBoundary, BindingKind, MissingBinding, resolveElementBinding } from '../bindings';
7
+ import { booleanProperty, objectProperty, stringProperty } from '../properties';
8
+ import { SingleResultStatus } from './SingleResultStatus';
9
+
10
+ const SingleResultRuntime = lazy(() => import('./SingleResultRuntime'));
11
+
12
+ /** Read-only scalar field of an optional single model; the host owns and commits the input. */
13
+ export function SceneSingleResult({ element }: RegisteredComponentProps) {
14
+ const { name, target } = resolveElementBinding(element, BindingKind.Query);
15
+ if (!target) return <MissingBinding element={element} kind={BindingKind.Query} name={name} />;
16
+
17
+ // Nothing, including loading Arc, happens until the host explicitly commits its input.
18
+ if (!element.isEnabled || booleanProperty(element.properties, 'enabled') !== true) {
19
+ return <SingleResultStatus state='idle' />;
20
+ }
21
+
22
+ const queryArguments = objectProperty(element.properties, 'queryArguments');
23
+ const resultField = stringProperty(element.properties, 'resultField');
24
+ if (!queryArguments || !resultField) return <SingleResultStatus state='failure' />;
25
+
26
+ return <ArcRuntimeBoundary>
27
+ <SingleResultRuntime query={target} queryArguments={queryArguments} resultField={resultField} />
28
+ </ArcRuntimeBoundary>;
29
+ }
@@ -0,0 +1,103 @@
1
+ // Copyright (c) Cratis. All rights reserved.
2
+ // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
+
4
+ import { useContext, useEffect, useMemo, useState } from 'react';
5
+ import { ArcContext } from '@cratis/arc.react';
6
+ import type { QueryFor } from '@cratis/arc/queries';
7
+ import { Guid } from '@cratis/fundamentals';
8
+ import type { BoundConstructor } from '../bindings/BoundConstructor';
9
+ import { SingleResultStatus } from './SingleResultStatus';
10
+
11
+ interface Props {
12
+ query: BoundConstructor;
13
+ queryArguments: Record<string, unknown>;
14
+ resultField: string;
15
+ /** queryInputForm alone opts into scalar String/Guid descriptor mapping; no value conversion. */
16
+ stringInputs?: boolean;
17
+ }
18
+
19
+ type Outcome = { state: 'failure' | 'notFound' } | { state: 'success'; value: string };
20
+
21
+ /**
22
+ * Private execution owner. Arc owns validation, routing, HTTP and deserialization; this component
23
+ * owns only request lifetime and presentation. No shared cache can hand B the result belonging to A.
24
+ */
25
+ export default function SingleResultRuntime({ query, queryArguments, resultField, stringInputs = false }: Props) {
26
+ const arc = useContext(ArcContext);
27
+ const { microservice, apiBasePath, origin, httpHeadersCallback, queryVersion } = arc;
28
+ // Identity is deliberate: hosts replace immutable committed arguments rather than mutating them.
29
+ const request = useMemo(() => ({ query, queryArguments, resultField, stringInputs, microservice, apiBasePath, origin, httpHeadersCallback, queryVersion }),
30
+ [query, queryArguments, resultField, stringInputs, microservice, apiBasePath, origin, httpHeadersCallback, queryVersion]);
31
+ const [settled, setSettled] = useState<{ request: typeof request; outcome: Outcome }>();
32
+
33
+ useEffect(() => {
34
+ // Each effect setup is a generation, including StrictMode's setup/cleanup/setup replay.
35
+ let current = true;
36
+ let instance: QueryFor<unknown, Record<string, unknown>> | undefined;
37
+ const publish = (outcome: Outcome) => {
38
+ if (current) setSettled({ request, outcome });
39
+ };
40
+ const execute = async () => {
41
+ try {
42
+ // The registry intentionally stores constructors without an eager Arc dependency.
43
+ instance = new request.query() as QueryFor<unknown, Record<string, unknown>>;
44
+ if (instance.enumerable !== false) {
45
+ publish({ state: 'failure' });
46
+ return;
47
+ }
48
+ // Arc perform does not validate argument-to-descriptor types. The form supports only
49
+ // scalar String/Guid inputs, matched by exact name and constructor, never by naming
50
+ // convention. This check is lazy and form-only; singleResult's host contract is intact.
51
+ if (request.stringInputs && !Object.entries(request.queryArguments).every(([name, value]) => {
52
+ const descriptors = instance!.parameterDescriptors.filter(descriptor => descriptor.name === name);
53
+ return typeof value === 'string' && descriptors.length === 1 && !descriptors[0].isEnumerable
54
+ && (descriptors[0].type === String || descriptors[0].type === Guid);
55
+ })) {
56
+ publish({ state: 'failure' });
57
+ return;
58
+ }
59
+ instance.setMicroservice(request.microservice);
60
+ instance.setApiBasePath(request.apiBasePath ?? '');
61
+ instance.setOrigin(request.origin ?? '');
62
+ instance.setHttpHeadersCallback(request.httpHeadersCallback ?? (() => ({})));
63
+ // Native perform validates required arguments and proxy rules before HTTP. No copying,
64
+ // defaults, business validation, route construction or array wrapping happens here.
65
+ const result = await instance.perform(request.queryArguments);
66
+ if (!current) return;
67
+ if (result.isSuccess !== true || result.isAuthorized !== true || result.isValid !== true || result.hasExceptions !== false || result.isReady === false) {
68
+ publish({ state: 'failure' });
69
+ return;
70
+ }
71
+ if (result.data === null || result.data === undefined) {
72
+ publish({ state: 'notFound' });
73
+ return;
74
+ }
75
+ if (typeof result.data !== 'object' || Array.isArray(result.data)) {
76
+ publish({ state: 'failure' });
77
+ return;
78
+ }
79
+ // Only one own data field, never a path, inherited member, getter or expression.
80
+ const descriptor = Object.getOwnPropertyDescriptor(result.data, request.resultField);
81
+ const value: unknown = descriptor && 'value' in descriptor ? descriptor.value : undefined;
82
+ if (typeof value === 'string' || typeof value === 'boolean' || (typeof value === 'number' && Number.isFinite(value))) {
83
+ publish({ state: 'success', value: String(value) });
84
+ } else {
85
+ publish({ state: 'failure' });
86
+ }
87
+ } catch {
88
+ // Includes native AbortError rejections. Disposed generations cannot publish or leak
89
+ // an unhandled promise, even when the HTTP boundary ignores cancellation.
90
+ publish({ state: 'failure' });
91
+ }
92
+ };
93
+ void execute();
94
+ return () => {
95
+ current = false;
96
+ instance?.abortController?.abort();
97
+ };
98
+ }, [request]);
99
+
100
+ // Mask old data during the render that changes inputs, before effect cleanup/setup runs.
101
+ if (settled?.request !== request) return <SingleResultStatus state='loading' />;
102
+ return <SingleResultStatus {...settled.outcome} />;
103
+ }
@@ -0,0 +1,13 @@
1
+ // Copyright (c) Cratis. All rights reserved.
2
+ // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
+
4
+ type State = 'idle' | 'loading' | 'notFound' | 'failure' | 'success';
5
+
6
+ const messages = { idle: 'Idle', loading: 'Loading', notFound: 'Not found', failure: 'Unable to load result', success: '' };
7
+
8
+ /** Internal shared presentation; never exposes query exception messages or server stack traces. */
9
+ export function SingleResultStatus({ state, value }: { state: State; value?: string }) {
10
+ return <span role={state === 'failure' ? 'alert' : 'status'} data-scene-single-result={state}>
11
+ {state === 'success' ? value : messages[state]}
12
+ </span>;
13
+ }
@@ -5,8 +5,8 @@ import { act, cleanup, render, screen } from '@testing-library/react';
5
5
  import { vi } from 'vitest';
6
6
  import { externalComponent } from '../../given';
7
7
 
8
- class InvoicesForProject {}
9
- class AnotherQuery {}
8
+ let InvoicesForProject: typeof import('../../bindings/for_requireArcProxy/NativeProxies').Snapshot;
9
+ let AnotherQuery: typeof InvoicesForProject;
10
10
 
11
11
  // Load a fresh adapter so its lazy component cannot leak the peer double into other specs.
12
12
  describe('when forwarding query arguments', () => {
@@ -24,6 +24,8 @@ describe('when forwarding query arguments', () => {
24
24
  return <div data-testid='query-table' />;
25
25
  },
26
26
  }));
27
+ ({ Snapshot: InvoicesForProject } = await import('../../bindings/for_requireArcProxy/NativeProxies'));
28
+ AnotherQuery = InvoicesForProject;
27
29
  bindings = await import('../../bindings');
28
30
  bindings.clearBindings();
29
31
  bindings.registerQuery('InvoicesForProject', InvoicesForProject);
@@ -55,6 +57,25 @@ describe('when forwarding query arguments', () => {
55
57
  properties.should.have.property('queryArguments').that.deep.equals({ projectId: suppliedId });
56
58
  });
57
59
 
60
+ it('should bridge an identity-only query to an existing table without a second legacy registration', async () => {
61
+ bindings.clearBindings();
62
+ bindings.registerQueryIdentity('InvoicesForProject', 'Invoices/ForProject', InvoicesForProject);
63
+ const queryArguments = { projectId: suppliedId };
64
+ await renderTable({ query: 'InvoicesForProject', queryArguments });
65
+ await screen.findByTestId('query-table');
66
+ (received.mock.lastCall![0].query === InvoicesForProject).should.equal(true);
67
+ (received.mock.lastCall![0].queryArguments === queryArguments).should.equal(true);
68
+ });
69
+
70
+ it('should visibly reject ambiguous identity-only tables without selecting either native proxy', async () => {
71
+ bindings.clearBindings();
72
+ bindings.registerQueryIdentity('InvoicesForProject', 'One/ForProject', InvoicesForProject);
73
+ bindings.registerQueryIdentity('InvoicesForProject', 'Two/ForProject', AnotherQuery);
74
+ await renderTable({ query: 'InvoicesForProject' });
75
+ (screen.getByText("Unresolved query binding 'InvoicesForProject' on Cratis.Components:dataTable") !== null).should.equal(true);
76
+ received.mock.calls.should.have.lengthOf(0);
77
+ });
78
+
58
79
  it('should preserve all supplied keys and values without normalization or defaults', async () => {
59
80
  const queryArguments = { projectId: suppliedId, ProjectID: 'distinct', limit: 0, enabled: false, optional: null, filter: { names: ['one'] } };
60
81
  await renderTable({ query: 'InvoicesForProject', queryArguments });
@@ -0,0 +1,290 @@
1
+ // Copyright (c) Cratis. All rights reserved.
2
+ // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
+
4
+ import { act, cleanup, render, screen, waitFor } from '@testing-library/react';
5
+ import { expect, vi } from 'vitest';
6
+ import { StrictMode } from 'react';
7
+ import { ArcContext, type ArcConfiguration } from '@cratis/arc.react';
8
+ import { QueryFor, QueryHttpMethod, QueryValidator } from '@cratis/arc/queries';
9
+ import { clearBindings, registerQuery } from '../../bindings';
10
+ import { externalComponent } from '../../given';
11
+ import { SceneSingleResult } from '../SceneSingleResult';
12
+
13
+ class ProjectName extends QueryFor<object | undefined, { projectId: string }> {
14
+ readonly route = '/projects/{projectId}/name';
15
+ readonly parameterDescriptors = [];
16
+ readonly requiredRequestParameters = ['projectId'];
17
+ defaultValue = undefined;
18
+
19
+ constructor() {
20
+ super(Object, false);
21
+ this.setHttpMethod(QueryHttpMethod.Get);
22
+ }
23
+ }
24
+
25
+ class ProjectNames extends QueryFor<object[]> {
26
+ readonly route = '/projects';
27
+ readonly parameterDescriptors = [];
28
+ readonly requiredRequestParameters = [];
29
+ defaultValue = [];
30
+
31
+ constructor() { super(Object, true); }
32
+ }
33
+
34
+ class ProjectArgumentsValidator extends QueryValidator<{ projectId: string }> {
35
+ static received: unknown;
36
+
37
+ constructor() {
38
+ super();
39
+ this.ruleFor(args => {
40
+ ProjectArgumentsValidator.received = args;
41
+ return args.projectId;
42
+ }).minLength(2);
43
+ }
44
+ }
45
+
46
+ class ValidatedProjectName extends ProjectName {
47
+ readonly validation = new ProjectArgumentsValidator();
48
+ }
49
+
50
+ function envelope(data: unknown, overrides: Record<string, unknown> = {}) {
51
+ return {
52
+ data, isSuccess: true, isAuthorized: true, isValid: true, hasExceptions: false,
53
+ validationResults: [], exceptionMessages: [], exceptionStackTrace: '',
54
+ paging: { page: 0, size: 0, totalItems: 0, totalPages: 0 }, ...overrides,
55
+ };
56
+ }
57
+
58
+ function response(data: unknown, overrides: Record<string, unknown> = {}) {
59
+ return { ok: true, status: 200, json: async () => envelope(data, overrides) } as Response;
60
+ }
61
+
62
+ function deferred() {
63
+ let resolve!: (value: Response) => void;
64
+ let reject!: (error: unknown) => void;
65
+ const promise = new Promise<Response>((yes, no) => { resolve = yes; reject = no; });
66
+ return { promise, resolve, reject };
67
+ }
68
+
69
+ describe('when executing an optional single result with native Arc 22.16.1', () => {
70
+ const headers = () => ({ 'x-host': 'one' });
71
+ const context: ArcConfiguration = { microservice: 'projects', origin: 'https://example.test', apiBasePath: '/backend', httpHeadersCallback: headers };
72
+ const fetch = vi.fn<typeof globalThis.fetch>();
73
+
74
+ function view(queryArguments: unknown = { projectId: 'A' }, arc = context, properties: Record<string, unknown> = {}) {
75
+ return <ArcContext.Provider value={arc}>
76
+ <SceneSingleResult element={externalComponent('Cratis.Components:singleResult', {
77
+ query: 'ProjectName', queryArguments, enabled: true, resultField: 'name', ...properties,
78
+ })} slots={{}} />
79
+ </ArcContext.Provider>;
80
+ }
81
+
82
+ beforeEach(() => {
83
+ clearBindings();
84
+ registerQuery('ProjectName', ProjectName);
85
+ registerQuery('ProjectNames', ProjectNames);
86
+ registerQuery('ValidatedProjectName', ValidatedProjectName);
87
+ fetch.mockReset();
88
+ vi.stubGlobal('fetch', fetch);
89
+ });
90
+
91
+ afterEach(() => {
92
+ cleanup();
93
+ clearBindings();
94
+ vi.unstubAllGlobals();
95
+ });
96
+
97
+ it('uses the native URL, arguments and context headers without changing the host object', async () => {
98
+ const args = Object.freeze({ projectId: 'AA', locale: 'en' });
99
+ fetch.mockResolvedValue(response({ name: '<b>Project A</b>' }));
100
+ await act(async () => { render(view(args, context, { query: 'ValidatedProjectName' })); });
101
+ expect(await screen.findByText('<b>Project A</b>')).not.toBeNull();
102
+ expect(document.querySelector('b')).toBeNull();
103
+ expect(fetch).toHaveBeenCalledTimes(1);
104
+ const [url, init] = fetch.mock.calls[0];
105
+ expect(String(url)).toBe('https://example.test/backend/projects/AA/name?locale=en');
106
+ expect(ProjectArgumentsValidator.received).toBe(args);
107
+ expect(new Headers(init?.headers).get('x-host')).toBe('one');
108
+ expect(new Headers(init?.headers).get('x-cratis-microservice')).toBe('projects');
109
+ expect(args).toEqual({ projectId: 'AA', locale: 'en' });
110
+ });
111
+
112
+ for (const value of [false, 0, '']) {
113
+ it(`renders scalar own-field ${JSON.stringify(value)} as present`, async () => {
114
+ fetch.mockResolvedValue(response({ name: value }));
115
+ await act(async () => { render(view()); });
116
+ await waitFor(() => expect(screen.getByRole('status').getAttribute('data-scene-single-result')).toBe('success'));
117
+ expect(screen.getByRole('status').textContent).toBe(String(value));
118
+ });
119
+ }
120
+
121
+ for (const data of [null, undefined]) {
122
+ it(`shows not found for successful ${String(data)}`, async () => {
123
+ fetch.mockResolvedValue(response(data));
124
+ await act(async () => { render(view()); });
125
+ expect(await screen.findByText('Not found')).not.toBeNull();
126
+ });
127
+ }
128
+
129
+ for (const failure of [{ isSuccess: false }, { isAuthorized: false }, { isValid: false }, { hasExceptions: true }]) {
130
+ it(`prioritizes failure over missing data: ${JSON.stringify(failure)}`, async () => {
131
+ fetch.mockResolvedValue(response(null, { ...failure, exceptionMessages: ['secret'], exceptionStackTrace: 'server stack' }));
132
+ await act(async () => { render(view()); });
133
+ expect(await screen.findByRole('alert')).toHaveProperty('textContent', 'Unable to load result');
134
+ expect(screen.queryByText('Not found')).toBeNull();
135
+ expect(document.body.textContent).not.toMatch(/secret|server stack/);
136
+ });
137
+ }
138
+
139
+ for (const args of [{}, { projectId: '' }, { projectId: null }, [], null, 'A', undefined]) {
140
+ it(`does not send invalid or missing arguments: ${JSON.stringify(args)}`, async () => {
141
+ await act(async () => { render(view(args, context, { queryArguments: args })); });
142
+ expect(await screen.findByRole('alert')).not.toBeNull();
143
+ expect(fetch).not.toHaveBeenCalled();
144
+ });
145
+ }
146
+
147
+ it('lets the native proxy validator reject arguments without HTTP', async () => {
148
+ await act(async () => { render(view({ projectId: 'A' }, context, { query: 'ValidatedProjectName' })); });
149
+ expect(await screen.findByRole('alert')).not.toBeNull();
150
+ expect(fetch).not.toHaveBeenCalled();
151
+ });
152
+
153
+ for (const enabled of [false, undefined, 'true']) {
154
+ it(`is idle unless explicitly enabled: ${String(enabled)}`, async () => {
155
+ await act(async () => { render(view({ projectId: 'A' }, context, { enabled })); });
156
+ expect(screen.getByText('Idle')).not.toBeNull();
157
+ expect(fetch).not.toHaveBeenCalled();
158
+ });
159
+ }
160
+
161
+ it('never chooses another query when the binding is missing', async () => {
162
+ await act(async () => { render(view({}, context, { query: 'Missing' })); });
163
+ expect(screen.getByText(/Unresolved query binding 'Missing'/)).not.toBeNull();
164
+ expect(fetch).not.toHaveBeenCalled();
165
+ });
166
+
167
+ it('aborts A and ignores reverse completion when the transport ignores abort', async () => {
168
+ const a = deferred();
169
+ const b = deferred();
170
+ fetch.mockReturnValueOnce(a.promise).mockReturnValueOnce(b.promise);
171
+ let mounted!: ReturnType<typeof render>;
172
+ await act(async () => { mounted = render(view()); });
173
+ expect(screen.getByText('Loading')).not.toBeNull();
174
+ const signal = fetch.mock.calls[0][1]?.signal;
175
+ await act(async () => { mounted.rerender(view({ projectId: 'B' })); });
176
+ expect(signal?.aborted).toBe(true);
177
+ await act(async () => { b.resolve(response({ name: 'B' })); });
178
+ expect(screen.getByText('B')).not.toBeNull();
179
+ await act(async () => { a.resolve(response({ name: 'A' })); });
180
+ expect(screen.queryByText('A')).toBeNull();
181
+ expect(screen.getByText('B')).not.toBeNull();
182
+ });
183
+
184
+ it('hides a settled A immediately on B and aborts B on disposal', async () => {
185
+ const b = deferred();
186
+ fetch.mockResolvedValueOnce(response({ name: 'A' })).mockReturnValueOnce(b.promise);
187
+ let mounted!: ReturnType<typeof render>;
188
+ await act(async () => { mounted = render(view()); });
189
+ expect(screen.getByText('A')).not.toBeNull();
190
+ await act(async () => { mounted.rerender(view({ projectId: 'B' })); });
191
+ expect(screen.queryByText('A')).toBeNull();
192
+ expect(screen.getByText('Loading')).not.toBeNull();
193
+ mounted.unmount();
194
+ expect(fetch.mock.calls[1][1]?.signal?.aborted).toBe(true);
195
+ await act(async () => { b.reject(new DOMException('Aborted', 'AbortError')); });
196
+ });
197
+
198
+ it('reexecutes for header-only context changes and ignores the old response', async () => {
199
+ const a = deferred();
200
+ const b = deferred();
201
+ const args = { projectId: 'A' };
202
+ fetch.mockReturnValueOnce(a.promise).mockReturnValueOnce(b.promise);
203
+ let mounted!: ReturnType<typeof render>;
204
+ await act(async () => { mounted = render(view(args)); });
205
+ await act(async () => { mounted.rerender(view(args, { ...context, httpHeadersCallback: () => ({ 'x-host': 'two' }) })); });
206
+ expect(fetch.mock.calls[0][1]?.signal?.aborted).toBe(true);
207
+ expect(new Headers(fetch.mock.calls[1][1]?.headers).get('x-host')).toBe('two');
208
+ await act(async () => { b.resolve(response({ name: 'New context' })); a.resolve(response({ name: 'Old context' })); });
209
+ expect(screen.getByText('New context')).not.toBeNull();
210
+ expect(screen.queryByText('Old context')).toBeNull();
211
+ });
212
+
213
+ it('rejects enumerable proxies before HTTP rather than adapting them to a single model', async () => {
214
+ await act(async () => { render(view({}, context, { query: 'ProjectNames' })); });
215
+ expect(await screen.findByRole('alert')).not.toBeNull();
216
+ expect(fetch).not.toHaveBeenCalled();
217
+ });
218
+
219
+ it('handles StrictMode disposal without an unhandled abort promise', async () => {
220
+ const a = deferred();
221
+ const b = deferred();
222
+ fetch.mockReturnValueOnce(a.promise).mockReturnValueOnce(b.promise);
223
+ await act(async () => { render(<StrictMode>{view()}</StrictMode>); });
224
+ expect(fetch).toHaveBeenCalledTimes(2);
225
+ expect(fetch.mock.calls[0][1]?.signal?.aborted).toBe(true);
226
+ await act(async () => {
227
+ a.reject(new DOMException('Aborted', 'AbortError'));
228
+ b.resolve(response({ name: 'Current generation' }));
229
+ });
230
+ expect(screen.getByText('Current generation')).not.toBeNull();
231
+ });
232
+
233
+ it('ignores successful completion after unmount even if HTTP ignores abort', async () => {
234
+ const pending = deferred();
235
+ fetch.mockReturnValueOnce(pending.promise);
236
+ let mounted!: ReturnType<typeof render>;
237
+ await act(async () => { mounted = render(view()); });
238
+ mounted.unmount();
239
+ expect(fetch.mock.calls[0][1]?.signal?.aborted).toBe(true);
240
+ await act(async () => { pending.resolve(response({ name: 'Disposed' })); });
241
+ expect(screen.queryByText('Disposed')).toBeNull();
242
+ });
243
+
244
+ it('turns transport rejection and malformed envelopes into safe failure', async () => {
245
+ fetch.mockRejectedValueOnce(new Error('private network detail'));
246
+ let mounted!: ReturnType<typeof render>;
247
+ await act(async () => { mounted = render(view()); });
248
+ expect(await screen.findByRole('alert')).toHaveProperty('textContent', 'Unable to load result');
249
+ fetch.mockResolvedValueOnce({ ok: false, status: 500, json: async () => ({}) } as Response);
250
+ await act(async () => { mounted.rerender(view({ projectId: 'B' })); });
251
+ expect(await screen.findByRole('alert')).toHaveProperty('textContent', 'Unable to load result');
252
+ expect(document.body.textContent).not.toContain('private network detail');
253
+ });
254
+
255
+ it('aborts and clears a pending request when disabled', async () => {
256
+ const pending = deferred();
257
+ fetch.mockReturnValueOnce(pending.promise);
258
+ let mounted!: ReturnType<typeof render>;
259
+ await act(async () => { mounted = render(view()); });
260
+ await act(async () => { mounted.rerender(view({ projectId: 'A' }, context, { enabled: false })); });
261
+ expect(fetch.mock.calls[0][1]?.signal?.aborted).toBe(true);
262
+ await act(async () => { pending.resolve(response({ name: 'Obsolete' })); });
263
+ expect(screen.getByText('Idle')).not.toBeNull();
264
+ expect(screen.queryByText('Obsolete')).toBeNull();
265
+ });
266
+
267
+ for (const change of [{ origin: 'https://other.test' }, { apiBasePath: '/v2' }, { microservice: 'other' }, { queryVersion: 1 }]) {
268
+ it(`invalidates a settled result after context changes: ${JSON.stringify(change)}`, async () => {
269
+ const pending = deferred();
270
+ const args = { projectId: 'A' };
271
+ fetch.mockResolvedValueOnce(response({ name: 'Old' })).mockReturnValueOnce(pending.promise);
272
+ let mounted!: ReturnType<typeof render>;
273
+ await act(async () => { mounted = render(view(args)); });
274
+ await act(async () => { mounted.rerender(view(args, { ...context, ...change })); });
275
+ expect(screen.queryByText('Old')).toBeNull();
276
+ expect(screen.getByText('Loading')).not.toBeNull();
277
+ expect(fetch).toHaveBeenCalledTimes(2);
278
+ await act(async () => { pending.resolve(response({ name: 'New' })); });
279
+ expect(screen.getByText('New')).not.toBeNull();
280
+ });
281
+ }
282
+
283
+ for (const data of [{ name: {} }, { name: [] }, {}, Object.create({ name: 'Inherited' }), [{ name: 'A' }]]) {
284
+ it(`rejects non-scalar or absent own fields: ${JSON.stringify(data)}`, async () => {
285
+ fetch.mockResolvedValue(response(data));
286
+ await act(async () => { render(view()); });
287
+ expect(await screen.findByRole('alert')).not.toBeNull();
288
+ });
289
+ }
290
+ });
@@ -0,0 +1,60 @@
1
+ // Copyright (c) Cratis. All rights reserved.
2
+ // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
+
4
+ import { act, cleanup, render, screen } from '@testing-library/react';
5
+ import { expect, vi } from 'vitest';
6
+ import { externalComponent } from '../../given';
7
+
8
+ class RegisteredQuery {}
9
+
10
+ describe('when gating the single result lazy runtime', () => {
11
+ const loaded = vi.fn();
12
+ let SceneSingleResult: typeof import('../SceneSingleResult').SceneSingleResult;
13
+ let bindings: typeof import('../../bindings');
14
+
15
+ beforeEach(async () => {
16
+ vi.resetModules();
17
+ loaded.mockClear();
18
+ vi.doMock('../SingleResultRuntime', () => {
19
+ loaded();
20
+ return { default: () => <span>Runtime mounted</span> };
21
+ });
22
+ bindings = await import('../../bindings');
23
+ bindings.clearBindings();
24
+ bindings.registerQuery('Registered', RegisteredQuery);
25
+ ({ SceneSingleResult } = await import('../SceneSingleResult'));
26
+ });
27
+
28
+ afterEach(() => {
29
+ cleanup();
30
+ bindings.clearBindings();
31
+ vi.doUnmock('../SingleResultRuntime');
32
+ vi.resetModules();
33
+ });
34
+
35
+ for (const properties of [
36
+ { query: 'Missing', enabled: true, queryArguments: {}, resultField: 'name' },
37
+ { query: 'Registered', queryArguments: {}, resultField: 'name' },
38
+ { query: 'Registered', enabled: true, queryArguments: [], resultField: 'name' },
39
+ { query: 'Registered', enabled: true, queryArguments: {}, resultField: '' },
40
+ ]) {
41
+ it(`does not import Arc-bound code for ${JSON.stringify(properties)}`, async () => {
42
+ await act(async () => {
43
+ render(<SceneSingleResult element={externalComponent('Cratis.Components:singleResult', properties)} slots={{}} />);
44
+ });
45
+ expect(loaded).not.toHaveBeenCalled();
46
+ expect(screen.queryByText('Runtime mounted')).toBeNull();
47
+ expect(document.body.textContent).toMatch(/Unresolved query binding|Idle|Unable to load result/);
48
+ });
49
+ }
50
+
51
+ it('loads only the explicitly bound and enabled view', async () => {
52
+ await act(async () => {
53
+ render(<SceneSingleResult element={externalComponent('Cratis.Components:singleResult', {
54
+ query: 'Registered', enabled: true, queryArguments: {}, resultField: 'name',
55
+ })} slots={{}} />);
56
+ });
57
+ expect(await screen.findByText('Runtime mounted')).not.toBeNull();
58
+ expect(loaded).toHaveBeenCalledTimes(1);
59
+ });
60
+ });
package/data/index.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  // Copyright (c) Cratis. All rights reserved.
2
2
  // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
3
 
4
+ export * from './SceneSingleResult';
4
5
  export * from './SceneDataTable';
5
6
  export * from './SceneObservableDataTable';
@@ -6,7 +6,7 @@ import { RegisteredComponentProps } from '@cratis/scene.react';
6
6
  import { ArcRuntimeBoundary, BindingKind, MissingBinding, resolveElementBinding } from '../bindings';
7
7
  import { booleanProperty, stringProperty } from '../properties';
8
8
 
9
- const CommandDialog = lazy(async () => ({ default: (await import('@cratis/components/CommandDialog')).CommandDialog }));
9
+ const CommandDialog = lazy(async () => ({ default: (await import('../bindings/ArcBoundComponents')).BoundCommandDialog }));
10
10
 
11
11
  /**
12
12
  * The `Cratis.Components:commandDialog` component - `CommandDialog` from `@cratis/components/CommandDialog`.
@@ -7,7 +7,7 @@ import { ArcRuntimeBoundary, BindingKind, MissingBinding, resolveElementBinding
7
7
  import { booleanProperty, stringProperty, unionProperty } from '../properties';
8
8
 
9
9
  const StepperCommandDialog = lazy(async () => ({
10
- default: (await import('@cratis/components/CommandDialog')).StepperCommandDialog,
10
+ default: (await import('../bindings/ArcBoundComponents')).BoundStepperCommandDialog,
11
11
  }));
12
12
 
13
13
  /** Whether the step headers run across the top or down the side. */
@@ -0,0 +1,55 @@
1
+ 'use strict';
2
+
3
+ var jsxRuntime = require('react/jsx-runtime');
4
+ var queries = require('@cratis/arc/queries');
5
+ var commands = require('@cratis/arc/commands');
6
+ var DataTables = require('@cratis/components/DataTables');
7
+ var DataPage = require('@cratis/components/DataPage');
8
+ var CommandDialog = require('@cratis/components/CommandDialog');
9
+ var requireArcProxy = require('./requireArcProxy.js');
10
+
11
+ // Copyright (c) Cratis. All rights reserved.
12
+ // Licensed under the MIT license. See LICENSE file in the project root for full license information.
13
+ // Runtime-only module: import dynamically beneath ArcRuntimeBoundary, never through the barrel.
14
+ // Scene carries property names and argument objects, not compile-time row schemas. These adapters
15
+ // support generated object/collection queries, as required by the Components public prop contracts.
16
+ function BoundDataTable({ query, ...props }) {
17
+ return /*#__PURE__*/ jsxRuntime.jsx(DataTables.DataTableForQuery, {
18
+ ...props,
19
+ query: requireArcProxy.requireArcProxy(query, queries.QueryFor, 'query')
20
+ });
21
+ }
22
+ function BoundObservableDataTable({ query, ...props }) {
23
+ return /*#__PURE__*/ jsxRuntime.jsx(DataTables.DataTableForObservableQuery, {
24
+ ...props,
25
+ query: requireArcProxy.requireArcProxy(query, queries.ObservableQueryFor, 'observable query')
26
+ });
27
+ }
28
+ function BoundDataPage({ query, ...props }) {
29
+ const base = query.prototype instanceof queries.ObservableQueryFor ? queries.ObservableQueryFor : queries.QueryFor;
30
+ // This chooses only the kind of the supplied constructor, never another registered binding.
31
+ const proxy = requireArcProxy.requireArcProxy(query, base, 'query');
32
+ return /*#__PURE__*/ jsxRuntime.jsx(DataPage.DataPage, {
33
+ ...props,
34
+ query: proxy
35
+ });
36
+ }
37
+ function BoundCommandDialog({ command, ...props }) {
38
+ return /*#__PURE__*/ jsxRuntime.jsx(CommandDialog.CommandDialog, {
39
+ ...props,
40
+ command: requireArcProxy.requireArcProxy(command, commands.Command, 'command')
41
+ });
42
+ }
43
+ function BoundStepperCommandDialog({ command, ...props }) {
44
+ return /*#__PURE__*/ jsxRuntime.jsx(CommandDialog.StepperCommandDialog, {
45
+ ...props,
46
+ command: requireArcProxy.requireArcProxy(command, commands.Command, 'command')
47
+ });
48
+ }
49
+
50
+ exports.BoundCommandDialog = BoundCommandDialog;
51
+ exports.BoundDataPage = BoundDataPage;
52
+ exports.BoundDataTable = BoundDataTable;
53
+ exports.BoundObservableDataTable = BoundObservableDataTable;
54
+ exports.BoundStepperCommandDialog = BoundStepperCommandDialog;
55
+ //# sourceMappingURL=ArcBoundComponents.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ArcBoundComponents.js","sources":["../../../bindings/ArcBoundComponents.tsx"],"sourcesContent":["// Copyright (c) Cratis. All rights reserved.\n// Licensed under the MIT license. See LICENSE file in the project root for full license information.\n\n// Runtime-only module: import dynamically beneath ArcRuntimeBoundary, never through the barrel.\nimport type { ComponentProps } from 'react';\nimport { QueryFor, ObservableQueryFor } from '@cratis/arc/queries';\nimport { Command } from '@cratis/arc/commands';\nimport { DataTableForQuery, DataTableForObservableQuery } from '@cratis/components/DataTables';\nimport { DataPage } from '@cratis/components/DataPage';\nimport { CommandDialog, StepperCommandDialog } from '@cratis/components/CommandDialog';\nimport type { BoundConstructor } from './BoundConstructor';\nimport { requireArcProxy } from './requireArcProxy';\n\ntype QueryProps<T> = Omit<T, 'query'> & { query: BoundConstructor };\ntype CommandProps<T> = Omit<T, 'command'> & { command: BoundConstructor };\n\n// Scene carries property names and argument objects, not compile-time row schemas. These adapters\n// support generated object/collection queries, as required by the Components public prop contracts.\nexport function BoundDataTable({ query, ...props }: QueryProps<ComponentProps<typeof DataTableForQuery>>) {\n return <DataTableForQuery {...props} query={requireArcProxy(query, QueryFor<object, object>, 'query')} />;\n}\n\nexport function BoundObservableDataTable({ query, ...props }: QueryProps<ComponentProps<typeof DataTableForObservableQuery>>) {\n return <DataTableForObservableQuery {...props} query={requireArcProxy(query, ObservableQueryFor<object, object>, 'observable query')} />;\n}\n\nexport function BoundDataPage({ query, ...props }: QueryProps<ComponentProps<typeof DataPage>>) {\n const base = query.prototype instanceof ObservableQueryFor ? ObservableQueryFor<object, object> : QueryFor<object, object>;\n // This chooses only the kind of the supplied constructor, never another registered binding.\n const proxy = requireArcProxy<QueryFor<object, object> | ObservableQueryFor<object, object>>(query, base, 'query');\n return <DataPage {...props} query={proxy} />;\n}\n\nexport function BoundCommandDialog({ command, ...props }: CommandProps<ComponentProps<typeof CommandDialog>>) {\n return <CommandDialog {...props} command={requireArcProxy(command, Command<object, object>, 'command')} />;\n}\n\nexport function BoundStepperCommandDialog({ command, ...props }: CommandProps<ComponentProps<typeof StepperCommandDialog>>) {\n return <StepperCommandDialog {...props} command={requireArcProxy(command, Command<object, object>, 'command')} />;\n}\n"],"names":["BoundDataTable","query","props","_jsx","DataTableForQuery","requireArcProxy","QueryFor","BoundObservableDataTable","DataTableForObservableQuery","ObservableQueryFor","BoundDataPage","base","prototype","proxy","DataPage","BoundCommandDialog","command","CommandDialog","Command","BoundStepperCommandDialog","StepperCommandDialog"],"mappings":";;;;;;;;;;AAAA;AACA;AAEA;AAaA;AACA;AACO,SAASA,cAAAA,CAAe,EAAEC,KAAK,EAAE,GAAGC,KAAAA,EAA6D,EAAA;AACpG,IAAA,qBAAOC,cAAA,CAACC,4BAAAA,EAAAA;AAAmB,QAAA,GAAGF,KAAK;QAAED,KAAAA,EAAOI,+BAAAA,CAAgBJ,OAAOK,gBAAAA,EAA0B,OAAA;;AACjG;AAEO,SAASC,wBAAAA,CAAyB,EAAEN,KAAK,EAAE,GAAGC,KAAAA,EAAuE,EAAA;AACxH,IAAA,qBAAOC,cAAA,CAACK,sCAAAA,EAAAA;AAA6B,QAAA,GAAGN,KAAK;QAAED,KAAAA,EAAOI,+BAAAA,CAAgBJ,OAAOQ,0BAAAA,EAAoC,kBAAA;;AACrH;AAEO,SAASC,aAAAA,CAAc,EAAET,KAAK,EAAE,GAAGC,KAAAA,EAAoD,EAAA;AAC1F,IAAA,MAAMS,IAAAA,GAAOV,KAAAA,CAAMW,SAAS,YAAYH,6BAAqBA,0BAAAA,GAAqCH,gBAAAA;;IAElG,MAAMO,KAAAA,GAAQR,+BAAAA,CAA+EJ,KAAAA,EAAOU,IAAAA,EAAM,OAAA,CAAA;AAC1G,IAAA,qBAAOR,cAAA,CAACW,iBAAAA,EAAAA;AAAU,QAAA,GAAGZ,KAAK;QAAED,KAAAA,EAAOY;;AACvC;AAEO,SAASE,kBAAAA,CAAmB,EAAEC,OAAO,EAAE,GAAGd,KAAAA,EAA2D,EAAA;AACxG,IAAA,qBAAOC,cAAA,CAACc,2BAAAA,EAAAA;AAAe,QAAA,GAAGf,KAAK;QAAEc,OAAAA,EAASX,+BAAAA,CAAgBW,SAASE,gBAAAA,EAAyB,SAAA;;AAChG;AAEO,SAASC,yBAAAA,CAA0B,EAAEH,OAAO,EAAE,GAAGd,KAAAA,EAAkE,EAAA;AACtH,IAAA,qBAAOC,cAAA,CAACiB,kCAAAA,EAAAA;AAAsB,QAAA,GAAGlB,KAAK;QAAEc,OAAAA,EAASX,+BAAAA,CAAgBW,SAASE,gBAAAA,EAAyB,SAAA;;AACvG;;;;;;"}