mirror of
https://github.com/amigan/aim-oscar-server.git
synced 2024-11-21 12:09:48 -05:00
add all services and set version numbers AIM 3.x expects
This commit is contained in:
parent
b699ef0ba0
commit
0e42714b4c
22 changed files with 593 additions and 49 deletions
|
@ -4,7 +4,8 @@
|
|||
"main": "src/index.js",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"dev": "nodemon --ignore ./dist/ --watch ./src -e ts --exec 'tsc && node ./dist/index.js'",
|
||||
"dev:tsc": "tsc --watch",
|
||||
"dev:nodemon": "nodemon --watch ./dist ./dist/index.js",
|
||||
"start": "tsc && node ./dist/index.js"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
@ -12,5 +13,8 @@
|
|||
"@types/node": "^16.7.13",
|
||||
"nodemon": "^2.0.12",
|
||||
"typescript": "^4.4.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"table-layout": "^3.0.0"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,13 +3,29 @@ import { FLAP, SNAC, TLV, TLVType } from './structures';
|
|||
import { logDataStream } from './util';
|
||||
import { FLAGS_EMPTY } from './consts';
|
||||
|
||||
import AuthorizationRegistrationService from "./services/authorization-registration";
|
||||
import GenericServiceControls from "./services/0x01-GenericServiceControls";
|
||||
import LocationServices from "./services/0x02-LocationSerices";
|
||||
import BuddyListManagement from "./services/0x03-BuddyListManagement";
|
||||
import ICBM from "./services/0x04-ICBM";
|
||||
import Invitation from "./services/0x06-Invitation";
|
||||
import Administration from "./services/0x07-Administration";
|
||||
import Popups from "./services/0x08-Popups";
|
||||
import PrivacyManagement from "./services/0x09-PrivacyManagement";
|
||||
import UserLookup from "./services/0x0a-UserLookup";
|
||||
import UsageStats from "./services/0x0b-UsageStats";
|
||||
import ChatNavigation from "./services/0x0d-ChatNavigation";
|
||||
import Chat from "./services/0x0e-Chat";;
|
||||
import DirectorySearch from "./services/0x0f-DirectorySearch";
|
||||
import ServerStoredBuddyIcons from "./services/0x10-ServerStoredBuddyIcons";
|
||||
import SSI from "./services/0x13-SSI";
|
||||
import AuthorizationRegistrationService from "./services/0x17-AuthorizationRegistration";
|
||||
|
||||
import BaseService from "./services/base";
|
||||
|
||||
export default class Communicator {
|
||||
|
||||
private _sequenceNumber = 0;
|
||||
private services : {[key: number]: BaseService} = {};
|
||||
public services : {[key: number]: BaseService} = {};
|
||||
|
||||
constructor(public socket : net.Socket) {
|
||||
// Hold on to the socket
|
||||
|
@ -17,9 +33,9 @@ export default class Communicator {
|
|||
|
||||
this.socket.on('data', (data : Buffer) => {
|
||||
console.log('DATA-----------------------');
|
||||
console.log('RAW\n' + logDataStream(data));
|
||||
const flap = FLAP.fromBuffer(data);
|
||||
console.log('RECV', flap.toString());
|
||||
console.log('RAW\n' + logDataStream(data));
|
||||
this.handleMessage(flap);
|
||||
});
|
||||
|
||||
|
@ -29,7 +45,6 @@ export default class Communicator {
|
|||
|
||||
start() {
|
||||
// Start negotiating a connection
|
||||
console.log(FLAP, typeof FLAP);
|
||||
const hello = new FLAP(0x01, 0, Buffer.from([0x00, 0x00, 0x00, 0x01]));
|
||||
this.send(hello);
|
||||
}
|
||||
|
@ -37,6 +52,21 @@ export default class Communicator {
|
|||
registerServices() {
|
||||
const services = [
|
||||
new AuthorizationRegistrationService(this),
|
||||
new GenericServiceControls(this),
|
||||
new LocationServices(this),
|
||||
new BuddyListManagement(this),
|
||||
new ICBM(this),
|
||||
new Invitation(this),
|
||||
new Administration(this),
|
||||
new Popups(this),
|
||||
new PrivacyManagement(this),
|
||||
new UserLookup(this),
|
||||
new UsageStats(this),
|
||||
new ChatNavigation(this),
|
||||
new Chat(this),
|
||||
new DirectorySearch(this),
|
||||
new ServerStoredBuddyIcons(this),
|
||||
new SSI(this),
|
||||
];
|
||||
|
||||
this.services = {};
|
||||
|
@ -86,9 +116,7 @@ export default class Communicator {
|
|||
servicesOffered.push(Buffer.from([0x00, service.family]));
|
||||
});
|
||||
const resp = new FLAP(2, this._getNewSequenceNumber(),
|
||||
new SNAC(0x01, 0x03, FLAGS_EMPTY, 0, [
|
||||
Buffer.concat(servicesOffered),
|
||||
]));
|
||||
new SNAC(0x01, 0x03, FLAGS_EMPTY, 0, Buffer.concat(servicesOffered)));
|
||||
this.send(resp);
|
||||
return;
|
||||
}
|
||||
|
@ -107,6 +135,7 @@ export default class Communicator {
|
|||
}
|
||||
|
||||
familyService.handleMessage(message);
|
||||
return;
|
||||
default:
|
||||
console.warn('No handlers for channel', message.channel);
|
||||
return;
|
||||
|
|
40
src/services/0x01-GenericServiceControls.ts
Normal file
40
src/services/0x01-GenericServiceControls.ts
Normal file
|
@ -0,0 +1,40 @@
|
|||
import BaseService from './base';
|
||||
import Communicator from '../communicator';
|
||||
import { FLAP, Rate, RateClass, RatedServiceGroup, RateGroupPair, SNAC } from '../structures';
|
||||
import { FLAGS_EMPTY } from '../consts';
|
||||
|
||||
export default class GenericServiceControls extends BaseService {
|
||||
constructor(communicator : Communicator) {
|
||||
super({family: 0x01, version: 0x03}, communicator)
|
||||
}
|
||||
|
||||
override handleMessage(message : FLAP) {
|
||||
if (!(message.payload instanceof SNAC)) {
|
||||
throw new Error('Require SNAC');
|
||||
}
|
||||
|
||||
if (message.payload.service === 0x06) { // Client ask server for rate limits info
|
||||
const resp = new FLAP(0x02, this._getNewSequenceNumber(),
|
||||
SNAC.forRateClass(0x01, 0x07, FLAGS_EMPTY, 0, [
|
||||
new Rate(
|
||||
new RateClass(1, 80, 2500, 2000, 1500, 800, 3400 /*fake*/, 6000, 0, 0),
|
||||
new RatedServiceGroup(1, [new RateGroupPair(0x00, 0x00)])
|
||||
)
|
||||
]))
|
||||
this.send(resp);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (message.payload.service === 0x17) {
|
||||
const serviceVersions : Buffer[] = [];
|
||||
Object.values(this.communicator.services).forEach((service) => {
|
||||
serviceVersions.push(Buffer.from([0x00, service.family, 0x00, service.version]));
|
||||
});
|
||||
const resp = new FLAP(0x02, this._getNewSequenceNumber(),
|
||||
new SNAC(0x01, 0x18, FLAGS_EMPTY, 0, Buffer.concat(serviceVersions)));
|
||||
this.send(resp);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
8
src/services/0x02-LocationSerices.ts
Normal file
8
src/services/0x02-LocationSerices.ts
Normal file
|
@ -0,0 +1,8 @@
|
|||
import BaseService from './base';
|
||||
import Communicator from '../communicator';
|
||||
|
||||
export default class LocationServices extends BaseService {
|
||||
constructor(communicator : Communicator) {
|
||||
super({family: 0x02, version: 0x01}, communicator)
|
||||
}
|
||||
}
|
8
src/services/0x03-BuddyListManagement.ts
Normal file
8
src/services/0x03-BuddyListManagement.ts
Normal file
|
@ -0,0 +1,8 @@
|
|||
import BaseService from './base';
|
||||
import Communicator from '../communicator';
|
||||
|
||||
export default class BuddyListManagement extends BaseService {
|
||||
constructor(communicator : Communicator) {
|
||||
super({family: 0x03, version: 0x01}, communicator)
|
||||
}
|
||||
}
|
8
src/services/0x04-ICBM.ts
Normal file
8
src/services/0x04-ICBM.ts
Normal file
|
@ -0,0 +1,8 @@
|
|||
import BaseService from './base';
|
||||
import Communicator from '../communicator';
|
||||
|
||||
export default class ICBM extends BaseService {
|
||||
constructor(communicator : Communicator) {
|
||||
super({family: 0x04, version: 0x01}, communicator)
|
||||
}
|
||||
}
|
8
src/services/0x06-Invitation.ts
Normal file
8
src/services/0x06-Invitation.ts
Normal file
|
@ -0,0 +1,8 @@
|
|||
import BaseService from './base';
|
||||
import Communicator from '../communicator';
|
||||
|
||||
export default class Invitation extends BaseService {
|
||||
constructor(communicator : Communicator) {
|
||||
super({family: 0x06, version: 0x01}, communicator)
|
||||
}
|
||||
}
|
8
src/services/0x07-Administration.ts
Normal file
8
src/services/0x07-Administration.ts
Normal file
|
@ -0,0 +1,8 @@
|
|||
import BaseService from './base';
|
||||
import Communicator from '../communicator';
|
||||
|
||||
export default class Administration extends BaseService {
|
||||
constructor(communicator : Communicator) {
|
||||
super({family: 0x07, version: 0x01}, communicator)
|
||||
}
|
||||
}
|
8
src/services/0x08-Popups.ts
Normal file
8
src/services/0x08-Popups.ts
Normal file
|
@ -0,0 +1,8 @@
|
|||
import BaseService from './base';
|
||||
import Communicator from '../communicator';
|
||||
|
||||
export default class Popups extends BaseService {
|
||||
constructor(communicator : Communicator) {
|
||||
super({family: 0x08, version: 0x01}, communicator)
|
||||
}
|
||||
}
|
8
src/services/0x09-PrivacyManagement.ts
Normal file
8
src/services/0x09-PrivacyManagement.ts
Normal file
|
@ -0,0 +1,8 @@
|
|||
import BaseService from './base';
|
||||
import Communicator from '../communicator';
|
||||
|
||||
export default class PrivacyManagement extends BaseService {
|
||||
constructor(communicator : Communicator) {
|
||||
super({family: 0x09, version: 0x01}, communicator)
|
||||
}
|
||||
}
|
8
src/services/0x0a-UserLookup.ts
Normal file
8
src/services/0x0a-UserLookup.ts
Normal file
|
@ -0,0 +1,8 @@
|
|||
import BaseService from './base';
|
||||
import Communicator from '../communicator';
|
||||
|
||||
export default class UserLookup extends BaseService {
|
||||
constructor(communicator : Communicator) {
|
||||
super({family: 0x0a, version: 0x01}, communicator)
|
||||
}
|
||||
}
|
8
src/services/0x0b-UsageStats.ts
Normal file
8
src/services/0x0b-UsageStats.ts
Normal file
|
@ -0,0 +1,8 @@
|
|||
import BaseService from './base';
|
||||
import Communicator from '../communicator';
|
||||
|
||||
export default class UsageStats extends BaseService {
|
||||
constructor(communicator : Communicator) {
|
||||
super({family: 0x0b, version: 0x01}, communicator)
|
||||
}
|
||||
}
|
8
src/services/0x0d-ChatNavigation.ts
Normal file
8
src/services/0x0d-ChatNavigation.ts
Normal file
|
@ -0,0 +1,8 @@
|
|||
import BaseService from './base';
|
||||
import Communicator from '../communicator';
|
||||
|
||||
export default class ChatNavigation extends BaseService {
|
||||
constructor(communicator : Communicator) {
|
||||
super({family: 0x0d, version: 0x02}, communicator)
|
||||
}
|
||||
}
|
8
src/services/0x0e-Chat.ts
Normal file
8
src/services/0x0e-Chat.ts
Normal file
|
@ -0,0 +1,8 @@
|
|||
import BaseService from './base';
|
||||
import Communicator from '../communicator';
|
||||
|
||||
export default class Chat extends BaseService {
|
||||
constructor(communicator : Communicator) {
|
||||
super({family: 0x0e, version: 0x01}, communicator)
|
||||
}
|
||||
}
|
8
src/services/0x0f-DirectorySearch.ts
Normal file
8
src/services/0x0f-DirectorySearch.ts
Normal file
|
@ -0,0 +1,8 @@
|
|||
import BaseService from './base';
|
||||
import Communicator from '../communicator';
|
||||
|
||||
export default class DirectorySearch extends BaseService {
|
||||
constructor(communicator : Communicator) {
|
||||
super({family: 0x0f, version: 0x01}, communicator)
|
||||
}
|
||||
}
|
8
src/services/0x10-ServerStoredBuddyIcons.ts
Normal file
8
src/services/0x10-ServerStoredBuddyIcons.ts
Normal file
|
@ -0,0 +1,8 @@
|
|||
import BaseService from './base';
|
||||
import Communicator from '../communicator';
|
||||
|
||||
export default class ServerStoredBuddyIcons extends BaseService {
|
||||
constructor(communicator : Communicator) {
|
||||
super({family: 0x10, version: 0x01}, communicator)
|
||||
}
|
||||
}
|
9
src/services/0x13-SSI.ts
Normal file
9
src/services/0x13-SSI.ts
Normal file
|
@ -0,0 +1,9 @@
|
|||
import BaseService from './base';
|
||||
import Communicator from '../communicator';
|
||||
|
||||
// SSI is Server Stored Information
|
||||
export default class SSI extends BaseService {
|
||||
constructor(communicator : Communicator) {
|
||||
super({family: 0x10, version: 0x01}, communicator)
|
||||
}
|
||||
}
|
|
@ -25,14 +25,14 @@ export default class AuthorizationRegistrationService extends BaseService {
|
|||
|
||||
switch (message.payload.service) {
|
||||
case 0x02: // Client login request (md5 login sequence)
|
||||
const tlvs = message.payload.tlvs;
|
||||
const clientNameTLV = tlvs.find((tlv) => tlv instanceof TLV && tlv.type === TLVType.ClientName);
|
||||
const payload = message.payload.payload;
|
||||
const clientNameTLV = payload.find((tlv) => tlv instanceof TLV && tlv.type === TLVType.ClientName);
|
||||
if (!clientNameTLV || !(clientNameTLV instanceof TLV)) {
|
||||
return;
|
||||
}
|
||||
console.log("Attempting connection from", clientNameTLV.payload.toString('ascii'));
|
||||
|
||||
const userTLV = tlvs.find((tlv) => tlv instanceof TLV && tlv.type === TLVType.User);
|
||||
const userTLV = payload.find((tlv) => tlv instanceof TLV && tlv.type === TLVType.User);
|
||||
if (!userTLV || !(userTLV instanceof TLV)) {
|
||||
return;
|
||||
}
|
||||
|
@ -50,7 +50,7 @@ export default class AuthorizationRegistrationService extends BaseService {
|
|||
return;
|
||||
}
|
||||
|
||||
const passwordHashTLV = tlvs.find((tlv) => tlv instanceof TLV && tlv.type === TLVType.PasswordHash);
|
||||
const passwordHashTLV = payload.find((tlv) => tlv instanceof TLV && tlv.type === TLVType.PasswordHash);
|
||||
if (!passwordHashTLV || !(passwordHashTLV instanceof TLV)) {
|
||||
return;
|
||||
}
|
||||
|
@ -76,18 +76,18 @@ export default class AuthorizationRegistrationService extends BaseService {
|
|||
new SNAC(0x17, 0x03, FLAGS_EMPTY, 0, [
|
||||
TLV.forUsername(username), // username
|
||||
TLV.forBOSAddress('10.0.1.29:5190'), // BOS address
|
||||
TLV.forCookie('im a cookie uwu') // Authorization cookie
|
||||
TLV.forCookie(JSON.stringify({cookie: 'uwu', user: 'toof'})) // Authorization cookie
|
||||
]));
|
||||
|
||||
this.send(authResp);
|
||||
return;
|
||||
case 0x06: // Request md5 authkey
|
||||
const payload = Buffer.alloc(2, 0xFF, 'hex');
|
||||
payload.writeUInt16BE(this.cipher.length);
|
||||
const MD5AuthKeyHeader = Buffer.alloc(2, 0xFF, 'hex');
|
||||
MD5AuthKeyHeader.writeUInt16BE(this.cipher.length);
|
||||
const md5ReqResp = new FLAP(2, this._getNewSequenceNumber(),
|
||||
new SNAC(0x17, 0x07, FLAGS_EMPTY, 0, [
|
||||
Buffer.concat([payload, Buffer.from(this.cipher, 'binary')]),
|
||||
]));
|
||||
new SNAC(0x17, 0x07, FLAGS_EMPTY, 0,
|
||||
Buffer.concat([MD5AuthKeyHeader, Buffer.from(this.cipher, 'binary')]),
|
||||
));
|
||||
this.send(md5ReqResp);
|
||||
break;
|
||||
}
|
|
@ -25,6 +25,7 @@ export default class BaseService {
|
|||
}
|
||||
|
||||
handleMessage(message : FLAP) : void {
|
||||
return;
|
||||
throw new Error(''+
|
||||
`Unhandled message for family ${this.family.toString(16)} supporting version ${this.version.toString(16)}`);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,41 +1,120 @@
|
|||
import assert from "assert";
|
||||
import { TLV } from "./TLV";
|
||||
|
||||
export class RateClass {
|
||||
constructor(
|
||||
public ID: number,
|
||||
public WindowSize: number,
|
||||
public ClearLevel: number,
|
||||
public AlertLevel: number,
|
||||
public LimitLevel: number,
|
||||
public DisconnectLevel: number,
|
||||
public CurrentLevel: number,
|
||||
public MaxLevel: number,
|
||||
public LastTime: number,
|
||||
public CurrentStat: number,
|
||||
){}
|
||||
|
||||
toBuffer() : Buffer {
|
||||
const buf = Buffer.alloc(35, 0x00);
|
||||
buf.writeUInt16BE(this.ID, 0);
|
||||
buf.writeUInt32BE(this.WindowSize, 2);
|
||||
buf.writeUInt32BE(this.ClearLevel, 6);
|
||||
buf.writeUInt32BE(this.AlertLevel, 10);
|
||||
buf.writeUInt32BE(this.LimitLevel, 14);
|
||||
buf.writeUInt32BE(this.DisconnectLevel,18);
|
||||
buf.writeUInt32BE(this.CurrentLevel, 22);
|
||||
buf.writeUInt32BE(this.MaxLevel, 26);
|
||||
buf.writeUInt32BE(this.LastTime, 30);
|
||||
buf.writeUInt8(this.CurrentStat, 34);
|
||||
return buf;
|
||||
}
|
||||
}
|
||||
|
||||
export class RateGroupPair {
|
||||
constructor(public family : number, public service : number) {}
|
||||
toBuffer() : Buffer {
|
||||
const buf = Buffer.alloc(4, 0x00);
|
||||
buf.writeInt16BE(this.family, 0);
|
||||
buf.writeInt16BE(this.service, 2);
|
||||
return buf;
|
||||
}
|
||||
}
|
||||
|
||||
export class RatedServiceGroup {
|
||||
constructor(public rateGroupID : number, public pairs : RateGroupPair[]){}
|
||||
|
||||
toBuffer() : Buffer {
|
||||
const ratedServiceGroupHeader = Buffer.alloc(4, 0x00);
|
||||
ratedServiceGroupHeader.writeInt16BE(this.rateGroupID);
|
||||
ratedServiceGroupHeader.writeInt16BE(this.pairs.length, 2);
|
||||
const pairs = this.pairs.map((pair) => pair.toBuffer());
|
||||
return Buffer.concat([ratedServiceGroupHeader, ...pairs]);
|
||||
}
|
||||
}
|
||||
|
||||
export class Rate {
|
||||
constructor(public rateClass : RateClass, public ratedServiceGroup : RatedServiceGroup) {}
|
||||
toBuffer() : Buffer {
|
||||
return Buffer.concat([this.rateClass.toBuffer(), this.ratedServiceGroup.toBuffer()]);
|
||||
}
|
||||
}
|
||||
|
||||
export class SNAC {
|
||||
constructor(public family : number, public service : number, public flags : Buffer, public requestID : number , public payload : (TLV[] | Buffer) = Buffer.alloc(0)) {
|
||||
this.family = family;
|
||||
this.service = service;
|
||||
this.flags = flags;
|
||||
this.requestID = requestID;
|
||||
this.payload = payload;
|
||||
}
|
||||
|
||||
static fromBuffer(buf : Buffer, payloadLength = 0) {
|
||||
assert(buf.length >= 10, 'Expected 10 bytes for SNAC header');
|
||||
const family = buf.slice(0,2).readInt16BE(0);
|
||||
const service = buf.slice(2,4).readInt16BE(0);
|
||||
const flags = buf.slice(4, 6);
|
||||
const requestID = buf.slice(6, 10).readInt32BE(0);
|
||||
const tlvs : TLV[] = []; // SNACs can have multiple TLVs
|
||||
let payload : Buffer | TLV[]; // SNACs can have multiple payload
|
||||
|
||||
let tlvsIdx = 10;
|
||||
let cb = 0, cbLimit = 20; //circuit breaker
|
||||
while (tlvsIdx < payloadLength && cb < cbLimit) {
|
||||
const tlv = TLV.fromBuffer(buf.slice(tlvsIdx));
|
||||
tlvs.push(tlv);
|
||||
tlvsIdx += tlv.length + 4; // 4 bytes for TLV type + tlvs length
|
||||
cb++;
|
||||
// Some SNACs don't have TLV payloads
|
||||
if (family === 0x01 && service === 0x17 ||
|
||||
family === 0x01 && service === 0x07 ||
|
||||
family === 0x01 && service === 0x08 ||
|
||||
family === 0x01 && service === 0x0e) {
|
||||
payload = buf.slice(10, 10 + payloadLength);
|
||||
} else {
|
||||
payload = [];
|
||||
// Try to parse TLVs
|
||||
let payloadIdx = 10;
|
||||
let cb = 0, cbLimit = 20; //circuit breaker
|
||||
while (payloadIdx < payloadLength && cb < cbLimit) {
|
||||
const tlv = TLV.fromBuffer(buf.slice(payloadIdx));
|
||||
payload.push(tlv);
|
||||
payloadIdx += tlv.length + 4; // 4 bytes for TLV type + payload length
|
||||
cb++;
|
||||
}
|
||||
if (cb === cbLimit) {
|
||||
console.error('Application error, cb limit reached');
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
if (cb === cbLimit) {
|
||||
console.error('Application error, cb limit reached');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
return new SNAC(family, service, flags, requestID, tlvs);
|
||||
|
||||
return new SNAC(family, service, flags, requestID, payload);
|
||||
}
|
||||
|
||||
constructor(public family : number, public service : number, public flags : Buffer, public requestID : number , public tlvs : Array<TLV | Buffer> = []) {
|
||||
this.family = family;
|
||||
this.service = service;
|
||||
this.flags = flags;
|
||||
this.requestID = requestID;
|
||||
this.tlvs = tlvs;
|
||||
static forRateClass(family : number, service : number, flags : Buffer, requestID : number, rates : Rate[]) : SNAC {
|
||||
const payloadHeader = Buffer.alloc(2, 0x00);
|
||||
payloadHeader.writeUInt16BE(rates.length);
|
||||
|
||||
const payloadBody = rates.map((rateClass) => rateClass.toBuffer());
|
||||
const payload = Buffer.concat([payloadHeader, ...payloadBody]);
|
||||
|
||||
return new SNAC(family, service, flags, requestID, payload);
|
||||
}
|
||||
|
||||
toString() {
|
||||
return `SNAC(${this.family.toString(16)},${this.service.toString(16)}) #${this.requestID}\n ${this.tlvs}`;
|
||||
return `SNAC(${this.family.toString(16)},${this.service.toString(16)}) #${this.requestID}\n ${this.payload}`;
|
||||
}
|
||||
|
||||
toBuffer() {
|
||||
|
@ -45,12 +124,12 @@ export class SNAC {
|
|||
SNACHeader.set(this.flags, 4);
|
||||
SNACHeader.writeUInt32BE(this.requestID, 6);
|
||||
|
||||
const payload = this.tlvs.map((thing) => {
|
||||
if (thing instanceof TLV) {
|
||||
return thing.toBuffer();
|
||||
}
|
||||
return thing;
|
||||
});
|
||||
let payload : Buffer[] = [];
|
||||
if (this.payload instanceof Buffer) {
|
||||
payload = [this.payload];
|
||||
} else if (this.payload.length && this.payload[0] instanceof TLV) {
|
||||
payload = (this.payload as TLV[]).map((thing : TLV) => thing.toBuffer());
|
||||
}
|
||||
|
||||
return Buffer.concat([SNACHeader, ...payload]);
|
||||
}
|
||||
|
|
126
src/util.ts
126
src/util.ts
|
@ -1,4 +1,5 @@
|
|||
function chunkString(str : string, len : number) {
|
||||
import { FLAP } from './structures'
|
||||
export function chunkString(str : string, len : number) {
|
||||
const size = Math.ceil(str.length/len)
|
||||
const r = Array(size)
|
||||
let offset = 0
|
||||
|
@ -15,3 +16,126 @@ export function logDataStream(data : Buffer){
|
|||
const strs = chunkString(data.toString('hex'), 16);
|
||||
return strs.map((str) => chunkString(str, 2).join(' ')).join('\n');
|
||||
}
|
||||
|
||||
// Experiment to provide descriptive print-outs of the data structures
|
||||
// @ts-ignore
|
||||
import Table from 'table-layout';
|
||||
|
||||
type DataSize = -1 | 8 | 16 | 32;
|
||||
interface Spec {
|
||||
description: string,
|
||||
size : DataSize,
|
||||
isRepeat? : boolean,
|
||||
repeatSpecs?: Spec[],
|
||||
}
|
||||
|
||||
function byte(description : string) : Spec {
|
||||
return {size: 8, description}
|
||||
}
|
||||
|
||||
function word(description : string) : Spec {
|
||||
return {size: 16, description};
|
||||
}
|
||||
|
||||
function dword(description : string) : Spec {
|
||||
return {size: 32, description};
|
||||
}
|
||||
|
||||
function repeat(size: DataSize, description : string, specs : Spec[]) : Spec {
|
||||
return {size, description, isRepeat: true, repeatSpecs: specs};
|
||||
}
|
||||
|
||||
const FLAPSpec = [
|
||||
byte("FLAP Header"),
|
||||
byte("Channel"),
|
||||
word("Sequence ID"),
|
||||
word("Payload Length"),
|
||||
word("SNAC Family"),
|
||||
word("SNAC Subtype"),
|
||||
word("SNAC Flags"),
|
||||
dword("SNAC Request-ID"),
|
||||
repeat(16, "Number of rate classes", [
|
||||
word("Rate Class ID"),
|
||||
dword("Window Size"),
|
||||
dword("Clear Level"),
|
||||
dword("Alert Level"),
|
||||
dword("Limit Level"),
|
||||
dword("Disconnect Level"),
|
||||
dword("Current Level"),
|
||||
dword("Max Level"),
|
||||
dword("Last Time"),
|
||||
byte("Current State")
|
||||
]),
|
||||
repeat(-1, "", [
|
||||
word("Rate Group ID"),
|
||||
repeat(16, "Number of pairs in group", [
|
||||
dword("Family/Subtype pair"),
|
||||
]),
|
||||
]),
|
||||
];
|
||||
|
||||
function parseBuffer(buf : Buffer, spec : Spec[], repeatTimes = 0) {
|
||||
let offset = 0;
|
||||
let rows = [];
|
||||
let repeat = repeatTimes;
|
||||
|
||||
for (let section of spec) {
|
||||
let value : number = 0;
|
||||
if (section.size === 8) {
|
||||
const bufStr = buf.slice(offset, offset + 1).toString('hex');
|
||||
value = buf.readInt8(offset);
|
||||
rows.push([chunkString(bufStr, 2).join(' '), value, section.description]);
|
||||
offset += 1;
|
||||
} else if (section.size === 16) {
|
||||
const bufStr = buf.slice(offset, offset + 2).toString('hex');
|
||||
value = buf.readUInt16BE(offset);
|
||||
rows.push([chunkString(bufStr, 2).join(' '), value, section.description]);
|
||||
offset += 2;
|
||||
} else if (section.size === 32) {
|
||||
const bufStr = buf.slice(offset, offset + 4).toString('hex');
|
||||
value = buf.readUInt32BE(offset);
|
||||
rows.push([chunkString(bufStr, 2).join(' '), value, section.description]);
|
||||
offset += 4;
|
||||
}
|
||||
|
||||
if (section.isRepeat && section.repeatSpecs) {
|
||||
if (section.size !== -1) {
|
||||
repeat = value;
|
||||
}
|
||||
|
||||
let specs : Spec[] = [];
|
||||
for (let i = 0; i < repeat; i++) {
|
||||
specs.push(...section.repeatSpecs);
|
||||
}
|
||||
|
||||
const subrows : any[] = parseBuffer(buf.slice(offset), specs, repeat);
|
||||
rows.push(...subrows);
|
||||
}
|
||||
}
|
||||
|
||||
return rows;
|
||||
}
|
||||
|
||||
function printBuffer(buf : Buffer, spec : Spec[]) {
|
||||
const rows = parseBuffer(buf, spec);
|
||||
console.log((new Table(rows)).toString());
|
||||
}
|
||||
|
||||
function bufferFromWebText(webtext : string) : Buffer {
|
||||
return Buffer.from(webtext.replace(/\s/g, ''), 'hex');
|
||||
}
|
||||
|
||||
const exampleWebText = ''+
|
||||
`
|
||||
2a 02 00 03 00 37 00 01
|
||||
00 07 00 00 00 00 00 00
|
||||
00 01 00 01 00 00 00 50
|
||||
00 00 09 c4 00 00 07 d0
|
||||
00 00 05 dc 00 00 03 20
|
||||
00 00 0d 48 00 00 17 70
|
||||
00 00 00 00 00 00 01 00
|
||||
01 00 00 00 00 `
|
||||
|
||||
if (require.main === module) {
|
||||
printBuffer(bufferFromWebText(exampleWebText), FLAPSpec);
|
||||
}
|
||||
|
|
158
yarn.lock
158
yarn.lock
|
@ -2,6 +2,14 @@
|
|||
# yarn lockfile v1
|
||||
|
||||
|
||||
"@75lb/deep-merge@^1.1.0":
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/@75lb/deep-merge/-/deep-merge-1.1.1.tgz#3b06155b90d34f5f8cc2107d796f1853ba02fd6d"
|
||||
integrity sha512-xvgv6pkMGBA6GwdyJbNAnDmfAIR/DfWhrj9jgWh3TY7gRm3KO46x/GPjRg6wJ0nOepwqrNxFfojebh0Df4h4Tw==
|
||||
dependencies:
|
||||
lodash.assignwith "^4.2.0"
|
||||
typical "^7.1.1"
|
||||
|
||||
"@sindresorhus/is@^0.14.0":
|
||||
version "0.14.0"
|
||||
resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea"
|
||||
|
@ -46,6 +54,13 @@ ansi-regex@^5.0.0:
|
|||
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75"
|
||||
integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==
|
||||
|
||||
ansi-styles@^3.2.1:
|
||||
version "3.2.1"
|
||||
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d"
|
||||
integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==
|
||||
dependencies:
|
||||
color-convert "^1.9.0"
|
||||
|
||||
ansi-styles@^4.1.0:
|
||||
version "4.3.0"
|
||||
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937"
|
||||
|
@ -61,6 +76,21 @@ anymatch@~3.1.2:
|
|||
normalize-path "^3.0.0"
|
||||
picomatch "^2.0.4"
|
||||
|
||||
array-back@^3.0.1, array-back@^3.1.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/array-back/-/array-back-3.1.0.tgz#b8859d7a508871c9a7b2cf42f99428f65e96bfb0"
|
||||
integrity sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q==
|
||||
|
||||
array-back@^4.0.1:
|
||||
version "4.0.2"
|
||||
resolved "https://registry.yarnpkg.com/array-back/-/array-back-4.0.2.tgz#8004e999a6274586beeb27342168652fdb89fa1e"
|
||||
integrity sha512-NbdMezxqf94cnNfWLL7V/im0Ub+Anbb0IoZhvzie8+4HJ4nMQuzHuy49FkGYCJK2yAloZ3meiB6AVMClbrI1vg==
|
||||
|
||||
array-back@^6.2.0:
|
||||
version "6.2.0"
|
||||
resolved "https://registry.yarnpkg.com/array-back/-/array-back-6.2.0.tgz#83cc80fbef5a46269b1f6ecc82011cfc19cf1c1e"
|
||||
integrity sha512-mixVv03GOOn/ubHE4STQ+uevX42ETdk0JoMVEjNkSOCT7WgERh7C8/+NyhWYNpE3BN69pxFyJIBcF7CxWz/+4A==
|
||||
|
||||
balanced-match@^1.0.0:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
|
||||
|
@ -118,6 +148,15 @@ camelcase@^5.3.1:
|
|||
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320"
|
||||
integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==
|
||||
|
||||
chalk@^2.4.2:
|
||||
version "2.4.2"
|
||||
resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
|
||||
integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==
|
||||
dependencies:
|
||||
ansi-styles "^3.2.1"
|
||||
escape-string-regexp "^1.0.5"
|
||||
supports-color "^5.3.0"
|
||||
|
||||
chalk@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/chalk/-/chalk-3.0.0.tgz#3f73c2bf526591f574cc492c51e2456349f844e4"
|
||||
|
@ -158,6 +197,13 @@ clone-response@^1.0.2:
|
|||
dependencies:
|
||||
mimic-response "^1.0.0"
|
||||
|
||||
color-convert@^1.9.0:
|
||||
version "1.9.3"
|
||||
resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8"
|
||||
integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==
|
||||
dependencies:
|
||||
color-name "1.1.3"
|
||||
|
||||
color-convert@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3"
|
||||
|
@ -165,11 +211,36 @@ color-convert@^2.0.1:
|
|||
dependencies:
|
||||
color-name "~1.1.4"
|
||||
|
||||
color-name@1.1.3:
|
||||
version "1.1.3"
|
||||
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25"
|
||||
integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=
|
||||
|
||||
color-name@~1.1.4:
|
||||
version "1.1.4"
|
||||
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
|
||||
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
|
||||
|
||||
command-line-args@^5.2.0:
|
||||
version "5.2.0"
|
||||
resolved "https://registry.yarnpkg.com/command-line-args/-/command-line-args-5.2.0.tgz#087b02748272169741f1fd7c785b295df079b9be"
|
||||
integrity sha512-4zqtU1hYsSJzcJBOcNZIbW5Fbk9BkjCp1pZVhQKoRaWL5J7N4XphDLwo8aWwdQpTugxwu+jf9u2ZhkXiqp5Z6A==
|
||||
dependencies:
|
||||
array-back "^3.1.0"
|
||||
find-replace "^3.0.0"
|
||||
lodash.camelcase "^4.3.0"
|
||||
typical "^4.0.0"
|
||||
|
||||
command-line-usage@^6.1.1:
|
||||
version "6.1.1"
|
||||
resolved "https://registry.yarnpkg.com/command-line-usage/-/command-line-usage-6.1.1.tgz#c908e28686108917758a49f45efb4f02f76bc03f"
|
||||
integrity sha512-F59pEuAR9o1SF/bD0dQBDluhpT4jJQNWUHEuVBqpDmCUo6gPjCi+m9fCWnWZVR/oG6cMTUms4h+3NPl74wGXvA==
|
||||
dependencies:
|
||||
array-back "^4.0.1"
|
||||
chalk "^2.4.2"
|
||||
table-layout "^1.0.1"
|
||||
typical "^5.2.0"
|
||||
|
||||
concat-map@0.0.1:
|
||||
version "0.0.1"
|
||||
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
|
||||
|
@ -213,7 +284,7 @@ decompress-response@^3.3.0:
|
|||
dependencies:
|
||||
mimic-response "^1.0.0"
|
||||
|
||||
deep-extend@^0.6.0:
|
||||
deep-extend@^0.6.0, deep-extend@~0.6.0:
|
||||
version "0.6.0"
|
||||
resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac"
|
||||
integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==
|
||||
|
@ -257,6 +328,11 @@ escape-goat@^2.0.0:
|
|||
resolved "https://registry.yarnpkg.com/escape-goat/-/escape-goat-2.1.1.tgz#1b2dc77003676c457ec760b2dc68edb648188675"
|
||||
integrity sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q==
|
||||
|
||||
escape-string-regexp@^1.0.5:
|
||||
version "1.0.5"
|
||||
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
|
||||
integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=
|
||||
|
||||
fill-range@^7.0.1:
|
||||
version "7.0.1"
|
||||
resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40"
|
||||
|
@ -264,6 +340,13 @@ fill-range@^7.0.1:
|
|||
dependencies:
|
||||
to-regex-range "^5.0.1"
|
||||
|
||||
find-replace@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/find-replace/-/find-replace-3.0.0.tgz#3e7e23d3b05167a76f770c9fbd5258b0def68c38"
|
||||
integrity sha512-6Tb2myMioCAgv5kfvP5/PkZZ/ntTpVK39fHY7WkWBgvbeE+VHd/tZuZ4mrC+bxh4cfOZeYKVPaJIZtZXV7GNCQ==
|
||||
dependencies:
|
||||
array-back "^3.0.1"
|
||||
|
||||
fsevents@~2.3.2:
|
||||
version "2.3.2"
|
||||
resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a"
|
||||
|
@ -457,6 +540,16 @@ latest-version@^5.0.0:
|
|||
dependencies:
|
||||
package-json "^6.3.0"
|
||||
|
||||
lodash.assignwith@^4.2.0:
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/lodash.assignwith/-/lodash.assignwith-4.2.0.tgz#127a97f02adc41751a954d24b0de17e100e038eb"
|
||||
integrity sha1-EnqX8CrcQXUalU0ksN4X4QDgOOs=
|
||||
|
||||
lodash.camelcase@^4.3.0:
|
||||
version "4.3.0"
|
||||
resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6"
|
||||
integrity sha1-soqmKIorn8ZRA1x3EfZathkDMaY=
|
||||
|
||||
lowercase-keys@^1.0.0, lowercase-keys@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f"
|
||||
|
@ -603,6 +696,11 @@ readdirp@~3.6.0:
|
|||
dependencies:
|
||||
picomatch "^2.2.1"
|
||||
|
||||
reduce-flatten@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/reduce-flatten/-/reduce-flatten-2.0.0.tgz#734fd84e65f375d7ca4465c69798c25c9d10ae27"
|
||||
integrity sha512-EJ4UNY/U1t2P/2k6oqotuX2Cc3T6nxJwsM0N0asT7dhrtH1ltUxDn4NalSYmPE2rCkVpcf/X6R0wDwcFpzhd4w==
|
||||
|
||||
registry-auth-token@^4.0.0:
|
||||
version "4.2.1"
|
||||
resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-4.2.1.tgz#6d7b4006441918972ccd5fedcd41dc322c79b250"
|
||||
|
@ -646,6 +744,11 @@ signal-exit@^3.0.2:
|
|||
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c"
|
||||
integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==
|
||||
|
||||
stream-read-all@^3.0.1:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/stream-read-all/-/stream-read-all-3.0.1.tgz#60762ae45e61d93ba0978cda7f3913790052ad96"
|
||||
integrity sha512-EWZT9XOceBPlVJRrYcykW8jyRSZYbkb/0ZK36uLEmoWVO5gxBOnntNTseNzfREsqxqdfEGQrD8SXQ3QWbBmq8A==
|
||||
|
||||
string-width@^3.0.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961"
|
||||
|
@ -683,7 +786,7 @@ strip-json-comments@~2.0.1:
|
|||
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a"
|
||||
integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo=
|
||||
|
||||
supports-color@^5.5.0:
|
||||
supports-color@^5.3.0, supports-color@^5.5.0:
|
||||
version "5.5.0"
|
||||
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
|
||||
integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==
|
||||
|
@ -697,6 +800,29 @@ supports-color@^7.1.0:
|
|||
dependencies:
|
||||
has-flag "^4.0.0"
|
||||
|
||||
table-layout@^1.0.1:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/table-layout/-/table-layout-1.0.2.tgz#c4038a1853b0136d63365a734b6931cf4fad4a04"
|
||||
integrity sha512-qd/R7n5rQTRFi+Zf2sk5XVVd9UQl6ZkduPFC3S7WEGJAmetDTjY3qPN50eSKzwuzEyQKy5TN2TiZdkIjos2L6A==
|
||||
dependencies:
|
||||
array-back "^4.0.1"
|
||||
deep-extend "~0.6.0"
|
||||
typical "^5.2.0"
|
||||
wordwrapjs "^4.0.0"
|
||||
|
||||
table-layout@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/table-layout/-/table-layout-3.0.0.tgz#bd0f207ff73eb4dba79019e4f672e61874181b7f"
|
||||
integrity sha512-chMCAvqzsw2k0WVZLQwyiNfHV3lwLdrmTKaofEt3PhedSVdEwOK8/nGetZFaQEg9HjPL9CrhkJWc4ZauFCihXw==
|
||||
dependencies:
|
||||
"@75lb/deep-merge" "^1.1.0"
|
||||
array-back "^6.2.0"
|
||||
command-line-args "^5.2.0"
|
||||
command-line-usage "^6.1.1"
|
||||
stream-read-all "^3.0.1"
|
||||
typical "^7.1.1"
|
||||
wordwrapjs "^5.1.0"
|
||||
|
||||
term-size@^2.1.0:
|
||||
version "2.2.1"
|
||||
resolved "https://registry.yarnpkg.com/term-size/-/term-size-2.2.1.tgz#2a6a54840432c2fb6320fea0f415531e90189f54"
|
||||
|
@ -738,6 +864,21 @@ typescript@^4.4.2:
|
|||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.4.2.tgz#6d618640d430e3569a1dfb44f7d7e600ced3ee86"
|
||||
integrity sha512-gzP+t5W4hdy4c+68bfcv0t400HVJMMd2+H9B7gae1nQlBzCqvrXX+6GL/b3GAgyTH966pzrZ70/fRjwAtZksSQ==
|
||||
|
||||
typical@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/typical/-/typical-4.0.0.tgz#cbeaff3b9d7ae1e2bbfaf5a4e6f11eccfde94fc4"
|
||||
integrity sha512-VAH4IvQ7BDFYglMd7BPRDfLgxZZX4O4TFcRDA6EN5X7erNJJq+McIEp8np9aVtxrCJ6qx4GTYVfOWNjcqwZgRw==
|
||||
|
||||
typical@^5.2.0:
|
||||
version "5.2.0"
|
||||
resolved "https://registry.yarnpkg.com/typical/-/typical-5.2.0.tgz#4daaac4f2b5315460804f0acf6cb69c52bb93066"
|
||||
integrity sha512-dvdQgNDNJo+8B2uBQoqdb11eUCE1JQXhvjC/CZtgvZseVd5TYMXnq0+vuUemXbd/Se29cTaUuPX3YIc2xgbvIg==
|
||||
|
||||
typical@^7.1.1:
|
||||
version "7.1.1"
|
||||
resolved "https://registry.yarnpkg.com/typical/-/typical-7.1.1.tgz#ba177ab7ab103b78534463ffa4c0c9754523ac1f"
|
||||
integrity sha512-T+tKVNs6Wu7IWiAce5BgMd7OZfNYUndHwc5MknN+UHOudi7sGZzuHdCadllRuqJ3fPtgFtIH9+lt9qRv6lmpfA==
|
||||
|
||||
undefsafe@^2.0.3:
|
||||
version "2.0.3"
|
||||
resolved "https://registry.yarnpkg.com/undefsafe/-/undefsafe-2.0.3.tgz#6b166e7094ad46313b2202da7ecc2cd7cc6e7aae"
|
||||
|
@ -785,6 +926,19 @@ widest-line@^3.1.0:
|
|||
dependencies:
|
||||
string-width "^4.0.0"
|
||||
|
||||
wordwrapjs@^4.0.0:
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/wordwrapjs/-/wordwrapjs-4.0.1.tgz#d9790bccfb110a0fc7836b5ebce0937b37a8b98f"
|
||||
integrity sha512-kKlNACbvHrkpIw6oPeYDSmdCTu2hdMHoyXLTcUKala++lx5Y+wjJ/e474Jqv5abnVmwxw08DiTuHmw69lJGksA==
|
||||
dependencies:
|
||||
reduce-flatten "^2.0.0"
|
||||
typical "^5.2.0"
|
||||
|
||||
wordwrapjs@^5.1.0:
|
||||
version "5.1.0"
|
||||
resolved "https://registry.yarnpkg.com/wordwrapjs/-/wordwrapjs-5.1.0.tgz#4c4d20446dcc670b14fa115ef4f8fd9947af2b3a"
|
||||
integrity sha512-JNjcULU2e4KJwUNv6CHgI46UvDGitb6dGryHajXTDiLgg1/RiGoPSDw4kZfYnwGtEXf2ZMeIewDQgFGzkCB2Sg==
|
||||
|
||||
wrappy@1:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
|
||||
|
|
Loading…
Reference in a new issue