@jsii/kernel 1.86.1 → 1.87.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/lib/disk-cache/disk-cache.d.ts +21 -1
- package/lib/disk-cache/disk-cache.js +105 -12
- package/lib/link.d.ts +10 -5
- package/lib/link.js +37 -18
- package/lib/tar-cache/index.js +12 -23
- package/package.json +5 -5
|
@@ -13,12 +13,32 @@ export declare class Entry {
|
|
|
13
13
|
constructor(path: string);
|
|
14
14
|
get atime(): Date;
|
|
15
15
|
get pathExists(): boolean;
|
|
16
|
+
/**
|
|
17
|
+
* Whether the directory has been completely written
|
|
18
|
+
*
|
|
19
|
+
* The presence of the marker file is a signal that we can skip trying to lock the directory.
|
|
20
|
+
*/
|
|
21
|
+
get isComplete(): boolean;
|
|
22
|
+
/**
|
|
23
|
+
* Retrieve an entry from the cache
|
|
24
|
+
*
|
|
25
|
+
* If the entry doesn't exist yet, use 'cb' to produce the file contents.
|
|
26
|
+
*/
|
|
27
|
+
retrieve(cb: (path: string) => void): {
|
|
28
|
+
path: string;
|
|
29
|
+
cache: 'hit' | 'miss';
|
|
30
|
+
};
|
|
16
31
|
lock<T>(cb: (entry: LockedEntry) => T): T;
|
|
17
32
|
read(file: string): Buffer | undefined;
|
|
18
33
|
}
|
|
19
34
|
export interface LockedEntry {
|
|
20
35
|
delete(): void;
|
|
21
36
|
write(name: string, data: Buffer): void;
|
|
22
|
-
|
|
37
|
+
/**
|
|
38
|
+
* Mark the entry has having been completed
|
|
39
|
+
*
|
|
40
|
+
* The modification time of this file is used for cleanup.
|
|
41
|
+
*/
|
|
42
|
+
markComplete(): void;
|
|
23
43
|
}
|
|
24
44
|
//# sourceMappingURL=disk-cache.d.ts.map
|
|
@@ -10,7 +10,7 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
|
|
|
10
10
|
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
11
11
|
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
12
12
|
};
|
|
13
|
-
var _DiskCache_instances, _a, _DiskCache_CACHE, _DiskCache_root, _DiskCache_entries, _Entry_instances, _Entry_lockFile_get, _Entry_markerFile_get;
|
|
13
|
+
var _DiskCache_instances, _a, _DiskCache_CACHE, _DiskCache_root, _DiskCache_entries, _Entry_instances, _Entry_lockFile_get, _Entry_markerFile_get, _Entry_touchMarkerFile;
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
15
|
exports.Entry = exports.DiskCache = void 0;
|
|
16
16
|
const fs_1 = require("fs");
|
|
@@ -128,9 +128,56 @@ class Entry {
|
|
|
128
128
|
get pathExists() {
|
|
129
129
|
return (0, fs_1.existsSync)(this.path);
|
|
130
130
|
}
|
|
131
|
+
/**
|
|
132
|
+
* Whether the directory has been completely written
|
|
133
|
+
*
|
|
134
|
+
* The presence of the marker file is a signal that we can skip trying to lock the directory.
|
|
135
|
+
*/
|
|
136
|
+
get isComplete() {
|
|
137
|
+
return (0, fs_1.existsSync)(__classPrivateFieldGet(this, _Entry_instances, "a", _Entry_markerFile_get));
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Retrieve an entry from the cache
|
|
141
|
+
*
|
|
142
|
+
* If the entry doesn't exist yet, use 'cb' to produce the file contents.
|
|
143
|
+
*/
|
|
144
|
+
retrieve(cb) {
|
|
145
|
+
// If the marker file already exists, update its timestamp and immediately return.
|
|
146
|
+
// We don't even try to lock.
|
|
147
|
+
if (this.isComplete) {
|
|
148
|
+
__classPrivateFieldGet(this, _Entry_instances, "m", _Entry_touchMarkerFile).call(this);
|
|
149
|
+
return { path: this.path, cache: 'hit' };
|
|
150
|
+
}
|
|
151
|
+
let cache = 'miss';
|
|
152
|
+
this.lock((lock) => {
|
|
153
|
+
// While we all fought to acquire the lock, someone else might have completed already.
|
|
154
|
+
if (this.isComplete) {
|
|
155
|
+
cache = 'hit';
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
// !!!IMPORTANT!!!
|
|
159
|
+
// Extract directly into the final target directory, as certain antivirus
|
|
160
|
+
// software configurations on Windows will make a `renameSync` operation
|
|
161
|
+
// fail with EPERM until the files have been fully analyzed.
|
|
162
|
+
(0, fs_1.mkdirSync)(this.path, { recursive: true });
|
|
163
|
+
try {
|
|
164
|
+
cb(this.path);
|
|
165
|
+
}
|
|
166
|
+
catch (error) {
|
|
167
|
+
(0, fs_1.rmSync)(this.path, { force: true, recursive: true });
|
|
168
|
+
throw error;
|
|
169
|
+
}
|
|
170
|
+
lock.markComplete();
|
|
171
|
+
});
|
|
172
|
+
return { path: this.path, cache };
|
|
173
|
+
}
|
|
131
174
|
lock(cb) {
|
|
132
175
|
(0, fs_1.mkdirSync)((0, path_1.dirname)(this.path), { recursive: true });
|
|
133
|
-
(
|
|
176
|
+
lockSyncWithWait(__classPrivateFieldGet(this, _Entry_instances, "a", _Entry_lockFile_get), {
|
|
177
|
+
retries: 12,
|
|
178
|
+
// Extracting the largest tarball takes ~5s
|
|
179
|
+
stale: 10000,
|
|
180
|
+
});
|
|
134
181
|
let disposed = false;
|
|
135
182
|
try {
|
|
136
183
|
return cb({
|
|
@@ -147,19 +194,11 @@ class Entry {
|
|
|
147
194
|
(0, fs_1.mkdirSync)((0, path_1.dirname)((0, path_1.join)(this.path, name)), { recursive: true });
|
|
148
195
|
(0, fs_1.writeFileSync)((0, path_1.join)(this.path, name), content);
|
|
149
196
|
},
|
|
150
|
-
|
|
197
|
+
markComplete: () => {
|
|
151
198
|
if (disposed) {
|
|
152
199
|
throw new Error(`Cannot touch ${this.path} once the lock block was returned!`);
|
|
153
200
|
}
|
|
154
|
-
|
|
155
|
-
if ((0, fs_1.existsSync)(__classPrivateFieldGet(this, _Entry_instances, "a", _Entry_markerFile_get))) {
|
|
156
|
-
const now = new Date();
|
|
157
|
-
(0, fs_1.utimesSync)(__classPrivateFieldGet(this, _Entry_instances, "a", _Entry_markerFile_get), now, now);
|
|
158
|
-
}
|
|
159
|
-
else {
|
|
160
|
-
(0, fs_1.writeFileSync)(__classPrivateFieldGet(this, _Entry_instances, "a", _Entry_markerFile_get), '');
|
|
161
|
-
}
|
|
162
|
-
}
|
|
201
|
+
__classPrivateFieldGet(this, _Entry_instances, "m", _Entry_touchMarkerFile).call(this);
|
|
163
202
|
},
|
|
164
203
|
});
|
|
165
204
|
}
|
|
@@ -185,6 +224,19 @@ _Entry_instances = new WeakSet(), _Entry_lockFile_get = function _Entry_lockFile
|
|
|
185
224
|
return `${this.path}.lock`;
|
|
186
225
|
}, _Entry_markerFile_get = function _Entry_markerFile_get() {
|
|
187
226
|
return (0, path_1.join)(this.path, MARKER_FILE_NAME);
|
|
227
|
+
}, _Entry_touchMarkerFile = function _Entry_touchMarkerFile() {
|
|
228
|
+
if (this.pathExists) {
|
|
229
|
+
try {
|
|
230
|
+
const now = new Date();
|
|
231
|
+
(0, fs_1.utimesSync)(__classPrivateFieldGet(this, _Entry_instances, "a", _Entry_markerFile_get), now, now);
|
|
232
|
+
}
|
|
233
|
+
catch (e) {
|
|
234
|
+
if (e.code !== 'ENOENT') {
|
|
235
|
+
throw e;
|
|
236
|
+
}
|
|
237
|
+
(0, fs_1.writeFileSync)(__classPrivateFieldGet(this, _Entry_instances, "a", _Entry_markerFile_get), '');
|
|
238
|
+
}
|
|
239
|
+
}
|
|
188
240
|
};
|
|
189
241
|
function* directoriesUnder(root, recursive = false, ignoreErrors = true) {
|
|
190
242
|
for (const file of (0, fs_1.readdirSync)(root)) {
|
|
@@ -205,4 +257,45 @@ function* directoriesUnder(root, recursive = false, ignoreErrors = true) {
|
|
|
205
257
|
}
|
|
206
258
|
}
|
|
207
259
|
}
|
|
260
|
+
/**
|
|
261
|
+
* We must use 'lockSync', but that doesn't support waiting (because waiting is only supported for async APIs)
|
|
262
|
+
* so we have to build our own looping locker with waits
|
|
263
|
+
*/
|
|
264
|
+
function lockSyncWithWait(path, options) {
|
|
265
|
+
var _b;
|
|
266
|
+
let retries = (_b = options.retries) !== null && _b !== void 0 ? _b : 0;
|
|
267
|
+
let sleep = 100;
|
|
268
|
+
// eslint-disable-next-line no-constant-condition
|
|
269
|
+
while (true) {
|
|
270
|
+
try {
|
|
271
|
+
(0, lockfile_1.lockSync)(path, {
|
|
272
|
+
retries: 0,
|
|
273
|
+
stale: options.stale,
|
|
274
|
+
});
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
catch (e) {
|
|
278
|
+
if (retries === 0) {
|
|
279
|
+
throw e;
|
|
280
|
+
}
|
|
281
|
+
retries--;
|
|
282
|
+
if (e.code === 'EEXIST') {
|
|
283
|
+
// Most common case, needs longest sleep. Randomize the herd.
|
|
284
|
+
sleepSync(Math.floor(Math.random() * sleep));
|
|
285
|
+
sleep *= 1.5;
|
|
286
|
+
}
|
|
287
|
+
else {
|
|
288
|
+
sleepSync(5);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* Abuse Atomics.wait() to come up with a sync sleep
|
|
295
|
+
*
|
|
296
|
+
* We must use a sync sleep because all of jsii is sync.
|
|
297
|
+
*/
|
|
298
|
+
function sleepSync(ms) {
|
|
299
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
300
|
+
}
|
|
208
301
|
//# sourceMappingURL=disk-cache.js.map
|
package/lib/link.d.ts
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* copy otherwise.
|
|
2
|
+
* Link existing to destination directory
|
|
4
3
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* - If Node has been started with a module resolution strategy that does not
|
|
5
|
+
* resolve symlinks (so peerDependencies can be found), use symlinking.
|
|
6
|
+
* Symlinking may fail on Windows for non-Admin users.
|
|
7
|
+
* - If not symlinking the entire directory, crawl the directory tree and
|
|
8
|
+
* hardlink all files (if possible), copying them if not.
|
|
9
|
+
*
|
|
10
|
+
* @param existingRoot is the original file or directory to link.
|
|
11
|
+
* @param destinationRoot is the new file or directory to create.
|
|
7
12
|
*/
|
|
8
|
-
export declare function link(
|
|
13
|
+
export declare function link(existingRoot: string, destinationRoot: string): void;
|
|
9
14
|
//# sourceMappingURL=link.d.ts.map
|
package/lib/link.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.link = void 0;
|
|
4
4
|
const fs_1 = require("fs");
|
|
5
|
+
const os = require("os");
|
|
5
6
|
const path_1 = require("path");
|
|
6
7
|
/**
|
|
7
8
|
* If `node` is started with `--preserve-symlinks`, the module loaded will
|
|
@@ -10,31 +11,49 @@ const path_1 = require("path");
|
|
|
10
11
|
*/
|
|
11
12
|
const PRESERVE_SYMLINKS = process.execArgv.includes('--preserve-symlinks');
|
|
12
13
|
/**
|
|
13
|
-
*
|
|
14
|
-
* copy otherwise.
|
|
14
|
+
* Link existing to destination directory
|
|
15
15
|
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
16
|
+
* - If Node has been started with a module resolution strategy that does not
|
|
17
|
+
* resolve symlinks (so peerDependencies can be found), use symlinking.
|
|
18
|
+
* Symlinking may fail on Windows for non-Admin users.
|
|
19
|
+
* - If not symlinking the entire directory, crawl the directory tree and
|
|
20
|
+
* hardlink all files (if possible), copying them if not.
|
|
21
|
+
*
|
|
22
|
+
* @param existingRoot is the original file or directory to link.
|
|
23
|
+
* @param destinationRoot is the new file or directory to create.
|
|
18
24
|
*/
|
|
19
|
-
function link(
|
|
25
|
+
function link(existingRoot, destinationRoot) {
|
|
26
|
+
(0, fs_1.mkdirSync)((0, path_1.dirname)(destinationRoot), { recursive: true });
|
|
20
27
|
if (PRESERVE_SYMLINKS) {
|
|
21
|
-
(0, fs_1.mkdirSync)((0, path_1.dirname)(destination), { recursive: true });
|
|
22
|
-
(0, fs_1.symlinkSync)(existing, destination);
|
|
23
|
-
return;
|
|
24
|
-
}
|
|
25
|
-
const stat = (0, fs_1.statSync)(existing);
|
|
26
|
-
if (!stat.isDirectory()) {
|
|
27
28
|
try {
|
|
28
|
-
(0, fs_1.
|
|
29
|
+
(0, fs_1.symlinkSync)(existingRoot, destinationRoot);
|
|
30
|
+
return;
|
|
29
31
|
}
|
|
30
|
-
catch {
|
|
31
|
-
|
|
32
|
+
catch (e) {
|
|
33
|
+
// On Windows, non-Admin users aren't allowed to create symlinks. In that case, fall back to the copying workflow.
|
|
34
|
+
const winNoSymlink = e.code === 'EPERM' && os.platform() === 'win32';
|
|
35
|
+
if (!winNoSymlink) {
|
|
36
|
+
throw e;
|
|
37
|
+
}
|
|
32
38
|
}
|
|
33
|
-
return;
|
|
34
39
|
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
40
|
+
// Fall back to the slow method
|
|
41
|
+
recurse(existingRoot, destinationRoot);
|
|
42
|
+
function recurse(existing, destination) {
|
|
43
|
+
const stat = (0, fs_1.statSync)(existing);
|
|
44
|
+
if (!stat.isDirectory()) {
|
|
45
|
+
try {
|
|
46
|
+
(0, fs_1.linkSync)(existing, destination);
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
(0, fs_1.copyFileSync)(existing, destination);
|
|
50
|
+
}
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
(0, fs_1.mkdirSync)(destination, { recursive: true });
|
|
54
|
+
for (const file of (0, fs_1.readdirSync)(existing)) {
|
|
55
|
+
recurse((0, path_1.join)(existing, file), (0, path_1.join)(destination, file));
|
|
56
|
+
}
|
|
38
57
|
}
|
|
39
58
|
}
|
|
40
59
|
exports.link = link;
|
package/lib/tar-cache/index.js
CHANGED
|
@@ -28,38 +28,27 @@ function extract(file, outDir, options, ...comments) {
|
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
30
|
exports.extract = extract;
|
|
31
|
+
/**
|
|
32
|
+
* Extract the tarball into a cached directory, symlink that directory into the target location
|
|
33
|
+
*/
|
|
31
34
|
function extractViaCache(file, outDir, options = {}, ...comments) {
|
|
32
35
|
var _a;
|
|
33
36
|
const cacheRoot = (_a = process.env.JSII_RUNTIME_PACKAGE_CACHE_ROOT) !== null && _a !== void 0 ? _a : (0, default_cache_root_1.defaultCacheRoot)();
|
|
34
37
|
const dirCache = disk_cache_1.DiskCache.inDirectory(cacheRoot);
|
|
35
38
|
const entry = dirCache.entryFor(file, ...comments);
|
|
36
|
-
const { path, cache } = entry.
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
// fail with EPERM until the files have been fully analyzed.
|
|
43
|
-
(0, fs_1.mkdirSync)(entry.path, { recursive: true });
|
|
44
|
-
try {
|
|
45
|
-
untarInto({
|
|
46
|
-
...options,
|
|
47
|
-
cwd: entry.path,
|
|
48
|
-
file,
|
|
49
|
-
});
|
|
50
|
-
}
|
|
51
|
-
catch (error) {
|
|
52
|
-
(0, fs_1.rmSync)(entry.path, { force: true, recursive: true });
|
|
53
|
-
throw error;
|
|
54
|
-
}
|
|
55
|
-
cache = 'miss';
|
|
56
|
-
}
|
|
57
|
-
lock.touch();
|
|
58
|
-
return { path: entry.path, cache };
|
|
39
|
+
const { path, cache } = entry.retrieve((path) => {
|
|
40
|
+
untarInto({
|
|
41
|
+
...options,
|
|
42
|
+
cwd: path,
|
|
43
|
+
file,
|
|
44
|
+
});
|
|
59
45
|
});
|
|
60
46
|
(0, link_1.link)(path, outDir);
|
|
61
47
|
return { cache };
|
|
62
48
|
}
|
|
49
|
+
/**
|
|
50
|
+
* Extract directory into the target location
|
|
51
|
+
*/
|
|
63
52
|
function extractToOutDir(file, cwd, options = {}) {
|
|
64
53
|
// The output directory must already exist...
|
|
65
54
|
(0, fs_1.mkdirSync)(cwd, { recursive: true });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jsii/kernel",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.87.0",
|
|
4
4
|
"description": "kernel for jsii execution environment",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": {
|
|
@@ -31,19 +31,19 @@
|
|
|
31
31
|
"package": "package-js"
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@jsii/spec": "^1.
|
|
34
|
+
"@jsii/spec": "^1.87.0",
|
|
35
35
|
"fs-extra": "^10.1.0",
|
|
36
36
|
"lockfile": "^1.0.4",
|
|
37
37
|
"tar": "^6.1.15"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
|
-
"@scope/jsii-calc-base": "^1.
|
|
41
|
-
"@scope/jsii-calc-lib": "^1.
|
|
40
|
+
"@scope/jsii-calc-base": "^1.87.0",
|
|
41
|
+
"@scope/jsii-calc-lib": "^1.87.0",
|
|
42
42
|
"@types/fs-extra": "^9.0.13",
|
|
43
43
|
"@types/lockfile": "^1.0.2",
|
|
44
44
|
"@types/tar": "^6.1.5",
|
|
45
45
|
"jest-expect-message": "^1.1.3",
|
|
46
|
-
"jsii-build-tools": "^1.
|
|
46
|
+
"jsii-build-tools": "^1.87.0",
|
|
47
47
|
"jsii-calc": "^3.20.120"
|
|
48
48
|
}
|
|
49
49
|
}
|