@hitachivantara/app-shell-services 2.0.0-next.1 → 2.0.0-next.2

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.
@@ -1,29 +1,4 @@
1
- import { useState, useEffect, createElement, createContext, useMemo, useContext, useCallback, useRef } from "react";
2
- function isEqual(a, b) {
3
- if (a === b) {
4
- return true;
5
- }
6
- if (a == null || b == null || typeof a !== typeof b || typeof a !== "object" || Array.isArray(a) !== Array.isArray(b)) {
7
- return false;
8
- }
9
- if (Array.isArray(a) && Array.isArray(b)) {
10
- if (a.length !== b.length) {
11
- return false;
12
- }
13
- return a.every((item, index) => isEqual(item, b[index]));
14
- }
15
- const keysA = Object.keys(a);
16
- const keysB = Object.keys(b);
17
- if (keysA.length !== keysB.length) {
18
- return false;
19
- }
20
- return keysA.every(
21
- (key) => isEqual(
22
- a[key],
23
- b[key]
24
- )
25
- );
26
- }
1
+ import { useState, useEffect, createElement, useMemo, createContext, useContext, useCallback, useRef } from "react";
27
2
  function useAsync(promiseFactory, options) {
28
3
  const dataProp = options?.dataProp ?? "data";
29
4
  const pendingData = options?.pendingData;
@@ -71,6 +46,31 @@ function useAsync(promiseFactory, options) {
71
46
  [dataProp]: data
72
47
  };
73
48
  }
