@atomic-ehr/fhir-canonical-manager 0.0.13 → 0.0.14
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/cache.d.ts +16 -0
- package/dist/cache.d.ts.map +1 -0
- package/dist/cache.js +49 -0
- package/dist/cache.js.map +1 -0
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +15 -15
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/init.d.ts.map +1 -1
- package/dist/cli/init.js +7 -10
- package/dist/cli/init.js.map +1 -1
- package/dist/cli/list.d.ts.map +1 -1
- package/dist/cli/list.js +14 -10
- package/dist/cli/list.js.map +1 -1
- package/dist/cli/resolve.js +7 -7
- package/dist/cli/resolve.js.map +1 -1
- package/dist/cli/search.js +9 -9
- package/dist/cli/search.js.map +1 -1
- package/dist/cli/searchparam.d.ts.map +1 -1
- package/dist/cli/searchparam.js +27 -24
- package/dist/cli/searchparam.js.map +1 -1
- package/dist/fs/index.d.ts +1 -1
- package/dist/fs/index.d.ts.map +1 -1
- package/dist/fs/index.js +1 -1
- package/dist/fs/index.js.map +1 -1
- package/dist/fs/utils.js +2 -2
- package/dist/fs/utils.js.map +1 -1
- package/dist/index.d.ts +6 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -4
- package/dist/index.js.map +1 -1
- package/dist/manager/canonical.d.ts +1 -1
- package/dist/manager/canonical.d.ts.map +1 -1
- package/dist/manager/canonical.js +46 -42
- package/dist/manager/canonical.js.map +1 -1
- package/dist/manager/index.d.ts +1 -2
- package/dist/manager/index.d.ts.map +1 -1
- package/dist/manager/index.js +1 -2
- package/dist/manager/index.js.map +1 -1
- package/dist/package.d.ts +4 -0
- package/dist/package.d.ts.map +1 -0
- package/dist/package.js +69 -0
- package/dist/package.js.map +1 -0
- package/dist/reference/index.d.ts +3 -4
- package/dist/reference/index.d.ts.map +1 -1
- package/dist/reference/index.js +2 -3
- package/dist/reference/index.js.map +1 -1
- package/dist/reference/manager.d.ts +2 -2
- package/dist/reference/manager.d.ts.map +1 -1
- package/dist/reference/manager.js +7 -3
- package/dist/reference/manager.js.map +1 -1
- package/dist/reference/store.d.ts.map +1 -1
- package/dist/reference/store.js +1 -1
- package/dist/reference/store.js.map +1 -1
- package/dist/resolver/context.d.ts +3 -3
- package/dist/resolver/context.d.ts.map +1 -1
- package/dist/resolver/context.js +1 -1
- package/dist/resolver/context.js.map +1 -1
- package/dist/resolver/index.d.ts +1 -1
- package/dist/resolver/index.js +1 -1
- package/dist/scanner/directory.d.ts +2 -2
- package/dist/scanner/directory.d.ts.map +1 -1
- package/dist/scanner/directory.js +24 -27
- package/dist/scanner/directory.js.map +1 -1
- package/dist/scanner/index.d.ts +4 -4
- package/dist/scanner/index.d.ts.map +1 -1
- package/dist/scanner/index.js +4 -4
- package/dist/scanner/index.js.map +1 -1
- package/dist/scanner/package.d.ts +1 -1
- package/dist/scanner/package.d.ts.map +1 -1
- package/dist/scanner/package.js +4 -4
- package/dist/scanner/package.js.map +1 -1
- package/dist/scanner/parser.d.ts +2 -2
- package/dist/scanner/parser.d.ts.map +1 -1
- package/dist/scanner/parser.js +1 -1
- package/dist/scanner/parser.js.map +1 -1
- package/dist/scanner/processor.d.ts +2 -2
- package/dist/scanner/processor.d.ts.map +1 -1
- package/dist/scanner/processor.js +3 -3
- package/dist/scanner/processor.js.map +1 -1
- package/dist/search/index.d.ts +2 -2
- package/dist/search/index.d.ts.map +1 -1
- package/dist/search/index.js +2 -2
- package/dist/search/index.js.map +1 -1
- package/dist/search/smart.d.ts +1 -1
- package/dist/search/smart.d.ts.map +1 -1
- package/dist/search/smart.js +3 -7
- package/dist/search/smart.js.map +1 -1
- package/dist/search/terms.js.map +1 -1
- package/dist/types/core.d.ts +3 -2
- package/dist/types/core.d.ts.map +1 -1
- package/dist/types/index.d.ts +2 -2
- package/dist/types/index.js +2 -2
- package/dist/types/internal.d.ts +7 -4
- package/dist/types/internal.d.ts.map +1 -1
- package/package.json +12 -2
- package/src/cache.ts +58 -0
- package/src/cli/index.ts +112 -114
- package/src/cli/init.ts +104 -108
- package/src/cli/list.ts +84 -77
- package/src/cli/resolve.ts +57 -57
- package/src/cli/search.ts +72 -72
- package/src/cli/searchparam.ts +118 -117
- package/src/constants.ts +1 -1
- package/src/fs/index.ts +1 -1
- package/src/fs/utils.ts +16 -16
- package/src/index.ts +9 -12
- package/src/manager/canonical.ts +317 -360
- package/src/manager/index.ts +1 -3
- package/src/package.ts +79 -0
- package/src/reference/index.ts +3 -5
- package/src/reference/manager.ts +43 -39
- package/src/reference/store.ts +7 -8
- package/src/resolver/context.ts +17 -17
- package/src/resolver/index.ts +1 -1
- package/src/scanner/directory.ts +25 -31
- package/src/scanner/index.ts +4 -4
- package/src/scanner/package.ts +26 -29
- package/src/scanner/parser.ts +25 -27
- package/src/scanner/processor.ts +51 -55
- package/src/search/index.ts +2 -2
- package/src/search/smart.ts +41 -48
- package/src/search/terms.ts +17 -17
- package/src/types/core.ts +102 -89
- package/src/types/index.ts +2 -2
- package/src/types/internal.ts +36 -34
- package/dist/cache/core.d.ts +0 -7
- package/dist/cache/core.d.ts.map +0 -1
- package/dist/cache/core.js +0 -11
- package/dist/cache/core.js.map +0 -1
- package/dist/cache/index.d.ts +0 -4
- package/dist/cache/index.d.ts.map +0 -1
- package/dist/cache/index.js +0 -4
- package/dist/cache/index.js.map +0 -1
- package/dist/cache/persistence.d.ts +0 -5
- package/dist/cache/persistence.d.ts.map +0 -1
- package/dist/cache/persistence.js +0 -25
- package/dist/cache/persistence.js.map +0 -1
- package/dist/cache/validation.d.ts +0 -2
- package/dist/cache/validation.d.ts.map +0 -1
- package/dist/cache/validation.js +0 -21
- package/dist/cache/validation.js.map +0 -1
- package/dist/package/detector.d.ts +0 -2
- package/dist/package/detector.d.ts.map +0 -1
- package/dist/package/detector.js +0 -19
- package/dist/package/detector.js.map +0 -1
- package/dist/package/index.d.ts +0 -3
- package/dist/package/index.d.ts.map +0 -1
- package/dist/package/index.js +0 -3
- package/dist/package/index.js.map +0 -1
- package/dist/package/installer.d.ts +0 -2
- package/dist/package/installer.d.ts.map +0 -1
- package/dist/package/installer.js +0 -55
- package/dist/package/installer.js.map +0 -1
- package/src/cache/core.ts +0 -20
- package/src/cache/index.ts +0 -7
- package/src/cache/persistence.ts +0 -39
- package/src/cache/validation.ts +0 -27
- package/src/package/detector.ts +0 -24
- package/src/package/index.ts +0 -6
- package/src/package/installer.ts +0 -77
package/src/manager/canonical.ts
CHANGED
|
@@ -2,369 +2,326 @@
|
|
|
2
2
|
* Main CanonicalManager implementation
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import * as
|
|
6
|
-
import * as
|
|
5
|
+
import * as afs from "node:fs/promises";
|
|
6
|
+
import * as Path from "node:path";
|
|
7
|
+
import { cacheRecordPaths, createCacheRecord, loadCacheRecordFromDisk, saveCacheRecordToDisk } from "../cache.js";
|
|
8
|
+
import { DEFAULT_REGISTRY } from "../constants.js";
|
|
9
|
+
import { ensureDir } from "../fs/index.js";
|
|
10
|
+
import { installPackages } from "../package.js";
|
|
11
|
+
import { resolveWithContext } from "../resolver/index.js";
|
|
12
|
+
import { scanDirectory } from "../scanner/index.js";
|
|
13
|
+
import { filterBySmartSearch } from "../search/index.js";
|
|
7
14
|
import type {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
import { ensureDir } from '../fs/index.js';
|
|
19
|
-
import { installPackages } from '../package/index.js';
|
|
20
|
-
import {
|
|
21
|
-
createCache,
|
|
22
|
-
saveCacheToDisk,
|
|
23
|
-
loadCacheFromDisk,
|
|
24
|
-
computePackageLockHash,
|
|
25
|
-
type ExtendedCache,
|
|
26
|
-
} from '../cache/index.js';
|
|
27
|
-
import { scanDirectory } from '../scanner/index.js';
|
|
28
|
-
import { resolveWithContext } from '../resolver/index.js';
|
|
29
|
-
import { filterBySmartSearch } from '../search/index.js';
|
|
15
|
+
CanonicalManager,
|
|
16
|
+
Config,
|
|
17
|
+
IndexEntry,
|
|
18
|
+
PackageId,
|
|
19
|
+
PackageInfo,
|
|
20
|
+
Reference,
|
|
21
|
+
Resource,
|
|
22
|
+
SearchParameter,
|
|
23
|
+
SourceContext,
|
|
24
|
+
} from "../types/index.js";
|
|
30
25
|
|
|
31
26
|
export const createCanonicalManager = (config: Config): CanonicalManager => {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
: `${config.registry}/`
|
|
38
|
-
: DEFAULT_REGISTRY;
|
|
39
|
-
const nodeModulesPath = path.join(workingDir, "node_modules");
|
|
40
|
-
const cacheDir = path.join(workingDir, ".fcm", "cache");
|
|
41
|
-
|
|
42
|
-
let cache = createCache();
|
|
43
|
-
let initialized = false;
|
|
44
|
-
const searchParamsCache = new Map<string, SearchParameter[]>();
|
|
45
|
-
|
|
46
|
-
const ensureInitialized = (): void => {
|
|
47
|
-
if (!initialized) {
|
|
48
|
-
throw new Error("CanonicalManager not initialized. Call init() first.");
|
|
49
|
-
}
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
const init = async (): Promise<void> => {
|
|
53
|
-
if (initialized) return;
|
|
54
|
-
|
|
55
|
-
// Ensure directories exist
|
|
56
|
-
await ensureDir(workingDir);
|
|
57
|
-
await ensureDir(cacheDir);
|
|
58
|
-
|
|
59
|
-
// Get current package-lock.json hash
|
|
60
|
-
const currentPackageLockHash = await computePackageLockHash(workingDir);
|
|
61
|
-
|
|
62
|
-
// Try to load cache from disk
|
|
63
|
-
const cachedData = await loadCacheFromDisk(cacheDir);
|
|
64
|
-
|
|
65
|
-
// Check if cache is valid (exists and package-lock.json hasn't changed)
|
|
66
|
-
const cacheValid =
|
|
67
|
-
cachedData &&
|
|
68
|
-
cachedData.packageLockHash === currentPackageLockHash &&
|
|
69
|
-
currentPackageLockHash !== null;
|
|
70
|
-
|
|
71
|
-
if (cacheValid) {
|
|
72
|
-
// Restore cache from disk
|
|
73
|
-
cache.entries = cachedData.entries;
|
|
74
|
-
cache.packages = cachedData.packages;
|
|
75
|
-
Object.entries(cachedData.references).forEach(([id, metadata]) => {
|
|
76
|
-
cache.referenceManager.set(id, metadata);
|
|
77
|
-
});
|
|
78
|
-
} else {
|
|
79
|
-
// Cache is invalid or doesn't exist - rebuild it
|
|
80
|
-
if (cachedData && cachedData.packageLockHash !== currentPackageLockHash) {
|
|
81
|
-
console.log("Package dependencies have changed, rebuilding index...");
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
// Install packages if needed
|
|
85
|
-
await installPackages(packages, workingDir, registry);
|
|
86
|
-
|
|
87
|
-
// Clear cache before scanning
|
|
88
|
-
cache = createCache();
|
|
89
|
-
|
|
90
|
-
// Scan installed packages
|
|
91
|
-
await scanDirectory(nodeModulesPath, cache);
|
|
92
|
-
|
|
93
|
-
// Save cache to disk with current package-lock hash
|
|
94
|
-
await saveCacheToDisk(cache, cacheDir, workingDir);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
initialized = true;
|
|
98
|
-
};
|
|
99
|
-
|
|
100
|
-
const destroy = async (): Promise<void> => {
|
|
101
|
-
cache.entries = {};
|
|
102
|
-
cache.packages = {};
|
|
103
|
-
cache.referenceManager.clear();
|
|
104
|
-
searchParamsCache.clear();
|
|
105
|
-
initialized = false;
|
|
106
|
-
};
|
|
107
|
-
|
|
108
|
-
const getPackages = async (): Promise<PackageId[]> => {
|
|
109
|
-
ensureInitialized();
|
|
110
|
-
return Object.values(cache.packages).map((p) => p.id);
|
|
111
|
-
};
|
|
112
|
-
|
|
113
|
-
const addPackages = async (...newPackages: string[]): Promise<void> => {
|
|
114
|
-
if (newPackages.length === 0) return;
|
|
115
|
-
|
|
116
|
-
// Check if packages already exists in packages
|
|
117
|
-
const packagesToAdd = newPackages.filter((pkg) => !packages.includes(pkg));
|
|
118
|
-
if (packagesToAdd.length !== 0) {
|
|
119
|
-
// Update config packages
|
|
120
|
-
packages.push(...packagesToAdd);
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
// If not initialized yet, init will handle installation
|
|
124
|
-
if(!initialized) {
|
|
125
|
-
await init();
|
|
126
|
-
return;
|
|
27
|
+
const { packages = [], workingDir } = config;
|
|
28
|
+
// Ensure registry URL ends with /
|
|
29
|
+
let registry = DEFAULT_REGISTRY;
|
|
30
|
+
if (config.registry) {
|
|
31
|
+
registry = config.registry.endsWith("/") ? config.registry : `${config.registry}/`;
|
|
127
32
|
}
|
|
128
33
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
34
|
+
const cache = createCacheRecord();
|
|
35
|
+
let initialized = false;
|
|
36
|
+
const searchParamsCache = new Map<string, SearchParameter[]>();
|
|
37
|
+
|
|
38
|
+
const ensureInitialized = (): void => {
|
|
39
|
+
if (!initialized) {
|
|
40
|
+
throw new Error("CanonicalManager not initialized. Call init() first.");
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const init = async (): Promise<void> => {
|
|
45
|
+
if (initialized) return;
|
|
46
|
+
|
|
47
|
+
await ensureDir(workingDir);
|
|
48
|
+
const { cacheKey, npmPackagePath } = cacheRecordPaths(workingDir, packages);
|
|
49
|
+
|
|
50
|
+
const cachedData = await loadCacheRecordFromDisk(workingDir, cacheKey);
|
|
51
|
+
const isCacheValid = cachedData && cachedData.packageLockHash === cacheKey;
|
|
52
|
+
if (isCacheValid) {
|
|
53
|
+
// Restore cache from disk
|
|
54
|
+
cache.entries = cachedData.entries;
|
|
55
|
+
cache.packages = cachedData.packages;
|
|
56
|
+
Object.entries(cachedData.references).forEach(([id, metadata]) => {
|
|
57
|
+
cache.referenceManager.set(id, metadata);
|
|
58
|
+
});
|
|
59
|
+
} else {
|
|
60
|
+
await installPackages(packages, npmPackagePath, registry);
|
|
61
|
+
await scanDirectory(cache, npmPackagePath);
|
|
62
|
+
await saveCacheRecordToDisk(cache, workingDir, cacheKey);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
initialized = true;
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
const destroy = async (): Promise<void> => {
|
|
69
|
+
cache.entries = {};
|
|
70
|
+
cache.packages = {};
|
|
71
|
+
cache.referenceManager.clear();
|
|
72
|
+
searchParamsCache.clear();
|
|
73
|
+
initialized = false;
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
const getPackages = async (): Promise<PackageId[]> => {
|
|
77
|
+
ensureInitialized();
|
|
78
|
+
return Object.values(cache.packages).map((p: PackageInfo) => p.id);
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
const addPackages = async (...newPackages: string[]): Promise<void> => {
|
|
82
|
+
if (newPackages.length === 0) return;
|
|
83
|
+
|
|
84
|
+
// Check if packages already exists in packages
|
|
85
|
+
const packagesToAdd = newPackages.filter((pkg) => !packages.includes(pkg));
|
|
86
|
+
if (packagesToAdd.length !== 0) {
|
|
87
|
+
packages.push(...packagesToAdd);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (!initialized) {
|
|
91
|
+
await init();
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (packagesToAdd.length > 0) {
|
|
96
|
+
// TODO: very expensive, we can just copy/paste old cache to avoid reinstalling packages
|
|
97
|
+
await destroy();
|
|
98
|
+
await init();
|
|
99
|
+
}
|
|
100
|
+
return;
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
const resolveEntry = async (
|
|
104
|
+
canonicalUrl: string,
|
|
105
|
+
options?: {
|
|
106
|
+
package?: string;
|
|
107
|
+
version?: string;
|
|
108
|
+
sourceContext?: SourceContext;
|
|
109
|
+
},
|
|
110
|
+
): Promise<IndexEntry> => {
|
|
111
|
+
ensureInitialized();
|
|
112
|
+
|
|
113
|
+
if (options?.sourceContext) {
|
|
114
|
+
const contextResolved = await resolveWithContext(canonicalUrl, options.sourceContext, cache, resolveEntry);
|
|
115
|
+
if (contextResolved) {
|
|
116
|
+
return contextResolved;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const entries = cache.entries[canonicalUrl] || [];
|
|
121
|
+
|
|
122
|
+
if (entries.length === 0) {
|
|
123
|
+
throw new Error(`Cannot resolve canonical URL: ${canonicalUrl}`);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
let filtered = [...entries];
|
|
127
|
+
|
|
128
|
+
if (options?.package) {
|
|
129
|
+
filtered = filtered.filter((e) => e.package?.name === options.package);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (options?.version) {
|
|
133
|
+
filtered = filtered.filter((e) => e.version === options.version);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (filtered.length === 0) {
|
|
137
|
+
throw new Error(`No matching resource found for ${canonicalUrl} with given options`);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const result = filtered[0];
|
|
141
|
+
if (!result) {
|
|
142
|
+
throw new Error(`No matching resource found for ${canonicalUrl}`);
|
|
143
|
+
}
|
|
144
|
+
return result;
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
const resolve = async (
|
|
148
|
+
canonicalUrl: string,
|
|
149
|
+
options?: {
|
|
150
|
+
package?: string;
|
|
151
|
+
version?: string;
|
|
152
|
+
sourceContext?: SourceContext;
|
|
153
|
+
},
|
|
154
|
+
): Promise<Resource> => {
|
|
155
|
+
const entry = await resolveEntry(canonicalUrl, options);
|
|
156
|
+
return read(entry);
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
const read = async (reference: Reference): Promise<Resource> => {
|
|
160
|
+
ensureInitialized();
|
|
161
|
+
|
|
162
|
+
const metadata = cache.referenceManager.get(reference.id);
|
|
163
|
+
if (!metadata) {
|
|
164
|
+
throw new Error(`Invalid reference ID: ${reference.id}`);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
try {
|
|
168
|
+
const content = await afs.readFile(metadata.filePath, "utf-8");
|
|
169
|
+
const resource = JSON.parse(content);
|
|
170
|
+
|
|
171
|
+
return {
|
|
172
|
+
...resource,
|
|
173
|
+
id: reference.id,
|
|
174
|
+
resourceType: reference.resourceType,
|
|
175
|
+
};
|
|
176
|
+
} catch (err) {
|
|
177
|
+
throw new Error(`Failed to read resource: ${err}`);
|
|
178
|
+
}
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
const searchEntries = async (params: {
|
|
182
|
+
kind?: string;
|
|
183
|
+
url?: string;
|
|
184
|
+
type?: string;
|
|
185
|
+
version?: string;
|
|
186
|
+
package?: PackageId;
|
|
187
|
+
}): Promise<IndexEntry[]> => {
|
|
188
|
+
ensureInitialized();
|
|
189
|
+
|
|
190
|
+
let results: IndexEntry[] = [];
|
|
191
|
+
|
|
192
|
+
if (params.url) {
|
|
193
|
+
results = cache.entries[params.url] || [];
|
|
194
|
+
} else {
|
|
195
|
+
for (const entries of Object.values(cache.entries)) {
|
|
196
|
+
results.push(...entries);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
if (params.kind !== undefined) {
|
|
201
|
+
results = results.filter((e) => e.kind === params.kind);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
if (params.type !== undefined) {
|
|
205
|
+
results = results.filter((e) => e.type === params.type);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
if (params.version !== undefined) {
|
|
209
|
+
results = results.filter((e) => e.version === params.version);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
if (params.package) {
|
|
213
|
+
const pkg = params.package;
|
|
214
|
+
results = results.filter((e) => e.package?.name === pkg.name && e.package?.version === pkg.version);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
return results;
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
const search = async (params: {
|
|
221
|
+
kind?: string;
|
|
222
|
+
url?: string;
|
|
223
|
+
type?: string;
|
|
224
|
+
version?: string;
|
|
225
|
+
package?: PackageId;
|
|
226
|
+
}): Promise<Resource[]> => {
|
|
227
|
+
const entries = await searchEntries(params);
|
|
228
|
+
const resources = await Promise.all(entries.map((entry) => read(entry)));
|
|
229
|
+
return resources;
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
const smartSearch = async (
|
|
233
|
+
searchTerms: string[],
|
|
234
|
+
filters?: {
|
|
235
|
+
resourceType?: string;
|
|
236
|
+
type?: string;
|
|
237
|
+
kind?: string;
|
|
238
|
+
package?: PackageId;
|
|
239
|
+
},
|
|
240
|
+
): Promise<IndexEntry[]> => {
|
|
241
|
+
ensureInitialized();
|
|
242
|
+
|
|
243
|
+
// Start with base search using filters
|
|
244
|
+
let results = await searchEntries({
|
|
245
|
+
kind: filters?.kind,
|
|
246
|
+
package: filters?.package,
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
// Apply resourceType filter
|
|
250
|
+
if (filters?.resourceType) {
|
|
251
|
+
results = results.filter((entry) => entry.resourceType === filters.resourceType);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// Apply type filter
|
|
255
|
+
if (filters?.type) {
|
|
256
|
+
results = results.filter((entry) => entry.type === filters.type);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// Apply smart search filtering
|
|
260
|
+
return filterBySmartSearch(results, searchTerms);
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
const getSearchParametersForResource = async (resourceType: string): Promise<SearchParameter[]> => {
|
|
264
|
+
ensureInitialized();
|
|
265
|
+
|
|
266
|
+
// Check cache first
|
|
267
|
+
if (searchParamsCache.has(resourceType)) {
|
|
268
|
+
const cached = searchParamsCache.get(resourceType);
|
|
269
|
+
if (cached) {
|
|
270
|
+
return cached;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// Query all SearchParameter resources
|
|
275
|
+
// Note: We search by resourceType, not type, because for SearchParameter resources,
|
|
276
|
+
// 'type' refers to the search parameter type (token, string, etc.)
|
|
277
|
+
const allEntries = await searchEntries({});
|
|
278
|
+
const searchParamEntries = allEntries.filter((entry) => entry.resourceType === "SearchParameter");
|
|
279
|
+
|
|
280
|
+
const results: SearchParameter[] = [];
|
|
281
|
+
|
|
282
|
+
for (const entry of searchParamEntries) {
|
|
283
|
+
const resource = await read(entry);
|
|
284
|
+
|
|
285
|
+
// Check if this parameter applies to the requested resource
|
|
286
|
+
const bases = resource.base || [];
|
|
287
|
+
if (Array.isArray(bases) && bases.includes(resourceType)) {
|
|
288
|
+
// Return the full original resource - it already contains all fields
|
|
289
|
+
// Cast through unknown to satisfy TypeScript
|
|
290
|
+
results.push(resource as unknown as SearchParameter);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// Sort by code for consistent output
|
|
295
|
+
results.sort((a, b) => {
|
|
296
|
+
const codeA = a.code || "";
|
|
297
|
+
const codeB = b.code || "";
|
|
298
|
+
return codeA.localeCompare(codeB);
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
// Cache the results
|
|
302
|
+
searchParamsCache.set(resourceType, results);
|
|
303
|
+
|
|
304
|
+
return results;
|
|
305
|
+
};
|
|
306
|
+
|
|
307
|
+
return {
|
|
308
|
+
init,
|
|
309
|
+
destroy,
|
|
310
|
+
packages: getPackages,
|
|
311
|
+
addPackages,
|
|
159
312
|
resolveEntry,
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
filtered = filtered.filter((e) => e.package?.name === options.package);
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
if (options?.version) {
|
|
179
|
-
filtered = filtered.filter((e) => e.version === options.version);
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
if (filtered.length === 0) {
|
|
183
|
-
throw new Error(
|
|
184
|
-
`No matching resource found for ${canonicalUrl} with given options`,
|
|
185
|
-
);
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
return filtered[0]!;
|
|
189
|
-
};
|
|
190
|
-
|
|
191
|
-
const resolve = async (
|
|
192
|
-
canonicalUrl: string,
|
|
193
|
-
options?: {
|
|
194
|
-
package?: string;
|
|
195
|
-
version?: string;
|
|
196
|
-
sourceContext?: SourceContext;
|
|
197
|
-
},
|
|
198
|
-
): Promise<Resource> => {
|
|
199
|
-
const entry = await resolveEntry(canonicalUrl, options);
|
|
200
|
-
return read(entry);
|
|
201
|
-
};
|
|
202
|
-
|
|
203
|
-
const read = async (reference: Reference): Promise<Resource> => {
|
|
204
|
-
ensureInitialized();
|
|
205
|
-
|
|
206
|
-
const metadata = cache.referenceManager.get(reference.id);
|
|
207
|
-
if (!metadata) {
|
|
208
|
-
throw new Error(`Invalid reference ID: ${reference.id}`);
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
try {
|
|
212
|
-
const content = await fs.readFile(metadata.filePath, "utf-8");
|
|
213
|
-
const resource = JSON.parse(content);
|
|
214
|
-
|
|
215
|
-
return {
|
|
216
|
-
...resource,
|
|
217
|
-
id: reference.id,
|
|
218
|
-
resourceType: reference.resourceType,
|
|
219
|
-
};
|
|
220
|
-
} catch (err) {
|
|
221
|
-
throw new Error(`Failed to read resource: ${err}`);
|
|
222
|
-
}
|
|
223
|
-
};
|
|
224
|
-
|
|
225
|
-
const searchEntries = async (params: {
|
|
226
|
-
kind?: string;
|
|
227
|
-
url?: string;
|
|
228
|
-
type?: string;
|
|
229
|
-
version?: string;
|
|
230
|
-
package?: PackageId;
|
|
231
|
-
}): Promise<IndexEntry[]> => {
|
|
232
|
-
ensureInitialized();
|
|
233
|
-
|
|
234
|
-
let results: IndexEntry[] = [];
|
|
235
|
-
|
|
236
|
-
if (params.url) {
|
|
237
|
-
results = cache.entries[params.url] || [];
|
|
238
|
-
} else {
|
|
239
|
-
for (const entries of Object.values(cache.entries)) {
|
|
240
|
-
results.push(...entries);
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
if (params.kind !== undefined) {
|
|
245
|
-
results = results.filter((e) => e.kind === params.kind);
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
if (params.type !== undefined) {
|
|
249
|
-
results = results.filter((e) => e.type === params.type);
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
if (params.version !== undefined) {
|
|
253
|
-
results = results.filter((e) => e.version === params.version);
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
if (params.package) {
|
|
257
|
-
const pkg = params.package;
|
|
258
|
-
results = results.filter(
|
|
259
|
-
(e) =>
|
|
260
|
-
e.package?.name === pkg.name && e.package?.version === pkg.version,
|
|
261
|
-
);
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
return results;
|
|
265
|
-
};
|
|
266
|
-
|
|
267
|
-
const search = async (params: {
|
|
268
|
-
kind?: string;
|
|
269
|
-
url?: string;
|
|
270
|
-
type?: string;
|
|
271
|
-
version?: string;
|
|
272
|
-
package?: PackageId;
|
|
273
|
-
}): Promise<Resource[]> => {
|
|
274
|
-
const entries = await searchEntries(params);
|
|
275
|
-
const resources = await Promise.all(entries.map((entry) => read(entry)));
|
|
276
|
-
return resources;
|
|
277
|
-
};
|
|
278
|
-
|
|
279
|
-
const smartSearch = async (
|
|
280
|
-
searchTerms: string[],
|
|
281
|
-
filters?: {
|
|
282
|
-
resourceType?: string;
|
|
283
|
-
type?: string;
|
|
284
|
-
kind?: string;
|
|
285
|
-
package?: PackageId;
|
|
286
|
-
},
|
|
287
|
-
): Promise<IndexEntry[]> => {
|
|
288
|
-
ensureInitialized();
|
|
289
|
-
|
|
290
|
-
// Start with base search using filters
|
|
291
|
-
let results = await searchEntries({
|
|
292
|
-
kind: filters?.kind,
|
|
293
|
-
package: filters?.package,
|
|
294
|
-
});
|
|
295
|
-
|
|
296
|
-
// Apply resourceType filter
|
|
297
|
-
if (filters?.resourceType) {
|
|
298
|
-
results = results.filter(
|
|
299
|
-
(entry) => entry.resourceType === filters.resourceType,
|
|
300
|
-
);
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
// Apply type filter
|
|
304
|
-
if (filters?.type) {
|
|
305
|
-
results = results.filter((entry) => entry.type === filters.type);
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
// Apply smart search filtering
|
|
309
|
-
return filterBySmartSearch(results, searchTerms);
|
|
310
|
-
};
|
|
311
|
-
|
|
312
|
-
const getSearchParametersForResource = async (
|
|
313
|
-
resourceType: string
|
|
314
|
-
): Promise<SearchParameter[]> => {
|
|
315
|
-
ensureInitialized();
|
|
316
|
-
|
|
317
|
-
// Check cache first
|
|
318
|
-
if (searchParamsCache.has(resourceType)) {
|
|
319
|
-
return searchParamsCache.get(resourceType)!;
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
// Query all SearchParameter resources
|
|
323
|
-
// Note: We search by resourceType, not type, because for SearchParameter resources,
|
|
324
|
-
// 'type' refers to the search parameter type (token, string, etc.)
|
|
325
|
-
const allEntries = await searchEntries({});
|
|
326
|
-
const searchParamEntries = allEntries.filter(
|
|
327
|
-
entry => entry.resourceType === 'SearchParameter'
|
|
328
|
-
);
|
|
329
|
-
|
|
330
|
-
const results: SearchParameter[] = [];
|
|
331
|
-
|
|
332
|
-
for (const entry of searchParamEntries) {
|
|
333
|
-
const resource = await read(entry);
|
|
334
|
-
|
|
335
|
-
// Check if this parameter applies to the requested resource
|
|
336
|
-
const bases = resource.base || [];
|
|
337
|
-
if (Array.isArray(bases) && bases.includes(resourceType)) {
|
|
338
|
-
// Return the full original resource - it already contains all fields
|
|
339
|
-
// Cast through unknown to satisfy TypeScript
|
|
340
|
-
results.push(resource as unknown as SearchParameter);
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
// Sort by code for consistent output
|
|
345
|
-
results.sort((a, b) => {
|
|
346
|
-
const codeA = a.code || '';
|
|
347
|
-
const codeB = b.code || '';
|
|
348
|
-
return codeA.localeCompare(codeB);
|
|
349
|
-
});
|
|
350
|
-
|
|
351
|
-
// Cache the results
|
|
352
|
-
searchParamsCache.set(resourceType, results);
|
|
353
|
-
|
|
354
|
-
return results;
|
|
355
|
-
};
|
|
356
|
-
|
|
357
|
-
return {
|
|
358
|
-
init,
|
|
359
|
-
destroy,
|
|
360
|
-
packages: getPackages,
|
|
361
|
-
addPackages,
|
|
362
|
-
resolveEntry,
|
|
363
|
-
resolve,
|
|
364
|
-
read,
|
|
365
|
-
searchEntries,
|
|
366
|
-
search,
|
|
367
|
-
smartSearch,
|
|
368
|
-
getSearchParametersForResource,
|
|
369
|
-
};
|
|
370
|
-
};
|
|
313
|
+
resolve,
|
|
314
|
+
read,
|
|
315
|
+
searchEntries,
|
|
316
|
+
search,
|
|
317
|
+
smartSearch,
|
|
318
|
+
getSearchParametersForResource,
|
|
319
|
+
packageJson: async (packageName: string) => {
|
|
320
|
+
ensureInitialized();
|
|
321
|
+
const fn = cache.packages[packageName]?.path;
|
|
322
|
+
if (!fn) throw new Error(`Package ${packageName} not found`);
|
|
323
|
+
const packageJSON = JSON.parse(await afs.readFile(Path.join(fn, "package.json"), "utf8"));
|
|
324
|
+
return packageJSON;
|
|
325
|
+
},
|
|
326
|
+
};
|
|
327
|
+
};
|
package/src/manager/index.ts
CHANGED
|
@@ -2,7 +2,5 @@
|
|
|
2
2
|
* Manager module exports
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
export { createCanonicalManager } from './canonical.js';
|
|
6
|
-
|
|
7
5
|
// For backward compatibility
|
|
8
|
-
export { createCanonicalManager as CanonicalManager } from
|
|
6
|
+
export { createCanonicalManager, createCanonicalManager as CanonicalManager } from "./canonical.js";
|