@lhechenberger/check-links 0.2.0 → 0.2.2

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Lukas Hechenberger
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.
package/README.md CHANGED
@@ -64,6 +64,11 @@ export default defineConfig({
64
64
  | `concurrency` | `20` | Parallel requests of the crawl |
65
65
  | `timeout` | `30000` | Timeout per request in ms |
66
66
 
67
+ ### Type-checking and unused-file tools
68
+
69
+ - The package ships TypeScript source (no build step). Relative imports inside it have no `.ts` extension, so a project that type-checks its `check-links.config.ts` (e.g. listed in `tsconfig.json` `include`) needs `moduleResolution: "bundler"` (or `"node16"`/`"nodenext"` is **not** supported) and Node or Bun types (`@types/node` or `@types/bun`), since the code uses `process`. Before 0.2.2 the imports ended in `.ts`; if you see `TS5097` from `node_modules/@lhechenberger/check-links`, update the package.
70
+ - [knip](https://knip.dev) reports the config as an unused file. Declare it as an entry in `knip.json`: `{ "workspaces": { "apps/web": { "entry": ["check-links.config.ts"] } } }` (use `"entry"` at the top level in a single-package repo).
71
+
67
72
  ## GitHub workflow: check every Vercel deployment
68
73
 
69
74
  The package contains a workflow template that runs the check when Vercel reports a ready deployment, shows the result as a **"Broken Links" check run** on the deployed commit (so it appears on the pull request, with the full output) and reports the commit status that Vercel's Deployment Checks can require before a production deployment gets its domain.
@@ -93,3 +98,7 @@ import { classify, run, defineConfig } from '@lhechenberger/check-links';
93
98
  ```
94
99
 
95
100
  `run(url, config)` crawls and prints, and resolves to the exit code; `classify` is the pure part that sorts broken links into `errors`, `flaky` and `warnings`.
101
+
102
+ ## License
103
+
104
+ [MIT](LICENSE)
package/package.json CHANGED
@@ -1,28 +1,40 @@
1
1
  {
2
2
  "name": "@lhechenberger/check-links",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "description": "Crawls a deployed site for broken links, separating flaky failures from real ones. Built for Vercel preview deployments.",
5
5
  "keywords": [
6
6
  "broken-links",
7
+ "ci",
7
8
  "link-checker",
8
9
  "linkinator",
9
- "vercel",
10
- "ci"
10
+ "vercel"
11
11
  ],
12
- "license": "UNLICENSED",
12
+ "homepage": "https://github.com/LukasHechenberger/workbench/tree/main/packages/check-links#readme",
13
+ "bugs": {
14
+ "url": "https://github.com/LukasHechenberger/workbench/issues"
15
+ },
16
+ "license": "MIT",
13
17
  "author": "Lukas Hechenberger",
14
- "type": "module",
15
- "exports": {
16
- ".": "./src/index.ts"
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "git+https://github.com/LukasHechenberger/workbench.git",
21
+ "directory": "packages/check-links"
17
22
  },
18
23
  "bin": {
19
- "check-links": "./src/cli.ts"
24
+ "check-links": "src/cli.ts"
20
25
  },
21
26
  "files": [
22
27
  "src",
23
28
  "!src/**/*.test.ts",
24
29
  "templates"
25
30
  ],
31
+ "type": "module",
32
+ "exports": {
33
+ ".": "./src/index.ts"
34
+ },
35
+ "publishConfig": {
36
+ "access": "public"
37
+ },
26
38
  "scripts": {
27
39
  "test": "bun test"
28
40
  },
@@ -31,8 +43,5 @@
31
43
  },
32
44
  "engines": {
33
45
  "bun": ">=1.2"
34
- },
35
- "publishConfig": {
36
- "access": "public"
37
46
  }
38
47
  }
package/src/classify.ts CHANGED
@@ -38,6 +38,7 @@ export async function classify(
38
38
  // A burst of requests is what failed in the first place, so the second pass is gentle
39
39
  for (let i = 0; i < toRecheck.length; i += concurrency) {
40
40
  const batch = toRecheck.slice(i, i + concurrency);
41
+ // oxlint-disable-next-line no-await-in-loop -- one batch after the other, on purpose
41
42
  const results = await Promise.all(batch.map((link) => recovers(link)));
42
43
  for (const [index, link] of batch.entries()) (results[index] ? flaky : errors).push(link);
43
44
  }
package/src/cli.ts CHANGED
@@ -2,8 +2,8 @@
2
2
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
3
3
  import { dirname, resolve } from 'node:path';
4
4
  import { parseArgs } from 'node:util';
5
- import type { CheckLinksConfig } from './config.ts';
6
- import { run } from './run.ts';
5
+ import type { CheckLinksConfig } from './config';
6
+ import { run } from './run';
7
7
 
8
8
  const usage = `Usage:
9
9
  check-links [url] [--config check-links.config.ts]
@@ -37,7 +37,8 @@ if (positionals[0] === 'init-workflow') {
37
37
  const configPath = resolve(values.config ?? 'check-links.config.ts');
38
38
  let config: CheckLinksConfig = {};
39
39
  if (existsSync(configPath)) {
40
- config = ((await import(configPath)) as { default: CheckLinksConfig }).default;
40
+ const loaded: { default: CheckLinksConfig } = await import(configPath);
41
+ config = loaded.default;
41
42
  } else if (values.config) {
42
43
  console.error(`Config ${configPath} not found`);
43
44
  process.exit(1);
package/src/index.ts CHANGED
@@ -1,3 +1,3 @@
1
- export { classify, type Classification, type ClassifyOptions } from './classify.ts';
2
- export { defineConfig, type CheckLinksConfig, type LinkContext } from './config.ts';
3
- export { run } from './run.ts';
1
+ export { classify, type Classification, type ClassifyOptions } from './classify';
2
+ export { defineConfig, type CheckLinksConfig, type LinkContext } from './config';
3
+ export { run } from './run';
package/src/run.ts CHANGED
@@ -1,9 +1,17 @@
1
1
  import { check, type LinkResult, LinkState } from 'linkinator';
2
- import { classify } from './classify.ts';
3
- import type { CheckLinksConfig } from './config.ts';
2
+ import { classify } from './classify';
3
+ import type { CheckLinksConfig } from './config';
4
4
 
5
5
  const BYPASS_ENV = 'VERCEL_AUTOMATION_BYPASS_SECRET';
6
6
 
7
+ const print = (title: string, results: LinkResult[]) => {
8
+ if (results.length === 0) return;
9
+ console.log(`\n${title}`);
10
+ for (const link of results) {
11
+ console.log(` [${link.status ?? 'error'}] ${link.url}\n on ${link.parent}`);
12
+ }
13
+ };
14
+
7
15
  /** Crawls `url` and prints the result. Returns the process exit code. */
8
16
  export async function run(url: string, config: CheckLinksConfig): Promise<number> {
9
17
  const { origin } = new URL(url);
@@ -64,14 +72,6 @@ export async function run(url: string, config: CheckLinksConfig): Promise<number
64
72
  concurrency: config.recheckConcurrency ?? 5,
65
73
  });
66
74
 
67
- const print = (title: string, results: LinkResult[]) => {
68
- if (results.length === 0) return;
69
- console.log(`\n${title}`);
70
- for (const link of results) {
71
- console.log(` [${link.status ?? 'error'}] ${link.url}\n on ${link.parent}`);
72
- }
73
- };
74
-
75
75
  print(`${config.warningTitle ?? 'Broken links that are only warnings'} (warnings):`, warnings);
76
76
  print('Links that worked on a second request (warnings):', flaky);
77
77
  print('Broken links:', errors);