@uniformdev/canvas 19.179.1-alpha.1 → 19.180.0

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/dist/index.mjs CHANGED
@@ -462,14 +462,17 @@ var nullLimitPolicy = async (func) => await func();
462
462
  // src/utils/rewriteFilters.ts
463
463
  var isPlainObject = (obj) => typeof obj === "object" && obj !== null && !Array.isArray(obj);
464
464
  function rewriteFilters(filters) {
465
- return Object.entries(filters != null ? filters : {}).reduce((acc, [key, value]) => {
466
- const lhs = `filters.${key}` + (isPlainObject(value) ? `[${Object.keys(value)[0]}]` : "");
467
- const rhs = isPlainObject(value) ? Object.values(value)[0] : value;
468
- return {
469
- ...acc,
470
- [lhs]: Array.isArray(rhs) ? rhs.map((v) => `${v}`.trim()).join(",") : `${rhs}`.trim()
471
- };
472
- }, {});
465
+ return Object.entries(filters != null ? filters : {}).reduce(
466
+ (acc, [key, value]) => {
467
+ const lhs = `filters.${key}` + (isPlainObject(value) ? `[${Object.keys(value)[0]}]` : "");
468
+ const rhs = isPlainObject(value) ? Object.values(value)[0] : value;
469
+ return {
470
+ ...acc,
471
+ [lhs]: Array.isArray(rhs) ? rhs.map((v) => `${v}`.trim()).join(",") : `${rhs}`.trim()
472
+ };
473
+ },
474
+ {}
475
+ );
473
476
  }
474
477
 
475
478
  // src/CanvasClient.ts
