@0xrama/bnpm 0.0.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.
Files changed (204) hide show
  1. package/README.md +83 -0
  2. package/dist/package.json +45 -0
  3. package/dist/src/cache/archive.d.ts +15 -0
  4. package/dist/src/cache/archive.js +163 -0
  5. package/dist/src/cache/archive.js.map +1 -0
  6. package/dist/src/cache/commands.d.ts +15 -0
  7. package/dist/src/cache/commands.js +168 -0
  8. package/dist/src/cache/commands.js.map +1 -0
  9. package/dist/src/cache/quarantine.d.ts +18 -0
  10. package/dist/src/cache/quarantine.js +104 -0
  11. package/dist/src/cache/quarantine.js.map +1 -0
  12. package/dist/src/cache/store.d.ts +10 -0
  13. package/dist/src/cache/store.js +155 -0
  14. package/dist/src/cache/store.js.map +1 -0
  15. package/dist/src/cli.d.ts +2 -0
  16. package/dist/src/cli.js +4 -0
  17. package/dist/src/cli.js.map +1 -0
  18. package/dist/src/commands/diff.d.ts +34 -0
  19. package/dist/src/commands/diff.js +223 -0
  20. package/dist/src/commands/diff.js.map +1 -0
  21. package/dist/src/commands/doctor.d.ts +14 -0
  22. package/dist/src/commands/doctor.js +42 -0
  23. package/dist/src/commands/doctor.js.map +1 -0
  24. package/dist/src/commands/edit.d.ts +12 -0
  25. package/dist/src/commands/edit.js +100 -0
  26. package/dist/src/commands/edit.js.map +1 -0
  27. package/dist/src/commands/fund.d.ts +10 -0
  28. package/dist/src/commands/fund.js +32 -0
  29. package/dist/src/commands/fund.js.map +1 -0
  30. package/dist/src/commands/graph.d.ts +29 -0
  31. package/dist/src/commands/graph.js +90 -0
  32. package/dist/src/commands/graph.js.map +1 -0
  33. package/dist/src/commands/index.d.ts +20 -0
  34. package/dist/src/commands/index.js +1753 -0
  35. package/dist/src/commands/index.js.map +1 -0
  36. package/dist/src/commands/navigation.d.ts +2 -0
  37. package/dist/src/commands/navigation.js +47 -0
  38. package/dist/src/commands/navigation.js.map +1 -0
  39. package/dist/src/commands/outdated.d.ts +17 -0
  40. package/dist/src/commands/outdated.js +69 -0
  41. package/dist/src/commands/outdated.js.map +1 -0
  42. package/dist/src/commands/process.d.ts +11 -0
  43. package/dist/src/commands/process.js +189 -0
  44. package/dist/src/commands/process.js.map +1 -0
  45. package/dist/src/commands/prompts.d.ts +6 -0
  46. package/dist/src/commands/prompts.js +86 -0
  47. package/dist/src/commands/prompts.js.map +1 -0
  48. package/dist/src/commands/query.d.ts +48 -0
  49. package/dist/src/commands/query.js +639 -0
  50. package/dist/src/commands/query.js.map +1 -0
  51. package/dist/src/commands/rebuild.d.ts +16 -0
  52. package/dist/src/commands/rebuild.js +44 -0
  53. package/dist/src/commands/rebuild.js.map +1 -0
  54. package/dist/src/commands/sbom.d.ts +6 -0
  55. package/dist/src/commands/sbom.js +31 -0
  56. package/dist/src/commands/sbom.js.map +1 -0
  57. package/dist/src/commands/script-approvals.d.ts +22 -0
  58. package/dist/src/commands/script-approvals.js +128 -0
  59. package/dist/src/commands/script-approvals.js.map +1 -0
  60. package/dist/src/commands/shrinkwrap.d.ts +6 -0
  61. package/dist/src/commands/shrinkwrap.js +97 -0
  62. package/dist/src/commands/shrinkwrap.js.map +1 -0
  63. package/dist/src/commands/stage.d.ts +20 -0
  64. package/dist/src/commands/stage.js +40 -0
  65. package/dist/src/commands/stage.js.map +1 -0
  66. package/dist/src/commands/token.d.ts +2 -0
  67. package/dist/src/commands/token.js +47 -0
  68. package/dist/src/commands/token.js.map +1 -0
  69. package/dist/src/commands/trust.d.ts +5 -0
  70. package/dist/src/commands/trust.js +41 -0
  71. package/dist/src/commands/trust.js.map +1 -0
  72. package/dist/src/config/commands.d.ts +5 -0
  73. package/dist/src/config/commands.js +91 -0
  74. package/dist/src/config/commands.js.map +1 -0
  75. package/dist/src/config/configuration.d.ts +40 -0
  76. package/dist/src/config/configuration.js +157 -0
  77. package/dist/src/config/configuration.js.map +1 -0
  78. package/dist/src/config/interactive.d.ts +16 -0
  79. package/dist/src/config/interactive.js +19 -0
  80. package/dist/src/config/interactive.js.map +1 -0
  81. package/dist/src/config/paths.d.ts +22 -0
  82. package/dist/src/config/paths.js +46 -0
  83. package/dist/src/config/paths.js.map +1 -0
  84. package/dist/src/config/types.d.ts +15 -0
  85. package/dist/src/config/types.js +6 -0
  86. package/dist/src/config/types.js.map +1 -0
  87. package/dist/src/core/cli-parser.d.ts +102 -0
  88. package/dist/src/core/cli-parser.js +1148 -0
  89. package/dist/src/core/cli-parser.js.map +1 -0
  90. package/dist/src/core/cli-runner.d.ts +15 -0
  91. package/dist/src/core/cli-runner.js +261 -0
  92. package/dist/src/core/cli-runner.js.map +1 -0
  93. package/dist/src/core/exit-codes.d.ts +13 -0
  94. package/dist/src/core/exit-codes.js +13 -0
  95. package/dist/src/core/exit-codes.js.map +1 -0
  96. package/dist/src/core/output.d.ts +23 -0
  97. package/dist/src/core/output.js +123 -0
  98. package/dist/src/core/output.js.map +1 -0
  99. package/dist/src/installer/install.d.ts +73 -0
  100. package/dist/src/installer/install.js +596 -0
  101. package/dist/src/installer/install.js.map +1 -0
  102. package/dist/src/installer/mutations.d.ts +5 -0
  103. package/dist/src/installer/mutations.js +160 -0
  104. package/dist/src/installer/mutations.js.map +1 -0
  105. package/dist/src/linker/project-linker.d.ts +9 -0
  106. package/dist/src/linker/project-linker.js +223 -0
  107. package/dist/src/linker/project-linker.js.map +1 -0
  108. package/dist/src/lockfile/index.d.ts +27 -0
  109. package/dist/src/lockfile/index.js +221 -0
  110. package/dist/src/lockfile/index.js.map +1 -0
  111. package/dist/src/package/authoring.d.ts +31 -0
  112. package/dist/src/package/authoring.js +125 -0
  113. package/dist/src/package/authoring.js.map +1 -0
  114. package/dist/src/package/pack.d.ts +22 -0
  115. package/dist/src/package/pack.js +238 -0
  116. package/dist/src/package/pack.js.map +1 -0
  117. package/dist/src/package/provenance.d.ts +28 -0
  118. package/dist/src/package/provenance.js +162 -0
  119. package/dist/src/package/provenance.js.map +1 -0
  120. package/dist/src/package/publish.d.ts +28 -0
  121. package/dist/src/package/publish.js +189 -0
  122. package/dist/src/package/publish.js.map +1 -0
  123. package/dist/src/package/trusted-publishing.d.ts +7 -0
  124. package/dist/src/package/trusted-publishing.js +84 -0
  125. package/dist/src/package/trusted-publishing.js.map +1 -0
  126. package/dist/src/project/dependency-error.d.ts +3 -0
  127. package/dist/src/project/dependency-error.js +7 -0
  128. package/dist/src/project/dependency-error.js.map +1 -0
  129. package/dist/src/project/dev-engines.d.ts +10 -0
  130. package/dist/src/project/dev-engines.js +70 -0
  131. package/dist/src/project/dev-engines.js.map +1 -0
  132. package/dist/src/project/discovery.d.ts +6 -0
  133. package/dist/src/project/discovery.js +51 -0
  134. package/dist/src/project/discovery.js.map +1 -0
  135. package/dist/src/project/global.d.ts +3 -0
  136. package/dist/src/project/global.js +43 -0
  137. package/dist/src/project/global.js.map +1 -0
  138. package/dist/src/project/invalidation.d.ts +3 -0
  139. package/dist/src/project/invalidation.js +21 -0
  140. package/dist/src/project/invalidation.js.map +1 -0
  141. package/dist/src/project/link.d.ts +21 -0
  142. package/dist/src/project/link.js +152 -0
  143. package/dist/src/project/link.js.map +1 -0
  144. package/dist/src/project/manifest.d.ts +27 -0
  145. package/dist/src/project/manifest.js +378 -0
  146. package/dist/src/project/manifest.js.map +1 -0
  147. package/dist/src/project/pkg.d.ts +5 -0
  148. package/dist/src/project/pkg.js +94 -0
  149. package/dist/src/project/pkg.js.map +1 -0
  150. package/dist/src/project/recovery.d.ts +2 -0
  151. package/dist/src/project/recovery.js +82 -0
  152. package/dist/src/project/recovery.js.map +1 -0
  153. package/dist/src/project/workspaces.d.ts +4 -0
  154. package/dist/src/project/workspaces.js +59 -0
  155. package/dist/src/project/workspaces.js.map +1 -0
  156. package/dist/src/registry/account.d.ts +79 -0
  157. package/dist/src/registry/account.js +305 -0
  158. package/dist/src/registry/account.js.map +1 -0
  159. package/dist/src/registry/audit.d.ts +18 -0
  160. package/dist/src/registry/audit.js +74 -0
  161. package/dist/src/registry/audit.js.map +1 -0
  162. package/dist/src/registry/client.d.ts +19 -0
  163. package/dist/src/registry/client.js +102 -0
  164. package/dist/src/registry/client.js.map +1 -0
  165. package/dist/src/registry/configuration.d.ts +36 -0
  166. package/dist/src/registry/configuration.js +133 -0
  167. package/dist/src/registry/configuration.js.map +1 -0
  168. package/dist/src/registry/operations.d.ts +163 -0
  169. package/dist/src/registry/operations.js +502 -0
  170. package/dist/src/registry/operations.js.map +1 -0
  171. package/dist/src/registry/types.d.ts +30 -0
  172. package/dist/src/registry/types.js +2 -0
  173. package/dist/src/registry/types.js.map +1 -0
  174. package/dist/src/resolver/registry-resolver.d.ts +40 -0
  175. package/dist/src/resolver/registry-resolver.js +354 -0
  176. package/dist/src/resolver/registry-resolver.js.map +1 -0
  177. package/dist/src/resolver/source-provider.d.ts +17 -0
  178. package/dist/src/resolver/source-provider.js +336 -0
  179. package/dist/src/resolver/source-provider.js.map +1 -0
  180. package/dist/src/resolver/types.d.ts +28 -0
  181. package/dist/src/resolver/types.js +2 -0
  182. package/dist/src/resolver/types.js.map +1 -0
  183. package/dist/src/security/analyzer.d.ts +38 -0
  184. package/dist/src/security/analyzer.js +241 -0
  185. package/dist/src/security/analyzer.js.map +1 -0
  186. package/dist/src/security/audit.d.ts +19 -0
  187. package/dist/src/security/audit.js +61 -0
  188. package/dist/src/security/audit.js.map +1 -0
  189. package/dist/src/security/findings.d.ts +23 -0
  190. package/dist/src/security/findings.js +2 -0
  191. package/dist/src/security/findings.js.map +1 -0
  192. package/dist/src/security/integrity.d.ts +14 -0
  193. package/dist/src/security/integrity.js +40 -0
  194. package/dist/src/security/integrity.js.map +1 -0
  195. package/dist/src/security/policy.d.ts +16 -0
  196. package/dist/src/security/policy.js +24 -0
  197. package/dist/src/security/policy.js.map +1 -0
  198. package/dist/src/security/recent-release.d.ts +19 -0
  199. package/dist/src/security/recent-release.js +27 -0
  200. package/dist/src/security/recent-release.js.map +1 -0
  201. package/dist/src/security/script-runner.d.ts +13 -0
  202. package/dist/src/security/script-runner.js +73 -0
  203. package/dist/src/security/script-runner.js.map +1 -0
  204. package/package.json +45 -0
