@cratis/scene.react 4.12.0 → 4.14.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 (178) hide show
  1. package/dist/cjs/core/coreComponents.js +2 -1
  2. package/dist/cjs/core/coreComponents.js.map +1 -1
  3. package/dist/cjs/core/coreDescriptors.js +1 -0
  4. package/dist/cjs/core/coreDescriptors.js.map +1 -1
  5. package/dist/cjs/index.js +27 -0
  6. package/dist/cjs/index.js.map +1 -1
  7. package/dist/cjs/navigation/SceneNavigationContext.js.map +1 -1
  8. package/dist/cjs/navigation/SceneNavigationHost.js +19 -44
  9. package/dist/cjs/navigation/SceneNavigationHost.js.map +1 -1
  10. package/dist/cjs/navigation/createBrowserSceneHistory.js +39 -0
  11. package/dist/cjs/navigation/createBrowserSceneHistory.js.map +1 -0
  12. package/dist/cjs/navigation/createMemorySceneHistory.js +53 -0
  13. package/dist/cjs/navigation/createMemorySceneHistory.js.map +1 -0
  14. package/dist/cjs/navigation/diagnoseRenderedNavigation.js +17 -0
  15. package/dist/cjs/navigation/diagnoseRenderedNavigation.js.map +1 -0
  16. package/dist/cjs/navigation/identityRoute.js +19 -0
  17. package/dist/cjs/navigation/identityRoute.js.map +1 -0
  18. package/dist/cjs/navigation/matchSceneRoute.js +43 -0
  19. package/dist/cjs/navigation/matchSceneRoute.js.map +1 -0
  20. package/dist/cjs/navigation/navigationRouteKey.js +19 -0
  21. package/dist/cjs/navigation/navigationRouteKey.js.map +1 -0
  22. package/dist/cjs/navigation/resolveDestination.js +3 -10
  23. package/dist/cjs/navigation/resolveDestination.js.map +1 -1
  24. package/dist/cjs/navigation/sceneRoutesFrom.js +37 -0
  25. package/dist/cjs/navigation/sceneRoutesFrom.js.map +1 -0
  26. package/dist/cjs/navigation/useSceneHistoryNavigation.js +112 -0
  27. package/dist/cjs/navigation/useSceneHistoryNavigation.js.map +1 -0
  28. package/dist/cjs/packages/DesignTimeContractVersion.js +15 -0
  29. package/dist/cjs/packages/DesignTimeContractVersion.js.map +1 -0
  30. package/dist/cjs/packages/DesignTimeExtensionPoint.js +26 -0
  31. package/dist/cjs/packages/DesignTimeExtensionPoint.js.map +1 -0
  32. package/dist/cjs/packages/PackageHostConfiguration.js +1 -0
  33. package/dist/cjs/packages/PackageHostConfiguration.js.map +1 -1
  34. package/dist/cjs/packages/loadDesignTimeContributions.js +80 -0
  35. package/dist/cjs/packages/loadDesignTimeContributions.js.map +1 -0
  36. package/dist/cjs/packages/resolveDesignTimeHost.js +114 -0
  37. package/dist/cjs/packages/resolveDesignTimeHost.js.map +1 -0
  38. package/dist/cjs/packages/runDesignTimeAction.js +50 -0
  39. package/dist/cjs/packages/runDesignTimeAction.js.map +1 -0
  40. package/dist/esm/core/coreComponents.js +2 -1
  41. package/dist/esm/core/coreComponents.js.map +1 -1
  42. package/dist/esm/core/coreDescriptors.js +1 -0
  43. package/dist/esm/core/coreDescriptors.js.map +1 -1
  44. package/dist/esm/index.js +13 -0
  45. package/dist/esm/index.js.map +1 -1
  46. package/dist/esm/navigation/DestinationResolution.d.ts +1 -0
  47. package/dist/esm/navigation/DestinationResolution.d.ts.map +1 -1
  48. package/dist/esm/navigation/DialogResult.d.ts +5 -0
  49. package/dist/esm/navigation/DialogResult.d.ts.map +1 -0
  50. package/dist/esm/navigation/DialogResult.js +2 -0
  51. package/dist/esm/navigation/DialogResult.js.map +1 -0
  52. package/dist/esm/navigation/SceneHistory.d.ts +10 -0
  53. package/dist/esm/navigation/SceneHistory.d.ts.map +1 -0
  54. package/dist/esm/navigation/SceneHistory.js +2 -0
  55. package/dist/esm/navigation/SceneHistory.js.map +1 -0
  56. package/dist/esm/navigation/SceneNavigationContext.d.ts +6 -1
  57. package/dist/esm/navigation/SceneNavigationContext.d.ts.map +1 -1
  58. package/dist/esm/navigation/SceneNavigationContext.js.map +1 -1
  59. package/dist/esm/navigation/SceneNavigationHost.d.ts +7 -2
  60. package/dist/esm/navigation/SceneNavigationHost.d.ts.map +1 -1
  61. package/dist/esm/navigation/SceneNavigationHost.js +20 -45
  62. package/dist/esm/navigation/SceneNavigationHost.js.map +1 -1
  63. package/dist/esm/navigation/SceneNavigationState.d.ts +8 -0
  64. package/dist/esm/navigation/SceneNavigationState.d.ts.map +1 -0
  65. package/dist/esm/navigation/SceneNavigationState.js +2 -0
  66. package/dist/esm/navigation/SceneNavigationState.js.map +1 -0
  67. package/dist/esm/navigation/SceneRoute.d.ts +6 -0
  68. package/dist/esm/navigation/SceneRoute.d.ts.map +1 -0
  69. package/dist/esm/navigation/SceneRoute.js +2 -0
  70. package/dist/esm/navigation/SceneRoute.js.map +1 -0
  71. package/dist/esm/navigation/createBrowserSceneHistory.d.ts +4 -0
  72. package/dist/esm/navigation/createBrowserSceneHistory.d.ts.map +1 -0
  73. package/dist/esm/navigation/createBrowserSceneHistory.js +37 -0
  74. package/dist/esm/navigation/createBrowserSceneHistory.js.map +1 -0
  75. package/dist/esm/navigation/createMemorySceneHistory.d.ts +7 -0
  76. package/dist/esm/navigation/createMemorySceneHistory.d.ts.map +1 -0
  77. package/dist/esm/navigation/createMemorySceneHistory.js +51 -0
  78. package/dist/esm/navigation/createMemorySceneHistory.js.map +1 -0
  79. package/dist/esm/navigation/diagnoseRenderedNavigation.d.ts +3 -0
  80. package/dist/esm/navigation/diagnoseRenderedNavigation.d.ts.map +1 -0
  81. package/dist/esm/navigation/diagnoseRenderedNavigation.js +15 -0
  82. package/dist/esm/navigation/diagnoseRenderedNavigation.js.map +1 -0
  83. package/dist/esm/navigation/identityRoute.d.ts +3 -0
  84. package/dist/esm/navigation/identityRoute.d.ts.map +1 -0
  85. package/dist/esm/navigation/identityRoute.js +17 -0
  86. package/dist/esm/navigation/identityRoute.js.map +1 -0
  87. package/dist/esm/navigation/index.d.ts +12 -0
  88. package/dist/esm/navigation/index.d.ts.map +1 -1
  89. package/dist/esm/navigation/index.js +12 -0
  90. package/dist/esm/navigation/index.js.map +1 -1
  91. package/dist/esm/navigation/matchSceneRoute.d.ts +7 -0
  92. package/dist/esm/navigation/matchSceneRoute.d.ts.map +1 -0
  93. package/dist/esm/navigation/matchSceneRoute.js +41 -0
  94. package/dist/esm/navigation/matchSceneRoute.js.map +1 -0
  95. package/dist/esm/navigation/navigationRouteKey.d.ts +3 -0
  96. package/dist/esm/navigation/navigationRouteKey.d.ts.map +1 -0
  97. package/dist/esm/navigation/navigationRouteKey.js +17 -0
  98. package/dist/esm/navigation/navigationRouteKey.js.map +1 -0
  99. package/dist/esm/navigation/resolveDestination.d.ts.map +1 -1
  100. package/dist/esm/navigation/resolveDestination.js +2 -9
  101. package/dist/esm/navigation/resolveDestination.js.map +1 -1
  102. package/dist/esm/navigation/sceneRoutesFrom.d.ts +4 -0
  103. package/dist/esm/navigation/sceneRoutesFrom.d.ts.map +1 -0
  104. package/dist/esm/navigation/sceneRoutesFrom.js +35 -0
  105. package/dist/esm/navigation/sceneRoutesFrom.js.map +1 -0
  106. package/dist/esm/navigation/useSceneHistoryNavigation.d.ts +6 -0
  107. package/dist/esm/navigation/useSceneHistoryNavigation.d.ts.map +1 -0
  108. package/dist/esm/navigation/useSceneHistoryNavigation.js +110 -0
  109. package/dist/esm/navigation/useSceneHistoryNavigation.js.map +1 -0
  110. package/dist/esm/packages/DesignTimeActionOutcome.d.ts +7 -0
  111. package/dist/esm/packages/DesignTimeActionOutcome.d.ts.map +1 -0
  112. package/dist/esm/packages/DesignTimeActionOutcome.js +2 -0
  113. package/dist/esm/packages/DesignTimeActionOutcome.js.map +1 -0
  114. package/dist/esm/packages/DesignTimeActionState.d.ts +9 -0
  115. package/dist/esm/packages/DesignTimeActionState.d.ts.map +1 -0
  116. package/dist/esm/packages/DesignTimeActionState.js +2 -0
  117. package/dist/esm/packages/DesignTimeActionState.js.map +1 -0
  118. package/dist/esm/packages/DesignTimeContractVersion.d.ts +2 -0
  119. package/dist/esm/packages/DesignTimeContractVersion.d.ts.map +1 -0
  120. package/dist/esm/packages/DesignTimeContractVersion.js +13 -0
  121. package/dist/esm/packages/DesignTimeContractVersion.js.map +1 -0
  122. package/dist/esm/packages/DesignTimeExtensionPoint.d.ts +9 -0
  123. package/dist/esm/packages/DesignTimeExtensionPoint.d.ts.map +1 -0
  124. package/dist/esm/packages/DesignTimeExtensionPoint.js +23 -0
  125. package/dist/esm/packages/DesignTimeExtensionPoint.js.map +1 -0
  126. package/dist/esm/packages/DesignTimeResolution.d.ts +7 -0
  127. package/dist/esm/packages/DesignTimeResolution.d.ts.map +1 -0
  128. package/dist/esm/packages/DesignTimeResolution.js +2 -0
  129. package/dist/esm/packages/DesignTimeResolution.js.map +1 -0
  130. package/dist/esm/packages/PackageHostConfiguration.d.ts +3 -0
  131. package/dist/esm/packages/PackageHostConfiguration.d.ts.map +1 -1
  132. package/dist/esm/packages/PackageHostConfiguration.js +2 -1
  133. package/dist/esm/packages/PackageHostConfiguration.js.map +1 -1
  134. package/dist/esm/packages/index.d.ts +8 -0
  135. package/dist/esm/packages/index.d.ts.map +1 -1
  136. package/dist/esm/packages/index.js +8 -0
  137. package/dist/esm/packages/index.js.map +1 -1
  138. package/dist/esm/packages/loadDesignTimeContributions.d.ts +11 -0
  139. package/dist/esm/packages/loadDesignTimeContributions.d.ts.map +1 -0
  140. package/dist/esm/packages/loadDesignTimeContributions.js +78 -0
  141. package/dist/esm/packages/loadDesignTimeContributions.js.map +1 -0
  142. package/dist/esm/packages/resolveDesignTimeHost.d.ts +26 -0
  143. package/dist/esm/packages/resolveDesignTimeHost.d.ts.map +1 -0
  144. package/dist/esm/packages/resolveDesignTimeHost.js +112 -0
  145. package/dist/esm/packages/resolveDesignTimeHost.js.map +1 -0
  146. package/dist/esm/packages/runDesignTimeAction.d.ts +4 -0
  147. package/dist/esm/packages/runDesignTimeAction.d.ts.map +1 -0
  148. package/dist/esm/packages/runDesignTimeAction.js +48 -0
  149. package/dist/esm/packages/runDesignTimeAction.js.map +1 -0
  150. package/dist/esm/tsconfig.tsbuildinfo +1 -1
  151. package/navigation/DestinationResolution.ts +3 -0
  152. package/navigation/DialogResult.ts +8 -0
  153. package/navigation/SceneHistory.ts +24 -0
  154. package/navigation/SceneNavigationContext.tsx +16 -1
  155. package/navigation/SceneNavigationHost.tsx +30 -40
  156. package/navigation/SceneNavigationState.ts +19 -0
  157. package/navigation/SceneRoute.ts +12 -0
  158. package/navigation/createBrowserSceneHistory.ts +40 -0
  159. package/navigation/createMemorySceneHistory.ts +44 -0
  160. package/navigation/diagnoseRenderedNavigation.ts +14 -0
  161. package/navigation/identityRoute.ts +14 -0
  162. package/navigation/index.ts +12 -0
  163. package/navigation/matchSceneRoute.ts +45 -0
  164. package/navigation/navigationRouteKey.ts +22 -0
  165. package/navigation/resolveDestination.ts +2 -6
  166. package/navigation/sceneRoutesFrom.ts +24 -0
  167. package/navigation/useSceneHistoryNavigation.ts +80 -0
  168. package/package.json +5 -5
  169. package/packages/DesignTimeActionOutcome.ts +18 -0
  170. package/packages/DesignTimeActionState.ts +21 -0
  171. package/packages/DesignTimeContractVersion.ts +12 -0
  172. package/packages/DesignTimeExtensionPoint.ts +23 -0
  173. package/packages/DesignTimeResolution.ts +23 -0
  174. package/packages/PackageHostConfiguration.ts +11 -1
  175. package/packages/index.ts +8 -0
  176. package/packages/loadDesignTimeContributions.ts +98 -0
  177. package/packages/resolveDesignTimeHost.ts +133 -0
  178. package/packages/runDesignTimeAction.ts +36 -0
