@carbonorm/carbonreact 6.0.1 → 6.0.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.
@@ -4,6 +4,7 @@ import { iRestfulObjectArrayTypes } from "schema/C6";
4
4
  import { iCarbonWebSocketProps } from "components/WebSocket/CarbonWebSocket";
5
5
  import { iUpdateRestfulObjectArrays } from "state/updateRestfulObjectArrays";
6
6
  import { iDeleteRestfulObjectArrays } from "state/deleteRestfulObjectArrays";
7
+ import { iStateAdapter } from "state/stateAdapter";
7
8
  export type tStatefulApiData<T extends {
8
9
  [key: string]: any;
9
10
  } = {}> = T[] | undefined | null;
@@ -28,6 +29,7 @@ declare abstract class CarbonReact<P = {}, S extends iCarbonReactState = iCarbon
28
29
  persistentState?: boolean;
29
30
  routerType?: eRouterType;
30
31
  websocket?: Omit<iCarbonWebSocketProps<P, S>, "instance"> | false;
32
+ stateAdapter?: iStateAdapter<S>;
31
33
  } & P, S> {
32
34
  private static allInstances;
33
35
  context: Context<S & iCarbonReactState>;
@@ -49,7 +51,9 @@ declare abstract class CarbonReact<P = {}, S extends iCarbonReactState = iCarbon
49
51
  websocket?: boolean | iCarbonWebSocketProps<P, S> | undefined;
50
52
  instanceId?: string;
51
53
  persistentState?: boolean;
54
+ stateAdapter?: iStateAdapter<S>;
52
55
  } & P);
56
+ stateAdapter?: iStateAdapter<S>;
53
57
  private static generateIdentifier;
54
58
  private generateIdentifier;
55
59
  shouldComponentUpdate(nextProps: Readonly<P>, nextState: Readonly<S>, _nextContext: any): boolean;
package/dist/index.cjs CHANGED
@@ -2654,9 +2654,10 @@ exports.eUpdateInsertMethod = void 0;
2654
2654
  * @param insertUpdateOrder - The order in which new data should be inserted/updated.
2655
2655
  * @param callback - Optional callback function to run after state update.
2656
2656
  */
