@lwrjs/config 0.11.0-alpha.4 → 0.11.0-alpha.6

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.
@@ -66,11 +66,13 @@ function executeStartHooks(hooks, globalConfig, runtimeEnvironment, skipValidate
66
66
  }
67
67
  const onStartConfig = {
68
68
  basePath: globalConfig.basePath,
69
+ i18n: globalConfig.i18n,
69
70
  routes: globalConfig.routes
70
71
  };
71
72
  hook.onStart(onStartConfig);
72
- globalConfig.routes = onStartConfig.routes || [];
73
73
  runtimeEnvironment.basePath = globalConfig.basePath = onStartConfig.basePath || "";
74
+ globalConfig.i18n = onStartConfig.i18n;
75
+ globalConfig.routes = onStartConfig.routes || [];
74
76
  }
75
77
  globalConfig.routes = (0, import_routes.normalizeRoutes)(globalConfig.routes, globalConfig.routeHandlers);
76
78
  const ssrConfig = (0, import_global_config.applySsrConfig)(globalConfig);
@@ -61,7 +61,7 @@ function normalizeRoutePaths(routes = [], resourcePaths) {
61
61
  return routes.map((route) => {
62
62
  const {contentTemplate, layoutTemplate, subRoutes} = route;
63
63
  if (contentTemplate) {
64
- route.contentTemplate = import_path.default.resolve((0, import_shared_utils.normalizeResourcePath)(contentTemplate, resourcePaths));
64
+ route.contentTemplate = typeof contentTemplate === "string" ? import_path.default.resolve((0, import_shared_utils.normalizeResourcePath)(contentTemplate, resourcePaths)) : contentTemplate;
65
65
  }
66
66
  if (layoutTemplate) {
67
67
  route.layoutTemplate = import_path.default.resolve((0, import_shared_utils.normalizeResourcePath)(layoutTemplate, resourcePaths));
@@ -38,6 +38,7 @@ __export(exports, {
38
38
  var import_jsonc_parser = __toModule(require("jsonc-parser"));
39
39
  var import_diagnostics = __toModule(require("@lwrjs/diagnostics"));
40
40
  var import_helpers = __toModule(require("./helpers.cjs"));
41
+ var import_shared_utils = __toModule(require("@lwrjs/shared-utils"));
41
42
  function createKeys(p, t) {
42
43
  return p && t;
43
44
  }
@@ -82,7 +83,7 @@ var ROOT_ATTRIBUTE_KEYS = createKeys("root", [
82
83
  var ASSET_DIR_ATTRIBUTE_KEYS = createKeys("assetDir", ["alias", "dir", "urlPath", "root"]);
83
84
  var ASSET_FILE_ATTRIBUTE_KEYS = createKeys("assetFile", ["alias", "file", "urlPath"]);
84
85
  var LOCKER_ATTRIBUTE_KEYS = createKeys("locker", ["enabled", "trustedComponents", "clientOnly"]);
85
- var I18N_ATTRIBUTE_KEYS = createKeys("i18n", ["defaultLocale", "locales"]);
86
+ var I18N_ATTRIBUTE_KEYS = createKeys("i18n", ["defaultLocale", "locales", "uriPattern"]);
86
87
  var ROUTE_ATTRIBUTE_KEYS = createKeys("routes", [
87
88
  "bootstrap",
88
89
  "subRoutes",
@@ -119,7 +120,6 @@ var BOOTSTRAP_ATTRIBUTE_KEYS = createKeys("bootstrap", [
119
120
  "module",
120
121
  "preloadModules"
121
122
  ]);
122
- var SPECIFIER_REGEX = /^@?[\w-]+(\/[\w-]+)*$/;
123
123
  function isNotEmptyString(node) {
124
124
  return node.type === "string" && node.value.length > 0;
125
125
  }
@@ -157,7 +157,7 @@ var ValidationContext = class {
157
157
  }
158
158
  }
159
159
  assertIsSpecifier(node, property) {
160
- if (node && (node.type !== "string" || !SPECIFIER_REGEX.test(node.value))) {
160
+ if (node && (node.type !== "string" || !(0, import_shared_utils.isSpecifier)(node.value))) {
161
161
  this.diagnostics.push({
162
162
  description: import_diagnostics.descriptions.CONFIG_PARSER.INVALID_SPECIFIER(property, node.value),
163
163
  location: this.getLocationFromNode(node)
@@ -334,7 +334,7 @@ var ValidationContext = class {
334
334
  });
335
335
  } else if (node.children && node.children.length > 0) {
336
336
  node.children.forEach((n, index) => {
337
- if (n.type !== "string" || !SPECIFIER_REGEX.test(n.value)) {
337
+ if (n.type !== "string" || !(0, import_shared_utils.isSpecifier)(n.value)) {
338
338
  this.diagnostics.push({
339
339
  description: import_diagnostics.descriptions.CONFIG_PARSER.INVALID_SPECIFIER(`${property}[${index}]`, n.value),
340
340
  location: this.getLocationFromNode(n)
@@ -356,6 +356,22 @@ var ValidationContext = class {
356
356
  node.children.forEach((n, index) => this.assertIsService(n, property, index));
357
357
  }
358
358
  }
359
+ assertIsStringOrObject(node, property, index) {
360
+ if (!node) {
361
+ return;
362
+ }
363
+ if (node.type !== "string" && node.type !== "object") {
364
+ this.diagnostics.push({
365
+ description: import_diagnostics.descriptions.CONFIG_PARSER.INCORRECT_NODE_TYPE(index !== void 0 ? `${property}[${index}]` : property, "string or object", node.type),
366
+ location: this.getLocationFromNode(node)
367
+ });
368
+ } else if (node.type === "string" && !isNotEmptyString(node)) {
369
+ this.diagnostics.push({
370
+ description: import_diagnostics.descriptions.CONFIG_PARSER.NON_EMPTY_STRING(property, node.value),
371
+ location: this.getLocationFromNode(node)
372
+ });
373
+ }
374
+ }
359
375
  assertIsService(node, property, index) {
360
376
  if (!node) {
361
377
  return;
@@ -456,4 +472,21 @@ var ValidationContext = class {
456
472
  });
457
473
  }
458
474
  }
475
+ assertFallbackIds(nodes) {
476
+ const localesIds = nodes.map((n) => {
477
+ const idNode = (0, import_jsonc_parser.findNodeAtLocation)(n, ["id"]);
478
+ return idNode ? idNode.value : void 0;
479
+ }).filter((id) => id !== void 0);
480
+ for (const n of nodes) {
481
+ const fallbackNode = (0, import_jsonc_parser.findNodeAtLocation)(n, ["fallback"]);
482
+ if (fallbackNode?.value) {
483
+ if (!localesIds.includes(fallbackNode.value)) {
484
+ this.diagnostics.push({
485
+ description: import_diagnostics.descriptions.CONFIG_PARSER.FALLBACK_NOT_IN_LOCALES(fallbackNode.value, localesIds),
486
+ location: this.getLocationFromNode(fallbackNode)
487
+ });
488
+ }
489
+ }
490
+ }
491
+ }
459
492
  };
@@ -68,7 +68,7 @@ function validateRouteCommon(node, validationContext, propPrefix) {
68
68
  ]);
69
69
  validationContext.assertNotEmptyString((0, import_jsonc_parser.findNodeAtLocation)(node, ["id"]), `${propPrefix}.id`);
70
70
  validationContext.assertIsSpecifier((0, import_jsonc_parser.findNodeAtLocation)(node, ["rootComponent"]), `${propPrefix}.rootComponent`);
71
- validationContext.assertNotEmptyString((0, import_jsonc_parser.findNodeAtLocation)(node, ["contentTemplate"]), `${propPrefix}.contentTemplate`);
71
+ validationContext.assertIsStringOrObject((0, import_jsonc_parser.findNodeAtLocation)(node, ["contentTemplate"]), `${propPrefix}.contentTemplate`);
72
72
  validationContext.assertNotEmptyString((0, import_jsonc_parser.findNodeAtLocation)(node, ["layoutTemplate"]), `${propPrefix}.layoutTemplate`);
73
73
  validationContext.assertIsService((0, import_jsonc_parser.findNodeAtLocation)(node, ["routeHandler"]), `${propPrefix}.routeHandler`);
74
74
  validateBootstrap((0, import_jsonc_parser.findNodeAtLocation)(node, ["bootstrap"]), validationContext, `${propPrefix}.bootstrap`);
@@ -101,7 +101,10 @@ function validateI18NConfig(node, validationContext, preMerge) {
101
101
  validationContext.assertRequiredKeys(node, "i18n", ["defaultLocale", "locales"]);
102
102
  const locales = (0, import_jsonc_parser.findNodeAtLocation)(node, ["locales"]);
103
103
  validationContext.assertNotEmptyArray(locales, "i18n.locales");
104
- validationContext.assertUniqueIds([...locales?.children || []], "i18n.locales");
104
+ if (locales) {
105
+ validationContext.assertUniqueIds([...locales?.children || []], "i18n.locales");
106
+ validationContext.assertFallbackIds([...locales?.children || []]);
107
+ }
105
108
  const defaultLocale = (0, import_jsonc_parser.findNodeAtLocation)(node, ["defaultLocale"]);
106
109
  validationContext.assertNotEmptyString(defaultLocale, "i18n.defaultLocale");
107
110
  const localeIds = locales?.children?.map((n) => (0, import_jsonc_parser.findNodeAtLocation)(n, ["id"])?.value) || [];
package/build/es/hooks.js CHANGED
@@ -65,12 +65,14 @@ export function executeStartHooks(hooks, globalConfig, runtimeEnvironment, skipV
65
65
  }
66
66
  const onStartConfig = {
67
67
  basePath: globalConfig.basePath,
68
+ i18n: globalConfig.i18n,
68
69
  routes: globalConfig.routes,
69
70
  };
70
71
  hook.onStart(onStartConfig);
71
72
  // copy updated values back to the globalConfig
72
- globalConfig.routes = (onStartConfig.routes || []);
73
73
  runtimeEnvironment.basePath = globalConfig.basePath = onStartConfig.basePath || '';
74
+ globalConfig.i18n = onStartConfig.i18n;
75
+ globalConfig.routes = (onStartConfig.routes || []);
74
76
  }
75
77
  globalConfig.routes = normalizeRoutes(globalConfig.routes, globalConfig.routeHandlers);
76
78
  const ssrConfig = applySsrConfig(globalConfig);
@@ -34,7 +34,10 @@ export function normalizeRoutePaths(routes = [], resourcePaths) {
34
34
  // route handler paths are NOT normalized here to maintain the id lookup for route handler invocation
35
35
  const { contentTemplate, layoutTemplate, subRoutes } = route;
36
36
  if (contentTemplate) {
37
- route.contentTemplate = path.resolve(normalizeResourcePath(contentTemplate, resourcePaths));
37
+ route.contentTemplate =
38
+ typeof contentTemplate === 'string'
39
+ ? path.resolve(normalizeResourcePath(contentTemplate, resourcePaths))
40
+ : contentTemplate;
38
41
  }
39
42
  if (layoutTemplate) {
40
43
  route.layoutTemplate = path.resolve(normalizeResourcePath(layoutTemplate, resourcePaths));
@@ -1,4 +1,4 @@
1
- import type { AssetDirConfig, AssetFileConfig, LwrErrorRoute, LwrRoute, NormalizedLwrGlobalConfig, NormalizedLwrAppBootstrapConfig, LwrLockerConfig, RouteHandlersConfig, BundleConfig, I18NConfig } from '@lwrjs/types';
1
+ import type { AssetDirConfig, AssetFileConfig, LwrErrorRoute, LwrRoute, NormalizedLwrGlobalConfig, NormalizedLwrAppBootstrapConfig, LwrLockerConfig, RouteHandlersConfig, BundleConfig, I18NConfig, Locale } from '@lwrjs/types';
2
2
  import { Node } from 'jsonc-parser';
3
3
  import { Diagnostic } from '@lwrjs/diagnostics';
4
4
  type RequiredAssetDirConfig = Required<AssetDirConfig>;
@@ -7,6 +7,7 @@ type RequiredLwrRoute = Required<LwrRoute>;
7
7
  type RequiredLwrErrorRoute = Required<LwrErrorRoute>;
8
8
  type RequiredLwrLockerConfig = Required<LwrLockerConfig>;
9
9
  type RequiredI18NConfig = Required<I18NConfig>;
10
+ type RequiredLocalesConfig = Required<Locale>;
10
11
  interface ConfigMap {
11
12
  root: NormalizedLwrGlobalConfig;
12
13
  assetDir: RequiredAssetDirConfig;
@@ -20,12 +21,13 @@ interface ConfigMap {
20
21
  'bundleConfig.external': BundleConfig;
21
22
  'bundleConfig.groups': BundleConfig;
22
23
  i18n: RequiredI18NConfig;
24
+ 'i18n.locales': RequiredLocalesConfig;
23
25
  }
24
26
  export declare const ROOT_ATTRIBUTE_KEYS: ["amdLoader", "apiVersion", "assets", "assetProviders", "assetTransformers", "bundleConfig", "bundleProviders", "cacheDir", "contentDir", "environment", "errorRoutes", "esmLoader", "staticSiteGenerator", "globalData", "globalDataDir", "hooks", "i18n", "ignoreLwrConfigFile", "lwrConfigFile", "layoutsDir", "locker", "lwc", "lwrVersion", "moduleProviders", "port", "basePath", "resourceProviders", "rootDir", "routeHandlers", "routes", "serverMode", "minify", "serverType", "uriTransformers", "viewProviders", "viewTransformers"];
25
27
  export declare const ASSET_DIR_ATTRIBUTE_KEYS: ["alias", "dir", "urlPath", "root"];
26
28
  export declare const ASSET_FILE_ATTRIBUTE_KEYS: ["alias", "file", "urlPath"];
27
29
  export declare const LOCKER_ATTRIBUTE_KEYS: ["enabled", "trustedComponents", "clientOnly"];
28
- export declare const I18N_ATTRIBUTE_KEYS: ["defaultLocale", "locales"];
30
+ export declare const I18N_ATTRIBUTE_KEYS: ["defaultLocale", "locales", "uriPattern"];
29
31
  export declare const ROUTE_ATTRIBUTE_KEYS: ["bootstrap", "subRoutes", "contentTemplate", "id", "cache", "layoutTemplate", "method", "path", "rootComponent", "routeHandler", "properties"];
30
32
  export declare const ERROR_ROUTE_ATTRIBUTE_KEYS: ["bootstrap", "subRoutes", "contentTemplate", "id", "layoutTemplate", "rootComponent", "routeHandler", "status", "properties", "cache"];
31
33
  export declare const BOOTSTRAP_ATTRIBUTE_KEYS: ["autoBoot", "syntheticShadow", "workers", "services", "configAsSrc", "ssr", "mixedMode", "module", "preloadModules"];
@@ -54,6 +56,7 @@ export declare class ValidationContext {
54
56
  assertArrayOfStrings(node: Node | undefined, property: string): void;
55
57
  assertArrayOfSpecifiers(node: Node | undefined, property: string): void;
56
58
  assertArrayOfServices(node: Node | undefined, property: string): void;
59
+ assertIsStringOrObject(node: Node | undefined, property: string, index?: number): void;
57
60
  assertIsService(node: Node | undefined, property: string, index?: number): void;
58
61
  assertUniqueIds(nodes: Node[], property: string): void;
59
62
  assertClientLockerSSR(routesNode: Node | undefined, lockerNode: Node | undefined): void;
@@ -61,6 +64,7 @@ export declare class ValidationContext {
61
64
  assertValidKeys<T extends keyof ConfigMap>(node: Node, property: T, validPropertyKeys: (keyof ConfigMap[T])[]): void;
62
65
  assertNoBundleConfigDupes(node: Node, dupes: string[]): void;
63
66
  assertDefaultInLocales(node: Node, defaultLocale: string, localesIds: string[]): void;
67
+ assertFallbackIds(nodes: Node[]): void;
64
68
  }
65
69
  export {};
66
70
  //# sourceMappingURL=app-config-context.d.ts.map
@@ -1,6 +1,7 @@
1
1
  import { findNodeAtLocation } from 'jsonc-parser';
2
2
  import { descriptions } from '@lwrjs/diagnostics';
3
3
  import { calculatePositionFromSource } from './helpers.js';
4
+ import { isSpecifier } from '@lwrjs/shared-utils';
4
5
  // Run the duplicate and missing property checks against an object of a given type
5
6
  function createKeys(p, t) {
6
7
  return p && t;
@@ -47,7 +48,7 @@ export const ROOT_ATTRIBUTE_KEYS = createKeys('root', [
47
48
  export const ASSET_DIR_ATTRIBUTE_KEYS = createKeys('assetDir', ['alias', 'dir', 'urlPath', 'root']);
48
49
  export const ASSET_FILE_ATTRIBUTE_KEYS = createKeys('assetFile', ['alias', 'file', 'urlPath']);
49
50
  export const LOCKER_ATTRIBUTE_KEYS = createKeys('locker', ['enabled', 'trustedComponents', 'clientOnly']);
50
- export const I18N_ATTRIBUTE_KEYS = createKeys('i18n', ['defaultLocale', 'locales']);
51
+ export const I18N_ATTRIBUTE_KEYS = createKeys('i18n', ['defaultLocale', 'locales', 'uriPattern']);
51
52
  export const ROUTE_ATTRIBUTE_KEYS = createKeys('routes', [
52
53
  'bootstrap',
53
54
  'subRoutes',
@@ -84,7 +85,6 @@ export const BOOTSTRAP_ATTRIBUTE_KEYS = createKeys('bootstrap', [
84
85
  'module',
85
86
  'preloadModules',
86
87
  ]);
87
- const SPECIFIER_REGEX = /^@?[\w-]+(\/[\w-]+)*$/;
88
88
  function isNotEmptyString(node) {
89
89
  return node.type === 'string' && node.value.length > 0;
90
90
  }
@@ -122,7 +122,7 @@ export class ValidationContext {
122
122
  }
123
123
  }
124
124
  assertIsSpecifier(node, property) {
125
- if (node && (node.type !== 'string' || !SPECIFIER_REGEX.test(node.value))) {
125
+ if (node && (node.type !== 'string' || !isSpecifier(node.value))) {
126
126
  this.diagnostics.push({
127
127
  description: descriptions.CONFIG_PARSER.INVALID_SPECIFIER(property, node.value),
128
128
  location: this.getLocationFromNode(node),
@@ -307,7 +307,7 @@ export class ValidationContext {
307
307
  }
308
308
  else if (node.children && node.children.length > 0) {
309
309
  node.children.forEach((n, index) => {
310
- if (n.type !== 'string' || !SPECIFIER_REGEX.test(n.value)) {
310
+ if (n.type !== 'string' || !isSpecifier(n.value)) {
311
311
  this.diagnostics.push({
312
312
  description: descriptions.CONFIG_PARSER.INVALID_SPECIFIER(`${property}[${index}]`, n.value),
313
313
  location: this.getLocationFromNode(n),
@@ -330,6 +330,23 @@ export class ValidationContext {
330
330
  node.children.forEach((n, index) => this.assertIsService(n, property, index));
331
331
  }
332
332
  }
333
+ assertIsStringOrObject(node, property, index) {
334
+ if (!node) {
335
+ return;
336
+ }
337
+ if (node.type !== 'string' && node.type !== 'object') {
338
+ this.diagnostics.push({
339
+ description: descriptions.CONFIG_PARSER.INCORRECT_NODE_TYPE(index !== undefined ? `${property}[${index}]` : property, 'string or object', node.type),
340
+ location: this.getLocationFromNode(node),
341
+ });
342
+ }
343
+ else if (node.type === 'string' && !isNotEmptyString(node)) {
344
+ this.diagnostics.push({
345
+ description: descriptions.CONFIG_PARSER.NON_EMPTY_STRING(property, node.value),
346
+ location: this.getLocationFromNode(node),
347
+ });
348
+ }
349
+ }
333
350
  assertIsService(node, property, index) {
334
351
  if (!node) {
335
352
  return;
@@ -439,5 +456,24 @@ export class ValidationContext {
439
456
  });
440
457
  }
441
458
  }
459
+ assertFallbackIds(nodes) {
460
+ const localesIds = nodes
461
+ .map((n) => {
462
+ const idNode = findNodeAtLocation(n, ['id']);
463
+ return idNode ? idNode.value : undefined;
464
+ })
465
+ .filter((id) => id !== undefined);
466
+ for (const n of nodes) {
467
+ const fallbackNode = findNodeAtLocation(n, ['fallback']);
468
+ if (fallbackNode?.value) {
469
+ if (!localesIds.includes(fallbackNode.value)) {
470
+ this.diagnostics.push({
471
+ description: descriptions.CONFIG_PARSER.FALLBACK_NOT_IN_LOCALES(fallbackNode.value, localesIds),
472
+ location: this.getLocationFromNode(fallbackNode),
473
+ });
474
+ }
475
+ }
476
+ }
477
+ }
442
478
  }
443
479
  //# sourceMappingURL=app-config-context.js.map
@@ -51,7 +51,7 @@ function validateBootstrap(node, validationContext, propPrefix) {
51
51
  * - contentTemplate
52
52
  * - routeHandler
53
53
  * - rootComponent: optional specifier
54
- * - contentTemplate: optional string
54
+ * - contentTemplate: optional string or object
55
55
  * - layoutTemplate: optional string
56
56
  * - routeHandler: optional string
57
57
  * - optional bootstrap...
@@ -64,7 +64,7 @@ function validateRouteCommon(node, validationContext, propPrefix) {
64
64
  ]);
65
65
  validationContext.assertNotEmptyString(findNode(node, ['id']), `${propPrefix}.id`);
66
66
  validationContext.assertIsSpecifier(findNode(node, ['rootComponent']), `${propPrefix}.rootComponent`);
67
- validationContext.assertNotEmptyString(findNode(node, ['contentTemplate']), `${propPrefix}.contentTemplate`);
67
+ validationContext.assertIsStringOrObject(findNode(node, ['contentTemplate']), `${propPrefix}.contentTemplate`);
68
68
  validationContext.assertNotEmptyString(findNode(node, ['layoutTemplate']), `${propPrefix}.layoutTemplate`);
69
69
  validationContext.assertIsService(findNode(node, ['routeHandler']), `${propPrefix}.routeHandler`);
70
70
  validateBootstrap(findNode(node, ['bootstrap']), validationContext, `${propPrefix}.bootstrap`);
@@ -110,7 +110,10 @@ function validateI18NConfig(node, validationContext, preMerge) {
110
110
  // Validate locales
111
111
  const locales = findNode(node, ['locales']);
112
112
  validationContext.assertNotEmptyArray(locales, 'i18n.locales');
113
- validationContext.assertUniqueIds([...(locales?.children || [])], 'i18n.locales');
113
+ if (locales) {
114
+ validationContext.assertUniqueIds([...(locales?.children || [])], 'i18n.locales');
115
+ validationContext.assertFallbackIds([...(locales?.children || [])]);
116
+ }
114
117
  // Validate defaultLocale
115
118
  const defaultLocale = findNode(node, ['defaultLocale']);
116
119
  validationContext.assertNotEmptyString(defaultLocale, 'i18n.defaultLocale');
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
7
- "version": "0.11.0-alpha.4",
7
+ "version": "0.11.0-alpha.6",
8
8
  "homepage": "https://developer.salesforce.com/docs/platform/lwr/overview",
9
9
  "repository": {
10
10
  "type": "git",
@@ -41,14 +41,14 @@
41
41
  "test": "jest"
42
42
  },
43
43
  "dependencies": {
44
- "@lwrjs/diagnostics": "0.11.0-alpha.4",
45
- "@lwrjs/instrumentation": "0.11.0-alpha.4",
46
- "@lwrjs/shared-utils": "0.11.0-alpha.4",
44
+ "@lwrjs/diagnostics": "0.11.0-alpha.6",
45
+ "@lwrjs/instrumentation": "0.11.0-alpha.6",
46
+ "@lwrjs/shared-utils": "0.11.0-alpha.6",
47
47
  "fs-extra": "^11.1.1",
48
48
  "jsonc-parser": "^3.0.0"
49
49
  },
50
50
  "devDependencies": {
51
- "@lwrjs/types": "0.11.0-alpha.4",
51
+ "@lwrjs/types": "0.11.0-alpha.6",
52
52
  "jest": "^26.6.3",
53
53
  "ts-jest": "^26.5.6"
54
54
  },
@@ -61,5 +61,5 @@
61
61
  "volta": {
62
62
  "extends": "../../../package.json"
63
63
  },
64
- "gitHead": "89a7e9815e0e381d9fd67212f0471d9a11f73985"
64
+ "gitHead": "571d4bac5650765aa818bcb9d5ed752a8cf041af"
65
65
  }