@canmi/web 2026.1005.1 → 2026.1005.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.
@@ -0,0 +1,12 @@
1
+ import { Disclosure } from "./index.js";
2
+ //#region disclose/src/build.d.ts
3
+ /**
4
+ * What an app is made of, read from its `package.json` at build time.
5
+ *
6
+ * A version is the installed package's, read from its own `package.json` beside the app rather than
7
+ * through its exports, which rarely offer it. See spec/web/disclose.md.
8
+ */
9
+ export declare function disclosure(root: string): Disclosure;
10
+ /** `disclosure(root)` as a Vite `define` entry, so a page reads it as `import.meta.env.VITE_DISCLOSURE`. */
11
+ export declare function discloseDefine(root: string): Record<string, string>;
12
+ //#endregion
@@ -0,0 +1,44 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ //#region disclose/src/build.ts
4
+ /**
5
+ * The packages whose version a Wappalyzer fingerprint reads, by the name an app installs them as.
6
+ *
7
+ * Only an app that lists one in its own `package.json` gets its version; the others are absent.
8
+ */
9
+ const VERSIONED = [
10
+ "@codemirror/view",
11
+ "@videojs/core",
12
+ "algoliasearch",
13
+ "d3-hierarchy"
14
+ ];
15
+ /** A dependency an app is deployed to Cloudflare Workers with. */
16
+ const WORKERS = ["@sveltejs/adapter-cloudflare", "wrangler"];
17
+ function readJson(path) {
18
+ return JSON.parse(readFileSync(path, "utf8"));
19
+ }
20
+ /**
21
+ * What an app is made of, read from its `package.json` at build time.
22
+ *
23
+ * A version is the installed package's, read from its own `package.json` beside the app rather than
24
+ * through its exports, which rarely offer it. See spec/web/disclose.md.
25
+ */
26
+ function disclosure(root) {
27
+ const manifest = readJson(join(root, "package.json"));
28
+ const names = /* @__PURE__ */ new Set([...Object.keys(manifest.dependencies ?? {}), ...Object.keys(manifest.devDependencies ?? {})]);
29
+ const versions = {};
30
+ for (const name of VERSIONED) {
31
+ if (!names.has(name)) continue;
32
+ versions[name] = readJson(join(root, "node_modules", name, "package.json")).version;
33
+ }
34
+ return {
35
+ versions,
36
+ ...WORKERS.some((name) => names.has(name)) ? { runtime: "Cloudflare Workers" } : {}
37
+ };
38
+ }
39
+ /** `disclosure(root)` as a Vite `define` entry, so a page reads it as `import.meta.env.VITE_DISCLOSURE`. */
40
+ function discloseDefine(root) {
41
+ return { "import.meta.env.VITE_DISCLOSURE": JSON.stringify(disclosure(root)) };
42
+ }
43
+ //#endregion
44
+ export { discloseDefine, disclosure };
@@ -1,7 +1,18 @@
1
1
  //#region disclose/src/index.d.ts
2
+ /**
3
+ * What an app is made of, worked out at build time by `disclosure` in `./build` and read by its
4
+ * pages as `import.meta.env.VITE_DISCLOSURE`.
5
+ */
6
+ export interface Disclosure {
7
+ /** Installed versions, by package name, of the packages a fingerprint reads a version for. */
8
+ readonly versions: Readonly<Record<string, string>>;
9
+ /** Where the app runs, when a fingerprint names it: set for an app deployed to Workers. */
10
+ readonly runtime?: 'Cloudflare Workers';
11
+ }
2
12
  /**
3
13
  * What the page is built with, said where a profiler reads it: each dotted path set on `window`,
4
- * the objects on the way made as needed, and nothing already there replaced. A patch for
14
+ * the objects on the way made as needed, nothing already there replaced, and an undefined value
15
+ * skipped, so an entry the build did not find is simply absent. A patch for
5
16
  * Wappalyzer, whose fingerprints read globals -- and read a version only from some. See
6
17
  * spec/web/disclose.md.
7
18
  */
@@ -1,7 +1,8 @@
1
1
  //#region disclose/src/index.ts
2
2
  /**
3
3
  * What the page is built with, said where a profiler reads it: each dotted path set on `window`,
4
- * the objects on the way made as needed, and nothing already there replaced. A patch for
4
+ * the objects on the way made as needed, nothing already there replaced, and an undefined value
5
+ * skipped, so an entry the build did not find is simply absent. A patch for
5
6
  * Wappalyzer, whose fingerprints read globals -- and read a version only from some. See
6
7
  * spec/web/disclose.md.
7
8
  */
@@ -10,7 +11,7 @@ function disclose(entries) {
10
11
  for (const [path, value] of Object.entries(entries)) {
11
12
  const keys = path.split(".");
12
13
  const last = keys.pop();
13
- if (!last) continue;
14
+ if (!last || value === void 0) continue;
14
15
  let target = window;
15
16
  for (const key of keys) {
16
17
  const next = target[key];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@canmi/web",
3
- "version": "2026.1005.1",
3
+ "version": "2026.1005.2",
4
4
  "description": "SvelteKit compat, referer and Sentry.",
5
5
  "keywords": [
6
6
  "browserslist",
@@ -44,6 +44,10 @@
44
44
  "types": "./dist/sentry/src/server.d.ts",
45
45
  "default": "./dist/sentry/src/server.js"
46
46
  },
47
+ "./disclose/build": {
48
+ "types": "./dist/disclose/src/build.d.ts",
49
+ "default": "./dist/disclose/src/build.js"
50
+ },
47
51
  "./disclose": {
48
52
  "types": "./dist/disclose/src/index.d.ts",
49
53
  "default": "./dist/disclose/src/index.js"