@@ -0,0 +1,94 @@
1
+ import { randomBytes } from "node:crypto";
2
+ import { readFile, rename, rm, writeFile } from "node:fs/promises";
3
+ import { join } from "node:path";
4
+ import { ManifestError, parseManifest } from "./manifest.js";
5
+ const forbidden = new Set(["__proto__", "prototype", "constructor"]);
6
+ function segments(path) {
7
+ if (!path || path.includes("\0"))
8
+ throw new ManifestError(`invalid package property path ${path}`);
9
+ const normalized = path.replace(/\[([0-9]+|"[^"]+"|'[^']+')\]/g, (_match, value) => `.${value.replace(/^['"]|['"]$/g, "")}`);
10
+ const result = normalized.split(".");
11
+ if (result.some((part) => !part || forbidden.has(part) || !/^[A-Za-z0-9_@/-]+$/.test(part)))
12
+ throw new ManifestError(`invalid package property path ${path}`);
13
+ return result;
14
+ }
15
+ function readAt(root, path) {
16
+ let value = root;
17
+ for (const part of segments(path)) {
18
+ if (typeof value !== "object" || value === null)
19
+ return undefined;
20
+ value = value[part];
21
+ }
22
+ return value;
23
+ }
24
+ function setAt(root, path, value) {
25
+ const parts = segments(path);
26
+ let current = root;
27
+ for (const part of parts.slice(0, -1)) {
28
+ const nested = current[part];
29
+ if (nested === undefined)
30
+ current[part] = {};
31
+ else if (typeof nested !== "object" || nested === null || Array.isArray(nested))
32
+ throw new ManifestError(`${path} crosses a non-object property`);
33
+ current = current[part];
34
+ }
35
+ current[parts.at(-1) ?? ""] = value;
36
+ }
37
+ function deleteAt(root, path) {
38
+ const parts = segments(path);
39
+ let current = root;
40
+ for (const part of parts.slice(0, -1)) {
41
+ const nested = current?.[part];
42
+ if (typeof nested !== "object" || nested === null || Array.isArray(nested))
43
+ return;
44
+ current = nested;
45
+ }
46
+ if (current)
47
+ delete current[parts.at(-1) ?? ""];
48
+ }
49
+ async function manifest(directory) {
50
+ const path = join(directory, "package.json");
51
+ const bytes = await readFile(path, "utf8");
52
+ parseManifest(bytes, path);
53
+ const value = JSON.parse(bytes);
54
+ if (typeof value !== "object" || value === null || Array.isArray(value))
55
+ throw new ManifestError(`${path}: root must be an object`);
56
+ return { path, value: value };
57
+ }
58
+ async function save(path, value) {
59
+ const temporary = `${path}.${process.pid}.${randomBytes(8).toString("hex")}.tmp`;
60
+ try {
61
+ await writeFile(temporary, `${JSON.stringify(value, null, 2)}\n`, { flag: "wx", mode: 0o644 });
62
+ parseManifest(await readFile(temporary, "utf8"), path);
63
+ await rename(temporary, path);
64
+ }
65
+ catch (error) {
66
+ await rm(temporary, { force: true });
67
+ throw error;
68
+ }
69
+ }
70
+ export async function packageProperties(options) {
71
+ const loaded = await manifest(options.directory);
72
+ if (options.action === "get") {
73
+ if (options.operands.length === 0)
74
+ return loaded.value;
75
+ if (options.operands.length === 1)
76
+ return readAt(loaded.value, options.operands[0] ?? "");
77
+ return Object.fromEntries(options.operands.map((path) => [path, readAt(loaded.value, path)]));
78
+ }
79
+ if (options.action === "set") {
80
+ for (const operand of options.operands) {
81
+ const separator = operand.indexOf("=");
82
+ if (separator <= 0)
83
+ throw new ManifestError(`pkg set requires key=value, received ${operand}`);
84
+ setAt(loaded.value, operand.slice(0, separator), operand.slice(separator + 1));
85
+ }
86
+ }
87
+ else {
88
+ for (const operand of options.operands)
89
+ deleteAt(loaded.value, operand);
90
+ }
91
+ await save(loaded.path, loaded.value);
92
+ return loaded.value;
93
+ }
94
+ //# sourceMappingURL=pkg.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pkg.js","sourceRoot":"","sources":["../../../src/project/pkg.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export declare function recoverProjectLayout(projectRoot: string): Promise<void>;
2
+ export declare function activateWithRecovery(projectRoot: string, prepared: string): Promise<void>;
@@ -0,0 +1,82 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { readFile, rename, rm, stat, writeFile } from "node:fs/promises";
3
+ import { basename, dirname, join } from "node:path";
4
+ const journalName = ".bnpm-layout-transaction.json";
5
+ function journalPath(projectRoot) { return join(projectRoot, journalName); }
6
+ function validate(projectRoot, journal) {
7
+ if (journal.version !== 1 || journal.target !== join(projectRoot, "node_modules"))
8
+ throw new Error("Invalid bnpm recovery journal");
9
+ if (dirname(journal.backup) !== projectRoot || !basename(journal.backup).startsWith(".bnpm-node_modules-backup-"))
10
+ throw new Error("Invalid bnpm recovery backup path");
11
+ if (dirname(dirname(journal.prepared)) !== projectRoot || basename(journal.prepared) !== "node_modules" || !basename(dirname(journal.prepared)).startsWith(".bnpm-install-"))
12
+ throw new Error("Invalid bnpm prepared layout path");
13
+ }
14
+ async function exists(path) { try {
15
+ await stat(path);
16
+ return true;
17
+ }
18
+ catch (error) {
19
+ if (error.code === "ENOENT")
20
+ return false;
21
+ throw error;
22
+ } }
23
+ async function save(path, journal) {
24
+ const temporary = `${path}.${randomUUID()}.tmp`;
25
+ try {
26
+ await writeFile(temporary, `${JSON.stringify(journal)}\n`, { flag: "wx", mode: 0o600 });
27
+ await rename(temporary, path);
28
+ }
29
+ finally {
30
+ await rm(temporary, { force: true });
31
+ }
32
+ }
33
+ export async function recoverProjectLayout(projectRoot) {
34
+ const path = journalPath(projectRoot);
35
+ let journal;
36
+ try {
37
+ journal = JSON.parse(await readFile(path, "utf8"));
38
+ }
39
+ catch (error) {
40
+ if (error.code === "ENOENT")
41
+ return;
42
+ throw error;
43
+ }
44
+ validate(projectRoot, journal);
45
+ const targetExists = await exists(journal.target);
46
+ const backupExists = await exists(journal.backup);
47
+ if (!targetExists && backupExists)
48
+ await rename(journal.backup, journal.target);
49
+ else if (targetExists && backupExists)
50
+ await rm(journal.backup, { recursive: true, force: true });
51
+ await rm(dirname(journal.prepared), { recursive: true, force: true });
52
+ await rm(path, { force: true });
53
+ }
54
+ export async function activateWithRecovery(projectRoot, prepared) {
55
+ await recoverProjectLayout(projectRoot);
56
+ const target = join(projectRoot, "node_modules");
57
+ const backup = join(projectRoot, `.bnpm-node_modules-backup-${randomUUID()}`);
58
+ const path = journalPath(projectRoot);
59
+ let journal = { version: 1, target, backup, prepared, phase: "prepared" };
60
+ await save(path, journal);
61
+ try {
62
+ try {
63
+ await rename(target, backup);
64
+ journal = { ...journal, phase: "backed-up" };
65
+ await save(path, journal);
66
+ }
67
+ catch (error) {
68
+ if (error.code !== "ENOENT")
69
+ throw error;
70
+ }
71
+ await rename(prepared, target);
72
+ journal = { ...journal, phase: "activated" };
73
+ await save(path, journal);
74
+ await rm(backup, { recursive: true, force: true });
75
+ await rm(path, { force: true });
76
+ }
77
+ catch (error) {
78
+ await recoverProjectLayout(projectRoot);
79
+ throw error;
80
+ }
81
+ }
82
+ //# sourceMappingURL=recovery.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recovery.js","sourceRoot":"","sources":["../../../src/project/recovery.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ export declare class WorkspaceError extends Error {
2
+ constructor(message: string);
3
+ }
4
+ export declare function discoverWorkspacePackages(root: string, patterns: readonly string[]): Promise<ReadonlyMap<string, string>>;
@@ -0,0 +1,59 @@
1
+ import { readdir, readFile, realpath } from "node:fs/promises";
2
+ import { join, relative, sep } from "node:path";
3
+ import { parseManifest } from "./manifest.js";
4
+ export class WorkspaceError extends Error {
5
+ constructor(message) {
6
+ super(`Workspace error: ${message}`);
7
+ this.name = "WorkspaceError";
8
+ }
9
+ }
10
+ function segmentMatches(pattern, value) {
11
+ const expression = new RegExp(`^${pattern.split("*").map((part) => part.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("[^/]*")}$`);
12
+ return expression.test(value);
13
+ }
14
+ function patternMatches(pattern, path) {
15
+ const patterns = pattern.replace(/\\/g, "/").replace(/^\.\//, "").replace(/\/$/, "").split("/");
16
+ const parts = path.replace(/\\/g, "/").split("/");
17
+ const match = (patternIndex, partIndex) => {
18
+ if (patternIndex === patterns.length)
19
+ return partIndex === parts.length;
20
+ if (patterns[patternIndex] === "**")
21
+ return match(patternIndex + 1, partIndex) || (partIndex < parts.length && match(patternIndex, partIndex + 1));
22
+ return partIndex < parts.length && segmentMatches(patterns[patternIndex] ?? "", parts[partIndex] ?? "") && match(patternIndex + 1, partIndex + 1);
23
+ };
24
+ return match(0, 0);
25
+ }
26
+ export async function discoverWorkspacePackages(root, patterns) {
27
+ const canonicalRoot = await realpath(root);
28
+ const packages = new Map();
29
+ let visited = 0;
30
+ const walk = async (directory) => {
31
+ visited += 1;
32
+ if (visited > 10_000)
33
+ throw new WorkspaceError("workspace discovery exceeded 10,000 directories");
34
+ const relativePath = relative(canonicalRoot, directory).split(sep).join("/");
35
+ if (relativePath && patterns.some((pattern) => patternMatches(pattern, relativePath))) {
36
+ try {
37
+ const manifestPath = join(directory, "package.json");
38
+ const manifest = parseManifest(await readFile(manifestPath, "utf8"), manifestPath);
39
+ if (!manifest.name)
40
+ throw new WorkspaceError(`${manifestPath} requires a package name`);
41
+ if (packages.has(manifest.name))
42
+ throw new WorkspaceError(`duplicate workspace package name ${manifest.name}`);
43
+ packages.set(manifest.name, await realpath(directory));
44
+ }
45
+ catch (error) {
46
+ if (error.code !== "ENOENT")
47
+ throw error;
48
+ }
49
+ }
50
+ for (const entry of await readdir(directory, { withFileTypes: true })) {
51
+ if (!entry.isDirectory() || ["node_modules", ".git", ".bnpm"].includes(entry.name))
52
+ continue;
53
+ await walk(join(directory, entry.name));
54
+ }
55
+ };
56
+ await walk(canonicalRoot);
57
+ return new Map([...packages].sort(([left], [right]) => left.localeCompare(right)));
58
+ }
59
+ //# sourceMappingURL=workspaces.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workspaces.js","sourceRoot":"","sources":["../../../src/project/workspaces.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,79 @@
1
+ import type { BnpmPaths } from "../config/paths.js";
2
+ export declare function registryWhoami(options: {
3
+ readonly paths: BnpmPaths;
4
+ readonly registry?: URL;
5
+ readonly fetch?: typeof globalThis.fetch;
6
+ readonly signal?: AbortSignal;
7
+ }): Promise<string>;
8
+ export declare function registryLogin(options: {
9
+ readonly paths: BnpmPaths;
10
+ readonly registry?: URL;
11
+ readonly fetch?: typeof globalThis.fetch;
12
+ readonly signal?: AbortSignal;
13
+ readonly open: (url: URL) => Promise<void>;
14
+ readonly announce?: (url: URL) => void;
15
+ readonly now?: () => number;
16
+ readonly wait?: (milliseconds: number, signal?: AbortSignal) => Promise<void>;
17
+ }): Promise<{
18
+ readonly registry: string;
19
+ readonly loginUrl: string;
20
+ }>;
21
+ export declare function registryLegacyLogin(options: {
22
+ readonly paths: BnpmPaths;
23
+ readonly username: string;
24
+ readonly password: string;
25
+ readonly email?: string;
26
+ readonly create?: boolean;
27
+ readonly registry?: URL;
28
+ readonly fetch?: typeof globalThis.fetch;
29
+ readonly signal?: AbortSignal;
30
+ readonly otp?: string;
31
+ }): Promise<{
32
+ readonly registry: string;
33
+ readonly username: string;
34
+ readonly created: boolean;
35
+ }>;
36
+ export declare function registryLogout(options: {
37
+ readonly paths: BnpmPaths;
38
+ readonly registry?: URL;
39
+ readonly fetch?: typeof globalThis.fetch;
40
+ readonly signal?: AbortSignal;
41
+ }): Promise<string>;
42
+ export interface RegistryToken {
43
+ readonly id: string;
44
+ readonly name?: string;
45
+ readonly created?: string;
46
+ readonly readonly: boolean;
47
+ readonly cidr: readonly string[];
48
+ }
49
+ export interface CreatedRegistryToken {
50
+ readonly token: string;
51
+ readonly name?: string;
52
+ readonly created?: string;
53
+ readonly expires?: string;
54
+ readonly readonly?: boolean;
55
+ readonly cidr?: readonly string[];
56
+ }
57
+ export declare function registryTokens(options: {
58
+ readonly paths: BnpmPaths;
59
+ readonly registry?: URL;
60
+ readonly fetch?: typeof globalThis.fetch;
61
+ readonly signal?: AbortSignal;
62
+ }): Promise<readonly RegistryToken[]>;
63
+ export declare function revokeRegistryTokens(options: {
64
+ readonly ids: readonly string[];
65
+ readonly paths: BnpmPaths;
66
+ readonly registry?: URL;
67
+ readonly fetch?: typeof globalThis.fetch;
68
+ readonly signal?: AbortSignal;
69
+ readonly otp?: string;
70
+ }): Promise<readonly string[]>;
71
+ export declare function createRegistryToken(options: {
72
+ readonly paths: BnpmPaths;
73
+ readonly password: string;
74
+ readonly body: Readonly<Record<string, unknown>>;
75
+ readonly registry?: URL;
76
+ readonly fetch?: typeof globalThis.fetch;
77
+ readonly signal?: AbortSignal;
78
+ readonly otp?: string;
79
+ }): Promise<CreatedRegistryToken>;
@@ -0,0 +1,305 @@
1
+ import { mkdir, readFile, rename, rm, writeFile } from "node:fs/promises";
2
+ import { dirname } from "node:path";
3
+ import { randomBytes } from "node:crypto";
4
+ import { RegistryError } from "./client.js";
5
+ import { loadRegistryConfiguration } from "./configuration.js";
6
+ const maxResponseBytes = 1024 * 1024;
7
+ function requestSignal(signal) {
8
+ const timeout = AbortSignal.timeout(30_000);
9
+ return signal === undefined ? timeout : AbortSignal.any([signal, timeout]);
10
+ }
11
+ async function json(response) {
12
+ if (!response.body)
13
+ throw new RegistryError("Registry returned an empty account response", response.status);
14
+ const chunks = [];
15
+ let size = 0;
16
+ for await (const value of response.body) {
17
+ const chunk = Buffer.from(value);
18
+ size += chunk.length;
19
+ if (size > maxResponseBytes)
20
+ throw new RegistryError("Registry account response exceeded the size limit", response.status);
21
+ chunks.push(chunk);
22
+ }
23
+ try {
24
+ const value = JSON.parse(Buffer.concat(chunks).toString("utf8"));
25
+ if (typeof value !== "object" || value === null || Array.isArray(value))
26
+ throw new Error("not an object");
27
+ return value;
28
+ }
29
+ catch {
30
+ throw new RegistryError("Registry returned invalid account JSON", response.status);
31
+ }
32
+ }
33
+ function secureUrl(value, purpose) {
34
+ let url;
35
+ try {
36
+ url = new URL(typeof value === "string" ? value : "");
37
+ }
38
+ catch {
39
+ throw new RegistryError(`Registry returned an invalid ${purpose} URL`);
40
+ }
41
+ if (url.protocol !== "https:" || url.username || url.password || url.hash)
42
+ throw new RegistryError(`Registry returned an unsafe ${purpose} URL`);
43
+ return url;
44
+ }
45
+ function credentialKey(registry) {
46
+ return `//${registry.host}${registry.pathname}:_authToken`;
47
+ }
48
+ async function npmrc(path) {
49
+ try {
50
+ return await readFile(path, "utf8");
51
+ }
52
+ catch (error) {
53
+ if (error.code === "ENOENT")
54
+ return "";
55
+ throw error;
56
+ }
57
+ }
58
+ async function updateCredential(path, registry, token) {
59
+ const key = credentialKey(registry);
60
+ const lines = (await npmrc(path)).split(/\r?\n/).filter((line) => {
61
+ const trimmed = line.trim();
62
+ return !trimmed.startsWith(`${key}=`) && !trimmed.startsWith(`${key.replace(":_authToken", ":_auth")}=`);
63
+ });
64
+ while (lines.at(-1) === "")
65
+ lines.pop();
66
+ if (token !== undefined)
67
+ lines.push(`${key}=${token}`);
68
+ const bytes = lines.length === 0 ? "" : `${lines.join("\n")}\n`;
69
+ await mkdir(dirname(path), { recursive: true, mode: 0o700 });
70
+ const temporary = `${path}.${process.pid}.${randomBytes(8).toString("hex")}.tmp`;
71
+ try {
72
+ await writeFile(temporary, bytes, { flag: "wx", mode: 0o600 });
73
+ await rename(temporary, path);
74
+ }
75
+ catch (error) {
76
+ await rm(temporary, { force: true });
77
+ throw error;
78
+ }
79
+ }
80
+ async function updateBasicCredential(path, registry, username, password) {
81
+ const key = credentialKey(registry).replace(":_authToken", ":_auth");
82
+ const tokenKey = key.replace(":_auth", ":_authToken");
83
+ const lines = (await npmrc(path)).split(/\r?\n/).filter((line) => { const trimmed = line.trim(); return !trimmed.startsWith(`${key}=`) && !trimmed.startsWith(`${tokenKey}=`); });
84
+ while (lines.at(-1) === "")
85
+ lines.pop();
86
+ lines.push(`${key}=${Buffer.from(`${username}:${password}`, "utf8").toString("base64")}`);
87
+ await mkdir(dirname(path), { recursive: true, mode: 0o700 });
88
+ const temporary = `${path}.${process.pid}.${randomBytes(8).toString("hex")}.tmp`;
89
+ try {
90
+ await writeFile(temporary, `${lines.join("\n")}\n`, { flag: "wx", mode: 0o600 });
91
+ await rename(temporary, path);
92
+ }
93
+ catch (error) {
94
+ await rm(temporary, { force: true });
95
+ throw error;
96
+ }
97
+ }
98
+ async function configuration(paths, registry) {
99
+ const loaded = await loadRegistryConfiguration({ userNpmrc: paths.userNpmrc, projectNpmrc: paths.projectNpmrc, ...(registry === undefined ? {} : { defaultRegistry: registry }) });
100
+ return { loaded, registry: registry ?? loaded.defaultRegistry };
101
+ }
102
+ export async function registryWhoami(options) {
103
+ const { loaded, registry } = await configuration(options.paths, options.registry);
104
+ const response = await (options.fetch ?? globalThis.fetch)(new URL("/-/whoami", registry), {
105
+ redirect: "error",
106
+ signal: requestSignal(options.signal),
107
+ headers: { accept: "application/json", ...loaded.headersFor(registry) },
108
+ });
109
+ if (!response.ok)
110
+ throw new RegistryError(`Registry identity request failed with ${response.status}`, response.status);
111
+ const body = await json(response);
112
+ if (typeof body.username !== "string" || body.username.length === 0)
113
+ throw new RegistryError("Registry identity response has no username");
114
+ return body.username;
115
+ }
116
+ export async function registryLogin(options) {
117
+ const { registry } = await configuration(options.paths, options.registry);
118
+ const request = options.fetch ?? globalThis.fetch;
119
+ const response = await request(new URL("/-/v1/login", registry), {
120
+ method: "POST",
121
+ redirect: "error",
122
+ signal: requestSignal(options.signal),
123
+ headers: { accept: "application/json", "content-type": "application/json", "npm-auth-type": "web" },
124
+ body: "{}",
125
+ });
126
+ if (!response.ok)
127
+ throw new RegistryError(`Registry web login is unavailable (${response.status})`, response.status);
128
+ const body = await json(response);
129
+ const loginUrl = secureUrl(body.loginUrl, "login");
130
+ const doneUrl = secureUrl(body.doneUrl, "login completion");
131
+ options.announce?.(loginUrl);
132
+ await options.open(loginUrl);
133
+ const now = options.now ?? Date.now;
134
+ const deadline = now() + 10 * 60_000;
135
+ const wait = options.wait ?? (async (milliseconds, signal) => {
136
+ await new Promise((resolvePromise, reject) => {
137
+ const timer = setTimeout(resolvePromise, milliseconds);
138
+ signal?.addEventListener("abort", () => { clearTimeout(timer); reject(signal.reason); }, { once: true });
139
+ });
140
+ });
141
+ while (now() < deadline) {
142
+ const check = await request(doneUrl, { redirect: "error", signal: requestSignal(options.signal), headers: { accept: "application/json" } });
143
+ const result = await json(check);
144
+ if (check.status === 200) {
145
+ if (typeof result.token !== "string" || result.token.length === 0)
146
+ throw new RegistryError("Registry completed login without a token");
147
+ await updateCredential(options.paths.userNpmrc, registry, result.token);
148
+ return { registry: registry.href, loginUrl: loginUrl.href };
149
+ }
150
+ if (check.status !== 202)
151
+ throw new RegistryError(`Registry login polling failed with ${check.status}`, check.status);
152
+ const retry = Number(check.headers.get("retry-after"));
153
+ await wait(Number.isFinite(retry) && retry > 0 ? Math.min(retry * 1000, 30_000) : 1000, options.signal);
154
+ }
155
+ throw new RegistryError("Registry login timed out");
156
+ }
157
+ export async function registryLegacyLogin(options) {
158
+ if (!/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/.test(options.username))
159
+ throw new RegistryError("Legacy login requires a valid username");
160
+ if (!options.password || options.password.length > 1024)
161
+ throw new RegistryError("Legacy login requires a bounded password");
162
+ if (options.create && (!options.email || options.email.length > 320 || !/^[^\s@]+@[^\s@]+$/.test(options.email)))
163
+ throw new RegistryError("Legacy adduser requires a valid email address");
164
+ const { registry } = await configuration(options.paths, options.registry);
165
+ const request = options.fetch ?? globalThis.fetch;
166
+ const endpoint = new URL(`/-/user/org.couchdb.user:${encodeURIComponent(options.username)}`, registry);
167
+ const base = { _id: `org.couchdb.user:${options.username}`, name: options.username, password: options.password, ...(options.email === undefined ? {} : { email: options.email }), type: "user", roles: [], date: new Date().toISOString() };
168
+ const headers = { accept: "application/json", "content-type": "application/json", ...(options.otp === undefined ? {} : { "npm-otp": options.otp }) };
169
+ const initial = await request(endpoint, { method: "PUT", redirect: "error", signal: requestSignal(options.signal), headers, body: JSON.stringify(base) });
170
+ let result;
171
+ if (initial.ok)
172
+ result = await json(initial);
173
+ else {
174
+ if (options.create || initial.status !== 409)
175
+ throw new RegistryError(`Legacy registry login failed with ${initial.status}`, initial.status);
176
+ const readUrl = new URL(endpoint);
177
+ readUrl.searchParams.set("write", "true");
178
+ const read = await request(readUrl, { redirect: "error", signal: requestSignal(options.signal), headers: { accept: "application/json" } });
179
+ if (!read.ok)
180
+ throw new RegistryError(`Legacy registry identity lookup failed with ${read.status}`, read.status);
181
+ const current = await json(read);
182
+ if (typeof current._rev !== "string" || current._rev.length === 0)
183
+ throw new RegistryError("Legacy registry identity has no revision");
184
+ const document = { ...current, ...base, roles: Array.isArray(current.roles) ? current.roles : [] };
185
+ const update = new URL(`${endpoint.pathname}/-rev/${encodeURIComponent(current._rev)}`, registry);
186
+ const authorization = `Basic ${Buffer.from(`${options.username}:${options.password}`, "utf8").toString("base64")}`;
187
+ const response = await request(update, { method: "PUT", redirect: "error", signal: requestSignal(options.signal), headers: { ...headers, authorization }, body: JSON.stringify(document) });
188
+ if (!response.ok)
189
+ throw new RegistryError(`Legacy registry authentication failed with ${response.status}`, response.status);
190
+ result = await json(response);
191
+ }
192
+ if (typeof result.token === "string" && result.token.length > 0)
193
+ await updateCredential(options.paths.userNpmrc, registry, result.token);
194
+ else
195
+ await updateBasicCredential(options.paths.userNpmrc, registry, options.username, options.password);
196
+ return { registry: registry.href, username: options.username, created: options.create === true };
197
+ }
198
+ export async function registryLogout(options) {
199
+ const { loaded, registry } = await configuration(options.paths, options.registry);
200
+ const authorization = loaded.headersFor(registry).authorization;
201
+ if (!authorization?.startsWith("Bearer "))
202
+ throw new RegistryError(`Not logged in to ${registry.href}`);
203
+ const token = authorization.slice("Bearer ".length);
204
+ const response = await (options.fetch ?? globalThis.fetch)(new URL(`/-/user/token/${encodeURIComponent(token)}`, registry), {
205
+ method: "DELETE",
206
+ redirect: "error",
207
+ signal: requestSignal(options.signal),
208
+ headers: { accept: "application/json", authorization },
209
+ });
210
+ if (!response.ok)
211
+ throw new RegistryError(`Registry logout failed with ${response.status}`, response.status);
212
+ await updateCredential(options.paths.userNpmrc, registry);
213
+ return registry.href;
214
+ }
215
+ async function tokenPage(url, loaded, request, signal) {
216
+ const response = await request(url, { redirect: "error", signal: requestSignal(signal), headers: { accept: "application/json", ...loaded.headersFor(url) } });
217
+ if (!response.ok)
218
+ throw new RegistryError(`Registry token request failed with ${response.status}`, response.status);
219
+ const body = await json(response);
220
+ if (!Array.isArray(body.objects))
221
+ throw new RegistryError("Registry token response has no objects array");
222
+ const tokens = body.objects.map((value) => {
223
+ if (typeof value !== "object" || value === null || Array.isArray(value) || typeof value.key !== "string")
224
+ throw new RegistryError("Registry returned an invalid token record");
225
+ const token = value;
226
+ if (token.name !== undefined && typeof token.name !== "string" || token.created !== undefined && typeof token.created !== "string" || token.readonly !== undefined && typeof token.readonly !== "boolean" || token.cidr_whitelist !== undefined && (!Array.isArray(token.cidr_whitelist) || token.cidr_whitelist.some((entry) => typeof entry !== "string")))
227
+ throw new RegistryError("Registry returned invalid token metadata");
228
+ return { key: token.key, ...(token.name === undefined ? {} : { name: token.name }), ...(token.created === undefined ? {} : { created: token.created }), readonly: token.readonly === true, cidr_whitelist: (token.cidr_whitelist ?? []) };
229
+ });
230
+ const nextValue = typeof body.urls === "object" && body.urls !== null && !Array.isArray(body.urls) ? body.urls.next : undefined;
231
+ if (nextValue !== undefined && typeof nextValue !== "string")
232
+ throw new RegistryError("Registry token pagination URL is invalid");
233
+ return { tokens, ...(nextValue === undefined || nextValue === "" ? {} : { next: new URL(nextValue, url) }) };
234
+ }
235
+ async function rawTokens(options) {
236
+ const { loaded, registry } = await configuration(options.paths, options.registry);
237
+ const request = options.fetch ?? globalThis.fetch;
238
+ const tokens = [];
239
+ let url = new URL("/-/npm/v1/tokens", registry);
240
+ for (let pages = 0; url !== undefined && pages < 100; pages += 1) {
241
+ if (url.protocol !== "https:" || url.origin !== registry.origin)
242
+ throw new RegistryError("Registry token pagination must remain on the HTTPS registry origin");
243
+ const page = await tokenPage(url, loaded, request, options.signal);
244
+ tokens.push(...page.tokens);
245
+ if (tokens.length > 10_000)
246
+ throw new RegistryError("Registry returned more than 10,000 tokens");
247
+ url = page.next;
248
+ }
249
+ if (url !== undefined)
250
+ throw new RegistryError("Registry token pagination exceeded 100 pages");
251
+ return { loaded, registry, tokens };
252
+ }
253
+ function tokenIds(tokens) {
254
+ const result = new Map();
255
+ for (const token of tokens) {
256
+ let id = token.key;
257
+ for (let length = 6; length < token.key.length; length += 1) {
258
+ const candidate = token.key.slice(0, length);
259
+ if (!tokens.some((other) => other !== token && other.key.startsWith(candidate))) {
260
+ id = candidate;
261
+ break;
262
+ }
263
+ }
264
+ result.set(token.key, id);
265
+ }
266
+ return result;
267
+ }
268
+ export async function registryTokens(options) {
269
+ const { tokens } = await rawTokens(options);
270
+ const ids = tokenIds(tokens);
271
+ return tokens.map((token) => ({ id: ids.get(token.key) ?? token.key, ...(token.name === undefined ? {} : { name: token.name }), ...(token.created === undefined ? {} : { created: token.created }), readonly: token.readonly === true, cidr: token.cidr_whitelist ?? [] }));
272
+ }
273
+ export async function revokeRegistryTokens(options) {
274
+ const { loaded, registry, tokens } = await rawTokens(options);
275
+ const keys = options.ids.map((id) => {
276
+ const matches = tokens.filter((token) => token.key.startsWith(id));
277
+ if (matches.length !== 1)
278
+ throw new RegistryError(matches.length === 0 ? `Unknown token id ${id}` : `Ambiguous token id ${id}`);
279
+ return matches[0]?.key;
280
+ });
281
+ for (const key of keys) {
282
+ const url = new URL(`/-/npm/v1/tokens/token/${encodeURIComponent(key)}`, registry);
283
+ const response = await (options.fetch ?? globalThis.fetch)(url, { method: "DELETE", redirect: "error", signal: requestSignal(options.signal), headers: { accept: "application/json", ...loaded.headersFor(url), ...(options.otp === undefined ? {} : { "npm-otp": options.otp }) } });
284
+ if (!response.ok)
285
+ throw new RegistryError(`Registry token revocation failed with ${response.status}`, response.status);
286
+ }
287
+ return options.ids;
288
+ }
289
+ export async function createRegistryToken(options) {
290
+ if (!options.password || options.password.length > 1024)
291
+ throw new RegistryError("A bounded account password is required to create a token");
292
+ const { loaded, registry } = await configuration(options.paths, options.registry);
293
+ const url = new URL("/-/npm/v1/tokens", registry);
294
+ const body = JSON.stringify({ ...options.body, password: options.password });
295
+ const response = await (options.fetch ?? globalThis.fetch)(url, { method: "POST", redirect: "error", signal: requestSignal(options.signal), headers: { accept: "application/json", "content-type": "application/json", "content-length": String(Buffer.byteLength(body)), ...loaded.headersFor(url), ...(options.otp === undefined ? {} : { "npm-otp": options.otp }) }, body });
296
+ if (!response.ok)
297
+ throw new RegistryError(`Registry token creation failed with ${response.status}`, response.status);
298
+ const result = await json(response);
299
+ if (typeof result.token !== "string" || result.token.length < 8 || result.token.length > 4096)
300
+ throw new RegistryError("Registry token creation response has no bounded token");
301
+ if ((result.name !== undefined && typeof result.name !== "string") || (result.created !== undefined && typeof result.created !== "string") || (result.expires !== undefined && typeof result.expires !== "string") || (result.readonly !== undefined && typeof result.readonly !== "boolean") || (result.cidr_whitelist !== undefined && (!Array.isArray(result.cidr_whitelist) || result.cidr_whitelist.some((value) => typeof value !== "string"))))
302
+ throw new RegistryError("Registry returned invalid created token metadata");
303
+ return { token: result.token, ...(result.name === undefined ? {} : { name: result.name }), ...(result.created === undefined ? {} : { created: result.created }), ...(result.expires === undefined ? {} : { expires: result.expires }), ...(result.readonly === undefined ? {} : { readonly: result.readonly }), ...(result.cidr_whitelist === undefined ? {} : { cidr: result.cidr_whitelist }) };
304
+ }
305
+ //# sourceMappingURL=account.js.map