@wix/headless-restaurants-olo 0.0.15 → 0.0.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cjs/dist/react/ItemDetails.d.ts +4 -131
- package/cjs/dist/react/ItemDetails.js +4 -55
- package/cjs/dist/react/ModifierGroup.d.ts +57 -0
- package/cjs/dist/react/ModifierGroup.js +61 -0
- package/cjs/dist/react/core/ItemDetails.d.ts +1 -11
- package/cjs/dist/react/core/ItemDetails.js +1 -20
- package/cjs/dist/react/core/ModifierGroup.d.ts +42 -0
- package/cjs/dist/react/core/ModifierGroup.js +67 -0
- package/cjs/dist/react/index.d.ts +1 -0
- package/cjs/dist/react/index.js +1 -0
- package/cjs/dist/services/common-types.d.ts +18 -0
- package/cjs/dist/services/common-types.js +10 -0
- package/cjs/dist/services/item-details-service.d.ts +2 -0
- package/cjs/dist/services/item-details-service.js +8 -1
- package/cjs/dist/services/utils.d.ts +17 -0
- package/cjs/dist/services/utils.js +104 -0
- package/dist/react/ItemDetails.d.ts +4 -131
- package/dist/react/ItemDetails.js +4 -55
- package/dist/react/ModifierGroup.d.ts +57 -0
- package/dist/react/ModifierGroup.js +61 -0
- package/dist/react/core/ItemDetails.d.ts +1 -11
- package/dist/react/core/ItemDetails.js +1 -20
- package/dist/react/core/ModifierGroup.d.ts +42 -0
- package/dist/react/core/ModifierGroup.js +67 -0
- package/dist/react/index.d.ts +1 -0
- package/dist/react/index.js +1 -0
- package/dist/services/common-types.d.ts +18 -0
- package/dist/services/common-types.js +10 -0
- package/dist/services/item-details-service.d.ts +2 -0
- package/dist/services/item-details-service.js +8 -1
- package/dist/services/utils.d.ts +17 -0
- package/dist/services/utils.js +104 -0
- package/package.json +5 -6
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import React, { createContext } from 'react';
|
|
3
|
+
import { useService } from '@wix/services-manager-react';
|
|
4
|
+
import { useModifierGroupContext } from '@wix/headless-restaurants-menus/react';
|
|
5
|
+
import { convertModifierToFormModifier, getModifierGroupRuleType, getRuleTypeMapValue, isSingleSelectRule, } from '../../services/utils.js';
|
|
6
|
+
import { ItemServiceDefinition } from '../../services/item-details-service.js';
|
|
7
|
+
const ModifiersContext = createContext(null);
|
|
8
|
+
export function useModifiersContext() {
|
|
9
|
+
const context = React.useContext(ModifiersContext);
|
|
10
|
+
if (!context) {
|
|
11
|
+
throw new Error('useModifiersContext must be used within a ModifierGroupComponent');
|
|
12
|
+
}
|
|
13
|
+
return context;
|
|
14
|
+
}
|
|
15
|
+
export const ModifierGroupComponent = ({ children, }) => {
|
|
16
|
+
const service = useService(ItemServiceDefinition);
|
|
17
|
+
const { modifierGroup } = useModifierGroupContext();
|
|
18
|
+
const rule = modifierGroup.rule;
|
|
19
|
+
const isSingleSelect = Boolean(rule ? isSingleSelectRule(rule) : false);
|
|
20
|
+
const groupId = modifierGroup._id;
|
|
21
|
+
const groupSelectedModifierIds = service.getSelectedModifiers?.(groupId ?? '');
|
|
22
|
+
const onToggle = (modifierId) => {
|
|
23
|
+
if (groupId) {
|
|
24
|
+
service.toggleModifier?.(groupId, modifierId, isSingleSelect);
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
const ruleType = getModifierGroupRuleType(modifierGroup.rule);
|
|
28
|
+
const contextValue = {
|
|
29
|
+
selectedModifierIds: groupSelectedModifierIds,
|
|
30
|
+
onToggle,
|
|
31
|
+
modifierGroup,
|
|
32
|
+
modifiers: modifierGroup.modifiers.map(convertModifierToFormModifier),
|
|
33
|
+
isSingleSelect,
|
|
34
|
+
ruleType,
|
|
35
|
+
};
|
|
36
|
+
return (_jsx(ModifiersContext.Provider, { value: contextValue, children: children }));
|
|
37
|
+
};
|
|
38
|
+
export const ModifiersComponent = ({ children, }) => {
|
|
39
|
+
const { selectedModifierIds, onToggle, modifierGroup, modifiers, isSingleSelect, } = useModifiersContext();
|
|
40
|
+
const singleSelectedModifierId = selectedModifierIds.length > 0 ? (selectedModifierIds[0] ?? '') : '';
|
|
41
|
+
return children({
|
|
42
|
+
selectedModifierIds,
|
|
43
|
+
onToggle,
|
|
44
|
+
modifierGroup,
|
|
45
|
+
modifiers,
|
|
46
|
+
isSingleSelect,
|
|
47
|
+
singleSelectedModifierId,
|
|
48
|
+
});
|
|
49
|
+
};
|
|
50
|
+
export const Description = ({ ruleTypeMap, children, }) => {
|
|
51
|
+
const { ruleType, modifierGroup } = useModifiersContext();
|
|
52
|
+
const modifierGroupName = modifierGroup.name || '';
|
|
53
|
+
const description = getRuleTypeMapValue(ruleTypeMap, ruleType, modifierGroupName, modifierGroup.rule);
|
|
54
|
+
return children({ description });
|
|
55
|
+
};
|
|
56
|
+
export const GroupError = ({ ruleTypeMap, children, }) => {
|
|
57
|
+
const service = useService(ItemServiceDefinition);
|
|
58
|
+
const { ruleType, modifierGroup } = useModifiersContext();
|
|
59
|
+
const groupId = modifierGroup._id || '';
|
|
60
|
+
const modifierGroupErrors = service.modifierGroupError?.get() || {};
|
|
61
|
+
const modifierGroupName = modifierGroup.name || '';
|
|
62
|
+
let error;
|
|
63
|
+
if (modifierGroupErrors[groupId]) {
|
|
64
|
+
error = getRuleTypeMapValue(ruleTypeMap, ruleType, modifierGroupName, modifierGroup.rule);
|
|
65
|
+
}
|
|
66
|
+
return children({ error });
|
|
67
|
+
};
|
package/dist/react/index.d.ts
CHANGED
package/dist/react/index.js
CHANGED
|
@@ -1,6 +1,24 @@
|
|
|
1
1
|
import * as currentCart from '@wix/auto_sdk_ecom_current-cart';
|
|
2
2
|
export type LineItem = currentCart.LineItem;
|
|
3
3
|
export type DescriptionLine = currentCart.DescriptionLine;
|
|
4
|
+
export declare enum RuleType {
|
|
5
|
+
NO_LIMIT = "NO_LIMIT",
|
|
6
|
+
CHOOSE_ONE = "CHOOSE_ONE",
|
|
7
|
+
CHOOSE_X = "CHOOSE_X",
|
|
8
|
+
CHOOSE_AT_LEAST_ONE = "CHOOSE_AT_LEAST_ONE",
|
|
9
|
+
CHOOSE_AT_LEAST_X = "CHOOSE_AT_LEAST_X",
|
|
10
|
+
CHOOSE_UP_TO_X = "CHOOSE_UP_TO_X",
|
|
11
|
+
CHOOSE_BETWEEN_X_AND_Y = "CHOOSE_BETWEEN_X_AND_Y"
|
|
12
|
+
}
|
|
13
|
+
export interface RuleTypeMap {
|
|
14
|
+
[RuleType.NO_LIMIT]?: (modifierGroupName: string) => string;
|
|
15
|
+
[RuleType.CHOOSE_ONE]?: (modifierGroupName: string) => string;
|
|
16
|
+
[RuleType.CHOOSE_X]?: (modifierGroupName: string, x: number) => string;
|
|
17
|
+
[RuleType.CHOOSE_AT_LEAST_ONE]?: (modifierGroupName: string) => string;
|
|
18
|
+
[RuleType.CHOOSE_AT_LEAST_X]?: (modifierGroupName: string, x: number) => string;
|
|
19
|
+
[RuleType.CHOOSE_UP_TO_X]?: (modifierGroupName: string, x: number) => string;
|
|
20
|
+
[RuleType.CHOOSE_BETWEEN_X_AND_Y]?: (modifierGroupName: string, x: number, y: number) => string;
|
|
21
|
+
}
|
|
4
22
|
export declare enum AvailabilityStatus {
|
|
5
23
|
AVAILABLE = 0,
|
|
6
24
|
NOT_AVAILABLE = 1,
|
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
export var RuleType;
|
|
2
|
+
(function (RuleType) {
|
|
3
|
+
RuleType["NO_LIMIT"] = "NO_LIMIT";
|
|
4
|
+
RuleType["CHOOSE_ONE"] = "CHOOSE_ONE";
|
|
5
|
+
RuleType["CHOOSE_X"] = "CHOOSE_X";
|
|
6
|
+
RuleType["CHOOSE_AT_LEAST_ONE"] = "CHOOSE_AT_LEAST_ONE";
|
|
7
|
+
RuleType["CHOOSE_AT_LEAST_X"] = "CHOOSE_AT_LEAST_X";
|
|
8
|
+
RuleType["CHOOSE_UP_TO_X"] = "CHOOSE_UP_TO_X";
|
|
9
|
+
RuleType["CHOOSE_BETWEEN_X_AND_Y"] = "CHOOSE_BETWEEN_X_AND_Y";
|
|
10
|
+
})(RuleType || (RuleType = {}));
|
|
1
11
|
export var AvailabilityStatus;
|
|
2
12
|
(function (AvailabilityStatus) {
|
|
3
13
|
AvailabilityStatus[AvailabilityStatus["AVAILABLE"] = 0] = "AVAILABLE";
|
|
@@ -23,6 +23,8 @@ export interface ItemServiceAPI {
|
|
|
23
23
|
isLoading: Signal<boolean>;
|
|
24
24
|
/** Reactive signal containing any error message, or null if no error */
|
|
25
25
|
error: Signal<string | null>;
|
|
26
|
+
/** Reactive signal containing error state for each modifier group */
|
|
27
|
+
modifierGroupError: Signal<Record<string, boolean>>;
|
|
26
28
|
/** Function to update the quantity of the item */
|
|
27
29
|
updateQuantity: (quantity: number) => void;
|
|
28
30
|
/** Function to update the special request of the item */
|
|
@@ -58,7 +58,13 @@ export const ItemService = implementService.withConfig()(ItemServiceDefinition,
|
|
|
58
58
|
const modifierGroups = config.item?.modifierGroups || [];
|
|
59
59
|
const initialSelectedModifiers = getModifiersInitState(modifierGroups);
|
|
60
60
|
const selectedModifiers = signalsService.signal(initialSelectedModifiers);
|
|
61
|
-
|
|
61
|
+
const initialModifierGroupError = modifierGroups.reduce((acc, group) => {
|
|
62
|
+
if (group._id) {
|
|
63
|
+
acc[group._id] = false;
|
|
64
|
+
}
|
|
65
|
+
return acc;
|
|
66
|
+
}, {});
|
|
67
|
+
const modifierGroupError = signalsService.signal(initialModifierGroupError);
|
|
62
68
|
if (config.item) {
|
|
63
69
|
lineItem.set({
|
|
64
70
|
quantity: quantity.get(),
|
|
@@ -150,6 +156,7 @@ export const ItemService = implementService.withConfig()(ItemServiceDefinition,
|
|
|
150
156
|
selectedModifiers,
|
|
151
157
|
availabilityStatus,
|
|
152
158
|
getSelectedModifiers,
|
|
159
|
+
modifierGroupError,
|
|
153
160
|
};
|
|
154
161
|
});
|
|
155
162
|
/**
|
package/dist/services/utils.d.ts
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
import type { EnhancedModifier, EnhancedModifierGroup } from '@wix/headless-restaurants-menus/services';
|
|
2
|
+
import { RuleType, RuleTypeMap } from './common-types.js';
|
|
3
|
+
interface ruleUtilsArgs {
|
|
4
|
+
required: boolean;
|
|
5
|
+
minSelections: number;
|
|
6
|
+
maxSelections?: number | null;
|
|
7
|
+
}
|
|
2
8
|
export declare const getModifiersInitState: (modifierGroups: EnhancedModifierGroup[]) => Record<string, string[]>;
|
|
3
9
|
export declare const isSingleSelectRule: (rule: NonNullable<EnhancedModifierGroup["rule"]>) => boolean | null | undefined;
|
|
4
10
|
export declare const getFirstPreSelectedModifier: (modifiers: EnhancedModifier[]) => string | null | undefined;
|
|
@@ -18,3 +24,14 @@ export declare const convertModifierToFormModifier: (modifier: EnhancedModifier,
|
|
|
18
24
|
};
|
|
19
25
|
preSelected?: boolean;
|
|
20
26
|
};
|
|
27
|
+
export declare const getModifierGroupRuleType: (modifierGroupRule: EnhancedModifierGroup["rule"]) => RuleType;
|
|
28
|
+
export declare const hasNoLimit: ({ required, minSelections, maxSelections, }: ruleUtilsArgs) => boolean;
|
|
29
|
+
export declare const canChooseOne: ({ required, minSelections, maxSelections, }: ruleUtilsArgs) => boolean;
|
|
30
|
+
export declare const hasToChooseOne: ({ required, minSelections, maxSelections, }: ruleUtilsArgs) => boolean;
|
|
31
|
+
export declare const hasToChooseX: ({ required, minSelections, maxSelections, }: ruleUtilsArgs) => boolean | 0 | null | undefined;
|
|
32
|
+
export declare const hasToChooseAtLeastOne: ({ required, minSelections, maxSelections, }: ruleUtilsArgs) => boolean;
|
|
33
|
+
export declare const hasToChooseAtLeastX: ({ required, minSelections, maxSelections, }: ruleUtilsArgs) => boolean;
|
|
34
|
+
export declare const chooseUpToX: ({ required, minSelections, maxSelections, }: ruleUtilsArgs) => boolean;
|
|
35
|
+
export declare const hasToChooseBetweenXAndY: ({ required, minSelections, maxSelections, }: ruleUtilsArgs) => boolean;
|
|
36
|
+
export declare const getRuleTypeMapValue: (ruleTypeMap: RuleTypeMap, ruleType: RuleType, modifierGroupName: string, rule: EnhancedModifierGroup["rule"]) => string | undefined;
|
|
37
|
+
export {};
|
package/dist/services/utils.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { RuleType } from './common-types.js';
|
|
1
2
|
export const getModifiersInitState = (modifierGroups) => {
|
|
2
3
|
const initialSelectedModifiers = {};
|
|
3
4
|
modifierGroups.forEach((group) => {
|
|
@@ -32,3 +33,106 @@ export const convertModifierToFormModifier = (modifier, index) => {
|
|
|
32
33
|
_id: `${modifier._id}~${index}`,
|
|
33
34
|
};
|
|
34
35
|
};
|
|
36
|
+
export const getModifierGroupRuleType = (modifierGroupRule) => {
|
|
37
|
+
const required = modifierGroupRule?.required ?? false;
|
|
38
|
+
const minSelections = modifierGroupRule?.minSelections ?? 0;
|
|
39
|
+
const maxSelections = modifierGroupRule?.maxSelections;
|
|
40
|
+
const ruleFields = { required, minSelections, maxSelections };
|
|
41
|
+
if (hasNoLimit(ruleFields)) {
|
|
42
|
+
return RuleType.NO_LIMIT;
|
|
43
|
+
}
|
|
44
|
+
if (hasToChooseOne(ruleFields) || canChooseOne(ruleFields)) {
|
|
45
|
+
return RuleType.CHOOSE_ONE;
|
|
46
|
+
}
|
|
47
|
+
if (hasToChooseX(ruleFields)) {
|
|
48
|
+
return RuleType.CHOOSE_X;
|
|
49
|
+
}
|
|
50
|
+
if (hasToChooseAtLeastOne(ruleFields)) {
|
|
51
|
+
return RuleType.CHOOSE_AT_LEAST_ONE;
|
|
52
|
+
}
|
|
53
|
+
if (hasToChooseAtLeastX(ruleFields)) {
|
|
54
|
+
return RuleType.CHOOSE_AT_LEAST_X;
|
|
55
|
+
}
|
|
56
|
+
if (chooseUpToX(ruleFields)) {
|
|
57
|
+
return RuleType.CHOOSE_UP_TO_X;
|
|
58
|
+
}
|
|
59
|
+
if (hasToChooseBetweenXAndY(ruleFields)) {
|
|
60
|
+
return RuleType.CHOOSE_BETWEEN_X_AND_Y;
|
|
61
|
+
}
|
|
62
|
+
return RuleType.NO_LIMIT;
|
|
63
|
+
};
|
|
64
|
+
const hasNoValue = (variable) => variable === null || variable === undefined;
|
|
65
|
+
export const hasNoLimit = ({ required, minSelections, maxSelections, }) => !required && minSelections < 1 && (maxSelections ? maxSelections < 1 : true);
|
|
66
|
+
export const canChooseOne = ({ required, minSelections, maxSelections, }) => !required &&
|
|
67
|
+
minSelections === 0 &&
|
|
68
|
+
(maxSelections ? maxSelections === 1 : false);
|
|
69
|
+
export const hasToChooseOne = ({ required, minSelections, maxSelections, }) => required &&
|
|
70
|
+
minSelections === 1 &&
|
|
71
|
+
(maxSelections ? maxSelections === 1 : false);
|
|
72
|
+
export const hasToChooseX = ({ required, minSelections, maxSelections, }) => required ? maxSelections && minSelections === maxSelections : false;
|
|
73
|
+
export const hasToChooseAtLeastOne = ({ required, minSelections, maxSelections, }) => required ? minSelections === 1 && hasNoValue(maxSelections) : false;
|
|
74
|
+
export const hasToChooseAtLeastX = ({ required, minSelections, maxSelections, }) => required ? minSelections > 1 && hasNoValue(maxSelections) : false;
|
|
75
|
+
export const chooseUpToX = ({ required, minSelections, maxSelections, }) => !required &&
|
|
76
|
+
minSelections === 0 &&
|
|
77
|
+
(maxSelections ? maxSelections > 1 : false);
|
|
78
|
+
export const hasToChooseBetweenXAndY = ({ required, minSelections, maxSelections, }) => required
|
|
79
|
+
? maxSelections
|
|
80
|
+
? minSelections > 0 && maxSelections > minSelections
|
|
81
|
+
: false
|
|
82
|
+
: false;
|
|
83
|
+
export const getRuleTypeMapValue = (ruleTypeMap, ruleType, modifierGroupName, rule) => {
|
|
84
|
+
const minSelections = rule?.minSelections ?? 0;
|
|
85
|
+
const maxSelections = rule?.maxSelections;
|
|
86
|
+
switch (ruleType) {
|
|
87
|
+
case RuleType.NO_LIMIT: {
|
|
88
|
+
const callback = ruleTypeMap[RuleType.NO_LIMIT];
|
|
89
|
+
if (callback) {
|
|
90
|
+
return callback(modifierGroupName);
|
|
91
|
+
}
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
94
|
+
case RuleType.CHOOSE_ONE: {
|
|
95
|
+
const callback = ruleTypeMap[RuleType.CHOOSE_ONE];
|
|
96
|
+
if (callback) {
|
|
97
|
+
return callback(modifierGroupName);
|
|
98
|
+
}
|
|
99
|
+
break;
|
|
100
|
+
}
|
|
101
|
+
case RuleType.CHOOSE_X: {
|
|
102
|
+
const callback = ruleTypeMap[RuleType.CHOOSE_X];
|
|
103
|
+
if (callback) {
|
|
104
|
+
return callback(modifierGroupName, minSelections);
|
|
105
|
+
}
|
|
106
|
+
break;
|
|
107
|
+
}
|
|
108
|
+
case RuleType.CHOOSE_AT_LEAST_ONE: {
|
|
109
|
+
const callback = ruleTypeMap[RuleType.CHOOSE_AT_LEAST_ONE];
|
|
110
|
+
if (callback) {
|
|
111
|
+
return callback(modifierGroupName);
|
|
112
|
+
}
|
|
113
|
+
break;
|
|
114
|
+
}
|
|
115
|
+
case RuleType.CHOOSE_AT_LEAST_X: {
|
|
116
|
+
const callback = ruleTypeMap[RuleType.CHOOSE_AT_LEAST_X];
|
|
117
|
+
if (callback) {
|
|
118
|
+
return callback(modifierGroupName, minSelections);
|
|
119
|
+
}
|
|
120
|
+
break;
|
|
121
|
+
}
|
|
122
|
+
case RuleType.CHOOSE_UP_TO_X: {
|
|
123
|
+
const callback = ruleTypeMap[RuleType.CHOOSE_UP_TO_X];
|
|
124
|
+
if (callback && maxSelections) {
|
|
125
|
+
return callback(modifierGroupName, maxSelections);
|
|
126
|
+
}
|
|
127
|
+
break;
|
|
128
|
+
}
|
|
129
|
+
case RuleType.CHOOSE_BETWEEN_X_AND_Y: {
|
|
130
|
+
const callback = ruleTypeMap[RuleType.CHOOSE_BETWEEN_X_AND_Y];
|
|
131
|
+
if (callback && maxSelections) {
|
|
132
|
+
return callback(modifierGroupName, minSelections, maxSelections);
|
|
133
|
+
}
|
|
134
|
+
break;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return undefined;
|
|
138
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wix/headless-restaurants-olo",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.17",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
@@ -40,6 +40,7 @@
|
|
|
40
40
|
"@typescript-eslint/eslint-plugin": "^6.21.0",
|
|
41
41
|
"@typescript-eslint/parser": "^6.21.0",
|
|
42
42
|
"@vitest/ui": "^3.1.4",
|
|
43
|
+
"@wix/headless-components": "0.0.0",
|
|
43
44
|
"eslint": "^8.57.0",
|
|
44
45
|
"eslint-config-prettier": "^10.1.8",
|
|
45
46
|
"eslint-plugin-react": "^7.34.1",
|
|
@@ -55,8 +56,9 @@
|
|
|
55
56
|
"@radix-ui/react-slot": "^1.1.0",
|
|
56
57
|
"@wix/auto_sdk_restaurants_items": "^1.0.48",
|
|
57
58
|
"@wix/ecom": "^1.0.1461",
|
|
59
|
+
"@wix/headless-components": "0.0.27",
|
|
58
60
|
"@wix/headless-media": "0.0.17",
|
|
59
|
-
"@wix/headless-restaurants-menus": "0.0.
|
|
61
|
+
"@wix/headless-restaurants-menus": "0.0.20",
|
|
60
62
|
"@wix/headless-utils": "0.0.7",
|
|
61
63
|
"@wix/redirects": "^1.0.0",
|
|
62
64
|
"@wix/restaurants": "^1.0.396",
|
|
@@ -64,9 +66,6 @@
|
|
|
64
66
|
"@wix/services-definitions": "^0.1.4",
|
|
65
67
|
"@wix/services-manager-react": "^0.1.26"
|
|
66
68
|
},
|
|
67
|
-
"peerDependencies": {
|
|
68
|
-
"@wix/headless-components": "^0.0.0"
|
|
69
|
-
},
|
|
70
69
|
"publishConfig": {
|
|
71
70
|
"registry": "https://registry.npmjs.org/",
|
|
72
71
|
"access": "public"
|
|
@@ -77,5 +76,5 @@
|
|
|
77
76
|
"groupId": "com.wixpress.headless-components"
|
|
78
77
|
}
|
|
79
78
|
},
|
|
80
|
-
"falconPackageHash": "
|
|
79
|
+
"falconPackageHash": "4642f5bdaedb6d472e41409ca88d2be3914e062b018972f37b44ea55"
|
|
81
80
|
}
|