@player-ui/player 0.15.3 → 0.15.4--canary.881.37421

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 (56) hide show
  1. package/dist/Player.native.js +3259 -2768
  2. package/dist/Player.native.js.map +1 -1
  3. package/dist/cjs/index.cjs +2553 -2114
  4. package/dist/cjs/index.cjs.map +1 -1
  5. package/dist/index.legacy-esm.js +2535 -2103
  6. package/dist/index.mjs +2535 -2103
  7. package/dist/index.mjs.map +1 -1
  8. package/package.json +4 -4
  9. package/src/__tests__/data.test.ts +0 -13
  10. package/src/__tests__/view.test.ts +34 -1
  11. package/src/controllers/data/controller.ts +1 -1
  12. package/src/controllers/data/utils.ts +5 -26
  13. package/src/controllers/error/__tests__/controller.test.ts +359 -0
  14. package/src/controllers/error/__tests__/middleware.test.ts +237 -0
  15. package/src/controllers/error/__tests__/navigation.test.ts +190 -0
  16. package/src/controllers/error/controller.ts +257 -0
  17. package/src/controllers/error/index.ts +3 -0
  18. package/src/controllers/error/middleware.ts +106 -0
  19. package/src/controllers/error/types.ts +42 -0
  20. package/src/controllers/error/utils/__tests__/isErrorWithMetadata.test.ts +114 -0
  21. package/src/controllers/error/utils/__tests__/makeJsonStringifyReplacer.test.ts +24 -0
  22. package/src/controllers/error/utils/index.ts +2 -0
  23. package/src/controllers/error/utils/isErrorWithMetadata.ts +28 -0
  24. package/src/controllers/error/utils/makeJsonStringifyReplacer.ts +17 -0
  25. package/src/controllers/flow/__tests__/flow.test.ts +268 -0
  26. package/src/controllers/flow/flow.ts +96 -4
  27. package/src/controllers/index.ts +1 -0
  28. package/src/controllers/view/controller.ts +22 -3
  29. package/src/data/model.ts +6 -0
  30. package/src/expressions/types.ts +8 -4
  31. package/src/player.ts +20 -1
  32. package/src/types.ts +6 -0
  33. package/src/validator/types.ts +2 -1
  34. package/src/view/parser/types.ts +6 -3
  35. package/src/view/plugins/__tests__/template.test.ts +7 -2
  36. package/src/view/resolver/ResolverError.ts +25 -0
  37. package/src/view/resolver/__tests__/index.test.ts +53 -1
  38. package/src/view/resolver/index.ts +68 -37
  39. package/src/view/resolver/types.ts +13 -0
  40. package/src/view/resolver/utils.ts +1 -1
  41. package/types/controllers/data/utils.d.ts +3 -7
  42. package/types/controllers/error/controller.d.ts +82 -0
  43. package/types/controllers/error/index.d.ts +4 -0
  44. package/types/controllers/error/middleware.d.ts +23 -0
  45. package/types/controllers/error/types.d.ts +35 -0
  46. package/types/controllers/error/utils/index.d.ts +3 -0
  47. package/types/controllers/error/utils/isErrorWithMetadata.d.ts +3 -0
  48. package/types/controllers/error/utils/makeJsonStringifyReplacer.d.ts +5 -0
  49. package/types/controllers/flow/flow.d.ts +17 -0
  50. package/types/controllers/index.d.ts +1 -0
  51. package/types/controllers/view/controller.d.ts +4 -0
  52. package/types/data/model.d.ts +5 -0
  53. package/types/types.d.ts +5 -1
  54. package/types/view/resolver/ResolverError.d.ts +13 -0
  55. package/types/view/resolver/index.d.ts +2 -1
  56. package/types/view/resolver/types.d.ts +11 -0
@@ -2646,13 +2646,13 @@ var ValidatorRegistry = class {
2646
2646
  };
2647
2647
 
2648
2648
  // ../../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/core/player/src/view/view.ts
2649
- import { SyncHook as SyncHook4 } from "tapable-ts";
2649
+ import { SyncHook as SyncHook9 } from "tapable-ts";
2650
2650
 
2651
2651
  // ../../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/core/player/src/view/resolver/index.ts
2652
- import { SyncHook as SyncHook3, SyncWaterfallHook as SyncWaterfallHook5 } from "tapable-ts";
2653
- import { addLast, clone, setIn as setIn5 } from "timm";
2652
+ import { SyncHook as SyncHook8, SyncWaterfallHook as SyncWaterfallHook9 } from "tapable-ts";
2653
+ import { addLast, clone, setIn as setIn6 } from "timm";
2654
2654
  import dlv from "dlv";
2655
- import { dequal } from "dequal";
2655
+ import { dequal as dequal2 } from "dequal";
2656
2656
 
2657
2657
  // ../../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/core/player/src/view/parser/index.ts
2658
2658
  import { setIn as setIn4 } from "timm";
@@ -2828,978 +2828,1128 @@ function toNodeResolveOptions(resolverOptions) {
2828
2828
  };
2829
2829
  }
2830
2830
 
