koishi-plugin-ccb-plus 0.2.6 → 0.2.8-beta.1

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 (2) hide show
  1. package/lib/index.js +150 -104
  2. package/package.json +1 -1
package/lib/index.js CHANGED
@@ -42,10 +42,10 @@ var path = __toESM(require("path"));
42
42
  var name = "ccb-plus";
43
43
  var inject = ["database"];
44
44
  var Config = import_koishi.Schema.object({
45
- ywWindow: import_koishi.Schema.number().default(60).description("全局触发赛博阳痿的窗口时间(秒)"),
45
+ ywWindow: import_koishi.Schema.number().default(60).description("全局触发冷却的窗口时间(秒)"),
46
46
  ywThreshold: import_koishi.Schema.number().default(5).description("全局窗口时间内最大ccb数"),
47
- ywBanDuration: import_koishi.Schema.number().default(900).description("全局养胃时长(秒)"),
48
- ywProbability: import_koishi.Schema.number().default(0.1).min(0).max(1).description("全局随机养胃概率"),
47
+ ywBanDuration: import_koishi.Schema.number().default(900).description("全局冷却时长(秒)"),
48
+ ywProbability: import_koishi.Schema.number().default(0.1).min(0).max(1).description("全局随机冷却概率"),
49
49
  whiteList: import_koishi.Schema.array(String).default([]).description("全局配置的黑名单"),
50
50
  selfCcb: import_koishi.Schema.boolean().default(false).description("是否允许对自己ccb"),
51
51
  critProb: import_koishi.Schema.number().default(0.2).min(0).max(1).description("全局暴击概率"),
@@ -54,9 +54,9 @@ var Config = import_koishi.Schema.object({
54
54
  userId: import_koishi.Schema.string().required().description("用户ID"),
55
55
  ywWindow: import_koishi.Schema.number().default(10).description("特权窗口时间(秒)"),
56
56
  ywThreshold: import_koishi.Schema.number().default(999).description("特权窗口内最大次数"),
57
- ywProbability: import_koishi.Schema.number().default(0).min(0).max(1).description("特权养胃概率"),
57
+ ywProbability: import_koishi.Schema.number().default(0).min(0).max(1).description("特权冷却概率"),
58
58
  critProb: import_koishi.Schema.number().default(0.8).min(0).max(1).description("特权暴击概率"),
59
- ywBanDuration: import_koishi.Schema.number().default(60).description("特权养胃时长(秒)")
59
+ ywBanDuration: import_koishi.Schema.number().default(60).description("特权冷却时长(秒)")
60
60
  })).role("table").description("开挂名单(优先级高于全局设置)")
61
61
  });