2657
- function updateRestfulObjectArrays({ instance, dataOrCallback, stateKey, uniqueObjectId, insertUpdateOrder = exports.eUpdateInsertMethod.LAST, callback, }) {
2657
+ function updateRestfulObjectArrays({ instance, dataOrCallback, stateKey, uniqueObjectId, insertUpdateOrder = exports.eUpdateInsertMethod.LAST, callback, stateAdapter, }) {
2658
2658
  const uniqueObjectIds = Array.isArray(uniqueObjectId) ? uniqueObjectId : [uniqueObjectId];
2659
- instance.setState((previousBootstrapState, props) => {
2659
+ const resolvedStateAdapter = stateAdapter ?? instance.stateAdapter;
2660
+ const computeNextState = (previousBootstrapState, props) => {
2660
2661
  let newOrReplacementData = [];
2661
2662
  if (Array.isArray(dataOrCallback)) {
2662
2663
  newOrReplacementData = dataOrCallback;
@@ -2711,12 +2712,23 @@ function updateRestfulObjectArrays({ instance, dataOrCallback, stateKey, uniqueO
2711
2712
  throw new Error("The insertUpdateOrder (eUpdateInsertMethod) was not implemented");
2712
2713
  }
2713
2714
  return newState;
2714
- }, callback);
2715
+ };
2716
+ if (resolvedStateAdapter) {
2717
+ const previousBootstrapState = resolvedStateAdapter.getState();
2718
+ const nextState = computeNextState(previousBootstrapState, instance.props);
2719
+ if (nextState !== null) {
2720
+ resolvedStateAdapter.setState(nextState);
2721
+ callback?.();
2722
+ }
2723
+ return;
2724
+ }
2725
+ instance.setState(computeNextState, callback);
2715
2726
  }
2716
2727
 
2717
- function deleteRestfulObjectArrays({ instance, dataOrCallback, stateKey, uniqueObjectId, callback }) {
2728
+ function deleteRestfulObjectArrays({ instance, dataOrCallback, stateKey, uniqueObjectId, callback, stateAdapter }) {
2718
2729
  const uniqueObjectIds = Array.isArray(uniqueObjectId) ? uniqueObjectId : [uniqueObjectId];
2719
- instance.setState((previousBootstrapState, props) => {
2730
+ const resolvedStateAdapter = stateAdapter ?? instance.stateAdapter;
2731
+ const computeNextState = (previousBootstrapState, props) => {
2720
2732
  let newOrReplacementData = [];
2721
2733
  if (Array.isArray(dataOrCallback)) {
2722
2734
  newOrReplacementData = dataOrCallback;
@@ -2736,7 +2748,17 @@ function deleteRestfulObjectArrays({ instance, dataOrCallback, stateKey, uniqueO
2736
2748
  return {
2737
2749
  [stateKey]: updatedStateProperty
2738
2750
  };
2739
- }, callback);
2751
+ };
2752
+ if (resolvedStateAdapter) {
2753
+ const previousBootstrapState = resolvedStateAdapter.getState();
2754
+ const nextState = computeNextState(previousBootstrapState, instance.props);
2755
+ if (nextState !== null) {
2756
+ resolvedStateAdapter.setState(nextState);
2757
+ callback?.();
2758
+ }
2759
+ return;
2760
+ }
2761
+ instance.setState(computeNextState, callback);
2740
2762
  }
2741
2763
 
2742
2764
  const initialRequiredCarbonORMState = {
@@ -2812,11 +2834,13 @@ class CarbonReact extends react.Component {
2812
2834
  CarbonReact.allInstances.set(identifier, this);
2813
2835
  }
2814
2836
  this.target = new.target;
2837
+ this.stateAdapter = props.stateAdapter;
2815
2838
  console.log('CarbonORM TSX CONSTRUCTOR');
2816
2839
  Object.assign(this.target, {
2817
2840
  _instance: this
2818
2841
  });
2819
2842
  }
2843
+ stateAdapter;
2820
2844
  static generateIdentifier(instanceId) {
2821
2845
  const className = this.name;
2822
2846
  return instanceId ? `${className}-${instanceId}` : className;
@@ -2961,15 +2985,8 @@ function initiateWebsocket(props) {
2961
2985
  const METHOD = parsedData?.REST?.METHOD;
2962
2986
  const REQUEST = parsedData?.REST?.REQUEST;
2963
2987
  const REQUEST_PRIMARY_KEY = parsedData?.REST?.REQUEST_PRIMARY_KEY ?? null;
2964
- if (null === REQUEST_PRIMARY_KEY) {
2965
- if (verbose) {
2966
- console.log('WebSocket updates without a primary key are not yet supported.');
2967
- }
2968
- return;
2969
- }
2970
- if (verbose) {
2971
- console.log('todo - going to impl REST', TABLE_NAME, METHOD, REQUEST_PRIMARY_KEY, parsedData?.REST);
2972
- }
2988
+ const RESPONSE_PRIMARY_KEY = parsedData?.REST?.RESPONSE_PRIMARY_KEY ?? null;
2989
+ const RESPONSE = parsedData?.REST?.RESPONSE ?? null;
2973
2990
  const TABLE_NAME_SHORT = TABLE_NAME.startsWith(TABLE_PREFIX)
2974
2991
  ? TABLE_NAME.substring(TABLE_PREFIX.length)
2975
2992
  : TABLE_NAME;
@@ -2995,20 +3012,36 @@ function initiateWebsocket(props) {
2995
3012
  }
2996
3013
  return normalized;
2997
3014
  };
2998
- const normalizedPrimaryKey = normalizeRecord(REQUEST_PRIMARY_KEY);
2999
- const primaryKeyKeys = Object.keys(normalizedPrimaryKey);
3015
+ const normalizedPrimaryKey = normalizeRecord(REQUEST_PRIMARY_KEY ?? {});
3016
+ const normalizedResponsePrimaryKey = normalizeRecord(RESPONSE_PRIMARY_KEY ?? {});
3017
+ const primaryKeyKeys = Object.keys(Object.keys(normalizedPrimaryKey).length
3018
+ ? normalizedPrimaryKey
3019
+ : normalizedResponsePrimaryKey);
3000
3020
  if (primaryKeyKeys.length === 0) {
3001
3021
  if (verbose) {
3002
- console.error('WebSocket update could not map primary keys for', TABLE_NAME_SHORT, REQUEST_PRIMARY_KEY);
3022
+ console.error('WebSocket update could not map primary keys for', TABLE_NAME_SHORT, REQUEST_PRIMARY_KEY, RESPONSE_PRIMARY_KEY);
3003
3023
  }
3004
3024
  return;
3005
3025
  }
3006
3026
  // todo - which direction should we filter
3007
- const elementsToUpdate = currentCache?.filter((row) => primaryKeyKeys.every((key) => normalizedPrimaryKey[key] === row[key])) ?? [];
3027
+ const elementsToUpdate = currentCache?.filter((row) => primaryKeyKeys.every((key) => {
3028
+ const expected = normalizedPrimaryKey[key] ?? normalizedResponsePrimaryKey[key];
3029
+ return expected === row[key];
3030
+ })) ?? [];
3008
3031
  if (verbose) {
3009
3032
  console.log('elementsToUpdate', elementsToUpdate);
3010
3033
  }
3011
3034
  if (elementsToUpdate.length === 0) {
3035
+ if (RESPONSE) {
3036
+ const responseRows = Array.isArray(RESPONSE) ? RESPONSE : [RESPONSE];
3037
+ const normalizedResponseRows = responseRows.map((row) => normalizeRecord(row));
3038
+ instance.updateRestfulObjectArrays({
3039
+ dataOrCallback: normalizedResponseRows,
3040
+ stateKey: TABLE_NAME_SHORT,
3041
+ uniqueObjectId: c6Table.PRIMARY_SHORT,
3042
+ });
3043
+ return;
3044
+ }
3012
3045
  if (verbose) {
3013
3046
  console.error('Could not find any elements to update in the cache.', elementsToUpdate, primaryKeyKeys, REQUEST_PRIMARY_KEY, currentCache);
3014
3047
  }
@@ -3054,58 +3087,29 @@ function initiateWebsocket(props) {
3054
3087
  console.log(`WebSocket reconnect will be attempted in ${retrySeconds} second(s).`);
3055
3088
  connectInterval = setTimeout(() => initiateWebsocket(props), retrySeconds);
3056
3089
  };
3057
- // See https://www.rfc-editor.org/rfc/rfc6455#section-7.4.1
3090
+ if (event.code === 1000) {
3091
+ console.log("WebSocket: closed cleanly");
3092
+ return;
3093
+ }
3058
3094
  switch (event.code) {
3059
- case 1000:
3060
- reason = "Normal closure, meaning that the purpose for which the connection was established has been fulfilled.";
3061
- break;
3062
- case 1001:
3063
- retry(); //call check function after timeout
3064
- reason = "An endpoint is \"going away\", such as a server going down or a browser having navigated away from a page.";
3065
- break;
3066
- case 1002:
3067
- reason = "An endpoint is terminating the connection due to a protocol error";
3068
- break;
3069
- case 1003:
3070
- reason = "An endpoint is terminating the connection because it has received a type of data it cannot accept (e.g., an endpoint that understands only text data MAY send this if it receives a binary message).";
3071
- break;
3072
- case 1004:
3073
- reason = "Reserved. The specific meaning might be defined in the future.";
3074
- break;
3075
- case 1005:
3076
- reason = "No status code was actually present.";
3077
- break;
3078
3095
  case 1006:
3079
- retry();
3080
- reason = "The connection was closed abnormally, e.g., without sending or receiving a close control frame";
3081
- break;
3082
- case 1007:
3083
- reason = "An endpoint is terminating the connection because it has received data within a message that was not consistent with the type of the message (e.g., non-UTF-8 [https://www.rfc-editor.org/rfc/rfc3629] data within a text message).";
3084
- break;
3085
- case 1008:
3086
- reason = "An endpoint is terminating the connection because it has received a message that \"violates its policy\". This reason is given either if there is no other suitable reason, or if there is a need to hide specific details about the policy.";
3096
+ reason = "Abnormal closure";
3087
3097
  break;
3088
- case 1009:
3089
- reason = "An endpoint is terminating the connection because it has received a message that is too big for it to process.";
3090
- break;
3091
- case 1010:
3092
- reason = "An endpoint (client) is terminating the connection because it has expected the server to negotiate one or more extension, but the server didn't return them in the response message of the WebSocket handshake. <br /> Specifically, the extensions that are needed are: " + event.reason;
3098
+ case 1001:
3099
+ reason = "Going away";
3093
3100
  break;
3094
3101
  case 1011:
3095
- reason = "A server is terminating the connection because it encountered an un expected condition that prevented it from fulfilling the request.";
3096
- break;
3097
- case 1015:
3098
- reason = "The connection was closed due to a failure to perform a TLS handshake (e.g., the server certificate can't be verified).";
3102
+ reason = "Internal server error";
3099
3103
  break;
3100
3104
  default:
3101
- reason = "Unknown reason";
3105
+ reason = "Unknown";
3102
3106
  }
3103
- console.log("The connection was closed for reason: " + reason);
3107
+ console.log(`WebSocket: closed with code ${event.code} (${reason})`);
3108
+ retry();
3104
3109
  });
3105
3110
  // websocket onerror event listener
3106
3111
  connection.addEventListener('websocket error', (e) => {
3107
- console.error("Socket encountered error: ", e, JSON.stringify(e));
3108
- connection.close();
3112
+ console.error("WebSocket error observed:", e);
3109
3113
  });
3110
3114
  });
3111
3115
  }
@@ -3262,6 +3266,29 @@ function useWindowDimensions() {
3262
3266
  return windowDimensions;
3263
3267
  }
3264
3268
 
3269
+ class InMemoryStateAdapter {
3270
+ state;
3271
+ constructor(initialState) {
3272
+ this.state = initialState;
3273
+ }
3274
+ getState() {
3275
+ return this.state;
3276
+ }
3277
+ setState(partial) {
3278
+ this.state = { ...this.state, ...partial };
3279
+ }
3280
+ snapshot() {
3281
+ return this.state;
3282
+ }
3283
+ hydrate(snapshot) {
3284
+ this.state = snapshot;
3285
+ }
3286
+ }
3287
+ const serializeStateSnapshot = (state) => JSON.stringify(state);
3288
+ const deserializeStateSnapshot = (serialized) => JSON.parse(serialized);
3289
+ const getStateSnapshot = (adapter) => adapter.getState();
3290
+ const createInMemoryStateAdapter = (initialState) => new InMemoryStateAdapter(initialState);
3291
+
3265
3292
  const addValidSQL = [];
3266
3293
  function addValidSQL$1 (sql) {
3267
3294
  const { expect } = require("@jest/globals");
@@ -3390,6 +3417,7 @@ exports.CarbonReact = CarbonReact;
3390
3417
  exports.CarbonWebSocket = CarbonWebSocket;
3391
3418
  exports.ErrorHttpCode = ErrorHttpCode;
3392
3419
  exports.GlobalHistory = GlobalHistory;
3420
+ exports.InMemoryStateAdapter = InMemoryStateAdapter;
3393
3421
  exports.Loading = Loading;
3394
3422
  exports.OutsideClickHandler = OutsideClickHandler;
3395
3423
  exports.PageNotFound = PageNotFound;
@@ -3400,12 +3428,15 @@ exports.addValidSQL = addValidSQL$1;
3400
3428
  exports.carbons = carbons;
3401
3429
  exports.changed = changed;
3402
3430
  exports.comments = comments;
3431
+ exports.createInMemoryStateAdapter = createInMemoryStateAdapter;
3403
3432
  exports.deleteRestfulObjectArrays = deleteRestfulObjectArrays;
3433
+ exports.deserializeStateSnapshot = deserializeStateSnapshot;
3404
3434
  exports.documentation = documentation;
3405
3435
  exports.feature_group_references = feature_group_references;
3406
3436
  exports.features = features;
3407
3437
  exports.getEnv = getEnv;
3408
3438
  exports.getRootStyleValue = getRootStyleValue;
3439
+ exports.getStateSnapshot = getStateSnapshot;
3409
3440
  exports.getStatefulObjectWithWhere = getStatefulObjectWithWhere;
3410
3441
  exports.getStyles = getStyles;
3411
3442
  exports.group_references = group_references;
@@ -3425,6 +3456,7 @@ exports.parseMultipleJson = parseMultipleJson;
3425
3456
  exports.photos = photos;
3426
3457
  exports.reports = reports;
3427
3458
  exports.scrollIntoView = ScrollIntoViewDirective;
3459
+ exports.serializeStateSnapshot = serializeStateSnapshot;
3428
3460
  exports.setCookies = setCookies;
3429
3461
  exports.setupTests = setupTests;
3430
3462
  exports.toDataURL = toDataURL;