diff --git a/plugins/agentbridge/server/bridge-server.js b/plugins/agentbridge/server/bridge-server.js index ae4455f..7da41c5 100755 --- a/plugins/agentbridge/server/bridge-server.js +++ b/plugins/agentbridge/server/bridge-server.js @@ -45,7 +45,7 @@ var __export = (target, all) => { }); }; -// node_modules/ajv/dist/compile/codegen/code.js +// ../../../node_modules/ajv/dist/compile/codegen/code.js var require_code = __commonJS((exports) => { Object.defineProperty(exports, "__esModule", { value: true }); exports.regexpCode = exports.getEsmExportName = exports.getProperty = exports.safeStringify = exports.stringify = exports.strConcat = exports.addCodeArg = exports.str = exports._ = exports.nil = exports._Code = exports.Name = exports.IDENTIFIER = exports._CodeOrName = undefined; @@ -199,7 +199,7 @@ var require_code = __commonJS((exports) => { exports.regexpCode = regexpCode; }); -// node_modules/ajv/dist/compile/codegen/scope.js +// ../../../node_modules/ajv/dist/compile/codegen/scope.js var require_scope = __commonJS((exports) => { Object.defineProperty(exports, "__esModule", { value: true }); exports.ValueScope = exports.ValueScopeName = exports.Scope = exports.varKinds = exports.UsedValueState = undefined; @@ -345,7 +345,7 @@ var require_scope = __commonJS((exports) => { exports.ValueScope = ValueScope; }); -// node_modules/ajv/dist/compile/codegen/index.js +// ../../../node_modules/ajv/dist/compile/codegen/index.js var require_codegen = __commonJS((exports) => { Object.defineProperty(exports, "__esModule", { value: true }); exports.or = exports.and = exports.not = exports.CodeGen = exports.operators = exports.varKinds = exports.ValueScopeName = exports.ValueScope = exports.Scope = exports.Name = exports.regexpCode = exports.stringify = exports.getProperty = exports.nil = exports.strConcat = exports.str = exports._ = undefined; @@ -1055,7 +1055,7 @@ var require_codegen = __commonJS((exports) => { } }); -// node_modules/ajv/dist/compile/util.js +// ../../../node_modules/ajv/dist/compile/util.js var require_util = __commonJS((exports) => { Object.defineProperty(exports, "__esModule", { value: true }); exports.checkStrictMode = exports.getErrorPath = exports.Type = exports.useFunc = exports.setEvaluated = exports.evaluatedPropsToName = exports.mergeEvaluated = exports.eachItem = exports.unescapeJsonPointer = exports.escapeJsonPointer = exports.escapeFragment = exports.unescapeFragment = exports.schemaRefOrVal = exports.schemaHasRulesButRef = exports.schemaHasRules = exports.checkUnknownRules = exports.alwaysValidSchema = exports.toHash = undefined; @@ -1219,7 +1219,7 @@ var require_util = __commonJS((exports) => { exports.checkStrictMode = checkStrictMode; }); -// node_modules/ajv/dist/compile/names.js +// ../../../node_modules/ajv/dist/compile/names.js var require_names = __commonJS((exports) => { Object.defineProperty(exports, "__esModule", { value: true }); var codegen_1 = require_codegen(); @@ -1244,7 +1244,7 @@ var require_names = __commonJS((exports) => { exports.default = names; }); -// node_modules/ajv/dist/compile/errors.js +// ../../../node_modules/ajv/dist/compile/errors.js var require_errors = __commonJS((exports) => { Object.defineProperty(exports, "__esModule", { value: true }); exports.extendErrors = exports.resetErrorsCount = exports.reportExtraError = exports.reportError = exports.keyword$DataError = exports.keywordError = undefined; @@ -1362,7 +1362,7 @@ var require_errors = __commonJS((exports) => { } }); -// node_modules/ajv/dist/compile/validate/boolSchema.js +// ../../../node_modules/ajv/dist/compile/validate/boolSchema.js var require_boolSchema = __commonJS((exports) => { Object.defineProperty(exports, "__esModule", { value: true }); exports.boolOrEmptySchema = exports.topBoolOrEmptySchema = undefined; @@ -1410,7 +1410,7 @@ var require_boolSchema = __commonJS((exports) => { } }); -// node_modules/ajv/dist/compile/rules.js +// ../../../node_modules/ajv/dist/compile/rules.js var require_rules = __commonJS((exports) => { Object.defineProperty(exports, "__esModule", { value: true }); exports.getRules = exports.isJSONType = undefined; @@ -1438,7 +1438,7 @@ var require_rules = __commonJS((exports) => { exports.getRules = getRules; }); -// node_modules/ajv/dist/compile/validate/applicability.js +// ../../../node_modules/ajv/dist/compile/validate/applicability.js var require_applicability = __commonJS((exports) => { Object.defineProperty(exports, "__esModule", { value: true }); exports.shouldUseRule = exports.shouldUseGroup = exports.schemaHasRulesForType = undefined; @@ -1458,7 +1458,7 @@ var require_applicability = __commonJS((exports) => { exports.shouldUseRule = shouldUseRule; }); -// node_modules/ajv/dist/compile/validate/dataType.js +// ../../../node_modules/ajv/dist/compile/validate/dataType.js var require_dataType = __commonJS((exports) => { Object.defineProperty(exports, "__esModule", { value: true }); exports.reportTypeError = exports.checkDataTypes = exports.checkDataType = exports.coerceAndCheckDataType = exports.getJSONTypes = exports.getSchemaTypes = exports.DataType = undefined; @@ -1639,7 +1639,7 @@ var require_dataType = __commonJS((exports) => { } }); -// node_modules/ajv/dist/compile/validate/defaults.js +// ../../../node_modules/ajv/dist/compile/validate/defaults.js var require_defaults = __commonJS((exports) => { Object.defineProperty(exports, "__esModule", { value: true }); exports.assignDefaults = undefined; @@ -1673,7 +1673,7 @@ var require_defaults = __commonJS((exports) => { } }); -// node_modules/ajv/dist/vocabularies/code.js +// ../../../node_modules/ajv/dist/vocabularies/code.js var require_code2 = __commonJS((exports) => { Object.defineProperty(exports, "__esModule", { value: true }); exports.validateUnion = exports.validateArray = exports.usePattern = exports.callValidateCode = exports.schemaProperties = exports.allSchemaProperties = exports.noPropertyInData = exports.propertyInData = exports.isOwnProperty = exports.hasPropFunc = exports.reportMissingProp = exports.checkMissingProp = exports.checkReportMissingProp = undefined; @@ -1802,7 +1802,7 @@ var require_code2 = __commonJS((exports) => { exports.validateUnion = validateUnion; }); -// node_modules/ajv/dist/compile/validate/keyword.js +// ../../../node_modules/ajv/dist/compile/validate/keyword.js var require_keyword = __commonJS((exports) => { Object.defineProperty(exports, "__esModule", { value: true }); exports.validateKeywordUsage = exports.validSchemaType = exports.funcKeywordCode = exports.macroKeywordCode = undefined; @@ -1917,7 +1917,7 @@ var require_keyword = __commonJS((exports) => { exports.validateKeywordUsage = validateKeywordUsage; }); -// node_modules/ajv/dist/compile/validate/subschema.js +// ../../../node_modules/ajv/dist/compile/validate/subschema.js var require_subschema = __commonJS((exports) => { Object.defineProperty(exports, "__esModule", { value: true }); exports.extendSubschemaMode = exports.extendSubschemaData = exports.getSubschema = undefined; @@ -1997,7 +1997,7 @@ var require_subschema = __commonJS((exports) => { exports.extendSubschemaMode = extendSubschemaMode; }); -// node_modules/fast-deep-equal/index.js +// ../../../node_modules/fast-deep-equal/index.js var require_fast_deep_equal = __commonJS((exports, module) => { module.exports = function equal(a, b) { if (a === b) @@ -2039,7 +2039,7 @@ var require_fast_deep_equal = __commonJS((exports, module) => { }; }); -// node_modules/json-schema-traverse/index.js +// ../../../node_modules/json-schema-traverse/index.js var require_json_schema_traverse = __commonJS((exports, module) => { var traverse = module.exports = function(schema, opts, cb) { if (typeof opts == "function") { @@ -2122,7 +2122,7 @@ var require_json_schema_traverse = __commonJS((exports, module) => { } }); -// node_modules/ajv/dist/compile/resolve.js +// ../../../node_modules/ajv/dist/compile/resolve.js var require_resolve = __commonJS((exports) => { Object.defineProperty(exports, "__esModule", { value: true }); exports.getSchemaRefs = exports.resolveUrl = exports.normalizeId = exports._getFullPath = exports.getFullPath = exports.inlineRef = undefined; @@ -2275,7 +2275,7 @@ var require_resolve = __commonJS((exports) => { exports.getSchemaRefs = getSchemaRefs; }); -// node_modules/ajv/dist/compile/validate/index.js +// ../../../node_modules/ajv/dist/compile/validate/index.js var require_validate = __commonJS((exports) => { Object.defineProperty(exports, "__esModule", { value: true }); exports.getData = exports.KeywordCxt = exports.validateFunctionCode = undefined; @@ -2780,7 +2780,7 @@ var require_validate = __commonJS((exports) => { exports.getData = getData; }); -// node_modules/ajv/dist/runtime/validation_error.js +// ../../../node_modules/ajv/dist/runtime/validation_error.js var require_validation_error = __commonJS((exports) => { Object.defineProperty(exports, "__esModule", { value: true }); @@ -2794,7 +2794,7 @@ var require_validation_error = __commonJS((exports) => { exports.default = ValidationError; }); -// node_modules/ajv/dist/compile/ref_error.js +// ../../../node_modules/ajv/dist/compile/ref_error.js var require_ref_error = __commonJS((exports) => { Object.defineProperty(exports, "__esModule", { value: true }); var resolve_1 = require_resolve(); @@ -2809,7 +2809,7 @@ var require_ref_error = __commonJS((exports) => { exports.default = MissingRefError; }); -// node_modules/ajv/dist/compile/index.js +// ../../../node_modules/ajv/dist/compile/index.js var require_compile = __commonJS((exports) => { Object.defineProperty(exports, "__esModule", { value: true }); exports.resolveSchema = exports.getCompilingSchema = exports.resolveRef = exports.compileSchema = exports.SchemaEnv = undefined; @@ -3030,7 +3030,7 @@ var require_compile = __commonJS((exports) => { } }); -// node_modules/ajv/dist/refs/data.json +// ../../../node_modules/ajv/dist/refs/data.json var require_data = __commonJS((exports, module) => { module.exports = { $id: "https://raw.githubusercontent.com/ajv-validator/ajv/master/lib/refs/data.json#", @@ -3047,7 +3047,7 @@ var require_data = __commonJS((exports, module) => { }; }); -// node_modules/fast-uri/lib/utils.js +// ../../../node_modules/fast-uri/lib/utils.js var require_utils = __commonJS((exports, module) => { var isUUID = RegExp.prototype.test.bind(/^[\da-f]{8}-[\da-f]{4}-[\da-f]{4}-[\da-f]{4}-[\da-f]{12}$/iu); var isIPv4 = RegExp.prototype.test.bind(/^(?:(?:25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)$/u); @@ -3302,7 +3302,7 @@ var require_utils = __commonJS((exports, module) => { }; }); -// node_modules/fast-uri/lib/schemes.js +// ../../../node_modules/fast-uri/lib/schemes.js var require_schemes = __commonJS((exports, module) => { var { isUUID } = require_utils(); var URN_REG = /([\da-z][\d\-a-z]{0,31}):((?:[\w!$'()*+,\-.:;=@]|%[\da-f]{2})+)/iu; @@ -3476,7 +3476,7 @@ var require_schemes = __commonJS((exports, module) => { }; }); -// node_modules/fast-uri/index.js +// ../../../node_modules/fast-uri/index.js var require_fast_uri = __commonJS((exports, module) => { var { normalizeIPv6, removeDotSegments, recomposeAuthority, normalizeComponentEncoding, isIPv4, nonSimpleDomain } = require_utils(); var { SCHEMES, getSchemeHandler } = require_schemes(); @@ -3727,7 +3727,7 @@ var require_fast_uri = __commonJS((exports, module) => { module.exports.fastUri = fastUri; }); -// node_modules/ajv/dist/runtime/uri.js +// ../../../node_modules/ajv/dist/runtime/uri.js var require_uri = __commonJS((exports) => { Object.defineProperty(exports, "__esModule", { value: true }); var uri = require_fast_uri(); @@ -3735,7 +3735,7 @@ var require_uri = __commonJS((exports) => { exports.default = uri; }); -// node_modules/ajv/dist/core.js +// ../../../node_modules/ajv/dist/core.js var require_core = __commonJS((exports) => { Object.defineProperty(exports, "__esModule", { value: true }); exports.CodeGen = exports.Name = exports.nil = exports.stringify = exports.str = exports._ = exports.KeywordCxt = undefined; @@ -4328,7 +4328,7 @@ var require_core = __commonJS((exports) => { } }); -// node_modules/ajv/dist/vocabularies/core/id.js +// ../../../node_modules/ajv/dist/vocabularies/core/id.js var require_id = __commonJS((exports) => { Object.defineProperty(exports, "__esModule", { value: true }); var def = { @@ -4340,7 +4340,7 @@ var require_id = __commonJS((exports) => { exports.default = def; }); -// node_modules/ajv/dist/vocabularies/core/ref.js +// ../../../node_modules/ajv/dist/vocabularies/core/ref.js var require_ref = __commonJS((exports) => { Object.defineProperty(exports, "__esModule", { value: true }); exports.callRef = exports.getValidate = undefined; @@ -4459,7 +4459,7 @@ var require_ref = __commonJS((exports) => { exports.default = def; }); -// node_modules/ajv/dist/vocabularies/core/index.js +// ../../../node_modules/ajv/dist/vocabularies/core/index.js var require_core2 = __commonJS((exports) => { Object.defineProperty(exports, "__esModule", { value: true }); var id_1 = require_id(); @@ -4477,7 +4477,7 @@ var require_core2 = __commonJS((exports) => { exports.default = core2; }); -// node_modules/ajv/dist/vocabularies/validation/limitNumber.js +// ../../../node_modules/ajv/dist/vocabularies/validation/limitNumber.js var require_limitNumber = __commonJS((exports) => { Object.defineProperty(exports, "__esModule", { value: true }); var codegen_1 = require_codegen(); @@ -4506,7 +4506,7 @@ var require_limitNumber = __commonJS((exports) => { exports.default = def; }); -// node_modules/ajv/dist/vocabularies/validation/multipleOf.js +// ../../../node_modules/ajv/dist/vocabularies/validation/multipleOf.js var require_multipleOf = __commonJS((exports) => { Object.defineProperty(exports, "__esModule", { value: true }); var codegen_1 = require_codegen(); @@ -4531,7 +4531,7 @@ var require_multipleOf = __commonJS((exports) => { exports.default = def; }); -// node_modules/ajv/dist/runtime/ucs2length.js +// ../../../node_modules/ajv/dist/runtime/ucs2length.js var require_ucs2length = __commonJS((exports) => { Object.defineProperty(exports, "__esModule", { value: true }); function ucs2length(str) { @@ -4554,7 +4554,7 @@ var require_ucs2length = __commonJS((exports) => { ucs2length.code = 'require("ajv/dist/runtime/ucs2length").default'; }); -// node_modules/ajv/dist/vocabularies/validation/limitLength.js +// ../../../node_modules/ajv/dist/vocabularies/validation/limitLength.js var require_limitLength = __commonJS((exports) => { Object.defineProperty(exports, "__esModule", { value: true }); var codegen_1 = require_codegen(); @@ -4583,7 +4583,7 @@ var require_limitLength = __commonJS((exports) => { exports.default = def; }); -// node_modules/ajv/dist/vocabularies/validation/pattern.js +// ../../../node_modules/ajv/dist/vocabularies/validation/pattern.js var require_pattern = __commonJS((exports) => { Object.defineProperty(exports, "__esModule", { value: true }); var code_1 = require_code2(); @@ -4617,7 +4617,7 @@ var require_pattern = __commonJS((exports) => { exports.default = def; }); -// node_modules/ajv/dist/vocabularies/validation/limitProperties.js +// ../../../node_modules/ajv/dist/vocabularies/validation/limitProperties.js var require_limitProperties = __commonJS((exports) => { Object.defineProperty(exports, "__esModule", { value: true }); var codegen_1 = require_codegen(); @@ -4643,7 +4643,7 @@ var require_limitProperties = __commonJS((exports) => { exports.default = def; }); -// node_modules/ajv/dist/vocabularies/validation/required.js +// ../../../node_modules/ajv/dist/vocabularies/validation/required.js var require_required = __commonJS((exports) => { Object.defineProperty(exports, "__esModule", { value: true }); var code_1 = require_code2(); @@ -4722,7 +4722,7 @@ var require_required = __commonJS((exports) => { exports.default = def; }); -// node_modules/ajv/dist/vocabularies/validation/limitItems.js +// ../../../node_modules/ajv/dist/vocabularies/validation/limitItems.js var require_limitItems = __commonJS((exports) => { Object.defineProperty(exports, "__esModule", { value: true }); var codegen_1 = require_codegen(); @@ -4748,7 +4748,7 @@ var require_limitItems = __commonJS((exports) => { exports.default = def; }); -// node_modules/ajv/dist/runtime/equal.js +// ../../../node_modules/ajv/dist/runtime/equal.js var require_equal = __commonJS((exports) => { Object.defineProperty(exports, "__esModule", { value: true }); var equal = require_fast_deep_equal(); @@ -4756,7 +4756,7 @@ var require_equal = __commonJS((exports) => { exports.default = equal; }); -// node_modules/ajv/dist/vocabularies/validation/uniqueItems.js +// ../../../node_modules/ajv/dist/vocabularies/validation/uniqueItems.js var require_uniqueItems = __commonJS((exports) => { Object.defineProperty(exports, "__esModule", { value: true }); var dataType_1 = require_dataType(); @@ -4820,7 +4820,7 @@ var require_uniqueItems = __commonJS((exports) => { exports.default = def; }); -// node_modules/ajv/dist/vocabularies/validation/const.js +// ../../../node_modules/ajv/dist/vocabularies/validation/const.js var require_const = __commonJS((exports) => { Object.defineProperty(exports, "__esModule", { value: true }); var codegen_1 = require_codegen(); @@ -4846,7 +4846,7 @@ var require_const = __commonJS((exports) => { exports.default = def; }); -// node_modules/ajv/dist/vocabularies/validation/enum.js +// ../../../node_modules/ajv/dist/vocabularies/validation/enum.js var require_enum = __commonJS((exports) => { Object.defineProperty(exports, "__esModule", { value: true }); var codegen_1 = require_codegen(); @@ -4892,7 +4892,7 @@ var require_enum = __commonJS((exports) => { exports.default = def; }); -// node_modules/ajv/dist/vocabularies/validation/index.js +// ../../../node_modules/ajv/dist/vocabularies/validation/index.js var require_validation = __commonJS((exports) => { Object.defineProperty(exports, "__esModule", { value: true }); var limitNumber_1 = require_limitNumber(); @@ -4922,7 +4922,7 @@ var require_validation = __commonJS((exports) => { exports.default = validation; }); -// node_modules/ajv/dist/vocabularies/applicator/additionalItems.js +// ../../../node_modules/ajv/dist/vocabularies/applicator/additionalItems.js var require_additionalItems = __commonJS((exports) => { Object.defineProperty(exports, "__esModule", { value: true }); exports.validateAdditionalItems = undefined; @@ -4972,7 +4972,7 @@ var require_additionalItems = __commonJS((exports) => { exports.default = def; }); -// node_modules/ajv/dist/vocabularies/applicator/items.js +// ../../../node_modules/ajv/dist/vocabularies/applicator/items.js var require_items = __commonJS((exports) => { Object.defineProperty(exports, "__esModule", { value: true }); exports.validateTuple = undefined; @@ -5026,7 +5026,7 @@ var require_items = __commonJS((exports) => { exports.default = def; }); -// node_modules/ajv/dist/vocabularies/applicator/prefixItems.js +// ../../../node_modules/ajv/dist/vocabularies/applicator/prefixItems.js var require_prefixItems = __commonJS((exports) => { Object.defineProperty(exports, "__esModule", { value: true }); var items_1 = require_items(); @@ -5040,7 +5040,7 @@ var require_prefixItems = __commonJS((exports) => { exports.default = def; }); -// node_modules/ajv/dist/vocabularies/applicator/items2020.js +// ../../../node_modules/ajv/dist/vocabularies/applicator/items2020.js var require_items2020 = __commonJS((exports) => { Object.defineProperty(exports, "__esModule", { value: true }); var codegen_1 = require_codegen(); @@ -5072,7 +5072,7 @@ var require_items2020 = __commonJS((exports) => { exports.default = def; }); -// node_modules/ajv/dist/vocabularies/applicator/contains.js +// ../../../node_modules/ajv/dist/vocabularies/applicator/contains.js var require_contains = __commonJS((exports) => { Object.defineProperty(exports, "__esModule", { value: true }); var codegen_1 = require_codegen(); @@ -5163,7 +5163,7 @@ var require_contains = __commonJS((exports) => { exports.default = def; }); -// node_modules/ajv/dist/vocabularies/applicator/dependencies.js +// ../../../node_modules/ajv/dist/vocabularies/applicator/dependencies.js var require_dependencies = __commonJS((exports) => { Object.defineProperty(exports, "__esModule", { value: true }); exports.validateSchemaDeps = exports.validatePropertyDeps = exports.error = undefined; @@ -5248,7 +5248,7 @@ var require_dependencies = __commonJS((exports) => { exports.default = def; }); -// node_modules/ajv/dist/vocabularies/applicator/propertyNames.js +// ../../../node_modules/ajv/dist/vocabularies/applicator/propertyNames.js var require_propertyNames = __commonJS((exports) => { Object.defineProperty(exports, "__esModule", { value: true }); var codegen_1 = require_codegen(); @@ -5288,7 +5288,7 @@ var require_propertyNames = __commonJS((exports) => { exports.default = def; }); -// node_modules/ajv/dist/vocabularies/applicator/additionalProperties.js +// ../../../node_modules/ajv/dist/vocabularies/applicator/additionalProperties.js var require_additionalProperties = __commonJS((exports) => { Object.defineProperty(exports, "__esModule", { value: true }); var code_1 = require_code2(); @@ -5391,7 +5391,7 @@ var require_additionalProperties = __commonJS((exports) => { exports.default = def; }); -// node_modules/ajv/dist/vocabularies/applicator/properties.js +// ../../../node_modules/ajv/dist/vocabularies/applicator/properties.js var require_properties = __commonJS((exports) => { Object.defineProperty(exports, "__esModule", { value: true }); var validate_1 = require_validate(); @@ -5446,7 +5446,7 @@ var require_properties = __commonJS((exports) => { exports.default = def; }); -// node_modules/ajv/dist/vocabularies/applicator/patternProperties.js +// ../../../node_modules/ajv/dist/vocabularies/applicator/patternProperties.js var require_patternProperties = __commonJS((exports) => { Object.defineProperty(exports, "__esModule", { value: true }); var code_1 = require_code2(); @@ -5517,7 +5517,7 @@ var require_patternProperties = __commonJS((exports) => { exports.default = def; }); -// node_modules/ajv/dist/vocabularies/applicator/not.js +// ../../../node_modules/ajv/dist/vocabularies/applicator/not.js var require_not = __commonJS((exports) => { Object.defineProperty(exports, "__esModule", { value: true }); var util_1 = require_util(); @@ -5545,7 +5545,7 @@ var require_not = __commonJS((exports) => { exports.default = def; }); -// node_modules/ajv/dist/vocabularies/applicator/anyOf.js +// ../../../node_modules/ajv/dist/vocabularies/applicator/anyOf.js var require_anyOf = __commonJS((exports) => { Object.defineProperty(exports, "__esModule", { value: true }); var code_1 = require_code2(); @@ -5559,7 +5559,7 @@ var require_anyOf = __commonJS((exports) => { exports.default = def; }); -// node_modules/ajv/dist/vocabularies/applicator/oneOf.js +// ../../../node_modules/ajv/dist/vocabularies/applicator/oneOf.js var require_oneOf = __commonJS((exports) => { Object.defineProperty(exports, "__esModule", { value: true }); var codegen_1 = require_codegen(); @@ -5614,7 +5614,7 @@ var require_oneOf = __commonJS((exports) => { exports.default = def; }); -// node_modules/ajv/dist/vocabularies/applicator/allOf.js +// ../../../node_modules/ajv/dist/vocabularies/applicator/allOf.js var require_allOf = __commonJS((exports) => { Object.defineProperty(exports, "__esModule", { value: true }); var util_1 = require_util(); @@ -5638,7 +5638,7 @@ var require_allOf = __commonJS((exports) => { exports.default = def; }); -// node_modules/ajv/dist/vocabularies/applicator/if.js +// ../../../node_modules/ajv/dist/vocabularies/applicator/if.js var require_if = __commonJS((exports) => { Object.defineProperty(exports, "__esModule", { value: true }); var codegen_1 = require_codegen(); @@ -5704,7 +5704,7 @@ var require_if = __commonJS((exports) => { exports.default = def; }); -// node_modules/ajv/dist/vocabularies/applicator/thenElse.js +// ../../../node_modules/ajv/dist/vocabularies/applicator/thenElse.js var require_thenElse = __commonJS((exports) => { Object.defineProperty(exports, "__esModule", { value: true }); var util_1 = require_util(); @@ -5719,7 +5719,7 @@ var require_thenElse = __commonJS((exports) => { exports.default = def; }); -// node_modules/ajv/dist/vocabularies/applicator/index.js +// ../../../node_modules/ajv/dist/vocabularies/applicator/index.js var require_applicator = __commonJS((exports) => { Object.defineProperty(exports, "__esModule", { value: true }); var additionalItems_1 = require_additionalItems(); @@ -5762,7 +5762,7 @@ var require_applicator = __commonJS((exports) => { exports.default = getApplicator; }); -// node_modules/ajv/dist/vocabularies/format/format.js +// ../../../node_modules/ajv/dist/vocabularies/format/format.js var require_format = __commonJS((exports) => { Object.defineProperty(exports, "__esModule", { value: true }); var codegen_1 = require_codegen(); @@ -5849,7 +5849,7 @@ var require_format = __commonJS((exports) => { exports.default = def; }); -// node_modules/ajv/dist/vocabularies/format/index.js +// ../../../node_modules/ajv/dist/vocabularies/format/index.js var require_format2 = __commonJS((exports) => { Object.defineProperty(exports, "__esModule", { value: true }); var format_1 = require_format(); @@ -5857,7 +5857,7 @@ var require_format2 = __commonJS((exports) => { exports.default = format; }); -// node_modules/ajv/dist/vocabularies/metadata.js +// ../../../node_modules/ajv/dist/vocabularies/metadata.js var require_metadata = __commonJS((exports) => { Object.defineProperty(exports, "__esModule", { value: true }); exports.contentVocabulary = exports.metadataVocabulary = undefined; @@ -5877,7 +5877,7 @@ var require_metadata = __commonJS((exports) => { ]; }); -// node_modules/ajv/dist/vocabularies/draft7.js +// ../../../node_modules/ajv/dist/vocabularies/draft7.js var require_draft7 = __commonJS((exports) => { Object.defineProperty(exports, "__esModule", { value: true }); var core_1 = require_core2(); @@ -5896,7 +5896,7 @@ var require_draft7 = __commonJS((exports) => { exports.default = draft7Vocabularies; }); -// node_modules/ajv/dist/vocabularies/discriminator/types.js +// ../../../node_modules/ajv/dist/vocabularies/discriminator/types.js var require_types = __commonJS((exports) => { Object.defineProperty(exports, "__esModule", { value: true }); exports.DiscrError = undefined; @@ -5907,7 +5907,7 @@ var require_types = __commonJS((exports) => { })(DiscrError || (exports.DiscrError = DiscrError = {})); }); -// node_modules/ajv/dist/vocabularies/discriminator/index.js +// ../../../node_modules/ajv/dist/vocabularies/discriminator/index.js var require_discriminator = __commonJS((exports) => { Object.defineProperty(exports, "__esModule", { value: true }); var codegen_1 = require_codegen(); @@ -6009,7 +6009,7 @@ var require_discriminator = __commonJS((exports) => { exports.default = def; }); -// node_modules/ajv/dist/refs/json-schema-draft-07.json +// ../../../node_modules/ajv/dist/refs/json-schema-draft-07.json var require_json_schema_draft_07 = __commonJS((exports, module) => { module.exports = { $schema: "http://json-schema.org/draft-07/schema#", @@ -6164,7 +6164,7 @@ var require_json_schema_draft_07 = __commonJS((exports, module) => { }; }); -// node_modules/ajv/dist/ajv.js +// ../../../node_modules/ajv/dist/ajv.js var require_ajv = __commonJS((exports, module) => { Object.defineProperty(exports, "__esModule", { value: true }); exports.MissingRefError = exports.ValidationError = exports.CodeGen = exports.Name = exports.nil = exports.stringify = exports.str = exports._ = exports.KeywordCxt = exports.Ajv = undefined; @@ -6232,7 +6232,7 @@ var require_ajv = __commonJS((exports, module) => { } }); }); -// node_modules/ajv-formats/dist/formats.js +// ../../../node_modules/ajv-formats/dist/formats.js var require_formats = __commonJS((exports) => { Object.defineProperty(exports, "__esModule", { value: true }); exports.formatNames = exports.fastFormats = exports.fullFormats = undefined; @@ -6409,7 +6409,7 @@ var require_formats = __commonJS((exports) => { } }); -// node_modules/ajv-formats/dist/limit.js +// ../../../node_modules/ajv-formats/dist/limit.js var require_limit = __commonJS((exports) => { Object.defineProperty(exports, "__esModule", { value: true }); exports.formatLimitDefinition = undefined; @@ -6478,7 +6478,7 @@ var require_limit = __commonJS((exports) => { exports.default = formatLimitPlugin; }); -// node_modules/ajv-formats/dist/index.js +// ../../../node_modules/ajv-formats/dist/index.js var require_dist = __commonJS((exports, module) => { Object.defineProperty(exports, "__esModule", { value: true }); var formats_1 = require_formats(); @@ -6520,7 +6520,7 @@ var require_dist = __commonJS((exports, module) => { // src/bridge.ts import { existsSync as existsSync7 } from "fs"; -// node_modules/zod/v4/core/core.js +// ../../../node_modules/zod/v4/core/core.js var NEVER = Object.freeze({ status: "aborted" }); @@ -6596,7 +6596,7 @@ function config(newConfig) { Object.assign(globalConfig, newConfig); return globalConfig; } -// node_modules/zod/v4/core/util.js +// ../../../node_modules/zod/v4/core/util.js var exports_util = {}; __export(exports_util, { unwrapMessage: () => unwrapMessage, @@ -7270,7 +7270,7 @@ class Class { constructor(..._args) {} } -// node_modules/zod/v4/core/errors.js +// ../../../node_modules/zod/v4/core/errors.js var initializer = (inst, def) => { inst.name = "$ZodError"; Object.defineProperty(inst, "_zod", { @@ -7336,7 +7336,7 @@ function formatError(error, mapper = (issue2) => issue2.message) { return fieldErrors; } -// node_modules/zod/v4/core/parse.js +// ../../../node_modules/zod/v4/core/parse.js var _parse = (_Err) => (schema, value, _ctx, _params) => { const ctx = _ctx ? Object.assign(_ctx, { async: false }) : { async: false }; const result = schema._zod.run({ value, issues: [] }, ctx); @@ -7413,7 +7413,7 @@ var _safeEncodeAsync = (_Err) => async (schema, value, _ctx) => { var _safeDecodeAsync = (_Err) => async (schema, value, _ctx) => { return _safeParseAsync(_Err)(schema, value, _ctx); }; -// node_modules/zod/v4/core/regexes.js +// ../../../node_modules/zod/v4/core/regexes.js var cuid = /^[cC][^\s-]{8,}$/; var cuid2 = /^[0-9a-z]+$/; var ulid = /^[0-9A-HJKMNP-TV-Za-hjkmnp-tv-z]{26}$/; @@ -7470,7 +7470,7 @@ var _null = /^null$/i; var lowercase = /^[^A-Z]*$/; var uppercase = /^[^a-z]*$/; -// node_modules/zod/v4/core/checks.js +// ../../../node_modules/zod/v4/core/checks.js var $ZodCheck = /* @__PURE__ */ $constructor("$ZodCheck", (inst, def) => { var _a; inst._zod ?? (inst._zod = {}); @@ -7859,7 +7859,7 @@ var $ZodCheckOverwrite = /* @__PURE__ */ $constructor("$ZodCheckOverwrite", (ins }; }); -// node_modules/zod/v4/core/doc.js +// ../../../node_modules/zod/v4/core/doc.js class Doc { constructor(args = []) { this.content = []; @@ -7897,14 +7897,14 @@ class Doc { } } -// node_modules/zod/v4/core/versions.js +// ../../../node_modules/zod/v4/core/versions.js var version = { major: 4, minor: 3, patch: 6 }; -// node_modules/zod/v4/core/schemas.js +// ../../../node_modules/zod/v4/core/schemas.js var $ZodType = /* @__PURE__ */ $constructor("$ZodType", (inst, def) => { var _a; inst ?? (inst = {}); @@ -9289,7 +9289,7 @@ function handleRefineResult(result, payload, input, inst) { payload.issues.push(issue(_iss)); } } -// node_modules/zod/v4/locales/en.js +// ../../../node_modules/zod/v4/locales/en.js var error = () => { const Sizable = { string: { unit: "characters", verb: "to have" }, @@ -9395,7 +9395,7 @@ function en_default() { localeError: error() }; } -// node_modules/zod/v4/core/registries.js +// ../../../node_modules/zod/v4/core/registries.js var _a; var $output = Symbol("ZodOutput"); var $input = Symbol("ZodInput"); @@ -9445,7 +9445,7 @@ function registry() { } (_a = globalThis).__zod_globalRegistry ?? (_a.__zod_globalRegistry = registry()); var globalRegistry = globalThis.__zod_globalRegistry; -// node_modules/zod/v4/core/api.js +// ../../../node_modules/zod/v4/core/api.js function _string(Class2, params) { return new Class2({ type: "string", @@ -9911,7 +9911,7 @@ function _check(fn, params) { ch._zod.check = fn; return ch; } -// node_modules/zod/v4/core/to-json-schema.js +// ../../../node_modules/zod/v4/core/to-json-schema.js function initializeContext(params) { let target = params?.target ?? "draft-2020-12"; if (target === "draft-4") @@ -10256,7 +10256,7 @@ var createStandardJSONSchemaMethod = (schema, io, processors = {}) => (params) = extractDefs(ctx, schema); return finalize(ctx, schema); }; -// node_modules/zod/v4/core/json-schema-processors.js +// ../../../node_modules/zod/v4/core/json-schema-processors.js var formatMap = { guid: "uuid", url: "uri", @@ -10591,7 +10591,7 @@ var optionalProcessor = (schema, ctx, _json, params) => { const seen = ctx.seen.get(schema); seen.ref = def.innerType; }; -// node_modules/@modelcontextprotocol/sdk/dist/esm/server/zod-compat.js +// ../../../node_modules/@modelcontextprotocol/sdk/dist/esm/server/zod-compat.js function isZ4Schema(s) { const schema = s; return !!schema._zod; @@ -10653,7 +10653,7 @@ function getLiteralValue(schema) { return directValue; return; } -// node_modules/zod/v4/classic/iso.js +// ../../../node_modules/zod/v4/classic/iso.js var exports_iso = {}; __export(exports_iso, { time: () => time2, @@ -10694,7 +10694,7 @@ function duration2(params) { return _isoDuration(ZodISODuration, params); } -// node_modules/zod/v4/classic/errors.js +// ../../../node_modules/zod/v4/classic/errors.js var initializer2 = (inst, issues) => { $ZodError.init(inst, issues); inst.name = "ZodError"; @@ -10729,7 +10729,7 @@ var ZodRealError = $constructor("ZodError", initializer2, { Parent: Error }); -// node_modules/zod/v4/classic/parse.js +// ../../../node_modules/zod/v4/classic/parse.js var parse3 = /* @__PURE__ */ _parse(ZodRealError); var parseAsync2 = /* @__PURE__ */ _parseAsync(ZodRealError); var safeParse3 = /* @__PURE__ */ _safeParse(ZodRealError); @@ -10743,7 +10743,7 @@ var safeDecode2 = /* @__PURE__ */ _safeDecode(ZodRealError); var safeEncodeAsync2 = /* @__PURE__ */ _safeEncodeAsync(ZodRealError); var safeDecodeAsync2 = /* @__PURE__ */ _safeDecodeAsync(ZodRealError); -// node_modules/zod/v4/classic/schemas.js +// ../../../node_modules/zod/v4/classic/schemas.js var ZodType = /* @__PURE__ */ $constructor("ZodType", (inst, def) => { $ZodType.init(inst, def); Object.assign(inst["~standard"], { @@ -11382,10 +11382,10 @@ function superRefine(fn) { function preprocess(fn, schema) { return pipe(transform(fn), schema); } -// node_modules/zod/v4/classic/external.js +// ../../../node_modules/zod/v4/classic/external.js config(en_default()); -// node_modules/@modelcontextprotocol/sdk/dist/esm/types.js +// ../../../node_modules/@modelcontextprotocol/sdk/dist/esm/types.js var LATEST_PROTOCOL_VERSION = "2025-11-25"; var SUPPORTED_PROTOCOL_VERSIONS = [LATEST_PROTOCOL_VERSION, "2025-06-18", "2025-03-26", "2024-11-05", "2024-10-07"]; var RELATED_TASK_META_KEY = "io.modelcontextprotocol/related-task"; @@ -12217,16 +12217,16 @@ class UrlElicitationRequiredError extends McpError { } } -// node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/interfaces.js +// ../../../node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/interfaces.js function isTerminal(status) { return status === "completed" || status === "failed" || status === "cancelled"; } -// node_modules/zod-to-json-schema/dist/esm/Options.js +// ../../../node_modules/zod-to-json-schema/dist/esm/Options.js var ignoreOverride = Symbol("Let zodToJsonSchema decide on which parser to use"); -// node_modules/zod-to-json-schema/dist/esm/parsers/string.js +// ../../../node_modules/zod-to-json-schema/dist/esm/parsers/string.js var ALPHA_NUMERIC = new Set("ABCDEFGHIJKLMNOPQRSTUVXYZabcdefghijklmnopqrstuvxyz0123456789"); -// node_modules/@modelcontextprotocol/sdk/dist/esm/server/zod-json-schema-compat.js +// ../../../node_modules/@modelcontextprotocol/sdk/dist/esm/server/zod-json-schema-compat.js function getMethodLiteral(schema) { const shape = getObjectShape(schema); const methodSchema = shape?.method; @@ -12247,7 +12247,7 @@ function parseWithCompat(schema, data) { return result.data; } -// node_modules/@modelcontextprotocol/sdk/dist/esm/shared/protocol.js +// ../../../node_modules/@modelcontextprotocol/sdk/dist/esm/shared/protocol.js var DEFAULT_REQUEST_TIMEOUT_MSEC = 60000; class Protocol { @@ -13082,7 +13082,7 @@ function mergeCapabilities(base, additional) { return result; } -// node_modules/@modelcontextprotocol/sdk/dist/esm/validation/ajv-provider.js +// ../../../node_modules/@modelcontextprotocol/sdk/dist/esm/validation/ajv-provider.js var import_ajv = __toESM(require_ajv(), 1); var import_ajv_formats = __toESM(require_dist(), 1); function createDefaultAjvInstance() { @@ -13122,7 +13122,7 @@ class AjvJsonSchemaValidator { } } -// node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/server.js +// ../../../node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/server.js class ExperimentalServerTasks { constructor(_server) { this._server = _server; @@ -13200,7 +13200,7 @@ class ExperimentalServerTasks { } } -// node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/helpers.js +// ../../../node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/helpers.js function assertToolsCallTaskCapability(requests, method, entityName) { if (!requests) { throw new Error(`${entityName} does not support task creation (required for ${method})`); @@ -13235,7 +13235,7 @@ function assertClientRequestTaskCapability(requests, method, entityName) { } } -// node_modules/@modelcontextprotocol/sdk/dist/esm/server/index.js +// ../../../node_modules/@modelcontextprotocol/sdk/dist/esm/server/index.js class Server extends Protocol { constructor(_serverInfo, options) { super(options); @@ -13568,10 +13568,10 @@ class Server extends Protocol { } } -// node_modules/@modelcontextprotocol/sdk/dist/esm/server/stdio.js +// ../../../node_modules/@modelcontextprotocol/sdk/dist/esm/server/stdio.js import process3 from "process"; -// node_modules/@modelcontextprotocol/sdk/dist/esm/shared/stdio.js +// ../../../node_modules/@modelcontextprotocol/sdk/dist/esm/shared/stdio.js class ReadBuffer { append(chunk) { this._buffer = this._buffer ? Buffer.concat([this._buffer, chunk]) : chunk; @@ -13601,7 +13601,7 @@ function serializeMessage(message) { `; } -// node_modules/@modelcontextprotocol/sdk/dist/esm/server/stdio.js +// ../../../node_modules/@modelcontextprotocol/sdk/dist/esm/server/stdio.js class StdioServerTransport { constructor(_stdin = process3.stdin, _stdout = process3.stdout) { this._stdin = _stdin; @@ -14707,10 +14707,10 @@ function defineNumber(value, fallback) { } var BUILD_INFO = Object.freeze({ version: defineString("0.1.24", "0.0.0-source"), - commit: defineString("fdc78c6", "source"), + commit: defineString("544002e", "source"), bundle: defineBundle("plugin"), contractVersion: defineNumber(1, CONTRACT_VERSION), - codeHash: defineString("ace71209327c", "source") + codeHash: defineString("da0d48f98844", "source") }); function sameRuntimeContract(a, b) { if (!a || !b) diff --git a/plugins/agentbridge/server/daemon.js b/plugins/agentbridge/server/daemon.js index 59a3354..475498b 100755 --- a/plugins/agentbridge/server/daemon.js +++ b/plugins/agentbridge/server/daemon.js @@ -30,10 +30,10 @@ function defineNumber(value, fallback) { } var BUILD_INFO = Object.freeze({ version: defineString("0.1.24", "0.0.0-source"), - commit: defineString("fdc78c6", "source"), + commit: defineString("544002e", "source"), bundle: defineBundle("plugin"), contractVersion: defineNumber(1, CONTRACT_VERSION), - codeHash: defineString("ace71209327c", "source") + codeHash: defineString("da0d48f98844", "source") }); function daemonStatusBuildInfo() { return { ...BUILD_INFO }; diff --git a/src/broker.ts b/src/broker.ts index ad35a42..63a04df 100644 --- a/src/broker.ts +++ b/src/broker.ts @@ -13,6 +13,11 @@ const CLOSE_AUTH_FAILED = 4401; // distinct topic/identity pairs over a long-lived process. FIFO-ish eviction of // the oldest key once full (same pattern as the room-bridge SEEN_CAP). const MEMBER_CACHE_CAP = 2000; +// Per-connection bounded outbox for backpressure: when Bun's ws.send() drops a frame +// (returns 0, over the backpressure limit) we re-queue it and resend on the drain event +// instead of losing it silently. Drop oldest when full — bounded loss beats unbounded growth. +// ponytail: 256 frames covers any realistic burst; upgrade to per-type priority if needed. +const OUTBOX_CAP = 256; // Bound attacker-controlled presence fields at the SOURCE: a member's hello blob is // broadcast to the whole room in member_joined, so cap each string field's length and // the capabilities count — one member must not be able to fan out a multi-MB field or @@ -55,6 +60,12 @@ interface BrokerSocketData { presence?: PresenceMeta; /** topic → unsubscribe handle for this connection's subscriptions. */ subs: Map void>; + /** + * Bounded outbox for backpressure: a frame is RETAINED for retry when ws.send() + * returns 0 (DROPPED), and flushed on drain(ws). (r===-1 means Bun buffered the + * frame itself and will deliver it, so it is removed — see flushOutbox.) + */ + outbox: string[]; } type ClientMessage = @@ -137,7 +148,7 @@ export class Broker { return Response.json(self.healthBody()); } if (pathname === "/ws") { - if (server.upgrade(req, { data: { connId: ++self.nextConnId, subs: new Map() } })) { + if (server.upgrade(req, { data: { connId: ++self.nextConnId, subs: new Map(), outbox: [] } })) { self.liveConnections++; return undefined; } @@ -168,6 +179,9 @@ export class Broker { if (self.liveConnections > 0) self.liveConnections--; self.log(`conn #${ws.data.connId} closed`); }, + drain(ws) { + self.flushOutbox(ws); + }, }, }); this.server = server; @@ -185,16 +199,61 @@ export class Broker { }; } - stop(): void { - this.server?.stop(true); + // Best-effort + never-reject: callers (cli/broker.ts shutdown, test cleanup) may not + // await the returned promise, so a rejecting server.stop() must NOT become an unhandled + // rejection. Swallow + log; shutdown proceeds to store.close() regardless, and the cli + // forceExit fuse covers a hung stop. + async stop(): Promise { + try { + await this.server?.stop(true); + } catch (e) { + this.log(`server stop failed: ${String(e)}`); + } this.server = null; } + // Bun ServerWebSocket.send() return contract (empirically verified, Bun 1.3.11): + // r > 0 → sent (bytes written) + // r === -1 → backpressured, but Bun BUFFERED the frame and WILL deliver it on drain + // r === 0 → DROPPED (over backpressureLimit / socket not open) — frame NOT delivered + // So the ONLY frame that needs our own retry is the r===0 drop; a -1 frame is already + // owned by Bun and re-sending it would DOUBLE-deliver. The outbox is the FIFO source of + // truth: enqueue then flush, so ordering holds even when a send is queued behind a drop. private send(ws: ServerWebSocket, msg: unknown): void { - try { - ws.send(JSON.stringify(msg)); - } catch (e) { - this.log(`send failed (#${ws.data.connId}): ${String(e)}`); + this.enqueue(ws, JSON.stringify(msg)); + this.flushOutbox(ws); + } + + /** Enqueue a serialised frame; drop oldest when outbox is full (bounded loss > unbounded growth). */ + private enqueue(ws: ServerWebSocket, frame: string): void { + if (ws.data.outbox.length >= OUTBOX_CAP) { + ws.data.outbox.shift(); // drop oldest + this.log(`outbox full (#${ws.data.connId}): dropped oldest frame (cap=${OUTBOX_CAP})`); + } + ws.data.outbox.push(frame); + } + + /** + * Drain the FIFO outbox into the socket. Called on every send and on Bun's drain + * event. Per the send() return contract above: + * r === 0 → DROPPED: keep the frame at the head and stop; retry on the next drain. + * r < 0 → buffered by Bun (delivered): remove the frame, then stop (we're backpressured). + * r > 0 → sent: remove the frame and keep flushing. + */ + private flushOutbox(ws: ServerWebSocket): void { + while (ws.data.outbox.length > 0) { + const frame = ws.data.outbox[0]!; + let r: number; + try { + r = ws.send(frame); + } catch (e) { + ws.data.outbox.shift(); // socket closed/errored — can't deliver; discard head and stop + this.log(`flush send failed (#${ws.data.connId}): ${String(e)}`); + return; + } + if (r === 0) return; // dropped (over backpressure limit) — keep frame, wait for drain + ws.data.outbox.shift(); // r>0 sent, or r<0 buffered-by-Bun (will deliver) — remove either way + if (r < 0) return; // backpressured: Bun took this frame, stop sending more until drain } } diff --git a/src/cli/broker.ts b/src/cli/broker.ts index 632b61d..9cf4eed 100644 --- a/src/cli/broker.ts +++ b/src/cli/broker.ts @@ -126,9 +126,26 @@ export async function runBrokerStart(argv: string[]): Promise { if (stopping) return; stopping = true; console.error(`[broker] ${sig} 收到,正在优雅关闭…`); - webHandle?.stop(); - broker.stop(); - store.close().finally(() => process.exit(0)); + // Ordered teardown: web first (stop accepting requests), then broker (drain in-flight WS), + // then store (WAL checkpoint). Failures must not prevent process.exit(0). + // ponytail: 10s force-exit is a safety valve against Bun.serve.stop() hanging; + // upgrade to configurable --shutdown-timeout if operational needs arise. + void (async () => { + const forceExit = setTimeout(() => { + console.error("[broker] 关闭超时(10s),强制退出"); + process.exit(1); + }, 10_000); + try { + webHandle?.stop(); + await broker.stop(); + await store.close(); + } catch (e) { + console.error(`[broker] 关闭出错:${e instanceof Error ? e.message : String(e)}`); + } finally { + clearTimeout(forceExit); + process.exit(0); + } + })(); }; process.once("SIGTERM", () => shutdown("SIGTERM")); process.once("SIGINT", () => shutdown("SIGINT")); diff --git a/src/unit-test/broker-backpressure.test.ts b/src/unit-test/broker-backpressure.test.ts new file mode 100644 index 0000000..18e3991 --- /dev/null +++ b/src/unit-test/broker-backpressure.test.ts @@ -0,0 +1,162 @@ +/** + * Unit tests for Broker outbox backpressure logic (Fix R3 LOW-3). + * + * Exercises the private enqueue / flushOutbox / send via `(broker as any)` — acceptable + * in unit tests where we own both sides. Real Bun backpressure is hard to trigger + * deterministically in CI; these cover the pure outbox state machine against the + * empirically-verified Bun ws.send() contract: + * r > 0 → sent + * r === -1 → buffered by Bun (will deliver) — frame consumed, stop + * r === 0 → dropped (over backpressure limit) — frame kept, retry on drain + */ +import { describe, test, expect } from "bun:test"; +import { Broker } from "../broker"; +import { InMemoryStore } from "../backbone/store/memory-store"; +import { StorePskIdentityProvider } from "../backbone/identity/store-psk-identity-provider"; + +// Must stay in sync with the constant in broker.ts +const OUTBOX_CAP = 256; + +function makeBroker() { + const store = new InMemoryStore(); + return new Broker({ + store, + identityProvider: new StorePskIdentityProvider(store), + host: "127.0.0.1", + port: 0, + log: () => {}, + }); +} + +/** + * Minimal fake ServerWebSocket whose send() returns a caller-scripted result, recording + * every frame it was asked to send (an "attempt") regardless of the result. + */ +function makeFakeWs(sendResult: (frame: string) => number) { + const data = { + connId: 1, + outbox: [] as string[], + subs: new Map void>(), + identity: undefined, + presence: undefined, + }; + const attempts: string[] = []; + return { + data, + attempts, + send(frame: string): number { + attempts.push(frame); + return sendResult(frame); + }, + }; +} + +describe("Broker outbox — backpressure buffering", () => { + test("enqueue stores frames up to OUTBOX_CAP and drops oldest on overflow", () => { + const broker = makeBroker(); + const ws = makeFakeWs(() => 0); // result irrelevant: enqueue tested in isolation + + for (let i = 0; i < OUTBOX_CAP + 10; i++) { + (broker as any).enqueue(ws, `frame-${i}`); + } + + expect(ws.data.outbox.length).toBe(OUTBOX_CAP); + // First 10 frames (0–9) dropped; frame-10 is now the oldest + expect(ws.data.outbox[0]).toBe(`frame-10`); + expect(ws.data.outbox[ws.data.outbox.length - 1]).toBe(`frame-${OUTBOX_CAP + 9}`); + }); + + test("flushOutbox sends successful frames (r>0) in FIFO order and empties the outbox", () => { + const broker = makeBroker(); + const ws = makeFakeWs(() => 5); // all succeed + + ws.data.outbox = ["x", "y", "z"]; + (broker as any).flushOutbox(ws); + + expect(ws.attempts).toEqual(["x", "y", "z"]); + expect(ws.data.outbox).toHaveLength(0); + }); + + test("flushOutbox KEEPS a dropped frame (r===0) at the head and stops", () => { + const broker = makeBroker(); + let calls = 0; + // First frame sends, second is dropped (0), rest untried + const ws = makeFakeWs(() => (++calls === 1 ? 5 : 0)); + + ws.data.outbox = ["a", "b", "c", "d"]; + (broker as any).flushOutbox(ws); + + expect(ws.attempts).toEqual(["a", "b"]); // stopped at the dropped frame + expect(ws.data.outbox).toEqual(["b", "c", "d"]); // dropped frame "b" retained for retry + }); + + test("flushOutbox CONSUMES a buffered frame (r===-1) and stops (no double-send)", () => { + const broker = makeBroker(); + let calls = 0; + // First succeeds, second is buffered by Bun (-1) → consumed, then stop + const ws = makeFakeWs(() => (++calls === 1 ? 5 : -1)); + + ws.data.outbox = ["a", "b", "c"]; + (broker as any).flushOutbox(ws); + + expect(ws.attempts).toEqual(["a", "b"]); + // "b" was buffered by Bun (will deliver) → removed so the next drain won't re-send it + expect(ws.data.outbox).toEqual(["c"]); + }); + + test("flushOutbox discards head and stops when send() throws (closed socket)", () => { + const broker = makeBroker(); + const ws = makeFakeWs(() => { + throw new Error("closed"); + }); + ws.data.outbox = ["a", "b"]; + (broker as any).flushOutbox(ws); + + expect(ws.data.outbox).toEqual(["b"]); // head discarded, stopped + }); + + test("send() enqueues then flushes — a dropped frame stays queued for the next drain", () => { + const broker = makeBroker(); + const ws = makeFakeWs(() => 0); // every send dropped + + (broker as any).send(ws, { seq: 1 }); + (broker as any).send(ws, { seq: 2 }); + + // send#1: seq:1 dropped → stays at head. send#2: enqueues seq:2 BEHIND it, then + // re-flushes from the head → seq:1 is re-attempted (dropped again, still head); + // seq:2 is NEVER attempted (head-of-line blocked). So 2 attempts (both seq:1), + // outbox keeps [seq:1, seq:2] in FIFO order for the next drain. + expect(ws.attempts).toHaveLength(2); + expect(ws.attempts.map((a: string) => JSON.parse(a).seq)).toEqual([1, 1]); // both attempts were the head (seq:1), not seq:2 + expect(ws.data.outbox).toHaveLength(2); + expect(JSON.parse(ws.data.outbox[0]!)).toEqual({ seq: 1 }); + expect(JSON.parse(ws.data.outbox[1]!)).toEqual({ seq: 2 }); + }); + + test("send() delivers immediately when the socket is not backpressured", () => { + const broker = makeBroker(); + const ws = makeFakeWs(() => 5); // always sent + + (broker as any).send(ws, { type: "event" }); + + expect(ws.attempts).toHaveLength(1); + expect(ws.data.outbox).toHaveLength(0); // sent, nothing queued + }); + + test("drain-then-flush re-delivers a previously dropped frame", () => { + const broker = makeBroker(); + let dropping = true; + const ws = makeFakeWs(() => (dropping ? 0 : 5)); + + (broker as any).send(ws, { seq: 1 }); // dropped → queued + expect(ws.data.outbox).toHaveLength(1); + const queuedFrame = ws.data.outbox[0]; + + // backpressure relieved → drain calls flushOutbox + dropping = false; + (broker as any).flushOutbox(ws); + + expect(ws.data.outbox).toHaveLength(0); // re-delivered + expect(ws.attempts).toEqual([queuedFrame, queuedFrame]); // same frame attempted twice (drop, then resend) + }); +});