knowns 0.5.0 → 0.6.0
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/CHANGELOG.md +33 -0
- package/CLAUDE.md +97 -10
- package/dist/index.js +173 -3768
- package/dist/ui/assets/{index-cRjSkI-I.js → index-B6xSBrRN.js} +118 -118
- package/dist/ui/index.html +1 -1
- package/package.json +2 -4
package/dist/index.js
CHANGED
|
@@ -32259,11 +32259,11 @@ var require_router = __commonJS({
|
|
|
32259
32259
|
var slice = Array.prototype.slice;
|
|
32260
32260
|
var flatten = Array.prototype.flat;
|
|
32261
32261
|
var methods = METHODS.map((method) => method.toLowerCase());
|
|
32262
|
-
module2.exports =
|
|
32262
|
+
module2.exports = Router10;
|
|
32263
32263
|
module2.exports.Route = Route;
|
|
32264
|
-
function
|
|
32265
|
-
if (!(this instanceof
|
|
32266
|
-
return new
|
|
32264
|
+
function Router10(options2) {
|
|
32265
|
+
if (!(this instanceof Router10)) {
|
|
32266
|
+
return new Router10(options2);
|
|
32267
32267
|
}
|
|
32268
32268
|
const opts = options2 || {};
|
|
32269
32269
|
function router(req, res, next) {
|
|
@@ -32277,9 +32277,9 @@ var require_router = __commonJS({
|
|
|
32277
32277
|
router.stack = [];
|
|
32278
32278
|
return router;
|
|
32279
32279
|
}
|
|
32280
|
-
|
|
32280
|
+
Router10.prototype = function() {
|
|
32281
32281
|
};
|
|
32282
|
-
|
|
32282
|
+
Router10.prototype.param = function param(name, fn) {
|
|
32283
32283
|
if (!name) {
|
|
32284
32284
|
throw new TypeError("argument name is required");
|
|
32285
32285
|
}
|
|
@@ -32299,7 +32299,7 @@ var require_router = __commonJS({
|
|
|
32299
32299
|
params.push(fn);
|
|
32300
32300
|
return this;
|
|
32301
32301
|
};
|
|
32302
|
-
|
|
32302
|
+
Router10.prototype.handle = function handle(req, res, callback) {
|
|
32303
32303
|
if (!callback) {
|
|
32304
32304
|
throw new TypeError("argument callback is required");
|
|
32305
32305
|
}
|
|
@@ -32426,7 +32426,7 @@ var require_router = __commonJS({
|
|
|
32426
32426
|
}
|
|
32427
32427
|
}
|
|
32428
32428
|
};
|
|
32429
|
-
|
|
32429
|
+
Router10.prototype.use = function use(handler) {
|
|
32430
32430
|
let offset = 0;
|
|
32431
32431
|
let path = "/";
|
|
32432
32432
|
if (typeof handler !== "function") {
|
|
@@ -32459,7 +32459,7 @@ var require_router = __commonJS({
|
|
|
32459
32459
|
}
|
|
32460
32460
|
return this;
|
|
32461
32461
|
};
|
|
32462
|
-
|
|
32462
|
+
Router10.prototype.route = function route(path) {
|
|
32463
32463
|
const route2 = new Route(path);
|
|
32464
32464
|
const layer = new Layer(path, {
|
|
32465
32465
|
sensitive: this.caseSensitive,
|
|
@@ -32474,7 +32474,7 @@ var require_router = __commonJS({
|
|
|
32474
32474
|
return route2;
|
|
32475
32475
|
};
|
|
32476
32476
|
methods.concat("all").forEach(function(method) {
|
|
32477
|
-
|
|
32477
|
+
Router10.prototype[method] = function(path) {
|
|
32478
32478
|
const route = this.route(path);
|
|
32479
32479
|
route[method].apply(route, slice.call(arguments, 1));
|
|
32480
32480
|
return this;
|
|
@@ -32657,7 +32657,7 @@ var require_application = __commonJS({
|
|
|
32657
32657
|
var compileTrust = require_utils4().compileTrust;
|
|
32658
32658
|
var resolve = __require("node:path").resolve;
|
|
32659
32659
|
var once = require_once();
|
|
32660
|
-
var
|
|
32660
|
+
var Router10 = require_router();
|
|
32661
32661
|
var slice = Array.prototype.slice;
|
|
32662
32662
|
var flatten = Array.prototype.flat;
|
|
32663
32663
|
var app = exports2 = module2.exports = {};
|
|
@@ -32673,7 +32673,7 @@ var require_application = __commonJS({
|
|
|
32673
32673
|
enumerable: true,
|
|
32674
32674
|
get: function getrouter() {
|
|
32675
32675
|
if (router === null) {
|
|
32676
|
-
router = new
|
|
32676
|
+
router = new Router10({
|
|
32677
32677
|
caseSensitive: this.enabled("case sensitive routing"),
|
|
32678
32678
|
strict: this.enabled("strict routing")
|
|
32679
32679
|
});
|
|
@@ -35211,7 +35211,7 @@ var require_express = __commonJS({
|
|
|
35211
35211
|
var EventEmitter = __require("node:events").EventEmitter;
|
|
35212
35212
|
var mixin = require_merge_descriptors();
|
|
35213
35213
|
var proto2 = require_application();
|
|
35214
|
-
var
|
|
35214
|
+
var Router10 = require_router();
|
|
35215
35215
|
var req = require_request();
|
|
35216
35216
|
var res = require_response();
|
|
35217
35217
|
exports2 = module2.exports = createApplication;
|
|
@@ -35233,8 +35233,8 @@ var require_express = __commonJS({
|
|
|
35233
35233
|
exports2.application = proto2;
|
|
35234
35234
|
exports2.request = req;
|
|
35235
35235
|
exports2.response = res;
|
|
35236
|
-
exports2.Route =
|
|
35237
|
-
exports2.Router =
|
|
35236
|
+
exports2.Route = Router10.Route;
|
|
35237
|
+
exports2.Router = Router10;
|
|
35238
35238
|
exports2.json = bodyParser.json;
|
|
35239
35239
|
exports2.raw = bodyParser.raw;
|
|
35240
35240
|
exports2.static = require_serve_static();
|
|
@@ -35251,3614 +35251,6 @@ var require_express2 = __commonJS({
|
|
|
35251
35251
|
}
|
|
35252
35252
|
});
|
|
35253
35253
|
|
|
35254
|
-
// node_modules/ws/lib/constants.js
|
|
35255
|
-
var require_constants = __commonJS({
|
|
35256
|
-
"node_modules/ws/lib/constants.js"(exports2, module2) {
|
|
35257
|
-
"use strict";
|
|
35258
|
-
var BINARY_TYPES = ["nodebuffer", "arraybuffer", "fragments"];
|
|
35259
|
-
var hasBlob = typeof Blob !== "undefined";
|
|
35260
|
-
if (hasBlob) BINARY_TYPES.push("blob");
|
|
35261
|
-
module2.exports = {
|
|
35262
|
-
BINARY_TYPES,
|
|
35263
|
-
EMPTY_BUFFER: Buffer.alloc(0),
|
|
35264
|
-
GUID: "258EAFA5-E914-47DA-95CA-C5AB0DC85B11",
|
|
35265
|
-
hasBlob,
|
|
35266
|
-
kForOnEventAttribute: /* @__PURE__ */ Symbol("kIsForOnEventAttribute"),
|
|
35267
|
-
kListener: /* @__PURE__ */ Symbol("kListener"),
|
|
35268
|
-
kStatusCode: /* @__PURE__ */ Symbol("status-code"),
|
|
35269
|
-
kWebSocket: /* @__PURE__ */ Symbol("websocket"),
|
|
35270
|
-
NOOP: () => {
|
|
35271
|
-
}
|
|
35272
|
-
};
|
|
35273
|
-
}
|
|
35274
|
-
});
|
|
35275
|
-
|
|
35276
|
-
// node_modules/ws/lib/buffer-util.js
|
|
35277
|
-
var require_buffer_util = __commonJS({
|
|
35278
|
-
"node_modules/ws/lib/buffer-util.js"(exports2, module2) {
|
|
35279
|
-
"use strict";
|
|
35280
|
-
var { EMPTY_BUFFER } = require_constants();
|
|
35281
|
-
var FastBuffer = Buffer[Symbol.species];
|
|
35282
|
-
function concat(list, totalLength) {
|
|
35283
|
-
if (list.length === 0) return EMPTY_BUFFER;
|
|
35284
|
-
if (list.length === 1) return list[0];
|
|
35285
|
-
const target = Buffer.allocUnsafe(totalLength);
|
|
35286
|
-
let offset = 0;
|
|
35287
|
-
for (let i = 0; i < list.length; i++) {
|
|
35288
|
-
const buf = list[i];
|
|
35289
|
-
target.set(buf, offset);
|
|
35290
|
-
offset += buf.length;
|
|
35291
|
-
}
|
|
35292
|
-
if (offset < totalLength) {
|
|
35293
|
-
return new FastBuffer(target.buffer, target.byteOffset, offset);
|
|
35294
|
-
}
|
|
35295
|
-
return target;
|
|
35296
|
-
}
|
|
35297
|
-
function _mask(source, mask, output, offset, length) {
|
|
35298
|
-
for (let i = 0; i < length; i++) {
|
|
35299
|
-
output[offset + i] = source[i] ^ mask[i & 3];
|
|
35300
|
-
}
|
|
35301
|
-
}
|
|
35302
|
-
function _unmask(buffer, mask) {
|
|
35303
|
-
for (let i = 0; i < buffer.length; i++) {
|
|
35304
|
-
buffer[i] ^= mask[i & 3];
|
|
35305
|
-
}
|
|
35306
|
-
}
|
|
35307
|
-
function toArrayBuffer(buf) {
|
|
35308
|
-
if (buf.length === buf.buffer.byteLength) {
|
|
35309
|
-
return buf.buffer;
|
|
35310
|
-
}
|
|
35311
|
-
return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.length);
|
|
35312
|
-
}
|
|
35313
|
-
function toBuffer(data) {
|
|
35314
|
-
toBuffer.readOnly = true;
|
|
35315
|
-
if (Buffer.isBuffer(data)) return data;
|
|
35316
|
-
let buf;
|
|
35317
|
-
if (data instanceof ArrayBuffer) {
|
|
35318
|
-
buf = new FastBuffer(data);
|
|
35319
|
-
} else if (ArrayBuffer.isView(data)) {
|
|
35320
|
-
buf = new FastBuffer(data.buffer, data.byteOffset, data.byteLength);
|
|
35321
|
-
} else {
|
|
35322
|
-
buf = Buffer.from(data);
|
|
35323
|
-
toBuffer.readOnly = false;
|
|
35324
|
-
}
|
|
35325
|
-
return buf;
|
|
35326
|
-
}
|
|
35327
|
-
module2.exports = {
|
|
35328
|
-
concat,
|
|
35329
|
-
mask: _mask,
|
|
35330
|
-
toArrayBuffer,
|
|
35331
|
-
toBuffer,
|
|
35332
|
-
unmask: _unmask
|
|
35333
|
-
};
|
|
35334
|
-
if (!process.env.WS_NO_BUFFER_UTIL) {
|
|
35335
|
-
try {
|
|
35336
|
-
const bufferUtil = __require("bufferutil");
|
|
35337
|
-
module2.exports.mask = function(source, mask, output, offset, length) {
|
|
35338
|
-
if (length < 48) _mask(source, mask, output, offset, length);
|
|
35339
|
-
else bufferUtil.mask(source, mask, output, offset, length);
|
|
35340
|
-
};
|
|
35341
|
-
module2.exports.unmask = function(buffer, mask) {
|
|
35342
|
-
if (buffer.length < 32) _unmask(buffer, mask);
|
|
35343
|
-
else bufferUtil.unmask(buffer, mask);
|
|
35344
|
-
};
|
|
35345
|
-
} catch (e) {
|
|
35346
|
-
}
|
|
35347
|
-
}
|
|
35348
|
-
}
|
|
35349
|
-
});
|
|
35350
|
-
|
|
35351
|
-
// node_modules/ws/lib/limiter.js
|
|
35352
|
-
var require_limiter = __commonJS({
|
|
35353
|
-
"node_modules/ws/lib/limiter.js"(exports2, module2) {
|
|
35354
|
-
"use strict";
|
|
35355
|
-
var kDone = /* @__PURE__ */ Symbol("kDone");
|
|
35356
|
-
var kRun = /* @__PURE__ */ Symbol("kRun");
|
|
35357
|
-
var Limiter = class {
|
|
35358
|
-
/**
|
|
35359
|
-
* Creates a new `Limiter`.
|
|
35360
|
-
*
|
|
35361
|
-
* @param {Number} [concurrency=Infinity] The maximum number of jobs allowed
|
|
35362
|
-
* to run concurrently
|
|
35363
|
-
*/
|
|
35364
|
-
constructor(concurrency) {
|
|
35365
|
-
this[kDone] = () => {
|
|
35366
|
-
this.pending--;
|
|
35367
|
-
this[kRun]();
|
|
35368
|
-
};
|
|
35369
|
-
this.concurrency = concurrency || Infinity;
|
|
35370
|
-
this.jobs = [];
|
|
35371
|
-
this.pending = 0;
|
|
35372
|
-
}
|
|
35373
|
-
/**
|
|
35374
|
-
* Adds a job to the queue.
|
|
35375
|
-
*
|
|
35376
|
-
* @param {Function} job The job to run
|
|
35377
|
-
* @public
|
|
35378
|
-
*/
|
|
35379
|
-
add(job) {
|
|
35380
|
-
this.jobs.push(job);
|
|
35381
|
-
this[kRun]();
|
|
35382
|
-
}
|
|
35383
|
-
/**
|
|
35384
|
-
* Removes a job from the queue and runs it if possible.
|
|
35385
|
-
*
|
|
35386
|
-
* @private
|
|
35387
|
-
*/
|
|
35388
|
-
[kRun]() {
|
|
35389
|
-
if (this.pending === this.concurrency) return;
|
|
35390
|
-
if (this.jobs.length) {
|
|
35391
|
-
const job = this.jobs.shift();
|
|
35392
|
-
this.pending++;
|
|
35393
|
-
job(this[kDone]);
|
|
35394
|
-
}
|
|
35395
|
-
}
|
|
35396
|
-
};
|
|
35397
|
-
module2.exports = Limiter;
|
|
35398
|
-
}
|
|
35399
|
-
});
|
|
35400
|
-
|
|
35401
|
-
// node_modules/ws/lib/permessage-deflate.js
|
|
35402
|
-
var require_permessage_deflate = __commonJS({
|
|
35403
|
-
"node_modules/ws/lib/permessage-deflate.js"(exports2, module2) {
|
|
35404
|
-
"use strict";
|
|
35405
|
-
var zlib = __require("zlib");
|
|
35406
|
-
var bufferUtil = require_buffer_util();
|
|
35407
|
-
var Limiter = require_limiter();
|
|
35408
|
-
var { kStatusCode } = require_constants();
|
|
35409
|
-
var FastBuffer = Buffer[Symbol.species];
|
|
35410
|
-
var TRAILER = Buffer.from([0, 0, 255, 255]);
|
|
35411
|
-
var kPerMessageDeflate = /* @__PURE__ */ Symbol("permessage-deflate");
|
|
35412
|
-
var kTotalLength = /* @__PURE__ */ Symbol("total-length");
|
|
35413
|
-
var kCallback = /* @__PURE__ */ Symbol("callback");
|
|
35414
|
-
var kBuffers = /* @__PURE__ */ Symbol("buffers");
|
|
35415
|
-
var kError = /* @__PURE__ */ Symbol("error");
|
|
35416
|
-
var zlibLimiter;
|
|
35417
|
-
var PerMessageDeflate = class {
|
|
35418
|
-
/**
|
|
35419
|
-
* Creates a PerMessageDeflate instance.
|
|
35420
|
-
*
|
|
35421
|
-
* @param {Object} [options] Configuration options
|
|
35422
|
-
* @param {(Boolean|Number)} [options.clientMaxWindowBits] Advertise support
|
|
35423
|
-
* for, or request, a custom client window size
|
|
35424
|
-
* @param {Boolean} [options.clientNoContextTakeover=false] Advertise/
|
|
35425
|
-
* acknowledge disabling of client context takeover
|
|
35426
|
-
* @param {Number} [options.concurrencyLimit=10] The number of concurrent
|
|
35427
|
-
* calls to zlib
|
|
35428
|
-
* @param {(Boolean|Number)} [options.serverMaxWindowBits] Request/confirm the
|
|
35429
|
-
* use of a custom server window size
|
|
35430
|
-
* @param {Boolean} [options.serverNoContextTakeover=false] Request/accept
|
|
35431
|
-
* disabling of server context takeover
|
|
35432
|
-
* @param {Number} [options.threshold=1024] Size (in bytes) below which
|
|
35433
|
-
* messages should not be compressed if context takeover is disabled
|
|
35434
|
-
* @param {Object} [options.zlibDeflateOptions] Options to pass to zlib on
|
|
35435
|
-
* deflate
|
|
35436
|
-
* @param {Object} [options.zlibInflateOptions] Options to pass to zlib on
|
|
35437
|
-
* inflate
|
|
35438
|
-
* @param {Boolean} [isServer=false] Create the instance in either server or
|
|
35439
|
-
* client mode
|
|
35440
|
-
* @param {Number} [maxPayload=0] The maximum allowed message length
|
|
35441
|
-
*/
|
|
35442
|
-
constructor(options2, isServer, maxPayload) {
|
|
35443
|
-
this._maxPayload = maxPayload | 0;
|
|
35444
|
-
this._options = options2 || {};
|
|
35445
|
-
this._threshold = this._options.threshold !== void 0 ? this._options.threshold : 1024;
|
|
35446
|
-
this._isServer = !!isServer;
|
|
35447
|
-
this._deflate = null;
|
|
35448
|
-
this._inflate = null;
|
|
35449
|
-
this.params = null;
|
|
35450
|
-
if (!zlibLimiter) {
|
|
35451
|
-
const concurrency = this._options.concurrencyLimit !== void 0 ? this._options.concurrencyLimit : 10;
|
|
35452
|
-
zlibLimiter = new Limiter(concurrency);
|
|
35453
|
-
}
|
|
35454
|
-
}
|
|
35455
|
-
/**
|
|
35456
|
-
* @type {String}
|
|
35457
|
-
*/
|
|
35458
|
-
static get extensionName() {
|
|
35459
|
-
return "permessage-deflate";
|
|
35460
|
-
}
|
|
35461
|
-
/**
|
|
35462
|
-
* Create an extension negotiation offer.
|
|
35463
|
-
*
|
|
35464
|
-
* @return {Object} Extension parameters
|
|
35465
|
-
* @public
|
|
35466
|
-
*/
|
|
35467
|
-
offer() {
|
|
35468
|
-
const params = {};
|
|
35469
|
-
if (this._options.serverNoContextTakeover) {
|
|
35470
|
-
params.server_no_context_takeover = true;
|
|
35471
|
-
}
|
|
35472
|
-
if (this._options.clientNoContextTakeover) {
|
|
35473
|
-
params.client_no_context_takeover = true;
|
|
35474
|
-
}
|
|
35475
|
-
if (this._options.serverMaxWindowBits) {
|
|
35476
|
-
params.server_max_window_bits = this._options.serverMaxWindowBits;
|
|
35477
|
-
}
|
|
35478
|
-
if (this._options.clientMaxWindowBits) {
|
|
35479
|
-
params.client_max_window_bits = this._options.clientMaxWindowBits;
|
|
35480
|
-
} else if (this._options.clientMaxWindowBits == null) {
|
|
35481
|
-
params.client_max_window_bits = true;
|
|
35482
|
-
}
|
|
35483
|
-
return params;
|
|
35484
|
-
}
|
|
35485
|
-
/**
|
|
35486
|
-
* Accept an extension negotiation offer/response.
|
|
35487
|
-
*
|
|
35488
|
-
* @param {Array} configurations The extension negotiation offers/reponse
|
|
35489
|
-
* @return {Object} Accepted configuration
|
|
35490
|
-
* @public
|
|
35491
|
-
*/
|
|
35492
|
-
accept(configurations) {
|
|
35493
|
-
configurations = this.normalizeParams(configurations);
|
|
35494
|
-
this.params = this._isServer ? this.acceptAsServer(configurations) : this.acceptAsClient(configurations);
|
|
35495
|
-
return this.params;
|
|
35496
|
-
}
|
|
35497
|
-
/**
|
|
35498
|
-
* Releases all resources used by the extension.
|
|
35499
|
-
*
|
|
35500
|
-
* @public
|
|
35501
|
-
*/
|
|
35502
|
-
cleanup() {
|
|
35503
|
-
if (this._inflate) {
|
|
35504
|
-
this._inflate.close();
|
|
35505
|
-
this._inflate = null;
|
|
35506
|
-
}
|
|
35507
|
-
if (this._deflate) {
|
|
35508
|
-
const callback = this._deflate[kCallback];
|
|
35509
|
-
this._deflate.close();
|
|
35510
|
-
this._deflate = null;
|
|
35511
|
-
if (callback) {
|
|
35512
|
-
callback(
|
|
35513
|
-
new Error(
|
|
35514
|
-
"The deflate stream was closed while data was being processed"
|
|
35515
|
-
)
|
|
35516
|
-
);
|
|
35517
|
-
}
|
|
35518
|
-
}
|
|
35519
|
-
}
|
|
35520
|
-
/**
|
|
35521
|
-
* Accept an extension negotiation offer.
|
|
35522
|
-
*
|
|
35523
|
-
* @param {Array} offers The extension negotiation offers
|
|
35524
|
-
* @return {Object} Accepted configuration
|
|
35525
|
-
* @private
|
|
35526
|
-
*/
|
|
35527
|
-
acceptAsServer(offers) {
|
|
35528
|
-
const opts = this._options;
|
|
35529
|
-
const accepted = offers.find((params) => {
|
|
35530
|
-
if (opts.serverNoContextTakeover === false && params.server_no_context_takeover || params.server_max_window_bits && (opts.serverMaxWindowBits === false || typeof opts.serverMaxWindowBits === "number" && opts.serverMaxWindowBits > params.server_max_window_bits) || typeof opts.clientMaxWindowBits === "number" && !params.client_max_window_bits) {
|
|
35531
|
-
return false;
|
|
35532
|
-
}
|
|
35533
|
-
return true;
|
|
35534
|
-
});
|
|
35535
|
-
if (!accepted) {
|
|
35536
|
-
throw new Error("None of the extension offers can be accepted");
|
|
35537
|
-
}
|
|
35538
|
-
if (opts.serverNoContextTakeover) {
|
|
35539
|
-
accepted.server_no_context_takeover = true;
|
|
35540
|
-
}
|
|
35541
|
-
if (opts.clientNoContextTakeover) {
|
|
35542
|
-
accepted.client_no_context_takeover = true;
|
|
35543
|
-
}
|
|
35544
|
-
if (typeof opts.serverMaxWindowBits === "number") {
|
|
35545
|
-
accepted.server_max_window_bits = opts.serverMaxWindowBits;
|
|
35546
|
-
}
|
|
35547
|
-
if (typeof opts.clientMaxWindowBits === "number") {
|
|
35548
|
-
accepted.client_max_window_bits = opts.clientMaxWindowBits;
|
|
35549
|
-
} else if (accepted.client_max_window_bits === true || opts.clientMaxWindowBits === false) {
|
|
35550
|
-
delete accepted.client_max_window_bits;
|
|
35551
|
-
}
|
|
35552
|
-
return accepted;
|
|
35553
|
-
}
|
|
35554
|
-
/**
|
|
35555
|
-
* Accept the extension negotiation response.
|
|
35556
|
-
*
|
|
35557
|
-
* @param {Array} response The extension negotiation response
|
|
35558
|
-
* @return {Object} Accepted configuration
|
|
35559
|
-
* @private
|
|
35560
|
-
*/
|
|
35561
|
-
acceptAsClient(response) {
|
|
35562
|
-
const params = response[0];
|
|
35563
|
-
if (this._options.clientNoContextTakeover === false && params.client_no_context_takeover) {
|
|
35564
|
-
throw new Error('Unexpected parameter "client_no_context_takeover"');
|
|
35565
|
-
}
|
|
35566
|
-
if (!params.client_max_window_bits) {
|
|
35567
|
-
if (typeof this._options.clientMaxWindowBits === "number") {
|
|
35568
|
-
params.client_max_window_bits = this._options.clientMaxWindowBits;
|
|
35569
|
-
}
|
|
35570
|
-
} else if (this._options.clientMaxWindowBits === false || typeof this._options.clientMaxWindowBits === "number" && params.client_max_window_bits > this._options.clientMaxWindowBits) {
|
|
35571
|
-
throw new Error(
|
|
35572
|
-
'Unexpected or invalid parameter "client_max_window_bits"'
|
|
35573
|
-
);
|
|
35574
|
-
}
|
|
35575
|
-
return params;
|
|
35576
|
-
}
|
|
35577
|
-
/**
|
|
35578
|
-
* Normalize parameters.
|
|
35579
|
-
*
|
|
35580
|
-
* @param {Array} configurations The extension negotiation offers/reponse
|
|
35581
|
-
* @return {Array} The offers/response with normalized parameters
|
|
35582
|
-
* @private
|
|
35583
|
-
*/
|
|
35584
|
-
normalizeParams(configurations) {
|
|
35585
|
-
configurations.forEach((params) => {
|
|
35586
|
-
Object.keys(params).forEach((key) => {
|
|
35587
|
-
let value = params[key];
|
|
35588
|
-
if (value.length > 1) {
|
|
35589
|
-
throw new Error(`Parameter "${key}" must have only a single value`);
|
|
35590
|
-
}
|
|
35591
|
-
value = value[0];
|
|
35592
|
-
if (key === "client_max_window_bits") {
|
|
35593
|
-
if (value !== true) {
|
|
35594
|
-
const num = +value;
|
|
35595
|
-
if (!Number.isInteger(num) || num < 8 || num > 15) {
|
|
35596
|
-
throw new TypeError(
|
|
35597
|
-
`Invalid value for parameter "${key}": ${value}`
|
|
35598
|
-
);
|
|
35599
|
-
}
|
|
35600
|
-
value = num;
|
|
35601
|
-
} else if (!this._isServer) {
|
|
35602
|
-
throw new TypeError(
|
|
35603
|
-
`Invalid value for parameter "${key}": ${value}`
|
|
35604
|
-
);
|
|
35605
|
-
}
|
|
35606
|
-
} else if (key === "server_max_window_bits") {
|
|
35607
|
-
const num = +value;
|
|
35608
|
-
if (!Number.isInteger(num) || num < 8 || num > 15) {
|
|
35609
|
-
throw new TypeError(
|
|
35610
|
-
`Invalid value for parameter "${key}": ${value}`
|
|
35611
|
-
);
|
|
35612
|
-
}
|
|
35613
|
-
value = num;
|
|
35614
|
-
} else if (key === "client_no_context_takeover" || key === "server_no_context_takeover") {
|
|
35615
|
-
if (value !== true) {
|
|
35616
|
-
throw new TypeError(
|
|
35617
|
-
`Invalid value for parameter "${key}": ${value}`
|
|
35618
|
-
);
|
|
35619
|
-
}
|
|
35620
|
-
} else {
|
|
35621
|
-
throw new Error(`Unknown parameter "${key}"`);
|
|
35622
|
-
}
|
|
35623
|
-
params[key] = value;
|
|
35624
|
-
});
|
|
35625
|
-
});
|
|
35626
|
-
return configurations;
|
|
35627
|
-
}
|
|
35628
|
-
/**
|
|
35629
|
-
* Decompress data. Concurrency limited.
|
|
35630
|
-
*
|
|
35631
|
-
* @param {Buffer} data Compressed data
|
|
35632
|
-
* @param {Boolean} fin Specifies whether or not this is the last fragment
|
|
35633
|
-
* @param {Function} callback Callback
|
|
35634
|
-
* @public
|
|
35635
|
-
*/
|
|
35636
|
-
decompress(data, fin, callback) {
|
|
35637
|
-
zlibLimiter.add((done) => {
|
|
35638
|
-
this._decompress(data, fin, (err, result) => {
|
|
35639
|
-
done();
|
|
35640
|
-
callback(err, result);
|
|
35641
|
-
});
|
|
35642
|
-
});
|
|
35643
|
-
}
|
|
35644
|
-
/**
|
|
35645
|
-
* Compress data. Concurrency limited.
|
|
35646
|
-
*
|
|
35647
|
-
* @param {(Buffer|String)} data Data to compress
|
|
35648
|
-
* @param {Boolean} fin Specifies whether or not this is the last fragment
|
|
35649
|
-
* @param {Function} callback Callback
|
|
35650
|
-
* @public
|
|
35651
|
-
*/
|
|
35652
|
-
compress(data, fin, callback) {
|
|
35653
|
-
zlibLimiter.add((done) => {
|
|
35654
|
-
this._compress(data, fin, (err, result) => {
|
|
35655
|
-
done();
|
|
35656
|
-
callback(err, result);
|
|
35657
|
-
});
|
|
35658
|
-
});
|
|
35659
|
-
}
|
|
35660
|
-
/**
|
|
35661
|
-
* Decompress data.
|
|
35662
|
-
*
|
|
35663
|
-
* @param {Buffer} data Compressed data
|
|
35664
|
-
* @param {Boolean} fin Specifies whether or not this is the last fragment
|
|
35665
|
-
* @param {Function} callback Callback
|
|
35666
|
-
* @private
|
|
35667
|
-
*/
|
|
35668
|
-
_decompress(data, fin, callback) {
|
|
35669
|
-
const endpoint = this._isServer ? "client" : "server";
|
|
35670
|
-
if (!this._inflate) {
|
|
35671
|
-
const key = `${endpoint}_max_window_bits`;
|
|
35672
|
-
const windowBits = typeof this.params[key] !== "number" ? zlib.Z_DEFAULT_WINDOWBITS : this.params[key];
|
|
35673
|
-
this._inflate = zlib.createInflateRaw({
|
|
35674
|
-
...this._options.zlibInflateOptions,
|
|
35675
|
-
windowBits
|
|
35676
|
-
});
|
|
35677
|
-
this._inflate[kPerMessageDeflate] = this;
|
|
35678
|
-
this._inflate[kTotalLength] = 0;
|
|
35679
|
-
this._inflate[kBuffers] = [];
|
|
35680
|
-
this._inflate.on("error", inflateOnError);
|
|
35681
|
-
this._inflate.on("data", inflateOnData);
|
|
35682
|
-
}
|
|
35683
|
-
this._inflate[kCallback] = callback;
|
|
35684
|
-
this._inflate.write(data);
|
|
35685
|
-
if (fin) this._inflate.write(TRAILER);
|
|
35686
|
-
this._inflate.flush(() => {
|
|
35687
|
-
const err = this._inflate[kError];
|
|
35688
|
-
if (err) {
|
|
35689
|
-
this._inflate.close();
|
|
35690
|
-
this._inflate = null;
|
|
35691
|
-
callback(err);
|
|
35692
|
-
return;
|
|
35693
|
-
}
|
|
35694
|
-
const data2 = bufferUtil.concat(
|
|
35695
|
-
this._inflate[kBuffers],
|
|
35696
|
-
this._inflate[kTotalLength]
|
|
35697
|
-
);
|
|
35698
|
-
if (this._inflate._readableState.endEmitted) {
|
|
35699
|
-
this._inflate.close();
|
|
35700
|
-
this._inflate = null;
|
|
35701
|
-
} else {
|
|
35702
|
-
this._inflate[kTotalLength] = 0;
|
|
35703
|
-
this._inflate[kBuffers] = [];
|
|
35704
|
-
if (fin && this.params[`${endpoint}_no_context_takeover`]) {
|
|
35705
|
-
this._inflate.reset();
|
|
35706
|
-
}
|
|
35707
|
-
}
|
|
35708
|
-
callback(null, data2);
|
|
35709
|
-
});
|
|
35710
|
-
}
|
|
35711
|
-
/**
|
|
35712
|
-
* Compress data.
|
|
35713
|
-
*
|
|
35714
|
-
* @param {(Buffer|String)} data Data to compress
|
|
35715
|
-
* @param {Boolean} fin Specifies whether or not this is the last fragment
|
|
35716
|
-
* @param {Function} callback Callback
|
|
35717
|
-
* @private
|
|
35718
|
-
*/
|
|
35719
|
-
_compress(data, fin, callback) {
|
|
35720
|
-
const endpoint = this._isServer ? "server" : "client";
|
|
35721
|
-
if (!this._deflate) {
|
|
35722
|
-
const key = `${endpoint}_max_window_bits`;
|
|
35723
|
-
const windowBits = typeof this.params[key] !== "number" ? zlib.Z_DEFAULT_WINDOWBITS : this.params[key];
|
|
35724
|
-
this._deflate = zlib.createDeflateRaw({
|
|
35725
|
-
...this._options.zlibDeflateOptions,
|
|
35726
|
-
windowBits
|
|
35727
|
-
});
|
|
35728
|
-
this._deflate[kTotalLength] = 0;
|
|
35729
|
-
this._deflate[kBuffers] = [];
|
|
35730
|
-
this._deflate.on("data", deflateOnData);
|
|
35731
|
-
}
|
|
35732
|
-
this._deflate[kCallback] = callback;
|
|
35733
|
-
this._deflate.write(data);
|
|
35734
|
-
this._deflate.flush(zlib.Z_SYNC_FLUSH, () => {
|
|
35735
|
-
if (!this._deflate) {
|
|
35736
|
-
return;
|
|
35737
|
-
}
|
|
35738
|
-
let data2 = bufferUtil.concat(
|
|
35739
|
-
this._deflate[kBuffers],
|
|
35740
|
-
this._deflate[kTotalLength]
|
|
35741
|
-
);
|
|
35742
|
-
if (fin) {
|
|
35743
|
-
data2 = new FastBuffer(data2.buffer, data2.byteOffset, data2.length - 4);
|
|
35744
|
-
}
|
|
35745
|
-
this._deflate[kCallback] = null;
|
|
35746
|
-
this._deflate[kTotalLength] = 0;
|
|
35747
|
-
this._deflate[kBuffers] = [];
|
|
35748
|
-
if (fin && this.params[`${endpoint}_no_context_takeover`]) {
|
|
35749
|
-
this._deflate.reset();
|
|
35750
|
-
}
|
|
35751
|
-
callback(null, data2);
|
|
35752
|
-
});
|
|
35753
|
-
}
|
|
35754
|
-
};
|
|
35755
|
-
module2.exports = PerMessageDeflate;
|
|
35756
|
-
function deflateOnData(chunk) {
|
|
35757
|
-
this[kBuffers].push(chunk);
|
|
35758
|
-
this[kTotalLength] += chunk.length;
|
|
35759
|
-
}
|
|
35760
|
-
function inflateOnData(chunk) {
|
|
35761
|
-
this[kTotalLength] += chunk.length;
|
|
35762
|
-
if (this[kPerMessageDeflate]._maxPayload < 1 || this[kTotalLength] <= this[kPerMessageDeflate]._maxPayload) {
|
|
35763
|
-
this[kBuffers].push(chunk);
|
|
35764
|
-
return;
|
|
35765
|
-
}
|
|
35766
|
-
this[kError] = new RangeError("Max payload size exceeded");
|
|
35767
|
-
this[kError].code = "WS_ERR_UNSUPPORTED_MESSAGE_LENGTH";
|
|
35768
|
-
this[kError][kStatusCode] = 1009;
|
|
35769
|
-
this.removeListener("data", inflateOnData);
|
|
35770
|
-
this.reset();
|
|
35771
|
-
}
|
|
35772
|
-
function inflateOnError(err) {
|
|
35773
|
-
this[kPerMessageDeflate]._inflate = null;
|
|
35774
|
-
if (this[kError]) {
|
|
35775
|
-
this[kCallback](this[kError]);
|
|
35776
|
-
return;
|
|
35777
|
-
}
|
|
35778
|
-
err[kStatusCode] = 1007;
|
|
35779
|
-
this[kCallback](err);
|
|
35780
|
-
}
|
|
35781
|
-
}
|
|
35782
|
-
});
|
|
35783
|
-
|
|
35784
|
-
// node_modules/ws/lib/validation.js
|
|
35785
|
-
var require_validation = __commonJS({
|
|
35786
|
-
"node_modules/ws/lib/validation.js"(exports2, module2) {
|
|
35787
|
-
"use strict";
|
|
35788
|
-
var { isUtf8 } = __require("buffer");
|
|
35789
|
-
var { hasBlob } = require_constants();
|
|
35790
|
-
var tokenChars = [
|
|
35791
|
-
0,
|
|
35792
|
-
0,
|
|
35793
|
-
0,
|
|
35794
|
-
0,
|
|
35795
|
-
0,
|
|
35796
|
-
0,
|
|
35797
|
-
0,
|
|
35798
|
-
0,
|
|
35799
|
-
0,
|
|
35800
|
-
0,
|
|
35801
|
-
0,
|
|
35802
|
-
0,
|
|
35803
|
-
0,
|
|
35804
|
-
0,
|
|
35805
|
-
0,
|
|
35806
|
-
0,
|
|
35807
|
-
// 0 - 15
|
|
35808
|
-
0,
|
|
35809
|
-
0,
|
|
35810
|
-
0,
|
|
35811
|
-
0,
|
|
35812
|
-
0,
|
|
35813
|
-
0,
|
|
35814
|
-
0,
|
|
35815
|
-
0,
|
|
35816
|
-
0,
|
|
35817
|
-
0,
|
|
35818
|
-
0,
|
|
35819
|
-
0,
|
|
35820
|
-
0,
|
|
35821
|
-
0,
|
|
35822
|
-
0,
|
|
35823
|
-
0,
|
|
35824
|
-
// 16 - 31
|
|
35825
|
-
0,
|
|
35826
|
-
1,
|
|
35827
|
-
0,
|
|
35828
|
-
1,
|
|
35829
|
-
1,
|
|
35830
|
-
1,
|
|
35831
|
-
1,
|
|
35832
|
-
1,
|
|
35833
|
-
0,
|
|
35834
|
-
0,
|
|
35835
|
-
1,
|
|
35836
|
-
1,
|
|
35837
|
-
0,
|
|
35838
|
-
1,
|
|
35839
|
-
1,
|
|
35840
|
-
0,
|
|
35841
|
-
// 32 - 47
|
|
35842
|
-
1,
|
|
35843
|
-
1,
|
|
35844
|
-
1,
|
|
35845
|
-
1,
|
|
35846
|
-
1,
|
|
35847
|
-
1,
|
|
35848
|
-
1,
|
|
35849
|
-
1,
|
|
35850
|
-
1,
|
|
35851
|
-
1,
|
|
35852
|
-
0,
|
|
35853
|
-
0,
|
|
35854
|
-
0,
|
|
35855
|
-
0,
|
|
35856
|
-
0,
|
|
35857
|
-
0,
|
|
35858
|
-
// 48 - 63
|
|
35859
|
-
0,
|
|
35860
|
-
1,
|
|
35861
|
-
1,
|
|
35862
|
-
1,
|
|
35863
|
-
1,
|
|
35864
|
-
1,
|
|
35865
|
-
1,
|
|
35866
|
-
1,
|
|
35867
|
-
1,
|
|
35868
|
-
1,
|
|
35869
|
-
1,
|
|
35870
|
-
1,
|
|
35871
|
-
1,
|
|
35872
|
-
1,
|
|
35873
|
-
1,
|
|
35874
|
-
1,
|
|
35875
|
-
// 64 - 79
|
|
35876
|
-
1,
|
|
35877
|
-
1,
|
|
35878
|
-
1,
|
|
35879
|
-
1,
|
|
35880
|
-
1,
|
|
35881
|
-
1,
|
|
35882
|
-
1,
|
|
35883
|
-
1,
|
|
35884
|
-
1,
|
|
35885
|
-
1,
|
|
35886
|
-
1,
|
|
35887
|
-
0,
|
|
35888
|
-
0,
|
|
35889
|
-
0,
|
|
35890
|
-
1,
|
|
35891
|
-
1,
|
|
35892
|
-
// 80 - 95
|
|
35893
|
-
1,
|
|
35894
|
-
1,
|
|
35895
|
-
1,
|
|
35896
|
-
1,
|
|
35897
|
-
1,
|
|
35898
|
-
1,
|
|
35899
|
-
1,
|
|
35900
|
-
1,
|
|
35901
|
-
1,
|
|
35902
|
-
1,
|
|
35903
|
-
1,
|
|
35904
|
-
1,
|
|
35905
|
-
1,
|
|
35906
|
-
1,
|
|
35907
|
-
1,
|
|
35908
|
-
1,
|
|
35909
|
-
// 96 - 111
|
|
35910
|
-
1,
|
|
35911
|
-
1,
|
|
35912
|
-
1,
|
|
35913
|
-
1,
|
|
35914
|
-
1,
|
|
35915
|
-
1,
|
|
35916
|
-
1,
|
|
35917
|
-
1,
|
|
35918
|
-
1,
|
|
35919
|
-
1,
|
|
35920
|
-
1,
|
|
35921
|
-
0,
|
|
35922
|
-
1,
|
|
35923
|
-
0,
|
|
35924
|
-
1,
|
|
35925
|
-
0
|
|
35926
|
-
// 112 - 127
|
|
35927
|
-
];
|
|
35928
|
-
function isValidStatusCode(code) {
|
|
35929
|
-
return code >= 1e3 && code <= 1014 && code !== 1004 && code !== 1005 && code !== 1006 || code >= 3e3 && code <= 4999;
|
|
35930
|
-
}
|
|
35931
|
-
function _isValidUTF8(buf) {
|
|
35932
|
-
const len = buf.length;
|
|
35933
|
-
let i = 0;
|
|
35934
|
-
while (i < len) {
|
|
35935
|
-
if ((buf[i] & 128) === 0) {
|
|
35936
|
-
i++;
|
|
35937
|
-
} else if ((buf[i] & 224) === 192) {
|
|
35938
|
-
if (i + 1 === len || (buf[i + 1] & 192) !== 128 || (buf[i] & 254) === 192) {
|
|
35939
|
-
return false;
|
|
35940
|
-
}
|
|
35941
|
-
i += 2;
|
|
35942
|
-
} else if ((buf[i] & 240) === 224) {
|
|
35943
|
-
if (i + 2 >= len || (buf[i + 1] & 192) !== 128 || (buf[i + 2] & 192) !== 128 || buf[i] === 224 && (buf[i + 1] & 224) === 128 || // Overlong
|
|
35944
|
-
buf[i] === 237 && (buf[i + 1] & 224) === 160) {
|
|
35945
|
-
return false;
|
|
35946
|
-
}
|
|
35947
|
-
i += 3;
|
|
35948
|
-
} else if ((buf[i] & 248) === 240) {
|
|
35949
|
-
if (i + 3 >= len || (buf[i + 1] & 192) !== 128 || (buf[i + 2] & 192) !== 128 || (buf[i + 3] & 192) !== 128 || buf[i] === 240 && (buf[i + 1] & 240) === 128 || // Overlong
|
|
35950
|
-
buf[i] === 244 && buf[i + 1] > 143 || buf[i] > 244) {
|
|
35951
|
-
return false;
|
|
35952
|
-
}
|
|
35953
|
-
i += 4;
|
|
35954
|
-
} else {
|
|
35955
|
-
return false;
|
|
35956
|
-
}
|
|
35957
|
-
}
|
|
35958
|
-
return true;
|
|
35959
|
-
}
|
|
35960
|
-
function isBlob(value) {
|
|
35961
|
-
return hasBlob && typeof value === "object" && typeof value.arrayBuffer === "function" && typeof value.type === "string" && typeof value.stream === "function" && (value[Symbol.toStringTag] === "Blob" || value[Symbol.toStringTag] === "File");
|
|
35962
|
-
}
|
|
35963
|
-
module2.exports = {
|
|
35964
|
-
isBlob,
|
|
35965
|
-
isValidStatusCode,
|
|
35966
|
-
isValidUTF8: _isValidUTF8,
|
|
35967
|
-
tokenChars
|
|
35968
|
-
};
|
|
35969
|
-
if (isUtf8) {
|
|
35970
|
-
module2.exports.isValidUTF8 = function(buf) {
|
|
35971
|
-
return buf.length < 24 ? _isValidUTF8(buf) : isUtf8(buf);
|
|
35972
|
-
};
|
|
35973
|
-
} else if (!process.env.WS_NO_UTF_8_VALIDATE) {
|
|
35974
|
-
try {
|
|
35975
|
-
const isValidUTF8 = __require("utf-8-validate");
|
|
35976
|
-
module2.exports.isValidUTF8 = function(buf) {
|
|
35977
|
-
return buf.length < 32 ? _isValidUTF8(buf) : isValidUTF8(buf);
|
|
35978
|
-
};
|
|
35979
|
-
} catch (e) {
|
|
35980
|
-
}
|
|
35981
|
-
}
|
|
35982
|
-
}
|
|
35983
|
-
});
|
|
35984
|
-
|
|
35985
|
-
// node_modules/ws/lib/receiver.js
|
|
35986
|
-
var require_receiver = __commonJS({
|
|
35987
|
-
"node_modules/ws/lib/receiver.js"(exports2, module2) {
|
|
35988
|
-
"use strict";
|
|
35989
|
-
var { Writable } = __require("stream");
|
|
35990
|
-
var PerMessageDeflate = require_permessage_deflate();
|
|
35991
|
-
var {
|
|
35992
|
-
BINARY_TYPES,
|
|
35993
|
-
EMPTY_BUFFER,
|
|
35994
|
-
kStatusCode,
|
|
35995
|
-
kWebSocket
|
|
35996
|
-
} = require_constants();
|
|
35997
|
-
var { concat, toArrayBuffer, unmask } = require_buffer_util();
|
|
35998
|
-
var { isValidStatusCode, isValidUTF8 } = require_validation();
|
|
35999
|
-
var FastBuffer = Buffer[Symbol.species];
|
|
36000
|
-
var GET_INFO = 0;
|
|
36001
|
-
var GET_PAYLOAD_LENGTH_16 = 1;
|
|
36002
|
-
var GET_PAYLOAD_LENGTH_64 = 2;
|
|
36003
|
-
var GET_MASK = 3;
|
|
36004
|
-
var GET_DATA = 4;
|
|
36005
|
-
var INFLATING = 5;
|
|
36006
|
-
var DEFER_EVENT = 6;
|
|
36007
|
-
var Receiver2 = class extends Writable {
|
|
36008
|
-
/**
|
|
36009
|
-
* Creates a Receiver instance.
|
|
36010
|
-
*
|
|
36011
|
-
* @param {Object} [options] Options object
|
|
36012
|
-
* @param {Boolean} [options.allowSynchronousEvents=true] Specifies whether
|
|
36013
|
-
* any of the `'message'`, `'ping'`, and `'pong'` events can be emitted
|
|
36014
|
-
* multiple times in the same tick
|
|
36015
|
-
* @param {String} [options.binaryType=nodebuffer] The type for binary data
|
|
36016
|
-
* @param {Object} [options.extensions] An object containing the negotiated
|
|
36017
|
-
* extensions
|
|
36018
|
-
* @param {Boolean} [options.isServer=false] Specifies whether to operate in
|
|
36019
|
-
* client or server mode
|
|
36020
|
-
* @param {Number} [options.maxPayload=0] The maximum allowed message length
|
|
36021
|
-
* @param {Boolean} [options.skipUTF8Validation=false] Specifies whether or
|
|
36022
|
-
* not to skip UTF-8 validation for text and close messages
|
|
36023
|
-
*/
|
|
36024
|
-
constructor(options2 = {}) {
|
|
36025
|
-
super();
|
|
36026
|
-
this._allowSynchronousEvents = options2.allowSynchronousEvents !== void 0 ? options2.allowSynchronousEvents : true;
|
|
36027
|
-
this._binaryType = options2.binaryType || BINARY_TYPES[0];
|
|
36028
|
-
this._extensions = options2.extensions || {};
|
|
36029
|
-
this._isServer = !!options2.isServer;
|
|
36030
|
-
this._maxPayload = options2.maxPayload | 0;
|
|
36031
|
-
this._skipUTF8Validation = !!options2.skipUTF8Validation;
|
|
36032
|
-
this[kWebSocket] = void 0;
|
|
36033
|
-
this._bufferedBytes = 0;
|
|
36034
|
-
this._buffers = [];
|
|
36035
|
-
this._compressed = false;
|
|
36036
|
-
this._payloadLength = 0;
|
|
36037
|
-
this._mask = void 0;
|
|
36038
|
-
this._fragmented = 0;
|
|
36039
|
-
this._masked = false;
|
|
36040
|
-
this._fin = false;
|
|
36041
|
-
this._opcode = 0;
|
|
36042
|
-
this._totalPayloadLength = 0;
|
|
36043
|
-
this._messageLength = 0;
|
|
36044
|
-
this._fragments = [];
|
|
36045
|
-
this._errored = false;
|
|
36046
|
-
this._loop = false;
|
|
36047
|
-
this._state = GET_INFO;
|
|
36048
|
-
}
|
|
36049
|
-
/**
|
|
36050
|
-
* Implements `Writable.prototype._write()`.
|
|
36051
|
-
*
|
|
36052
|
-
* @param {Buffer} chunk The chunk of data to write
|
|
36053
|
-
* @param {String} encoding The character encoding of `chunk`
|
|
36054
|
-
* @param {Function} cb Callback
|
|
36055
|
-
* @private
|
|
36056
|
-
*/
|
|
36057
|
-
_write(chunk, encoding, cb) {
|
|
36058
|
-
if (this._opcode === 8 && this._state == GET_INFO) return cb();
|
|
36059
|
-
this._bufferedBytes += chunk.length;
|
|
36060
|
-
this._buffers.push(chunk);
|
|
36061
|
-
this.startLoop(cb);
|
|
36062
|
-
}
|
|
36063
|
-
/**
|
|
36064
|
-
* Consumes `n` bytes from the buffered data.
|
|
36065
|
-
*
|
|
36066
|
-
* @param {Number} n The number of bytes to consume
|
|
36067
|
-
* @return {Buffer} The consumed bytes
|
|
36068
|
-
* @private
|
|
36069
|
-
*/
|
|
36070
|
-
consume(n) {
|
|
36071
|
-
this._bufferedBytes -= n;
|
|
36072
|
-
if (n === this._buffers[0].length) return this._buffers.shift();
|
|
36073
|
-
if (n < this._buffers[0].length) {
|
|
36074
|
-
const buf = this._buffers[0];
|
|
36075
|
-
this._buffers[0] = new FastBuffer(
|
|
36076
|
-
buf.buffer,
|
|
36077
|
-
buf.byteOffset + n,
|
|
36078
|
-
buf.length - n
|
|
36079
|
-
);
|
|
36080
|
-
return new FastBuffer(buf.buffer, buf.byteOffset, n);
|
|
36081
|
-
}
|
|
36082
|
-
const dst = Buffer.allocUnsafe(n);
|
|
36083
|
-
do {
|
|
36084
|
-
const buf = this._buffers[0];
|
|
36085
|
-
const offset = dst.length - n;
|
|
36086
|
-
if (n >= buf.length) {
|
|
36087
|
-
dst.set(this._buffers.shift(), offset);
|
|
36088
|
-
} else {
|
|
36089
|
-
dst.set(new Uint8Array(buf.buffer, buf.byteOffset, n), offset);
|
|
36090
|
-
this._buffers[0] = new FastBuffer(
|
|
36091
|
-
buf.buffer,
|
|
36092
|
-
buf.byteOffset + n,
|
|
36093
|
-
buf.length - n
|
|
36094
|
-
);
|
|
36095
|
-
}
|
|
36096
|
-
n -= buf.length;
|
|
36097
|
-
} while (n > 0);
|
|
36098
|
-
return dst;
|
|
36099
|
-
}
|
|
36100
|
-
/**
|
|
36101
|
-
* Starts the parsing loop.
|
|
36102
|
-
*
|
|
36103
|
-
* @param {Function} cb Callback
|
|
36104
|
-
* @private
|
|
36105
|
-
*/
|
|
36106
|
-
startLoop(cb) {
|
|
36107
|
-
this._loop = true;
|
|
36108
|
-
do {
|
|
36109
|
-
switch (this._state) {
|
|
36110
|
-
case GET_INFO:
|
|
36111
|
-
this.getInfo(cb);
|
|
36112
|
-
break;
|
|
36113
|
-
case GET_PAYLOAD_LENGTH_16:
|
|
36114
|
-
this.getPayloadLength16(cb);
|
|
36115
|
-
break;
|
|
36116
|
-
case GET_PAYLOAD_LENGTH_64:
|
|
36117
|
-
this.getPayloadLength64(cb);
|
|
36118
|
-
break;
|
|
36119
|
-
case GET_MASK:
|
|
36120
|
-
this.getMask();
|
|
36121
|
-
break;
|
|
36122
|
-
case GET_DATA:
|
|
36123
|
-
this.getData(cb);
|
|
36124
|
-
break;
|
|
36125
|
-
case INFLATING:
|
|
36126
|
-
case DEFER_EVENT:
|
|
36127
|
-
this._loop = false;
|
|
36128
|
-
return;
|
|
36129
|
-
}
|
|
36130
|
-
} while (this._loop);
|
|
36131
|
-
if (!this._errored) cb();
|
|
36132
|
-
}
|
|
36133
|
-
/**
|
|
36134
|
-
* Reads the first two bytes of a frame.
|
|
36135
|
-
*
|
|
36136
|
-
* @param {Function} cb Callback
|
|
36137
|
-
* @private
|
|
36138
|
-
*/
|
|
36139
|
-
getInfo(cb) {
|
|
36140
|
-
if (this._bufferedBytes < 2) {
|
|
36141
|
-
this._loop = false;
|
|
36142
|
-
return;
|
|
36143
|
-
}
|
|
36144
|
-
const buf = this.consume(2);
|
|
36145
|
-
if ((buf[0] & 48) !== 0) {
|
|
36146
|
-
const error48 = this.createError(
|
|
36147
|
-
RangeError,
|
|
36148
|
-
"RSV2 and RSV3 must be clear",
|
|
36149
|
-
true,
|
|
36150
|
-
1002,
|
|
36151
|
-
"WS_ERR_UNEXPECTED_RSV_2_3"
|
|
36152
|
-
);
|
|
36153
|
-
cb(error48);
|
|
36154
|
-
return;
|
|
36155
|
-
}
|
|
36156
|
-
const compressed = (buf[0] & 64) === 64;
|
|
36157
|
-
if (compressed && !this._extensions[PerMessageDeflate.extensionName]) {
|
|
36158
|
-
const error48 = this.createError(
|
|
36159
|
-
RangeError,
|
|
36160
|
-
"RSV1 must be clear",
|
|
36161
|
-
true,
|
|
36162
|
-
1002,
|
|
36163
|
-
"WS_ERR_UNEXPECTED_RSV_1"
|
|
36164
|
-
);
|
|
36165
|
-
cb(error48);
|
|
36166
|
-
return;
|
|
36167
|
-
}
|
|
36168
|
-
this._fin = (buf[0] & 128) === 128;
|
|
36169
|
-
this._opcode = buf[0] & 15;
|
|
36170
|
-
this._payloadLength = buf[1] & 127;
|
|
36171
|
-
if (this._opcode === 0) {
|
|
36172
|
-
if (compressed) {
|
|
36173
|
-
const error48 = this.createError(
|
|
36174
|
-
RangeError,
|
|
36175
|
-
"RSV1 must be clear",
|
|
36176
|
-
true,
|
|
36177
|
-
1002,
|
|
36178
|
-
"WS_ERR_UNEXPECTED_RSV_1"
|
|
36179
|
-
);
|
|
36180
|
-
cb(error48);
|
|
36181
|
-
return;
|
|
36182
|
-
}
|
|
36183
|
-
if (!this._fragmented) {
|
|
36184
|
-
const error48 = this.createError(
|
|
36185
|
-
RangeError,
|
|
36186
|
-
"invalid opcode 0",
|
|
36187
|
-
true,
|
|
36188
|
-
1002,
|
|
36189
|
-
"WS_ERR_INVALID_OPCODE"
|
|
36190
|
-
);
|
|
36191
|
-
cb(error48);
|
|
36192
|
-
return;
|
|
36193
|
-
}
|
|
36194
|
-
this._opcode = this._fragmented;
|
|
36195
|
-
} else if (this._opcode === 1 || this._opcode === 2) {
|
|
36196
|
-
if (this._fragmented) {
|
|
36197
|
-
const error48 = this.createError(
|
|
36198
|
-
RangeError,
|
|
36199
|
-
`invalid opcode ${this._opcode}`,
|
|
36200
|
-
true,
|
|
36201
|
-
1002,
|
|
36202
|
-
"WS_ERR_INVALID_OPCODE"
|
|
36203
|
-
);
|
|
36204
|
-
cb(error48);
|
|
36205
|
-
return;
|
|
36206
|
-
}
|
|
36207
|
-
this._compressed = compressed;
|
|
36208
|
-
} else if (this._opcode > 7 && this._opcode < 11) {
|
|
36209
|
-
if (!this._fin) {
|
|
36210
|
-
const error48 = this.createError(
|
|
36211
|
-
RangeError,
|
|
36212
|
-
"FIN must be set",
|
|
36213
|
-
true,
|
|
36214
|
-
1002,
|
|
36215
|
-
"WS_ERR_EXPECTED_FIN"
|
|
36216
|
-
);
|
|
36217
|
-
cb(error48);
|
|
36218
|
-
return;
|
|
36219
|
-
}
|
|
36220
|
-
if (compressed) {
|
|
36221
|
-
const error48 = this.createError(
|
|
36222
|
-
RangeError,
|
|
36223
|
-
"RSV1 must be clear",
|
|
36224
|
-
true,
|
|
36225
|
-
1002,
|
|
36226
|
-
"WS_ERR_UNEXPECTED_RSV_1"
|
|
36227
|
-
);
|
|
36228
|
-
cb(error48);
|
|
36229
|
-
return;
|
|
36230
|
-
}
|
|
36231
|
-
if (this._payloadLength > 125 || this._opcode === 8 && this._payloadLength === 1) {
|
|
36232
|
-
const error48 = this.createError(
|
|
36233
|
-
RangeError,
|
|
36234
|
-
`invalid payload length ${this._payloadLength}`,
|
|
36235
|
-
true,
|
|
36236
|
-
1002,
|
|
36237
|
-
"WS_ERR_INVALID_CONTROL_PAYLOAD_LENGTH"
|
|
36238
|
-
);
|
|
36239
|
-
cb(error48);
|
|
36240
|
-
return;
|
|
36241
|
-
}
|
|
36242
|
-
} else {
|
|
36243
|
-
const error48 = this.createError(
|
|
36244
|
-
RangeError,
|
|
36245
|
-
`invalid opcode ${this._opcode}`,
|
|
36246
|
-
true,
|
|
36247
|
-
1002,
|
|
36248
|
-
"WS_ERR_INVALID_OPCODE"
|
|
36249
|
-
);
|
|
36250
|
-
cb(error48);
|
|
36251
|
-
return;
|
|
36252
|
-
}
|
|
36253
|
-
if (!this._fin && !this._fragmented) this._fragmented = this._opcode;
|
|
36254
|
-
this._masked = (buf[1] & 128) === 128;
|
|
36255
|
-
if (this._isServer) {
|
|
36256
|
-
if (!this._masked) {
|
|
36257
|
-
const error48 = this.createError(
|
|
36258
|
-
RangeError,
|
|
36259
|
-
"MASK must be set",
|
|
36260
|
-
true,
|
|
36261
|
-
1002,
|
|
36262
|
-
"WS_ERR_EXPECTED_MASK"
|
|
36263
|
-
);
|
|
36264
|
-
cb(error48);
|
|
36265
|
-
return;
|
|
36266
|
-
}
|
|
36267
|
-
} else if (this._masked) {
|
|
36268
|
-
const error48 = this.createError(
|
|
36269
|
-
RangeError,
|
|
36270
|
-
"MASK must be clear",
|
|
36271
|
-
true,
|
|
36272
|
-
1002,
|
|
36273
|
-
"WS_ERR_UNEXPECTED_MASK"
|
|
36274
|
-
);
|
|
36275
|
-
cb(error48);
|
|
36276
|
-
return;
|
|
36277
|
-
}
|
|
36278
|
-
if (this._payloadLength === 126) this._state = GET_PAYLOAD_LENGTH_16;
|
|
36279
|
-
else if (this._payloadLength === 127) this._state = GET_PAYLOAD_LENGTH_64;
|
|
36280
|
-
else this.haveLength(cb);
|
|
36281
|
-
}
|
|
36282
|
-
/**
|
|
36283
|
-
* Gets extended payload length (7+16).
|
|
36284
|
-
*
|
|
36285
|
-
* @param {Function} cb Callback
|
|
36286
|
-
* @private
|
|
36287
|
-
*/
|
|
36288
|
-
getPayloadLength16(cb) {
|
|
36289
|
-
if (this._bufferedBytes < 2) {
|
|
36290
|
-
this._loop = false;
|
|
36291
|
-
return;
|
|
36292
|
-
}
|
|
36293
|
-
this._payloadLength = this.consume(2).readUInt16BE(0);
|
|
36294
|
-
this.haveLength(cb);
|
|
36295
|
-
}
|
|
36296
|
-
/**
|
|
36297
|
-
* Gets extended payload length (7+64).
|
|
36298
|
-
*
|
|
36299
|
-
* @param {Function} cb Callback
|
|
36300
|
-
* @private
|
|
36301
|
-
*/
|
|
36302
|
-
getPayloadLength64(cb) {
|
|
36303
|
-
if (this._bufferedBytes < 8) {
|
|
36304
|
-
this._loop = false;
|
|
36305
|
-
return;
|
|
36306
|
-
}
|
|
36307
|
-
const buf = this.consume(8);
|
|
36308
|
-
const num = buf.readUInt32BE(0);
|
|
36309
|
-
if (num > Math.pow(2, 53 - 32) - 1) {
|
|
36310
|
-
const error48 = this.createError(
|
|
36311
|
-
RangeError,
|
|
36312
|
-
"Unsupported WebSocket frame: payload length > 2^53 - 1",
|
|
36313
|
-
false,
|
|
36314
|
-
1009,
|
|
36315
|
-
"WS_ERR_UNSUPPORTED_DATA_PAYLOAD_LENGTH"
|
|
36316
|
-
);
|
|
36317
|
-
cb(error48);
|
|
36318
|
-
return;
|
|
36319
|
-
}
|
|
36320
|
-
this._payloadLength = num * Math.pow(2, 32) + buf.readUInt32BE(4);
|
|
36321
|
-
this.haveLength(cb);
|
|
36322
|
-
}
|
|
36323
|
-
/**
|
|
36324
|
-
* Payload length has been read.
|
|
36325
|
-
*
|
|
36326
|
-
* @param {Function} cb Callback
|
|
36327
|
-
* @private
|
|
36328
|
-
*/
|
|
36329
|
-
haveLength(cb) {
|
|
36330
|
-
if (this._payloadLength && this._opcode < 8) {
|
|
36331
|
-
this._totalPayloadLength += this._payloadLength;
|
|
36332
|
-
if (this._totalPayloadLength > this._maxPayload && this._maxPayload > 0) {
|
|
36333
|
-
const error48 = this.createError(
|
|
36334
|
-
RangeError,
|
|
36335
|
-
"Max payload size exceeded",
|
|
36336
|
-
false,
|
|
36337
|
-
1009,
|
|
36338
|
-
"WS_ERR_UNSUPPORTED_MESSAGE_LENGTH"
|
|
36339
|
-
);
|
|
36340
|
-
cb(error48);
|
|
36341
|
-
return;
|
|
36342
|
-
}
|
|
36343
|
-
}
|
|
36344
|
-
if (this._masked) this._state = GET_MASK;
|
|
36345
|
-
else this._state = GET_DATA;
|
|
36346
|
-
}
|
|
36347
|
-
/**
|
|
36348
|
-
* Reads mask bytes.
|
|
36349
|
-
*
|
|
36350
|
-
* @private
|
|
36351
|
-
*/
|
|
36352
|
-
getMask() {
|
|
36353
|
-
if (this._bufferedBytes < 4) {
|
|
36354
|
-
this._loop = false;
|
|
36355
|
-
return;
|
|
36356
|
-
}
|
|
36357
|
-
this._mask = this.consume(4);
|
|
36358
|
-
this._state = GET_DATA;
|
|
36359
|
-
}
|
|
36360
|
-
/**
|
|
36361
|
-
* Reads data bytes.
|
|
36362
|
-
*
|
|
36363
|
-
* @param {Function} cb Callback
|
|
36364
|
-
* @private
|
|
36365
|
-
*/
|
|
36366
|
-
getData(cb) {
|
|
36367
|
-
let data = EMPTY_BUFFER;
|
|
36368
|
-
if (this._payloadLength) {
|
|
36369
|
-
if (this._bufferedBytes < this._payloadLength) {
|
|
36370
|
-
this._loop = false;
|
|
36371
|
-
return;
|
|
36372
|
-
}
|
|
36373
|
-
data = this.consume(this._payloadLength);
|
|
36374
|
-
if (this._masked && (this._mask[0] | this._mask[1] | this._mask[2] | this._mask[3]) !== 0) {
|
|
36375
|
-
unmask(data, this._mask);
|
|
36376
|
-
}
|
|
36377
|
-
}
|
|
36378
|
-
if (this._opcode > 7) {
|
|
36379
|
-
this.controlMessage(data, cb);
|
|
36380
|
-
return;
|
|
36381
|
-
}
|
|
36382
|
-
if (this._compressed) {
|
|
36383
|
-
this._state = INFLATING;
|
|
36384
|
-
this.decompress(data, cb);
|
|
36385
|
-
return;
|
|
36386
|
-
}
|
|
36387
|
-
if (data.length) {
|
|
36388
|
-
this._messageLength = this._totalPayloadLength;
|
|
36389
|
-
this._fragments.push(data);
|
|
36390
|
-
}
|
|
36391
|
-
this.dataMessage(cb);
|
|
36392
|
-
}
|
|
36393
|
-
/**
|
|
36394
|
-
* Decompresses data.
|
|
36395
|
-
*
|
|
36396
|
-
* @param {Buffer} data Compressed data
|
|
36397
|
-
* @param {Function} cb Callback
|
|
36398
|
-
* @private
|
|
36399
|
-
*/
|
|
36400
|
-
decompress(data, cb) {
|
|
36401
|
-
const perMessageDeflate = this._extensions[PerMessageDeflate.extensionName];
|
|
36402
|
-
perMessageDeflate.decompress(data, this._fin, (err, buf) => {
|
|
36403
|
-
if (err) return cb(err);
|
|
36404
|
-
if (buf.length) {
|
|
36405
|
-
this._messageLength += buf.length;
|
|
36406
|
-
if (this._messageLength > this._maxPayload && this._maxPayload > 0) {
|
|
36407
|
-
const error48 = this.createError(
|
|
36408
|
-
RangeError,
|
|
36409
|
-
"Max payload size exceeded",
|
|
36410
|
-
false,
|
|
36411
|
-
1009,
|
|
36412
|
-
"WS_ERR_UNSUPPORTED_MESSAGE_LENGTH"
|
|
36413
|
-
);
|
|
36414
|
-
cb(error48);
|
|
36415
|
-
return;
|
|
36416
|
-
}
|
|
36417
|
-
this._fragments.push(buf);
|
|
36418
|
-
}
|
|
36419
|
-
this.dataMessage(cb);
|
|
36420
|
-
if (this._state === GET_INFO) this.startLoop(cb);
|
|
36421
|
-
});
|
|
36422
|
-
}
|
|
36423
|
-
/**
|
|
36424
|
-
* Handles a data message.
|
|
36425
|
-
*
|
|
36426
|
-
* @param {Function} cb Callback
|
|
36427
|
-
* @private
|
|
36428
|
-
*/
|
|
36429
|
-
dataMessage(cb) {
|
|
36430
|
-
if (!this._fin) {
|
|
36431
|
-
this._state = GET_INFO;
|
|
36432
|
-
return;
|
|
36433
|
-
}
|
|
36434
|
-
const messageLength = this._messageLength;
|
|
36435
|
-
const fragments = this._fragments;
|
|
36436
|
-
this._totalPayloadLength = 0;
|
|
36437
|
-
this._messageLength = 0;
|
|
36438
|
-
this._fragmented = 0;
|
|
36439
|
-
this._fragments = [];
|
|
36440
|
-
if (this._opcode === 2) {
|
|
36441
|
-
let data;
|
|
36442
|
-
if (this._binaryType === "nodebuffer") {
|
|
36443
|
-
data = concat(fragments, messageLength);
|
|
36444
|
-
} else if (this._binaryType === "arraybuffer") {
|
|
36445
|
-
data = toArrayBuffer(concat(fragments, messageLength));
|
|
36446
|
-
} else if (this._binaryType === "blob") {
|
|
36447
|
-
data = new Blob(fragments);
|
|
36448
|
-
} else {
|
|
36449
|
-
data = fragments;
|
|
36450
|
-
}
|
|
36451
|
-
if (this._allowSynchronousEvents) {
|
|
36452
|
-
this.emit("message", data, true);
|
|
36453
|
-
this._state = GET_INFO;
|
|
36454
|
-
} else {
|
|
36455
|
-
this._state = DEFER_EVENT;
|
|
36456
|
-
setImmediate(() => {
|
|
36457
|
-
this.emit("message", data, true);
|
|
36458
|
-
this._state = GET_INFO;
|
|
36459
|
-
this.startLoop(cb);
|
|
36460
|
-
});
|
|
36461
|
-
}
|
|
36462
|
-
} else {
|
|
36463
|
-
const buf = concat(fragments, messageLength);
|
|
36464
|
-
if (!this._skipUTF8Validation && !isValidUTF8(buf)) {
|
|
36465
|
-
const error48 = this.createError(
|
|
36466
|
-
Error,
|
|
36467
|
-
"invalid UTF-8 sequence",
|
|
36468
|
-
true,
|
|
36469
|
-
1007,
|
|
36470
|
-
"WS_ERR_INVALID_UTF8"
|
|
36471
|
-
);
|
|
36472
|
-
cb(error48);
|
|
36473
|
-
return;
|
|
36474
|
-
}
|
|
36475
|
-
if (this._state === INFLATING || this._allowSynchronousEvents) {
|
|
36476
|
-
this.emit("message", buf, false);
|
|
36477
|
-
this._state = GET_INFO;
|
|
36478
|
-
} else {
|
|
36479
|
-
this._state = DEFER_EVENT;
|
|
36480
|
-
setImmediate(() => {
|
|
36481
|
-
this.emit("message", buf, false);
|
|
36482
|
-
this._state = GET_INFO;
|
|
36483
|
-
this.startLoop(cb);
|
|
36484
|
-
});
|
|
36485
|
-
}
|
|
36486
|
-
}
|
|
36487
|
-
}
|
|
36488
|
-
/**
|
|
36489
|
-
* Handles a control message.
|
|
36490
|
-
*
|
|
36491
|
-
* @param {Buffer} data Data to handle
|
|
36492
|
-
* @return {(Error|RangeError|undefined)} A possible error
|
|
36493
|
-
* @private
|
|
36494
|
-
*/
|
|
36495
|
-
controlMessage(data, cb) {
|
|
36496
|
-
if (this._opcode === 8) {
|
|
36497
|
-
if (data.length === 0) {
|
|
36498
|
-
this._loop = false;
|
|
36499
|
-
this.emit("conclude", 1005, EMPTY_BUFFER);
|
|
36500
|
-
this.end();
|
|
36501
|
-
} else {
|
|
36502
|
-
const code = data.readUInt16BE(0);
|
|
36503
|
-
if (!isValidStatusCode(code)) {
|
|
36504
|
-
const error48 = this.createError(
|
|
36505
|
-
RangeError,
|
|
36506
|
-
`invalid status code ${code}`,
|
|
36507
|
-
true,
|
|
36508
|
-
1002,
|
|
36509
|
-
"WS_ERR_INVALID_CLOSE_CODE"
|
|
36510
|
-
);
|
|
36511
|
-
cb(error48);
|
|
36512
|
-
return;
|
|
36513
|
-
}
|
|
36514
|
-
const buf = new FastBuffer(
|
|
36515
|
-
data.buffer,
|
|
36516
|
-
data.byteOffset + 2,
|
|
36517
|
-
data.length - 2
|
|
36518
|
-
);
|
|
36519
|
-
if (!this._skipUTF8Validation && !isValidUTF8(buf)) {
|
|
36520
|
-
const error48 = this.createError(
|
|
36521
|
-
Error,
|
|
36522
|
-
"invalid UTF-8 sequence",
|
|
36523
|
-
true,
|
|
36524
|
-
1007,
|
|
36525
|
-
"WS_ERR_INVALID_UTF8"
|
|
36526
|
-
);
|
|
36527
|
-
cb(error48);
|
|
36528
|
-
return;
|
|
36529
|
-
}
|
|
36530
|
-
this._loop = false;
|
|
36531
|
-
this.emit("conclude", code, buf);
|
|
36532
|
-
this.end();
|
|
36533
|
-
}
|
|
36534
|
-
this._state = GET_INFO;
|
|
36535
|
-
return;
|
|
36536
|
-
}
|
|
36537
|
-
if (this._allowSynchronousEvents) {
|
|
36538
|
-
this.emit(this._opcode === 9 ? "ping" : "pong", data);
|
|
36539
|
-
this._state = GET_INFO;
|
|
36540
|
-
} else {
|
|
36541
|
-
this._state = DEFER_EVENT;
|
|
36542
|
-
setImmediate(() => {
|
|
36543
|
-
this.emit(this._opcode === 9 ? "ping" : "pong", data);
|
|
36544
|
-
this._state = GET_INFO;
|
|
36545
|
-
this.startLoop(cb);
|
|
36546
|
-
});
|
|
36547
|
-
}
|
|
36548
|
-
}
|
|
36549
|
-
/**
|
|
36550
|
-
* Builds an error object.
|
|
36551
|
-
*
|
|
36552
|
-
* @param {function(new:Error|RangeError)} ErrorCtor The error constructor
|
|
36553
|
-
* @param {String} message The error message
|
|
36554
|
-
* @param {Boolean} prefix Specifies whether or not to add a default prefix to
|
|
36555
|
-
* `message`
|
|
36556
|
-
* @param {Number} statusCode The status code
|
|
36557
|
-
* @param {String} errorCode The exposed error code
|
|
36558
|
-
* @return {(Error|RangeError)} The error
|
|
36559
|
-
* @private
|
|
36560
|
-
*/
|
|
36561
|
-
createError(ErrorCtor, message, prefix, statusCode, errorCode) {
|
|
36562
|
-
this._loop = false;
|
|
36563
|
-
this._errored = true;
|
|
36564
|
-
const err = new ErrorCtor(
|
|
36565
|
-
prefix ? `Invalid WebSocket frame: ${message}` : message
|
|
36566
|
-
);
|
|
36567
|
-
Error.captureStackTrace(err, this.createError);
|
|
36568
|
-
err.code = errorCode;
|
|
36569
|
-
err[kStatusCode] = statusCode;
|
|
36570
|
-
return err;
|
|
36571
|
-
}
|
|
36572
|
-
};
|
|
36573
|
-
module2.exports = Receiver2;
|
|
36574
|
-
}
|
|
36575
|
-
});
|
|
36576
|
-
|
|
36577
|
-
// node_modules/ws/lib/sender.js
|
|
36578
|
-
var require_sender = __commonJS({
|
|
36579
|
-
"node_modules/ws/lib/sender.js"(exports2, module2) {
|
|
36580
|
-
"use strict";
|
|
36581
|
-
var { Duplex } = __require("stream");
|
|
36582
|
-
var { randomFillSync } = __require("crypto");
|
|
36583
|
-
var PerMessageDeflate = require_permessage_deflate();
|
|
36584
|
-
var { EMPTY_BUFFER, kWebSocket, NOOP } = require_constants();
|
|
36585
|
-
var { isBlob, isValidStatusCode } = require_validation();
|
|
36586
|
-
var { mask: applyMask, toBuffer } = require_buffer_util();
|
|
36587
|
-
var kByteLength = /* @__PURE__ */ Symbol("kByteLength");
|
|
36588
|
-
var maskBuffer = Buffer.alloc(4);
|
|
36589
|
-
var RANDOM_POOL_SIZE = 8 * 1024;
|
|
36590
|
-
var randomPool;
|
|
36591
|
-
var randomPoolPointer = RANDOM_POOL_SIZE;
|
|
36592
|
-
var DEFAULT = 0;
|
|
36593
|
-
var DEFLATING = 1;
|
|
36594
|
-
var GET_BLOB_DATA = 2;
|
|
36595
|
-
var Sender2 = class _Sender {
|
|
36596
|
-
/**
|
|
36597
|
-
* Creates a Sender instance.
|
|
36598
|
-
*
|
|
36599
|
-
* @param {Duplex} socket The connection socket
|
|
36600
|
-
* @param {Object} [extensions] An object containing the negotiated extensions
|
|
36601
|
-
* @param {Function} [generateMask] The function used to generate the masking
|
|
36602
|
-
* key
|
|
36603
|
-
*/
|
|
36604
|
-
constructor(socket, extensions, generateMask) {
|
|
36605
|
-
this._extensions = extensions || {};
|
|
36606
|
-
if (generateMask) {
|
|
36607
|
-
this._generateMask = generateMask;
|
|
36608
|
-
this._maskBuffer = Buffer.alloc(4);
|
|
36609
|
-
}
|
|
36610
|
-
this._socket = socket;
|
|
36611
|
-
this._firstFragment = true;
|
|
36612
|
-
this._compress = false;
|
|
36613
|
-
this._bufferedBytes = 0;
|
|
36614
|
-
this._queue = [];
|
|
36615
|
-
this._state = DEFAULT;
|
|
36616
|
-
this.onerror = NOOP;
|
|
36617
|
-
this[kWebSocket] = void 0;
|
|
36618
|
-
}
|
|
36619
|
-
/**
|
|
36620
|
-
* Frames a piece of data according to the HyBi WebSocket protocol.
|
|
36621
|
-
*
|
|
36622
|
-
* @param {(Buffer|String)} data The data to frame
|
|
36623
|
-
* @param {Object} options Options object
|
|
36624
|
-
* @param {Boolean} [options.fin=false] Specifies whether or not to set the
|
|
36625
|
-
* FIN bit
|
|
36626
|
-
* @param {Function} [options.generateMask] The function used to generate the
|
|
36627
|
-
* masking key
|
|
36628
|
-
* @param {Boolean} [options.mask=false] Specifies whether or not to mask
|
|
36629
|
-
* `data`
|
|
36630
|
-
* @param {Buffer} [options.maskBuffer] The buffer used to store the masking
|
|
36631
|
-
* key
|
|
36632
|
-
* @param {Number} options.opcode The opcode
|
|
36633
|
-
* @param {Boolean} [options.readOnly=false] Specifies whether `data` can be
|
|
36634
|
-
* modified
|
|
36635
|
-
* @param {Boolean} [options.rsv1=false] Specifies whether or not to set the
|
|
36636
|
-
* RSV1 bit
|
|
36637
|
-
* @return {(Buffer|String)[]} The framed data
|
|
36638
|
-
* @public
|
|
36639
|
-
*/
|
|
36640
|
-
static frame(data, options2) {
|
|
36641
|
-
let mask;
|
|
36642
|
-
let merge2 = false;
|
|
36643
|
-
let offset = 2;
|
|
36644
|
-
let skipMasking = false;
|
|
36645
|
-
if (options2.mask) {
|
|
36646
|
-
mask = options2.maskBuffer || maskBuffer;
|
|
36647
|
-
if (options2.generateMask) {
|
|
36648
|
-
options2.generateMask(mask);
|
|
36649
|
-
} else {
|
|
36650
|
-
if (randomPoolPointer === RANDOM_POOL_SIZE) {
|
|
36651
|
-
if (randomPool === void 0) {
|
|
36652
|
-
randomPool = Buffer.alloc(RANDOM_POOL_SIZE);
|
|
36653
|
-
}
|
|
36654
|
-
randomFillSync(randomPool, 0, RANDOM_POOL_SIZE);
|
|
36655
|
-
randomPoolPointer = 0;
|
|
36656
|
-
}
|
|
36657
|
-
mask[0] = randomPool[randomPoolPointer++];
|
|
36658
|
-
mask[1] = randomPool[randomPoolPointer++];
|
|
36659
|
-
mask[2] = randomPool[randomPoolPointer++];
|
|
36660
|
-
mask[3] = randomPool[randomPoolPointer++];
|
|
36661
|
-
}
|
|
36662
|
-
skipMasking = (mask[0] | mask[1] | mask[2] | mask[3]) === 0;
|
|
36663
|
-
offset = 6;
|
|
36664
|
-
}
|
|
36665
|
-
let dataLength;
|
|
36666
|
-
if (typeof data === "string") {
|
|
36667
|
-
if ((!options2.mask || skipMasking) && options2[kByteLength] !== void 0) {
|
|
36668
|
-
dataLength = options2[kByteLength];
|
|
36669
|
-
} else {
|
|
36670
|
-
data = Buffer.from(data);
|
|
36671
|
-
dataLength = data.length;
|
|
36672
|
-
}
|
|
36673
|
-
} else {
|
|
36674
|
-
dataLength = data.length;
|
|
36675
|
-
merge2 = options2.mask && options2.readOnly && !skipMasking;
|
|
36676
|
-
}
|
|
36677
|
-
let payloadLength = dataLength;
|
|
36678
|
-
if (dataLength >= 65536) {
|
|
36679
|
-
offset += 8;
|
|
36680
|
-
payloadLength = 127;
|
|
36681
|
-
} else if (dataLength > 125) {
|
|
36682
|
-
offset += 2;
|
|
36683
|
-
payloadLength = 126;
|
|
36684
|
-
}
|
|
36685
|
-
const target = Buffer.allocUnsafe(merge2 ? dataLength + offset : offset);
|
|
36686
|
-
target[0] = options2.fin ? options2.opcode | 128 : options2.opcode;
|
|
36687
|
-
if (options2.rsv1) target[0] |= 64;
|
|
36688
|
-
target[1] = payloadLength;
|
|
36689
|
-
if (payloadLength === 126) {
|
|
36690
|
-
target.writeUInt16BE(dataLength, 2);
|
|
36691
|
-
} else if (payloadLength === 127) {
|
|
36692
|
-
target[2] = target[3] = 0;
|
|
36693
|
-
target.writeUIntBE(dataLength, 4, 6);
|
|
36694
|
-
}
|
|
36695
|
-
if (!options2.mask) return [target, data];
|
|
36696
|
-
target[1] |= 128;
|
|
36697
|
-
target[offset - 4] = mask[0];
|
|
36698
|
-
target[offset - 3] = mask[1];
|
|
36699
|
-
target[offset - 2] = mask[2];
|
|
36700
|
-
target[offset - 1] = mask[3];
|
|
36701
|
-
if (skipMasking) return [target, data];
|
|
36702
|
-
if (merge2) {
|
|
36703
|
-
applyMask(data, mask, target, offset, dataLength);
|
|
36704
|
-
return [target];
|
|
36705
|
-
}
|
|
36706
|
-
applyMask(data, mask, data, 0, dataLength);
|
|
36707
|
-
return [target, data];
|
|
36708
|
-
}
|
|
36709
|
-
/**
|
|
36710
|
-
* Sends a close message to the other peer.
|
|
36711
|
-
*
|
|
36712
|
-
* @param {Number} [code] The status code component of the body
|
|
36713
|
-
* @param {(String|Buffer)} [data] The message component of the body
|
|
36714
|
-
* @param {Boolean} [mask=false] Specifies whether or not to mask the message
|
|
36715
|
-
* @param {Function} [cb] Callback
|
|
36716
|
-
* @public
|
|
36717
|
-
*/
|
|
36718
|
-
close(code, data, mask, cb) {
|
|
36719
|
-
let buf;
|
|
36720
|
-
if (code === void 0) {
|
|
36721
|
-
buf = EMPTY_BUFFER;
|
|
36722
|
-
} else if (typeof code !== "number" || !isValidStatusCode(code)) {
|
|
36723
|
-
throw new TypeError("First argument must be a valid error code number");
|
|
36724
|
-
} else if (data === void 0 || !data.length) {
|
|
36725
|
-
buf = Buffer.allocUnsafe(2);
|
|
36726
|
-
buf.writeUInt16BE(code, 0);
|
|
36727
|
-
} else {
|
|
36728
|
-
const length = Buffer.byteLength(data);
|
|
36729
|
-
if (length > 123) {
|
|
36730
|
-
throw new RangeError("The message must not be greater than 123 bytes");
|
|
36731
|
-
}
|
|
36732
|
-
buf = Buffer.allocUnsafe(2 + length);
|
|
36733
|
-
buf.writeUInt16BE(code, 0);
|
|
36734
|
-
if (typeof data === "string") {
|
|
36735
|
-
buf.write(data, 2);
|
|
36736
|
-
} else {
|
|
36737
|
-
buf.set(data, 2);
|
|
36738
|
-
}
|
|
36739
|
-
}
|
|
36740
|
-
const options2 = {
|
|
36741
|
-
[kByteLength]: buf.length,
|
|
36742
|
-
fin: true,
|
|
36743
|
-
generateMask: this._generateMask,
|
|
36744
|
-
mask,
|
|
36745
|
-
maskBuffer: this._maskBuffer,
|
|
36746
|
-
opcode: 8,
|
|
36747
|
-
readOnly: false,
|
|
36748
|
-
rsv1: false
|
|
36749
|
-
};
|
|
36750
|
-
if (this._state !== DEFAULT) {
|
|
36751
|
-
this.enqueue([this.dispatch, buf, false, options2, cb]);
|
|
36752
|
-
} else {
|
|
36753
|
-
this.sendFrame(_Sender.frame(buf, options2), cb);
|
|
36754
|
-
}
|
|
36755
|
-
}
|
|
36756
|
-
/**
|
|
36757
|
-
* Sends a ping message to the other peer.
|
|
36758
|
-
*
|
|
36759
|
-
* @param {*} data The message to send
|
|
36760
|
-
* @param {Boolean} [mask=false] Specifies whether or not to mask `data`
|
|
36761
|
-
* @param {Function} [cb] Callback
|
|
36762
|
-
* @public
|
|
36763
|
-
*/
|
|
36764
|
-
ping(data, mask, cb) {
|
|
36765
|
-
let byteLength;
|
|
36766
|
-
let readOnly;
|
|
36767
|
-
if (typeof data === "string") {
|
|
36768
|
-
byteLength = Buffer.byteLength(data);
|
|
36769
|
-
readOnly = false;
|
|
36770
|
-
} else if (isBlob(data)) {
|
|
36771
|
-
byteLength = data.size;
|
|
36772
|
-
readOnly = false;
|
|
36773
|
-
} else {
|
|
36774
|
-
data = toBuffer(data);
|
|
36775
|
-
byteLength = data.length;
|
|
36776
|
-
readOnly = toBuffer.readOnly;
|
|
36777
|
-
}
|
|
36778
|
-
if (byteLength > 125) {
|
|
36779
|
-
throw new RangeError("The data size must not be greater than 125 bytes");
|
|
36780
|
-
}
|
|
36781
|
-
const options2 = {
|
|
36782
|
-
[kByteLength]: byteLength,
|
|
36783
|
-
fin: true,
|
|
36784
|
-
generateMask: this._generateMask,
|
|
36785
|
-
mask,
|
|
36786
|
-
maskBuffer: this._maskBuffer,
|
|
36787
|
-
opcode: 9,
|
|
36788
|
-
readOnly,
|
|
36789
|
-
rsv1: false
|
|
36790
|
-
};
|
|
36791
|
-
if (isBlob(data)) {
|
|
36792
|
-
if (this._state !== DEFAULT) {
|
|
36793
|
-
this.enqueue([this.getBlobData, data, false, options2, cb]);
|
|
36794
|
-
} else {
|
|
36795
|
-
this.getBlobData(data, false, options2, cb);
|
|
36796
|
-
}
|
|
36797
|
-
} else if (this._state !== DEFAULT) {
|
|
36798
|
-
this.enqueue([this.dispatch, data, false, options2, cb]);
|
|
36799
|
-
} else {
|
|
36800
|
-
this.sendFrame(_Sender.frame(data, options2), cb);
|
|
36801
|
-
}
|
|
36802
|
-
}
|
|
36803
|
-
/**
|
|
36804
|
-
* Sends a pong message to the other peer.
|
|
36805
|
-
*
|
|
36806
|
-
* @param {*} data The message to send
|
|
36807
|
-
* @param {Boolean} [mask=false] Specifies whether or not to mask `data`
|
|
36808
|
-
* @param {Function} [cb] Callback
|
|
36809
|
-
* @public
|
|
36810
|
-
*/
|
|
36811
|
-
pong(data, mask, cb) {
|
|
36812
|
-
let byteLength;
|
|
36813
|
-
let readOnly;
|
|
36814
|
-
if (typeof data === "string") {
|
|
36815
|
-
byteLength = Buffer.byteLength(data);
|
|
36816
|
-
readOnly = false;
|
|
36817
|
-
} else if (isBlob(data)) {
|
|
36818
|
-
byteLength = data.size;
|
|
36819
|
-
readOnly = false;
|
|
36820
|
-
} else {
|
|
36821
|
-
data = toBuffer(data);
|
|
36822
|
-
byteLength = data.length;
|
|
36823
|
-
readOnly = toBuffer.readOnly;
|
|
36824
|
-
}
|
|
36825
|
-
if (byteLength > 125) {
|
|
36826
|
-
throw new RangeError("The data size must not be greater than 125 bytes");
|
|
36827
|
-
}
|
|
36828
|
-
const options2 = {
|
|
36829
|
-
[kByteLength]: byteLength,
|
|
36830
|
-
fin: true,
|
|
36831
|
-
generateMask: this._generateMask,
|
|
36832
|
-
mask,
|
|
36833
|
-
maskBuffer: this._maskBuffer,
|
|
36834
|
-
opcode: 10,
|
|
36835
|
-
readOnly,
|
|
36836
|
-
rsv1: false
|
|
36837
|
-
};
|
|
36838
|
-
if (isBlob(data)) {
|
|
36839
|
-
if (this._state !== DEFAULT) {
|
|
36840
|
-
this.enqueue([this.getBlobData, data, false, options2, cb]);
|
|
36841
|
-
} else {
|
|
36842
|
-
this.getBlobData(data, false, options2, cb);
|
|
36843
|
-
}
|
|
36844
|
-
} else if (this._state !== DEFAULT) {
|
|
36845
|
-
this.enqueue([this.dispatch, data, false, options2, cb]);
|
|
36846
|
-
} else {
|
|
36847
|
-
this.sendFrame(_Sender.frame(data, options2), cb);
|
|
36848
|
-
}
|
|
36849
|
-
}
|
|
36850
|
-
/**
|
|
36851
|
-
* Sends a data message to the other peer.
|
|
36852
|
-
*
|
|
36853
|
-
* @param {*} data The message to send
|
|
36854
|
-
* @param {Object} options Options object
|
|
36855
|
-
* @param {Boolean} [options.binary=false] Specifies whether `data` is binary
|
|
36856
|
-
* or text
|
|
36857
|
-
* @param {Boolean} [options.compress=false] Specifies whether or not to
|
|
36858
|
-
* compress `data`
|
|
36859
|
-
* @param {Boolean} [options.fin=false] Specifies whether the fragment is the
|
|
36860
|
-
* last one
|
|
36861
|
-
* @param {Boolean} [options.mask=false] Specifies whether or not to mask
|
|
36862
|
-
* `data`
|
|
36863
|
-
* @param {Function} [cb] Callback
|
|
36864
|
-
* @public
|
|
36865
|
-
*/
|
|
36866
|
-
send(data, options2, cb) {
|
|
36867
|
-
const perMessageDeflate = this._extensions[PerMessageDeflate.extensionName];
|
|
36868
|
-
let opcode = options2.binary ? 2 : 1;
|
|
36869
|
-
let rsv1 = options2.compress;
|
|
36870
|
-
let byteLength;
|
|
36871
|
-
let readOnly;
|
|
36872
|
-
if (typeof data === "string") {
|
|
36873
|
-
byteLength = Buffer.byteLength(data);
|
|
36874
|
-
readOnly = false;
|
|
36875
|
-
} else if (isBlob(data)) {
|
|
36876
|
-
byteLength = data.size;
|
|
36877
|
-
readOnly = false;
|
|
36878
|
-
} else {
|
|
36879
|
-
data = toBuffer(data);
|
|
36880
|
-
byteLength = data.length;
|
|
36881
|
-
readOnly = toBuffer.readOnly;
|
|
36882
|
-
}
|
|
36883
|
-
if (this._firstFragment) {
|
|
36884
|
-
this._firstFragment = false;
|
|
36885
|
-
if (rsv1 && perMessageDeflate && perMessageDeflate.params[perMessageDeflate._isServer ? "server_no_context_takeover" : "client_no_context_takeover"]) {
|
|
36886
|
-
rsv1 = byteLength >= perMessageDeflate._threshold;
|
|
36887
|
-
}
|
|
36888
|
-
this._compress = rsv1;
|
|
36889
|
-
} else {
|
|
36890
|
-
rsv1 = false;
|
|
36891
|
-
opcode = 0;
|
|
36892
|
-
}
|
|
36893
|
-
if (options2.fin) this._firstFragment = true;
|
|
36894
|
-
const opts = {
|
|
36895
|
-
[kByteLength]: byteLength,
|
|
36896
|
-
fin: options2.fin,
|
|
36897
|
-
generateMask: this._generateMask,
|
|
36898
|
-
mask: options2.mask,
|
|
36899
|
-
maskBuffer: this._maskBuffer,
|
|
36900
|
-
opcode,
|
|
36901
|
-
readOnly,
|
|
36902
|
-
rsv1
|
|
36903
|
-
};
|
|
36904
|
-
if (isBlob(data)) {
|
|
36905
|
-
if (this._state !== DEFAULT) {
|
|
36906
|
-
this.enqueue([this.getBlobData, data, this._compress, opts, cb]);
|
|
36907
|
-
} else {
|
|
36908
|
-
this.getBlobData(data, this._compress, opts, cb);
|
|
36909
|
-
}
|
|
36910
|
-
} else if (this._state !== DEFAULT) {
|
|
36911
|
-
this.enqueue([this.dispatch, data, this._compress, opts, cb]);
|
|
36912
|
-
} else {
|
|
36913
|
-
this.dispatch(data, this._compress, opts, cb);
|
|
36914
|
-
}
|
|
36915
|
-
}
|
|
36916
|
-
/**
|
|
36917
|
-
* Gets the contents of a blob as binary data.
|
|
36918
|
-
*
|
|
36919
|
-
* @param {Blob} blob The blob
|
|
36920
|
-
* @param {Boolean} [compress=false] Specifies whether or not to compress
|
|
36921
|
-
* the data
|
|
36922
|
-
* @param {Object} options Options object
|
|
36923
|
-
* @param {Boolean} [options.fin=false] Specifies whether or not to set the
|
|
36924
|
-
* FIN bit
|
|
36925
|
-
* @param {Function} [options.generateMask] The function used to generate the
|
|
36926
|
-
* masking key
|
|
36927
|
-
* @param {Boolean} [options.mask=false] Specifies whether or not to mask
|
|
36928
|
-
* `data`
|
|
36929
|
-
* @param {Buffer} [options.maskBuffer] The buffer used to store the masking
|
|
36930
|
-
* key
|
|
36931
|
-
* @param {Number} options.opcode The opcode
|
|
36932
|
-
* @param {Boolean} [options.readOnly=false] Specifies whether `data` can be
|
|
36933
|
-
* modified
|
|
36934
|
-
* @param {Boolean} [options.rsv1=false] Specifies whether or not to set the
|
|
36935
|
-
* RSV1 bit
|
|
36936
|
-
* @param {Function} [cb] Callback
|
|
36937
|
-
* @private
|
|
36938
|
-
*/
|
|
36939
|
-
getBlobData(blob, compress, options2, cb) {
|
|
36940
|
-
this._bufferedBytes += options2[kByteLength];
|
|
36941
|
-
this._state = GET_BLOB_DATA;
|
|
36942
|
-
blob.arrayBuffer().then((arrayBuffer) => {
|
|
36943
|
-
if (this._socket.destroyed) {
|
|
36944
|
-
const err = new Error(
|
|
36945
|
-
"The socket was closed while the blob was being read"
|
|
36946
|
-
);
|
|
36947
|
-
process.nextTick(callCallbacks, this, err, cb);
|
|
36948
|
-
return;
|
|
36949
|
-
}
|
|
36950
|
-
this._bufferedBytes -= options2[kByteLength];
|
|
36951
|
-
const data = toBuffer(arrayBuffer);
|
|
36952
|
-
if (!compress) {
|
|
36953
|
-
this._state = DEFAULT;
|
|
36954
|
-
this.sendFrame(_Sender.frame(data, options2), cb);
|
|
36955
|
-
this.dequeue();
|
|
36956
|
-
} else {
|
|
36957
|
-
this.dispatch(data, compress, options2, cb);
|
|
36958
|
-
}
|
|
36959
|
-
}).catch((err) => {
|
|
36960
|
-
process.nextTick(onError, this, err, cb);
|
|
36961
|
-
});
|
|
36962
|
-
}
|
|
36963
|
-
/**
|
|
36964
|
-
* Dispatches a message.
|
|
36965
|
-
*
|
|
36966
|
-
* @param {(Buffer|String)} data The message to send
|
|
36967
|
-
* @param {Boolean} [compress=false] Specifies whether or not to compress
|
|
36968
|
-
* `data`
|
|
36969
|
-
* @param {Object} options Options object
|
|
36970
|
-
* @param {Boolean} [options.fin=false] Specifies whether or not to set the
|
|
36971
|
-
* FIN bit
|
|
36972
|
-
* @param {Function} [options.generateMask] The function used to generate the
|
|
36973
|
-
* masking key
|
|
36974
|
-
* @param {Boolean} [options.mask=false] Specifies whether or not to mask
|
|
36975
|
-
* `data`
|
|
36976
|
-
* @param {Buffer} [options.maskBuffer] The buffer used to store the masking
|
|
36977
|
-
* key
|
|
36978
|
-
* @param {Number} options.opcode The opcode
|
|
36979
|
-
* @param {Boolean} [options.readOnly=false] Specifies whether `data` can be
|
|
36980
|
-
* modified
|
|
36981
|
-
* @param {Boolean} [options.rsv1=false] Specifies whether or not to set the
|
|
36982
|
-
* RSV1 bit
|
|
36983
|
-
* @param {Function} [cb] Callback
|
|
36984
|
-
* @private
|
|
36985
|
-
*/
|
|
36986
|
-
dispatch(data, compress, options2, cb) {
|
|
36987
|
-
if (!compress) {
|
|
36988
|
-
this.sendFrame(_Sender.frame(data, options2), cb);
|
|
36989
|
-
return;
|
|
36990
|
-
}
|
|
36991
|
-
const perMessageDeflate = this._extensions[PerMessageDeflate.extensionName];
|
|
36992
|
-
this._bufferedBytes += options2[kByteLength];
|
|
36993
|
-
this._state = DEFLATING;
|
|
36994
|
-
perMessageDeflate.compress(data, options2.fin, (_, buf) => {
|
|
36995
|
-
if (this._socket.destroyed) {
|
|
36996
|
-
const err = new Error(
|
|
36997
|
-
"The socket was closed while data was being compressed"
|
|
36998
|
-
);
|
|
36999
|
-
callCallbacks(this, err, cb);
|
|
37000
|
-
return;
|
|
37001
|
-
}
|
|
37002
|
-
this._bufferedBytes -= options2[kByteLength];
|
|
37003
|
-
this._state = DEFAULT;
|
|
37004
|
-
options2.readOnly = false;
|
|
37005
|
-
this.sendFrame(_Sender.frame(buf, options2), cb);
|
|
37006
|
-
this.dequeue();
|
|
37007
|
-
});
|
|
37008
|
-
}
|
|
37009
|
-
/**
|
|
37010
|
-
* Executes queued send operations.
|
|
37011
|
-
*
|
|
37012
|
-
* @private
|
|
37013
|
-
*/
|
|
37014
|
-
dequeue() {
|
|
37015
|
-
while (this._state === DEFAULT && this._queue.length) {
|
|
37016
|
-
const params = this._queue.shift();
|
|
37017
|
-
this._bufferedBytes -= params[3][kByteLength];
|
|
37018
|
-
Reflect.apply(params[0], this, params.slice(1));
|
|
37019
|
-
}
|
|
37020
|
-
}
|
|
37021
|
-
/**
|
|
37022
|
-
* Enqueues a send operation.
|
|
37023
|
-
*
|
|
37024
|
-
* @param {Array} params Send operation parameters.
|
|
37025
|
-
* @private
|
|
37026
|
-
*/
|
|
37027
|
-
enqueue(params) {
|
|
37028
|
-
this._bufferedBytes += params[3][kByteLength];
|
|
37029
|
-
this._queue.push(params);
|
|
37030
|
-
}
|
|
37031
|
-
/**
|
|
37032
|
-
* Sends a frame.
|
|
37033
|
-
*
|
|
37034
|
-
* @param {(Buffer | String)[]} list The frame to send
|
|
37035
|
-
* @param {Function} [cb] Callback
|
|
37036
|
-
* @private
|
|
37037
|
-
*/
|
|
37038
|
-
sendFrame(list, cb) {
|
|
37039
|
-
if (list.length === 2) {
|
|
37040
|
-
this._socket.cork();
|
|
37041
|
-
this._socket.write(list[0]);
|
|
37042
|
-
this._socket.write(list[1], cb);
|
|
37043
|
-
this._socket.uncork();
|
|
37044
|
-
} else {
|
|
37045
|
-
this._socket.write(list[0], cb);
|
|
37046
|
-
}
|
|
37047
|
-
}
|
|
37048
|
-
};
|
|
37049
|
-
module2.exports = Sender2;
|
|
37050
|
-
function callCallbacks(sender, err, cb) {
|
|
37051
|
-
if (typeof cb === "function") cb(err);
|
|
37052
|
-
for (let i = 0; i < sender._queue.length; i++) {
|
|
37053
|
-
const params = sender._queue[i];
|
|
37054
|
-
const callback = params[params.length - 1];
|
|
37055
|
-
if (typeof callback === "function") callback(err);
|
|
37056
|
-
}
|
|
37057
|
-
}
|
|
37058
|
-
function onError(sender, err, cb) {
|
|
37059
|
-
callCallbacks(sender, err, cb);
|
|
37060
|
-
sender.onerror(err);
|
|
37061
|
-
}
|
|
37062
|
-
}
|
|
37063
|
-
});
|
|
37064
|
-
|
|
37065
|
-
// node_modules/ws/lib/event-target.js
|
|
37066
|
-
var require_event_target = __commonJS({
|
|
37067
|
-
"node_modules/ws/lib/event-target.js"(exports2, module2) {
|
|
37068
|
-
"use strict";
|
|
37069
|
-
var { kForOnEventAttribute, kListener } = require_constants();
|
|
37070
|
-
var kCode = /* @__PURE__ */ Symbol("kCode");
|
|
37071
|
-
var kData = /* @__PURE__ */ Symbol("kData");
|
|
37072
|
-
var kError = /* @__PURE__ */ Symbol("kError");
|
|
37073
|
-
var kMessage = /* @__PURE__ */ Symbol("kMessage");
|
|
37074
|
-
var kReason = /* @__PURE__ */ Symbol("kReason");
|
|
37075
|
-
var kTarget = /* @__PURE__ */ Symbol("kTarget");
|
|
37076
|
-
var kType = /* @__PURE__ */ Symbol("kType");
|
|
37077
|
-
var kWasClean = /* @__PURE__ */ Symbol("kWasClean");
|
|
37078
|
-
var Event = class {
|
|
37079
|
-
/**
|
|
37080
|
-
* Create a new `Event`.
|
|
37081
|
-
*
|
|
37082
|
-
* @param {String} type The name of the event
|
|
37083
|
-
* @throws {TypeError} If the `type` argument is not specified
|
|
37084
|
-
*/
|
|
37085
|
-
constructor(type) {
|
|
37086
|
-
this[kTarget] = null;
|
|
37087
|
-
this[kType] = type;
|
|
37088
|
-
}
|
|
37089
|
-
/**
|
|
37090
|
-
* @type {*}
|
|
37091
|
-
*/
|
|
37092
|
-
get target() {
|
|
37093
|
-
return this[kTarget];
|
|
37094
|
-
}
|
|
37095
|
-
/**
|
|
37096
|
-
* @type {String}
|
|
37097
|
-
*/
|
|
37098
|
-
get type() {
|
|
37099
|
-
return this[kType];
|
|
37100
|
-
}
|
|
37101
|
-
};
|
|
37102
|
-
Object.defineProperty(Event.prototype, "target", { enumerable: true });
|
|
37103
|
-
Object.defineProperty(Event.prototype, "type", { enumerable: true });
|
|
37104
|
-
var CloseEvent = class extends Event {
|
|
37105
|
-
/**
|
|
37106
|
-
* Create a new `CloseEvent`.
|
|
37107
|
-
*
|
|
37108
|
-
* @param {String} type The name of the event
|
|
37109
|
-
* @param {Object} [options] A dictionary object that allows for setting
|
|
37110
|
-
* attributes via object members of the same name
|
|
37111
|
-
* @param {Number} [options.code=0] The status code explaining why the
|
|
37112
|
-
* connection was closed
|
|
37113
|
-
* @param {String} [options.reason=''] A human-readable string explaining why
|
|
37114
|
-
* the connection was closed
|
|
37115
|
-
* @param {Boolean} [options.wasClean=false] Indicates whether or not the
|
|
37116
|
-
* connection was cleanly closed
|
|
37117
|
-
*/
|
|
37118
|
-
constructor(type, options2 = {}) {
|
|
37119
|
-
super(type);
|
|
37120
|
-
this[kCode] = options2.code === void 0 ? 0 : options2.code;
|
|
37121
|
-
this[kReason] = options2.reason === void 0 ? "" : options2.reason;
|
|
37122
|
-
this[kWasClean] = options2.wasClean === void 0 ? false : options2.wasClean;
|
|
37123
|
-
}
|
|
37124
|
-
/**
|
|
37125
|
-
* @type {Number}
|
|
37126
|
-
*/
|
|
37127
|
-
get code() {
|
|
37128
|
-
return this[kCode];
|
|
37129
|
-
}
|
|
37130
|
-
/**
|
|
37131
|
-
* @type {String}
|
|
37132
|
-
*/
|
|
37133
|
-
get reason() {
|
|
37134
|
-
return this[kReason];
|
|
37135
|
-
}
|
|
37136
|
-
/**
|
|
37137
|
-
* @type {Boolean}
|
|
37138
|
-
*/
|
|
37139
|
-
get wasClean() {
|
|
37140
|
-
return this[kWasClean];
|
|
37141
|
-
}
|
|
37142
|
-
};
|
|
37143
|
-
Object.defineProperty(CloseEvent.prototype, "code", { enumerable: true });
|
|
37144
|
-
Object.defineProperty(CloseEvent.prototype, "reason", { enumerable: true });
|
|
37145
|
-
Object.defineProperty(CloseEvent.prototype, "wasClean", { enumerable: true });
|
|
37146
|
-
var ErrorEvent = class extends Event {
|
|
37147
|
-
/**
|
|
37148
|
-
* Create a new `ErrorEvent`.
|
|
37149
|
-
*
|
|
37150
|
-
* @param {String} type The name of the event
|
|
37151
|
-
* @param {Object} [options] A dictionary object that allows for setting
|
|
37152
|
-
* attributes via object members of the same name
|
|
37153
|
-
* @param {*} [options.error=null] The error that generated this event
|
|
37154
|
-
* @param {String} [options.message=''] The error message
|
|
37155
|
-
*/
|
|
37156
|
-
constructor(type, options2 = {}) {
|
|
37157
|
-
super(type);
|
|
37158
|
-
this[kError] = options2.error === void 0 ? null : options2.error;
|
|
37159
|
-
this[kMessage] = options2.message === void 0 ? "" : options2.message;
|
|
37160
|
-
}
|
|
37161
|
-
/**
|
|
37162
|
-
* @type {*}
|
|
37163
|
-
*/
|
|
37164
|
-
get error() {
|
|
37165
|
-
return this[kError];
|
|
37166
|
-
}
|
|
37167
|
-
/**
|
|
37168
|
-
* @type {String}
|
|
37169
|
-
*/
|
|
37170
|
-
get message() {
|
|
37171
|
-
return this[kMessage];
|
|
37172
|
-
}
|
|
37173
|
-
};
|
|
37174
|
-
Object.defineProperty(ErrorEvent.prototype, "error", { enumerable: true });
|
|
37175
|
-
Object.defineProperty(ErrorEvent.prototype, "message", { enumerable: true });
|
|
37176
|
-
var MessageEvent = class extends Event {
|
|
37177
|
-
/**
|
|
37178
|
-
* Create a new `MessageEvent`.
|
|
37179
|
-
*
|
|
37180
|
-
* @param {String} type The name of the event
|
|
37181
|
-
* @param {Object} [options] A dictionary object that allows for setting
|
|
37182
|
-
* attributes via object members of the same name
|
|
37183
|
-
* @param {*} [options.data=null] The message content
|
|
37184
|
-
*/
|
|
37185
|
-
constructor(type, options2 = {}) {
|
|
37186
|
-
super(type);
|
|
37187
|
-
this[kData] = options2.data === void 0 ? null : options2.data;
|
|
37188
|
-
}
|
|
37189
|
-
/**
|
|
37190
|
-
* @type {*}
|
|
37191
|
-
*/
|
|
37192
|
-
get data() {
|
|
37193
|
-
return this[kData];
|
|
37194
|
-
}
|
|
37195
|
-
};
|
|
37196
|
-
Object.defineProperty(MessageEvent.prototype, "data", { enumerable: true });
|
|
37197
|
-
var EventTarget = {
|
|
37198
|
-
/**
|
|
37199
|
-
* Register an event listener.
|
|
37200
|
-
*
|
|
37201
|
-
* @param {String} type A string representing the event type to listen for
|
|
37202
|
-
* @param {(Function|Object)} handler The listener to add
|
|
37203
|
-
* @param {Object} [options] An options object specifies characteristics about
|
|
37204
|
-
* the event listener
|
|
37205
|
-
* @param {Boolean} [options.once=false] A `Boolean` indicating that the
|
|
37206
|
-
* listener should be invoked at most once after being added. If `true`,
|
|
37207
|
-
* the listener would be automatically removed when invoked.
|
|
37208
|
-
* @public
|
|
37209
|
-
*/
|
|
37210
|
-
addEventListener(type, handler, options2 = {}) {
|
|
37211
|
-
for (const listener of this.listeners(type)) {
|
|
37212
|
-
if (!options2[kForOnEventAttribute] && listener[kListener] === handler && !listener[kForOnEventAttribute]) {
|
|
37213
|
-
return;
|
|
37214
|
-
}
|
|
37215
|
-
}
|
|
37216
|
-
let wrapper;
|
|
37217
|
-
if (type === "message") {
|
|
37218
|
-
wrapper = function onMessage(data, isBinary) {
|
|
37219
|
-
const event = new MessageEvent("message", {
|
|
37220
|
-
data: isBinary ? data : data.toString()
|
|
37221
|
-
});
|
|
37222
|
-
event[kTarget] = this;
|
|
37223
|
-
callListener(handler, this, event);
|
|
37224
|
-
};
|
|
37225
|
-
} else if (type === "close") {
|
|
37226
|
-
wrapper = function onClose(code, message) {
|
|
37227
|
-
const event = new CloseEvent("close", {
|
|
37228
|
-
code,
|
|
37229
|
-
reason: message.toString(),
|
|
37230
|
-
wasClean: this._closeFrameReceived && this._closeFrameSent
|
|
37231
|
-
});
|
|
37232
|
-
event[kTarget] = this;
|
|
37233
|
-
callListener(handler, this, event);
|
|
37234
|
-
};
|
|
37235
|
-
} else if (type === "error") {
|
|
37236
|
-
wrapper = function onError(error48) {
|
|
37237
|
-
const event = new ErrorEvent("error", {
|
|
37238
|
-
error: error48,
|
|
37239
|
-
message: error48.message
|
|
37240
|
-
});
|
|
37241
|
-
event[kTarget] = this;
|
|
37242
|
-
callListener(handler, this, event);
|
|
37243
|
-
};
|
|
37244
|
-
} else if (type === "open") {
|
|
37245
|
-
wrapper = function onOpen() {
|
|
37246
|
-
const event = new Event("open");
|
|
37247
|
-
event[kTarget] = this;
|
|
37248
|
-
callListener(handler, this, event);
|
|
37249
|
-
};
|
|
37250
|
-
} else {
|
|
37251
|
-
return;
|
|
37252
|
-
}
|
|
37253
|
-
wrapper[kForOnEventAttribute] = !!options2[kForOnEventAttribute];
|
|
37254
|
-
wrapper[kListener] = handler;
|
|
37255
|
-
if (options2.once) {
|
|
37256
|
-
this.once(type, wrapper);
|
|
37257
|
-
} else {
|
|
37258
|
-
this.on(type, wrapper);
|
|
37259
|
-
}
|
|
37260
|
-
},
|
|
37261
|
-
/**
|
|
37262
|
-
* Remove an event listener.
|
|
37263
|
-
*
|
|
37264
|
-
* @param {String} type A string representing the event type to remove
|
|
37265
|
-
* @param {(Function|Object)} handler The listener to remove
|
|
37266
|
-
* @public
|
|
37267
|
-
*/
|
|
37268
|
-
removeEventListener(type, handler) {
|
|
37269
|
-
for (const listener of this.listeners(type)) {
|
|
37270
|
-
if (listener[kListener] === handler && !listener[kForOnEventAttribute]) {
|
|
37271
|
-
this.removeListener(type, listener);
|
|
37272
|
-
break;
|
|
37273
|
-
}
|
|
37274
|
-
}
|
|
37275
|
-
}
|
|
37276
|
-
};
|
|
37277
|
-
module2.exports = {
|
|
37278
|
-
CloseEvent,
|
|
37279
|
-
ErrorEvent,
|
|
37280
|
-
Event,
|
|
37281
|
-
EventTarget,
|
|
37282
|
-
MessageEvent
|
|
37283
|
-
};
|
|
37284
|
-
function callListener(listener, thisArg, event) {
|
|
37285
|
-
if (typeof listener === "object" && listener.handleEvent) {
|
|
37286
|
-
listener.handleEvent.call(listener, event);
|
|
37287
|
-
} else {
|
|
37288
|
-
listener.call(thisArg, event);
|
|
37289
|
-
}
|
|
37290
|
-
}
|
|
37291
|
-
}
|
|
37292
|
-
});
|
|
37293
|
-
|
|
37294
|
-
// node_modules/ws/lib/extension.js
|
|
37295
|
-
var require_extension = __commonJS({
|
|
37296
|
-
"node_modules/ws/lib/extension.js"(exports2, module2) {
|
|
37297
|
-
"use strict";
|
|
37298
|
-
var { tokenChars } = require_validation();
|
|
37299
|
-
function push(dest, name, elem) {
|
|
37300
|
-
if (dest[name] === void 0) dest[name] = [elem];
|
|
37301
|
-
else dest[name].push(elem);
|
|
37302
|
-
}
|
|
37303
|
-
function parse4(header) {
|
|
37304
|
-
const offers = /* @__PURE__ */ Object.create(null);
|
|
37305
|
-
let params = /* @__PURE__ */ Object.create(null);
|
|
37306
|
-
let mustUnescape = false;
|
|
37307
|
-
let isEscaping = false;
|
|
37308
|
-
let inQuotes = false;
|
|
37309
|
-
let extensionName;
|
|
37310
|
-
let paramName;
|
|
37311
|
-
let start = -1;
|
|
37312
|
-
let code = -1;
|
|
37313
|
-
let end = -1;
|
|
37314
|
-
let i = 0;
|
|
37315
|
-
for (; i < header.length; i++) {
|
|
37316
|
-
code = header.charCodeAt(i);
|
|
37317
|
-
if (extensionName === void 0) {
|
|
37318
|
-
if (end === -1 && tokenChars[code] === 1) {
|
|
37319
|
-
if (start === -1) start = i;
|
|
37320
|
-
} else if (i !== 0 && (code === 32 || code === 9)) {
|
|
37321
|
-
if (end === -1 && start !== -1) end = i;
|
|
37322
|
-
} else if (code === 59 || code === 44) {
|
|
37323
|
-
if (start === -1) {
|
|
37324
|
-
throw new SyntaxError(`Unexpected character at index ${i}`);
|
|
37325
|
-
}
|
|
37326
|
-
if (end === -1) end = i;
|
|
37327
|
-
const name = header.slice(start, end);
|
|
37328
|
-
if (code === 44) {
|
|
37329
|
-
push(offers, name, params);
|
|
37330
|
-
params = /* @__PURE__ */ Object.create(null);
|
|
37331
|
-
} else {
|
|
37332
|
-
extensionName = name;
|
|
37333
|
-
}
|
|
37334
|
-
start = end = -1;
|
|
37335
|
-
} else {
|
|
37336
|
-
throw new SyntaxError(`Unexpected character at index ${i}`);
|
|
37337
|
-
}
|
|
37338
|
-
} else if (paramName === void 0) {
|
|
37339
|
-
if (end === -1 && tokenChars[code] === 1) {
|
|
37340
|
-
if (start === -1) start = i;
|
|
37341
|
-
} else if (code === 32 || code === 9) {
|
|
37342
|
-
if (end === -1 && start !== -1) end = i;
|
|
37343
|
-
} else if (code === 59 || code === 44) {
|
|
37344
|
-
if (start === -1) {
|
|
37345
|
-
throw new SyntaxError(`Unexpected character at index ${i}`);
|
|
37346
|
-
}
|
|
37347
|
-
if (end === -1) end = i;
|
|
37348
|
-
push(params, header.slice(start, end), true);
|
|
37349
|
-
if (code === 44) {
|
|
37350
|
-
push(offers, extensionName, params);
|
|
37351
|
-
params = /* @__PURE__ */ Object.create(null);
|
|
37352
|
-
extensionName = void 0;
|
|
37353
|
-
}
|
|
37354
|
-
start = end = -1;
|
|
37355
|
-
} else if (code === 61 && start !== -1 && end === -1) {
|
|
37356
|
-
paramName = header.slice(start, i);
|
|
37357
|
-
start = end = -1;
|
|
37358
|
-
} else {
|
|
37359
|
-
throw new SyntaxError(`Unexpected character at index ${i}`);
|
|
37360
|
-
}
|
|
37361
|
-
} else {
|
|
37362
|
-
if (isEscaping) {
|
|
37363
|
-
if (tokenChars[code] !== 1) {
|
|
37364
|
-
throw new SyntaxError(`Unexpected character at index ${i}`);
|
|
37365
|
-
}
|
|
37366
|
-
if (start === -1) start = i;
|
|
37367
|
-
else if (!mustUnescape) mustUnescape = true;
|
|
37368
|
-
isEscaping = false;
|
|
37369
|
-
} else if (inQuotes) {
|
|
37370
|
-
if (tokenChars[code] === 1) {
|
|
37371
|
-
if (start === -1) start = i;
|
|
37372
|
-
} else if (code === 34 && start !== -1) {
|
|
37373
|
-
inQuotes = false;
|
|
37374
|
-
end = i;
|
|
37375
|
-
} else if (code === 92) {
|
|
37376
|
-
isEscaping = true;
|
|
37377
|
-
} else {
|
|
37378
|
-
throw new SyntaxError(`Unexpected character at index ${i}`);
|
|
37379
|
-
}
|
|
37380
|
-
} else if (code === 34 && header.charCodeAt(i - 1) === 61) {
|
|
37381
|
-
inQuotes = true;
|
|
37382
|
-
} else if (end === -1 && tokenChars[code] === 1) {
|
|
37383
|
-
if (start === -1) start = i;
|
|
37384
|
-
} else if (start !== -1 && (code === 32 || code === 9)) {
|
|
37385
|
-
if (end === -1) end = i;
|
|
37386
|
-
} else if (code === 59 || code === 44) {
|
|
37387
|
-
if (start === -1) {
|
|
37388
|
-
throw new SyntaxError(`Unexpected character at index ${i}`);
|
|
37389
|
-
}
|
|
37390
|
-
if (end === -1) end = i;
|
|
37391
|
-
let value = header.slice(start, end);
|
|
37392
|
-
if (mustUnescape) {
|
|
37393
|
-
value = value.replace(/\\/g, "");
|
|
37394
|
-
mustUnescape = false;
|
|
37395
|
-
}
|
|
37396
|
-
push(params, paramName, value);
|
|
37397
|
-
if (code === 44) {
|
|
37398
|
-
push(offers, extensionName, params);
|
|
37399
|
-
params = /* @__PURE__ */ Object.create(null);
|
|
37400
|
-
extensionName = void 0;
|
|
37401
|
-
}
|
|
37402
|
-
paramName = void 0;
|
|
37403
|
-
start = end = -1;
|
|
37404
|
-
} else {
|
|
37405
|
-
throw new SyntaxError(`Unexpected character at index ${i}`);
|
|
37406
|
-
}
|
|
37407
|
-
}
|
|
37408
|
-
}
|
|
37409
|
-
if (start === -1 || inQuotes || code === 32 || code === 9) {
|
|
37410
|
-
throw new SyntaxError("Unexpected end of input");
|
|
37411
|
-
}
|
|
37412
|
-
if (end === -1) end = i;
|
|
37413
|
-
const token = header.slice(start, end);
|
|
37414
|
-
if (extensionName === void 0) {
|
|
37415
|
-
push(offers, token, params);
|
|
37416
|
-
} else {
|
|
37417
|
-
if (paramName === void 0) {
|
|
37418
|
-
push(params, token, true);
|
|
37419
|
-
} else if (mustUnescape) {
|
|
37420
|
-
push(params, paramName, token.replace(/\\/g, ""));
|
|
37421
|
-
} else {
|
|
37422
|
-
push(params, paramName, token);
|
|
37423
|
-
}
|
|
37424
|
-
push(offers, extensionName, params);
|
|
37425
|
-
}
|
|
37426
|
-
return offers;
|
|
37427
|
-
}
|
|
37428
|
-
function format(extensions) {
|
|
37429
|
-
return Object.keys(extensions).map((extension) => {
|
|
37430
|
-
let configurations = extensions[extension];
|
|
37431
|
-
if (!Array.isArray(configurations)) configurations = [configurations];
|
|
37432
|
-
return configurations.map((params) => {
|
|
37433
|
-
return [extension].concat(
|
|
37434
|
-
Object.keys(params).map((k) => {
|
|
37435
|
-
let values = params[k];
|
|
37436
|
-
if (!Array.isArray(values)) values = [values];
|
|
37437
|
-
return values.map((v) => v === true ? k : `${k}=${v}`).join("; ");
|
|
37438
|
-
})
|
|
37439
|
-
).join("; ");
|
|
37440
|
-
}).join(", ");
|
|
37441
|
-
}).join(", ");
|
|
37442
|
-
}
|
|
37443
|
-
module2.exports = { format, parse: parse4 };
|
|
37444
|
-
}
|
|
37445
|
-
});
|
|
37446
|
-
|
|
37447
|
-
// node_modules/ws/lib/websocket.js
|
|
37448
|
-
var require_websocket = __commonJS({
|
|
37449
|
-
"node_modules/ws/lib/websocket.js"(exports2, module2) {
|
|
37450
|
-
"use strict";
|
|
37451
|
-
var EventEmitter = __require("events");
|
|
37452
|
-
var https = __require("https");
|
|
37453
|
-
var http = __require("http");
|
|
37454
|
-
var net = __require("net");
|
|
37455
|
-
var tls = __require("tls");
|
|
37456
|
-
var { randomBytes, createHash } = __require("crypto");
|
|
37457
|
-
var { Duplex, Readable } = __require("stream");
|
|
37458
|
-
var { URL: URL2 } = __require("url");
|
|
37459
|
-
var PerMessageDeflate = require_permessage_deflate();
|
|
37460
|
-
var Receiver2 = require_receiver();
|
|
37461
|
-
var Sender2 = require_sender();
|
|
37462
|
-
var { isBlob } = require_validation();
|
|
37463
|
-
var {
|
|
37464
|
-
BINARY_TYPES,
|
|
37465
|
-
EMPTY_BUFFER,
|
|
37466
|
-
GUID,
|
|
37467
|
-
kForOnEventAttribute,
|
|
37468
|
-
kListener,
|
|
37469
|
-
kStatusCode,
|
|
37470
|
-
kWebSocket,
|
|
37471
|
-
NOOP
|
|
37472
|
-
} = require_constants();
|
|
37473
|
-
var {
|
|
37474
|
-
EventTarget: { addEventListener, removeEventListener }
|
|
37475
|
-
} = require_event_target();
|
|
37476
|
-
var { format, parse: parse4 } = require_extension();
|
|
37477
|
-
var { toBuffer } = require_buffer_util();
|
|
37478
|
-
var closeTimeout = 30 * 1e3;
|
|
37479
|
-
var kAborted = /* @__PURE__ */ Symbol("kAborted");
|
|
37480
|
-
var protocolVersions = [8, 13];
|
|
37481
|
-
var readyStates = ["CONNECTING", "OPEN", "CLOSING", "CLOSED"];
|
|
37482
|
-
var subprotocolRegex = /^[!#$%&'*+\-.0-9A-Z^_`|a-z~]+$/;
|
|
37483
|
-
var WebSocket2 = class _WebSocket extends EventEmitter {
|
|
37484
|
-
/**
|
|
37485
|
-
* Create a new `WebSocket`.
|
|
37486
|
-
*
|
|
37487
|
-
* @param {(String|URL)} address The URL to which to connect
|
|
37488
|
-
* @param {(String|String[])} [protocols] The subprotocols
|
|
37489
|
-
* @param {Object} [options] Connection options
|
|
37490
|
-
*/
|
|
37491
|
-
constructor(address, protocols, options2) {
|
|
37492
|
-
super();
|
|
37493
|
-
this._binaryType = BINARY_TYPES[0];
|
|
37494
|
-
this._closeCode = 1006;
|
|
37495
|
-
this._closeFrameReceived = false;
|
|
37496
|
-
this._closeFrameSent = false;
|
|
37497
|
-
this._closeMessage = EMPTY_BUFFER;
|
|
37498
|
-
this._closeTimer = null;
|
|
37499
|
-
this._errorEmitted = false;
|
|
37500
|
-
this._extensions = {};
|
|
37501
|
-
this._paused = false;
|
|
37502
|
-
this._protocol = "";
|
|
37503
|
-
this._readyState = _WebSocket.CONNECTING;
|
|
37504
|
-
this._receiver = null;
|
|
37505
|
-
this._sender = null;
|
|
37506
|
-
this._socket = null;
|
|
37507
|
-
if (address !== null) {
|
|
37508
|
-
this._bufferedAmount = 0;
|
|
37509
|
-
this._isServer = false;
|
|
37510
|
-
this._redirects = 0;
|
|
37511
|
-
if (protocols === void 0) {
|
|
37512
|
-
protocols = [];
|
|
37513
|
-
} else if (!Array.isArray(protocols)) {
|
|
37514
|
-
if (typeof protocols === "object" && protocols !== null) {
|
|
37515
|
-
options2 = protocols;
|
|
37516
|
-
protocols = [];
|
|
37517
|
-
} else {
|
|
37518
|
-
protocols = [protocols];
|
|
37519
|
-
}
|
|
37520
|
-
}
|
|
37521
|
-
initAsClient(this, address, protocols, options2);
|
|
37522
|
-
} else {
|
|
37523
|
-
this._autoPong = options2.autoPong;
|
|
37524
|
-
this._isServer = true;
|
|
37525
|
-
}
|
|
37526
|
-
}
|
|
37527
|
-
/**
|
|
37528
|
-
* For historical reasons, the custom "nodebuffer" type is used by the default
|
|
37529
|
-
* instead of "blob".
|
|
37530
|
-
*
|
|
37531
|
-
* @type {String}
|
|
37532
|
-
*/
|
|
37533
|
-
get binaryType() {
|
|
37534
|
-
return this._binaryType;
|
|
37535
|
-
}
|
|
37536
|
-
set binaryType(type) {
|
|
37537
|
-
if (!BINARY_TYPES.includes(type)) return;
|
|
37538
|
-
this._binaryType = type;
|
|
37539
|
-
if (this._receiver) this._receiver._binaryType = type;
|
|
37540
|
-
}
|
|
37541
|
-
/**
|
|
37542
|
-
* @type {Number}
|
|
37543
|
-
*/
|
|
37544
|
-
get bufferedAmount() {
|
|
37545
|
-
if (!this._socket) return this._bufferedAmount;
|
|
37546
|
-
return this._socket._writableState.length + this._sender._bufferedBytes;
|
|
37547
|
-
}
|
|
37548
|
-
/**
|
|
37549
|
-
* @type {String}
|
|
37550
|
-
*/
|
|
37551
|
-
get extensions() {
|
|
37552
|
-
return Object.keys(this._extensions).join();
|
|
37553
|
-
}
|
|
37554
|
-
/**
|
|
37555
|
-
* @type {Boolean}
|
|
37556
|
-
*/
|
|
37557
|
-
get isPaused() {
|
|
37558
|
-
return this._paused;
|
|
37559
|
-
}
|
|
37560
|
-
/**
|
|
37561
|
-
* @type {Function}
|
|
37562
|
-
*/
|
|
37563
|
-
/* istanbul ignore next */
|
|
37564
|
-
get onclose() {
|
|
37565
|
-
return null;
|
|
37566
|
-
}
|
|
37567
|
-
/**
|
|
37568
|
-
* @type {Function}
|
|
37569
|
-
*/
|
|
37570
|
-
/* istanbul ignore next */
|
|
37571
|
-
get onerror() {
|
|
37572
|
-
return null;
|
|
37573
|
-
}
|
|
37574
|
-
/**
|
|
37575
|
-
* @type {Function}
|
|
37576
|
-
*/
|
|
37577
|
-
/* istanbul ignore next */
|
|
37578
|
-
get onopen() {
|
|
37579
|
-
return null;
|
|
37580
|
-
}
|
|
37581
|
-
/**
|
|
37582
|
-
* @type {Function}
|
|
37583
|
-
*/
|
|
37584
|
-
/* istanbul ignore next */
|
|
37585
|
-
get onmessage() {
|
|
37586
|
-
return null;
|
|
37587
|
-
}
|
|
37588
|
-
/**
|
|
37589
|
-
* @type {String}
|
|
37590
|
-
*/
|
|
37591
|
-
get protocol() {
|
|
37592
|
-
return this._protocol;
|
|
37593
|
-
}
|
|
37594
|
-
/**
|
|
37595
|
-
* @type {Number}
|
|
37596
|
-
*/
|
|
37597
|
-
get readyState() {
|
|
37598
|
-
return this._readyState;
|
|
37599
|
-
}
|
|
37600
|
-
/**
|
|
37601
|
-
* @type {String}
|
|
37602
|
-
*/
|
|
37603
|
-
get url() {
|
|
37604
|
-
return this._url;
|
|
37605
|
-
}
|
|
37606
|
-
/**
|
|
37607
|
-
* Set up the socket and the internal resources.
|
|
37608
|
-
*
|
|
37609
|
-
* @param {Duplex} socket The network socket between the server and client
|
|
37610
|
-
* @param {Buffer} head The first packet of the upgraded stream
|
|
37611
|
-
* @param {Object} options Options object
|
|
37612
|
-
* @param {Boolean} [options.allowSynchronousEvents=false] Specifies whether
|
|
37613
|
-
* any of the `'message'`, `'ping'`, and `'pong'` events can be emitted
|
|
37614
|
-
* multiple times in the same tick
|
|
37615
|
-
* @param {Function} [options.generateMask] The function used to generate the
|
|
37616
|
-
* masking key
|
|
37617
|
-
* @param {Number} [options.maxPayload=0] The maximum allowed message size
|
|
37618
|
-
* @param {Boolean} [options.skipUTF8Validation=false] Specifies whether or
|
|
37619
|
-
* not to skip UTF-8 validation for text and close messages
|
|
37620
|
-
* @private
|
|
37621
|
-
*/
|
|
37622
|
-
setSocket(socket, head, options2) {
|
|
37623
|
-
const receiver = new Receiver2({
|
|
37624
|
-
allowSynchronousEvents: options2.allowSynchronousEvents,
|
|
37625
|
-
binaryType: this.binaryType,
|
|
37626
|
-
extensions: this._extensions,
|
|
37627
|
-
isServer: this._isServer,
|
|
37628
|
-
maxPayload: options2.maxPayload,
|
|
37629
|
-
skipUTF8Validation: options2.skipUTF8Validation
|
|
37630
|
-
});
|
|
37631
|
-
const sender = new Sender2(socket, this._extensions, options2.generateMask);
|
|
37632
|
-
this._receiver = receiver;
|
|
37633
|
-
this._sender = sender;
|
|
37634
|
-
this._socket = socket;
|
|
37635
|
-
receiver[kWebSocket] = this;
|
|
37636
|
-
sender[kWebSocket] = this;
|
|
37637
|
-
socket[kWebSocket] = this;
|
|
37638
|
-
receiver.on("conclude", receiverOnConclude);
|
|
37639
|
-
receiver.on("drain", receiverOnDrain);
|
|
37640
|
-
receiver.on("error", receiverOnError);
|
|
37641
|
-
receiver.on("message", receiverOnMessage);
|
|
37642
|
-
receiver.on("ping", receiverOnPing);
|
|
37643
|
-
receiver.on("pong", receiverOnPong);
|
|
37644
|
-
sender.onerror = senderOnError;
|
|
37645
|
-
if (socket.setTimeout) socket.setTimeout(0);
|
|
37646
|
-
if (socket.setNoDelay) socket.setNoDelay();
|
|
37647
|
-
if (head.length > 0) socket.unshift(head);
|
|
37648
|
-
socket.on("close", socketOnClose);
|
|
37649
|
-
socket.on("data", socketOnData);
|
|
37650
|
-
socket.on("end", socketOnEnd);
|
|
37651
|
-
socket.on("error", socketOnError);
|
|
37652
|
-
this._readyState = _WebSocket.OPEN;
|
|
37653
|
-
this.emit("open");
|
|
37654
|
-
}
|
|
37655
|
-
/**
|
|
37656
|
-
* Emit the `'close'` event.
|
|
37657
|
-
*
|
|
37658
|
-
* @private
|
|
37659
|
-
*/
|
|
37660
|
-
emitClose() {
|
|
37661
|
-
if (!this._socket) {
|
|
37662
|
-
this._readyState = _WebSocket.CLOSED;
|
|
37663
|
-
this.emit("close", this._closeCode, this._closeMessage);
|
|
37664
|
-
return;
|
|
37665
|
-
}
|
|
37666
|
-
if (this._extensions[PerMessageDeflate.extensionName]) {
|
|
37667
|
-
this._extensions[PerMessageDeflate.extensionName].cleanup();
|
|
37668
|
-
}
|
|
37669
|
-
this._receiver.removeAllListeners();
|
|
37670
|
-
this._readyState = _WebSocket.CLOSED;
|
|
37671
|
-
this.emit("close", this._closeCode, this._closeMessage);
|
|
37672
|
-
}
|
|
37673
|
-
/**
|
|
37674
|
-
* Start a closing handshake.
|
|
37675
|
-
*
|
|
37676
|
-
* +----------+ +-----------+ +----------+
|
|
37677
|
-
* - - -|ws.close()|-->|close frame|-->|ws.close()|- - -
|
|
37678
|
-
* | +----------+ +-----------+ +----------+ |
|
|
37679
|
-
* +----------+ +-----------+ |
|
|
37680
|
-
* CLOSING |ws.close()|<--|close frame|<--+-----+ CLOSING
|
|
37681
|
-
* +----------+ +-----------+ |
|
|
37682
|
-
* | | | +---+ |
|
|
37683
|
-
* +------------------------+-->|fin| - - - -
|
|
37684
|
-
* | +---+ | +---+
|
|
37685
|
-
* - - - - -|fin|<---------------------+
|
|
37686
|
-
* +---+
|
|
37687
|
-
*
|
|
37688
|
-
* @param {Number} [code] Status code explaining why the connection is closing
|
|
37689
|
-
* @param {(String|Buffer)} [data] The reason why the connection is
|
|
37690
|
-
* closing
|
|
37691
|
-
* @public
|
|
37692
|
-
*/
|
|
37693
|
-
close(code, data) {
|
|
37694
|
-
if (this.readyState === _WebSocket.CLOSED) return;
|
|
37695
|
-
if (this.readyState === _WebSocket.CONNECTING) {
|
|
37696
|
-
const msg = "WebSocket was closed before the connection was established";
|
|
37697
|
-
abortHandshake(this, this._req, msg);
|
|
37698
|
-
return;
|
|
37699
|
-
}
|
|
37700
|
-
if (this.readyState === _WebSocket.CLOSING) {
|
|
37701
|
-
if (this._closeFrameSent && (this._closeFrameReceived || this._receiver._writableState.errorEmitted)) {
|
|
37702
|
-
this._socket.end();
|
|
37703
|
-
}
|
|
37704
|
-
return;
|
|
37705
|
-
}
|
|
37706
|
-
this._readyState = _WebSocket.CLOSING;
|
|
37707
|
-
this._sender.close(code, data, !this._isServer, (err) => {
|
|
37708
|
-
if (err) return;
|
|
37709
|
-
this._closeFrameSent = true;
|
|
37710
|
-
if (this._closeFrameReceived || this._receiver._writableState.errorEmitted) {
|
|
37711
|
-
this._socket.end();
|
|
37712
|
-
}
|
|
37713
|
-
});
|
|
37714
|
-
setCloseTimer(this);
|
|
37715
|
-
}
|
|
37716
|
-
/**
|
|
37717
|
-
* Pause the socket.
|
|
37718
|
-
*
|
|
37719
|
-
* @public
|
|
37720
|
-
*/
|
|
37721
|
-
pause() {
|
|
37722
|
-
if (this.readyState === _WebSocket.CONNECTING || this.readyState === _WebSocket.CLOSED) {
|
|
37723
|
-
return;
|
|
37724
|
-
}
|
|
37725
|
-
this._paused = true;
|
|
37726
|
-
this._socket.pause();
|
|
37727
|
-
}
|
|
37728
|
-
/**
|
|
37729
|
-
* Send a ping.
|
|
37730
|
-
*
|
|
37731
|
-
* @param {*} [data] The data to send
|
|
37732
|
-
* @param {Boolean} [mask] Indicates whether or not to mask `data`
|
|
37733
|
-
* @param {Function} [cb] Callback which is executed when the ping is sent
|
|
37734
|
-
* @public
|
|
37735
|
-
*/
|
|
37736
|
-
ping(data, mask, cb) {
|
|
37737
|
-
if (this.readyState === _WebSocket.CONNECTING) {
|
|
37738
|
-
throw new Error("WebSocket is not open: readyState 0 (CONNECTING)");
|
|
37739
|
-
}
|
|
37740
|
-
if (typeof data === "function") {
|
|
37741
|
-
cb = data;
|
|
37742
|
-
data = mask = void 0;
|
|
37743
|
-
} else if (typeof mask === "function") {
|
|
37744
|
-
cb = mask;
|
|
37745
|
-
mask = void 0;
|
|
37746
|
-
}
|
|
37747
|
-
if (typeof data === "number") data = data.toString();
|
|
37748
|
-
if (this.readyState !== _WebSocket.OPEN) {
|
|
37749
|
-
sendAfterClose(this, data, cb);
|
|
37750
|
-
return;
|
|
37751
|
-
}
|
|
37752
|
-
if (mask === void 0) mask = !this._isServer;
|
|
37753
|
-
this._sender.ping(data || EMPTY_BUFFER, mask, cb);
|
|
37754
|
-
}
|
|
37755
|
-
/**
|
|
37756
|
-
* Send a pong.
|
|
37757
|
-
*
|
|
37758
|
-
* @param {*} [data] The data to send
|
|
37759
|
-
* @param {Boolean} [mask] Indicates whether or not to mask `data`
|
|
37760
|
-
* @param {Function} [cb] Callback which is executed when the pong is sent
|
|
37761
|
-
* @public
|
|
37762
|
-
*/
|
|
37763
|
-
pong(data, mask, cb) {
|
|
37764
|
-
if (this.readyState === _WebSocket.CONNECTING) {
|
|
37765
|
-
throw new Error("WebSocket is not open: readyState 0 (CONNECTING)");
|
|
37766
|
-
}
|
|
37767
|
-
if (typeof data === "function") {
|
|
37768
|
-
cb = data;
|
|
37769
|
-
data = mask = void 0;
|
|
37770
|
-
} else if (typeof mask === "function") {
|
|
37771
|
-
cb = mask;
|
|
37772
|
-
mask = void 0;
|
|
37773
|
-
}
|
|
37774
|
-
if (typeof data === "number") data = data.toString();
|
|
37775
|
-
if (this.readyState !== _WebSocket.OPEN) {
|
|
37776
|
-
sendAfterClose(this, data, cb);
|
|
37777
|
-
return;
|
|
37778
|
-
}
|
|
37779
|
-
if (mask === void 0) mask = !this._isServer;
|
|
37780
|
-
this._sender.pong(data || EMPTY_BUFFER, mask, cb);
|
|
37781
|
-
}
|
|
37782
|
-
/**
|
|
37783
|
-
* Resume the socket.
|
|
37784
|
-
*
|
|
37785
|
-
* @public
|
|
37786
|
-
*/
|
|
37787
|
-
resume() {
|
|
37788
|
-
if (this.readyState === _WebSocket.CONNECTING || this.readyState === _WebSocket.CLOSED) {
|
|
37789
|
-
return;
|
|
37790
|
-
}
|
|
37791
|
-
this._paused = false;
|
|
37792
|
-
if (!this._receiver._writableState.needDrain) this._socket.resume();
|
|
37793
|
-
}
|
|
37794
|
-
/**
|
|
37795
|
-
* Send a data message.
|
|
37796
|
-
*
|
|
37797
|
-
* @param {*} data The message to send
|
|
37798
|
-
* @param {Object} [options] Options object
|
|
37799
|
-
* @param {Boolean} [options.binary] Specifies whether `data` is binary or
|
|
37800
|
-
* text
|
|
37801
|
-
* @param {Boolean} [options.compress] Specifies whether or not to compress
|
|
37802
|
-
* `data`
|
|
37803
|
-
* @param {Boolean} [options.fin=true] Specifies whether the fragment is the
|
|
37804
|
-
* last one
|
|
37805
|
-
* @param {Boolean} [options.mask] Specifies whether or not to mask `data`
|
|
37806
|
-
* @param {Function} [cb] Callback which is executed when data is written out
|
|
37807
|
-
* @public
|
|
37808
|
-
*/
|
|
37809
|
-
send(data, options2, cb) {
|
|
37810
|
-
if (this.readyState === _WebSocket.CONNECTING) {
|
|
37811
|
-
throw new Error("WebSocket is not open: readyState 0 (CONNECTING)");
|
|
37812
|
-
}
|
|
37813
|
-
if (typeof options2 === "function") {
|
|
37814
|
-
cb = options2;
|
|
37815
|
-
options2 = {};
|
|
37816
|
-
}
|
|
37817
|
-
if (typeof data === "number") data = data.toString();
|
|
37818
|
-
if (this.readyState !== _WebSocket.OPEN) {
|
|
37819
|
-
sendAfterClose(this, data, cb);
|
|
37820
|
-
return;
|
|
37821
|
-
}
|
|
37822
|
-
const opts = {
|
|
37823
|
-
binary: typeof data !== "string",
|
|
37824
|
-
mask: !this._isServer,
|
|
37825
|
-
compress: true,
|
|
37826
|
-
fin: true,
|
|
37827
|
-
...options2
|
|
37828
|
-
};
|
|
37829
|
-
if (!this._extensions[PerMessageDeflate.extensionName]) {
|
|
37830
|
-
opts.compress = false;
|
|
37831
|
-
}
|
|
37832
|
-
this._sender.send(data || EMPTY_BUFFER, opts, cb);
|
|
37833
|
-
}
|
|
37834
|
-
/**
|
|
37835
|
-
* Forcibly close the connection.
|
|
37836
|
-
*
|
|
37837
|
-
* @public
|
|
37838
|
-
*/
|
|
37839
|
-
terminate() {
|
|
37840
|
-
if (this.readyState === _WebSocket.CLOSED) return;
|
|
37841
|
-
if (this.readyState === _WebSocket.CONNECTING) {
|
|
37842
|
-
const msg = "WebSocket was closed before the connection was established";
|
|
37843
|
-
abortHandshake(this, this._req, msg);
|
|
37844
|
-
return;
|
|
37845
|
-
}
|
|
37846
|
-
if (this._socket) {
|
|
37847
|
-
this._readyState = _WebSocket.CLOSING;
|
|
37848
|
-
this._socket.destroy();
|
|
37849
|
-
}
|
|
37850
|
-
}
|
|
37851
|
-
};
|
|
37852
|
-
Object.defineProperty(WebSocket2, "CONNECTING", {
|
|
37853
|
-
enumerable: true,
|
|
37854
|
-
value: readyStates.indexOf("CONNECTING")
|
|
37855
|
-
});
|
|
37856
|
-
Object.defineProperty(WebSocket2.prototype, "CONNECTING", {
|
|
37857
|
-
enumerable: true,
|
|
37858
|
-
value: readyStates.indexOf("CONNECTING")
|
|
37859
|
-
});
|
|
37860
|
-
Object.defineProperty(WebSocket2, "OPEN", {
|
|
37861
|
-
enumerable: true,
|
|
37862
|
-
value: readyStates.indexOf("OPEN")
|
|
37863
|
-
});
|
|
37864
|
-
Object.defineProperty(WebSocket2.prototype, "OPEN", {
|
|
37865
|
-
enumerable: true,
|
|
37866
|
-
value: readyStates.indexOf("OPEN")
|
|
37867
|
-
});
|
|
37868
|
-
Object.defineProperty(WebSocket2, "CLOSING", {
|
|
37869
|
-
enumerable: true,
|
|
37870
|
-
value: readyStates.indexOf("CLOSING")
|
|
37871
|
-
});
|
|
37872
|
-
Object.defineProperty(WebSocket2.prototype, "CLOSING", {
|
|
37873
|
-
enumerable: true,
|
|
37874
|
-
value: readyStates.indexOf("CLOSING")
|
|
37875
|
-
});
|
|
37876
|
-
Object.defineProperty(WebSocket2, "CLOSED", {
|
|
37877
|
-
enumerable: true,
|
|
37878
|
-
value: readyStates.indexOf("CLOSED")
|
|
37879
|
-
});
|
|
37880
|
-
Object.defineProperty(WebSocket2.prototype, "CLOSED", {
|
|
37881
|
-
enumerable: true,
|
|
37882
|
-
value: readyStates.indexOf("CLOSED")
|
|
37883
|
-
});
|
|
37884
|
-
[
|
|
37885
|
-
"binaryType",
|
|
37886
|
-
"bufferedAmount",
|
|
37887
|
-
"extensions",
|
|
37888
|
-
"isPaused",
|
|
37889
|
-
"protocol",
|
|
37890
|
-
"readyState",
|
|
37891
|
-
"url"
|
|
37892
|
-
].forEach((property) => {
|
|
37893
|
-
Object.defineProperty(WebSocket2.prototype, property, { enumerable: true });
|
|
37894
|
-
});
|
|
37895
|
-
["open", "error", "close", "message"].forEach((method) => {
|
|
37896
|
-
Object.defineProperty(WebSocket2.prototype, `on${method}`, {
|
|
37897
|
-
enumerable: true,
|
|
37898
|
-
get() {
|
|
37899
|
-
for (const listener of this.listeners(method)) {
|
|
37900
|
-
if (listener[kForOnEventAttribute]) return listener[kListener];
|
|
37901
|
-
}
|
|
37902
|
-
return null;
|
|
37903
|
-
},
|
|
37904
|
-
set(handler) {
|
|
37905
|
-
for (const listener of this.listeners(method)) {
|
|
37906
|
-
if (listener[kForOnEventAttribute]) {
|
|
37907
|
-
this.removeListener(method, listener);
|
|
37908
|
-
break;
|
|
37909
|
-
}
|
|
37910
|
-
}
|
|
37911
|
-
if (typeof handler !== "function") return;
|
|
37912
|
-
this.addEventListener(method, handler, {
|
|
37913
|
-
[kForOnEventAttribute]: true
|
|
37914
|
-
});
|
|
37915
|
-
}
|
|
37916
|
-
});
|
|
37917
|
-
});
|
|
37918
|
-
WebSocket2.prototype.addEventListener = addEventListener;
|
|
37919
|
-
WebSocket2.prototype.removeEventListener = removeEventListener;
|
|
37920
|
-
module2.exports = WebSocket2;
|
|
37921
|
-
function initAsClient(websocket, address, protocols, options2) {
|
|
37922
|
-
const opts = {
|
|
37923
|
-
allowSynchronousEvents: true,
|
|
37924
|
-
autoPong: true,
|
|
37925
|
-
protocolVersion: protocolVersions[1],
|
|
37926
|
-
maxPayload: 100 * 1024 * 1024,
|
|
37927
|
-
skipUTF8Validation: false,
|
|
37928
|
-
perMessageDeflate: true,
|
|
37929
|
-
followRedirects: false,
|
|
37930
|
-
maxRedirects: 10,
|
|
37931
|
-
...options2,
|
|
37932
|
-
socketPath: void 0,
|
|
37933
|
-
hostname: void 0,
|
|
37934
|
-
protocol: void 0,
|
|
37935
|
-
timeout: void 0,
|
|
37936
|
-
method: "GET",
|
|
37937
|
-
host: void 0,
|
|
37938
|
-
path: void 0,
|
|
37939
|
-
port: void 0
|
|
37940
|
-
};
|
|
37941
|
-
websocket._autoPong = opts.autoPong;
|
|
37942
|
-
if (!protocolVersions.includes(opts.protocolVersion)) {
|
|
37943
|
-
throw new RangeError(
|
|
37944
|
-
`Unsupported protocol version: ${opts.protocolVersion} (supported versions: ${protocolVersions.join(", ")})`
|
|
37945
|
-
);
|
|
37946
|
-
}
|
|
37947
|
-
let parsedUrl;
|
|
37948
|
-
if (address instanceof URL2) {
|
|
37949
|
-
parsedUrl = address;
|
|
37950
|
-
} else {
|
|
37951
|
-
try {
|
|
37952
|
-
parsedUrl = new URL2(address);
|
|
37953
|
-
} catch (e) {
|
|
37954
|
-
throw new SyntaxError(`Invalid URL: ${address}`);
|
|
37955
|
-
}
|
|
37956
|
-
}
|
|
37957
|
-
if (parsedUrl.protocol === "http:") {
|
|
37958
|
-
parsedUrl.protocol = "ws:";
|
|
37959
|
-
} else if (parsedUrl.protocol === "https:") {
|
|
37960
|
-
parsedUrl.protocol = "wss:";
|
|
37961
|
-
}
|
|
37962
|
-
websocket._url = parsedUrl.href;
|
|
37963
|
-
const isSecure = parsedUrl.protocol === "wss:";
|
|
37964
|
-
const isIpcUrl = parsedUrl.protocol === "ws+unix:";
|
|
37965
|
-
let invalidUrlMessage;
|
|
37966
|
-
if (parsedUrl.protocol !== "ws:" && !isSecure && !isIpcUrl) {
|
|
37967
|
-
invalidUrlMessage = `The URL's protocol must be one of "ws:", "wss:", "http:", "https:", or "ws+unix:"`;
|
|
37968
|
-
} else if (isIpcUrl && !parsedUrl.pathname) {
|
|
37969
|
-
invalidUrlMessage = "The URL's pathname is empty";
|
|
37970
|
-
} else if (parsedUrl.hash) {
|
|
37971
|
-
invalidUrlMessage = "The URL contains a fragment identifier";
|
|
37972
|
-
}
|
|
37973
|
-
if (invalidUrlMessage) {
|
|
37974
|
-
const err = new SyntaxError(invalidUrlMessage);
|
|
37975
|
-
if (websocket._redirects === 0) {
|
|
37976
|
-
throw err;
|
|
37977
|
-
} else {
|
|
37978
|
-
emitErrorAndClose(websocket, err);
|
|
37979
|
-
return;
|
|
37980
|
-
}
|
|
37981
|
-
}
|
|
37982
|
-
const defaultPort = isSecure ? 443 : 80;
|
|
37983
|
-
const key = randomBytes(16).toString("base64");
|
|
37984
|
-
const request = isSecure ? https.request : http.request;
|
|
37985
|
-
const protocolSet = /* @__PURE__ */ new Set();
|
|
37986
|
-
let perMessageDeflate;
|
|
37987
|
-
opts.createConnection = opts.createConnection || (isSecure ? tlsConnect : netConnect);
|
|
37988
|
-
opts.defaultPort = opts.defaultPort || defaultPort;
|
|
37989
|
-
opts.port = parsedUrl.port || defaultPort;
|
|
37990
|
-
opts.host = parsedUrl.hostname.startsWith("[") ? parsedUrl.hostname.slice(1, -1) : parsedUrl.hostname;
|
|
37991
|
-
opts.headers = {
|
|
37992
|
-
...opts.headers,
|
|
37993
|
-
"Sec-WebSocket-Version": opts.protocolVersion,
|
|
37994
|
-
"Sec-WebSocket-Key": key,
|
|
37995
|
-
Connection: "Upgrade",
|
|
37996
|
-
Upgrade: "websocket"
|
|
37997
|
-
};
|
|
37998
|
-
opts.path = parsedUrl.pathname + parsedUrl.search;
|
|
37999
|
-
opts.timeout = opts.handshakeTimeout;
|
|
38000
|
-
if (opts.perMessageDeflate) {
|
|
38001
|
-
perMessageDeflate = new PerMessageDeflate(
|
|
38002
|
-
opts.perMessageDeflate !== true ? opts.perMessageDeflate : {},
|
|
38003
|
-
false,
|
|
38004
|
-
opts.maxPayload
|
|
38005
|
-
);
|
|
38006
|
-
opts.headers["Sec-WebSocket-Extensions"] = format({
|
|
38007
|
-
[PerMessageDeflate.extensionName]: perMessageDeflate.offer()
|
|
38008
|
-
});
|
|
38009
|
-
}
|
|
38010
|
-
if (protocols.length) {
|
|
38011
|
-
for (const protocol of protocols) {
|
|
38012
|
-
if (typeof protocol !== "string" || !subprotocolRegex.test(protocol) || protocolSet.has(protocol)) {
|
|
38013
|
-
throw new SyntaxError(
|
|
38014
|
-
"An invalid or duplicated subprotocol was specified"
|
|
38015
|
-
);
|
|
38016
|
-
}
|
|
38017
|
-
protocolSet.add(protocol);
|
|
38018
|
-
}
|
|
38019
|
-
opts.headers["Sec-WebSocket-Protocol"] = protocols.join(",");
|
|
38020
|
-
}
|
|
38021
|
-
if (opts.origin) {
|
|
38022
|
-
if (opts.protocolVersion < 13) {
|
|
38023
|
-
opts.headers["Sec-WebSocket-Origin"] = opts.origin;
|
|
38024
|
-
} else {
|
|
38025
|
-
opts.headers.Origin = opts.origin;
|
|
38026
|
-
}
|
|
38027
|
-
}
|
|
38028
|
-
if (parsedUrl.username || parsedUrl.password) {
|
|
38029
|
-
opts.auth = `${parsedUrl.username}:${parsedUrl.password}`;
|
|
38030
|
-
}
|
|
38031
|
-
if (isIpcUrl) {
|
|
38032
|
-
const parts = opts.path.split(":");
|
|
38033
|
-
opts.socketPath = parts[0];
|
|
38034
|
-
opts.path = parts[1];
|
|
38035
|
-
}
|
|
38036
|
-
let req;
|
|
38037
|
-
if (opts.followRedirects) {
|
|
38038
|
-
if (websocket._redirects === 0) {
|
|
38039
|
-
websocket._originalIpc = isIpcUrl;
|
|
38040
|
-
websocket._originalSecure = isSecure;
|
|
38041
|
-
websocket._originalHostOrSocketPath = isIpcUrl ? opts.socketPath : parsedUrl.host;
|
|
38042
|
-
const headers = options2 && options2.headers;
|
|
38043
|
-
options2 = { ...options2, headers: {} };
|
|
38044
|
-
if (headers) {
|
|
38045
|
-
for (const [key2, value] of Object.entries(headers)) {
|
|
38046
|
-
options2.headers[key2.toLowerCase()] = value;
|
|
38047
|
-
}
|
|
38048
|
-
}
|
|
38049
|
-
} else if (websocket.listenerCount("redirect") === 0) {
|
|
38050
|
-
const isSameHost = isIpcUrl ? websocket._originalIpc ? opts.socketPath === websocket._originalHostOrSocketPath : false : websocket._originalIpc ? false : parsedUrl.host === websocket._originalHostOrSocketPath;
|
|
38051
|
-
if (!isSameHost || websocket._originalSecure && !isSecure) {
|
|
38052
|
-
delete opts.headers.authorization;
|
|
38053
|
-
delete opts.headers.cookie;
|
|
38054
|
-
if (!isSameHost) delete opts.headers.host;
|
|
38055
|
-
opts.auth = void 0;
|
|
38056
|
-
}
|
|
38057
|
-
}
|
|
38058
|
-
if (opts.auth && !options2.headers.authorization) {
|
|
38059
|
-
options2.headers.authorization = "Basic " + Buffer.from(opts.auth).toString("base64");
|
|
38060
|
-
}
|
|
38061
|
-
req = websocket._req = request(opts);
|
|
38062
|
-
if (websocket._redirects) {
|
|
38063
|
-
websocket.emit("redirect", websocket.url, req);
|
|
38064
|
-
}
|
|
38065
|
-
} else {
|
|
38066
|
-
req = websocket._req = request(opts);
|
|
38067
|
-
}
|
|
38068
|
-
if (opts.timeout) {
|
|
38069
|
-
req.on("timeout", () => {
|
|
38070
|
-
abortHandshake(websocket, req, "Opening handshake has timed out");
|
|
38071
|
-
});
|
|
38072
|
-
}
|
|
38073
|
-
req.on("error", (err) => {
|
|
38074
|
-
if (req === null || req[kAborted]) return;
|
|
38075
|
-
req = websocket._req = null;
|
|
38076
|
-
emitErrorAndClose(websocket, err);
|
|
38077
|
-
});
|
|
38078
|
-
req.on("response", (res) => {
|
|
38079
|
-
const location = res.headers.location;
|
|
38080
|
-
const statusCode = res.statusCode;
|
|
38081
|
-
if (location && opts.followRedirects && statusCode >= 300 && statusCode < 400) {
|
|
38082
|
-
if (++websocket._redirects > opts.maxRedirects) {
|
|
38083
|
-
abortHandshake(websocket, req, "Maximum redirects exceeded");
|
|
38084
|
-
return;
|
|
38085
|
-
}
|
|
38086
|
-
req.abort();
|
|
38087
|
-
let addr;
|
|
38088
|
-
try {
|
|
38089
|
-
addr = new URL2(location, address);
|
|
38090
|
-
} catch (e) {
|
|
38091
|
-
const err = new SyntaxError(`Invalid URL: ${location}`);
|
|
38092
|
-
emitErrorAndClose(websocket, err);
|
|
38093
|
-
return;
|
|
38094
|
-
}
|
|
38095
|
-
initAsClient(websocket, addr, protocols, options2);
|
|
38096
|
-
} else if (!websocket.emit("unexpected-response", req, res)) {
|
|
38097
|
-
abortHandshake(
|
|
38098
|
-
websocket,
|
|
38099
|
-
req,
|
|
38100
|
-
`Unexpected server response: ${res.statusCode}`
|
|
38101
|
-
);
|
|
38102
|
-
}
|
|
38103
|
-
});
|
|
38104
|
-
req.on("upgrade", (res, socket, head) => {
|
|
38105
|
-
websocket.emit("upgrade", res);
|
|
38106
|
-
if (websocket.readyState !== WebSocket2.CONNECTING) return;
|
|
38107
|
-
req = websocket._req = null;
|
|
38108
|
-
const upgrade = res.headers.upgrade;
|
|
38109
|
-
if (upgrade === void 0 || upgrade.toLowerCase() !== "websocket") {
|
|
38110
|
-
abortHandshake(websocket, socket, "Invalid Upgrade header");
|
|
38111
|
-
return;
|
|
38112
|
-
}
|
|
38113
|
-
const digest = createHash("sha1").update(key + GUID).digest("base64");
|
|
38114
|
-
if (res.headers["sec-websocket-accept"] !== digest) {
|
|
38115
|
-
abortHandshake(websocket, socket, "Invalid Sec-WebSocket-Accept header");
|
|
38116
|
-
return;
|
|
38117
|
-
}
|
|
38118
|
-
const serverProt = res.headers["sec-websocket-protocol"];
|
|
38119
|
-
let protError;
|
|
38120
|
-
if (serverProt !== void 0) {
|
|
38121
|
-
if (!protocolSet.size) {
|
|
38122
|
-
protError = "Server sent a subprotocol but none was requested";
|
|
38123
|
-
} else if (!protocolSet.has(serverProt)) {
|
|
38124
|
-
protError = "Server sent an invalid subprotocol";
|
|
38125
|
-
}
|
|
38126
|
-
} else if (protocolSet.size) {
|
|
38127
|
-
protError = "Server sent no subprotocol";
|
|
38128
|
-
}
|
|
38129
|
-
if (protError) {
|
|
38130
|
-
abortHandshake(websocket, socket, protError);
|
|
38131
|
-
return;
|
|
38132
|
-
}
|
|
38133
|
-
if (serverProt) websocket._protocol = serverProt;
|
|
38134
|
-
const secWebSocketExtensions = res.headers["sec-websocket-extensions"];
|
|
38135
|
-
if (secWebSocketExtensions !== void 0) {
|
|
38136
|
-
if (!perMessageDeflate) {
|
|
38137
|
-
const message = "Server sent a Sec-WebSocket-Extensions header but no extension was requested";
|
|
38138
|
-
abortHandshake(websocket, socket, message);
|
|
38139
|
-
return;
|
|
38140
|
-
}
|
|
38141
|
-
let extensions;
|
|
38142
|
-
try {
|
|
38143
|
-
extensions = parse4(secWebSocketExtensions);
|
|
38144
|
-
} catch (err) {
|
|
38145
|
-
const message = "Invalid Sec-WebSocket-Extensions header";
|
|
38146
|
-
abortHandshake(websocket, socket, message);
|
|
38147
|
-
return;
|
|
38148
|
-
}
|
|
38149
|
-
const extensionNames = Object.keys(extensions);
|
|
38150
|
-
if (extensionNames.length !== 1 || extensionNames[0] !== PerMessageDeflate.extensionName) {
|
|
38151
|
-
const message = "Server indicated an extension that was not requested";
|
|
38152
|
-
abortHandshake(websocket, socket, message);
|
|
38153
|
-
return;
|
|
38154
|
-
}
|
|
38155
|
-
try {
|
|
38156
|
-
perMessageDeflate.accept(extensions[PerMessageDeflate.extensionName]);
|
|
38157
|
-
} catch (err) {
|
|
38158
|
-
const message = "Invalid Sec-WebSocket-Extensions header";
|
|
38159
|
-
abortHandshake(websocket, socket, message);
|
|
38160
|
-
return;
|
|
38161
|
-
}
|
|
38162
|
-
websocket._extensions[PerMessageDeflate.extensionName] = perMessageDeflate;
|
|
38163
|
-
}
|
|
38164
|
-
websocket.setSocket(socket, head, {
|
|
38165
|
-
allowSynchronousEvents: opts.allowSynchronousEvents,
|
|
38166
|
-
generateMask: opts.generateMask,
|
|
38167
|
-
maxPayload: opts.maxPayload,
|
|
38168
|
-
skipUTF8Validation: opts.skipUTF8Validation
|
|
38169
|
-
});
|
|
38170
|
-
});
|
|
38171
|
-
if (opts.finishRequest) {
|
|
38172
|
-
opts.finishRequest(req, websocket);
|
|
38173
|
-
} else {
|
|
38174
|
-
req.end();
|
|
38175
|
-
}
|
|
38176
|
-
}
|
|
38177
|
-
function emitErrorAndClose(websocket, err) {
|
|
38178
|
-
websocket._readyState = WebSocket2.CLOSING;
|
|
38179
|
-
websocket._errorEmitted = true;
|
|
38180
|
-
websocket.emit("error", err);
|
|
38181
|
-
websocket.emitClose();
|
|
38182
|
-
}
|
|
38183
|
-
function netConnect(options2) {
|
|
38184
|
-
options2.path = options2.socketPath;
|
|
38185
|
-
return net.connect(options2);
|
|
38186
|
-
}
|
|
38187
|
-
function tlsConnect(options2) {
|
|
38188
|
-
options2.path = void 0;
|
|
38189
|
-
if (!options2.servername && options2.servername !== "") {
|
|
38190
|
-
options2.servername = net.isIP(options2.host) ? "" : options2.host;
|
|
38191
|
-
}
|
|
38192
|
-
return tls.connect(options2);
|
|
38193
|
-
}
|
|
38194
|
-
function abortHandshake(websocket, stream, message) {
|
|
38195
|
-
websocket._readyState = WebSocket2.CLOSING;
|
|
38196
|
-
const err = new Error(message);
|
|
38197
|
-
Error.captureStackTrace(err, abortHandshake);
|
|
38198
|
-
if (stream.setHeader) {
|
|
38199
|
-
stream[kAborted] = true;
|
|
38200
|
-
stream.abort();
|
|
38201
|
-
if (stream.socket && !stream.socket.destroyed) {
|
|
38202
|
-
stream.socket.destroy();
|
|
38203
|
-
}
|
|
38204
|
-
process.nextTick(emitErrorAndClose, websocket, err);
|
|
38205
|
-
} else {
|
|
38206
|
-
stream.destroy(err);
|
|
38207
|
-
stream.once("error", websocket.emit.bind(websocket, "error"));
|
|
38208
|
-
stream.once("close", websocket.emitClose.bind(websocket));
|
|
38209
|
-
}
|
|
38210
|
-
}
|
|
38211
|
-
function sendAfterClose(websocket, data, cb) {
|
|
38212
|
-
if (data) {
|
|
38213
|
-
const length = isBlob(data) ? data.size : toBuffer(data).length;
|
|
38214
|
-
if (websocket._socket) websocket._sender._bufferedBytes += length;
|
|
38215
|
-
else websocket._bufferedAmount += length;
|
|
38216
|
-
}
|
|
38217
|
-
if (cb) {
|
|
38218
|
-
const err = new Error(
|
|
38219
|
-
`WebSocket is not open: readyState ${websocket.readyState} (${readyStates[websocket.readyState]})`
|
|
38220
|
-
);
|
|
38221
|
-
process.nextTick(cb, err);
|
|
38222
|
-
}
|
|
38223
|
-
}
|
|
38224
|
-
function receiverOnConclude(code, reason) {
|
|
38225
|
-
const websocket = this[kWebSocket];
|
|
38226
|
-
websocket._closeFrameReceived = true;
|
|
38227
|
-
websocket._closeMessage = reason;
|
|
38228
|
-
websocket._closeCode = code;
|
|
38229
|
-
if (websocket._socket[kWebSocket] === void 0) return;
|
|
38230
|
-
websocket._socket.removeListener("data", socketOnData);
|
|
38231
|
-
process.nextTick(resume, websocket._socket);
|
|
38232
|
-
if (code === 1005) websocket.close();
|
|
38233
|
-
else websocket.close(code, reason);
|
|
38234
|
-
}
|
|
38235
|
-
function receiverOnDrain() {
|
|
38236
|
-
const websocket = this[kWebSocket];
|
|
38237
|
-
if (!websocket.isPaused) websocket._socket.resume();
|
|
38238
|
-
}
|
|
38239
|
-
function receiverOnError(err) {
|
|
38240
|
-
const websocket = this[kWebSocket];
|
|
38241
|
-
if (websocket._socket[kWebSocket] !== void 0) {
|
|
38242
|
-
websocket._socket.removeListener("data", socketOnData);
|
|
38243
|
-
process.nextTick(resume, websocket._socket);
|
|
38244
|
-
websocket.close(err[kStatusCode]);
|
|
38245
|
-
}
|
|
38246
|
-
if (!websocket._errorEmitted) {
|
|
38247
|
-
websocket._errorEmitted = true;
|
|
38248
|
-
websocket.emit("error", err);
|
|
38249
|
-
}
|
|
38250
|
-
}
|
|
38251
|
-
function receiverOnFinish() {
|
|
38252
|
-
this[kWebSocket].emitClose();
|
|
38253
|
-
}
|
|
38254
|
-
function receiverOnMessage(data, isBinary) {
|
|
38255
|
-
this[kWebSocket].emit("message", data, isBinary);
|
|
38256
|
-
}
|
|
38257
|
-
function receiverOnPing(data) {
|
|
38258
|
-
const websocket = this[kWebSocket];
|
|
38259
|
-
if (websocket._autoPong) websocket.pong(data, !this._isServer, NOOP);
|
|
38260
|
-
websocket.emit("ping", data);
|
|
38261
|
-
}
|
|
38262
|
-
function receiverOnPong(data) {
|
|
38263
|
-
this[kWebSocket].emit("pong", data);
|
|
38264
|
-
}
|
|
38265
|
-
function resume(stream) {
|
|
38266
|
-
stream.resume();
|
|
38267
|
-
}
|
|
38268
|
-
function senderOnError(err) {
|
|
38269
|
-
const websocket = this[kWebSocket];
|
|
38270
|
-
if (websocket.readyState === WebSocket2.CLOSED) return;
|
|
38271
|
-
if (websocket.readyState === WebSocket2.OPEN) {
|
|
38272
|
-
websocket._readyState = WebSocket2.CLOSING;
|
|
38273
|
-
setCloseTimer(websocket);
|
|
38274
|
-
}
|
|
38275
|
-
this._socket.end();
|
|
38276
|
-
if (!websocket._errorEmitted) {
|
|
38277
|
-
websocket._errorEmitted = true;
|
|
38278
|
-
websocket.emit("error", err);
|
|
38279
|
-
}
|
|
38280
|
-
}
|
|
38281
|
-
function setCloseTimer(websocket) {
|
|
38282
|
-
websocket._closeTimer = setTimeout(
|
|
38283
|
-
websocket._socket.destroy.bind(websocket._socket),
|
|
38284
|
-
closeTimeout
|
|
38285
|
-
);
|
|
38286
|
-
}
|
|
38287
|
-
function socketOnClose() {
|
|
38288
|
-
const websocket = this[kWebSocket];
|
|
38289
|
-
this.removeListener("close", socketOnClose);
|
|
38290
|
-
this.removeListener("data", socketOnData);
|
|
38291
|
-
this.removeListener("end", socketOnEnd);
|
|
38292
|
-
websocket._readyState = WebSocket2.CLOSING;
|
|
38293
|
-
let chunk;
|
|
38294
|
-
if (!this._readableState.endEmitted && !websocket._closeFrameReceived && !websocket._receiver._writableState.errorEmitted && (chunk = websocket._socket.read()) !== null) {
|
|
38295
|
-
websocket._receiver.write(chunk);
|
|
38296
|
-
}
|
|
38297
|
-
websocket._receiver.end();
|
|
38298
|
-
this[kWebSocket] = void 0;
|
|
38299
|
-
clearTimeout(websocket._closeTimer);
|
|
38300
|
-
if (websocket._receiver._writableState.finished || websocket._receiver._writableState.errorEmitted) {
|
|
38301
|
-
websocket.emitClose();
|
|
38302
|
-
} else {
|
|
38303
|
-
websocket._receiver.on("error", receiverOnFinish);
|
|
38304
|
-
websocket._receiver.on("finish", receiverOnFinish);
|
|
38305
|
-
}
|
|
38306
|
-
}
|
|
38307
|
-
function socketOnData(chunk) {
|
|
38308
|
-
if (!this[kWebSocket]._receiver.write(chunk)) {
|
|
38309
|
-
this.pause();
|
|
38310
|
-
}
|
|
38311
|
-
}
|
|
38312
|
-
function socketOnEnd() {
|
|
38313
|
-
const websocket = this[kWebSocket];
|
|
38314
|
-
websocket._readyState = WebSocket2.CLOSING;
|
|
38315
|
-
websocket._receiver.end();
|
|
38316
|
-
this.end();
|
|
38317
|
-
}
|
|
38318
|
-
function socketOnError() {
|
|
38319
|
-
const websocket = this[kWebSocket];
|
|
38320
|
-
this.removeListener("error", socketOnError);
|
|
38321
|
-
this.on("error", NOOP);
|
|
38322
|
-
if (websocket) {
|
|
38323
|
-
websocket._readyState = WebSocket2.CLOSING;
|
|
38324
|
-
this.destroy();
|
|
38325
|
-
}
|
|
38326
|
-
}
|
|
38327
|
-
}
|
|
38328
|
-
});
|
|
38329
|
-
|
|
38330
|
-
// node_modules/ws/lib/stream.js
|
|
38331
|
-
var require_stream = __commonJS({
|
|
38332
|
-
"node_modules/ws/lib/stream.js"(exports2, module2) {
|
|
38333
|
-
"use strict";
|
|
38334
|
-
var WebSocket2 = require_websocket();
|
|
38335
|
-
var { Duplex } = __require("stream");
|
|
38336
|
-
function emitClose(stream) {
|
|
38337
|
-
stream.emit("close");
|
|
38338
|
-
}
|
|
38339
|
-
function duplexOnEnd() {
|
|
38340
|
-
if (!this.destroyed && this._writableState.finished) {
|
|
38341
|
-
this.destroy();
|
|
38342
|
-
}
|
|
38343
|
-
}
|
|
38344
|
-
function duplexOnError(err) {
|
|
38345
|
-
this.removeListener("error", duplexOnError);
|
|
38346
|
-
this.destroy();
|
|
38347
|
-
if (this.listenerCount("error") === 0) {
|
|
38348
|
-
this.emit("error", err);
|
|
38349
|
-
}
|
|
38350
|
-
}
|
|
38351
|
-
function createWebSocketStream2(ws, options2) {
|
|
38352
|
-
let terminateOnDestroy = true;
|
|
38353
|
-
const duplex = new Duplex({
|
|
38354
|
-
...options2,
|
|
38355
|
-
autoDestroy: false,
|
|
38356
|
-
emitClose: false,
|
|
38357
|
-
objectMode: false,
|
|
38358
|
-
writableObjectMode: false
|
|
38359
|
-
});
|
|
38360
|
-
ws.on("message", function message(msg, isBinary) {
|
|
38361
|
-
const data = !isBinary && duplex._readableState.objectMode ? msg.toString() : msg;
|
|
38362
|
-
if (!duplex.push(data)) ws.pause();
|
|
38363
|
-
});
|
|
38364
|
-
ws.once("error", function error48(err) {
|
|
38365
|
-
if (duplex.destroyed) return;
|
|
38366
|
-
terminateOnDestroy = false;
|
|
38367
|
-
duplex.destroy(err);
|
|
38368
|
-
});
|
|
38369
|
-
ws.once("close", function close() {
|
|
38370
|
-
if (duplex.destroyed) return;
|
|
38371
|
-
duplex.push(null);
|
|
38372
|
-
});
|
|
38373
|
-
duplex._destroy = function(err, callback) {
|
|
38374
|
-
if (ws.readyState === ws.CLOSED) {
|
|
38375
|
-
callback(err);
|
|
38376
|
-
process.nextTick(emitClose, duplex);
|
|
38377
|
-
return;
|
|
38378
|
-
}
|
|
38379
|
-
let called = false;
|
|
38380
|
-
ws.once("error", function error48(err2) {
|
|
38381
|
-
called = true;
|
|
38382
|
-
callback(err2);
|
|
38383
|
-
});
|
|
38384
|
-
ws.once("close", function close() {
|
|
38385
|
-
if (!called) callback(err);
|
|
38386
|
-
process.nextTick(emitClose, duplex);
|
|
38387
|
-
});
|
|
38388
|
-
if (terminateOnDestroy) ws.terminate();
|
|
38389
|
-
};
|
|
38390
|
-
duplex._final = function(callback) {
|
|
38391
|
-
if (ws.readyState === ws.CONNECTING) {
|
|
38392
|
-
ws.once("open", function open() {
|
|
38393
|
-
duplex._final(callback);
|
|
38394
|
-
});
|
|
38395
|
-
return;
|
|
38396
|
-
}
|
|
38397
|
-
if (ws._socket === null) return;
|
|
38398
|
-
if (ws._socket._writableState.finished) {
|
|
38399
|
-
callback();
|
|
38400
|
-
if (duplex._readableState.endEmitted) duplex.destroy();
|
|
38401
|
-
} else {
|
|
38402
|
-
ws._socket.once("finish", function finish() {
|
|
38403
|
-
callback();
|
|
38404
|
-
});
|
|
38405
|
-
ws.close();
|
|
38406
|
-
}
|
|
38407
|
-
};
|
|
38408
|
-
duplex._read = function() {
|
|
38409
|
-
if (ws.isPaused) ws.resume();
|
|
38410
|
-
};
|
|
38411
|
-
duplex._write = function(chunk, encoding, callback) {
|
|
38412
|
-
if (ws.readyState === ws.CONNECTING) {
|
|
38413
|
-
ws.once("open", function open() {
|
|
38414
|
-
duplex._write(chunk, encoding, callback);
|
|
38415
|
-
});
|
|
38416
|
-
return;
|
|
38417
|
-
}
|
|
38418
|
-
ws.send(chunk, callback);
|
|
38419
|
-
};
|
|
38420
|
-
duplex.on("end", duplexOnEnd);
|
|
38421
|
-
duplex.on("error", duplexOnError);
|
|
38422
|
-
return duplex;
|
|
38423
|
-
}
|
|
38424
|
-
module2.exports = createWebSocketStream2;
|
|
38425
|
-
}
|
|
38426
|
-
});
|
|
38427
|
-
|
|
38428
|
-
// node_modules/ws/lib/subprotocol.js
|
|
38429
|
-
var require_subprotocol = __commonJS({
|
|
38430
|
-
"node_modules/ws/lib/subprotocol.js"(exports2, module2) {
|
|
38431
|
-
"use strict";
|
|
38432
|
-
var { tokenChars } = require_validation();
|
|
38433
|
-
function parse4(header) {
|
|
38434
|
-
const protocols = /* @__PURE__ */ new Set();
|
|
38435
|
-
let start = -1;
|
|
38436
|
-
let end = -1;
|
|
38437
|
-
let i = 0;
|
|
38438
|
-
for (i; i < header.length; i++) {
|
|
38439
|
-
const code = header.charCodeAt(i);
|
|
38440
|
-
if (end === -1 && tokenChars[code] === 1) {
|
|
38441
|
-
if (start === -1) start = i;
|
|
38442
|
-
} else if (i !== 0 && (code === 32 || code === 9)) {
|
|
38443
|
-
if (end === -1 && start !== -1) end = i;
|
|
38444
|
-
} else if (code === 44) {
|
|
38445
|
-
if (start === -1) {
|
|
38446
|
-
throw new SyntaxError(`Unexpected character at index ${i}`);
|
|
38447
|
-
}
|
|
38448
|
-
if (end === -1) end = i;
|
|
38449
|
-
const protocol2 = header.slice(start, end);
|
|
38450
|
-
if (protocols.has(protocol2)) {
|
|
38451
|
-
throw new SyntaxError(`The "${protocol2}" subprotocol is duplicated`);
|
|
38452
|
-
}
|
|
38453
|
-
protocols.add(protocol2);
|
|
38454
|
-
start = end = -1;
|
|
38455
|
-
} else {
|
|
38456
|
-
throw new SyntaxError(`Unexpected character at index ${i}`);
|
|
38457
|
-
}
|
|
38458
|
-
}
|
|
38459
|
-
if (start === -1 || end !== -1) {
|
|
38460
|
-
throw new SyntaxError("Unexpected end of input");
|
|
38461
|
-
}
|
|
38462
|
-
const protocol = header.slice(start, i);
|
|
38463
|
-
if (protocols.has(protocol)) {
|
|
38464
|
-
throw new SyntaxError(`The "${protocol}" subprotocol is duplicated`);
|
|
38465
|
-
}
|
|
38466
|
-
protocols.add(protocol);
|
|
38467
|
-
return protocols;
|
|
38468
|
-
}
|
|
38469
|
-
module2.exports = { parse: parse4 };
|
|
38470
|
-
}
|
|
38471
|
-
});
|
|
38472
|
-
|
|
38473
|
-
// node_modules/ws/lib/websocket-server.js
|
|
38474
|
-
var require_websocket_server = __commonJS({
|
|
38475
|
-
"node_modules/ws/lib/websocket-server.js"(exports2, module2) {
|
|
38476
|
-
"use strict";
|
|
38477
|
-
var EventEmitter = __require("events");
|
|
38478
|
-
var http = __require("http");
|
|
38479
|
-
var { Duplex } = __require("stream");
|
|
38480
|
-
var { createHash } = __require("crypto");
|
|
38481
|
-
var extension = require_extension();
|
|
38482
|
-
var PerMessageDeflate = require_permessage_deflate();
|
|
38483
|
-
var subprotocol = require_subprotocol();
|
|
38484
|
-
var WebSocket2 = require_websocket();
|
|
38485
|
-
var { GUID, kWebSocket } = require_constants();
|
|
38486
|
-
var keyRegex = /^[+/0-9A-Za-z]{22}==$/;
|
|
38487
|
-
var RUNNING = 0;
|
|
38488
|
-
var CLOSING = 1;
|
|
38489
|
-
var CLOSED = 2;
|
|
38490
|
-
var WebSocketServer2 = class extends EventEmitter {
|
|
38491
|
-
/**
|
|
38492
|
-
* Create a `WebSocketServer` instance.
|
|
38493
|
-
*
|
|
38494
|
-
* @param {Object} options Configuration options
|
|
38495
|
-
* @param {Boolean} [options.allowSynchronousEvents=true] Specifies whether
|
|
38496
|
-
* any of the `'message'`, `'ping'`, and `'pong'` events can be emitted
|
|
38497
|
-
* multiple times in the same tick
|
|
38498
|
-
* @param {Boolean} [options.autoPong=true] Specifies whether or not to
|
|
38499
|
-
* automatically send a pong in response to a ping
|
|
38500
|
-
* @param {Number} [options.backlog=511] The maximum length of the queue of
|
|
38501
|
-
* pending connections
|
|
38502
|
-
* @param {Boolean} [options.clientTracking=true] Specifies whether or not to
|
|
38503
|
-
* track clients
|
|
38504
|
-
* @param {Function} [options.handleProtocols] A hook to handle protocols
|
|
38505
|
-
* @param {String} [options.host] The hostname where to bind the server
|
|
38506
|
-
* @param {Number} [options.maxPayload=104857600] The maximum allowed message
|
|
38507
|
-
* size
|
|
38508
|
-
* @param {Boolean} [options.noServer=false] Enable no server mode
|
|
38509
|
-
* @param {String} [options.path] Accept only connections matching this path
|
|
38510
|
-
* @param {(Boolean|Object)} [options.perMessageDeflate=false] Enable/disable
|
|
38511
|
-
* permessage-deflate
|
|
38512
|
-
* @param {Number} [options.port] The port where to bind the server
|
|
38513
|
-
* @param {(http.Server|https.Server)} [options.server] A pre-created HTTP/S
|
|
38514
|
-
* server to use
|
|
38515
|
-
* @param {Boolean} [options.skipUTF8Validation=false] Specifies whether or
|
|
38516
|
-
* not to skip UTF-8 validation for text and close messages
|
|
38517
|
-
* @param {Function} [options.verifyClient] A hook to reject connections
|
|
38518
|
-
* @param {Function} [options.WebSocket=WebSocket] Specifies the `WebSocket`
|
|
38519
|
-
* class to use. It must be the `WebSocket` class or class that extends it
|
|
38520
|
-
* @param {Function} [callback] A listener for the `listening` event
|
|
38521
|
-
*/
|
|
38522
|
-
constructor(options2, callback) {
|
|
38523
|
-
super();
|
|
38524
|
-
options2 = {
|
|
38525
|
-
allowSynchronousEvents: true,
|
|
38526
|
-
autoPong: true,
|
|
38527
|
-
maxPayload: 100 * 1024 * 1024,
|
|
38528
|
-
skipUTF8Validation: false,
|
|
38529
|
-
perMessageDeflate: false,
|
|
38530
|
-
handleProtocols: null,
|
|
38531
|
-
clientTracking: true,
|
|
38532
|
-
verifyClient: null,
|
|
38533
|
-
noServer: false,
|
|
38534
|
-
backlog: null,
|
|
38535
|
-
// use default (511 as implemented in net.js)
|
|
38536
|
-
server: null,
|
|
38537
|
-
host: null,
|
|
38538
|
-
path: null,
|
|
38539
|
-
port: null,
|
|
38540
|
-
WebSocket: WebSocket2,
|
|
38541
|
-
...options2
|
|
38542
|
-
};
|
|
38543
|
-
if (options2.port == null && !options2.server && !options2.noServer || options2.port != null && (options2.server || options2.noServer) || options2.server && options2.noServer) {
|
|
38544
|
-
throw new TypeError(
|
|
38545
|
-
'One and only one of the "port", "server", or "noServer" options must be specified'
|
|
38546
|
-
);
|
|
38547
|
-
}
|
|
38548
|
-
if (options2.port != null) {
|
|
38549
|
-
this._server = http.createServer((req, res) => {
|
|
38550
|
-
const body = http.STATUS_CODES[426];
|
|
38551
|
-
res.writeHead(426, {
|
|
38552
|
-
"Content-Length": body.length,
|
|
38553
|
-
"Content-Type": "text/plain"
|
|
38554
|
-
});
|
|
38555
|
-
res.end(body);
|
|
38556
|
-
});
|
|
38557
|
-
this._server.listen(
|
|
38558
|
-
options2.port,
|
|
38559
|
-
options2.host,
|
|
38560
|
-
options2.backlog,
|
|
38561
|
-
callback
|
|
38562
|
-
);
|
|
38563
|
-
} else if (options2.server) {
|
|
38564
|
-
this._server = options2.server;
|
|
38565
|
-
}
|
|
38566
|
-
if (this._server) {
|
|
38567
|
-
const emitConnection = this.emit.bind(this, "connection");
|
|
38568
|
-
this._removeListeners = addListeners(this._server, {
|
|
38569
|
-
listening: this.emit.bind(this, "listening"),
|
|
38570
|
-
error: this.emit.bind(this, "error"),
|
|
38571
|
-
upgrade: (req, socket, head) => {
|
|
38572
|
-
this.handleUpgrade(req, socket, head, emitConnection);
|
|
38573
|
-
}
|
|
38574
|
-
});
|
|
38575
|
-
}
|
|
38576
|
-
if (options2.perMessageDeflate === true) options2.perMessageDeflate = {};
|
|
38577
|
-
if (options2.clientTracking) {
|
|
38578
|
-
this.clients = /* @__PURE__ */ new Set();
|
|
38579
|
-
this._shouldEmitClose = false;
|
|
38580
|
-
}
|
|
38581
|
-
this.options = options2;
|
|
38582
|
-
this._state = RUNNING;
|
|
38583
|
-
}
|
|
38584
|
-
/**
|
|
38585
|
-
* Returns the bound address, the address family name, and port of the server
|
|
38586
|
-
* as reported by the operating system if listening on an IP socket.
|
|
38587
|
-
* If the server is listening on a pipe or UNIX domain socket, the name is
|
|
38588
|
-
* returned as a string.
|
|
38589
|
-
*
|
|
38590
|
-
* @return {(Object|String|null)} The address of the server
|
|
38591
|
-
* @public
|
|
38592
|
-
*/
|
|
38593
|
-
address() {
|
|
38594
|
-
if (this.options.noServer) {
|
|
38595
|
-
throw new Error('The server is operating in "noServer" mode');
|
|
38596
|
-
}
|
|
38597
|
-
if (!this._server) return null;
|
|
38598
|
-
return this._server.address();
|
|
38599
|
-
}
|
|
38600
|
-
/**
|
|
38601
|
-
* Stop the server from accepting new connections and emit the `'close'` event
|
|
38602
|
-
* when all existing connections are closed.
|
|
38603
|
-
*
|
|
38604
|
-
* @param {Function} [cb] A one-time listener for the `'close'` event
|
|
38605
|
-
* @public
|
|
38606
|
-
*/
|
|
38607
|
-
close(cb) {
|
|
38608
|
-
if (this._state === CLOSED) {
|
|
38609
|
-
if (cb) {
|
|
38610
|
-
this.once("close", () => {
|
|
38611
|
-
cb(new Error("The server is not running"));
|
|
38612
|
-
});
|
|
38613
|
-
}
|
|
38614
|
-
process.nextTick(emitClose, this);
|
|
38615
|
-
return;
|
|
38616
|
-
}
|
|
38617
|
-
if (cb) this.once("close", cb);
|
|
38618
|
-
if (this._state === CLOSING) return;
|
|
38619
|
-
this._state = CLOSING;
|
|
38620
|
-
if (this.options.noServer || this.options.server) {
|
|
38621
|
-
if (this._server) {
|
|
38622
|
-
this._removeListeners();
|
|
38623
|
-
this._removeListeners = this._server = null;
|
|
38624
|
-
}
|
|
38625
|
-
if (this.clients) {
|
|
38626
|
-
if (!this.clients.size) {
|
|
38627
|
-
process.nextTick(emitClose, this);
|
|
38628
|
-
} else {
|
|
38629
|
-
this._shouldEmitClose = true;
|
|
38630
|
-
}
|
|
38631
|
-
} else {
|
|
38632
|
-
process.nextTick(emitClose, this);
|
|
38633
|
-
}
|
|
38634
|
-
} else {
|
|
38635
|
-
const server2 = this._server;
|
|
38636
|
-
this._removeListeners();
|
|
38637
|
-
this._removeListeners = this._server = null;
|
|
38638
|
-
server2.close(() => {
|
|
38639
|
-
emitClose(this);
|
|
38640
|
-
});
|
|
38641
|
-
}
|
|
38642
|
-
}
|
|
38643
|
-
/**
|
|
38644
|
-
* See if a given request should be handled by this server instance.
|
|
38645
|
-
*
|
|
38646
|
-
* @param {http.IncomingMessage} req Request object to inspect
|
|
38647
|
-
* @return {Boolean} `true` if the request is valid, else `false`
|
|
38648
|
-
* @public
|
|
38649
|
-
*/
|
|
38650
|
-
shouldHandle(req) {
|
|
38651
|
-
if (this.options.path) {
|
|
38652
|
-
const index = req.url.indexOf("?");
|
|
38653
|
-
const pathname = index !== -1 ? req.url.slice(0, index) : req.url;
|
|
38654
|
-
if (pathname !== this.options.path) return false;
|
|
38655
|
-
}
|
|
38656
|
-
return true;
|
|
38657
|
-
}
|
|
38658
|
-
/**
|
|
38659
|
-
* Handle a HTTP Upgrade request.
|
|
38660
|
-
*
|
|
38661
|
-
* @param {http.IncomingMessage} req The request object
|
|
38662
|
-
* @param {Duplex} socket The network socket between the server and client
|
|
38663
|
-
* @param {Buffer} head The first packet of the upgraded stream
|
|
38664
|
-
* @param {Function} cb Callback
|
|
38665
|
-
* @public
|
|
38666
|
-
*/
|
|
38667
|
-
handleUpgrade(req, socket, head, cb) {
|
|
38668
|
-
socket.on("error", socketOnError);
|
|
38669
|
-
const key = req.headers["sec-websocket-key"];
|
|
38670
|
-
const upgrade = req.headers.upgrade;
|
|
38671
|
-
const version2 = +req.headers["sec-websocket-version"];
|
|
38672
|
-
if (req.method !== "GET") {
|
|
38673
|
-
const message = "Invalid HTTP method";
|
|
38674
|
-
abortHandshakeOrEmitwsClientError(this, req, socket, 405, message);
|
|
38675
|
-
return;
|
|
38676
|
-
}
|
|
38677
|
-
if (upgrade === void 0 || upgrade.toLowerCase() !== "websocket") {
|
|
38678
|
-
const message = "Invalid Upgrade header";
|
|
38679
|
-
abortHandshakeOrEmitwsClientError(this, req, socket, 400, message);
|
|
38680
|
-
return;
|
|
38681
|
-
}
|
|
38682
|
-
if (key === void 0 || !keyRegex.test(key)) {
|
|
38683
|
-
const message = "Missing or invalid Sec-WebSocket-Key header";
|
|
38684
|
-
abortHandshakeOrEmitwsClientError(this, req, socket, 400, message);
|
|
38685
|
-
return;
|
|
38686
|
-
}
|
|
38687
|
-
if (version2 !== 13 && version2 !== 8) {
|
|
38688
|
-
const message = "Missing or invalid Sec-WebSocket-Version header";
|
|
38689
|
-
abortHandshakeOrEmitwsClientError(this, req, socket, 400, message, {
|
|
38690
|
-
"Sec-WebSocket-Version": "13, 8"
|
|
38691
|
-
});
|
|
38692
|
-
return;
|
|
38693
|
-
}
|
|
38694
|
-
if (!this.shouldHandle(req)) {
|
|
38695
|
-
abortHandshake(socket, 400);
|
|
38696
|
-
return;
|
|
38697
|
-
}
|
|
38698
|
-
const secWebSocketProtocol = req.headers["sec-websocket-protocol"];
|
|
38699
|
-
let protocols = /* @__PURE__ */ new Set();
|
|
38700
|
-
if (secWebSocketProtocol !== void 0) {
|
|
38701
|
-
try {
|
|
38702
|
-
protocols = subprotocol.parse(secWebSocketProtocol);
|
|
38703
|
-
} catch (err) {
|
|
38704
|
-
const message = "Invalid Sec-WebSocket-Protocol header";
|
|
38705
|
-
abortHandshakeOrEmitwsClientError(this, req, socket, 400, message);
|
|
38706
|
-
return;
|
|
38707
|
-
}
|
|
38708
|
-
}
|
|
38709
|
-
const secWebSocketExtensions = req.headers["sec-websocket-extensions"];
|
|
38710
|
-
const extensions = {};
|
|
38711
|
-
if (this.options.perMessageDeflate && secWebSocketExtensions !== void 0) {
|
|
38712
|
-
const perMessageDeflate = new PerMessageDeflate(
|
|
38713
|
-
this.options.perMessageDeflate,
|
|
38714
|
-
true,
|
|
38715
|
-
this.options.maxPayload
|
|
38716
|
-
);
|
|
38717
|
-
try {
|
|
38718
|
-
const offers = extension.parse(secWebSocketExtensions);
|
|
38719
|
-
if (offers[PerMessageDeflate.extensionName]) {
|
|
38720
|
-
perMessageDeflate.accept(offers[PerMessageDeflate.extensionName]);
|
|
38721
|
-
extensions[PerMessageDeflate.extensionName] = perMessageDeflate;
|
|
38722
|
-
}
|
|
38723
|
-
} catch (err) {
|
|
38724
|
-
const message = "Invalid or unacceptable Sec-WebSocket-Extensions header";
|
|
38725
|
-
abortHandshakeOrEmitwsClientError(this, req, socket, 400, message);
|
|
38726
|
-
return;
|
|
38727
|
-
}
|
|
38728
|
-
}
|
|
38729
|
-
if (this.options.verifyClient) {
|
|
38730
|
-
const info = {
|
|
38731
|
-
origin: req.headers[`${version2 === 8 ? "sec-websocket-origin" : "origin"}`],
|
|
38732
|
-
secure: !!(req.socket.authorized || req.socket.encrypted),
|
|
38733
|
-
req
|
|
38734
|
-
};
|
|
38735
|
-
if (this.options.verifyClient.length === 2) {
|
|
38736
|
-
this.options.verifyClient(info, (verified, code, message, headers) => {
|
|
38737
|
-
if (!verified) {
|
|
38738
|
-
return abortHandshake(socket, code || 401, message, headers);
|
|
38739
|
-
}
|
|
38740
|
-
this.completeUpgrade(
|
|
38741
|
-
extensions,
|
|
38742
|
-
key,
|
|
38743
|
-
protocols,
|
|
38744
|
-
req,
|
|
38745
|
-
socket,
|
|
38746
|
-
head,
|
|
38747
|
-
cb
|
|
38748
|
-
);
|
|
38749
|
-
});
|
|
38750
|
-
return;
|
|
38751
|
-
}
|
|
38752
|
-
if (!this.options.verifyClient(info)) return abortHandshake(socket, 401);
|
|
38753
|
-
}
|
|
38754
|
-
this.completeUpgrade(extensions, key, protocols, req, socket, head, cb);
|
|
38755
|
-
}
|
|
38756
|
-
/**
|
|
38757
|
-
* Upgrade the connection to WebSocket.
|
|
38758
|
-
*
|
|
38759
|
-
* @param {Object} extensions The accepted extensions
|
|
38760
|
-
* @param {String} key The value of the `Sec-WebSocket-Key` header
|
|
38761
|
-
* @param {Set} protocols The subprotocols
|
|
38762
|
-
* @param {http.IncomingMessage} req The request object
|
|
38763
|
-
* @param {Duplex} socket The network socket between the server and client
|
|
38764
|
-
* @param {Buffer} head The first packet of the upgraded stream
|
|
38765
|
-
* @param {Function} cb Callback
|
|
38766
|
-
* @throws {Error} If called more than once with the same socket
|
|
38767
|
-
* @private
|
|
38768
|
-
*/
|
|
38769
|
-
completeUpgrade(extensions, key, protocols, req, socket, head, cb) {
|
|
38770
|
-
if (!socket.readable || !socket.writable) return socket.destroy();
|
|
38771
|
-
if (socket[kWebSocket]) {
|
|
38772
|
-
throw new Error(
|
|
38773
|
-
"server.handleUpgrade() was called more than once with the same socket, possibly due to a misconfiguration"
|
|
38774
|
-
);
|
|
38775
|
-
}
|
|
38776
|
-
if (this._state > RUNNING) return abortHandshake(socket, 503);
|
|
38777
|
-
const digest = createHash("sha1").update(key + GUID).digest("base64");
|
|
38778
|
-
const headers = [
|
|
38779
|
-
"HTTP/1.1 101 Switching Protocols",
|
|
38780
|
-
"Upgrade: websocket",
|
|
38781
|
-
"Connection: Upgrade",
|
|
38782
|
-
`Sec-WebSocket-Accept: ${digest}`
|
|
38783
|
-
];
|
|
38784
|
-
const ws = new this.options.WebSocket(null, void 0, this.options);
|
|
38785
|
-
if (protocols.size) {
|
|
38786
|
-
const protocol = this.options.handleProtocols ? this.options.handleProtocols(protocols, req) : protocols.values().next().value;
|
|
38787
|
-
if (protocol) {
|
|
38788
|
-
headers.push(`Sec-WebSocket-Protocol: ${protocol}`);
|
|
38789
|
-
ws._protocol = protocol;
|
|
38790
|
-
}
|
|
38791
|
-
}
|
|
38792
|
-
if (extensions[PerMessageDeflate.extensionName]) {
|
|
38793
|
-
const params = extensions[PerMessageDeflate.extensionName].params;
|
|
38794
|
-
const value = extension.format({
|
|
38795
|
-
[PerMessageDeflate.extensionName]: [params]
|
|
38796
|
-
});
|
|
38797
|
-
headers.push(`Sec-WebSocket-Extensions: ${value}`);
|
|
38798
|
-
ws._extensions = extensions;
|
|
38799
|
-
}
|
|
38800
|
-
this.emit("headers", headers, req);
|
|
38801
|
-
socket.write(headers.concat("\r\n").join("\r\n"));
|
|
38802
|
-
socket.removeListener("error", socketOnError);
|
|
38803
|
-
ws.setSocket(socket, head, {
|
|
38804
|
-
allowSynchronousEvents: this.options.allowSynchronousEvents,
|
|
38805
|
-
maxPayload: this.options.maxPayload,
|
|
38806
|
-
skipUTF8Validation: this.options.skipUTF8Validation
|
|
38807
|
-
});
|
|
38808
|
-
if (this.clients) {
|
|
38809
|
-
this.clients.add(ws);
|
|
38810
|
-
ws.on("close", () => {
|
|
38811
|
-
this.clients.delete(ws);
|
|
38812
|
-
if (this._shouldEmitClose && !this.clients.size) {
|
|
38813
|
-
process.nextTick(emitClose, this);
|
|
38814
|
-
}
|
|
38815
|
-
});
|
|
38816
|
-
}
|
|
38817
|
-
cb(ws, req);
|
|
38818
|
-
}
|
|
38819
|
-
};
|
|
38820
|
-
module2.exports = WebSocketServer2;
|
|
38821
|
-
function addListeners(server2, map2) {
|
|
38822
|
-
for (const event of Object.keys(map2)) server2.on(event, map2[event]);
|
|
38823
|
-
return function removeListeners() {
|
|
38824
|
-
for (const event of Object.keys(map2)) {
|
|
38825
|
-
server2.removeListener(event, map2[event]);
|
|
38826
|
-
}
|
|
38827
|
-
};
|
|
38828
|
-
}
|
|
38829
|
-
function emitClose(server2) {
|
|
38830
|
-
server2._state = CLOSED;
|
|
38831
|
-
server2.emit("close");
|
|
38832
|
-
}
|
|
38833
|
-
function socketOnError() {
|
|
38834
|
-
this.destroy();
|
|
38835
|
-
}
|
|
38836
|
-
function abortHandshake(socket, code, message, headers) {
|
|
38837
|
-
message = message || http.STATUS_CODES[code];
|
|
38838
|
-
headers = {
|
|
38839
|
-
Connection: "close",
|
|
38840
|
-
"Content-Type": "text/html",
|
|
38841
|
-
"Content-Length": Buffer.byteLength(message),
|
|
38842
|
-
...headers
|
|
38843
|
-
};
|
|
38844
|
-
socket.once("finish", socket.destroy);
|
|
38845
|
-
socket.end(
|
|
38846
|
-
`HTTP/1.1 ${code} ${http.STATUS_CODES[code]}\r
|
|
38847
|
-
` + Object.keys(headers).map((h) => `${h}: ${headers[h]}`).join("\r\n") + "\r\n\r\n" + message
|
|
38848
|
-
);
|
|
38849
|
-
}
|
|
38850
|
-
function abortHandshakeOrEmitwsClientError(server2, req, socket, code, message, headers) {
|
|
38851
|
-
if (server2.listenerCount("wsClientError")) {
|
|
38852
|
-
const err = new Error(message);
|
|
38853
|
-
Error.captureStackTrace(err, abortHandshakeOrEmitwsClientError);
|
|
38854
|
-
server2.emit("wsClientError", err, socket, req);
|
|
38855
|
-
} else {
|
|
38856
|
-
abortHandshake(socket, code, message, headers);
|
|
38857
|
-
}
|
|
38858
|
-
}
|
|
38859
|
-
}
|
|
38860
|
-
});
|
|
38861
|
-
|
|
38862
35254
|
// node_modules/ajv/dist/compile/codegen/code.js
|
|
38863
35255
|
var require_code = __commonJS({
|
|
38864
35256
|
"node_modules/ajv/dist/compile/codegen/code.js"(exports2) {
|
|
@@ -43874,7 +40266,7 @@ var require_enum = __commonJS({
|
|
|
43874
40266
|
});
|
|
43875
40267
|
|
|
43876
40268
|
// node_modules/ajv/dist/vocabularies/validation/index.js
|
|
43877
|
-
var
|
|
40269
|
+
var require_validation = __commonJS({
|
|
43878
40270
|
"node_modules/ajv/dist/vocabularies/validation/index.js"(exports2) {
|
|
43879
40271
|
"use strict";
|
|
43880
40272
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
@@ -44940,7 +41332,7 @@ var require_draft7 = __commonJS({
|
|
|
44940
41332
|
"use strict";
|
|
44941
41333
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
44942
41334
|
var core_1 = require_core3();
|
|
44943
|
-
var validation_1 =
|
|
41335
|
+
var validation_1 = require_validation();
|
|
44944
41336
|
var applicator_1 = require_applicator();
|
|
44945
41337
|
var format_1 = require_format2();
|
|
44946
41338
|
var metadata_1 = require_metadata();
|
|
@@ -45620,7 +42012,6 @@ var require_dist3 = __commonJS({
|
|
|
45620
42012
|
});
|
|
45621
42013
|
|
|
45622
42014
|
// src/commands/init.ts
|
|
45623
|
-
import { execSync } from "node:child_process";
|
|
45624
42015
|
import { existsSync as existsSync2 } from "node:fs";
|
|
45625
42016
|
import { basename, join as join4 } from "node:path";
|
|
45626
42017
|
|
|
@@ -47067,16 +43458,16 @@ import { dirname, join as join3 } from "node:path";
|
|
|
47067
43458
|
var import_prompts = __toESM(require_prompts3(), 1);
|
|
47068
43459
|
|
|
47069
43460
|
// src/templates/cli/gemini.md
|
|
47070
|
-
var gemini_default = '<!-- KNOWNS GUIDELINES START -->\n# Knowns CLI - Gemini Quick Reference\n\n## RULES\n- NEVER edit .md files directly - use CLI only\n-
|
|
43461
|
+
var gemini_default = '<!-- KNOWNS GUIDELINES START -->\n# Knowns CLI - Gemini Quick Reference\n\n## RULES\n- NEVER edit .md files directly - use CLI only\n- Use `--plain` flag for VIEW/LIST/SEARCH commands only (NOT for create/edit)\n- Read docs BEFORE coding\n- Start timer when taking task, stop when done\n\n## SESSION START\n```bash\nknowns doc list --plain\nknowns doc "README" --plain\nknowns task list --plain\n```\n\n## TASK WORKFLOW\n\n### 1. Take Task\n```bash\nknowns task <id> --plain # View task\nknowns task edit <id> -s in-progress -a @me\nknowns time start <id>\n```\n\n### 2. Read Context\n```bash\nknowns doc list "guides/" --plain # List by folder\nknowns doc "path/name" --plain # View doc\nknowns search "keyword" --type doc --plain\n```\n\n### 3. Plan & Implement\n```bash\nknowns task edit <id> --plan $\'1. Step one\\n2. Step two\'\n# Wait for approval, then code\nknowns task edit <id> --check-ac 1 # Check criteria after done\nknowns task edit <id> --append-notes "Done: feature X"\n```\n\n### 4. Complete\n```bash\nknowns time stop\nknowns task edit <id> -s done\n```\n\n## COMMANDS CHEATSHEET\n\n### Task\n```bash\nknowns task list --plain\nknowns task list --status in-progress --plain\nknowns task <id> --plain\nknowns task create "Title" -d "Desc" --ac "Criterion" --priority high\nknowns task edit <id> -s <status> -a @me\nknowns task edit <id> --check-ac 1 --check-ac 2\nknowns task edit <id> --plan "..."\nknowns task edit <id> --notes "..."\nknowns task edit <id> --append-notes "..."\n```\n\n### Doc\n```bash\nknowns doc list --plain\nknowns doc list "folder/" --plain\nknowns doc "name" --plain\nknowns doc create "Title" -d "Desc" -t "tags" -f "folder"\nknowns doc edit "name" -c "content"\nknowns doc edit "name" -a "append"\nknowns doc edit "name" --content-file ./file.md\nknowns doc edit "name" --append-file ./file.md\nknowns doc search-in "name" "query" --plain\nknowns doc replace "name" "old" "new"\n```\n\n**Doc Organization:**\n| Type | Location |\n|------|----------|\n| Core docs | Root `.knowns/docs/` (no -f flag) |\n| Categorized | `.knowns/docs/<folder>/` (use -f flag) |\n\n### Time\n```bash\nknowns time start <id>\nknowns time stop\nknowns time status\nknowns time add <id> 2h -n "Note"\n```\n\n### Search\n```bash\nknowns search "query" --plain\nknowns search "query" --type doc --plain\nknowns search "query" --type task --plain\n```\n\n## REFS\n\n### Reading (output format)\n- `@.knowns/tasks/task-X - Title.md` \u2192 `knowns task X --plain`\n- `@.knowns/docs/path/name.md` \u2192 `knowns doc "path/name" --plain`\n\n### Writing (input format)\n- Task: `@task-X`\n- Doc: `@doc/path/name`\n\n## STATUS & PRIORITY\n\n**Status:** `todo`, `in-progress`, `in-review`, `blocked`, `done`\n**Priority:** `low`, `medium`, `high`\n\n## --plain FLAG\n\n\u26A0\uFE0F **CRITICAL**: Only use `--plain` with VIEW/LIST/SEARCH commands!\n\n| \u2705 Supports --plain | \u274C NO --plain |\n|---------------------|---------------|\n| `task <id> --plain` | `task create` |\n| `task list --plain` | `task edit` |\n| `doc <path> --plain` | `doc create` |\n| `doc list --plain` | `doc edit` |\n| `search --plain` | `time start/stop/add` |\n\n## LONG CONTENT\n\nWindows has ~8191 char limit. Use:\n\n```bash\n# Append in chunks\nknowns doc edit "name" -a "Section 1..."\nknowns doc edit "name" -a "Section 2..."\n\n# Or file-based\nknowns doc edit "name" --content-file ./content.md\nknowns doc edit "name" --append-file ./more.md\n```\n\n## VALIDATE & REPAIR\n\n```bash\nknowns doc validate "name" --plain\nknowns doc repair "name" --plain\nknowns task validate <id> --plain\nknowns task repair <id> --plain\n```\n<!-- KNOWNS GUIDELINES END -->\n';
|
|
47071
43462
|
|
|
47072
43463
|
// src/templates/cli/general.md
|
|
47073
|
-
var general_default = '<!-- KNOWNS GUIDELINES START -->\n# Knowns CLI Guidelines\n\n## Core Principles\n\n### 1. CLI-Only Operations\n**NEVER edit .md files directly. ALL operations MUST use CLI commands.**\n\nThis ensures data integrity, maintains proper change history, and prevents file corruption.\n\n### 2. Documentation-First (For AI Agents)\n**ALWAYS read project documentation BEFORE planning or coding.**\n\nAI agents must understand project context, conventions, and existing patterns before making any changes. This prevents rework and ensures consistency.\n\n---\n\n## AI Agent Guidelines\n\n> **CRITICAL**: Before performing ANY task, AI agents MUST read documentation to understand project context.\n\n### First-Time Initialization\n\nWhen starting a new session or working on an unfamiliar project:\n\n```bash\n# 1. List all available documentation\nknowns doc list --plain\n\n# 2. Read essential project docs (prioritize these)\nknowns doc "README" --plain # Project overview\nknowns doc "ARCHITECTURE" --plain # System design\nknowns doc "CONVENTIONS" --plain # Coding standards\nknowns doc "API" --plain # API specifications\n\n# 3. Review current task backlog\nknowns task list --plain\nknowns task list --status in-progress --plain\n```\n\n### Before Taking Any Task\n\n```bash\n# 1. View the task details\nknowns task <id> --plain\n\n# 2. Follow ALL refs in the task (see Reference System section)\n# @.knowns/tasks/task-44 - ... \u2192 knowns task 44 --plain\n# @.knowns/docs/patterns/module.md \u2192 knowns doc "patterns/module" --plain\n\n# 3. Search for additional related documentation\nknowns search "<keywords from task>" --type doc --plain\n\n# 4. Read ALL related docs before planning\nknowns doc "<related-doc>" --plain\n\n# 5. Check for similar completed tasks (learn from history)\nknowns search "<keywords>" --type task --status done --plain\n```\n\n### Why Documentation First?\n\n| Without Reading Docs | With Reading Docs |\n|---------------------|-------------------|\n| Reinvent existing patterns | Reuse established patterns |\n| Break conventions | Follow project standards |\n| Duplicate code | Use existing utilities |\n| Wrong architecture decisions | Align with system design |\n| Inconsistent naming | Match naming conventions |\n\n### Context Checklist for Agents\n\nBefore writing ANY code, ensure you can answer:\n\n- [ ] Have I followed ALL refs (`@.knowns/...`) in the task?\n- [ ] Have I followed nested refs recursively?\n- [ ] What is the project\'s overall architecture?\n- [ ] What coding conventions does this project follow?\n- [ ] Are there existing patterns/utilities I should reuse?\n- [ ] What are the testing requirements?\n- [ ] How should I structure my implementation?\n\n> **Remember**: A few minutes reading docs saves hours of rework. NEVER skip this step.\n\n---\n\n## Reference System (Refs)\n\nTasks and docs can contain **references** to other tasks/docs. AI agents MUST understand and follow these refs to gather complete context.\n\n### Reference Formats\n\n| Type | When Writing (Input) | When Reading (Output) | CLI Command |\n|------|---------------------|----------------------|-------------|\n| **Task ref** | `@task-<id>` | `@.knowns/tasks/task-<id> - <title>.md` | `knowns task <id> --plain` |\n| **Doc ref** | `@doc/<path>` | `@.knowns/docs/<path>.md` | `knowns doc <path> --plain` |\n\n> **CRITICAL for AI Agents**:\n> - When **WRITING** refs (in descriptions, plans, notes): Use `@task-<id>` and `@doc/<path>`\n> - When **READING** output from `--plain`: You\'ll see `@.knowns/tasks/...` and `@.knowns/docs/...`\n> - **NEVER write** the output format (`@.knowns/...`) - always use input format (`@task-<id>`, `@doc/<path>`)\n\n### How to Follow Refs\n\nWhen you read a task and see refs in system output format, follow them:\n\n```bash\n# Example: Task 42 output contains:\n# @.knowns/tasks/task-44 - CLI Task Create Command.md\n# @.knowns/docs/patterns/module.md\n\n# Follow task ref (extract ID from task-<id>)\nknowns task 44 --plain\n\n# Follow doc ref (extract path, remove .md)\nknowns doc "patterns/module" --plain\n```\n\n### Parsing Rules\n\n1. **Task refs**: `@.knowns/tasks/task-<id> - ...` \u2192 extract `<id>` \u2192 `knowns task <id> --plain`\n2. **Doc refs**: `@.knowns/docs/<path>.md` \u2192 extract `<path>` \u2192 `knowns doc "<path>" --plain`\n\n### Recursive Following\n\nRefs can be nested. Follow until complete context is gathered:\n\n```\nTask 42\n \u2192 @.knowns/docs/README.md\n \u2192 @.knowns/docs/patterns/module.md (found in README)\n \u2192 (read for full pattern details)\n```\n\n### When to Follow Refs\n\n| Situation | Action |\n|-----------|--------|\n| Refs in Description | ALWAYS follow - critical context |\n| Refs in Implementation Plan | Follow if implementing related work |\n| Refs in Notes | Optional - for historical context |\n| Dependency mentions | Follow if marked as blocker |\n\n### Example: Complete Ref Resolution\n\n```bash\n# 1. Read the task\n$ knowns task 42 --plain\n\n# Output contains:\n# @.knowns/tasks/task-44 - CLI Task Create Command.md\n# @.knowns/docs/README.md\n\n# 2. Follow task ref\n$ knowns task 44 --plain\n\n# 3. Follow doc ref\n$ knowns doc "README" --plain\n\n# 4. If README contains more refs, follow them too\n# @.knowns/docs/patterns/module.md \u2192 knowns doc "patterns/module" --plain\n\n# Now you have complete context\n```\n\n> **CRITICAL**: Never assume you understand a task fully without following its refs. Refs contain essential context that may change your implementation approach.\n\n---\n\n## Quick Start\n\n```bash\n# Initialize project\nknowns init [name]\n\n# Create task with acceptance criteria\nknowns task create "Title" -d "Description" --ac "Criterion 1" --ac "Criterion 2"\n\n# View task (ALWAYS use --plain for AI)\nknowns task <id> --plain # Shorthand\nknowns task view <id> --plain # Full command\n\n# View doc (ALWAYS use --plain for AI)\nknowns doc <path> --plain # Shorthand\nknowns doc view "<path>" --plain # Full command\n\n# List tasks\nknowns task list --plain\n\n# Search (tasks + docs)\nknowns search "query" --plain\n```\n\n---\n\n## End-to-End Example\n\nHere\'s a complete workflow for an AI agent implementing a feature:\n\n```bash\n# === AGENT SESSION START (Do this once per session) ===\n\n# 0a. List all available documentation\n$ knowns doc list --plain\n\n# Output:\n# DOC: README.md\n# DOC: ARCHITECTURE.md\n# DOC: CONVENTIONS.md\n# DOC: security-patterns.md\n# DOC: api-guidelines.md\n# DOC: email-templates.md\n\n# 0b. Read essential project docs\n$ knowns doc "README" --plain\n$ knowns doc "ARCHITECTURE" --plain\n$ knowns doc "CONVENTIONS" --plain\n\n# Now the agent understands project context and conventions\n\n# === TASK WORKFLOW ===\n\n# 1. Create the task\n$ knowns task create "Add password reset flow" \\\n -d "Users need ability to reset forgotten passwords via email" \\\n --ac "User can request password reset via email" \\\n --ac "Reset link expires after 1 hour" \\\n --ac "User can set new password via reset link" \\\n --ac "Unit tests cover all scenarios" \\\n --priority high \\\n -l "auth,feature"\n\n# Output: Created task AUTH-042\n\n# 2. Take the task and start timer (uses defaultAssignee or @me fallback)\n$ knowns task edit AUTH-042 -s in-progress -a $(knowns config get defaultAssignee --plain || echo "@me")\n$ knowns time start AUTH-042\n\n# Output: Timer started for AUTH-042\n\n# 3. Search for related documentation\n$ knowns search "password security" --type doc --plain\n\n# Output:\n# DOC: security-patterns.md (score: 0.92)\n# DOC: email-templates.md (score: 0.78)\n\n# 4. Read the documentation\n$ knowns doc "security-patterns" --plain\n\n# 5. Create implementation plan (SHARE WITH USER, WAIT FOR APPROVAL)\n$ knowns task edit AUTH-042 --plan $\'1. Review security patterns (see @doc/security-patterns)\n2. Design token generation with 1-hour expiry\n3. Create email template (see @doc/email-templates)\n4. Implement /forgot-password endpoint\n5. Implement /reset-password endpoint\n6. Add unit tests\n7. Update API documentation\'\n\n# 6. After approval, implement and check criteria as you go\n$ knowns task edit AUTH-042 --check-ac 1\n$ knowns task edit AUTH-042 --append-notes "\u2713 Implemented /forgot-password endpoint"\n\n$ knowns task edit AUTH-042 --check-ac 2\n$ knowns task edit AUTH-042 --append-notes "\u2713 Token expiry set to 1 hour"\n\n$ knowns task edit AUTH-042 --check-ac 3\n$ knowns task edit AUTH-042 --append-notes "\u2713 Implemented /reset-password endpoint"\n\n$ knowns task edit AUTH-042 --check-ac 4\n$ knowns task edit AUTH-042 --append-notes "\u2713 Added 12 unit tests, 100% coverage"\n\n# 7. Add final implementation notes\n$ knowns task edit AUTH-042 --notes $\'## Summary\nImplemented complete password reset flow with secure token generation.\n\n## Changes\n- Added POST /forgot-password endpoint\n- Added POST /reset-password endpoint\n- Created password_reset_tokens table\n- Added email template for reset link\n\n## Security\n- Tokens use crypto.randomBytes(32)\n- 1-hour expiry enforced at DB level\n- Rate limiting: 3 requests per hour per email\n\n## Tests\n- 12 unit tests added\n- Coverage: 100% for new code\n\n## Documentation\n- Updated API.md with new endpoints\'\n\n# 8. Stop timer and complete\n$ knowns time stop\n$ knowns task edit AUTH-042 -s done\n\n# Output: Task AUTH-042 marked as done\n```\n\n---\n\n## Task Workflow\n\n### Step 1: Take Task\n\n```bash\n# Assign using defaultAssignee from config (falls back to @me if not set)\nknowns task edit <id> -s in-progress -a $(knowns config get defaultAssignee --plain || echo "@me")\n```\n\n> **Note**: The `defaultAssignee` is configured in `.knowns/config.json` during `knowns init`. If not set, `@me` is used as fallback. To update: `knowns config set defaultAssignee "@username"`\n\n### Step 2: Start Time Tracking (REQUIRED)\n\n```bash\nknowns time start <id>\n```\n\n> **CRITICAL**: Time tracking is MANDATORY. Always start timer when taking a task and stop when done. This data is essential for:\n> - Accurate project estimation\n> - Identifying bottlenecks\n> - Resource planning\n> - Sprint retrospectives\n\n### Step 3: Read Related Documentation\n\n> **FOR AI AGENTS**: This step is MANDATORY, not optional. You must understand the codebase before planning.\n\n```bash\n# Search for related docs\nknowns search "authentication" --type doc --plain\n\n# View relevant documents\nknowns doc "API Guidelines" --plain\nknowns doc "Security Patterns" --plain\n\n# Also check for similar completed tasks\nknowns search "auth" --type task --status done --plain\n```\n\n> **CRITICAL**: ALWAYS read related documentation BEFORE planning! Understanding existing patterns and conventions prevents rework and ensures consistency.\n\n### Step 4: Create Implementation Plan\n\n```bash\nknowns task edit <id> --plan $\'1. Research patterns (see @doc/security-patterns)\n2. Design middleware\n3. Implement\n4. Add tests\n5. Update docs\'\n```\n\n> **CRITICAL**:\n> - Share plan with user and **WAIT for approval** before coding\n> - Include doc references using `@doc/<path>` format\n\n### Step 5: Implement\n\n```bash\n# Work through implementation plan step by step\n# IMPORTANT: Only check AC AFTER completing the work, not before\n\n# After completing work for AC #1:\nknowns task edit <id> --check-ac 1\nknowns task edit <id> --append-notes "\u2713 Completed: <brief description>"\n\n# After completing work for AC #2:\nknowns task edit <id> --check-ac 2\nknowns task edit <id> --append-notes "\u2713 Completed: <brief description>"\n```\n\n> **CRITICAL**: Never check an AC before the work is actually done. ACs represent completed outcomes, not intentions.\n\n### Step 6: Handle Dynamic Requests (During Implementation)\n\nIf the user adds new requirements during implementation:\n\n```bash\n# Add new acceptance criteria\nknowns task edit <id> --ac "New requirement from user"\n\n# Update implementation plan to include new steps\nknowns task edit <id> --plan $\'1. Original step 1\n2. Original step 2\n3. NEW: Handle user request for X\n4. Continue with remaining work\'\n\n# Append note about scope change\nknowns task edit <id> --append-notes "\u26A0\uFE0F Scope updated: Added requirement for X per user request"\n\n# Continue with Step 5 (Implement) for new requirements\n```\n\n> **Note**: Always document scope changes. This helps track why a task took longer than expected.\n\n### Step 7: Add Implementation Notes\n\n```bash\n# Add comprehensive notes (suitable for PR description)\nknowns task edit <id> --notes $\'## Summary\n\nImplemented JWT auth.\n\n## Changes\n- Added middleware\n- Added tests\'\n\n# OR append progressively (recommended)\nknowns task edit <id> --append-notes "\u2713 Implemented middleware"\nknowns task edit <id> --append-notes "\u2713 Added tests"\n```\n\n### Step 8: Stop Time Tracking (REQUIRED)\n\n```bash\nknowns time stop\n```\n\n> **CRITICAL**: Never forget to stop the timer. If you forget, use manual entry: `knowns time add <id> <duration> -n "Forgot to stop timer"`\n\n### Step 9: Complete Task\n\n```bash\nknowns task edit <id> -s done\n```\n\n### Step 10: Handle Post-Completion Changes (If Applicable)\n\nIf the user requests changes or updates AFTER task is marked done:\n\n```bash\n# 1. Reopen task - set back to in-progress\nknowns task edit <id> -s in-progress\n\n# 2. Restart time tracking (REQUIRED)\nknowns time start <id>\n\n# 3. Add new AC for the changes requested\nknowns task edit <id> --ac "Post-completion fix: <description>"\n\n# 4. Document the reopen reason\nknowns task edit <id> --append-notes "\u{1F504} Reopened: User requested changes - <reason>"\n\n# 5. Follow Step 5-9 again (Implement \u2192 Notes \u2192 Stop Timer \u2192 Done)\n```\n\n> **CRITICAL**: Treat post-completion changes as a mini-workflow. Always:\n> - Reopen task (in-progress)\n> - Start timer again\n> - Add AC for traceability\n> - Document why it was reopened\n> - Follow the same completion process\n\n### Step 11: Knowledge Extraction (Post-Completion)\n\nAfter completing a task, extract reusable knowledge to docs:\n\n```bash\n# Search if similar pattern already documented\nknowns search "<pattern/concept>" --type doc --plain\n\n# If new knowledge, create a doc for future reference\nknowns doc create "Pattern: <Name>" \\\n -d "Reusable pattern discovered during task implementation" \\\n -t "pattern,<domain>" \\\n -f "patterns"\n\n# Or append to existing doc\nknowns doc edit "<existing-doc>" -a "## New Section\\n\\nLearned from task <id>: ..."\n\n# Reference the source task\nknowns doc edit "<doc-name>" -a "\\n\\n> Source: @task-<id>"\n```\n\n**When to extract knowledge:**\n- New patterns/conventions discovered\n- Common error solutions\n- Reusable code snippets or approaches\n- Integration patterns with external services\n- Performance optimization techniques\n\n> **CRITICAL**: Only extract **generalizable** knowledge. Task-specific details belong in implementation notes, not docs.\n\n---\n\n## Essential Commands\n\n### Task Management\n\n```bash\n# Create task\nknowns task create "Title" -d "Description" --ac "Criterion" -l "labels" --priority high\n\n# Edit task\nknowns task edit <id> -t "New title"\nknowns task edit <id> -d "New description"\nknowns task edit <id> -s in-progress\nknowns task edit <id> --priority high\nknowns task edit <id> -a <assignee> # $(knowns config get defaultAssignee --plain || echo "@me")\n\n# Acceptance Criteria\nknowns task edit <id> --ac "New criterion" # Add\nknowns task edit <id> --check-ac 1 --check-ac 2 # Check (1-indexed)\nknowns task edit <id> --uncheck-ac 2 # Uncheck\nknowns task edit <id> --remove-ac 3 # Remove\n\n# Implementation Plan & Notes\nknowns task edit <id> --plan $\'1. Step\\n2. Step\'\nknowns task edit <id> --notes "Implementation summary"\nknowns task edit <id> --append-notes "Progress update"\n\n# View & List\nknowns task <id> --plain # Shorthand (ALWAYS use --plain)\nknowns task view <id> --plain # Full command\nknowns task list --plain\nknowns task list --status in-progress --plain\nknowns task list --assignee <assignee> --plain # $(knowns config get defaultAssignee --plain || echo "@me")\nknowns task list --tree --plain # Tree hierarchy\n```\n\n### Time Tracking\n\n```bash\n# Timer\nknowns time start <id>\nknowns time stop\nknowns time pause\nknowns time resume\nknowns time status\n\n# Manual entry\nknowns time add <id> 2h -n "Note" -d "2025-12-25"\n\n# Reports\nknowns time report --from "2025-12-01" --to "2025-12-31"\nknowns time report --by-label --csv > report.csv\n```\n\n### Documentation\n\n```bash\n# List & View\nknowns doc list --plain\nknowns doc list --tag architecture --plain\nknowns doc <path> --plain # Shorthand (ALWAYS use --plain)\nknowns doc view "<path>" --plain # Full command\n\n# Create (with optional folder)\nknowns doc create "Title" -d "Description" -t "tags"\nknowns doc create "Title" -d "Description" -t "tags" -f "folder/path"\n\n# Edit metadata\nknowns doc edit "Doc Name" -t "New Title" --tags "new,tags"\n\n# Edit content\nknowns doc edit "Doc Name" -c "New content" # Replace content\nknowns doc edit "Doc Name" -a "Appended content" # Append to content\n```\n\n### Search\n\n```bash\n# Search everything\nknowns search "query" --plain\n\n# Search specific type\nknowns search "auth" --type task --plain\nknowns search "patterns" --type doc --plain\n\n# Filter\nknowns search "bug" --status in-progress --priority high --plain\n```\n\n---\n\n## Task Structure\n\n### Title\n\nClear summary (WHAT needs to be done).\n\n| Bad | Good |\n|-----|------|\n| Do auth stuff | Add JWT authentication |\n| Fix bug | Fix login timeout on slow networks |\n| Update docs | Document rate limiting in API.md |\n\n### Description\n\nExplains WHY and WHAT (not HOW). **Link related docs using `@doc/<path>`**\n\n```markdown\nWe need JWT authentication because sessions don\'t scale for our microservices architecture.\n\nRelated docs: @doc/security-patterns, @doc/api-guidelines\n```\n\n### Acceptance Criteria\n\n**Outcome-oriented**, testable criteria. NOT implementation steps.\n\n| Bad (Implementation details) | Good (Outcomes) |\n|------------------------------|-----------------|\n| Add function handleLogin() in auth.ts | User can login and receive JWT token |\n| Use bcrypt for hashing | Passwords are securely hashed |\n| Add try-catch blocks | Errors return appropriate HTTP status codes |\n\n### Implementation Plan\n\nHOW to solve. Added AFTER taking task, BEFORE coding.\n\n```markdown\n1. Research JWT libraries (see @doc/security-patterns)\n2. Design token structure (access + refresh tokens)\n3. Implement auth middleware\n4. Add unit tests (aim for 90%+ coverage)\n5. Update API.md with new endpoints\n```\n\n### Implementation Notes\n\nSummary for PR description. Added AFTER completion.\n\n```markdown\n## Summary\nImplemented JWT auth using jsonwebtoken library.\n\n## Changes\n- Added auth middleware in src/middleware/auth.ts\n- Added /login and /refresh endpoints\n- Created JWT utility functions\n\n## Tests\n- Added 15 unit tests\n- Coverage: 94%\n\n## Documentation\n- Updated API.md with authentication section\n```\n\n---\n\n## Error Handling\n\n### Common Errors and Solutions\n\n| Error | Cause | Solution |\n|-------|-------|----------|\n| `Error: Task not found` | Invalid task ID | Run `knowns task list --plain` to find correct ID |\n| `Error: No active timer` | Calling `time stop` without active timer | Start timer first: `knowns time start <id>` |\n| `Error: Timer already running` | Starting timer when one is active | Stop current: `knowns time stop` |\n| `Error: Invalid status` | Wrong status format | Use lowercase with hyphens: `in-progress`, not `In Progress` |\n| `Error: AC index out of range` | Checking non-existent criterion | View task first: `knowns task <id> --plain` |\n| `Error: Document not found` | Wrong document name | Run `knowns doc list --plain` to find correct name |\n| `Error: Not initialized` | Running commands without init | Run `knowns init` first |\n\n### Debugging Commands\n\n```bash\n# Check CLI version\nknowns --version\n\n# Verify project is initialized\nknowns status\n\n# View raw task data (for debugging)\nknowns task <id> --json\n\n# Check timer status\nknowns time status\n```\n\n---\n\n## Definition of Done\n\nA task is **Done** ONLY when **ALL** criteria are met:\n\n### Via CLI (Required)\n\n- [ ] All acceptance criteria checked: `--check-ac <index>` (only after work is actually done)\n- [ ] Implementation notes added: `--notes "..."`\n- [ ] \u23F1\uFE0F Timer stopped: `knowns time stop` (MANDATORY - do not skip!)\n- [ ] Status set to done: `-s done`\n- [ ] Knowledge extracted to docs (if applicable)\n\n### Via Code (Required)\n\n- [ ] All tests pass\n- [ ] Documentation updated\n- [ ] Code reviewed (linting, formatting)\n- [ ] No regressions introduced\n\n---\n\n## Status & Priority Reference\n\n### Status Values\n\nUse **lowercase with hyphens**:\n\n| Status | Description | When to Use |\n|--------|-------------|-------------|\n| `todo` | Not started | Default for new tasks |\n| `in-progress` | Currently working | After taking task |\n| `in-review` | In code review | PR submitted |\n| `blocked` | Waiting on dependency | External blocker |\n| `done` | Completed | All criteria met |\n\n### Priority Values\n\n| Priority | Description |\n|----------|-------------|\n| `low` | Can wait, nice-to-have |\n| `medium` | Normal priority (default) |\n| `high` | Urgent, time-sensitive |\n\n---\n\n## Common Mistakes\n\n| Wrong | Right |\n|-------|-------|\n| Edit .md files directly | Use `knowns task edit` |\n| Change `- [ ]` to `- [x]` in file | Use `--check-ac <index>` |\n| Check AC before completing work | Only check AC AFTER work is actually done |\n| Skip time tracking | ALWAYS use `time start` and `time stop` |\n| Start coding without reading docs | Read ALL related docs FIRST |\n| Skip `knowns doc list` on new project | Always list docs when starting |\n| Assume you know the conventions | Read CONVENTIONS/ARCHITECTURE docs |\n| Plan without checking docs | Read docs before planning |\n| Ignore similar completed tasks | Search done tasks for patterns |\n| Missing doc links in description/plan | Link docs using `@doc/<path>` |\n| Write refs as `@.knowns/docs/...` or `@.knowns/tasks/...` | Use input format: `@doc/<path>`, `@task-<id>` |\n| Forget `--plain` flag | Always use `--plain` for AI |\n| Code before plan approval | Share plan, WAIT for approval |\n| Mark done without all criteria | Check ALL criteria first |\n| Write implementation steps in AC | Write outcome-oriented criteria |\n| Use `"In Progress"` or `"Done"` | Use `in-progress`, `done` |\n| Use `@yourself` or unknown assignee | Use `$(knowns config get defaultAssignee --plain \\|\\| echo "@me")` |\n| Ignore refs in task description | Follow ALL refs (`@.knowns/...`) before planning |\n| See `@.knowns/docs/...` but don\'t read | Use `knowns doc "<path>" --plain` |\n| See `@.knowns/tasks/task-X` but don\'t check | Use `knowns task X --plain` for context |\n| Follow only first-level refs | Recursively follow nested refs until complete |\n\n---\n\n## Platform-Specific Notes\n\n### Multi-line Input\n\nDifferent shells handle multi-line strings differently:\n\n**Bash / Zsh (Recommended)**\n```bash\nknowns task edit <id> --plan $\'1. First step\\n2. Second step\\n3. Third step\'\n```\n\n**PowerShell**\n```powershell\nknowns task edit <id> --plan "1. First step`n2. Second step`n3. Third step"\n```\n\n**Cross-platform (Using printf)**\n```bash\nknowns task edit <id> --plan "$(printf \'1. First step\\n2. Second step\\n3. Third step\')"\n```\n\n**Using heredoc (for long content)**\n```bash\nknowns task edit <id> --plan "$(cat <<EOF\n1. First step\n2. Second step\n3. Third step\nEOF\n)"\n```\n\n### Path Separators\n\n- **Unix/macOS**: Use forward slashes: `./docs/api.md`\n- **Windows**: Both work, but prefer forward slashes for consistency\n\n### Windows Command Line Limit\n\nWindows has ~8191 character limit. For long content, append in chunks:\n\n```bash\n# 1. Create or reset with short content\nknowns doc edit "doc-name" -c "## Overview\\n\\nShort intro."\n\n# 2. Append each section separately\nknowns doc edit "doc-name" -a "## Section 1\\n\\nContent..."\nknowns doc edit "doc-name" -a "## Section 2\\n\\nMore content..."\n```\n\nOr use file-based options:\n\n```bash\nknowns doc edit "doc-name" --content-file ./content.md\nknowns doc edit "doc-name" --append-file ./more.md\n```\n\n---\n\n## Best Practices Checklist\n\n### For AI Agents: Session Start\n\n- [ ] List all docs: `knowns doc list --plain`\n- [ ] Read README/ARCHITECTURE docs\n- [ ] Understand coding conventions\n- [ ] Review current task backlog\n\n### Before Starting Work\n\n- [ ] Task has clear acceptance criteria\n- [ ] ALL refs in task followed (`@.knowns/...`)\n- [ ] Nested refs recursively followed until complete context gathered\n- [ ] Related docs searched: `knowns search "keyword" --type doc --plain`\n- [ ] ALL relevant docs read: `knowns doc "Doc Name" --plain`\n- [ ] Similar done tasks reviewed for patterns\n- [ ] Task assigned to self: `-a $(knowns config get defaultAssignee --plain || echo "@me")`\n- [ ] Status set to in-progress: `-s in-progress`\n- [ ] Timer started: `knowns time start <id>`\n\n### During Work\n\n- [ ] Implementation plan created and approved\n- [ ] Doc links included in plan: `@doc/<path>`\n- [ ] Criteria checked as completed: `--check-ac <index>`\n- [ ] Progress notes appended: `--append-notes "\u2713 ..."`\n\n### After Work\n\n- [ ] All acceptance criteria checked (only after work is done)\n- [ ] Implementation notes added: `--notes "..."`\n- [ ] Timer stopped: `knowns time stop`\n- [ ] Tests passing\n- [ ] Documentation updated\n- [ ] Status set to done: `-s done`\n- [ ] Knowledge extracted to docs (if applicable): patterns, solutions, conventions\n\n---\n\n## Quick Reference Card\n\n```bash\n# === AGENT INITIALIZATION (Once per session) ===\nknowns doc list --plain\nknowns doc "README" --plain\nknowns doc "ARCHITECTURE" --plain\nknowns doc "CONVENTIONS" --plain\n\n# === FULL WORKFLOW ===\nknowns task create "Title" -d "Description" --ac "Criterion"\nknowns task edit <id> -s in-progress -a $(knowns config get defaultAssignee --plain || echo "@me")\nknowns time start <id> # \u23F1\uFE0F REQUIRED: Start timer\nknowns search "keyword" --type doc --plain\nknowns doc "Doc Name" --plain\nknowns search "keyword" --type task --status done --plain # Learn from history\nknowns task edit <id> --plan $\'1. Step (see @doc/file)\\n2. Step\'\n# ... wait for approval, then implement ...\n# Only check AC AFTER completing the work:\nknowns task edit <id> --check-ac 1\nknowns task edit <id> --append-notes "\u2713 Completed: feature X"\nknowns task edit <id> --check-ac 2\nknowns task edit <id> --append-notes "\u2713 Completed: feature Y"\nknowns time stop # \u23F1\uFE0F REQUIRED: Stop timer\nknowns task edit <id> -s done\n# Optional: Extract knowledge to docs if generalizable patterns found\n\n# === VIEW & SEARCH ===\nknowns task <id> --plain # Shorthand for view\nknowns task list --plain\nknowns task list --status in-progress --assignee $(knowns config get defaultAssignee --plain || echo "@me") --plain\nknowns search "query" --plain\nknowns search "bug" --type task --status in-progress --plain\n\n# === TIME TRACKING ===\nknowns time start <id>\nknowns time stop\nknowns time status\nknowns time report --from "2025-12-01" --to "2025-12-31"\n\n# === DOCUMENTATION ===\nknowns doc list --plain\nknowns doc "path/doc-name" --plain # Shorthand for view\nknowns doc create "Title" -d "Description" -t "tags" -f "folder"\nknowns doc edit "doc-name" -c "New content"\nknowns doc edit "doc-name" -a "Appended content"\n```\n\n---\n\n**Maintained By**: Knowns CLI Team\n\n<!-- KNOWNS GUIDELINES END -->\n';
|
|
43464
|
+
var general_default = '<!-- KNOWNS GUIDELINES START -->\n# Knowns CLI Guidelines\n\n## Core Principles\n\n### 1. CLI-Only Operations\n**NEVER edit .md files directly. ALL operations MUST use CLI commands.**\n\nThis ensures data integrity, maintains proper change history, and prevents file corruption.\n\n### 2. Documentation-First (For AI Agents)\n**ALWAYS read project documentation BEFORE planning or coding.**\n\nAI agents must understand project context, conventions, and existing patterns before making any changes. This prevents rework and ensures consistency.\n\n---\n\n## AI Agent Guidelines\n\n> **CRITICAL**: Before performing ANY task, AI agents MUST read documentation to understand project context.\n\n### First-Time Initialization\n\nWhen starting a new session or working on an unfamiliar project:\n\n```bash\n# 1. List all available documentation\nknowns doc list --plain\n\n# 2. Read essential project docs (prioritize these)\nknowns doc "README" --plain # Project overview\nknowns doc "ARCHITECTURE" --plain # System design\nknowns doc "CONVENTIONS" --plain # Coding standards\nknowns doc "API" --plain # API specifications\n\n# 3. Review current task backlog\nknowns task list --plain\nknowns task list --status in-progress --plain\n```\n\n### Before Taking Any Task\n\n```bash\n# 1. View the task details\nknowns task <id> --plain\n\n# 2. Follow ALL refs in the task (see Reference System section)\n# @.knowns/tasks/task-44 - ... \u2192 knowns task 44 --plain\n# @.knowns/docs/patterns/module.md \u2192 knowns doc "patterns/module" --plain\n\n# 3. Search for additional related documentation\nknowns search "<keywords from task>" --type doc --plain\n\n# 4. Read ALL related docs before planning\nknowns doc "<related-doc>" --plain\n\n# 5. Check for similar completed tasks (learn from history)\nknowns search "<keywords>" --type task --status done --plain\n```\n\n### Why Documentation First?\n\n| Without Reading Docs | With Reading Docs |\n|---------------------|-------------------|\n| Reinvent existing patterns | Reuse established patterns |\n| Break conventions | Follow project standards |\n| Duplicate code | Use existing utilities |\n| Wrong architecture decisions | Align with system design |\n| Inconsistent naming | Match naming conventions |\n\n### Context Checklist for Agents\n\nBefore writing ANY code, ensure you can answer:\n\n- [ ] Have I followed ALL refs (`@.knowns/...`) in the task?\n- [ ] Have I followed nested refs recursively?\n- [ ] What is the project\'s overall architecture?\n- [ ] What coding conventions does this project follow?\n- [ ] Are there existing patterns/utilities I should reuse?\n- [ ] What are the testing requirements?\n- [ ] How should I structure my implementation?\n\n> **Remember**: A few minutes reading docs saves hours of rework. NEVER skip this step.\n\n---\n\n## Reference System (Refs)\n\nTasks and docs can contain **references** to other tasks/docs. AI agents MUST understand and follow these refs to gather complete context.\n\n### Reference Formats\n\n| Type | When Writing (Input) | When Reading (Output) | CLI Command |\n|------|---------------------|----------------------|-------------|\n| **Task ref** | `@task-<id>` | `@.knowns/tasks/task-<id> - <title>.md` | `knowns task <id> --plain` |\n| **Doc ref** | `@doc/<path>` | `@.knowns/docs/<path>.md` | `knowns doc <path> --plain` |\n\n> **CRITICAL for AI Agents**:\n> - When **WRITING** refs (in descriptions, plans, notes): Use `@task-<id>` and `@doc/<path>`\n> - When **READING** output from `--plain`: You\'ll see `@.knowns/tasks/...` and `@.knowns/docs/...`\n> - **NEVER write** the output format (`@.knowns/...`) - always use input format (`@task-<id>`, `@doc/<path>`)\n\n### How to Follow Refs\n\nWhen you read a task and see refs in system output format, follow them:\n\n```bash\n# Example: Task 42 output contains:\n# @.knowns/tasks/task-44 - CLI Task Create Command.md\n# @.knowns/docs/patterns/module.md\n\n# Follow task ref (extract ID from task-<id>)\nknowns task 44 --plain\n\n# Follow doc ref (extract path, remove .md)\nknowns doc "patterns/module" --plain\n```\n\n### Parsing Rules\n\n1. **Task refs**: `@.knowns/tasks/task-<id> - ...` \u2192 extract `<id>` \u2192 `knowns task <id> --plain`\n2. **Doc refs**: `@.knowns/docs/<path>.md` \u2192 extract `<path>` \u2192 `knowns doc "<path>" --plain`\n\n### Recursive Following\n\nRefs can be nested. Follow until complete context is gathered:\n\n```\nTask 42\n \u2192 @.knowns/docs/README.md\n \u2192 @.knowns/docs/patterns/module.md (found in README)\n \u2192 (read for full pattern details)\n```\n\n### When to Follow Refs\n\n| Situation | Action |\n|-----------|--------|\n| Refs in Description | ALWAYS follow - critical context |\n| Refs in Implementation Plan | Follow if implementing related work |\n| Refs in Notes | Optional - for historical context |\n| Dependency mentions | Follow if marked as blocker |\n\n### Example: Complete Ref Resolution\n\n```bash\n# 1. Read the task\n$ knowns task 42 --plain\n\n# Output contains:\n# @.knowns/tasks/task-44 - CLI Task Create Command.md\n# @.knowns/docs/README.md\n\n# 2. Follow task ref\n$ knowns task 44 --plain\n\n# 3. Follow doc ref\n$ knowns doc "README" --plain\n\n# 4. If README contains more refs, follow them too\n# @.knowns/docs/patterns/module.md \u2192 knowns doc "patterns/module" --plain\n\n# Now you have complete context\n```\n\n> **CRITICAL**: Never assume you understand a task fully without following its refs. Refs contain essential context that may change your implementation approach.\n\n---\n\n## Quick Start\n\n```bash\n# Initialize project\nknowns init [name]\n\n# Create task with acceptance criteria\nknowns task create "Title" -d "Description" --ac "Criterion 1" --ac "Criterion 2"\n\n# View task (ALWAYS use --plain for AI)\nknowns task <id> --plain # Shorthand\nknowns task view <id> --plain # Full command\n\n# View doc (ALWAYS use --plain for AI)\nknowns doc <path> --plain # Shorthand\nknowns doc view "<path>" --plain # Full command\n\n# List tasks\nknowns task list --plain\n\n# Search (tasks + docs)\nknowns search "query" --plain\n```\n\n---\n\n## End-to-End Example\n\nHere\'s a complete workflow for an AI agent implementing a feature:\n\n```bash\n# === AGENT SESSION START (Do this once per session) ===\n\n# 0a. List all available documentation\n$ knowns doc list --plain\n\n# Output:\n# DOC: README.md\n# DOC: ARCHITECTURE.md\n# DOC: CONVENTIONS.md\n# DOC: security-patterns.md\n# DOC: api-guidelines.md\n# DOC: email-templates.md\n\n# 0b. Read essential project docs\n$ knowns doc "README" --plain\n$ knowns doc "ARCHITECTURE" --plain\n$ knowns doc "CONVENTIONS" --plain\n\n# Now the agent understands project context and conventions\n\n# === TASK WORKFLOW ===\n\n# 1. Create the task\n$ knowns task create "Add password reset flow" \\\n -d "Users need ability to reset forgotten passwords via email" \\\n --ac "User can request password reset via email" \\\n --ac "Reset link expires after 1 hour" \\\n --ac "User can set new password via reset link" \\\n --ac "Unit tests cover all scenarios" \\\n --priority high \\\n -l "auth,feature"\n\n# Output: Created task AUTH-042\n\n# 2. Take the task and start timer (uses defaultAssignee or @me fallback)\n$ knowns task edit AUTH-042 -s in-progress -a $(knowns config get defaultAssignee --plain || echo "@me")\n$ knowns time start AUTH-042\n\n# Output: Timer started for AUTH-042\n\n# 3. Search for related documentation\n$ knowns search "password security" --type doc --plain\n\n# Output:\n# DOC: security-patterns.md (score: 0.92)\n# DOC: email-templates.md (score: 0.78)\n\n# 4. Read the documentation\n$ knowns doc "security-patterns" --plain\n\n# 5. Create implementation plan (SHARE WITH USER, WAIT FOR APPROVAL)\n$ knowns task edit AUTH-042 --plan $\'1. Review security patterns (see @doc/security-patterns)\n2. Design token generation with 1-hour expiry\n3. Create email template (see @doc/email-templates)\n4. Implement /forgot-password endpoint\n5. Implement /reset-password endpoint\n6. Add unit tests\n7. Update API documentation\'\n\n# 6. After approval, implement and check criteria as you go\n$ knowns task edit AUTH-042 --check-ac 1\n$ knowns task edit AUTH-042 --append-notes "\u2713 Implemented /forgot-password endpoint"\n\n$ knowns task edit AUTH-042 --check-ac 2\n$ knowns task edit AUTH-042 --append-notes "\u2713 Token expiry set to 1 hour"\n\n$ knowns task edit AUTH-042 --check-ac 3\n$ knowns task edit AUTH-042 --append-notes "\u2713 Implemented /reset-password endpoint"\n\n$ knowns task edit AUTH-042 --check-ac 4\n$ knowns task edit AUTH-042 --append-notes "\u2713 Added 12 unit tests, 100% coverage"\n\n# 7. Add final implementation notes\n$ knowns task edit AUTH-042 --notes $\'## Summary\nImplemented complete password reset flow with secure token generation.\n\n## Changes\n- Added POST /forgot-password endpoint\n- Added POST /reset-password endpoint\n- Created password_reset_tokens table\n- Added email template for reset link\n\n## Security\n- Tokens use crypto.randomBytes(32)\n- 1-hour expiry enforced at DB level\n- Rate limiting: 3 requests per hour per email\n\n## Tests\n- 12 unit tests added\n- Coverage: 100% for new code\n\n## Documentation\n- Updated API.md with new endpoints\'\n\n# 8. Stop timer and complete\n$ knowns time stop\n$ knowns task edit AUTH-042 -s done\n\n# Output: Task AUTH-042 marked as done\n```\n\n---\n\n## Task Workflow\n\n### Step 1: Take Task\n\n```bash\n# Assign using defaultAssignee from config (falls back to @me if not set)\nknowns task edit <id> -s in-progress -a $(knowns config get defaultAssignee --plain || echo "@me")\n```\n\n> **Note**: The `defaultAssignee` is configured in `.knowns/config.json` during `knowns init`. If not set, `@me` is used as fallback. To update: `knowns config set defaultAssignee "@username"`\n\n### Step 2: Start Time Tracking (REQUIRED)\n\n```bash\nknowns time start <id>\n```\n\n> **CRITICAL**: Time tracking is MANDATORY. Always start timer when taking a task and stop when done. This data is essential for:\n> - Accurate project estimation\n> - Identifying bottlenecks\n> - Resource planning\n> - Sprint retrospectives\n\n### Step 3: Read Related Documentation\n\n> **FOR AI AGENTS**: This step is MANDATORY, not optional. You must understand the codebase before planning.\n\n```bash\n# Search for related docs\nknowns search "authentication" --type doc --plain\n\n# View relevant documents\nknowns doc "API Guidelines" --plain\nknowns doc "Security Patterns" --plain\n\n# Also check for similar completed tasks\nknowns search "auth" --type task --status done --plain\n```\n\n> **CRITICAL**: ALWAYS read related documentation BEFORE planning! Understanding existing patterns and conventions prevents rework and ensures consistency.\n\n### Step 4: Create Implementation Plan\n\n```bash\nknowns task edit <id> --plan $\'1. Research patterns (see @doc/security-patterns)\n2. Design middleware\n3. Implement\n4. Add tests\n5. Update docs\'\n```\n\n> **CRITICAL**:\n> - Share plan with user and **WAIT for approval** before coding\n> - Include doc references using `@doc/<path>` format\n\n### Step 5: Implement\n\n```bash\n# Work through implementation plan step by step\n# IMPORTANT: Only check AC AFTER completing the work, not before\n\n# After completing work for AC #1:\nknowns task edit <id> --check-ac 1\nknowns task edit <id> --append-notes "\u2713 Completed: <brief description>"\n\n# After completing work for AC #2:\nknowns task edit <id> --check-ac 2\nknowns task edit <id> --append-notes "\u2713 Completed: <brief description>"\n```\n\n> **CRITICAL**: Never check an AC before the work is actually done. ACs represent completed outcomes, not intentions.\n\n### Step 6: Handle Dynamic Requests (During Implementation)\n\nIf the user adds new requirements during implementation:\n\n```bash\n# Add new acceptance criteria\nknowns task edit <id> --ac "New requirement from user"\n\n# Update implementation plan to include new steps\nknowns task edit <id> --plan $\'1. Original step 1\n2. Original step 2\n3. NEW: Handle user request for X\n4. Continue with remaining work\'\n\n# Append note about scope change\nknowns task edit <id> --append-notes "\u26A0\uFE0F Scope updated: Added requirement for X per user request"\n\n# Continue with Step 5 (Implement) for new requirements\n```\n\n> **Note**: Always document scope changes. This helps track why a task took longer than expected.\n\n### Step 7: Add Implementation Notes\n\n```bash\n# Add comprehensive notes (suitable for PR description)\nknowns task edit <id> --notes $\'## Summary\n\nImplemented JWT auth.\n\n## Changes\n- Added middleware\n- Added tests\'\n\n# OR append progressively (recommended)\nknowns task edit <id> --append-notes "\u2713 Implemented middleware"\nknowns task edit <id> --append-notes "\u2713 Added tests"\n```\n\n### Step 8: Stop Time Tracking (REQUIRED)\n\n```bash\nknowns time stop\n```\n\n> **CRITICAL**: Never forget to stop the timer. If you forget, use manual entry: `knowns time add <id> <duration> -n "Forgot to stop timer"`\n\n### Step 9: Complete Task\n\n```bash\nknowns task edit <id> -s done\n```\n\n### Step 10: Handle Post-Completion Changes (If Applicable)\n\nIf the user requests changes or updates AFTER task is marked done:\n\n```bash\n# 1. Reopen task - set back to in-progress\nknowns task edit <id> -s in-progress\n\n# 2. Restart time tracking (REQUIRED)\nknowns time start <id>\n\n# 3. Add new AC for the changes requested\nknowns task edit <id> --ac "Post-completion fix: <description>"\n\n# 4. Document the reopen reason\nknowns task edit <id> --append-notes "\u{1F504} Reopened: User requested changes - <reason>"\n\n# 5. Follow Step 5-9 again (Implement \u2192 Notes \u2192 Stop Timer \u2192 Done)\n```\n\n> **CRITICAL**: Treat post-completion changes as a mini-workflow. Always:\n> - Reopen task (in-progress)\n> - Start timer again\n> - Add AC for traceability\n> - Document why it was reopened\n> - Follow the same completion process\n\n### Step 11: Knowledge Extraction (Post-Completion)\n\nAfter completing a task, extract reusable knowledge to docs:\n\n```bash\n# Search if similar pattern already documented\nknowns search "<pattern/concept>" --type doc --plain\n\n# If new knowledge, create a doc for future reference\nknowns doc create "Pattern: <Name>" \\\n -d "Reusable pattern discovered during task implementation" \\\n -t "pattern,<domain>" \\\n -f "patterns"\n\n# Or append to existing doc\nknowns doc edit "<existing-doc>" -a "## New Section\\n\\nLearned from task <id>: ..."\n\n# Reference the source task\nknowns doc edit "<doc-name>" -a "\\n\\n> Source: @task-<id>"\n```\n\n**When to extract knowledge:**\n- New patterns/conventions discovered\n- Common error solutions\n- Reusable code snippets or approaches\n- Integration patterns with external services\n- Performance optimization techniques\n\n> **CRITICAL**: Only extract **generalizable** knowledge. Task-specific details belong in implementation notes, not docs.\n\n---\n\n## Essential Commands\n\n### Task Management\n\n```bash\n# Create task\nknowns task create "Title" -d "Description" --ac "Criterion" -l "labels" --priority high\n\n# Edit task\nknowns task edit <id> -t "New title"\nknowns task edit <id> -d "New description"\nknowns task edit <id> -s in-progress\nknowns task edit <id> --priority high\nknowns task edit <id> -a <assignee> # $(knowns config get defaultAssignee --plain || echo "@me")\n\n# Acceptance Criteria\nknowns task edit <id> --ac "New criterion" # Add\nknowns task edit <id> --check-ac 1 --check-ac 2 # Check (1-indexed)\nknowns task edit <id> --uncheck-ac 2 # Uncheck\nknowns task edit <id> --remove-ac 3 # Remove\n\n# Implementation Plan & Notes\nknowns task edit <id> --plan $\'1. Step\\n2. Step\'\nknowns task edit <id> --notes "Implementation summary"\nknowns task edit <id> --append-notes "Progress update"\n\n# View & List\nknowns task <id> --plain # Shorthand (ALWAYS use --plain)\nknowns task view <id> --plain # Full command\nknowns task list --plain\nknowns task list --status in-progress --plain\nknowns task list --assignee <assignee> --plain # $(knowns config get defaultAssignee --plain || echo "@me")\nknowns task list --tree --plain # Tree hierarchy\n```\n\n### Time Tracking\n\n```bash\n# Timer\nknowns time start <id>\nknowns time stop\nknowns time pause\nknowns time resume\nknowns time status\n\n# Manual entry\nknowns time add <id> 2h -n "Note" -d "2025-12-25"\n\n# Reports\nknowns time report --from "2025-12-01" --to "2025-12-31"\nknowns time report --by-label --csv > report.csv\n```\n\n### Documentation\n\n```bash\n# List & View\nknowns doc list --plain\nknowns doc list --tag architecture --plain\nknowns doc <path> --plain # Shorthand (ALWAYS use --plain)\nknowns doc view "<path>" --plain # Full command\n\n# Create (with optional folder)\nknowns doc create "Title" -d "Description" -t "tags"\nknowns doc create "Title" -d "Description" -t "tags" -f "folder/path"\n\n# Edit metadata\nknowns doc edit "Doc Name" -t "New Title" --tags "new,tags"\n\n# Edit content\nknowns doc edit "Doc Name" -c "New content" # Replace content\nknowns doc edit "Doc Name" -a "Appended content" # Append to content\n```\n\n#### Doc Organization\n\n| Doc Type | Location | Example |\n|----------|----------|---------|\n| **Important/Core docs** | Root `.knowns/docs/` | `README.md`, `ARCHITECTURE.md`, `CONVENTIONS.md` |\n| **Guides** | `.knowns/docs/guides/` | `guides/getting-started.md` |\n| **Patterns** | `.knowns/docs/patterns/` | `patterns/controller.md` |\n| **API docs** | `.knowns/docs/api/` | `api/endpoints.md` |\n| **Other categorized docs** | `.knowns/docs/<category>/` | `security/auth-patterns.md` |\n\n```bash\n# Important docs - at root (no -f flag)\nknowns doc create "README" -d "Project overview" -t "core"\nknowns doc create "ARCHITECTURE" -d "System design" -t "core"\n\n# Categorized docs - use -f for folder\nknowns doc create "Getting Started" -d "Setup guide" -t "guide" -f "guides"\nknowns doc create "Controller Pattern" -d "MVC pattern" -t "pattern" -f "patterns"\n```\n\n### Search\n\n```bash\n# Search everything\nknowns search "query" --plain\n\n# Search specific type\nknowns search "auth" --type task --plain\nknowns search "patterns" --type doc --plain\n\n# Filter\nknowns search "bug" --status in-progress --priority high --plain\n```\n\n---\n\n## Task Structure\n\n### Title\n\nClear summary (WHAT needs to be done).\n\n| Bad | Good |\n|-----|------|\n| Do auth stuff | Add JWT authentication |\n| Fix bug | Fix login timeout on slow networks |\n| Update docs | Document rate limiting in API.md |\n\n### Description\n\nExplains WHY and WHAT (not HOW). **Link related docs using `@doc/<path>`**\n\n```markdown\nWe need JWT authentication because sessions don\'t scale for our microservices architecture.\n\nRelated docs: @doc/security-patterns, @doc/api-guidelines\n```\n\n### Acceptance Criteria\n\n**Outcome-oriented**, testable criteria. NOT implementation steps.\n\n| Bad (Implementation details) | Good (Outcomes) |\n|------------------------------|-----------------|\n| Add function handleLogin() in auth.ts | User can login and receive JWT token |\n| Use bcrypt for hashing | Passwords are securely hashed |\n| Add try-catch blocks | Errors return appropriate HTTP status codes |\n\n### Implementation Plan\n\nHOW to solve. Added AFTER taking task, BEFORE coding.\n\n```markdown\n1. Research JWT libraries (see @doc/security-patterns)\n2. Design token structure (access + refresh tokens)\n3. Implement auth middleware\n4. Add unit tests (aim for 90%+ coverage)\n5. Update API.md with new endpoints\n```\n\n### Implementation Notes\n\nSummary for PR description. Added AFTER completion.\n\n```markdown\n## Summary\nImplemented JWT auth using jsonwebtoken library.\n\n## Changes\n- Added auth middleware in src/middleware/auth.ts\n- Added /login and /refresh endpoints\n- Created JWT utility functions\n\n## Tests\n- Added 15 unit tests\n- Coverage: 94%\n\n## Documentation\n- Updated API.md with authentication section\n```\n\n---\n\n## Error Handling\n\n### Common Errors and Solutions\n\n| Error | Cause | Solution |\n|-------|-------|----------|\n| `Error: Task not found` | Invalid task ID | Run `knowns task list --plain` to find correct ID |\n| `Error: No active timer` | Calling `time stop` without active timer | Start timer first: `knowns time start <id>` |\n| `Error: Timer already running` | Starting timer when one is active | Stop current: `knowns time stop` |\n| `Error: Invalid status` | Wrong status format | Use lowercase with hyphens: `in-progress`, not `In Progress` |\n| `Error: AC index out of range` | Checking non-existent criterion | View task first: `knowns task <id> --plain` |\n| `Error: Document not found` | Wrong document name | Run `knowns doc list --plain` to find correct name |\n| `Error: Not initialized` | Running commands without init | Run `knowns init` first |\n\n### Debugging Commands\n\n```bash\n# Check CLI version\nknowns --version\n\n# Verify project is initialized\nknowns status\n\n# View raw task data (for debugging)\nknowns task <id> --json\n\n# Check timer status\nknowns time status\n```\n\n---\n\n## Definition of Done\n\nA task is **Done** ONLY when **ALL** criteria are met:\n\n### Via CLI (Required)\n\n- [ ] All acceptance criteria checked: `--check-ac <index>` (only after work is actually done)\n- [ ] Implementation notes added: `--notes "..."`\n- [ ] \u23F1\uFE0F Timer stopped: `knowns time stop` (MANDATORY - do not skip!)\n- [ ] Status set to done: `-s done`\n- [ ] Knowledge extracted to docs (if applicable)\n\n### Via Code (Required)\n\n- [ ] All tests pass\n- [ ] Documentation updated\n- [ ] Code reviewed (linting, formatting)\n- [ ] No regressions introduced\n\n---\n\n## Status & Priority Reference\n\n### Status Values\n\nUse **lowercase with hyphens**:\n\n| Status | Description | When to Use |\n|--------|-------------|-------------|\n| `todo` | Not started | Default for new tasks |\n| `in-progress` | Currently working | After taking task |\n| `in-review` | In code review | PR submitted |\n| `blocked` | Waiting on dependency | External blocker |\n| `done` | Completed | All criteria met |\n\n### Priority Values\n\n| Priority | Description |\n|----------|-------------|\n| `low` | Can wait, nice-to-have |\n| `medium` | Normal priority (default) |\n| `high` | Urgent, time-sensitive |\n\n---\n\n## Common Mistakes\n\n| Wrong | Right |\n|-------|-------|\n| Edit .md files directly | Use `knowns task edit` |\n| Change `- [ ]` to `- [x]` in file | Use `--check-ac <index>` |\n| Check AC before completing work | Only check AC AFTER work is actually done |\n| Skip time tracking | ALWAYS use `time start` and `time stop` |\n| Start coding without reading docs | Read ALL related docs FIRST |\n| Skip `knowns doc list` on new project | Always list docs when starting |\n| Assume you know the conventions | Read CONVENTIONS/ARCHITECTURE docs |\n| Plan without checking docs | Read docs before planning |\n| Ignore similar completed tasks | Search done tasks for patterns |\n| Missing doc links in description/plan | Link docs using `@doc/<path>` |\n| Write refs as `@.knowns/docs/...` or `@.knowns/tasks/...` | Use input format: `@doc/<path>`, `@task-<id>` |\n| Forget `--plain` flag | Always use `--plain` for AI |\n| Code before plan approval | Share plan, WAIT for approval |\n| Mark done without all criteria | Check ALL criteria first |\n| Write implementation steps in AC | Write outcome-oriented criteria |\n| Use `"In Progress"` or `"Done"` | Use `in-progress`, `done` |\n| Use `@yourself` or unknown assignee | Use `$(knowns config get defaultAssignee --plain \\|\\| echo "@me")` |\n| Ignore refs in task description | Follow ALL refs (`@.knowns/...`) before planning |\n| See `@.knowns/docs/...` but don\'t read | Use `knowns doc "<path>" --plain` |\n| See `@.knowns/tasks/task-X` but don\'t check | Use `knowns task X --plain` for context |\n| Follow only first-level refs | Recursively follow nested refs until complete |\n| Use `--plain` with `task create` | `--plain` is only for view/list commands |\n| Use `--plain` with `task edit` | `--plain` is only for view/list commands |\n| Use `--plain` with `doc create` | `--plain` is only for view/list commands |\n| Use `--plain` with `doc edit` | `--plain` is only for view/list commands |\n\n---\n\n## The `--plain` Flag (AI Agents)\n\n> **\u26A0\uFE0F CRITICAL FOR AI AGENTS**: The `--plain` flag is ONLY supported by **view/list/search** commands. Using it with create/edit commands will cause errors!\n\n### \u2705 Commands that support `--plain`\n\nThese are **read-only** commands - use `--plain` to get clean output:\n\n```bash\n# Task viewing/listing\nknowns task <id> --plain\nknowns task view <id> --plain\nknowns task list --plain\nknowns task list --status in-progress --plain\n\n# Doc viewing/listing\nknowns doc <path> --plain\nknowns doc view "<path>" --plain\nknowns doc list --plain\nknowns doc list --tag <tag> --plain\n\n# Search\nknowns search "<query>" --plain\nknowns search "<query>" --type task --plain\nknowns search "<query>" --type doc --plain\n\n# Config\nknowns config get <key> --plain\n```\n\n### \u274C Commands that do NOT support `--plain`\n\nThese are **write** commands - NEVER use `--plain`:\n\n```bash\n# Task create/edit - NO --plain!\nknowns task create "Title" -d "Description" # \u2705 Correct\nknowns task create "Title" --plain # \u274C ERROR!\nknowns task edit <id> -s done # \u2705 Correct\nknowns task edit <id> -s done --plain # \u274C ERROR!\n\n# Doc create/edit - NO --plain!\nknowns doc create "Title" -d "Description" # \u2705 Correct\nknowns doc create "Title" --plain # \u274C ERROR!\nknowns doc edit "name" -c "content" # \u2705 Correct\nknowns doc edit "name" -c "content" --plain # \u274C ERROR!\n\n# Time tracking - NO --plain!\nknowns time start <id> # \u2705 Correct\nknowns time stop # \u2705 Correct\nknowns time add <id> 2h # \u2705 Correct\n```\n\n### Quick Reference Table\n\n| Command Type | Example | `--plain` Support |\n|-------------|---------|-------------------|\n| `task <id>` | `knowns task 42 --plain` | \u2705 Yes |\n| `task list` | `knowns task list --plain` | \u2705 Yes |\n| `task create` | `knowns task create "Title"` | \u274C No |\n| `task edit` | `knowns task edit 42 -s done` | \u274C No |\n| `doc <path>` | `knowns doc "README" --plain` | \u2705 Yes |\n| `doc list` | `knowns doc list --plain` | \u2705 Yes |\n| `doc create` | `knowns doc create "Title"` | \u274C No |\n| `doc edit` | `knowns doc edit "name" -c "..."` | \u274C No |\n| `search` | `knowns search "query" --plain` | \u2705 Yes |\n| `time start/stop` | `knowns time start 42` | \u274C No |\n| `time add` | `knowns time add 42 2h` | \u274C No |\n| `config get` | `knowns config get key --plain` | \u2705 Yes |\n| `config set` | `knowns config set key value` | \u274C No |\n\n---\n\n## Platform-Specific Notes\n\n### Multi-line Input\n\nDifferent shells handle multi-line strings differently:\n\n**Bash / Zsh (Recommended)**\n```bash\nknowns task edit <id> --plan $\'1. First step\\n2. Second step\\n3. Third step\'\n```\n\n**PowerShell**\n```powershell\nknowns task edit <id> --plan "1. First step`n2. Second step`n3. Third step"\n```\n\n**Cross-platform (Using printf)**\n```bash\nknowns task edit <id> --plan "$(printf \'1. First step\\n2. Second step\\n3. Third step\')"\n```\n\n**Using heredoc (for long content)**\n```bash\nknowns task edit <id> --plan "$(cat <<EOF\n1. First step\n2. Second step\n3. Third step\nEOF\n)"\n```\n\n### Path Separators\n\n- **Unix/macOS**: Use forward slashes: `./docs/api.md`\n- **Windows**: Both work, but prefer forward slashes for consistency\n\n### Windows Command Line Limit\n\nWindows has ~8191 character limit. For long content, append in chunks:\n\n```bash\n# 1. Create or reset with short content\nknowns doc edit "doc-name" -c "## Overview\\n\\nShort intro."\n\n# 2. Append each section separately\nknowns doc edit "doc-name" -a "## Section 1\\n\\nContent..."\nknowns doc edit "doc-name" -a "## Section 2\\n\\nMore content..."\n```\n\nOr use file-based options:\n\n```bash\nknowns doc edit "doc-name" --content-file ./content.md\nknowns doc edit "doc-name" --append-file ./more.md\n```\n\n---\n\n## Best Practices Checklist\n\n### For AI Agents: Session Start\n\n- [ ] List all docs: `knowns doc list --plain`\n- [ ] Read README/ARCHITECTURE docs\n- [ ] Understand coding conventions\n- [ ] Review current task backlog\n\n### Before Starting Work\n\n- [ ] Task has clear acceptance criteria\n- [ ] ALL refs in task followed (`@.knowns/...`)\n- [ ] Nested refs recursively followed until complete context gathered\n- [ ] Related docs searched: `knowns search "keyword" --type doc --plain`\n- [ ] ALL relevant docs read: `knowns doc "Doc Name" --plain`\n- [ ] Similar done tasks reviewed for patterns\n- [ ] Task assigned to self: `-a $(knowns config get defaultAssignee --plain || echo "@me")`\n- [ ] Status set to in-progress: `-s in-progress`\n- [ ] Timer started: `knowns time start <id>`\n\n### During Work\n\n- [ ] Implementation plan created and approved\n- [ ] Doc links included in plan: `@doc/<path>`\n- [ ] Criteria checked as completed: `--check-ac <index>`\n- [ ] Progress notes appended: `--append-notes "\u2713 ..."`\n\n### After Work\n\n- [ ] All acceptance criteria checked (only after work is done)\n- [ ] Implementation notes added: `--notes "..."`\n- [ ] Timer stopped: `knowns time stop`\n- [ ] Tests passing\n- [ ] Documentation updated\n- [ ] Status set to done: `-s done`\n- [ ] Knowledge extracted to docs (if applicable): patterns, solutions, conventions\n\n---\n\n## Quick Reference Card\n\n```bash\n# === AGENT INITIALIZATION (Once per session) ===\nknowns doc list --plain\nknowns doc "README" --plain\nknowns doc "ARCHITECTURE" --plain\nknowns doc "CONVENTIONS" --plain\n\n# === FULL WORKFLOW ===\nknowns task create "Title" -d "Description" --ac "Criterion"\nknowns task edit <id> -s in-progress -a $(knowns config get defaultAssignee --plain || echo "@me")\nknowns time start <id> # \u23F1\uFE0F REQUIRED: Start timer\nknowns search "keyword" --type doc --plain\nknowns doc "Doc Name" --plain\nknowns search "keyword" --type task --status done --plain # Learn from history\nknowns task edit <id> --plan $\'1. Step (see @doc/file)\\n2. Step\'\n# ... wait for approval, then implement ...\n# Only check AC AFTER completing the work:\nknowns task edit <id> --check-ac 1\nknowns task edit <id> --append-notes "\u2713 Completed: feature X"\nknowns task edit <id> --check-ac 2\nknowns task edit <id> --append-notes "\u2713 Completed: feature Y"\nknowns time stop # \u23F1\uFE0F REQUIRED: Stop timer\nknowns task edit <id> -s done\n# Optional: Extract knowledge to docs if generalizable patterns found\n\n# === VIEW & SEARCH ===\nknowns task <id> --plain # Shorthand for view\nknowns task list --plain\nknowns task list --status in-progress --assignee $(knowns config get defaultAssignee --plain || echo "@me") --plain\nknowns search "query" --plain\nknowns search "bug" --type task --status in-progress --plain\n\n# === TIME TRACKING ===\nknowns time start <id>\nknowns time stop\nknowns time status\nknowns time report --from "2025-12-01" --to "2025-12-31"\n\n# === DOCUMENTATION ===\nknowns doc list --plain\nknowns doc "path/doc-name" --plain # Shorthand for view\nknowns doc create "Title" -d "Description" -t "tags" -f "folder"\nknowns doc edit "doc-name" -c "New content"\nknowns doc edit "doc-name" -a "Appended content"\n```\n\n---\n\n**Maintained By**: Knowns CLI Team\n\n<!-- KNOWNS GUIDELINES END -->\n';
|
|
47074
43465
|
|
|
47075
43466
|
// src/templates/mcp/gemini.md
|
|
47076
|
-
var gemini_default2 = '<!-- KNOWNS GUIDELINES START -->\n# Knowns MCP - Gemini Quick Reference\n\n## RULES\n- NEVER edit .md files directly - use MCP tools only\n- Read docs BEFORE coding\n- Start timer when taking task, stop when done\n\n## SESSION START\n```\nmcp__knowns__list_docs({})\nmcp__knowns__get_doc({ path: "README" })\nmcp__knowns__list_tasks({})\n```\n\n## TASK WORKFLOW\n\n### 1. Take Task\n```\nmcp__knowns__get_task({ taskId: "<id>" })\nmcp__knowns__update_task({ taskId: "<id>", status: "in-progress", assignee: "@me" })\nmcp__knowns__start_time({ taskId: "<id>" })\n```\n\n### 2. Read Context\n```\nmcp__knowns__list_docs({ tag: "guides" })\nmcp__knowns__get_doc({ path: "path/name" })\nmcp__knowns__search_docs({ query: "keyword" })\n```\n\n### 3. Plan & Implement\n```\nmcp__knowns__update_task({ taskId: "<id>", description: "Updated with plan..." })\n// Wait for approval, then code\nmcp__knowns__update_task({ taskId: "<id>", labels: ["done-ac-1"] })\n```\n\n### 4. Complete\n```\nmcp__knowns__stop_time({ taskId: "<id>" })\nmcp__knowns__update_task({ taskId: "<id>", status: "done" })\n```\n\n## MCP TOOLS CHEATSHEET\n\n### Task\n```\nmcp__knowns__list_tasks({})\nmcp__knowns__list_tasks({ status: "in-progress" })\nmcp__knowns__get_task({ taskId: "<id>" })\nmcp__knowns__create_task({ title: "Title", description: "Desc", priority: "high" })\nmcp__knowns__update_task({ taskId: "<id>", status: "<status>", assignee: "@me" })\nmcp__knowns__search_tasks({ query: "keyword" })\n```\n\n### Doc\n```\nmcp__knowns__list_docs({})\nmcp__knowns__list_docs({ tag: "patterns" })\nmcp__knowns__get_doc({ path: "name" })\nmcp__knowns__create_doc({ title: "Title", description: "Desc", tags: ["tag1"] })\nmcp__knowns__update_doc({ path: "name", content: "new content" })\nmcp__knowns__update_doc({ path: "name", appendContent: "more content" })\nmcp__knowns__search_docs({ query: "keyword" })\n```\n\n### Time\n```\nmcp__knowns__start_time({ taskId: "<id>" })\nmcp__knowns__stop_time({ taskId: "<id>" })\nmcp__knowns__add_time({ taskId: "<id>", duration: "2h", note: "Note" })\nmcp__knowns__get_time_report({})\n```\n\n### Board\n```\nmcp__knowns__get_board({})\n```\n\n## REFS\n\n### Reading (output format)\n- `@.knowns/tasks/task-X - Title.md` \u2192 `mcp__knowns__get_task({ taskId: "X" })`\n- `@.knowns/docs/path/name.md` \u2192 `mcp__knowns__get_doc({ path: "path/name" })`\n\n### Writing (input format)\n- Task: `@task-X`\n- Doc: `@doc/path/name`\n\n## STATUS & PRIORITY\n\n**Status:** `todo`, `in-progress`, `in-review`, `blocked`, `done`\n**Priority:** `low`, `medium`, `high`\n\n## LONG CONTENT\n\nFor large docs, append in chunks:\n```\n# Create with initial content\nmcp__knowns__create_doc({ title: "Title", content: "## Overview\\n\\nIntro." })\n\n# Append sections\nmcp__knowns__update_doc({ path: "name", appendContent: "## Section 1\\n\\n..." })\nmcp__knowns__update_doc({ path: "name", appendContent: "## Section 2\\n\\n..." })\n```\n<!-- KNOWNS GUIDELINES END -->\n';
|
|
43467
|
+
var gemini_default2 = '<!-- KNOWNS GUIDELINES START -->\n# Knowns MCP - Gemini Quick Reference\n\n## RULES\n- NEVER edit .md files directly - use MCP tools only\n- Read docs BEFORE coding\n- Start timer when taking task, stop when done\n\n## SESSION START\n```\nmcp__knowns__list_docs({})\nmcp__knowns__get_doc({ path: "README" })\nmcp__knowns__list_tasks({})\n```\n\n## TASK WORKFLOW\n\n### 1. Take Task\n```\nmcp__knowns__get_task({ taskId: "<id>" })\nmcp__knowns__update_task({ taskId: "<id>", status: "in-progress", assignee: "@me" })\nmcp__knowns__start_time({ taskId: "<id>" })\n```\n\n### 2. Read Context\n```\nmcp__knowns__list_docs({ tag: "guides" })\nmcp__knowns__get_doc({ path: "path/name" })\nmcp__knowns__search_docs({ query: "keyword" })\n```\n\n### 3. Plan & Implement\n```\nmcp__knowns__update_task({ taskId: "<id>", description: "Updated with plan..." })\n// Wait for approval, then code\nmcp__knowns__update_task({ taskId: "<id>", labels: ["done-ac-1"] })\n```\n\n### 4. Complete\n```\nmcp__knowns__stop_time({ taskId: "<id>" })\nmcp__knowns__update_task({ taskId: "<id>", status: "done" })\n```\n\n## MCP TOOLS CHEATSHEET\n\n### Task\n```\nmcp__knowns__list_tasks({})\nmcp__knowns__list_tasks({ status: "in-progress" })\nmcp__knowns__get_task({ taskId: "<id>" })\nmcp__knowns__create_task({ title: "Title", description: "Desc", priority: "high" })\nmcp__knowns__update_task({ taskId: "<id>", status: "<status>", assignee: "@me" })\nmcp__knowns__search_tasks({ query: "keyword" })\n```\n\n### Doc\n```\nmcp__knowns__list_docs({})\nmcp__knowns__list_docs({ tag: "patterns" })\nmcp__knowns__get_doc({ path: "name" })\nmcp__knowns__create_doc({ title: "Title", description: "Desc", tags: ["tag1"] })\nmcp__knowns__update_doc({ path: "name", content: "new content" })\nmcp__knowns__update_doc({ path: "name", appendContent: "more content" })\nmcp__knowns__search_docs({ query: "keyword" })\n```\n\n**Doc Organization:**\n| Type | Location |\n|------|----------|\n| Core docs | Root `.knowns/docs/` (no folder param) |\n| Categorized | `.knowns/docs/<folder>/` (use folder param) |\n\n### Time\n```\nmcp__knowns__start_time({ taskId: "<id>" })\nmcp__knowns__stop_time({ taskId: "<id>" })\nmcp__knowns__add_time({ taskId: "<id>", duration: "2h", note: "Note" })\nmcp__knowns__get_time_report({})\n```\n\n### Board\n```\nmcp__knowns__get_board({})\n```\n\n## REFS\n\n### Reading (output format)\n- `@.knowns/tasks/task-X - Title.md` \u2192 `mcp__knowns__get_task({ taskId: "X" })`\n- `@.knowns/docs/path/name.md` \u2192 `mcp__knowns__get_doc({ path: "path/name" })`\n\n### Writing (input format)\n- Task: `@task-X`\n- Doc: `@doc/path/name`\n\n## STATUS & PRIORITY\n\n**Status:** `todo`, `in-progress`, `in-review`, `blocked`, `done`\n**Priority:** `low`, `medium`, `high`\n\n## LONG CONTENT\n\nFor large docs, append in chunks:\n```\n# Create with initial content\nmcp__knowns__create_doc({ title: "Title", content: "## Overview\\n\\nIntro." })\n\n# Append sections\nmcp__knowns__update_doc({ path: "name", appendContent: "## Section 1\\n\\n..." })\nmcp__knowns__update_doc({ path: "name", appendContent: "## Section 2\\n\\n..." })\n```\n<!-- KNOWNS GUIDELINES END -->\n';
|
|
47077
43468
|
|
|
47078
43469
|
// src/templates/mcp/general.md
|
|
47079
|
-
var general_default2 = '<!-- KNOWNS GUIDELINES START -->\n# Knowns MCP Guidelines\n\n## Core Principles\n\n### 1. MCP Tool Operations\n**Use MCP tools for ALL Knowns operations. NEVER edit .md files directly.**\n\nThis ensures data integrity, maintains proper change history, and prevents file corruption.\n\n### 2. Documentation-First (For AI Agents)\n**ALWAYS read project documentation BEFORE planning or coding.**\n\nAI agents must understand project context, conventions, and existing patterns before making any changes. This prevents rework and ensures consistency.\n\n---\n\n## AI Agent Guidelines\n\n> **CRITICAL**: Before performing ANY task, AI agents MUST read documentation to understand project context.\n\n### First-Time Initialization\n\nWhen starting a new session or working on an unfamiliar project:\n\n```\n# 1. List all available documentation\nmcp__knowns__list_docs({})\n\n# 2. Read essential project docs (prioritize these)\nmcp__knowns__get_doc({ path: "README" })\nmcp__knowns__get_doc({ path: "ARCHITECTURE" })\nmcp__knowns__get_doc({ path: "CONVENTIONS" })\nmcp__knowns__get_doc({ path: "API" })\n\n# 3. Review current task backlog\nmcp__knowns__list_tasks({})\nmcp__knowns__list_tasks({ status: "in-progress" })\n```\n\n### Before Taking Any Task\n\n```\n# 1. View the task details\nmcp__knowns__get_task({ taskId: "<id>" })\n\n# 2. Follow ALL refs in the task (see Reference System section)\n# @.knowns/tasks/task-44 - ... \u2192 mcp__knowns__get_task({ taskId: "44" })\n# @.knowns/docs/patterns/module.md \u2192 mcp__knowns__get_doc({ path: "patterns/module" })\n\n# 3. Search for additional related documentation\nmcp__knowns__search_docs({ query: "<keywords from task>" })\n\n# 4. Read ALL related docs before planning\nmcp__knowns__get_doc({ path: "<related-doc>" })\n\n# 5. Check for similar completed tasks (learn from history)\nmcp__knowns__search_tasks({ query: "<keywords>", status: "done" })\n```\n\n### Why Documentation First?\n\n| Without Reading Docs | With Reading Docs |\n|---------------------|-------------------|\n| Reinvent existing patterns | Reuse established patterns |\n| Break conventions | Follow project standards |\n| Duplicate code | Use existing utilities |\n| Wrong architecture decisions | Align with system design |\n| Inconsistent naming | Match naming conventions |\n\n### Context Checklist for Agents\n\nBefore writing ANY code, ensure you can answer:\n\n- [ ] Have I followed ALL refs (`@.knowns/...`) in the task?\n- [ ] Have I followed nested refs recursively?\n- [ ] What is the project\'s overall architecture?\n- [ ] What coding conventions does this project follow?\n- [ ] Are there existing patterns/utilities I should reuse?\n- [ ] What are the testing requirements?\n- [ ] How should I structure my implementation?\n\n> **Remember**: A few minutes reading docs saves hours of rework. NEVER skip this step.\n\n---\n\n## Reference System (Refs)\n\nTasks and docs can contain **references** to other tasks/docs. AI agents MUST understand and follow these refs to gather complete context.\n\n### Reference Formats\n\n| Type | When Writing (Input) | When Reading (Output) | MCP Tool |\n|------|---------------------|----------------------|----------|\n| **Task ref** | `@task-<id>` | `@.knowns/tasks/task-<id> - <title>.md` | `mcp__knowns__get_task({ taskId: "<id>" })` |\n| **Doc ref** | `@doc/<path>` | `@.knowns/docs/<path>.md` | `mcp__knowns__get_doc({ path: "<path>" })` |\n\n> **CRITICAL for AI Agents**:\n> - When **WRITING** refs (in descriptions, plans, notes): Use `@task-<id>` and `@doc/<path>`\n> - When **READING** output: You\'ll see `@.knowns/tasks/...` and `@.knowns/docs/...`\n> - **NEVER write** the output format (`@.knowns/...`) - always use input format\n\n### How to Follow Refs\n\nWhen you read a task and see refs in system output format, follow them:\n\n```\n# Example: Task 42 output contains:\n# @.knowns/tasks/task-44 - CLI Task Create Command.md\n# @.knowns/docs/patterns/module.md\n\n# Follow task ref (extract ID from task-<id>)\nmcp__knowns__get_task({ taskId: "44" })\n\n# Follow doc ref (extract path, remove .md)\nmcp__knowns__get_doc({ path: "patterns/module" })\n```\n\n### Recursive Following\n\nRefs can be nested. Follow until complete context is gathered:\n\n```\nTask 42\n \u2192 @.knowns/docs/README.md\n \u2192 @.knowns/docs/patterns/module.md (found in README)\n \u2192 (read for full pattern details)\n```\n\n> **CRITICAL**: Never assume you understand a task fully without following its refs. Refs contain essential context that may change your implementation approach.\n\n---\n\n## Quick Start\n\n```\n# Initialize project (use CLI for this)\nknowns init [name]\n\n# Create task with acceptance criteria\nmcp__knowns__create_task({\n title: "Title",\n description: "Description",\n priority: "high",\n labels: ["label1", "label2"]\n})\n\n# View task\nmcp__knowns__get_task({ taskId: "<id>" })\n\n# List tasks\nmcp__knowns__list_tasks({})\n\n# Search (tasks + docs)\nmcp__knowns__search_tasks({ query: "query" })\nmcp__knowns__search_docs({ query: "query" })\n```\n\n---\n\n## End-to-End Example\n\nHere\'s a complete workflow for an AI agent implementing a feature:\n\n```\n# === AGENT SESSION START (Do this once per session) ===\n\n# 0a. List all available documentation\nmcp__knowns__list_docs({})\n\n# 0b. Read essential project docs\nmcp__knowns__get_doc({ path: "README" })\nmcp__knowns__get_doc({ path: "ARCHITECTURE" })\nmcp__knowns__get_doc({ path: "CONVENTIONS" })\n\n# Now the agent understands project context and conventions\n\n# === TASK WORKFLOW ===\n\n# 1. Create the task\nmcp__knowns__create_task({\n title: "Add password reset flow",\n description: "Users need ability to reset forgotten passwords via email",\n priority: "high",\n labels: ["auth", "feature"]\n})\n\n# Output: Created task 42\n\n# 2. Take the task and start timer\nmcp__knowns__update_task({\n taskId: "42",\n status: "in-progress",\n assignee: "@howznguyen"\n})\nmcp__knowns__start_time({ taskId: "42" })\n\n# 3. Search for related documentation\nmcp__knowns__search_docs({ query: "password security" })\n\n# 4. Read the documentation\nmcp__knowns__get_doc({ path: "security-patterns" })\n\n# 5. Create implementation plan (SHARE WITH USER, WAIT FOR APPROVAL)\nmcp__knowns__update_task({\n taskId: "42",\n plan: "1. Review security patterns (see @doc/security-patterns)\\n2. Design token generation with 1-hour expiry\\n3. Implement /forgot-password endpoint\\n4. Add unit tests"\n})\n\n# 6. After approval, implement and update task progressively\nmcp__knowns__update_task({\n taskId: "42",\n appendNotes: "\u2713 Implemented /forgot-password endpoint"\n})\n\n# 7. Add final implementation notes\nmcp__knowns__update_task({\n taskId: "42",\n notes: "## Summary\\nImplemented complete password reset flow.\\n\\n## Changes\\n- Added POST /forgot-password endpoint\\n- Added POST /reset-password endpoint"\n})\n\n# 8. Stop timer and complete\nmcp__knowns__stop_time({ taskId: "42" })\nmcp__knowns__update_task({\n taskId: "42",\n status: "done"\n})\n```\n\n---\n\n## Task Workflow\n\n### Step 1: Take Task\n\n```\n# Update task status and assign to self\nmcp__knowns__update_task({\n taskId: "<id>",\n status: "in-progress",\n assignee: "@<your-username>"\n})\n```\n\n> **Note**: Use your username as configured in the project. Check project config for `defaultAssignee`.\n\n### Step 2: Start Time Tracking (REQUIRED)\n\n```\nmcp__knowns__start_time({ taskId: "<id>" })\n```\n\n> **CRITICAL**: Time tracking is MANDATORY. Always start timer when taking a task and stop when done. This data is essential for:\n> - Accurate project estimation\n> - Identifying bottlenecks\n> - Resource planning\n> - Sprint retrospectives\n\n### Step 3: Read Related Documentation\n\n> **FOR AI AGENTS**: This step is MANDATORY, not optional. You must understand the codebase before planning.\n\n```\n# Search for related docs\nmcp__knowns__search_docs({ query: "authentication" })\n\n# View relevant documents\nmcp__knowns__get_doc({ path: "API Guidelines" })\nmcp__knowns__get_doc({ path: "Security Patterns" })\n\n# Also check for similar completed tasks\nmcp__knowns__search_tasks({ query: "auth", status: "done" })\n```\n\n> **CRITICAL**: ALWAYS read related documentation BEFORE planning!\n\n### Step 4: Create Implementation Plan\n\n```\nmcp__knowns__update_task({\n taskId: "<id>",\n plan: "1. Research patterns (see @doc/security-patterns)\\n2. Design middleware\\n3. Implement\\n4. Add tests\\n5. Update docs"\n})\n```\n\n> **CRITICAL**:\n> - Share plan with user and **WAIT for approval** before coding\n> - Include doc references using `@doc/<path>` format\n\n### Step 5: Implement\n\n```\n# Work through implementation plan step by step\n# IMPORTANT: Only update task AFTER completing the work, not before\n\n# After completing work, append notes:\nmcp__knowns__update_task({\n taskId: "<id>",\n appendNotes: "\u2713 Completed: <brief description>"\n})\n```\n\n> **CRITICAL**: Never claim work is done before it\'s actually completed.\n\n### Step 6: Handle Dynamic Requests (During Implementation)\n\nIf the user adds new requirements during implementation:\n\n```\n# Append note about scope change\nmcp__knowns__update_task({\n taskId: "<id>",\n appendNotes: "\u26A0\uFE0F Scope updated: Added requirement for X per user request"\n})\n\n# Continue with Step 5 (Implement) for new requirements\n```\n\n> **Note**: Always document scope changes. This helps track why a task took longer than expected.\n\n### Step 7: Add Implementation Notes\n\n```\n# Add comprehensive notes (suitable for PR description)\nmcp__knowns__update_task({\n taskId: "<id>",\n notes: "## Summary\\n\\nImplemented JWT auth.\\n\\n## Changes\\n- Added middleware\\n- Added tests"\n})\n\n# OR append progressively (recommended)\nmcp__knowns__update_task({\n taskId: "<id>",\n appendNotes: "\u2713 Implemented middleware"\n})\n```\n\n### Step 8: Stop Time Tracking (REQUIRED)\n\n```\nmcp__knowns__stop_time({ taskId: "<id>" })\n```\n\n> **CRITICAL**: Never forget to stop the timer. If you forget, use manual entry:\n> `mcp__knowns__add_time({ taskId: "<id>", duration: "2h", note: "Forgot to stop timer" })`\n\n### Step 9: Complete Task\n\n```\nmcp__knowns__update_task({\n taskId: "<id>",\n status: "done"\n})\n```\n\n### Step 10: Handle Post-Completion Changes (If Applicable)\n\nIf the user requests changes or updates AFTER task is marked done:\n\n```\n# 1. Reopen task - set back to in-progress\nmcp__knowns__update_task({\n taskId: "<id>",\n status: "in-progress"\n})\n\n# 2. Restart time tracking (REQUIRED)\nmcp__knowns__start_time({ taskId: "<id>" })\n\n# 3. Document the reopen reason\nmcp__knowns__update_task({\n taskId: "<id>",\n appendNotes: "\u{1F504} Reopened: User requested changes - <reason>"\n})\n\n# 4. Follow Step 5-9 again (Implement \u2192 Notes \u2192 Stop Timer \u2192 Done)\n```\n\n> **CRITICAL**: Treat post-completion changes as a mini-workflow. Always:\n> - Reopen task (in-progress)\n> - Start timer again\n> - Document why it was reopened\n> - Follow the same completion process\n\n### Step 11: Knowledge Extraction (Post-Completion)\n\nAfter completing a task, extract reusable knowledge to docs:\n\n```\n# Search if similar pattern already documented\nmcp__knowns__search_docs({ query: "<pattern/concept>" })\n\n# If new knowledge, create a doc for future reference\nmcp__knowns__create_doc({\n title: "Pattern: <Name>",\n description: "Reusable pattern discovered during task implementation",\n tags: ["pattern", "<domain>"],\n folder: "patterns"\n})\n\n# Or append to existing doc\nmcp__knowns__update_doc({\n path: "<existing-doc>",\n appendContent: "## New Section\\n\\nLearned from task <id>: ..."\n})\n```\n\n**When to extract knowledge:**\n- New patterns/conventions discovered\n- Common error solutions\n- Reusable code snippets or approaches\n- Integration patterns with external services\n- Performance optimization techniques\n\n> **CRITICAL**: Only extract **generalizable** knowledge. Task-specific details belong in implementation notes, not docs.\n\n---\n\n## Essential MCP Tools\n\n### Task Management\n\n```\n# Create task\nmcp__knowns__create_task({\n title: "Title",\n description: "Description",\n priority: "high", # low, medium, high\n labels: ["label1"],\n status: "todo", # todo, in-progress, in-review, done, blocked\n assignee: "@username"\n})\n\n# Get task\nmcp__knowns__get_task({ taskId: "<id>" })\n\n# Update task\nmcp__knowns__update_task({\n taskId: "<id>",\n title: "New title",\n description: "New description",\n status: "in-progress",\n priority: "high",\n assignee: "@username",\n labels: ["new", "labels"],\n plan: "Implementation plan...",\n notes: "Implementation notes...",\n appendNotes: "Append to existing notes..."\n})\n\n# List tasks\nmcp__knowns__list_tasks({})\nmcp__knowns__list_tasks({ status: "in-progress" })\nmcp__knowns__list_tasks({ assignee: "@username" })\nmcp__knowns__list_tasks({ priority: "high" })\nmcp__knowns__list_tasks({ label: "feature" })\n\n# Search tasks\nmcp__knowns__search_tasks({ query: "auth" })\n```\n\n### Time Tracking\n\n```\n# Start timer\nmcp__knowns__start_time({ taskId: "<id>" })\n\n# Stop timer\nmcp__knowns__stop_time({ taskId: "<id>" })\n\n# Manual entry\nmcp__knowns__add_time({\n taskId: "<id>",\n duration: "2h", # e.g., "2h", "30m", "1h30m"\n note: "Optional note",\n date: "2025-12-25" # Optional, defaults to now\n})\n\n# Reports\nmcp__knowns__get_time_report({})\nmcp__knowns__get_time_report({\n from: "2025-12-01",\n to: "2025-12-31",\n groupBy: "task" # task, label, status\n})\n```\n\n### Documentation\n\n```\n# List docs\nmcp__knowns__list_docs({})\nmcp__knowns__list_docs({ tag: "architecture" })\n\n# Get doc\nmcp__knowns__get_doc({ path: "README" })\nmcp__knowns__get_doc({ path: "patterns/module" })\n\n# Create doc\nmcp__knowns__create_doc({\n title: "Title",\n description: "Description",\n tags: ["tag1", "tag2"],\n folder: "folder/path", # Optional\n content: "Initial content..."\n})\n\n# Update doc\nmcp__knowns__update_doc({\n path: "doc-name",\n title: "New Title",\n description: "New description",\n tags: ["new", "tags"],\n content: "Replace content...",\n appendContent: "Append to content..."\n})\n\n# Search docs\nmcp__knowns__search_docs({ query: "patterns" })\nmcp__knowns__search_docs({ query: "auth", tag: "security" })\n```\n\n### Board\n\n```\n# Get kanban board view\nmcp__knowns__get_board({})\n```\n\n---\n\n## Task Structure\n\n### Title\n\nClear summary (WHAT needs to be done).\n\n| Bad | Good |\n|-----|------|\n| Do auth stuff | Add JWT authentication |\n| Fix bug | Fix login timeout on slow networks |\n| Update docs | Document rate limiting in API.md |\n\n### Description\n\nExplains WHY and WHAT (not HOW). **Link related docs using `@doc/<path>`**\n\n```\nWe need JWT authentication because sessions don\'t scale for our microservices architecture.\n\nRelated docs: @doc/security-patterns, @doc/api-guidelines\n```\n\n### Acceptance Criteria\n\n**Outcome-oriented**, testable criteria. NOT implementation steps.\n\n| Bad (Implementation details) | Good (Outcomes) |\n|------------------------------|-----------------|\n| Add function handleLogin() in auth.ts | User can login and receive JWT token |\n| Use bcrypt for hashing | Passwords are securely hashed |\n| Add try-catch blocks | Errors return appropriate HTTP status codes |\n\n---\n\n## Definition of Done\n\nA task is **Done** ONLY when **ALL** criteria are met:\n\n### Via MCP Tools (Required)\n\n- [ ] All work completed and verified\n- [ ] Implementation notes added via `update_task`\n- [ ] \u23F1\uFE0F Timer stopped: `mcp__knowns__stop_time` (MANDATORY - do not skip!)\n- [ ] Status set to done via `update_task`\n- [ ] Knowledge extracted to docs (if applicable)\n\n### Via Code (Required)\n\n- [ ] All tests pass\n- [ ] Documentation updated\n- [ ] Code reviewed (linting, formatting)\n- [ ] No regressions introduced\n\n---\n\n## Status & Priority Reference\n\n### Status Values\n\n| Status | Description | When to Use |\n|--------|-------------|-------------|\n| `todo` | Not started | Default for new tasks |\n| `in-progress` | Currently working | After taking task |\n| `in-review` | In code review | PR submitted |\n| `blocked` | Waiting on dependency | External blocker |\n| `done` | Completed | All criteria met |\n\n### Priority Values\n\n| Priority | Description |\n|----------|-------------|\n| `low` | Can wait, nice-to-have |\n| `medium` | Normal priority (default) |\n| `high` | Urgent, time-sensitive |\n\n---\n\n## Common Mistakes\n\n| Wrong | Right |\n|-------|-------|\n| Edit .md files directly | Use MCP tools |\n| Skip time tracking | ALWAYS use `start_time` and `stop_time` |\n| Start coding without reading docs | Read ALL related docs FIRST |\n| Plan without checking docs | Read docs before planning |\n| Code before plan approval | Share plan, WAIT for approval |\n| Mark done without all criteria | Verify ALL criteria first |\n| Ignore refs in task description | Follow ALL refs before planning |\n| Follow only first-level refs | Recursively follow nested refs |\n\n---\n\n## Long Content Handling\n\nFor long documentation content, append in chunks:\n\n```\n# 1. Create doc with initial content\nmcp__knowns__create_doc({\n title: "Doc Title",\n content: "## Overview\\n\\nShort intro."\n})\n\n# 2. Append each section separately\nmcp__knowns__update_doc({\n path: "doc-title",\n appendContent: "## Section 1\\n\\nContent for section 1..."\n})\n\nmcp__knowns__update_doc({\n path: "doc-title",\n appendContent: "## Section 2\\n\\nContent for section 2..."\n})\n```\n\n> **Tip**: Each `appendContent` adds content after existing content. Use this for large docs to avoid context limits.\n\n---\n\n## Best Practices Checklist\n\n### For AI Agents: Session Start\n\n- [ ] List all docs: `mcp__knowns__list_docs({})`\n- [ ] Read README/ARCHITECTURE docs\n- [ ] Understand coding conventions\n- [ ] Review current task backlog\n\n### Before Starting Work\n\n- [ ] Task details retrieved: `mcp__knowns__get_task`\n- [ ] ALL refs in task followed\n- [ ] Nested refs recursively followed\n- [ ] Related docs searched and read\n- [ ] Similar done tasks reviewed for patterns\n- [ ] Task assigned to self via `update_task`\n- [ ] Status set to in-progress\n- [ ] Timer started: `mcp__knowns__start_time`\n\n### During Work\n\n- [ ] Implementation plan created and approved\n- [ ] Doc links included in plan: `@doc/<path>`\n- [ ] Progress notes appended: `appendNotes`\n\n### After Work\n\n- [ ] All work verified complete\n- [ ] Implementation notes added\n- [ ] Timer stopped: `mcp__knowns__stop_time`\n- [ ] Tests passing\n- [ ] Documentation updated\n- [ ] Status set to done\n- [ ] Knowledge extracted to docs (if applicable)\n\n---\n\n## Quick Reference\n\n```\n# === AGENT INITIALIZATION (Once per session) ===\nmcp__knowns__list_docs({})\nmcp__knowns__get_doc({ path: "README" })\nmcp__knowns__get_doc({ path: "ARCHITECTURE" })\nmcp__knowns__get_doc({ path: "CONVENTIONS" })\n\n# === FULL WORKFLOW ===\nmcp__knowns__create_task({ title: "Title", description: "Description" })\nmcp__knowns__update_task({ taskId: "<id>", status: "in-progress", assignee: "@me" })\nmcp__knowns__start_time({ taskId: "<id>" }) # \u23F1\uFE0F REQUIRED\nmcp__knowns__search_docs({ query: "keyword" })\nmcp__knowns__get_doc({ path: "Doc Name" })\nmcp__knowns__search_tasks({ query: "keyword", status: "done" }) # Learn from history\nmcp__knowns__update_task({ taskId: "<id>", plan: "1. Step\\n2. Step" })\n# ... wait for approval, then implement ...\nmcp__knowns__update_task({ taskId: "<id>", appendNotes: "\u2713 Completed: feature X" })\nmcp__knowns__stop_time({ taskId: "<id>" }) # \u23F1\uFE0F REQUIRED\nmcp__knowns__update_task({ taskId: "<id>", status: "done" })\n# Optional: Extract knowledge to docs if generalizable patterns found\n\n# === VIEW & SEARCH ===\nmcp__knowns__get_task({ taskId: "<id>" })\nmcp__knowns__list_tasks({})\nmcp__knowns__list_tasks({ status: "in-progress", assignee: "@me" })\nmcp__knowns__search_tasks({ query: "bug" })\nmcp__knowns__search_docs({ query: "pattern" })\n\n# === TIME TRACKING ===\nmcp__knowns__start_time({ taskId: "<id>" })\nmcp__knowns__stop_time({ taskId: "<id>" })\nmcp__knowns__add_time({ taskId: "<id>", duration: "2h" })\nmcp__knowns__get_time_report({})\n\n# === DOCUMENTATION ===\nmcp__knowns__list_docs({})\nmcp__knowns__get_doc({ path: "path/doc-name" })\nmcp__knowns__create_doc({ title: "Title", description: "Desc", tags: ["tag"] })\nmcp__knowns__update_doc({ path: "doc-name", appendContent: "New content" })\n```\n\n---\n\n**Maintained By**: Knowns CLI Team\n\n<!-- KNOWNS GUIDELINES END -->\n';
|
|
43470
|
+
var general_default2 = '<!-- KNOWNS GUIDELINES START -->\n# Knowns MCP Guidelines\n\n## Core Principles\n\n### 1. MCP Tool Operations\n**Use MCP tools for ALL Knowns operations. NEVER edit .md files directly.**\n\nThis ensures data integrity, maintains proper change history, and prevents file corruption.\n\n### 2. Documentation-First (For AI Agents)\n**ALWAYS read project documentation BEFORE planning or coding.**\n\nAI agents must understand project context, conventions, and existing patterns before making any changes. This prevents rework and ensures consistency.\n\n---\n\n## AI Agent Guidelines\n\n> **CRITICAL**: Before performing ANY task, AI agents MUST read documentation to understand project context.\n\n### First-Time Initialization\n\nWhen starting a new session or working on an unfamiliar project:\n\n```\n# 1. List all available documentation\nmcp__knowns__list_docs({})\n\n# 2. Read essential project docs (prioritize these)\nmcp__knowns__get_doc({ path: "README" })\nmcp__knowns__get_doc({ path: "ARCHITECTURE" })\nmcp__knowns__get_doc({ path: "CONVENTIONS" })\nmcp__knowns__get_doc({ path: "API" })\n\n# 3. Review current task backlog\nmcp__knowns__list_tasks({})\nmcp__knowns__list_tasks({ status: "in-progress" })\n```\n\n### Before Taking Any Task\n\n```\n# 1. View the task details\nmcp__knowns__get_task({ taskId: "<id>" })\n\n# 2. Follow ALL refs in the task (see Reference System section)\n# @.knowns/tasks/task-44 - ... \u2192 mcp__knowns__get_task({ taskId: "44" })\n# @.knowns/docs/patterns/module.md \u2192 mcp__knowns__get_doc({ path: "patterns/module" })\n\n# 3. Search for additional related documentation\nmcp__knowns__search_docs({ query: "<keywords from task>" })\n\n# 4. Read ALL related docs before planning\nmcp__knowns__get_doc({ path: "<related-doc>" })\n\n# 5. Check for similar completed tasks (learn from history)\nmcp__knowns__search_tasks({ query: "<keywords>", status: "done" })\n```\n\n### Why Documentation First?\n\n| Without Reading Docs | With Reading Docs |\n|---------------------|-------------------|\n| Reinvent existing patterns | Reuse established patterns |\n| Break conventions | Follow project standards |\n| Duplicate code | Use existing utilities |\n| Wrong architecture decisions | Align with system design |\n| Inconsistent naming | Match naming conventions |\n\n### Context Checklist for Agents\n\nBefore writing ANY code, ensure you can answer:\n\n- [ ] Have I followed ALL refs (`@.knowns/...`) in the task?\n- [ ] Have I followed nested refs recursively?\n- [ ] What is the project\'s overall architecture?\n- [ ] What coding conventions does this project follow?\n- [ ] Are there existing patterns/utilities I should reuse?\n- [ ] What are the testing requirements?\n- [ ] How should I structure my implementation?\n\n> **Remember**: A few minutes reading docs saves hours of rework. NEVER skip this step.\n\n---\n\n## Reference System (Refs)\n\nTasks and docs can contain **references** to other tasks/docs. AI agents MUST understand and follow these refs to gather complete context.\n\n### Reference Formats\n\n| Type | When Writing (Input) | When Reading (Output) | MCP Tool |\n|------|---------------------|----------------------|----------|\n| **Task ref** | `@task-<id>` | `@.knowns/tasks/task-<id> - <title>.md` | `mcp__knowns__get_task({ taskId: "<id>" })` |\n| **Doc ref** | `@doc/<path>` | `@.knowns/docs/<path>.md` | `mcp__knowns__get_doc({ path: "<path>" })` |\n\n> **CRITICAL for AI Agents**:\n> - When **WRITING** refs (in descriptions, plans, notes): Use `@task-<id>` and `@doc/<path>`\n> - When **READING** output: You\'ll see `@.knowns/tasks/...` and `@.knowns/docs/...`\n> - **NEVER write** the output format (`@.knowns/...`) - always use input format\n\n### How to Follow Refs\n\nWhen you read a task and see refs in system output format, follow them:\n\n```\n# Example: Task 42 output contains:\n# @.knowns/tasks/task-44 - CLI Task Create Command.md\n# @.knowns/docs/patterns/module.md\n\n# Follow task ref (extract ID from task-<id>)\nmcp__knowns__get_task({ taskId: "44" })\n\n# Follow doc ref (extract path, remove .md)\nmcp__knowns__get_doc({ path: "patterns/module" })\n```\n\n### Recursive Following\n\nRefs can be nested. Follow until complete context is gathered:\n\n```\nTask 42\n \u2192 @.knowns/docs/README.md\n \u2192 @.knowns/docs/patterns/module.md (found in README)\n \u2192 (read for full pattern details)\n```\n\n> **CRITICAL**: Never assume you understand a task fully without following its refs. Refs contain essential context that may change your implementation approach.\n\n---\n\n## Quick Start\n\n```\n# Initialize project (use CLI for this)\nknowns init [name]\n\n# Create task with acceptance criteria\nmcp__knowns__create_task({\n title: "Title",\n description: "Description",\n priority: "high",\n labels: ["label1", "label2"]\n})\n\n# View task\nmcp__knowns__get_task({ taskId: "<id>" })\n\n# List tasks\nmcp__knowns__list_tasks({})\n\n# Search (tasks + docs)\nmcp__knowns__search_tasks({ query: "query" })\nmcp__knowns__search_docs({ query: "query" })\n```\n\n---\n\n## End-to-End Example\n\nHere\'s a complete workflow for an AI agent implementing a feature:\n\n```\n# === AGENT SESSION START (Do this once per session) ===\n\n# 0a. List all available documentation\nmcp__knowns__list_docs({})\n\n# 0b. Read essential project docs\nmcp__knowns__get_doc({ path: "README" })\nmcp__knowns__get_doc({ path: "ARCHITECTURE" })\nmcp__knowns__get_doc({ path: "CONVENTIONS" })\n\n# Now the agent understands project context and conventions\n\n# === TASK WORKFLOW ===\n\n# 1. Create the task\nmcp__knowns__create_task({\n title: "Add password reset flow",\n description: "Users need ability to reset forgotten passwords via email",\n priority: "high",\n labels: ["auth", "feature"]\n})\n\n# Output: Created task 42\n\n# 2. Take the task and start timer\nmcp__knowns__update_task({\n taskId: "42",\n status: "in-progress",\n assignee: "@howznguyen"\n})\nmcp__knowns__start_time({ taskId: "42" })\n\n# 3. Search for related documentation\nmcp__knowns__search_docs({ query: "password security" })\n\n# 4. Read the documentation\nmcp__knowns__get_doc({ path: "security-patterns" })\n\n# 5. Create implementation plan (SHARE WITH USER, WAIT FOR APPROVAL)\nmcp__knowns__update_task({\n taskId: "42",\n plan: "1. Review security patterns (see @doc/security-patterns)\\n2. Design token generation with 1-hour expiry\\n3. Implement /forgot-password endpoint\\n4. Add unit tests"\n})\n\n# 6. After approval, implement and update task progressively\nmcp__knowns__update_task({\n taskId: "42",\n appendNotes: "\u2713 Implemented /forgot-password endpoint"\n})\n\n# 7. Add final implementation notes\nmcp__knowns__update_task({\n taskId: "42",\n notes: "## Summary\\nImplemented complete password reset flow.\\n\\n## Changes\\n- Added POST /forgot-password endpoint\\n- Added POST /reset-password endpoint"\n})\n\n# 8. Stop timer and complete\nmcp__knowns__stop_time({ taskId: "42" })\nmcp__knowns__update_task({\n taskId: "42",\n status: "done"\n})\n```\n\n---\n\n## Task Workflow\n\n### Step 1: Take Task\n\n```\n# Update task status and assign to self\nmcp__knowns__update_task({\n taskId: "<id>",\n status: "in-progress",\n assignee: "@<your-username>"\n})\n```\n\n> **Note**: Use your username as configured in the project. Check project config for `defaultAssignee`.\n\n### Step 2: Start Time Tracking (REQUIRED)\n\n```\nmcp__knowns__start_time({ taskId: "<id>" })\n```\n\n> **CRITICAL**: Time tracking is MANDATORY. Always start timer when taking a task and stop when done. This data is essential for:\n> - Accurate project estimation\n> - Identifying bottlenecks\n> - Resource planning\n> - Sprint retrospectives\n\n### Step 3: Read Related Documentation\n\n> **FOR AI AGENTS**: This step is MANDATORY, not optional. You must understand the codebase before planning.\n\n```\n# Search for related docs\nmcp__knowns__search_docs({ query: "authentication" })\n\n# View relevant documents\nmcp__knowns__get_doc({ path: "API Guidelines" })\nmcp__knowns__get_doc({ path: "Security Patterns" })\n\n# Also check for similar completed tasks\nmcp__knowns__search_tasks({ query: "auth", status: "done" })\n```\n\n> **CRITICAL**: ALWAYS read related documentation BEFORE planning!\n\n### Step 4: Create Implementation Plan\n\n```\nmcp__knowns__update_task({\n taskId: "<id>",\n plan: "1. Research patterns (see @doc/security-patterns)\\n2. Design middleware\\n3. Implement\\n4. Add tests\\n5. Update docs"\n})\n```\n\n> **CRITICAL**:\n> - Share plan with user and **WAIT for approval** before coding\n> - Include doc references using `@doc/<path>` format\n\n### Step 5: Implement\n\n```\n# Work through implementation plan step by step\n# IMPORTANT: Only update task AFTER completing the work, not before\n\n# After completing work, append notes:\nmcp__knowns__update_task({\n taskId: "<id>",\n appendNotes: "\u2713 Completed: <brief description>"\n})\n```\n\n> **CRITICAL**: Never claim work is done before it\'s actually completed.\n\n### Step 6: Handle Dynamic Requests (During Implementation)\n\nIf the user adds new requirements during implementation:\n\n```\n# Append note about scope change\nmcp__knowns__update_task({\n taskId: "<id>",\n appendNotes: "\u26A0\uFE0F Scope updated: Added requirement for X per user request"\n})\n\n# Continue with Step 5 (Implement) for new requirements\n```\n\n> **Note**: Always document scope changes. This helps track why a task took longer than expected.\n\n### Step 7: Add Implementation Notes\n\n```\n# Add comprehensive notes (suitable for PR description)\nmcp__knowns__update_task({\n taskId: "<id>",\n notes: "## Summary\\n\\nImplemented JWT auth.\\n\\n## Changes\\n- Added middleware\\n- Added tests"\n})\n\n# OR append progressively (recommended)\nmcp__knowns__update_task({\n taskId: "<id>",\n appendNotes: "\u2713 Implemented middleware"\n})\n```\n\n### Step 8: Stop Time Tracking (REQUIRED)\n\n```\nmcp__knowns__stop_time({ taskId: "<id>" })\n```\n\n> **CRITICAL**: Never forget to stop the timer. If you forget, use manual entry:\n> `mcp__knowns__add_time({ taskId: "<id>", duration: "2h", note: "Forgot to stop timer" })`\n\n### Step 9: Complete Task\n\n```\nmcp__knowns__update_task({\n taskId: "<id>",\n status: "done"\n})\n```\n\n### Step 10: Handle Post-Completion Changes (If Applicable)\n\nIf the user requests changes or updates AFTER task is marked done:\n\n```\n# 1. Reopen task - set back to in-progress\nmcp__knowns__update_task({\n taskId: "<id>",\n status: "in-progress"\n})\n\n# 2. Restart time tracking (REQUIRED)\nmcp__knowns__start_time({ taskId: "<id>" })\n\n# 3. Document the reopen reason\nmcp__knowns__update_task({\n taskId: "<id>",\n appendNotes: "\u{1F504} Reopened: User requested changes - <reason>"\n})\n\n# 4. Follow Step 5-9 again (Implement \u2192 Notes \u2192 Stop Timer \u2192 Done)\n```\n\n> **CRITICAL**: Treat post-completion changes as a mini-workflow. Always:\n> - Reopen task (in-progress)\n> - Start timer again\n> - Document why it was reopened\n> - Follow the same completion process\n\n### Step 11: Knowledge Extraction (Post-Completion)\n\nAfter completing a task, extract reusable knowledge to docs:\n\n```\n# Search if similar pattern already documented\nmcp__knowns__search_docs({ query: "<pattern/concept>" })\n\n# If new knowledge, create a doc for future reference\nmcp__knowns__create_doc({\n title: "Pattern: <Name>",\n description: "Reusable pattern discovered during task implementation",\n tags: ["pattern", "<domain>"],\n folder: "patterns"\n})\n\n# Or append to existing doc\nmcp__knowns__update_doc({\n path: "<existing-doc>",\n appendContent: "## New Section\\n\\nLearned from task <id>: ..."\n})\n```\n\n**When to extract knowledge:**\n- New patterns/conventions discovered\n- Common error solutions\n- Reusable code snippets or approaches\n- Integration patterns with external services\n- Performance optimization techniques\n\n> **CRITICAL**: Only extract **generalizable** knowledge. Task-specific details belong in implementation notes, not docs.\n\n---\n\n## Essential MCP Tools\n\n### Task Management\n\n```\n# Create task\nmcp__knowns__create_task({\n title: "Title",\n description: "Description",\n priority: "high", # low, medium, high\n labels: ["label1"],\n status: "todo", # todo, in-progress, in-review, done, blocked\n assignee: "@username"\n})\n\n# Get task\nmcp__knowns__get_task({ taskId: "<id>" })\n\n# Update task\nmcp__knowns__update_task({\n taskId: "<id>",\n title: "New title",\n description: "New description",\n status: "in-progress",\n priority: "high",\n assignee: "@username",\n labels: ["new", "labels"],\n plan: "Implementation plan...",\n notes: "Implementation notes...",\n appendNotes: "Append to existing notes..."\n})\n\n# List tasks\nmcp__knowns__list_tasks({})\nmcp__knowns__list_tasks({ status: "in-progress" })\nmcp__knowns__list_tasks({ assignee: "@username" })\nmcp__knowns__list_tasks({ priority: "high" })\nmcp__knowns__list_tasks({ label: "feature" })\n\n# Search tasks\nmcp__knowns__search_tasks({ query: "auth" })\n```\n\n### Time Tracking\n\n```\n# Start timer\nmcp__knowns__start_time({ taskId: "<id>" })\n\n# Stop timer\nmcp__knowns__stop_time({ taskId: "<id>" })\n\n# Manual entry\nmcp__knowns__add_time({\n taskId: "<id>",\n duration: "2h", # e.g., "2h", "30m", "1h30m"\n note: "Optional note",\n date: "2025-12-25" # Optional, defaults to now\n})\n\n# Reports\nmcp__knowns__get_time_report({})\nmcp__knowns__get_time_report({\n from: "2025-12-01",\n to: "2025-12-31",\n groupBy: "task" # task, label, status\n})\n```\n\n### Documentation\n\n```\n# List docs\nmcp__knowns__list_docs({})\nmcp__knowns__list_docs({ tag: "architecture" })\n\n# Get doc\nmcp__knowns__get_doc({ path: "README" })\nmcp__knowns__get_doc({ path: "patterns/module" })\n\n# Create doc\nmcp__knowns__create_doc({\n title: "Title",\n description: "Description",\n tags: ["tag1", "tag2"],\n folder: "folder/path", # Optional\n content: "Initial content..."\n})\n\n# Update doc\nmcp__knowns__update_doc({\n path: "doc-name",\n title: "New Title",\n description: "New description",\n tags: ["new", "tags"],\n content: "Replace content...",\n appendContent: "Append to content..."\n})\n\n# Search docs\nmcp__knowns__search_docs({ query: "patterns" })\nmcp__knowns__search_docs({ query: "auth", tag: "security" })\n```\n\n#### Doc Organization\n\n| Doc Type | Location | Example |\n|----------|----------|---------|\n| **Important/Core docs** | Root `.knowns/docs/` | `README.md`, `ARCHITECTURE.md`, `CONVENTIONS.md` |\n| **Guides** | `.knowns/docs/guides/` | `guides/getting-started.md` |\n| **Patterns** | `.knowns/docs/patterns/` | `patterns/controller.md` |\n| **API docs** | `.knowns/docs/api/` | `api/endpoints.md` |\n| **Other categorized docs** | `.knowns/docs/<category>/` | `security/auth-patterns.md` |\n\n```\n# Important docs - at root (no folder)\nmcp__knowns__create_doc({ title: "README", description: "Project overview", tags: ["core"] })\nmcp__knowns__create_doc({ title: "ARCHITECTURE", description: "System design", tags: ["core"] })\n\n# Categorized docs - use folder\nmcp__knowns__create_doc({ title: "Getting Started", description: "Setup guide", tags: ["guide"], folder: "guides" })\nmcp__knowns__create_doc({ title: "Controller Pattern", description: "MVC pattern", tags: ["pattern"], folder: "patterns" })\n```\n\n### Board\n\n```\n# Get kanban board view\nmcp__knowns__get_board({})\n```\n\n---\n\n## Task Structure\n\n### Title\n\nClear summary (WHAT needs to be done).\n\n| Bad | Good |\n|-----|------|\n| Do auth stuff | Add JWT authentication |\n| Fix bug | Fix login timeout on slow networks |\n| Update docs | Document rate limiting in API.md |\n\n### Description\n\nExplains WHY and WHAT (not HOW). **Link related docs using `@doc/<path>`**\n\n```\nWe need JWT authentication because sessions don\'t scale for our microservices architecture.\n\nRelated docs: @doc/security-patterns, @doc/api-guidelines\n```\n\n### Acceptance Criteria\n\n**Outcome-oriented**, testable criteria. NOT implementation steps.\n\n| Bad (Implementation details) | Good (Outcomes) |\n|------------------------------|-----------------|\n| Add function handleLogin() in auth.ts | User can login and receive JWT token |\n| Use bcrypt for hashing | Passwords are securely hashed |\n| Add try-catch blocks | Errors return appropriate HTTP status codes |\n\n---\n\n## Definition of Done\n\nA task is **Done** ONLY when **ALL** criteria are met:\n\n### Via MCP Tools (Required)\n\n- [ ] All work completed and verified\n- [ ] Implementation notes added via `update_task`\n- [ ] \u23F1\uFE0F Timer stopped: `mcp__knowns__stop_time` (MANDATORY - do not skip!)\n- [ ] Status set to done via `update_task`\n- [ ] Knowledge extracted to docs (if applicable)\n\n### Via Code (Required)\n\n- [ ] All tests pass\n- [ ] Documentation updated\n- [ ] Code reviewed (linting, formatting)\n- [ ] No regressions introduced\n\n---\n\n## Status & Priority Reference\n\n### Status Values\n\n| Status | Description | When to Use |\n|--------|-------------|-------------|\n| `todo` | Not started | Default for new tasks |\n| `in-progress` | Currently working | After taking task |\n| `in-review` | In code review | PR submitted |\n| `blocked` | Waiting on dependency | External blocker |\n| `done` | Completed | All criteria met |\n\n### Priority Values\n\n| Priority | Description |\n|----------|-------------|\n| `low` | Can wait, nice-to-have |\n| `medium` | Normal priority (default) |\n| `high` | Urgent, time-sensitive |\n\n---\n\n## Common Mistakes\n\n| Wrong | Right |\n|-------|-------|\n| Edit .md files directly | Use MCP tools |\n| Skip time tracking | ALWAYS use `start_time` and `stop_time` |\n| Start coding without reading docs | Read ALL related docs FIRST |\n| Plan without checking docs | Read docs before planning |\n| Code before plan approval | Share plan, WAIT for approval |\n| Mark done without all criteria | Verify ALL criteria first |\n| Ignore refs in task description | Follow ALL refs before planning |\n| Follow only first-level refs | Recursively follow nested refs |\n\n---\n\n## Long Content Handling\n\nFor long documentation content, append in chunks:\n\n```\n# 1. Create doc with initial content\nmcp__knowns__create_doc({\n title: "Doc Title",\n content: "## Overview\\n\\nShort intro."\n})\n\n# 2. Append each section separately\nmcp__knowns__update_doc({\n path: "doc-title",\n appendContent: "## Section 1\\n\\nContent for section 1..."\n})\n\nmcp__knowns__update_doc({\n path: "doc-title",\n appendContent: "## Section 2\\n\\nContent for section 2..."\n})\n```\n\n> **Tip**: Each `appendContent` adds content after existing content. Use this for large docs to avoid context limits.\n\n---\n\n## Best Practices Checklist\n\n### For AI Agents: Session Start\n\n- [ ] List all docs: `mcp__knowns__list_docs({})`\n- [ ] Read README/ARCHITECTURE docs\n- [ ] Understand coding conventions\n- [ ] Review current task backlog\n\n### Before Starting Work\n\n- [ ] Task details retrieved: `mcp__knowns__get_task`\n- [ ] ALL refs in task followed\n- [ ] Nested refs recursively followed\n- [ ] Related docs searched and read\n- [ ] Similar done tasks reviewed for patterns\n- [ ] Task assigned to self via `update_task`\n- [ ] Status set to in-progress\n- [ ] Timer started: `mcp__knowns__start_time`\n\n### During Work\n\n- [ ] Implementation plan created and approved\n- [ ] Doc links included in plan: `@doc/<path>`\n- [ ] Progress notes appended: `appendNotes`\n\n### After Work\n\n- [ ] All work verified complete\n- [ ] Implementation notes added\n- [ ] Timer stopped: `mcp__knowns__stop_time`\n- [ ] Tests passing\n- [ ] Documentation updated\n- [ ] Status set to done\n- [ ] Knowledge extracted to docs (if applicable)\n\n---\n\n## Quick Reference\n\n```\n# === AGENT INITIALIZATION (Once per session) ===\nmcp__knowns__list_docs({})\nmcp__knowns__get_doc({ path: "README" })\nmcp__knowns__get_doc({ path: "ARCHITECTURE" })\nmcp__knowns__get_doc({ path: "CONVENTIONS" })\n\n# === FULL WORKFLOW ===\nmcp__knowns__create_task({ title: "Title", description: "Description" })\nmcp__knowns__update_task({ taskId: "<id>", status: "in-progress", assignee: "@me" })\nmcp__knowns__start_time({ taskId: "<id>" }) # \u23F1\uFE0F REQUIRED\nmcp__knowns__search_docs({ query: "keyword" })\nmcp__knowns__get_doc({ path: "Doc Name" })\nmcp__knowns__search_tasks({ query: "keyword", status: "done" }) # Learn from history\nmcp__knowns__update_task({ taskId: "<id>", plan: "1. Step\\n2. Step" })\n# ... wait for approval, then implement ...\nmcp__knowns__update_task({ taskId: "<id>", appendNotes: "\u2713 Completed: feature X" })\nmcp__knowns__stop_time({ taskId: "<id>" }) # \u23F1\uFE0F REQUIRED\nmcp__knowns__update_task({ taskId: "<id>", status: "done" })\n# Optional: Extract knowledge to docs if generalizable patterns found\n\n# === VIEW & SEARCH ===\nmcp__knowns__get_task({ taskId: "<id>" })\nmcp__knowns__list_tasks({})\nmcp__knowns__list_tasks({ status: "in-progress", assignee: "@me" })\nmcp__knowns__search_tasks({ query: "bug" })\nmcp__knowns__search_docs({ query: "pattern" })\n\n# === TIME TRACKING ===\nmcp__knowns__start_time({ taskId: "<id>" })\nmcp__knowns__stop_time({ taskId: "<id>" })\nmcp__knowns__add_time({ taskId: "<id>", duration: "2h" })\nmcp__knowns__get_time_report({})\n\n# === DOCUMENTATION ===\nmcp__knowns__list_docs({})\nmcp__knowns__get_doc({ path: "path/doc-name" })\nmcp__knowns__create_doc({ title: "Title", description: "Desc", tags: ["tag"] })\nmcp__knowns__update_doc({ path: "doc-name", appendContent: "New content" })\n```\n\n---\n\n**Maintained By**: Knowns CLI Team\n\n<!-- KNOWNS GUIDELINES END -->\n';
|
|
47080
43471
|
|
|
47081
43472
|
// src/commands/agents.ts
|
|
47082
43473
|
var PROJECT_ROOT = process.cwd();
|
|
@@ -47287,76 +43678,41 @@ Syncing agent instructions (${label})...
|
|
|
47287
43678
|
agentsCommand.addCommand(syncCommand);
|
|
47288
43679
|
|
|
47289
43680
|
// src/commands/init.ts
|
|
47290
|
-
|
|
43681
|
+
function checkGitExists(projectRoot) {
|
|
47291
43682
|
const gitPath = join4(projectRoot, ".git");
|
|
47292
|
-
if (existsSync2(gitPath)) {
|
|
47293
|
-
|
|
43683
|
+
if (!existsSync2(gitPath)) {
|
|
43684
|
+
console.log();
|
|
43685
|
+
console.log(source_default.red("\u2717 Not a git repository"));
|
|
43686
|
+
console.log();
|
|
43687
|
+
console.log(source_default.gray(" Knowns requires git for version control."));
|
|
43688
|
+
console.log(source_default.gray(" Please initialize git first:"));
|
|
43689
|
+
console.log();
|
|
43690
|
+
console.log(source_default.cyan(" git init"));
|
|
43691
|
+
console.log();
|
|
43692
|
+
process.exit(1);
|
|
47294
43693
|
}
|
|
47295
|
-
|
|
47296
|
-
|
|
47297
|
-
const
|
|
47298
|
-
|
|
47299
|
-
|
|
47300
|
-
|
|
47301
|
-
|
|
47302
|
-
|
|
47303
|
-
|
|
47304
|
-
try {
|
|
47305
|
-
execSync("git init", { cwd: projectRoot, stdio: "pipe" });
|
|
47306
|
-
console.log(source_default.green("\u2713 Git initialized successfully"));
|
|
47307
|
-
const gitignorePath = join4(projectRoot, ".gitignore");
|
|
47308
|
-
if (!existsSync2(gitignorePath)) {
|
|
47309
|
-
const addGitignore = await (0, import_prompts2.default)({
|
|
47310
|
-
type: "confirm",
|
|
47311
|
-
name: "add",
|
|
47312
|
-
message: "Create .gitignore with recommended exclusions?",
|
|
47313
|
-
initial: true
|
|
47314
|
-
});
|
|
47315
|
-
if (addGitignore.add) {
|
|
47316
|
-
const { writeFileSync } = await import("node:fs");
|
|
47317
|
-
const gitignoreContent = `# Dependencies
|
|
47318
|
-
node_modules/
|
|
47319
|
-
|
|
47320
|
-
# Build output
|
|
47321
|
-
dist/
|
|
47322
|
-
|
|
47323
|
-
# Environment files
|
|
47324
|
-
.env
|
|
47325
|
-
.env.local
|
|
47326
|
-
.env.*.local
|
|
47327
|
-
|
|
47328
|
-
# IDE
|
|
47329
|
-
.idea/
|
|
47330
|
-
.vscode/
|
|
47331
|
-
*.swp
|
|
47332
|
-
*.swo
|
|
47333
|
-
|
|
47334
|
-
# OS
|
|
47335
|
-
.DS_Store
|
|
47336
|
-
Thumbs.db
|
|
47337
|
-
|
|
47338
|
-
# Logs
|
|
47339
|
-
*.log
|
|
47340
|
-
npm-debug.log*
|
|
47341
|
-
|
|
47342
|
-
# Optional: Exclude time tracking data (uncomment if desired)
|
|
47343
|
-
# .knowns/time/
|
|
43694
|
+
}
|
|
43695
|
+
async function updateGitignoreForIgnoredMode(projectRoot) {
|
|
43696
|
+
const { appendFileSync, readFileSync: readFileSync2, writeFileSync } = await import("node:fs");
|
|
43697
|
+
const gitignorePath = join4(projectRoot, ".gitignore");
|
|
43698
|
+
const knownsIgnorePattern = `
|
|
43699
|
+
# knowns (ignore all except docs)
|
|
43700
|
+
.knowns/*
|
|
43701
|
+
!.knowns/docs/
|
|
43702
|
+
!.knowns/docs/**
|
|
47344
43703
|
`;
|
|
47345
|
-
|
|
47346
|
-
|
|
47347
|
-
|
|
47348
|
-
|
|
47349
|
-
|
|
47350
|
-
} catch (error48) {
|
|
47351
|
-
console.log(source_default.red("\u2717 Failed to initialize git"));
|
|
47352
|
-
if (error48 instanceof Error) {
|
|
47353
|
-
console.log(source_default.gray(` ${error48.message}`));
|
|
47354
|
-
}
|
|
47355
|
-
console.log();
|
|
43704
|
+
if (existsSync2(gitignorePath)) {
|
|
43705
|
+
const content = readFileSync2(gitignorePath, "utf-8");
|
|
43706
|
+
if (content.includes(".knowns/*")) {
|
|
43707
|
+
console.log(source_default.gray(" .gitignore already has knowns pattern"));
|
|
43708
|
+
return;
|
|
47356
43709
|
}
|
|
43710
|
+
appendFileSync(gitignorePath, knownsIgnorePattern);
|
|
43711
|
+
console.log(source_default.green("\u2713 Updated .gitignore with knowns pattern"));
|
|
47357
43712
|
} else {
|
|
47358
|
-
|
|
47359
|
-
|
|
43713
|
+
writeFileSync(gitignorePath, `${knownsIgnorePattern.trim()}
|
|
43714
|
+
`, "utf-8");
|
|
43715
|
+
console.log(source_default.green("\u2713 Created .gitignore with knowns pattern"));
|
|
47360
43716
|
}
|
|
47361
43717
|
}
|
|
47362
43718
|
async function runWizard() {
|
|
@@ -47375,6 +43731,25 @@ async function runWizard() {
|
|
|
47375
43731
|
initial: defaultName,
|
|
47376
43732
|
validate: (value) => value.trim() ? true : "Project name is required"
|
|
47377
43733
|
},
|
|
43734
|
+
{
|
|
43735
|
+
type: "select",
|
|
43736
|
+
name: "gitTrackingMode",
|
|
43737
|
+
message: "Git tracking mode",
|
|
43738
|
+
choices: [
|
|
43739
|
+
{
|
|
43740
|
+
title: "Git Tracked (recommended for teams)",
|
|
43741
|
+
value: "git-tracked",
|
|
43742
|
+
description: "All .knowns/ files tracked in git"
|
|
43743
|
+
},
|
|
43744
|
+
{
|
|
43745
|
+
title: "Git Ignored (personal use)",
|
|
43746
|
+
value: "git-ignored",
|
|
43747
|
+
description: "Only docs tracked, tasks/config ignored"
|
|
43748
|
+
}
|
|
43749
|
+
],
|
|
43750
|
+
initial: 0
|
|
43751
|
+
// git-tracked
|
|
43752
|
+
},
|
|
47378
43753
|
{
|
|
47379
43754
|
type: "text",
|
|
47380
43755
|
name: "defaultAssignee",
|
|
@@ -47452,6 +43827,7 @@ async function runWizard() {
|
|
|
47452
43827
|
defaultPriority: response.defaultPriority,
|
|
47453
43828
|
defaultLabels: response.defaultLabels?.filter((l) => l.trim()) || [],
|
|
47454
43829
|
timeFormat: response.timeFormat,
|
|
43830
|
+
gitTrackingMode: response.gitTrackingMode || "git-tracked",
|
|
47455
43831
|
guidelinesType: response.guidelinesType || "cli",
|
|
47456
43832
|
agentFiles: response.agentFiles || []
|
|
47457
43833
|
};
|
|
@@ -47470,7 +43846,7 @@ var initCommand = new Command("init").description("Initialize .knowns/ folder in
|
|
|
47470
43846
|
console.log(source_default.yellow("\u26A0\uFE0F Reinitializing existing project (--force)"));
|
|
47471
43847
|
console.log();
|
|
47472
43848
|
}
|
|
47473
|
-
|
|
43849
|
+
checkGitExists(projectRoot);
|
|
47474
43850
|
let config2;
|
|
47475
43851
|
const shouldRunWizard = options2.wizard === true || name === void 0 && options2.wizard !== false;
|
|
47476
43852
|
if (shouldRunWizard) {
|
|
@@ -47485,21 +43861,29 @@ var initCommand = new Command("init").description("Initialize .knowns/ folder in
|
|
|
47485
43861
|
defaultPriority: "medium",
|
|
47486
43862
|
defaultLabels: [],
|
|
47487
43863
|
timeFormat: "24h",
|
|
43864
|
+
gitTrackingMode: "git-tracked",
|
|
47488
43865
|
guidelinesType: "cli",
|
|
47489
43866
|
agentFiles: INSTRUCTION_FILES.filter((f) => f.selected)
|
|
47490
43867
|
};
|
|
47491
43868
|
}
|
|
43869
|
+
if (config2.gitTrackingMode === "git-ignored") {
|
|
43870
|
+
await updateGitignoreForIgnoredMode(projectRoot);
|
|
43871
|
+
}
|
|
47492
43872
|
const fileStore2 = new FileStore(projectRoot);
|
|
47493
43873
|
const project = await fileStore2.initProject(config2.name, {
|
|
47494
43874
|
defaultAssignee: config2.defaultAssignee,
|
|
47495
43875
|
defaultPriority: config2.defaultPriority,
|
|
47496
43876
|
defaultLabels: config2.defaultLabels,
|
|
47497
|
-
timeFormat: config2.timeFormat
|
|
43877
|
+
timeFormat: config2.timeFormat,
|
|
43878
|
+
gitTrackingMode: config2.gitTrackingMode
|
|
47498
43879
|
});
|
|
47499
43880
|
console.log();
|
|
47500
43881
|
console.log(source_default.green("\u2713 Project initialized successfully!"));
|
|
47501
43882
|
console.log(source_default.gray(` Name: ${project.name}`));
|
|
47502
43883
|
console.log(source_default.gray(` Location: ${knownsPath}`));
|
|
43884
|
+
console.log(
|
|
43885
|
+
source_default.gray(` Git Mode: ${config2.gitTrackingMode === "git-tracked" ? "Tracked (team)" : "Ignored (personal)"}`)
|
|
43886
|
+
);
|
|
47503
43887
|
if (config2.defaultAssignee) {
|
|
47504
43888
|
console.log(source_default.gray(` Default Assignee: ${config2.defaultAssignee}`));
|
|
47505
43889
|
}
|
|
@@ -50232,18 +46616,10 @@ import { join as join16 } from "node:path";
|
|
|
50232
46616
|
// src/server/index.ts
|
|
50233
46617
|
import { spawn } from "node:child_process";
|
|
50234
46618
|
import { existsSync as existsSync12, realpathSync } from "node:fs";
|
|
50235
|
-
import { createServer } from "node:http";
|
|
50236
46619
|
import { basename as basename2, dirname as dirname3, join as join15 } from "node:path";
|
|
50237
46620
|
import { fileURLToPath } from "node:url";
|
|
50238
46621
|
var import_cors = __toESM(require_lib2(), 1);
|
|
50239
|
-
var
|
|
50240
|
-
|
|
50241
|
-
// node_modules/ws/wrapper.mjs
|
|
50242
|
-
var import_stream = __toESM(require_stream(), 1);
|
|
50243
|
-
var import_receiver = __toESM(require_receiver(), 1);
|
|
50244
|
-
var import_sender = __toESM(require_sender(), 1);
|
|
50245
|
-
var import_websocket = __toESM(require_websocket(), 1);
|
|
50246
|
-
var import_websocket_server = __toESM(require_websocket_server(), 1);
|
|
46622
|
+
var import_express10 = __toESM(require_express2(), 1);
|
|
50247
46623
|
|
|
50248
46624
|
// src/server/middleware/error-handler.ts
|
|
50249
46625
|
function errorHandler(err, _req, res, _next) {
|
|
@@ -50394,7 +46770,7 @@ async function findMarkdownFiles(dir, baseDir) {
|
|
|
50394
46770
|
// src/server/routes/docs.ts
|
|
50395
46771
|
function createDocRoutes(ctx) {
|
|
50396
46772
|
const router = (0, import_express3.Router)();
|
|
50397
|
-
const { store, broadcast } = ctx;
|
|
46773
|
+
const { store, broadcast: broadcast2 } = ctx;
|
|
50398
46774
|
const docsDir = join12(store.projectRoot, ".knowns", "docs");
|
|
50399
46775
|
router.get("/", async (_req, res) => {
|
|
50400
46776
|
try {
|
|
@@ -50549,7 +46925,7 @@ function createDocRoutes(ctx) {
|
|
|
50549
46925
|
metadata: updatedFrontmatter,
|
|
50550
46926
|
content: content ?? ""
|
|
50551
46927
|
};
|
|
50552
|
-
|
|
46928
|
+
broadcast2({ type: "docs:updated", doc: updatedDoc });
|
|
50553
46929
|
res.json(updatedDoc);
|
|
50554
46930
|
} catch (error48) {
|
|
50555
46931
|
console.error("Error updating doc:", error48);
|
|
@@ -50563,32 +46939,32 @@ function createDocRoutes(ctx) {
|
|
|
50563
46939
|
var import_express4 = __toESM(require_express2(), 1);
|
|
50564
46940
|
function createNotifyRoutes(ctx) {
|
|
50565
46941
|
const router = (0, import_express4.Router)();
|
|
50566
|
-
const { store, broadcast } = ctx;
|
|
46942
|
+
const { store, broadcast: broadcast2 } = ctx;
|
|
50567
46943
|
router.post("/", async (req, res) => {
|
|
50568
46944
|
try {
|
|
50569
46945
|
const { taskId, type, docPath } = req.body;
|
|
50570
46946
|
if (taskId) {
|
|
50571
46947
|
const task = await store.getTask(taskId);
|
|
50572
46948
|
if (task) {
|
|
50573
|
-
|
|
46949
|
+
broadcast2({ type: "tasks:updated", task });
|
|
50574
46950
|
res.json({ success: true });
|
|
50575
46951
|
return;
|
|
50576
46952
|
}
|
|
50577
46953
|
} else if (type === "tasks:refresh") {
|
|
50578
|
-
|
|
46954
|
+
broadcast2({ type: "tasks:refresh" });
|
|
50579
46955
|
res.json({ success: true });
|
|
50580
46956
|
return;
|
|
50581
46957
|
} else if (type === "docs:updated" && docPath) {
|
|
50582
|
-
|
|
46958
|
+
broadcast2({ type: "docs:updated", docPath });
|
|
50583
46959
|
res.json({ success: true });
|
|
50584
46960
|
return;
|
|
50585
46961
|
} else if (type === "docs:refresh") {
|
|
50586
|
-
|
|
46962
|
+
broadcast2({ type: "docs:refresh" });
|
|
50587
46963
|
res.json({ success: true });
|
|
50588
46964
|
return;
|
|
50589
46965
|
} else if (type === "time:updated") {
|
|
50590
46966
|
const { active } = req.body;
|
|
50591
|
-
|
|
46967
|
+
broadcast2({ type: "time:updated", active });
|
|
50592
46968
|
res.json({ success: true });
|
|
50593
46969
|
return;
|
|
50594
46970
|
}
|
|
@@ -50667,7 +47043,7 @@ function createSearchRoutes(ctx) {
|
|
|
50667
47043
|
var import_express6 = __toESM(require_express2(), 1);
|
|
50668
47044
|
function createTaskRoutes(ctx) {
|
|
50669
47045
|
const router = (0, import_express6.Router)();
|
|
50670
|
-
const { store, broadcast } = ctx;
|
|
47046
|
+
const { store, broadcast: broadcast2 } = ctx;
|
|
50671
47047
|
router.get("/", async (_req, res) => {
|
|
50672
47048
|
try {
|
|
50673
47049
|
const tasks = await store.getAllTasks();
|
|
@@ -50704,7 +47080,7 @@ function createTaskRoutes(ctx) {
|
|
|
50704
47080
|
try {
|
|
50705
47081
|
const data = req.body;
|
|
50706
47082
|
const task = await store.createTask(data);
|
|
50707
|
-
|
|
47083
|
+
broadcast2({ type: "tasks:updated", task });
|
|
50708
47084
|
res.status(201).json(task);
|
|
50709
47085
|
} catch (error48) {
|
|
50710
47086
|
console.error("Error creating task:", error48);
|
|
@@ -50719,7 +47095,7 @@ function createTaskRoutes(ctx) {
|
|
|
50719
47095
|
return;
|
|
50720
47096
|
}
|
|
50721
47097
|
const archivedTasks = await store.batchArchiveTasks(olderThanMs);
|
|
50722
|
-
|
|
47098
|
+
broadcast2({ type: "tasks:batch-archived", tasks: archivedTasks });
|
|
50723
47099
|
res.json({ success: true, count: archivedTasks.length, tasks: archivedTasks });
|
|
50724
47100
|
} catch (error48) {
|
|
50725
47101
|
console.error("Error batch archiving tasks:", error48);
|
|
@@ -50730,7 +47106,7 @@ function createTaskRoutes(ctx) {
|
|
|
50730
47106
|
try {
|
|
50731
47107
|
const updates = req.body;
|
|
50732
47108
|
const task = await store.updateTask(req.params.id, updates);
|
|
50733
|
-
|
|
47109
|
+
broadcast2({ type: "tasks:updated", task });
|
|
50734
47110
|
res.json(task);
|
|
50735
47111
|
} catch (error48) {
|
|
50736
47112
|
console.error("Error updating task:", error48);
|
|
@@ -50740,7 +47116,7 @@ function createTaskRoutes(ctx) {
|
|
|
50740
47116
|
router.post("/:id/archive", async (req, res) => {
|
|
50741
47117
|
try {
|
|
50742
47118
|
const task = await store.archiveTask(req.params.id);
|
|
50743
|
-
|
|
47119
|
+
broadcast2({ type: "tasks:archived", task });
|
|
50744
47120
|
res.json({ success: true, task });
|
|
50745
47121
|
} catch (error48) {
|
|
50746
47122
|
console.error("Error archiving task:", error48);
|
|
@@ -50750,7 +47126,7 @@ function createTaskRoutes(ctx) {
|
|
|
50750
47126
|
router.post("/:id/unarchive", async (req, res) => {
|
|
50751
47127
|
try {
|
|
50752
47128
|
const task = await store.unarchiveTask(req.params.id);
|
|
50753
|
-
|
|
47129
|
+
broadcast2({ type: "tasks:unarchived", task });
|
|
50754
47130
|
res.json({ success: true, task });
|
|
50755
47131
|
} catch (error48) {
|
|
50756
47132
|
console.error("Error unarchiving task:", error48);
|
|
@@ -50767,7 +47143,7 @@ import { readFile as readFile9, writeFile as writeFile6 } from "node:fs/promises
|
|
|
50767
47143
|
import { join as join14 } from "node:path";
|
|
50768
47144
|
function createTimeRoutes(ctx) {
|
|
50769
47145
|
const router = (0, import_express7.Router)();
|
|
50770
|
-
const { store, broadcast } = ctx;
|
|
47146
|
+
const { store, broadcast: broadcast2 } = ctx;
|
|
50771
47147
|
const timePath = join14(store.projectRoot, ".knowns", "time.json");
|
|
50772
47148
|
async function loadTimeData2() {
|
|
50773
47149
|
if (!existsSync11(timePath)) {
|
|
@@ -50825,7 +47201,7 @@ function createTimeRoutes(ctx) {
|
|
|
50825
47201
|
totalPausedMs: 0
|
|
50826
47202
|
};
|
|
50827
47203
|
await saveTimeData2(data);
|
|
50828
|
-
|
|
47204
|
+
broadcast2({ type: "time:updated", active: { ...data.active, taskTitle: task.title } });
|
|
50829
47205
|
res.json({
|
|
50830
47206
|
success: true,
|
|
50831
47207
|
active: { ...data.active, taskTitle: task.title }
|
|
@@ -50860,11 +47236,11 @@ function createTimeRoutes(ctx) {
|
|
|
50860
47236
|
timeEntries: task.timeEntries,
|
|
50861
47237
|
timeSpent: task.timeSpent
|
|
50862
47238
|
});
|
|
50863
|
-
|
|
47239
|
+
broadcast2({ type: "tasks:updated", task });
|
|
50864
47240
|
}
|
|
50865
47241
|
data.active = null;
|
|
50866
47242
|
await saveTimeData2(data);
|
|
50867
|
-
|
|
47243
|
+
broadcast2({ type: "time:updated", active: null });
|
|
50868
47244
|
res.json({
|
|
50869
47245
|
success: true,
|
|
50870
47246
|
duration: seconds,
|
|
@@ -50889,7 +47265,7 @@ function createTimeRoutes(ctx) {
|
|
|
50889
47265
|
data.active.pausedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
50890
47266
|
await saveTimeData2(data);
|
|
50891
47267
|
const task = await store.getTask(data.active.taskId);
|
|
50892
|
-
|
|
47268
|
+
broadcast2({ type: "time:updated", active: { ...data.active, taskTitle: task?.title } });
|
|
50893
47269
|
res.json({
|
|
50894
47270
|
success: true,
|
|
50895
47271
|
active: { ...data.active, taskTitle: task?.title }
|
|
@@ -50915,7 +47291,7 @@ function createTimeRoutes(ctx) {
|
|
|
50915
47291
|
data.active.pausedAt = null;
|
|
50916
47292
|
await saveTimeData2(data);
|
|
50917
47293
|
const task = await store.getTask(data.active.taskId);
|
|
50918
|
-
|
|
47294
|
+
broadcast2({ type: "time:updated", active: { ...data.active, taskTitle: task?.title } });
|
|
50919
47295
|
res.json({
|
|
50920
47296
|
success: true,
|
|
50921
47297
|
active: { ...data.active, taskTitle: task?.title }
|
|
@@ -50941,19 +47317,64 @@ function createRoutes(ctx) {
|
|
|
50941
47317
|
return router;
|
|
50942
47318
|
}
|
|
50943
47319
|
|
|
47320
|
+
// src/server/routes/events.ts
|
|
47321
|
+
var import_express9 = __toESM(require_express2(), 1);
|
|
47322
|
+
var clients = /* @__PURE__ */ new Set();
|
|
47323
|
+
function broadcast(event, data) {
|
|
47324
|
+
const message = `event: ${event}
|
|
47325
|
+
data: ${JSON.stringify(data)}
|
|
47326
|
+
|
|
47327
|
+
`;
|
|
47328
|
+
for (const client of clients) {
|
|
47329
|
+
try {
|
|
47330
|
+
client.write(message);
|
|
47331
|
+
} catch {
|
|
47332
|
+
clients.delete(client);
|
|
47333
|
+
}
|
|
47334
|
+
}
|
|
47335
|
+
}
|
|
47336
|
+
function createEventsRoute() {
|
|
47337
|
+
const router = (0, import_express9.Router)();
|
|
47338
|
+
router.get("/", (req, res) => {
|
|
47339
|
+
res.setHeader("Content-Type", "text/event-stream");
|
|
47340
|
+
res.setHeader("Cache-Control", "no-cache");
|
|
47341
|
+
res.setHeader("Connection", "keep-alive");
|
|
47342
|
+
res.setHeader("X-Accel-Buffering", "no");
|
|
47343
|
+
res.flushHeaders();
|
|
47344
|
+
res.write(`event: connected
|
|
47345
|
+
data: ${JSON.stringify({ timestamp: Date.now() })}
|
|
47346
|
+
|
|
47347
|
+
`);
|
|
47348
|
+
clients.add(res);
|
|
47349
|
+
req.on("close", () => {
|
|
47350
|
+
clients.delete(res);
|
|
47351
|
+
});
|
|
47352
|
+
const heartbeatInterval = setInterval(() => {
|
|
47353
|
+
try {
|
|
47354
|
+
res.write(`: heartbeat ${Date.now()}
|
|
47355
|
+
|
|
47356
|
+
`);
|
|
47357
|
+
} catch {
|
|
47358
|
+
clearInterval(heartbeatInterval);
|
|
47359
|
+
clients.delete(res);
|
|
47360
|
+
}
|
|
47361
|
+
}, 3e4);
|
|
47362
|
+
res.on("close", () => {
|
|
47363
|
+
clearInterval(heartbeatInterval);
|
|
47364
|
+
clients.delete(res);
|
|
47365
|
+
});
|
|
47366
|
+
});
|
|
47367
|
+
return router;
|
|
47368
|
+
}
|
|
47369
|
+
|
|
50944
47370
|
// src/server/index.ts
|
|
50945
47371
|
var isBun2 = typeof globalThis.Bun !== "undefined";
|
|
50946
47372
|
async function startServer(options2) {
|
|
50947
47373
|
const { port, projectRoot, open } = options2;
|
|
50948
47374
|
const store = new FileStore(projectRoot);
|
|
50949
|
-
const
|
|
50950
|
-
|
|
50951
|
-
|
|
50952
|
-
for (const client of clients) {
|
|
50953
|
-
if (client.readyState === 1) {
|
|
50954
|
-
client.send(msg);
|
|
50955
|
-
}
|
|
50956
|
-
}
|
|
47375
|
+
const broadcastEvent = (data) => {
|
|
47376
|
+
const { type, ...payload } = data;
|
|
47377
|
+
broadcast(type, payload);
|
|
50957
47378
|
};
|
|
50958
47379
|
const currentFile = realpathSync(fileURLToPath(import.meta.url));
|
|
50959
47380
|
const currentDir = dirname3(currentFile);
|
|
@@ -50969,31 +47390,18 @@ async function startServer(options2) {
|
|
|
50969
47390
|
if (!existsSync12(join15(uiDistPath, "index.html"))) {
|
|
50970
47391
|
throw new Error(`UI build not found at ${uiDistPath}. Run 'bun run build:ui' first.`);
|
|
50971
47392
|
}
|
|
50972
|
-
const app = (0,
|
|
47393
|
+
const app = (0, import_express10.default)();
|
|
50973
47394
|
app.use((0, import_cors.default)());
|
|
50974
|
-
app.use(
|
|
50975
|
-
const httpServer = createServer(app);
|
|
50976
|
-
const wss = new import_websocket_server.default({ server: httpServer, path: "/ws" });
|
|
50977
|
-
wss.on("error", () => {
|
|
50978
|
-
});
|
|
50979
|
-
wss.on("connection", (ws) => {
|
|
50980
|
-
clients.add(ws);
|
|
50981
|
-
ws.on("close", () => {
|
|
50982
|
-
clients.delete(ws);
|
|
50983
|
-
});
|
|
50984
|
-
ws.on("error", (error48) => {
|
|
50985
|
-
console.error("WebSocket error:", error48);
|
|
50986
|
-
clients.delete(ws);
|
|
50987
|
-
});
|
|
50988
|
-
});
|
|
47395
|
+
app.use(import_express10.default.json());
|
|
50989
47396
|
app.use(
|
|
50990
47397
|
"/assets",
|
|
50991
|
-
|
|
47398
|
+
import_express10.default.static(join15(uiDistPath, "assets"), {
|
|
50992
47399
|
maxAge: "1y",
|
|
50993
47400
|
immutable: true
|
|
50994
47401
|
})
|
|
50995
47402
|
);
|
|
50996
|
-
app.use("/api",
|
|
47403
|
+
app.use("/api/events", createEventsRoute());
|
|
47404
|
+
app.use("/api", createRoutes({ store, broadcast: broadcastEvent }));
|
|
50997
47405
|
app.use(errorHandler);
|
|
50998
47406
|
app.get("/{*path}", (_req, res) => {
|
|
50999
47407
|
const indexPath = join15(uiDistPath, "index.html");
|
|
@@ -51004,16 +47412,7 @@ async function startServer(options2) {
|
|
|
51004
47412
|
}
|
|
51005
47413
|
});
|
|
51006
47414
|
return new Promise((resolve, reject) => {
|
|
51007
|
-
|
|
51008
|
-
if (err.code === "EADDRINUSE") {
|
|
51009
|
-
console.error(`Error: Port ${port} is already in use`);
|
|
51010
|
-
console.error("Please stop the process using this port or choose a different port");
|
|
51011
|
-
reject(err);
|
|
51012
|
-
} else {
|
|
51013
|
-
reject(err);
|
|
51014
|
-
}
|
|
51015
|
-
});
|
|
51016
|
-
httpServer.listen(port, () => {
|
|
47415
|
+
const server2 = app.listen(port, () => {
|
|
51017
47416
|
console.log(`\u2713 Server running at http://localhost:${port}`);
|
|
51018
47417
|
if (open) {
|
|
51019
47418
|
try {
|
|
@@ -51034,11 +47433,19 @@ async function startServer(options2) {
|
|
|
51034
47433
|
}
|
|
51035
47434
|
resolve({
|
|
51036
47435
|
close: () => {
|
|
51037
|
-
|
|
51038
|
-
httpServer.close();
|
|
47436
|
+
server2.close();
|
|
51039
47437
|
}
|
|
51040
47438
|
});
|
|
51041
47439
|
});
|
|
47440
|
+
server2.on("error", (err) => {
|
|
47441
|
+
if (err.code === "EADDRINUSE") {
|
|
47442
|
+
console.error(`Error: Port ${port} is already in use`);
|
|
47443
|
+
console.error("Please stop the process using this port or choose a different port");
|
|
47444
|
+
reject(err);
|
|
47445
|
+
} else {
|
|
47446
|
+
reject(err);
|
|
47447
|
+
}
|
|
47448
|
+
});
|
|
51042
47449
|
});
|
|
51043
47450
|
}
|
|
51044
47451
|
|
|
@@ -74120,7 +70527,7 @@ function showConfigInfo() {
|
|
|
74120
70527
|
// package.json
|
|
74121
70528
|
var package_default = {
|
|
74122
70529
|
name: "knowns",
|
|
74123
|
-
version: "0.
|
|
70530
|
+
version: "0.6.0",
|
|
74124
70531
|
description: "CLI tool for dev teams to manage tasks and documentation",
|
|
74125
70532
|
module: "index.ts",
|
|
74126
70533
|
type: "module",
|
|
@@ -74162,7 +70569,6 @@ var package_default = {
|
|
|
74162
70569
|
"@blocknote/core": "^0.45.0",
|
|
74163
70570
|
"@blocknote/react": "^0.45.0",
|
|
74164
70571
|
"@blocknote/shadcn": "^0.45.0",
|
|
74165
|
-
"@tiptap/extensions": "^3.14.0",
|
|
74166
70572
|
"@dnd-kit/core": "^6.3.1",
|
|
74167
70573
|
"@dnd-kit/sortable": "^10.0.0",
|
|
74168
70574
|
"@dnd-kit/utilities": "^3.2.2",
|
|
@@ -74181,6 +70587,7 @@ var package_default = {
|
|
|
74181
70587
|
"@radix-ui/react-slot": "^1.2.4",
|
|
74182
70588
|
"@radix-ui/react-tooltip": "^1.2.8",
|
|
74183
70589
|
"@tanstack/react-table": "^8.21.3",
|
|
70590
|
+
"@tiptap/extensions": "^3.14.0",
|
|
74184
70591
|
"@types/cors": "^2.8.19",
|
|
74185
70592
|
"@uiw/react-md-editor": "^4.0.11",
|
|
74186
70593
|
chalk: "^5.3.0",
|
|
@@ -74203,7 +70610,6 @@ var package_default = {
|
|
|
74203
70610
|
"tunnel-rat": "^0.1.2",
|
|
74204
70611
|
turndown: "^7.2.2",
|
|
74205
70612
|
"unist-util-visit": "^5.0.0",
|
|
74206
|
-
ws: "^8.18.3",
|
|
74207
70613
|
zod: "^4.2.1"
|
|
74208
70614
|
},
|
|
74209
70615
|
overrides: {
|
|
@@ -74220,7 +70626,6 @@ var package_default = {
|
|
|
74220
70626
|
"@types/prompts": "^2.4.9",
|
|
74221
70627
|
"@types/react": "^19.2.7",
|
|
74222
70628
|
"@types/react-dom": "^19.2.3",
|
|
74223
|
-
"@types/ws": "^8.18.1",
|
|
74224
70629
|
"@vitejs/plugin-react": "^5.1.2",
|
|
74225
70630
|
"@vitest/coverage-v8": "^4.0.16",
|
|
74226
70631
|
autoprefixer: "^10.4.23",
|