62
62
  function apply(ctx, config) {
@@ -82,6 +82,7 @@ function apply(ctx, config) {
82
82
  const actionTimes = {};
83
83
  const banList = {};
84
84
  const nicknameCache = /* @__PURE__ */ new Map();
85
+ const MAX_CACHE_SIZE = 2e3;
85
86
  const CACHE_DURATION = 5 * 60 * 1e3;
86
87
  ctx.on("ready", async () => {
87
88
  const DATA_FILE = path.join(ctx.baseDir, "data", "ccb.json");
@@ -118,6 +119,27 @@ function apply(ctx, config) {
118
119
  }
119
120
  }
120
121
  });
122
+ const CLEANUP_INTERVAL = 10 * 60 * 1e3;
123
+ const cleanupTimer = setInterval(() => {
124
+ const now = Date.now() / 1e3;
125
+ for (const userId in banList) {
126
+ if (banList[userId] < now) delete banList[userId];
127
+ }
128
+ for (const userId in actionTimes) {
129
+ if (!actionTimes[userId] || actionTimes[userId].length === 0) {
130
+ delete actionTimes[userId];
131
+ }
132
+ }
133
+ const cacheNow = Date.now();
134
+ for (const [key, value] of nicknameCache) {
135
+ if (cacheNow - value.timestamp > CACHE_DURATION) {
136
+ nicknameCache.delete(key);
137
+ }
138
+ }
139
+ }, CLEANUP_INTERVAL);
140
+ ctx.on("dispose", () => {
141
+ clearInterval(cleanupTimer);
142
+ });
121
143
  function getAvatar(userId) {
122
144
  return `https://q4.qlogo.cn/headimg_dl?dst_uin=${userId}&spec=640`;
123
145
  }
@@ -133,6 +155,10 @@ function apply(ctx, config) {
133
155
  if (name2 && name2 !== userId) {
134
156
  const actualName = name2.trim();
135
157
  if (actualName) {
158
+ if (nicknameCache.size >= MAX_CACHE_SIZE) {
159
+ const oldestKey = nicknameCache.keys().next().value;
160
+ if (oldestKey) nicknameCache.delete(oldestKey);
161
+ }
136
162
  nicknameCache.set(cacheKey, { name: actualName, timestamp: now });
137
163
  return actualName;
138
164
  }
@@ -150,7 +176,7 @@ function apply(ctx, config) {
150
176
  }
151
177
  try {
152
178
  const userInfo = await session.bot.getUser(userId);
153
- const displayName = userInfo?.name || userInfo?.nick || userInfo?.nickname;
179
+ const displayName = userInfo?.name || userInfo?.nick;
154
180
  const result = setAndReturnName(displayName);
155
181
  if (result) return result;
156
182
  } catch (e) {
@@ -174,39 +200,62 @@ function apply(ctx, config) {
174
200
  return null;
175
201
  }
176
202
  __name(checkGroupCommand, "checkGroupCommand");
203
+ async function findTargetUser(session, input) {
204
+ if (!input) return null;
205
+ const atMatch = input.match(/<at\s[^>]*id="([^"]+)"/);
206
+ if (atMatch) return atMatch[1];
207
+ const unionMatch = input.match(/^[^:]+:(.+)$/);
208
+ if (unionMatch) {
209
+ return unionMatch[1];
210
+ }
211
+ if (/^\d+$/.test(input)) {
212
+ return input;
213
+ }
214
+ try {
215
+ const list = await session.bot.getGuildMemberList(session.guildId);
216
+ const members = list?.data || [];
217
+ const clean = /* @__PURE__ */ __name((s) => s.replace(/\s/g, "").toLowerCase(), "clean");
218
+ const targetName = clean(input);
219
+ let found = members.find((m) => {
220
+ const nick = m.nick || m.user?.name || m.name || "";
221
+ return clean(nick) === targetName;
222
+ });
223
+ if (!found) {
224
+ found = members.find((m) => {
225
+ const nick = m.nick || m.user?.name || m.name || "";
226
+ return clean(nick).includes(targetName);
227
+ });
228
+ }
229
+ if (found) return found.user?.id;
230
+ } catch (e) {
231
+ }
232
+ return null;
233
+ }
234
+ __name(findTargetUser, "findTargetUser");
177
235
  async function validateTargetUser(session, target) {
178
- let targetUserId = session.userId;
179
236
  if (target) {
180
- const match = target.match(/^[^:]+:(.+)$/);
181
- if (match) {
182
- targetUserId = match[1];
237
+ const foundId = await findTargetUser(session, target);
238
+ if (foundId) {
183
239
  try {
184
- const memberInfo = await session.bot.getGuildMember(session.guildId, targetUserId);
185
- if (!memberInfo) {
186
- return "无法找到指定用户,请检查输入是否正确。";
187
- }
188
- } catch (error) {
240
+ const member = await session.bot.getGuildMember(session.guildId, foundId);
241
+ if (!member) return "无法找到指定用户,请检查输入是否正确。";
242
+ } catch {
189
243
  return "无法找到指定用户,请检查输入是否正确。";
190
244
  }
245
+ return foundId;
191
246
  }
192
- } else if (session.quote?.user?.id) {
193
- targetUserId = session.quote.user.id;
194
- try {
195
- const memberInfo = await session.bot.getGuildMember(session.guildId, targetUserId);
196
- if (!memberInfo) {
197
- return "无法找到指定用户,请检查输入是否正确。";
198
- }
199
- } catch (error) {
200
- return "无法找到指定用户,请检查输入是否正确。";
201
- }
247
+ return "无法找到指定用户,请检查输入是否正确。";
248
+ }
249
+ if (session.quote?.user?.id) {
250
+ return session.quote.user.id;
202
251
  }
203
- return targetUserId;
252
+ return session.userId;
204
253
  }
205
254
  __name(validateTargetUser, "validateTargetUser");
206
255
  async function updateCCBRecord(session, groupId, targetUserId, duration, V, nickname, crit, pic) {
207
256
  const [record] = await ctx.database.get("ccb_record", { groupId, userId: targetUserId });
208
257
  if (!record) {
209
- return await createNewCCBRecord(session, groupId, targetUserId, duration, V, nickname, pic);
258
+ return await createNewCCBRecord(session, groupId, targetUserId, duration, V, nickname, crit, pic);
210
259
  }
211
260
  const senderId = session.userId;
212
261
  const newNum = (record.num || 0) + 1;
@@ -234,12 +283,6 @@ function apply(ctx, config) {
234
283
  if (ccb_by[k]) ccb_by[k].max = false;
235
284
  }
236
285
  if (ccb_by[senderId]) ccb_by[senderId].max = true;
237
- } else {
238
- for (const k in ccb_by) {
239
- if (ccb_by[k] && !ccb_by[k].max) {
240
- ccb_by[k].max = false;
241
- }
242
- }
243
286
  }
244
287
  await ctx.database.set("ccb_record", { groupId, userId: targetUserId }, {
245
288
  num: newNum,
@@ -256,7 +299,7 @@ function apply(ctx, config) {
256
299
  return message;
257
300
  }
258
301
  __name(updateCCBRecord, "updateCCBRecord");
259
- async function createNewCCBRecord(session, groupId, targetUserId, duration, V, nickname, pic) {
302
+ async function createNewCCBRecord(session, groupId, targetUserId, duration, V, nickname, crit, pic) {
260
303
  const newRecord = {
261
304
  groupId,
262
305
  userId: targetUserId,
@@ -266,7 +309,7 @@ function apply(ctx, config) {
266
309
  ccb_by: { [session.userId]: { count: 1, first: true, max: true } }
267
310
  };
268
311
  await ctx.database.upsert("ccb_record", [newRecord]);
269
- const resultMessage = `你和${nickname}发生了${duration}min长的ccb行为,向ta注入了${V.toFixed(2)}ml的生命因子`;
312
+ const resultMessage = crit ? `你和${nickname}发生了${duration}min长的ccb行为,向ta注入了 💥 暴击!${V.toFixed(2)}ml的生命因子` : `你和${nickname}发生了${duration}min长的ccb行为,向ta注入了${V.toFixed(2)}ml的生命因子`;
270
313
  const message = [
271
314
  resultMessage,
272
315
  import_koishi.segment.image(pic),
@@ -279,6 +322,17 @@ function apply(ctx, config) {
279
322
  const checkResult = checkGroupCommand(session);
280
323
  if (checkResult) return checkResult;
281
324
  const senderId = session.userId;
325
+ const checkCooldown = /* @__PURE__ */ __name((lastToggle) => {
326
+ const now2 = Date.now();
327
+ const cooldownMs = config.toggleCooldown * 1e3;
328
+ if (now2 - lastToggle < cooldownMs) {
329
+ const remain = Math.ceil((cooldownMs - (now2 - lastToggle)) / 1e3);
330
+ const m = Math.floor(remain / 60);
331
+ const s = remain % 60;
332
+ return `操作太频繁了,请等待 ${m}分${s}秒 后再试。`;
333
+ }
334
+ return null;
335
+ }, "checkCooldown");
282
336
  const hasOff = "off" in options;
283
337
  const hasOn = "on" in options;
284
338
  if (hasOff || hasOn) {
@@ -286,13 +340,7 @@ function apply(ctx, config) {
286
340
  const optionVal = isOff ? options.off : options.on;
287
341
  let targetUserStr = null;
288
342
  if (typeof optionVal === "string" && optionVal.trim()) {
289
- const val = optionVal.trim();
290
- const atIdMatch = val.match(/<at\s[^>]*id="([^"]+)"/);
291
- if (atIdMatch) {
292
- targetUserStr = atIdMatch[1];
293
- } else {
294
- targetUserStr = val;
295
- }
343
+ targetUserStr = await findTargetUser(session, optionVal.trim());
296
344
  }
297
345
  if (!targetUserStr) {
298
346
  const atEl = session.elements?.find((el) => el.type === "at");
@@ -301,16 +349,14 @@ function apply(ctx, config) {
301
349
  }
302
350
  }
303
351
  if (!targetUserStr) {
352
+ if (typeof optionVal === "string" && optionVal.trim()) {
353
+ return `无法找到用户「${optionVal}」,请检查输入是否正确。`;
354
+ }
304
355
  const now2 = Date.now();
305
356
  const [userSetting] = await ctx.database.get("ccb_setting", { userId: senderId });
306
357
  const lastToggle = userSetting?.lastToggleTime || 0;
307
- const cooldownMs = config.toggleCooldown * 1e3;
308
- if (now2 - lastToggle < cooldownMs) {
309
- const remain = Math.ceil((cooldownMs - (now2 - lastToggle)) / 1e3);
310
- const m = Math.floor(remain / 60);
311
- const s = remain % 60;
312
- return `操作太频繁了,请等待 ${m}分${s}秒 后再试。`;
313
- }
358
+ const cooldownResult = checkCooldown(lastToggle);
359
+ if (cooldownResult) return cooldownResult;
314
360
  const newOptOut = !!isOff;
315
361
  await ctx.database.upsert("ccb_setting", [{
316
362
  userId: senderId,
@@ -320,30 +366,27 @@ function apply(ctx, config) {
320
366
  }]);
321
367
  return newOptOut ? "已开启全局保护模式,阻止你被ccb。" : "已关闭全局保护模式,允许你被ccb。";
322
368
  } else {
323
- let targetId = targetUserStr;
324
- const match = targetUserStr.match(/^[^:]+:(.+)$/);
325
- if (match) targetId = match[1];
326
369
  try {
327
- const memberInfo = await session.bot.getGuildMember(session.guildId, targetId);
370
+ const memberInfo = await session.bot.getGuildMember(session.guildId, targetUserStr);
328
371
  if (!memberInfo) {
329
372
  return "无法找到指定用户,请检查输入是否正确。";
330
373
  }
331
374
  } catch (error) {
332
375
  return "无法找到指定用户,请检查输入是否正确。";
333
376
  }
377
+ const targetId = targetUserStr;
378
+ const now2 = Date.now();
334
379
  const [userSetting] = await ctx.database.get("ccb_setting", { userId: senderId });
380
+ const lastToggle = userSetting?.lastToggleTime || 0;
381
+ const cooldownResult = checkCooldown(lastToggle);
382
+ if (cooldownResult) return cooldownResult;
335
383
  const overrides = userSetting?.overrides || {};
336
- if (isOff) {
337
- overrides[targetId] = false;
338
- } else {
339
- overrides[targetId] = true;
340
- }
384
+ overrides[targetId] = !isOff;
341
385
  await ctx.database.upsert("ccb_setting", [{
342
386
  userId: senderId,
343
387
  overrides,
344
- // 保持其他字段默认值或原值
345
388
  optOut: userSetting?.optOut ?? false,
346
- lastToggleTime: userSetting?.lastToggleTime ?? 0
389
+ lastToggleTime: now2
347
390
  }]);
348
391
  const targetNick = await getUserNickname(session, targetId).catch(() => targetId) || targetId;
349
392
  return isOff ? `已禁止用户 ${targetNick} 对你ccb。` : `已允许用户 ${targetNick} 对你ccb。`;
@@ -389,6 +432,10 @@ function apply(ctx, config) {
389
432
  const nickname = await getUserNickname(session, targetUserId) || targetUserId;
390
433
  return `${nickname} 拒绝了和你ccb。`;
391
434
  }
435
+ if (senderSetting?.overrides?.[targetUserId] === false) {
436
+ const nickname = await getUserNickname(session, targetUserId) || targetUserId;
437
+ return `你已禁止与 ${nickname} 进行ccb。`;
438
+ }
392
439
  const [targetSetting] = await ctx.database.get("ccb_setting", { userId: targetUserId });
393
440
  if (targetSetting) {
394
441
  const overrides = targetSetting.overrides || {};
@@ -402,7 +449,7 @@ function apply(ctx, config) {
402
449
  }
403
450
  }
404
451
  if (targetUserId === actorId && !config.selfCcb) {
405
- return "怎么还能捅到自己的啊(恼)";
452
+ return "怎么还能对自己下手啊(恼)";
406
453
  }
407
454
  const duration = parseFloat((Math.random() * 59 + 1).toFixed(2));
408
455
  let V = parseFloat((Math.random() * 99 + 1).toFixed(2));
@@ -413,18 +460,10 @@ function apply(ctx, config) {
413
460
  crit = true;
414
461
  }
415
462
  const pic = getAvatar(targetUserId);
416
- const exists = await ctx.database.get("ccb_record", {
417
- groupId: session.guildId,
418
- userId: targetUserId
419
- });
420
463
  let message;
421
464
  try {
422
465
  const nickname = await getUserNickname(session, targetUserId);
423
- if (exists.length > 0) {
424
- message = await updateCCBRecord(session, session.guildId, targetUserId, duration, V, nickname, crit, pic);
425
- } else {
426
- message = await createNewCCBRecord(session, session.guildId, targetUserId, duration, V, nickname, pic);
427
- }
466
+ message = await updateCCBRecord(session, session.guildId, targetUserId, duration, V, nickname, crit, pic);
428
467
  } catch (e) {
429
468
  console.error(`报错: ${e}`);
430
469
  return "对方拒绝了和你ccb";
@@ -432,26 +471,37 @@ function apply(ctx, config) {
432
471
  if (Math.random() < currentConfig.ywProbability) {
433
472
  banList[actorId] = now + currentConfig.ywBanDuration;
434
473
  await session.send(message);
435
- return "💥你炸膛了!再也不能ccb了(悲)";
474
+ return "💥你炸膛了!不能ccb了(悲)";
436
475
  }
437
476
  return message;
438
477
  });
478
+ async function buildRanking(session, title, data, formatLine) {
479
+ const nicknameMap = /* @__PURE__ */ new Map();
480
+ await Promise.all(data.map(async (r) => {
481
+ nicknameMap.set(r.userId, await getUserNickname(session, r.userId));
482
+ }));
483
+ let msg = `${title}
484
+ `;
485
+ for (let i = 0; i < data.length; i++) {
486
+ const nick = nicknameMap.get(data[i].userId) || data[i].userId;
487
+ msg += formatLine(data[i], nick, i);
488
+ }
489
+ return msg.trim();
490
+ }
491
+ __name(buildRanking, "buildRanking");
439
492
  ctx.command("ccbtop", "按次数排行").action(async ({ session }) => {
440
493
  const checkResult = checkGroupCommand(session);
441
494
  if (checkResult) return checkResult;
442
495
  const groupData = await ctx.database.get("ccb_record", { groupId: session.guildId });
443
496
  if (!groupData.length) return "当前群暂无ccb记录。";
444
497
  const top5 = groupData.sort((a, b) => b.num - a.num).slice(0, 5);
445
- const nicknamePromises = top5.map((r) => getUserNickname(session, r.userId));
446
- const nicknames = await Promise.all(nicknamePromises);
447
- let msg = "被ccb排行榜 TOP5:\n";
448
- for (let i = 0; i < top5.length; i++) {
449
- const r = top5[i];
450
- const nick = nicknames[i] || r.userId;
451
- msg += `${i + 1}. ${nick} - 次数:${r.num}
452
- `;
453
- }
454
- return msg.trim();
498
+ return buildRanking(
499
+ session,
500
+ "被ccb排行榜 TOP5",
501
+ top5,
502
+ (r, nick, i) => `${i + 1}. ${nick} - 次数:${r.num}
503
+ `
504
+ );
455
505
  });
456
506
  ctx.command("ccbvol", "按注入量排行").action(async ({ session }) => {
457
507
  const checkResult = checkGroupCommand(session);
@@ -459,16 +509,13 @@ function apply(ctx, config) {
459
509
  const groupData = await ctx.database.get("ccb_record", { groupId: session.guildId });
460
510
  if (!groupData.length) return "当前群暂无ccb记录。";
461
511
  const top5 = groupData.sort((a, b) => b.vol - a.vol).slice(0, 5);
462
- const nicknamePromises = top5.map((r) => getUserNickname(session, r.userId));
463
- const nicknames = await Promise.all(nicknamePromises);
464
- let msg = "被注入量排行榜 TOP5:\n";
465
- for (let i = 0; i < top5.length; i++) {
466
- const r = top5[i];
467
- const nick = nicknames[i] || r.userId;
468
- msg += `${i + 1}. ${nick} - 累计注入:${r.vol.toFixed(2)}ml
469
- `;
470
- }
471
- return msg.trim();
512
+ return buildRanking(
513
+ session,
514
+ "被注入量排行榜 TOP5",
515
+ top5,
516
+ (r, nick, i) => `${i + 1}. ${nick} - 累计注入:${r.vol.toFixed(2)}ml
517
+ `
518
+ );
472
519
  });
473
520
  ctx.command("ccbmax", "按max值排行并输出产生者").action(async ({ session }) => {
474
521
  const checkResult = checkGroupCommand(session);
@@ -527,16 +574,15 @@ function apply(ctx, config) {
527
574
  ctx.command("ccbinfo [target:user]", "查询某人ccb信息").action(async ({ session }, target) => {
528
575
  const checkResult = checkGroupCommand(session);
529
576
  if (checkResult) return checkResult;
530
- let targetUserId = session.userId;
531
- if (target) {
532
- const match = target.match(/^[^:]+:(.+)$/);
533
- if (match) targetUserId = match[1];
577
+ let targetUserId = await validateTargetUser(session, target);
578
+ if (targetUserId.startsWith("无法找到")) {
579
+ return targetUserId;
534
580
  }
535
581
  const [record] = await ctx.database.get("ccb_record", { groupId: session.guildId, userId: targetUserId });
536
582
  if (!record) return "该用户暂无ccb记录。";
537
583
  const total_num = record.num;
538
584
  const total_vol = record.vol;
539
- let max_val = record.max || (total_num > 0 ? total_vol / total_num : 0);
585
+ const max_val = record.max || (total_num > 0 ? total_vol / total_num : 0);
540
586
  const groupData = await ctx.database.get("ccb_record", { groupId: session.guildId });
541
587
  let cb_total = 0;
542
588
  for (const r of groupData) {
@@ -564,15 +610,15 @@ function apply(ctx, config) {
564
610
  const first_nick = first_actor ? await getUserNickname(session, first_actor) : "未知";
565
611
  const msg = [
566
612
  `【${target_nick} 】`,
567
- `• 破壁人:${first_nick}`,
568
- `• 北朝:${total_num}`,
569
- `• 朝壁:${cb_total}`,
570
- `• 诗经:${total_vol.toFixed(2)}ml`,
571
- `• 马克思:${max_val.toFixed(2)}ml`
613
+ `• 开拓者:${first_nick}`,
614
+ `• 被注入次数:${total_num}`,
615
+ `• 主动出击:${cb_total}`,
616
+ `• 累计容量:${total_vol.toFixed(2)}ml`,
617
+ `• 单次最高:${max_val.toFixed(2)}ml`
572
618
  ].join("\n");
573
619
  return msg;
574
620
  });
575
- ctx.command("xnn", "XNN榜 - 计算群中最xnn特质的群友").action(async ({ session }) => {
621
+ ctx.command("ccbcharm", "魅力榜 - 计算群中最受欢迎的群友").action(async ({ session }) => {
576
622
  const checkResult = checkGroupCommand(session);
577
623
  if (checkResult) return checkResult;
578
624
  const w_num = 1;
@@ -596,11 +642,11 @@ function apply(ctx, config) {
596
642
  await Promise.all(ranking.map(async (r) => {
597
643
  nicknameMap.set(r.userId, await getUserNickname(session, r.userId));
598
644
  }));
599
- let msg = "💎 XNN TOP5 💎\n";
645
+ let msg = "💎 魅力榜 TOP5 💎\n";
600
646
  for (let i = 0; i < ranking.length; i++) {
601
647
  const { userId, val } = ranking[i];
602
648
  const nick = nicknameMap.get(userId) || userId;
603
- msg += `${i + 1}. ${nick} - XNN值:${val.toFixed(2)}
649
+ msg += `${i + 1}. ${nick} - 魅力值:${val.toFixed(2)}
604
650
  `;
605
651
  }
606
652
  return msg.trim();
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "koishi-plugin-ccb-plus",
3
3
  "description": "Koishi插件,与群友发生ccb行为。(移植自astrbot_plugin_ccb_plus)",
4
- "version": "0.2.6",
4
+ "version": "0.2.8-beta.1",
5
5
  "main": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",
7
7
  "files": [