@sanctumterra/raknet 1.3.75 → 1.3.76

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.
@@ -29,10 +29,14 @@ export declare class Framer {
29
29
  outputBackup: Map<number, Frame[]>;
30
30
  _tickCount: number;
31
31
  private mtuDiff;
32
+ private readonly BATCH_SIZE;
33
+ private readonly MAX_BATCH_INTERVAL;
34
+ private lastBatchTime;
32
35
  private readonly ORDERING_QUEUE_TIMEOUT;
33
- private readonly FRAGMENT_TIMEOUT;
34
36
  constructor(client: Client);
35
37
  tick(): void;
38
+ private processBatchedAcksAndNacks;
39
+ private processBatchedOrderedFrames;
36
40
  incommingMessage(payload: Buffer, rinfo: RemoteInfo): void;
37
41
  private processFrame;
38
42
  handle(frameSet: Frameset): void;
@@ -1 +1 @@
1
- {"version":3,"file":"framer.d.ts","sourceRoot":"","sources":["../../src/client/framer.ts"],"names":[],"mappings":"AAAA,OAAO,EAIN,KAAK,EAGL,QAAQ,EAWR,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AAEpC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAGvC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAG7C,UAAU,WAAW;IACpB,KAAK,EAAE,KAAK,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;CAClB;AAED,qBAAa,MAAM;IAClB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,iBAAiB,CAAM;IAC/B,OAAO,CAAC,sBAAsB,CAA0B;IACxD,OAAO,CAAC,kBAAkB,CAA0B;IACpD,OAAO,CAAC,yBAAyB,CAAmC;IACpE,OAAO,CAAC,eAAe,CAAmC;IAC1D,SAAS,CAAC,kBAAkB,EAAE,KAAK,CAAC,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,CAG3D;IACF,SAAS,CAAC,QAAQ,CAAC,cAAc,EAAE,GAAG,CACrC,MAAM,EACN;QAAE,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CACpD,CAAa;IAEP,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,mBAAmB,EAAE,MAAM,EAAE,CAAC;IAC9B,gBAAgB,EAAE,QAAQ,CAAC;IAClC,SAAS,CAAC,cAAc,SAAK;IAC7B,SAAS,CAAC,gBAAgB,SAAK;IAC/B,SAAS,CAAC,mBAAmB,SAAK;IAC3B,YAAY,EAAE,KAAK,EAAE,CAAM;IAClC,OAAO,CAAC,sBAAsB,CAAK;IAC5B,YAAY,uBAA8B;IAC1C,UAAU,SAAK;IAEtB,OAAO,CAAC,OAAO,CAAS;IAExB,OAAO,CAAC,QAAQ,CAAC,sBAAsB,CAAO;IAC9C,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAQ;gBAE7B,MAAM,EAAE,MAAM;IASnB,IAAI;IAkFJ,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU;IAyF1D,OAAO,CAAC,YAAY;IA6Eb,MAAM,CAAC,QAAQ,EAAE,QAAQ;IAqDhC,OAAO,CAAC,WAAW;IAkBnB,OAAO,CAAC,aAAa;IAqBrB,OAAO,CAAC,oBAAoB;IAkB5B,OAAO,CAAC,eAAe;IAyBvB,OAAO,CAAC,WAAW;IAcnB,OAAO,CAAC,4BAA4B;IA6B7B,YAAY,CAClB,OAAO,EAAE,MAAM,EACf,QAAQ,GAAE,QAA0B,GAClC,IAAI;IAOA,SAAS,CAAC,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,GAAG,IAAI;IA+BxD,OAAO,CAAC,gBAAgB;IAsBxB,OAAO,CAAC,UAAU;IAgBX,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;CA8BtC"}
1
+ {"version":3,"file":"framer.d.ts","sourceRoot":"","sources":["../../src/client/framer.ts"],"names":[],"mappings":"AAAA,OAAO,EAIN,KAAK,EAGL,QAAQ,EAUR,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AAEpC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAGvC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C,UAAU,WAAW;IACpB,KAAK,EAAE,KAAK,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;CAClB;AAED,qBAAa,MAAM;IAClB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,iBAAiB,CAAM;IAC/B,OAAO,CAAC,sBAAsB,CAA0B;IACxD,OAAO,CAAC,kBAAkB,CAA0B;IACpD,OAAO,CAAC,yBAAyB,CAAmC;IACpE,OAAO,CAAC,eAAe,CAAmC;IAC1D,SAAS,CAAC,kBAAkB,EAAE,KAAK,CAAC,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,CAG3D;IACF,SAAS,CAAC,QAAQ,CAAC,cAAc,EAAE,GAAG,CACrC,MAAM,EACN;QAAE,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CACpD,CAAa;IAEP,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,mBAAmB,EAAE,MAAM,EAAE,CAAC;IAC9B,gBAAgB,EAAE,QAAQ,CAAC;IAClC,SAAS,CAAC,cAAc,SAAK;IAC7B,SAAS,CAAC,gBAAgB,SAAK;IAC/B,SAAS,CAAC,mBAAmB,SAAK;IAC3B,YAAY,EAAE,KAAK,EAAE,CAAM;IAClC,OAAO,CAAC,sBAAsB,CAAK;IAC5B,YAAY,uBAA8B;IAC1C,UAAU,SAAK;IAEtB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAM;IACjC,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAM;IACzC,OAAO,CAAC,aAAa,CAAK;IAC1B,OAAO,CAAC,QAAQ,CAAC,sBAAsB,CAAO;gBAElC,MAAM,EAAE,MAAM;IASnB,IAAI;IAmCX,OAAO,CAAC,0BAA0B;IAoBlC,OAAO,CAAC,2BAA2B;IAiC5B,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU;IAoE1D,OAAO,CAAC,YAAY;IA6Eb,MAAM,CAAC,QAAQ,EAAE,QAAQ;IA0DhC,OAAO,CAAC,WAAW;IAYnB,OAAO,CAAC,aAAa;IAcrB,OAAO,CAAC,oBAAoB;IAqB5B,OAAO,CAAC,eAAe;IAevB,OAAO,CAAC,WAAW;IAenB,OAAO,CAAC,4BAA4B;IA2B7B,YAAY,CAClB,OAAO,EAAE,MAAM,EACf,QAAQ,GAAE,QAA0B,GAClC,IAAI;IAOA,SAAS,CAAC,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,GAAG,IAAI;IA+BxD,OAAO,CAAC,gBAAgB;IAsBxB,OAAO,CAAC,UAAU;IAqBX,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;CA6BtC"}
@@ -26,8 +26,10 @@ class Framer {
26
26
  outputBackup = new Map();
27
27
  _tickCount = 0;
28
28
  mtuDiff;
29
+ BATCH_SIZE = 32; // Maximum frames to process in a batch
30
+ MAX_BATCH_INTERVAL = 50; // Maximum time (ms) to wait before processing a batch
31
+ lastBatchTime = 0;
29
32
  ORDERING_QUEUE_TIMEOUT = 500;
30
- FRAGMENT_TIMEOUT = 5000;
31
33
  constructor(client) {
32
34
  this.client = client;
33
35
  this.outputFrameQueue = new proto_2.Frameset();
@@ -38,82 +40,84 @@ class Framer {
38
40
  }
39
41
  tick() {
40
42
  this._tickCount++;
43
+ const now = Date.now();
41
44
  if (this.client.status === proto_1.Status.Disconnected ||
42
45
  this.client.status === proto_1.Status.Disconnecting) {
43
- if (this.client.options.debug) {
44
- utils_1.Logger.debug(`[Framer] Skipping tick - client status: ${proto_1.Status[this.client.status]}`);
45
- }
46
46
  return;
47
47
  }
48
48
  // Send a ping every 50 ticks.
49
49
  if (this._tickCount % 50 === 0) {
50
- const now = Date.now();
51
50
  const ping = new proto_1.ConnectedPing();
52
51
  ping.timestamp = BigInt(now);
53
52
  this.frameAndSend(ping.serialize(), proto_1.Priority.Immediate);
54
53
  }
55
- // Process ACKs.
54
+ // Batch process ACKs and NACKs
55
+ this.processBatchedAcksAndNacks();
56
+ // Process ordered frames in batches
57
+ this.processBatchedOrderedFrames(now);
58
+ // Send queued frames if we have enough or enough time has passed
59
+ if (this.outputFrames.length >= this.BATCH_SIZE ||
60
+ (this.outputFrames.length > 0 &&
61
+ now - this.lastBatchTime >= this.MAX_BATCH_INTERVAL)) {
62
+ this.sendQueue(this.outputFrames.length);
63
+ this.lastBatchTime = now;
64
+ }
65
+ }
66
+ processBatchedAcksAndNacks() {
67
+ // Batch process ACKs
56
68
  if (this.receivedFrameSequences.size > 0) {
57
69
  const ackSeqs = Array.from(this.receivedFrameSequences);
58
70
  this.receivedFrameSequences.clear();
59
- if (this.client.options.debug) {
60
- utils_1.Logger.debug(`[Framer] Sending ACK for ${ackSeqs.length} sequences`);
61
- }
62
71
  const ack = new proto_1.Ack();
63
72
  ack.sequences = ackSeqs;
64
73
  this.client.send(ack.serialize());
65
74
  }
66
- // Process NACKs.
75
+ // Batch process NACKs
67
76
  if (this.lostFrameSequences.size > 0) {
68
77
  const nackSeqs = Array.from(this.lostFrameSequences);
69
78
  this.lostFrameSequences.clear();
70
- if (this.client.options.debug) {
71
- utils_1.Logger.debug(`[Framer] Sending NACK for ${nackSeqs.length} sequences`);
72
- }
73
79
  const nack = new proto_1.Nack();
74
80
  nack.sequences = nackSeqs;
75
81
  this.client.send(nack.serialize());
76
82
  }
77
- const now = Date.now();
83
+ }
84
+ processBatchedOrderedFrames(now) {
85
+ const processedChannels = new Set();
78
86
  for (let channel = 0; channel < this.inputOrderingQueue.length; channel++) {
87
+ if (processedChannels.has(channel))
88
+ continue;
79
89
  const queue = this.inputOrderingQueue[channel];
90
+ let processed = 0;
80
91
  let expected = this.inputOrderIndex[channel];
81
- if (queue.has(expected)) {
82
- // biome-ignore lint/style/noNonNullAssertion: <explanation>
92
+ while (queue.has(expected) && processed < this.BATCH_SIZE) {
83
93
  const queued = queue.get(expected);
94
+ if (!queued) {
95
+ expected++;
96
+ continue;
97
+ }
84
98
  if (now - queued.timestamp > this.ORDERING_QUEUE_TIMEOUT) {
85
- utils_1.Logger.warn(`[Framer] Timeout waiting for ordered frame ${expected} on channel ${channel}; skipping it`);
86
99
  this.processFrame(queued.frame);
87
100
  queue.delete(expected);
88
101
  expected++;
89
- this.inputOrderIndex[channel] = expected;
102
+ processed++;
103
+ }
104
+ else {
105
+ break;
90
106
  }
91
107
  }
92
- }
93
- for (const [splitId, entry] of this.fragmentsQueue.entries()) {
94
- if (now - entry.timestamp > this.FRAGMENT_TIMEOUT) {
95
- utils_1.Logger.warn(`[Framer] Timeout reassembling fragments for splitId=${splitId}; discarding incomplete fragments`);
96
- this.fragmentsQueue.delete(splitId);
108
+ if (processed > 0) {
109
+ this.inputOrderIndex[channel] = expected;
110
+ processedChannels.add(channel);
97
111
  }
98
112
  }
99
- if (this.client.options.debug) {
100
- utils_1.Logger.debug(`[Framer] Queue state - Frames: ${this.outputFrames.length}, Backup: ${this.outputBackup.size}`);
101
- }
102
- this.sendQueue(this.outputFrames.length);
103
113
  }
104
114
  incommingMessage(payload, rinfo) {
105
115
  let header = payload.readUint8();
106
116
  if ((header & 0xf0) === 0x80)
107
117
  header = 0x80;
108
- if (this.client.options.debug) {
109
- utils_1.Logger.debug(`[Framer] Received packet ${header} from ${rinfo.address}:${rinfo.port}`);
110
- }
111
118
  switch (header) {
112
119
  case proto_1.Packet.Ack: {
113
120
  const ack = new proto_1.Ack(payload).deserialize();
114
- if (this.client.options.debug) {
115
- utils_1.Logger.debug(`[Framer] Processing ACK with ${ack.sequences.length} sequences`);
116
- }
117
121
  for (let i = 0, len = ack.sequences.length; i < len; i++) {
118
122
  this.outputBackup.delete(ack.sequences[i]);
119
123
  }
@@ -121,15 +125,9 @@ class Framer {
121
125
  }
122
126
  case proto_1.Packet.Nack: {
123
127
  const nack = new proto_1.Nack(payload).deserialize();
124
- if (this.client.options.debug) {
125
- utils_1.Logger.debug(`[Framer] Processing NACK with ${nack.sequences.length} sequences`);
126
- }
127
128
  for (let i = 0, len = nack.sequences.length; i < len; i++) {
128
129
  const seq = nack.sequences[i];
129
130
  const lostFrames = this.outputBackup.get(seq) || [];
130
- if (this.client.options.debug) {
131
- utils_1.Logger.debug(`[Framer] Resending ${lostFrames.length} lost frames for sequence ${seq}`);
132
- }
133
131
  for (let j = 0, lostLen = lostFrames.length; j < lostLen; j++) {
134
132
  this.sendFrame(lostFrames[j], proto_1.Priority.Immediate);
135
133
  }
@@ -157,7 +155,6 @@ class Framer {
157
155
  const packet = new proto_1.OpenConnectionReplyTwo(payload).deserialize();
158
156
  this.client.emit("open-connection-reply-two", packet);
159
157
  this.client.options.mtuSize = packet.mtu;
160
- // this.mtuDiff = this.client.options.mtuSize - 36;
161
158
  const conReq = new proto_1.ConnectionRequest();
162
159
  conReq.clientGuid = this.client.options.clientId;
163
160
  conReq.timestamp = BigInt(Date.now());
@@ -269,12 +266,17 @@ class Framer {
269
266
  }
270
267
  }
271
268
  this.lastInputSequence = frameSet.sequence;
272
- for (let i = 0, len = frameSet.frames.length; i < len; i++) {
273
- try {
274
- this.handleFrame(frameSet.frames[i]);
275
- }
276
- catch (err) {
277
- utils_1.Logger.error("[Framer] Error handling frame", err);
269
+ // Process frames in batches
270
+ const frames = frameSet.frames;
271
+ for (let i = 0; i < frames.length; i += this.BATCH_SIZE) {
272
+ const batch = frames.slice(i, i + this.BATCH_SIZE);
273
+ for (const frame of batch) {
274
+ try {
275
+ this.handleFrame(frame);
276
+ }
277
+ catch (err) {
278
+ utils_1.Logger.error("[Framer] Error handling frame", err);
279
+ }
278
280
  }
279
281
  }
280
282
  }
@@ -283,9 +285,6 @@ class Framer {
283
285
  }
284
286
  }
285
287
  handleFrame(frame) {
286
- if (this.client.options.debug) {
287
- utils_1.Logger.debug(`[Framer] Handling frame - Split: ${frame.isSplit}, Sequenced: ${frame.isSequenced}, Ordered: ${frame.isOrdered}`);
288
- }
289
288
  if (frame.isSplit) {
290
289
  this.handleSplit(frame);
291
290
  }
@@ -306,19 +305,11 @@ class Framer {
306
305
  this.processOrderedFrames(frame);
307
306
  }
308
307
  else if (frame.orderedFrameIndex > expectedOrderIndex) {
309
- if (this.client.options.debug) {
310
- utils_1.Logger.debug(`Queuing out-of-order frame: ${frame.orderedFrameIndex}`);
311
- }
312
308
  this.inputOrderingQueue[channel].set(frame.orderedFrameIndex, {
313
309
  frame,
314
310
  timestamp: Date.now(),
315
311
  });
316
312
  }
317
- else {
318
- if (this.client.options.debug) {
319
- utils_1.Logger.debug(`Discarding old frame: ${frame.orderedFrameIndex}`);
320
- }
321
- }
322
313
  }
323
314
  processOrderedFrames(frame) {
324
315
  const channel = frame.orderChannel;
@@ -328,8 +319,11 @@ class Framer {
328
319
  const queue = this.inputOrderingQueue[channel];
329
320
  let nextOrderIndex = this.inputOrderIndex[channel];
330
321
  while (queue.has(nextOrderIndex)) {
331
- // biome-ignore lint/style/noNonNullAssertion: <explanation>
332
322
  const queued = queue.get(nextOrderIndex);
323
+ if (!queued) {
324
+ nextOrderIndex++;
325
+ continue;
326
+ }
333
327
  this.processFrame(queued.frame);
334
328
  queue.delete(nextOrderIndex);
335
329
  nextOrderIndex++;
@@ -339,14 +333,8 @@ class Framer {
339
333
  handleSequenced(frame) {
340
334
  const channel = frame.orderChannel;
341
335
  const currentHighestSequence = this.inputHighestSequenceIndex[channel];
342
- if (this.client.options.debug) {
343
- utils_1.Logger.debug(`Handling sequenced frame: sequenceFrameIndex=${frame.sequenceFrameIndex}, currentHighest=${currentHighestSequence}`);
344
- }
345
336
  if (frame.sequenceFrameIndex < currentHighestSequence ||
346
337
  frame.orderedFrameIndex === this.inputOrderIndex[channel]) {
347
- if (this.client.options.debug) {
348
- utils_1.Logger.debug(`Discarding old sequenced frame: ${frame.sequenceFrameIndex}`);
349
- }
350
338
  return;
351
339
  }
352
340
  this.inputHighestSequenceIndex[channel] = frame.sequenceFrameIndex + 1;
@@ -362,19 +350,18 @@ class Framer {
362
350
  entry.fragments.set(frame.splitFrameIndex, frame);
363
351
  if (entry.fragments.size === frame.splitCount) {
364
352
  this.reassembleAndProcessFragment(frame, entry.fragments);
353
+ this.fragmentsQueue.delete(splitId);
365
354
  }
366
355
  }
367
356
  reassembleAndProcessFragment(frame, fragment) {
368
357
  const stream = new binarystream_1.BinaryStream();
369
358
  for (let index = 0; index < frame.splitCount; index++) {
370
359
  const sframe = fragment.get(index);
371
- if (sframe) {
372
- stream.writeBuffer(sframe.payload);
373
- }
374
- else {
360
+ if (!sframe) {
375
361
  utils_1.Logger.error(`Missing fragment at index ${index} for splitId=${frame.splitId}`);
376
362
  return;
377
363
  }
364
+ stream.writeBuffer(sframe.payload);
378
365
  }
379
366
  const reassembledFrame = new proto_1.Frame();
380
367
  reassembledFrame.reliability = frame.reliability;
@@ -383,7 +370,6 @@ class Framer {
383
370
  reassembledFrame.orderedFrameIndex = frame.orderedFrameIndex;
384
371
  reassembledFrame.orderChannel = frame.orderChannel;
385
372
  reassembledFrame.payload = stream.getBuffer();
386
- this.fragmentsQueue.delete(frame.splitId);
387
373
  this.handleFrame(reassembledFrame);
388
374
  }
389
375
  frameAndSend(payload, priority = proto_1.Priority.Normal) {
@@ -436,32 +422,39 @@ class Framer {
436
422
  queueFrame(frame, priority) {
437
423
  const frameLength = frame.getByteLength();
438
424
  const totalLength = 4 + this.outputFramesByteLength + frameLength;
439
- if (totalLength > this.mtuDiff) {
425
+ if (totalLength > this.mtuDiff ||
426
+ this.outputFrames.length >= this.BATCH_SIZE) {
440
427
  this.sendQueue(this.outputFrames.length);
428
+ this.lastBatchTime = Date.now();
441
429
  }
442
430
  this.outputFrames.push(frame);
443
431
  this.outputFramesByteLength += frameLength;
444
432
  if (priority === proto_1.Priority.Immediate) {
445
433
  this.sendQueue(1);
434
+ this.lastBatchTime = Date.now();
446
435
  }
447
436
  }
448
437
  sendQueue(amount) {
449
438
  if (this.outputFrames.length === 0)
450
439
  return;
451
- if (this.client.options.debug) {
452
- utils_1.Logger.debug(`[Framer] Sending queue with ${amount} frames, total queued: ${this.outputFrames.length}`);
453
- }
454
440
  const frameset = new proto_2.Frameset();
455
441
  frameset.sequence = this.outputSequence++;
456
- const framesToSend = this.outputFrames.splice(0, amount);
457
- frameset.frames = framesToSend;
458
- const sentLength = framesToSend.reduce((sum, f) => sum + f.getByteLength(), 0);
459
- this.outputFramesByteLength -= sentLength;
460
- this.outputBackup.set(frameset.sequence, framesToSend);
461
- if (this.client.options.debug) {
462
- utils_1.Logger.debug(`[Framer] Frameset sequence: ${frameset.sequence}, remaining in queue: ${this.outputFrames.length}`);
442
+ // Send in batches if amount is larger than batch size
443
+ const remainingFrames = [...this.outputFrames];
444
+ this.outputFrames = [];
445
+ while (remainingFrames.length > 0) {
446
+ const batchSize = Math.min(this.BATCH_SIZE, remainingFrames.length);
447
+ const framesToSend = remainingFrames.splice(0, batchSize);
448
+ frameset.frames = framesToSend;
449
+ const sentLength = framesToSend.reduce((sum, f) => sum + f.getByteLength(), 0);
450
+ this.outputFramesByteLength -= sentLength;
451
+ this.outputBackup.set(frameset.sequence, framesToSend);
452
+ this.client.send(frameset.serialize());
453
+ // Increment sequence for next batch if there are more frames
454
+ if (remainingFrames.length > 0) {
455
+ frameset.sequence = this.outputSequence++;
456
+ }
463
457
  }
464
- this.client.send(frameset.serialize());
465
458
  }
466
459
  }
467
460
  exports.Framer = Framer;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sanctumterra/raknet",
3
- "version": "1.3.75",
3
+ "version": "1.3.76",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "module": "commonjs",