@buildinternet/uploads 0.16.0 → 0.16.1

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.
@@ -0,0 +1,8 @@
1
+ export interface DistStalenessResult {
2
+ stale: boolean;
3
+ checked: boolean;
4
+ reason?: string;
5
+ }
6
+
7
+ export declare function checkDistStaleness(packageRoot: string): DistStalenessResult;
8
+ export declare function warnIfDistStale(packageRoot: string): void;
@@ -0,0 +1,94 @@
1
+ // Dev-only staleness guard for the linked `uploads` CLI (issue #295).
2
+ //
3
+ // A globally-linked `uploads` binary resolves to this monorepo package and
4
+ // imports the compiled `dist/cli.js`. `dist/` is only rebuilt by an explicit
5
+ // `pnpm --filter @buildinternet/uploads build`, so after pulling new source
6
+ // the linked CLI can silently keep running old compiled code while
7
+ // `--version` still reports the current package version.
8
+ //
9
+ // This module is intentionally plain, dependency-free JS (not compiled)
10
+ // so it can run before `dist/` is imported, and so it has no cost at all
11
+ // for a published npm install: `files` in package.json ships only
12
+ // `bin/`, `dist/`, and `README.md` — never `src/` — so `sourceDir` below
13
+ // simply won't exist there, and `checkDistStaleness` returns early after
14
+ // one `existsSync` call.
15
+
16
+ import { existsSync, readdirSync, statSync } from "node:fs";
17
+ import { join } from "node:path";
18
+
19
+ /** Recursively find the newest mtime (ms) among files under `dir`. */
20
+ function newestMtimeMs(dir) {
21
+ let newest = -Infinity;
22
+ const stack = [dir];
23
+ while (stack.length > 0) {
24
+ const current = stack.pop();
25
+ let entries;
26
+ try {
27
+ entries = readdirSync(current, { withFileTypes: true });
28
+ } catch {
29
+ continue;
30
+ }
31
+ for (const entry of entries) {
32
+ if (entry.name === "node_modules" || entry.name.startsWith(".")) continue;
33
+ const full = join(current, entry.name);
34
+ if (entry.isDirectory()) {
35
+ stack.push(full);
36
+ } else if (entry.isFile()) {
37
+ try {
38
+ const mtimeMs = statSync(full).mtimeMs;
39
+ if (mtimeMs > newest) newest = mtimeMs;
40
+ } catch {
41
+ // ignore races (file removed between readdir and stat)
42
+ }
43
+ }
44
+ }
45
+ }
46
+ return newest;
47
+ }
48
+
49
+ /**
50
+ * Compare source mtime against dist mtime.
51
+ *
52
+ * @param {string} packageRoot absolute path to packages/uploads
53
+ * @returns {{ stale: boolean, checked: boolean, reason?: string }}
54
+ * `checked` is false when there's no source tree to compare against
55
+ * (i.e. a published install) — the check is a no-op in that case.
56
+ */
57
+ export function checkDistStaleness(packageRoot) {
58
+ const sourceDir = join(packageRoot, "src");
59
+ const distDir = join(packageRoot, "dist");
60
+
61
+ // No src/ tree ships in the published npm package (see package.json
62
+ // "files"), so this is the cheapest reliable signal that we're running
63
+ // inside the monorepo (dev/linked context) rather than a published install.
64
+ if (!existsSync(sourceDir)) {
65
+ return { stale: false, checked: false };
66
+ }
67
+
68
+ if (!existsSync(distDir)) {
69
+ return { stale: true, checked: true, reason: "dist/ is missing" };
70
+ }
71
+
72
+ const sourceMtime = newestMtimeMs(sourceDir);
73
+ const distMtime = newestMtimeMs(distDir);
74
+
75
+ if (sourceMtime > distMtime) {
76
+ return { stale: true, checked: true, reason: "dist/ predates src/" };
77
+ }
78
+
79
+ return { stale: false, checked: true };
80
+ }
81
+
82
+ /** Print a one-line stderr warning if the dev build looks stale. Never throws. */
83
+ export function warnIfDistStale(packageRoot) {
84
+ try {
85
+ const result = checkDistStaleness(packageRoot);
86
+ if (result.checked && result.stale) {
87
+ process.stderr.write(
88
+ `warning: uploads dev build is stale (${result.reason}) — run \`pnpm --filter @buildinternet/uploads build\`\n`,
89
+ );
90
+ }
91
+ } catch {
92
+ // Never let the staleness check itself break the CLI.
93
+ }
94
+ }
package/bin/uploads.js CHANGED
@@ -1,5 +1,12 @@
1
1
  #!/usr/bin/env node
2
- import { runCli } from "../dist/cli.js";
2
+ import { fileURLToPath } from "node:url";
3
+ import { dirname, join } from "node:path";
4
+ import { warnIfDistStale } from "./dist-staleness.mjs";
5
+
6
+ const packageRoot = join(dirname(fileURLToPath(import.meta.url)), "..");
7
+ warnIfDistStale(packageRoot);
8
+
9
+ const { runCli } = await import("../dist/cli.js");
3
10
 
4
11
  runCli(process.argv)
5
12
  .then((code) => process.exit(code ?? 0))
package/dist/github.js CHANGED
@@ -239,5 +239,6 @@ export function attachmentsCommentBody(items, galleries = [], marker = ATTACHMEN
239
239
  lines.push("", "</details>", "");
240
240
  }
241
241
  lines.push('<sub>Maintained by <a href="https://uploads.sh">uploads.sh</a> — re-uploading a file with the same name updates it everywhere it is embedded.</sub>');
242
+ lines.push('<sub>Add media: <code>uploads put &lt;file&gt; --pr &lt;N&gt; --comment</code> (or <code>--issue &lt;N&gt;</code>) · <a href="https://uploads.sh/docs/github-app">docs</a></sub>');
242
243
  return lines.join("\n");
243
244
  }
package/dist/mcp/tools.js CHANGED
@@ -334,7 +334,7 @@ export function createUploadsMcpTools(opts) {
334
334
  noGit: { type: "boolean", description: "Don't derive the repo segment from git." },
335
335
  comment: {
336
336
  type: "boolean",
337
- description: "With pr/issue: create or update the managed attachments comment via local gh auth (best-effort).",
337
+ description: "With pr/issue: create or update the managed attachments comment. Posts as uploads-sh[bot] when the GitHub App is installed on the repo; otherwise via local gh auth (best-effort).",
338
338
  },
339
339
  dryRun: {
340
340
  type: "boolean",
@@ -1049,7 +1049,7 @@ export function createUploadsMcpTools(opts) {
1049
1049
  },
1050
1050
  {
1051
1051
  name: "comment",
1052
- description: "Create or update the managed attachments comment on a GitHub PR or issue, listing everything uploaded for it. Uses local gh auth; edits its own prior comment in place and never touches other comments.",
1052
+ description: "Create or update the managed attachments comment on a GitHub PR or issue, listing everything uploaded for it. Posts as uploads-sh[bot] when the GitHub App is installed on the repo; otherwise via local gh auth. Edits its own prior comment in place and never touches other comments.",
1053
1053
  inputSchema: {
1054
1054
  type: "object",
1055
1055
  properties: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@buildinternet/uploads",
3
- "version": "0.16.0",
3
+ "version": "0.16.1",
4
4
  "description": "CLI and client for uploads.sh — workspace-scoped image hosting for GitHub embeds",
5
5
  "type": "module",
6
6
  "sideEffects": false,