@evoke-platform/context 1.1.0-dev.1 → 1.1.0-dev.3

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.
package/README.md CHANGED
@@ -36,6 +36,8 @@ const applications = useObject('application');
36
36
  - [getInstanceHistory](#getinstancehistoryinstanceid)
37
37
  - [newInstance](#newinstanceinput)
38
38
  - [instanceAction](#instanceactioninput)
39
+ - [invalidateCache](#invalidatecache)
40
+ - [invalidateAllCache](#invalidateallcache-static)
39
41
 
40
42
  #### `useObject(objectId)`
41
43
 
@@ -67,15 +69,21 @@ applications.findInstances(callback);
67
69
  const results = await applications.findInstances();
68
70
  ```
69
71
 
72
+ ObjectStore includes built-in caching for object definitions with a 30-second time-to-live (TTL). This improves performance by reducing API calls for frequently accessed objects.
73
+
70
74
  ##### `get(options)`
71
75
 
72
76
  Get the object definition for this store's object. The returned object includes inherited properties and actions if
73
- this object is derived from another object.
77
+ this object is derived from another object. Results are cached for improved performance.
74
78
 
75
79
  - `options` _[object]_ - _optional_
76
80
  - `sanitized` _[boolean]_
77
81
  - If `true`, returns a sanitized version of the object reflecting only the properties and actions available
78
82
  to the current user.
83
+ - `bypassCache` _[boolean]_
84
+ - If `true`, bypasses the cache and forces a new API call. The cache is still updated with the results of the new API call.
85
+ - `skipAlphabetize` _[boolean]_
86
+ - If `true`, preserves the original order of properties instead of alphabetizing them (properties are alphabetized by default).
79
87
 
80
88
  ##### `findInstances(filter)`
81
89
 
@@ -124,6 +132,14 @@ Performs an action on an existing instance.
124
132
  - Action to be executed. The action must not be a create action.
125
133
  - Returns updated instance.
126
134
 
135
+ ##### `invalidateCache()`
136
+
137
+ Invalidates cached data for this specific object ID and all its option variants. Use this when you know the object definition has changed on the server.
138
+
139
+ ##### `invalidateAllCache()` (static)
140
+
141
+ Static method that invalidates the entire object cache across all ObjectStore instances. Use this when you need to force fresh data for all objects.
142
+
127
143
  ### Page Context
128
144
 
129
145
  - [usePageParam](#usepageparamparam)
@@ -133,14 +133,15 @@ export type SelectOption = {
133
133
  label: string;
134
134
  value: string;
135
135
  };
136
+ export type VisibilityCondition = {
137
+ property: string;
138
+ operator: 'eq' | 'neq';
139
+ value: string | number | boolean;
140
+ isInstanceProperty?: boolean;
141
+ };
136
142
  export type VisibilityConfiguration = {
137
143
  operator?: 'any' | 'all';
138
- conditions?: {
139
- property: string;
140
- operator: 'eq' | 'neq';
141
- value: string | number | boolean;
142
- isInstanceProperty?: boolean;
143
- }[];
144
+ conditions?: VisibilityCondition[];
144
145
  };
145
146
  export type RelatedObjectDefaultValue = {
146
147
  criteria: Record<string, unknown>;
@@ -148,6 +149,7 @@ export type RelatedObjectDefaultValue = {
148
149
  orderBy?: 'asc' | 'desc' | 'ASC' | 'DESC';
149
150
  };
150
151
  export type CriteriaDefaultValue = Record<string, unknown>;
152
+ export type JsonLogic = Record<string, unknown> | boolean | number | string | null;
151
153
  export type DisplayConfiguration = {
152
154
  label?: string;
153
155
  placeholder?: string;
@@ -163,7 +165,7 @@ export type DisplayConfiguration = {
163
165
  charCount?: boolean;
164
166
  mode?: 'default' | 'existingOnly';
165
167
  relatedObjectDisplay?: 'dropdown' | 'dialogBox';
166
- visibility?: VisibilityConfiguration | string;
168
+ visibility?: VisibilityConfiguration | JsonLogic;
167
169
  viewLayout?: ViewLayoutEntityReference;
168
170
  choicesDisplay?: {
169
171
  type: 'dropdown' | 'radioButton';
@@ -180,7 +182,7 @@ export type InputParameterReference = {
180
182
  export type Content = {
181
183
  type: 'content';
182
184
  html: string;
183
- visibility?: VisibilityConfiguration | string;
185
+ visibility?: VisibilityConfiguration | JsonLogic;
184
186
  };
185
187
  export type Column = {
186
188
  width: number;
@@ -189,7 +191,7 @@ export type Column = {
189
191
  export type Columns = {
190
192
  type: 'columns';
191
193
  columns: Column[];
192
- visibility?: VisibilityConfiguration | string;
194
+ visibility?: VisibilityConfiguration | JsonLogic;
193
195
  };
194
196
  export type Section = {
195
197
  label: string;
@@ -197,8 +199,9 @@ export type Section = {
197
199
  };
198
200
  export type Sections = {
199
201
  type: 'sections';
202
+ label?: string;
200
203
  sections: Section[];
201
- visibility?: VisibilityConfiguration | string;
204
+ visibility?: VisibilityConfiguration | JsonLogic;
202
205
  };
203
206
  export type FormEntry = InputParameterReference | Columns | Sections | Content;
204
207
  export type Form = {
@@ -245,7 +248,7 @@ export type ActionInput = {
245
248
  input?: boolean;
246
249
  widget?: string;
247
250
  conditional?: {
248
- json?: string;
251
+ json?: JsonLogic;
249
252
  show?: boolean;
250
253
  when?: string;
251
254
  eq?: string | number | boolean;
@@ -312,26 +315,89 @@ export type Reference = {
312
315
  name?: string;
313
316
  };
314
317
  export type ObjectOptions = {
318
+ /**
319
+ * When true, returns a sanitized version of the object reflecting
320
+ * only the properties and actions available to the current user.
321
+ */
315
322
  sanitized?: boolean;
323
+ /**
324
+ * When true, bypasses the cache and forces a new API call.
325
+ */
326
+ bypassCache?: boolean;
327
+ /**
328
+ * When true, preserves the original order of properties instead of
329
+ * alphabetizing them (properties are alphabetized by default).
330
+ */
331
+ skipAlphabetize?: boolean;
316
332
  };
333
+ /**
334
+ * Provides methods for working with objects and their instances in Evoke.
335
+ * Supports retrieving object definitions, finding/retrieving instances,
336
+ * creating new instances, and performing actions on existing instances.
337
+ */
317
338
  export declare class ObjectStore {
318
339
  private services;
319
340
  private objectId;
341
+ private static cache;
320
342
  constructor(services: ApiServices, objectId: string);
343
+ private getCacheKey;
344
+ private processObject;
345
+ /**
346
+ * Invalidates cached data for this specific object ID and all its option variants.
347
+ * Use this when you know the object definition has changed on the server.
348
+ */
349
+ invalidateCache(): void;
350
+ /**
351
+ * Invalidates the entire object cache across all ObjectStore instances.
352
+ * Use this when you need to force fresh data for all objects.
353
+ */
354
+ static invalidateAllCache(): void;
355
+ /**
356
+ * Retrieves the object definition with inherited properties and actions.
357
+ * Results are cached with a 30-second TTL to reduce API calls for frequently accessed objects.
358
+ *
359
+ * By default, properties are alphabetized by name. Use options to customize behavior.
360
+ *
361
+ * @param options - Configuration options for object retrieval and processing
362
+ * @returns A promise resolving to the object with root
363
+ */
321
364
  get(options?: ObjectOptions): Promise<ObjWithRoot>;
322
365
  get(cb?: Callback<ObjWithRoot>): void;
323
366
  get(options: ObjectOptions, cb?: Callback<ObjWithRoot>): void;
367
+ /**
368
+ * Finds instances of the object that match the specified filter criteria.
369
+ */
324
370
  findInstances<T extends ObjectInstance = ObjectInstance>(filter?: Filter): Promise<T[]>;
325
371
  findInstances<T extends ObjectInstance = ObjectInstance>(cb: Callback<T[]>): void;
326
372
  findInstances<T extends ObjectInstance = ObjectInstance>(filter: Filter, cb: Callback<T[]>): void;
373
+ /**
374
+ * Retrieves a specific instance of the object by ID.
375
+ */
327
376
  getInstance<T extends ObjectInstance = ObjectInstance>(id: string): Promise<T>;
328
377
  getInstance<T extends ObjectInstance = ObjectInstance>(id: string, cb: Callback<T>): void;
378
+ /**
379
+ * Retrieves the history of an instance of the object.
380
+ */
329
381
  getInstanceHistory(id: string): Promise<History[]>;
330
382
  getInstanceHistory(id: string, cb: Callback<History[]>): void;
383
+ /**
384
+ * Creates a new instance of the object.
385
+ */
331
386
  newInstance<T extends ObjectInstance = ObjectInstance>(input: ActionRequest): Promise<T>;
332
387
  newInstance<T extends ObjectInstance = ObjectInstance>(input: ActionRequest, cb: Callback<T>): void;
388
+ /**
389
+ * Performs an action on an existing instance of the object.
390
+ */
333
391
  instanceAction<T extends ObjectInstance = ObjectInstance>(id: string, input: ActionRequest): Promise<T>;
334
392
  instanceAction<T extends ObjectInstance = ObjectInstance>(id: string, input: ActionRequest, cb: Callback<T>): void;
335
393
  }
394
+ /**
395
+ * Creates an ObjectStore instance for the specified object.
396
+ * Provides access to object definitions and instance operations.
397
+ * Object definitions are cached for performance.
398
+ *
399
+ * @param objectId - ID of the object to access
400
+ * @returns ObjectStore instance
401
+ */
336
402
  export declare function useObject(objectId: string): ObjectStore;
337
403
  export {};
@@ -1,12 +1,50 @@
1
1
  // Copyright (c) 2023 System Automation Corporation.
2
2
  // This file is licensed under the MIT License.
3
+ import TTLCache from '@isaacs/ttlcache';
3
4
  import { useMemo } from 'react';
4
5
  import { useApiServices } from '../api/index.js';
6
+ /**
7
+ * Provides methods for working with objects and their instances in Evoke.
8
+ * Supports retrieving object definitions, finding/retrieving instances,
9
+ * creating new instances, and performing actions on existing instances.
10
+ */
5
11
  export class ObjectStore {
6
12
  constructor(services, objectId) {
7
13
  this.services = services;
8
14
  this.objectId = objectId;
9
15
  }
16
+ getCacheKey(options) {
17
+ return `${this.objectId}:${(options === null || options === void 0 ? void 0 : options.sanitized) ? 'sanitized' : 'default'}:${(options === null || options === void 0 ? void 0 : options.skipAlphabetize) ? 'unsorted' : 'sorted'}`;
18
+ }
19
+ processObject(object, options) {
20
+ const result = Object.assign({}, object);
21
+ if (result.properties) {
22
+ // alphabetize properties by default unless disabled
23
+ if (!(options === null || options === void 0 ? void 0 : options.skipAlphabetize)) {
24
+ result.properties = [...result.properties].sort((a, b) => a.name.localeCompare(b.name, undefined, { sensitivity: 'base' }));
25
+ }
26
+ }
27
+ return result;
28
+ }
29
+ /**
30
+ * Invalidates cached data for this specific object ID and all its option variants.
31
+ * Use this when you know the object definition has changed on the server.
32
+ */
33
+ invalidateCache() {
34
+ const prefix = `${this.objectId}:`;
35
+ for (const key of ObjectStore.cache.keys()) {
36
+ if (typeof key === 'string' && key.startsWith(prefix)) {
37
+ ObjectStore.cache.delete(key);
38
+ }
39
+ }
40
+ }
41
+ /**
42
+ * Invalidates the entire object cache across all ObjectStore instances.
43
+ * Use this when you need to force fresh data for all objects.
44
+ */
45
+ static invalidateAllCache() {
46
+ ObjectStore.cache.clear();
47
+ }
10
48
  get(optionsOrCallback, cb) {
11
49
  let options;
12
50
  if (cb) {
@@ -18,15 +56,33 @@ export class ObjectStore {
18
56
  else {
19
57
  options = optionsOrCallback;
20
58
  }
59
+ const cacheKey = this.getCacheKey(options);
60
+ if (!(options === null || options === void 0 ? void 0 : options.bypassCache)) {
61
+ const cachedPromise = ObjectStore.cache.get(cacheKey);
62
+ if (cachedPromise) {
63
+ if (cb) {
64
+ const callback = cb;
65
+ cachedPromise.then((data) => callback(null, data)).catch((err) => callback(err));
66
+ return;
67
+ }
68
+ return cachedPromise;
69
+ }
70
+ }
21
71
  const config = {
22
72
  params: {
23
73
  sanitizedVersion: options === null || options === void 0 ? void 0 : options.sanitized,
24
74
  },
25
75
  };
26
- if (!cb) {
27
- return this.services.get(`data/objects/${this.objectId}/effective`, config);
76
+ const promise = this.services
77
+ .get(`data/objects/${this.objectId}/effective`, config)
78
+ .then((result) => this.processObject(result, options));
79
+ ObjectStore.cache.set(cacheKey, promise);
80
+ if (cb) {
81
+ const callback = cb;
82
+ promise.then((data) => callback(null, data)).catch((err) => callback(err));
83
+ return;
28
84
  }
29
- this.services.get(`data/objects/${this.objectId}/effective`, config, cb);
85
+ return promise;
30
86
  }
31
87
  findInstances(filterOrCallback, cb) {
32
88
  let filter;
@@ -74,6 +130,19 @@ export class ObjectStore {
74
130
  this.services.post(`data/objects/${this.objectId}/instances/${id}/actions`, input, cb);
75
131
  }
76
132
  }
133
+ // Cache that stores in-flight promises
134
+ // 30 second TTL for cached promises
135
+ ObjectStore.cache = new TTLCache({
136
+ ttl: 30 * 1000,
137
+ });
138
+ /**
139
+ * Creates an ObjectStore instance for the specified object.
140
+ * Provides access to object definitions and instance operations.
141
+ * Object definitions are cached for performance.
142
+ *
143
+ * @param objectId - ID of the object to access
144
+ * @returns ObjectStore instance
145
+ */
77
146
  export function useObject(objectId) {
78
147
  const services = useApiServices();
79
148
  return useMemo(() => new ObjectStore(services, objectId), [services, objectId]);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@evoke-platform/context",
3
- "version": "1.1.0-dev.1",
3
+ "version": "1.1.0-dev.3",
4
4
  "description": "Utilities that provide context to Evoke platform widgets",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -67,6 +67,7 @@
67
67
  "react-router-dom": ">=6"
68
68
  },
69
69
  "dependencies": {
70
+ "@isaacs/ttlcache": "^1.4.1",
70
71
  "@microsoft/signalr": "^7.0.12",
71
72
  "axios": "^1.7.9",
72
73
  "uuid": "^9.0.1"