@opentermsarchive/engine 13.0.0 → 14.0.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opentermsarchive/engine",
3
- "version": "13.0.0",
3
+ "version": "14.0.0",
4
4
  "description": "Tracks and makes visible changes to the terms of online services",
5
5
  "homepage": "https://opentermsarchive.org",
6
6
  "bugs": {
@@ -92,7 +92,7 @@
92
92
  "morgan": "^1.10.1",
93
93
  "node-fetch": "^3.3.2",
94
94
  "octokit": "^5.0.3",
95
- "puppeteer": "^25.1.0",
95
+ "puppeteer": "^25.2.0",
96
96
  "puppeteer-extra": "^3.3.6",
97
97
  "puppeteer-extra-plugin-stealth": "^2.11.2",
98
98
  "simple-git": "^3.28.0",
@@ -116,7 +116,7 @@
116
116
  "@opentermsarchive/terms-types": "^2.2.0"
117
117
  },
118
118
  "engines": {
119
- "node": ">=20 <=24"
119
+ "node": ">=22 <=26"
120
120
  },
121
121
  "overrides": {
122
122
  "jsdom": {
@@ -108,7 +108,7 @@ export default async function fetch(url, cssSelectors, config) {
108
108
 
109
109
  throw new Error(error.message);
110
110
  } finally {
111
- await cleanupPage(client, page, context);
111
+ await cleanupPage(client, context);
112
112
  }
113
113
  }
114
114
 
@@ -261,14 +261,11 @@ async function waitForSelectors(page, selectors, timeout) {
261
261
  });
262
262
  }
263
263
 
264
- async function cleanupPage(client, page, context) {
264
+ async function cleanupPage(client, context) {
265
265
  if (client) {
266
266
  await client.detach().catch(() => {});
267
267
  }
268
- if (page) {
269
- await page.close().catch(() => {});
270
- }
271
268
  if (context) {
272
- await context.close().catch(() => {}); // Close the isolated context to free resources and ensure complete cleanup
269
+ await context.close().catch(() => {}); // Closing the context disposes its page too; closing the page explicitly is not only redundant but can hang indefinitely when a navigation is still in flight, for instance after a navigation timeout
273
270
  }
274
271
  }
@@ -33,7 +33,8 @@ export default class Git {
33
33
  .addConfig('user.name', this.author.name)
34
34
  .addConfig('user.email', this.author.email)
35
35
  .addConfig('core.quotePath', false) // Disable Git's encoding of special characters in pathnames. For example, `service·A` will be encoded as `service\302\267A` without this setting, leading to issues. See https://git-scm.com/docs/git-config#Documentation/git-config.txt-corequotePath
36
- .addConfig('core.commitGraph', true); // Enable `commit-graph` feature for efficient commit data storage, improving performance of operations like `git log`
36
+ .addConfig('core.commitGraph', true) // Enable `commit-graph` feature for efficient commit data storage, improving performance of operations like `git log`
37
+ .addConfig('gc.writeCommitGraph', false); // Prevent automatic `git gc` from also writing the commit-graph: the engine writes it explicitly (see `writeCommitGraph`/`updateCommitGraph`), and a concurrent gc write races those, which can leave a stale `commit-graph.lock` and make subsequent operations fail
37
38
  }
38
39
 
39
40
  add(filePath) {
@@ -128,6 +129,7 @@ export default class Git {
128
129
  }
129
130
 
130
131
  async cleanUp() {
132
+ await fs.rm(path.join(this.path, '.git', 'objects', 'info', 'commit-graph.lock'), { force: true }); // Remove a leftover commit-graph lock from a previous `commit-graph write` that was killed mid-write (e.g. the process was terminated during a deploy or restart). The commit-graph is a disposable cache rebuilt by `writeCommitGraph`, so clearing a stale lock is safe and prevents every subsequent run from failing.
131
133
  await this.git.reset('hard');
132
134
 
133
135
  return this.git.clean('f', '-d');
@@ -83,4 +83,32 @@ describe('Git', () => {
83
83
  });
84
84
  });
85
85
  });
86
+
87
+ describe('#cleanUp', () => {
88
+ context('when a commit-graph lock has been left behind by an interrupted process', () => {
89
+ const infoDirectoryPath = path.join(RECORDER_PATH, '.git', 'objects', 'info');
90
+ const lockFilePath = path.join(infoDirectoryPath, 'commit-graph.lock');
91
+
92
+ before(async () => {
93
+ const filePath = `${RECORDER_PATH}/file-to-clean.md`;
94
+
95
+ await fs.writeFile(filePath, DEFAULT_CONTENT);
96
+ await subject.add(filePath);
97
+ await subject.commit({ filePath, message: DEFAULT_COMMIT_MESSAGE });
98
+
99
+ await fs.mkdir(infoDirectoryPath, { recursive: true });
100
+ await fs.writeFile(lockFilePath, '');
101
+
102
+ await subject.cleanUp();
103
+ });
104
+
105
+ after(() => subject.destroyHistory());
106
+
107
+ it('removes the stale commit-graph lock', async () => {
108
+ const infoDirectoryContent = await fs.readdir(infoDirectoryPath);
109
+
110
+ expect(infoDirectoryContent).to.not.include('commit-graph.lock');
111
+ });
112
+ });
113
+ });
86
114
  });
@@ -260,6 +260,9 @@ async function loadServiceHistoryFiles(serviceId) {
260
260
  ]);
261
261
 
262
262
  Object.entries(serviceDeclaration.terms).forEach(([ termsType, declaration ]) => {
263
+ if (!Array.isArray(serviceHistory[termsType]) && serviceHistory[termsType] !== undefined) {
264
+ throw new Error(`History file for "${termsType}" is not properly formatted: expected an array but got an object. Please use [] instead of {}.`);
265
+ }
263
266
  serviceHistory[termsType] = serviceHistory[termsType] || [];
264
267
  serviceHistory[termsType].push(declaration);
265
268
  });