@@ -0,0 +1,23 @@
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
+ /**
5
+ * The versioned design-time extension points a package can fill. The values are the keys shared by
6
+ * `PackageDesignTimeMetadata` (what a manifest declares) and `DesignTimeBundle` (what a bundle provides).
7
+ */
8
+ export enum DesignTimeExtensionPoint {
9
+ Preview = 'previews',
10
+ Designer = 'designers',
11
+ PropertyEditor = 'propertyEditors',
12
+ PropertyDisplay = 'propertyDisplays',
13
+ Action = 'actions',
14
+ }
15
+
16
+ /** How a diagnostic names an extension point. */
17
+ export const designTimeExtensionPointLabels: Record<DesignTimeExtensionPoint, string> = {
18
+ [DesignTimeExtensionPoint.Preview]: 'preview',
19
+ [DesignTimeExtensionPoint.Designer]: 'designer',
20
+ [DesignTimeExtensionPoint.PropertyEditor]: 'property editor',
21
+ [DesignTimeExtensionPoint.PropertyDisplay]: 'property display',
22
+ [DesignTimeExtensionPoint.Action]: 'action',
23
+ };
@@ -0,0 +1,23 @@
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
+ /**
5
+ * What a design-time host found for one extension point request.
6
+ *
7
+ * When `contribution` is absent the host renders its generic fallback - the default preview, the
8
+ * descriptor-driven inspector, the default editor for the property's value type - and `diagnostic` says
9
+ * why, unless nothing was requested in the first place.
10
+ */
11
+ export interface DesignTimeResolution<T> {
12
+ /** The package-provided contribution, when one was found and loaded. */
13
+ contribution?: T;
14
+
15
+ /** The package that provided it. */
16
+ package?: string;
17
+
18
+ /** The host-provided editor kind used instead, for property editors the host implements itself. */
19
+ hostKind?: string;
20
+
21
+ /** Why the generic fallback is used. Absent when the request was satisfied or nothing was requested. */
22
+ diagnostic?: string;
23
+ }
@@ -2,7 +2,7 @@
2
2
  // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