@@ -1254,7 +1257,16 @@ function walkNodeTree(node, visitor, options) {
1254
1257
  const propertyEntries = Object.entries(properties);
1255
1258
  for (let propIndex = propertyEntries.length - 1; propIndex >= 0; propIndex--) {
1256
1259
  const [propKey, propObject] = propertyEntries[propIndex];
1257
- if (!isNestedNodeType(propObject.type)) continue;
1260
+ if (!isNestedNodeType(propObject.type)) {
1261
+ continue;
1262
+ }
1263
+ if (!Array.isArray(propObject.value)) {
1264
+ throw new BlockFormatError(
1265
+ `${getComponentPath(currentQueueEntry.ancestorsAndSelf)}`,
1266
+ propKey,
1267
+ propObject.value
1268
+ );
1269
+ }
1258
1270
  const blocks = (_b = propObject.value) != null ? _b : [];
1259
1271
  for (let blockIndex = blocks.length - 1; blockIndex >= 0; blockIndex--) {
1260
1272
  const enqueueingBlock = blocks[blockIndex];
@@ -1290,6 +1302,17 @@ function getBlockValue(component, parameterName) {
1290
1302
  }
1291
1303
  return [];
1292
1304
  }
1305
+ var BlockFormatError = class _BlockFormatError extends Error {
1306
+ constructor(componentPath, propertyId, blockValue) {
1307
+ super(
1308
+ `${componentPath} has an invalid block property value on ${propertyId}. Block values must be arrays of blocks (BlockValue type), but received ${typeof blockValue}`
1309
+ );
1310
+ this.componentPath = componentPath;
1311
+ this.propertyId = propertyId;
1312
+ this.blockValue = blockValue;
1313
+ Object.setPrototypeOf(this, _BlockFormatError.prototype);
1314
+ }
1315
+ };
1293
1316
 
1294
1317
  // src/enhancement/enhance.ts
1295
1318
  async function enhance({
@@ -1599,6 +1622,86 @@ function findParameterInNodeTree(data, predicate) {
1599
1622
  return results;
1600
1623
  }
1601
1624
 
1625
+ // src/utils/variables/parseVariableExpression.ts
1626
+ var escapeCharacter = "\\";
1627
+ var variablePrefix = "${";
1628
+ var variableSuffix = "}";
1629
+ function parseVariableExpression(serialized, onToken) {
1630
+ let bufferStartIndex = 0;
1631
+ let bufferEndIndex = 0;
1632
+ let tokenCount = 0;
1633
+ const handleToken = (token, type) => {
1634
+ tokenCount++;
1635
+ return onToken == null ? void 0 : onToken(token, type);
1636
+ };
1637
+ let state = "text";
1638
+ for (let index = 0; index < serialized.length; index++) {
1639
+ const char = serialized[index];
1640
+ if (bufferStartIndex > bufferEndIndex) {
1641
+ bufferEndIndex = bufferStartIndex;
1642
+ }
1643
+ if (char === variablePrefix[0] && serialized[index + 1] === variablePrefix[1]) {
1644
+ if (serialized[index - 1] === escapeCharacter) {
1645
+ bufferEndIndex -= escapeCharacter.length;
1646
+ if (handleToken(serialized.substring(bufferStartIndex, bufferEndIndex), "text") === false) {
1647
+ return tokenCount;
1648
+ }
1649
+ bufferStartIndex = index;
1650
+ bufferEndIndex = index + 1;
1651
+ continue;
1652
+ }
1653
+ state = "variable";
1654
+ if (bufferEndIndex > bufferStartIndex) {
1655
+ if (handleToken(serialized.substring(bufferStartIndex, bufferEndIndex), "text") === false) {
1656
+ return tokenCount;
1657
+ }
1658
+ bufferStartIndex = bufferEndIndex;
1659
+ }
1660
+ index += variablePrefix.length - 1;
1661
+ bufferStartIndex += variablePrefix.length;
1662
+ continue;
1663
+ }
1664
+ if (char === variableSuffix && state === "variable") {
1665
+ if (serialized[index - 1] === escapeCharacter) {
1666
+ bufferEndIndex++;
1667
+ continue;
1668
+ }
1669
+ state = "text";
1670
+ if (bufferEndIndex > bufferStartIndex) {
1671
+ const unescapedVariableName = serialized.substring(bufferStartIndex, bufferEndIndex).replace(/\\([${}])/g, "$1");
1672
+ if (handleToken(unescapedVariableName, "variable") === false) {
1673
+ return tokenCount;
1674
+ }
1675
+ bufferStartIndex = bufferEndIndex + variableSuffix.length;
1676
+ }
1677
+ continue;
1678
+ }
1679
+ bufferEndIndex++;
1680
+ }
1681
+ if (bufferEndIndex > bufferStartIndex) {
1682
+ if (state === "variable") {
1683
+ state = "text";
1684
+ bufferStartIndex -= variablePrefix.length;
1685
+ }
1686
+ handleToken(serialized.substring(bufferStartIndex), state);
1687
+ }
1688
+ return tokenCount;
1689
+ }
1690
+
1691
+ // src/utils/variables/hasReferencedVariables.ts
1692
+ function hasReferencedVariables(value) {
1693
+ if (value === void 0) {
1694
+ return 0;
1695
+ }
1696
+ let variableTokenCount = 0;
1697
+ parseVariableExpression(value, (_, tokenType) => {
1698
+ if (tokenType === "variable") {
1699
+ variableTokenCount++;
1700
+ }
1701
+ });
1702
+ return variableTokenCount;
1703
+ }
1704
+
1602
1705
  // src/enhancement/visibility/evaluateVisibilityCriteriaGroup.ts
1603
1706
  function evaluateVisibilityCriteriaGroup(options) {
1604
1707
  const { criteriaGroup, simplifyCriteria } = options;
@@ -1618,12 +1721,18 @@ function evaluateVisibilityCriteriaGroup(options) {
1618
1721
  return hasIndeterminateClauses ? null : !earlyExitResult;
1619
1722
  }
1620
1723
  function evaluateCriterion(clause, rootOptions) {
1724
+ var _a;
1621
1725
  if ("clauses" in clause) {
1622
1726
  return evaluateVisibilityCriteriaGroup({
1623
1727
  ...rootOptions,
1624
1728
  criteriaGroup: clause
1625
1729
  });
1626
1730
  }
1731
+ const lhs = (_a = clause.source) != null ? _a : clause.rule;
1732
+ const rhs = Array.isArray(clause.value) ? clause.value : clause.value !== void 0 ? [clause.value] : void 0;
1733
+ if (hasReferencedVariables(lhs) > 0 || (rhs == null ? void 0 : rhs.some((rhv) => hasReferencedVariables(rhv) > 0))) {
1734
+ return null;
1735
+ }
1627
1736
  const rule = rootOptions.rules[clause.rule];
1628
1737
  if (rule) {
1629
1738
  return rule(clause);
@@ -1649,6 +1758,59 @@ function evaluateNodeVisibilityParameter({
1649
1758
  return result;
1650
1759
  }
1651
1760
 
1761
+ // src/enhancement/visibility/evaluatePropertyCriteria.ts
1762
+ function evaluatePropertyCriteria({
1763
+ baseValue,
1764
+ conditionalValues,
1765
+ keepIndeterminate,
1766
+ ...evaluateGroupOptions
1767
+ }) {
1768
+ let authoritative = true;
1769
+ const result = {
1770
+ currentValue: baseValue,
1771
+ remainingConditionalValues: evaluateGroupOptions.simplifyCriteria && conditionalValues ? [...conditionalValues] : conditionalValues,
1772
+ currentConditionIndex: -1
1773
+ };
1774
+ if (!conditionalValues) {
1775
+ return result;
1776
+ }
1777
+ const conditionIndexesToRemove = [];
1778
+ for (let i = 0; i < conditionalValues.length; i++) {
1779
+ const conditionalVariant = conditionalValues[i];
1780
+ if (result.matched) {
1781
+ conditionIndexesToRemove.push(i);
1782
+ continue;
1783
+ }
1784
+ const evaluationResult = evaluateVisibilityCriteriaGroup({
1785
+ ...evaluateGroupOptions,
1786
+ criteriaGroup: conditionalVariant.when
1787
+ });
1788
+ if (evaluationResult === null) {
1789
+ if (keepIndeterminate) {
1790
+ authoritative = false;
1791
+ } else {
1792
+ conditionIndexesToRemove.push(i);
1793
+ }
1794
+ } else if (evaluationResult === true) {
1795
+ result.matched = conditionalVariant;
1796
+ result.currentValue = conditionalVariant.value;
1797
+ result.currentConditionIndex = i;
1798
+ conditionIndexesToRemove.push(i);
1799
+ } else {
1800
+ conditionIndexesToRemove.push(i);
1801
+ }
1802
+ }
1803
+ if (evaluateGroupOptions.simplifyCriteria) {
1804
+ for (let i = conditionIndexesToRemove.length - 1; i >= 0; i--) {
1805
+ result.remainingConditionalValues.splice(conditionIndexesToRemove[i], 1);
1806
+ }
1807
+ }
1808
+ if (authoritative) {
1809
+ result.remainingConditionalValues = void 0;
1810
+ }
1811
+ return result;
1812
+ }
1813
+
1652
1814
  // src/enhancement/visibility/types.ts
1653
1815
  var CANVAS_VIZ_CONTROL_PARAM = "$viz";
1654
1816
 
@@ -1670,8 +1832,8 @@ function evaluateNodeVisibility({
1670
1832
  return result;
1671
1833
  }
1672
1834
 
1673
- // src/enhancement/visibility/evaluateWalkTreeVisibility.ts
1674
- function evaluateWalkTreeVisibility({
1835
+ // src/enhancement/visibility/evaluateWalkTreeNodeVisibility.ts
1836
+ function evaluateWalkTreeNodeVisibility({
1675
1837
  rules,
1676
1838
  showIndeterminate,
1677
1839
  context
@@ -1688,6 +1850,69 @@ function evaluateWalkTreeVisibility({
1688
1850
  return true;
1689
1851
  }
1690
1852
 
1853
+ // src/enhancement/visibility/evaluateWalkTreePropertyCriteria.ts
1854
+ function evaluateWalkTreePropertyCriteria({
1855
+ rules,
1856
+ node,
1857
+ keepIndeterminate
1858
+ }) {
1859
+ const properties = getPropertiesValue(node);
1860
+ if (!properties) {
1861
+ return;
1862
+ }
1863
+ Object.entries(properties).forEach(([propertyName, property]) => {
1864
+ var _a, _b, _c;
1865
+ if (property.locales || property.localesConditions) {
1866
+ const localesDefined = [
1867
+ ...Object.keys((_a = property.locales) != null ? _a : {}),
1868
+ ...Object.keys((_b = property.localesConditions) != null ? _b : {})
1869
+ ];
1870
+ localesDefined.forEach((locale) => {
1871
+ var _a2, _b2, _c2, _d, _e, _f, _g;
1872
+ const { currentValue, remainingConditionalValues } = evaluatePropertyCriteria({
1873
+ baseValue: (_a2 = property.locales) == null ? void 0 : _a2[locale],
1874
+ conditionalValues: (_b2 = property.localesConditions) == null ? void 0 : _b2[locale],
1875
+ rules,
1876
+ simplifyCriteria: true,
1877
+ keepIndeterminate
1878
+ });
1879
+ if (currentValue === null) {
1880
+ (_c2 = property.locales) == null ? true : delete _c2[locale];
1881
+ if (!Object.keys((_d = property.locales) != null ? _d : {}).length) {
1882
+ delete properties[propertyName];
1883
+ }
1884
+ } else {
1885
+ (_e = property.locales) != null ? _e : property.locales = {};
1886
+ property.locales[locale] = currentValue;
1887
+ }
1888
+ if (!(remainingConditionalValues == null ? void 0 : remainingConditionalValues.length)) {
1889
+ (_f = property.localesConditions) == null ? true : delete _f[locale];
1890
+ } else {
1891
+ (_g = property.localesConditions) != null ? _g : property.localesConditions = {};
1892
+ property.localesConditions[locale] = remainingConditionalValues;
1893
+ }
1894
+ });
1895
+ if (!Object.keys((_c = property.localesConditions) != null ? _c : {}).length) {
1896
+ delete property.localesConditions;
1897
+ }
1898
+ } else {
1899
+ const { currentValue, remainingConditionalValues } = evaluatePropertyCriteria({
1900
+ baseValue: property.value,
1901
+ conditionalValues: property.conditions,
1902
+ rules,
1903
+ simplifyCriteria: true,
1904
+ keepIndeterminate
1905
+ });
1906
+ if (currentValue === null) {
1907
+ delete properties[propertyName];
1908
+ } else {
1909
+ property.value = currentValue;
1910
+ }
1911
+ property.conditions = remainingConditionalValues;
1912
+ }
1913
+ });
1914
+ }
1915
+
1691
1916
  // src/enhancement/visibility/rules/evaluateStringMatch.ts
1692
1917
  var isEvaluator = (criteria, matchValue) => {
1693
1918
  if (Array.isArray(criteria)) {
@@ -1765,6 +1990,32 @@ function createDynamicInputVisibilityRule(dynamicInputs) {
1765
1990
  };
1766
1991
  }
1767
1992
 
1993
+ // src/enhancement/visibility/rules/createDynamicTokenVisibilityRule.ts
1994
+ var dynamicTokenVisibilityOperators = /* @__PURE__ */ new Set([
1995
+ "is",
1996
+ "!is",
1997
+ "has",
1998
+ "!has",
1999
+ "startswith",
2000
+ "!startswith",
2001
+ "endswith",
2002
+ "!endswith",
2003
+ "empty",
2004
+ "!empty"
2005
+ ]);
2006
+ var CANVAS_VIZ_DYNAMIC_TOKEN_RULE = "$dt";
2007
+ function createDynamicTokenVisibilityRule() {
2008
+ return {
2009
+ [CANVAS_VIZ_DYNAMIC_TOKEN_RULE]: (criterion) => {
2010
+ var _a;
2011
+ if (hasReferencedVariables(criterion.source)) {
2012
+ return null;
2013
+ }
2014
+ return evaluateStringMatch(criterion, (_a = criterion.source) != null ? _a : "", dynamicTokenVisibilityOperators);
2015
+ }
2016
+ };
2017
+ }
2018
+
1768
2019
  // src/enhancement/visibility/rules/createLocaleVisibilityRule.ts
1769
2020
  var localeVisibilityOperators = /* @__PURE__ */ new Set(["is", "!is"]);
1770
2021
  var CANVAS_VIZ_LOCALE_RULE = "$locale";
@@ -1832,7 +2083,7 @@ function localize(options) {
1832
2083
  return;
1833
2084
  }
1834
2085
  if (isUsingModernOptions) {
1835
- const result = evaluateWalkTreeVisibility({
2086
+ const result = evaluateWalkTreeNodeVisibility({
1836
2087
  context,
1837
2088
  rules: vizControlLocaleRule,
1838
2089
  showIndeterminate: true
@@ -1840,6 +2091,11 @@ function localize(options) {
1840
2091
  if (!result) {
1841
2092
  return;
1842
2093
  }
2094
+ evaluateWalkTreePropertyCriteria({
2095
+ node: context.node,
2096
+ rules: vizControlLocaleRule,
2097
+ keepIndeterminate: true
2098
+ });
1843
2099
  }
1844
2100
  if (node.type === CANVAS_LOCALIZATION_TYPE) {
1845
2101
  const locales = extractLocales({ component: node });
@@ -1906,13 +2162,16 @@ function localizeProperties(properties, locale) {
1906
2162
  // src/enhancement/UniqueBatchEntries.ts
1907
2163
  var UniqueBatchEntries = class {
1908
2164
  constructor(entries, uniqueKeySelector) {
1909
- this.groups = entries.reduce((acc, task) => {
1910
- var _a;
1911
- const key = uniqueKeySelector(task.args);
1912
- acc[key] = (_a = acc[key]) != null ? _a : [];
1913
- acc[key].push(task);
1914
- return acc;
1915
- }, {});
2165
+ this.groups = entries.reduce(
2166
+ (acc, task) => {
2167
+ var _a;
2168
+ const key = uniqueKeySelector(task.args);
2169
+ acc[key] = (_a = acc[key]) != null ? _a : [];
2170
+ acc[key].push(task);
2171
+ return acc;
2172
+ },
2173
+ {}
2174
+ );
1916
2175
  }
1917
2176
  /** Resolves all entries in a group key with the same result value. */
1918
2177
  resolveKey(key, result) {
@@ -2786,72 +3045,6 @@ var parseComponentPlaceholderId = (id) => {
2786
3045
  return result;
2787
3046
  };
2788
3047
 
2789
- // src/utils/variables/parseVariableExpression.ts
2790
- var escapeCharacter = "\\";
2791
- var variablePrefix = "${";
2792
- var variableSuffix = "}";
2793
- function parseVariableExpression(serialized, onToken) {
2794
- let bufferStartIndex = 0;
2795
- let bufferEndIndex = 0;
2796
- let tokenCount = 0;
2797
- const handleToken = (token, type) => {
2798
- tokenCount++;
2799
- return onToken == null ? void 0 : onToken(token, type);
2800
- };
2801
- let state = "text";
2802
- for (let index = 0; index < serialized.length; index++) {
2803
- const char = serialized[index];
2804
- if (bufferStartIndex > bufferEndIndex) {
2805
- bufferEndIndex = bufferStartIndex;
2806
- }
2807
- if (char === variablePrefix[0] && serialized[index + 1] === variablePrefix[1]) {
2808
- if (serialized[index - 1] === escapeCharacter) {
2809
- bufferEndIndex -= escapeCharacter.length;
2810
- if (handleToken(serialized.substring(bufferStartIndex, bufferEndIndex), "text") === false) {
2811
- return tokenCount;
2812
- }
2813
- bufferStartIndex = index;
2814
- bufferEndIndex = index + 1;
2815
- continue;
2816
- }
2817
- state = "variable";
2818
- if (bufferEndIndex > bufferStartIndex) {
2819
- if (handleToken(serialized.substring(bufferStartIndex, bufferEndIndex), "text") === false) {
2820
- return tokenCount;
2821
- }
2822
- bufferStartIndex = bufferEndIndex;
2823
- }
2824
- index += variablePrefix.length - 1;
2825
- bufferStartIndex += variablePrefix.length;
2826
- continue;
2827
- }
2828
- if (char === variableSuffix && state === "variable") {
2829
- if (serialized[index - 1] === escapeCharacter) {
2830
- bufferEndIndex++;
2831
- continue;
2832
- }
2833
- state = "text";
2834
- if (bufferEndIndex > bufferStartIndex) {
2835
- const unescapedVariableName = serialized.substring(bufferStartIndex, bufferEndIndex).replace(/\\([${}])/g, "$1");
2836
- if (handleToken(unescapedVariableName, "variable") === false) {
2837
- return tokenCount;
2838
- }
2839
- bufferStartIndex = bufferEndIndex + variableSuffix.length;
2840
- }
2841
- continue;
2842
- }
2843
- bufferEndIndex++;
2844
- }
2845
- if (bufferEndIndex > bufferStartIndex) {
2846
- if (state === "variable") {
2847
- state = "text";
2848
- bufferStartIndex -= variablePrefix.length;
2849
- }
2850
- handleToken(serialized.substring(bufferStartIndex), state);
2851
- }
2852
- return tokenCount;
2853
- }
2854
-
2855
3048
  // src/utils/variables/bindVariables.ts
2856
3049
  function bindVariables({
2857
3050
  variables,
@@ -3023,6 +3216,7 @@ export {
3023
3216
  ATTRIBUTE_PLACEHOLDER,
3024
3217
  ApiClientError2 as ApiClientError,
3025
3218
  BatchEntry,
3219
+ BlockFormatError,
3026
3220
  CANVAS_BLOCK_PARAM_TYPE,
3027
3221
  CANVAS_DRAFT_STATE,
3028
3222
  CANVAS_EDITOR_STATE,
@@ -3042,6 +3236,7 @@ export {
3042
3236
  CANVAS_TEST_VARIANT_PARAM,
3043
3237
  CANVAS_VIZ_CONTROL_PARAM,
3044
3238
  CANVAS_VIZ_DI_RULE,
3239
+ CANVAS_VIZ_DYNAMIC_TOKEN_RULE,
3045
3240
  CANVAS_VIZ_LOCALE_RULE,
3046
3241
  CANVAS_VIZ_QUIRKS_RULE,
3047
3242
  CanvasClient,
@@ -3087,6 +3282,7 @@ export {
3087
3282
  createBatchEnhancer,
3088
3283
  createCanvasChannel,
3089
3284
  createDynamicInputVisibilityRule,
3285
+ createDynamicTokenVisibilityRule,
3090
3286
  createEventBus,
3091
3287
  createLimitPolicy,
3092
3288
  createLocaleVisibilityRule,
@@ -3095,8 +3291,10 @@ export {
3095
3291
  createVariableReference,
3096
3292
  enhance,
3097
3293
  evaluateNodeVisibilityParameter,
3294
+ evaluatePropertyCriteria,
3098
3295
  evaluateVisibilityCriteriaGroup,
3099
- evaluateWalkTreeVisibility,
3296
+ evaluateWalkTreeNodeVisibility,
3297
+ evaluateWalkTreePropertyCriteria,
3100
3298
  extractLocales,
3101
3299
  findParameterInNodeTree,
3102
3300
  flattenValues,
@@ -3112,6 +3310,7 @@ export {
3112
3310
  getParameterAttributes,
3113
3311
  getPropertiesValue,
3114
3312
  getPropertyValue,
3313
+ hasReferencedVariables,
3115
3314
  isAddComponentMessage,
3116
3315
  isAllowedReferrer,
3117
3316
  isAssetParamValue,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uniformdev/canvas",
3
- "version": "19.179.1-alpha.1+3adc08c00a",
3
+ "version": "19.180.0",
4
4
  "description": "Common functionality and types for Uniform Canvas",
5
5
  "license": "SEE LICENSE IN LICENSE.txt",
6
6
  "main": "./dist/index.js",
@@ -32,14 +32,14 @@
32
32
  },
33
33
  "devDependencies": {
34
34
  "@types/retry": "0.12.5",
35
- "lexical": "0.16.1",
35
+ "lexical": "0.17.0",
36
36
  "p-retry": "5.1.2",
37
37
  "p-throttle": "5.0.0",
38
38
  "pusher-js": "8.2.0"
39
39
  },
40
40
  "dependencies": {
41
- "@uniformdev/assets": "19.179.1-alpha.1+3adc08c00a",
42
- "@uniformdev/context": "19.179.1-alpha.1+3adc08c00a",
41
+ "@uniformdev/assets": "19.180.0",
42
+ "@uniformdev/context": "19.180.0",
43
43
  "immer": "10.1.1"
44
44
  },
45
45
  "files": [
@@ -48,5 +48,5 @@
48
48
  "publishConfig": {
49
49
  "access": "public"
50
50
  },
51
- "gitHead": "3adc08c00a0d3932b6b767faaa6157f8b71ef0a0"
51
+ "gitHead": "309600b88571d0ffb2b80d7d2c27b7aa3e5ae72b"
52
52
  }