@fnndsc/cumin 3.2.4
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 +21 -0
- package/README.md +78 -0
- package/dist/activities/chrisActivity.d.ts +19 -0
- package/dist/activities/chrisActivity.js +29 -0
- package/dist/activities/chrisActivity.js.map +1 -0
- package/dist/cache/index.d.ts +9 -0
- package/dist/cache/index.js +15 -0
- package/dist/cache/index.js.map +1 -0
- package/dist/cache/listCache.d.ts +182 -0
- package/dist/cache/listCache.js +266 -0
- package/dist/cache/listCache.js.map +1 -0
- package/dist/cache/procCache.d.ts +150 -0
- package/dist/cache/procCache.js +257 -0
- package/dist/cache/procCache.js.map +1 -0
- package/dist/cli.d.ts +7 -0
- package/dist/cli.js +43 -0
- package/dist/cli.js.map +1 -0
- package/dist/compute/computeResources.d.ts +65 -0
- package/dist/compute/computeResources.js +113 -0
- package/dist/compute/computeResources.js.map +1 -0
- package/dist/config/config.d.ts +221 -0
- package/dist/config/config.js +350 -0
- package/dist/config/config.js.map +1 -0
- package/dist/connect/chrisConnection.d.ts +121 -0
- package/dist/connect/chrisConnection.js +303 -0
- package/dist/connect/chrisConnection.js.map +1 -0
- package/dist/context/chrisContext.d.ts +110 -0
- package/dist/context/chrisContext.js +324 -0
- package/dist/context/chrisContext.js.map +1 -0
- package/dist/error/errorStack.d.ts +115 -0
- package/dist/error/errorStack.js +175 -0
- package/dist/error/errorStack.js.map +1 -0
- package/dist/feeds/chrisFeed.d.ts +188 -0
- package/dist/feeds/chrisFeed.js +432 -0
- package/dist/feeds/chrisFeed.js.map +1 -0
- package/dist/filebrowser/chrisFileBrowser.d.ts +77 -0
- package/dist/filebrowser/chrisFileBrowser.js +134 -0
- package/dist/filebrowser/chrisFileBrowser.js.map +1 -0
- package/dist/filebrowser/chrisFiles.d.ts +67 -0
- package/dist/filebrowser/chrisFiles.js +130 -0
- package/dist/filebrowser/chrisFiles.js.map +1 -0
- package/dist/filebrowser/chrisPACS.d.ts +43 -0
- package/dist/filebrowser/chrisPACS.js +103 -0
- package/dist/filebrowser/chrisPACS.js.map +1 -0
- package/dist/filebrowser/chrisPipeline.d.ts +47 -0
- package/dist/filebrowser/chrisPipeline.js +118 -0
- package/dist/filebrowser/chrisPipeline.js.map +1 -0
- package/dist/groups/chrisGroups.d.ts +11 -0
- package/dist/groups/chrisGroups.js +18 -0
- package/dist/groups/chrisGroups.js.map +1 -0
- package/dist/index.d.ts +68 -0
- package/dist/index.js +86 -0
- package/dist/index.js.map +1 -0
- package/dist/io/browser_io.d.ts +97 -0
- package/dist/io/browser_io.js +149 -0
- package/dist/io/browser_io.js.map +1 -0
- package/dist/io/chrisIO.d.ts +110 -0
- package/dist/io/chrisIO.js +415 -0
- package/dist/io/chrisIO.js.map +1 -0
- package/dist/io/io.d.ts +21 -0
- package/dist/io/io.js +6 -0
- package/dist/io/io.js.map +1 -0
- package/dist/io/node_io.d.ts +30 -0
- package/dist/io/node_io.js +188 -0
- package/dist/io/node_io.js.map +1 -0
- package/dist/jobs/chrisJob.d.ts +43 -0
- package/dist/jobs/chrisJob.js +169 -0
- package/dist/jobs/chrisJob.js.map +1 -0
- package/dist/jobs/index.d.ts +7 -0
- package/dist/jobs/index.js +24 -0
- package/dist/jobs/index.js.map +1 -0
- package/dist/jobs/jobMonitor.d.ts +46 -0
- package/dist/jobs/jobMonitor.js +102 -0
- package/dist/jobs/jobMonitor.js.map +1 -0
- package/dist/pacs/chrisPACS.d.ts +200 -0
- package/dist/pacs/chrisPACS.js +562 -0
- package/dist/pacs/chrisPACS.js.map +1 -0
- package/dist/path/chrisPath.d.ts +78 -0
- package/dist/path/chrisPath.js +190 -0
- package/dist/path/chrisPath.js.map +1 -0
- package/dist/pipelines/chrisPipeline.d.ts +86 -0
- package/dist/pipelines/chrisPipeline.js +180 -0
- package/dist/pipelines/chrisPipeline.js.map +1 -0
- package/dist/pluginmetas/chrisPluginMetas.d.ts +11 -0
- package/dist/pluginmetas/chrisPluginMetas.js +18 -0
- package/dist/pluginmetas/chrisPluginMetas.js.map +1 -0
- package/dist/plugins/chrisPluginMetaPlugins.d.ts +12 -0
- package/dist/plugins/chrisPluginMetaPlugins.js +19 -0
- package/dist/plugins/chrisPluginMetaPlugins.js.map +1 -0
- package/dist/plugins/chrisPlugins.d.ts +127 -0
- package/dist/plugins/chrisPlugins.js +528 -0
- package/dist/plugins/chrisPlugins.js.map +1 -0
- package/dist/resources/chrisEmbeddedResourceGroup.d.ts +73 -0
- package/dist/resources/chrisEmbeddedResourceGroup.js +148 -0
- package/dist/resources/chrisEmbeddedResourceGroup.js.map +1 -0
- package/dist/resources/chrisObjContext.d.ts +16 -0
- package/dist/resources/chrisObjContext.js +109 -0
- package/dist/resources/chrisObjContext.js.map +1 -0
- package/dist/resources/chrisResourceGroup.d.ts +30 -0
- package/dist/resources/chrisResourceGroup.js +47 -0
- package/dist/resources/chrisResourceGroup.js.map +1 -0
- package/dist/resources/chrisResources.d.ts +163 -0
- package/dist/resources/chrisResources.js +415 -0
- package/dist/resources/chrisResources.js.map +1 -0
- package/dist/tags/chrisTags.d.ts +11 -0
- package/dist/tags/chrisTags.js +18 -0
- package/dist/tags/chrisTags.js.map +1 -0
- package/dist/users/chrisUsers.d.ts +34 -0
- package/dist/users/chrisUsers.js +57 -0
- package/dist/users/chrisUsers.js.map +1 -0
- package/dist/utils/keypair.d.ts +77 -0
- package/dist/utils/keypair.js +144 -0
- package/dist/utils/keypair.js.map +1 -0
- package/dist/utils/result.d.ts +77 -0
- package/dist/utils/result.js +58 -0
- package/dist/utils/result.js.map +1 -0
- package/dist/utils/searchable.d.ts +143 -0
- package/dist/utils/searchable.js +212 -0
- package/dist/utils/searchable.js.map +1 -0
- package/dist/workflows/chrisWorkflows.d.ts +11 -0
- package/dist/workflows/chrisWorkflows.js +18 -0
- package/dist/workflows/chrisWorkflows.js.map +1 -0
- package/package.json +55 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 FNNDSC / BCH
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
```
|
|
2
|
+
____ _
|
|
3
|
+
/ ___| _ ___ ____(_)_ __
|
|
4
|
+
| | | | | | '_ \| '_ \| '_ \
|
|
5
|
+
| |__| |_| | | | | | | | | | |
|
|
6
|
+
\____\__,_|_| |_|_| |_|_| |_|
|
|
7
|
+
```
|
|
8
|
+
**Cumin Underpins Management Infrastructure Needs**
|
|
9
|
+
|
|
10
|
+
`cumin` is the infrastructure and state management layer of the ChRIS interface ecosystem. It is a backend library that handles the "dirty work" of connecting to ChRIS, managing authentication tokens, and persisting user sessions.
|
|
11
|
+
|
|
12
|
+
## Installation
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npm install @fnndsc/cumin
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Requires Node.js ≥ 20.12. Ships with TypeScript type definitions.
|
|
19
|
+
|
|
20
|
+
## Usage
|
|
21
|
+
|
|
22
|
+
```typescript
|
|
23
|
+
import { ChRISConnection, ChrisContext, ChrisIO } from "@fnndsc/cumin";
|
|
24
|
+
|
|
25
|
+
// The context engine remembers the active user, CUBE URL, and working directory
|
|
26
|
+
const context = new ChrisContext();
|
|
27
|
+
await context.init();
|
|
28
|
+
const cubeURL: string | null = await context.ChRISURL_get();
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
See [Core Features](#core-features) for the full API surface.
|
|
32
|
+
|
|
33
|
+
## Abstract
|
|
34
|
+
|
|
35
|
+
`cumin` abstracts the ChRIS REST API into a stateful, object-oriented environment. It is responsible for:
|
|
36
|
+
1. **Connection Management**: Handling authentication, token storage, and client initialization.
|
|
37
|
+
2. **Context Persistence**: Implementing the "Context" engine that remembers the active User, CUBE URL, and Working Directory.
|
|
38
|
+
3. **IO & Storage**: Providing an abstraction (`IStorageProvider`) for filesystem access, enabling support for both Node.js (via `fs`) and other environments.
|
|
39
|
+
|
|
40
|
+
## Role in the Ecosystem
|
|
41
|
+
|
|
42
|
+
In the "Sandwich Model" architecture, `cumin` is the bottom layer (just above the raw API client).
|
|
43
|
+
|
|
44
|
+
- **Consumers**: Primarily [`salsa`](https://github.com/FNNDSC/salsa) (logic) and [`chili`](https://github.com/FNNDSC/chili) (CLI state).
|
|
45
|
+
- **Environment**: Designed for Node.js but architected with interfaces to support browser environments.
|
|
46
|
+
|
|
47
|
+
## Core Features
|
|
48
|
+
|
|
49
|
+
- **`ChrisContext`**: The state machine for multi-tenant, multi-backend sessions.
|
|
50
|
+
- **`ChRISConnection`**: Wrapper for the low-level API client.
|
|
51
|
+
- **`IStorageProvider`**: Abstraction interface for reading/writing config and data.
|
|
52
|
+
- Includes `readBinary` for binary file uploads.
|
|
53
|
+
- Includes recursive directory traversal helpers.
|
|
54
|
+
- **`ChrisIO`**: High-level IO operations, including recursive directory uploading (`uploadLocalPath`).
|
|
55
|
+
- **`ChRISResource.resources_getAll()`**: Generic pagination handler.
|
|
56
|
+
- **`ListCache`** (`listCache_get()`): Session-scoped directory listing cache for the VFS.
|
|
57
|
+
- **`ProcCache`** (`procCache_get()`): Session-scoped job monitoring cache. Stores all visible plugin instances as a DAG (two flat maps: `instances` keyed by ID, `children`/`feedRoots` for tree traversal). Structure is permanent once built; only non-terminal instance statuses are refreshed on read. Powers the `/proc/feeds/` VFS provider in salsa.
|
|
58
|
+
|
|
59
|
+
## Developer Setup
|
|
60
|
+
|
|
61
|
+
To build the `cumin` library from source:
|
|
62
|
+
|
|
63
|
+
1. **Install dependencies:**
|
|
64
|
+
```bash
|
|
65
|
+
npm install
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
2. **Compile:**
|
|
69
|
+
```bash
|
|
70
|
+
npm run build
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## License
|
|
74
|
+
|
|
75
|
+
MIT — part of the [ChRIS Project](https://chrisproject.org).
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
_-30-_
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file ChRIS Activity
|
|
3
|
+
*
|
|
4
|
+
* This module defines the base Activity class for interactions with ChRIS.
|
|
5
|
+
*
|
|
6
|
+
* @module
|
|
7
|
+
*/
|
|
8
|
+
import Client from "@fnndsc/chrisapi";
|
|
9
|
+
/**
|
|
10
|
+
* Base class for ChRIS activities.
|
|
11
|
+
*/
|
|
12
|
+
export declare class Activity {
|
|
13
|
+
constructor();
|
|
14
|
+
/**
|
|
15
|
+
* Retrieves the ChRIS client instance asynchronously.
|
|
16
|
+
* @returns A Promise resolving to the Client instance or null.
|
|
17
|
+
*/
|
|
18
|
+
client_get(): Promise<Client | null>;
|
|
19
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @file ChRIS Activity
|
|
4
|
+
*
|
|
5
|
+
* This module defines the base Activity class for interactions with ChRIS.
|
|
6
|
+
*
|
|
7
|
+
* @module
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.Activity = void 0;
|
|
11
|
+
const chrisConnection_js_1 = require("../connect/chrisConnection.js");
|
|
12
|
+
/**
|
|
13
|
+
* Base class for ChRIS activities.
|
|
14
|
+
*/
|
|
15
|
+
class Activity {
|
|
16
|
+
// private _client: Client | null = null; // No longer needed
|
|
17
|
+
constructor() {
|
|
18
|
+
// Client is fetched lazily
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Retrieves the ChRIS client instance asynchronously.
|
|
22
|
+
* @returns A Promise resolving to the Client instance or null.
|
|
23
|
+
*/
|
|
24
|
+
async client_get() {
|
|
25
|
+
return await chrisConnection_js_1.chrisConnection.client_get();
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
exports.Activity = Activity;
|
|
29
|
+
//# sourceMappingURL=chrisActivity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chrisActivity.js","sourceRoot":"","sources":["../../src/activities/chrisActivity.ts"],"names":[],"mappings":";AAAA;;;;;;GAMG;;;AAGH,sEAAgE;AAEhE;;GAEG;AACH,MAAa,QAAQ;IACnB,6DAA6D;IAE7D;QACE,2BAA2B;IAC7B,CAAC;IAED;;;OAGG;IACI,KAAK,CAAC,UAAU;QACrB,OAAO,MAAM,oCAAe,CAAC,UAAU,EAAE,CAAC;IAC5C,CAAC;CACF;AAdD,4BAcC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Cache module exports.
|
|
3
|
+
*
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
export { ListCache, listCache_get } from './listCache.js';
|
|
7
|
+
export type { CacheStats, CacheResult, CacheOptions } from './listCache.js';
|
|
8
|
+
export { ProcCache, procCache_get } from './procCache.js';
|
|
9
|
+
export type { ProcInstance, ProcFeed, ProcWarmupProgress } from './procCache.js';
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.procCache_get = exports.ProcCache = exports.listCache_get = exports.ListCache = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* @file Cache module exports.
|
|
6
|
+
*
|
|
7
|
+
* @module
|
|
8
|
+
*/
|
|
9
|
+
var listCache_js_1 = require("./listCache.js");
|
|
10
|
+
Object.defineProperty(exports, "ListCache", { enumerable: true, get: function () { return listCache_js_1.ListCache; } });
|
|
11
|
+
Object.defineProperty(exports, "listCache_get", { enumerable: true, get: function () { return listCache_js_1.listCache_get; } });
|
|
12
|
+
var procCache_js_1 = require("./procCache.js");
|
|
13
|
+
Object.defineProperty(exports, "ProcCache", { enumerable: true, get: function () { return procCache_js_1.ProcCache; } });
|
|
14
|
+
Object.defineProperty(exports, "procCache_get", { enumerable: true, get: function () { return procCache_js_1.procCache_get; } });
|
|
15
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cache/index.ts"],"names":[],"mappings":";;;AAAA;;;;GAIG;AACH,+CAA0D;AAAjD,yGAAA,SAAS,OAAA;AAAE,6GAAA,aAAa,OAAA;AAEjC,+CAA0D;AAAjD,yGAAA,SAAS,OAAA;AAAE,6GAAA,aAAa,OAAA"}
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Enhanced Directory Listing Cache with TTL and dirty tracking.
|
|
3
|
+
*
|
|
4
|
+
* Provides a singleton cache for directory listings to avoid redundant API calls
|
|
5
|
+
* during tab completion and file operations. Features:
|
|
6
|
+
* - TTL-based expiration (different TTLs per path pattern)
|
|
7
|
+
* - Dirty flag tracking (local mutations mark cache as needing refresh)
|
|
8
|
+
* - LRU eviction (bounded memory usage)
|
|
9
|
+
* - Optimistic serving (serve stale cache while refreshing)
|
|
10
|
+
*
|
|
11
|
+
* Generic implementation - stores any data type keyed by path.
|
|
12
|
+
*
|
|
13
|
+
* @module
|
|
14
|
+
*/
|
|
15
|
+
/**
|
|
16
|
+
* Result returned by cache_get with freshness information.
|
|
17
|
+
*/
|
|
18
|
+
export interface CacheResult<T = unknown> {
|
|
19
|
+
/** The cached data */
|
|
20
|
+
data: T;
|
|
21
|
+
/** True if within TTL and not dirty */
|
|
22
|
+
fresh: boolean;
|
|
23
|
+
/** Age in milliseconds */
|
|
24
|
+
age: number;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Options for cache_set.
|
|
28
|
+
*/
|
|
29
|
+
export interface CacheOptions {
|
|
30
|
+
/** Override default TTL for this entry (milliseconds) */
|
|
31
|
+
ttl?: number;
|
|
32
|
+
/** Mark as dirty immediately */
|
|
33
|
+
dirty?: boolean;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Statistics about cache usage.
|
|
37
|
+
*/
|
|
38
|
+
export interface CacheStats {
|
|
39
|
+
/** Total number of cache hits (fresh). */
|
|
40
|
+
hits: number;
|
|
41
|
+
/** Total number of cache misses. */
|
|
42
|
+
misses: number;
|
|
43
|
+
/** Total number of stale hits (served but expired). */
|
|
44
|
+
staleHits: number;
|
|
45
|
+
/** Number of LRU evictions. */
|
|
46
|
+
evictions: number;
|
|
47
|
+
/** Number of entries currently in cache. */
|
|
48
|
+
entries: number;
|
|
49
|
+
/** Estimated total memory usage in bytes. */
|
|
50
|
+
totalSize: number;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Singleton cache for directory listings with TTL and dirty tracking.
|
|
54
|
+
*
|
|
55
|
+
* Generic cache that stores any data type keyed by path. Eliminates redundant
|
|
56
|
+
* API calls by caching directory contents after ls operations.
|
|
57
|
+
*
|
|
58
|
+
* Enhanced features:
|
|
59
|
+
* - TTL-based expiration: Different paths have different TTLs
|
|
60
|
+
* - Dirty flag tracking: Local mutations mark cache as needing refresh
|
|
61
|
+
* - LRU eviction: Bounded memory usage
|
|
62
|
+
* - Optimistic serving: Serve stale cache immediately, refresh in background
|
|
63
|
+
*
|
|
64
|
+
* Usage:
|
|
65
|
+
* ```typescript
|
|
66
|
+
* const cache = listCache_get();
|
|
67
|
+
*
|
|
68
|
+
* // Check cache (returns CacheResult with freshness info)
|
|
69
|
+
* const cached = cache.cache_get("/PUBLIC");
|
|
70
|
+
* if (cached) {
|
|
71
|
+
* console.log(cached.data); // Show immediately
|
|
72
|
+
* if (!cached.fresh) {
|
|
73
|
+
* console.log('Refreshing...');
|
|
74
|
+
* // Fetch fresh data in background
|
|
75
|
+
* }
|
|
76
|
+
* }
|
|
77
|
+
*
|
|
78
|
+
* // Store data
|
|
79
|
+
* cache.cache_set("/PUBLIC", items);
|
|
80
|
+
*
|
|
81
|
+
* // Mark dirty after mutation
|
|
82
|
+
* cache.cache_markDirty("/home/user");
|
|
83
|
+
*
|
|
84
|
+
* // Optimistic update
|
|
85
|
+
* cache.cache_update("/home/user", (items) => items.filter(i => i.name !== 'deleted.txt'));
|
|
86
|
+
* ```
|
|
87
|
+
*/
|
|
88
|
+
export declare class ListCache {
|
|
89
|
+
private static instance;
|
|
90
|
+
/** Cache storage: path → entry. */
|
|
91
|
+
private cache;
|
|
92
|
+
/** Maximum cache entries (LRU eviction). */
|
|
93
|
+
private maxEntries;
|
|
94
|
+
/** Default TTL for unconfigured paths (3 minutes). */
|
|
95
|
+
private defaultTTL;
|
|
96
|
+
/** Path-specific TTL configuration. */
|
|
97
|
+
private ttlConfig;
|
|
98
|
+
/** Cache statistics. */
|
|
99
|
+
private stats;
|
|
100
|
+
/**
|
|
101
|
+
* Private constructor to enforce singleton pattern.
|
|
102
|
+
*/
|
|
103
|
+
private constructor();
|
|
104
|
+
/**
|
|
105
|
+
* Gets the singleton instance of ListCache.
|
|
106
|
+
*
|
|
107
|
+
* @returns The ListCache singleton instance.
|
|
108
|
+
*/
|
|
109
|
+
static instance_get(): ListCache;
|
|
110
|
+
/**
|
|
111
|
+
* Retrieves data from cache with freshness information.
|
|
112
|
+
*
|
|
113
|
+
* @param path - The path to retrieve.
|
|
114
|
+
* @returns CacheResult with freshness info, or null if not cached.
|
|
115
|
+
*/
|
|
116
|
+
cache_get<T = unknown>(path: string): CacheResult<T> | null;
|
|
117
|
+
/**
|
|
118
|
+
* Stores data in cache with optional TTL override.
|
|
119
|
+
*
|
|
120
|
+
* @param path - The path to cache.
|
|
121
|
+
* @param data - The data to cache.
|
|
122
|
+
* @param options - Optional TTL and dirty flag.
|
|
123
|
+
*/
|
|
124
|
+
cache_set<T = unknown>(path: string, data: T, options?: CacheOptions): void;
|
|
125
|
+
/**
|
|
126
|
+
* Marks a cached path as dirty (needs refresh).
|
|
127
|
+
* Used when local mutations might have changed the data.
|
|
128
|
+
*
|
|
129
|
+
* @param path - The path to mark dirty.
|
|
130
|
+
*/
|
|
131
|
+
cache_markDirty(path: string): void;
|
|
132
|
+
/**
|
|
133
|
+
* Optimistically updates cache after a mutation.
|
|
134
|
+
* Example: After rm, remove item from parent directory cache.
|
|
135
|
+
*
|
|
136
|
+
* @param path - The parent directory path.
|
|
137
|
+
* @param updater - Function to transform cached data.
|
|
138
|
+
*/
|
|
139
|
+
cache_update<T = unknown>(path: string, updater: (data: T) => T): void;
|
|
140
|
+
/**
|
|
141
|
+
* Invalidates cache for a specific path or clears entire cache.
|
|
142
|
+
* Called by file operations that modify directory contents (mkdir, touch, rm, upload).
|
|
143
|
+
*
|
|
144
|
+
* @param path - Optional specific path to invalidate. If omitted, clears entire cache.
|
|
145
|
+
*/
|
|
146
|
+
cache_invalidate(path?: string): void;
|
|
147
|
+
/**
|
|
148
|
+
* @deprecated Use cache without cwd tracking. Cache persists across navigation.
|
|
149
|
+
* This method is kept for backward compatibility but does nothing.
|
|
150
|
+
*
|
|
151
|
+
* @param _newCwd - Ignored.
|
|
152
|
+
*/
|
|
153
|
+
cwd_update(_newCwd: string): void;
|
|
154
|
+
/**
|
|
155
|
+
* Gets TTL for a specific path based on configuration.
|
|
156
|
+
*
|
|
157
|
+
* @param path - The path to get TTL for.
|
|
158
|
+
* @returns TTL in milliseconds.
|
|
159
|
+
*/
|
|
160
|
+
private ttl_get;
|
|
161
|
+
/**
|
|
162
|
+
* Evicts oldest entries if cache exceeds max size.
|
|
163
|
+
*/
|
|
164
|
+
private evict_lru;
|
|
165
|
+
/**
|
|
166
|
+
* Gets cache statistics.
|
|
167
|
+
*
|
|
168
|
+
* @returns Cache statistics including hits, misses, and current state.
|
|
169
|
+
*/
|
|
170
|
+
stats_get(): CacheStats;
|
|
171
|
+
/**
|
|
172
|
+
* Resets cache statistics.
|
|
173
|
+
* Useful for testing and monitoring.
|
|
174
|
+
*/
|
|
175
|
+
stats_reset(): void;
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Convenience function to get the ListCache singleton instance.
|
|
179
|
+
*
|
|
180
|
+
* @returns The ListCache singleton instance.
|
|
181
|
+
*/
|
|
182
|
+
export declare function listCache_get(): ListCache;
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @file Enhanced Directory Listing Cache with TTL and dirty tracking.
|
|
4
|
+
*
|
|
5
|
+
* Provides a singleton cache for directory listings to avoid redundant API calls
|
|
6
|
+
* during tab completion and file operations. Features:
|
|
7
|
+
* - TTL-based expiration (different TTLs per path pattern)
|
|
8
|
+
* - Dirty flag tracking (local mutations mark cache as needing refresh)
|
|
9
|
+
* - LRU eviction (bounded memory usage)
|
|
10
|
+
* - Optimistic serving (serve stale cache while refreshing)
|
|
11
|
+
*
|
|
12
|
+
* Generic implementation - stores any data type keyed by path.
|
|
13
|
+
*
|
|
14
|
+
* @module
|
|
15
|
+
*/
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.ListCache = void 0;
|
|
18
|
+
exports.listCache_get = listCache_get;
|
|
19
|
+
/**
|
|
20
|
+
* Singleton cache for directory listings with TTL and dirty tracking.
|
|
21
|
+
*
|
|
22
|
+
* Generic cache that stores any data type keyed by path. Eliminates redundant
|
|
23
|
+
* API calls by caching directory contents after ls operations.
|
|
24
|
+
*
|
|
25
|
+
* Enhanced features:
|
|
26
|
+
* - TTL-based expiration: Different paths have different TTLs
|
|
27
|
+
* - Dirty flag tracking: Local mutations mark cache as needing refresh
|
|
28
|
+
* - LRU eviction: Bounded memory usage
|
|
29
|
+
* - Optimistic serving: Serve stale cache immediately, refresh in background
|
|
30
|
+
*
|
|
31
|
+
* Usage:
|
|
32
|
+
* ```typescript
|
|
33
|
+
* const cache = listCache_get();
|
|
34
|
+
*
|
|
35
|
+
* // Check cache (returns CacheResult with freshness info)
|
|
36
|
+
* const cached = cache.cache_get("/PUBLIC");
|
|
37
|
+
* if (cached) {
|
|
38
|
+
* console.log(cached.data); // Show immediately
|
|
39
|
+
* if (!cached.fresh) {
|
|
40
|
+
* console.log('Refreshing...');
|
|
41
|
+
* // Fetch fresh data in background
|
|
42
|
+
* }
|
|
43
|
+
* }
|
|
44
|
+
*
|
|
45
|
+
* // Store data
|
|
46
|
+
* cache.cache_set("/PUBLIC", items);
|
|
47
|
+
*
|
|
48
|
+
* // Mark dirty after mutation
|
|
49
|
+
* cache.cache_markDirty("/home/user");
|
|
50
|
+
*
|
|
51
|
+
* // Optimistic update
|
|
52
|
+
* cache.cache_update("/home/user", (items) => items.filter(i => i.name !== 'deleted.txt'));
|
|
53
|
+
* ```
|
|
54
|
+
*/
|
|
55
|
+
class ListCache {
|
|
56
|
+
/**
|
|
57
|
+
* Private constructor to enforce singleton pattern.
|
|
58
|
+
*/
|
|
59
|
+
constructor() {
|
|
60
|
+
/** Cache storage: path → entry. */
|
|
61
|
+
this.cache = new Map();
|
|
62
|
+
/** Maximum cache entries (LRU eviction). */
|
|
63
|
+
this.maxEntries = 100;
|
|
64
|
+
/** Default TTL for unconfigured paths (3 minutes). */
|
|
65
|
+
this.defaultTTL = 3 * 60 * 1000;
|
|
66
|
+
/** Path-specific TTL configuration. */
|
|
67
|
+
this.ttlConfig = new Map([
|
|
68
|
+
['/PUBLIC', 10 * 60 * 1000], // 10 min (stable, public directory)
|
|
69
|
+
['/home', 5 * 60 * 1000], // 5 min (user home directories)
|
|
70
|
+
['/bin', 60 * 60 * 1000], // 1 hour (plugins rarely change)
|
|
71
|
+
['/feeds/*', 5 * 60 * 1000], // 5 min (feed outputs)
|
|
72
|
+
]);
|
|
73
|
+
/** Cache statistics. */
|
|
74
|
+
this.stats = {
|
|
75
|
+
hits: 0,
|
|
76
|
+
misses: 0,
|
|
77
|
+
staleHits: 0,
|
|
78
|
+
evictions: 0,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Gets the singleton instance of ListCache.
|
|
83
|
+
*
|
|
84
|
+
* @returns The ListCache singleton instance.
|
|
85
|
+
*/
|
|
86
|
+
static instance_get() {
|
|
87
|
+
if (!ListCache.instance) {
|
|
88
|
+
ListCache.instance = new ListCache();
|
|
89
|
+
}
|
|
90
|
+
return ListCache.instance;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Retrieves data from cache with freshness information.
|
|
94
|
+
*
|
|
95
|
+
* @param path - The path to retrieve.
|
|
96
|
+
* @returns CacheResult with freshness info, or null if not cached.
|
|
97
|
+
*/
|
|
98
|
+
cache_get(path) {
|
|
99
|
+
const entry = this.cache.get(path);
|
|
100
|
+
if (!entry) {
|
|
101
|
+
this.stats.misses++;
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
// LRU: Move to end (mark as recently used)
|
|
105
|
+
this.cache.delete(path);
|
|
106
|
+
this.cache.set(path, entry);
|
|
107
|
+
// Calculate freshness
|
|
108
|
+
const age = Date.now() - entry.timestamp;
|
|
109
|
+
const fresh = !entry.dirty && age < entry.ttl;
|
|
110
|
+
if (fresh) {
|
|
111
|
+
this.stats.hits++;
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
this.stats.staleHits++;
|
|
115
|
+
}
|
|
116
|
+
return { data: entry.data, fresh, age };
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Stores data in cache with optional TTL override.
|
|
120
|
+
*
|
|
121
|
+
* @param path - The path to cache.
|
|
122
|
+
* @param data - The data to cache.
|
|
123
|
+
* @param options - Optional TTL and dirty flag.
|
|
124
|
+
*/
|
|
125
|
+
cache_set(path, data, options) {
|
|
126
|
+
// LRU: If exists, delete and re-add (moves to end)
|
|
127
|
+
if (this.cache.has(path)) {
|
|
128
|
+
this.cache.delete(path);
|
|
129
|
+
}
|
|
130
|
+
const ttl = options?.ttl ?? this.ttl_get(path);
|
|
131
|
+
this.cache.set(path, {
|
|
132
|
+
data,
|
|
133
|
+
timestamp: Date.now(),
|
|
134
|
+
dirty: options?.dirty ?? false,
|
|
135
|
+
ttl,
|
|
136
|
+
});
|
|
137
|
+
// LRU eviction if over limit
|
|
138
|
+
this.evict_lru();
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Marks a cached path as dirty (needs refresh).
|
|
142
|
+
* Used when local mutations might have changed the data.
|
|
143
|
+
*
|
|
144
|
+
* @param path - The path to mark dirty.
|
|
145
|
+
*/
|
|
146
|
+
cache_markDirty(path) {
|
|
147
|
+
const entry = this.cache.get(path);
|
|
148
|
+
if (entry) {
|
|
149
|
+
entry.dirty = true;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Optimistically updates cache after a mutation.
|
|
154
|
+
* Example: After rm, remove item from parent directory cache.
|
|
155
|
+
*
|
|
156
|
+
* @param path - The parent directory path.
|
|
157
|
+
* @param updater - Function to transform cached data.
|
|
158
|
+
*/
|
|
159
|
+
cache_update(path, updater) {
|
|
160
|
+
const entry = this.cache.get(path);
|
|
161
|
+
if (entry) {
|
|
162
|
+
entry.data = updater(entry.data);
|
|
163
|
+
entry.timestamp = Date.now(); // Reset timestamp
|
|
164
|
+
entry.dirty = false; // Clean after update
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Invalidates cache for a specific path or clears entire cache.
|
|
169
|
+
* Called by file operations that modify directory contents (mkdir, touch, rm, upload).
|
|
170
|
+
*
|
|
171
|
+
* @param path - Optional specific path to invalidate. If omitted, clears entire cache.
|
|
172
|
+
*/
|
|
173
|
+
cache_invalidate(path) {
|
|
174
|
+
if (path !== undefined) {
|
|
175
|
+
this.cache.delete(path);
|
|
176
|
+
}
|
|
177
|
+
else {
|
|
178
|
+
this.cache.clear();
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* @deprecated Use cache without cwd tracking. Cache persists across navigation.
|
|
183
|
+
* This method is kept for backward compatibility but does nothing.
|
|
184
|
+
*
|
|
185
|
+
* @param _newCwd - Ignored.
|
|
186
|
+
*/
|
|
187
|
+
cwd_update(_newCwd) {
|
|
188
|
+
// No-op: Cache no longer flushes on directory change
|
|
189
|
+
// Kept for backward compatibility during transition
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Gets TTL for a specific path based on configuration.
|
|
193
|
+
*
|
|
194
|
+
* @param path - The path to get TTL for.
|
|
195
|
+
* @returns TTL in milliseconds.
|
|
196
|
+
*/
|
|
197
|
+
ttl_get(path) {
|
|
198
|
+
if (this.ttlConfig.has(path)) {
|
|
199
|
+
return this.ttlConfig.get(path);
|
|
200
|
+
}
|
|
201
|
+
for (const [pattern, ttl] of this.ttlConfig) {
|
|
202
|
+
if (pattern.includes('*')) {
|
|
203
|
+
const regex = new RegExp('^' + pattern.replace(/\*/g, '.*') + '$');
|
|
204
|
+
if (regex.test(path)) {
|
|
205
|
+
return ttl;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
return this.defaultTTL;
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Evicts oldest entries if cache exceeds max size.
|
|
213
|
+
*/
|
|
214
|
+
evict_lru() {
|
|
215
|
+
while (this.cache.size > this.maxEntries) {
|
|
216
|
+
const oldestKey = this.cache.keys().next().value;
|
|
217
|
+
if (oldestKey !== undefined) {
|
|
218
|
+
this.cache.delete(oldestKey);
|
|
219
|
+
this.stats.evictions++;
|
|
220
|
+
}
|
|
221
|
+
else {
|
|
222
|
+
break; // Should never happen, but safety check
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Gets cache statistics.
|
|
228
|
+
*
|
|
229
|
+
* @returns Cache statistics including hits, misses, and current state.
|
|
230
|
+
*/
|
|
231
|
+
stats_get() {
|
|
232
|
+
let totalSize = 0;
|
|
233
|
+
for (const entry of this.cache.values()) {
|
|
234
|
+
// Rough estimate: JSON string length
|
|
235
|
+
totalSize += JSON.stringify(entry.data).length;
|
|
236
|
+
}
|
|
237
|
+
return {
|
|
238
|
+
hits: this.stats.hits,
|
|
239
|
+
misses: this.stats.misses,
|
|
240
|
+
staleHits: this.stats.staleHits,
|
|
241
|
+
evictions: this.stats.evictions,
|
|
242
|
+
entries: this.cache.size,
|
|
243
|
+
totalSize,
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Resets cache statistics.
|
|
248
|
+
* Useful for testing and monitoring.
|
|
249
|
+
*/
|
|
250
|
+
stats_reset() {
|
|
251
|
+
this.stats.hits = 0;
|
|
252
|
+
this.stats.misses = 0;
|
|
253
|
+
this.stats.staleHits = 0;
|
|
254
|
+
this.stats.evictions = 0;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
exports.ListCache = ListCache;
|
|
258
|
+
/**
|
|
259
|
+
* Convenience function to get the ListCache singleton instance.
|
|
260
|
+
*
|
|
261
|
+
* @returns The ListCache singleton instance.
|
|
262
|
+
*/
|
|
263
|
+
function listCache_get() {
|
|
264
|
+
return ListCache.instance_get();
|
|
265
|
+
}
|
|
266
|
+
//# sourceMappingURL=listCache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"listCache.js","sourceRoot":"","sources":["../../src/cache/listCache.ts"],"names":[],"mappings":"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"}
|