tokenami 0.0.81 → 0.0.82

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.js CHANGED
@@ -17,8 +17,7 @@ var __hasOwnProp = Object.prototype.hasOwnProperty;
17
17
  var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
18
18
  get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
19
19
  }) : x)(function(x) {
20
- if (typeof require !== "undefined")
21
- return require.apply(this, arguments);
20
+ if (typeof require !== "undefined") return require.apply(this, arguments);
22
21
  throw Error('Dynamic require of "' + x + '" is not supported');
23
22
  });
24
23
  var __esm = (fn, res) => function __init() {
@@ -45,7 +44,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
45
44
  ));
46
45
  var getFilename, getDirname, __dirname, __filename;
47
46
  var init_esm_shims = __esm({
48
- "../../node_modules/.pnpm/tsup@7.3.0_typescript@5.4.5/node_modules/tsup/assets/esm_shims.js"() {
47
+ "../../node_modules/.pnpm/tsup@8.4.0_jiti@1.21.6_typescript@5.4.5/node_modules/tsup/assets/esm_shims.js"() {
49
48
  getFilename = () => fileURLToPath(import.meta.url);
50
49
  getDirname = () => path.dirname(getFilename());
51
50
  __dirname = /* @__PURE__ */ getDirname();
@@ -244,8 +243,7 @@ var require_HashArray = __commonJS({
244
243
  continue;
245
244
  }
246
245
  this._map[inst].push(obj);
247
- } else
248
- this._map[inst] = [obj];
246
+ } else this._map[inst] = [obj];
249
247
  }
250
248
  }
251
249
  if (!needsDupCheck || this._list.indexOf(obj) == -1)
@@ -334,8 +332,7 @@ var require_HashArray = __commonJS({
334
332
  count = Math.min(Math.max(max - min, 1), count);
335
333
  while (res.length < count) {
336
334
  var r = Math.floor(min + Math.random() * (max + 1));
337
- if (map[r])
338
- continue;
335
+ if (map[r]) continue;
339
336
  map[r] = true;
340
337
  res.push(r);
341
338
  }
@@ -608,8 +605,7 @@ var require_crypt = __commonJS({
608
605
  base64ToBytes: function(base64) {
609
606
  base64 = base64.replace(/[^A-Z0-9+\/]/ig, "");
610
607
  for (var bytes = [], i = 0, imod4 = 0; i < base64.length; imod4 = ++i % 4) {
611
- if (imod4 == 0)
612
- continue;
608
+ if (imod4 == 0) continue;
613
609
  bytes.push((base64map.indexOf(base64.charAt(i - 1)) & Math.pow(2, -2 * imod4 + 8) - 1) << imod4 * 2 | base64map.indexOf(base64.charAt(i)) >>> 6 - imod4 * 2);
614
610
  }
615
611
  return bytes;
@@ -858,16 +854,14 @@ var require_TrieSearch = __commonJS({
858
854
  }
859
855
  TrieSearch2.prototype = {
860
856
  add: function(item, customKeys) {
861
- if (this.options.cache)
862
- this.clearCache();
857
+ if (this.options.cache) this.clearCache();
863
858
  if (typeof customKeys === "number") {
864
859
  customKeys = void 0;
865
860
  }
866
861
  var keyFields = customKeys || this.keyFields;
867
862
  for (var k in keyFields) {
868
863
  var key = keyFields[k], isKeyArr = key instanceof Array, val = isKeyArr ? deepLookup(item, key) : item[key];
869
- if (!val)
870
- continue;
864
+ if (!val) continue;
871
865
  val = val.toString();
872
866
  if (this.options.ignoreCase) {
873
867
  val = val.toLowerCase();
@@ -887,14 +881,12 @@ var require_TrieSearch = __commonJS({
887
881
  * @param keyFields The keyfields in which to search for this phrase to remove
888
882
  */
889
883
  remove: function(phrase, keyFields) {
890
- if (!phrase)
891
- return;
884
+ if (!phrase) return;
892
885
  phrase = phrase.toString();
893
886
  phrase = this.options.ignoreCase ? phrase.toLowerCase() : phrase;
894
887
  keyFields = keyFields || this.keyFields;
895
888
  keyFields = keyFields instanceof Array ? keyFields : [keyFields];
896
- if (this.options.cache)
897
- this.clearCache();
889
+ if (this.options.cache) this.clearCache();
898
890
  var diacriticalVariants = this.expandString(phrase);
899
891
  for (var variant of diacriticalVariants) {
900
892
  var words = this.options.splitOnRegEx ? variant.split(this.options.splitOnRegEx) : [variant];
@@ -1046,17 +1038,14 @@ var require_TrieSearch = __commonJS({
1046
1038
  return [];
1047
1039
  keyArr = [key.substring(0, this.options.min)];
1048
1040
  keyArr = keyArr.concat(key.substring(this.options.min).split(""));
1049
- } else
1050
- keyArr = key.split("");
1041
+ } else keyArr = key.split("");
1051
1042
  return keyArr;
1052
1043
  },
1053
1044
  findNode: function(key) {
1054
1045
  return f(this.keyToArr(key), this.root);
1055
1046
  function f(keyArr, node) {
1056
- if (!node)
1057
- return void 0;
1058
- if (keyArr.length === 0)
1059
- return node;
1047
+ if (!node) return void 0;
1048
+ if (keyArr.length === 0) return node;
1060
1049
  var k = keyArr.shift();
1061
1050
  return f(keyArr, node[k]);
1062
1051
  }
@@ -1093,8 +1082,7 @@ var require_TrieSearch = __commonJS({
1093
1082
  var item = node2.value[i];
1094
1083
  if (!limit || all.length < limit) {
1095
1084
  const id = self.getId(item);
1096
- if (dedupDict[id])
1097
- continue;
1085
+ if (dedupDict[id]) continue;
1098
1086
  all.push(item);
1099
1087
  dedupDict[id] = item;
1100
1088
  }
@@ -1128,8 +1116,7 @@ var require_TrieSearch = __commonJS({
1128
1116
  return this.get(phrases, reducer, limit);
1129
1117
  },
1130
1118
  getId: function(item, customKeys) {
1131
- if (item.$tsid)
1132
- return item.$tsid;
1119
+ if (item.$tsid) return item.$tsid;
1133
1120
  if (!this.options.idFieldOrFunction) {
1134
1121
  var s = "";
1135
1122
  var kf = customKeys || this.keyFields;
@@ -1203,8 +1190,7 @@ var TokenamiDiagnostics = class {
1203
1190
  const processNode = this.#processNode.bind(this);
1204
1191
  ts.forEachChild(sourceFile, function nextNode(node) {
1205
1192
  const nodeDiagnostics = processNode(node, sourceFile);
1206
- if (nodeDiagnostics)
1207
- diagnostics.push(...nodeDiagnostics);
1193
+ if (nodeDiagnostics) diagnostics.push(...nodeDiagnostics);
1208
1194
  ts.forEachChild(node, nextNode);
1209
1195
  });
1210
1196
  }
@@ -1212,16 +1198,14 @@ var TokenamiDiagnostics = class {
1212
1198
  }
1213
1199
  #processNode(node, sourceFile) {
1214
1200
  const isDiagnosticPrevented = this.#shouldSuppressDiagnosticForNode(node, sourceFile);
1215
- if (isDiagnosticPrevented)
1216
- return;
1201
+ if (isDiagnosticPrevented) return;
1217
1202
  if (ts.isCallExpression(node) && ts.isPropertyAccessExpression(node.expression) && ts.isIdentifier(node.expression.expression) && node.expression.expression.text === "css" && node.expression.name.text === "compose" && node.arguments[0] && ts.isObjectLiteralExpression(node.arguments[0])) {
1218
1203
  return this.#validateComposeConfig(node.arguments[0], sourceFile);
1219
1204
  }
1220
1205
  if (ts.isPropertyAssignment(node)) {
1221
1206
  const nodeProperty = ts.isStringLiteral(node.name) ? node.name.text : null;
1222
1207
  const property = TokenamiConfig4.TokenProperty.safeParse(nodeProperty);
1223
- if (!property.success)
1224
- return;
1208
+ if (!property.success) return;
1225
1209
  return this.#validateTokenamiProperty(property.output, node, sourceFile);
1226
1210
  }
1227
1211
  }
@@ -1229,8 +1213,7 @@ var TokenamiDiagnostics = class {
1229
1213
  const { variants } = TokenamiConfig4.getTokenPropertySplit(property);
1230
1214
  const parts = TokenamiConfig4.getTokenPropertyParts(property, this.#config);
1231
1215
  const isArbitrarySelector = variants.some(TokenamiConfig4.getArbitrarySelector);
1232
- if (!variants.length || parts || isArbitrarySelector)
1233
- return;
1216
+ if (!variants.length || parts || isArbitrarySelector) return;
1234
1217
  const selector = variants.join("_");
1235
1218
  const isEmptyArbSelector = variants.includes("{}");
1236
1219
  const arbSuffix = isEmptyArbSelector ? ` Add an arbitrary selector or remove '${selector}'.` : "";
@@ -1258,8 +1241,7 @@ var TokenamiDiagnostics = class {
1258
1241
  const length = prop.getWidth(sourceFile);
1259
1242
  return [{ ...diagnostic, start, length }];
1260
1243
  }
1261
- if (!ts.isPropertyAssignment(prop))
1262
- continue;
1244
+ if (!ts.isPropertyAssignment(prop)) continue;
1263
1245
  const key = prop.name;
1264
1246
  const value = prop.initializer;
1265
1247
  if (ts.isComputedPropertyName(key)) {
@@ -1277,8 +1259,7 @@ var TokenamiDiagnostics = class {
1277
1259
  }
1278
1260
  }
1279
1261
  #shouldSuppressDiagnosticForNode(node, sourceFile) {
1280
- if (!sourceFile)
1281
- return false;
1262
+ if (!sourceFile) return false;
1282
1263
  const lineStarts = sourceFile.getLineStarts();
1283
1264
  const nodeStartPos = node.getStart(sourceFile);
1284
1265
  const nodeStartLine = sourceFile.getLineAndCharacterOfPosition(nodeStartPos).line;
@@ -1790,14 +1771,13 @@ var DEFAULT_PATHS = {
1790
1771
  mjs: "./.tokenami/tokenami.config.mjs"
1791
1772
  };
1792
1773
  function getConfigPath(cwd, path2, type) {
1793
- path2 = path2 || getConfigDefaultPath(cwd, type);
1774
+ path2 = path2 || getConfigDefaultPath(cwd);
1794
1775
  return pathe.join(cwd, path2);
1795
1776
  }
1796
1777
  function getConfigAtPath(path2, opts = { cache: true }) {
1797
1778
  const config = (() => {
1798
1779
  try {
1799
- if (!opts.cache)
1800
- delete __require.cache[__require.resolve(path2)];
1780
+ if (!opts.cache) delete __require.cache[__require.resolve(path2)];
1801
1781
  return __require(path2);
1802
1782
  } catch {
1803
1783
  return lazyJiti({ cache: opts.cache })(path2);
@@ -1809,7 +1789,7 @@ function getConfigDefaultPath(cwd, type) {
1809
1789
  const existingConfig = Object.values(DEFAULT_PATHS).find((path2) => {
1810
1790
  return fs.existsSync(pathe.join(cwd, path2));
1811
1791
  });
1812
- return existingConfig || DEFAULT_PATHS[type || "js"];
1792
+ return existingConfig || DEFAULT_PATHS["js"];
1813
1793
  }
1814
1794
  function getTypeDefsPath(configPath) {
1815
1795
  const dirname2 = pathe.dirname(configPath);
@@ -1974,10 +1954,8 @@ var TrieCompletions = class {
1974
1954
  variantSearch(search) {
1975
1955
  const input = this.#createTrieInput(search);
1976
1956
  const parts = TokenamiConfig4.getTokenPropertySplit(search);
1977
- if (!parts.variants.length)
1978
- return this.#selectorSnippets.search(input);
1979
- if (parts.variants.length > 1)
1980
- return this.#responsiveSelectorsSearch(input, parts.variants);
1957
+ if (!parts.variants.length) return this.#selectorSnippets.search(input);
1958
+ if (parts.variants.length > 1) return this.#responsiveSelectorsSearch(input, parts.variants);
1981
1959
  const selectors = this.#selectorsSearch(input, parts.variants);
1982
1960
  const snippets = this.#responsiveSelectorSnippets.search(input);
1983
1961
  return [...selectors, ...snippets];
@@ -1994,8 +1972,7 @@ var TrieCompletions = class {
1994
1972
  }
1995
1973
  #selectorsSearch(input, variants) {
1996
1974
  const [_, arbSelector] = input.match(/\{(.*)\}/) ?? [];
1997
- if (!arbSelector)
1998
- return this.#selectors.search(input);
1975
+ if (!arbSelector) return this.#selectors.search(input);
1999
1976
  const key = String(variants);
2000
1977
  this.#arbSelectors[key] ??= this.#createCompletionEntriesTrie(
2001
1978
  this.#getSelectorCompletions(arbSelector)
@@ -2004,8 +1981,7 @@ var TrieCompletions = class {
2004
1981
  }
2005
1982
  #responsiveSelectorsSearch(input, variants) {
2006
1983
  const [_, arbSelector] = input.match(/\{(.*)\}/) ?? [];
2007
- if (!arbSelector)
2008
- return this.#responsiveSelectors.search(input);
1984
+ if (!arbSelector) return this.#responsiveSelectors.search(input);
2009
1985
  const key = String(variants);
2010
1986
  this.#arbResponsiveSelectors[key] ??= this.#createCompletionEntriesTrie(
2011
1987
  this.#getResponsiveSelectorCompletions(arbSelector)
@@ -2153,8 +2129,7 @@ var TokenamiPlugin = class {
2153
2129
  }
2154
2130
  #watchConfig(configPath) {
2155
2131
  this.#ctx.ts.sys.watchFile?.(configPath, (_fileName, eventKind) => {
2156
- if (eventKind !== this.#ctx.ts.FileWatcherEventKind.Changed)
2157
- return;
2132
+ if (eventKind !== this.#ctx.ts.FileWatcherEventKind.Changed) return;
2158
2133
  try {
2159
2134
  const reloadedConfig = getConfigAtPath(configPath, { cache: false });
2160
2135
  updateEnvFile(configPath, reloadedConfig);
@@ -2173,8 +2148,7 @@ var TokenamiPlugin = class {
2173
2148
  getSemanticDiagnostics(fileName) {
2174
2149
  const original = this.#ctx.info.languageService.getSemanticDiagnostics(fileName);
2175
2150
  const sourceFile = this.#ctx.info.languageService.getProgram()?.getSourceFile(fileName);
2176
- if (!sourceFile)
2177
- return original;
2151
+ if (!sourceFile) return original;
2178
2152
  return [...this.#diagnostics.getSemanticDiagnostics(sourceFile), ...original];
2179
2153
  }
2180
2154
  getCompletionsAtPosition = (fileName, position, options) => {
@@ -2185,8 +2159,7 @@ var TokenamiPlugin = class {
2185
2159
  );
2186
2160
  const program = this.#ctx.info.languageService.getProgram();
2187
2161
  const sourceFile = program?.getSourceFile(fileName);
2188
- if (!original || !sourceFile)
2189
- return original;
2162
+ if (!original || !sourceFile) return original;
2190
2163
  const isTokenPropertyEntries = original.entries.some(
2191
2164
  (entry) => TokenamiConfig4.TokenProperty.safeParse(entry.name).success
2192
2165
  );
@@ -2194,8 +2167,7 @@ var TokenamiPlugin = class {
2194
2167
  (entry) => TokenamiConfig4.TokenValue.safeParse(entry.name).success
2195
2168
  );
2196
2169
  const node = findNodeAtPosition(sourceFile, position);
2197
- if (!node)
2198
- return original;
2170
+ if (!node) return original;
2199
2171
  if (isTokenValueEntries) {
2200
2172
  const input = getValueAtPosition(node, position);
2201
2173
  const needsQuotes = !input.startsWith('"') && !input.startsWith("'");
@@ -2234,22 +2206,17 @@ var TokenamiPlugin = class {
2234
2206
  return entry2 ? createEntryDetails(original, entry2, String(entry2.details.selector)) : original;
2235
2207
  }
2236
2208
  const sourceFile = this.#ctx.info.languageService.getProgram()?.getSourceFile(fileName);
2237
- if (!sourceFile)
2238
- return original;
2209
+ if (!sourceFile) return original;
2239
2210
  const node = findNodeAtPosition(sourceFile, position);
2240
- if (!node || !ts.isPropertyAssignment(node.parent))
2241
- return original;
2211
+ if (!node || !ts.isPropertyAssignment(node.parent)) return original;
2242
2212
  const parentProperty = node.parent.name.getText();
2243
2213
  const isTokenamiValue = TokenamiConfig4.TokenProperty.safeParse(parentProperty).success;
2244
- if (!isTokenamiValue)
2245
- return original;
2214
+ if (!isTokenamiValue) return original;
2246
2215
  const [entry] = this.#completions.valueSearch(search);
2247
- if (!entry)
2248
- return original;
2216
+ if (!entry) return original;
2249
2217
  const themeEntries = Object.entries(entry.details.modeValues);
2250
2218
  const [mode, firstValue] = themeEntries[0] || [];
2251
- if (!firstValue)
2252
- return original;
2219
+ if (!firstValue) return original;
2253
2220
  if (isColorThemeEntry(entry.details.modeValues)) {
2254
2221
  const colorDescription = createColorTokenDescription(entry.details.modeValues);
2255
2222
  const rgb2 = convertToRgb(replaceCssVarsWithFallback(firstValue), mode);
@@ -2264,22 +2231,18 @@ ${colorDescription}`);
2264
2231
  getQuickInfoAtPosition(fileName, position) {
2265
2232
  const original = this.#ctx.info.languageService.getQuickInfoAtPosition(fileName, position);
2266
2233
  const sourceFile = this.#ctx.info.languageService.getProgram()?.getSourceFile(fileName);
2267
- if (!original || !sourceFile)
2268
- return original;
2234
+ if (!original || !sourceFile) return original;
2269
2235
  const node = findNodeAtPosition(sourceFile, position);
2270
- if (!node || !node.parent || !ts.isPropertyAssignment(node.parent))
2271
- return original;
2236
+ if (!node || !node.parent || !ts.isPropertyAssignment(node.parent)) return original;
2272
2237
  const property = node.parent;
2273
2238
  const propertyName = property.name.getText(sourceFile);
2274
2239
  const propertyValue = property.initializer.getText();
2275
2240
  const tokenProperty3 = TokenamiConfig4.TokenProperty.safeParse(propertyName);
2276
2241
  const tokenValue2 = TokenamiConfig4.TokenValue.safeParse(propertyValue);
2277
- if (!tokenProperty3.success || !tokenValue2.success)
2278
- return original;
2242
+ if (!tokenProperty3.success || !tokenValue2.success) return original;
2279
2243
  const { variants } = TokenamiConfig4.getTokenPropertySplit(tokenProperty3.output);
2280
2244
  const propertyParts = TokenamiConfig4.getTokenPropertyParts(tokenProperty3.output, this.#config);
2281
- if (!propertyParts && variants.length)
2282
- return;
2245
+ if (!propertyParts && variants.length) return;
2283
2246
  const modeValues = getThemeValuesByThemeMode(tokenValue2.output, this.#config.theme);
2284
2247
  const text = isColorThemeEntry(modeValues) ? createColorTokenDescription(modeValues) : createTokenDescription(modeValues);
2285
2248
  return { ...original, documentation: [{ text, kind: "markdown" }] };
@@ -2294,16 +2257,13 @@ ${colorDescription}`);
2294
2257
  preferences
2295
2258
  );
2296
2259
  const sourceFile = this.#ctx.info.languageService.getProgram()?.getSourceFile(fileName);
2297
- if (!sourceFile || !errorCodes.includes(INVALID_VALUE))
2298
- return original;
2260
+ if (!sourceFile || !errorCodes.includes(INVALID_VALUE)) return original;
2299
2261
  const node = findNodeAtPosition(sourceFile, start);
2300
- if (!node?.parent || !ts.isPropertyAssignment(node.parent))
2301
- return original;
2262
+ if (!node?.parent || !ts.isPropertyAssignment(node.parent)) return original;
2302
2263
  const assignment = node.parent;
2303
2264
  const valueSpan = createTextSpanFromNode(assignment.initializer);
2304
2265
  const value = ts.isStringLiteral(assignment.initializer) && assignment.initializer.text;
2305
- if (!value)
2306
- return original;
2266
+ if (!value) return original;
2307
2267
  const quoteMark = assignment.initializer.getText().slice(-1);
2308
2268
  const arbitraryValue2 = TokenamiConfig4.arbitraryValue(value);
2309
2269
  const arbitraryText = `${quoteMark}${arbitraryValue2}${quoteMark}`;
@@ -2319,13 +2279,11 @@ ${colorDescription}`);
2319
2279
  function updateEnvFile(configPath, config) {
2320
2280
  const envFilePath = getTypeDefsPath(configPath);
2321
2281
  const envFileContent = ts.sys.readFile(envFilePath, "utf-8");
2322
- if (!envFileContent)
2323
- throw new Error("Cannot read tokenami.env.d.ts file");
2282
+ if (!envFileContent) throw new Error("Cannot read tokenami.env.d.ts file");
2324
2283
  const properties = Object.keys(config.properties || {});
2325
2284
  const customProperties = Object.keys(config.customProperties || {});
2326
2285
  const experimentalProperties = properties.flatMap((property) => {
2327
- if (supportedProperties.has(property))
2328
- return [];
2286
+ if (supportedProperties.has(property)) return [];
2329
2287
  return [property];
2330
2288
  });
2331
2289
  const customPropertyTypes = [...experimentalProperties, ...customProperties].map((property) => {
@@ -2371,8 +2329,7 @@ function getValueAtPosition(node, position) {
2371
2329
  let input = node.getText();
2372
2330
  if (ts.isObjectLiteralExpression(node)) {
2373
2331
  for (const property of node.properties) {
2374
- if (!ts.isPropertyAssignment(property))
2375
- continue;
2332
+ if (!ts.isPropertyAssignment(property)) continue;
2376
2333
  const value = property.initializer;
2377
2334
  const start = value.getStart();
2378
2335
  const end = value.getEnd();
@@ -2409,8 +2366,7 @@ function convertToRgb(fill, mode) {
2409
2366
  const color = culori2.rgb(parsed);
2410
2367
  const modeColor = culori2.rgb(mode == "dark" ? "#000" : "#fff");
2411
2368
  const bgColor = fill === "transparent" ? void 0 : modeColor;
2412
- if (!color)
2413
- return fill;
2369
+ if (!color) return fill;
2414
2370
  if (!bgColor || parsed?.alpha === void 0 || parsed.alpha === 1) {
2415
2371
  return culori2.formatRgb(color);
2416
2372
  }
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "tokenami",
3
- "version": "0.0.81",
3
+ "version": "0.0.82",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
7
7
  "module": "./dist/index.js",
8
8
  "types": "./dist/index.d.ts",
9
- "repository": "https://github.com/tokenami/tokenami",
9
+ "repository": "tokenami/tokenami",
10
10
  "publishConfig": {
11
11
  "access": "public"
12
12
  },
@@ -40,13 +40,13 @@
40
40
  "@types/culori": "^2.1.0",
41
41
  "@types/inquirer": "^9.0.7",
42
42
  "@types/node": "^20.3.1",
43
- "tsup": "^7.0.0",
43
+ "tsup": "^8.4.0",
44
44
  "typescript": "^5.1.3"
45
45
  },
46
46
  "dependencies": {
47
47
  "@stitches/stringify": "^1.2.8",
48
- "@tokenami/config": "0.0.81",
49
- "@tokenami/ds": "0.0.81",
48
+ "@tokenami/config": "0.0.82",
49
+ "@tokenami/ds": "0.0.82",
50
50
  "acorn": "^8.11.3",
51
51
  "acorn-walk": "^8.3.2",
52
52
  "browserslist": "^4.24.4",
@@ -67,5 +67,5 @@
67
67
  "peerDependencies": {
68
68
  "typescript": ">= 5"
69
69
  },
70
- "gitHead": "ebf9ebecf6d23586be19b859f7d243de7346f9f7"
70
+ "gitHead": "ba381af577865573615b630033e9fbd429d60bba"
71
71
  }