agentsys 5.0.3 → 5.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +21 -14
- package/.claude-plugin/plugin.json +1 -1
- package/AGENTS.md +2 -1
- package/CHANGELOG.md +18 -0
- package/README.md +7 -6
- package/adapters/codex/skills/agnix/SKILL.md +0 -1
- package/adapters/codex/skills/audit-project/SKILL.md +0 -1
- package/adapters/codex/skills/audit-project-agents/SKILL.md +0 -1
- package/adapters/codex/skills/audit-project-github/SKILL.md +0 -1
- package/adapters/codex/skills/consult/SKILL.md +132 -57
- package/adapters/codex/skills/debate/SKILL.md +214 -0
- package/adapters/codex/skills/delivery-approval/SKILL.md +0 -1
- package/adapters/codex/skills/deslop/SKILL.md +0 -1
- package/adapters/codex/skills/drift-detect/SKILL.md +0 -1
- package/adapters/codex/skills/enhance/SKILL.md +0 -1
- package/adapters/codex/skills/learn/SKILL.md +0 -1
- package/adapters/codex/skills/next-task/SKILL.md +0 -1
- package/adapters/codex/skills/perf/SKILL.md +0 -1
- package/adapters/codex/skills/repo-map/SKILL.md +0 -1
- package/adapters/codex/skills/ship/SKILL.md +0 -1
- package/adapters/codex/skills/ship-ci-review-loop/SKILL.md +0 -1
- package/adapters/codex/skills/ship-deployment/SKILL.md +0 -1
- package/adapters/codex/skills/ship-error-handling/SKILL.md +0 -1
- package/adapters/codex/skills/sync-docs/SKILL.md +0 -1
- package/adapters/opencode/agents/agent-enhancer.md +0 -1
- package/adapters/opencode/agents/agnix-agent.md +0 -1
- package/adapters/opencode/agents/ci-fixer.md +0 -1
- package/adapters/opencode/agents/ci-monitor.md +0 -1
- package/adapters/opencode/agents/claudemd-enhancer.md +0 -1
- package/adapters/opencode/agents/consult-agent.md +122 -30
- package/adapters/opencode/agents/cross-file-enhancer.md +0 -1
- package/adapters/opencode/agents/debate-orchestrator.md +169 -0
- package/adapters/opencode/agents/delivery-validator.md +0 -1
- package/adapters/opencode/agents/deslop-agent.md +0 -1
- package/adapters/opencode/agents/docs-enhancer.md +0 -1
- package/adapters/opencode/agents/exploration-agent.md +0 -1
- package/adapters/opencode/agents/hooks-enhancer.md +0 -1
- package/adapters/opencode/agents/implementation-agent.md +0 -1
- package/adapters/opencode/agents/learn-agent.md +0 -1
- package/adapters/opencode/agents/map-validator.md +0 -1
- package/adapters/opencode/agents/perf-analyzer.md +0 -1
- package/adapters/opencode/agents/perf-code-paths.md +0 -1
- package/adapters/opencode/agents/perf-investigation-logger.md +0 -1
- package/adapters/opencode/agents/perf-orchestrator.md +0 -1
- package/adapters/opencode/agents/perf-theory-gatherer.md +0 -1
- package/adapters/opencode/agents/perf-theory-tester.md +0 -1
- package/adapters/opencode/agents/plan-synthesizer.md +0 -1
- package/adapters/opencode/agents/planning-agent.md +0 -1
- package/adapters/opencode/agents/plugin-enhancer.md +0 -1
- package/adapters/opencode/agents/prompt-enhancer.md +0 -1
- package/adapters/opencode/agents/simple-fixer.md +0 -1
- package/adapters/opencode/agents/skills-enhancer.md +0 -1
- package/adapters/opencode/agents/sync-docs-agent.md +0 -1
- package/adapters/opencode/agents/task-discoverer.md +0 -1
- package/adapters/opencode/agents/test-coverage-checker.md +0 -1
- package/adapters/opencode/agents/worktree-manager.md +0 -1
- package/adapters/opencode/commands/agnix.md +0 -1
- package/adapters/opencode/commands/audit-project-agents.md +0 -1
- package/adapters/opencode/commands/audit-project-github.md +0 -1
- package/adapters/opencode/commands/audit-project.md +0 -1
- package/adapters/opencode/commands/consult.md +133 -57
- package/adapters/opencode/commands/debate.md +224 -0
- package/adapters/opencode/commands/delivery-approval.md +0 -1
- package/adapters/opencode/commands/deslop.md +0 -1
- package/adapters/opencode/commands/drift-detect.md +0 -1
- package/adapters/opencode/commands/enhance.md +0 -1
- package/adapters/opencode/commands/learn.md +0 -1
- package/adapters/opencode/commands/next-task.md +0 -1
- package/adapters/opencode/commands/perf.md +0 -1
- package/adapters/opencode/commands/repo-map.md +0 -1
- package/adapters/opencode/commands/ship-ci-review-loop.md +0 -1
- package/adapters/opencode/commands/ship-deployment.md +0 -1
- package/adapters/opencode/commands/ship-error-handling.md +0 -1
- package/adapters/opencode/commands/ship.md +0 -1
- package/adapters/opencode/commands/sync-docs.md +0 -1
- package/adapters/opencode/skills/agnix/SKILL.md +1 -2
- package/adapters/opencode/skills/consult/SKILL.md +33 -23
- package/adapters/opencode/skills/debate/SKILL.md +245 -0
- package/adapters/opencode/skills/deslop/SKILL.md +1 -2
- package/adapters/opencode/skills/discover-tasks/SKILL.md +1 -2
- package/adapters/opencode/skills/drift-analysis/SKILL.md +1 -2
- package/adapters/opencode/skills/enhance-agent-prompts/SKILL.md +1 -2
- package/adapters/opencode/skills/enhance-claude-memory/SKILL.md +1 -2
- package/adapters/opencode/skills/enhance-cross-file/SKILL.md +1 -2
- package/adapters/opencode/skills/enhance-docs/SKILL.md +1 -2
- package/adapters/opencode/skills/enhance-hooks/SKILL.md +1 -2
- package/adapters/opencode/skills/enhance-orchestrator/SKILL.md +1 -2
- package/adapters/opencode/skills/enhance-plugins/SKILL.md +1 -2
- package/adapters/opencode/skills/enhance-prompts/SKILL.md +1 -2
- package/adapters/opencode/skills/enhance-skills/SKILL.md +1 -2
- package/adapters/opencode/skills/learn/SKILL.md +1 -2
- package/adapters/opencode/skills/orchestrate-review/SKILL.md +0 -1
- package/adapters/opencode/skills/perf-analyzer/SKILL.md +1 -2
- package/adapters/opencode/skills/perf-baseline-manager/SKILL.md +1 -2
- package/adapters/opencode/skills/perf-benchmarker/SKILL.md +1 -2
- package/adapters/opencode/skills/perf-code-paths/SKILL.md +1 -2
- package/adapters/opencode/skills/perf-investigation-logger/SKILL.md +1 -2
- package/adapters/opencode/skills/perf-profiler/SKILL.md +1 -2
- package/adapters/opencode/skills/perf-theory-gatherer/SKILL.md +1 -2
- package/adapters/opencode/skills/perf-theory-tester/SKILL.md +1 -2
- package/adapters/opencode/skills/repo-mapping/SKILL.md +1 -2
- package/adapters/opencode/skills/sync-docs/SKILL.md +1 -2
- package/adapters/opencode/skills/validate-delivery/SKILL.md +1 -2
- package/lib/adapter-transforms.js +24 -4
- package/package.json +1 -1
- package/plugins/agnix/.claude-plugin/plugin.json +1 -1
- package/plugins/agnix/skills/agnix/SKILL.md +1 -1
- package/plugins/audit-project/.claude-plugin/plugin.json +1 -1
- package/plugins/audit-project/lib/adapter-transforms.js +24 -4
- package/plugins/consult/.claude-plugin/plugin.json +1 -1
- package/plugins/consult/agents/consult-agent.md +122 -29
- package/plugins/consult/commands/consult.md +135 -58
- package/plugins/consult/skills/consult/SKILL.md +31 -20
- package/plugins/debate/.claude-plugin/plugin.json +21 -0
- package/plugins/debate/agents/debate-orchestrator.md +175 -0
- package/plugins/debate/commands/debate.md +221 -0
- package/plugins/debate/lib/adapter-transforms.js +298 -0
- package/plugins/debate/lib/collectors/codebase.js +392 -0
- package/plugins/debate/lib/collectors/docs-patterns.js +713 -0
- package/plugins/debate/lib/collectors/documentation.js +219 -0
- package/plugins/debate/lib/collectors/github.js +330 -0
- package/plugins/debate/lib/collectors/index.js +126 -0
- package/plugins/debate/lib/config/index.js +14 -0
- package/plugins/debate/lib/cross-platform/index.js +539 -0
- package/plugins/debate/lib/discovery/index.js +352 -0
- package/plugins/debate/lib/drift-detect/collectors.js +37 -0
- package/plugins/debate/lib/enhance/agent-analyzer.js +421 -0
- package/plugins/debate/lib/enhance/agent-patterns.js +571 -0
- package/plugins/debate/lib/enhance/auto-suppression.js +622 -0
- package/plugins/debate/lib/enhance/benchmark.js +417 -0
- package/plugins/debate/lib/enhance/cross-file-analyzer.js +930 -0
- package/plugins/debate/lib/enhance/cross-file-patterns.js +370 -0
- package/plugins/debate/lib/enhance/docs-analyzer.js +325 -0
- package/plugins/debate/lib/enhance/docs-patterns.js +671 -0
- package/plugins/debate/lib/enhance/fixer.js +721 -0
- package/plugins/debate/lib/enhance/hook-analyzer.js +135 -0
- package/plugins/debate/lib/enhance/hook-patterns.js +40 -0
- package/plugins/debate/lib/enhance/index.js +127 -0
- package/plugins/debate/lib/enhance/plugin-analyzer.js +402 -0
- package/plugins/debate/lib/enhance/plugin-patterns.js +326 -0
- package/plugins/debate/lib/enhance/projectmemory-analyzer.js +551 -0
- package/plugins/debate/lib/enhance/projectmemory-patterns.js +617 -0
- package/plugins/debate/lib/enhance/prompt-analyzer.js +457 -0
- package/plugins/debate/lib/enhance/prompt-patterns.js +1484 -0
- package/plugins/debate/lib/enhance/reporter.js +1348 -0
- package/plugins/debate/lib/enhance/security-patterns.js +284 -0
- package/plugins/debate/lib/enhance/skill-analyzer.js +182 -0
- package/plugins/debate/lib/enhance/skill-patterns.js +147 -0
- package/plugins/debate/lib/enhance/suppression.js +352 -0
- package/plugins/debate/lib/enhance/tool-patterns.js +373 -0
- package/plugins/debate/lib/index.js +270 -0
- package/plugins/debate/lib/patterns/cli-enhancers.js +611 -0
- package/plugins/debate/lib/patterns/pipeline.js +948 -0
- package/plugins/debate/lib/patterns/review-patterns.js +558 -0
- package/plugins/debate/lib/patterns/slop-analyzers.js +2305 -0
- package/plugins/debate/lib/patterns/slop-patterns.js +1187 -0
- package/plugins/debate/lib/perf/analyzer/index.js +22 -0
- package/plugins/debate/lib/perf/argument-parser.js +105 -0
- package/plugins/debate/lib/perf/baseline-comparator.js +50 -0
- package/plugins/debate/lib/perf/baseline-store.js +127 -0
- package/plugins/debate/lib/perf/benchmark-runner.js +404 -0
- package/plugins/debate/lib/perf/breaking-point-finder.js +52 -0
- package/plugins/debate/lib/perf/breaking-point-runner.js +60 -0
- package/plugins/debate/lib/perf/checkpoint.js +123 -0
- package/plugins/debate/lib/perf/code-paths.js +86 -0
- package/plugins/debate/lib/perf/consolidation.js +37 -0
- package/plugins/debate/lib/perf/constraint-runner.js +71 -0
- package/plugins/debate/lib/perf/experiment-runner.js +32 -0
- package/plugins/debate/lib/perf/index.js +41 -0
- package/plugins/debate/lib/perf/investigation-state.js +874 -0
- package/plugins/debate/lib/perf/optimization-runner.js +79 -0
- package/plugins/debate/lib/perf/profilers/go.js +22 -0
- package/plugins/debate/lib/perf/profilers/index.js +46 -0
- package/plugins/debate/lib/perf/profilers/java.js +23 -0
- package/plugins/debate/lib/perf/profilers/node.js +27 -0
- package/plugins/debate/lib/perf/profilers/python.js +23 -0
- package/plugins/debate/lib/perf/profilers/rust.js +23 -0
- package/plugins/debate/lib/perf/profiling-runner.js +75 -0
- package/plugins/debate/lib/perf/schemas.js +140 -0
- package/plugins/debate/lib/platform/detect-platform.js +413 -0
- package/plugins/debate/lib/platform/detection-configs.js +93 -0
- package/plugins/debate/lib/platform/state-dir.js +132 -0
- package/plugins/debate/lib/platform/verify-tools.js +182 -0
- package/plugins/debate/lib/repo-map/cache.js +152 -0
- package/plugins/debate/lib/repo-map/concurrency.js +29 -0
- package/plugins/debate/lib/repo-map/index.js +222 -0
- package/plugins/debate/lib/repo-map/installer.js +212 -0
- package/plugins/debate/lib/repo-map/queries/go.js +27 -0
- package/plugins/debate/lib/repo-map/queries/index.js +100 -0
- package/plugins/debate/lib/repo-map/queries/java.js +38 -0
- package/plugins/debate/lib/repo-map/queries/javascript.js +55 -0
- package/plugins/debate/lib/repo-map/queries/python.js +24 -0
- package/plugins/debate/lib/repo-map/queries/rust.js +73 -0
- package/plugins/debate/lib/repo-map/queries/typescript.js +38 -0
- package/plugins/debate/lib/repo-map/runner.js +1364 -0
- package/plugins/debate/lib/repo-map/updater.js +562 -0
- package/plugins/debate/lib/repo-map/usage-analyzer.js +407 -0
- package/plugins/debate/lib/schemas/plugin-manifest.schema.json +57 -0
- package/plugins/debate/lib/schemas/validator.js +247 -0
- package/plugins/debate/lib/sources/custom-handler.js +199 -0
- package/plugins/debate/lib/sources/policy-questions.js +246 -0
- package/plugins/debate/lib/sources/source-cache.js +165 -0
- package/plugins/debate/lib/state/workflow-state.js +576 -0
- package/plugins/debate/lib/types/agent-frontmatter.d.ts +134 -0
- package/plugins/debate/lib/types/command-frontmatter.d.ts +107 -0
- package/plugins/debate/lib/types/hook-frontmatter.d.ts +115 -0
- package/plugins/debate/lib/types/index.d.ts +84 -0
- package/plugins/debate/lib/types/plugin-manifest.d.ts +102 -0
- package/plugins/debate/lib/types/skill-frontmatter.d.ts +89 -0
- package/plugins/debate/lib/utils/atomic-write.js +94 -0
- package/plugins/debate/lib/utils/cache-manager.js +159 -0
- package/plugins/debate/lib/utils/command-parser.js +0 -0
- package/plugins/debate/lib/utils/context-optimizer.js +300 -0
- package/plugins/debate/lib/utils/deprecation.js +37 -0
- package/plugins/debate/lib/utils/shell-escape.js +88 -0
- package/plugins/debate/lib/utils/state-helpers.js +61 -0
- package/plugins/debate/skills/debate/SKILL.md +264 -0
- package/plugins/deslop/.claude-plugin/plugin.json +1 -1
- package/plugins/deslop/lib/adapter-transforms.js +24 -4
- package/plugins/deslop/skills/deslop/SKILL.md +1 -1
- package/plugins/drift-detect/.claude-plugin/plugin.json +1 -1
- package/plugins/drift-detect/lib/adapter-transforms.js +24 -4
- package/plugins/drift-detect/skills/drift-analysis/SKILL.md +1 -1
- package/plugins/enhance/.claude-plugin/plugin.json +1 -1
- package/plugins/enhance/lib/adapter-transforms.js +24 -4
- package/plugins/enhance/skills/enhance-agent-prompts/SKILL.md +1 -1
- package/plugins/enhance/skills/enhance-claude-memory/SKILL.md +1 -1
- package/plugins/enhance/skills/enhance-cross-file/SKILL.md +1 -1
- package/plugins/enhance/skills/enhance-docs/SKILL.md +1 -1
- package/plugins/enhance/skills/enhance-hooks/SKILL.md +1 -1
- package/plugins/enhance/skills/enhance-orchestrator/SKILL.md +1 -1
- package/plugins/enhance/skills/enhance-plugins/SKILL.md +1 -1
- package/plugins/enhance/skills/enhance-prompts/SKILL.md +1 -1
- package/plugins/enhance/skills/enhance-skills/SKILL.md +1 -1
- package/plugins/learn/.claude-plugin/plugin.json +1 -1
- package/plugins/learn/agents/learn-agent.md +1 -1
- package/plugins/learn/lib/adapter-transforms.js +24 -4
- package/plugins/learn/skills/learn/SKILL.md +1 -1
- package/plugins/next-task/.claude-plugin/plugin.json +1 -1
- package/plugins/next-task/agents/exploration-agent.md +1 -1
- package/plugins/next-task/lib/adapter-transforms.js +24 -4
- package/plugins/next-task/skills/discover-tasks/SKILL.md +1 -1
- package/plugins/next-task/skills/validate-delivery/SKILL.md +1 -1
- package/plugins/perf/.claude-plugin/plugin.json +1 -1
- package/plugins/perf/lib/adapter-transforms.js +24 -4
- package/plugins/perf/skills/perf-analyzer/SKILL.md +1 -1
- package/plugins/perf/skills/perf-baseline-manager/SKILL.md +1 -1
- package/plugins/perf/skills/perf-benchmarker/SKILL.md +1 -1
- package/plugins/perf/skills/perf-code-paths/SKILL.md +1 -1
- package/plugins/perf/skills/perf-investigation-logger/SKILL.md +1 -1
- package/plugins/perf/skills/perf-profiler/SKILL.md +1 -1
- package/plugins/perf/skills/perf-theory-gatherer/SKILL.md +1 -1
- package/plugins/perf/skills/perf-theory-tester/SKILL.md +1 -1
- package/plugins/repo-map/.claude-plugin/plugin.json +1 -1
- package/plugins/repo-map/lib/adapter-transforms.js +24 -4
- package/plugins/ship/.claude-plugin/plugin.json +1 -1
- package/plugins/ship/lib/adapter-transforms.js +24 -4
- package/plugins/sync-docs/.claude-plugin/plugin.json +1 -1
- package/plugins/sync-docs/lib/adapter-transforms.js +24 -4
- package/plugins/sync-docs/skills/sync-docs/SKILL.md +1 -1
- package/scripts/gen-adapters.js +6 -7
- package/scripts/generate-docs.js +4 -2
- package/scripts/plugins.txt +1 -0
- package/site/content.json +6 -6
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cache Manager
|
|
3
|
+
* Centralized caching abstraction with TTL and size limits
|
|
4
|
+
*
|
|
5
|
+
* @module lib/utils/cache-manager
|
|
6
|
+
* @author Avi Fenesh
|
|
7
|
+
* @license MIT
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Cache manager with TTL and size limits
|
|
12
|
+
*/
|
|
13
|
+
class CacheManager {
|
|
14
|
+
/**
|
|
15
|
+
* Create a new cache manager
|
|
16
|
+
* @param {Object} options - Cache configuration
|
|
17
|
+
* @param {number} options.maxSize - Maximum number of entries (default: 100)
|
|
18
|
+
* @param {number} options.ttl - Time-to-live in milliseconds (default: 60000)
|
|
19
|
+
* @param {number} options.maxValueSize - Maximum size per value in bytes (default: null - unlimited)
|
|
20
|
+
*/
|
|
21
|
+
constructor(options = {}) {
|
|
22
|
+
this.maxSize = options.maxSize || 100;
|
|
23
|
+
this.ttl = options.ttl || 60000; // 1 minute default
|
|
24
|
+
this.maxValueSize = options.maxValueSize || null;
|
|
25
|
+
|
|
26
|
+
// Use Map for insertion-order guarantee (FIFO eviction)
|
|
27
|
+
this._cache = new Map();
|
|
28
|
+
this._timestamps = new Map();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Get a value from cache
|
|
33
|
+
* @param {string} key - Cache key
|
|
34
|
+
* @returns {*} Cached value or undefined if not found/expired
|
|
35
|
+
*/
|
|
36
|
+
get(key) {
|
|
37
|
+
if (!this._cache.has(key)) {
|
|
38
|
+
return undefined;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Check if expired
|
|
42
|
+
const timestamp = this._timestamps.get(key);
|
|
43
|
+
if (Date.now() - timestamp > this.ttl) {
|
|
44
|
+
this.delete(key);
|
|
45
|
+
return undefined;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return this._cache.get(key);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Set a value in cache
|
|
53
|
+
* @param {string} key - Cache key
|
|
54
|
+
* @param {*} value - Value to cache
|
|
55
|
+
* @returns {boolean} True if cached, false if value too large
|
|
56
|
+
*/
|
|
57
|
+
set(key, value) {
|
|
58
|
+
// Check value size if limit set
|
|
59
|
+
if (this.maxValueSize !== null && typeof value === 'string') {
|
|
60
|
+
if (value.length > this.maxValueSize) {
|
|
61
|
+
return false; // Value too large, don't cache
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Update or add entry
|
|
66
|
+
this._cache.set(key, value);
|
|
67
|
+
this._timestamps.set(key, Date.now());
|
|
68
|
+
|
|
69
|
+
// Enforce size limit with FIFO eviction
|
|
70
|
+
this._enforceMaxSize();
|
|
71
|
+
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Check if key exists and is not expired
|
|
77
|
+
* @param {string} key - Cache key
|
|
78
|
+
* @returns {boolean} True if key exists and is valid
|
|
79
|
+
*/
|
|
80
|
+
has(key) {
|
|
81
|
+
return this.get(key) !== undefined;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Delete a key from cache
|
|
86
|
+
* @param {string} key - Cache key
|
|
87
|
+
* @returns {boolean} True if key existed
|
|
88
|
+
*/
|
|
89
|
+
delete(key) {
|
|
90
|
+
this._timestamps.delete(key);
|
|
91
|
+
return this._cache.delete(key);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Clear all cache entries
|
|
96
|
+
*/
|
|
97
|
+
clear() {
|
|
98
|
+
this._cache.clear();
|
|
99
|
+
this._timestamps.clear();
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Get current cache size
|
|
104
|
+
* @returns {number} Number of entries
|
|
105
|
+
*/
|
|
106
|
+
get size() {
|
|
107
|
+
return this._cache.size;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Get cache statistics
|
|
112
|
+
* @returns {Object} Stats object with size, ttl, maxSize
|
|
113
|
+
*/
|
|
114
|
+
getStats() {
|
|
115
|
+
return {
|
|
116
|
+
size: this._cache.size,
|
|
117
|
+
maxSize: this.maxSize,
|
|
118
|
+
ttl: this.ttl,
|
|
119
|
+
maxValueSize: this.maxValueSize
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Enforce maximum cache size using FIFO eviction
|
|
125
|
+
* @private
|
|
126
|
+
*/
|
|
127
|
+
_enforceMaxSize() {
|
|
128
|
+
// Only evict if cache exceeds limit
|
|
129
|
+
if (this._cache.size <= this.maxSize) {
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Map maintains insertion order - first key is oldest
|
|
134
|
+
while (this._cache.size > this.maxSize) {
|
|
135
|
+
const firstKey = this._cache.keys().next().value;
|
|
136
|
+
this.delete(firstKey);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Remove expired entries (useful for long-running processes)
|
|
142
|
+
* @returns {number} Number of entries removed
|
|
143
|
+
*/
|
|
144
|
+
prune() {
|
|
145
|
+
let removed = 0;
|
|
146
|
+
const now = Date.now();
|
|
147
|
+
|
|
148
|
+
for (const [key, timestamp] of this._timestamps.entries()) {
|
|
149
|
+
if (now - timestamp > this.ttl) {
|
|
150
|
+
this.delete(key);
|
|
151
|
+
removed++;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return removed;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
module.exports = { CacheManager };
|
|
Binary file
|
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Context Optimizer Utilities
|
|
3
|
+
* Provides optimized git commands to minimize token usage while gathering context
|
|
4
|
+
*
|
|
5
|
+
* Target: Keep command execution under 50k tokens
|
|
6
|
+
*
|
|
7
|
+
* @author Avi Fenesh
|
|
8
|
+
* @license MIT
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
const {
|
|
12
|
+
escapeShell,
|
|
13
|
+
escapeSingleQuotes,
|
|
14
|
+
sanitizeExtension
|
|
15
|
+
} = require('./shell-escape');
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Validate git branch name to prevent command injection
|
|
19
|
+
* @param {string} branch - Branch name to validate
|
|
20
|
+
* @returns {string} Validated branch name
|
|
21
|
+
* @throws {Error} If branch name contains invalid characters
|
|
22
|
+
*/
|
|
23
|
+
function validateBranchName(branch) {
|
|
24
|
+
if (typeof branch !== 'string' || branch.length === 0) {
|
|
25
|
+
throw new Error('Branch name must be a non-empty string');
|
|
26
|
+
}
|
|
27
|
+
if (branch.length > 255) {
|
|
28
|
+
throw new Error('Branch name too long (max 255 characters)');
|
|
29
|
+
}
|
|
30
|
+
// Allow alphanumeric, underscore, hyphen, forward slash, and dot
|
|
31
|
+
if (!/^[a-zA-Z0-9/_.-]+$/.test(branch)) {
|
|
32
|
+
throw new Error('Branch name contains invalid characters');
|
|
33
|
+
}
|
|
34
|
+
// Prevent git option injection
|
|
35
|
+
if (branch.startsWith('-')) {
|
|
36
|
+
throw new Error('Branch name cannot start with hyphen');
|
|
37
|
+
}
|
|
38
|
+
return branch;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Validate git reference to prevent command injection
|
|
43
|
+
* @param {string} ref - Git reference to validate
|
|
44
|
+
* @returns {string} Validated reference
|
|
45
|
+
* @throws {Error} If reference contains invalid characters
|
|
46
|
+
*/
|
|
47
|
+
function validateGitRef(ref) {
|
|
48
|
+
if (typeof ref !== 'string' || ref.length === 0) {
|
|
49
|
+
throw new Error('Git reference must be a non-empty string');
|
|
50
|
+
}
|
|
51
|
+
if (ref.length > 255) {
|
|
52
|
+
throw new Error('Git reference too long (max 255 characters)');
|
|
53
|
+
}
|
|
54
|
+
// Allow alphanumeric, tilde, caret, dot, hyphen, underscore, forward slash
|
|
55
|
+
if (!/^[a-zA-Z0-9~^._/-]+$/.test(ref)) {
|
|
56
|
+
throw new Error('Git reference contains invalid characters');
|
|
57
|
+
}
|
|
58
|
+
// Prevent git option injection
|
|
59
|
+
if (ref.startsWith('-')) {
|
|
60
|
+
throw new Error('Git reference cannot start with hyphen');
|
|
61
|
+
}
|
|
62
|
+
return ref;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Validate numeric limit parameter
|
|
67
|
+
* @param {number} limit - Limit value to validate
|
|
68
|
+
* @param {number} max - Maximum allowed value (default: 1000)
|
|
69
|
+
* @returns {number} Validated limit
|
|
70
|
+
* @throws {Error} If limit is invalid
|
|
71
|
+
*/
|
|
72
|
+
function validateLimit(limit, max = 1000) {
|
|
73
|
+
// Strict type check - must be number or numeric string
|
|
74
|
+
if (typeof limit === 'string') {
|
|
75
|
+
// Only allow pure numeric strings
|
|
76
|
+
if (!/^\d+$/.test(limit)) {
|
|
77
|
+
throw new Error('Limit must be a positive integer');
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
const num = typeof limit === 'number' ? limit : parseInt(limit, 10);
|
|
81
|
+
if (!Number.isInteger(num) || num < 1) {
|
|
82
|
+
throw new Error('Limit must be a positive integer');
|
|
83
|
+
}
|
|
84
|
+
if (num > max) {
|
|
85
|
+
throw new Error(`Limit cannot exceed ${max}`);
|
|
86
|
+
}
|
|
87
|
+
return num;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Git command optimization utilities for context efficiency
|
|
92
|
+
*/
|
|
93
|
+
const contextOptimizer = {
|
|
94
|
+
/**
|
|
95
|
+
* Get recent commits with minimal formatting
|
|
96
|
+
* @param {number} limit - Number of commits to retrieve (default: 10)
|
|
97
|
+
* @returns {string} Git command
|
|
98
|
+
*/
|
|
99
|
+
recentCommits: (limit = 10) => {
|
|
100
|
+
const safeLimit = validateLimit(limit);
|
|
101
|
+
return `git log --oneline --no-decorate -${safeLimit} --format="%h %s"`;
|
|
102
|
+
},
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Get compact git status (untracked files excluded)
|
|
106
|
+
* @returns {string} Git command
|
|
107
|
+
*/
|
|
108
|
+
compactStatus: () =>
|
|
109
|
+
'git status -uno --porcelain',
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Get file changes between refs
|
|
113
|
+
* @param {string} ref - Reference to compare from (default: 'HEAD~5')
|
|
114
|
+
* @returns {string} Git command
|
|
115
|
+
*/
|
|
116
|
+
fileChanges: (ref = 'HEAD~5') => {
|
|
117
|
+
const safeRef = validateGitRef(ref);
|
|
118
|
+
return `git diff ${safeRef}..HEAD --name-status`;
|
|
119
|
+
},
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Get current branch name
|
|
123
|
+
* @returns {string} Git command
|
|
124
|
+
*/
|
|
125
|
+
currentBranch: () =>
|
|
126
|
+
'git branch --show-current',
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Get remote information (limited to 2 lines)
|
|
130
|
+
* @returns {string} Git command
|
|
131
|
+
*/
|
|
132
|
+
remoteInfo: () =>
|
|
133
|
+
'git remote -v | head -2',
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Check if there are stashed changes
|
|
137
|
+
* @returns {string} Git command
|
|
138
|
+
*/
|
|
139
|
+
hasStashes: () =>
|
|
140
|
+
'git stash list --oneline | wc -l',
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Get worktree list in porcelain format
|
|
144
|
+
* @returns {string} Git command
|
|
145
|
+
*/
|
|
146
|
+
worktreeList: () =>
|
|
147
|
+
'git worktree list --porcelain',
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Get the age of a specific line (for TODO checking)
|
|
151
|
+
* @param {string} file - File path
|
|
152
|
+
* @param {number} line - Line number
|
|
153
|
+
* @returns {string} Git command
|
|
154
|
+
*/
|
|
155
|
+
lineAge: (file, line) => {
|
|
156
|
+
// Validate line is a positive integer with reasonable bounds
|
|
157
|
+
const lineNum = parseInt(line, 10);
|
|
158
|
+
const MAX_LINE_NUMBER = 10000000; // 10 million lines - reasonable upper bound
|
|
159
|
+
if (!Number.isInteger(lineNum) || lineNum < 1) {
|
|
160
|
+
throw new Error('Line must be a positive integer');
|
|
161
|
+
}
|
|
162
|
+
if (lineNum > MAX_LINE_NUMBER) {
|
|
163
|
+
throw new Error(`Line number cannot exceed ${MAX_LINE_NUMBER}`);
|
|
164
|
+
}
|
|
165
|
+
// Escape file path for safe shell usage
|
|
166
|
+
const safeFile = escapeShell(file);
|
|
167
|
+
return `git blame -L ${lineNum},${lineNum} "${safeFile}" --porcelain | grep '^committer-time' | cut -d' ' -f2`;
|
|
168
|
+
},
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Find source files by extension
|
|
172
|
+
* @param {string} extension - File extension (e.g., 'ts', 'py', 'rs')
|
|
173
|
+
* @returns {string} Git command
|
|
174
|
+
*/
|
|
175
|
+
findSourceFiles: (extension = 'ts') => {
|
|
176
|
+
const safeExt = sanitizeExtension(extension);
|
|
177
|
+
return `git ls-files | grep '\\.${safeExt}$'`;
|
|
178
|
+
},
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Get diff stat summary
|
|
182
|
+
* @param {string} ref - Reference to compare from (default: 'HEAD~5')
|
|
183
|
+
* @returns {string} Git command
|
|
184
|
+
*/
|
|
185
|
+
diffStat: (ref = 'HEAD~5') => {
|
|
186
|
+
const safeRef = validateGitRef(ref);
|
|
187
|
+
return `git diff ${safeRef}..HEAD --stat | head -20`;
|
|
188
|
+
},
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Get contributors list (limited to top 10)
|
|
192
|
+
* @returns {string} Git command
|
|
193
|
+
*/
|
|
194
|
+
contributors: () =>
|
|
195
|
+
'git shortlog -sn --no-merges | head -10',
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Get last commit message
|
|
199
|
+
* @returns {string} Git command
|
|
200
|
+
*/
|
|
201
|
+
lastCommitMessage: () =>
|
|
202
|
+
'git log -1 --format=%s',
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Get files changed in last commit
|
|
206
|
+
* @returns {string} Git command
|
|
207
|
+
*/
|
|
208
|
+
lastCommitFiles: () =>
|
|
209
|
+
'git diff-tree --no-commit-id --name-only -r HEAD',
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Get branch list (local only, limited)
|
|
213
|
+
* @param {number} limit - Number of branches (default: 10)
|
|
214
|
+
* @returns {string} Git command
|
|
215
|
+
*/
|
|
216
|
+
branches: (limit = 10) => {
|
|
217
|
+
const safeLimit = validateLimit(limit);
|
|
218
|
+
return `git branch --format='%(refname:short)' | head -${safeLimit}`;
|
|
219
|
+
},
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Get tags list (limited)
|
|
223
|
+
* @param {number} limit - Number of tags (default: 10)
|
|
224
|
+
* @returns {string} Git command
|
|
225
|
+
*/
|
|
226
|
+
tags: (limit = 10) => {
|
|
227
|
+
const safeLimit = validateLimit(limit);
|
|
228
|
+
return `git tag --sort=-creatordate | head -${safeLimit}`;
|
|
229
|
+
},
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Get count of commits on current branch since branching from main
|
|
233
|
+
* @param {string} mainBranch - Main branch name (default: 'main')
|
|
234
|
+
* @returns {string} Git command
|
|
235
|
+
*/
|
|
236
|
+
commitsSinceBranch: (mainBranch = 'main') => {
|
|
237
|
+
const safeBranch = validateBranchName(mainBranch);
|
|
238
|
+
return `git rev-list --count ${safeBranch}..HEAD`;
|
|
239
|
+
},
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Check if working directory is clean
|
|
243
|
+
* @returns {string} Git command
|
|
244
|
+
*/
|
|
245
|
+
isClean: () =>
|
|
246
|
+
'git status --porcelain | wc -l',
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Get merge base with main branch
|
|
250
|
+
* @param {string} mainBranch - Main branch name (default: 'main')
|
|
251
|
+
* @returns {string} Git command
|
|
252
|
+
*/
|
|
253
|
+
mergeBase: (mainBranch = 'main') => {
|
|
254
|
+
const safeBranch = validateBranchName(mainBranch);
|
|
255
|
+
return `git merge-base ${safeBranch} HEAD`;
|
|
256
|
+
},
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Get files modified in current branch (since branching)
|
|
260
|
+
* @param {string} mainBranch - Main branch name (default: 'main')
|
|
261
|
+
* @returns {string} Git command
|
|
262
|
+
*/
|
|
263
|
+
branchChangedFiles: (mainBranch = 'main') => {
|
|
264
|
+
const safeBranch = validateBranchName(mainBranch);
|
|
265
|
+
return `git diff ${safeBranch}...HEAD --name-only`;
|
|
266
|
+
},
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Get commit count by author
|
|
270
|
+
* @param {string} author - Author name or email
|
|
271
|
+
* @returns {string} Git command
|
|
272
|
+
*/
|
|
273
|
+
authorCommitCount: (author) => {
|
|
274
|
+
const safeAuthor = escapeShell(author);
|
|
275
|
+
return `git log --author="${safeAuthor}" --oneline | wc -l`;
|
|
276
|
+
},
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Check if file exists in repository
|
|
280
|
+
* @param {string} file - File path
|
|
281
|
+
* @returns {string} Git command
|
|
282
|
+
*/
|
|
283
|
+
fileExists: (file) => {
|
|
284
|
+
const safeFile = escapeSingleQuotes(file);
|
|
285
|
+
return `git ls-files | grep -q '${safeFile}' && echo 'true' || echo 'false'`;
|
|
286
|
+
}
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
// Export main API
|
|
290
|
+
module.exports = contextOptimizer;
|
|
291
|
+
|
|
292
|
+
// Export internal functions for testing
|
|
293
|
+
module.exports._internal = {
|
|
294
|
+
escapeShell,
|
|
295
|
+
escapeSingleQuotes,
|
|
296
|
+
sanitizeExtension,
|
|
297
|
+
validateBranchName,
|
|
298
|
+
validateGitRef,
|
|
299
|
+
validateLimit
|
|
300
|
+
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deprecation Warning Utility
|
|
3
|
+
* Centralized utility for handling deprecation warnings across the codebase
|
|
4
|
+
*
|
|
5
|
+
* @author Avi Fenesh
|
|
6
|
+
* @license MIT
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
// Track which functions have already shown deprecation warnings (once per function)
|
|
10
|
+
const _deprecationWarned = new Set();
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Show deprecation warning for sync functions (once per function name)
|
|
14
|
+
* @param {string} funcName - Name of the deprecated sync function
|
|
15
|
+
* @param {string} asyncAlt - Name of the async alternative
|
|
16
|
+
*/
|
|
17
|
+
function warnDeprecation(funcName, asyncAlt) {
|
|
18
|
+
if (_deprecationWarned.has(funcName)) return;
|
|
19
|
+
_deprecationWarned.add(funcName);
|
|
20
|
+
console.warn(
|
|
21
|
+
`DEPRECATED: ${funcName}() is synchronous and blocks the event loop. ` +
|
|
22
|
+
`Use ${asyncAlt}() instead. Will be removed in v3.0.0.`
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Reset deprecation warnings (for testing only)
|
|
28
|
+
* @private
|
|
29
|
+
*/
|
|
30
|
+
function _resetDeprecationWarnings() {
|
|
31
|
+
_deprecationWarned.clear();
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
module.exports = {
|
|
35
|
+
warnDeprecation,
|
|
36
|
+
_resetDeprecationWarnings
|
|
37
|
+
};
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shell Escaping Utilities
|
|
3
|
+
* Centralized string escaping functions for safe shell command construction
|
|
4
|
+
*
|
|
5
|
+
* @module lib/utils/shell-escape
|
|
6
|
+
* @author Avi Fenesh
|
|
7
|
+
* @license MIT
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Escape shell special characters for safe command interpolation
|
|
12
|
+
* Handles all dangerous shell metacharacters including command injection vectors
|
|
13
|
+
* Optimized: uses single regex test instead of multiple .includes() calls
|
|
14
|
+
* @param {string} str - String to escape
|
|
15
|
+
* @returns {string} Escaped string safe for shell use
|
|
16
|
+
* @throws {Error} If string contains null bytes or newlines
|
|
17
|
+
*/
|
|
18
|
+
function escapeShell(str) {
|
|
19
|
+
if (typeof str !== 'string') return '';
|
|
20
|
+
|
|
21
|
+
// Reject null bytes and newlines which could be used for injection
|
|
22
|
+
// Optimized: single regex test instead of 3 separate .includes() scans
|
|
23
|
+
// Use \x00 instead of \0 for better portability across JS engines
|
|
24
|
+
if (/[\x00\n\r]/.test(str)) {
|
|
25
|
+
throw new Error('Input contains invalid characters (null bytes or newlines)');
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Escape all shell metacharacters: " $ ` \ ! ; | & > < ( ) { } [ ] * ? ~ # ' space tab
|
|
29
|
+
return str.replace(/["\$`\\!;|&><(){}[\]*?~#'\s]/g, '\\$&');
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Escape single quotes for shell (replace ' with '\''
|
|
34
|
+
* Use this for strings that will be wrapped in single quotes
|
|
35
|
+
* @param {string} str - String to escape
|
|
36
|
+
* @returns {string} Escaped string safe for single-quoted shell use
|
|
37
|
+
*/
|
|
38
|
+
function escapeSingleQuotes(str) {
|
|
39
|
+
if (typeof str !== 'string') return '';
|
|
40
|
+
return str.replace(/'/g, "'\\''");
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Validate and sanitize file extension
|
|
45
|
+
* Removes all non-alphanumeric characters
|
|
46
|
+
* @param {string} ext - Extension to validate
|
|
47
|
+
* @returns {string} Safe extension (alphanumeric only), defaults to 'ts' if empty
|
|
48
|
+
*/
|
|
49
|
+
function sanitizeExtension(ext) {
|
|
50
|
+
if (typeof ext !== 'string') return 'ts';
|
|
51
|
+
const safe = ext.replace(/[^a-zA-Z0-9]/g, '');
|
|
52
|
+
return safe || 'ts';
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Escape a string for use in a double-quoted shell context
|
|
57
|
+
* More permissive than escapeShell but still safe
|
|
58
|
+
* @param {string} str - String to escape
|
|
59
|
+
* @returns {string} Escaped string safe for double-quoted shell use
|
|
60
|
+
*/
|
|
61
|
+
function escapeDoubleQuotes(str) {
|
|
62
|
+
if (typeof str !== 'string') return '';
|
|
63
|
+
|
|
64
|
+
// In double quotes, we need to escape: $ ` " \ and newlines
|
|
65
|
+
return str.replace(/[$`"\\\n]/g, '\\$&');
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Quote a string for safe shell use
|
|
70
|
+
* Wraps in single quotes and escapes any embedded single quotes
|
|
71
|
+
* This is often safer than escapeShell for complex strings
|
|
72
|
+
* @param {string} str - String to quote
|
|
73
|
+
* @returns {string} Safely quoted string
|
|
74
|
+
*/
|
|
75
|
+
function quoteShell(str) {
|
|
76
|
+
if (typeof str !== 'string') return "''";
|
|
77
|
+
|
|
78
|
+
// Wrap in single quotes and escape any embedded single quotes
|
|
79
|
+
return "'" + str.replace(/'/g, "'\\''") + "'";
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
module.exports = {
|
|
83
|
+
escapeShell,
|
|
84
|
+
escapeSingleQuotes,
|
|
85
|
+
sanitizeExtension,
|
|
86
|
+
escapeDoubleQuotes,
|
|
87
|
+
quoteShell
|
|
88
|
+
};
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { isDeepStrictEqual } = require('util');
|
|
4
|
+
|
|
5
|
+
const RETRY_SLEEP_STATE = typeof SharedArrayBuffer === 'function' && typeof Atomics === 'object' && typeof Atomics.wait === 'function'
|
|
6
|
+
? new Int32Array(new SharedArrayBuffer(4))
|
|
7
|
+
: null;
|
|
8
|
+
|
|
9
|
+
function isPlainObject(value) {
|
|
10
|
+
return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function hasUpdatedSubset(target, subset) {
|
|
14
|
+
if (!isPlainObject(subset)) {
|
|
15
|
+
return isDeepStrictEqual(target, subset);
|
|
16
|
+
}
|
|
17
|
+
if (!isPlainObject(target)) {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
for (const [key, value] of Object.entries(subset)) {
|
|
22
|
+
if (!hasUpdatedSubset(target[key], value)) {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function updatesApplied(state, updates) {
|
|
30
|
+
if (!state) return false;
|
|
31
|
+
|
|
32
|
+
for (const [key, value] of Object.entries(updates || {})) {
|
|
33
|
+
if (key === '_version') continue;
|
|
34
|
+
if (!hasUpdatedSubset(state[key], value)) {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return true;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function sleepForRetry(ms) {
|
|
43
|
+
if (!Number.isFinite(ms) || ms <= 0) {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const delayMs = Math.floor(ms);
|
|
48
|
+
if (RETRY_SLEEP_STATE) {
|
|
49
|
+
try {
|
|
50
|
+
Atomics.wait(RETRY_SLEEP_STATE, 0, 0, delayMs);
|
|
51
|
+
} catch {
|
|
52
|
+
// Ignore environments where Atomics.wait exists but cannot be used.
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
module.exports = {
|
|
58
|
+
isPlainObject,
|
|
59
|
+
updatesApplied,
|
|
60
|
+
sleepForRetry
|
|
61
|
+
};
|