@dxos/lock-file 0.1.57 → 0.1.58-main.0e9c99e
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/lib/node/index.cjs
CHANGED
|
@@ -26,15 +26,15 @@ module.exports = __toCommonJS(src_exports);
|
|
|
26
26
|
|
|
27
27
|
// packages/common/lock-file/src/lock-file.ts
|
|
28
28
|
var import_fs_ext = require("fs-ext");
|
|
29
|
-
var import_node_fs = require("node:fs");
|
|
30
29
|
var import_promises = require("node:fs/promises");
|
|
31
30
|
var LockFile = class _LockFile {
|
|
32
31
|
static async acquire(filename) {
|
|
33
|
-
const handle = await (0, import_promises.open)(filename,
|
|
32
|
+
const handle = await (0, import_promises.open)(filename, import_promises.constants.O_CREAT);
|
|
34
33
|
await new Promise((resolve, reject) => {
|
|
35
|
-
(0, import_fs_ext.flock)(handle.fd, "exnb", (err) => {
|
|
34
|
+
(0, import_fs_ext.flock)(handle.fd, "exnb", async (err) => {
|
|
36
35
|
if (err) {
|
|
37
36
|
reject(err);
|
|
37
|
+
await handle.close();
|
|
38
38
|
return;
|
|
39
39
|
}
|
|
40
40
|
resolve();
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/index.ts", "../../../src/lock-file.ts"],
|
|
4
|
-
"sourcesContent": ["//\n// Copyright 2023 DXOS.org\n//\n\nexport * from './lock-file';\n", "//\n// Copyright 2023 DXOS.org\n//\n\nimport { flock } from 'fs-ext';\nimport {
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;;;;;;;ACIA,oBAAsB;AACtB,
|
|
6
|
-
"names": ["LockFile", "acquire", "filename", "handle", "open", "constants", "O_CREAT", "Promise", "resolve", "reject", "flock", "fd", "err", "
|
|
4
|
+
"sourcesContent": ["//\n// Copyright 2023 DXOS.org\n//\n\nexport * from './lock-file';\n", "//\n// Copyright 2023 DXOS.org\n//\n\nimport { flock } from 'fs-ext';\nimport { open, FileHandle, constants } from 'node:fs/promises';\n\nexport class LockFile {\n static async acquire(filename: string): Promise<FileHandle> {\n const handle = await open(filename, constants.O_CREAT);\n await new Promise<void>((resolve, reject) => {\n flock(handle.fd, 'exnb', async (err) => {\n if (err) {\n reject(err);\n await handle.close();\n return;\n }\n resolve();\n });\n });\n return handle;\n }\n\n static async release(handle: FileHandle): Promise<void> {\n await new Promise<void>((resolve, reject) => {\n flock(handle.fd, 'un', (err) => {\n if (err) {\n reject(err);\n return;\n }\n resolve();\n });\n });\n await handle.close();\n }\n\n static async isLocked(filename: string): Promise<boolean> {\n try {\n const handle = await LockFile.acquire(filename);\n await LockFile.release(handle);\n await handle.close();\n\n return false;\n } catch (e) {\n return true;\n }\n }\n}\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;;;;;;;ACIA,oBAAsB;AACtB,sBAA4C;AAErC,IAAMA,WAAN,MAAMA,UAAAA;EACX,aAAaC,QAAQC,UAAuC;AAC1D,UAAMC,SAAS,UAAMC,sBAAKF,UAAUG,0BAAUC,OAAO;AACrD,UAAM,IAAIC,QAAc,CAACC,SAASC,WAAAA;AAChCC,+BAAMP,OAAOQ,IAAI,QAAQ,OAAOC,QAAAA;AAC9B,YAAIA,KAAK;AACPH,iBAAOG,GAAAA;AACP,gBAAMT,OAAOU,MAAK;AAClB;QACF;AACAL,gBAAAA;MACF,CAAA;IACF,CAAA;AACA,WAAOL;EACT;EAEA,aAAaW,QAAQX,QAAmC;AACtD,UAAM,IAAII,QAAc,CAACC,SAASC,WAAAA;AAChCC,+BAAMP,OAAOQ,IAAI,MAAM,CAACC,QAAAA;AACtB,YAAIA,KAAK;AACPH,iBAAOG,GAAAA;AACP;QACF;AACAJ,gBAAAA;MACF,CAAA;IACF,CAAA;AACA,UAAML,OAAOU,MAAK;EACpB;EAEA,aAAaE,SAASb,UAAoC;AACxD,QAAI;AACF,YAAMC,SAAS,MAAMH,UAASC,QAAQC,QAAAA;AACtC,YAAMF,UAASc,QAAQX,MAAAA;AACvB,YAAMA,OAAOU,MAAK;AAElB,aAAO;IACT,SAASG,GAAG;AACV,aAAO;IACT;EACF;AACF;",
|
|
6
|
+
"names": ["LockFile", "acquire", "filename", "handle", "open", "constants", "O_CREAT", "Promise", "resolve", "reject", "flock", "fd", "err", "close", "release", "isLocked", "e"]
|
|
7
7
|
}
|
package/dist/lib/node/meta.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"inputs":{"packages/common/lock-file/src/lock-file.ts":{"bytes":
|
|
1
|
+
{"inputs":{"packages/common/lock-file/src/lock-file.ts":{"bytes":4154,"imports":[{"path":"fs-ext","kind":"import-statement","external":true},{"path":"node:fs/promises","kind":"import-statement","external":true}],"format":"esm"},"packages/common/lock-file/src/index.ts":{"bytes":470,"imports":[{"path":"packages/common/lock-file/src/lock-file.ts","kind":"import-statement","original":"./lock-file"}],"format":"esm"}},"outputs":{"packages/common/lock-file/dist/lib/node/index.cjs.map":{"imports":[],"exports":[],"inputs":{},"bytes":2274},"packages/common/lock-file/dist/lib/node/index.cjs":{"imports":[{"path":"fs-ext","kind":"require-call","external":true},{"path":"node:fs/promises","kind":"require-call","external":true}],"exports":[],"entryPoint":"packages/common/lock-file/src/index.ts","inputs":{"packages/common/lock-file/src/index.ts":{"bytesInOutput":121},"packages/common/lock-file/src/lock-file.ts":{"bytesInOutput":1042}},"bytes":2189}}}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lock-file.d.ts","sourceRoot":"","sources":["../../../src/lock-file.ts"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"file":"lock-file.d.ts","sourceRoot":"","sources":["../../../src/lock-file.ts"],"names":[],"mappings":";AAKA,OAAO,EAAQ,UAAU,EAAa,MAAM,kBAAkB,CAAC;AAE/D,qBAAa,QAAQ;WACN,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;WAe9C,OAAO,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;WAa1C,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;CAW1D"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dxos/lock-file",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.58-main.0e9c99e",
|
|
4
4
|
"description": "Lock file .",
|
|
5
5
|
"homepage": "https://dxos.org",
|
|
6
6
|
"bugs": "https://github.com/dxos/dxos/issues",
|
|
@@ -14,9 +14,9 @@
|
|
|
14
14
|
],
|
|
15
15
|
"dependencies": {
|
|
16
16
|
"fs-ext": "^2.0.0",
|
|
17
|
-
"@dxos/async": "0.1.
|
|
18
|
-
"@dxos/log": "0.1.
|
|
19
|
-
"@dxos/node-std": "0.1.
|
|
17
|
+
"@dxos/async": "0.1.58-main.0e9c99e",
|
|
18
|
+
"@dxos/log": "0.1.58-main.0e9c99e",
|
|
19
|
+
"@dxos/node-std": "0.1.58-main.0e9c99e"
|
|
20
20
|
},
|
|
21
21
|
"devDependencies": {
|
|
22
22
|
"@types/fs-ext": "^2.0.0",
|
package/src/lock-file.test.ts
CHANGED
|
@@ -5,17 +5,24 @@
|
|
|
5
5
|
import chai, { expect } from 'chai';
|
|
6
6
|
import chaiAsPromised from 'chai-as-promised';
|
|
7
7
|
import { spawn } from 'node:child_process';
|
|
8
|
-
import { existsSync } from 'node:fs';
|
|
8
|
+
import { existsSync, mkdirSync, unlinkSync } from 'node:fs';
|
|
9
9
|
import { join } from 'node:path';
|
|
10
|
+
import waitForExpect from 'wait-for-expect';
|
|
10
11
|
|
|
11
|
-
import { Trigger
|
|
12
|
-
import { describe, test } from '@dxos/test';
|
|
12
|
+
import { Trigger } from '@dxos/async';
|
|
13
|
+
import { afterTest, beforeAll, describe, test } from '@dxos/test';
|
|
13
14
|
|
|
14
15
|
import { LockFile } from './lock-file';
|
|
15
16
|
|
|
16
17
|
chai.use(chaiAsPromised);
|
|
17
18
|
|
|
19
|
+
const TEST_DIR = '/tmp/dxos/testing/lock-file';
|
|
20
|
+
|
|
18
21
|
describe('LockFile', () => {
|
|
22
|
+
beforeAll(() => {
|
|
23
|
+
mkdirSync(TEST_DIR, { recursive: true });
|
|
24
|
+
});
|
|
25
|
+
|
|
19
26
|
test('basic', async () => {
|
|
20
27
|
const filename = join('/tmp', `lock-${Math.random()}.lock`);
|
|
21
28
|
|
|
@@ -27,32 +34,55 @@ describe('LockFile', () => {
|
|
|
27
34
|
await LockFile.release(handle2);
|
|
28
35
|
});
|
|
29
36
|
|
|
30
|
-
test('released when process
|
|
31
|
-
const filename = join(
|
|
32
|
-
|
|
33
|
-
|
|
37
|
+
test('released when process exits', async () => {
|
|
38
|
+
const filename = join(TEST_DIR, `lock-${Math.random()}.lock`);
|
|
39
|
+
afterTest(() => {
|
|
40
|
+
if (existsSync(filename)) {
|
|
41
|
+
unlinkSync(filename);
|
|
42
|
+
}
|
|
34
43
|
});
|
|
35
44
|
|
|
36
|
-
// Wait for process to start
|
|
37
45
|
const trigger = new Trigger();
|
|
38
|
-
const
|
|
39
|
-
|
|
46
|
+
const processHandle = spawn('node', ['-e', `(${lockInProcess.toString()})(${JSON.stringify(filename)})`], {
|
|
47
|
+
stdio: 'pipe',
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
{
|
|
51
|
+
// Wait for process to start
|
|
52
|
+
|
|
53
|
+
processHandle.stdout.on('data', (data: Uint8Array) => {
|
|
54
|
+
expect(data.toString().trim()).to.equal('locked');
|
|
40
55
|
trigger.wake();
|
|
41
|
-
}
|
|
42
|
-
}
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
|
|
43
59
|
await trigger.wait({ timeout: 1_000 });
|
|
44
|
-
clearInterval(intervalId);
|
|
45
60
|
|
|
46
61
|
await expect(LockFile.acquire(filename)).to.be.rejected;
|
|
47
62
|
|
|
63
|
+
processHandle.stdin.write('close');
|
|
48
64
|
processHandle.kill();
|
|
49
65
|
|
|
50
66
|
// Wait for process to be killed
|
|
51
|
-
await
|
|
67
|
+
await waitForExpect(async () => {
|
|
68
|
+
expect(await LockFile.isLocked(filename)).to.be.false;
|
|
69
|
+
});
|
|
52
70
|
|
|
53
71
|
const handle = await LockFile.acquire(filename);
|
|
54
72
|
await LockFile.release(handle);
|
|
55
73
|
});
|
|
74
|
+
|
|
75
|
+
test('spam with isLocked calls', async () => {
|
|
76
|
+
const checksNumber = 1000;
|
|
77
|
+
const filename = join('/tmp', `lock-${Math.random()}.lock`);
|
|
78
|
+
|
|
79
|
+
for (const _ of Array(checksNumber).keys()) {
|
|
80
|
+
const handle = await LockFile.acquire(filename);
|
|
81
|
+
expect(await LockFile.isLocked(filename)).to.be.true;
|
|
82
|
+
await LockFile.release(handle);
|
|
83
|
+
expect(await LockFile.isLocked(filename)).to.be.false;
|
|
84
|
+
}
|
|
85
|
+
});
|
|
56
86
|
});
|
|
57
87
|
|
|
58
88
|
// NOTE: Self-contained so when function.toString is called the code runs.
|
|
@@ -64,6 +94,9 @@ const lockInProcess = (filename: string) => {
|
|
|
64
94
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
65
95
|
const { flock } = require('fs-ext');
|
|
66
96
|
|
|
97
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
98
|
+
// @ts-ignore
|
|
99
|
+
let fileHandle;
|
|
67
100
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
68
101
|
// @ts-ignore
|
|
69
102
|
open(filename, constants.O_CREAT).then((handle) => {
|
|
@@ -71,11 +104,23 @@ const lockInProcess = (filename: string) => {
|
|
|
71
104
|
// @ts-ignore
|
|
72
105
|
flock(handle.fd, 'exnb', (err) => {
|
|
73
106
|
if (err) {
|
|
107
|
+
handle.close();
|
|
74
108
|
console.error(err);
|
|
75
109
|
return;
|
|
76
110
|
}
|
|
111
|
+
fileHandle = handle;
|
|
112
|
+
console.log('locked');
|
|
77
113
|
// Hang
|
|
78
114
|
setTimeout(() => {}, 1_000_000);
|
|
79
115
|
});
|
|
80
116
|
});
|
|
117
|
+
|
|
118
|
+
// Close file handle on stdin close.
|
|
119
|
+
process.stdin.on('data', (data) => {
|
|
120
|
+
if (data.toString().trim() === 'close') {
|
|
121
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
122
|
+
// @ts-ignore
|
|
123
|
+
fileHandle.close();
|
|
124
|
+
}
|
|
125
|
+
});
|
|
81
126
|
};
|
package/src/lock-file.ts
CHANGED
|
@@ -3,16 +3,16 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import { flock } from 'fs-ext';
|
|
6
|
-
import { constants } from 'node:fs';
|
|
7
|
-
import { open, FileHandle } from 'node:fs/promises';
|
|
6
|
+
import { open, FileHandle, constants } from 'node:fs/promises';
|
|
8
7
|
|
|
9
8
|
export class LockFile {
|
|
10
9
|
static async acquire(filename: string): Promise<FileHandle> {
|
|
11
10
|
const handle = await open(filename, constants.O_CREAT);
|
|
12
11
|
await new Promise<void>((resolve, reject) => {
|
|
13
|
-
flock(handle.fd, 'exnb', (err) => {
|
|
12
|
+
flock(handle.fd, 'exnb', async (err) => {
|
|
14
13
|
if (err) {
|
|
15
14
|
reject(err);
|
|
15
|
+
await handle.close();
|
|
16
16
|
return;
|
|
17
17
|
}
|
|
18
18
|
resolve();
|