@myagentroam/node 0.9.62 → 0.9.65
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/LICENSE +202 -0
- package/NOTICE +4 -0
- package/dist/capabilities.js +110 -2
- package/dist/claude-agent-sdk.js +330 -2
- package/dist/claude-channel.js +44 -2
- package/dist/codex-app-server.js +510 -2
- package/dist/config.js +106 -2
- package/dist/connector/node-connector-options.js +1 -2
- package/dist/connector/node-operation-router.js +22 -2
- package/dist/connector.js +1736 -2
- package/dist/control-outbound-scheduler.js +153 -2
- package/dist/database.js +639 -2
- package/dist/fake-runner.js +129 -2
- package/dist/git-askpass.js +12 -0
- package/dist/git-execution-helper.js +266 -0
- package/dist/health.js +3 -2
- package/dist/main.js +57 -2
- package/dist/mcp-installation-model.js +1 -2
- package/dist/migrations/v004.js +55 -3
- package/dist/migrations/v005.js +13 -3
- package/dist/migrations/v006.js +90 -3
- package/dist/migrations/v007.js +22 -3
- package/dist/migrations/v008.js +18 -3
- package/dist/migrations/v009.js +70 -4
- package/dist/migrations/v010.js +43 -3
- package/dist/migrations/v011.js +20 -3
- package/dist/migrations/v012.js +8 -0
- package/dist/native-jsonl-reader.js +186 -2
- package/dist/native-session-history.js +1143 -2
- package/dist/opencode-runtime.js +80 -2
- package/dist/opencode-server.js +259 -2
- package/dist/operational.js +122 -2
- package/dist/process-tree.js +18 -2
- package/dist/ripgrep-binary.js +31 -2
- package/dist/rotating-log.js +67 -2
- package/dist/runner/abstract-runner.js +260 -2
- package/dist/runner/claude/managed-run-controller.js +355 -2
- package/dist/runner/claude/tool-normalizer.js +120 -2
- package/dist/runner/claude-code-runner.js +303 -2
- package/dist/runner/codex/composer-image-markers.js +8 -2
- package/dist/runner/codex/conversation-parser.js +913 -2
- package/dist/runner/codex/managed-run-controller.js +494 -2
- package/dist/runner/codex/tool-normalizer.js +214 -2
- package/dist/runner/codex-runner.js +585 -2
- package/dist/runner/fake-test-runner.js +47 -2
- package/dist/runner/mar-agent/conversation-normalizer.js +644 -2
- package/dist/runner/mar-agent/executor-stdio-client-transport.js +139 -2
- package/dist/runner/mar-agent/generated-images.js +41 -2
- package/dist/runner/mar-agent/history-projection.js +106 -2
- package/dist/runner/mar-agent/hosted-conversation-runtime.js +446 -2
- package/dist/runner/mar-agent/managed-run-controller.js +785 -2
- package/dist/runner/mar-agent/mcp-client-pool.js +233 -2
- package/dist/runner/mar-agent/runtime-model-factory.js +326 -2
- package/dist/runner/mar-agent-runner.js +458 -2
- package/dist/runner/opencode/conversation-parser.js +221 -2
- package/dist/runner/opencode/managed-run-controller.js +588 -2
- package/dist/runner/opencode/tool-normalizer.js +80 -2
- package/dist/runner/opencode-runner.js +426 -2
- package/dist/runner/runner-registry.js +87 -2
- package/dist/runner-command-engine.js +71 -2
- package/dist/runner-profiles.js +323 -2
- package/dist/runner-usage.js +190 -2
- package/dist/runtime-command-detector.js +73 -2
- package/dist/runtime-model.js +1 -2
- package/dist/runtime-state.js +1509 -2
- package/dist/service/attachment-domain-state.js +7 -2
- package/dist/service/bubblewrap-sandbox-executor.js +543 -2
- package/dist/service/continuous-run-coordinator.js +425 -2
- package/dist/service/conversation-history-service.js +552 -2
- package/dist/service/conversation-hosting-capability-service.js +61 -2
- package/dist/service/conversation-hosting-enablement-service.js +91 -2
- package/dist/service/conversation-hosting-maintenance-service.js +174 -2
- package/dist/service/conversation-segment-service.js +162 -2
- package/dist/service/conversation-template-node-service.js +992 -2
- package/dist/service/external-session-resume-service.js +152 -2
- package/dist/service/fake-managed-run-service.js +60 -2
- package/dist/service/git-execution-service.js +418 -0
- package/dist/service/git-operation-service.js +57 -0
- package/dist/service/hosted-conversation-node-service.js +1982 -2
- package/dist/service/hosted-conversation-store.js +1405 -2
- package/dist/service/managed-runner-session-service.js +35 -2
- package/dist/service/mar-agent-model-transport-test-service.js +88 -2
- package/dist/service/mcp-installation-verifier.js +87 -2
- package/dist/service/mcp-node-operation-service.js +90 -2
- package/dist/service/mission-mcp-server.js +124 -2
- package/dist/service/mission-mcp-stdio.js +40 -2
- package/dist/service/mission-runtime.js +142 -2
- package/dist/service/native-session-projection-service.js +274 -2
- package/dist/service/native-session-watch-service.js +683 -2
- package/dist/service/node-connection-lifecycle-service.js +88 -2
- package/dist/service/node-control-channel.js +85 -2
- package/dist/service/node-control-message-service.js +109 -2
- package/dist/service/node-request-service.js +62 -2
- package/dist/service/node-run-event-bridge.js +88 -2
- package/dist/service/node-terminal-channel.js +202 -2
- package/dist/service/node-upgrade-coordinator.js +137 -2
- package/dist/service/node-workspace-coordinator.js +97 -2
- package/dist/service/recent-run-projection-service.js +99 -2
- package/dist/service/run-attachment-service.js +321 -2
- package/dist/service/run-domain-state.js +4 -2
- package/dist/service/run-event-service.js +246 -2
- package/dist/service/run-ownership-policy.js +22 -2
- package/dist/service/run-workbench-service.js +120 -2
- package/dist/service/runner-channel-message-service.js +20 -2
- package/dist/service/runner-interaction-service.js +243 -2
- package/dist/service/runner-service.js +69 -2
- package/dist/service/runtime-credential-control-service.js +95 -2
- package/dist/service/session-catalog-service.js +404 -2
- package/dist/service/session-command-service.js +125 -2
- package/dist/service/session-context-service.js +128 -2
- package/dist/service/session-domain-state.js +9 -2
- package/dist/service/session-identity-service.js +86 -2
- package/dist/service/session-lifecycle-service.js +248 -2
- package/dist/service/session-message-service.js +270 -2
- package/dist/service/session-presentation-service.js +43 -2
- package/dist/service/session-query-service.js +82 -2
- package/dist/service/session-watch-slot-coordinator.js +21 -2
- package/dist/service/skill-directory-service.js +238 -2
- package/dist/service/skill-install-service.js +434 -2
- package/dist/service/skill-node-operation-service.js +206 -2
- package/dist/service/terminal-relay-state.js +3 -2
- package/dist/service/terminal-service.js +90 -2
- package/dist/service/web-search-error.js +40 -2
- package/dist/service/workbench-event-service.js +21 -2
- package/dist/service/workbench-manifest-service.js +69 -2
- package/dist/service/workspace-change-service.js +290 -2
- package/dist/service/workspace-content-search-service.js +457 -2
- package/dist/service/workspace-domain-state.js +41 -2
- package/dist/service/workspace-file-service.js +150 -2
- package/dist/service/workspace-git-exclude-service.js +119 -2
- package/dist/service/workspace-queue-workbench-service.js +176 -2
- package/dist/service/workspace-service.js +92 -2
- package/dist/service/workspace-session-service.js +154 -2
- package/dist/service/workspace-upload-service.js +262 -2
- package/dist/service/workspace-watch-controller.js +68 -2
- package/dist/service/workspace-watch-service.js +170 -2
- package/dist/service/workspace-watch-snapshot-service.js +218 -2
- package/dist/service/workspace-workbench-service.js +239 -2
- package/dist/service.js +91 -2
- package/dist/storage.js +32 -2
- package/dist/supervisor.js +136 -2
- package/dist/terminal.js +496 -2
- package/dist/util/git-credential-client.js +32 -0
- package/dist/util/node-operation-data.js +9 -2
- package/dist/util/node-operation-parsers.js +382 -2
- package/dist/util/personal-instructions.js +34 -2
- package/dist/util/runner-error.js +70 -2
- package/dist/util/runner-native-session-parsers.js +510 -2
- package/dist/util/safe-error.js +8 -2
- package/dist/util/secret-environment.js +25 -2
- package/dist/workspace.js +1793 -2
- package/package.json +11 -8
- package/vendor/ripgrep/darwin-arm64/rg +0 -0
- package/vendor/ripgrep/darwin-x64/rg +0 -0
- package/vendor/ripgrep/manifest.json +12 -0
- package/README.md +0 -42
- package/dist/event-buffer.js +0 -2
- package/dist/migrations/v001.js +0 -107
- package/dist/migrations/v002.js +0 -2
- package/dist/migrations/v003.js +0 -2
- package/dist/service/mcp-package-installer.js +0 -2
|
@@ -1,2 +1,457 @@
|
|
|
1
|
-
|
|
2
|
-
const _0x362cb8=_0xa3b5;(function(_0x406a64,_0x3baf65){const _0x1bfb51=_0xa3b5,_0x3a636d=_0x406a64();while(!![]){try{const _0xaa5437=-parseInt(_0x1bfb51(0x182))/(-0xac+-0x2458+0x2505)+-parseInt(_0x1bfb51(0x16d))/(-0xd2a+0x130c+-0x5e0)*(-parseInt(_0x1bfb51(0x1c4))/(-0x61*-0x43+0x1*-0x16fe+-0x262))+parseInt(_0x1bfb51(0x1b9))/(0xd*-0x6d+0x27a*0x4+-0x45b*0x1)+parseInt(_0x1bfb51(0x171))/(-0x2*-0xa7a+0x22*-0x43+-0xc09)+-parseInt(_0x1bfb51(0x166))/(0x1*-0x1f7c+-0x10ea+-0x812*-0x6)*(parseInt(_0x1bfb51(0x18e))/(0xe*-0xc6+-0xa27*-0x1+-0xc*-0xf))+parseInt(_0x1bfb51(0x1bb))/(0x221*0x1+-0x17e5+-0xa*-0x22e)+parseInt(_0x1bfb51(0x17b))/(0x1a4e+0xebe+-0x2903)*(-parseInt(_0x1bfb51(0x186))/(0x1d21+-0x14c4+-0x1*0x853));if(_0xaa5437===_0x3baf65)break;else _0x3a636d['push'](_0x3a636d['shift']());}catch(_0x121629){_0x3a636d['push'](_0x3a636d['shift']());}}}(_0xcfa3,-0xb*-0x9f8d+-0x33914+0x2af1e*0x1));import{spawn}from'node:child_process';import{createReadStream}from'node:fs';import{lstat,realpath}from'node:fs/promises';import{isAbsolute,join,relative,sep}from'node:path';import{bundledRipgrepPath}from'../ripgrep-binary.js';const RESULT_LIMIT=0x1*-0x189+-0x585+-0x3b*-0x22,PER_FILE_LIMIT=-0x23cc+-0x2091+0x4471,FILE_BYTE_LIMIT=(-0x52*0x71+-0x923+-0x49*-0x9f)*(-0x2003+-0x408+0xd59*0x3)*(-0x19ad*-0x1+0x710*-0x2+-0x1*0x78d),SCAN_BYTE_LIMIT=(-0x18cf+0x89f+0xc*0x164)*(-0x443+-0x32*-0xbb+-0x1c43)*(-0x3*0xa8e+0x9f7+0x19b3),OUTPUT_BYTE_LIMIT=(0x25ab+-0x1*-0x1235+-0x37de)*(0x227f*-0x1+-0x2c*-0x61+0x1*0x15d3)*(0x1192+0xa33+0x5*-0x4c1),TIMEOUT_MS=-0xa4d*-0x2+-0x2066+0x1f54,WINDOWS_ARGUMENT_BATCH_BYTES=(-0x1807*-0x1+-0x1*0x7f9+0x7fb*-0x2)*(0x1bae+0xeb9+-0x57*0x71),POSIX_ARGUMENT_BATCH_BYTES=(-0x3c1*0x9+-0xd0f+-0x179c*-0x2)*(0x89*0x15+-0x24d2+0x1d95),SEARCH_CONCURRENCY=-0xe8e+-0x227b+0x310b,SEARCH_QUEUE_LIMIT=-0xd7+-0x222a+0x2311,CANDIDATE_VALIDATION_CONCURRENCY=-0x1*-0x2339+0x3e*-0x6+-0x30f*0xb;let activeSearches=-0x122f+0x1d8f*0x1+-0xb60;const searchWaiters=[];async function acquireSearch(_0xfe5049){const _0x3ef959=_0xa3b5;if(activeSearches>=SEARCH_CONCURRENCY){if(searchWaiters[_0x3ef959(0x162)]>=SEARCH_QUEUE_LIMIT)throw new Error(_0x3ef959(0x17c));await new Promise((_0x5c8350,_0x60ba12)=>{const _0x20fdfa=_0x3ef959,_0x1ba50e=()=>{clearTimeout(_0x3fed58),_0x5c8350();},_0x3fed58=setTimeout(()=>{const _0x3b1adf=_0xa3b5,_0xd1c5d1=searchWaiters[_0x3b1adf(0x1a9)](_0x1ba50e);if(_0xd1c5d1>=0x64d*0x2+-0x9a3+-0x2f7)searchWaiters[_0x3b1adf(0x179)](_0xd1c5d1,-0x4*0x295+-0x6d1+0x1126);_0x60ba12(new Error(_0x3b1adf(0x1a0)));},Math[_0x20fdfa(0x169)](-0x533+-0x4a3*-0x4+-0x3d*0x38,_0xfe5049-Date[_0x20fdfa(0x1b5)]()));searchWaiters[_0x20fdfa(0x170)](_0x1ba50e);});}return activeSearches+=-0x8*0x12d+0x3*-0x632+-0x1bff*-0x1,()=>{const _0x2b820b=_0x3ef959;activeSearches-=-0xd83*0x1+0xb45+-0x1*-0x23f,searchWaiters[_0x2b820b(0x16b)]()?.();};}export class WorkspaceContentSearchService{[_0x362cb8(0x19b)];[_0x362cb8(0x1a2)];[_0x362cb8(0x1a1)];[_0x362cb8(0x1c0)]=new Map();constructor(_0xb478fc,_0x9d0d91=process[_0x362cb8(0x1a2)],_0x3544dc=resolveRipgrep()){const _0x2a0d46=_0x362cb8;this[_0x2a0d46(0x19b)]=_0xb478fc,this[_0x2a0d46(0x1a2)]=_0x9d0d91,this[_0x2a0d46(0x1a1)]=_0x3544dc;}[_0x362cb8(0x1be)](_0x2a3493,_0x1f7774){const _0x4ba813=_0x362cb8;if(typeof _0x1f7774[_0x4ba813(0x163)]!==_0x4ba813(0x1bc)||_0x1f7774[_0x4ba813(0x163)][_0x4ba813(0x162)]<0x1*0x1f88+-0x19d9+-0x2d7*0x2||_0x1f7774[_0x4ba813(0x163)][_0x4ba813(0x162)]>-0x1333+0xf*-0x1f7+0x22*0x176)throw new Error(_0x4ba813(0x175));const _0x5b66fd=_0x2a3493['id']+'\x00'+_0x1f7774[_0x4ba813(0x163)][_0x4ba813(0x16a)](),_0x1e481a=this[_0x4ba813(0x1c0)][_0x4ba813(0x174)](_0x5b66fd);if(_0x1e481a!==undefined)return _0x1e481a;const _0x3f8435=this[_0x4ba813(0x1af)](_0x2a3493,_0x1f7774[_0x4ba813(0x163)])[_0x4ba813(0x19a)](()=>this[_0x4ba813(0x1c0)][_0x4ba813(0x188)](_0x5b66fd));return this[_0x4ba813(0x1c0)][_0x4ba813(0x1bd)](_0x5b66fd,_0x3f8435),_0x3f8435;}async[_0x362cb8(0x1af)](_0x14619c,_0x2a963b){const _0xd1f325=_0x362cb8,_0x2cba1d=Date[_0xd1f325(0x1b5)]()+TIMEOUT_MS,_0x37fc98=await acquireSearch(_0x2cba1d);try{const _0x3b8ba3=await this[_0xd1f325(0x18f)](_0x14619c,_0x2cba1d);if(_0x3b8ba3[_0xd1f325(0x19b)][_0xd1f325(0x162)]===-0x5ff+-0xff7+0x15f6)return{'matches':[],'engine':_0xd1f325(0x198),'truncated':_0x3b8ba3[_0xd1f325(0x16e)]||Date[_0xd1f325(0x1b5)]()>=_0x2cba1d,'skippedFiles':_0x3b8ba3[_0xd1f325(0x16f)]};if(this[_0xd1f325(0x1a1)]!==null)try{const _0x1dc0e4=await this[_0xd1f325(0x194)](_0xd1f325(0x176),_0x14619c[_0xd1f325(0x1b0)],_0x3b8ba3[_0xd1f325(0x19b)],_0x2a963b,_0x2cba1d);return{'matches':_0x1dc0e4[_0xd1f325(0x1ac)](-0x9*0x26b+0x839+0xd8a,RESULT_LIMIT),'engine':_0xd1f325(0x176),'truncated':_0x3b8ba3[_0xd1f325(0x16e)]||_0x1dc0e4[_0xd1f325(0x162)]>RESULT_LIMIT||Date[_0xd1f325(0x1b5)]()>=_0x2cba1d,'skippedFiles':_0x3b8ba3[_0xd1f325(0x16f)]};}catch{}const _0x5c8a93=await nodeSearch(_0x3b8ba3[_0xd1f325(0x19b)],_0x2a963b,_0x2cba1d);return{'matches':_0x5c8a93[_0xd1f325(0x1b3)][_0xd1f325(0x1ac)](0x1ad8+-0x1246*-0x2+0x3f64*-0x1,RESULT_LIMIT),'engine':_0xd1f325(0x198),'truncated':_0x3b8ba3[_0xd1f325(0x16e)]||_0x5c8a93[_0xd1f325(0x1b3)][_0xd1f325(0x162)]>RESULT_LIMIT||Date[_0xd1f325(0x1b5)]()>=_0x2cba1d,'skippedFiles':_0x3b8ba3[_0xd1f325(0x16f)]+_0x5c8a93[_0xd1f325(0x16f)]};}finally{_0x37fc98();}}async[_0x362cb8(0x18f)](_0xba9bb7,_0x182e65){const _0x51f2ec=_0x362cb8,_0x58ec45=await realpath(_0xba9bb7[_0x51f2ec(0x1b0)]);let _0x3c864e;try{_0x3c864e=await beforeDeadline(this[_0x51f2ec(0x19b)][_0x51f2ec(0x1c3)](_0xba9bb7)[_0x51f2ec(0x18a)](),_0x182e65);}catch(_0x2de342){if(_0x2de342 instanceof Error&&_0x2de342[_0x51f2ec(0x1a5)]===_0x51f2ec(0x1a0))return{'files':[],'skippedFiles':0x0,'truncated':!![]};throw _0x2de342;}const _0x1c965b=[];let _0x2d38f6=-0xd3a+0xb*-0xd+0xdc9,_0x468132=-0x1087+0x10eb*0x1+-0x64,_0x101863=_0x3c864e[_0x51f2ec(0x16e)];for(let _0x2c3741=0x2*0xc11+0x1d71+-0x3593;_0x2c3741<_0x3c864e[_0x51f2ec(0x18c)][_0x51f2ec(0x162)];_0x2c3741+=CANDIDATE_VALIDATION_CONCURRENCY){if(Date[_0x51f2ec(0x1b5)]()>=_0x182e65){_0x101863=!![];break;}const _0x36799b=await Promise[_0x51f2ec(0x193)](_0x3c864e[_0x51f2ec(0x18c)][_0x51f2ec(0x1ac)](_0x2c3741,_0x2c3741+CANDIDATE_VALIDATION_CONCURRENCY)[_0x51f2ec(0x1ba)](_0x59cfcd=>validateCandidate(_0x58ec45,_0x59cfcd[_0x51f2ec(0x1b0)])));for(const _0x3096ad of _0x36799b){if(_0x3096ad===undefined){_0x2d38f6+=-0x42d*0x4+-0x5fb+0x16b0;continue;}if(_0x468132+_0x3096ad[_0x51f2ec(0x1aa)]>SCAN_BYTE_LIMIT){_0x101863=!![];break;}_0x468132+=_0x3096ad[_0x51f2ec(0x1aa)],_0x1c965b[_0x51f2ec(0x170)]({'path':_0x3096ad[_0x51f2ec(0x1b0)],'absolutePath':_0x3096ad[_0x51f2ec(0x161)]});}if(_0x101863)break;}return{'files':_0x1c965b,'skippedFiles':_0x2d38f6,'truncated':_0x101863};}async[_0x362cb8(0x194)](_0x8da83e,_0x474336,_0x389ba0,_0x52a2c8,_0x41fb28){const _0x4cc617=_0x362cb8,_0x58cf6e=[],_0x45f481=this[_0x4cc617(0x1a2)]===_0x4cc617(0x184)?WINDOWS_ARGUMENT_BATCH_BYTES:POSIX_ARGUMENT_BATCH_BYTES;for(const _0x1d987d of batches(_0x389ba0,_0x45f481)){if(Date[_0x4cc617(0x1b5)]()>=_0x41fb28||_0x58cf6e[_0x4cc617(0x162)]>RESULT_LIMIT)break;const _0x3382fc=commandFor(_0x8da83e,_0x1d987d,_0x52a2c8,this[_0x4cc617(0x1a1)]??undefined),_0x20a20d=await runCommand(_0x3382fc[_0x4cc617(0x17a)],_0x3382fc[_0x4cc617(0x1bf)],_0x474336,_0x41fb28,_0x8da83e===_0x4cc617(0x176));if(!_0x3382fc[_0x4cc617(0x1c5)][_0x4cc617(0x17d)](_0x20a20d[_0x4cc617(0x1a6)]))throw new Error(_0x4cc617(0x199));const _0x58e5fb=new Map(_0x1d987d[_0x4cc617(0x1a4)](_0x1ea775=>[[normalizeResultPath(_0x1ea775[_0x4cc617(0x1b0)],this[_0x4cc617(0x1a2)]),_0x1ea775[_0x4cc617(0x1b0)]],[normalizeResultPath(_0x1ea775[_0x4cc617(0x161)],this[_0x4cc617(0x1a2)]),_0x1ea775[_0x4cc617(0x1b0)]]]));_0x58cf6e[_0x4cc617(0x170)](...parseOutput(_0x8da83e,_0x20a20d[_0x4cc617(0x187)],_0x52a2c8)[_0x4cc617(0x1ba)](_0x3d52f1=>({..._0x3d52f1,'path':_0x58e5fb[_0x4cc617(0x174)](normalizeResultPath(_0x3d52f1[_0x4cc617(0x1b0)],this[_0x4cc617(0x1a2)]))??((()=>{const _0x53f2b0=_0x4cc617;throw new Error(_0x53f2b0(0x172));})())})));}return limitPerFile(_0x58cf6e);}}function _0xa3b5(_0x4d031a,_0x25f516){_0x4d031a=_0x4d031a-(-0x2156+-0x9c4+-0x1*-0x2c7b);const _0x4bd822=_0xcfa3();let _0x411ff4=_0x4bd822[_0x4d031a];if(_0xa3b5['wGktoM']===undefined){var _0x324e1e=function(_0x5ac912){const _0x51786d='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=';let _0x41a891='',_0x2a1b16='';for(let _0xd7763c=0x1a*0x16f+0x69b+-0x2be1,_0x53826a,_0x1c9a88,_0x674837=0x173a+-0x6b*-0x15+-0x2001;_0x1c9a88=_0x5ac912['charAt'](_0x674837++);~_0x1c9a88&&(_0x53826a=_0xd7763c%(-0x2091+-0x10ab+0x3140)?_0x53826a*(-0x923+0x2324+-0x13*0x15b)+_0x1c9a88:_0x1c9a88,_0xd7763c++%(-0x2003+-0x408+0x240f*0x1))?_0x41a891+=String['fromCharCode'](-0x19ad*-0x1+0x710*-0x2+-0x2*0x547&_0x53826a>>(-(-0x18cf+0x89f+0x3*0x566)*_0xd7763c&-0x443+-0x32*-0xbb+-0x203d)):-0x3*0xa8e+0x9f7+0x15b3){_0x1c9a88=_0x51786d['indexOf'](_0x1c9a88);}for(let _0x1fc076=0x25ab+-0x1*-0x1235+-0x37e0,_0x3a8678=_0x41a891['length'];_0x1fc076<_0x3a8678;_0x1fc076++){_0x2a1b16+='%'+('00'+_0x41a891['charCodeAt'](_0x1fc076)['toString'](0x227f*-0x1+-0x2c*-0x61+0x1*0x11e3))['slice'](-(0x1192+0xa33+0x45*-0x67));}return decodeURIComponent(_0x2a1b16);};_0xa3b5['cPMxQd']=_0x324e1e,_0xa3b5['kqCtEH']={},_0xa3b5['wGktoM']=!![];}const _0x2bbaf0=_0x4bd822[-0xa4d*-0x2+-0x2066+0xbcc];_0xa3b5['omLRff']!==_0x2bbaf0&&(_0xa3b5['kqCtEH']={},_0xa3b5['omLRff']=_0x2bbaf0);const _0x4c40f6=_0xa3b5['kqCtEH'][_0x4d031a];return _0x4c40f6===undefined?(_0x411ff4=_0xa3b5['cPMxQd'](_0x411ff4),_0xa3b5['kqCtEH'][_0x4d031a]=_0x411ff4):_0x411ff4=_0x4c40f6,_0x411ff4;}async function validateCandidate(_0x25eec4,_0x35780a){const _0x236e26=_0x362cb8;try{const _0x3a5d86=await realpath(join(_0x25eec4,_0x35780a)),_0x2486c9=relative(_0x25eec4,_0x3a5d86);if(_0x2486c9==='..'||_0x2486c9[_0x236e26(0x185)]('..'+sep)||isAbsolute(_0x2486c9))return undefined;const _0x3183bb=await lstat(_0x3a5d86);return _0x3183bb[_0x236e26(0x1c7)]()&&_0x3183bb[_0x236e26(0x1aa)]<=FILE_BYTE_LIMIT?{'path':_0x35780a,'absolutePath':_0x3a5d86,'size':_0x3183bb[_0x236e26(0x1aa)]}:undefined;}catch{return undefined;}}async function beforeDeadline(_0x21db80,_0xcfb4d8){return new Promise((_0x740b31,_0x2fe504)=>{const _0x37e8f9=_0xa3b5,_0x537304=setTimeout(()=>_0x2fe504(new Error(_0x37e8f9(0x1a0))),Math[_0x37e8f9(0x169)](0x11*0x19f+0x1*-0x184+0x12f*-0x16,_0xcfb4d8-Date[_0x37e8f9(0x1b5)]()));_0x21db80[_0x37e8f9(0x178)](_0x1366d0=>{clearTimeout(_0x537304),_0x740b31(_0x1366d0);},_0x583ba6=>{clearTimeout(_0x537304),_0x2fe504(_0x583ba6);});});}function _0xcfa3(){const _0x2b0ac0=['ovLAywTXsW','DMfSAwrdB2rLCW','CMvWBgfJzq','AxngAwXL','ywjZB2X1DgvqyxrO','BgvUz3rO','CxvLCNK','Bwf0y2G','BgLUzxm','nKzYBeHmza','C3bSAxq','ls1UBY1JB25MAwC','Bwf4','Dg9mB2nHBgvmB3DLCKnHC2u','C2HPzNq','ywXSB2m','mZu4nZm4sKjgBMPf','Dhj1BMnHDgvK','C2TPChbLzezPBgvZ','ChvZAa','nJm2mJuWD0jJtw9p','q09ovevovf9trufsq0HFt1vuufvux0LovKfmsuq','DxrMltG','z2v0','rKLmrv9dt05uru5ux1nfqvjdsf9jtLzbteLe','uKLqr1jfua','zNjVBq','DgHLBG','C3bSAwnL','zMLSzq','mtG5nZq3uuPdyw9f','rKLmrv9dt05uru5ux1nfqvjdsf9cvvnz','Aw5JBhvKzxm','ls1QC29U','AwDUB3jL','CgLWzq','y29Uy2f0','mtKWmJm2BxvXqufp','zgvJB2rL','D2LUmZi','C3rHCNrZv2L0Aa','mtCWwe9ryKTp','C3rKB3v0','zgvSzxrL','zMLSDgvY','y29UDgvUDenHBMrPzgf0zxm','Dg9tDhjPBMC','zw50CMLLCW','DxrMoa','mZmWotK2ngvmBev0Ba','y2fUzgLKyxrLCW','Dgv4Da','ChjLDMLLDW','zgf0yq','ywXS','BMf0AxzLu2vHCMnO','BgLUzq','A2LSBa','zxjYB3i','tK9erq','q09ovevovf9trufsq0HFq09ntuforf9gquLmruq','zMLUywXSEq','zMLSzxm','ls1TyxGTy291BNq','CgfYC2u','iNr5CguIoIjTyxrJAci','y2XVC2u','q09ovevovf9trufsq0HFveLnru9vva','CMLWz3jLCa','CgXHDgzVCM0','ls1Pz25VCMuTy2fZzq','zMXHDe1HCa','BwvZC2fNzq','y29Kzq','zw5KC1DPDgG','q09ovevovf9trufsq0HFru5hsu5fx1vou1vque9sveve','Aw5KzxHpzG','C2L6zq','yNL0zuXLBMD0Aa','C2XPy2u','ls1MAxHLzc1ZDhjPBMDZ','q09ovevovf9trufsq0HFq09ntuforf9msu1jva','CgvYzM9YBq','Cgf0Aa','C3vIyxjYyxK','AxnjBNrLz2vY','Bwf0y2HLCW','BgLUzv9UDw1Izxi','BM93','r1jfua','CMvWBgfJzufSBa','B25Jzq','mtG4odyYmfHHBgrjwa','BwfW','mJm4mZCXmMrHyMfZyW','C3rYAw5N','C2v0','C2vHCMnO','yxjNCW','CgvUzgLUzW','DhLWzq','uKLqr1jfuf9srvnpvvjdrv9vtKfwquLmqujmrq','Aw5KzxG'];_0xcfa3=function(){return _0x2b0ac0;};return _0xcfa3();}function normalizeResultPath(_0x2d1adc,_0x5cc9b7){const _0x488c38=_0x362cb8,_0x502bdb=_0x2d1adc[_0x488c38(0x1b7)]('\x5c','/');return _0x5cc9b7===_0x488c38(0x184)?_0x502bdb[_0x488c38(0x16a)]():_0x502bdb;}function batches(_0x3d3d42,_0x2162f0){const _0x3adaf7=_0x362cb8,_0x6fc762=[];let _0x8ea58c=[],_0x4aa9fa=0x1*-0x161b+-0x1944+-0x2f5f*-0x1;for(const _0x526ea7 of _0x3d3d42){const _0x5acca5=Buffer[_0x3adaf7(0x1ab)](_0x526ea7[_0x3adaf7(0x1b0)])+(-0x860+0x1*0xd9e+-0x53d);_0x8ea58c[_0x3adaf7(0x162)]>-0x1ea8+0x582*0x5+0x31e&&_0x4aa9fa+_0x5acca5>_0x2162f0&&(_0x6fc762[_0x3adaf7(0x170)](_0x8ea58c),_0x8ea58c=[],_0x4aa9fa=-0x3f*0x80+-0x2*-0x112c+-0x34*0xe),_0x8ea58c[_0x3adaf7(0x170)](_0x526ea7),_0x4aa9fa+=_0x5acca5;}if(_0x8ea58c[_0x3adaf7(0x162)]>0x3e3*-0x4+0x786+0x806)_0x6fc762[_0x3adaf7(0x170)](_0x8ea58c);return _0x6fc762;}function commandFor(_0x572d90,_0x5c3254,_0x4841f4,_0x539045){const _0x58d564=_0x362cb8,_0x47f072=_0x5c3254[_0x58d564(0x1ba)](_0x1981f2=>_0x1981f2[_0x58d564(0x1b0)]);if(_0x572d90===_0x58d564(0x176))return{'file':_0x539045??((()=>{const _0x47b190=_0x58d564;throw new Error(_0x47b190(0x1c2));})()),'args':[_0x58d564(0x17e),_0x58d564(0x168),_0x58d564(0x1ad),_0x58d564(0x1a3),_0x58d564(0x19c),String(PER_FILE_LIMIT),'--',_0x4841f4,..._0x47f072],'validCodes':[-0x262f+-0x1b53+0xd*0x50a,-0x2e7+-0x2*0xbba+-0x7*-0x3c4]};throw new Error(_0x58d564(0x1a8));}function resolveRipgrep(){try{return bundledRipgrepPath();}catch{return null;}}async function runCommand(_0x120545,_0x422db2,_0x4ce424,_0xac7b7d,_0x120fd1=![]){return new Promise((_0x5f1916,_0x2b28b0)=>{const _0x31d74f=_0xa3b5,_0x349580=spawn(_0x120545,_0x422db2,{'cwd':_0x4ce424,'windowsHide':!![],'stdio':[_0x31d74f(0x17f),_0x31d74f(0x180),_0x31d74f(0x17f)]}),_0x2a10fa=[];let _0x16c75b=Buffer[_0x31d74f(0x16c)](-0xb63*0x1+-0x8e4+0x1447),_0x4a3f6=0x359*-0x8+0x2306+-0x83e*0x1,_0x2d8bc5=-0x1ce*-0xe+-0x1be2+0x29e,_0x499266=![];const _0x342197=setTimeout(()=>_0x349580[_0x31d74f(0x196)](),Math[_0x31d74f(0x169)](0x34a*0x7+0xf27+-0x262c,_0xac7b7d-Date[_0x31d74f(0x1b5)]()));_0x349580[_0x31d74f(0x1b8)](_0x31d74f(0x197),_0x4f079d=>{clearTimeout(_0x342197),_0x2b28b0(_0x4f079d);}),_0x349580[_0x31d74f(0x187)]['on'](_0x31d74f(0x192),_0x5ef9c9=>{const _0x494d5c=_0x31d74f;_0x4a3f6+=_0x5ef9c9[_0x494d5c(0x162)];if(_0x4a3f6>OUTPUT_BYTE_LIMIT){_0x349580[_0x494d5c(0x196)]();return;}if(!_0x120fd1){_0x2a10fa[_0x494d5c(0x170)](_0x5ef9c9);return;}_0x16c75b=Buffer[_0x494d5c(0x181)]([_0x16c75b,_0x5ef9c9]);let _0x395f13=_0x16c75b[_0x494d5c(0x1a9)](0x2*-0xe90+-0x33d*-0x1+0x19ed);while(_0x395f13>=0x206f+-0x1b59+0x2*-0x28b){const _0x5780bf=_0x16c75b[_0x494d5c(0x1b1)](0x1*-0x217b+-0x224*-0xd+0x5a7*0x1,_0x395f13+(0x1aa2+-0x1*-0x6ad+-0x214e));_0x2a10fa[_0x494d5c(0x170)](_0x5780bf);if(_0x5780bf[_0x494d5c(0x17d)](Buffer[_0x494d5c(0x177)](_0x494d5c(0x19e))))_0x2d8bc5+=0x60f+-0x11e2+0xbd4;_0x16c75b=_0x16c75b[_0x494d5c(0x1b1)](_0x395f13+(0x1c71*-0x1+-0x1792+-0xd01*-0x4));if(_0x2d8bc5>RESULT_LIMIT){_0x499266=!![],_0x349580[_0x494d5c(0x196)]();return;}_0x395f13=_0x16c75b[_0x494d5c(0x1a9)](-0x1e24+-0x526+0x2354);}}),_0x349580[_0x31d74f(0x1b8)](_0x31d74f(0x19f),(_0x506792,_0x3450f5)=>{const _0x241d66=_0x31d74f;clearTimeout(_0x342197);if(_0x499266)_0x5f1916({'code':0x0,'stdout':Buffer[_0x241d66(0x181)](_0x2a10fa)});else{if(_0x3450f5!==null||_0x4a3f6>OUTPUT_BYTE_LIMIT)_0x2b28b0(new Error(_0x241d66(0x1ae)));else{if(_0x120fd1&&_0x16c75b[_0x241d66(0x162)]>-0x8d*-0x11+-0x1dde+-0x1d*-0xb5)_0x2a10fa[_0x241d66(0x170)](_0x16c75b);_0x5f1916({'code':_0x506792??-(0x7fb+-0x2660+-0x1*-0x1e66),'stdout':Buffer[_0x241d66(0x181)](_0x2a10fa)});}}});});}function parseOutput(_0x396043,_0x398a4a,_0x2c6217){const _0x257e66=_0x362cb8;if(_0x396043===_0x257e66(0x176))return parseRipgrep(_0x398a4a,_0x2c6217);if(_0x396043===_0x257e66(0x1b6))return parseGrep(_0x398a4a,_0x2c6217);return parsePowerShell(_0x398a4a,_0x2c6217);}function parseRipgrep(_0x41c93a,_0x2fb6e2){const _0x5d426b=_0x362cb8;return _0x41c93a[_0x5d426b(0x18b)](_0x5d426b(0x18d))[_0x5d426b(0x167)]('\x0a')[_0x5d426b(0x1a4)](_0x214b91=>{const _0x54ba92=_0x5d426b;if(_0x214b91==='')return[];const _0xcee3bd=JSON[_0x54ba92(0x19d)](_0x214b91);if(_0xcee3bd[_0x54ba92(0x1c1)]!==_0x54ba92(0x164))return[];const _0x211305=_0xcee3bd[_0x54ba92(0x192)],_0x2d67b2=String(_0x211305?.[_0x54ba92(0x165)]?.[_0x54ba92(0x190)])[_0x54ba92(0x1c6)](/[\r\n]+$/u,''),_0x388df5=lowerIndex(_0x2d67b2,_0x2fb6e2);if(typeof _0x211305?.[_0x54ba92(0x1b0)]?.[_0x54ba92(0x190)]!==_0x54ba92(0x1bc)||!Number[_0x54ba92(0x1b2)](_0x211305[_0x54ba92(0x1b4)])||_0x388df5<0x1*-0x1a1b+-0x1707+0x3122)throw new Error(_0x54ba92(0x172));return[{'path':_0x211305[_0x54ba92(0x1b0)][_0x54ba92(0x190)],'line':_0x211305[_0x54ba92(0x1b4)],'column':_0x388df5+(-0x16df+-0x2*-0x145+0x1456),'preview':_0x2d67b2[_0x54ba92(0x1ac)](-0xda*-0x14+-0x19*0xb2+-0x5*-0x12,0x2e29+-0x1669*-0x1+0x97*-0x3e)}];});}function parseGrep(_0x530003,_0x495212){const _0x3824f3=_0x362cb8,_0xa64092=[];let _0x2f2cbe=0x3de*-0x1+-0x2f*-0x16+-0x2c;while(_0x2f2cbe<_0x530003[_0x3824f3(0x162)]){const _0x54c47b=_0x530003[_0x3824f3(0x1a9)](0xf37+0x2049+-0x2f80,_0x2f2cbe),_0x3dbe7f=_0x530003[_0x3824f3(0x1a9)](0x1*0x1dcb+0x2221*-0x1+0x50*0xe,_0x54c47b+(-0x1c39+0x1*-0x2603+0x423d));if(_0x54c47b<-0x1c1c+0x61*-0x3f+0x33fb||_0x3dbe7f<0x1f4a+0x24f3+-0x443d)throw new Error(_0x3824f3(0x172));const _0x5a2d08=_0x530003[_0x3824f3(0x1b1)](_0x2f2cbe,_0x54c47b)[_0x3824f3(0x18b)](_0x3824f3(0x18d)),_0x3798ba=_0x530003[_0x3824f3(0x1b1)](_0x54c47b+(0x1354+-0x22a6+0xf53),_0x3dbe7f)[_0x3824f3(0x18b)](_0x3824f3(0x18d)),_0x14203b=_0x3798ba[_0x3824f3(0x1a9)](':'),_0x5f4dae=_0x3798ba[_0x3824f3(0x1ac)](_0x14203b+(0x337*0x1+0x1*-0x1e2f+0x1af9*0x1))[_0x3824f3(0x1c6)](/\r$/u,''),_0x509c60=lowerIndex(_0x5f4dae,_0x495212);if(_0x14203b<0x59c*-0x2+-0x1*0x23ef+0x2f28||_0x509c60<0x26e5*0x1+-0x959*-0x4+-0x3b*0x14b)throw new Error(_0x3824f3(0x172));_0xa64092[_0x3824f3(0x170)]({'path':_0x5a2d08,'line':Number(_0x3798ba[_0x3824f3(0x1ac)](-0x7*-0x442+-0x1afd+-0x2d1,_0x14203b)),'column':_0x509c60+(-0x26*0xa7+-0x106c+0x1*0x2937),'preview':_0x5f4dae[_0x3824f3(0x1ac)](-0x2a6+-0x1b00*0x1+0x1da6,0xc7a*0x2+-0x734+0xe40)}),_0x2f2cbe=_0x3dbe7f+(0x222c+0x23d4+-0x45ff);}return _0xa64092;}function parsePowerShell(_0x5be5dc,_0x3b6379){const _0x4869a8=_0x362cb8;return _0x5be5dc[_0x4869a8(0x18b)](_0x4869a8(0x18d))[_0x4869a8(0x167)](/\r?\n/u)[_0x4869a8(0x1a4)](_0x4fb568=>{const _0x117f46=_0x4869a8;if(_0x4fb568==='')return[];const _0xd62d4b=JSON[_0x117f46(0x19d)](_0x4fb568);if(typeof _0xd62d4b[_0x117f46(0x1b0)]!==_0x117f46(0x1bc)||!Number[_0x117f46(0x1b2)](_0xd62d4b[_0x117f46(0x195)])||typeof _0xd62d4b[_0x117f46(0x191)]!==_0x117f46(0x1bc))throw new Error(_0x117f46(0x172));const _0x49ef35=lowerIndex(_0xd62d4b[_0x117f46(0x191)],_0x3b6379);if(_0x49ef35<0x130d+0x42*0x52+-0x1*0x2831)throw new Error(_0x117f46(0x172));return[{'path':_0xd62d4b[_0x117f46(0x1b0)],'line':_0xd62d4b[_0x117f46(0x195)],'column':_0x49ef35+(0xb*-0x364+-0x1e6*0x1+0x2733),'preview':_0xd62d4b[_0x117f46(0x191)][_0x117f46(0x1ac)](-0x1b02*-0x1+0x10e0+-0x2be2,0x154c+-0x7*-0x167+0xe3)}];});}async function nodeSearch(_0x4df1b8,_0x34c970,_0x18b442){const _0xf28069=_0x362cb8,_0x43c5c5=[];let _0x36206e=0x2609+0x1d*-0xef+-0xaf6;for(const _0x5d6ecc of _0x4df1b8){if(Date[_0xf28069(0x1b5)]()>=_0x18b442||_0x43c5c5[_0xf28069(0x162)]>RESULT_LIMIT)break;const _0x32c0b4=[],_0x2f6806=createReadStream(_0x5d6ecc[_0xf28069(0x161)]),_0x537152=new TextDecoder(_0xf28069(0x173),{'fatal':!![]});let _0x586d25='',_0x590dc8=-0x14cd+0x9d5*-0x2+0x1*0x2877,_0x26e8f9=-0x8*-0x219+0x941+-0x1a09;const _0x1ce1de=_0x5ec825=>{const _0x459b04=_0xf28069;_0x590dc8+=0xc87+-0x21*-0x4d+0x7*-0x335;const _0x5ac711=_0x5ec825[_0x459b04(0x1a7)]('\x0d')?_0x5ec825[_0x459b04(0x1ac)](0xcf*0x1c+-0xf95+-0x8b*0xd,-(0xb*0x223+-0x539+0x1247*-0x1)):_0x5ec825,_0x1611d3=lowerIndex(_0x5ac711,_0x34c970);return _0x1611d3>=-0x11bb*-0x2+0xa*0x1b1+-0x3460&&(_0x32c0b4[_0x459b04(0x170)]({'path':_0x5d6ecc[_0x459b04(0x1b0)],'line':_0x590dc8,'column':_0x1611d3+(-0x1778+0xd7b*-0x2+0x326f),'preview':_0x5ac711[_0x459b04(0x1ac)](-0x13a5+0x1*0x1b7f+0x1*-0x7da,-0x2c49+-0xc99+0x58e2)}),_0x26e8f9+=0xf97+-0xe25+-0x171),_0x26e8f9>=PER_FILE_LIMIT||_0x43c5c5[_0x459b04(0x162)]+_0x32c0b4[_0x459b04(0x162)]>RESULT_LIMIT||Date[_0x459b04(0x1b5)]()>=_0x18b442;};try{let _0x2b8f97=![];for await(const _0x26fa2f of _0x2f6806){_0x586d25+=_0x537152[_0xf28069(0x183)](_0x26fa2f,{'stream':!![]});let _0x4501cc=_0x586d25[_0xf28069(0x1a9)]('\x0a');while(_0x4501cc>=-0x1*0x526+0xed+0x439){if(_0x1ce1de(_0x586d25[_0xf28069(0x1ac)](-0x18da+-0x1*0x1b97+-0x3*-0x117b,_0x4501cc))){_0x2b8f97=!![];break;}_0x586d25=_0x586d25[_0xf28069(0x1ac)](_0x4501cc+(-0x158f+-0x1311+-0x3*-0xd8b)),_0x4501cc=_0x586d25[_0xf28069(0x1a9)]('\x0a');}if(_0x2b8f97)break;}if(!_0x2b8f97){_0x586d25+=_0x537152[_0xf28069(0x183)]();if(_0x586d25!=='')_0x1ce1de(_0x586d25);}_0x43c5c5[_0xf28069(0x170)](..._0x32c0b4);}catch{_0x36206e+=-0x676+0x1f*-0x77+0x2*0xa70;}}return{'matches':_0x43c5c5,'skippedFiles':_0x36206e};}function lowerIndex(_0x1c2aa1,_0x4d5be2){const _0x3c48a7=_0x362cb8;return _0x1c2aa1[_0x3c48a7(0x16a)]()[_0x3c48a7(0x1a9)](_0x4d5be2[_0x3c48a7(0x16a)]());}function limitPerFile(_0x560b81){const _0x1a3070=_0x362cb8,_0x436fbe=new Map();return _0x560b81[_0x1a3070(0x189)](_0x2dedd2=>{const _0x241668=_0x1a3070,_0x1010a9=_0x436fbe[_0x241668(0x174)](_0x2dedd2[_0x241668(0x1b0)])??-0x1*-0xb57+-0xd*0x296+-0x3*-0x76d;if(_0x1010a9>=PER_FILE_LIMIT)return![];return _0x436fbe[_0x241668(0x1bd)](_0x2dedd2[_0x241668(0x1b0)],_0x1010a9+(0x12*-0x95+0x1d66+0x12eb*-0x1)),!![];});}
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import { createReadStream } from 'node:fs';
|
|
3
|
+
import { lstat, realpath } from 'node:fs/promises';
|
|
4
|
+
import { isAbsolute, join, relative, sep } from 'node:path';
|
|
5
|
+
import { bundledRipgrepPath } from '../ripgrep-binary.js';
|
|
6
|
+
const RESULT_LIMIT = 200;
|
|
7
|
+
const PER_FILE_LIMIT = 20;
|
|
8
|
+
const FILE_BYTE_LIMIT = 2 * 1024 * 1024;
|
|
9
|
+
const SCAN_BYTE_LIMIT = 128 * 1024 * 1024;
|
|
10
|
+
const OUTPUT_BYTE_LIMIT = 2 * 1024 * 1024;
|
|
11
|
+
const TIMEOUT_MS = 5_000;
|
|
12
|
+
const WINDOWS_ARGUMENT_BATCH_BYTES = 24 * 1024;
|
|
13
|
+
const POSIX_ARGUMENT_BATCH_BYTES = 96 * 1024;
|
|
14
|
+
const SEARCH_CONCURRENCY = 2;
|
|
15
|
+
const SEARCH_QUEUE_LIMIT = 16;
|
|
16
|
+
const CANDIDATE_VALIDATION_CONCURRENCY = 32;
|
|
17
|
+
let activeSearches = 0;
|
|
18
|
+
const searchWaiters = [];
|
|
19
|
+
async function acquireSearch(deadline) {
|
|
20
|
+
if (activeSearches >= SEARCH_CONCURRENCY) {
|
|
21
|
+
if (searchWaiters.length >= SEARCH_QUEUE_LIMIT)
|
|
22
|
+
throw new Error('FILE_CONTENT_SEARCH_BUSY');
|
|
23
|
+
await new Promise((resolve, reject) => {
|
|
24
|
+
const resume = () => {
|
|
25
|
+
clearTimeout(timer);
|
|
26
|
+
resolve();
|
|
27
|
+
};
|
|
28
|
+
const timer = setTimeout(() => {
|
|
29
|
+
const index = searchWaiters.indexOf(resume);
|
|
30
|
+
if (index >= 0)
|
|
31
|
+
searchWaiters.splice(index, 1);
|
|
32
|
+
reject(new Error('CONTENT_SEARCH_TIMEOUT'));
|
|
33
|
+
}, Math.max(1, deadline - Date.now()));
|
|
34
|
+
searchWaiters.push(resume);
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
activeSearches += 1;
|
|
38
|
+
return () => {
|
|
39
|
+
activeSearches -= 1;
|
|
40
|
+
searchWaiters.shift()?.();
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
export class WorkspaceContentSearchService {
|
|
44
|
+
files;
|
|
45
|
+
platform;
|
|
46
|
+
ripgrep;
|
|
47
|
+
pending = new Map();
|
|
48
|
+
constructor(files, platform = process.platform, ripgrep = resolveRipgrep()) {
|
|
49
|
+
this.files = files;
|
|
50
|
+
this.platform = platform;
|
|
51
|
+
this.ripgrep = ripgrep;
|
|
52
|
+
}
|
|
53
|
+
search(workspace, input) {
|
|
54
|
+
if (typeof input.query !== 'string' || input.query.length < 1 || input.query.length > 256)
|
|
55
|
+
throw new Error('FILE_CONTENT_SEARCH_INVALID');
|
|
56
|
+
const key = `${workspace.id}\0${input.query.toLocaleLowerCase()}`;
|
|
57
|
+
const existing = this.pending.get(key);
|
|
58
|
+
if (existing !== undefined)
|
|
59
|
+
return existing;
|
|
60
|
+
const task = this.perform(workspace, input.query).finally(() => this.pending.delete(key));
|
|
61
|
+
this.pending.set(key, task);
|
|
62
|
+
return task;
|
|
63
|
+
}
|
|
64
|
+
async perform(workspace, query) {
|
|
65
|
+
const deadline = Date.now() + TIMEOUT_MS;
|
|
66
|
+
const release = await acquireSearch(deadline);
|
|
67
|
+
try {
|
|
68
|
+
const prepared = await this.candidates(workspace, deadline);
|
|
69
|
+
if (prepared.files.length === 0)
|
|
70
|
+
return {
|
|
71
|
+
matches: [],
|
|
72
|
+
engine: 'NODE',
|
|
73
|
+
truncated: prepared.truncated || Date.now() >= deadline,
|
|
74
|
+
skippedFiles: prepared.skippedFiles
|
|
75
|
+
};
|
|
76
|
+
if (this.ripgrep !== null) {
|
|
77
|
+
try {
|
|
78
|
+
const matches = await this.nativeSearch('RIPGREP', workspace.path, prepared.files, query, deadline);
|
|
79
|
+
return {
|
|
80
|
+
matches: matches.slice(0, RESULT_LIMIT),
|
|
81
|
+
engine: 'RIPGREP',
|
|
82
|
+
truncated: prepared.truncated || matches.length > RESULT_LIMIT || Date.now() >= deadline,
|
|
83
|
+
skippedFiles: prepared.skippedFiles
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
// Missing, incompatible or failed bundled resources safely fall through.
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
const fallback = await nodeSearch(prepared.files, query, deadline);
|
|
91
|
+
return {
|
|
92
|
+
matches: fallback.matches.slice(0, RESULT_LIMIT),
|
|
93
|
+
engine: 'NODE',
|
|
94
|
+
truncated: prepared.truncated || fallback.matches.length > RESULT_LIMIT || Date.now() >= deadline,
|
|
95
|
+
skippedFiles: prepared.skippedFiles + fallback.skippedFiles
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
finally {
|
|
99
|
+
release();
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
async candidates(workspace, deadline) {
|
|
103
|
+
const root = await realpath(workspace.path);
|
|
104
|
+
let candidates;
|
|
105
|
+
try {
|
|
106
|
+
candidates = await beforeDeadline(this.files.index(workspace).contentCandidates(), deadline);
|
|
107
|
+
}
|
|
108
|
+
catch (error) {
|
|
109
|
+
if (error instanceof Error && error.message === 'CONTENT_SEARCH_TIMEOUT')
|
|
110
|
+
return { files: [], skippedFiles: 0, truncated: true };
|
|
111
|
+
throw error;
|
|
112
|
+
}
|
|
113
|
+
const files = [];
|
|
114
|
+
let skippedFiles = 0;
|
|
115
|
+
let bytes = 0;
|
|
116
|
+
let truncated = candidates.truncated;
|
|
117
|
+
for (let offset = 0; offset < candidates.entries.length; offset += CANDIDATE_VALIDATION_CONCURRENCY) {
|
|
118
|
+
if (Date.now() >= deadline) {
|
|
119
|
+
truncated = true;
|
|
120
|
+
break;
|
|
121
|
+
}
|
|
122
|
+
const validated = await Promise.all(candidates.entries
|
|
123
|
+
.slice(offset, offset + CANDIDATE_VALIDATION_CONCURRENCY)
|
|
124
|
+
.map((entry) => validateCandidate(root, entry.path)));
|
|
125
|
+
for (const candidate of validated) {
|
|
126
|
+
if (candidate === undefined) {
|
|
127
|
+
skippedFiles += 1;
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
if (bytes + candidate.size > SCAN_BYTE_LIMIT) {
|
|
131
|
+
truncated = true;
|
|
132
|
+
break;
|
|
133
|
+
}
|
|
134
|
+
bytes += candidate.size;
|
|
135
|
+
files.push({ path: candidate.path, absolutePath: candidate.absolutePath });
|
|
136
|
+
}
|
|
137
|
+
if (truncated)
|
|
138
|
+
break;
|
|
139
|
+
}
|
|
140
|
+
return { files, skippedFiles, truncated };
|
|
141
|
+
}
|
|
142
|
+
async nativeSearch(engine, root, files, query, deadline) {
|
|
143
|
+
const matches = [];
|
|
144
|
+
const argumentBatchBytes = this.platform === 'win32' ? WINDOWS_ARGUMENT_BATCH_BYTES : POSIX_ARGUMENT_BATCH_BYTES;
|
|
145
|
+
for (const batch of batches(files, argumentBatchBytes)) {
|
|
146
|
+
if (Date.now() >= deadline || matches.length > RESULT_LIMIT)
|
|
147
|
+
break;
|
|
148
|
+
const command = commandFor(engine, batch, query, this.ripgrep ?? undefined);
|
|
149
|
+
const result = await runCommand(command.file, command.args, root, deadline, engine === 'RIPGREP');
|
|
150
|
+
if (!command.validCodes.includes(result.code))
|
|
151
|
+
throw new Error('CONTENT_SEARCH_COMMAND_FAILED');
|
|
152
|
+
const paths = new Map(batch.flatMap((file) => [
|
|
153
|
+
[normalizeResultPath(file.path, this.platform), file.path],
|
|
154
|
+
[normalizeResultPath(file.absolutePath, this.platform), file.path]
|
|
155
|
+
]));
|
|
156
|
+
matches.push(...parseOutput(engine, result.stdout, query).map((match) => ({
|
|
157
|
+
...match,
|
|
158
|
+
path: paths.get(normalizeResultPath(match.path, this.platform)) ??
|
|
159
|
+
(() => {
|
|
160
|
+
throw new Error('CONTENT_SEARCH_OUTPUT_INVALID');
|
|
161
|
+
})()
|
|
162
|
+
})));
|
|
163
|
+
}
|
|
164
|
+
return limitPerFile(matches);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
async function validateCandidate(root, path) {
|
|
168
|
+
try {
|
|
169
|
+
const absolutePath = await realpath(join(root, path));
|
|
170
|
+
const inside = relative(root, absolutePath);
|
|
171
|
+
if (inside === '..' || inside.startsWith(`..${sep}`) || isAbsolute(inside))
|
|
172
|
+
return undefined;
|
|
173
|
+
const metadata = await lstat(absolutePath);
|
|
174
|
+
return metadata.isFile() && metadata.size <= FILE_BYTE_LIMIT
|
|
175
|
+
? { path, absolutePath, size: metadata.size }
|
|
176
|
+
: undefined;
|
|
177
|
+
}
|
|
178
|
+
catch {
|
|
179
|
+
return undefined;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
async function beforeDeadline(promise, deadline) {
|
|
183
|
+
return new Promise((resolve, reject) => {
|
|
184
|
+
const timer = setTimeout(() => reject(new Error('CONTENT_SEARCH_TIMEOUT')), Math.max(1, deadline - Date.now()));
|
|
185
|
+
promise.then((value) => {
|
|
186
|
+
clearTimeout(timer);
|
|
187
|
+
resolve(value);
|
|
188
|
+
}, (error) => {
|
|
189
|
+
clearTimeout(timer);
|
|
190
|
+
reject(error);
|
|
191
|
+
});
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
function normalizeResultPath(path, platform) {
|
|
195
|
+
const normalized = path.replaceAll('\\', '/');
|
|
196
|
+
return platform === 'win32' ? normalized.toLocaleLowerCase() : normalized;
|
|
197
|
+
}
|
|
198
|
+
function batches(files, maximumBytes) {
|
|
199
|
+
const result = [];
|
|
200
|
+
let current = [];
|
|
201
|
+
let bytes = 0;
|
|
202
|
+
for (const file of files) {
|
|
203
|
+
const size = Buffer.byteLength(file.path) + 1;
|
|
204
|
+
if (current.length > 0 && bytes + size > maximumBytes) {
|
|
205
|
+
result.push(current);
|
|
206
|
+
current = [];
|
|
207
|
+
bytes = 0;
|
|
208
|
+
}
|
|
209
|
+
current.push(file);
|
|
210
|
+
bytes += size;
|
|
211
|
+
}
|
|
212
|
+
if (current.length > 0)
|
|
213
|
+
result.push(current);
|
|
214
|
+
return result;
|
|
215
|
+
}
|
|
216
|
+
function commandFor(engine, files, query, ripgrep) {
|
|
217
|
+
const paths = files.map((file) => file.path);
|
|
218
|
+
if (engine === 'RIPGREP')
|
|
219
|
+
return {
|
|
220
|
+
file: ripgrep ??
|
|
221
|
+
(() => {
|
|
222
|
+
throw new Error('RIPGREP_RESOURCE_UNAVAILABLE');
|
|
223
|
+
})(),
|
|
224
|
+
args: [
|
|
225
|
+
'--json',
|
|
226
|
+
'--no-config',
|
|
227
|
+
'--fixed-strings',
|
|
228
|
+
'--ignore-case',
|
|
229
|
+
'--max-count',
|
|
230
|
+
String(PER_FILE_LIMIT),
|
|
231
|
+
'--',
|
|
232
|
+
query,
|
|
233
|
+
...paths
|
|
234
|
+
],
|
|
235
|
+
validCodes: [0, 1]
|
|
236
|
+
};
|
|
237
|
+
throw new Error('CONTENT_SEARCH_ENGINE_UNSUPPORTED');
|
|
238
|
+
}
|
|
239
|
+
function resolveRipgrep() {
|
|
240
|
+
try {
|
|
241
|
+
return bundledRipgrepPath();
|
|
242
|
+
}
|
|
243
|
+
catch {
|
|
244
|
+
return null;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
async function runCommand(file, args, cwd, deadline, stopAfterResultLimit = false) {
|
|
248
|
+
return new Promise((resolve, reject) => {
|
|
249
|
+
const child = spawn(file, args, {
|
|
250
|
+
cwd,
|
|
251
|
+
windowsHide: true,
|
|
252
|
+
stdio: ['ignore', 'pipe', 'ignore']
|
|
253
|
+
});
|
|
254
|
+
const chunks = [];
|
|
255
|
+
let pending = Buffer.alloc(0);
|
|
256
|
+
let bytes = 0;
|
|
257
|
+
let matches = 0;
|
|
258
|
+
let stopped = false;
|
|
259
|
+
const timer = setTimeout(() => child.kill(), Math.max(1, deadline - Date.now()));
|
|
260
|
+
child.once('error', (error) => {
|
|
261
|
+
clearTimeout(timer);
|
|
262
|
+
reject(error);
|
|
263
|
+
});
|
|
264
|
+
child.stdout.on('data', (chunk) => {
|
|
265
|
+
bytes += chunk.length;
|
|
266
|
+
if (bytes > OUTPUT_BYTE_LIMIT) {
|
|
267
|
+
child.kill();
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
if (!stopAfterResultLimit) {
|
|
271
|
+
chunks.push(chunk);
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
pending = Buffer.concat([pending, chunk]);
|
|
275
|
+
let newline = pending.indexOf(0x0a);
|
|
276
|
+
while (newline >= 0) {
|
|
277
|
+
const record = pending.subarray(0, newline + 1);
|
|
278
|
+
chunks.push(record);
|
|
279
|
+
if (record.includes(Buffer.from('"type":"match"')))
|
|
280
|
+
matches += 1;
|
|
281
|
+
pending = pending.subarray(newline + 1);
|
|
282
|
+
if (matches > RESULT_LIMIT) {
|
|
283
|
+
stopped = true;
|
|
284
|
+
child.kill();
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
newline = pending.indexOf(0x0a);
|
|
288
|
+
}
|
|
289
|
+
});
|
|
290
|
+
child.once('close', (code, signal) => {
|
|
291
|
+
clearTimeout(timer);
|
|
292
|
+
if (stopped)
|
|
293
|
+
resolve({ code: 0, stdout: Buffer.concat(chunks) });
|
|
294
|
+
else if (signal !== null || bytes > OUTPUT_BYTE_LIMIT)
|
|
295
|
+
reject(new Error('CONTENT_SEARCH_COMMAND_LIMIT'));
|
|
296
|
+
else {
|
|
297
|
+
if (stopAfterResultLimit && pending.length > 0)
|
|
298
|
+
chunks.push(pending);
|
|
299
|
+
resolve({ code: code ?? -1, stdout: Buffer.concat(chunks) });
|
|
300
|
+
}
|
|
301
|
+
});
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
function parseOutput(engine, output, query) {
|
|
305
|
+
if (engine === 'RIPGREP')
|
|
306
|
+
return parseRipgrep(output, query);
|
|
307
|
+
if (engine === 'GREP')
|
|
308
|
+
return parseGrep(output, query);
|
|
309
|
+
return parsePowerShell(output, query);
|
|
310
|
+
}
|
|
311
|
+
function parseRipgrep(output, query) {
|
|
312
|
+
return output
|
|
313
|
+
.toString('utf8')
|
|
314
|
+
.split('\n')
|
|
315
|
+
.flatMap((line) => {
|
|
316
|
+
if (line === '')
|
|
317
|
+
return [];
|
|
318
|
+
const value = JSON.parse(line);
|
|
319
|
+
if (value.type !== 'match')
|
|
320
|
+
return [];
|
|
321
|
+
const data = value.data;
|
|
322
|
+
const preview = String(data?.lines?.text).replace(/[\r\n]+$/u, '');
|
|
323
|
+
const column = lowerIndex(preview, query);
|
|
324
|
+
if (typeof data?.path?.text !== 'string' || !Number.isInteger(data.line_number) || column < 0)
|
|
325
|
+
throw new Error('CONTENT_SEARCH_OUTPUT_INVALID');
|
|
326
|
+
return [
|
|
327
|
+
{
|
|
328
|
+
path: data.path.text,
|
|
329
|
+
line: data.line_number,
|
|
330
|
+
column: column + 1,
|
|
331
|
+
preview: preview.slice(0, 8192)
|
|
332
|
+
}
|
|
333
|
+
];
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
function parseGrep(output, query) {
|
|
337
|
+
const matches = [];
|
|
338
|
+
let offset = 0;
|
|
339
|
+
while (offset < output.length) {
|
|
340
|
+
const separator = output.indexOf(0, offset);
|
|
341
|
+
const end = output.indexOf(10, separator + 1);
|
|
342
|
+
if (separator < 0 || end < 0)
|
|
343
|
+
throw new Error('CONTENT_SEARCH_OUTPUT_INVALID');
|
|
344
|
+
const path = output.subarray(offset, separator).toString('utf8');
|
|
345
|
+
const record = output.subarray(separator + 1, end).toString('utf8');
|
|
346
|
+
const colon = record.indexOf(':');
|
|
347
|
+
const preview = record.slice(colon + 1).replace(/\r$/u, '');
|
|
348
|
+
const column = lowerIndex(preview, query);
|
|
349
|
+
if (colon < 1 || column < 0)
|
|
350
|
+
throw new Error('CONTENT_SEARCH_OUTPUT_INVALID');
|
|
351
|
+
matches.push({
|
|
352
|
+
path,
|
|
353
|
+
line: Number(record.slice(0, colon)),
|
|
354
|
+
column: column + 1,
|
|
355
|
+
preview: preview.slice(0, 8192)
|
|
356
|
+
});
|
|
357
|
+
offset = end + 1;
|
|
358
|
+
}
|
|
359
|
+
return matches;
|
|
360
|
+
}
|
|
361
|
+
function parsePowerShell(output, query) {
|
|
362
|
+
return output
|
|
363
|
+
.toString('utf8')
|
|
364
|
+
.split(/\r?\n/u)
|
|
365
|
+
.flatMap((line) => {
|
|
366
|
+
if (line === '')
|
|
367
|
+
return [];
|
|
368
|
+
const match = JSON.parse(line);
|
|
369
|
+
if (typeof match.path !== 'string' ||
|
|
370
|
+
!Number.isInteger(match.line) ||
|
|
371
|
+
typeof match.preview !== 'string')
|
|
372
|
+
throw new Error('CONTENT_SEARCH_OUTPUT_INVALID');
|
|
373
|
+
const column = lowerIndex(match.preview, query);
|
|
374
|
+
if (column < 0)
|
|
375
|
+
throw new Error('CONTENT_SEARCH_OUTPUT_INVALID');
|
|
376
|
+
return [
|
|
377
|
+
{
|
|
378
|
+
path: match.path,
|
|
379
|
+
line: match.line,
|
|
380
|
+
column: column + 1,
|
|
381
|
+
preview: match.preview.slice(0, 8192)
|
|
382
|
+
}
|
|
383
|
+
];
|
|
384
|
+
});
|
|
385
|
+
}
|
|
386
|
+
async function nodeSearch(files, query, deadline) {
|
|
387
|
+
const matches = [];
|
|
388
|
+
let skippedFiles = 0;
|
|
389
|
+
for (const file of files) {
|
|
390
|
+
if (Date.now() >= deadline || matches.length > RESULT_LIMIT)
|
|
391
|
+
break;
|
|
392
|
+
const fileResults = [];
|
|
393
|
+
const stream = createReadStream(file.absolutePath);
|
|
394
|
+
const decoder = new TextDecoder('utf-8', { fatal: true });
|
|
395
|
+
let pending = '';
|
|
396
|
+
let line = 0;
|
|
397
|
+
let fileMatches = 0;
|
|
398
|
+
const addLine = (value) => {
|
|
399
|
+
line += 1;
|
|
400
|
+
const preview = value.endsWith('\r') ? value.slice(0, -1) : value;
|
|
401
|
+
const column = lowerIndex(preview, query);
|
|
402
|
+
if (column >= 0) {
|
|
403
|
+
fileResults.push({
|
|
404
|
+
path: file.path,
|
|
405
|
+
line,
|
|
406
|
+
column: column + 1,
|
|
407
|
+
preview: preview.slice(0, 8192)
|
|
408
|
+
});
|
|
409
|
+
fileMatches += 1;
|
|
410
|
+
}
|
|
411
|
+
return (fileMatches >= PER_FILE_LIMIT ||
|
|
412
|
+
matches.length + fileResults.length > RESULT_LIMIT ||
|
|
413
|
+
Date.now() >= deadline);
|
|
414
|
+
};
|
|
415
|
+
try {
|
|
416
|
+
let stopped = false;
|
|
417
|
+
for await (const chunk of stream) {
|
|
418
|
+
pending += decoder.decode(chunk, { stream: true });
|
|
419
|
+
let newline = pending.indexOf('\n');
|
|
420
|
+
while (newline >= 0) {
|
|
421
|
+
if (addLine(pending.slice(0, newline))) {
|
|
422
|
+
stopped = true;
|
|
423
|
+
break;
|
|
424
|
+
}
|
|
425
|
+
pending = pending.slice(newline + 1);
|
|
426
|
+
newline = pending.indexOf('\n');
|
|
427
|
+
}
|
|
428
|
+
if (stopped)
|
|
429
|
+
break;
|
|
430
|
+
}
|
|
431
|
+
if (!stopped) {
|
|
432
|
+
pending += decoder.decode();
|
|
433
|
+
if (pending !== '')
|
|
434
|
+
addLine(pending);
|
|
435
|
+
}
|
|
436
|
+
matches.push(...fileResults);
|
|
437
|
+
}
|
|
438
|
+
catch {
|
|
439
|
+
// Files that disappear, become unreadable or contain invalid UTF-8 are skipped atomically.
|
|
440
|
+
skippedFiles += 1;
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
return { matches, skippedFiles };
|
|
444
|
+
}
|
|
445
|
+
function lowerIndex(source, query) {
|
|
446
|
+
return source.toLocaleLowerCase().indexOf(query.toLocaleLowerCase());
|
|
447
|
+
}
|
|
448
|
+
function limitPerFile(matches) {
|
|
449
|
+
const counts = new Map();
|
|
450
|
+
return matches.filter((match) => {
|
|
451
|
+
const count = counts.get(match.path) ?? 0;
|
|
452
|
+
if (count >= PER_FILE_LIMIT)
|
|
453
|
+
return false;
|
|
454
|
+
counts.set(match.path, count + 1);
|
|
455
|
+
return true;
|
|
456
|
+
});
|
|
457
|
+
}
|
|
@@ -1,2 +1,41 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
export class WorkspaceDomainState {
|
|
2
|
+
uploads = new Map();
|
|
3
|
+
composerAttachments = new Map();
|
|
4
|
+
changesReads = new Map();
|
|
5
|
+
changesCache = new Map();
|
|
6
|
+
changesVersions = new Map();
|
|
7
|
+
fileIndexes = new Map();
|
|
8
|
+
watches = new Map();
|
|
9
|
+
watchRevisions = new Map();
|
|
10
|
+
watchSnapshotSequence = 0;
|
|
11
|
+
sessionWatchTimers = new Map();
|
|
12
|
+
sessionWatchSignatures = new Map();
|
|
13
|
+
sessionWatchReads = new Map();
|
|
14
|
+
sessionCache = new Map();
|
|
15
|
+
sessionCacheReads = new Map();
|
|
16
|
+
changesLimiter = new AsyncLimiter(3);
|
|
17
|
+
}
|
|
18
|
+
class AsyncLimiter {
|
|
19
|
+
limit;
|
|
20
|
+
active = 0;
|
|
21
|
+
waiters = [];
|
|
22
|
+
constructor(limit) {
|
|
23
|
+
this.limit = limit;
|
|
24
|
+
}
|
|
25
|
+
async run(task) {
|
|
26
|
+
if (this.active < this.limit)
|
|
27
|
+
this.active += 1;
|
|
28
|
+
else
|
|
29
|
+
await new Promise((resolve) => this.waiters.push(resolve));
|
|
30
|
+
try {
|
|
31
|
+
return await task();
|
|
32
|
+
}
|
|
33
|
+
finally {
|
|
34
|
+
const next = this.waiters.shift();
|
|
35
|
+
if (next === undefined)
|
|
36
|
+
this.active -= 1;
|
|
37
|
+
else
|
|
38
|
+
next();
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|