2831
- // ../../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/core/player/src/view/resolver/index.ts
2832
- var withContext = (model) => {
2833
- return {
2834
- get: (binding, options) => {
2835
- return model.get(binding, {
2836
- context: { model },
2837
- ...options
2838
- });
2839
- },
2840
- set: (transaction, options) => {
2841
- return model.set(transaction, {
2842
- context: { model },
2843
- ...options
2844
- });
2845
- },
2846
- delete: (binding, options) => {
2847
- return model.delete(binding, {
2848
- context: { model },
2849
- ...options
2850
- });
2851
- }
2852
- };
2853
- };
2854
- var Resolver = class {
2855
- constructor(root, options) {
2831
+ // ../../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/core/player/src/view/resolver/types.ts
2832
+ var ResolverStage = /* @__PURE__ */ ((ResolverStage2) => {
2833
+ ResolverStage2["ResolveOptions"] = "resolveOptions";
2834
+ ResolverStage2["SkipResolve"] = "skipResolve";
2835
+ ResolverStage2["BeforeResolve"] = "beforeResolve";
2836
+ ResolverStage2["Resolve"] = "resolve";
2837
+ ResolverStage2["AfterResolve"] = "afterResolve";
2838
+ ResolverStage2["AfterNodeUpdate"] = "afterNodeUpdate";
2839
+ return ResolverStage2;
2840
+ })(ResolverStage || {});
2841
+
2842
+ // ../../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/core/player/src/controllers/flow/flow.ts
2843
+ import { SyncBailHook as SyncBailHook4, SyncHook as SyncHook3, SyncWaterfallHook as SyncWaterfallHook5 } from "tapable-ts";
2844
+ import defer from "p-defer";
2845
+ var FlowInstance = class {
2846
+ constructor(id, flow, options) {
2847
+ this.isTransitioning = false;
2856
2848
  this.hooks = {
2857
- skipResolve: new SyncWaterfallHook5(),
2858
- beforeUpdate: new SyncHook3(),
2859
- afterUpdate: new SyncHook3(),
2860
- resolveOptions: new SyncWaterfallHook5(),
2861
- beforeResolve: new SyncWaterfallHook5(),
2862
- resolve: new SyncWaterfallHook5(),
2863
- afterResolve: new SyncWaterfallHook5(),
2864
- afterNodeUpdate: new SyncHook3()
2849
+ beforeStart: new SyncBailHook4(),
2850
+ onStart: new SyncHook3(),
2851
+ onEnd: new SyncHook3(),
2852
+ skipTransition: new SyncBailHook4(),
2853
+ beforeTransition: new SyncWaterfallHook5(),
2854
+ resolveTransitionNode: new SyncWaterfallHook5(),
2855
+ transition: new SyncHook3(),
2856
+ afterTransition: new SyncHook3()
2865
2857
  };
2866
- this.root = root;
2867
- this.options = options;
2868
- this.resolveCache = /* @__PURE__ */ new Map();
2869
- this.ASTMap = /* @__PURE__ */ new Map();
2870
- this.logger = options.logger;
2871
- this.idCache = /* @__PURE__ */ new Set();
2872
- }
2873
- getSourceNode(convertedAST) {
2874
- return this.ASTMap.get(convertedAST);
2875
- }
2876
- update(dataChanges, nodeChanges) {
2877
- this.hooks.beforeUpdate.call(dataChanges);
2878
- const resolveCache = /* @__PURE__ */ new Map();
2879
- this.idCache.clear();
2880
- const prevASTMap = new Map(this.ASTMap);
2881
- this.ASTMap.clear();
2882
- const realNodeChanges = /* @__PURE__ */ new Set();
2883
- for (const node of nodeChanges?.values() ?? []) {
2884
- let current = node;
2885
- while (current) {
2886
- const original = prevASTMap.get(current) ?? current;
2887
- if (realNodeChanges.has(original)) {
2888
- break;
2858
+ this.id = id;
2859
+ this.flow = flow;
2860
+ this.log = options?.logger;
2861
+ this.history = [];
2862
+ this.hooks.transition.tap(
2863
+ "startPromise",
2864
+ async (_oldState, nextState) => {
2865
+ const newState = nextState.value;
2866
+ if (this.flowPromise && newState.state_type === "END") {
2867
+ this.flowPromise.resolve(newState);
2889
2868
  }
2890
- realNodeChanges.add(original);
2891
- current = current.parent;
2892
2869
  }
2893
- }
2894
- const updated = this.computeTree(
2895
- this.root,
2896
- void 0,
2897
- dataChanges,
2898
- resolveCache,
2899
- toNodeResolveOptions(this.options),
2900
- void 0,
2901
- prevASTMap,
2902
- realNodeChanges
2903
2870
  );
2904
- this.resolveCache = resolveCache;
2905
- this.hooks.afterUpdate.call(updated.value);
2906
- return updated.value;
2907
2871
  }
2908
- getResolveCache() {
2909
- return new Map(this.resolveCache);
2872
+ /** Start the state machine */
2873
+ async start() {
2874
+ if (this.flowPromise) {
2875
+ this.log?.warn("Already called start for flow");
2876
+ return this.flowPromise.promise;
2877
+ }
2878
+ this.flow = this.hooks.beforeStart.call(this.flow) || this.flow;
2879
+ if (this.flow.onStart) {
2880
+ this.hooks.onStart.call(this.flow.onStart);
2881
+ }
2882
+ const initialState = this.flow.startState;
2883
+ if (!initialState) {
2884
+ return Promise.reject(new Error("No 'startState' defined for flow"));
2885
+ }
2886
+ this.flowPromise = defer();
2887
+ this.pushHistory(initialState);
2888
+ return this.flowPromise.promise;
2910
2889
  }
2911
- getPreviousResult(node) {
2912
- if (!node) {
2913
- return;
2890
+ /**
2891
+ * Get the flow-level error transitions map
2892
+ */
2893
+ getFlowErrorTransitions() {
2894
+ return this.flow.errorTransitions;
2895
+ }
2896
+ /**
2897
+ * Helper to lookup a key in a map with wildcard fallback
2898
+ */
2899
+ lookupInMap(map, key) {
2900
+ if (!map)
2901
+ return void 0;
2902
+ return map[key] || map["*"];
2903
+ }
2904
+ /** Check if the flow has a transition for the given error type in its current state. */
2905
+ getErrorTransitionState(errorType) {
2906
+ if (this.currentState?.value.state_type === "END") {
2907
+ this.log?.warn("Cannot error transition from END state");
2908
+ return void 0;
2914
2909
  }
2915
- const isFirstUpdate = this.resolveCache.size === 0;
2916
- const id = getNodeID(node);
2917
- if (id) {
2918
- if (this.idCache.has(id)) {
2919
- if (isFirstUpdate) {
2920
- if (node.type === "asset" /* Asset */ || node.type === "view" /* View */) {
2921
- this.logger?.error(
2922
- `Cache conflict: Found Asset/View nodes that have conflicting ids: ${id}, may cause cache issues.`
2923
- );
2924
- } else if (node.type === "value" /* Value */) {
2925
- this.logger?.info(
2926
- `Cache conflict: Found Value nodes that have conflicting ids: ${id}, may cause cache issues. To improve performance make value node IDs globally unique.`
2927
- );
2928
- }
2910
+ if (this.currentState) {
2911
+ const nodeState = this.lookupInMap(
2912
+ this.currentState.value.errorTransitions,
2913
+ errorType
2914
+ );
2915
+ if (nodeState) {
2916
+ if (!Object.prototype.hasOwnProperty.call(this.flow, nodeState)) {
2917
+ this.log?.debug(
2918
+ `Node-level errorTransition references non-existent state "${nodeState}", trying flow-level fallback`
2919
+ );
2920
+ } else {
2921
+ this.log?.debug(
2922
+ `Error transition (node-level) from ${this.currentState.name} to ${nodeState} using ${errorType}`
2923
+ );
2924
+ return nodeState;
2929
2925
  }
2930
- return;
2931
2926
  }
2932
- this.idCache.add(id);
2933
2927
  }
2934
- return this.resolveCache.get(node);
2928
+ const flowState = this.lookupInMap(this.flow.errorTransitions, errorType);
2929
+ if (flowState) {
2930
+ if (!Object.prototype.hasOwnProperty.call(this.flow, flowState)) {
2931
+ this.log?.debug(
2932
+ `Flow-level errorTransition references non-existent state "${flowState}"`
2933
+ );
2934
+ } else {
2935
+ this.log?.debug(
2936
+ `Error transition (flow-level) to ${flowState} using ${errorType}${this.currentState ? ` from ${this.currentState.name}` : ""}`
2937
+ );
2938
+ return flowState;
2939
+ }
2940
+ }
2941
+ return void 0;
2935
2942
  }
2936
- cloneNode(node) {
2937
- const clonedNode = clone(node);
2938
- Object.keys(clonedNode).forEach((key) => {
2939
- if (key === "parent")
2943
+ /**
2944
+ * Navigate using errorTransitions map.
2945
+ * Tries node-level first, then falls back to flow-level.
2946
+ * Bypasses validation hooks and expression resolution.
2947
+ * @throws Error if errorTransitions references a non-existent state
2948
+ */
2949
+ errorTransition(errorType) {
2950
+ const transitionState = this.getErrorTransitionState(errorType);
2951
+ if (transitionState === void 0) {
2952
+ this.log?.warn(
2953
+ `No errorTransition found for ${errorType} (checked node and flow level)`
2954
+ );
2955
+ return;
2956
+ }
2957
+ this.pushHistory(transitionState);
2958
+ }
2959
+ transition(transitionValue, options) {
2960
+ if (this.isTransitioning) {
2961
+ throw new Error(
2962
+ `Transitioning while ongoing transition from ${this.currentState?.name} is in progress is not supported`
2963
+ );
2964
+ }
2965
+ if (this.currentState?.value.state_type === "END") {
2966
+ this.log?.warn(
2967
+ `Skipping transition using ${transitionValue}. Already at END state`
2968
+ );
2969
+ return;
2970
+ }
2971
+ if (this.currentState === void 0) {
2972
+ throw new Error("Cannot transition when there's no current state");
2973
+ }
2974
+ const currentState = this.currentState.value;
2975
+ if (options?.force) {
2976
+ this.log?.debug(`Forced transition. Skipping validation checks`);
2977
+ } else {
2978
+ const skipTransition = this.hooks.skipTransition.call(this.currentState);
2979
+ if (skipTransition) {
2980
+ this.log?.debug(
2981
+ `Skipping transition from ${this.currentState.name} b/c hook told us to`
2982
+ );
2940
2983
  return;
2941
- const value = clonedNode[key];
2942
- if (typeof value === "object" && value !== null) {
2943
- clonedNode[key] = Array.isArray(value) ? [...value] : { ...value };
2944
2984
  }
2945
- });
2946
- return clonedNode;
2947
- }
2948
- computeTree(node, rawParent, dataChanges, cacheUpdate, options, partiallyResolvedParent, prevASTMap, nodeChanges) {
2949
- const dependencyModel = new DependencyModel(options.data.model);
2950
- dependencyModel.trackSubset("core");
2951
- const depModelWithParser = withContext(
2952
- withParser(dependencyModel, this.options.parseBinding)
2953
- );
2954
- const resolveOptions = this.hooks.resolveOptions.call(
2955
- {
2956
- ...options,
2957
- data: {
2958
- ...options.data,
2959
- model: depModelWithParser
2960
- },
2961
- evaluate: (exp) => this.options.evaluator.evaluate(exp, { model: depModelWithParser }),
2962
- node
2963
- },
2964
- node
2965
- );
2966
- const previousResult = this.getPreviousResult(node);
2967
- const previousDeps = previousResult?.dependencies;
2968
- const isChanged = nodeChanges.has(node);
2969
- const dataChanged = caresAboutDataChanges(dataChanges, previousDeps);
2970
- const shouldUseLastValue = this.hooks.skipResolve.call(
2971
- !dataChanged && !isChanged,
2972
- node,
2973
- resolveOptions
2974
- );
2975
- if (previousResult && shouldUseLastValue) {
2976
- const update2 = {
2977
- ...previousResult,
2978
- updated: false
2979
- };
2980
- const repopulateASTMapFromCache = (resolvedNode, AST, ASTParent) => {
2981
- const { node: resolvedASTLocal } = resolvedNode;
2982
- this.ASTMap.set(resolvedASTLocal, AST);
2983
- const resolvedUpdate = {
2984
- ...resolvedNode,
2985
- updated: false
2986
- };
2987
- cacheUpdate.set(AST, resolvedUpdate);
2988
- const handleChildNode = (childNode) => {
2989
- const originalChildNode = prevASTMap.get(childNode) ?? childNode;
2990
- const previousChildResult = this.getPreviousResult(originalChildNode);
2991
- if (!previousChildResult)
2992
- return;
2993
- repopulateASTMapFromCache(
2994
- previousChildResult,
2995
- originalChildNode,
2996
- AST
2997
- );
2998
- };
2999
- if ("children" in resolvedASTLocal) {
3000
- resolvedASTLocal.children?.forEach(
3001
- ({ value: childAST }) => handleChildNode(childAST)
3002
- );
3003
- } else if (resolvedASTLocal.type === "multi-node" /* MultiNode */) {
3004
- resolvedASTLocal.values.forEach(handleChildNode);
3005
- }
3006
- this.hooks.afterNodeUpdate.call(AST, ASTParent, resolvedUpdate);
3007
- };
3008
- previousResult.node.parent = partiallyResolvedParent;
3009
- repopulateASTMapFromCache(previousResult, node, rawParent);
3010
- return update2;
3011
2985
  }
3012
- const clonedNode = {
3013
- ...this.cloneNode(node),
3014
- parent: partiallyResolvedParent
3015
- };
3016
- const resolvedAST = this.hooks.beforeResolve.call(
3017
- clonedNode,
3018
- resolveOptions
3019
- ) ?? {
3020
- type: "empty" /* Empty */
3021
- };
3022
- resolvedAST.parent = partiallyResolvedParent;
3023
- resolveOptions.node = resolvedAST;
3024
- this.ASTMap.set(resolvedAST, node);
3025
- let resolved = this.hooks.resolve.call(
3026
- void 0,
3027
- resolvedAST,
3028
- resolveOptions
2986
+ const state = this.hooks.beforeTransition.call(
2987
+ currentState,
2988
+ transitionValue
3029
2989
  );
3030
- let updated = !dequal(previousResult?.value, resolved);
3031
- if (previousResult && !updated) {
3032
- resolved = previousResult?.value;
2990
+ if (!("transitions" in state)) {
2991
+ throw new Error(`No transitions defined for ${this.currentState.value}`);
3033
2992
  }
3034
- const childDependencies = /* @__PURE__ */ new Set();
3035
- dependencyModel.trackSubset("children");
3036
- if ("children" in resolvedAST) {
3037
- const newChildren = resolvedAST.children?.map((child) => {
3038
- const computedChildTree = this.computeTree(
3039
- child.value,
3040
- node,
3041
- dataChanges,
3042
- cacheUpdate,
3043
- resolveOptions,
3044
- resolvedAST,
3045
- prevASTMap,
3046
- nodeChanges
3047
- );
3048
- const {
3049
- dependencies: childTreeDeps,
3050
- node: childNode,
3051
- updated: childUpdated,
3052
- value: childValue
3053
- } = computedChildTree;
3054
- childTreeDeps.forEach((binding) => childDependencies.add(binding));
3055
- if (childValue) {
3056
- if (childNode.type === "multi-node" /* MultiNode */ && !childNode.override) {
3057
- const arr = addLast(
3058
- dlv(resolved, child.path, []),
3059
- childValue
3060
- );
3061
- resolved = setIn5(resolved, child.path, arr);
3062
- } else {
3063
- resolved = setIn5(resolved, child.path, childValue);
3064
- }
3065
- }
3066
- updated = updated || childUpdated;
3067
- return { ...child, value: childNode };
3068
- });
3069
- resolvedAST.children = newChildren;
3070
- } else if (resolvedAST.type === "multi-node" /* MultiNode */) {
3071
- const childValue = [];
3072
- const rawParentToPassIn = node;
3073
- resolvedAST.values = resolvedAST.values.map((mValue) => {
3074
- const mTree = this.computeTree(
3075
- mValue,
3076
- rawParentToPassIn,
3077
- dataChanges,
3078
- cacheUpdate,
3079
- resolveOptions,
3080
- resolvedAST,
3081
- prevASTMap,
3082
- nodeChanges
3083
- );
3084
- if (mTree.value !== void 0 && mTree.value !== null) {
3085
- mTree.dependencies.forEach(
3086
- (bindingDep) => childDependencies.add(bindingDep)
3087
- );
3088
- updated = updated || mTree.updated;
3089
- childValue.push(mTree.value);
3090
- }
3091
- return mTree.node;
3092
- });
3093
- resolved = childValue;
2993
+ const { transitions } = state;
2994
+ const nextState = transitions[transitionValue] || transitions["*"];
2995
+ if (nextState === void 0) {
2996
+ this.log?.warn(
2997
+ `No transition from ${this.currentState.name} using ${transitionValue} or *`
2998
+ );
2999
+ return;
3094
3000
  }
3095
- childDependencies.forEach(
3096
- (bindingDep) => dependencyModel.addChildReadDep(bindingDep)
3001
+ this.log?.debug(
3002
+ `Transitioning from ${this.currentState.name} to ${nextState} using ${transitionValue} `
3097
3003
  );
3098
- dependencyModel.trackSubset("core");
3099
- if (previousResult && !updated) {
3100
- resolved = previousResult?.value;
3101
- }
3102
- resolved = this.hooks.afterResolve.call(resolved, resolvedAST, {
3103
- ...resolveOptions,
3104
- getDependencies: (scope) => dependencyModel.getDependencies(scope)
3105
- });
3106
- const update = {
3107
- node: resolvedAST,
3108
- updated,
3109
- value: resolved,
3110
- dependencies: /* @__PURE__ */ new Set([
3111
- ...dependencyModel.getDependencies(),
3112
- ...childDependencies
3113
- ])
3114
- };
3115
- this.hooks.afterNodeUpdate.call(node, rawParent, update);
3116
- cacheUpdate.set(node, update);
3117
- return update;
3118
- }
3119
- };
3120
-
3121
- // ../../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/core/player/src/view/view.ts
3122
- var CrossfieldProvider = class {
3123
- constructor(initialView, parser, logger) {
3124
- this.allValidations = /* @__PURE__ */ new Set();
3125
- this.byBinding = /* @__PURE__ */ new Map();
3126
- this.logger = logger;
3127
- this.parse(initialView, parser);
3004
+ return this.pushHistory(nextState, options);
3128
3005
  }
3129
- parse(contentView, parser) {
3130
- const xfieldRefs = contentView.validation;
3131
- if (xfieldRefs === void 0) {
3132
- return;
3006
+ pushHistory(stateName, options) {
3007
+ if (!Object.prototype.hasOwnProperty.call(this.flow, stateName)) {
3008
+ throw new Error(`No flow definition for: ${stateName} was found.`);
3133
3009
  }
3134
- if (!Array.isArray(xfieldRefs)) {
3135
- this.logger?.warn(
3136
- `Unable to register view validations for id: ${contentView.id}. 'validation' property must be an Array.`
3137
- );
3010
+ let nextState = this.flow[stateName];
3011
+ if (!this.flow[stateName] || typeof nextState !== "object" || !("state_type" in nextState)) {
3012
+ this.log?.error(`Flow doesn't contain any states named: ${stateName}`);
3138
3013
  return;
3139
3014
  }
3140
- xfieldRefs.forEach((vRef) => {
3141
- const withDefaults = {
3142
- trigger: "navigation",
3143
- severity: "error",
3144
- ...vRef
3145
- };
3146
- this.allValidations.add(withDefaults);
3147
- const { ref } = vRef;
3148
- if (ref) {
3149
- const parsed = parser(ref);
3150
- if (this.byBinding.has(parsed)) {
3151
- this.byBinding.get(parsed)?.push(withDefaults);
3152
- } else {
3153
- this.byBinding.set(parsed, [withDefaults]);
3154
- }
3155
- }
3015
+ const prevState = this.currentState;
3016
+ this.isTransitioning = true;
3017
+ nextState = this.hooks.resolveTransitionNode.call(
3018
+ nextState
3019
+ );
3020
+ const newCurrentState = {
3021
+ name: stateName,
3022
+ value: nextState
3023
+ };
3024
+ this.currentState = newCurrentState;
3025
+ this.history.push(stateName);
3026
+ if (newCurrentState.value.state_type === "END" && this.flow.onEnd) {
3027
+ this.hooks.onEnd.call(this.flow.onEnd);
3028
+ }
3029
+ this.hooks.transition.call(prevState, {
3030
+ ...newCurrentState
3156
3031
  });
3157
- }
3158
- getValidationsForBinding(binding) {
3159
- return this.byBinding.get(binding);
3032
+ this.isTransitioning = false;
3033
+ this.hooks.afterTransition.call(this);
3160
3034
  }
3161
3035
  };
3162
- var ViewInstance = class {
3163
- constructor(initialView, resolverOptions) {
3036
+
3037
+ // ../../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/core/player/src/controllers/flow/controller.ts
3038
+ import { SyncHook as SyncHook4 } from "tapable-ts";
3039
+ var FlowController = class {
3040
+ constructor(navigation, options) {
3164
3041
  this.hooks = {
3165
- onUpdate: new SyncHook4(),
3166
- parser: new SyncHook4(),
3167
- resolver: new SyncHook4(),
3168
- templatePlugin: new SyncHook4()
3042
+ flow: new SyncHook4()
3169
3043
  };
3170
- this.initialView = initialView;
3171
- this.resolverOptions = resolverOptions;
3044
+ this.navigation = navigation;
3045
+ this.navStack = [];
3046
+ this.log = options?.logger;
3047
+ this.start = this.start.bind(this);
3048
+ this.run = this.run.bind(this);
3049
+ this.transition = this.transition.bind(this);
3050
+ this.addNewFlow = this.addNewFlow.bind(this);
3172
3051
  }
3173
- /** @deprecated use ViewController.updateViewAST */
3174
- updateAsync(asyncNode) {
3175
- const update = this.resolver?.update();
3176
- this.lastUpdate = update;
3177
- this.hooks.onUpdate.call(update);
3052
+ /** Navigate to another state in the state-machine */
3053
+ transition(stateTransition, options) {
3054
+ if (this.current === void 0) {
3055
+ throw new Error("Not currently in a flow. Cannot transition.");
3056
+ }
3057
+ this.current.transition(stateTransition, options);
3178
3058
  }
3179
- update(changes, nodeChanges) {
3180
- if (this.rootNode === void 0) {
3181
- this.validationProvider = new CrossfieldProvider(
3182
- this.initialView,
3183
- this.resolverOptions.parseBinding,
3184
- this.resolverOptions.logger
3059
+ addNewFlow(flow) {
3060
+ this.navStack.push(flow);
3061
+ this.current = flow;
3062
+ this.hooks.flow.call(flow);
3063
+ }
3064
+ async run(startState) {
3065
+ if (!Object.prototype.hasOwnProperty.call(this.navigation, startState)) {
3066
+ return Promise.reject(new Error(`No flow defined for: ${startState}`));
3067
+ }
3068
+ const startFlow = this.navigation[startState];
3069
+ if (startFlow === null || typeof startFlow !== "object") {
3070
+ return Promise.reject(
3071
+ new Error(`Flow: ${startState} needs to be an object`)
3185
3072
  );
3186
- if (this.templatePlugin) {
3187
- this.hooks.templatePlugin.call(this.templatePlugin);
3188
- } else {
3189
- this.resolverOptions.logger?.warn(
3190
- "templatePlugin not set for View, legacy templates may not work"
3191
- );
3192
- }
3193
- const parser = new Parser();
3194
- this.hooks.parser.call(parser);
3195
- this.rootNode = parser.parseView(this.initialView);
3196
- this.resolver = new Resolver(this.rootNode, {
3197
- ...this.resolverOptions,
3198
- parseNode: parser.parseObject.bind(parser)
3199
- });
3200
- this.hooks.resolver.call(this.resolver);
3201
3073
  }
3202
- const update = this.resolver?.update(changes, nodeChanges);
3203
- if (this.lastUpdate === update) {
3204
- return this.lastUpdate;
3074
+ this.log?.debug(`Starting flow: ${startState}`);
3075
+ const flow = new FlowInstance(startState, startFlow, { logger: this.log });
3076
+ this.addNewFlow(flow);
3077
+ flow.hooks.afterTransition.tap("flow-controller", (flowInstance) => {
3078
+ if (flowInstance.currentState?.value.state_type === "FLOW") {
3079
+ const subflowId = flowInstance.currentState?.value.ref;
3080
+ this.log?.debug(`Loading subflow ${subflowId}`);
3081
+ this.run(subflowId).then((subFlowEndState) => {
3082
+ this.log?.debug(
3083
+ `Subflow ended. Using outcome: ${subFlowEndState.outcome}`
3084
+ );
3085
+ flowInstance.transition(subFlowEndState?.outcome);
3086
+ });
3087
+ }
3088
+ });
3089
+ const end = await flow.start();
3090
+ this.navStack.pop();
3091
+ if (this.navStack.length > 0) {
3092
+ const firstItem = 0;
3093
+ this.current = this.navStack[firstItem];
3205
3094
  }
3206
- this.lastUpdate = update;
3207
- this.hooks.onUpdate.call(update);
3208
- return update;
3209
- }
3210
- getValidationsForBinding(binding) {
3211
- return this.validationProvider?.getValidationsForBinding(binding);
3095
+ return end;
3212
3096
  }
3213
- setTemplatePlugin(plugin) {
3214
- this.templatePlugin = plugin;
3097
+ async start() {
3098
+ if (!this.navigation.BEGIN) {
3099
+ return Promise.reject(new Error("Must supply a BEGIN state"));
3100
+ }
3101
+ return this.run(this.navigation.BEGIN);
3215
3102
  }
3216
3103
  };
3217
3104
 
3218
- // ../../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/core/player/src/view/builder/index.ts
3219
- var Builder = class _Builder {
3220
- /**
3221
- * Creates an asset node
3222
- *
3223
- * @param value - the value to put in the asset node
3224
- */
3225
- static asset(value) {
3226
- return {
3227
- type: "asset" /* Asset */,
3228
- value
3229
- };
3230
- }
3231
- static assetWrapper(value) {
3232
- const valueNode = _Builder.value();
3233
- _Builder.addChild(valueNode, "asset", value);
3234
- return valueNode;
3105
+ // ../../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/core/player/src/controllers/validation/controller.ts
3106
+ import { SyncHook as SyncHook5, SyncWaterfallHook as SyncWaterfallHook6 } from "tapable-ts";
3107
+ import { setIn as setIn5 } from "timm";
3108
+
3109
+ // ../../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/core/player/src/utils/replaceParams.ts
3110
+ var ANY_CHAR_REGEX = /%([a-zA-Z]+)/g;
3111
+ function replaceParams(message, params) {
3112
+ return message.slice().replace(ANY_CHAR_REGEX, (keyExpr) => params[keyExpr.slice(1)] || keyExpr);
3113
+ }
3114
+
3115
+ // ../../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/core/player/src/controllers/validation/binding-tracker.ts
3116
+ var CONTEXT = "validation-binding-tracker";
3117
+ var ValidationBindingTrackerViewPlugin = class {
3118
+ constructor(options) {
3119
+ this.trackedBindings = /* @__PURE__ */ new Set();
3120
+ this.options = options;
3235
3121
  }
3236
- /**
3237
- * Creates a value node
3238
- *
3239
- * @param v - The object to put in the value node
3240
- */
3241
- static value(v) {
3242
- return {
3243
- type: "value" /* Value */,
3244
- value: v
3245
- };
3122
+ /** Fetch the tracked bindings in the current view */
3123
+ getBindings() {
3124
+ return this.trackedBindings;
3246
3125
  }
3247
- /**
3248
- * Creates a multiNode and associates the multiNode as the parent
3249
- * of all the value nodes
3250
- *
3251
- * @param values - the value, applicability or async nodes to put in the multinode
3252
- */
3253
- static multiNode(...values) {
3254
- const m = {
3255
- type: "multi-node" /* MultiNode */,
3256
- override: true,
3257
- values
3258
- };
3259
- values.forEach((v) => {
3260
- v.parent = m;
3261
- });
3262
- return m;
3126
+ /** Add a binding to the tracked set */
3127
+ trackBinding(binding) {
3128
+ if (this.trackedBindings.has(binding)) {
3129
+ return;
3130
+ }
3131
+ this.trackedBindings.add(binding);
3132
+ this.options.callbacks?.onAdd?.(binding);
3263
3133
  }
3264
- /**
3265
- * Creates an async node
3266
- *
3267
- * @param id - the id of async node. It should be identical for each async node
3268
- */
3269
- static asyncNode(id, flatten2 = true, onValueReceived) {
3270
- return {
3271
- id,
3272
- type: "async" /* Async */,
3273
- flatten: flatten2,
3274
- onValueReceived,
3275
- value: {
3276
- type: "value" /* Value */,
3277
- value: {
3278
- id
3134
+ /** Attach hooks to the given resolver */
3135
+ applyResolver(resolver) {
3136
+ this.trackedBindings.clear();
3137
+ const tracked = /* @__PURE__ */ new Map();
3138
+ const sections = /* @__PURE__ */ new Map();
3139
+ let lastViewUpdateChangeSet;
3140
+ const lastComputedBindingTree = /* @__PURE__ */ new Map();
3141
+ let currentBindingTree = /* @__PURE__ */ new Map();
3142
+ const lastSectionBindingTree = /* @__PURE__ */ new Map();
3143
+ const resolvedNodeMap = /* @__PURE__ */ new Map();
3144
+ resolver.hooks.beforeUpdate.tap(CONTEXT, (changes) => {
3145
+ lastViewUpdateChangeSet = changes;
3146
+ });
3147
+ resolver.hooks.skipResolve.tap(CONTEXT, (shouldSkip, node) => {
3148
+ const trackedBindingsForNode = lastComputedBindingTree.get(node);
3149
+ if (!shouldSkip || !lastViewUpdateChangeSet || !trackedBindingsForNode) {
3150
+ return shouldSkip;
3151
+ }
3152
+ const intersection = new Set(
3153
+ [...lastViewUpdateChangeSet].filter(
3154
+ (b) => trackedBindingsForNode.has(b)
3155
+ )
3156
+ );
3157
+ return intersection.size === 0;
3158
+ });
3159
+ resolver.hooks.resolveOptions.tap(CONTEXT, (options, node) => {
3160
+ if (options.validation === void 0) {
3161
+ return options;
3162
+ }
3163
+ tracked.delete(node);
3164
+ const track = (binding) => {
3165
+ const parsed = isBinding(binding) ? binding : this.options.parseBinding(binding);
3166
+ if (tracked.has(node)) {
3167
+ tracked.get(node)?.add(parsed);
3168
+ } else {
3169
+ tracked.set(node, /* @__PURE__ */ new Set([parsed]));
3170
+ }
3171
+ let { parent } = node;
3172
+ while (parent) {
3173
+ if (sections.has(parent)) {
3174
+ sections.get(parent)?.add(node);
3175
+ break;
3176
+ } else {
3177
+ parent = parent.parent;
3178
+ }
3179
+ }
3180
+ this.trackedBindings.add(parsed);
3181
+ this.options.callbacks?.onAdd?.(parsed);
3182
+ };
3183
+ return {
3184
+ ...options,
3185
+ validation: {
3186
+ ...options.validation,
3187
+ get: (binding, getOptions) => {
3188
+ if (getOptions?.track) {
3189
+ track(binding);
3190
+ }
3191
+ const eows = options.validation?._getValidationForBinding(binding)?.getAll(getOptions);
3192
+ const firstFieldEOW = eows?.find(
3193
+ (eow) => eow.displayTarget === "field" || eow.displayTarget === void 0
3194
+ );
3195
+ return firstFieldEOW;
3196
+ },
3197
+ getValidationsForBinding(binding, getOptions) {
3198
+ if (getOptions?.track) {
3199
+ track(binding);
3200
+ }
3201
+ return options.validation?._getValidationForBinding(binding)?.getAll(getOptions) ?? [];
3202
+ },
3203
+ getChildren: (type) => {
3204
+ const validations = new Array();
3205
+ lastComputedBindingTree.get(node)?.forEach((binding) => {
3206
+ const eow = options.validation?._getValidationForBinding(binding)?.get();
3207
+ if (eow && (type === void 0 || type === eow.displayTarget)) {
3208
+ validations.push(eow);
3209
+ }
3210
+ });
3211
+ return validations;
3212
+ },
3213
+ getValidationsForSection: () => {
3214
+ const validations = new Array();
3215
+ lastSectionBindingTree.get(node)?.forEach((binding) => {
3216
+ const eow = options.validation?._getValidationForBinding(binding)?.get();
3217
+ if (eow && eow.displayTarget === "section") {
3218
+ validations.push(eow);
3219
+ }
3220
+ });
3221
+ return validations;
3222
+ },
3223
+ register: (registerOptions) => {
3224
+ if (registerOptions?.type === "section") {
3225
+ if (!sections.has(node)) {
3226
+ sections.set(node, /* @__PURE__ */ new Set());
3227
+ }
3228
+ }
3229
+ },
3230
+ track
3231
+ }
3232
+ };
3233
+ });
3234
+ resolver.hooks.afterNodeUpdate.tap(
3235
+ CONTEXT,
3236
+ (originalNode, parent, update) => {
3237
+ const { updated, node: resolvedNode } = update;
3238
+ resolvedNodeMap.set(resolvedNode, originalNode);
3239
+ if (updated) {
3240
+ const newlyComputed = new Set(tracked.get(originalNode));
3241
+ if (resolvedNode.type === "multi-node" /* MultiNode */) {
3242
+ resolvedNode.values.forEach(
3243
+ (value) => currentBindingTree.get(value)?.forEach((b) => newlyComputed.add(b))
3244
+ );
3245
+ }
3246
+ if ("children" in resolvedNode && resolvedNode.children) {
3247
+ resolvedNode.children.forEach((child) => {
3248
+ currentBindingTree.get(child.value)?.forEach((b) => newlyComputed.add(b));
3249
+ });
3250
+ }
3251
+ currentBindingTree.set(resolvedNode, newlyComputed);
3252
+ } else {
3253
+ currentBindingTree.set(
3254
+ resolvedNode,
3255
+ lastComputedBindingTree.get(originalNode) ?? /* @__PURE__ */ new Set()
3256
+ );
3257
+ }
3258
+ if (originalNode === resolver.root) {
3259
+ this.trackedBindings = new Set(currentBindingTree.get(resolvedNode));
3260
+ lastComputedBindingTree.clear();
3261
+ currentBindingTree.forEach((value, key) => {
3262
+ const node = resolvedNodeMap.get(key);
3263
+ if (node) {
3264
+ lastComputedBindingTree.set(node, value);
3265
+ }
3266
+ });
3267
+ lastSectionBindingTree.clear();
3268
+ sections.forEach((nodeSet, sectionNode) => {
3269
+ const temp = /* @__PURE__ */ new Set();
3270
+ nodeSet.forEach((n) => {
3271
+ tracked.get(n)?.forEach(temp.add, temp);
3272
+ });
3273
+ lastSectionBindingTree.set(sectionNode, temp);
3274
+ });
3275
+ tracked.clear();
3276
+ sections.clear();
3277
+ currentBindingTree = /* @__PURE__ */ new Map();
3279
3278
  }
3280
3279
  }
3281
- };
3280
+ );
3282
3281
  }
3283
- /**
3284
- * Adds a child node to a node
3285
- *
3286
- * @param node - The node to add a child to
3287
- * @param path - The path at which to add the child
3288
- * @param child - The child node
3289
- */
3290
- static addChild(node, path, child) {
3291
- child.parent = node;
3292
- const newChild = {
3293
- path: Array.isArray(path) ? path : [path],
3294
- value: child
3282
+ apply(view) {
3283
+ view.hooks.resolver.tap(CONTEXT, this.applyResolver.bind(this));
3284
+ }
3285
+ };
3286
+
3287
+ // ../../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/core/player/src/controllers/validation/controller.ts
3288
+ var SCHEMA_VALIDATION_PROVIDER_NAME = "schema";
3289
+ var VIEW_VALIDATION_PROVIDER_NAME = "view";
3290
+ var VALIDATION_PROVIDER_NAME_SYMBOL = Symbol.for(
3291
+ "validation-provider-name"
3292
+ );
3293
+ function isSubset(subset, containingSet) {
3294
+ if (subset.size > containingSet.size)
3295
+ return false;
3296
+ for (const entry of subset)
3297
+ if (!containingSet.has(entry))
3298
+ return false;
3299
+ return true;
3300
+ }
3301
+ function createStatefulValidationObject(obj) {
3302
+ return {
3303
+ value: obj,
3304
+ type: obj.severity,
3305
+ state: "none",
3306
+ isBlockingNavigation: false
3307
+ };
3308
+ }
3309
+ var ValidatedBinding = class {
3310
+ constructor(possibleValidations, onDismiss, log, weakBindings) {
3311
+ this.applicableValidations = [];
3312
+ this.validationsByState = {
3313
+ load: [],
3314
+ change: [],
3315
+ navigation: []
3295
3316
  };
3296
- node.children = node.children || [];
3297
- node.children.push(newChild);
3298
- return node;
3317
+ this.onDismiss = onDismiss;
3318
+ possibleValidations.forEach((vObj) => {
3319
+ const { trigger } = vObj;
3320
+ if (this.validationsByState[trigger]) {
3321
+ const statefulValidationObject = createStatefulValidationObject(vObj);
3322
+ this.validationsByState[trigger].push(statefulValidationObject);
3323
+ } else {
3324
+ log?.warn(`Unknown validation trigger: ${trigger}`);
3325
+ }
3326
+ });
3327
+ this.weakBindings = weakBindings ?? /* @__PURE__ */ new Set();
3299
3328
  }
3300
- /**
3301
- * Updates children of a node of the same path and preserves order
3302
- *
3303
- * @param node - The node to update children for
3304
- * @param pathToMatch - The path to match against child paths
3305
- * @param mapFn - Function to transform matching children
3306
- */
3307
- static updateChildrenByPath(node, pathToMatch, updateFn) {
3308
- if (!node.children)
3309
- return node;
3310
- const updatedChildren = node.children.map(
3311
- (child) => (
3312
- // Check if paths match exactly
3313
- child.path.join() === pathToMatch.join() ? { ...child, value: updateFn(child) } : child
3314
- )
3329
+ get allValidations() {
3330
+ return Object.values(this.validationsByState).flat();
3331
+ }
3332
+ checkIfBlocking(statefulObj) {
3333
+ if (statefulObj.state === "active") {
3334
+ const { isBlockingNavigation } = statefulObj;
3335
+ return isBlockingNavigation;
3336
+ }
3337
+ return false;
3338
+ }
3339
+ getAll() {
3340
+ return this.applicableValidations.reduce((all, statefulObj) => {
3341
+ if (statefulObj.state === "active" && statefulObj.response) {
3342
+ all.push({
3343
+ ...statefulObj.response,
3344
+ blocking: this.checkIfBlocking(statefulObj)
3345
+ });
3346
+ }
3347
+ return all;
3348
+ }, []);
3349
+ }
3350
+ get() {
3351
+ const firstInvalid = this.applicableValidations.find((statefulObj) => {
3352
+ return statefulObj.state === "active" && statefulObj.response;
3353
+ });
3354
+ if (firstInvalid?.state === "active") {
3355
+ return {
3356
+ ...firstInvalid.response,
3357
+ blocking: this.checkIfBlocking(firstInvalid)
3358
+ };
3359
+ }
3360
+ }
3361
+ runApplicableValidations(runner, canDismiss, phase) {
3362
+ this.applicableValidations = this.applicableValidations.map(
3363
+ (originalValue) => {
3364
+ if (originalValue.state === "dismissed") {
3365
+ return originalValue;
3366
+ }
3367
+ const blocking = originalValue.value.blocking ?? (originalValue.value.severity === "warning" && "once" || true);
3368
+ const obj = setIn5(
3369
+ originalValue,
3370
+ ["value", "blocking"],
3371
+ blocking
3372
+ );
3373
+ const isBlockingNavigation = blocking === true || blocking === "once" && !canDismiss;
3374
+ if (phase === "navigation" && obj.state === "active" && obj.value.blocking !== true) {
3375
+ if (obj.value.severity === "warning") {
3376
+ const warn = obj;
3377
+ if (warn.dismissable && warn.response.dismiss && (warn.response.blocking !== "once" || !warn.response.blocking)) {
3378
+ warn.response.dismiss();
3379
+ } else {
3380
+ if (warn?.response.blocking === "once") {
3381
+ warn.response.blocking = false;
3382
+ }
3383
+ warn.dismissable = true;
3384
+ }
3385
+ return warn;
3386
+ }
3387
+ }
3388
+ const response = runner(obj.value);
3389
+ const newState = {
3390
+ type: obj.type,
3391
+ value: obj.value,
3392
+ state: response ? "active" : "none",
3393
+ isBlockingNavigation,
3394
+ dismissable: obj.value.severity === "warning" && phase === "navigation",
3395
+ response: response ? {
3396
+ ...obj.value,
3397
+ message: response.message ?? "Something is broken",
3398
+ severity: obj.value.severity,
3399
+ displayTarget: obj.value.displayTarget ?? "field"
3400
+ } : void 0
3401
+ };
3402
+ if (newState.state === "active" && obj.value.severity === "warning") {
3403
+ newState.response.dismiss = () => {
3404
+ newState.state = "dismissed";
3405
+ this.onDismiss?.();
3406
+ };
3407
+ }
3408
+ return newState;
3409
+ }
3315
3410
  );
3316
- return {
3317
- ...node,
3318
- children: updatedChildren
3319
- };
3320
- }
3321
- };
3322
-
3323
- // ../../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/core/player/src/view/plugins/template.ts
3324
- import { SyncWaterfallHook as SyncWaterfallHook6 } from "tapable-ts";
3325
- var templateSymbol = Symbol("template");
3326
- var TemplatePlugin = class {
3327
- constructor(options) {
3328
- this.hooks = {
3329
- resolveTemplateSubstitutions: new SyncWaterfallHook6()
3330
- };
3331
- this.options = options;
3332
3411
  }
3333
- parseTemplate(parseObject, node, options) {
3334
- const { template, depth } = node;
3335
- const data = options.data.model.get(node.data);
3336
- if (!data) {
3337
- return null;
3412
+ update(phase, canDismiss, runner) {
3413
+ const newApplicableValidations = [];
3414
+ if (phase === "load" && this.currentPhase !== void 0) {
3415
+ return;
3338
3416
  }
3339
- if (!Array.isArray(data)) {
3340
- throw new Error(`Template using '${node.data}' but is not an array`);
3417
+ if (this.currentPhase === "navigation" || phase === this.currentPhase) {
3418
+ this.runApplicableValidations(runner, canDismiss, phase);
3419
+ return;
3341
3420
  }
3342
- const values = [];
3343
- data.forEach((dataItem, index) => {
3344
- const templateSubstitutions = this.hooks.resolveTemplateSubstitutions.call(
3345
- [
3346
- {
3347
- expression: new RegExp(`_index${depth || ""}_`),
3348
- value: String(index)
3349
- }
3350
- ],
3351
- {
3352
- depth,
3353
- data: dataItem,
3354
- index
3355
- }
3356
- );
3357
- let templateStr = JSON.stringify(template);
3358
- for (const { expression, value } of templateSubstitutions) {
3359
- let flags = "g";
3360
- if (typeof expression === "object") {
3361
- flags = `${expression.flags}${expression.global ? "" : "g"}`;
3421
+ if (phase === "load") {
3422
+ this.currentPhase = "load";
3423
+ this.applicableValidations = [...this.validationsByState.load];
3424
+ } else if (phase === "change" && this.currentPhase === "load") {
3425
+ this.currentPhase = "change";
3426
+ this.applicableValidations = [
3427
+ ...this.applicableValidations,
3428
+ ...this.validationsByState.change
3429
+ ];
3430
+ } else if (phase === "navigation" && (this.currentPhase === "load" || this.currentPhase === "change")) {
3431
+ this.applicableValidations.forEach((element) => {
3432
+ if (!(element.type === "error" && element.state === "active" && element.isBlockingNavigation === false)) {
3433
+ newApplicableValidations.push(element);
3362
3434
  }
3363
- templateStr = templateStr.replace(new RegExp(expression, flags), value);
3364
- }
3365
- const parsed = parseObject(JSON.parse(templateStr), "value" /* Value */, {
3366
- templateDepth: node.depth + 1
3367
3435
  });
3368
- if (parsed) {
3369
- values.push(parsed);
3370
- }
3371
- });
3372
- const result = {
3373
- type: "multi-node" /* MultiNode */,
3374
- override: false,
3375
- values
3376
- };
3377
- if (node.placement !== void 0) {
3378
- result[templateSymbol] = node.placement;
3436
+ this.applicableValidations = [
3437
+ ...newApplicableValidations,
3438
+ ...this.validationsByState.navigation,
3439
+ ...this.currentPhase === "load" ? this.validationsByState.change : []
3440
+ ];
3441
+ this.currentPhase = "navigation";
3379
3442
  }
3380
- return result;
3443
+ this.runApplicableValidations(runner, canDismiss, phase);
3381
3444
  }
3382
- applyParser(parser) {
3383
- parser.hooks.onCreateASTNode.tap("template", (node) => {
3384
- if (node && node.type === "template" /* Template */ && !node.dynamic) {
3385
- return this.parseTemplate(
3386
- parser.parseObject.bind(parser),
3387
- node,
3388
- this.options
3389
- );
3390
- }
3391
- return node;
3392
- });
3393
- parser.hooks.onCreateASTNode.tap("template", (node) => {
3394
- function getTemplateSymbolValue(node2) {
3395
- if (node2.type === "multi-node" /* MultiNode */) {
3396
- return node2[templateSymbol];
3397
- } else if (node2.type === "template" /* Template */) {
3398
- return node2.placement;
3445
+ };
3446
+ var ValidationController = class {
3447
+ constructor(schema, options) {
3448
+ this.hooks = {
3449
+ /** A hook called to tap into the validator registry for adding more validators */
3450
+ createValidatorRegistry: new SyncHook5(),
3451
+ /** A callback/event when a new validation is added to the view */
3452
+ onAddValidation: new SyncWaterfallHook6(),
3453
+ /** The inverse of onAddValidation, this is called when a validation is removed from the list */
3454
+ onRemoveValidation: new SyncWaterfallHook6(),
3455
+ resolveValidationProviders: new SyncWaterfallHook6(),
3456
+ /** A hook called when a binding is added to the tracker */
3457
+ onTrackBinding: new SyncHook5()
3458
+ };
3459
+ this.validations = /* @__PURE__ */ new Map();
3460
+ this.weakBindingTracker = /* @__PURE__ */ new Set();
3461
+ this.schema = schema;
3462
+ this.options = options;
3463
+ this.reset();
3464
+ }
3465
+ setOptions(options) {
3466
+ this.options = options;
3467
+ }
3468
+ /** Return the middleware for the data-model to stop propagation of invalid data */
3469
+ getDataMiddleware() {
3470
+ return [
3471
+ {
3472
+ set: (transaction, options, next) => {
3473
+ return next?.set(transaction, options) ?? [];
3474
+ },
3475
+ get: (binding, options, next) => {
3476
+ return next?.get(binding, options);
3477
+ },
3478
+ delete: (binding, options, next) => {
3479
+ this.validations = removeBindingAndChildrenFromMap(
3480
+ this.validations,
3481
+ binding
3482
+ );
3483
+ return next?.delete(binding, options);
3399
3484
  }
3400
- return void 0;
3401
- }
3402
- if (node && (node.type === "view" /* View */ || node.type === "asset" /* Asset */) && Array.isArray(node.children)) {
3403
- node.children = node.children.sort((a, b) => {
3404
- const aPath = a.path.join();
3405
- const bPath = b.path.join();
3406
- const pathsEqual = aPath === bPath;
3407
- if (pathsEqual) {
3408
- const aPlacement = getTemplateSymbolValue(a.value);
3409
- const bPlacement = getTemplateSymbolValue(b.value);
3410
- if (aPlacement !== void 0 && bPlacement === void 0) {
3411
- return aPlacement === "prepend" ? -1 : 1;
3412
- } else if (bPlacement !== void 0 && aPlacement === void 0) {
3413
- return bPlacement === "prepend" ? 1 : -1;
3414
- } else if (aPlacement !== void 0 && bPlacement !== void 0) {
3415
- if (aPlacement === bPlacement) {
3416
- return 0;
3417
- }
3418
- return aPlacement === "prepend" ? -1 : 1;
3485
+ },
3486
+ new ValidationMiddleware(
3487
+ (binding) => {
3488
+ if (!this.options) {
3489
+ return;
3490
+ }
3491
+ this.updateValidationsForBinding(binding, "change", this.options);
3492
+ const strongValidation = this.getValidationForBinding(binding);
3493
+ if (strongValidation?.get()?.severity === "error") {
3494
+ return strongValidation.get();
3495
+ }
3496
+ const newInvalidBindings = /* @__PURE__ */ new Set();
3497
+ this.validations.forEach((weakValidation, strongBinding) => {
3498
+ if (caresAboutDataChanges(
3499
+ /* @__PURE__ */ new Set([binding]),
3500
+ weakValidation.weakBindings
3501
+ ) && weakValidation?.get()?.severity === "error") {
3502
+ weakValidation?.weakBindings.forEach((weakBinding) => {
3503
+ if (weakBinding === strongBinding) {
3504
+ newInvalidBindings.add({
3505
+ binding: weakBinding,
3506
+ isStrong: true
3507
+ });
3508
+ } else {
3509
+ newInvalidBindings.add({
3510
+ binding: weakBinding,
3511
+ isStrong: false
3512
+ });
3513
+ }
3514
+ });
3419
3515
  }
3420
- return 0;
3516
+ });
3517
+ if (newInvalidBindings.size > 0) {
3518
+ return newInvalidBindings;
3421
3519
  }
3422
- return aPath > bPath ? 1 : -1;
3423
- });
3424
- }
3425
- return node;
3426
- });
3427
- parser.hooks.parseNode.tap(
3428
- "template",
3429
- (obj, _nodeType, options, childOptions) => {
3430
- if (childOptions && hasTemplateKey(childOptions.key)) {
3431
- return obj.map((template) => {
3432
- const templateAST = parser.createASTNode(
3433
- {
3434
- type: "template" /* Template */,
3435
- depth: options.templateDepth ?? 0,
3436
- data: template.data,
3437
- template: template.value,
3438
- dynamic: template.dynamic ?? false,
3439
- placement: template.placement
3440
- },
3441
- template
3520
+ },
3521
+ { logger: new ProxyLogger(() => this.options?.logger) }
3522
+ )
3523
+ ];
3524
+ }
3525
+ getValidationProviders() {
3526
+ if (this.providers) {
3527
+ return this.providers;
3528
+ }
3529
+ this.providers = this.hooks.resolveValidationProviders.call([
3530
+ {
3531
+ source: SCHEMA_VALIDATION_PROVIDER_NAME,
3532
+ provider: this.schema
3533
+ },
3534
+ {
3535
+ source: VIEW_VALIDATION_PROVIDER_NAME,
3536
+ provider: {
3537
+ getValidationsForBinding: (binding) => {
3538
+ return this.viewValidationProvider?.getValidationsForBinding?.(
3539
+ binding
3442
3540
  );
3443
- if (!templateAST)
3444
- return;
3445
- if (templateAST.type === "multi-node" /* MultiNode */) {
3446
- templateAST.values.forEach((v) => {
3447
- v.parent = templateAST;
3448
- });
3541
+ },
3542
+ getValidationsForView: () => {
3543
+ return this.viewValidationProvider?.getValidationsForView?.();
3544
+ }
3545
+ }
3546
+ }
3547
+ ]);
3548
+ return this.providers;
3549
+ }
3550
+ reset() {
3551
+ this.validations.clear();
3552
+ this.tracker = void 0;
3553
+ }
3554
+ onView(view) {
3555
+ this.validations.clear();
3556
+ if (!this.options) {
3557
+ return;
3558
+ }
3559
+ const bindingTrackerPlugin = new ValidationBindingTrackerViewPlugin({
3560
+ ...this.options,
3561
+ callbacks: {
3562
+ onAdd: (binding) => {
3563
+ if (!this.options || this.getValidationForBinding(binding) !== void 0) {
3564
+ return;
3565
+ }
3566
+ const originalValue = this.options.model.get(binding);
3567
+ const withoutDefault = this.options.model.get(binding, {
3568
+ ignoreDefaultValue: true
3569
+ });
3570
+ if (originalValue !== withoutDefault) {
3571
+ this.options.model.set([[binding, originalValue]], {
3572
+ silent: true
3573
+ });
3574
+ }
3575
+ this.updateValidationsForBinding(
3576
+ binding,
3577
+ "load",
3578
+ this.options,
3579
+ () => {
3580
+ view.update(/* @__PURE__ */ new Set([binding]));
3449
3581
  }
3450
- return {
3451
- path: [...childOptions.path, template.output],
3452
- value: templateAST
3453
- };
3454
- }).filter(Boolean);
3582
+ );
3583
+ this.hooks.onTrackBinding.call(binding);
3455
3584
  }
3456
3585
  }
3457
- );
3458
- }
3459
- applyResolverHooks(resolver) {
3460
- resolver.hooks.beforeResolve.tap("template", (node, options) => {
3461
- if (node && node.type === "template" /* Template */ && node.dynamic) {
3462
- return this.parseTemplate(options.parseNode, node, options);
3463
- }
3464
- return node;
3465
3586
  });
3587
+ this.tracker = bindingTrackerPlugin;
3588
+ this.viewValidationProvider = view;
3589
+ bindingTrackerPlugin.apply(view);
3466
3590
  }
3467
- apply(view) {
3468
- view.hooks.parser.tap("template", this.applyParser.bind(this));
3469
- view.hooks.resolver.tap("template", this.applyResolverHooks.bind(this));
3470
- view.setTemplatePlugin(this);
3471
- }
3472
- };
3473
-
3474
- // ../../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/core/player/src/view/plugins/string-resolver.ts
3475
- import { set } from "timm";
3476
- var createPatternMatcher = (start, end) => {
3477
- return (testStr) => {
3478
- const startLocation = testStr.indexOf(start);
3479
- if (startLocation === -1) {
3480
- return false;
3591
+ updateValidationsForBinding(binding, trigger, validationContext, onDismiss) {
3592
+ const context = validationContext ?? this.options;
3593
+ if (!context) {
3594
+ throw new Error(`Context is required for executing validations`);
3481
3595
  }
3482
- const endLocation = testStr.indexOf(end);
3483
- if (endLocation === -1) {
3484
- return false;
3596
+ if (trigger === "load") {
3597
+ const possibleValidations = this.getValidationProviders().reduce((vals, provider) => {
3598
+ vals.push(
3599
+ ...provider.provider.getValidationsForBinding?.(binding)?.map((valObj) => ({
3600
+ ...valObj,
3601
+ [VALIDATION_PROVIDER_NAME_SYMBOL]: provider.source
3602
+ })) ?? []
3603
+ );
3604
+ return vals;
3605
+ }, []);
3606
+ if (possibleValidations.length === 0) {
3607
+ return;
3608
+ }
3609
+ this.validations.set(
3610
+ binding,
3611
+ new ValidatedBinding(
3612
+ possibleValidations,
3613
+ onDismiss,
3614
+ this.options?.logger
3615
+ )
3616
+ );
3617
+ }
3618
+ const trackedValidations = this.validations.get(binding);
3619
+ trackedValidations?.update(trigger, true, (validationObj) => {
3620
+ const response = this.validationRunner(validationObj, binding, context);
3621
+ if (this.weakBindingTracker.size > 0) {
3622
+ const t2 = this.validations.get(binding);
3623
+ this.weakBindingTracker.forEach((b) => t2.weakBindings.add(b));
3624
+ }
3625
+ return response ? { message: response.message } : void 0;
3626
+ });
3627
+ if (trigger !== "load") {
3628
+ this.validations.forEach((validation, vBinding) => {
3629
+ if (vBinding !== binding && caresAboutDataChanges(/* @__PURE__ */ new Set([binding]), validation.weakBindings)) {
3630
+ validation.update(trigger, true, (validationObj) => {
3631
+ const response = this.validationRunner(
3632
+ validationObj,
3633
+ vBinding,
3634
+ context
3635
+ );
3636
+ return response ? { message: response.message } : void 0;
3637
+ });
3638
+ }
3639
+ });
3485
3640
  }
3486
- return startLocation < endLocation;
3487
- };
3488
- };
3489
- var bindingResolveLookup = createPatternMatcher("{{", "}}");
3490
- var expressionResolveLookup = createPatternMatcher("@[", "]@");
3491
- function hasSomethingToResolve(str) {
3492
- return bindingResolveLookup(str) || expressionResolveLookup(str);
3493
- }
3494
- function resolveString(str, resolveOptions) {
3495
- return hasSomethingToResolve(str) ? resolveDataRefs(str, {
3496
- model: resolveOptions.data.model,
3497
- evaluate: resolveOptions.evaluate
3498
- }) : str;
3499
- }
3500
- function resolveAllRefs(node, resolveOptions, propertiesToSkip) {
3501
- if (node === null || node === void 0 || typeof node !== "object" && typeof node !== "string") {
3502
- return node;
3503
- }
3504
- if (typeof node === "string") {
3505
- return resolveString(node, resolveOptions);
3506
3641
  }
3507
- let newNode = node;
3508
- Object.keys(node).forEach((key) => {
3509
- if (propertiesToSkip.has(key)) {
3510
- return;
3642
+ validationRunner(validationObj, binding, context = this.options) {
3643
+ if (!context) {
3644
+ throw new Error("No context provided to validation runner");
3511
3645
  }
3512
- const val = node[key];
3513
- let newVal = val;
3514
- if (typeof val === "object") {
3515
- newVal = resolveAllRefs(val, resolveOptions, propertiesToSkip);
3516
- } else if (typeof val === "string") {
3517
- newVal = resolveString(val, resolveOptions);
3646
+ const handler = validationObj.handler ?? this.getValidator(validationObj.type);
3647
+ const weakBindings = /* @__PURE__ */ new Set();
3648
+ const model = {
3649
+ get(b, options) {
3650
+ weakBindings.add(isBinding(b) ? binding : context.parseBinding(b));
3651
+ return context.model.get(b, { ...options, includeInvalid: true });
3652
+ },
3653
+ set: context.model.set,
3654
+ delete: context.model.delete
3655
+ };
3656
+ const result = handler?.(
3657
+ {
3658
+ ...context,
3659
+ evaluate: (exp, options = { model }) => context.evaluate(exp, options),
3660
+ model,
3661
+ validation: validationObj,
3662
+ schemaType: this.schema.getType(binding)
3663
+ },
3664
+ context.model.get(binding, {
3665
+ includeInvalid: true,
3666
+ formatted: validationObj.dataTarget === "formatted"
3667
+ }),
3668
+ validationObj
3669
+ );
3670
+ this.weakBindingTracker = weakBindings;
3671
+ if (result) {
3672
+ let { message } = result;
3673
+ const { parameters } = result;
3674
+ if (validationObj.message) {
3675
+ message = resolveDataRefs(validationObj.message, {
3676
+ model,
3677
+ evaluate: context.evaluate
3678
+ });
3679
+ if (parameters) {
3680
+ message = replaceParams(message, parameters);
3681
+ }
3682
+ }
3683
+ return {
3684
+ message
3685
+ };
3518
3686
  }
3519
- if (newVal !== val) {
3520
- newNode = set(newNode, key, newVal);
3687
+ }
3688
+ updateValidationsForView(trigger) {
3689
+ const isNavigationTrigger = trigger === "navigation";
3690
+ const lastActiveBindings = this.activeBindings;
3691
+ const updateValidations = (dismissValidations) => {
3692
+ this.getBindings().forEach((binding) => {
3693
+ this.validations.get(binding)?.update(trigger, dismissValidations, (obj) => {
3694
+ if (!this.options) {
3695
+ return;
3696
+ }
3697
+ return this.validationRunner(obj, binding, this.options);
3698
+ });
3699
+ });
3700
+ };
3701
+ updateValidations(!isNavigationTrigger);
3702
+ if (isNavigationTrigger) {
3703
+ const { activeBindings } = this;
3704
+ if (isSubset(activeBindings, lastActiveBindings)) {
3705
+ updateValidations(true);
3706
+ }
3521
3707
  }
3522
- });
3523
- return newNode;
3524
- }
3525
- var findBasePath = (node, resolver) => {
3526
- const parentNode = node.parent;
3527
- if (!parentNode) {
3528
- return [];
3529
3708
  }
3530
- if ("children" in parentNode) {
3531
- const original = resolver.getSourceNode(node);
3532
- return parentNode.children?.find((child) => child.value === original)?.path ?? [];
3709
+ get activeBindings() {
3710
+ return new Set(
3711
+ Array.from(this.getBindings()).filter(
3712
+ (b) => this.validations.get(b)?.get() !== void 0
3713
+ )
3714
+ );
3533
3715
  }
3534
- if (parentNode.type !== "multi-node" /* MultiNode */) {
3535
- return [];
3716
+ getValidator(type) {
3717
+ if (this.validatorRegistry) {
3718
+ return this.validatorRegistry.get(type);
3719
+ }
3720
+ const registry = new ValidatorRegistry();
3721
+ this.hooks.createValidatorRegistry.call(registry);
3722
+ this.validatorRegistry = registry;
3723
+ return registry.get(type);
3536
3724
  }
3537
- return findBasePath(parentNode, resolver);
3538
- };
3539
- var StringResolverPlugin = class {
3540
- constructor() {
3541
- this.propertiesToSkipCache = /* @__PURE__ */ new Map();
3725
+ getBindings() {
3726
+ return this.tracker?.getBindings() ?? /* @__PURE__ */ new Set();
3542
3727
  }
3543
- applyResolver(resolver) {
3544
- resolver.hooks.resolve.tap("string-resolver", (value, node, options) => {
3545
- if (node.type === "empty" /* Empty */ || node.type === "unknown" /* Unknown */) {
3546
- return null;
3547
- }
3548
- if (node.type === "value" /* Value */ || node.type === "asset" /* Asset */ || node.type === "view" /* View */) {
3549
- let propsToSkip;
3550
- if (node.type === "asset" /* Asset */ || node.type === "view" /* View */) {
3551
- propsToSkip = new Set(
3552
- node.plugins?.stringResolver?.propertiesToSkip ?? ["exp"]
3553
- );
3554
- if (node.value?.id) {
3555
- this.propertiesToSkipCache.set(node.value.id, propsToSkip);
3556
- }
3557
- } else if (node.parent?.type === "multi-node" /* MultiNode */ && (node.parent?.parent?.type === "asset" /* Asset */ || node.parent?.parent?.type === "view" /* View */) && node.parent.parent.value?.id && this.propertiesToSkipCache.has(node.parent.parent.value.id)) {
3558
- propsToSkip = this.propertiesToSkipCache.get(
3559
- node.parent.parent.value.id
3728
+ trackBinding(binding) {
3729
+ this.tracker?.trackBinding(binding);
3730
+ }
3731
+ /** Executes all known validations for the tracked bindings using the given model */
3732
+ validateView(trigger = "navigation") {
3733
+ this.updateValidationsForView(trigger);
3734
+ const validations = /* @__PURE__ */ new Map();
3735
+ let canTransition = true;
3736
+ this.getBindings().forEach((b) => {
3737
+ const allValidations = this.getValidationForBinding(b)?.getAll();
3738
+ allValidations?.forEach((v) => {
3739
+ if (trigger === "navigation" && v.blocking) {
3740
+ this.options?.logger.debug(
3741
+ `Validation on binding: ${b.asString()} is preventing navigation. ${JSON.stringify(
3742
+ v
3743
+ )}`
3560
3744
  );
3561
- } else {
3562
- propsToSkip = /* @__PURE__ */ new Set(["exp"]);
3745
+ canTransition = false;
3563
3746
  }
3564
- const nodePath = findBasePath(node, resolver);
3565
- if (nodePath.length > 0 && nodePath.some((segment) => propsToSkip.has(segment.toString()))) {
3566
- return node.value;
3747
+ if (!validations.has(b)) {
3748
+ validations.set(b, v);
3567
3749
  }
3568
- return resolveAllRefs(node.value, options, propsToSkip);
3569
- }
3570
- return value;
3750
+ });
3571
3751
  });
3752
+ return {
3753
+ canTransition,
3754
+ validations: validations.size ? validations : void 0
3755
+ };
3572
3756
  }
3573
- apply(view) {
3574
- view.hooks.resolver.tap("string-resolver", this.applyResolver.bind(this));
3575
- }
3576
- };
3577
-
3578
- // ../../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/core/player/src/view/plugins/applicability.ts
3579
- import { omit as omit2 } from "timm";
3580
- var ApplicabilityPlugin = class {
3581
- isApplicability(obj) {
3582
- return obj && Object.prototype.hasOwnProperty.call(obj, "applicability");
3757
+ /** Get the current tracked validation for the given binding */
3758
+ getValidationForBinding(binding) {
3759
+ return this.validations.get(binding);
3583
3760
  }
3584
- applyResolver(resolver) {
3585
- resolver.hooks.beforeResolve.tap(
3586
- "applicability",
3587
- (node, options) => {
3588
- let newNode = node;
3589
- if (node?.type === "applicability" /* Applicability */) {
3590
- const isApplicable = options.evaluate(node.expression);
3591
- if (isApplicable === false) {
3592
- return null;
3593
- }
3594
- newNode = node.value;
3761
+ forView(parser) {
3762
+ return {
3763
+ _getValidationForBinding: (binding) => {
3764
+ return this.getValidationForBinding(
3765
+ isBinding(binding) ? binding : parser(binding)
3766
+ );
3767
+ },
3768
+ getAll: () => {
3769
+ const bindings = this.getBindings();
3770
+ if (bindings.size === 0) {
3771
+ return void 0;
3595
3772
  }
3596
- return newNode;
3597
- }
3598
- );
3599
- }
3600
- applyParser(parser) {
3601
- parser.hooks.parseNode.tap(
3602
- "applicability",
3603
- (obj, nodeType, options, childOptions) => {
3604
- if (this.isApplicability(obj)) {
3605
- const parsedApplicability = parser.parseObject(
3606
- omit2(obj, "applicability"),
3607
- nodeType,
3608
- options
3609
- );
3610
- if (!parsedApplicability) {
3611
- return childOptions ? [] : null;
3612
- }
3613
- const applicabilityNode = parser.createASTNode(
3614
- {
3615
- type: "applicability" /* Applicability */,
3616
- expression: obj.applicability,
3617
- value: parsedApplicability
3618
- },
3619
- obj
3620
- );
3621
- if (!applicabilityNode) {
3622
- return childOptions ? [] : null;
3623
- }
3624
- if (applicabilityNode.type === "applicability" /* Applicability */) {
3625
- applicabilityNode.value.parent = applicabilityNode;
3773
+ const validationMapping = /* @__PURE__ */ new Map();
3774
+ bindings.forEach((b) => {
3775
+ const validation = this.getValidationForBinding(b)?.get();
3776
+ if (validation) {
3777
+ validationMapping.set(b, validation);
3626
3778
  }
3627
- return childOptions ? [
3628
- {
3629
- path: [...childOptions.path, childOptions.key],
3630
- value: applicabilityNode
3631
- }
3632
- ] : applicabilityNode;
3633
- }
3634
- }
3635
- );
3636
- }
3637
- apply(view) {
3638
- view.hooks.resolver.tap("applicability", this.applyResolver.bind(this));
3639
- view.hooks.parser.tap("applicability", this.applyParser.bind(this));
3779
+ });
3780
+ return validationMapping.size === 0 ? void 0 : validationMapping;
3781
+ },
3782
+ get() {
3783
+ throw new Error("Error Access be provided by the view plugin");
3784
+ },
3785
+ getValidationsForBinding() {
3786
+ throw new Error("Error rollup should be provided by the view plugin");
3787
+ },
3788
+ getChildren() {
3789
+ throw new Error("Error rollup should be provided by the view plugin");
3790
+ },
3791
+ getValidationsForSection() {
3792
+ throw new Error("Error rollup should be provided by the view plugin");
3793
+ },
3794
+ track: () => {
3795
+ throw new Error("Tracking should be provided by the view plugin");
3796
+ },
3797
+ register: () => {
3798
+ throw new Error(
3799
+ "Section functionality should be provided by the view plugin"
3800
+ );
3801
+ },
3802
+ type: (binding) => this.schema.getType(isBinding(binding) ? binding : parser(binding))
3803
+ };
3640
3804
  }
3641
3805
  };
3642
3806
 
3643
- // ../../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/core/player/src/view/plugins/switch.ts
3644
- var SwitchPlugin = class {
3645
- constructor(options) {
3646
- this.options = options;
3647
- }
3648
- resolveSwitch(node, options) {
3649
- for (const switchCase of node.cases) {
3650
- const isApplicable = options.evaluate(switchCase.case);
3651
- if (isApplicable) {
3652
- return switchCase.value;
3653
- }
3654
- }
3655
- return EMPTY_NODE;
3656
- }
3657
- isSwitch(obj) {
3658
- return obj && (Object.prototype.hasOwnProperty.call(obj, "dynamicSwitch") || Object.prototype.hasOwnProperty.call(obj, "staticSwitch"));
3659
- }
3660
- applyParser(parser) {
3661
- parser.hooks.onCreateASTNode.tap("switch", (node) => {
3662
- if (node && node.type === "switch" /* Switch */ && !node.dynamic) {
3663
- return this.resolveSwitch(node, this.options);
3664
- }
3665
- return node;
3666
- });
3667
- parser.hooks.parseNode.tap(
3668
- "switch",
3669
- (obj, _nodeType, options, childOptions) => {
3670
- if (this.isSwitch(obj) || childOptions && hasSwitchKey(childOptions.key)) {
3671
- const objToParse = childOptions && hasSwitchKey(childOptions.key) ? { [childOptions.key]: obj } : obj;
3672
- const dynamic = "dynamicSwitch" in objToParse;
3673
- const switchContent = dynamic ? objToParse.dynamicSwitch : objToParse.staticSwitch;
3674
- const cases = switchContent.map(
3675
- (switchCase) => {
3676
- const { case: switchCaseExpr, ...switchBody } = switchCase;
3677
- const value = parser.parseObject(
3678
- switchBody,
3679
- "value" /* Value */,
3680
- options
3681
- );
3682
- if (value) {
3683
- return {
3684
- case: switchCaseExpr,
3685
- value
3686
- };
3687
- }
3688
- return;
3689
- }
3690
- ).filter(Boolean);
3691
- const switchAST = parser.createASTNode(
3692
- {
3693
- type: "switch" /* Switch */,
3694
- dynamic,
3695
- cases
3696
- },
3697
- objToParse
3698
- );
3699
- if (!switchAST || switchAST.type === "empty" /* Empty */) {
3700
- return childOptions ? [] : null;
3701
- }
3702
- if (switchAST.type === "switch" /* Switch */) {
3703
- switchAST.cases.forEach((sCase) => {
3704
- sCase.value.parent = switchAST;
3705
- });
3706
- }
3707
- if (childOptions) {
3708
- let path = [...childOptions.path, childOptions.key];
3709
- let value = switchAST;
3710
- if (switchAST.type === "value" /* Value */ && switchAST.children?.length === 1 && switchAST.value === void 0) {
3711
- const firstChild = switchAST.children[0];
3712
- path = [...path, ...firstChild.path];
3713
- value = firstChild.value;
3714
- }
3715
- return [{ path, value }];
3807
+ // ../../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/core/player/src/controllers/view/controller.ts
3808
+ import { SyncHook as SyncHook6, SyncWaterfallHook as SyncWaterfallHook7 } from "tapable-ts";
3809
+ import queueMicrotask from "queue-microtask";
3810
+ import { Registry } from "@player-ui/partial-match-registry";
3811
+ var mergeSets = (setA, setB) => {
3812
+ return /* @__PURE__ */ new Set([...setA?.values() ?? [], ...setB?.values() ?? []]);
3813
+ };
3814
+ var ViewController = class {
3815
+ constructor(initialViews, options) {
3816
+ this.hooks = {
3817
+ resolveView: new SyncWaterfallHook7(),
3818
+ view: new SyncHook6()
3819
+ };
3820
+ this.transformRegistry = new Registry();
3821
+ this.optimizeUpdates = true;
3822
+ this.viewOptions = options;
3823
+ this.viewMap = initialViews.reduce(
3824
+ (viewMap, view) => {
3825
+ viewMap[view.id] = view;
3826
+ return viewMap;
3827
+ },
3828
+ {}
3829
+ );
3830
+ options.flowController.hooks.flow.tap(
3831
+ "viewController",
3832
+ (flow) => {
3833
+ flow.hooks.transition.tap("viewController", (_oldState, newState) => {
3834
+ if (newState.value.state_type === "VIEW") {
3835
+ this.onView(newState.value);
3836
+ } else {
3837
+ this.currentView = void 0;
3716
3838
  }
3717
- return switchAST;
3839
+ });
3840
+ }
3841
+ );
3842
+ const update = (updates, silent = false) => {
3843
+ if (this.currentView) {
3844
+ if (this.optimizeUpdates) {
3845
+ this.queueUpdate(updates, void 0, silent);
3846
+ } else {
3847
+ this.updateView();
3718
3848
  }
3719
3849
  }
3850
+ };
3851
+ options.model.hooks.onUpdate.tap(
3852
+ "viewController",
3853
+ (updates, updateOptions) => {
3854
+ update(
3855
+ new Set(updates.map((t2) => t2.binding)),
3856
+ updateOptions?.silent ?? false
3857
+ );
3858
+ }
3720
3859
  );
3721
- }
3722
- applyResolver(resolver) {
3723
- resolver.hooks.beforeResolve.tap("switch", (node, options) => {
3724
- if (node && node.type === "switch" /* Switch */ && node.dynamic) {
3725
- return this.resolveSwitch(node, options);
3860
+ options.model.hooks.onDelete.tap("viewController", (binding) => {
3861
+ const parentBinding = binding.parent();
3862
+ const property = binding.key();
3863
+ if (typeof property === "number" && parentBinding) {
3864
+ update(/* @__PURE__ */ new Set([parentBinding]));
3865
+ } else {
3866
+ update(/* @__PURE__ */ new Set([binding]));
3726
3867
  }
3727
- return node;
3728
3868
  });
3869
+ this.viewPlugins = this.createViewPlugins();
3729
3870
  }
3730
- apply(view) {
3731
- view.hooks.parser.tap("switch", this.applyParser.bind(this));
3732
- view.hooks.resolver.tap("switch", this.applyResolver.bind(this));
3871
+ queueUpdate(bindings, nodes, silent = false) {
3872
+ if (!this.pendingUpdate) {
3873
+ this.pendingUpdate = {
3874
+ scheduled: false
3875
+ };
3876
+ }
3877
+ this.pendingUpdate = {
3878
+ ...this.pendingUpdate,
3879
+ changedBindings: mergeSets(this.pendingUpdate.changedBindings, bindings),
3880
+ changedNodes: mergeSets(this.pendingUpdate.changedNodes, nodes)
3881
+ };
3882
+ if (!this.pendingUpdate.scheduled && !silent) {
3883
+ this.pendingUpdate.scheduled = true;
3884
+ queueMicrotask(() => {
3885
+ const { changedBindings, changedNodes } = this.pendingUpdate ?? {};
3886
+ this.pendingUpdate = void 0;
3887
+ this.updateView(changedBindings, changedNodes);
3888
+ });
3889
+ }
3733
3890
  }
3734
- };
3735
-
3736
- // ../../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/core/player/src/view/plugins/multi-node.ts
3737
- var MultiNodePlugin = class {
3738
- applyParser(parser) {
3739
- parser.hooks.parseNode.tap(
3740
- "multi-node",
3741
- (obj, nodeType, options, childOptions) => {
3742
- if ((childOptions === void 0 || !hasTemplateKey(childOptions.key)) && Array.isArray(obj)) {
3743
- const values = obj.map(
3744
- (childVal) => parser.parseObject(childVal, "value" /* Value */, options)
3745
- ).filter((child) => !!child);
3746
- if (!values.length) {
3747
- return [];
3748
- }
3749
- const multiNode = parser.createASTNode(
3750
- {
3751
- type: "multi-node" /* MultiNode */,
3752
- override: childOptions !== void 0 && !hasTemplateValues(childOptions.parentObj, childOptions.key),
3753
- values
3754
- },
3755
- obj
3756
- );
3757
- if (!multiNode) {
3758
- return [];
3759
- }
3760
- if (multiNode.type === "multi-node" /* MultiNode */) {
3761
- multiNode.values.forEach((v) => {
3762
- v.parent = multiNode;
3763
- });
3764
- }
3765
- return childOptions === void 0 ? multiNode : [
3766
- {
3767
- path: [...childOptions.path, childOptions.key],
3768
- value: multiNode
3769
- }
3770
- ];
3771
- }
3772
- }
3773
- );
3891
+ updateView(changedBindings, changedNodes) {
3892
+ try {
3893
+ this.currentView?.update(changedBindings, changedNodes);
3894
+ } catch (exception) {
3895
+ const err = exception instanceof Error ? exception : new Error(String(exception));
3896
+ this.queueUpdate(changedBindings, changedNodes, true);
3897
+ this.viewOptions.errorController.captureError(err);
3898
+ }
3774
3899
  }
3775
- apply(view) {
3776
- view.hooks.parser.tap("multi-node", this.applyParser.bind(this));
3900
+ getViewForRef(viewRef) {
3901
+ if (this.viewMap[viewRef]) {
3902
+ return this.viewMap[viewRef];
3903
+ }
3904
+ const matchingViewId = Object.keys(this.viewMap).find(
3905
+ (possibleViewIdMatch) => viewRef === resolveDataRefsInString(possibleViewIdMatch, {
3906
+ model: this.viewOptions.model,
3907
+ evaluate: this.viewOptions.evaluator.evaluate
3908
+ })
3909
+ );
3910
+ if (matchingViewId && this.viewMap[matchingViewId]) {
3911
+ return this.viewMap[matchingViewId];
3912
+ }
3777
3913
  }
3778
- };
3779
-
3780
- // ../../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/core/player/src/view/plugins/asset.ts
3781
- var AssetPlugin = class {
3782
- applyParser(parser) {
3783
- parser.hooks.parseNode.tap(
3784
- "asset",
3785
- (obj, nodeType, options, childOptions) => {
3786
- if (childOptions?.key === "asset" && typeof obj === "object") {
3787
- const assetAST = parser.parseObject(obj, "asset" /* Asset */, options);
3788
- if (!assetAST) {
3789
- return [];
3790
- }
3791
- return [
3792
- {
3793
- path: [...childOptions.path, childOptions.key],
3794
- value: assetAST
3795
- }
3796
- ];
3797
- }
3798
- }
3914
+ onView(state) {
3915
+ const viewId = state.ref;
3916
+ const source = this.hooks.resolveView.call(
3917
+ this.getViewForRef(viewId),
3918
+ viewId,
3919
+ state
3799
3920
  );
3921
+ if (!source) {
3922
+ throw new Error(`No view with id ${viewId}`);
3923
+ }
3924
+ const view = new ViewInstance(source, this.viewOptions);
3925
+ this.currentView = view;
3926
+ this.applyViewPlugins(view);
3927
+ this.hooks.view.call(view);
3928
+ this.updateView();
3800
3929
  }
3801
- apply(view) {
3802
- view.hooks.parser.tap("asset", this.applyParser.bind(this));
3930
+ applyViewPlugins(view) {
3931
+ for (const plugin of this.viewPlugins) {
3932
+ plugin.apply(view);
3933
+ }
3934
+ }
3935
+ createViewPlugins() {
3936
+ const pluginOptions = toNodeResolveOptions(this.viewOptions);
3937
+ return [
3938
+ new AssetPlugin(),
3939
+ new SwitchPlugin(pluginOptions),
3940
+ new ApplicabilityPlugin(),
3941
+ new AssetTransformCorePlugin(this.transformRegistry),
3942
+ new StringResolverPlugin(),
3943
+ new TemplatePlugin(pluginOptions),
3944
+ new MultiNodePlugin()
3945
+ ];
3946
+ }
3947
+ /** Marks all AST nodes in `nodes` as changed, triggering the view to update and re-resolve these nodes. View updates are triggered asynchronously and many calls to this in a short time will batch into a single update.
3948
+ *
3949
+ * NOTE: In most cases view updates are handled automatically by changes to data or any other built-in functionality that would require a view update. Only call this function if absolutely necessary.
3950
+ */
3951
+ updateViewAST(nodes) {
3952
+ this.queueUpdate(void 0, nodes);
3803
3953
  }
3804
3954
  };
3805
3955
 
@@ -3847,1385 +3997,1645 @@ var LocalStateStore = class {
3847
3997
  localState.push(initialState);
3848
3998
  }
3849
3999
  const value = localState[oldCount];
3850
- return [
3851
- value,
3852
- (newState) => {
3853
- const oldValue = localState[oldCount];
3854
- localState[oldCount] = newState;
3855
- if (oldValue !== newState) {
3856
- this.updateCallback?.();
3857
- }
3858
- }
3859
- ];
3860
- };
3861
- }
3862
- };
3863
-
3864
- // ../../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/core/player/src/view/plugins/asset-transform.ts
3865
- function findUp(node, target) {
3866
- if (node === target) {
3867
- return true;
3868
- }
3869
- if (node.parent) {
3870
- return findUp(node.parent, target);
3871
- }
3872
- return false;
3873
- }
3874
- var AssetTransformCorePlugin = class {
3875
- constructor(registry) {
3876
- this.registry = registry;
3877
- this.stateStore = /* @__PURE__ */ new Map();
3878
- this.beforeResolveSymbol = Symbol("before resolve");
3879
- this.resolveSymbol = Symbol("resolve");
3880
- this.beforeResolveCountSymbol = Symbol("before resolve count");
3881
- this.resolveCountSymbol = Symbol("resolve count");
3882
- }
3883
- apply(view) {
3884
- this.stateStore.clear();
3885
- view.hooks.resolver.tap("asset-transform", (resolver) => {
3886
- let lastUpdatedNode;
3887
- const updateState = (node) => {
3888
- lastUpdatedNode = node;
3889
- view.update(/* @__PURE__ */ new Set());
3890
- };
3891
- const getStore = (node, stepKey) => {
3892
- let store;
3893
- const countKey = stepKey === this.resolveSymbol ? this.resolveCountSymbol : this.beforeResolveCountSymbol;
3894
- const storedState = this.stateStore.get(node);
3895
- if (storedState) {
3896
- store = storedState;
3897
- store.removeKey(countKey);
3898
- } else {
3899
- store = new LocalStateStore(() => {
3900
- updateState(node);
3901
- });
3902
- this.stateStore.set(node, store);
3903
- }
3904
- return {
3905
- useSharedState: (key) => {
3906
- return store.useSharedState(key);
3907
- },
3908
- useLocalState: (initialState) => {
3909
- return store.getLocalStateFunction(
3910
- stepKey,
3911
- countKey
3912
- )(initialState);
3913
- }
3914
- };
3915
- };
3916
- resolver.hooks.beforeResolve.tap("asset-transform", (node, options) => {
3917
- if (node && (node.type === "asset" || node.type === "view")) {
3918
- const transform = this.registry.get(node.value);
3919
- if (transform?.beforeResolve) {
3920
- const store = getStore(
3921
- options.node ?? node,
3922
- this.beforeResolveSymbol
3923
- );
3924
- return transform.beforeResolve(node, options, store);
3925
- }
3926
- }
3927
- return node;
3928
- });
3929
- resolver.hooks.afterUpdate.tap("asset-transform", () => {
3930
- lastUpdatedNode = void 0;
3931
- });
3932
- resolver.hooks.skipResolve.tap("asset-transform", (skip, node) => {
3933
- if (!skip || !lastUpdatedNode) {
3934
- return skip;
3935
- }
3936
- const isParentOfUpdated = findUp(lastUpdatedNode, node);
3937
- const isChildOfUpdated = findUp(node, lastUpdatedNode);
3938
- return !isParentOfUpdated && !isChildOfUpdated;
3939
- });
3940
- resolver.hooks.afterResolve.tap(
3941
- "asset-transform",
3942
- (value, node, options) => {
3943
- if (node.type !== "asset" /* Asset */ && node.type !== "view" /* View */) {
3944
- return value;
3945
- }
3946
- const originalNode = resolver.getSourceNode(node);
3947
- if (!originalNode) {
3948
- return value;
3949
- }
3950
- const transform = this.registry.get(value);
3951
- if (transform?.resolve) {
3952
- const store = getStore(originalNode, this.resolveSymbol);
3953
- return transform?.resolve(value, options, store);
4000
+ return [
4001
+ value,
4002
+ (newState) => {
4003
+ const oldValue = localState[oldCount];
4004
+ localState[oldCount] = newState;
4005
+ if (oldValue !== newState) {
4006
+ this.updateCallback?.();
3954
4007
  }
3955
- return value;
3956
4008
  }
3957
- );
3958
- });
4009
+ ];
4010
+ };
3959
4011
  }
3960
4012
  };
3961
4013
 
3962
- // ../../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/core/player/src/player.ts
3963
- import { setIn as setIn7 } from "timm";
3964
- import deferred from "p-defer";
3965
- import queueMicrotask2 from "queue-microtask";
3966
- import { SyncHook as SyncHook10, SyncWaterfallHook as SyncWaterfallHook11 } from "tapable-ts";
4014
+ // ../../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/core/player/src/controllers/data/controller.ts
4015
+ import { SyncHook as SyncHook7, SyncWaterfallHook as SyncWaterfallHook8, SyncBailHook as SyncBailHook5 } from "tapable-ts";
4016
+ import { dequal } from "dequal";
3967
4017
 
3968
- // ../../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/core/player/src/controllers/flow/flow.ts
3969
- import { SyncBailHook as SyncBailHook4, SyncHook as SyncHook5, SyncWaterfallHook as SyncWaterfallHook7 } from "tapable-ts";
3970
- import defer from "p-defer";
3971
- var FlowInstance = class {
3972
- constructor(id, flow, options) {
3973
- this.isTransitioning = false;
4018
+ // ../../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/core/player/src/controllers/data/utils.ts
4019
+ var ReadOnlyDataController = class {
4020
+ constructor(controller) {
4021
+ this.controller = controller;
4022
+ }
4023
+ get(binding, options) {
4024
+ return this.controller.get(binding, options);
4025
+ }
4026
+ };
4027
+
4028
+ // ../../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/core/player/src/controllers/data/controller.ts
4029
+ var DataController = class {
4030
+ constructor(model, options) {
3974
4031
  this.hooks = {
3975
- beforeStart: new SyncBailHook4(),
3976
- onStart: new SyncHook5(),
3977
- onEnd: new SyncHook5(),
3978
- skipTransition: new SyncBailHook4(),
3979
- beforeTransition: new SyncWaterfallHook7(),
3980
- resolveTransitionNode: new SyncWaterfallHook7(),
3981
- transition: new SyncHook5(),
3982
- afterTransition: new SyncHook5()
4032
+ resolve: new SyncWaterfallHook8(),
4033
+ resolveDataStages: new SyncWaterfallHook8(),
4034
+ // On any set or get of an undefined value, redirect the value to be the default
4035
+ resolveDefaultValue: new SyncBailHook5(),
4036
+ onDelete: new SyncHook7(),
4037
+ onSet: new SyncHook7(),
4038
+ onGet: new SyncHook7(),
4039
+ onUpdate: new SyncHook7(),
4040
+ format: new SyncWaterfallHook8(),
4041
+ deformat: new SyncWaterfallHook8(),
4042
+ serialize: new SyncWaterfallHook8()
3983
4043
  };
3984
- this.id = id;
3985
- this.flow = flow;
3986
- this.log = options?.logger;
3987
- this.history = [];
3988
- this.hooks.transition.tap(
3989
- "startPromise",
3990
- async (_oldState, nextState) => {
3991
- const newState = nextState.value;
3992
- if (this.flowPromise && newState.state_type === "END") {
3993
- this.flowPromise.resolve(newState);
3994
- }
3995
- }
3996
- );
4044
+ this.logger = options.logger;
4045
+ const middleware = options.middleware || [];
4046
+ this.baseMiddleware = [new LocalModel(model), ...middleware];
4047
+ this.trash = /* @__PURE__ */ new Set();
4048
+ this.pathResolver = options.pathResolver;
3997
4049
  }
3998
- /** Start the state machine */
3999
- async start() {
4000
- if (this.flowPromise) {
4001
- this.log?.warn("Already called start for flow");
4002
- return this.flowPromise.promise;
4003
- }
4004
- this.flow = this.hooks.beforeStart.call(this.flow) || this.flow;
4005
- if (this.flow.onStart) {
4006
- this.hooks.onStart.call(this.flow.onStart);
4007
- }
4008
- const initialState = this.flow.startState;
4009
- if (!initialState) {
4010
- return Promise.reject(new Error("No 'startState' defined for flow"));
4050
+ getModel() {
4051
+ if (!this.model) {
4052
+ const stages = this.hooks.resolveDataStages.call(this.baseMiddleware);
4053
+ const model = new PipelinedDataModel();
4054
+ model.setMiddleware(stages);
4055
+ this.model = model;
4011
4056
  }
4012
- this.flowPromise = defer();
4013
- this.pushHistory(initialState);
4014
- return this.flowPromise.promise;
4057
+ return this.model;
4015
4058
  }
4016
- transition(transitionValue, options) {
4017
- if (this.isTransitioning) {
4018
- throw new Error(
4019
- `Transitioning while ongoing transition from ${this.currentState?.name} is in progress is not supported`
4020
- );
4059
+ resolveDataValue(binding, value, deformat) {
4060
+ if (deformat) {
4061
+ return this.hooks.deformat.call(value, binding);
4021
4062
  }
4022
- if (this.currentState?.value.state_type === "END") {
4023
- this.log?.warn(
4024
- `Skipping transition using ${transitionValue}. Already at and END state`
4063
+ return value;
4064
+ }
4065
+ set(transaction, options) {
4066
+ let normalizedTransaction = [];
4067
+ if (Array.isArray(transaction)) {
4068
+ normalizedTransaction = transaction.map(([binding, value]) => {
4069
+ const parsed = this.pathResolver.parse(binding);
4070
+ return [
4071
+ parsed,
4072
+ this.resolveDataValue(parsed, value, Boolean(options?.formatted))
4073
+ ];
4074
+ });
4075
+ } else {
4076
+ normalizedTransaction = Object.keys(transaction).map(
4077
+ (binding) => {
4078
+ const parsed = this.pathResolver.parse(binding);
4079
+ const val = transaction[binding];
4080
+ return [
4081
+ parsed,
4082
+ this.resolveDataValue(parsed, val, Boolean(options?.formatted))
4083
+ ];
4084
+ }
4025
4085
  );
4026
- return;
4027
- }
4028
- if (this.currentState === void 0) {
4029
- throw new Error("Cannot transition when there's no current state");
4030
4086
  }
4031
- if (options?.force) {
4032
- this.log?.debug(`Forced transition. Skipping validation checks`);
4033
- } else {
4034
- const skipTransition = this.hooks.skipTransition.call(this.currentState);
4035
- if (skipTransition) {
4036
- this.log?.debug(
4037
- `Skipping transition from ${this.currentState.name} b/c hook told us to`
4087
+ const setUpdates = normalizedTransaction.reduce(
4088
+ (updates, [binding, newVal]) => {
4089
+ const oldVal = this.get(binding, { includeInvalid: true });
4090
+ const update = {
4091
+ binding,
4092
+ newValue: newVal,
4093
+ oldValue: oldVal
4094
+ };
4095
+ if (dequal(oldVal, newVal)) {
4096
+ this.logger?.debug(
4097
+ `Skipping update for path: ${binding.asString()}. Value was unchanged: ${oldVal}`
4098
+ );
4099
+ } else {
4100
+ updates.push(update);
4101
+ this.logger?.debug(
4102
+ `Setting path: ${binding.asString()} from: ${oldVal} to: ${newVal}`
4103
+ );
4104
+ }
4105
+ return updates;
4106
+ },
4107
+ []
4108
+ );
4109
+ const result = this.getModel().set(normalizedTransaction, options);
4110
+ const setUpdateBindings = new Set(setUpdates.map((su) => su.binding));
4111
+ result.forEach((tr) => {
4112
+ if (!setUpdateBindings.has(tr.binding) && (tr.force === true || !dequal(tr.oldValue, tr.newValue))) {
4113
+ this.logger?.debug(
4114
+ `Path: ${tr.binding.asString()} was changed from: ${tr.oldValue} to: ${tr.newValue}`
4038
4115
  );
4039
- return;
4116
+ setUpdates.push(tr);
4040
4117
  }
4118
+ });
4119
+ this.hooks.onSet.call(normalizedTransaction);
4120
+ if (setUpdates.length > 0) {
4121
+ this.hooks.onUpdate.call(setUpdates, options);
4041
4122
  }
4042
- const state = this.hooks.beforeTransition.call(
4043
- this.currentState.value,
4044
- transitionValue
4045
- );
4046
- if (!("transitions" in state)) {
4047
- throw new Error(`No transitions defined for ${this.currentState.value}`);
4123
+ return result;
4124
+ }
4125
+ resolve(binding, readOnly) {
4126
+ return Array.isArray(binding) || typeof binding === "string" ? this.pathResolver.parse(binding, { readOnly }) : binding;
4127
+ }
4128
+ get(binding, options) {
4129
+ const resolved = binding instanceof BindingInstance ? binding : this.resolve(binding, true);
4130
+ let result = this.getModel().get(resolved, options);
4131
+ if (result === void 0 && !options?.ignoreDefaultValue) {
4132
+ const defaultVal = this.hooks.resolveDefaultValue.call(resolved);
4133
+ if (defaultVal !== result) {
4134
+ result = defaultVal;
4135
+ }
4048
4136
  }
4049
- const { transitions } = state;
4050
- const nextState = transitions[transitionValue] || transitions["*"];
4051
- if (nextState === void 0) {
4052
- this.log?.warn(
4053
- `No transition from ${this.currentState.name} using ${transitionValue} or *`
4054
- );
4055
- return;
4137
+ if (options?.formatted) {
4138
+ result = this.hooks.format.call(result, resolved);
4139
+ } else if (options?.formatted === false) {
4140
+ result = this.hooks.deformat.call(result, resolved);
4056
4141
  }
4057
- this.log?.debug(
4058
- `Transitioning from ${this.currentState.name} to ${nextState} using ${transitionValue} `
4059
- );
4060
- return this.pushHistory(nextState, options);
4142
+ this.hooks.onGet.call(binding, result);
4143
+ return result;
4061
4144
  }
4062
- pushHistory(stateName, options) {
4063
- if (!Object.prototype.hasOwnProperty.call(this.flow, stateName)) {
4064
- throw new Error(`No flow definition for: ${stateName} was found.`);
4065
- }
4066
- let nextState = this.flow[stateName];
4067
- if (!this.flow[stateName] || typeof nextState !== "object" || !("state_type" in nextState)) {
4068
- this.log?.error(`Flow doesn't contain any states named: ${stateName}`);
4069
- return;
4145
+ delete(binding, options) {
4146
+ if (typeof binding !== "string" && !Array.isArray(binding) && !(binding instanceof BindingInstance)) {
4147
+ throw new Error("Invalid arguments: delete expects a data path (string)");
4070
4148
  }
4071
- const prevState = this.currentState;
4072
- this.isTransitioning = true;
4073
- nextState = this.hooks.resolveTransitionNode.call(nextState);
4074
- const newCurrentState = {
4075
- name: stateName,
4076
- value: nextState
4077
- };
4078
- this.currentState = newCurrentState;
4079
- this.history.push(stateName);
4080
- if (newCurrentState.value.state_type === "END" && this.flow.onEnd) {
4081
- this.hooks.onEnd.call(this.flow.onEnd);
4149
+ const resolved = binding instanceof BindingInstance ? binding : this.resolve(binding, false);
4150
+ const parentBinding = resolved.parent();
4151
+ const property = resolved.key();
4152
+ const parentValue = this.get(parentBinding);
4153
+ const existedBeforeDelete = typeof parentValue === "object" && parentValue !== null && Object.prototype.hasOwnProperty.call(parentValue, property);
4154
+ this.getModel().delete(resolved, options);
4155
+ if (existedBeforeDelete && !this.get(resolved)) {
4156
+ this.trash.add(resolved);
4082
4157
  }
4083
- this.hooks.transition.call(prevState, {
4084
- ...newCurrentState
4085
- });
4086
- this.isTransitioning = false;
4087
- this.hooks.afterTransition.call(this);
4158
+ this.hooks.onDelete.call(resolved);
4159
+ }
4160
+ serialize() {
4161
+ return this.hooks.serialize.call(this.get(""));
4162
+ }
4163
+ makeReadOnly() {
4164
+ return new ReadOnlyDataController(this);
4088
4165
  }
4089
4166
  };
4090
4167
 
4091
- // ../../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/core/player/src/controllers/flow/controller.ts
4092
- import { SyncHook as SyncHook6 } from "tapable-ts";
4093
- var FlowController = class {
4094
- constructor(navigation, options) {
4095
- this.hooks = {
4096
- flow: new SyncHook6()
4097
- };
4098
- this.navigation = navigation;
4099
- this.navStack = [];
4100
- this.log = options?.logger;
4101
- this.start = this.start.bind(this);
4102
- this.run = this.run.bind(this);
4103
- this.transition = this.transition.bind(this);
4104
- this.addNewFlow = this.addNewFlow.bind(this);
4168
+ // ../../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/core/player/src/controllers/constants/utils.ts
4169
+ function flatten(obj, roots = [], sep = ".") {
4170
+ return Object.keys(obj).reduce(
4171
+ (memo, prop) => ({
4172
+ // create a new object
4173
+ // include previously returned object
4174
+ ...memo,
4175
+ ...Object.prototype.toString.call(obj[prop]) === "[object Object]" ? (
4176
+ // keep working if value is an object
4177
+ flatten(obj[prop], roots.concat([prop]))
4178
+ ) : (
4179
+ // include current prop and value and prefix prop with the roots
4180
+ { [roots.concat([prop]).join(sep)]: obj[prop] }
4181
+ )
4182
+ }),
4183
+ {}
4184
+ );
4185
+ }
4186
+ function objectToBatchSet(obj) {
4187
+ const flattenedObj = flatten(obj);
4188
+ const batchTxn = [];
4189
+ Object.keys(flattenedObj).forEach((key) => {
4190
+ batchTxn.push([new BindingInstance(key), flattenedObj[key]]);
4191
+ });
4192
+ return batchTxn;
4193
+ }
4194
+
4195
+ // ../../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/core/player/src/controllers/constants/index.ts
4196
+ var ConstantsController = class {
4197
+ constructor() {
4198
+ this.store = /* @__PURE__ */ new Map();
4199
+ this.tempStore = /* @__PURE__ */ new Map();
4105
4200
  }
4106
- /** Navigate to another state in the state-machine */
4107
- transition(stateTransition, options) {
4108
- if (this.current === void 0) {
4109
- throw new Error("Not currently in a flow. Cannot transition.");
4201
+ addConstants(data, namespace) {
4202
+ if (this.store.has(namespace)) {
4203
+ this.store.get(namespace)?.set(objectToBatchSet(data));
4204
+ } else {
4205
+ this.store.set(namespace, new LocalModel(data));
4110
4206
  }
4111
- this.current.transition(stateTransition, options);
4112
4207
  }
4113
- addNewFlow(flow) {
4114
- this.navStack.push(flow);
4115
- this.current = flow;
4116
- this.hooks.flow.call(flow);
4208
+ getConstants(key, namespace, fallback) {
4209
+ const path = new BindingInstance(key);
4210
+ return this.tempStore.get(namespace)?.get(path) ?? this.store.get(namespace)?.get(path) ?? fallback;
4117
4211
  }
4118
- async run(startState) {
4119
- if (!Object.prototype.hasOwnProperty.call(this.navigation, startState)) {
4120
- return Promise.reject(new Error(`No flow defined for: ${startState}`));
4121
- }
4122
- const startFlow = this.navigation[startState];
4123
- if (startFlow === null || typeof startFlow !== "object") {
4124
- return Promise.reject(
4125
- new Error(`Flow: ${startState} needs to be an object`)
4126
- );
4127
- }
4128
- this.log?.debug(`Starting flow: ${startState}`);
4129
- const flow = new FlowInstance(startState, startFlow, { logger: this.log });
4130
- this.addNewFlow(flow);
4131
- flow.hooks.afterTransition.tap("flow-controller", (flowInstance) => {
4132
- if (flowInstance.currentState?.value.state_type === "FLOW") {
4133
- const subflowId = flowInstance.currentState?.value.ref;
4134
- this.log?.debug(`Loading subflow ${subflowId}`);
4135
- this.run(subflowId).then((subFlowEndState) => {
4136
- this.log?.debug(
4137
- `Subflow ended. Using outcome: ${subFlowEndState.outcome}`
4138
- );
4139
- flowInstance.transition(subFlowEndState?.outcome);
4140
- });
4141
- }
4142
- });
4143
- const end = await flow.start();
4144
- this.navStack.pop();
4145
- if (this.navStack.length > 0) {
4146
- const firstItem = 0;
4147
- this.current = this.navStack[firstItem];
4212
+ setTemporaryValues(data, namespace) {
4213
+ if (this.tempStore.has(namespace)) {
4214
+ this.tempStore.get(namespace)?.set(objectToBatchSet(data));
4215
+ } else {
4216
+ this.tempStore.set(namespace, new LocalModel(data));
4148
4217
  }
4149
- return end;
4150
4218
  }
4151
- async start() {
4152
- if (!this.navigation.BEGIN) {
4153
- return Promise.reject(new Error("Must supply a BEGIN state"));
4219
+ clearTemporaryValues(namespace) {
4220
+ if (namespace) {
4221
+ this.tempStore.get(namespace)?.reset();
4222
+ } else {
4223
+ this.tempStore.forEach((value) => {
4224
+ value.reset();
4225
+ });
4154
4226
  }
4155
- return this.run(this.navigation.BEGIN);
4156
4227
  }
4157
4228
  };
4158
4229
 
4159
- // ../../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/core/player/src/controllers/validation/controller.ts
4160
- import { SyncHook as SyncHook7, SyncWaterfallHook as SyncWaterfallHook8 } from "tapable-ts";
4161
- import { setIn as setIn6 } from "timm";
4162
-
4163
- // ../../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/core/player/src/utils/replaceParams.ts
4164
- var ANY_CHAR_REGEX = /%([a-zA-Z]+)/g;
4165
- function replaceParams(message, params) {
4166
- return message.slice().replace(ANY_CHAR_REGEX, (keyExpr) => params[keyExpr.slice(1)] || keyExpr);
4167
- }
4230
+ // ../../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/core/player/src/controllers/error/controller.ts
4231
+ import { SyncBailHook as SyncBailHook6 } from "tapable-ts";
4168
4232
 
4169
- // ../../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/core/player/src/controllers/validation/binding-tracker.ts
4170
- var CONTEXT = "validation-binding-tracker";
4171
- var ValidationBindingTrackerViewPlugin = class {
4233
+ // ../../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/core/player/src/controllers/error/middleware.ts
4234
+ var ERROR_BINDING_PREFIX = "errorState";
4235
+ var isErrorBinding = (binding) => binding.asArray()[0] === ERROR_BINDING_PREFIX;
4236
+ var ErrorStateMiddleware = class {
4172
4237
  constructor(options) {
4173
- this.trackedBindings = /* @__PURE__ */ new Set();
4174
- this.options = options;
4175
- }
4176
- /** Fetch the tracked bindings in the current view */
4177
- getBindings() {
4178
- return this.trackedBindings;
4179
- }
4180
- /** Add a binding to the tracked set */
4181
- trackBinding(binding) {
4182
- if (this.trackedBindings.has(binding)) {
4183
- return;
4184
- }
4185
- this.trackedBindings.add(binding);
4186
- this.options.callbacks?.onAdd?.(binding);
4238
+ this.name = "error-state-middleware";
4239
+ // Internal model for error state to avoid data serialization
4240
+ this.dataModel = new LocalModel();
4241
+ this.logger = options.logger;
4242
+ this.writeSymbol = options.writeSymbol;
4187
4243
  }
4188
- /** Attach hooks to the given resolver */
4189
- applyResolver(resolver) {
4190
- this.trackedBindings.clear();
4191
- const tracked = /* @__PURE__ */ new Map();
4192
- const sections = /* @__PURE__ */ new Map();
4193
- let lastViewUpdateChangeSet;
4194
- const lastComputedBindingTree = /* @__PURE__ */ new Map();
4195
- let currentBindingTree = /* @__PURE__ */ new Map();
4196
- const lastSectionBindingTree = /* @__PURE__ */ new Map();
4197
- const resolvedNodeMap = /* @__PURE__ */ new Map();
4198
- resolver.hooks.beforeUpdate.tap(CONTEXT, (changes) => {
4199
- lastViewUpdateChangeSet = changes;
4200
- });
4201
- resolver.hooks.skipResolve.tap(CONTEXT, (shouldSkip, node) => {
4202
- const trackedBindingsForNode = lastComputedBindingTree.get(node);
4203
- if (!shouldSkip || !lastViewUpdateChangeSet || !trackedBindingsForNode) {
4204
- return shouldSkip;
4205
- }
4206
- const intersection = new Set(
4207
- [...lastViewUpdateChangeSet].filter(
4208
- (b) => trackedBindingsForNode.has(b)
4209
- )
4210
- );
4211
- return intersection.size === 0;
4212
- });
4213
- resolver.hooks.resolveOptions.tap(CONTEXT, (options, node) => {
4214
- if (options.validation === void 0) {
4215
- return options;
4216
- }
4217
- tracked.delete(node);
4218
- const track = (binding) => {
4219
- const parsed = isBinding(binding) ? binding : this.options.parseBinding(binding);
4220
- if (tracked.has(node)) {
4221
- tracked.get(node)?.add(parsed);
4222
- } else {
4223
- tracked.set(node, /* @__PURE__ */ new Set([parsed]));
4224
- }
4225
- let { parent } = node;
4226
- while (parent) {
4227
- if (sections.has(parent)) {
4228
- sections.get(parent)?.add(node);
4229
- break;
4230
- } else {
4231
- parent = parent.parent;
4232
- }
4233
- }
4234
- this.trackedBindings.add(parsed);
4235
- this.options.callbacks?.onAdd?.(parsed);
4236
- };
4237
- return {
4238
- ...options,
4239
- validation: {
4240
- ...options.validation,
4241
- get: (binding, getOptions) => {
4242
- if (getOptions?.track) {
4243
- track(binding);
4244
- }
4245
- const eows = options.validation?._getValidationForBinding(binding)?.getAll(getOptions);
4246
- const firstFieldEOW = eows?.find(
4247
- (eow) => eow.displayTarget === "field" || eow.displayTarget === void 0
4248
- );
4249
- return firstFieldEOW;
4250
- },
4251
- getValidationsForBinding(binding, getOptions) {
4252
- if (getOptions?.track) {
4253
- track(binding);
4254
- }
4255
- return options.validation?._getValidationForBinding(binding)?.getAll(getOptions) ?? [];
4256
- },
4257
- getChildren: (type) => {
4258
- const validations = new Array();
4259
- lastComputedBindingTree.get(node)?.forEach((binding) => {
4260
- const eow = options.validation?._getValidationForBinding(binding)?.get();
4261
- if (eow && (type === void 0 || type === eow.displayTarget)) {
4262
- validations.push(eow);
4263
- }
4264
- });
4265
- return validations;
4266
- },
4267
- getValidationsForSection: () => {
4268
- const validations = new Array();
4269
- lastSectionBindingTree.get(node)?.forEach((binding) => {
4270
- const eow = options.validation?._getValidationForBinding(binding)?.get();
4271
- if (eow && eow.displayTarget === "section") {
4272
- validations.push(eow);
4273
- }
4274
- });
4275
- return validations;
4276
- },
4277
- register: (registerOptions) => {
4278
- if (registerOptions?.type === "section") {
4279
- if (!sections.has(node)) {
4280
- sections.set(node, /* @__PURE__ */ new Set());
4281
- }
4282
- }
4283
- },
4284
- track
4285
- }
4286
- };
4244
+ set(transaction, options, next) {
4245
+ const filteredTransaction = [];
4246
+ const errorTransaction = [];
4247
+ transaction.forEach((transaction2) => {
4248
+ const [binding] = transaction2;
4249
+ const targetArray = isErrorBinding(binding) ? errorTransaction : filteredTransaction;
4250
+ targetArray.push(transaction2);
4287
4251
  });
4288
- resolver.hooks.afterNodeUpdate.tap(
4289
- CONTEXT,
4290
- (originalNode, parent, update) => {
4291
- const { updated, node: resolvedNode } = update;
4292
- resolvedNodeMap.set(resolvedNode, originalNode);
4293
- if (updated) {
4294
- const newlyComputed = new Set(tracked.get(originalNode));
4295
- if (resolvedNode.type === "multi-node" /* MultiNode */) {
4296
- resolvedNode.values.forEach(
4297
- (value) => currentBindingTree.get(value)?.forEach((b) => newlyComputed.add(b))
4298
- );
4299
- }
4300
- if ("children" in resolvedNode && resolvedNode.children) {
4301
- resolvedNode.children.forEach((child) => {
4302
- currentBindingTree.get(child.value)?.forEach((b) => newlyComputed.add(b));
4303
- });
4304
- }
4305
- currentBindingTree.set(resolvedNode, newlyComputed);
4306
- } else {
4307
- currentBindingTree.set(
4308
- resolvedNode,
4309
- lastComputedBindingTree.get(originalNode) ?? /* @__PURE__ */ new Set()
4310
- );
4311
- }
4312
- if (originalNode === resolver.root) {
4313
- this.trackedBindings = new Set(currentBindingTree.get(resolvedNode));
4314
- lastComputedBindingTree.clear();
4315
- currentBindingTree.forEach((value, key) => {
4316
- const node = resolvedNodeMap.get(key);
4317
- if (node) {
4318
- lastComputedBindingTree.set(node, value);
4319
- }
4320
- });
4321
- lastSectionBindingTree.clear();
4322
- sections.forEach((nodeSet, sectionNode) => {
4323
- const temp = /* @__PURE__ */ new Set();
4324
- nodeSet.forEach((n) => {
4325
- tracked.get(n)?.forEach(temp.add, temp);
4326
- });
4327
- lastSectionBindingTree.set(sectionNode, temp);
4328
- });
4329
- tracked.clear();
4330
- sections.clear();
4331
- currentBindingTree = /* @__PURE__ */ new Map();
4332
- }
4333
- }
4334
- );
4252
+ const nonErrorResults = next?.set(filteredTransaction, options) ?? [];
4253
+ const errorResults = options?.writeSymbol === this.writeSymbol ? this.dataModel.set(errorTransaction) : errorTransaction.map((transaction2) => {
4254
+ const [binding] = transaction2;
4255
+ this.logger?.warn(
4256
+ `[ErrorStateMiddleware] Blocked write to protected path: ${binding.asString()}`
4257
+ );
4258
+ const oldValue = next?.get(binding, options);
4259
+ return {
4260
+ binding,
4261
+ oldValue,
4262
+ newValue: oldValue,
4263
+ // Keep old value
4264
+ force: false
4265
+ };
4266
+ });
4267
+ return [...nonErrorResults, ...errorResults];
4335
4268
  }
4336
- apply(view) {
4337
- view.hooks.resolver.tap(CONTEXT, this.applyResolver.bind(this));
4269
+ get(binding, options, next) {
4270
+ return isErrorBinding(binding) ? this.dataModel.get(binding) : next?.get(binding, options);
4271
+ }
4272
+ delete(binding, options, next) {
4273
+ if (!isErrorBinding(binding)) {
4274
+ next?.delete(binding, options);
4275
+ return;
4276
+ }
4277
+ if (options?.writeSymbol !== this.writeSymbol) {
4278
+ this.logger?.warn(
4279
+ `[ErrorStateMiddleware] Blocked delete of protected path: ${binding.asString()}`
4280
+ );
4281
+ return;
4282
+ }
4283
+ this.dataModel.delete(binding);
4338
4284
  }
4339
4285
  };
4340
4286
 
4341
- // ../../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/core/player/src/controllers/validation/controller.ts
4342
- var SCHEMA_VALIDATION_PROVIDER_NAME = "schema";
4343
- var VIEW_VALIDATION_PROVIDER_NAME = "view";
4344
- var VALIDATION_PROVIDER_NAME_SYMBOL = Symbol.for(
4345
- "validation-provider-name"
4346
- );
4347
- function isSubset(subset, containingSet) {
4348
- if (subset.size > containingSet.size)
4287
+ // ../../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/core/player/src/controllers/error/types.ts
4288
+ var ErrorSeverity = /* @__PURE__ */ ((ErrorSeverity2) => {
4289
+ ErrorSeverity2["FATAL"] = "fatal";
4290
+ ErrorSeverity2["ERROR"] = "error";
4291
+ ErrorSeverity2["WARNING"] = "warning";
4292
+ return ErrorSeverity2;
4293
+ })(ErrorSeverity || {});
4294
+ var ErrorTypes = {
4295
+ EXPRESSION: "expression",
4296
+ BINDING: "binding",
4297
+ VIEW: "view",
4298
+ ASSET: "asset",
4299
+ NAVIGATION: "navigation",
4300
+ VALIDATION: "validation",
4301
+ DATA: "data",
4302
+ SCHEMA: "schema",
4303
+ NETWORK: "network",
4304
+ PLUGIN: "plugin",
4305
+ RENDER: "render",
4306
+ EXTERNAL_STATE: "externalState"
4307
+ };
4308
+
4309
+ // ../../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/core/player/src/controllers/error/utils/isErrorWithMetadata.ts
4310
+ var SEVERITY_SET = new Set(Object.values(ErrorSeverity));
4311
+ var isErrorWithMetadata = (error) => {
4312
+ if (!("type" in error) || typeof error.type !== "string") {
4349
4313
  return false;
4350
- for (const entry of subset)
4351
- if (!containingSet.has(entry))
4352
- return false;
4353
- return true;
4354
- }
4355
- function createStatefulValidationObject(obj) {
4356
- return {
4357
- value: obj,
4358
- type: obj.severity,
4359
- state: "none",
4360
- isBlockingNavigation: false
4314
+ }
4315
+ if ("severity" in error && error.severity !== void 0 && (typeof error.severity !== "string" || !SEVERITY_SET.has(error.severity))) {
4316
+ return false;
4317
+ }
4318
+ return !("metadata" in error) || error.metadata === void 0 || typeof error.metadata === "object" && error.metadata !== null && !Array.isArray(error.metadata);
4319
+ };
4320
+
4321
+ // ../../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/core/player/src/controllers/error/utils/makeJsonStringifyReplacer.ts
4322
+ var makeJsonStringifyReplacer = () => {
4323
+ const cache = /* @__PURE__ */ new Set();
4324
+ return (_, value) => {
4325
+ if (typeof value === "object" && value !== null) {
4326
+ if (cache.has(value)) {
4327
+ return "[CIRCULAR]";
4328
+ }
4329
+ cache.add(value);
4330
+ }
4331
+ return value;
4361
4332
  };
4362
- }
4363
- var ValidatedBinding = class {
4364
- constructor(possibleValidations, onDismiss, log, weakBindings) {
4365
- this.applicableValidations = [];
4366
- this.validationsByState = {
4367
- load: [],
4368
- change: [],
4369
- navigation: []
4333
+ };
4334
+
4335
+ // ../../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/core/player/src/controllers/error/controller.ts
4336
+ var errorControllerWriteSymbol = Symbol(
4337
+ "errorControllerWrite"
4338
+ );
4339
+ var ErrorController = class {
4340
+ constructor(options) {
4341
+ this.hooks = {
4342
+ onError: new SyncBailHook6()
4370
4343
  };
4371
- this.onDismiss = onDismiss;
4372
- possibleValidations.forEach((vObj) => {
4373
- const { trigger } = vObj;
4374
- if (this.validationsByState[trigger]) {
4375
- const statefulValidationObject = createStatefulValidationObject(vObj);
4376
- this.validationsByState[trigger].push(statefulValidationObject);
4377
- } else {
4378
- log?.warn(`Unknown validation trigger: ${trigger}`);
4379
- }
4344
+ /**
4345
+ * Complete history of all captured errors in chronological order
4346
+ * Newest errors are APPENDED to the end of the array
4347
+ */
4348
+ this.errorHistory = [];
4349
+ this.options = options;
4350
+ this.middleware = new ErrorStateMiddleware({
4351
+ logger: options.logger,
4352
+ writeSymbol: errorControllerWriteSymbol
4380
4353
  });
4381
- this.weakBindings = weakBindings ?? /* @__PURE__ */ new Set();
4382
4354
  }
4383
- get allValidations() {
4384
- return Object.values(this.validationsByState).flat();
4355
+ /**
4356
+ * Get the middleware for protecting errorState
4357
+ * This should be added to DataController's middleware array
4358
+ */
4359
+ getDataMiddleware() {
4360
+ return this.middleware;
4385
4361
  }
4386
- checkIfBlocking(statefulObj) {
4387
- if (statefulObj.state === "active") {
4388
- const { isBlockingNavigation } = statefulObj;
4389
- return isBlockingNavigation;
4390
- }
4391
- return false;
4362
+ /**
4363
+ * Set the DataController after initialization
4364
+ */
4365
+ setOptions(options) {
4366
+ this.options.model = options.model;
4392
4367
  }
4393
- getAll() {
4394
- return this.applicableValidations.reduce((all, statefulObj) => {
4395
- if (statefulObj.state === "active" && statefulObj.response) {
4396
- all.push({
4397
- ...statefulObj.response,
4398
- blocking: this.checkIfBlocking(statefulObj)
4399
- });
4400
- }
4401
- return all;
4402
- }, []);
4368
+ /**
4369
+ * Capture an error and try to recover. Errors implementing the `PlayerErrorMetadata` interface will be added to history, fire hooks and update data model. As a fallback, all errors will try to trigger an errorTransition. If the error does not have a `type` property, it will default to only using the wildcard navigation.
4370
+ */
4371
+ captureError(error) {
4372
+ if (!isErrorWithMetadata(error)) {
4373
+ this.options.logger.debug(
4374
+ `[ErrorController] Captured error: ${error.message}
4375
+ `,
4376
+ "Cannot determine optional error metadata, attempting default error navigation..."
4377
+ );
4378
+ return this.tryNavigateToErrorState(error, "*");
4379
+ }
4380
+ this.errorHistory.push(error);
4381
+ this.currentError = error;
4382
+ this.options.logger.debug(
4383
+ `[ErrorController] Captured error: ${error.message}`,
4384
+ JSON.stringify(
4385
+ {
4386
+ errorType: error.type,
4387
+ severity: error.severity,
4388
+ metadata: error.metadata
4389
+ },
4390
+ makeJsonStringifyReplacer()
4391
+ )
4392
+ );
4393
+ const shouldSkip = this.hooks.onError.call(error) ?? false;
4394
+ if (shouldSkip) {
4395
+ this.options.logger.debug(
4396
+ "[ErrorController] Error state navigation skipped by plugin"
4397
+ );
4398
+ return true;
4399
+ }
4400
+ this.setErrorInDataModel(error);
4401
+ return this.tryNavigateToErrorState(error, error.type);
4403
4402
  }
4404
- get() {
4405
- const firstInvalid = this.applicableValidations.find((statefulObj) => {
4406
- return statefulObj.state === "active" && statefulObj.response;
4407
- });
4408
- if (firstInvalid?.state === "active") {
4409
- return {
4410
- ...firstInvalid.response,
4411
- blocking: this.checkIfBlocking(firstInvalid)
4412
- };
4403
+ /**
4404
+ * Navigate to error state using errorTransitions.
4405
+ * Uses errorTransition() which handles node-level and flow-level fallback internally.
4406
+ */
4407
+ tryNavigateToErrorState(error, transition) {
4408
+ const flowInstance = this.options.flow.current;
4409
+ if (!flowInstance) {
4410
+ this.options.logger.warn(
4411
+ "[ErrorController] No active flow instance for error navigation"
4412
+ );
4413
+ return false;
4414
+ }
4415
+ if (flowInstance.getErrorTransitionState(transition) === void 0) {
4416
+ this.options.fail(error);
4417
+ return false;
4418
+ }
4419
+ try {
4420
+ flowInstance.errorTransition(transition);
4421
+ return true;
4422
+ } catch (e) {
4423
+ this.options.logger.error(
4424
+ `[ErrorController] Error transition failed with unexpected error: ${e}`
4425
+ );
4426
+ this.options.logger.debug("[ErrorController] Rejecting flow with error");
4427
+ this.options.fail(error);
4428
+ return false;
4413
4429
  }
4414
4430
  }
4415
- runApplicableValidations(runner, canDismiss, phase) {
4416
- this.applicableValidations = this.applicableValidations.map(
4417
- (originalValue) => {
4418
- if (originalValue.state === "dismissed") {
4419
- return originalValue;
4420
- }
4421
- const blocking = originalValue.value.blocking ?? (originalValue.value.severity === "warning" && "once" || true);
4422
- const obj = setIn6(
4423
- originalValue,
4424
- ["value", "blocking"],
4425
- blocking
4426
- );
4427
- const isBlockingNavigation = blocking === true || blocking === "once" && !canDismiss;
4428
- if (phase === "navigation" && obj.state === "active" && obj.value.blocking !== true) {
4429
- if (obj.value.severity === "warning") {
4430
- const warn = obj;
4431
- if (warn.dismissable && warn.response.dismiss && (warn.response.blocking !== "once" || !warn.response.blocking)) {
4432
- warn.response.dismiss();
4433
- } else {
4434
- if (warn?.response.blocking === "once") {
4435
- warn.response.blocking = false;
4436
- }
4437
- warn.dismissable = true;
4438
- }
4439
- return warn;
4440
- }
4441
- }
4442
- const response = runner(obj.value);
4443
- const newState = {
4444
- type: obj.type,
4445
- value: obj.value,
4446
- state: response ? "active" : "none",
4447
- isBlockingNavigation,
4448
- dismissable: obj.value.severity === "warning" && phase === "navigation",
4449
- response: response ? {
4450
- ...obj.value,
4451
- message: response.message ?? "Something is broken",
4452
- severity: obj.value.severity,
4453
- displayTarget: obj.value.displayTarget ?? "field"
4454
- } : void 0
4455
- };
4456
- if (newState.state === "active" && obj.value.severity === "warning") {
4457
- newState.response.dismiss = () => {
4458
- newState.state = "dismissed";
4459
- this.onDismiss?.();
4460
- };
4461
- }
4462
- return newState;
4463
- }
4464
- );
4431
+ /**
4432
+ * Get most recent error
4433
+ */
4434
+ getCurrentError() {
4435
+ return this.currentError;
4465
4436
  }
4466
- update(phase, canDismiss, runner) {
4467
- const newApplicableValidations = [];
4468
- if (phase === "load" && this.currentPhase !== void 0) {
4437
+ /**
4438
+ * Get error history (read-only)
4439
+ */
4440
+ getErrors() {
4441
+ return this.errorHistory;
4442
+ }
4443
+ /**
4444
+ * Clear all errors (history + current + data model)
4445
+ */
4446
+ clearErrors() {
4447
+ this.errorHistory = [];
4448
+ this.currentError = void 0;
4449
+ this.deleteErrorFromDataModel();
4450
+ this.options.logger.debug("[ErrorController] All errors cleared");
4451
+ }
4452
+ /**
4453
+ * Clear only current error and remove from data model, preserve history
4454
+ */
4455
+ clearCurrentError() {
4456
+ this.currentError = void 0;
4457
+ this.deleteErrorFromDataModel();
4458
+ this.options.logger.debug("[ErrorController] Current error cleared");
4459
+ }
4460
+ /**
4461
+ * Write error to data model errorState
4462
+ */
4463
+ setErrorInDataModel(playerError) {
4464
+ if (!this.options.model) {
4465
+ this.options.logger.warn("[ErrorController] No DataController available");
4469
4466
  return;
4470
4467
  }
4471
- if (this.currentPhase === "navigation" || phase === this.currentPhase) {
4472
- this.runApplicableValidations(runner, canDismiss, phase);
4468
+ try {
4469
+ const { type, severity, metadata, name, message } = playerError;
4470
+ this.options.model.set(
4471
+ [
4472
+ [
4473
+ "errorState",
4474
+ {
4475
+ message,
4476
+ name,
4477
+ errorType: type,
4478
+ severity,
4479
+ ...metadata
4480
+ }
4481
+ ]
4482
+ ],
4483
+ { writeSymbol: errorControllerWriteSymbol }
4484
+ );
4485
+ this.options.logger.debug(
4486
+ "[ErrorController] Error set in data model at 'data.errorState'"
4487
+ );
4488
+ } catch (e) {
4489
+ this.options.logger.error(
4490
+ "[ErrorController] Failed to set error in data model",
4491
+ e
4492
+ );
4493
+ }
4494
+ }
4495
+ /**
4496
+ * Remove errorState from data model
4497
+ */
4498
+ deleteErrorFromDataModel() {
4499
+ if (!this.options.model) {
4473
4500
  return;
4474
4501
  }
4475
- if (phase === "load") {
4476
- this.currentPhase = "load";
4477
- this.applicableValidations = [...this.validationsByState.load];
4478
- } else if (phase === "change" && this.currentPhase === "load") {
4479
- this.currentPhase = "change";
4480
- this.applicableValidations = [
4481
- ...this.applicableValidations,
4482
- ...this.validationsByState.change
4483
- ];
4484
- } else if (phase === "navigation" && (this.currentPhase === "load" || this.currentPhase === "change")) {
4485
- this.applicableValidations.forEach((element) => {
4486
- if (!(element.type === "error" && element.state === "active" && element.isBlockingNavigation === false)) {
4487
- newApplicableValidations.push(element);
4488
- }
4502
+ try {
4503
+ this.options.model.delete("errorState", {
4504
+ writeSymbol: errorControllerWriteSymbol
4505
+ });
4506
+ this.options.logger.debug(
4507
+ "[ErrorController] errorState deleted from data model"
4508
+ );
4509
+ } catch (e) {
4510
+ this.options.logger.error(
4511
+ "[ErrorController] Failed to delete errorState from data model",
4512
+ e
4513
+ );
4514
+ }
4515
+ }
4516
+ };
4517
+
4518
+ // ../../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/core/player/src/view/resolver/ResolverError.ts
4519
+ var ResolverError = class extends Error {
4520
+ constructor(cause, stage, node) {
4521
+ super(`An error in the resolver occurred at stage '${stage}'`);
4522
+ this.cause = cause;
4523
+ this.stage = stage;
4524
+ this.type = ErrorTypes.VIEW;
4525
+ this.severity = "error" /* ERROR */;
4526
+ this.metadata = {
4527
+ node
4528
+ };
4529
+ }
4530
+ };
4531
+
4532
+ // ../../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/core/player/src/view/resolver/index.ts
4533
+ var withContext = (model) => {
4534
+ return {
4535
+ get: (binding, options) => {
4536
+ return model.get(binding, {
4537
+ context: { model },
4538
+ ...options
4539
+ });
4540
+ },
4541
+ set: (transaction, options) => {
4542
+ return model.set(transaction, {
4543
+ context: { model },
4544
+ ...options
4545
+ });
4546
+ },
4547
+ delete: (binding, options) => {
4548
+ return model.delete(binding, {
4549
+ context: { model },
4550
+ ...options
4489
4551
  });
4490
- this.applicableValidations = [
4491
- ...newApplicableValidations,
4492
- ...this.validationsByState.navigation,
4493
- ...this.currentPhase === "load" ? this.validationsByState.change : []
4494
- ];
4495
- this.currentPhase = "navigation";
4496
4552
  }
4497
- this.runApplicableValidations(runner, canDismiss, phase);
4498
- }
4553
+ };
4499
4554
  };
4500
- var ValidationController = class {
4501
- constructor(schema, options) {
4555
+ var Resolver = class {
4556
+ constructor(root, options) {
4502
4557
  this.hooks = {
4503
- /** A hook called to tap into the validator registry for adding more validators */
4504
- createValidatorRegistry: new SyncHook7(),
4505
- /** A callback/event when a new validation is added to the view */
4506
- onAddValidation: new SyncWaterfallHook8(),
4507
- /** The inverse of onAddValidation, this is called when a validation is removed from the list */
4508
- onRemoveValidation: new SyncWaterfallHook8(),
4509
- resolveValidationProviders: new SyncWaterfallHook8(),
4510
- /** A hook called when a binding is added to the tracker */
4511
- onTrackBinding: new SyncHook7()
4558
+ skipResolve: new SyncWaterfallHook9(),
4559
+ beforeUpdate: new SyncHook8(),
4560
+ afterUpdate: new SyncHook8(),
4561
+ resolveOptions: new SyncWaterfallHook9(),
4562
+ beforeResolve: new SyncWaterfallHook9(),
4563
+ resolve: new SyncWaterfallHook9(),
4564
+ afterResolve: new SyncWaterfallHook9(),
4565
+ afterNodeUpdate: new SyncHook8()
4512
4566
  };
4513
- this.validations = /* @__PURE__ */ new Map();
4514
- this.weakBindingTracker = /* @__PURE__ */ new Set();
4515
- this.schema = schema;
4567
+ this.root = root;
4516
4568
  this.options = options;
4517
- this.reset();
4569
+ this.resolveCache = /* @__PURE__ */ new Map();
4570
+ this.ASTMap = /* @__PURE__ */ new Map();
4571
+ this.logger = options.logger;
4572
+ this.idCache = /* @__PURE__ */ new Set();
4518
4573
  }
4519
- setOptions(options) {
4520
- this.options = options;
4574
+ getSourceNode(convertedAST) {
4575
+ return this.ASTMap.get(convertedAST);
4521
4576
  }
4522
- /** Return the middleware for the data-model to stop propagation of invalid data */
4523
- getDataMiddleware() {
4524
- return [
4525
- {
4526
- set: (transaction, options, next) => {
4527
- return next?.set(transaction, options) ?? [];
4528
- },
4529
- get: (binding, options, next) => {
4530
- return next?.get(binding, options);
4531
- },
4532
- delete: (binding, options, next) => {
4533
- this.validations = removeBindingAndChildrenFromMap(
4534
- this.validations,
4535
- binding
4536
- );
4537
- return next?.delete(binding, options);
4577
+ update(dataChanges, nodeChanges) {
4578
+ this.hooks.beforeUpdate.call(dataChanges);
4579
+ const resolveCache = /* @__PURE__ */ new Map();
4580
+ this.idCache.clear();
4581
+ const prevASTMap = new Map(this.ASTMap);
4582
+ this.ASTMap.clear();
4583
+ const realNodeChanges = /* @__PURE__ */ new Set();
4584
+ for (const node of nodeChanges?.values() ?? []) {
4585
+ let current = node;
4586
+ while (current) {
4587
+ const original = prevASTMap.get(current) ?? current;
4588
+ if (realNodeChanges.has(original)) {
4589
+ break;
4538
4590
  }
4539
- },
4540
- new ValidationMiddleware(
4541
- (binding) => {
4542
- if (!this.options) {
4543
- return;
4544
- }
4545
- this.updateValidationsForBinding(binding, "change", this.options);
4546
- const strongValidation = this.getValidationForBinding(binding);
4547
- if (strongValidation?.get()?.severity === "error") {
4548
- return strongValidation.get();
4549
- }
4550
- const newInvalidBindings = /* @__PURE__ */ new Set();
4551
- this.validations.forEach((weakValidation, strongBinding) => {
4552
- if (caresAboutDataChanges(
4553
- /* @__PURE__ */ new Set([binding]),
4554
- weakValidation.weakBindings
4555
- ) && weakValidation?.get()?.severity === "error") {
4556
- weakValidation?.weakBindings.forEach((weakBinding) => {
4557
- if (weakBinding === strongBinding) {
4558
- newInvalidBindings.add({
4559
- binding: weakBinding,
4560
- isStrong: true
4561
- });
4562
- } else {
4563
- newInvalidBindings.add({
4564
- binding: weakBinding,
4565
- isStrong: false
4566
- });
4567
- }
4568
- });
4569
- }
4570
- });
4571
- if (newInvalidBindings.size > 0) {
4572
- return newInvalidBindings;
4573
- }
4574
- },
4575
- { logger: new ProxyLogger(() => this.options?.logger) }
4576
- )
4577
- ];
4591
+ realNodeChanges.add(original);
4592
+ current = current.parent;
4593
+ }
4594
+ }
4595
+ const updated = this.computeTree(
4596
+ this.root,
4597
+ void 0,
4598
+ dataChanges,
4599
+ resolveCache,
4600
+ toNodeResolveOptions(this.options),
4601
+ void 0,
4602
+ prevASTMap,
4603
+ realNodeChanges
4604
+ );
4605
+ this.resolveCache = resolveCache;
4606
+ this.hooks.afterUpdate.call(updated.value);
4607
+ return updated.value;
4578
4608
  }
4579
- getValidationProviders() {
4580
- if (this.providers) {
4581
- return this.providers;
4609
+ getResolveCache() {
4610
+ return new Map(this.resolveCache);
4611
+ }
4612
+ getPreviousResult(node) {
4613
+ if (!node) {
4614
+ return;
4582
4615
  }
4583
- this.providers = this.hooks.resolveValidationProviders.call([
4584
- {
4585
- source: SCHEMA_VALIDATION_PROVIDER_NAME,
4586
- provider: this.schema
4587
- },
4588
- {
4589
- source: VIEW_VALIDATION_PROVIDER_NAME,
4590
- provider: {
4591
- getValidationsForBinding: (binding) => {
4592
- return this.viewValidationProvider?.getValidationsForBinding?.(
4593
- binding
4616
+ const isFirstUpdate = this.resolveCache.size === 0;
4617
+ const id = getNodeID(node);
4618
+ if (id) {
4619
+ if (this.idCache.has(id)) {
4620
+ if (isFirstUpdate) {
4621
+ if (node.type === "asset" /* Asset */ || node.type === "view" /* View */) {
4622
+ this.logger?.error(
4623
+ `Cache conflict: Found Asset/View nodes that have conflicting ids: ${id}, may cause cache issues.`
4624
+ );
4625
+ } else if (node.type === "value" /* Value */) {
4626
+ this.logger?.info(
4627
+ `Cache conflict: Found Value nodes that have conflicting ids: ${id}, may cause cache issues. To improve performance make value node IDs globally unique.`
4594
4628
  );
4595
- },
4596
- getValidationsForView: () => {
4597
- return this.viewValidationProvider?.getValidationsForView?.();
4598
4629
  }
4599
4630
  }
4631
+ return;
4600
4632
  }
4601
- ]);
4602
- return this.providers;
4633
+ this.idCache.add(id);
4634
+ }
4635
+ return this.resolveCache.get(node);
4603
4636
  }
4604
- reset() {
4605
- this.validations.clear();
4606
- this.tracker = void 0;
4637
+ cloneNode(node) {
4638
+ const clonedNode = clone(node);
4639
+ Object.keys(clonedNode).forEach((key) => {
4640
+ if (key === "parent")
4641
+ return;
4642
+ const value = clonedNode[key];
4643
+ if (typeof value === "object" && value !== null) {
4644
+ clonedNode[key] = Array.isArray(value) ? [...value] : { ...value };
4645
+ }
4646
+ });
4647
+ return clonedNode;
4607
4648
  }
4608
- onView(view) {
4609
- this.validations.clear();
4610
- if (!this.options) {
4611
- return;
4649
+ computeTree(node, rawParent, dataChanges, cacheUpdate, options, partiallyResolvedParent, prevASTMap, nodeChanges) {
4650
+ const dependencyModel = new DependencyModel(options.data.model);
4651
+ dependencyModel.trackSubset("core");
4652
+ const depModelWithParser = withContext(
4653
+ withParser(dependencyModel, this.options.parseBinding)
4654
+ );
4655
+ let resolveOptions = {
4656
+ ...options,
4657
+ data: {
4658
+ ...options.data,
4659
+ model: depModelWithParser
4660
+ },
4661
+ evaluate: (exp) => this.options.evaluator.evaluate(exp, { model: depModelWithParser }),
4662
+ node
4663
+ };
4664
+ try {
4665
+ resolveOptions = this.hooks.resolveOptions.call(resolveOptions, node);
4666
+ } catch (err) {
4667
+ throw new ResolverError(err, "resolveOptions" /* ResolveOptions */, node);
4612
4668
  }
4613
- const bindingTrackerPlugin = new ValidationBindingTrackerViewPlugin({
4614
- ...this.options,
4615
- callbacks: {
4616
- onAdd: (binding) => {
4617
- if (!this.options || this.getValidationForBinding(binding) !== void 0) {
4669
+ const previousResult = this.getPreviousResult(node);
4670
+ const previousDeps = previousResult?.dependencies;
4671
+ const isChanged = nodeChanges.has(node);
4672
+ const dataChanged = caresAboutDataChanges(dataChanges, previousDeps);
4673
+ let shouldUseLastValue = !dataChanged && !isChanged;
4674
+ try {
4675
+ shouldUseLastValue = this.hooks.skipResolve.call(
4676
+ shouldUseLastValue,
4677
+ node,
4678
+ resolveOptions
4679
+ );
4680
+ } catch (err) {
4681
+ throw new ResolverError(err, "skipResolve" /* SkipResolve */, node);
4682
+ }
4683
+ if (previousResult && shouldUseLastValue) {
4684
+ const update2 = {
4685
+ ...previousResult,
4686
+ updated: false
4687
+ };
4688
+ const repopulateASTMapFromCache = (resolvedNode, AST, ASTParent) => {
4689
+ const { node: resolvedASTLocal } = resolvedNode;
4690
+ this.ASTMap.set(resolvedASTLocal, AST);
4691
+ const resolvedUpdate = {
4692
+ ...resolvedNode,
4693
+ updated: false
4694
+ };
4695
+ cacheUpdate.set(AST, resolvedUpdate);
4696
+ const handleChildNode = (childNode) => {
4697
+ const originalChildNode = prevASTMap.get(childNode) ?? childNode;
4698
+ const previousChildResult = this.getPreviousResult(originalChildNode);
4699
+ if (!previousChildResult)
4618
4700
  return;
4619
- }
4620
- const originalValue = this.options.model.get(binding);
4621
- const withoutDefault = this.options.model.get(binding, {
4622
- ignoreDefaultValue: true
4623
- });
4624
- if (originalValue !== withoutDefault) {
4625
- this.options.model.set([[binding, originalValue]], {
4626
- silent: true
4627
- });
4628
- }
4629
- this.updateValidationsForBinding(
4630
- binding,
4631
- "load",
4632
- this.options,
4633
- () => {
4634
- view.update(/* @__PURE__ */ new Set([binding]));
4635
- }
4701
+ repopulateASTMapFromCache(
4702
+ previousChildResult,
4703
+ originalChildNode,
4704
+ AST
4636
4705
  );
4637
- this.hooks.onTrackBinding.call(binding);
4706
+ };
4707
+ if ("children" in resolvedASTLocal) {
4708
+ resolvedASTLocal.children?.forEach(
4709
+ ({ value: childAST }) => handleChildNode(childAST)
4710
+ );
4711
+ } else if (resolvedASTLocal.type === "multi-node" /* MultiNode */) {
4712
+ resolvedASTLocal.values.forEach(handleChildNode);
4638
4713
  }
4639
- }
4640
- });
4641
- this.tracker = bindingTrackerPlugin;
4642
- this.viewValidationProvider = view;
4643
- bindingTrackerPlugin.apply(view);
4644
- }
4645
- updateValidationsForBinding(binding, trigger, validationContext, onDismiss) {
4646
- const context = validationContext ?? this.options;
4647
- if (!context) {
4648
- throw new Error(`Context is required for executing validations`);
4714
+ try {
4715
+ this.hooks.afterNodeUpdate.call(AST, ASTParent, resolvedUpdate);
4716
+ } catch (err) {
4717
+ throw new ResolverError(err, "afterNodeUpdate" /* AfterNodeUpdate */, node);
4718
+ }
4719
+ };
4720
+ previousResult.node.parent = partiallyResolvedParent;
4721
+ repopulateASTMapFromCache(previousResult, node, rawParent);
4722
+ return update2;
4723
+ }
4724
+ let resolvedAST = {
4725
+ ...this.cloneNode(node),
4726
+ parent: partiallyResolvedParent
4727
+ };
4728
+ try {
4729
+ resolvedAST = this.hooks.beforeResolve.call(
4730
+ resolvedAST,
4731
+ resolveOptions
4732
+ ) ?? {
4733
+ type: "empty" /* Empty */
4734
+ };
4735
+ } catch (err) {
4736
+ throw new ResolverError(err, "beforeResolve" /* BeforeResolve */, node);
4649
4737
  }
4650
- if (trigger === "load") {
4651
- const possibleValidations = this.getValidationProviders().reduce((vals, provider) => {
4652
- vals.push(
4653
- ...provider.provider.getValidationsForBinding?.(binding)?.map((valObj) => ({
4654
- ...valObj,
4655
- [VALIDATION_PROVIDER_NAME_SYMBOL]: provider.source
4656
- })) ?? []
4657
- );
4658
- return vals;
4659
- }, []);
4660
- if (possibleValidations.length === 0) {
4661
- return;
4662
- }
4663
- this.validations.set(
4664
- binding,
4665
- new ValidatedBinding(
4666
- possibleValidations,
4667
- onDismiss,
4668
- this.options?.logger
4669
- )
4738
+ resolvedAST.parent = partiallyResolvedParent;
4739
+ resolveOptions.node = resolvedAST;
4740
+ this.ASTMap.set(resolvedAST, node);
4741
+ let resolved = void 0;
4742
+ try {
4743
+ resolved = this.hooks.resolve.call(
4744
+ void 0,
4745
+ resolvedAST,
4746
+ resolveOptions
4670
4747
  );
4748
+ } catch (err) {
4749
+ throw new ResolverError(err, "resolve" /* Resolve */, node);
4671
4750
  }
4672
- const trackedValidations = this.validations.get(binding);
4673
- trackedValidations?.update(trigger, true, (validationObj) => {
4674
- const response = this.validationRunner(validationObj, binding, context);
4675
- if (this.weakBindingTracker.size > 0) {
4676
- const t2 = this.validations.get(binding);
4677
- this.weakBindingTracker.forEach((b) => t2.weakBindings.add(b));
4678
- }
4679
- return response ? { message: response.message } : void 0;
4680
- });
4681
- if (trigger !== "load") {
4682
- this.validations.forEach((validation, vBinding) => {
4683
- if (vBinding !== binding && caresAboutDataChanges(/* @__PURE__ */ new Set([binding]), validation.weakBindings)) {
4684
- validation.update(trigger, true, (validationObj) => {
4685
- const response = this.validationRunner(
4686
- validationObj,
4687
- vBinding,
4688
- context
4751
+ let updated = !dequal2(previousResult?.value, resolved);
4752
+ if (previousResult && !updated) {
4753
+ resolved = previousResult?.value;
4754
+ }
4755
+ const childDependencies = /* @__PURE__ */ new Set();
4756
+ dependencyModel.trackSubset("children");
4757
+ if ("children" in resolvedAST) {
4758
+ const newChildren = resolvedAST.children?.map((child) => {
4759
+ const computedChildTree = this.computeTree(
4760
+ child.value,
4761
+ node,
4762
+ dataChanges,
4763
+ cacheUpdate,
4764
+ resolveOptions,
4765
+ resolvedAST,
4766
+ prevASTMap,
4767
+ nodeChanges
4768
+ );
4769
+ const {
4770
+ dependencies: childTreeDeps,
4771
+ node: childNode,
4772
+ updated: childUpdated,
4773
+ value: childValue
4774
+ } = computedChildTree;
4775
+ childTreeDeps.forEach((binding) => childDependencies.add(binding));
4776
+ if (childValue) {
4777
+ if (childNode.type === "multi-node" /* MultiNode */ && !childNode.override) {
4778
+ const arr = addLast(
4779
+ dlv(resolved, child.path, []),
4780
+ childValue
4689
4781
  );
4690
- return response ? { message: response.message } : void 0;
4691
- });
4782
+ resolved = setIn6(resolved, child.path, arr);
4783
+ } else {
4784
+ resolved = setIn6(resolved, child.path, childValue);
4785
+ }
4786
+ }
4787
+ updated = updated || childUpdated;
4788
+ return { ...child, value: childNode };
4789
+ });
4790
+ resolvedAST.children = newChildren;
4791
+ } else if (resolvedAST.type === "multi-node" /* MultiNode */) {
4792
+ const childValue = [];
4793
+ const rawParentToPassIn = node;
4794
+ resolvedAST.values = resolvedAST.values.map((mValue) => {
4795
+ const mTree = this.computeTree(
4796
+ mValue,
4797
+ rawParentToPassIn,
4798
+ dataChanges,
4799
+ cacheUpdate,
4800
+ resolveOptions,
4801
+ resolvedAST,
4802
+ prevASTMap,
4803
+ nodeChanges
4804
+ );
4805
+ if (mTree.value !== void 0 && mTree.value !== null) {
4806
+ mTree.dependencies.forEach(
4807
+ (bindingDep) => childDependencies.add(bindingDep)
4808
+ );
4809
+ updated = updated || mTree.updated;
4810
+ childValue.push(mTree.value);
4692
4811
  }
4812
+ return mTree.node;
4693
4813
  });
4814
+ resolved = childValue;
4694
4815
  }
4695
- }
4696
- validationRunner(validationObj, binding, context = this.options) {
4697
- if (!context) {
4698
- throw new Error("No context provided to validation runner");
4816
+ childDependencies.forEach(
4817
+ (bindingDep) => dependencyModel.addChildReadDep(bindingDep)
4818
+ );
4819
+ dependencyModel.trackSubset("core");
4820
+ if (previousResult && !updated) {
4821
+ resolved = previousResult?.value;
4699
4822
  }
4700
- const handler = validationObj.handler ?? this.getValidator(validationObj.type);
4701
- const weakBindings = /* @__PURE__ */ new Set();
4702
- const model = {
4703
- get(b, options) {
4704
- weakBindings.add(isBinding(b) ? binding : context.parseBinding(b));
4705
- return context.model.get(b, { ...options, includeInvalid: true });
4706
- },
4707
- set: context.model.set,
4708
- delete: context.model.delete
4823
+ try {
4824
+ resolved = this.hooks.afterResolve.call(resolved, resolvedAST, {
4825
+ ...resolveOptions,
4826
+ getDependencies: (scope) => dependencyModel.getDependencies(scope)
4827
+ });
4828
+ } catch (err) {
4829
+ throw new ResolverError(err, "afterResolve" /* AfterResolve */, node);
4830
+ }
4831
+ const update = {
4832
+ node: resolvedAST,
4833
+ updated,
4834
+ value: resolved,
4835
+ dependencies: /* @__PURE__ */ new Set([
4836
+ ...dependencyModel.getDependencies(),
4837
+ ...childDependencies
4838
+ ])
4709
4839
  };
4710
- const result = handler?.(
4711
- {
4712
- ...context,
4713
- evaluate: (exp, options = { model }) => context.evaluate(exp, options),
4714
- model,
4715
- validation: validationObj,
4716
- schemaType: this.schema.getType(binding)
4717
- },
4718
- context.model.get(binding, {
4719
- includeInvalid: true,
4720
- formatted: validationObj.dataTarget === "formatted"
4721
- }),
4722
- validationObj
4723
- );
4724
- this.weakBindingTracker = weakBindings;
4725
- if (result) {
4726
- let { message } = result;
4727
- const { parameters } = result;
4728
- if (validationObj.message) {
4729
- message = resolveDataRefs(validationObj.message, {
4730
- model,
4731
- evaluate: context.evaluate
4732
- });
4733
- if (parameters) {
4734
- message = replaceParams(message, parameters);
4840
+ try {
4841
+ this.hooks.afterNodeUpdate.call(node, rawParent, update);
4842
+ } catch (err) {
4843
+ throw new ResolverError(err, "afterNodeUpdate" /* AfterNodeUpdate */, node);
4844
+ }
4845
+ cacheUpdate.set(node, update);
4846
+ return update;
4847
+ }
4848
+ };
4849
+
4850
+ // ../../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/core/player/src/view/view.ts
4851
+ var CrossfieldProvider = class {
4852
+ constructor(initialView, parser, logger) {
4853
+ this.allValidations = /* @__PURE__ */ new Set();
4854
+ this.byBinding = /* @__PURE__ */ new Map();
4855
+ this.logger = logger;
4856
+ this.parse(initialView, parser);
4857
+ }
4858
+ parse(contentView, parser) {
4859
+ const xfieldRefs = contentView.validation;
4860
+ if (xfieldRefs === void 0) {
4861
+ return;
4862
+ }
4863
+ if (!Array.isArray(xfieldRefs)) {
4864
+ this.logger?.warn(
4865
+ `Unable to register view validations for id: ${contentView.id}. 'validation' property must be an Array.`
4866
+ );
4867
+ return;
4868
+ }
4869
+ xfieldRefs.forEach((vRef) => {
4870
+ const withDefaults = {
4871
+ trigger: "navigation",
4872
+ severity: "error",
4873
+ ...vRef
4874
+ };
4875
+ this.allValidations.add(withDefaults);
4876
+ const { ref } = vRef;
4877
+ if (ref) {
4878
+ const parsed = parser(ref);
4879
+ if (this.byBinding.has(parsed)) {
4880
+ this.byBinding.get(parsed)?.push(withDefaults);
4881
+ } else {
4882
+ this.byBinding.set(parsed, [withDefaults]);
4735
4883
  }
4736
4884
  }
4737
- return {
4738
- message
4739
- };
4740
- }
4885
+ });
4741
4886
  }
4742
- updateValidationsForView(trigger) {
4743
- const isNavigationTrigger = trigger === "navigation";
4744
- const lastActiveBindings = this.activeBindings;
4745
- const updateValidations = (dismissValidations) => {
4746
- this.getBindings().forEach((binding) => {
4747
- this.validations.get(binding)?.update(trigger, dismissValidations, (obj) => {
4748
- if (!this.options) {
4749
- return;
4750
- }
4751
- return this.validationRunner(obj, binding, this.options);
4752
- });
4753
- });
4887
+ getValidationsForBinding(binding) {
4888
+ return this.byBinding.get(binding);
4889
+ }
4890
+ };
4891
+ var ViewInstance = class {
4892
+ constructor(initialView, resolverOptions) {
4893
+ this.hooks = {
4894
+ onUpdate: new SyncHook9(),
4895
+ parser: new SyncHook9(),
4896
+ resolver: new SyncHook9(),
4897
+ templatePlugin: new SyncHook9()
4754
4898
  };
4755
- updateValidations(!isNavigationTrigger);
4756
- if (isNavigationTrigger) {
4757
- const { activeBindings } = this;
4758
- if (isSubset(activeBindings, lastActiveBindings)) {
4759
- updateValidations(true);
4899
+ this.initialView = initialView;
4900
+ this.resolverOptions = resolverOptions;
4901
+ }
4902
+ /** @deprecated use ViewController.updateViewAST */
4903
+ updateAsync(asyncNode) {
4904
+ const update = this.resolver?.update();
4905
+ this.lastUpdate = update;
4906
+ this.hooks.onUpdate.call(update);
4907
+ }
4908
+ update(changes, nodeChanges) {
4909
+ if (this.rootNode === void 0) {
4910
+ this.validationProvider = new CrossfieldProvider(
4911
+ this.initialView,
4912
+ this.resolverOptions.parseBinding,
4913
+ this.resolverOptions.logger
4914
+ );
4915
+ if (this.templatePlugin) {
4916
+ this.hooks.templatePlugin.call(this.templatePlugin);
4917
+ } else {
4918
+ this.resolverOptions.logger?.warn(
4919
+ "templatePlugin not set for View, legacy templates may not work"
4920
+ );
4760
4921
  }
4922
+ const parser = new Parser();
4923
+ this.hooks.parser.call(parser);
4924
+ this.rootNode = parser.parseView(this.initialView);
4925
+ this.resolver = new Resolver(this.rootNode, {
4926
+ ...this.resolverOptions,
4927
+ parseNode: parser.parseObject.bind(parser)
4928
+ });
4929
+ this.hooks.resolver.call(this.resolver);
4930
+ }
4931
+ const update = this.resolver?.update(changes, nodeChanges);
4932
+ if (this.lastUpdate === update) {
4933
+ return this.lastUpdate;
4761
4934
  }
4935
+ this.lastUpdate = update;
4936
+ this.hooks.onUpdate.call(update);
4937
+ return update;
4762
4938
  }
4763
- get activeBindings() {
4764
- return new Set(
4765
- Array.from(this.getBindings()).filter(
4766
- (b) => this.validations.get(b)?.get() !== void 0
4767
- )
4768
- );
4939
+ getValidationsForBinding(binding) {
4940
+ return this.validationProvider?.getValidationsForBinding(binding);
4769
4941
  }
4770
- getValidator(type) {
4771
- if (this.validatorRegistry) {
4772
- return this.validatorRegistry.get(type);
4773
- }
4774
- const registry = new ValidatorRegistry();
4775
- this.hooks.createValidatorRegistry.call(registry);
4776
- this.validatorRegistry = registry;
4777
- return registry.get(type);
4942
+ setTemplatePlugin(plugin) {
4943
+ this.templatePlugin = plugin;
4778
4944
  }
4779
- getBindings() {
4780
- return this.tracker?.getBindings() ?? /* @__PURE__ */ new Set();
4945
+ };
4946
+
4947
+ // ../../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/core/player/src/view/builder/index.ts
4948
+ var Builder = class _Builder {
4949
+ /**
4950
+ * Creates an asset node
4951
+ *
4952
+ * @param value - the value to put in the asset node
4953
+ */
4954
+ static asset(value) {
4955
+ return {
4956
+ type: "asset" /* Asset */,
4957
+ value
4958
+ };
4781
4959
  }
4782
- trackBinding(binding) {
4783
- this.tracker?.trackBinding(binding);
4960
+ static assetWrapper(value) {
4961
+ const valueNode = _Builder.value();
4962
+ _Builder.addChild(valueNode, "asset", value);
4963
+ return valueNode;
4784
4964
  }
4785
- /** Executes all known validations for the tracked bindings using the given model */
4786
- validateView(trigger = "navigation") {
4787
- this.updateValidationsForView(trigger);
4788
- const validations = /* @__PURE__ */ new Map();
4789
- let canTransition = true;
4790
- this.getBindings().forEach((b) => {
4791
- const allValidations = this.getValidationForBinding(b)?.getAll();
4792
- allValidations?.forEach((v) => {
4793
- if (trigger === "navigation" && v.blocking) {
4794
- this.options?.logger.debug(
4795
- `Validation on binding: ${b.asString()} is preventing navigation. ${JSON.stringify(
4796
- v
4797
- )}`
4798
- );
4799
- canTransition = false;
4800
- }
4801
- if (!validations.has(b)) {
4802
- validations.set(b, v);
4803
- }
4804
- });
4805
- });
4965
+ /**
4966
+ * Creates a value node
4967
+ *
4968
+ * @param v - The object to put in the value node
4969
+ */
4970
+ static value(v) {
4806
4971
  return {
4807
- canTransition,
4808
- validations: validations.size ? validations : void 0
4972
+ type: "value" /* Value */,
4973
+ value: v
4809
4974
  };
4810
4975
  }
4811
- /** Get the current tracked validation for the given binding */
4812
- getValidationForBinding(binding) {
4813
- return this.validations.get(binding);
4976
+ /**
4977
+ * Creates a multiNode and associates the multiNode as the parent
4978
+ * of all the value nodes
4979
+ *
4980
+ * @param values - the value, applicability or async nodes to put in the multinode
4981
+ */
4982
+ static multiNode(...values) {
4983
+ const m = {
4984
+ type: "multi-node" /* MultiNode */,
4985
+ override: true,
4986
+ values
4987
+ };
4988
+ values.forEach((v) => {
4989
+ v.parent = m;
4990
+ });
4991
+ return m;
4814
4992
  }
4815
- forView(parser) {
4993
+ /**
4994
+ * Creates an async node
4995
+ *
4996
+ * @param id - the id of async node. It should be identical for each async node
4997
+ */
4998
+ static asyncNode(id, flatten2 = true, onValueReceived) {
4816
4999
  return {
4817
- _getValidationForBinding: (binding) => {
4818
- return this.getValidationForBinding(
4819
- isBinding(binding) ? binding : parser(binding)
4820
- );
4821
- },
4822
- getAll: () => {
4823
- const bindings = this.getBindings();
4824
- if (bindings.size === 0) {
4825
- return void 0;
5000
+ id,
5001
+ type: "async" /* Async */,
5002
+ flatten: flatten2,
5003
+ onValueReceived,
5004
+ value: {
5005
+ type: "value" /* Value */,
5006
+ value: {
5007
+ id
4826
5008
  }
4827
- const validationMapping = /* @__PURE__ */ new Map();
4828
- bindings.forEach((b) => {
4829
- const validation = this.getValidationForBinding(b)?.get();
4830
- if (validation) {
4831
- validationMapping.set(b, validation);
4832
- }
4833
- });
4834
- return validationMapping.size === 0 ? void 0 : validationMapping;
4835
- },
4836
- get() {
4837
- throw new Error("Error Access be provided by the view plugin");
4838
- },
4839
- getValidationsForBinding() {
4840
- throw new Error("Error rollup should be provided by the view plugin");
4841
- },
4842
- getChildren() {
4843
- throw new Error("Error rollup should be provided by the view plugin");
4844
- },
4845
- getValidationsForSection() {
4846
- throw new Error("Error rollup should be provided by the view plugin");
4847
- },
4848
- track: () => {
4849
- throw new Error("Tracking should be provided by the view plugin");
4850
- },
4851
- register: () => {
4852
- throw new Error(
4853
- "Section functionality should be provided by the view plugin"
4854
- );
4855
- },
4856
- type: (binding) => this.schema.getType(isBinding(binding) ? binding : parser(binding))
5009
+ }
5010
+ };
5011
+ }
5012
+ /**
5013
+ * Adds a child node to a node
5014
+ *
5015
+ * @param node - The node to add a child to
5016
+ * @param path - The path at which to add the child
5017
+ * @param child - The child node
5018
+ */
5019
+ static addChild(node, path, child) {
5020
+ child.parent = node;
5021
+ const newChild = {
5022
+ path: Array.isArray(path) ? path : [path],
5023
+ value: child
5024
+ };
5025
+ node.children = node.children || [];
5026
+ node.children.push(newChild);
5027
+ return node;
5028
+ }
5029
+ /**
5030
+ * Updates children of a node of the same path and preserves order
5031
+ *
5032
+ * @param node - The node to update children for
5033
+ * @param pathToMatch - The path to match against child paths
5034
+ * @param mapFn - Function to transform matching children
5035
+ */
5036
+ static updateChildrenByPath(node, pathToMatch, updateFn) {
5037
+ if (!node.children)
5038
+ return node;
5039
+ const updatedChildren = node.children.map(
5040
+ (child) => (
5041
+ // Check if paths match exactly
5042
+ child.path.join() === pathToMatch.join() ? { ...child, value: updateFn(child) } : child
5043
+ )
5044
+ );
5045
+ return {
5046
+ ...node,
5047
+ children: updatedChildren
4857
5048
  };
4858
5049
  }
4859
5050
  };
4860
5051
 
4861
- // ../../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/core/player/src/controllers/view/controller.ts
4862
- import { SyncHook as SyncHook8, SyncWaterfallHook as SyncWaterfallHook9 } from "tapable-ts";
4863
- import queueMicrotask from "queue-microtask";
4864
- import { Registry } from "@player-ui/partial-match-registry";
4865
- var mergeSets = (setA, setB) => {
4866
- return /* @__PURE__ */ new Set([...setA?.values() ?? [], ...setB?.values() ?? []]);
4867
- };
4868
- var ViewController = class {
4869
- constructor(initialViews, options) {
5052
+ // ../../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/core/player/src/view/plugins/template.ts
5053
+ import { SyncWaterfallHook as SyncWaterfallHook10 } from "tapable-ts";
5054
+ var templateSymbol = Symbol("template");
5055
+ var TemplatePlugin = class {
5056
+ constructor(options) {
4870
5057
  this.hooks = {
4871
- resolveView: new SyncWaterfallHook9(),
4872
- view: new SyncHook8()
5058
+ resolveTemplateSubstitutions: new SyncWaterfallHook10()
4873
5059
  };
4874
- this.transformRegistry = new Registry();
4875
- this.optimizeUpdates = true;
4876
- this.viewOptions = options;
4877
- this.viewMap = initialViews.reduce(
4878
- (viewMap, view) => {
4879
- viewMap[view.id] = view;
4880
- return viewMap;
4881
- },
4882
- {}
4883
- );
4884
- options.flowController.hooks.flow.tap(
4885
- "viewController",
4886
- (flow) => {
4887
- flow.hooks.transition.tap("viewController", (_oldState, newState) => {
4888
- if (newState.value.state_type === "VIEW") {
4889
- this.onView(newState.value);
4890
- } else {
4891
- this.currentView = void 0;
5060
+ this.options = options;
5061
+ }
5062
+ parseTemplate(parseObject, node, options) {
5063
+ const { template, depth } = node;
5064
+ const data = options.data.model.get(node.data);
5065
+ if (!data) {
5066
+ return null;
5067
+ }
5068
+ if (!Array.isArray(data)) {
5069
+ throw new Error(`Template using '${node.data}' but is not an array`);
5070
+ }
5071
+ const values = [];
5072
+ data.forEach((dataItem, index) => {
5073
+ const templateSubstitutions = this.hooks.resolveTemplateSubstitutions.call(
5074
+ [
5075
+ {
5076
+ expression: new RegExp(`_index${depth || ""}_`),
5077
+ value: String(index)
4892
5078
  }
4893
- });
4894
- }
4895
- );
4896
- const update = (updates, silent = false) => {
4897
- if (this.currentView) {
4898
- if (this.optimizeUpdates) {
4899
- this.queueUpdate(updates, void 0, silent);
4900
- } else {
4901
- this.currentView.update();
5079
+ ],
5080
+ {
5081
+ depth,
5082
+ data: dataItem,
5083
+ index
5084
+ }
5085
+ );
5086
+ let templateStr = JSON.stringify(template);
5087
+ for (const { expression, value } of templateSubstitutions) {
5088
+ let flags = "g";
5089
+ if (typeof expression === "object") {
5090
+ flags = `${expression.flags}${expression.global ? "" : "g"}`;
4902
5091
  }
5092
+ templateStr = templateStr.replace(new RegExp(expression, flags), value);
5093
+ }
5094
+ const parsed = parseObject(JSON.parse(templateStr), "value" /* Value */, {
5095
+ templateDepth: node.depth + 1
5096
+ });
5097
+ if (parsed) {
5098
+ values.push(parsed);
4903
5099
  }
5100
+ });
5101
+ const result = {
5102
+ type: "multi-node" /* MultiNode */,
5103
+ override: false,
5104
+ values
4904
5105
  };
4905
- options.model.hooks.onUpdate.tap(
4906
- "viewController",
4907
- (updates, updateOptions) => {
4908
- update(
4909
- new Set(updates.map((t2) => t2.binding)),
4910
- updateOptions?.silent ?? false
5106
+ if (node.placement !== void 0) {
5107
+ result[templateSymbol] = node.placement;
5108
+ }
5109
+ return result;
5110
+ }
5111
+ applyParser(parser) {
5112
+ parser.hooks.onCreateASTNode.tap("template", (node) => {
5113
+ if (node && node.type === "template" /* Template */ && !node.dynamic) {
5114
+ return this.parseTemplate(
5115
+ parser.parseObject.bind(parser),
5116
+ node,
5117
+ this.options
4911
5118
  );
4912
5119
  }
5120
+ return node;
5121
+ });
5122
+ parser.hooks.onCreateASTNode.tap("template", (node) => {
5123
+ function getTemplateSymbolValue(node2) {
5124
+ if (node2.type === "multi-node" /* MultiNode */) {
5125
+ return node2[templateSymbol];
5126
+ } else if (node2.type === "template" /* Template */) {
5127
+ return node2.placement;
5128
+ }
5129
+ return void 0;
5130
+ }
5131
+ if (node && (node.type === "view" /* View */ || node.type === "asset" /* Asset */) && Array.isArray(node.children)) {
5132
+ node.children = node.children.sort((a, b) => {
5133
+ const aPath = a.path.join();
5134
+ const bPath = b.path.join();
5135
+ const pathsEqual = aPath === bPath;
5136
+ if (pathsEqual) {
5137
+ const aPlacement = getTemplateSymbolValue(a.value);
5138
+ const bPlacement = getTemplateSymbolValue(b.value);
5139
+ if (aPlacement !== void 0 && bPlacement === void 0) {
5140
+ return aPlacement === "prepend" ? -1 : 1;
5141
+ } else if (bPlacement !== void 0 && aPlacement === void 0) {
5142
+ return bPlacement === "prepend" ? 1 : -1;
5143
+ } else if (aPlacement !== void 0 && bPlacement !== void 0) {
5144
+ if (aPlacement === bPlacement) {
5145
+ return 0;
5146
+ }
5147
+ return aPlacement === "prepend" ? -1 : 1;
5148
+ }
5149
+ return 0;
5150
+ }
5151
+ return aPath > bPath ? 1 : -1;
5152
+ });
5153
+ }
5154
+ return node;
5155
+ });
5156
+ parser.hooks.parseNode.tap(
5157
+ "template",
5158
+ (obj, _nodeType, options, childOptions) => {
5159
+ if (childOptions && hasTemplateKey(childOptions.key)) {
5160
+ return obj.map((template) => {
5161
+ const templateAST = parser.createASTNode(
5162
+ {
5163
+ type: "template" /* Template */,
5164
+ depth: options.templateDepth ?? 0,
5165
+ data: template.data,
5166
+ template: template.value,
5167
+ dynamic: template.dynamic ?? false,
5168
+ placement: template.placement
5169
+ },
5170
+ template
5171
+ );
5172
+ if (!templateAST)
5173
+ return;
5174
+ if (templateAST.type === "multi-node" /* MultiNode */) {
5175
+ templateAST.values.forEach((v) => {
5176
+ v.parent = templateAST;
5177
+ });
5178
+ }
5179
+ return {
5180
+ path: [...childOptions.path, template.output],
5181
+ value: templateAST
5182
+ };
5183
+ }).filter(Boolean);
5184
+ }
5185
+ }
4913
5186
  );
4914
- options.model.hooks.onDelete.tap("viewController", (binding) => {
4915
- const parentBinding = binding.parent();
4916
- const property = binding.key();
4917
- if (typeof property === "number" && parentBinding) {
4918
- update(/* @__PURE__ */ new Set([parentBinding]));
4919
- } else {
4920
- update(/* @__PURE__ */ new Set([binding]));
5187
+ }
5188
+ applyResolverHooks(resolver) {
5189
+ resolver.hooks.beforeResolve.tap("template", (node, options) => {
5190
+ if (node && node.type === "template" /* Template */ && node.dynamic) {
5191
+ return this.parseTemplate(options.parseNode, node, options);
4921
5192
  }
5193
+ return node;
4922
5194
  });
4923
- this.viewPlugins = this.createViewPlugins();
4924
5195
  }
4925
- queueUpdate(bindings, nodes, silent = false) {
4926
- if (!this.pendingUpdate) {
4927
- this.pendingUpdate = {
4928
- scheduled: false
4929
- };
5196
+ apply(view) {
5197
+ view.hooks.parser.tap("template", this.applyParser.bind(this));
5198
+ view.hooks.resolver.tap("template", this.applyResolverHooks.bind(this));
5199
+ view.setTemplatePlugin(this);
5200
+ }
5201
+ };
5202
+
5203
+ // ../../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/core/player/src/view/plugins/string-resolver.ts
5204
+ import { set } from "timm";
5205
+ var createPatternMatcher = (start, end) => {
5206
+ return (testStr) => {
5207
+ const startLocation = testStr.indexOf(start);
5208
+ if (startLocation === -1) {
5209
+ return false;
4930
5210
  }
4931
- this.pendingUpdate = {
4932
- ...this.pendingUpdate,
4933
- changedBindings: mergeSets(this.pendingUpdate.changedBindings, bindings),
4934
- changedNodes: mergeSets(this.pendingUpdate.changedNodes, nodes)
4935
- };
4936
- if (!this.pendingUpdate.scheduled && !silent) {
4937
- this.pendingUpdate.scheduled = true;
4938
- queueMicrotask(() => {
4939
- const { changedBindings, changedNodes } = this.pendingUpdate ?? {};
4940
- this.pendingUpdate = void 0;
4941
- this.currentView?.update(changedBindings, changedNodes);
4942
- });
5211
+ const endLocation = testStr.indexOf(end);
5212
+ if (endLocation === -1) {
5213
+ return false;
4943
5214
  }
5215
+ return startLocation < endLocation;
5216
+ };
5217
+ };
5218
+ var bindingResolveLookup = createPatternMatcher("{{", "}}");
5219
+ var expressionResolveLookup = createPatternMatcher("@[", "]@");
5220
+ function hasSomethingToResolve(str) {
5221
+ return bindingResolveLookup(str) || expressionResolveLookup(str);
5222
+ }
5223
+ function resolveString(str, resolveOptions) {
5224
+ return hasSomethingToResolve(str) ? resolveDataRefs(str, {
5225
+ model: resolveOptions.data.model,
5226
+ evaluate: resolveOptions.evaluate
5227
+ }) : str;
5228
+ }
5229
+ function resolveAllRefs(node, resolveOptions, propertiesToSkip) {
5230
+ if (node === null || node === void 0 || typeof node !== "object" && typeof node !== "string") {
5231
+ return node;
4944
5232
  }
4945
- getViewForRef(viewRef) {
4946
- if (this.viewMap[viewRef]) {
4947
- return this.viewMap[viewRef];
5233
+ if (typeof node === "string") {
5234
+ return resolveString(node, resolveOptions);
5235
+ }
5236
+ let newNode = node;
5237
+ Object.keys(node).forEach((key) => {
5238
+ if (propertiesToSkip.has(key)) {
5239
+ return;
4948
5240
  }
4949
- const matchingViewId = Object.keys(this.viewMap).find(
4950
- (possibleViewIdMatch) => viewRef === resolveDataRefsInString(possibleViewIdMatch, {
4951
- model: this.viewOptions.model,
4952
- evaluate: this.viewOptions.evaluator.evaluate
4953
- })
4954
- );
4955
- if (matchingViewId && this.viewMap[matchingViewId]) {
4956
- return this.viewMap[matchingViewId];
5241
+ const val = node[key];
5242
+ let newVal = val;
5243
+ if (typeof val === "object") {
5244
+ newVal = resolveAllRefs(val, resolveOptions, propertiesToSkip);
5245
+ } else if (typeof val === "string") {
5246
+ newVal = resolveString(val, resolveOptions);
4957
5247
  }
4958
- }
4959
- onView(state) {
4960
- const viewId = state.ref;
4961
- const source = this.hooks.resolveView.call(
4962
- this.getViewForRef(viewId),
4963
- viewId,
4964
- state
4965
- );
4966
- if (!source) {
4967
- throw new Error(`No view with id ${viewId}`);
5248
+ if (newVal !== val) {
5249
+ newNode = set(newNode, key, newVal);
4968
5250
  }
4969
- const view = new ViewInstance(source, this.viewOptions);
4970
- this.currentView = view;
4971
- this.applyViewPlugins(view);
4972
- this.hooks.view.call(view);
4973
- view.update();
5251
+ });
5252
+ return newNode;
5253
+ }
5254
+ var findBasePath = (node, resolver) => {
5255
+ const parentNode = node.parent;
5256
+ if (!parentNode) {
5257
+ return [];
4974
5258
  }
4975
- applyViewPlugins(view) {
4976
- for (const plugin of this.viewPlugins) {
4977
- plugin.apply(view);
4978
- }
5259
+ if ("children" in parentNode) {
5260
+ const original = resolver.getSourceNode(node);
5261
+ return parentNode.children?.find((child) => child.value === original)?.path ?? [];
4979
5262
  }
4980
- createViewPlugins() {
4981
- const pluginOptions = toNodeResolveOptions(this.viewOptions);
4982
- return [
4983
- new AssetPlugin(),
4984
- new SwitchPlugin(pluginOptions),
4985
- new ApplicabilityPlugin(),
4986
- new AssetTransformCorePlugin(this.transformRegistry),
4987
- new StringResolverPlugin(),
4988
- new TemplatePlugin(pluginOptions),
4989
- new MultiNodePlugin()
4990
- ];
5263
+ if (parentNode.type !== "multi-node" /* MultiNode */) {
5264
+ return [];
5265
+ }
5266
+ return findBasePath(parentNode, resolver);
5267
+ };
5268
+ var StringResolverPlugin = class {
5269
+ constructor() {
5270
+ this.propertiesToSkipCache = /* @__PURE__ */ new Map();
5271
+ }
5272
+ applyResolver(resolver) {
5273
+ resolver.hooks.resolve.tap("string-resolver", (value, node, options) => {
5274
+ if (node.type === "empty" /* Empty */ || node.type === "unknown" /* Unknown */) {
5275
+ return null;
5276
+ }
5277
+ if (node.type === "value" /* Value */ || node.type === "asset" /* Asset */ || node.type === "view" /* View */) {
5278
+ let propsToSkip;
5279
+ if (node.type === "asset" /* Asset */ || node.type === "view" /* View */) {
5280
+ propsToSkip = new Set(
5281
+ node.plugins?.stringResolver?.propertiesToSkip ?? ["exp"]
5282
+ );
5283
+ if (node.value?.id) {
5284
+ this.propertiesToSkipCache.set(node.value.id, propsToSkip);
5285
+ }
5286
+ } else if (node.parent?.type === "multi-node" /* MultiNode */ && (node.parent?.parent?.type === "asset" /* Asset */ || node.parent?.parent?.type === "view" /* View */) && node.parent.parent.value?.id && this.propertiesToSkipCache.has(node.parent.parent.value.id)) {
5287
+ propsToSkip = this.propertiesToSkipCache.get(
5288
+ node.parent.parent.value.id
5289
+ );
5290
+ } else {
5291
+ propsToSkip = /* @__PURE__ */ new Set(["exp"]);
5292
+ }
5293
+ const nodePath = findBasePath(node, resolver);
5294
+ if (nodePath.length > 0 && nodePath.some((segment) => propsToSkip.has(segment.toString()))) {
5295
+ return node.value;
5296
+ }
5297
+ return resolveAllRefs(node.value, options, propsToSkip);
5298
+ }
5299
+ return value;
5300
+ });
4991
5301
  }
4992
- /** Marks all AST nodes in `nodes` as changed, triggering the view to update and re-resolve these nodes. View updates are triggered asynchronously and many calls to this in a short time will batch into a single update.
4993
- *
4994
- * NOTE: In most cases view updates are handled automatically by changes to data or any other built-in functionality that would require a view update. Only call this function if absolutely necessary.
4995
- */
4996
- updateViewAST(nodes) {
4997
- this.queueUpdate(void 0, nodes);
5302
+ apply(view) {
5303
+ view.hooks.resolver.tap("string-resolver", this.applyResolver.bind(this));
4998
5304
  }
4999
5305
  };
5000
5306
 
5001
- // ../../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/core/player/src/controllers/data/controller.ts
5002
- import { SyncHook as SyncHook9, SyncWaterfallHook as SyncWaterfallHook10, SyncBailHook as SyncBailHook5 } from "tapable-ts";
5003
- import { dequal as dequal2 } from "dequal";
5004
-
5005
- // ../../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/core/player/src/controllers/data/utils.ts
5006
- var ReadOnlyDataController = class {
5007
- constructor(controller, logger) {
5008
- this.controller = controller;
5009
- this.logger = logger;
5010
- }
5011
- get(binding, options) {
5012
- return this.controller.get(binding, options);
5307
+ // ../../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/core/player/src/view/plugins/applicability.ts
5308
+ import { omit as omit2 } from "timm";
5309
+ var ApplicabilityPlugin = class {
5310
+ isApplicability(obj) {
5311
+ return obj && Object.prototype.hasOwnProperty.call(obj, "applicability");
5013
5312
  }
5014
- set(transaction, options) {
5015
- this.logger?.error(
5016
- "Error: Tried to set in a read only instance of the DataController"
5313
+ applyResolver(resolver) {
5314
+ resolver.hooks.beforeResolve.tap(
5315
+ "applicability",
5316
+ (node, options) => {
5317
+ let newNode = node;
5318
+ if (node?.type === "applicability" /* Applicability */) {
5319
+ const isApplicable = options.evaluate(node.expression);
5320
+ if (isApplicable === false) {
5321
+ return null;
5322
+ }
5323
+ newNode = node.value;
5324
+ }
5325
+ return newNode;
5326
+ }
5017
5327
  );
5018
- return [];
5019
5328
  }
5020
- delete(binding, options) {
5021
- this.logger?.error(
5022
- "Error: Tried to delete in a read only instance of the DataController"
5329
+ applyParser(parser) {
5330
+ parser.hooks.parseNode.tap(
5331
+ "applicability",
5332
+ (obj, nodeType, options, childOptions) => {
5333
+ if (this.isApplicability(obj)) {
5334
+ const parsedApplicability = parser.parseObject(
5335
+ omit2(obj, "applicability"),
5336
+ nodeType,
5337
+ options
5338
+ );
5339
+ if (!parsedApplicability) {
5340
+ return childOptions ? [] : null;
5341
+ }
5342
+ const applicabilityNode = parser.createASTNode(
5343
+ {
5344
+ type: "applicability" /* Applicability */,
5345
+ expression: obj.applicability,
5346
+ value: parsedApplicability
5347
+ },
5348
+ obj
5349
+ );
5350
+ if (!applicabilityNode) {
5351
+ return childOptions ? [] : null;
5352
+ }
5353
+ if (applicabilityNode.type === "applicability" /* Applicability */) {
5354
+ applicabilityNode.value.parent = applicabilityNode;
5355
+ }
5356
+ return childOptions ? [
5357
+ {
5358
+ path: [...childOptions.path, childOptions.key],
5359
+ value: applicabilityNode
5360
+ }
5361
+ ] : applicabilityNode;
5362
+ }
5363
+ }
5023
5364
  );
5024
5365
  }
5366
+ apply(view) {
5367
+ view.hooks.resolver.tap("applicability", this.applyResolver.bind(this));
5368
+ view.hooks.parser.tap("applicability", this.applyParser.bind(this));
5369
+ }
5025
5370
  };
5026
5371
 
5027
- // ../../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/core/player/src/controllers/data/controller.ts
5028
- var DataController = class {
5029
- constructor(model, options) {
5030
- this.hooks = {
5031
- resolve: new SyncWaterfallHook10(),
5032
- resolveDataStages: new SyncWaterfallHook10(),
5033
- // On any set or get of an undefined value, redirect the value to be the default
5034
- resolveDefaultValue: new SyncBailHook5(),
5035
- onDelete: new SyncHook9(),
5036
- onSet: new SyncHook9(),
5037
- onGet: new SyncHook9(),
5038
- onUpdate: new SyncHook9(),
5039
- format: new SyncWaterfallHook10(),
5040
- deformat: new SyncWaterfallHook10(),
5041
- serialize: new SyncWaterfallHook10()
5042
- };
5043
- this.logger = options.logger;
5044
- const middleware = options.middleware || [];
5045
- this.baseMiddleware = [new LocalModel(model), ...middleware];
5046
- this.trash = /* @__PURE__ */ new Set();
5047
- this.pathResolver = options.pathResolver;
5372
+ // ../../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/core/player/src/view/plugins/switch.ts
5373
+ var SwitchPlugin = class {
5374
+ constructor(options) {
5375
+ this.options = options;
5048
5376
  }
5049
- getModel() {
5050
- if (!this.model) {
5051
- const stages = this.hooks.resolveDataStages.call(this.baseMiddleware);
5052
- const model = new PipelinedDataModel();
5053
- model.setMiddleware(stages);
5054
- this.model = model;
5377
+ resolveSwitch(node, options) {
5378
+ for (const switchCase of node.cases) {
5379
+ const isApplicable = options.evaluate(switchCase.case);
5380
+ if (isApplicable) {
5381
+ return switchCase.value;
5382
+ }
5055
5383
  }
5056
- return this.model;
5384
+ return EMPTY_NODE;
5057
5385
  }
5058
- resolveDataValue(binding, value, deformat) {
5059
- if (deformat) {
5060
- return this.hooks.deformat.call(value, binding);
5061
- }
5062
- return value;
5386
+ isSwitch(obj) {
5387
+ return obj && (Object.prototype.hasOwnProperty.call(obj, "dynamicSwitch") || Object.prototype.hasOwnProperty.call(obj, "staticSwitch"));
5063
5388
  }
5064
- set(transaction, options) {
5065
- let normalizedTransaction = [];
5066
- if (Array.isArray(transaction)) {
5067
- normalizedTransaction = transaction.map(([binding, value]) => {
5068
- const parsed = this.pathResolver.parse(binding);
5069
- return [
5070
- parsed,
5071
- this.resolveDataValue(parsed, value, Boolean(options?.formatted))
5072
- ];
5073
- });
5074
- } else {
5075
- normalizedTransaction = Object.keys(transaction).map(
5076
- (binding) => {
5077
- const parsed = this.pathResolver.parse(binding);
5078
- const val = transaction[binding];
5079
- return [
5080
- parsed,
5081
- this.resolveDataValue(parsed, val, Boolean(options?.formatted))
5082
- ];
5083
- }
5084
- );
5085
- }
5086
- const setUpdates = normalizedTransaction.reduce(
5087
- (updates, [binding, newVal]) => {
5088
- const oldVal = this.get(binding, { includeInvalid: true });
5089
- const update = {
5090
- binding,
5091
- newValue: newVal,
5092
- oldValue: oldVal
5093
- };
5094
- if (dequal2(oldVal, newVal)) {
5095
- this.logger?.debug(
5096
- `Skipping update for path: ${binding.asString()}. Value was unchanged: ${oldVal}`
5389
+ applyParser(parser) {
5390
+ parser.hooks.onCreateASTNode.tap("switch", (node) => {
5391
+ if (node && node.type === "switch" /* Switch */ && !node.dynamic) {
5392
+ return this.resolveSwitch(node, this.options);
5393
+ }
5394
+ return node;
5395
+ });
5396
+ parser.hooks.parseNode.tap(
5397
+ "switch",
5398
+ (obj, _nodeType, options, childOptions) => {
5399
+ if (this.isSwitch(obj) || childOptions && hasSwitchKey(childOptions.key)) {
5400
+ const objToParse = childOptions && hasSwitchKey(childOptions.key) ? { [childOptions.key]: obj } : obj;
5401
+ const dynamic = "dynamicSwitch" in objToParse;
5402
+ const switchContent = dynamic ? objToParse.dynamicSwitch : objToParse.staticSwitch;
5403
+ const cases = switchContent.map(
5404
+ (switchCase) => {
5405
+ const { case: switchCaseExpr, ...switchBody } = switchCase;
5406
+ const value = parser.parseObject(
5407
+ switchBody,
5408
+ "value" /* Value */,
5409
+ options
5410
+ );
5411
+ if (value) {
5412
+ return {
5413
+ case: switchCaseExpr,
5414
+ value
5415
+ };
5416
+ }
5417
+ return;
5418
+ }
5419
+ ).filter(Boolean);
5420
+ const switchAST = parser.createASTNode(
5421
+ {
5422
+ type: "switch" /* Switch */,
5423
+ dynamic,
5424
+ cases
5425
+ },
5426
+ objToParse
5097
5427
  );
5098
- } else {
5099
- updates.push(update);
5100
- this.logger?.debug(
5101
- `Setting path: ${binding.asString()} from: ${oldVal} to: ${newVal}`
5428
+ if (!switchAST || switchAST.type === "empty" /* Empty */) {
5429
+ return childOptions ? [] : null;
5430
+ }
5431
+ if (switchAST.type === "switch" /* Switch */) {
5432
+ switchAST.cases.forEach((sCase) => {
5433
+ sCase.value.parent = switchAST;
5434
+ });
5435
+ }
5436
+ if (childOptions) {
5437
+ let path = [...childOptions.path, childOptions.key];
5438
+ let value = switchAST;
5439
+ if (switchAST.type === "value" /* Value */ && switchAST.children?.length === 1 && switchAST.value === void 0) {
5440
+ const firstChild = switchAST.children[0];
5441
+ path = [...path, ...firstChild.path];
5442
+ value = firstChild.value;
5443
+ }
5444
+ return [{ path, value }];
5445
+ }
5446
+ return switchAST;
5447
+ }
5448
+ }
5449
+ );
5450
+ }
5451
+ applyResolver(resolver) {
5452
+ resolver.hooks.beforeResolve.tap("switch", (node, options) => {
5453
+ if (node && node.type === "switch" /* Switch */ && node.dynamic) {
5454
+ return this.resolveSwitch(node, options);
5455
+ }
5456
+ return node;
5457
+ });
5458
+ }
5459
+ apply(view) {
5460
+ view.hooks.parser.tap("switch", this.applyParser.bind(this));
5461
+ view.hooks.resolver.tap("switch", this.applyResolver.bind(this));
5462
+ }
5463
+ };
5464
+
5465
+ // ../../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/core/player/src/view/plugins/multi-node.ts
5466
+ var MultiNodePlugin = class {
5467
+ applyParser(parser) {
5468
+ parser.hooks.parseNode.tap(
5469
+ "multi-node",
5470
+ (obj, nodeType, options, childOptions) => {
5471
+ if ((childOptions === void 0 || !hasTemplateKey(childOptions.key)) && Array.isArray(obj)) {
5472
+ const values = obj.map(
5473
+ (childVal) => parser.parseObject(childVal, "value" /* Value */, options)
5474
+ ).filter((child) => !!child);
5475
+ if (!values.length) {
5476
+ return [];
5477
+ }
5478
+ const multiNode = parser.createASTNode(
5479
+ {
5480
+ type: "multi-node" /* MultiNode */,
5481
+ override: childOptions !== void 0 && !hasTemplateValues(childOptions.parentObj, childOptions.key),
5482
+ values
5483
+ },
5484
+ obj
5102
5485
  );
5486
+ if (!multiNode) {
5487
+ return [];
5488
+ }
5489
+ if (multiNode.type === "multi-node" /* MultiNode */) {
5490
+ multiNode.values.forEach((v) => {
5491
+ v.parent = multiNode;
5492
+ });
5493
+ }
5494
+ return childOptions === void 0 ? multiNode : [
5495
+ {
5496
+ path: [...childOptions.path, childOptions.key],
5497
+ value: multiNode
5498
+ }
5499
+ ];
5103
5500
  }
5104
- return updates;
5105
- },
5106
- []
5107
- );
5108
- const result = this.getModel().set(normalizedTransaction, options);
5109
- const setUpdateBindings = new Set(setUpdates.map((su) => su.binding));
5110
- result.forEach((tr) => {
5111
- if (!setUpdateBindings.has(tr.binding) && (tr.force === true || !dequal2(tr.oldValue, tr.newValue))) {
5112
- this.logger?.debug(
5113
- `Path: ${tr.binding.asString()} was changed from: ${tr.oldValue} to: ${tr.newValue}`
5114
- );
5115
- setUpdates.push(tr);
5116
5501
  }
5117
- });
5118
- this.hooks.onSet.call(normalizedTransaction);
5119
- if (setUpdates.length > 0) {
5120
- this.hooks.onUpdate.call(setUpdates, options);
5121
- }
5122
- return result;
5502
+ );
5123
5503
  }
5124
- resolve(binding, readOnly) {
5125
- return Array.isArray(binding) || typeof binding === "string" ? this.pathResolver.parse(binding, { readOnly }) : binding;
5504
+ apply(view) {
5505
+ view.hooks.parser.tap("multi-node", this.applyParser.bind(this));
5126
5506
  }
5127
- get(binding, options) {
5128
- const resolved = binding instanceof BindingInstance ? binding : this.resolve(binding, true);
5129
- let result = this.getModel().get(resolved, options);
5130
- if (result === void 0 && !options?.ignoreDefaultValue) {
5131
- const defaultVal = this.hooks.resolveDefaultValue.call(resolved);
5132
- if (defaultVal !== result) {
5133
- result = defaultVal;
5507
+ };
5508
+
5509
+ // ../../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/core/player/src/view/plugins/asset.ts
5510
+ var AssetPlugin = class {
5511
+ applyParser(parser) {
5512
+ parser.hooks.parseNode.tap(
5513
+ "asset",
5514
+ (obj, nodeType, options, childOptions) => {
5515
+ if (childOptions?.key === "asset" && typeof obj === "object") {
5516
+ const assetAST = parser.parseObject(obj, "asset" /* Asset */, options);
5517
+ if (!assetAST) {
5518
+ return [];
5519
+ }
5520
+ return [
5521
+ {
5522
+ path: [...childOptions.path, childOptions.key],
5523
+ value: assetAST
5524
+ }
5525
+ ];
5526
+ }
5134
5527
  }
5135
- }
5136
- if (options?.formatted) {
5137
- result = this.hooks.format.call(result, resolved);
5138
- } else if (options?.formatted === false) {
5139
- result = this.hooks.deformat.call(result, resolved);
5140
- }
5141
- this.hooks.onGet.call(binding, result);
5142
- return result;
5143
- }
5144
- delete(binding, options) {
5145
- if (typeof binding !== "string" && !Array.isArray(binding) && !(binding instanceof BindingInstance)) {
5146
- throw new Error("Invalid arguments: delete expects a data path (string)");
5147
- }
5148
- const resolved = binding instanceof BindingInstance ? binding : this.resolve(binding, false);
5149
- const parentBinding = resolved.parent();
5150
- const property = resolved.key();
5151
- const parentValue = this.get(parentBinding);
5152
- const existedBeforeDelete = typeof parentValue === "object" && parentValue !== null && Object.prototype.hasOwnProperty.call(parentValue, property);
5153
- this.getModel().delete(resolved, options);
5154
- if (existedBeforeDelete && !this.get(resolved)) {
5155
- this.trash.add(resolved);
5156
- }
5157
- this.hooks.onDelete.call(resolved);
5158
- }
5159
- serialize() {
5160
- return this.hooks.serialize.call(this.get(""));
5528
+ );
5161
5529
  }
5162
- makeReadOnly() {
5163
- return new ReadOnlyDataController(this, this.logger);
5530
+ apply(view) {
5531
+ view.hooks.parser.tap("asset", this.applyParser.bind(this));
5164
5532
  }
5165
5533
  };
5166
5534
 
5167
- // ../../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/core/player/src/controllers/constants/utils.ts
5168
- function flatten(obj, roots = [], sep = ".") {
5169
- return Object.keys(obj).reduce(
5170
- (memo, prop) => ({
5171
- // create a new object
5172
- // include previously returned object
5173
- ...memo,
5174
- ...Object.prototype.toString.call(obj[prop]) === "[object Object]" ? (
5175
- // keep working if value is an object
5176
- flatten(obj[prop], roots.concat([prop]))
5177
- ) : (
5178
- // include current prop and value and prefix prop with the roots
5179
- { [roots.concat([prop]).join(sep)]: obj[prop] }
5180
- )
5181
- }),
5182
- {}
5183
- );
5184
- }
5185
- function objectToBatchSet(obj) {
5186
- const flattenedObj = flatten(obj);
5187
- const batchTxn = [];
5188
- Object.keys(flattenedObj).forEach((key) => {
5189
- batchTxn.push([new BindingInstance(key), flattenedObj[key]]);
5190
- });
5191
- return batchTxn;
5192
- }
5193
-
5194
- // ../../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/core/player/src/controllers/constants/index.ts
5195
- var ConstantsController = class {
5196
- constructor() {
5197
- this.store = /* @__PURE__ */ new Map();
5198
- this.tempStore = /* @__PURE__ */ new Map();
5199
- }
5200
- addConstants(data, namespace) {
5201
- if (this.store.has(namespace)) {
5202
- this.store.get(namespace)?.set(objectToBatchSet(data));
5203
- } else {
5204
- this.store.set(namespace, new LocalModel(data));
5205
- }
5535
+ // ../../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/core/player/src/view/plugins/asset-transform.ts
5536
+ function findUp(node, target) {
5537
+ if (node === target) {
5538
+ return true;
5206
5539
  }
5207
- getConstants(key, namespace, fallback) {
5208
- const path = new BindingInstance(key);
5209
- return this.tempStore.get(namespace)?.get(path) ?? this.store.get(namespace)?.get(path) ?? fallback;
5540
+ if (node.parent) {
5541
+ return findUp(node.parent, target);
5210
5542
  }
5211
- setTemporaryValues(data, namespace) {
5212
- if (this.tempStore.has(namespace)) {
5213
- this.tempStore.get(namespace)?.set(objectToBatchSet(data));
5214
- } else {
5215
- this.tempStore.set(namespace, new LocalModel(data));
5216
- }
5543
+ return false;
5544
+ }
5545
+ var AssetTransformCorePlugin = class {
5546
+ constructor(registry) {
5547
+ this.registry = registry;
5548
+ this.stateStore = /* @__PURE__ */ new Map();
5549
+ this.beforeResolveSymbol = Symbol("before resolve");
5550
+ this.resolveSymbol = Symbol("resolve");
5551
+ this.beforeResolveCountSymbol = Symbol("before resolve count");
5552
+ this.resolveCountSymbol = Symbol("resolve count");
5217
5553
  }
5218
- clearTemporaryValues(namespace) {
5219
- if (namespace) {
5220
- this.tempStore.get(namespace)?.reset();
5221
- } else {
5222
- this.tempStore.forEach((value) => {
5223
- value.reset();
5554
+ apply(view) {
5555
+ this.stateStore.clear();
5556
+ view.hooks.resolver.tap("asset-transform", (resolver) => {
5557
+ let lastUpdatedNode;
5558
+ const updateState = (node) => {
5559
+ lastUpdatedNode = node;
5560
+ view.update(/* @__PURE__ */ new Set());
5561
+ };
5562
+ const getStore = (node, stepKey) => {
5563
+ let store;
5564
+ const countKey = stepKey === this.resolveSymbol ? this.resolveCountSymbol : this.beforeResolveCountSymbol;
5565
+ const storedState = this.stateStore.get(node);
5566
+ if (storedState) {
5567
+ store = storedState;
5568
+ store.removeKey(countKey);
5569
+ } else {
5570
+ store = new LocalStateStore(() => {
5571
+ updateState(node);
5572
+ });
5573
+ this.stateStore.set(node, store);
5574
+ }
5575
+ return {
5576
+ useSharedState: (key) => {
5577
+ return store.useSharedState(key);
5578
+ },
5579
+ useLocalState: (initialState) => {
5580
+ return store.getLocalStateFunction(
5581
+ stepKey,
5582
+ countKey
5583
+ )(initialState);
5584
+ }
5585
+ };
5586
+ };
5587
+ resolver.hooks.beforeResolve.tap("asset-transform", (node, options) => {
5588
+ if (node && (node.type === "asset" || node.type === "view")) {
5589
+ const transform = this.registry.get(node.value);
5590
+ if (transform?.beforeResolve) {
5591
+ const store = getStore(
5592
+ options.node ?? node,
5593
+ this.beforeResolveSymbol
5594
+ );
5595
+ return transform.beforeResolve(node, options, store);
5596
+ }
5597
+ }
5598
+ return node;
5224
5599
  });
5225
- }
5600
+ resolver.hooks.afterUpdate.tap("asset-transform", () => {
5601
+ lastUpdatedNode = void 0;
5602
+ });
5603
+ resolver.hooks.skipResolve.tap("asset-transform", (skip, node) => {
5604
+ if (!skip || !lastUpdatedNode) {
5605
+ return skip;
5606
+ }
5607
+ const isParentOfUpdated = findUp(lastUpdatedNode, node);
5608
+ const isChildOfUpdated = findUp(node, lastUpdatedNode);
5609
+ return !isParentOfUpdated && !isChildOfUpdated;
5610
+ });
5611
+ resolver.hooks.afterResolve.tap(
5612
+ "asset-transform",
5613
+ (value, node, options) => {
5614
+ if (node.type !== "asset" /* Asset */ && node.type !== "view" /* View */) {
5615
+ return value;
5616
+ }
5617
+ const originalNode = resolver.getSourceNode(node);
5618
+ if (!originalNode) {
5619
+ return value;
5620
+ }
5621
+ const transform = this.registry.get(value);
5622
+ if (transform?.resolve) {
5623
+ const store = getStore(originalNode, this.resolveSymbol);
5624
+ return transform?.resolve(value, options, store);
5625
+ }
5626
+ return value;
5627
+ }
5628
+ );
5629
+ });
5226
5630
  }
5227
5631
  };
5228
5632
 
5633
+ // ../../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/core/player/src/player.ts
5634
+ import { setIn as setIn7 } from "timm";
5635
+ import deferred from "p-defer";
5636
+ import queueMicrotask2 from "queue-microtask";
5637
+ import { SyncHook as SyncHook10, SyncWaterfallHook as SyncWaterfallHook11 } from "tapable-ts";
5638
+
5229
5639
  // ../../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/core/player/src/plugins/flow-exp-plugin.ts
5230
5640
  var FlowExpPlugin = class {
5231
5641
  constructor() {
@@ -5304,8 +5714,8 @@ var NOT_STARTED_STATE = {
5304
5714
  };
5305
5715
 
5306
5716
  // ../../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/core/player/src/player.ts
5307
- var PLAYER_VERSION = true ? "0.15.3" : "unknown";
5308
- var COMMIT = true ? "635ec38f97e5afa4d5f7ff4ddd3e4f7a6fbe0988" : "unknown";
5717
+ var PLAYER_VERSION = true ? "0.15.4--canary.881.37421" : "unknown";
5718
+ var COMMIT = true ? "b139e69374bc1b7f35ddd797a0f005be4b0e26e6" : "unknown";
5309
5719
  var _Player = class _Player {
5310
5720
  constructor(config) {
5311
5721
  this.logger = new TapableLogger();
@@ -5320,6 +5730,7 @@ var _Player = class _Player {
5320
5730
  schema: new SyncHook10(),
5321
5731
  validationController: new SyncHook10(),
5322
5732
  bindingParser: new SyncHook10(),
5733
+ errorController: new SyncHook10(),
5323
5734
  state: new SyncHook10(),
5324
5735
  onStart: new SyncHook10(),
5325
5736
  onEnd: new SyncHook10(),
@@ -5410,9 +5821,18 @@ var _Player = class _Player {
5410
5821
  this.hooks.schema.call(schema);
5411
5822
  const validationController = new ValidationController(schema);
5412
5823
  this.hooks.validationController.call(validationController);
5824
+ const errorController = new ErrorController({
5825
+ logger: this.logger,
5826
+ flow: flowController,
5827
+ fail: flowResultDeferred.reject
5828
+ });
5829
+ this.hooks.errorController.call(errorController);
5413
5830
  dataController = new DataController(userFlow.data, {
5414
5831
  pathResolver,
5415
- middleware: validationController.getDataMiddleware(),
5832
+ middleware: [
5833
+ ...validationController.getDataMiddleware(),
5834
+ errorController.getDataMiddleware()
5835
+ ],
5416
5836
  logger: this.logger
5417
5837
  });
5418
5838
  dataController.hooks.format.tap("player", (value, binding) => {
@@ -5427,6 +5847,9 @@ var _Player = class _Player {
5427
5847
  "player",
5428
5848
  (binding) => schema.getApparentType(binding)?.default
5429
5849
  );
5850
+ errorController.setOptions({
5851
+ model: dataController
5852
+ });
5430
5853
  let viewController;
5431
5854
  expressionEvaluator = new ExpressionEvaluator({
5432
5855
  model: dataController,
@@ -5565,7 +5988,8 @@ var _Player = class _Player {
5565
5988
  ...validationController.forView(parseBinding),
5566
5989
  type: (b) => schema.getType(parseBinding(b))
5567
5990
  },
5568
- constants: this.constantsController
5991
+ constants: this.constantsController,
5992
+ errorController
5569
5993
  });
5570
5994
  viewController.hooks.view.tap("player", (view) => {
5571
5995
  validationController.onView(view);
@@ -5595,7 +6019,8 @@ var _Player = class _Player {
5595
6019
  schema,
5596
6020
  expression: expressionEvaluator,
5597
6021
  binding: pathResolver,
5598
- validation: validationController
6022
+ validation: validationController,
6023
+ error: errorController
5599
6024
  },
5600
6025
  fail: flowResultDeferred.reject,
5601
6026
  flow: userFlow,
@@ -5670,6 +6095,11 @@ export {
5670
6095
  DependencyModel,
5671
6096
  DependencyTracker,
5672
6097
  EMPTY_NODE,
6098
+ ERROR_BINDING_PREFIX,
6099
+ ErrorController,
6100
+ ErrorSeverity,
6101
+ ErrorStateMiddleware,
6102
+ ErrorTypes,
5673
6103
  ExpNodeOpaqueIdentifier,
5674
6104
  ExpressionEvaluator,
5675
6105
  FlowController,
@@ -5689,6 +6119,8 @@ export {
5689
6119
  ProxyLogger,
5690
6120
  ROOT_BINDING,
5691
6121
  Resolver,
6122
+ ResolverError,
6123
+ ResolverStage,
5692
6124
  SCHEMA_VALIDATION_PROVIDER_NAME,
5693
6125
  SIMPLE_BINDING_REGEX,
5694
6126
  SchemaController,