@veryfront/ext-content-mdx 0.1.1190 → 0.1.1192
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/esm/deno.d.ts +6 -0
- package/esm/deno.js +13 -7
- package/esm/src/errors/error-registry/module.d.ts +4 -0
- package/esm/src/errors/error-registry/module.d.ts.map +1 -1
- package/esm/src/errors/error-registry/module.js +16 -0
- package/esm/src/errors/error-registry.d.ts +2 -0
- package/esm/src/errors/error-registry.d.ts.map +1 -1
- package/esm/src/errors/types.d.ts.map +1 -1
- package/esm/src/errors/types.js +5 -2
- package/esm/src/platform/compat/error-introspection.d.ts +12 -1
- package/esm/src/platform/compat/error-introspection.d.ts.map +1 -1
- package/esm/src/platform/compat/error-introspection.js +40 -5
- package/esm/src/platform/compat/path/basic-operations.d.ts.map +1 -1
- package/esm/src/platform/compat/path/basic-operations.js +6 -2
- package/esm/src/platform/compat/path/portable.d.ts.map +1 -1
- package/esm/src/platform/compat/path/portable.js +98 -54
- package/esm/src/platform/compat/path/resolution.d.ts.map +1 -1
- package/esm/src/platform/compat/path/resolution.js +14 -5
- package/esm/src/platform/compat/primordials/array.d.ts +8 -0
- package/esm/src/platform/compat/primordials/array.d.ts.map +1 -0
- package/esm/src/platform/compat/primordials/array.js +33 -0
- package/esm/src/transforms/import-rewriter/url-builder.d.ts +6 -0
- package/esm/src/transforms/import-rewriter/url-builder.d.ts.map +1 -1
- package/esm/src/transforms/import-rewriter/url-builder.js +96 -29
- package/esm/src/utils/base64url.d.ts.map +1 -1
- package/esm/src/utils/base64url.js +10 -4
- package/esm/src/utils/bundle-manifest.d.ts +19 -1
- package/esm/src/utils/bundle-manifest.d.ts.map +1 -1
- package/esm/src/utils/bundle-manifest.js +124 -27
- package/esm/src/utils/constants/limits.d.ts +1 -1
- package/esm/src/utils/constants/limits.d.ts.map +1 -1
- package/esm/src/utils/constants/limits.js +1 -1
- package/esm/src/utils/hash-utils.d.ts.map +1 -1
- package/esm/src/utils/hash-utils.js +32 -6
- package/esm/src/utils/id.d.ts +16 -0
- package/esm/src/utils/id.d.ts.map +1 -0
- package/esm/src/utils/id.js +69 -0
- package/esm/src/utils/import-lockfile.d.ts +52 -1
- package/esm/src/utils/import-lockfile.d.ts.map +1 -1
- package/esm/src/utils/import-lockfile.js +1042 -58
- package/esm/src/utils/index.d.ts +1 -1
- package/esm/src/utils/index.d.ts.map +1 -1
- package/esm/src/utils/index.js +1 -1
- package/esm/src/utils/logger/logger.d.ts +1 -0
- package/esm/src/utils/logger/logger.d.ts.map +1 -1
- package/esm/src/utils/logger/logger.js +359 -78
- package/esm/src/utils/logger/redact.d.ts +4 -2
- package/esm/src/utils/logger/redact.d.ts.map +1 -1
- package/esm/src/utils/logger/redact.js +300 -112
- package/esm/src/utils/logger/serialization.d.ts +3 -0
- package/esm/src/utils/logger/serialization.d.ts.map +1 -0
- package/esm/src/utils/logger/serialization.js +81 -0
- package/esm/src/utils/memoize.d.ts +1 -1
- package/esm/src/utils/memoize.d.ts.map +1 -1
- package/esm/src/utils/memoize.js +16 -5
- package/esm/src/utils/path-utils.d.ts.map +1 -1
- package/esm/src/utils/path-utils.js +4 -2
- package/esm/src/utils/sleep.d.ts +8 -0
- package/esm/src/utils/sleep.d.ts.map +1 -1
- package/esm/src/utils/sleep.js +11 -2
- package/esm/src/utils/timer.d.ts +10 -0
- package/esm/src/utils/timer.d.ts.map +1 -0
- package/esm/src/utils/timer.js +19 -0
- package/esm/src/utils/version-constant.d.ts +1 -1
- package/esm/src/utils/version-constant.js +1 -1
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"base64url.d.ts","sourceRoot":"","sources":["../../../src/src/utils/base64url.ts"],"names":[],"mappings":"AAYA;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAelD;AAED,2CAA2C;AAC3C,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,
|
|
1
|
+
{"version":3,"file":"base64url.d.ts","sourceRoot":"","sources":["../../../src/src/utils/base64url.ts"],"names":[],"mappings":"AAYA;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAelD;AAED,2CAA2C;AAC3C,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAoB3D;AAED,6CAA6C;AAC7C,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAErD;AAED,8CAA8C;AAC9C,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAE9D"}
|
|
@@ -36,10 +36,16 @@ export function encodeBase64Bytes(bytes) {
|
|
|
36
36
|
if (bufferCtor)
|
|
37
37
|
return bufferCtor.from(bytes).toString("base64");
|
|
38
38
|
if (typeof globalThis.btoa === "function") {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
39
|
+
// Bound the peak temporary binary string to 24 KiB. The chunk size is
|
|
40
|
+
// divisible by three, so only the final base64 chunk can contain padding.
|
|
41
|
+
const chunkSize = 24 * 1024;
|
|
42
|
+
const encodedChunks = [];
|
|
43
|
+
for (let offset = 0; offset < bytes.length; offset += chunkSize) {
|
|
44
|
+
const end = Math.min(offset + chunkSize, bytes.length);
|
|
45
|
+
const binaryChunk = String.fromCharCode(...bytes.subarray(offset, end));
|
|
46
|
+
encodedChunks.push(globalThis.btoa(binaryChunk));
|
|
47
|
+
}
|
|
48
|
+
return encodedChunks.join("");
|
|
43
49
|
}
|
|
44
50
|
throw new Error("Base64 encoding is not supported in this runtime");
|
|
45
51
|
}
|
|
@@ -41,11 +41,14 @@ export interface BundleManifestStore {
|
|
|
41
41
|
isAvailable(): Promise<boolean>;
|
|
42
42
|
getStats(): Promise<BundleManifestStats>;
|
|
43
43
|
}
|
|
44
|
+
/** Minimum interval between full expired-metadata sweeps (amortized on writes). */
|
|
45
|
+
export declare const BUNDLE_MANIFEST_SWEEP_INTERVAL_MS = 30000;
|
|
44
46
|
export declare class InMemoryBundleManifestStore implements BundleManifestStore {
|
|
45
47
|
private metadata;
|
|
46
48
|
private code;
|
|
47
49
|
private sourceIndex;
|
|
48
|
-
private
|
|
50
|
+
private codeReferences;
|
|
51
|
+
private lastExpiredSweepAt;
|
|
49
52
|
getBundleMetadata(key: string): Promise<BundleMetadata | undefined>;
|
|
50
53
|
setBundleMetadata(key: string, metadata: BundleMetadata, ttlMs?: number): Promise<void>;
|
|
51
54
|
getBundleCode(hash: string): Promise<BundleCode | undefined>;
|
|
@@ -55,6 +58,21 @@ export declare class InMemoryBundleManifestStore implements BundleManifestStore
|
|
|
55
58
|
clear(): Promise<void>;
|
|
56
59
|
isAvailable(): Promise<boolean>;
|
|
57
60
|
getStats(): Promise<BundleManifestStats>;
|
|
61
|
+
private addCodeReference;
|
|
62
|
+
/**
|
|
63
|
+
* Drop expired metadata entries referencing one code hash so the reference
|
|
64
|
+
* check in getBundleCode stays trustworthy without a full-map sweep.
|
|
65
|
+
*/
|
|
66
|
+
private pruneExpiredReferencesTo;
|
|
67
|
+
/**
|
|
68
|
+
* Full expired-metadata sweep, amortized: runs on writes at most once per
|
|
69
|
+
* BUNDLE_MANIFEST_SWEEP_INTERVAL_MS so unread expired entries cannot pin
|
|
70
|
+
* memory forever while per-render reads stay off the O(all-metadata) path.
|
|
71
|
+
*/
|
|
72
|
+
private sweepExpiredMetadataIfDue;
|
|
73
|
+
private removeCodeReference;
|
|
74
|
+
private removeMetadata;
|
|
75
|
+
private removeSourceReference;
|
|
58
76
|
}
|
|
59
77
|
export declare function setBundleManifestStore(store: BundleManifestStore): void;
|
|
60
78
|
/** Return bundle manifest store. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bundle-manifest.d.ts","sourceRoot":"","sources":["../../../src/src/utils/bundle-manifest.ts"],"names":[],"mappings":"AAIA,+CAA+C;AAC/C,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,aAAa,GAAG,YAAY,CAAC;IACnC,IAAI,CAAC,EAAE;QACL,IAAI,CAAC,EAAE,KAAK,GAAG,WAAW,GAAG,QAAQ,GAAG,UAAU,CAAC;QACnD,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,6DAA6D;QAC7D,QAAQ,CAAC,EAAE,KAAK,CAAC;YAAE,EAAE,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE,MAAM,CAAC;YAAC,KAAK,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;KAC/D,CAAC;CACH;AAED,2CAA2C;AAC3C,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,mBAAmB;IAClC,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,mBAAmB;IAClC,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC,CAAC;IACpE,iBAAiB,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,cAAc,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACxF,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC,CAAC;IAC7D,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7E,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACzC,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAClD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;IAChC,QAAQ,IAAI,OAAO,CAAC,mBAAmB,CAAC,CAAC;CAC1C;AAED,qBAAa,2BAA4B,YAAW,mBAAmB;IACrE,OAAO,CAAC,QAAQ,CAAiE;IACjF,OAAO,CAAC,IAAI,CAA6D;IACzE,OAAO,CAAC,WAAW,CAAkC;
|
|
1
|
+
{"version":3,"file":"bundle-manifest.d.ts","sourceRoot":"","sources":["../../../src/src/utils/bundle-manifest.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,55 +1,79 @@
|
|
|
1
1
|
import { serverLogger } from "./logger/index.js";
|
|
2
2
|
const logger = serverLogger.component("bundle-manifest");
|
|
3
|
+
/** Minimum interval between full expired-metadata sweeps (amortized on writes). */
|
|
4
|
+
export const BUNDLE_MANIFEST_SWEEP_INTERVAL_MS = 30_000;
|
|
3
5
|
export class InMemoryBundleManifestStore {
|
|
4
6
|
metadata = new Map();
|
|
5
7
|
code = new Map();
|
|
6
8
|
sourceIndex = new Map();
|
|
7
|
-
|
|
8
|
-
|
|
9
|
+
// codeHash → metadata keys holding a reference. Each key holds at most one
|
|
10
|
+
// reference per hash, so set size is the reference count and the members
|
|
11
|
+
// let reads prune only the metadata entries relevant to one hash.
|
|
12
|
+
codeReferences = new Map();
|
|
13
|
+
lastExpiredSweepAt = Date.now();
|
|
14
|
+
async getBundleMetadata(key) {
|
|
15
|
+
const entry = this.metadata.get(key);
|
|
9
16
|
if (!entry)
|
|
10
17
|
return undefined;
|
|
11
|
-
if (entry.expiry != null && Date.now()
|
|
12
|
-
|
|
18
|
+
if (entry.expiry != null && Date.now() >= entry.expiry) {
|
|
19
|
+
this.removeMetadata(key);
|
|
13
20
|
return undefined;
|
|
14
21
|
}
|
|
15
|
-
return entry.value;
|
|
16
|
-
}
|
|
17
|
-
async getBundleMetadata(key) {
|
|
18
|
-
return this.getIfNotExpired(this.metadata, key);
|
|
22
|
+
return structuredClone(entry.value);
|
|
19
23
|
}
|
|
20
24
|
async setBundleMetadata(key, metadata, ttlMs) {
|
|
25
|
+
this.sweepExpiredMetadataIfDue();
|
|
21
26
|
const expiry = ttlMs != null ? Date.now() + ttlMs : undefined;
|
|
22
|
-
|
|
23
|
-
const
|
|
27
|
+
const snapshot = structuredClone(metadata);
|
|
28
|
+
const previous = this.metadata.get(key)?.value;
|
|
29
|
+
if (!previous) {
|
|
30
|
+
this.addCodeReference(snapshot.codeHash, key);
|
|
31
|
+
}
|
|
32
|
+
else if (previous.codeHash !== snapshot.codeHash) {
|
|
33
|
+
this.removeCodeReference(previous.codeHash, key);
|
|
34
|
+
this.addCodeReference(snapshot.codeHash, key);
|
|
35
|
+
}
|
|
36
|
+
if (previous && previous.source !== snapshot.source) {
|
|
37
|
+
this.removeSourceReference(key, previous.source);
|
|
38
|
+
}
|
|
39
|
+
this.metadata.set(key, { value: snapshot, expiry });
|
|
40
|
+
const keys = this.sourceIndex.get(snapshot.source) ?? new Set();
|
|
24
41
|
keys.add(key);
|
|
25
|
-
this.sourceIndex.set(
|
|
42
|
+
this.sourceIndex.set(snapshot.source, keys);
|
|
26
43
|
}
|
|
27
44
|
async getBundleCode(hash) {
|
|
28
|
-
|
|
45
|
+
// Only the metadata entries referencing this hash decide its liveness, so
|
|
46
|
+
// prune those lazily instead of sweeping the entire metadata map on every
|
|
47
|
+
// per-render read. Full sweeps are amortized onto writes.
|
|
48
|
+
this.pruneExpiredReferencesTo(hash);
|
|
49
|
+
const entry = this.code.get(hash);
|
|
50
|
+
if (!entry)
|
|
51
|
+
return undefined;
|
|
52
|
+
// Metadata references own code liveness. A shorter code TTL must not turn
|
|
53
|
+
// a still-valid manifest into a pointer to a missing content blob.
|
|
54
|
+
if (entry.expiry != null &&
|
|
55
|
+
Date.now() >= entry.expiry &&
|
|
56
|
+
!this.codeReferences.has(hash)) {
|
|
57
|
+
this.code.delete(hash);
|
|
58
|
+
return undefined;
|
|
59
|
+
}
|
|
60
|
+
return structuredClone(entry.value);
|
|
29
61
|
}
|
|
30
62
|
async setBundleCode(hash, code, ttlMs) {
|
|
63
|
+
this.sweepExpiredMetadataIfDue();
|
|
31
64
|
const expiry = ttlMs != null ? Date.now() + ttlMs : undefined;
|
|
32
|
-
this.code.set(hash, { value: code, expiry });
|
|
65
|
+
this.code.set(hash, { value: structuredClone(code), expiry });
|
|
33
66
|
}
|
|
34
67
|
async deleteBundle(key) {
|
|
35
|
-
|
|
36
|
-
this.metadata.delete(key);
|
|
37
|
-
if (!metadata)
|
|
38
|
-
return;
|
|
39
|
-
this.code.delete(metadata.codeHash);
|
|
40
|
-
const sourceKeys = this.sourceIndex.get(metadata.source);
|
|
41
|
-
if (!sourceKeys)
|
|
42
|
-
return;
|
|
43
|
-
sourceKeys.delete(key);
|
|
44
|
-
if (sourceKeys.size === 0)
|
|
45
|
-
this.sourceIndex.delete(metadata.source);
|
|
68
|
+
this.removeMetadata(key);
|
|
46
69
|
}
|
|
47
70
|
async invalidateSource(source) {
|
|
48
71
|
const keys = this.sourceIndex.get(source);
|
|
49
72
|
if (!keys)
|
|
50
73
|
return 0;
|
|
51
74
|
const keysArray = [...keys];
|
|
52
|
-
|
|
75
|
+
for (const key of keysArray)
|
|
76
|
+
this.removeMetadata(key);
|
|
53
77
|
this.sourceIndex.delete(source);
|
|
54
78
|
return keysArray.length;
|
|
55
79
|
}
|
|
@@ -57,15 +81,23 @@ export class InMemoryBundleManifestStore {
|
|
|
57
81
|
this.metadata.clear();
|
|
58
82
|
this.code.clear();
|
|
59
83
|
this.sourceIndex.clear();
|
|
84
|
+
this.codeReferences.clear();
|
|
60
85
|
}
|
|
61
86
|
async isAvailable() {
|
|
62
87
|
return true;
|
|
63
88
|
}
|
|
64
89
|
async getStats() {
|
|
90
|
+
// Non-mutating expiry view: expired entries are excluded from the
|
|
91
|
+
// aggregate without paying a pruning sweep on this read path.
|
|
92
|
+
const now = Date.now();
|
|
93
|
+
let totalBundles = 0;
|
|
65
94
|
let totalSize = 0;
|
|
66
95
|
let oldestBundle;
|
|
67
96
|
let newestBundle;
|
|
68
|
-
for (const { value } of this.metadata.values()) {
|
|
97
|
+
for (const { value, expiry } of this.metadata.values()) {
|
|
98
|
+
if (expiry != null && now >= expiry)
|
|
99
|
+
continue;
|
|
100
|
+
totalBundles++;
|
|
69
101
|
totalSize += value.size;
|
|
70
102
|
oldestBundle = oldestBundle == null
|
|
71
103
|
? value.compiledAt
|
|
@@ -75,12 +107,77 @@ export class InMemoryBundleManifestStore {
|
|
|
75
107
|
: Math.max(newestBundle, value.compiledAt);
|
|
76
108
|
}
|
|
77
109
|
return {
|
|
78
|
-
totalBundles
|
|
110
|
+
totalBundles,
|
|
79
111
|
totalSize,
|
|
80
112
|
oldestBundle,
|
|
81
113
|
newestBundle,
|
|
82
114
|
};
|
|
83
115
|
}
|
|
116
|
+
addCodeReference(codeHash, key) {
|
|
117
|
+
const refs = this.codeReferences.get(codeHash) ?? new Set();
|
|
118
|
+
refs.add(key);
|
|
119
|
+
this.codeReferences.set(codeHash, refs);
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Drop expired metadata entries referencing one code hash so the reference
|
|
123
|
+
* check in getBundleCode stays trustworthy without a full-map sweep.
|
|
124
|
+
*/
|
|
125
|
+
pruneExpiredReferencesTo(hash, now = Date.now()) {
|
|
126
|
+
const refs = this.codeReferences.get(hash);
|
|
127
|
+
if (!refs)
|
|
128
|
+
return;
|
|
129
|
+
for (const key of [...refs]) {
|
|
130
|
+
const entry = this.metadata.get(key);
|
|
131
|
+
if (!entry) {
|
|
132
|
+
// Defensive: a reference without metadata is stale bookkeeping.
|
|
133
|
+
this.removeCodeReference(hash, key);
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
if (entry.expiry != null && now >= entry.expiry)
|
|
137
|
+
this.removeMetadata(key);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Full expired-metadata sweep, amortized: runs on writes at most once per
|
|
142
|
+
* BUNDLE_MANIFEST_SWEEP_INTERVAL_MS so unread expired entries cannot pin
|
|
143
|
+
* memory forever while per-render reads stay off the O(all-metadata) path.
|
|
144
|
+
*/
|
|
145
|
+
sweepExpiredMetadataIfDue(now = Date.now()) {
|
|
146
|
+
if (now - this.lastExpiredSweepAt < BUNDLE_MANIFEST_SWEEP_INTERVAL_MS)
|
|
147
|
+
return;
|
|
148
|
+
this.lastExpiredSweepAt = now;
|
|
149
|
+
for (const [key, entry] of this.metadata) {
|
|
150
|
+
if (entry.expiry != null && now >= entry.expiry)
|
|
151
|
+
this.removeMetadata(key);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
removeCodeReference(codeHash, key) {
|
|
155
|
+
const refs = this.codeReferences.get(codeHash);
|
|
156
|
+
if (refs) {
|
|
157
|
+
refs.delete(key);
|
|
158
|
+
if (refs.size > 0)
|
|
159
|
+
return;
|
|
160
|
+
this.codeReferences.delete(codeHash);
|
|
161
|
+
}
|
|
162
|
+
this.code.delete(codeHash);
|
|
163
|
+
}
|
|
164
|
+
removeMetadata(key) {
|
|
165
|
+
const metadata = this.metadata.get(key)?.value;
|
|
166
|
+
if (!metadata)
|
|
167
|
+
return undefined;
|
|
168
|
+
this.metadata.delete(key);
|
|
169
|
+
this.removeSourceReference(key, metadata.source);
|
|
170
|
+
this.removeCodeReference(metadata.codeHash, key);
|
|
171
|
+
return metadata;
|
|
172
|
+
}
|
|
173
|
+
removeSourceReference(key, source) {
|
|
174
|
+
const sourceKeys = this.sourceIndex.get(source);
|
|
175
|
+
if (!sourceKeys)
|
|
176
|
+
return;
|
|
177
|
+
sourceKeys.delete(key);
|
|
178
|
+
if (sourceKeys.size === 0)
|
|
179
|
+
this.sourceIndex.delete(source);
|
|
180
|
+
}
|
|
84
181
|
}
|
|
85
182
|
let manifestStore = new InMemoryBundleManifestStore();
|
|
86
183
|
export function setBundleManifestStore(store) {
|
|
@@ -26,7 +26,7 @@ export declare const MAX_ROUTE_SEGMENTS = 64;
|
|
|
26
26
|
export declare const MAX_PORT_NUMBER = 65535;
|
|
27
27
|
export declare const MIN_PORT_NUMBER = 1;
|
|
28
28
|
export declare const MAX_URL_LENGTH_FOR_VALIDATION = 2048;
|
|
29
|
-
|
|
29
|
+
/** Maximum batch size. */
|
|
30
30
|
export declare const MAX_BATCH_SIZE = 100;
|
|
31
31
|
/** Largest delay supported consistently by JavaScript timer implementations. */
|
|
32
32
|
export declare const MAX_TIMER_DELAY_MS = 2147483647;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"limits.d.ts","sourceRoot":"","sources":["../../../../src/src/utils/constants/limits.ts"],"names":[],"mappings":"AAAA,kCAAkC;AAClC,eAAO,MAAM,yBAAyB,OAAO,CAAC;AAC9C,eAAO,MAAM,4BAA4B,MAAM,CAAC;AAChD,eAAO,MAAM,8BAA8B,MAAM,CAAC;AAClD,eAAO,MAAM,6BAA6B,MAAM,CAAC;AACjD,eAAO,MAAM,qBAAqB,MAAM,CAAC;AAEzC,gCAAgC;AAChC,eAAO,MAAM,oBAAoB,OAAO,CAAC;AACzC,eAAO,MAAM,8BAA8B,QAAQ,CAAC;AACpD,eAAO,MAAM,mBAAmB,MAAM,CAAC;AACvC,eAAO,MAAM,kBAAkB,MAAM,CAAC;AAEtC,eAAO,MAAM,sBAAsB,KAAK,CAAC;AACzC,eAAO,MAAM,mBAAmB,MAAM,CAAC;AAEvC,qCAAqC;AACrC,eAAO,MAAM,uBAAuB,KAAK,CAAC;AAC1C,eAAO,MAAM,wBAAwB,MAAM,CAAC;AAC5C,eAAO,MAAM,uBAAuB,MAAM,CAAC;AAC3C,eAAO,MAAM,wBAAwB,OAAO,CAAC;AAC7C,eAAO,MAAM,2BAA2B,MAAM,CAAC;AAC/C,eAAO,MAAM,yBAAyB,MAAM,CAAC;AAC7C,yEAAyE;AACzE,eAAO,MAAM,wBAAwB,QAAS,CAAC;AAE/C,eAAO,MAAM,qBAAqB,OAAO,CAAC;AAC1C,6EAA6E;AAC7E,eAAO,MAAM,kBAAkB,KAAK,CAAC;AACrC,eAAO,MAAM,eAAe,QAAQ,CAAC;AACrC,eAAO,MAAM,eAAe,IAAI,CAAC;AACjC,eAAO,MAAM,6BAA6B,OAAO,CAAC;AAElD,
|
|
1
|
+
{"version":3,"file":"limits.d.ts","sourceRoot":"","sources":["../../../../src/src/utils/constants/limits.ts"],"names":[],"mappings":"AAAA,kCAAkC;AAClC,eAAO,MAAM,yBAAyB,OAAO,CAAC;AAC9C,eAAO,MAAM,4BAA4B,MAAM,CAAC;AAChD,eAAO,MAAM,8BAA8B,MAAM,CAAC;AAClD,eAAO,MAAM,6BAA6B,MAAM,CAAC;AACjD,eAAO,MAAM,qBAAqB,MAAM,CAAC;AAEzC,gCAAgC;AAChC,eAAO,MAAM,oBAAoB,OAAO,CAAC;AACzC,eAAO,MAAM,8BAA8B,QAAQ,CAAC;AACpD,eAAO,MAAM,mBAAmB,MAAM,CAAC;AACvC,eAAO,MAAM,kBAAkB,MAAM,CAAC;AAEtC,eAAO,MAAM,sBAAsB,KAAK,CAAC;AACzC,eAAO,MAAM,mBAAmB,MAAM,CAAC;AAEvC,qCAAqC;AACrC,eAAO,MAAM,uBAAuB,KAAK,CAAC;AAC1C,eAAO,MAAM,wBAAwB,MAAM,CAAC;AAC5C,eAAO,MAAM,uBAAuB,MAAM,CAAC;AAC3C,eAAO,MAAM,wBAAwB,OAAO,CAAC;AAC7C,eAAO,MAAM,2BAA2B,MAAM,CAAC;AAC/C,eAAO,MAAM,yBAAyB,MAAM,CAAC;AAC7C,yEAAyE;AACzE,eAAO,MAAM,wBAAwB,QAAS,CAAC;AAE/C,eAAO,MAAM,qBAAqB,OAAO,CAAC;AAC1C,6EAA6E;AAC7E,eAAO,MAAM,kBAAkB,KAAK,CAAC;AACrC,eAAO,MAAM,eAAe,QAAQ,CAAC;AACrC,eAAO,MAAM,eAAe,IAAI,CAAC;AACjC,eAAO,MAAM,6BAA6B,OAAO,CAAC;AAElD,0BAA0B;AAC1B,eAAO,MAAM,cAAc,MAAM,CAAC;AAElC,gFAAgF;AAChF,eAAO,MAAM,kBAAkB,aAAgB,CAAC"}
|
|
@@ -26,7 +26,7 @@ export const MAX_ROUTE_SEGMENTS = 64;
|
|
|
26
26
|
export const MAX_PORT_NUMBER = 65535;
|
|
27
27
|
export const MIN_PORT_NUMBER = 1;
|
|
28
28
|
export const MAX_URL_LENGTH_FOR_VALIDATION = 2048;
|
|
29
|
-
|
|
29
|
+
/** Maximum batch size. */
|
|
30
30
|
export const MAX_BATCH_SIZE = 100;
|
|
31
31
|
/** Largest delay supported consistently by JavaScript timer implementations. */
|
|
32
32
|
export const MAX_TIMER_DELAY_MS = 2_147_483_647;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hash-utils.d.ts","sourceRoot":"","sources":["../../../src/src/utils/hash-utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,OAAO,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"hash-utils.d.ts","sourceRoot":"","sources":["../../../src/src/utils/hash-utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,OAAO,MAAM,qBAAqB,CAAC;AA0C/C,kEAAkE;AAClE,wBAAsB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAalE;AAED,6DAA6D;AAC7D,wBAAsB,gBAAgB,CAAC,KAAK,EAAE,OAAO,CAAC,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,CAQnF;AACD,uDAAuD;AACvD,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,yBAAyB;AACzB,wBAAgB,eAAe,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAEjE;AAED,0BAA0B;AAC1B,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAS9C;AAED,+DAA+D;AAC/D,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAE/C;AAED,yBAAyB;AACzB,wBAAsB,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAGhE;AAED,mDAAmD;AACnD,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAS/C"}
|
|
@@ -3,17 +3,43 @@ import { FNV1A_PRIME_32 } from "./constants/crypto.js";
|
|
|
3
3
|
import { HASH_SEED_FNV1A } from "./constants/hash.js";
|
|
4
4
|
/** Number of hex characters kept by shortHash (8 hex chars = 32 bits) */
|
|
5
5
|
const SHORT_HASH_LENGTH = 8;
|
|
6
|
+
// Hashes participate in cache and request identities after project modules may
|
|
7
|
+
// have executed in the shared realm. Capture the small set of primordials used
|
|
8
|
+
// by that boundary before project code can replace their implementations.
|
|
9
|
+
const IntrinsicTextEncoder = TextEncoder;
|
|
10
|
+
const IntrinsicUint8Array = Uint8Array;
|
|
11
|
+
const NumberPrototypeToString = Number.prototype.toString;
|
|
12
|
+
const ObjectGetOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
|
|
13
|
+
const ObjectGetPrototypeOf = Object.getPrototypeOf;
|
|
14
|
+
const ReflectApply = Reflect.apply;
|
|
15
|
+
const StringPrototypePadStart = String.prototype.padStart;
|
|
16
|
+
const SubtleCryptoDigest = dntShim.crypto.subtle.digest;
|
|
17
|
+
const TextEncoderPrototypeEncode = TextEncoder.prototype.encode;
|
|
18
|
+
const cryptoSubtle = dntShim.crypto.subtle;
|
|
19
|
+
const hashTextEncoder = new IntrinsicTextEncoder();
|
|
20
|
+
const TypedArrayPrototype = ObjectGetPrototypeOf(IntrinsicUint8Array.prototype);
|
|
21
|
+
const TypedArrayLengthGetter = ObjectGetOwnPropertyDescriptor(TypedArrayPrototype, "length").get;
|
|
22
|
+
function typedArrayLength(value) {
|
|
23
|
+
return ReflectApply(TypedArrayLengthGetter, value, []);
|
|
24
|
+
}
|
|
6
25
|
function toHex(buffer) {
|
|
7
|
-
|
|
26
|
+
const bytes = new IntrinsicUint8Array(buffer);
|
|
27
|
+
let result = "";
|
|
28
|
+
const length = typedArrayLength(bytes);
|
|
29
|
+
for (let index = 0; index < length; index++) {
|
|
30
|
+
const hex = ReflectApply(NumberPrototypeToString, bytes[index], [16]);
|
|
31
|
+
result += ReflectApply(StringPrototypePadStart, hex, [2, "0"]);
|
|
32
|
+
}
|
|
33
|
+
return result;
|
|
8
34
|
}
|
|
9
35
|
/** Compute the lowercase hex SHA-256 digest of a UTF-8 string. */
|
|
10
36
|
export async function computeHash(content) {
|
|
11
|
-
const data =
|
|
12
|
-
return toHex(await
|
|
37
|
+
const data = ReflectApply(TextEncoderPrototypeEncode, hashTextEncoder, [content]);
|
|
38
|
+
return toHex(await ReflectApply(SubtleCryptoDigest, cryptoSubtle, ["SHA-256", data]));
|
|
13
39
|
}
|
|
14
40
|
/** Compute the lowercase hex SHA-256 digest of raw bytes. */
|
|
15
41
|
export async function computeHashBytes(bytes) {
|
|
16
|
-
return toHex(await
|
|
42
|
+
return toHex(await ReflectApply(SubtleCryptoDigest, cryptoSubtle, ["SHA-256", bytes]));
|
|
17
43
|
}
|
|
18
44
|
/** Compute code hash. */
|
|
19
45
|
export function computeCodeHash(code) {
|
|
@@ -40,8 +66,8 @@ export async function shortHash(content) {
|
|
|
40
66
|
/** FNV-1a hash for strings - returns hex string */
|
|
41
67
|
export function fnv1aHash(input) {
|
|
42
68
|
let hash = HASH_SEED_FNV1A >>> 0;
|
|
43
|
-
for (
|
|
44
|
-
hash ^=
|
|
69
|
+
for (let index = 0; index < input.length; index++) {
|
|
70
|
+
hash ^= input.charCodeAt(index);
|
|
45
71
|
hash = Math.imul(hash, FNV1A_PRIME_32);
|
|
46
72
|
}
|
|
47
73
|
return (hash >>> 0).toString(16);
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
interface CryptoUuidSource {
|
|
2
|
+
randomUUID?: () => string;
|
|
3
|
+
getRandomValues?: (bytes: Uint8Array) => Uint8Array;
|
|
4
|
+
}
|
|
5
|
+
/** Generate a secure RFC 4122 version 4 UUID across browser Crypto implementations. */
|
|
6
|
+
export declare function generateUuid(cryptoImpl?: CryptoUuidSource | null | undefined): string;
|
|
7
|
+
/** Generate a unique ID with optional prefix (e.g., "msg-a1B2c3D4e5F6g7H8") */
|
|
8
|
+
export declare function generateId(prefix?: string): string;
|
|
9
|
+
/** Create an ID generator with fixed prefix and optional configuration */
|
|
10
|
+
export declare function createIdGenerator(options: {
|
|
11
|
+
prefix?: string;
|
|
12
|
+
separator?: string;
|
|
13
|
+
size?: number;
|
|
14
|
+
}): () => string;
|
|
15
|
+
export {};
|
|
16
|
+
//# sourceMappingURL=id.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"id.d.ts","sourceRoot":"","sources":["../../../src/src/utils/id.ts"],"names":[],"mappings":"AAyCA,UAAU,gBAAgB;IACxB,UAAU,CAAC,EAAE,MAAM,MAAM,CAAC;IAC1B,eAAe,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,UAAU,CAAC;CACrD;AAED,uFAAuF;AACvF,wBAAgB,YAAY,CAC1B,UAAU,GAAE,gBAAgB,GAAG,IAAI,GAAG,SAEzB,GACZ,MAAM,CAmBR;AAED,+EAA+E;AAC/E,wBAAgB,UAAU,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAGlD;AAED,0EAA0E;AAC1E,wBAAgB,iBAAiB,CAAC,OAAO,EAAE;IACzC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,GAAG,MAAM,MAAM,CAQf"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/** ID generation utilities (16-char alphanumeric with optional prefix) */
|
|
2
|
+
import * as dntShim from "../../_dnt.shims.js";
|
|
3
|
+
const ALPHABET = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
|
4
|
+
const MAX_UNBIASED_BYTE = Math.floor(256 / ALPHABET.length) * ALPHABET.length;
|
|
5
|
+
const MAX_ID_SIZE = 1_024;
|
|
6
|
+
function requireIdSize(size) {
|
|
7
|
+
if (!Number.isSafeInteger(size) || size <= 0 || size > MAX_ID_SIZE) {
|
|
8
|
+
throw new RangeError(`ID size must be an integer between 1 and ${MAX_ID_SIZE}`);
|
|
9
|
+
}
|
|
10
|
+
return size;
|
|
11
|
+
}
|
|
12
|
+
function randomString(length) {
|
|
13
|
+
requireIdSize(length);
|
|
14
|
+
let result = "";
|
|
15
|
+
while (result.length < length) {
|
|
16
|
+
const remaining = length - result.length;
|
|
17
|
+
const batchSize = Math.min(65_536, Math.max(32, Math.ceil((remaining * 256) / MAX_UNBIASED_BYTE)));
|
|
18
|
+
const bytes = new Uint8Array(batchSize);
|
|
19
|
+
dntShim.crypto.getRandomValues(bytes);
|
|
20
|
+
// Iterate the allocated batch directly. A project can replace the ambient
|
|
21
|
+
// typed-array iterator after framework modules load; ID generation must not
|
|
22
|
+
// execute or depend on that mutable hook.
|
|
23
|
+
for (let index = 0; index < batchSize; index++) {
|
|
24
|
+
const byte = bytes[index] ?? 0;
|
|
25
|
+
if (byte >= MAX_UNBIASED_BYTE)
|
|
26
|
+
continue;
|
|
27
|
+
result += ALPHABET[byte % ALPHABET.length];
|
|
28
|
+
if (result.length === length)
|
|
29
|
+
break;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return result;
|
|
33
|
+
}
|
|
34
|
+
/** Generate a secure RFC 4122 version 4 UUID across browser Crypto implementations. */
|
|
35
|
+
export function generateUuid(cryptoImpl = typeof dntShim.crypto !== "undefined"
|
|
36
|
+
? dntShim.crypto
|
|
37
|
+
: undefined) {
|
|
38
|
+
if (typeof cryptoImpl?.randomUUID === "function") {
|
|
39
|
+
return cryptoImpl.randomUUID();
|
|
40
|
+
}
|
|
41
|
+
if (typeof cryptoImpl?.getRandomValues !== "function") {
|
|
42
|
+
throw new Error("Web Crypto with getRandomValues is required to generate a secure UUID.");
|
|
43
|
+
}
|
|
44
|
+
const bytes = cryptoImpl.getRandomValues(new Uint8Array(16));
|
|
45
|
+
bytes[6] = ((bytes[6] ?? 0) & 0x0f) | 0x40;
|
|
46
|
+
bytes[8] = ((bytes[8] ?? 0) & 0x3f) | 0x80;
|
|
47
|
+
const hex = Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
48
|
+
return [
|
|
49
|
+
hex.slice(0, 8),
|
|
50
|
+
hex.slice(8, 12),
|
|
51
|
+
hex.slice(12, 16),
|
|
52
|
+
hex.slice(16, 20),
|
|
53
|
+
hex.slice(20),
|
|
54
|
+
].join("-");
|
|
55
|
+
}
|
|
56
|
+
/** Generate a unique ID with optional prefix (e.g., "msg-a1B2c3D4e5F6g7H8") */
|
|
57
|
+
export function generateId(prefix) {
|
|
58
|
+
const id = randomString(16);
|
|
59
|
+
return prefix ? `${prefix}-${id}` : id;
|
|
60
|
+
}
|
|
61
|
+
/** Create an ID generator with fixed prefix and optional configuration */
|
|
62
|
+
export function createIdGenerator(options) {
|
|
63
|
+
const { prefix, separator = "-", size = 16 } = options;
|
|
64
|
+
requireIdSize(size);
|
|
65
|
+
return function generate() {
|
|
66
|
+
const id = randomString(size);
|
|
67
|
+
return prefix ? `${prefix}${separator}${id}` : id;
|
|
68
|
+
};
|
|
69
|
+
}
|
|
@@ -12,7 +12,15 @@ export declare function createEmptyLockfile(): LockfileData;
|
|
|
12
12
|
/** Compute integrity. */
|
|
13
13
|
export declare function computeIntegrity(content: string): Promise<string>;
|
|
14
14
|
export declare function verifyIntegrity(content: string, integrity: string): Promise<boolean>;
|
|
15
|
-
/**
|
|
15
|
+
/**
|
|
16
|
+
* Public API contract for lockfile manager.
|
|
17
|
+
*
|
|
18
|
+
* Reads and updates fail closed with `lockfile-format-mismatch` for an
|
|
19
|
+
* unsupported format and `lockfile-read-error` for unreadable or malformed
|
|
20
|
+
* data, so an older Veryfront build cannot overwrite unrecognized lockfile
|
|
21
|
+
* data. `clear()` is the explicit destructive recovery operation and does not
|
|
22
|
+
* parse the file before removing it.
|
|
23
|
+
*/
|
|
16
24
|
export interface LockfileManager {
|
|
17
25
|
read(): Promise<LockfileData | null>;
|
|
18
26
|
write(data: LockfileData): Promise<void>;
|
|
@@ -23,13 +31,56 @@ export interface LockfileManager {
|
|
|
23
31
|
flush(): Promise<void>;
|
|
24
32
|
}
|
|
25
33
|
export type FSAdapter = {
|
|
34
|
+
/**
|
|
35
|
+
* Authoritative queue identity for adapters that access the same backing
|
|
36
|
+
* filesystem. Separate adapter objects must share this key to coordinate all
|
|
37
|
+
* lockfile access, regardless of their optional canonicalization support.
|
|
38
|
+
*/
|
|
39
|
+
readonly coordinationKey?: string;
|
|
26
40
|
readFile(path: string): Promise<string>;
|
|
27
41
|
writeFile(path: string, content: string): Promise<void>;
|
|
42
|
+
/**
|
|
43
|
+
* Atomically replace `to` with the file at `from` (same-directory rename).
|
|
44
|
+
* When present, lockfile writes stage a temp file and rename it into place,
|
|
45
|
+
* so a crash mid-write can never leave a truncated lockfile behind.
|
|
46
|
+
*/
|
|
47
|
+
rename?(from: string, to: string): Promise<void>;
|
|
28
48
|
exists(path: string): Promise<boolean>;
|
|
29
49
|
remove?(path: string): Promise<void>;
|
|
50
|
+
/**
|
|
51
|
+
* Resolve an existing project path to its stable backing identity.
|
|
52
|
+
* Coordination through realPath is per-adapter-instance (or per shared
|
|
53
|
+
* coordinationKey domain): two separate adapter instances without a shared
|
|
54
|
+
* coordinationKey never serialize with each other through realPath alone,
|
|
55
|
+
* even when both implementations agree on the canonical path.
|
|
56
|
+
*/
|
|
57
|
+
realPath?(path: string): Promise<string>;
|
|
30
58
|
};
|
|
31
59
|
/** Create lockfile manager. */
|
|
32
60
|
export declare function createLockfileManager(projectDir: string, fsAdapter?: FSAdapter): LockfileManager;
|
|
61
|
+
/**
|
|
62
|
+
* Read a lockfile entry on a build or dev-server hot path.
|
|
63
|
+
*
|
|
64
|
+
* A lockfile written by a newer Veryfront build (`lockfile-format-mismatch`)
|
|
65
|
+
* keeps failing loudly: proceeding around data the running build cannot
|
|
66
|
+
* understand is exactly the overwrite hazard this module guards against. An
|
|
67
|
+
* unreadable or malformed lockfile (`lockfile-read-error`) instead degrades to
|
|
68
|
+
* a cache miss so builds keep working from fresh fetches; a warning naming
|
|
69
|
+
* the lockfile and the `veryfront lock --clear` remedy is logged once per
|
|
70
|
+
* manager instead of once per import.
|
|
71
|
+
*/
|
|
72
|
+
export declare function getLockfileEntryForBuild(lockfile: LockfileManager, url: string): Promise<LockfileEntry | null>;
|
|
73
|
+
/**
|
|
74
|
+
* Persist a lockfile entry from a build or dev-server hot path.
|
|
75
|
+
*
|
|
76
|
+
* Mirrors {@link getLockfileEntryForBuild}: when the on-disk lockfile is
|
|
77
|
+
* unreadable or malformed (`lockfile-read-error`), the file is left untouched
|
|
78
|
+
* for `veryfront lock --clear` and the entry is simply not recorded, so a
|
|
79
|
+
* successful refetch still serves the build. Returns whether the entry was
|
|
80
|
+
* staged; callers should only `flush()` after a `true` result. A newer-format
|
|
81
|
+
* lockfile (`lockfile-format-mismatch`) keeps failing loudly.
|
|
82
|
+
*/
|
|
83
|
+
export declare function setLockfileEntryForBuild(lockfile: LockfileManager, url: string, entry: LockfileEntry): Promise<boolean>;
|
|
33
84
|
export interface FetchWithLockOptions {
|
|
34
85
|
lockfile: LockfileManager;
|
|
35
86
|
url: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"import-lockfile.d.ts","sourceRoot":"","sources":["../../../src/src/utils/import-lockfile.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"import-lockfile.d.ts","sourceRoot":"","sources":["../../../src/src/utils/import-lockfile.ts"],"names":[],"mappings":"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"}
|