neon 2.47.0 → 3.1.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.
- package/README.md +54 -0
- package/dist/_shared/auth_selection.js +76 -79
- package/dist/_shared/credentials.js +166 -187
- package/dist/_shared/env-core/env.js +395 -0
- package/dist/_shared/env-core/reuse-secrets.js +179 -0
- package/dist/_shared/paths.js +129 -126
- package/dist/_shared/profiles.js +192 -242
- package/dist/_shared/secure_file.js +36 -38
- package/dist/_virtual/_rolldown/runtime.js +13 -0
- package/dist/analytics.js +163 -207
- package/dist/api.js +815 -758
- package/dist/auth.js +121 -141
- package/dist/auth_context.js +39 -53
- package/dist/cli.js +4 -7
- package/dist/commands/api.js +220 -250
- package/dist/commands/api_keys.js +251 -314
- package/dist/commands/auth.js +283 -328
- package/dist/commands/bootstrap.js +372 -437
- package/dist/commands/branches.js +304 -455
- package/dist/commands/bucket.js +374 -514
- package/dist/commands/checkout.js +213 -298
- package/dist/commands/config.js +575 -658
- package/dist/commands/connection_string.js +137 -165
- package/dist/commands/data_api.js +238 -260
- package/dist/commands/databases.js +67 -76
- package/dist/commands/deploy.js +31 -25
- package/dist/commands/dev.js +642 -681
- package/dist/commands/diff.js +156 -200
- package/dist/commands/env.js +243 -303
- package/dist/commands/functions.js +275 -341
- package/dist/commands/index.js +70 -65
- package/dist/commands/init.js +84 -119
- package/dist/commands/inspect.js +55 -55
- package/dist/commands/ip_allow.js +88 -120
- package/dist/commands/link.js +874 -1019
- package/dist/commands/logs.js +291 -0
- package/dist/commands/neon_auth.js +725 -933
- package/dist/commands/operations.js +34 -25
- package/dist/commands/orgs.js +28 -18
- package/dist/commands/profile.js +614 -845
- package/dist/commands/projects.js +313 -373
- package/dist/commands/psql.js +60 -58
- package/dist/commands/roles.js +55 -58
- package/dist/commands/schema_diff.js +87 -131
- package/dist/commands/set_context.js +34 -26
- package/dist/commands/snapshots.js +288 -413
- package/dist/commands/status.js +41 -37
- package/dist/commands/user.js +21 -10
- package/dist/commands/vpc_endpoints.js +85 -113
- package/dist/config.js +7 -6
- package/dist/config_format.js +50 -66
- package/dist/config_template.js +128 -157
- package/dist/context.js +183 -235
- package/dist/current_branch_fast_path.js +40 -49
- package/dist/dev/env.js +313 -394
- package/dist/dev/functions.js +54 -64
- package/dist/dev/inputs.js +46 -58
- package/dist/dev/runtime.js +135 -164
- package/dist/dev/websocket.js +766 -959
- package/dist/env.js +27 -33
- package/dist/env_file.js +118 -132
- package/dist/env_services.js +36 -38
- package/dist/errors.js +57 -68
- package/dist/functions_api.js +45 -43
- package/dist/help.js +189 -140
- package/dist/index.js +182 -257
- package/dist/init/agents.js +137 -118
- package/dist/init/auth.js +58 -68
- package/dist/init/bootstrap.js +325 -396
- package/dist/init/build_config.js +4 -2
- package/dist/init/detect_agent.js +56 -101
- package/dist/init/editors.js +35 -52
- package/dist/init/enrich_output.js +51 -66
- package/dist/init/extension.js +134 -171
- package/dist/init/inspect.js +179 -266
- package/dist/init/interactive.js +510 -622
- package/dist/init/neonctl.js +117 -168
- package/dist/init/orchestrate.js +157 -173
- package/dist/init/phases/auth.js +188 -202
- package/dist/init/phases/cleanup.js +23 -23
- package/dist/init/phases/db.js +251 -277
- package/dist/init/phases/getting_started.js +213 -223
- package/dist/init/phases/mcp.js +174 -224
- package/dist/init/phases/migrations.js +247 -248
- package/dist/init/phases/neon_auth.js +114 -133
- package/dist/init/phases/setup.js +546 -703
- package/dist/init/phases/skills.js +75 -86
- package/dist/init/phases/status.js +72 -67
- package/dist/init/resolve_context.js +102 -99
- package/dist/init/route_command.js +91 -98
- package/dist/init/skills.js +174 -218
- package/dist/init/vsix.js +77 -99
- package/dist/log.js +17 -16
- package/dist/neon_services.js +104 -129
- package/dist/parameters.gen.js +481 -471
- package/dist/pkg.js +17 -19
- package/dist/profile_keys.js +44 -47
- package/dist/psql/cli.js +44 -47
- package/dist/psql/command/cmd_cond.js +231 -406
- package/dist/psql/command/cmd_connect.js +557 -764
- package/dist/psql/command/cmd_copy.js +727 -983
- package/dist/psql/command/cmd_describe.js +1499 -1688
- package/dist/psql/command/cmd_format.js +733 -905
- package/dist/psql/command/cmd_io.js +1293 -2082
- package/dist/psql/command/cmd_lo.js +297 -359
- package/dist/psql/command/cmd_meta.js +727 -878
- package/dist/psql/command/cmd_misc.js +138 -172
- package/dist/psql/command/cmd_pipeline.js +547 -1099
- package/dist/psql/command/cmd_restrict.js +119 -155
- package/dist/psql/command/cmd_show.js +529 -688
- package/dist/psql/command/dispatch.js +261 -325
- package/dist/psql/command/inputQueue.js +35 -33
- package/dist/psql/command/shared.js +49 -63
- package/dist/psql/complete/filenames.js +90 -133
- package/dist/psql/complete/index.js +59 -97
- package/dist/psql/complete/matcher.js +236 -300
- package/dist/psql/complete/psqlVars.js +218 -223
- package/dist/psql/complete/queries.js +159 -177
- package/dist/psql/complete/rules.js +1493 -2299
- package/dist/psql/core/common.js +762 -1180
- package/dist/psql/core/help.js +456 -546
- package/dist/psql/core/mainloop.js +692 -1302
- package/dist/psql/core/prompt.js +391 -408
- package/dist/psql/core/settings.js +429 -644
- package/dist/psql/core/sqlHelp.js +480 -554
- package/dist/psql/core/startup.js +626 -815
- package/dist/psql/core/syncVars.js +67 -110
- package/dist/psql/core/variables.js +156 -278
- package/dist/psql/describe/formatters.js +884 -1285
- package/dist/psql/describe/processNamePattern.js +173 -260
- package/dist/psql/describe/queries.js +1368 -2403
- package/dist/psql/describe/versionGate.js +32 -41
- package/dist/psql/index.js +1414 -1927
- package/dist/psql/io/history.js +232 -271
- package/dist/psql/io/input.js +103 -108
- package/dist/psql/io/lineEditor/buffer.js +238 -319
- package/dist/psql/io/lineEditor/complete.js +135 -213
- package/dist/psql/io/lineEditor/filename.js +139 -148
- package/dist/psql/io/lineEditor/index.js +653 -870
- package/dist/psql/io/lineEditor/keymap.js +544 -702
- package/dist/psql/io/lineEditor/vt100.js +294 -341
- package/dist/psql/io/pgpass.js +158 -187
- package/dist/psql/io/pgservice.js +146 -183
- package/dist/psql/io/psqlrc.js +328 -403
- package/dist/psql/print/aligned.js +1020 -1683
- package/dist/psql/print/asciidoc.js +180 -214
- package/dist/psql/print/crosstab.js +281 -442
- package/dist/psql/print/csv.js +48 -70
- package/dist/psql/print/html.js +195 -226
- package/dist/psql/print/json.js +75 -88
- package/dist/psql/print/latex.js +291 -364
- package/dist/psql/print/pager.js +171 -242
- package/dist/psql/print/troff.js +194 -226
- package/dist/psql/print/unaligned.js +69 -95
- package/dist/psql/print/units.js +167 -169
- package/dist/psql/scanner/slash.js +428 -483
- package/dist/psql/scanner/sql.js +445 -889
- package/dist/psql/scanner/stringutils.js +309 -379
- package/dist/psql/types/index.js +2 -7
- package/dist/psql/types/scanner.js +25 -22
- package/dist/psql/wire/connection.js +2042 -2803
- package/dist/psql/wire/copy.js +84 -100
- package/dist/psql/wire/notify.js +39 -59
- package/dist/psql/wire/pipeline.js +305 -518
- package/dist/psql/wire/protocol.js +349 -417
- package/dist/psql/wire/sasl.js +180 -265
- package/dist/psql/wire/tls.js +400 -561
- package/dist/storage_api.js +115 -129
- package/dist/test_utils/fixtures.js +94 -113
- package/dist/test_utils/oauth_server.js +10 -7
- package/dist/test_utils/project_dir.js +33 -0
- package/dist/utils/ai_gateway_notice.js +131 -162
- package/dist/utils/api_enums.js +21 -28
- package/dist/utils/auth.js +10 -4
- package/dist/utils/branch_notice.js +20 -19
- package/dist/utils/branch_picker.js +83 -89
- package/dist/utils/cli_name.js +15 -12
- package/dist/utils/compute_units.js +20 -27
- package/dist/utils/config_diff.js +127 -158
- package/dist/utils/enrichers.js +95 -148
- package/dist/utils/esbuild.js +133 -147
- package/dist/utils/flags.js +35 -47
- package/dist/utils/formats.js +8 -15
- package/dist/utils/git_diff.js +69 -80
- package/dist/utils/inspect_db.js +101 -143
- package/dist/utils/inspect_queries.js +179 -142
- package/dist/utils/middlewares.js +37 -44
- package/dist/utils/openapi.js +87 -99
- package/dist/utils/package_manager.js +312 -110
- package/dist/utils/point_in_time.js +49 -53
- package/dist/utils/psql.js +89 -106
- package/dist/utils/service_picker.js +55 -58
- package/dist/utils/string.js +5 -5
- package/dist/utils/ui.js +38 -55
- package/dist/utils/write_sync.js +26 -35
- package/dist/utils/zip.js +4 -3
- package/dist/writer.js +67 -87
- package/package.json +9 -7
package/dist/env.js
CHANGED
|
@@ -1,36 +1,30 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
//#region src/env.ts
|
|
2
|
+
const isCi = () => {
|
|
3
|
+
return process.env.CI !== "false" && Boolean(process.env.CI);
|
|
3
4
|
};
|
|
4
|
-
|
|
5
|
-
|
|
5
|
+
const isDebug = () => {
|
|
6
|
+
return Boolean(process.env.DEBUG);
|
|
6
7
|
};
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
}
|
|
28
|
-
if (v === "GITHUB_ACTION_PATH") {
|
|
29
|
-
value = value.includes("actions/")
|
|
30
|
-
? value.replace(/^.*actions\/(.+)$/, "$1")
|
|
31
|
-
: value;
|
|
32
|
-
}
|
|
33
|
-
map.set(v, value);
|
|
34
|
-
});
|
|
35
|
-
return Object.fromEntries(map);
|
|
8
|
+
const getGithubEnvVars = (env) => {
|
|
9
|
+
const vars = [
|
|
10
|
+
"GITHUB_ACTION_PATH",
|
|
11
|
+
"GITHUB_REPOSITORY",
|
|
12
|
+
"GITHUB_RUN_ID",
|
|
13
|
+
"GITHUB_RUN_NUMBER",
|
|
14
|
+
"GITHUB_SERVER_URL",
|
|
15
|
+
"GITHUB_WORKFLOW_REF",
|
|
16
|
+
"RUNNER_ARCH",
|
|
17
|
+
"RUNNER_ENVIRONMENT",
|
|
18
|
+
"RUNNER_OS"
|
|
19
|
+
];
|
|
20
|
+
const map = /* @__PURE__ */ new Map();
|
|
21
|
+
vars.forEach((v) => {
|
|
22
|
+
let value = env[v];
|
|
23
|
+
if (value === void 0 || value === "") return;
|
|
24
|
+
if (v === "GITHUB_ACTION_PATH") value = value.includes("actions/") ? value.replace(/^.*actions\/(.+)$/, "$1") : value;
|
|
25
|
+
map.set(v, value);
|
|
26
|
+
});
|
|
27
|
+
return Object.fromEntries(map);
|
|
36
28
|
};
|
|
29
|
+
//#endregion
|
|
30
|
+
export { getGithubEnvVars, isCi, isDebug };
|
package/dist/env_file.js
CHANGED
|
@@ -1,159 +1,145 @@
|
|
|
1
1
|
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
|
+
//#region src/env_file.ts
|
|
3
4
|
/**
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
return join(cwd, ".env");
|
|
13
|
-
return join(cwd, ".env.local");
|
|
5
|
+
* Default dotenv file `env pull` writes to: `.env` when one already exists in the working
|
|
6
|
+
* directory (update where secrets already live), otherwise `.env.local` — matching the
|
|
7
|
+
* `vercel env pull` convention. An explicit `--file` always wins over this.
|
|
8
|
+
*/
|
|
9
|
+
const resolveEnvFilePath = (cwd, file) => {
|
|
10
|
+
if (file) return join(cwd, file);
|
|
11
|
+
if (existsSync(join(cwd, ".env"))) return join(cwd, ".env");
|
|
12
|
+
return join(cwd, ".env.local");
|
|
14
13
|
};
|
|
15
14
|
/**
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
15
|
+
* Merge `updates` into the dotenv content at `path`, preserving every other line
|
|
16
|
+
* (comments, blank lines, unrelated keys) and the file's existing order. Keys present in
|
|
17
|
+
* both are updated in place; keys only in `updates` are appended. A non-existent file is
|
|
18
|
+
* treated as empty. When `managedKeys` is given, any owned key on disk that is absent from
|
|
19
|
+
* `updates` is removed. Returns the keys written and the (managed) keys removed.
|
|
20
|
+
*/
|
|
21
|
+
const mergeEnvFile = (path, updates, options = {}) => {
|
|
22
|
+
const original = existsSync(path) ? readFileSync(path, "utf8") : "";
|
|
23
|
+
const { content, written, removed } = mergeEnvContent(original, updates, options);
|
|
24
|
+
writeFileSync(path, content);
|
|
25
|
+
return {
|
|
26
|
+
written,
|
|
27
|
+
removed
|
|
28
|
+
};
|
|
27
29
|
};
|
|
28
30
|
/**
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
const content = [...body, ...appended].join("\n");
|
|
66
|
-
return {
|
|
67
|
-
// A dotenv file ends with a trailing newline.
|
|
68
|
-
content: content === "" ? "" : `${content}\n`,
|
|
69
|
-
written: keys,
|
|
70
|
-
removed,
|
|
71
|
-
};
|
|
31
|
+
* Pure core of {@link mergeEnvFile}: takes the current file content and the updates, and
|
|
32
|
+
* returns the new content plus which keys were written / removed. Kept side-effect-free so
|
|
33
|
+
* it can be unit-tested without touching the filesystem.
|
|
34
|
+
*/
|
|
35
|
+
const mergeEnvContent = (original, updates, options = {}) => {
|
|
36
|
+
const keys = Object.keys(updates);
|
|
37
|
+
const stale = new Set([...options.managedKeys ?? []].filter((key) => !(key in updates)));
|
|
38
|
+
if (keys.length === 0 && stale.size === 0) return {
|
|
39
|
+
content: original,
|
|
40
|
+
written: [],
|
|
41
|
+
removed: []
|
|
42
|
+
};
|
|
43
|
+
const remaining = new Set(keys);
|
|
44
|
+
const removed = [];
|
|
45
|
+
const lines = original === "" ? [] : original.split("\n");
|
|
46
|
+
const updatedLines = [];
|
|
47
|
+
for (const line of lines) {
|
|
48
|
+
const key = parseKey(line);
|
|
49
|
+
if (key !== null && stale.has(key)) {
|
|
50
|
+
removed.push(key);
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
if (key !== null && remaining.has(key)) {
|
|
54
|
+
remaining.delete(key);
|
|
55
|
+
updatedLines.push(formatLine(key, updates[key]));
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
updatedLines.push(line);
|
|
59
|
+
}
|
|
60
|
+
const appended = keys.filter((key) => remaining.has(key)).map((key) => formatLine(key, updates[key]));
|
|
61
|
+
const content = [...trimTrailingBlank(updatedLines), ...appended].join("\n");
|
|
62
|
+
return {
|
|
63
|
+
content: content === "" ? "" : `${content}\n`,
|
|
64
|
+
written: keys,
|
|
65
|
+
removed
|
|
66
|
+
};
|
|
72
67
|
};
|
|
73
68
|
/**
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
out[parsed.key] = parsed.value;
|
|
89
|
-
}
|
|
90
|
-
return out;
|
|
69
|
+
* Read a dotenv file at `path` into a plain `{ KEY: value }` map. A non-existent file is an
|
|
70
|
+
* error (callers pass an explicit `--env` path, so a typo should fail loudly rather than
|
|
71
|
+
* silently load nothing). Quotes are stripped and `\"` / `\\` unescaped, matching the
|
|
72
|
+
* quoting {@link mergeEnvFile} writes. Comments, blank lines, and non-assignment lines are
|
|
73
|
+
* ignored.
|
|
74
|
+
*/
|
|
75
|
+
const readEnvFile = (path) => {
|
|
76
|
+
if (!existsSync(path)) throw new Error(`Env file not found: ${path}`);
|
|
77
|
+
const out = {};
|
|
78
|
+
for (const line of readFileSync(path, "utf8").split("\n")) {
|
|
79
|
+
const parsed = parseAssignment(line);
|
|
80
|
+
if (parsed) out[parsed.key] = parsed.value;
|
|
81
|
+
}
|
|
82
|
+
return out;
|
|
91
83
|
};
|
|
92
84
|
/**
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
}
|
|
107
|
-
return applied;
|
|
85
|
+
* Load a dotenv file into `process.env` so values are available to anything read later
|
|
86
|
+
* (e.g. a `neon.ts` whose function `env` values come from `process.env.X`). Existing
|
|
87
|
+
* `process.env` entries are **not** overridden — an already-exported var wins over the
|
|
88
|
+
* file, matching dotenv's default. Returns the keys that were applied.
|
|
89
|
+
*/
|
|
90
|
+
const loadEnvFileIntoProcess = (path) => {
|
|
91
|
+
const parsed = readEnvFile(path);
|
|
92
|
+
const applied = [];
|
|
93
|
+
for (const [key, value] of Object.entries(parsed)) if (process.env[key] === void 0) {
|
|
94
|
+
process.env[key] = value;
|
|
95
|
+
applied.push(key);
|
|
96
|
+
}
|
|
97
|
+
return applied;
|
|
108
98
|
};
|
|
109
99
|
/**
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
100
|
+
* Extract the variable name from a dotenv line, or `null` for comments / blank lines / any
|
|
101
|
+
* line that isn't a `KEY=value` assignment. Tolerates a leading `export ` and surrounding
|
|
102
|
+
* whitespace, matching common `.env` styles.
|
|
103
|
+
*/
|
|
114
104
|
const parseKey = (line) => {
|
|
115
|
-
|
|
116
|
-
|
|
105
|
+
const match = /^\s*(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*=/.exec(line);
|
|
106
|
+
return match ? match[1] : null;
|
|
117
107
|
};
|
|
118
108
|
/**
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
109
|
+
* Parse a `KEY=value` dotenv line into its key and unquoted value, or `null` for
|
|
110
|
+
* comments / blank lines / non-assignments. Mirrors {@link formatLine}'s quoting.
|
|
111
|
+
*/
|
|
122
112
|
const parseAssignment = (line) => {
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
113
|
+
const match = /^\s*(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*)$/.exec(line);
|
|
114
|
+
const key = match?.[1];
|
|
115
|
+
const raw = match?.[2];
|
|
116
|
+
if (key === void 0 || raw === void 0) return null;
|
|
117
|
+
return {
|
|
118
|
+
key,
|
|
119
|
+
value: unquote(raw.trim())
|
|
120
|
+
};
|
|
129
121
|
};
|
|
130
122
|
/** Strip matching surrounding quotes and unescape `\"` / `\\` inside double quotes. */
|
|
131
123
|
const unquote = (value) => {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
if (value.length >= 2 && value.startsWith("'") && value.endsWith("'")) {
|
|
136
|
-
return value.slice(1, -1);
|
|
137
|
-
}
|
|
138
|
-
return value;
|
|
124
|
+
if (value.length >= 2 && value.startsWith("\"") && value.endsWith("\"")) return value.slice(1, -1).replace(/\\"/g, "\"").replace(/\\\\/g, "\\");
|
|
125
|
+
if (value.length >= 2 && value.startsWith("'") && value.endsWith("'")) return value.slice(1, -1);
|
|
126
|
+
return value;
|
|
139
127
|
};
|
|
140
128
|
/**
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
129
|
+
* Render a `KEY=value` line. The value is wrapped in double quotes when it contains
|
|
130
|
+
* characters that would otherwise break parsing (spaces, `#`, quotes, `=`), with inner
|
|
131
|
+
* quotes/backslashes escaped — Neon connection strings and URLs are safe either way, but
|
|
132
|
+
* quoting defensively avoids surprises for tools that re-parse the file.
|
|
133
|
+
*/
|
|
146
134
|
const formatLine = (key, value) => {
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
return `${key}=${value}`;
|
|
150
|
-
const escaped = value.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
151
|
-
return `${key}="${escaped}"`;
|
|
135
|
+
if (!/[\s#"'=]/.test(value)) return `${key}=${value}`;
|
|
136
|
+
return `${key}="${value.replace(/\\/g, "\\\\").replace(/"/g, "\\\"")}"`;
|
|
152
137
|
};
|
|
153
138
|
/** Drop trailing blank lines so we don't accumulate them across repeated merges. */
|
|
154
139
|
const trimTrailingBlank = (lines) => {
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
return out;
|
|
140
|
+
const out = [...lines];
|
|
141
|
+
while (out.length > 0 && out[out.length - 1]?.trim() === "") out.pop();
|
|
142
|
+
return out;
|
|
159
143
|
};
|
|
144
|
+
//#endregion
|
|
145
|
+
export { loadEnvFileIntoProcess, mergeEnvContent, mergeEnvFile, readEnvFile, resolveEnvFilePath };
|
package/dist/env_services.js
CHANGED
|
@@ -1,51 +1,49 @@
|
|
|
1
|
-
import { NEON_ENV_VAR_KEYS } from "@neon/env";
|
|
2
1
|
import { NEON_SERVICES } from "./neon_services.js";
|
|
2
|
+
import { NEON_ENV_VAR_KEYS } from "./_shared/env-core/env.js";
|
|
3
|
+
//#region src/env_services.ts
|
|
3
4
|
/**
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
* The services `env pull --service` can select: every Neon service that produces branch env
|
|
6
|
+
* vars. `functions` is the one left out — a function's env comes from the local `neon.ts`,
|
|
7
|
+
* never from the branch, so there is nothing to pull.
|
|
8
|
+
*/
|
|
9
|
+
const ENV_PULL_SERVICES = NEON_SERVICES.filter((service) => service !== "functions");
|
|
9
10
|
/** Why the services `env pull` leaves out are not selectable, for the refusal message. */
|
|
10
|
-
|
|
11
|
-
functions: "a function's env comes from your neon.ts, not from the branch, so there is nothing to pull",
|
|
12
|
-
};
|
|
11
|
+
const ENV_PULL_UNAVAILABLE = { functions: "a function's env comes from your neon.ts, not from the branch, so there is nothing to pull" };
|
|
13
12
|
/** The OS-level env vars each service contributes to a pulled `.env`. */
|
|
14
13
|
const SERVICE_ENV_KEYS = {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
14
|
+
postgres: Object.values(NEON_ENV_VAR_KEYS.postgres),
|
|
15
|
+
auth: Object.values(NEON_ENV_VAR_KEYS.auth),
|
|
16
|
+
"data-api": Object.values(NEON_ENV_VAR_KEYS.dataApi),
|
|
17
|
+
"object-storage": Object.values(NEON_ENV_VAR_KEYS.storage),
|
|
18
|
+
"ai-gateway": Object.values(NEON_ENV_VAR_KEYS.aiGateway),
|
|
19
|
+
functions: []
|
|
21
20
|
};
|
|
22
21
|
/**
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
22
|
+
* The subset of {@link SERVICE_ENV_KEYS} a pull *owns*, and so may prune from the target file
|
|
23
|
+
* when the branch no longer has it. Object storage is deliberately absent: it is emitted under
|
|
24
|
+
* the third-party `AWS_*` names, which collide with credentials a user may set by hand, so
|
|
25
|
+
* `env pull` only ever writes them.
|
|
26
|
+
*/
|
|
28
27
|
const SERVICE_OWNED_ENV_KEYS = {
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
...SERVICE_ENV_KEYS,
|
|
29
|
+
"object-storage": []
|
|
31
30
|
};
|
|
32
31
|
/**
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
32
|
+
* Branch identity. Not a service — every branch has a name — so a scoped pull refreshes it
|
|
33
|
+
* alongside whatever services were selected.
|
|
34
|
+
*/
|
|
35
|
+
const BRANCH_ENV_KEY = NEON_ENV_VAR_KEYS.branch.name;
|
|
37
36
|
/** Every env var the selected services contribute, plus branch identity. */
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
keys.add(key);
|
|
43
|
-
}
|
|
44
|
-
return keys;
|
|
37
|
+
const envServiceKeys = (services) => {
|
|
38
|
+
const keys = /* @__PURE__ */ new Set([BRANCH_ENV_KEY]);
|
|
39
|
+
for (const service of services) for (const key of SERVICE_ENV_KEYS[service]) keys.add(key);
|
|
40
|
+
return keys;
|
|
45
41
|
};
|
|
46
42
|
/**
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
43
|
+
* The env vars a pull scoped to `services` may prune. Narrower than the unscoped set on
|
|
44
|
+
* purpose: `env pull -s ai-gateway` says nothing about `DATABASE_URL`, so it must leave it
|
|
45
|
+
* alone rather than treat its absence from this pull as "the branch no longer has it".
|
|
46
|
+
*/
|
|
47
|
+
const ownedEnvServiceKeys = (services) => services.flatMap((service) => SERVICE_OWNED_ENV_KEYS[service]);
|
|
48
|
+
//#endregion
|
|
49
|
+
export { BRANCH_ENV_KEY, ENV_PULL_SERVICES, ENV_PULL_UNAVAILABLE, envServiceKeys, ownedEnvServiceKeys };
|
package/dist/errors.js
CHANGED
|
@@ -1,80 +1,69 @@
|
|
|
1
|
+
//#region src/errors.ts
|
|
1
2
|
const ERROR_MATCHERS = [
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
[/^Unknown command: (.*)$/, "UNKNOWN_COMMAND"],
|
|
4
|
+
[/^Missing required argument: (.*)$/, "MISSING_ARGUMENT"],
|
|
5
|
+
[/^Failed to open web browser. (.*)$/, "AUTH_BROWSER_FAILED"]
|
|
5
6
|
];
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
for (const [matcher, code] of ERROR_MATCHERS) {
|
|
11
|
-
const match = message.match(matcher);
|
|
12
|
-
if (match) {
|
|
13
|
-
return code;
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
return "UNKNOWN_ERROR";
|
|
7
|
+
const matchErrorCode = (message) => {
|
|
8
|
+
if (!message) return "UNKNOWN_ERROR";
|
|
9
|
+
for (const [matcher, code] of ERROR_MATCHERS) if (message.match(matcher)) return code;
|
|
10
|
+
return "UNKNOWN_ERROR";
|
|
17
11
|
};
|
|
18
12
|
/**
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
"If your connection is fine and this keeps happening, check https://neonstatus.com for ongoing incidents.";
|
|
13
|
+
* The single, human-readable line shown when the CLI couldn't reach the Neon API because
|
|
14
|
+
* of a connection-level failure (DNS, refused/reset connection, offline). It replaces the
|
|
15
|
+
* cryptic `fetch failed` / empty axios message a network blip otherwise surfaces (see
|
|
16
|
+
* {@link isNetworkError}), pointing at the two things the user can actually check.
|
|
17
|
+
*/
|
|
18
|
+
const NETWORK_ERROR_MESSAGE = "Could not reach the Neon API. Please check your internet connection and try again. If your connection is fine and this keeps happening, check https://neonstatus.com for ongoing incidents.";
|
|
26
19
|
/**
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
const NETWORK_ERROR_CODES = new Set([
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
20
|
+
* Node-level socket/DNS error codes that mean the request never reached the server — a
|
|
21
|
+
* genuine connectivity problem rather than an API response we should surface. Deliberately
|
|
22
|
+
* excludes `ECONNABORTED` (axios' timeout), which the CLI already reports as a timeout.
|
|
23
|
+
*/
|
|
24
|
+
const NETWORK_ERROR_CODES = /* @__PURE__ */ new Set([
|
|
25
|
+
"ECONNREFUSED",
|
|
26
|
+
"ECONNRESET",
|
|
27
|
+
"ETIMEDOUT",
|
|
28
|
+
"ENOTFOUND",
|
|
29
|
+
"EAI_AGAIN",
|
|
30
|
+
"EPIPE",
|
|
31
|
+
"EHOSTUNREACH",
|
|
32
|
+
"ENETUNREACH",
|
|
33
|
+
"EHOSTDOWN",
|
|
34
|
+
"ENETDOWN"
|
|
42
35
|
]);
|
|
43
36
|
/**
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
37
|
+
* Message fragments that mark a connection-level failure across our two transports: the
|
|
38
|
+
* `@neon/sdk` / global `fetch` path (used by `env pull` / `config` / `deploy`, and `link`'s
|
|
39
|
+
* bundled env pull) throws a bare `TypeError: fetch failed` / "Failed to fetch", while the
|
|
40
|
+
* axios `api-client` path throws an `AxiosError` whose message is "Network Error".
|
|
41
|
+
*/
|
|
49
42
|
const NETWORK_ERROR_MESSAGE_PATTERN = /fetch failed|failed to fetch|network error/i;
|
|
50
43
|
const readErrorCode = (value) => {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
return typeof code === "string" ? code : undefined;
|
|
44
|
+
if (value === null || typeof value !== "object") return void 0;
|
|
45
|
+
const code = value.code;
|
|
46
|
+
return typeof code === "string" ? code : void 0;
|
|
55
47
|
};
|
|
56
48
|
/**
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
return true;
|
|
76
|
-
}
|
|
77
|
-
current = current.cause;
|
|
78
|
-
}
|
|
79
|
-
return false;
|
|
49
|
+
* Whether `err` is a connection-level failure (the network blip the user sees as a cryptic
|
|
50
|
+
* `fetch failed`, or no message at all on the axios path) rather than an actual API response.
|
|
51
|
+
*
|
|
52
|
+
* A Node `fetch` failure is a bare `TypeError: fetch failed` whose underlying `cause` carries
|
|
53
|
+
* the real socket `code` (e.g. `ECONNREFUSED`, `ENOTFOUND`, `EAI_AGAIN`), so we walk the
|
|
54
|
+
* `cause` chain checking both the code and the message. This is what lets the CLI swap the
|
|
55
|
+
* confusing default for {@link NETWORK_ERROR_MESSAGE}. It matches only failures where no
|
|
56
|
+
* response was ever received, so it never masks a real 4xx/5xx the user needs to see.
|
|
57
|
+
*/
|
|
58
|
+
const isNetworkError = (err) => {
|
|
59
|
+
let current = err;
|
|
60
|
+
for (let depth = 0; depth < 6 && current !== null && current !== void 0; depth++) {
|
|
61
|
+
const code = readErrorCode(current);
|
|
62
|
+
if (code !== void 0 && NETWORK_ERROR_CODES.has(code)) return true;
|
|
63
|
+
if (current instanceof Error && NETWORK_ERROR_MESSAGE_PATTERN.test(current.message)) return true;
|
|
64
|
+
current = current.cause;
|
|
65
|
+
}
|
|
66
|
+
return false;
|
|
80
67
|
};
|
|
68
|
+
//#endregion
|
|
69
|
+
export { NETWORK_ERROR_MESSAGE, isNetworkError, matchErrorCode };
|