@lpm-registry/cli 0.45.0 → 0.47.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.
@@ -1,8 +1,8 @@
1
- import fs from "node:fs"
2
- import path from "node:path"
3
- import { request } from "../api.js"
4
- import { getRegistryUrl, getToken } from "../config.js"
5
- import { createSpinner, log, printHeader } from "../ui.js"
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { request } from "../api.js";
4
+ import { getRegistryUrl, getToken } from "../config.js";
5
+ import { createSpinner, log, printHeader } from "../ui.js";
6
6
 
7
7
  /**
8
8
  * Generate a read-only .npmrc token for local development.
@@ -10,169 +10,169 @@ import { createSpinner, log, printHeader } from "../ui.js"
10
10
  * Also ensures .npmrc is in .gitignore to prevent accidental commits.
11
11
  */
12
12
  export async function npmrc(options) {
13
- printHeader()
14
-
15
- const token = await getToken()
16
- if (!token) {
17
- log.error(
18
- 'You must be logged in to generate an .npmrc token. Run "lpm login" first.',
19
- )
20
- process.exit(1)
21
- }
22
-
23
- const projectRoot = process.cwd()
24
- const npmrcPath = path.join(projectRoot, ".npmrc")
25
- const gitignorePath = path.join(projectRoot, ".gitignore")
26
-
27
- // Check for package.json
28
- const pkgJsonPath = path.join(projectRoot, "package.json")
29
- if (!fs.existsSync(pkgJsonPath)) {
30
- log.warn("No package.json found. Run this command in your project root.")
31
- }
32
-
33
- // Derive project name for the token label
34
- let projectName = "project"
35
- if (fs.existsSync(pkgJsonPath)) {
36
- try {
37
- const pkg = JSON.parse(fs.readFileSync(pkgJsonPath, "utf8"))
38
- if (pkg.name) {
39
- projectName = pkg.name.replace(/[^a-zA-Z0-9._-]/g, "-")
40
- }
41
- } catch {
42
- // Invalid package.json — use default name
43
- }
44
- }
45
-
46
- const expiryDays = parseInt(options?.days, 10) || 30
47
- const spinner = createSpinner("Creating read-only token...").start()
48
-
49
- // Call server to create a scoped read-only token
50
- let readToken
51
- let expiresAt
52
- try {
53
- const tokenName = `npmrc-${projectName}-${new Date().toISOString().slice(0, 10)}`
54
- const response = await request("/-/token/create", {
55
- method: "POST",
56
- body: JSON.stringify({
57
- scope: "read",
58
- name: tokenName,
59
- expiryDays,
60
- }),
61
- headers: { "Content-Type": "application/json" },
62
- })
63
-
64
- if (!response.ok) {
65
- const text = await response.text()
66
- throw new Error(`Token creation failed: ${response.status} ${text}`)
67
- }
68
-
69
- const data = await response.json()
70
- readToken = data.token
71
- expiresAt = data.expiresAt
72
- spinner.succeed("Read-only token created.")
73
- } catch (error) {
74
- spinner.fail(`Failed to create token: ${error.message}`)
75
- process.exit(1)
76
- }
77
-
78
- // Build .npmrc content
79
- const registryUrl = getRegistryUrl()
80
- const fullRegistryUrl = registryUrl.endsWith("/api/registry")
81
- ? registryUrl
82
- : `${registryUrl}/api/registry`
83
- const registryHost = fullRegistryUrl.replace(/^https?:/, "")
84
-
85
- // Default: scoped (only @lpm.dev packages through lpm.dev).
86
- // --proxy: route all npm traffic through lpm.dev (Pro/Org feature).
87
- let useScoped = !options?.proxy
88
-
89
- // Read existing .npmrc and check for custom default registry
90
- let npmrcContent = ""
91
- if (fs.existsSync(npmrcPath)) {
92
- const existingContent = fs.readFileSync(npmrcPath, "utf8")
93
-
94
- // Detect custom default registry (not npmjs.org and not lpm.dev)
95
- if (!useScoped) {
96
- const registryLine = existingContent
97
- .split("\n")
98
- .find(
99
- line =>
100
- line.match(/^registry=/) &&
101
- !line.includes("registry.npmjs.org") &&
102
- !line.includes("lpm.dev"),
103
- )
104
- if (registryLine) {
105
- log.warn(`Found existing default registry: ${registryLine.trim()}`)
106
- log.warn("Using --scoped mode to avoid overriding it.")
107
- log.warn(
108
- "To use LPM as your default registry, remove the existing registry= line first.",
109
- )
110
- useScoped = true
111
- }
112
- }
113
-
114
- // Remove old LPM config lines (both scoped and unscoped formats)
115
- npmrcContent = existingContent
116
- .split("\n")
117
- .filter(line => {
118
- return (
119
- !line.includes("@lpm.dev:registry") &&
120
- !line.match(/^registry=.*lpm\.dev/) &&
121
- !line.includes("lpm.dev/api/registry/:_authToken") &&
122
- !line.includes("_authToken=lpm_") &&
123
- // biome-ignore lint/suspicious/noTemplateCurlyInString: matching literal npm token placeholder
124
- !line.includes("_authToken=${LPM_TOKEN}") &&
125
- !line.includes("# LPM Registry")
126
- )
127
- })
128
- .join("\n")
129
- .trim()
130
- }
131
-
132
- const registryLine = useScoped
133
- ? `@lpm.dev:registry=${fullRegistryUrl}`
134
- : `registry=${fullRegistryUrl}`
135
-
136
- const lpmConfig = `# LPM Registry (generated by lpm npmrc — do not commit)
13
+ printHeader();
14
+
15
+ const token = await getToken();
16
+ if (!token) {
17
+ log.error(
18
+ 'You must be logged in to generate an .npmrc token. Run "lpm login" first.',
19
+ );
20
+ process.exit(1);
21
+ }
22
+
23
+ const projectRoot = process.cwd();
24
+ const npmrcPath = path.join(projectRoot, ".npmrc");
25
+ const gitignorePath = path.join(projectRoot, ".gitignore");
26
+
27
+ // Check for package.json
28
+ const pkgJsonPath = path.join(projectRoot, "package.json");
29
+ if (!fs.existsSync(pkgJsonPath)) {
30
+ log.warn("No package.json found. Run this command in your project root.");
31
+ }
32
+
33
+ // Derive project name for the token label
34
+ let projectName = "project";
35
+ if (fs.existsSync(pkgJsonPath)) {
36
+ try {
37
+ const pkg = JSON.parse(fs.readFileSync(pkgJsonPath, "utf8"));
38
+ if (pkg.name) {
39
+ projectName = pkg.name.replace(/[^a-zA-Z0-9._-]/g, "-");
40
+ }
41
+ } catch {
42
+ // Invalid package.json — use default name
43
+ }
44
+ }
45
+
46
+ const expiryDays = parseInt(options?.days, 10) || 30;
47
+ const spinner = createSpinner("Creating read-only token...").start();
48
+
49
+ // Call server to create a scoped read-only token
50
+ let readToken;
51
+ let expiresAt;
52
+ try {
53
+ const tokenName = `npmrc-${projectName}-${new Date().toISOString().slice(0, 10)}`;
54
+ const response = await request("/-/token/create", {
55
+ method: "POST",
56
+ body: JSON.stringify({
57
+ scope: "read",
58
+ name: tokenName,
59
+ expiryDays,
60
+ }),
61
+ headers: { "Content-Type": "application/json" },
62
+ });
63
+
64
+ if (!response.ok) {
65
+ const text = await response.text();
66
+ throw new Error(`Token creation failed: ${response.status} ${text}`);
67
+ }
68
+
69
+ const data = await response.json();
70
+ readToken = data.token;
71
+ expiresAt = data.expiresAt;
72
+ spinner.succeed("Read-only token created.");
73
+ } catch (error) {
74
+ spinner.fail(`Failed to create token: ${error.message}`);
75
+ process.exit(1);
76
+ }
77
+
78
+ // Build .npmrc content
79
+ const registryUrl = getRegistryUrl();
80
+ const fullRegistryUrl = registryUrl.endsWith("/api/registry")
81
+ ? registryUrl
82
+ : `${registryUrl}/api/registry`;
83
+ const registryHost = fullRegistryUrl.replace(/^https?:/, "");
84
+
85
+ // Default: scoped (only @lpm.dev packages through lpm.dev).
86
+ // --proxy: route all npm traffic through lpm.dev (Pro/Org feature).
87
+ let useScoped = !options?.proxy;
88
+
89
+ // Read existing .npmrc and check for custom default registry
90
+ let npmrcContent = "";
91
+ if (fs.existsSync(npmrcPath)) {
92
+ const existingContent = fs.readFileSync(npmrcPath, "utf8");
93
+
94
+ // Detect custom default registry (not npmjs.org and not lpm.dev)
95
+ if (!useScoped) {
96
+ const registryLine = existingContent
97
+ .split("\n")
98
+ .find(
99
+ (line) =>
100
+ line.match(/^registry=/) &&
101
+ !line.includes("registry.npmjs.org") &&
102
+ !line.includes("lpm.dev"),
103
+ );
104
+ if (registryLine) {
105
+ log.warn(`Found existing default registry: ${registryLine.trim()}`);
106
+ log.warn("Using --scoped mode to avoid overriding it.");
107
+ log.warn(
108
+ "To use LPM as your default registry, remove the existing registry= line first.",
109
+ );
110
+ useScoped = true;
111
+ }
112
+ }
113
+
114
+ // Remove old LPM config lines (both scoped and unscoped formats)
115
+ npmrcContent = existingContent
116
+ .split("\n")
117
+ .filter((line) => {
118
+ return (
119
+ !line.includes("@lpm.dev:registry") &&
120
+ !line.match(/^registry=.*lpm\.dev/) &&
121
+ !line.includes("lpm.dev/api/registry/:_authToken") &&
122
+ !line.includes("_authToken=lpm_") &&
123
+ // biome-ignore lint/suspicious/noTemplateCurlyInString: matching literal npm token placeholder
124
+ !line.includes("_authToken=${LPM_TOKEN}") &&
125
+ !line.includes("# LPM Registry")
126
+ );
127
+ })
128
+ .join("\n")
129
+ .trim();
130
+ }
131
+
132
+ const registryLine = useScoped
133
+ ? `@lpm.dev:registry=${fullRegistryUrl}`
134
+ : `registry=${fullRegistryUrl}`;
135
+
136
+ const lpmConfig = `# LPM Registry (generated by lpm setup local — do not commit)
137
137
  ${registryLine}
138
- ${registryHost}/:_authToken=${readToken}`
139
-
140
- npmrcContent = npmrcContent ? `${npmrcContent}\n\n${lpmConfig}` : lpmConfig
141
- fs.writeFileSync(npmrcPath, `${npmrcContent}\n`)
142
-
143
- // Ensure .npmrc is in .gitignore
144
- let gitignoreUpdated = false
145
- if (fs.existsSync(gitignorePath)) {
146
- const gitignore = fs.readFileSync(gitignorePath, "utf8")
147
- if (!gitignore.split("\n").some(line => line.trim() === ".npmrc")) {
148
- fs.writeFileSync(gitignorePath, `${gitignore.trimEnd()}\n.npmrc\n`)
149
- gitignoreUpdated = true
150
- }
151
- } else {
152
- fs.writeFileSync(gitignorePath, ".npmrc\n")
153
- gitignoreUpdated = true
154
- }
155
-
156
- // Output
157
- console.log("")
158
- log.success(".npmrc configured with read-only LPM token.")
159
- if (gitignoreUpdated) {
160
- log.info(".npmrc added to .gitignore to prevent token leaks.")
161
- }
162
-
163
- if (useScoped) {
164
- log.info("Only @lpm.dev packages will route through lpm.dev.")
165
- } else {
166
- log.info("All packages (LPM + npm) will route through lpm.dev.")
167
- log.info("Note: First install may update resolved URLs in your lockfile.")
168
- }
169
-
170
- const expiryDate = new Date(expiresAt).toLocaleDateString("en-US", {
171
- year: "numeric",
172
- month: "long",
173
- day: "numeric",
174
- })
175
- log.info(`Token expires: ${expiryDate}`)
176
- log.info("Run `lpm npmrc` again to refresh when expired.")
177
- console.log("")
138
+ ${registryHost}/:_authToken=${readToken}`;
139
+
140
+ npmrcContent = npmrcContent ? `${npmrcContent}\n\n${lpmConfig}` : lpmConfig;
141
+ fs.writeFileSync(npmrcPath, `${npmrcContent}\n`);
142
+
143
+ // Ensure .npmrc is in .gitignore
144
+ let gitignoreUpdated = false;
145
+ if (fs.existsSync(gitignorePath)) {
146
+ const gitignore = fs.readFileSync(gitignorePath, "utf8");
147
+ if (!gitignore.split("\n").some((line) => line.trim() === ".npmrc")) {
148
+ fs.writeFileSync(gitignorePath, `${gitignore.trimEnd()}\n.npmrc\n`);
149
+ gitignoreUpdated = true;
150
+ }
151
+ } else {
152
+ fs.writeFileSync(gitignorePath, ".npmrc\n");
153
+ gitignoreUpdated = true;
154
+ }
155
+
156
+ // Output
157
+ console.log("");
158
+ log.success(".npmrc configured with read-only LPM token.");
159
+ if (gitignoreUpdated) {
160
+ log.info(".npmrc added to .gitignore to prevent token leaks.");
161
+ }
162
+
163
+ if (useScoped) {
164
+ log.info("Only @lpm.dev packages will route through lpm.dev.");
165
+ } else {
166
+ log.info("All packages (LPM + npm) will route through lpm.dev.");
167
+ log.info("Note: First install may update resolved URLs in your lockfile.");
168
+ }
169
+
170
+ const expiryDate = new Date(expiresAt).toLocaleDateString("en-US", {
171
+ year: "numeric",
172
+ month: "long",
173
+ day: "numeric",
174
+ });
175
+ log.info(`Token expires: ${expiryDate}`);
176
+ log.info("Run `lpm setup local` again to refresh when expired.");
177
+ console.log("");
178
178
  }