type-registry-effect 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/821.js +628 -0
- package/LICENSE +21 -0
- package/README.md +45 -0
- package/index.d.ts +1754 -0
- package/index.js +173 -0
- package/node.d.ts +890 -0
- package/node.js +26 -0
- package/package.json +67 -0
- package/rslib-runtime.js +37 -0
- package/tsdoc-metadata.json +11 -0
package/821.js
ADDED
|
@@ -0,0 +1,628 @@
|
|
|
1
|
+
import { __webpack_require__ } from "./rslib-runtime.js";
|
|
2
|
+
import { Context, Data, Duration, Effect, Layer, Schedule, Schema } from "effect";
|
|
3
|
+
import { dirname, isAbsolute, join, relative } from "node:path";
|
|
4
|
+
import { FileSystem, HttpClient } from "@effect/platform";
|
|
5
|
+
import { homedir as external_node_os_homedir } from "node:os";
|
|
6
|
+
var TypeRegistry_namespaceObject = {};
|
|
7
|
+
__webpack_require__.r(TypeRegistry_namespaceObject);
|
|
8
|
+
__webpack_require__.d(TypeRegistry_namespaceObject, {
|
|
9
|
+
clearCache: ()=>clearCache,
|
|
10
|
+
fetchAndCache: ()=>fetchAndCache,
|
|
11
|
+
getPackageVFS: ()=>getPackageVFS,
|
|
12
|
+
getTypeEntries: ()=>getTypeEntries,
|
|
13
|
+
getVFS: ()=>getVFS,
|
|
14
|
+
hasCached: ()=>hasCached,
|
|
15
|
+
resolveImport: ()=>resolveImport,
|
|
16
|
+
resolveVersion: ()=>resolveVersion
|
|
17
|
+
});
|
|
18
|
+
const PackageNotFoundErrorBase = Data.TaggedError("PackageNotFoundError");
|
|
19
|
+
class PackageNotFoundError extends PackageNotFoundErrorBase {
|
|
20
|
+
}
|
|
21
|
+
const ParseErrorBase = Data.TaggedError("ParseError");
|
|
22
|
+
class ParseError extends ParseErrorBase {
|
|
23
|
+
}
|
|
24
|
+
const CacheService = Context.GenericTag("type-registry-effect/CacheService");
|
|
25
|
+
const PackageFetcher = Context.GenericTag("type-registry-effect/PackageFetcher");
|
|
26
|
+
const JSDELIVR_DATA_API = "https://data.jsdelivr.com/v1";
|
|
27
|
+
const JSDELIVR_CDN = "https://cdn.jsdelivr.net";
|
|
28
|
+
const TYPE_FILE_PATTERN = /\.d\.([^.]+\.)?[cm]?ts$/i;
|
|
29
|
+
new Set([
|
|
30
|
+
"assert",
|
|
31
|
+
"async_hooks",
|
|
32
|
+
"buffer",
|
|
33
|
+
"child_process",
|
|
34
|
+
"cluster",
|
|
35
|
+
"console",
|
|
36
|
+
"constants",
|
|
37
|
+
"crypto",
|
|
38
|
+
"dgram",
|
|
39
|
+
"diagnostics_channel",
|
|
40
|
+
"dns",
|
|
41
|
+
"domain",
|
|
42
|
+
"events",
|
|
43
|
+
"fs",
|
|
44
|
+
"http",
|
|
45
|
+
"http2",
|
|
46
|
+
"https",
|
|
47
|
+
"inspector",
|
|
48
|
+
"module",
|
|
49
|
+
"net",
|
|
50
|
+
"os",
|
|
51
|
+
"path",
|
|
52
|
+
"perf_hooks",
|
|
53
|
+
"process",
|
|
54
|
+
"punycode",
|
|
55
|
+
"querystring",
|
|
56
|
+
"readline",
|
|
57
|
+
"repl",
|
|
58
|
+
"stream",
|
|
59
|
+
"string_decoder",
|
|
60
|
+
"timers",
|
|
61
|
+
"tls",
|
|
62
|
+
"trace_events",
|
|
63
|
+
"tty",
|
|
64
|
+
"url",
|
|
65
|
+
"util",
|
|
66
|
+
"v8",
|
|
67
|
+
"vm",
|
|
68
|
+
"wasi",
|
|
69
|
+
"worker_threads",
|
|
70
|
+
"zlib",
|
|
71
|
+
"fs/promises",
|
|
72
|
+
"stream/web",
|
|
73
|
+
"stream/consumers",
|
|
74
|
+
"timers/promises",
|
|
75
|
+
"dns/promises"
|
|
76
|
+
]);
|
|
77
|
+
const TypeResolver = Context.GenericTag("type-registry-effect/TypeResolver");
|
|
78
|
+
const hasCached = (pkg)=>Effect.gen(function*() {
|
|
79
|
+
const cache = yield* CacheService;
|
|
80
|
+
return yield* cache.exists(pkg);
|
|
81
|
+
});
|
|
82
|
+
const fetchAndCache = (pkg, options)=>Effect.gen(function*() {
|
|
83
|
+
const cache = yield* CacheService;
|
|
84
|
+
const fetcher = yield* PackageFetcher;
|
|
85
|
+
const packageJson = yield* fetcher.getPackageJson(pkg);
|
|
86
|
+
const typeFiles = yield* fetcher.getTypeFiles(pkg);
|
|
87
|
+
yield* cache.write(pkg, "package.json", JSON.stringify(packageJson, null, 2));
|
|
88
|
+
for (const [filePath, content] of typeFiles){
|
|
89
|
+
const normalizedPath = filePath.replace(/^\//, "");
|
|
90
|
+
if ("package.json" !== normalizedPath) yield* cache.write(pkg, normalizedPath, content);
|
|
91
|
+
}
|
|
92
|
+
const metadata = {
|
|
93
|
+
cachedAt: Date.now(),
|
|
94
|
+
version: pkg.version,
|
|
95
|
+
...options?.ttl !== void 0 ? {
|
|
96
|
+
ttl: options.ttl
|
|
97
|
+
} : {}
|
|
98
|
+
};
|
|
99
|
+
yield* cache.writeMetadata(pkg, metadata);
|
|
100
|
+
});
|
|
101
|
+
const getPackageVFS = (pkg, options)=>Effect.gen(function*() {
|
|
102
|
+
const cache = yield* CacheService;
|
|
103
|
+
const autoFetch = options?.autoFetch ?? true;
|
|
104
|
+
const exists = yield* cache.exists(pkg);
|
|
105
|
+
if (!exists && autoFetch) yield* fetchAndCache(pkg, options);
|
|
106
|
+
else if (exists || autoFetch) {
|
|
107
|
+
if (exists && autoFetch) {
|
|
108
|
+
const isStale = yield* cache.readMetadata(pkg).pipe(Effect.map((meta)=>{
|
|
109
|
+
if (void 0 === meta.ttl) return false;
|
|
110
|
+
return meta.cachedAt + meta.ttl < Date.now();
|
|
111
|
+
}), Effect.catchAll(()=>Effect.succeed(false)));
|
|
112
|
+
if (isStale) yield* fetchAndCache(pkg, options);
|
|
113
|
+
}
|
|
114
|
+
} else yield* Effect.fail(new PackageNotFoundError({
|
|
115
|
+
name: pkg.name,
|
|
116
|
+
version: pkg.version,
|
|
117
|
+
message: `Package ${pkg.toString()} is not cached and autoFetch is disabled`
|
|
118
|
+
}));
|
|
119
|
+
return yield* cache.getVFS(pkg);
|
|
120
|
+
});
|
|
121
|
+
const getVFS = (packages, options)=>Effect.gen(function*() {
|
|
122
|
+
const results = yield* Effect.forEach(packages, (pkg)=>getPackageVFS(pkg, options).pipe(Effect.map((vfs)=>({
|
|
123
|
+
_tag: "ok",
|
|
124
|
+
vfs
|
|
125
|
+
})), Effect.catchAll((error)=>Effect.succeed({
|
|
126
|
+
_tag: "err",
|
|
127
|
+
pkg,
|
|
128
|
+
error
|
|
129
|
+
}))), {
|
|
130
|
+
concurrency: 5
|
|
131
|
+
});
|
|
132
|
+
const merged = new Map();
|
|
133
|
+
const errors = [];
|
|
134
|
+
for (const result of results)if ("ok" === result._tag) for (const [path, content] of result.vfs)merged.set(path, content);
|
|
135
|
+
else errors.push({
|
|
136
|
+
pkg: result.pkg,
|
|
137
|
+
error: result.error
|
|
138
|
+
});
|
|
139
|
+
if (errors.length === packages.length && packages.length > 0) yield* Effect.fail(new PackageNotFoundError({
|
|
140
|
+
name: errors.map((e)=>e.pkg.name).join(", "),
|
|
141
|
+
version: "",
|
|
142
|
+
message: `Failed to fetch VFS for all packages:\n${errors.map((e)=>`- ${e.pkg.toString()}: ${String(e.error)}`).join("\n")}`
|
|
143
|
+
}));
|
|
144
|
+
return merged;
|
|
145
|
+
});
|
|
146
|
+
const resolveImport = (pkg, specifier)=>Effect.gen(function*() {
|
|
147
|
+
const cache = yield* CacheService;
|
|
148
|
+
const resolver = yield* TypeResolver;
|
|
149
|
+
const packageJsonContent = yield* cache.read(pkg, "package.json");
|
|
150
|
+
const packageJson = yield* Effect["try"]({
|
|
151
|
+
try: ()=>JSON.parse(packageJsonContent),
|
|
152
|
+
catch: (error)=>new ParseError({
|
|
153
|
+
source: "package.json",
|
|
154
|
+
message: String(error)
|
|
155
|
+
})
|
|
156
|
+
});
|
|
157
|
+
return yield* resolver.resolveImport(specifier, packageJson, pkg);
|
|
158
|
+
});
|
|
159
|
+
const getTypeEntries = (pkg)=>Effect.gen(function*() {
|
|
160
|
+
const cache = yield* CacheService;
|
|
161
|
+
const resolver = yield* TypeResolver;
|
|
162
|
+
const packageJsonContent = yield* cache.read(pkg, "package.json");
|
|
163
|
+
const packageJson = yield* Effect["try"]({
|
|
164
|
+
try: ()=>JSON.parse(packageJsonContent),
|
|
165
|
+
catch: (error)=>new ParseError({
|
|
166
|
+
source: "package.json",
|
|
167
|
+
message: String(error)
|
|
168
|
+
})
|
|
169
|
+
});
|
|
170
|
+
return yield* resolver.resolveTypeEntries(packageJson, pkg);
|
|
171
|
+
});
|
|
172
|
+
const resolveVersion = (name, ref)=>Effect.gen(function*() {
|
|
173
|
+
const fetcher = yield* PackageFetcher;
|
|
174
|
+
return yield* fetcher.resolveVersion(name, ref);
|
|
175
|
+
});
|
|
176
|
+
const clearCache = (pkg)=>Effect.gen(function*() {
|
|
177
|
+
const cache = yield* CacheService;
|
|
178
|
+
yield* cache.remove(pkg);
|
|
179
|
+
});
|
|
180
|
+
const CacheMetadata = Schema.Struct({
|
|
181
|
+
version: Schema.String,
|
|
182
|
+
cachedAt: Schema.Number,
|
|
183
|
+
ttl: Schema.optional(Schema.Number)
|
|
184
|
+
});
|
|
185
|
+
const FileTreeEntry = Schema.Struct({
|
|
186
|
+
name: Schema.String,
|
|
187
|
+
hash: Schema.String,
|
|
188
|
+
time: Schema.String,
|
|
189
|
+
size: Schema.Number
|
|
190
|
+
});
|
|
191
|
+
const FileTreeResponse = Schema.Struct({
|
|
192
|
+
default: Schema.String,
|
|
193
|
+
files: Schema.Array(FileTreeEntry)
|
|
194
|
+
});
|
|
195
|
+
const PackageJson = Schema.Struct({
|
|
196
|
+
name: Schema.String,
|
|
197
|
+
version: Schema.String,
|
|
198
|
+
types: Schema.optional(Schema.String),
|
|
199
|
+
typings: Schema.optional(Schema.String),
|
|
200
|
+
main: Schema.optional(Schema.String),
|
|
201
|
+
module: Schema.optional(Schema.String),
|
|
202
|
+
exports: Schema.optional(Schema.Union(Schema.String, Schema.Record({
|
|
203
|
+
key: Schema.String,
|
|
204
|
+
value: Schema.Unknown
|
|
205
|
+
}))),
|
|
206
|
+
typesVersions: Schema.optional(Schema.Record({
|
|
207
|
+
key: Schema.String,
|
|
208
|
+
value: Schema.Record({
|
|
209
|
+
key: Schema.String,
|
|
210
|
+
value: Schema.Array(Schema.String)
|
|
211
|
+
})
|
|
212
|
+
})),
|
|
213
|
+
dependencies: Schema.optional(Schema.Record({
|
|
214
|
+
key: Schema.String,
|
|
215
|
+
value: Schema.String
|
|
216
|
+
})),
|
|
217
|
+
peerDependencies: Schema.optional(Schema.Record({
|
|
218
|
+
key: Schema.String,
|
|
219
|
+
value: Schema.String
|
|
220
|
+
})),
|
|
221
|
+
devDependencies: Schema.optional(Schema.Record({
|
|
222
|
+
key: Schema.String,
|
|
223
|
+
value: Schema.String
|
|
224
|
+
}))
|
|
225
|
+
});
|
|
226
|
+
var _computedKey;
|
|
227
|
+
const PackageSpecBase = Data.TaggedClass("PackageSpec");
|
|
228
|
+
_computedKey = Symbol.for("nodejs.util.inspect.custom");
|
|
229
|
+
class PackageSpec extends PackageSpecBase {
|
|
230
|
+
toString() {
|
|
231
|
+
return `${this.name}@${this.version}`;
|
|
232
|
+
}
|
|
233
|
+
[_computedKey]() {
|
|
234
|
+
return this.toString();
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
const ResolvedModuleBase = Data.TaggedClass("ResolvedModule");
|
|
238
|
+
class ResolvedModule extends ResolvedModuleBase {
|
|
239
|
+
}
|
|
240
|
+
const CacheErrorBase = Data.TaggedError("CacheError");
|
|
241
|
+
class CacheError extends CacheErrorBase {
|
|
242
|
+
}
|
|
243
|
+
const NetworkErrorBase = Data.TaggedError("NetworkError");
|
|
244
|
+
class NetworkError extends NetworkErrorBase {
|
|
245
|
+
}
|
|
246
|
+
const ResolutionErrorBase = Data.TaggedError("ResolutionError");
|
|
247
|
+
class ResolutionError extends ResolutionErrorBase {
|
|
248
|
+
}
|
|
249
|
+
const TimeoutErrorBase = Data.TaggedError("TimeoutError");
|
|
250
|
+
class TimeoutError extends TimeoutErrorBase {
|
|
251
|
+
}
|
|
252
|
+
function getXdgCacheHome() {
|
|
253
|
+
const xdgCacheHome = process.env.XDG_CACHE_HOME;
|
|
254
|
+
if (xdgCacheHome && isAbsolute(xdgCacheHome)) return xdgCacheHome;
|
|
255
|
+
return join(external_node_os_homedir(), ".cache");
|
|
256
|
+
}
|
|
257
|
+
function getDefaultCacheDir() {
|
|
258
|
+
return join(getXdgCacheHome(), "effect-type-registry");
|
|
259
|
+
}
|
|
260
|
+
const pkgDir = (baseDir, pkg)=>join(baseDir, pkg.toString());
|
|
261
|
+
const mapToCacheError = (operation, path)=>(error)=>new CacheError({
|
|
262
|
+
operation,
|
|
263
|
+
path,
|
|
264
|
+
message: String(error)
|
|
265
|
+
});
|
|
266
|
+
const makeNodeCacheLayer = (baseDir)=>Layer.effect(CacheService, Effect.gen(function*() {
|
|
267
|
+
const fs = yield* FileSystem.FileSystem;
|
|
268
|
+
const cacheDir = baseDir ?? getDefaultCacheDir();
|
|
269
|
+
const listRecursive = (dir, relativeTo)=>Effect.gen(function*() {
|
|
270
|
+
const entries = yield* fs.readDirectory(dir).pipe(Effect.mapError(mapToCacheError("list", dir)));
|
|
271
|
+
const files = [];
|
|
272
|
+
for (const entry of entries){
|
|
273
|
+
const fullPath = join(dir, entry);
|
|
274
|
+
const stat = yield* fs.stat(fullPath).pipe(Effect.mapError(mapToCacheError("list", fullPath)));
|
|
275
|
+
if ("Directory" === stat.type) files.push(...yield* listRecursive(fullPath, relativeTo));
|
|
276
|
+
else files.push(relative(relativeTo, fullPath));
|
|
277
|
+
}
|
|
278
|
+
return files;
|
|
279
|
+
});
|
|
280
|
+
return {
|
|
281
|
+
exists: (pkg)=>fs.exists(pkgDir(cacheDir, pkg)).pipe(Effect.catchAll(()=>Effect.succeed(false))),
|
|
282
|
+
read: (pkg, filePath)=>{
|
|
283
|
+
const fullPath = join(pkgDir(cacheDir, pkg), filePath);
|
|
284
|
+
return fs.readFileString(fullPath).pipe(Effect.mapError(mapToCacheError("read", fullPath)));
|
|
285
|
+
},
|
|
286
|
+
write: (pkg, filePath, content)=>{
|
|
287
|
+
const fullPath = join(pkgDir(cacheDir, pkg), filePath);
|
|
288
|
+
const dirPath = dirname(fullPath);
|
|
289
|
+
return Effect.gen(function*() {
|
|
290
|
+
yield* fs.makeDirectory(dirPath, {
|
|
291
|
+
recursive: true
|
|
292
|
+
}).pipe(Effect.mapError(mapToCacheError("write", dirPath)));
|
|
293
|
+
yield* fs.writeFileString(fullPath, content).pipe(Effect.mapError(mapToCacheError("write", fullPath)));
|
|
294
|
+
});
|
|
295
|
+
},
|
|
296
|
+
listFiles: (pkg)=>{
|
|
297
|
+
const cachePath = pkgDir(cacheDir, pkg);
|
|
298
|
+
return listRecursive(cachePath, cachePath);
|
|
299
|
+
},
|
|
300
|
+
readMetadata: (pkg)=>{
|
|
301
|
+
const metaPath = join(pkgDir(cacheDir, pkg), ".metadata.json");
|
|
302
|
+
return fs.readFileString(metaPath).pipe(Effect.mapError(mapToCacheError("read", metaPath)), Effect.flatMap((content)=>Effect["try"]({
|
|
303
|
+
try: ()=>JSON.parse(content),
|
|
304
|
+
catch: mapToCacheError("read", metaPath)
|
|
305
|
+
})), Effect.flatMap((parsed)=>Schema.decodeUnknown(CacheMetadata)(parsed).pipe(Effect.mapError(mapToCacheError("read", metaPath)))));
|
|
306
|
+
},
|
|
307
|
+
writeMetadata: (pkg, metadata)=>{
|
|
308
|
+
const metaPath = join(pkgDir(cacheDir, pkg), ".metadata.json");
|
|
309
|
+
const dirPath = dirname(metaPath);
|
|
310
|
+
return Effect.gen(function*() {
|
|
311
|
+
yield* fs.makeDirectory(dirPath, {
|
|
312
|
+
recursive: true
|
|
313
|
+
}).pipe(Effect.mapError(mapToCacheError("write", dirPath)));
|
|
314
|
+
const encoded = Schema.encodeSync(CacheMetadata)(metadata);
|
|
315
|
+
yield* fs.writeFileString(metaPath, JSON.stringify(encoded, null, 2)).pipe(Effect.mapError(mapToCacheError("write", metaPath)));
|
|
316
|
+
});
|
|
317
|
+
},
|
|
318
|
+
getVFS: (pkg)=>Effect.gen(function*() {
|
|
319
|
+
const cachePath = pkgDir(cacheDir, pkg);
|
|
320
|
+
const files = yield* listRecursive(cachePath, cachePath);
|
|
321
|
+
const vfs = new Map();
|
|
322
|
+
for (const file of files){
|
|
323
|
+
if (".metadata.json" === file) continue;
|
|
324
|
+
const fullPath = join(cachePath, file);
|
|
325
|
+
const content = yield* fs.readFileString(fullPath).pipe(Effect.mapError(mapToCacheError("read", fullPath)));
|
|
326
|
+
const vfsPath = `node_modules/${pkg.name}/${file.replace(/\\/g, "/")}`;
|
|
327
|
+
vfs.set(vfsPath, content);
|
|
328
|
+
}
|
|
329
|
+
return vfs;
|
|
330
|
+
}),
|
|
331
|
+
remove: (pkg)=>{
|
|
332
|
+
const cachePath = pkgDir(cacheDir, pkg);
|
|
333
|
+
return fs.remove(cachePath, {
|
|
334
|
+
recursive: true
|
|
335
|
+
}).pipe(Effect.mapError(mapToCacheError("delete", cachePath)));
|
|
336
|
+
}
|
|
337
|
+
};
|
|
338
|
+
}));
|
|
339
|
+
const CacheServiceLive = makeNodeCacheLayer();
|
|
340
|
+
const PackageMetadataSchema = Schema.Struct({
|
|
341
|
+
versions: Schema.mutable(Schema.Array(Schema.String)),
|
|
342
|
+
tags: Schema.mutable(Schema.Record({
|
|
343
|
+
key: Schema.String,
|
|
344
|
+
value: Schema.String
|
|
345
|
+
}))
|
|
346
|
+
});
|
|
347
|
+
const ResolveResponseSchema = Schema.Struct({
|
|
348
|
+
version: Schema.String
|
|
349
|
+
});
|
|
350
|
+
const retrySchedule = Schedule.exponential(Duration.millis(100)).pipe(Schedule.compose(Schedule.recurs(3)));
|
|
351
|
+
const defaultTimeout = Duration.seconds(30);
|
|
352
|
+
const PackageFetcherLive = Layer.effect(PackageFetcher, Effect.gen(function*() {
|
|
353
|
+
const http = yield* HttpClient.HttpClient;
|
|
354
|
+
const fetchJson = (url)=>http.get(url).pipe(Effect.flatMap((res)=>res.json), Effect.timeout(defaultTimeout), Effect.retry(retrySchedule), Effect.mapError((error)=>new NetworkError({
|
|
355
|
+
url,
|
|
356
|
+
message: String(error)
|
|
357
|
+
})));
|
|
358
|
+
const fetchText = (url)=>http.get(url).pipe(Effect.flatMap((res)=>res.text), Effect.timeout(defaultTimeout), Effect.retry(retrySchedule), Effect.mapError((error)=>new NetworkError({
|
|
359
|
+
url,
|
|
360
|
+
message: String(error)
|
|
361
|
+
})));
|
|
362
|
+
const getFileTree = (pkg)=>fetchJson(`${JSDELIVR_DATA_API}/package/npm/${pkg.name}@${pkg.version}/flat`).pipe(Effect.flatMap((data)=>Schema.decodeUnknown(FileTreeResponse)(data).pipe(Effect.mapError((e)=>new ParseError({
|
|
363
|
+
source: `${pkg.name}@${pkg.version}/flat`,
|
|
364
|
+
message: `Schema validation failed: ${String(e)}`
|
|
365
|
+
})))));
|
|
366
|
+
return {
|
|
367
|
+
getVersions: (name)=>fetchJson(`${JSDELIVR_DATA_API}/package/npm/${name}`).pipe(Effect.flatMap((data)=>Schema.decodeUnknown(PackageMetadataSchema)(data).pipe(Effect.mapError((e)=>new ParseError({
|
|
368
|
+
source: `${name}/versions`,
|
|
369
|
+
message: `Schema validation failed: ${String(e)}`
|
|
370
|
+
}))))),
|
|
371
|
+
resolveVersion: (name, ref)=>fetchJson(`${JSDELIVR_DATA_API}/package/resolve/npm/${name}@${ref}`).pipe(Effect.flatMap((data)=>Schema.decodeUnknown(ResolveResponseSchema)(data).pipe(Effect.mapError((e)=>new ParseError({
|
|
372
|
+
source: `resolve/${name}@${ref}`,
|
|
373
|
+
message: `Invalid response: ${String(e)}`
|
|
374
|
+
})))), Effect.map((data)=>data.version), Effect.catchTags({
|
|
375
|
+
ParseError: (e)=>Effect.fail(new PackageNotFoundError({
|
|
376
|
+
name,
|
|
377
|
+
version: ref,
|
|
378
|
+
message: e.message
|
|
379
|
+
})),
|
|
380
|
+
NetworkError: (e)=>e.message.includes("404") ? Effect.fail(new PackageNotFoundError({
|
|
381
|
+
name,
|
|
382
|
+
version: ref,
|
|
383
|
+
message: e.message
|
|
384
|
+
})) : Effect.fail(e)
|
|
385
|
+
})),
|
|
386
|
+
getFileTree,
|
|
387
|
+
downloadFile: (pkg, filePath)=>{
|
|
388
|
+
const normalizedPath = filePath.startsWith("/") ? filePath : `/${filePath}`;
|
|
389
|
+
return fetchText(`${JSDELIVR_CDN}/npm/${pkg.name}@${pkg.version}${normalizedPath}`);
|
|
390
|
+
},
|
|
391
|
+
getPackageJson: (pkg)=>fetchText(`${JSDELIVR_CDN}/npm/${pkg.name}@${pkg.version}/package.json`).pipe(Effect.flatMap((content)=>Effect["try"]({
|
|
392
|
+
try: ()=>JSON.parse(content),
|
|
393
|
+
catch: (e)=>new ParseError({
|
|
394
|
+
source: `${pkg.name}@${pkg.version}/package.json`,
|
|
395
|
+
message: `JSON parse failed: ${String(e)}`
|
|
396
|
+
})
|
|
397
|
+
})), Effect.flatMap((parsed)=>Schema.decodeUnknown(PackageJson)(parsed).pipe(Effect.mapError((e)=>new ParseError({
|
|
398
|
+
source: `${pkg.name}@${pkg.version}/package.json`,
|
|
399
|
+
message: `Schema validation failed: ${String(e)}`
|
|
400
|
+
}))))),
|
|
401
|
+
getTypeFiles: (pkg)=>Effect.gen(function*() {
|
|
402
|
+
const fileTree = yield* getFileTree(pkg);
|
|
403
|
+
const typeFiles = fileTree.files.filter((f)=>TYPE_FILE_PATTERN.test(f.name));
|
|
404
|
+
const entries = yield* Effect.forEach(typeFiles, (file)=>{
|
|
405
|
+
const normalizedPath = file.name.startsWith("/") ? file.name : `/${file.name}`;
|
|
406
|
+
return fetchText(`${JSDELIVR_CDN}/npm/${pkg.name}@${pkg.version}${normalizedPath}`).pipe(Effect.map((content)=>[
|
|
407
|
+
file.name,
|
|
408
|
+
content
|
|
409
|
+
]));
|
|
410
|
+
}, {
|
|
411
|
+
concurrency: 10
|
|
412
|
+
});
|
|
413
|
+
const vfs = new Map(entries);
|
|
414
|
+
const hasPackageJson = fileTree.files.some((f)=>"/package.json" === f.name);
|
|
415
|
+
if (!hasPackageJson) {
|
|
416
|
+
const pkgContent = yield* fetchText(`${JSDELIVR_CDN}/npm/${pkg.name}@${pkg.version}/package.json`);
|
|
417
|
+
vfs.set("/package.json", pkgContent);
|
|
418
|
+
}
|
|
419
|
+
return vfs;
|
|
420
|
+
})
|
|
421
|
+
};
|
|
422
|
+
}));
|
|
423
|
+
function isTypeDefinition(filePath) {
|
|
424
|
+
return filePath.endsWith(".d.ts") || filePath.endsWith(".d.mts") || filePath.endsWith(".d.cts");
|
|
425
|
+
}
|
|
426
|
+
function normalizePath(path) {
|
|
427
|
+
return path.replace(/\\/g, "/");
|
|
428
|
+
}
|
|
429
|
+
function escapeRegex(str) {
|
|
430
|
+
return str.replace(/[.+^${}()|[\]\\]/g, "\\$&");
|
|
431
|
+
}
|
|
432
|
+
function substituteWildcard(value, captured) {
|
|
433
|
+
if ("string" == typeof value) return value.replace(/\*/g, captured);
|
|
434
|
+
if ("object" == typeof value && null !== value) {
|
|
435
|
+
const result = {};
|
|
436
|
+
for (const [k, v] of Object.entries(value))if ("string" == typeof v) result[k] = v.replace(/\*/g, captured);
|
|
437
|
+
else if ("object" == typeof v && null !== v) result[k] = substituteWildcard(v, captured);
|
|
438
|
+
else result[k] = v;
|
|
439
|
+
return result;
|
|
440
|
+
}
|
|
441
|
+
return value;
|
|
442
|
+
}
|
|
443
|
+
function tryExtensions(basePath) {
|
|
444
|
+
return [
|
|
445
|
+
basePath,
|
|
446
|
+
`${basePath}.d.ts`,
|
|
447
|
+
`${basePath}.d.mts`,
|
|
448
|
+
`${basePath}.d.cts`,
|
|
449
|
+
`${basePath}.ts`,
|
|
450
|
+
`${basePath}.mts`,
|
|
451
|
+
`${basePath}.cts`,
|
|
452
|
+
`${basePath}.js`,
|
|
453
|
+
`${basePath}.mjs`,
|
|
454
|
+
`${basePath}.cjs`,
|
|
455
|
+
`${basePath}/index.d.ts`,
|
|
456
|
+
`${basePath}/index.d.mts`,
|
|
457
|
+
`${basePath}/index.d.cts`,
|
|
458
|
+
`${basePath}/index.ts`,
|
|
459
|
+
`${basePath}/index.js`
|
|
460
|
+
].map(normalizePath);
|
|
461
|
+
}
|
|
462
|
+
function getExportValue(exports, subpath) {
|
|
463
|
+
if (!exports) return null;
|
|
464
|
+
if ("string" == typeof exports) return "." === subpath ? exports : null;
|
|
465
|
+
if ("object" == typeof exports && null !== exports) {
|
|
466
|
+
const exportsObj = exports;
|
|
467
|
+
const value = exportsObj[subpath];
|
|
468
|
+
if (void 0 !== value) return value;
|
|
469
|
+
const withoutDot = subpath.replace(/^\.\//, "");
|
|
470
|
+
const alt = exportsObj[withoutDot];
|
|
471
|
+
if (void 0 !== alt) return alt;
|
|
472
|
+
for (const [pattern, val] of Object.entries(exportsObj))if (pattern.includes("*")) {
|
|
473
|
+
const escaped = escapeRegex(pattern).replace(/\*/g, "(.*)");
|
|
474
|
+
const regex = new RegExp(`^${escaped}$`);
|
|
475
|
+
const match = regex.exec(subpath) || regex.exec(withoutDot);
|
|
476
|
+
if (match) {
|
|
477
|
+
const captured = match[1] || "";
|
|
478
|
+
return substituteWildcard(val, captured);
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
return null;
|
|
483
|
+
}
|
|
484
|
+
function findMainTypePath(packageJson) {
|
|
485
|
+
if (packageJson.types) return packageJson.types;
|
|
486
|
+
if (packageJson.typings) return packageJson.typings;
|
|
487
|
+
if (packageJson.exports) {
|
|
488
|
+
const rootExport = getExportValue(packageJson.exports, ".");
|
|
489
|
+
if (rootExport) {
|
|
490
|
+
const typesPath = extractTypesFromExport(rootExport);
|
|
491
|
+
if (typesPath) return typesPath;
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
if (packageJson.main) {
|
|
495
|
+
const mainWithoutExt = packageJson.main.replace(/\.(m?[jt]s|cjs)$/, "");
|
|
496
|
+
const candidates = tryExtensions(mainWithoutExt);
|
|
497
|
+
const found = candidates.find((c)=>isTypeDefinition(c));
|
|
498
|
+
if (found) return found;
|
|
499
|
+
return packageJson.main;
|
|
500
|
+
}
|
|
501
|
+
return "index.d.ts";
|
|
502
|
+
}
|
|
503
|
+
function extractTypesFromExport(exportValue) {
|
|
504
|
+
if (!exportValue) return null;
|
|
505
|
+
if ("string" == typeof exportValue) return exportValue;
|
|
506
|
+
if ("object" == typeof exportValue && null !== exportValue) {
|
|
507
|
+
const obj = exportValue;
|
|
508
|
+
if ("string" == typeof obj.types) return obj.types;
|
|
509
|
+
if ("string" == typeof obj.import) return obj.import;
|
|
510
|
+
if ("string" == typeof obj.default) return obj.default;
|
|
511
|
+
if ("object" == typeof obj.import) return extractTypesFromExport(obj.import);
|
|
512
|
+
if ("object" == typeof obj.default) return extractTypesFromExport(obj.default);
|
|
513
|
+
}
|
|
514
|
+
return null;
|
|
515
|
+
}
|
|
516
|
+
const TypeResolverLive = Layer.succeed(TypeResolver, {
|
|
517
|
+
resolveImport: (specifier, packageJson, pkg)=>Effect.sync(()=>{
|
|
518
|
+
let subpath = specifier;
|
|
519
|
+
if (specifier.startsWith(pkg.name)) subpath = specifier.slice(pkg.name.length);
|
|
520
|
+
subpath = subpath.replace(/^\//, "");
|
|
521
|
+
if (!subpath.startsWith(".")) subpath = `./${subpath}`;
|
|
522
|
+
if (packageJson.exports) {
|
|
523
|
+
const lookupPath = subpath.startsWith("./") ? subpath : `./${subpath}`;
|
|
524
|
+
const exportValue = getExportValue(packageJson.exports, lookupPath);
|
|
525
|
+
if (exportValue) {
|
|
526
|
+
const typesPath = extractTypesFromExport(exportValue);
|
|
527
|
+
if (typesPath) return new ResolvedModule({
|
|
528
|
+
filePath: normalizePath(typesPath.replace(/^\.\//, "")),
|
|
529
|
+
isTypeDefinition: isTypeDefinition(typesPath),
|
|
530
|
+
package: pkg
|
|
531
|
+
});
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
if (packageJson.typesVersions) {
|
|
535
|
+
const versionMap = packageJson.typesVersions["*"];
|
|
536
|
+
if (versionMap) {
|
|
537
|
+
const lookupPath = subpath.replace(/^\.\//, "");
|
|
538
|
+
if (versionMap[lookupPath]) {
|
|
539
|
+
const resolved = versionMap[lookupPath];
|
|
540
|
+
const path = Array.isArray(resolved) ? resolved[0] : resolved;
|
|
541
|
+
if (path) return new ResolvedModule({
|
|
542
|
+
filePath: normalizePath(path.replace(/^\.\//, "")),
|
|
543
|
+
isTypeDefinition: isTypeDefinition(path),
|
|
544
|
+
package: pkg
|
|
545
|
+
});
|
|
546
|
+
}
|
|
547
|
+
for (const [pattern, paths] of Object.entries(versionMap))if (pattern.includes("*")) {
|
|
548
|
+
const escaped = escapeRegex(pattern).replace(/\*/g, "(.*)");
|
|
549
|
+
const regex = new RegExp(`^${escaped}$`);
|
|
550
|
+
if (regex.test(lookupPath)) {
|
|
551
|
+
const resolved = Array.isArray(paths) ? paths[0] : paths;
|
|
552
|
+
if (resolved) {
|
|
553
|
+
const captured = lookupPath.replace(regex, "$1");
|
|
554
|
+
const finalPath = resolved.replace("*", captured);
|
|
555
|
+
return new ResolvedModule({
|
|
556
|
+
filePath: normalizePath(finalPath.replace(/^\.\//, "")),
|
|
557
|
+
isTypeDefinition: isTypeDefinition(finalPath),
|
|
558
|
+
package: pkg
|
|
559
|
+
});
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
const candidates = tryExtensions(subpath.replace(/^\.\//, ""));
|
|
566
|
+
for (const candidate of candidates)if (isTypeDefinition(candidate)) return new ResolvedModule({
|
|
567
|
+
filePath: normalizePath(candidate),
|
|
568
|
+
isTypeDefinition: true,
|
|
569
|
+
package: pkg
|
|
570
|
+
});
|
|
571
|
+
const fallback = normalizePath(candidates[0] || subpath);
|
|
572
|
+
return new ResolvedModule({
|
|
573
|
+
filePath: fallback,
|
|
574
|
+
isTypeDefinition: isTypeDefinition(candidates[0] || subpath),
|
|
575
|
+
package: pkg
|
|
576
|
+
});
|
|
577
|
+
}),
|
|
578
|
+
resolveMainEntry: (packageJson, pkg)=>Effect.sync(()=>{
|
|
579
|
+
const mainPath = findMainTypePath(packageJson);
|
|
580
|
+
const normalizedPath = normalizePath(mainPath.replace(/^\.\//, ""));
|
|
581
|
+
return new ResolvedModule({
|
|
582
|
+
filePath: normalizedPath,
|
|
583
|
+
isTypeDefinition: isTypeDefinition(normalizedPath),
|
|
584
|
+
package: pkg
|
|
585
|
+
});
|
|
586
|
+
}),
|
|
587
|
+
resolveTypeEntries: (packageJson, pkg)=>Effect.sync(()=>{
|
|
588
|
+
const entries = [];
|
|
589
|
+
const mainPath = findMainTypePath(packageJson);
|
|
590
|
+
entries.push(new ResolvedModule({
|
|
591
|
+
filePath: normalizePath(mainPath.replace(/^\.\//, "")),
|
|
592
|
+
isTypeDefinition: isTypeDefinition(mainPath),
|
|
593
|
+
package: pkg
|
|
594
|
+
}));
|
|
595
|
+
if (packageJson.exports && "object" == typeof packageJson.exports) for (const [key, value] of Object.entries(packageJson.exports)){
|
|
596
|
+
if (!key.startsWith(".") && "*" !== key) continue;
|
|
597
|
+
const typesPath = extractTypesFromExport(value);
|
|
598
|
+
if (typesPath) entries.push(new ResolvedModule({
|
|
599
|
+
filePath: normalizePath(typesPath.replace(/^\.\//, "")),
|
|
600
|
+
isTypeDefinition: isTypeDefinition(typesPath),
|
|
601
|
+
package: pkg
|
|
602
|
+
}));
|
|
603
|
+
}
|
|
604
|
+
const seen = new Set();
|
|
605
|
+
return entries.filter((e)=>{
|
|
606
|
+
if (seen.has(e.filePath)) return false;
|
|
607
|
+
seen.add(e.filePath);
|
|
608
|
+
return true;
|
|
609
|
+
});
|
|
610
|
+
}),
|
|
611
|
+
findTypeDefinition: (jsFilePath, _packageJson, pkg)=>Effect.sync(()=>{
|
|
612
|
+
let typePath;
|
|
613
|
+
if (jsFilePath.endsWith(".mjs")) typePath = jsFilePath.replace(/\.mjs$/, ".d.mts");
|
|
614
|
+
else if (jsFilePath.endsWith(".cjs")) typePath = jsFilePath.replace(/\.cjs$/, ".d.cts");
|
|
615
|
+
else if (jsFilePath.endsWith(".js")) typePath = jsFilePath.replace(/\.js$/, ".d.ts");
|
|
616
|
+
else {
|
|
617
|
+
const withoutExt = jsFilePath.replace(/\.(m?js|cjs)$/, "");
|
|
618
|
+
typePath = `${withoutExt}.d.ts`;
|
|
619
|
+
}
|
|
620
|
+
return new ResolvedModule({
|
|
621
|
+
filePath: normalizePath(typePath),
|
|
622
|
+
isTypeDefinition: true,
|
|
623
|
+
package: pkg
|
|
624
|
+
});
|
|
625
|
+
})
|
|
626
|
+
});
|
|
627
|
+
const TypeRegistryLive = Layer.mergeAll(CacheServiceLive, PackageFetcherLive, TypeResolverLive);
|
|
628
|
+
export { CacheError, CacheErrorBase, CacheMetadata, CacheService, CacheServiceLive, Effect, FileTreeEntry, FileTreeResponse, Layer, NetworkError, NetworkErrorBase, PackageFetcher, PackageFetcherLive, PackageJson, PackageNotFoundError, PackageNotFoundErrorBase, PackageSpec, PackageSpecBase, ParseError, ParseErrorBase, ResolutionError, ResolutionErrorBase, ResolvedModule, ResolvedModuleBase, TimeoutError, TimeoutErrorBase, TypeRegistryLive, TypeRegistry_namespaceObject as TypeRegistry, TypeResolver, TypeResolverLive, fetchAndCache, getDefaultCacheDir, getVFS, hasCached, makeNodeCacheLayer, resolveVersion };
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 C. Spencer Beggs
|
|
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.
|