coding-tool-x 3.7.5 → 3.7.7
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/dist/web/assets/{Analytics-DnnufrQi.js → Analytics-B6FzPvdJ.js} +1 -1
- package/dist/web/assets/{ConfigTemplates-BTVhMSfe.js → ConfigTemplates-DXVnqR5c.js} +1 -1
- package/dist/web/assets/{Home-D03PEREx.js → Home-FHJB7hYI.js} +1 -1
- package/dist/web/assets/{PluginManager-BiMqEiyQ.js → PluginManager-D8RToURX.js} +1 -1
- package/dist/web/assets/ProjectList-Cdz4jnOg.js +1 -0
- package/dist/web/assets/ProjectList-VeMNXfgO.css +1 -0
- package/dist/web/assets/SessionList-C4chCPxK.css +1 -0
- package/dist/web/assets/SessionList-XYydFlQg.js +1 -0
- package/dist/web/assets/{SkillManager-DAy8k5CL.js → SkillManager-Bu9p7mMb.js} +1 -1
- package/dist/web/assets/{WorkspaceManager-Bs2D6R9j.js → WorkspaceManager-Bx0Gj3Mk.js} +1 -1
- package/dist/web/assets/index-DxkAvn4g.js +2 -0
- package/dist/web/assets/index-S8wrU2Nf.css +1 -0
- package/dist/web/index.html +2 -2
- package/package.json +1 -1
- package/src/commands/daemon.js +3 -1
- package/src/commands/port-config.js +0 -2
- package/src/commands/switch.js +1 -5
- package/src/commands/ui.js +4 -3
- package/src/commands/workspace.js +35 -3
- package/src/config/loader.js +76 -5
- package/src/index.js +11 -0
- package/src/server/api/config.js +0 -3
- package/src/server/api/omp-channels.js +50 -7
- package/src/server/api/settings.js +0 -2
- package/src/server/api/workspaces.js +39 -1
- package/src/server/services/dashboard-snapshot-worker.js +2 -1
- package/src/server/services/https-cert.js +3 -2
- package/src/server/services/native-oauth-adapters.js +2 -1
- package/src/server/services/omp-auth-provider-worker.js +29 -0
- package/src/server/services/omp-auth-providers.js +138 -11
- package/src/server/services/omp-config.js +7 -4
- package/src/server/services/omp-sessions.js +18 -14
- package/src/server/services/plugins-service.js +2 -1
- package/src/server/services/project-snapshots.js +8 -1
- package/src/server/services/session-snapshots.js +10 -1
- package/src/server/services/sessions.js +35 -19
- package/src/server/services/snapshot-cache.js +47 -3
- package/src/server/services/web-build.js +2 -1
- package/src/server/services/workspace-service.js +204 -98
- package/src/utils/cli-flags.js +7 -0
- package/src/utils/session.js +10 -4
- package/dist/web/assets/ProjectList-C6BA3V49.js +0 -1
- package/dist/web/assets/ProjectList-DQ5ZCJO2.css +0 -1
- package/dist/web/assets/SessionList--9Bl6EhV.css +0 -1
- package/dist/web/assets/SessionList-DChG04jz.js +0 -1
- package/dist/web/assets/index-DnDBHGCC.js +0 -2
- package/dist/web/assets/index-U3TOywtB.css +0 -1
|
@@ -61,7 +61,10 @@ function runOmpCommand(command, args = [], env = process.env, options = {}, stdi
|
|
|
61
61
|
encoding: 'utf8',
|
|
62
62
|
env: buildCommandEnv(env),
|
|
63
63
|
stdio,
|
|
64
|
-
timeout: options.timeout || 3000
|
|
64
|
+
timeout: options.timeout || 3000,
|
|
65
|
+
// On Windows, an npm/PowerShell command shim can otherwise create a visible
|
|
66
|
+
// console every time we probe OMP or resolve its configuration directory.
|
|
67
|
+
windowsHide: true
|
|
65
68
|
});
|
|
66
69
|
}
|
|
67
70
|
|
|
@@ -76,8 +79,8 @@ function readOmpAgentDirFromCommand(command, env = process.env, options = {}) {
|
|
|
76
79
|
}
|
|
77
80
|
|
|
78
81
|
function getOmpAgentDir(env = process.env, options = {}) {
|
|
79
|
-
const runtime = options.runtime || resolveOmpRuntime(env, options);
|
|
80
|
-
if (runtime
|
|
82
|
+
const runtime = options.runtime || (options.resolveRuntime === false ? null : resolveOmpRuntime(env, options));
|
|
83
|
+
if (runtime?.runtime === 'omp' && runtime.installed) {
|
|
81
84
|
const commandAgentDir = readOmpAgentDirFromCommand(runtime.command, env, options);
|
|
82
85
|
if (commandAgentDir) {
|
|
83
86
|
return commandAgentDir;
|
|
@@ -87,7 +90,7 @@ function getOmpAgentDir(env = process.env, options = {}) {
|
|
|
87
90
|
}
|
|
88
91
|
|
|
89
92
|
function getOmpPaths(env = process.env, options = {}) {
|
|
90
|
-
const runtime = options.runtime || resolveOmpRuntime(env, options);
|
|
93
|
+
const runtime = options.runtime || (options.resolveRuntime === false ? null : resolveOmpRuntime(env, options));
|
|
91
94
|
const agentDir = getOmpAgentDir(env, { ...options, runtime });
|
|
92
95
|
return {
|
|
93
96
|
agentDir,
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
2
|
const path = require('path');
|
|
3
|
-
const { execFileSync } = require('child_process');
|
|
4
3
|
const { PATHS, HOME_DIR } = require('../../config/paths');
|
|
5
|
-
const { getOmpCommand, getOmpPaths,
|
|
4
|
+
const { getOmpCommand, getOmpPaths, resolveOmpRuntime } = require('./omp-config');
|
|
6
5
|
|
|
7
6
|
const PROJECT_ORDER_FILE = PATHS.ompProjectOrder;
|
|
8
7
|
const SESSION_ORDER_FILE = PATHS.ompSessionOrder;
|
|
@@ -213,7 +212,20 @@ function parseSessionFile(filePath) {
|
|
|
213
212
|
};
|
|
214
213
|
}
|
|
215
214
|
|
|
216
|
-
function
|
|
215
|
+
function getOmpSessionPaths() {
|
|
216
|
+
// Reading historical sessions normally only needs OMP's native directory
|
|
217
|
+
// convention. Avoid starting the CLI here: project/count snapshot workers
|
|
218
|
+
// invoke this path independently, which made Windows repeatedly open
|
|
219
|
+
// PowerShell shims. Keep the CLI lookup as a fallback for non-standard OMP
|
|
220
|
+
// locations that cannot be derived from the environment.
|
|
221
|
+
const nativePaths = getOmpPaths(process.env, { resolveRuntime: false });
|
|
222
|
+
if (fs.existsSync(nativePaths.agentDir) || fs.existsSync(nativePaths.sessions)) {
|
|
223
|
+
return nativePaths;
|
|
224
|
+
}
|
|
225
|
+
return getOmpPaths();
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function scanSessionFiles(rootDir = getOmpSessionPaths().sessions) {
|
|
217
229
|
if (!fs.existsSync(rootDir)) {
|
|
218
230
|
return [];
|
|
219
231
|
}
|
|
@@ -462,20 +474,12 @@ function isOmpCliInstalled() {
|
|
|
462
474
|
}
|
|
463
475
|
|
|
464
476
|
let installed = false;
|
|
465
|
-
|
|
477
|
+
const nativePaths = getOmpSessionPaths();
|
|
478
|
+
if (fs.existsSync(nativePaths.agentDir)) {
|
|
466
479
|
installed = true;
|
|
467
480
|
} else {
|
|
468
481
|
const runtime = resolveOmpRuntime();
|
|
469
|
-
|
|
470
|
-
installed = true;
|
|
471
|
-
} else {
|
|
472
|
-
try {
|
|
473
|
-
execFileSync(getOmpCommand(), ['--version'], { stdio: 'ignore', timeout: 3000 });
|
|
474
|
-
installed = true;
|
|
475
|
-
} catch {
|
|
476
|
-
installed = false;
|
|
477
|
-
}
|
|
478
|
-
}
|
|
482
|
+
installed = Boolean(runtime && runtime.installed);
|
|
479
483
|
}
|
|
480
484
|
|
|
481
485
|
ompInstallCache = {
|
|
@@ -6,6 +6,8 @@ const {
|
|
|
6
6
|
} = require('./snapshot-cache');
|
|
7
7
|
|
|
8
8
|
const PROJECT_SNAPSHOT_TTL_MS = 60 * 1000;
|
|
9
|
+
const PROJECT_SNAPSHOT_WAIT_ON_MISS_MS = process.env.NODE_ENV === 'test' ? 0 : 1500;
|
|
10
|
+
const PROJECT_SNAPSHOT_WAIT_ON_FORCE_MS = process.env.NODE_ENV === 'test' ? 0 : 1500;
|
|
9
11
|
|
|
10
12
|
function projectListKey(source) {
|
|
11
13
|
return `projects:list:${source}`;
|
|
@@ -33,7 +35,10 @@ async function getProjectListSnapshot(source, {
|
|
|
33
35
|
fallbackValue,
|
|
34
36
|
refresh,
|
|
35
37
|
force,
|
|
36
|
-
backgroundOnMiss: !force
|
|
38
|
+
backgroundOnMiss: !force,
|
|
39
|
+
staleWhileForce: true,
|
|
40
|
+
waitOnMissMs: PROJECT_SNAPSHOT_WAIT_ON_MISS_MS,
|
|
41
|
+
waitOnForceMs: PROJECT_SNAPSHOT_WAIT_ON_FORCE_MS
|
|
37
42
|
});
|
|
38
43
|
}
|
|
39
44
|
|
|
@@ -44,6 +49,8 @@ function invalidateProjectSnapshots(source) {
|
|
|
44
49
|
|
|
45
50
|
module.exports = {
|
|
46
51
|
PROJECT_SNAPSHOT_TTL_MS,
|
|
52
|
+
PROJECT_SNAPSHOT_WAIT_ON_MISS_MS,
|
|
53
|
+
PROJECT_SNAPSHOT_WAIT_ON_FORCE_MS,
|
|
47
54
|
projectListKey,
|
|
48
55
|
projectCountKey,
|
|
49
56
|
emptyProjectList,
|
|
@@ -9,7 +9,9 @@ const {
|
|
|
9
9
|
|
|
10
10
|
const SESSION_SNAPSHOT_TTL_MS = 60 * 1000;
|
|
11
11
|
const SESSION_SNAPSHOT_DEFER_MS = process.env.NODE_ENV === 'test' ? 0 : 750;
|
|
12
|
-
const SESSION_SNAPSHOT_WORKER_TIMEOUT_MS =
|
|
12
|
+
const SESSION_SNAPSHOT_WORKER_TIMEOUT_MS = 180 * 1000;
|
|
13
|
+
const SESSION_SNAPSHOT_WAIT_ON_MISS_MS = process.env.NODE_ENV === 'test' ? 0 : 2500;
|
|
14
|
+
const SESSION_SNAPSHOT_WAIT_ON_FORCE_MS = process.env.NODE_ENV === 'test' ? 0 : 2500;
|
|
13
15
|
|
|
14
16
|
function sessionListKey(source, projectName) {
|
|
15
17
|
return `sessions:list:${source}:${projectName}`;
|
|
@@ -44,6 +46,7 @@ function runSessionSnapshotWorker(source, projectName, config = {}, options = {}
|
|
|
44
46
|
const workerPath = path.join(__dirname, 'session-snapshot-worker.js');
|
|
45
47
|
const child = fork(workerPath, [], {
|
|
46
48
|
stdio: ['ignore', 'ignore', 'pipe', 'ipc'],
|
|
49
|
+
windowsHide: true,
|
|
47
50
|
env: {
|
|
48
51
|
...process.env,
|
|
49
52
|
CC_TOOL_SESSION_SNAPSHOT_WORKER: '1'
|
|
@@ -113,6 +116,9 @@ async function getSessionListSnapshot(source, projectName, {
|
|
|
113
116
|
refresh,
|
|
114
117
|
force: process.env.NODE_ENV === 'test' ? true : force,
|
|
115
118
|
backgroundOnMiss: process.env.NODE_ENV === 'test' ? false : !force,
|
|
119
|
+
staleWhileForce: true,
|
|
120
|
+
waitOnMissMs: SESSION_SNAPSHOT_WAIT_ON_MISS_MS,
|
|
121
|
+
waitOnForceMs: SESSION_SNAPSHOT_WAIT_ON_FORCE_MS,
|
|
116
122
|
deferMs: SESSION_SNAPSHOT_DEFER_MS
|
|
117
123
|
});
|
|
118
124
|
}
|
|
@@ -128,6 +134,9 @@ function invalidateSessionSnapshots(source, projectName = null) {
|
|
|
128
134
|
|
|
129
135
|
module.exports = {
|
|
130
136
|
SESSION_SNAPSHOT_TTL_MS,
|
|
137
|
+
SESSION_SNAPSHOT_WAIT_ON_MISS_MS,
|
|
138
|
+
SESSION_SNAPSHOT_WAIT_ON_FORCE_MS,
|
|
139
|
+
SESSION_SNAPSHOT_WORKER_TIMEOUT_MS,
|
|
131
140
|
defaultProjectInfo,
|
|
132
141
|
emptySessionList,
|
|
133
142
|
getSessionListSnapshot,
|
|
@@ -17,6 +17,17 @@ const CLAUDE_PROJECTS_DIR = NATIVE_PATHS.claude.projects;
|
|
|
17
17
|
const CODEX_PROJECTS_DIR = path.join(path.dirname(NATIVE_PATHS.codex.config), 'projects');
|
|
18
18
|
const GEMINI_PROJECTS_DIR = path.join(path.dirname(NATIVE_PATHS.gemini.env), 'projects');
|
|
19
19
|
|
|
20
|
+
function resolveProjectsDir(config = {}) {
|
|
21
|
+
return config.projectsDir || CLAUDE_PROJECTS_DIR;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function withResolvedProjectsDir(config = {}) {
|
|
25
|
+
return {
|
|
26
|
+
...config,
|
|
27
|
+
projectsDir: resolveProjectsDir(config)
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
20
31
|
// Base directory for cc-tool data
|
|
21
32
|
function getCcToolDir() {
|
|
22
33
|
return PATHS.base;
|
|
@@ -182,7 +193,7 @@ function sliceClaudeSessionContentByUserMessage(content, afterUserMessageNumber)
|
|
|
182
193
|
|
|
183
194
|
// Get all projects with stats (async version)
|
|
184
195
|
async function getProjects(config) {
|
|
185
|
-
const projectsDir = config
|
|
196
|
+
const projectsDir = resolveProjectsDir(config);
|
|
186
197
|
|
|
187
198
|
if (!fs.existsSync(projectsDir)) {
|
|
188
199
|
return [];
|
|
@@ -383,16 +394,17 @@ function extractCwdFromSessionHeader(sessionFile) {
|
|
|
383
394
|
|
|
384
395
|
// Get projects with detailed stats (with caching) - async version
|
|
385
396
|
async function getProjectsWithStats(config, options = {}) {
|
|
397
|
+
const resolvedConfig = withResolvedProjectsDir(config);
|
|
386
398
|
if (!options.force) {
|
|
387
399
|
// Check enhanced cache first
|
|
388
|
-
const cacheKey = `${CacheKeys.PROJECTS}${
|
|
400
|
+
const cacheKey = `${CacheKeys.PROJECTS}${resolvedConfig.projectsDir}`;
|
|
389
401
|
const enhancedCached = globalCache.get(cacheKey);
|
|
390
402
|
if (enhancedCached) {
|
|
391
403
|
return enhancedCached;
|
|
392
404
|
}
|
|
393
405
|
|
|
394
406
|
// Check old cache
|
|
395
|
-
const cached = getCachedProjects(
|
|
407
|
+
const cached = getCachedProjects(resolvedConfig);
|
|
396
408
|
if (cached) {
|
|
397
409
|
globalCache.set(cacheKey, cached, 300000); // 5分钟
|
|
398
410
|
return cached;
|
|
@@ -400,22 +412,22 @@ async function getProjectsWithStats(config, options = {}) {
|
|
|
400
412
|
}
|
|
401
413
|
|
|
402
414
|
try {
|
|
403
|
-
const data = await buildProjectsWithStats(
|
|
415
|
+
const data = await buildProjectsWithStats(resolvedConfig);
|
|
404
416
|
if (!Array.isArray(data)) {
|
|
405
|
-
console.warn(`[getProjectsWithStats] Unexpected non-array result for ${
|
|
417
|
+
console.warn(`[getProjectsWithStats] Unexpected non-array result for ${resolvedConfig.projectsDir}, returning empty array.`);
|
|
406
418
|
return [];
|
|
407
419
|
}
|
|
408
|
-
setCachedProjects(
|
|
409
|
-
globalCache.set(`${CacheKeys.PROJECTS}${
|
|
420
|
+
setCachedProjects(resolvedConfig, data);
|
|
421
|
+
globalCache.set(`${CacheKeys.PROJECTS}${resolvedConfig.projectsDir}`, data, 300000);
|
|
410
422
|
return data;
|
|
411
423
|
} catch (err) {
|
|
412
|
-
console.error(`[getProjectsWithStats] Failed to build projects for ${
|
|
424
|
+
console.error(`[getProjectsWithStats] Failed to build projects for ${resolvedConfig.projectsDir}:`, err);
|
|
413
425
|
return [];
|
|
414
426
|
}
|
|
415
427
|
}
|
|
416
428
|
|
|
417
429
|
async function buildProjectsWithStats(config) {
|
|
418
|
-
const projectsDir = config
|
|
430
|
+
const projectsDir = resolveProjectsDir(config);
|
|
419
431
|
|
|
420
432
|
if (!fs.existsSync(projectsDir)) {
|
|
421
433
|
return [];
|
|
@@ -482,7 +494,7 @@ async function buildProjectsWithStats(config) {
|
|
|
482
494
|
|
|
483
495
|
// 获取 Claude 项目/会话数量(轻量统计)
|
|
484
496
|
function getProjectAndSessionCounts(config) {
|
|
485
|
-
const projectsDir = config
|
|
497
|
+
const projectsDir = resolveProjectsDir(config);
|
|
486
498
|
if (!fs.existsSync(projectsDir)) {
|
|
487
499
|
return { projectCount: 0, sessionCount: 0 };
|
|
488
500
|
}
|
|
@@ -672,7 +684,8 @@ async function getSessionsForProject(config, projectName, options = {}) {
|
|
|
672
684
|
|
|
673
685
|
// Delete a session
|
|
674
686
|
function deleteSession(config, projectName, sessionId) {
|
|
675
|
-
const
|
|
687
|
+
const resolvedConfig = withResolvedProjectsDir(config);
|
|
688
|
+
const projectDir = path.join(resolvedConfig.projectsDir, projectName);
|
|
676
689
|
const sessionFile = path.join(projectDir, sessionId + '.jsonl');
|
|
677
690
|
|
|
678
691
|
if (!fs.existsSync(sessionFile)) {
|
|
@@ -680,14 +693,15 @@ function deleteSession(config, projectName, sessionId) {
|
|
|
680
693
|
}
|
|
681
694
|
|
|
682
695
|
fs.unlinkSync(sessionFile);
|
|
683
|
-
invalidateProjectsCache(
|
|
696
|
+
invalidateProjectsCache(resolvedConfig);
|
|
684
697
|
invalidateSessionResultCache(projectName);
|
|
685
698
|
return { success: true };
|
|
686
699
|
}
|
|
687
700
|
|
|
688
701
|
// Fork a session
|
|
689
702
|
function forkSession(config, projectName, sessionId, options = {}) {
|
|
690
|
-
const
|
|
703
|
+
const resolvedConfig = withResolvedProjectsDir(config);
|
|
704
|
+
const projectDir = path.join(resolvedConfig.projectsDir, projectName);
|
|
691
705
|
const sessionFile = path.join(projectDir, sessionId + '.jsonl');
|
|
692
706
|
|
|
693
707
|
if (!fs.existsSync(sessionFile)) {
|
|
@@ -716,7 +730,7 @@ function forkSession(config, projectName, sessionId, options = {}) {
|
|
|
716
730
|
const { setAlias } = require('./alias');
|
|
717
731
|
setAlias(newSessionId, options.alias);
|
|
718
732
|
}
|
|
719
|
-
invalidateProjectsCache(
|
|
733
|
+
invalidateProjectsCache(resolvedConfig);
|
|
720
734
|
invalidateSessionResultCache(projectName);
|
|
721
735
|
|
|
722
736
|
return {
|
|
@@ -768,7 +782,8 @@ function saveSessionOrder(projectName, order) {
|
|
|
768
782
|
|
|
769
783
|
// Delete a project (remove the entire project directory)
|
|
770
784
|
function deleteProject(config, projectName) {
|
|
771
|
-
const
|
|
785
|
+
const resolvedConfig = withResolvedProjectsDir(config);
|
|
786
|
+
const projectDir = path.join(resolvedConfig.projectsDir, projectName);
|
|
772
787
|
|
|
773
788
|
if (!fs.existsSync(projectDir)) {
|
|
774
789
|
throw new Error('Project not found');
|
|
@@ -778,19 +793,20 @@ function deleteProject(config, projectName) {
|
|
|
778
793
|
fs.rmSync(projectDir, { recursive: true, force: true });
|
|
779
794
|
|
|
780
795
|
// Remove from order file if exists
|
|
781
|
-
const order = getProjectOrder(
|
|
796
|
+
const order = getProjectOrder(resolvedConfig);
|
|
782
797
|
const newOrder = order.filter(name => name !== projectName);
|
|
783
798
|
if (newOrder.length !== order.length) {
|
|
784
|
-
saveProjectOrder(
|
|
799
|
+
saveProjectOrder(resolvedConfig, newOrder);
|
|
785
800
|
}
|
|
786
801
|
|
|
787
|
-
invalidateProjectsCache(
|
|
802
|
+
invalidateProjectsCache(resolvedConfig);
|
|
788
803
|
return { success: true };
|
|
789
804
|
}
|
|
790
805
|
|
|
791
806
|
// Search sessions for keyword
|
|
792
807
|
function searchSessions(config, projectName, keyword, contextLength = 15) {
|
|
793
|
-
const
|
|
808
|
+
const resolvedConfig = withResolvedProjectsDir(config);
|
|
809
|
+
const projectDir = path.join(resolvedConfig.projectsDir, projectName);
|
|
794
810
|
|
|
795
811
|
if (!fs.existsSync(projectDir)) {
|
|
796
812
|
return [];
|
|
@@ -9,6 +9,7 @@ const DEFAULT_TTL_MS = 60 * 1000;
|
|
|
9
9
|
const SNAPSHOT_VERSION = 1;
|
|
10
10
|
const DEFAULT_BACKGROUND_DELAY_MS = process.env.NODE_ENV === 'test' ? 0 : 750;
|
|
11
11
|
const DEFAULT_BACKGROUND_GAP_MS = process.env.NODE_ENV === 'test' ? 0 : 100;
|
|
12
|
+
const WAIT_TIMEOUT = Symbol('snapshot-wait-timeout');
|
|
12
13
|
|
|
13
14
|
const snapshots = new Map();
|
|
14
15
|
const inflight = new Map();
|
|
@@ -196,6 +197,28 @@ function isExpired(entry, ttlMs, nowMs) {
|
|
|
196
197
|
return nowMs - Number(entry.updatedAtMs || 0) >= ttlMs;
|
|
197
198
|
}
|
|
198
199
|
|
|
200
|
+
async function waitForRefresh(promise, waitMs) {
|
|
201
|
+
const timeoutMs = Number(waitMs) || 0;
|
|
202
|
+
if (timeoutMs <= 0) return { skipped: true };
|
|
203
|
+
|
|
204
|
+
let timer = null;
|
|
205
|
+
const result = await Promise.race([
|
|
206
|
+
promise
|
|
207
|
+
.then(entry => ({ entry }))
|
|
208
|
+
.catch(error => ({ error })),
|
|
209
|
+
new Promise(resolve => {
|
|
210
|
+
timer = setTimeout(() => resolve(WAIT_TIMEOUT), timeoutMs);
|
|
211
|
+
})
|
|
212
|
+
]);
|
|
213
|
+
if (timer) {
|
|
214
|
+
clearTimeout(timer);
|
|
215
|
+
}
|
|
216
|
+
if (result === WAIT_TIMEOUT) {
|
|
217
|
+
return { timedOut: true };
|
|
218
|
+
}
|
|
219
|
+
return result;
|
|
220
|
+
}
|
|
221
|
+
|
|
199
222
|
function makeMeta(entry, {
|
|
200
223
|
key,
|
|
201
224
|
stale,
|
|
@@ -220,7 +243,9 @@ async function getSnapshot(key, {
|
|
|
220
243
|
force = false,
|
|
221
244
|
staleWhileForce = false,
|
|
222
245
|
nowMs = Date.now(),
|
|
223
|
-
deferMs = DEFAULT_BACKGROUND_DELAY_MS
|
|
246
|
+
deferMs = DEFAULT_BACKGROUND_DELAY_MS,
|
|
247
|
+
waitOnMissMs = 0,
|
|
248
|
+
waitOnForceMs = 0
|
|
224
249
|
} = {}) {
|
|
225
250
|
if (typeof refresh !== 'function') {
|
|
226
251
|
const entry = getEntry(key);
|
|
@@ -241,7 +266,26 @@ async function getSnapshot(key, {
|
|
|
241
266
|
let refreshError = null;
|
|
242
267
|
|
|
243
268
|
if (expired) {
|
|
244
|
-
|
|
269
|
+
const coldWaitMs = !entry
|
|
270
|
+
? (force ? (waitOnForceMs || waitOnMissMs) : waitOnMissMs)
|
|
271
|
+
: 0;
|
|
272
|
+
|
|
273
|
+
if (!entry && Number(coldWaitMs) > 0) {
|
|
274
|
+
refreshPromise = refreshSnapshot(key, refresh, { bypassInflight: force });
|
|
275
|
+
const waitResult = await waitForRefresh(refreshPromise, coldWaitMs);
|
|
276
|
+
if (waitResult.entry) {
|
|
277
|
+
entry = waitResult.entry;
|
|
278
|
+
expired = false;
|
|
279
|
+
refreshPromise = null;
|
|
280
|
+
} else if (waitResult.error) {
|
|
281
|
+
refreshError = waitResult.error?.message || String(waitResult.error);
|
|
282
|
+
entry = getEntry(key);
|
|
283
|
+
if (entry) {
|
|
284
|
+
expired = isExpired(entry, ttlMs, nowMs);
|
|
285
|
+
}
|
|
286
|
+
refreshPromise = null;
|
|
287
|
+
}
|
|
288
|
+
} else if (!entry && backgroundOnMiss === false) {
|
|
245
289
|
try {
|
|
246
290
|
entry = await refreshSnapshot(key, refresh, { bypassInflight: force });
|
|
247
291
|
expired = false;
|
|
@@ -251,7 +295,7 @@ async function getSnapshot(key, {
|
|
|
251
295
|
if (!entry) throw error;
|
|
252
296
|
}
|
|
253
297
|
} else {
|
|
254
|
-
refreshPromise = refreshSnapshot(key, refresh, { defer:
|
|
298
|
+
refreshPromise = refreshSnapshot(key, refresh, { defer: !force, deferMs, bypassInflight: force });
|
|
255
299
|
}
|
|
256
300
|
}
|
|
257
301
|
|