49
+ function isEqual(a, b) {
50
+ if (a === b) {
51
+ return true;
52
+ }
53
+ if (a == null || b == null || typeof a !== typeof b || typeof a !== "object" || Array.isArray(a) !== Array.isArray(b)) {
54
+ return false;
55
+ }
56
+ if (Array.isArray(a) && Array.isArray(b)) {
57
+ if (a.length !== b.length) {
58
+ return false;
59
+ }
60
+ return a.every((item, index) => isEqual(item, b[index]));
61
+ }
62
+ const keysA = Object.keys(a);
63
+ const keysB = Object.keys(b);
64
+ if (keysA.length !== keysB.length) {
65
+ return false;
66
+ }
67
+ return keysA.every(
68
+ (key) => isEqual(
69
+ a[key],
70
+ b[key]
71
+ )
72
+ );
73
+ }
74
74
  function createServiceReferenceBase(serviceId, serviceConfig, getService) {
75
75
  return {
76
76
  serviceId,
@@ -209,17 +209,17 @@ function createComponentReference(serviceId, config) {
209
209
  };
210
210
  return createServiceReferenceBase(serviceId, config, serviceLoader);
211
211
  }
212
- function createServiceReference(serviceId, serviceConfig) {
213
- if ("instance" in serviceConfig) {
214
- return createInstanceReference(serviceId, serviceConfig);
212
+ function createServiceReference(serviceId, serviceProviderConfig) {
213
+ if ("instance" in serviceProviderConfig) {
214
+ return createInstanceReference(serviceId, serviceProviderConfig);
215
215
  }
216
- if ("factory" in serviceConfig) {
217
- return createFactoryReference(serviceId, serviceConfig);
216
+ if ("factory" in serviceProviderConfig) {
217
+ return createFactoryReference(serviceId, serviceProviderConfig);
218
218
  }
219
- if ("component" in serviceConfig) {
219
+ if ("component" in serviceProviderConfig) {
220
220
  return createComponentReference(
221
221
  serviceId,
222
- serviceConfig
222
+ serviceProviderConfig
223
223
  );
224
224
  }
225
225
  throw new Error(
@@ -235,9 +235,9 @@ function bindComponent(Component, configProps) {
235
235
  }
236
236
  function createServiceReferenceMap(services = {}) {
237
237
  return Object.entries(services).reduce(
238
- (map, [serviceId, serviceConfigs]) => {
239
- const serviceReferences = serviceConfigs.map(
240
- (serviceConfig) => createServiceReference(serviceId, serviceConfig)
238
+ (map, [serviceId, serviceProviderConfigs]) => {
239
+ const serviceReferences = serviceProviderConfigs.map(
240
+ (serviceProviderConfig) => createServiceReference(serviceId, serviceProviderConfig)
241
241
  ).sort((a, b) => b.ranking - a.ranking);
242
242
  map.set(serviceId, serviceReferences);
243
243
  return map;
@@ -245,9 +245,7 @@ function createServiceReferenceMap(services = {}) {
245
245
  /* @__PURE__ */ new Map()
246
246
  );
247
247
  }
248
- function createServiceManager({
249
- services
250
- }) {
248
+ function createServiceManager(services = {}) {
251
249
  const serviceReferenceMap = createServiceReferenceMap(services);
252
250
  function queryServiceReferences(serviceId, _options) {
253
251
  return serviceReferenceMap.get(serviceId) ?? [];
@@ -281,7 +279,7 @@ function createServiceManager({
281
279
  );
282
280
  const successful = [];
283
281
  const errors = [];
284
- results.forEach((result) => {
282
+ for (const result of results) {
285
283
  if (result.status === "fulfilled") {
286
284
  successful.push(result.value);
287
285
  } else {
@@ -289,7 +287,7 @@ function createServiceManager({
289
287
  result.reason instanceof Error ? result.reason : new Error(String(result.reason))
290
288
  );
291
289
  }
292
- });
290
+ }
293
291
  switch (errorHandling) {
294
292
  case "reject-on-any-failure":
295
293
  if (errors.length > 0) {
@@ -312,7 +310,9 @@ const ServicesContext = createContext(
312
310
  void 0
313
311
  );
314
312
  const ServiceManagerProvider = ({ config, children }) => {
315
- const serviceManager = useMemo(() => createServiceManager(config), [config]);
313
+ const serviceManager = useMemo(() => {
314
+ return createServiceManager(config.services);
315
+ }, [config.services]);
316
316
  return createElement(
317
317
  ServicesContext.Provider,
318
318
  { value: serviceManager },
@@ -366,6 +366,7 @@ function isServiceReference(value) {
366
366
  }
367
367
  export {
368
368
  ServiceManagerProvider as default,
369
+ useAsync,
369
370
  useService,
370
371
  useServiceReference,
371
372
  useServiceReferences,
package/dist/index.d.ts CHANGED
@@ -2,16 +2,44 @@ import { FC } from 'react';
2
2
  import { PropsWithChildren } from 'react';
3
3
  import { ServiceManager as ServiceManager_2 } from '..';
4
4
 
5
+ export declare type AsyncBaseResult<TData, TError extends ErrorBase = Error, TDataProp extends string = "data", TDataPending extends TData | undefined = undefined> = {
6
+ isPending: boolean;
7
+ error: TError | null;
8
+ } & PropertyWithName<TDataProp, TData | TDataPending | undefined>;
9
+
10
+ export declare type AsyncErrorResult<TData, TError extends ErrorBase = Error, TDataProp extends string = "data", TDataPending extends TData | undefined = undefined> = AsyncBaseResult<TData, TError, TDataProp, TDataPending> & {
11
+ isPending: false;
12
+ error: TError;
13
+ } & PropertyWithName<TDataProp, undefined>;
14
+
15
+ declare type AsyncOptions<TData, TDataProp extends string, TPending extends TData | undefined> = {
16
+ dataProp?: TDataProp;
17
+ pendingData?: TPending;
18
+ };
19
+
20
+ export declare type AsyncPendingResult<TData, TError extends ErrorBase = Error, TDataProp extends string = "data", TDataPending extends TData | undefined = undefined> = AsyncBaseResult<TData, TError, TDataProp> & {
21
+ isPending: true;
22
+ error: null;
23
+ } & PropertyWithName<TDataProp, TDataPending>;
24
+
25
+ export declare type AsyncResult<TData, TError extends ErrorBase = Error, TDataProp extends string = "data", TDataPending extends TData | undefined = undefined> = AsyncPendingResult<TData, TError, TDataProp, TDataPending> | AsyncErrorResult<TData, TError, TDataProp, TDataPending> | AsyncSuccessResult<TData, TError, TDataProp, TDataPending>;
26
+
27
+ export declare type AsyncSuccessResult<TData, TError extends ErrorBase = Error, TDataProp extends string = "data", TDataPending extends TData | undefined = undefined> = AsyncBaseResult<TData, TError, TDataProp, TDataPending> & {
28
+ isPending: false;
29
+ error: null;
30
+ } & PropertyWithName<TDataProp, TData>;
31
+
5
32
  /** Bundle for lazy loading */
6
- export declare type Bundle = {
33
+ export declare interface Bundle {
7
34
  bundle: string;
8
- };
35
+ }
9
36
 
10
- export declare type BundleConfig = Record<string, unknown>;
37
+ export declare interface BundleConfig extends Record<string, unknown> {
38
+ }
11
39
 
12
- export declare type ComponentServiceConfig = ServiceConfigBase & {
40
+ export declare interface ComponentServiceProviderConfig extends ServiceConfigBase {
13
41
  component: ComponentValueOrBundle;
14
- };
42
+ }
15
43
 
16
44
  export declare type ComponentValueOrBundle<TBundleConfig = BundleConfig> = Value | (Bundle & {
17
45
  config?: TBundleConfig;
@@ -21,12 +49,12 @@ export declare type ErrorBase = NonNullable<unknown>;
21
49
 
22
50
  export declare type FactoryExport<TService = unknown, TConfig = BundleConfig> = FactoryServiceFunction<TService, TConfig>;
23
51
 
24
- export declare type FactoryServiceConfig = ServiceConfigBase & {
25
- factory: FactoryValueOrBundle;
26
- };
27
-
28
52
  export declare type FactoryServiceFunction<TService = unknown, TConfig = BundleConfig> = (config?: TConfig) => TService;
29
53
 
54
+ export declare interface FactoryServiceProviderConfig extends ServiceConfigBase {
55
+ factory: FactoryValueOrBundle;
56
+ }
57
+
30
58
  export declare type FactoryValueOrBundle<TBundleConfig = BundleConfig> = {
31
59
  value: FactoryServiceFunction;
32
60
  } | (Bundle & {
@@ -61,9 +89,9 @@ export declare type GetServicesOptions = GetServiceBaseOptions & {
61
89
  errorHandling?: ServicesErrorHandling;
62
90
  };
63
91
 
64
- export declare type InstanceServiceConfig = ServiceConfigBase & {
92
+ export declare interface InstanceServiceProviderConfig extends ServiceConfigBase {
65
93
  instance: InstanceValueOrBundle;
66
- };
94
+ }
67
95
 
68
96
  export declare type InstanceValueOrBundle = Value | Bundle;
69
97
 
@@ -75,17 +103,9 @@ declare interface Props extends PropsWithChildren {
75
103
  config: ServiceManagerConfig;
76
104
  }
77
105
 
78
- /**
79
- * A service configuration can be one of several kinds (instance, factory, component),
80
- * each of which supports two declaration modes:
81
- * - Value: directly provide the instance, factory or component
82
- * - Bundle: module to be lazy loaded with optional config object
83
- */
84
- export declare type ServiceConfig = InstanceServiceConfig | FactoryServiceConfig | ComponentServiceConfig;
85
-
86
- export declare type ServiceConfigBase = {
106
+ export declare interface ServiceConfigBase {
87
107
  ranking?: number;
88
- };
108
+ }
89
109
 
90
110
  export declare type ServiceId = string;
91
111
 
@@ -120,6 +140,14 @@ declare type ServiceManagerConfig = {
120
140
  declare const ServiceManagerProvider: FC<Props>;
121
141
  export default ServiceManagerProvider;
122
142
 
143
+ /**
144
+ * A service configuration can be one of several kinds (instance, factory, component),
145
+ * each of which supports two declaration modes:
146
+ * - Value: directly provide the instance, factory or component
147
+ * - Bundle: module to be lazy loaded with optional config object
148
+ */
149
+ export declare type ServiceProviderConfig = InstanceServiceProviderConfig | FactoryServiceProviderConfig | ComponentServiceProviderConfig;
150
+
123
151
  /**
124
152
  * Reference to a service, including metadata and a loader function.
125
153
  * Can be used to access service details and also resolve its service instance.
@@ -130,7 +158,8 @@ export declare type ServiceReference<TService = unknown> = {
130
158
  getService(): Promise<TService>;
131
159
  };
132
160
 
133
- export declare type ServicesConfig = Record<ServiceId, ServiceConfig[]>;
161
+ export declare interface ServicesConfig extends Record<ServiceId, ServiceProviderConfig[]> {
162
+ }
134
163
 
135
164
  /**
136
165
  * Error handling strategies that determine how errors are managed when loading services:
@@ -140,27 +169,31 @@ export declare type ServicesConfig = Record<ServiceId, ServiceConfig[]>;
140
169
  */
141
170
  export declare type ServicesErrorHandling = "reject-on-any-failure" | "reject-on-all-failures" | "ignore-failures";
142
171
 
143
- export declare type UseAsyncBaseResult<TData, TError extends ErrorBase = Error, TDataProp extends string = "data", TDataPending extends TData | undefined = undefined> = {
144
- isPending: boolean;
145
- error: TError | null;
146
- } & PropertyWithName<TDataProp, TData | TDataPending | undefined>;
147
-
148
- export declare type UseAsyncErrorResult<TData, TError extends ErrorBase = Error, TDataProp extends string = "data", TDataPending extends TData | undefined = undefined> = UseAsyncBaseResult<TData, TError, TDataProp, TDataPending> & {
149
- isPending: false;
150
- error: TError;
151
- } & PropertyWithName<TDataProp, undefined>;
152
-
153
- export declare type UseAsyncPendingResult<TData, TError extends ErrorBase = Error, TDataProp extends string = "data", TDataPending extends TData | undefined = undefined> = UseAsyncBaseResult<TData, TError, TDataProp> & {
154
- isPending: true;
155
- error: null;
156
- } & PropertyWithName<TDataProp, TDataPending>;
157
-
158
- export declare type UseAsyncResult<TData, TError extends ErrorBase = Error, TDataProp extends string = "data", TDataPending extends TData | undefined = undefined> = UseAsyncPendingResult<TData, TError, TDataProp, TDataPending> | UseAsyncErrorResult<TData, TError, TDataProp, TDataPending> | UseAsyncSuccessResult<TData, TError, TDataProp, TDataPending>;
159
-
160
- export declare type UseAsyncSuccessResult<TData, TError extends ErrorBase = Error, TDataProp extends string = "data", TDataPending extends TData | undefined = undefined> = UseAsyncBaseResult<TData, TError, TDataProp, TDataPending> & {
161
- isPending: false;
162
- error: null;
163
- } & PropertyWithName<TDataProp, TData>;
172
+ /**
173
+ * Generic hook that runs an async factory and returns a discriminated result.
174
+ *
175
+ * Responsibilities:
176
+ * - Invoke the provided `promiseFactory` when mounted (and when it changes).
177
+ * - Track pending state, resolved data and typed error.
178
+ * - Prevent state updates after unmount to avoid memory leaks (see {@link https://reactjs.org/docs/hooks-effect.html | React's useEffect}).
179
+ *
180
+ * Returns a {@link AsyncResult} which is a discriminated union:
181
+ * - {@link AsyncPendingResult} — pending: `isPending: true`, `error: null`, and the pending data property.
182
+ * - {@link AsyncSuccessResult} success: `isPending: false`, `error: null`, and the resolved data property.
183
+ * - {@link AsyncErrorResult} — error: `isPending: false`, `error: TError`, and the data property set to `undefined`.
184
+ *
185
+ * Type references:
186
+ * - Error type: {@link ErrorBase}
187
+ * - Result shapes: {@link AsyncResult}, {@link AsyncPendingResult}, {@link AsyncSuccessResult}, {@link AsyncErrorResult}
188
+ *
189
+ * Defaults:
190
+ * - `dataProp` = `"data"`
191
+ * - `pendingData` = `undefined`
192
+ *
193
+ * Example:
194
+ * - `useAsync(() => fetchServices(), { pendingData: [] })`
195
+ */
196
+ export declare function useAsync<TData, TError extends ErrorBase = Error, TDataProp extends string = "data", TPending extends TData | undefined = undefined>(promiseFactory: () => Promise<TData>, options?: AsyncOptions<TData, TDataProp, TPending>): AsyncResult<TData, TError, TDataProp, TPending>;
164
197
 
165
198
  /**
166
199
  * Resolves and returns the service instance for a given service id or reference.
@@ -218,7 +251,7 @@ export declare type UseServiceReferencesOptions = GetServiceReferencesOptions;
218
251
  /**
219
252
  * Result type for the {@link useService} hook that represents the async state of a single service fetch operation.
220
253
  */
221
- export declare type UseServiceResult<TService> = UseAsyncResult<TService, Error, "service">;
254
+ export declare type UseServiceResult<TService> = AsyncResult<TService, Error, "service">;
222
255
 
223
256
  /**
224
257
  * Gets all service instances for a given service id.
@@ -239,11 +272,11 @@ export declare type UseServicesOptions = GetServicesOptions;
239
272
  /**
240
273
  * Result type for the {@link useServices} hook that represents the async state of fetching multiple services.
241
274
  */
242
- export declare type UseServicesResult<TService> = UseAsyncResult<TService[], Error, "services", TService[]>;
275
+ export declare type UseServicesResult<TService> = AsyncResult<TService[], Error, "services", TService[]>;
243
276
 
244
277
  /** Directly provided value */
245
- export declare type Value = {
278
+ export declare interface Value {
246
279
  value: unknown;
247
- };
280
+ }
248
281
 
249
282
  export { }
package/dist/index.js CHANGED
@@ -1,8 +1,10 @@
1
+ import { useAsync } from "./hooks/useAsync.js";
1
2
  import { useService, useServiceReference, useServiceReferences, useServices } from "./hooks/Hooks.js";
2
3
  import { useServicesContext } from "./hooks/useServicesContext.js";
3
4
  import { default as default2 } from "./providers/ServiceManagerProvider.js";
4
5
  export {
5
6
  default2 as default,
7
+ useAsync,
6
8
  useService,
7
9
  useServiceReference,
8
10
  useServiceReferences,
@@ -1,10 +1,10 @@
1
- import { createContext, useMemo, createElement } from "react";
1
+ import { useMemo, createElement, createContext } from "react";
2
2
  import { createServiceReference } from "../utils/serviceReference.js";
3
3
  function createServiceReferenceMap(services = {}) {
4
4
  return Object.entries(services).reduce(
5
- (map, [serviceId, serviceConfigs]) => {
6
- const serviceReferences = serviceConfigs.map(
7
- (serviceConfig) => createServiceReference(serviceId, serviceConfig)
5
+ (map, [serviceId, serviceProviderConfigs]) => {
6
+ const serviceReferences = serviceProviderConfigs.map(
7
+ (serviceProviderConfig) => createServiceReference(serviceId, serviceProviderConfig)
8
8
  ).sort((a, b) => b.ranking - a.ranking);
9
9
  map.set(serviceId, serviceReferences);
10
10
  return map;
@@ -12,9 +12,7 @@ function createServiceReferenceMap(services = {}) {
12
12
  /* @__PURE__ */ new Map()
13
13
  );
14
14
  }
15
- function createServiceManager({
16
- services
17
- }) {
15
+ function createServiceManager(services = {}) {
18
16
  const serviceReferenceMap = createServiceReferenceMap(services);
19
17
  function queryServiceReferences(serviceId, _options) {
20
18
  return serviceReferenceMap.get(serviceId) ?? [];
@@ -48,7 +46,7 @@ function createServiceManager({
48
46
  );
49
47
  const successful = [];
50
48
  const errors = [];
51
- results.forEach((result) => {
49
+ for (const result of results) {
52
50
  if (result.status === "fulfilled") {
53
51
  successful.push(result.value);
54
52
  } else {
@@ -56,7 +54,7 @@ function createServiceManager({
56
54
  result.reason instanceof Error ? result.reason : new Error(String(result.reason))
57
55
  );
58
56
  }
59
- });
57
+ }
60
58
  switch (errorHandling) {
61
59
  case "reject-on-any-failure":
62
60
  if (errors.length > 0) {
@@ -79,7 +77,9 @@ const ServicesContext = createContext(
79
77
  void 0
80
78
  );
81
79
  const ServiceManagerProvider = ({ config, children }) => {
82
- const serviceManager = useMemo(() => createServiceManager(config), [config]);
80
+ const serviceManager = useMemo(() => {
81
+ return createServiceManager(config.services);
82
+ }, [config.services]);
83
83
  return createElement(
84
84
  ServicesContext.Provider,
85
85
  { value: serviceManager },
@@ -137,17 +137,17 @@ function createComponentReference(serviceId, config) {
137
137
  };
138
138
  return createServiceReferenceBase(serviceId, config, serviceLoader);
139
139
  }
140
- function createServiceReference(serviceId, serviceConfig) {
141
- if ("instance" in serviceConfig) {
142
- return createInstanceReference(serviceId, serviceConfig);
140
+ function createServiceReference(serviceId, serviceProviderConfig) {
141
+ if ("instance" in serviceProviderConfig) {
142
+ return createInstanceReference(serviceId, serviceProviderConfig);
143
143
  }
144
- if ("factory" in serviceConfig) {
145
- return createFactoryReference(serviceId, serviceConfig);
144
+ if ("factory" in serviceProviderConfig) {
145
+ return createFactoryReference(serviceId, serviceProviderConfig);
146
146
  }
147
- if ("component" in serviceConfig) {
147
+ if ("component" in serviceProviderConfig) {
148
148
  return createComponentReference(
149
149
  serviceId,
150
- serviceConfig
150
+ serviceProviderConfig
151
151
  );
152
152
  }
153
153
  throw new Error(
package/package.json CHANGED
@@ -1,19 +1,19 @@
1
1
  {
2
2
  "name": "@hitachivantara/app-shell-services",
3
- "version": "2.0.0-next.1",
3
+ "version": "2.0.0-next.2",
4
4
  "type": "module",
5
5
  "private": false,
6
6
  "author": "Hitachi Vantara UI Kit Team",
7
7
  "description": "AppShell Services",
8
- "homepage": "https://github.com/lumada-design/hv-uikit-react",
8
+ "homepage": "https://github.com/pentaho/hv-uikit-react",
9
9
  "sideEffects": false,
10
10
  "license": "Apache-2.0",
11
11
  "repository": {
12
12
  "type": "git",
13
- "url": "git+https://github.com/lumada-design/hv-uikit-react.git",
13
+ "url": "git+https://github.com/pentaho/hv-uikit-react.git",
14
14
  "directory": "packages/app-shell-services"
15
15
  },
16
- "bugs": "https://github.com/lumada-design/hv-uikit-react/issues",
16
+ "bugs": "https://github.com/pentaho/hv-uikit-react/issues",
17
17
  "peerDependencies": {
18
18
  "react": "^18.2.0"
19
19
  },
@@ -33,7 +33,7 @@
33
33
  "access": "public",
34
34
  "directory": "package"
35
35
  },
36
- "gitHead": "0b07240fb2ef75e046d8e6aaf0ff71bf2415c73f",
36
+ "gitHead": "f404f47dd2c0a9d4033b9acccc87282aa9e42119",
37
37
  "types": "./dist/index.d.ts",
38
38
  "module": "dist/index.js"
39
39
  }