3
 
4
4
  import { DialogTemplate, Layout, ScreenTemplate, Theme, UiProfile } from '@cratis/scene.model';
5
- import { resolvePackageDependencies } from '@cratis/scene.engine';
5
+ import { TemplateCatalogEntry, describeTemplateCatalog, resolvePackageDependencies } from '@cratis/scene.engine';
6
6
  import { IconAdapterRegistry, createIconAdapterRegistry } from '../icons/IconAdapterRegistry';
7
7
  import { ScenePackageBundle, mergePackageRegistries, validatePackageBundle } from './ScenePackageBundle';
8
8
 
@@ -18,6 +18,9 @@ export interface PackageHostConfiguration {
18
18
  bundles: ScenePackageBundle[];
19
19
  profile: UiProfile;
20
20
  policy: PackageHostPolicy;
21
+
22
+ /** The Scene version the host runs, checked against each template's compatibility range when given. */
23
+ sceneVersion?: string;
21
24
  }
22
25
 
23
26
  export interface ResolvedPackageHost {
@@ -27,6 +30,12 @@ export interface ResolvedPackageHost {
27
30
  screenTemplates: ScreenTemplate[];
28
31
  dialogTemplates: DialogTemplate[];
29
32
  themes: Theme[];
33
+
34
+ /**
35
+ * Every template the approved packages provide, with compatibility, attribution and license metadata and
36
+ * whether it can be used with this package set. Template browsers read this rather than the raw lists.
37
+ */
38
+ templates: TemplateCatalogEntry[];
30
39
  icons: IconAdapterRegistry;
31
40
  assets: string[];
32
41
  fonts: string[];
@@ -100,6 +109,7 @@ export function resolvePackageHost(configuration: PackageHostConfiguration): Res
100
109
  screenTemplates: approved.flatMap(bundle => bundle.screenTemplates ?? []),
101
110
  dialogTemplates: approved.flatMap(bundle => bundle.dialogTemplates ?? []),
102
111
  themes: approved.flatMap(bundle => bundle.themes ?? []),
112
+ templates: describeTemplateCatalog(approved, configuration.sceneVersion),
103
113
  icons: createIconAdapterRegistry(approved.map(bundle => bundle.iconLibrary?.adapter).filter(adapter => adapter !== undefined)),
104
114
  assets: approvedAssets,
105
115
  fonts: approvedAssets.filter(isFontAsset),
package/packages/index.ts CHANGED
@@ -2,18 +2,26 @@
2
2
  // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
3
 
4
4
  export * from './DesignTimeAction';
5
+ export * from './DesignTimeActionOutcome';
6
+ export * from './DesignTimeActionState';
5
7
  export * from './DesignTimeBundle';
6
8
  export * from './DesignTimeComponentProps';
7
9
  export * from './DesignTimeContext';
10
+ export * from './DesignTimeContractVersion';
11
+ export * from './DesignTimeExtensionPoint';
8
12
  export * from './DesignTimePropertyDisplayProps';
9
13
  export * from './DesignTimePropertyEditorProps';
10
14
  export * from './DesignTimeProvider';
15
+ export * from './DesignTimeResolution';
11
16
  export * from '../navigation';
12
17
  export * from './EmbeddedPackageHost';
18
+ export * from './loadDesignTimeContributions';
13
19
  export * from './PackageHostAssets';
14
20
  export * from './PackageHostConfiguration';
15
21
  export * from './PackageHostRenderMode';
16
22
  export * from './PackageHostView';
23
+ export * from './resolveDesignTimeHost';
24
+ export * from './runDesignTimeAction';
17
25
  export * from './ScenePackageBundle';
18
26
  export * from './StandalonePackageHost';
19
27
  export * from './WebViewPackageHost';
@@ -0,0 +1,98 @@
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 { DesignTimeBundle } from './DesignTimeBundle';
5
+ import { DesignTimeContractVersion } from './DesignTimeContractVersion';
6
+ import { DesignTimeExtensionPoint, designTimeExtensionPointLabels } from './DesignTimeExtensionPoint';
7
+ import { ScenePackageBundle } from './ScenePackageBundle';
8
+
9
+ /**
10
+ * A package's design-time contributions as a host loaded them: only what the manifest declares, or nothing
11
+ * with the reason when the package cannot be loaded at all.
12
+ */
13
+ export interface LoadedDesignTimeContributions {
14
+ contributions: Partial<Record<DesignTimeExtensionPoint, Record<string, unknown>>>;
15
+ unavailableReason?: string;
16
+ }
17
+
18
+ /**
19
+ * Loads the design-time contributions of approved bundles, checking each against the contract version this
20
+ * host implements and against what its manifest declares. Nothing is partially trusted: a package with an
21
+ * incompatible or unreadable contract version, or contributions its manifest does not declare at all,
22
+ * contributes nothing; a single undeclared contribution is left out; a declared one the bundle lacks is
23
+ * reported and falls back.
24
+ */
25
+ export function loadDesignTimeContributions(
26
+ bundles: ScenePackageBundle[],
27
+ loadDesignTime: boolean,
28
+ ): { packages: Map<string, LoadedDesignTimeContributions>; diagnostics: string[] } {
29
+ const packages = new Map<string, LoadedDesignTimeContributions>();
30
+ const diagnostics: string[] = [];
31
+ const hostMajor = DesignTimeContractVersion.split('.')[0];
32
+
33
+ for (const { manifest, designTime } of bundles) {
34
+ const declared = manifest.designTime;
35
+ const unavailable = (reason: string) => {
36
+ diagnostics.push(reason);
37
+ packages.set(manifest.name, { contributions: {}, unavailableReason: reason });
38
+ };
39
+
40
+ if (!declared && !designTime) continue;
41
+ if (!loadDesignTime) {
42
+ packages.set(manifest.name, { contributions: {}, unavailableReason: `This host does not load design-time contributions, so package '${manifest.name}' uses generic editors` });
43
+ continue;
44
+ }
45
+
46
+ if (!declared) {
47
+ unavailable(`Package '${manifest.name}' provides design-time contributions its manifest does not declare; none are loaded`);
48
+ continue;
49
+ }
50
+
51
+ const version = declared.contractVersion ?? '1.0';
52
+ if (!/^\d+\.\d+$/.test(version)) {
53
+ unavailable(`Package '${manifest.name}' declares design-time contract version '${version}', which is not a major.minor version; none of its contributions are loaded`);
54
+ continue;
55
+ }
56
+
57
+ if (version.split('.')[0] !== hostMajor) {
58
+ unavailable(`Package '${manifest.name}' targets design-time contract ${version}, but this host implements ${DesignTimeContractVersion}; none of its contributions are loaded`);
59
+ continue;
60
+ }
61
+
62
+ packages.set(manifest.name, { contributions: declaredContributions(manifest.name, declared, designTime, diagnostics) });
63
+ }
64
+
65
+ return { packages, diagnostics };
66
+ }
67
+
68
+ function declaredContributions(
69
+ packageName: string,
70
+ declared: Record<DesignTimeExtensionPoint, string[]>,
71
+ provided: DesignTimeBundle | undefined,
72
+ diagnostics: string[],
73
+ ): LoadedDesignTimeContributions['contributions'] {
74
+ const contributions: LoadedDesignTimeContributions['contributions'] = {};
75
+
76
+ for (const point of Object.values(DesignTimeExtensionPoint)) {
77
+ const label = designTimeExtensionPointLabels[point];
78
+ const names = new Set(declared[point] ?? []);
79
+ const available = (provided?.[point] ?? {}) as Record<string, unknown>;
80
+ const loaded: Record<string, unknown> = {};
81
+
82
+ for (const name of names) {
83
+ if (name in available) {
84
+ loaded[name] = available[name];
85
+ } else {
86
+ diagnostics.push(`Package '${packageName}' declares the ${label} '${name}', but its design-time bundle does not provide it`);
87
+ }
88
+ }
89
+
90
+ for (const name of Object.keys(available).filter(name => !names.has(name))) {
91
+ diagnostics.push(`Package '${packageName}' provides the ${label} '${name}', which its manifest does not declare; it is not loaded`);
92
+ }
93
+
94
+ contributions[point] = loaded;
95
+ }
96
+
97
+ return contributions;
98
+ }
@@ -0,0 +1,133 @@
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 { ComponentType } from 'react';
5
+ import { ComponentDescriptor, PropertyDescriptor } from '@cratis/scene.model';
6
+ import { DesignTimeAction, DesignTimeActionContext } from './DesignTimeAction';
7
+ import { DesignTimeActionOutcome } from './DesignTimeActionOutcome';
8
+ import { DesignTimeActionState } from './DesignTimeActionState';
9
+ import { DesignTimeComponentProps } from './DesignTimeComponentProps';
10
+ import { DesignTimeExtensionPoint, designTimeExtensionPointLabels } from './DesignTimeExtensionPoint';
11
+ import { DesignTimePropertyDisplayProps } from './DesignTimePropertyDisplayProps';
12
+ import { DesignTimePropertyEditorProps } from './DesignTimePropertyEditorProps';
13
+ import { DesignTimeResolution } from './DesignTimeResolution';
14
+ import { LoadedDesignTimeContributions, loadDesignTimeContributions } from './loadDesignTimeContributions';
15
+ import { PackageHostConfiguration, ResolvedPackageHost, resolvePackageHost } from './PackageHostConfiguration';
16
+ import { runDesignTimeAction } from './runDesignTimeAction';
17
+
18
+ /**
19
+ * The generic design-time host contract: every package - first or third party - is reached through these
20
+ * lookups, so a designer never switches on package or component names.
21
+ */
22
+ export interface DesignTimeHost {
23
+ /** The runtime package host the design-time contributions were loaded alongside. */
24
+ host: ResolvedPackageHost;
25
+
26
+ /** Package-level problems: the host's own, contract version mismatches and declared/provided disagreements. */
27
+ diagnostics: string[];
28
+
29
+ preview(descriptor: ComponentDescriptor): DesignTimeResolution<ComponentType<DesignTimeComponentProps>>;
30
+ designer(descriptor: ComponentDescriptor): DesignTimeResolution<ComponentType<DesignTimeComponentProps>>;
31
+ propertyEditor(descriptor: ComponentDescriptor, property: PropertyDescriptor): DesignTimeResolution<ComponentType<DesignTimePropertyEditorProps>>;
32
+ propertyDisplay(descriptor: ComponentDescriptor): DesignTimeResolution<ComponentType<DesignTimePropertyDisplayProps>>;
33
+
34
+ /** The component's actions, with the visibility and enablement their owning package decides. */
35
+ actions(descriptor: ComponentDescriptor, context: DesignTimeActionContext): DesignTimeActionState[];
36
+
37
+ /** Runs an action and submits its canonical edit batch to the host through `context.submitAction`. */
38
+ runAction(descriptor: ComponentDescriptor, actionId: string, context: DesignTimeActionContext): DesignTimeActionOutcome;
39
+
40
+ /** Every unknown or unavailable extension the approved packages' descriptors ask for. */
41
+ diagnoseDescriptors(): string[];
42
+ }
43
+
44
+ /** Options for {@link resolveDesignTimeHost}. */
45
+ export interface DesignTimeHostOptions {
46
+ /** Property editor kinds the host implements itself, such as an icon picker or a query binder. */
47
+ hostEditorKinds?: string[];
48
+ }
49
+
50
+ /**
51
+ * Resolves a package host and the design-time contributions of its approved packages. Design-time code is
52
+ * only loaded when the host policy sets `loadDesignTime`; a runtime host gets generic fallbacks everywhere.
53
+ */
54
+ export function resolveDesignTimeHost(configuration: PackageHostConfiguration, options: DesignTimeHostOptions = {}): DesignTimeHost {
55
+ const host = resolvePackageHost(configuration);
56
+ const loaded = loadDesignTimeContributions(host.bundles, configuration.policy.loadDesignTime === true);
57
+ const hostEditorKinds = new Set(options.hostEditorKinds ?? []);
58
+ const approved = new Set(host.bundles.map(bundle => bundle.manifest.name));
59
+
60
+ const lookup = <T>(descriptor: ComponentDescriptor, point: DesignTimeExtensionPoint, kind: string | undefined): DesignTimeResolution<T> => {
61
+ if (!kind) return {};
62
+ const label = designTimeExtensionPointLabels[point];
63
+ const separator = kind.indexOf(':');
64
+ const owner = separator > 0 ? kind.substring(0, separator) : ownerOf(descriptor);
65
+ const name = separator > 0 ? kind.substring(separator + 1) : kind;
66
+ const outcome = point === DesignTimeExtensionPoint.Action ? 'the host shows it disabled' : `the host uses its generic ${label}`;
67
+ const fallback = (reason: string) => ({ diagnostic: `'${descriptor.component}' asks for the ${label} '${kind}', ${reason}; ${outcome}` });
68
+
69
+ if (!approved.has(owner)) return fallback(`but package '${owner}' is not approved by this host`);
70
+ const contributions: LoadedDesignTimeContributions | undefined = loaded.packages.get(owner);
71
+ if (contributions?.unavailableReason) return fallback(`but design-time contributions from package '${owner}' are not loaded`);
72
+ const contribution = contributions?.contributions[point]?.[name] as T | undefined;
73
+ if (contribution !== undefined) return { contribution, package: owner };
74
+ if (point === DesignTimeExtensionPoint.PropertyEditor && separator < 0 && hostEditorKinds.has(kind)) return { hostKind: kind };
75
+ return fallback(`which package '${owner}' does not provide`);
76
+ };
77
+
78
+ const actionOf = (descriptor: ComponentDescriptor, actionId: string): DesignTimeAction | undefined =>
79
+ loaded.packages.get(ownerOf(descriptor))?.contributions[DesignTimeExtensionPoint.Action]?.[actionId] as DesignTimeAction | undefined;
80
+
81
+ const actions = (descriptor: ComponentDescriptor, context: DesignTimeActionContext): DesignTimeActionState[] =>
82
+ (descriptor.actions ?? []).map(action => {
83
+ const state = { descriptor: action, package: ownerOf(descriptor) };
84
+ const handler = actionOf(descriptor, action.id);
85
+ if (!handler) {
86
+ const resolution = lookup<DesignTimeAction>(descriptor, DesignTimeExtensionPoint.Action, action.id);
87
+ return { ...state, visible: true, enabled: false, diagnostic: resolution.diagnostic };
88
+ }
89
+
90
+ try {
91
+ return { ...state, visible: handler.isVisible(context), enabled: handler.isEnabled(context) };
92
+ } catch (error) {
93
+ return { ...state, visible: true, enabled: false, diagnostic: `Action '${action.id}' could not decide its availability: ${String(error)}` };
94
+ }
95
+ });
96
+
97
+ const descriptors = host.bundles.flatMap(bundle => bundle.descriptors ?? []);
98
+
99
+ return {
100
+ host,
101
+ diagnostics: [...host.diagnostics, ...loaded.diagnostics],
102
+ preview: descriptor => lookup(descriptor, DesignTimeExtensionPoint.Preview, descriptor.previewKind),
103
+ designer: descriptor => lookup(descriptor, DesignTimeExtensionPoint.Designer, descriptor.editorKind),
104
+ propertyEditor: (descriptor, property) => lookup(descriptor, DesignTimeExtensionPoint.PropertyEditor, property.editorKind),
105
+ propertyDisplay: descriptor => lookup(descriptor, DesignTimeExtensionPoint.PropertyDisplay, descriptor.propertyDisplayKind),
106
+ actions,
107
+ runAction: (descriptor, actionId, context) => {
108
+ const state = actions(descriptor, context).find(action => action.descriptor.id === actionId);
109
+ const handler = actionOf(descriptor, actionId);
110
+ if (!state || !handler || !state.visible || !state.enabled) {
111
+ const reason = state?.diagnostic ?? (state ? `Action '${actionId}' is not available here` : `'${descriptor.component}' has no action '${actionId}'`);
112
+ return { submitted: false, edits: [], diagnostics: [reason] };
113
+ }
114
+
115
+ return runDesignTimeAction(handler, context);
116
+ },
117
+ diagnoseDescriptors: () =>
118
+ descriptors.flatMap(descriptor => [
119
+ lookup(descriptor, DesignTimeExtensionPoint.Preview, descriptor.previewKind),
120
+ lookup(descriptor, DesignTimeExtensionPoint.Designer, descriptor.editorKind),
121
+ lookup(descriptor, DesignTimeExtensionPoint.PropertyDisplay, descriptor.propertyDisplayKind),
122
+ ...descriptor.properties.map(property => lookup(descriptor, DesignTimeExtensionPoint.PropertyEditor, property.editorKind)),
123
+ ...(descriptor.actions ?? []).map(action => lookup(descriptor, DesignTimeExtensionPoint.Action, action.id)),
124
+ ])
125
+ .map(resolution => resolution.diagnostic)
126
+ .filter((diagnostic): diagnostic is string => diagnostic !== undefined),
127
+ };
128
+ }
129
+
130
+ function ownerOf(descriptor: ComponentDescriptor): string {
131
+ const separator = descriptor.component.indexOf(':');
132
+ return separator > 0 ? descriptor.component.substring(0, separator) : descriptor.component;
133
+ }
@@ -0,0 +1,36 @@
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 { SceneEdit, SceneEditKind } from '@cratis/scene.model';
5
+ import { DesignTimeAction, DesignTimeActionContext } from './DesignTimeAction';
6
+ import { DesignTimeActionOutcome } from './DesignTimeActionOutcome';
7
+
8
+ const editKinds = new Set<string>(Object.values(SceneEditKind));
9
+
10
+ /**
11
+ * Runs one design-time action and hands its edit batch to the host as a single canonical batch, so the host's
12
+ * own validation and undo/redo stay authoritative. A batch containing anything that is not a canonical Scene
13
+ * edit is rejected whole - a partially applied action is never reported as success.
14
+ */
15
+ export function runDesignTimeAction(action: DesignTimeAction, context: DesignTimeActionContext): DesignTimeActionOutcome {
16
+ let result: { edits: SceneEdit[]; diagnostics: string[] };
17
+ try {
18
+ result = action.execute(context);
19
+ } catch (error) {
20
+ return { submitted: false, edits: [], diagnostics: [`Action '${action.descriptor.id}' failed: ${String(error)}`] };
21
+ }
22
+
23
+ const invalid = result.edits.filter(edit => !editKinds.has((edit as { kind?: string }).kind ?? ''));
24
+ if (invalid.length > 0) {
25
+ return {
26
+ submitted: false,
27
+ edits: result.edits,
28
+ diagnostics: [...result.diagnostics, `Action '${action.descriptor.id}' produced ${invalid.length} edit(s) that are not canonical Scene edits; nothing was submitted`],
29
+ };
30
+ }
31
+
32
+ if (result.edits.length === 0) return { submitted: false, edits: [], diagnostics: result.diagnostics };
33
+
34
+ context.submitAction(action.descriptor.id, result.edits);
35
+ return { submitted: true, edits: result.edits, diagnostics: result.diagnostics };
36
+ }