@iann29/synapse 1.8.3 → 1.8.5
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/lib/commands/select.js +7 -1
- package/lib/commands/status.js +16 -1
- package/lib/doctor/renderer.js +48 -1
- package/lib/doctor/runner.js +27 -1
- package/lib/env-file.js +27 -12
- package/package.json +1 -1
package/lib/commands/select.js
CHANGED
|
@@ -222,8 +222,11 @@ show up in the menu).`,
|
|
|
222
222
|
if (prod) {
|
|
223
223
|
ctx.out.info(`Selected prod deployment ${colors.bold(prod.name)}.`);
|
|
224
224
|
} else {
|
|
225
|
+
// v1.8.5: surface the dashboard URL so the operator can click
|
|
226
|
+
// straight to "New deployment" instead of hunting for it.
|
|
227
|
+
const projectUrl = `${cfg.baseUrl}/teams/${encodeURIComponent(team.slug || team.id)}/${encodeURIComponent(project.id)}`;
|
|
225
228
|
ctx.out.warn(
|
|
226
|
-
|
|
229
|
+
`no prod deployment found. Create one at ${projectUrl} → "New deployment" → mark as PROD, then run \`synapse select\` again. \`synapse deploy\` fails until then.`,
|
|
227
230
|
);
|
|
228
231
|
}
|
|
229
232
|
if (process.env.CONVEX_DEPLOYMENT) {
|
|
@@ -234,6 +237,9 @@ show up in the menu).`,
|
|
|
234
237
|
ctx.out.info(
|
|
235
238
|
`\nNext step: run ${colors.bold("synapse dev")} (or ${colors.bold("npx convex dev")}) once in this directory to push your schema and watch for changes.`,
|
|
236
239
|
);
|
|
240
|
+
ctx.out.info(
|
|
241
|
+
`Or run ${colors.bold("synapse doctor")} first to confirm everything is healthy (add ${colors.bold("--fix")} to auto-clean .gitignore + file modes).`,
|
|
242
|
+
);
|
|
237
243
|
},
|
|
238
244
|
);
|
|
239
245
|
// Force ctx.projectConfig to re-read on next access (commands chained
|
package/lib/commands/status.js
CHANGED
|
@@ -146,12 +146,27 @@ Run \`synapse doctor\` for a deeper health check.`,
|
|
|
146
146
|
],
|
|
147
147
|
);
|
|
148
148
|
const broken = rows.filter((r) => r.urlForm === "host").length;
|
|
149
|
+
const hasProd = rows.some((r) => r.type === "prod");
|
|
150
|
+
const hasDev = rows.some((r) => r.type === "dev");
|
|
151
|
+
|
|
149
152
|
if (broken > 0) {
|
|
150
153
|
stdout.write("\n");
|
|
151
154
|
ctx.out.warn(
|
|
152
|
-
`${broken} deployment${broken > 1 ? "s" : ""} not browser-reachable —
|
|
155
|
+
`${broken} deployment${broken > 1 ? "s" : ""} not browser-reachable — instance admin can enable wildcard at ${baseUrl}/admin/host-domain, or add a custom domain per deployment.`,
|
|
153
156
|
);
|
|
154
157
|
}
|
|
158
|
+
// v1.8.5 hints: when nothing's broken, surface the next-step
|
|
159
|
+
// bread-crumb so first-time operators know what to do next.
|
|
160
|
+
if (broken === 0 && rows.length > 0) {
|
|
161
|
+
stdout.write("\n");
|
|
162
|
+
if (!hasProd && hasDev) {
|
|
163
|
+
ctx.out.info(
|
|
164
|
+
`No prod deployment yet. Create one at ${baseUrl}/teams/<team>/${projectId} → "New deployment" → PROD, then \`synapse select\` again.`,
|
|
165
|
+
);
|
|
166
|
+
} else if (hasDev) {
|
|
167
|
+
ctx.out.info("Run `synapse dev` to start a Convex dev session in this directory.");
|
|
168
|
+
}
|
|
169
|
+
}
|
|
155
170
|
},
|
|
156
171
|
);
|
|
157
172
|
},
|
package/lib/doctor/renderer.js
CHANGED
|
@@ -33,6 +33,18 @@ function renderHeader(report, write) {
|
|
|
33
33
|
write(parts.join(" ") + "\n\n");
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
+
// v1.8.5: inline-tag formatter for the fix hint. Mirrors the cyan-on-dim
|
|
37
|
+
// rest of the remediation line; falls back to plain text if colors.cyan
|
|
38
|
+
// isn't defined (the `colors` shim is a no-op on non-tty stdouts).
|
|
39
|
+
function fixableTag(r) {
|
|
40
|
+
if (!r.fixable) return "";
|
|
41
|
+
if (r.fixedBy) return ""; // already fixed; no nudge needed
|
|
42
|
+
const text = r.fixable === "auto"
|
|
43
|
+
? " [run `synapse doctor --fix`]"
|
|
44
|
+
: " [run `synapse doctor --fix --yes`]";
|
|
45
|
+
return colors.cyan ? colors.cyan(text) : text;
|
|
46
|
+
}
|
|
47
|
+
|
|
36
48
|
function renderCheck(r, { verbose }, write) {
|
|
37
49
|
const sym = TONE[r.status](SYMBOL[r.status]);
|
|
38
50
|
write(` ${sym} ${r.title}`);
|
|
@@ -40,7 +52,9 @@ function renderCheck(r, { verbose }, write) {
|
|
|
40
52
|
if (r.fixedBy) write(` ${colors.cyan ? colors.cyan("↻ " + r.fixedBy) : "↻ " + r.fixedBy}`);
|
|
41
53
|
write("\n");
|
|
42
54
|
if (r.remediation && (r.status === "issue" || r.status === "warn")) {
|
|
43
|
-
write(colors.dim(` → ${r.remediation}
|
|
55
|
+
write(colors.dim(` → ${r.remediation}`));
|
|
56
|
+
write(fixableTag(r));
|
|
57
|
+
write("\n");
|
|
44
58
|
}
|
|
45
59
|
if (verbose && r.detail) {
|
|
46
60
|
write(colors.dim(` ${r.detail.replace(/\n/g, "\n ")}\n`));
|
|
@@ -88,6 +102,39 @@ function renderReport(report, { stdout, verbose = false } = {}) {
|
|
|
88
102
|
if (report.exitCode !== 0) {
|
|
89
103
|
write(` ${colors.dim("exit " + report.exitCode)}\n`);
|
|
90
104
|
}
|
|
105
|
+
|
|
106
|
+
// v1.8.5: actionable tip footer. Surfaces the right `--fix` invocation
|
|
107
|
+
// for whichever class of fixable issues remains, so an operator who
|
|
108
|
+
// just sees a wall of `! ...` warnings knows exactly which command
|
|
109
|
+
// would clean them up — no need to memorise the auto-vs-prompt split.
|
|
110
|
+
const autoCount = t.fixableAuto || 0;
|
|
111
|
+
const promptCount = t.fixablePrompt || 0;
|
|
112
|
+
const tip = (msg) =>
|
|
113
|
+
` ${colors.cyan ? colors.cyan("💡 Tip:") : "Tip:"} ${msg}\n`;
|
|
114
|
+
if (autoCount + promptCount > 0) {
|
|
115
|
+
write("\n");
|
|
116
|
+
if (autoCount > 0 && promptCount > 0) {
|
|
117
|
+
write(tip(
|
|
118
|
+
`${autoCount} auto-fixable + ${promptCount} prompt-fixable — run \`synapse doctor --fix --yes\` to apply both.`,
|
|
119
|
+
));
|
|
120
|
+
} else if (autoCount > 0) {
|
|
121
|
+
write(tip(
|
|
122
|
+
`${autoCount} ${autoCount === 1 ? "issue is" : "issues are"} auto-fixable — run \`synapse doctor --fix\`.`,
|
|
123
|
+
));
|
|
124
|
+
} else {
|
|
125
|
+
write(tip(
|
|
126
|
+
`${promptCount} ${promptCount === 1 ? "issue needs" : "issues need"} confirmation — run \`synapse doctor --fix --yes\`.`,
|
|
127
|
+
));
|
|
128
|
+
}
|
|
129
|
+
} else if (t.warn + t.issue === 0 && t.ok > 0) {
|
|
130
|
+
// Clean bill of health → point at the next step. Operators who run
|
|
131
|
+
// doctor first thing after `synapse select` shouldn't have to wonder
|
|
132
|
+
// what's next.
|
|
133
|
+
write("\n");
|
|
134
|
+
write(tip(
|
|
135
|
+
"everything healthy — run `synapse dev` to start working, or `synapse status` to list deployments.",
|
|
136
|
+
));
|
|
137
|
+
}
|
|
91
138
|
}
|
|
92
139
|
|
|
93
140
|
module.exports = { renderReport };
|
package/lib/doctor/runner.js
CHANGED
|
@@ -53,10 +53,21 @@ async function runChecks(checks, ctx) {
|
|
|
53
53
|
continue;
|
|
54
54
|
}
|
|
55
55
|
const result = await check.run(ctx);
|
|
56
|
+
// `fixable` carries the autoFix kind into the result so the
|
|
57
|
+
// renderer (and --json consumers) can surface a "this is
|
|
58
|
+
// fixable, here's the right flag" hint without re-importing
|
|
59
|
+
// the check catalog. Skipped checks aren't marked fixable —
|
|
60
|
+
// the fix wouldn't have ctx to operate on anyway.
|
|
61
|
+
const fixable =
|
|
62
|
+
typeof check.fix === "function" &&
|
|
63
|
+
(check.autoFix === "auto" || check.autoFix === "prompt")
|
|
64
|
+
? check.autoFix
|
|
65
|
+
: null;
|
|
56
66
|
resultsById.set(id, {
|
|
57
67
|
id,
|
|
58
68
|
category: check.category,
|
|
59
69
|
title: check.title,
|
|
70
|
+
fixable,
|
|
60
71
|
...result,
|
|
61
72
|
});
|
|
62
73
|
pending.delete(id);
|
|
@@ -83,13 +94,28 @@ async function runChecks(checks, ctx) {
|
|
|
83
94
|
}
|
|
84
95
|
|
|
85
96
|
function totalize(results) {
|
|
86
|
-
const t = {
|
|
97
|
+
const t = {
|
|
98
|
+
ok: 0,
|
|
99
|
+
warn: 0,
|
|
100
|
+
issue: 0,
|
|
101
|
+
skipped: 0,
|
|
102
|
+
fixed: 0,
|
|
103
|
+
// v1.8.5: count of results still in warn/issue that have a
|
|
104
|
+
// registered fix. Splits auto vs prompt so the renderer can show
|
|
105
|
+
// the right CLI hint at the bottom.
|
|
106
|
+
fixableAuto: 0,
|
|
107
|
+
fixablePrompt: 0,
|
|
108
|
+
};
|
|
87
109
|
for (const r of results) {
|
|
88
110
|
if (r.fixedBy) t.fixed += 1;
|
|
89
111
|
if (r.status === "ok") t.ok += 1;
|
|
90
112
|
else if (r.status === "warn") t.warn += 1;
|
|
91
113
|
else if (r.status === "issue") t.issue += 1;
|
|
92
114
|
else if (r.status === "skipped") t.skipped += 1;
|
|
115
|
+
if ((r.status === "warn" || r.status === "issue") && !r.fixedBy) {
|
|
116
|
+
if (r.fixable === "auto") t.fixableAuto += 1;
|
|
117
|
+
else if (r.fixable === "prompt") t.fixablePrompt += 1;
|
|
118
|
+
}
|
|
93
119
|
}
|
|
94
120
|
return t;
|
|
95
121
|
}
|
package/lib/env-file.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
// Writes / reads `.env.local` for Synapse-linked projects.
|
|
2
2
|
//
|
|
3
|
-
// As of v1.8.
|
|
4
|
-
// tutorials
|
|
3
|
+
// As of v1.8.4 the file is drop-in compatible with Convex Cloud
|
|
4
|
+
// tutorials (cloud-style NEXT_PUBLIC_* vars) while keeping CONVEX_DEPLOYMENT
|
|
5
|
+
// commented so the Convex CLI's mode picker doesn't see both modes active:
|
|
5
6
|
//
|
|
6
7
|
// # Convex (Synapse self-hosted — drop-in compatible with Cloud tutorials)
|
|
7
8
|
// NEXT_PUBLIC_CONVEX_URL="https://<name>.app.synapsepanel.com"
|
|
8
9
|
// NEXT_PUBLIC_CONVEX_SITE_URL="https://<name>.app.synapsepanel.com"
|
|
9
|
-
// CONVEX_DEPLOYMENT=dev:<name> # team: <team>, project: <project>
|
|
10
|
+
// # CONVEX_DEPLOYMENT=dev:<name> # team: <team>, project: <project>
|
|
10
11
|
//
|
|
11
12
|
// # Self-hosted auth (Synapse cannot use Cloud account session)
|
|
12
13
|
// CONVEX_SELF_HOSTED_URL="https://<name>.app.synapsepanel.com"
|
|
@@ -17,10 +18,16 @@
|
|
|
17
18
|
// origins. In self-hosted both point at the same URL; the backend
|
|
18
19
|
// container routes API calls and HTTP actions on the same host.
|
|
19
20
|
//
|
|
20
|
-
// CONVEX_DEPLOYMENT
|
|
21
|
-
//
|
|
22
|
-
//
|
|
23
|
-
//
|
|
21
|
+
// CONVEX_DEPLOYMENT stays commented in self-hosted mode. The Convex
|
|
22
|
+
// CLI errors with "CONVEX_DEPLOYMENT must not be set when
|
|
23
|
+
// CONVEX_SELF_HOSTED_URL and CONVEX_SELF_HOSTED_ADMIN_KEY are set" if
|
|
24
|
+
// both are present at runtime. Our wrapper deletes CONVEX_DEPLOYMENT
|
|
25
|
+
// from process.env before spawning npx convex, but the convex CLI
|
|
26
|
+
// invokes dotenv.config() on its own and re-reads .env.local, which
|
|
27
|
+
// would resurrect the var. Keeping the line commented preserves the
|
|
28
|
+
// human-readable context (team/project, deployment kind) without
|
|
29
|
+
// triggering the conflict. v1.8.2-v1.8.3 wrote it active — that
|
|
30
|
+
// regression broke `synapse dev` and is fixed in v1.8.4.
|
|
24
31
|
|
|
25
32
|
const fs = require("node:fs");
|
|
26
33
|
const path = require("node:path");
|
|
@@ -115,10 +122,18 @@ function readProjectEnv(projectDir) {
|
|
|
115
122
|
return parseEnvContent(fs.readFileSync(file, "utf8"));
|
|
116
123
|
}
|
|
117
124
|
|
|
118
|
-
// Build the
|
|
119
|
-
// don't have enough info
|
|
120
|
-
//
|
|
121
|
-
//
|
|
125
|
+
// Build the (commented) CONVEX_DEPLOYMENT line. Returns null when we
|
|
126
|
+
// don't have enough info (legacy caller with no deploymentName).
|
|
127
|
+
//
|
|
128
|
+
// REGRESSION FIX v1.8.4: this line MUST be commented. Convex CLI rejects
|
|
129
|
+
// the combination of CONVEX_DEPLOYMENT + CONVEX_SELF_HOSTED_URL/ADMIN_KEY
|
|
130
|
+
// — the two modes are mutually exclusive. v1.8.2 wrote it ACTIVE, which
|
|
131
|
+
// broke `npx convex` (and therefore `synapse dev`) because Convex's
|
|
132
|
+
// internal dotenv re-loads .env.local after our wrapper deletes the
|
|
133
|
+
// var from the child env. Keeping it commented preserves the human
|
|
134
|
+
// context (team/project label) while never showing up in the parsed
|
|
135
|
+
// env. See https://github.com/Iann29/convex-synapse/issues for the
|
|
136
|
+
// real-world report from the user's session.
|
|
122
137
|
function buildDeploymentLine({ deploymentName, target, teamName, projectName, teamSlug, projectSlug }) {
|
|
123
138
|
if (!deploymentName) return null;
|
|
124
139
|
const safeTarget = target === "prod" ? "prod" : "dev";
|
|
@@ -128,7 +143,7 @@ function buildDeploymentLine({ deploymentName, target, teamName, projectName, te
|
|
|
128
143
|
if (teamLabel) parts.push(`team: ${teamLabel}`);
|
|
129
144
|
if (projectLabel) parts.push(`project: ${projectLabel}`);
|
|
130
145
|
const comment = parts.length > 0 ? ` # ${parts.join(", ")}` : "";
|
|
131
|
-
return
|
|
146
|
+
return `# ${CONVEX_DEPLOYMENT}=${safeTarget}:${deploymentName}${comment}`;
|
|
132
147
|
}
|
|
133
148
|
|
|
134
149
|
function updateEnvContent(content, opts) {
|