@fnndsc/salsa 4.5.2 → 4.5.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.
@@ -0,0 +1,237 @@
1
+ /**
2
+ * @file Singleton path mapper for logical-to-physical ChRIS path resolution.
3
+ *
4
+ * Maintains a cached mapping of logical paths to physical locations,
5
+ * enabling incremental resolution by reusing cached path prefixes.
6
+ *
7
+ * Example:
8
+ * - First call: '/home/user/public/feed_4' → Full resolution, cache prefix
9
+ * - Second call: '/home/user/public/feed_5' → Reuse '/home/user/public', resolve only 'feed_5'
10
+ *
11
+ * This eliminates redundant tree walking and API calls for paths with common prefixes.
12
+ *
13
+ * It is CUBE's own mechanics (a `.chrislink` is a file CUBE lists among a
14
+ * folder's links), so it lives with the file reads. The kernel asks it
15
+ * through one door: the ChRIS backend's link walk, which brasa's
16
+ * `storePath_resolve` calls for every file command.
17
+ *
18
+ * @module
19
+ */
20
+ import { Result } from '@fnndsc/cumin';
21
+ /**
22
+ * Statistics for cache performance monitoring.
23
+ */
24
+ export interface CacheStats {
25
+ hits: number;
26
+ misses: number;
27
+ size: number;
28
+ hitRate: number;
29
+ }
30
+ /**
31
+ * Singleton PathMapper class for logical-to-physical path resolution.
32
+ *
33
+ * Maintains hierarchical cache of path mappings to minimize repeated
34
+ * link resolution operations.
35
+ *
36
+ * @example
37
+ * ```typescript
38
+ * const mapper = PathMapper.instance_get();
39
+ *
40
+ * // First resolution
41
+ * const result1 = await mapper.logicalPath_resolve('/home/user/public/feed_4');
42
+ * // → Resolves /home/user/public → /SHARED, then feed_4
43
+ * // → Caches intermediate mappings
44
+ *
45
+ * // Second resolution - prefix reuse
46
+ * const result2 = await mapper.logicalPath_resolve('/home/user/public/feed_5');
47
+ * // → Reuses cached '/home/user/public' → '/SHARED'
48
+ * // → Resolves only 'feed_5'
49
+ * ```
50
+ */
51
+ export declare class PathMapper {
52
+ private static instance;
53
+ private cache;
54
+ private readonly defaultTTL;
55
+ private stats;
56
+ /** Walked paths whose parent could not be asked for links, by the parent. */
57
+ private blind;
58
+ /** Parents found blind by resolutions since the last {@link blindParents_take}. */
59
+ private blindSeen;
60
+ /**
61
+ * Private constructor enforces singleton pattern.
62
+ */
63
+ private constructor();
64
+ /**
65
+ * Gets the singleton PathMapper instance.
66
+ *
67
+ * @returns The singleton PathMapper instance.
68
+ */
69
+ static instance_get(): PathMapper;
70
+ /**
71
+ * Resets the singleton instance (for testing).
72
+ *
73
+ * @internal
74
+ */
75
+ static instance_reset(): void;
76
+ /**
77
+ * Resolves a logical path to its physical location.
78
+ *
79
+ * Uses cached path prefixes when available, resolving only the
80
+ * uncached suffix to minimize API calls.
81
+ *
82
+ * @param logicalPath - The logical path to resolve.
83
+ * @returns Result containing the physical path, or Err on failure.
84
+ *
85
+ * @example
86
+ * ```typescript
87
+ * const mapper = PathMapper.instance_get();
88
+ *
89
+ * // First call: full resolution
90
+ * const result1 = await mapper.logicalPath_resolve('/home/user/public/feed_4');
91
+ * // → Resolves /home/user/public → /SHARED, then feed_4
92
+ * // → Caches: '/home/user/public' → '/SHARED'
93
+ * // → Caches: '/home/user/public/feed_4' → '/SHARED/feed_4'
94
+ *
95
+ * // Second call: prefix reuse
96
+ * const result2 = await mapper.logicalPath_resolve('/home/user/public/feed_5');
97
+ * // → Reuses cached '/home/user/public' → '/SHARED'
98
+ * // → Resolves only 'feed_5'
99
+ * ```
100
+ */
101
+ logicalPath_resolve(logicalPath: string): Promise<Result<string>>;
102
+ /**
103
+ * Finds the longest cached prefix of a logical path.
104
+ *
105
+ * Walks up the path hierarchy to find the deepest cached mapping,
106
+ * returning the cached physical prefix and remaining suffix.
107
+ *
108
+ * @param logicalPath - The full logical path.
109
+ * @returns Object with cached prefix and remaining suffix.
110
+ *
111
+ * @example
112
+ * ```typescript
113
+ * // Cache contains: '/home/user/public' → '/SHARED'
114
+ * const result = mapper.longestCachedPrefix_find('/home/user/public/feed_4/files');
115
+ * // Returns:
116
+ * // {
117
+ * // prefixLogical: '/home/user/public',
118
+ * // prefixPhysical: '/SHARED',
119
+ * // suffix: 'feed_4/files'
120
+ * // }
121
+ * ```
122
+ */
123
+ private longestCachedPrefix_find;
124
+ /**
125
+ * Resolves a path suffix by checking each component for links.
126
+ *
127
+ * Walks the suffix path, checking each component to see if it's a link.
128
+ * Caches intermediate mappings along the way for future prefix reuse.
129
+ *
130
+ * @param suffix - The path suffix to resolve (e.g., 'feed_4/files').
131
+ * @param physicalBase - The physical base path to build upon.
132
+ * @param logicalBase - The logical base path (for caching intermediate results).
133
+ * @returns Result containing the resolved physical path.
134
+ */
135
+ private suffix_resolve;
136
+ /**
137
+ * Checks if a path is a link and returns its target.
138
+ *
139
+ * Optimizes resolution by checking ListCache first. If the parent directory
140
+ * is cached, we can determine if the candidate is a link without an API call.
141
+ * Falls back to API (fetching all links in parent) if not cached.
142
+ *
143
+ * @param candidatePhysical - The physical path to check.
144
+ * @param candidateLogical - The logical path (used for cache lookup).
145
+ * @returns The link target if it's a link, null otherwise.
146
+ */
147
+ private link_checkAndResolve;
148
+ /**
149
+ * Records the parents a resolution of this path walked past without
150
+ * seeing their links.
151
+ *
152
+ * @param logicalPath - The path just resolved.
153
+ */
154
+ private blind_note;
155
+ /**
156
+ * The parents whose links could not be read by the resolutions since the
157
+ * last call, emptied by the call. A command whose answer failed reports
158
+ * them: a link in one of them, had it been seen, would have led elsewhere.
159
+ *
160
+ * @returns The parents, in the order first met.
161
+ */
162
+ blindParents_take(): string[];
163
+ /**
164
+ * Gets a cached mapping if it exists and hasn't expired.
165
+ *
166
+ * @param logicalPath - The logical path to look up.
167
+ * @returns The cached physical path, or null if not cached or expired.
168
+ */
169
+ private cache_get;
170
+ /**
171
+ * Caches a logical-to-physical path mapping.
172
+ *
173
+ * @param logicalPath - The logical path.
174
+ * @param physicalPath - The corresponding physical path.
175
+ * @param ttl - Optional TTL in milliseconds (defaults to 30s).
176
+ */
177
+ private cache_set;
178
+ /**
179
+ * Invalidates all cached mappings with a given logical path prefix.
180
+ *
181
+ * Call this when links are created/deleted/modified.
182
+ *
183
+ * @param logicalPathPrefix - The logical path prefix to invalidate.
184
+ *
185
+ * @example
186
+ * ```typescript
187
+ * // Link changed: /home/user/public → /SHARED becomes → /PUBLIC
188
+ * mapper.cache_invalidate('/home/user/public');
189
+ * // Invalidates:
190
+ * // - /home/user/public
191
+ * // - /home/user/public/feed_4
192
+ * // - /home/user/public/feed_5
193
+ * // - ... etc
194
+ * ```
195
+ */
196
+ cache_invalidate(logicalPathPrefix: string): void;
197
+ /**
198
+ * Clears all cached mappings.
199
+ */
200
+ cache_clear(): void;
201
+ /**
202
+ * Gets cache statistics.
203
+ *
204
+ * @returns Cache statistics including hit rate and size.
205
+ */
206
+ stats_get(): CacheStats;
207
+ }
208
+ /**
209
+ * Convenience function to get the singleton PathMapper instance.
210
+ *
211
+ * @returns The singleton PathMapper instance.
212
+ */
213
+ export declare function pathMapper_get(): PathMapper;
214
+ /**
215
+ * Resolves a logical path to its physical path by following links.
216
+ *
217
+ * Delegates to the PathMapper singleton, which uses hierarchical caching to
218
+ * optimize repeated resolutions with common prefixes.
219
+ *
220
+ * @param logicalPath - The logical (apparent) path the user sees/navigates.
221
+ * @returns A Result containing the physical path, or Err if path is invalid.
222
+ *
223
+ * @example
224
+ * ```ts
225
+ * await logicalPath_resolve('/home/user/public/data'); // Ok('/PUBLIC/data') where public -> /PUBLIC
226
+ * ```
227
+ */
228
+ export declare function logicalPath_resolve(logicalPath: string): Promise<Result<string>>;
229
+ /**
230
+ * Clears the path mapping cache. Useful when links are created/deleted.
231
+ *
232
+ * @example
233
+ * ```ts
234
+ * linkCache_clear();
235
+ * ```
236
+ */
237
+ export declare function linkCache_clear(): void;