From d325837848626afa9384b7643ad9d669395fd4bc Mon Sep 17 00:00:00 2001 From: Rita Chen Date: Wed, 13 May 2026 14:56:05 -0400 Subject: [PATCH 1/7] MLE-28335 added fragment option in fromSearch - Add 'fragment' option support to fromSearch() for MLS 12.1+ - Valid values: 'document' (default), 'properties', 'locks', 'any' - Client-side validation in PlanSearchOption (plan-builder-base.js) - Updated JSDoc for fromSearch() in plan-builder-generated.js - Added xdmp-lock-acquire/release privileges to rest-evaluator role in both test-setup-users.js and rest-evaluator.json (Gradle config) - Added fragment option integration tests to test-basic/plan-search.js (TC0-TC5, gated on serverVersion >= 12.1) --- etc/test-setup-users.js | 10 + lib/plan-builder-base.js | 7 + lib/plan-builder-generated.js | 2 +- .../security/roles/rest-evaluator.json | 10 + test-basic/plan-search.js | 181 ++++++++++++++++++ 5 files changed, 209 insertions(+), 1 deletion(-) diff --git a/etc/test-setup-users.js b/etc/test-setup-users.js index 87d8c7fd..a5deaa31 100644 --- a/etc/test-setup-users.js +++ b/etc/test-setup-users.js @@ -68,6 +68,16 @@ function setupUsers(manager, done) { 'privilege-name': 'xdmp-get-session-field', action: 'http://marklogic.com/xdmp/privileges/xdmp-get-session-field', kind: 'execute' + }, + { + 'privilege-name': 'xdmp-lock-acquire', + action: 'http://marklogic.com/xdmp/privileges/xdmp-lock-acquire', + kind: 'execute' + }, + { + 'privilege-name': 'xdmp-lock-release', + action: 'http://marklogic.com/xdmp/privileges/xdmp-lock-release', + kind: 'execute' } ] } diff --git a/lib/plan-builder-base.js b/lib/plan-builder-base.js index 1e802640..c983e446 100644 --- a/lib/plan-builder-base.js +++ b/lib/plan-builder-base.js @@ -136,6 +136,13 @@ function castArg(arg, funcName, paramName, argPos, paramTypes) { throw new Error( 'bm25LengthWeight must be a number' ); + case 'fragment': + if (['document', 'properties', 'locks', 'any'].includes(value)) { + return true; + } + throw new Error( + `${argLabel(funcName, paramName, argPos)} fragment can only be 'document', 'properties', 'locks', or 'any'` + ); default: return false; }}); diff --git a/lib/plan-builder-generated.js b/lib/plan-builder-generated.js index e833e29e..6beac4f6 100755 --- a/lib/plan-builder-generated.js +++ b/lib/plan-builder-generated.js @@ -9012,7 +9012,7 @@ fromSearchDocs(...args) { * @param { PlanSearchQuery } [query] - Qualifies and establishes the scores for a set of documents. The query can be a cts:query or a string as a shortcut for a cts:word-query. The fragments are not filtered to ensure they match the query, but instead selected in the same manner as "unfiltered" cts:search operations. * @param { PlanExprColName } [columns] - Specifies which of the available columns to include in the rows. The available columns include the metrics for relevance ('confidence', 'fitness', 'quality', and 'score') and fragmentId for the document identifier. By default, the rows have the fragmentId and score columns. To rename a column, use op:as specifying the new name for an op:col with the old name. * @param { XsString } [qualifierName] - Specifies a name for qualifying the column names. - * @param { PlanSearchOption } [option] - Similar to the options of cts:search, supplies the 'scoreMethod' key with a value of 'logtfidf', 'logtf', 'simple', 'zero', 'random', or 'bm25' to specify the method for assigning a score to matched documents or supplies the 'qualityWeight' key with a numeric value to specify a multiplier for the quality contribution to the score. Specify a value between 0 (exclusive) and 1 (inclusive) for bm25LengthWeight if 'bm25' scoring method is used. + * @param { PlanSearchOption } [option] - Similar to the options of cts:search, supplies the 'scoreMethod' key with a value of 'logtfidf', 'logtf', 'simple', 'zero', 'random', or 'bm25' to specify the method for assigning a score to matched documents or supplies the 'qualityWeight' key with a numeric value to specify a multiplier for the quality contribution to the score. Specify a value between 0 (exclusive) and 1 (inclusive) for bm25LengthWeight if 'bm25' scoring method is used. As of MLS 12.1, supplies the 'fragment' key with a value of 'document' (default), 'properties', 'locks', or 'any' to specify which document fragment type to search. Note: on servers earlier than MLS 12.1, the 'fragment' option is silently ignored and all fragment types are searched. * @returns { planBuilder.AccessPlan } */ fromSearch(...args) { diff --git a/test-app/src/main/ml-config/security/roles/rest-evaluator.json b/test-app/src/main/ml-config/security/roles/rest-evaluator.json index 9d315236..3da382b0 100644 --- a/test-app/src/main/ml-config/security/roles/rest-evaluator.json +++ b/test-app/src/main/ml-config/security/roles/rest-evaluator.json @@ -65,6 +65,16 @@ "privilege-name": "xdmp-xslt-invoke", "action": "http://marklogic.com/xdmp/privileges/xslt-invoke", "kind": "execute" + }, + { + "privilege-name": "xdmp-lock-acquire", + "action": "http://marklogic.com/xdmp/privileges/xdmp-lock-acquire", + "kind": "execute" + }, + { + "privilege-name": "xdmp-lock-release", + "action": "http://marklogic.com/xdmp/privileges/xdmp-lock-release", + "kind": "execute" } ] } diff --git a/test-basic/plan-search.js b/test-basic/plan-search.js index 20fc3022..697f3760 100644 --- a/test-basic/plan-search.js +++ b/test-basic/plan-search.js @@ -308,4 +308,185 @@ describe('search', function() { }).catch(error => done(error)); }); }); + + describe('fragment option tests for fromSearch', function() { + const setupXquery = ` + xquery version "1.0-ml"; + let $jsondoc1 := object-node {"AllDataTypes": array-node {object-node {"word":"dog"}, object-node {"rank":1}, object-node {"score":4}}} + let $jsondoc2 := object-node {"AllDataTypes": array-node {object-node {"word":"cat"}, object-node {"rank":2}, object-node {"score":5}}} + let $jsondoc3 := object-node {"AllDataTypes": array-node {object-node {"word":"duck"}, object-node {"rank":3}, object-node {"score":6}}} + return ( + xdmp:document-insert("range-prop-1.json", $jsondoc1, xdmp:default-permissions(), ("elemCol","jsondoc-range")), + xdmp:document-insert("range-prop-2.json", $jsondoc2, xdmp:default-permissions(), ("elemCol","jsondoc-range")), + xdmp:document-insert("range-prop-3.json", $jsondoc3, xdmp:default-permissions(), ("elemCol","jsondoc-range")), + xdmp:document-set-properties("range-prop-1.json", (opticfragmentpropvalue)), + xdmp:lock-acquire("range-prop-1.json", "exclusive", "0", "dog rose", xs:unsignedLong(120)), + xdmp:lock-acquire("range-prop-2.json", "exclusive", "0", "cat tulip", xs:unsignedLong(120)), + xdmp:lock-acquire("range-prop-3.json", "exclusive", "0", "duck lily", xs:unsignedLong(120)) + ) + `; + + const teardownReleaseLocks = ` + xquery version "1.0-ml"; + ( + xdmp:lock-release("range-prop-1.json"), + xdmp:lock-release("range-prop-2.json"), + xdmp:lock-release("range-prop-3.json") + ) + `; + + const teardownDeleteDocs = ` + xquery version "1.0-ml"; + ( + xdmp:document-delete("range-prop-1.json"), + xdmp:document-delete("range-prop-2.json"), + xdmp:document-delete("range-prop-3.json") + ) + `; + + before(function(done) { + if (serverConfiguration.serverVersion < 12.1) { + this.skip(); + } + pbb.dbWriter.xqueryEval(setupXquery).result() + .then(() => done()) + .catch(done); + }); + + after(function(done) { + pbb.dbWriter.xqueryEval(teardownReleaseLocks).result() + .then(() => pbb.dbWriter.xqueryEval(teardownDeleteDocs).result()) + .then(() => done()) + .catch(done); + }); + + // TC0: No fragment option — default behavior searches document content (same as fragment:'document') + it('TC0: fromSearch without fragment option should search document content by default', function(done) { + execPlan( + p.fromSearch( + p.cts.wordQuery('dog') + ) + .joinDocAndUri('doc', 'uri', p.fragmentIdCol('fragmentId')) + .orderBy('uri') + .select(['uri', 'doc']) + ).then(function(response) { + const output = getResults(response); + assert(output.length === 1, 'Expected exactly 1 document containing "dog" with default fragment'); + assert(output[0].uri.value === 'range-prop-1.json', 'Expected range-prop-1.json'); + assert(output[0].doc.type === 'object', 'Expected default fragment to return JSON document'); + assert(output[0].doc.value.AllDataTypes[0].word === 'dog', 'Expected word "dog" in document content'); + done(); + }).catch(done); + }); + + // TC0b: Invalid fragment value → client-side error (no server call needed) + it('TC0b: should throw error for invalid fragment value', function() { + assert.throws(function() { + p.fromSearch( + p.cts.wordQuery('dog'), null, null, { fragment: 'unknown' } + ); + }, /fragment can only be/); + }); + + // TC1: fragment:'locks' — doc joined from locks fragment must be XML containing 'lock-type' + it('TC1: fromSearch with fragment:locks should find documents by lock token', function(done) { + execPlan( + p.fromSearch( + p.cts.locksFragmentQuery(p.cts.wordQuery('dog')), + null, null, { fragment: 'locks' } + ) + .joinDocAndUri('doc', 'uri', p.fragmentIdCol('fragmentId')) + .orderBy('uri') + .select(['uri', 'doc']) + ).then(function(response) { + const output = getResults(response); + assert(output.length === 1, 'Expected exactly 1 result from locks fragment'); + assert(output[0].uri.value === 'range-prop-1.json', 'Expected range-prop-1.json'); + assert(output[0].doc.type === 'element', 'Expected lock doc to be XML element'); + assert(output[0].doc.value.includes('lock-type'), 'Expected lock-type element in lock document'); + done(); + }).catch(done); + }); + + // TC2: fragment:'properties' — doc joined from properties fragment must be XML containing the property value + it('TC2: fromSearch with fragment:properties should find doc by its properties', function(done) { + execPlan( + p.fromSearch( + p.cts.wordQuery('opticfragmentpropvalue'), + null, null, { fragment: 'properties' } + ) + .joinDocAndUri('doc', 'uri', p.fragmentIdCol('fragmentId')) + .orderBy('uri') + .select(['uri', 'doc']) + ).then(function(response) { + const output = getResults(response); + assert(output.length === 1, 'Expected exactly 1 result from properties fragment'); + assert(output[0].uri.value === 'range-prop-1.json', 'Expected range-prop-1.json'); + assert(output[0].doc.type === 'element', 'Expected properties doc to be XML element'); + assert(output[0].doc.value.includes('opticfragmentpropvalue'), 'Expected property value in properties document'); + done(); + }).catch(done); + }); + + // TC3: fragment:'any' — returns all fragment types; verify both XML (lock/properties) and JSON (document) rows present + it('TC3: fromSearch with fragment:any should return results across fragment types', function(done) { + execPlan( + p.fromSearch( + p.cts.locksFragmentQuery(p.cts.wordQuery('dog')), + null, null, { fragment: 'any' } + ) + .joinDocAndUri('doc', 'uri', p.fragmentIdCol('fragmentId')) + .orderBy('uri') + .select(['uri', 'doc']) + ).then(function(response) { + const output = getResults(response); + assert(output.length > 1, 'Expected multiple rows (all fragment types) with fragment:any'); + const types = output.map(row => row.doc.type); + assert(types.includes('element'), 'Expected at least one XML fragment (lock or properties)'); + assert(types.includes('object'), 'Expected at least one JSON document fragment'); + done(); + }).catch(done); + }); + + // TC4: fragment:'document' — doc must be JSON containing the word 'dog' + it('TC4: fromSearch with fragment:document should find documents by content word', function(done) { + execPlan( + p.fromSearch( + p.cts.wordQuery('dog'), + null, null, { fragment: 'document' } + ) + .joinDocAndUri('doc', 'uri', p.fragmentIdCol('fragmentId')) + .orderBy('uri') + .select(['uri', 'doc']) + ).then(function(response) { + const output = getResults(response); + assert(output.length === 1, 'Expected exactly 1 document containing "dog"'); + assert(output[0].uri.value === 'range-prop-1.json', 'Expected range-prop-1.json'); + assert(output[0].doc.type === 'object', 'Expected document fragment to be JSON'); + assert(output[0].doc.value.AllDataTypes[0].word === 'dog', 'Expected word "dog" in document content'); + done(); + }).catch(done); + }); + + // TC5: explain() on a locks fragment plan should return a valid execution plan structure. + // Note: the server-side equivalent (TEST26) additionally exercises plan:parse()/plan:execute() + // on the explain output, but the Node client has no equivalent of those functions. + it('TC5: explain() on a locks fragment plan should return a valid plan structure', function(done) { + const plan = p.fromSearch( + p.cts.locksFragmentQuery(p.cts.wordQuery('dog')), + null, null, { fragment: 'locks' } + ) + .joinDocAndUri('doc', 'uri', p.fragmentIdCol('fragmentId')) + .orderBy('uri') + .select(['uri', 'doc']); + + pbb.explainPlan(plan) + .then(function(output) { + assert(output.node === 'plan', 'Expected explain output to have node:"plan"'); + assert(output.expr != null, 'Expected expr to be present in explain output'); + done(); + }) + .catch(done); + }); + }); }); From 783d5554168fcddbd8de8328909a405959ae486b Mon Sep 17 00:00:00 2001 From: Rita Chen Date: Wed, 13 May 2026 15:01:43 -0400 Subject: [PATCH 2/7] MLE-28336 updated Copyright --- etc/test-setup-users.js | 2 +- test-basic/plan-search.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/etc/test-setup-users.js b/etc/test-setup-users.js index a5deaa31..9024024e 100644 --- a/etc/test-setup-users.js +++ b/etc/test-setup-users.js @@ -1,5 +1,5 @@ /* -* Copyright (c) 2015-2025 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved. +* Copyright (c) 2015-2026 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved. */ var valcheck = require('core-util-is'); diff --git a/test-basic/plan-search.js b/test-basic/plan-search.js index 697f3760..668dbe46 100644 --- a/test-basic/plan-search.js +++ b/test-basic/plan-search.js @@ -1,5 +1,5 @@ /* -* Copyright (c) 2015-2025 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved. +* Copyright (c) 2015-2026 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved. */ 'use strict'; From 1cd61d481ced840858b2c48a8091e7d89214a08e Mon Sep 17 00:00:00 2001 From: Rita Chen Date: Thu, 14 May 2026 16:06:18 -0400 Subject: [PATCH 3/7] MLE-28335 update fromSearchDocs with fragment option --- lib/plan-builder-generated.js | 2 +- test-basic/plan-search.js | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/lib/plan-builder-generated.js b/lib/plan-builder-generated.js index 6beac4f6..04f38879 100755 --- a/lib/plan-builder-generated.js +++ b/lib/plan-builder-generated.js @@ -8993,7 +8993,7 @@ fromSQL(...args) { * @since 2.1.1 * @param { PlanSearchQuery } [query] - Qualifies and establishes the scores for a set of documents. The query can be a cts:query or a string as a shortcut for a cts:word-query. * @param { XsString } [qualifierName] - Specifies a name for qualifying the column names. - * @param { PlanSearchOption } [option] - Similar to the options of cts:search, supplies the 'scoreMethod' key with a value of 'logtfidf', 'logtf', 'simple', 'zero', 'random', or 'bm25' to specify the method for assigning a score to matched documents or supplies the 'qualityWeight' key with a numeric value to specify a multiplier for the quality contribution to the score. 'logtfidf' is the default score method and the results are ordered by score by default. Specify a value between 0 (exclusive) and 1 (inclusive) for bm25LengthWeight if 'bm25' scoring method is used. + * @param { PlanSearchOption } [option] - Similar to the options of cts:search, supplies the 'scoreMethod' key with a value of 'logtfidf', 'logtf', 'simple', 'zero', 'random', or 'bm25' to specify the method for assigning a score to matched documents or supplies the 'qualityWeight' key with a numeric value to specify a multiplier for the quality contribution to the score. 'logtfidf' is the default score method and the results are ordered by score by default. Specify a value between 0 (exclusive) and 1 (inclusive) for bm25LengthWeight if 'bm25' scoring method is used. As of MLS 12.1, supplies the 'fragment' key with a value of 'document' (default), 'properties', 'locks', or 'any' to specify which document fragment type to search. Note: on servers earlier than MLS 12.1, the 'fragment' option is silently ignored and all fragment types are searched. * @returns { planBuilder.AccessPlan } */ fromSearchDocs(...args) { diff --git a/test-basic/plan-search.js b/test-basic/plan-search.js index 668dbe46..215643f3 100644 --- a/test-basic/plan-search.js +++ b/test-basic/plan-search.js @@ -488,5 +488,25 @@ describe('search', function() { }) .catch(done); }); + + // TC6: fromSearchDocs with fragment:'locks' — confirms fromSearchDocs honors the fragment option (MLS 12.1+) + it('TC6: fromSearchDocs with fragment:locks should find documents by lock token', function(done) { + execPlan( + p.fromSearchDocs( + p.cts.locksFragmentQuery(p.cts.wordQuery('dog')), + null, + { fragment: 'locks' } + ) + .orderBy('uri') + .select(['uri', 'doc']) + ).then(function(response) { + const output = getResults(response); + assert(output.length === 1, 'Expected exactly 1 result from fromSearchDocs with fragment:locks'); + assert(output[0].uri.value === 'range-prop-1.json', 'Expected range-prop-1.json'); + assert(output[0].doc.type === 'element', 'Expected lock doc to be XML element'); + assert(output[0].doc.value.includes('lock-type'), 'Expected lock-type element in lock document'); + done(); + }).catch(done); + }); }); }); From cd1559415b94dbcaa0f47e9267650e8872a76661 Mon Sep 17 00:00:00 2001 From: Rita Chen Date: Fri, 15 May 2026 10:18:20 -0400 Subject: [PATCH 4/7] MLE-28335 resolve npm audit vulnerabilities (0 remaining) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Security fixes: - sanitize-html: 2.17.0 → ^2.17.4 (critical XSS via xmp passthrough, GHSA-rpr9-rxv7-x643) - brace-expansion override: 2.0.2 → 5.0.6 (DoS via zero-step sequence, GHSA-f886-m6hf-6m8v) - serialize-javascript override: 7.0.4 → 7.0.5 (CPU exhaustion DoS, GHSA-qj8w-gfj5-8c6v) - diff override: added 9.0.0 (DoS in parsePatch/applyPatch for mocha 11.4+, GHSA-73rr-hh4g-fpgx) - fast-uri, flatted, lodash, picomatch, postcss updated via npm audit fix --- package-lock.json | 141 +++++++++++++++++++++++++++------------------- package.json | 7 ++- 2 files changed, 88 insertions(+), 60 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1b55c7ff..4fbf96d2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -38,7 +38,7 @@ "mocha": "^11.7.5", "mocha-junit-reporter": "2.2.1", "moment": "2.30.1", - "sanitize-html": "2.17.0", + "sanitize-html": "^2.17.4", "should": "13.2.3", "stream-to-array": "2.3.0", "typescript": "5.7.2" @@ -131,15 +131,15 @@ } }, "node_modules/@eslint/config-array": { - "version": "0.21.1", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.1.tgz", - "integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==", + "version": "0.21.2", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.2.tgz", + "integrity": "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==", "dev": true, "license": "Apache-2.0", "dependencies": { "@eslint/object-schema": "^2.1.7", "debug": "^4.3.1", - "minimatch": "^3.1.2" + "minimatch": "^3.1.5" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -185,9 +185,9 @@ } }, "node_modules/@eslint/eslintrc": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.4.tgz", - "integrity": "sha512-4h4MVF8pmBsncB60r0wSJiIeUKTSD4m7FmTFThG8RHlsg9ajqckLm9OraguFGZE4vVdpiI1Q4+hFnisopmG6gQ==", + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.5.tgz", + "integrity": "sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==", "dev": true, "license": "MIT", "dependencies": { @@ -198,7 +198,7 @@ "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.1", - "minimatch": "^3.1.3", + "minimatch": "^3.1.5", "strip-json-comments": "^3.1.1" }, "engines": { @@ -727,11 +727,14 @@ } }, "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } }, "node_modules/bare-events": { "version": "2.8.1", @@ -811,13 +814,16 @@ "license": "MIT" }, "node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", + "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", "dev": true, "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0" + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" } }, "node_modules/braces": { @@ -1172,6 +1178,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/dayjs": { + "version": "1.11.20", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.20.tgz", + "integrity": "sha512-YbwwqR/uYpeoP4pu043q+LTDLFBLApUP6VxRihdfNTqu4ubqMlGDLd6ErXhEgsyvY0K6nCs7nggYumAN+9uEuQ==", + "dev": true, + "license": "MIT" + }, "node_modules/debug": { "version": "4.3.6", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.6.tgz", @@ -1276,9 +1289,9 @@ } }, "node_modules/diff": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.2.tgz", - "integrity": "sha512-vtcDfH3TOjP8UekytvnHH1o1P4FcUdt4eQ1Y+Abap1tk/OB2MWQvcwS2ClCd1zuIhc3JKOx6p3kod8Vfys3E+A==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-9.0.0.tgz", + "integrity": "sha512-svtcdpS8CgJyqAjEQIXdb3OjhFVVYjzGAPO8WGCmRbrml64SPw/jJD4GoE98aR7r25A0XcgrK3F02yw9R/vhQw==", "dev": true, "license": "BSD-3-Clause", "engines": { @@ -1839,9 +1852,9 @@ "license": "MIT" }, "node_modules/fast-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", - "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.2.tgz", + "integrity": "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==", "dev": true, "funding": [ { @@ -1986,9 +1999,9 @@ } }, "node_modules/flatted": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", - "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", + "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", "dev": true, "license": "ISC" }, @@ -2580,9 +2593,9 @@ } }, "node_modules/htmlparser2": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", - "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-10.1.0.tgz", + "integrity": "sha512-VTZkM9GWRAtEpveh7MSF6SjjrpNVNNVJfFup7xTY3UpFtm67foy9HDVXneLtFVt4pMz5kZtgNcvCniNFb1hlEQ==", "dev": true, "funding": [ "https://github.com/fb55/htmlparser2?sponsor=1", @@ -2595,8 +2608,21 @@ "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.3", - "domutils": "^3.0.1", - "entities": "^4.4.0" + "domutils": "^3.2.2", + "entities": "^7.0.1" + } + }, + "node_modules/htmlparser2/node_modules/entities": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz", + "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" } }, "node_modules/human-signals": { @@ -3088,6 +3114,16 @@ "node": ">= 10.13.0" } }, + "node_modules/launder": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/launder/-/launder-1.7.1.tgz", + "integrity": "sha512-mU6WRz5EusL9ZZuiZ5SO4Y6C0P9PAUR9iwdb6bzj4KDihm28DiHFw+/yk9DBH4f+Pv1wuzQ4e2jV3oQ7mkIqvw==", + "dev": true, + "license": "MIT", + "dependencies": { + "dayjs": "^1.11.7" + } + }, "node_modules/lead": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/lead/-/lead-4.0.0.tgz", @@ -3158,9 +3194,9 @@ } }, "node_modules/lodash": { - "version": "4.17.23", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", - "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==", + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", + "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", "dev": true, "license": "MIT" }, @@ -3562,16 +3598,6 @@ "node": ">=12" } }, - "node_modules/mocha/node_modules/diff": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-7.0.0.tgz", - "integrity": "sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.3.1" - } - }, "node_modules/mocha/node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -4064,9 +4090,9 @@ "license": "ISC" }, "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", "dev": true, "license": "MIT", "engines": { @@ -4090,9 +4116,9 @@ } }, "node_modules/postcss": { - "version": "8.5.6", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", - "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "version": "8.5.14", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.14.tgz", + "integrity": "sha512-SoSL4+OSEtR99LHFZQiJLkT59C5B1amGO1NzTwj7TT1qCUgUO6hxOvzkOYxD+vMrXBM3XJIKzokoERdqQq/Zmg==", "dev": true, "funding": [ { @@ -4452,16 +4478,17 @@ } }, "node_modules/sanitize-html": { - "version": "2.17.0", - "resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-2.17.0.tgz", - "integrity": "sha512-dLAADUSS8rBwhaevT12yCezvioCA+bmUTPH/u57xKPT8d++voeYE6HeluA/bPbQ15TwDBG2ii+QZIEmYx8VdxA==", + "version": "2.17.4", + "resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-2.17.4.tgz", + "integrity": "sha512-2HW7v2ol/uAM7sX4hbD8Z59OGWmAPrvjL8E71UWlBcj6m+kcF6ilQBLny+cIgY214QJeJT5tQuxKKqX0SQqjGQ==", "dev": true, "license": "MIT", "dependencies": { "deepmerge": "^4.2.2", "escape-string-regexp": "^4.0.0", - "htmlparser2": "^8.0.0", + "htmlparser2": "^10.1.0", "is-plain-object": "^5.0.0", + "launder": "^1.7.1", "parse-srcset": "^1.0.2", "postcss": "^8.3.11" } @@ -4509,9 +4536,9 @@ } }, "node_modules/serialize-javascript": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-7.0.4.tgz", - "integrity": "sha512-DuGdB+Po43Q5Jxwpzt1lhyFSYKryqoNjQSA9M92tyw0lyHIOur+XCalOUe0KTJpyqzT8+fQ5A0Jf7vCx/NKmIg==", + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-7.0.5.tgz", + "integrity": "sha512-F4LcB0UqUl1zErq+1nYEEzSHJnIwb3AF2XWB94b+afhrekOUijwooAYqFyRbjYkm2PAKBabx6oYv/xDxNi8IBw==", "dev": true, "license": "BSD-3-Clause", "engines": { diff --git a/package.json b/package.json index e01bad67..960827d3 100644 --- a/package.json +++ b/package.json @@ -73,7 +73,7 @@ "mocha": "^11.7.5", "mocha-junit-reporter": "2.2.1", "moment": "2.30.1", - "sanitize-html": "2.17.0", + "sanitize-html": "^2.17.4", "should": "13.2.3", "stream-to-array": "2.3.0", "typescript": "5.7.2" @@ -86,18 +86,19 @@ "ansi-styles": "4.3.0", "ansi-regex": "5.0.1", "braces": "3.0.3", - "brace-expansion": "2.0.2", + "brace-expansion": "5.0.6", "chalk": "4.1.2", "color-convert": "3.1.0", "color-name": "2.0.0", "cross-spawn": "7.0.6", "debug": "4.3.6", + "diff": "9.0.0", "glob": "12.0.0", "glob-parent": "6.0.2", "markdown-it": "14.1.1", "minimatch": "10.2.4", "semver": "7.5.3", - "serialize-javascript": "7.0.4", + "serialize-javascript": "7.0.5", "strip-ansi": "6.0.0", "supports-color": "7.2.0", "tar-fs": "2.1.4", From 4e70939459ef2a0ed677aea21ad49a7241ce38f5 Mon Sep 17 00:00:00 2001 From: Rita Chen Date: Fri, 15 May 2026 11:44:12 -0400 Subject: [PATCH 5/7] MLE-28335 update scheduled trigger for regressions --- Jenkinsfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jenkinsfile b/Jenkinsfile index 5020e2fb..b149762d 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -125,7 +125,7 @@ pipeline { agent none triggers { - parameterizedCron(env.BRANCH_NAME == "develop" ? "00 02 * * * % regressions=true" : "") + parameterizedCron(env.BRANCH_NAME == "develop" ? "00 05 * * * % regressions=true" : "") } parameters { From cbffb54118aec9783583cedf1773d7c64b5e7d1d Mon Sep 17 00:00:00 2001 From: Rita Chen Date: Fri, 15 May 2026 16:03:22 -0400 Subject: [PATCH 6/7] MLE-28335 skip cts.param integration tests for server < 12.1 --- test-basic/optic-cts-param-test.js | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/test-basic/optic-cts-param-test.js b/test-basic/optic-cts-param-test.js index 53881db5..a9fd8189 100644 --- a/test-basic/optic-cts-param-test.js +++ b/test-basic/optic-cts-param-test.js @@ -18,6 +18,8 @@ const valcheck = require('core-util-is'); const testconfig = require('../etc/test-config.js'); const marklogic = require('../'); +const testlib = require('../etc/test-lib'); +let serverConfiguration = {}; // Allow overriding connection info via environment or direct config const connInfo = { @@ -35,6 +37,21 @@ const op = marklogic.planBuilder; describe('cts.param integration tests (MLE-27883)', function() { this.timeout(10000); // Allow 10 seconds for server queries + before(function(done) { + try { + testlib.findServerConfiguration(serverConfiguration); + setTimeout(() => { done(); }, 3000); + } catch(error) { + done(error); + } + }); + + before(function() { + if (serverConfiguration.serverVersion < 12.1) { + this.skip(); + } + }); + // ────────────────────────────────────────────────────────────────────────────── // Test: collectionQuery with cts.param binding // ────────────────────────────────────────────────────────────────────────────── From 5704d4dce962e660a0e23712c6f1a6d735411611 Mon Sep 17 00:00:00 2001 From: Rita Chen Date: Fri, 15 May 2026 16:12:23 -0400 Subject: [PATCH 7/7] MLE-28335 fix to use the correct optic test database testconfig.restWriterConnection --- test-basic/optic-cts-param-test.js | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/test-basic/optic-cts-param-test.js b/test-basic/optic-cts-param-test.js index a9fd8189..298fa1a3 100644 --- a/test-basic/optic-cts-param-test.js +++ b/test-basic/optic-cts-param-test.js @@ -21,17 +21,7 @@ const marklogic = require('../'); const testlib = require('../etc/test-lib'); let serverConfiguration = {}; -// Allow overriding connection info via environment or direct config -const connInfo = { - host: process.env.ML_TEST_HOST || testconfig.testHost || 'localhost', - port: process.env.ML_TEST_PORT || 8000, - database: process.env.ML_TEST_DATABASE || 'Documents', - authType: process.env.ML_TEST_AUTH_TYPE || 'digest', - user: process.env.ML_TEST_USER || testconfig.restWriterConnection.user, - password: process.env.ML_TEST_PASSWORD || testconfig.restWriterConnection.password, -}; - -const db = marklogic.createDatabaseClient(connInfo); +const db = marklogic.createDatabaseClient(testconfig.restWriterConnection); const op = marklogic.planBuilder; describe('cts.param integration tests (MLE-27883)', function() {