neon 7.0.8 → 8.0.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 +6 -2
- package/dist/_chunks/{env-CtOH9xsl.js → env-BgiaRNT1.js} +4 -2
- package/dist/commands/auth.js +1 -0
- package/dist/commands/checkout.js +3 -1
- package/dist/commands/claim.js +10 -29
- package/dist/commands/config.js +45 -45
- package/dist/commands/deploy.js +1 -1
- package/dist/commands/dev.js +10 -6
- package/dist/commands/env.js +12 -7
- package/dist/commands/init.js +5 -1
- package/dist/commands/link.js +8 -5
- package/dist/commands/user.js +37 -4
- package/dist/dev/env.js +1 -1
- package/dist/dev/functions.js +11 -7
- package/dist/env_file.js +9 -6
- package/dist/init/run.js +2 -1
- package/dist/project.js +49 -0
- package/dist/utils/hooks.js +7 -3
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -76,6 +76,8 @@ neon projects list --api-key <neon_api_key>
|
|
|
76
76
|
|
|
77
77
|
For information about obtaining an Neon API key, see [Authentication](https://neon.com/docs/reference/api/get-started), in the _Neon API Reference_.
|
|
78
78
|
|
|
79
|
+
To check which account and credential a command uses, run `neon me` with the same `--profile` or `--api-key` option and environment as that command. The `Authentication` row names the source, such as `OAuth (profile DEFAULT)`, `API key (profile work)`, `API key (--api-key)`, or `API key (NEON_API_KEY)`.
|
|
80
|
+
|
|
79
81
|
## Create a project without an account
|
|
80
82
|
|
|
81
83
|
`neon claim create` provisions a temporary Claimable Neon project for an agent without
|
|
@@ -594,7 +596,9 @@ If you'd rather not keep env vars on disk, inject them at runtime instead with `
|
|
|
594
596
|
|
|
595
597
|
**`neon dev` resolves the same set, by the same rules** — including the AI Gateway on a branch with no `neon.ts`. A function running locally gets the same Postgres / Auth / Data API / storage / AI Gateway vars a deploy would inject. Each `NEON_FUNCTION_<SLUG>_BASE_URL` this process is serving is rewritten to `http://localhost:<port>`: every neon.ts function, or the matching slug when `--source` names one. `neon env pull` and `neon-env run` write the production URL (`https://<branchId>-<slug>.compute.…`). `dev` writes nothing, but it does *read* your `.env` / `.env.local` to reuse the branch credential behind the AI Gateway and object storage. Without a file to read from it issues one on every start and leaves the last one live — it has nowhere to keep it, and so cannot name it to revoke it. It says so when it happens; run `env pull` (or just `link` / `checkout`) once and restarts reuse the credential instead.
|
|
596
598
|
|
|
597
|
-
**Where `.neon` lives**: `link` writes `.neon` into the **current working directory** by default. If an existing `.neon` is found in any parent directory, that file is reused — so commands run from a sub-directory of a linked project still pick up the project's context. To pin the location explicitly, pass `--context-file <path>`.
|
|
599
|
+
**Where `.neon` lives**: `link` writes `.neon` into the **current working directory** by default. If an existing `.neon` is found in any parent directory, that file is reused — so commands run from a sub-directory of a linked project still pick up the project's context. To pin the location explicitly, pass `--context-file <path>`. `neon init` always writes `.neon` into the current directory.
|
|
600
|
+
|
|
601
|
+
**The project directory** is the directory holding that `.neon`, or the current directory when there is none. The project's `neon.ts` and the default `.env` / `.env.local` live there and nowhere else: a `neon.ts` in a parent directory never applies. An explicit `--config` or `--file` path still wins.
|
|
598
602
|
|
|
599
603
|
**`.gitignore` scaffolding**: when `.neon` is **created** for the first time, the CLI also makes sure a `.gitignore` sits alongside it listing `.neon`. If `.gitignore` doesn't exist it's created with a single `.neon` line; if it does exist, `.neon` is appended only when missing (no duplicates, your other entries are left alone). On subsequent updates to an existing `.neon`, `.gitignore` is left untouched — so if you deliberately un-ignore `.neon` (e.g. to commit shared context), the entry is not re-added on every command.
|
|
600
604
|
|
|
@@ -732,7 +736,7 @@ neon deploy
|
|
|
732
736
|
2. `projectId` from the closest `.neon` file (found by walking up from the current directory — see "Where `.neon` lives" above)
|
|
733
737
|
3. If still unresolved and the API key maps to exactly one project, that project is auto-detected
|
|
734
738
|
|
|
735
|
-
The branch is chosen with `--branch <id|name>`; without it the project's default branch is used. The policy
|
|
739
|
+
The branch is chosen with `--branch <id|name>`; without it the project's default branch is used. The policy is the `neon.ts` in the project directory (next to `.neon`, or the current directory without one); pass `--config <path>` to point at another file.
|
|
736
740
|
|
|
737
741
|
**Apply-only flags** (also available on `deploy`):
|
|
738
742
|
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { log } from "../log.js";
|
|
2
2
|
import { getCliName } from "../utils/cli_name.js";
|
|
3
3
|
import { declaredNeonServices } from "../config_services.js";
|
|
4
|
+
import { loadProjectConfig } from "../project.js";
|
|
4
5
|
import { a as defaultAiGatewayCredential, c as fetchEnvKeysState, d as isLiveCredential, h as toEntries, i as credentialName, l as functionBaseUrlKey, m as resolveBranchPolicy, n as createApiFromOptions, o as defaultStorageCredential, p as policyEnvKeys, r as credentialEnvKeys, t as NEON_ENV_VAR_KEYS, u as isFunctionBaseUrlKey } from "./env-N19A7qND.js";
|
|
5
6
|
import { ENV_PULL_SERVICES, envKeysForSelection, serviceForEnvKey, servicesForEnvKeys } from "../env_services.js";
|
|
6
7
|
import { formatInstallCommand, resolvePackageManager } from "../utils/package_manager.js";
|
|
7
|
-
import { ErrorCode, createNeonApiFromOptions, isPlatformError
|
|
8
|
+
import { ErrorCode, createNeonApiFromOptions, isPlatformError } from "@neon/config";
|
|
8
9
|
import { credentialScopesSatisfied } from "@neon/config/v1";
|
|
9
10
|
import { plan, pullConfig } from "@neon/config-runtime";
|
|
10
11
|
//#region ../../internals/env-core/dist/reuse-secrets.js
|
|
@@ -708,8 +709,9 @@ const looksLikeMissingDependency = (err) => {
|
|
|
708
709
|
};
|
|
709
710
|
const loadNeonConfig = async (ctx) => {
|
|
710
711
|
try {
|
|
711
|
-
const { config } = await
|
|
712
|
+
const { config } = await loadProjectConfig({
|
|
712
713
|
cwd: ctx.cwd,
|
|
714
|
+
...ctx.contextFile ? { contextFile: ctx.contextFile } : {},
|
|
713
715
|
...ctx.config ? { path: ctx.config } : {},
|
|
714
716
|
...ctx.omitUnsetFunctionEnv ? { unsetFunctionEnv: "omit" } : {}
|
|
715
717
|
});
|
package/dist/commands/auth.js
CHANGED
|
@@ -366,6 +366,7 @@ const ensureAuth = async (props) => {
|
|
|
366
366
|
log.debug("Using an API key to authorize requests");
|
|
367
367
|
setAuthContext({
|
|
368
368
|
source: "api-key",
|
|
369
|
+
apiKeyFrom: selection.source === "explicit-api-key" ? "flag" : "env",
|
|
369
370
|
configDir: props.configDir
|
|
370
371
|
});
|
|
371
372
|
props.apiClient = getApiClient({
|
|
@@ -77,7 +77,7 @@ const handler = async (props) => {
|
|
|
77
77
|
const cwd = process.cwd();
|
|
78
78
|
const git = readGitContext(cwd);
|
|
79
79
|
const event = buildCheckoutEvent(git, props.id);
|
|
80
|
-
const config = await loadHookConfig(cwd);
|
|
80
|
+
const config = await loadHookConfig(cwd, props.contextFile);
|
|
81
81
|
const hooks = config?.experimental?.hooks;
|
|
82
82
|
if (props.id) {
|
|
83
83
|
const renamed = await runCheckoutBeforeHook({
|
|
@@ -108,6 +108,7 @@ const handler = async (props) => {
|
|
|
108
108
|
projectId,
|
|
109
109
|
branchId,
|
|
110
110
|
branchName,
|
|
111
|
+
contextFile: props.contextFile,
|
|
111
112
|
...props.apiKey ? { apiKey: props.apiKey } : {},
|
|
112
113
|
...props.apiHost ? { apiHost: props.apiHost } : {},
|
|
113
114
|
...props.color !== void 0 ? { color: props.color } : {},
|
|
@@ -239,6 +240,7 @@ const createCheckoutBranch = async (props, projectId, name, branches, hookCtx) =
|
|
|
239
240
|
const fromPolicy = await createBranchFromPolicyOnCheckout({
|
|
240
241
|
projectId,
|
|
241
242
|
branchName: name,
|
|
243
|
+
contextFile: props.contextFile,
|
|
242
244
|
...props.apiKey ? { apiKey: props.apiKey } : {},
|
|
243
245
|
...props.apiHost ? { apiHost: props.apiHost } : {},
|
|
244
246
|
...props.color !== void 0 ? { color: props.color } : {},
|
package/dist/commands/claim.js
CHANGED
|
@@ -8,13 +8,12 @@ import { ClaimableClient, ClaimableServiceError } from "../claimable/api.js";
|
|
|
8
8
|
import { assertionHasExpired, listClaimableCredentials, readClaimableCredentials, readLinkedClaimableCredentials, removeClaimableCredentials, writeClaimableCredentials } from "../claimable/state.js";
|
|
9
9
|
import { NEON_SERVICES, deprecatedServiceMessage, parseServices, servicesFlagValue, servicesOption } from "../neon_services.js";
|
|
10
10
|
import { claimableDataApiCreateBody, declaredNeonServices } from "../config_services.js";
|
|
11
|
+
import { projectConfigPath } from "../project.js";
|
|
11
12
|
import { resolveEnvFilePath } from "../env_file.js";
|
|
12
13
|
import { writer } from "../writer.js";
|
|
13
14
|
import { noPassthrough } from "../utils/flags.js";
|
|
14
15
|
import { pull } from "./env.js";
|
|
15
16
|
import { existsSync, readFileSync, unlinkSync, writeFileSync } from "node:fs";
|
|
16
|
-
import { dirname, join, resolve } from "node:path";
|
|
17
|
-
import { homedir } from "node:os";
|
|
18
17
|
import open from "open";
|
|
19
18
|
import prompts from "prompts";
|
|
20
19
|
import { loadConfigFromFile } from "@neon/config-runtime";
|
|
@@ -29,7 +28,6 @@ var claim_exports = /* @__PURE__ */ __exportAll({
|
|
|
29
28
|
command: () => command,
|
|
30
29
|
create: () => create,
|
|
31
30
|
describe: () => describe,
|
|
32
|
-
findNeonConfig: () => findNeonConfig,
|
|
33
31
|
handler: () => handler
|
|
34
32
|
});
|
|
35
33
|
const CAPABILITY_FOR_SERVICE = {
|
|
@@ -63,28 +61,8 @@ const claimableCapabilities = (services) => {
|
|
|
63
61
|
for (const service of services) requested.add(CAPABILITY_FOR_SERVICE[service]);
|
|
64
62
|
return CAPABILITY_ORDER.filter((capability) => requested.has(capability));
|
|
65
63
|
};
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
"neon.mts",
|
|
69
|
-
"neon.js",
|
|
70
|
-
"neon.mjs"
|
|
71
|
-
];
|
|
72
|
-
const findNeonConfig = (cwd = process.cwd()) => {
|
|
73
|
-
let current = resolve(cwd);
|
|
74
|
-
const stop = resolve(homedir());
|
|
75
|
-
while (true) {
|
|
76
|
-
for (const name of CONFIG_FILENAMES) {
|
|
77
|
-
const candidate = join(current, name);
|
|
78
|
-
if (existsSync(candidate)) return candidate;
|
|
79
|
-
}
|
|
80
|
-
if (existsSync(join(current, ".git")) || current === stop) return void 0;
|
|
81
|
-
const parent = dirname(current);
|
|
82
|
-
if (parent === current) return void 0;
|
|
83
|
-
current = parent;
|
|
84
|
-
}
|
|
85
|
-
};
|
|
86
|
-
const loadCreatePolicy = async (explicitPath) => {
|
|
87
|
-
const path = explicitPath ?? findNeonConfig();
|
|
64
|
+
const loadCreatePolicy = async (explicitPath, contextFile) => {
|
|
65
|
+
const path = explicitPath ?? projectConfigPath(contextFile ? { contextFile } : {});
|
|
88
66
|
if (!path) return void 0;
|
|
89
67
|
const { config } = await loadConfigFromFile({ path });
|
|
90
68
|
return {
|
|
@@ -135,7 +113,7 @@ const builder = (argv) => argv.usage("$0 claim <sub-command> [options]").option(
|
|
|
135
113
|
describe: "Target dotenv file. Defaults to an existing .env, otherwise .env.local",
|
|
136
114
|
type: "string"
|
|
137
115
|
}).option("config", {
|
|
138
|
-
describe: "Path to neon.ts for registration and the bundled env pull. Defaults to
|
|
116
|
+
describe: "Path to neon.ts for registration and the bundled env pull. Defaults to the neon.ts in the project directory (next to .neon, or cwd without one)",
|
|
139
117
|
type: "string"
|
|
140
118
|
}).option("env-pull", {
|
|
141
119
|
describe: "Write the same Neon env vars `env pull` would",
|
|
@@ -148,7 +126,7 @@ const builder = (argv) => argv.usage("$0 claim <sub-command> [options]").option(
|
|
|
148
126
|
flag: "--service",
|
|
149
127
|
onDeprecated: (used, canonical) => log.warning(deprecatedServiceMessage(used, canonical))
|
|
150
128
|
}) : [];
|
|
151
|
-
const policy = await loadCreatePolicy(typeof args.config === "string" ? args.config : void 0);
|
|
129
|
+
const policy = await loadCreatePolicy(typeof args.config === "string" ? args.config : void 0, typeof args.contextFile === "string" ? args.contextFile : void 0);
|
|
152
130
|
const configuredServices = policy ? declaredNeonServices(policy.config) : [];
|
|
153
131
|
const dataApi = policy ? claimableDataApiCreateBody(policy.config) : void 0;
|
|
154
132
|
await create({
|
|
@@ -217,7 +195,10 @@ const create = async (props) => {
|
|
|
217
195
|
if (existing.projectId || existing.orgId) throw new Error(`${props.contextFile} already links this directory to a Neon project. Run \`neon claim create\` from an unlinked directory.`);
|
|
218
196
|
const contextFileExisted = existsSync(props.contextFile);
|
|
219
197
|
const cwd = props.cwd ?? process.cwd();
|
|
220
|
-
const envFile = props.envPull ? resolveEnvFilePath(
|
|
198
|
+
const envFile = props.envPull ? resolveEnvFilePath({
|
|
199
|
+
cwd,
|
|
200
|
+
contextFile: props.contextFile
|
|
201
|
+
}, props.file) : void 0;
|
|
221
202
|
const envFileExisted = envFile ? existsSync(envFile) : false;
|
|
222
203
|
const previousEnv = envFileExisted && envFile ? readFileSync(envFile) : void 0;
|
|
223
204
|
const client = new ClaimableClient(props.claimableHost);
|
|
@@ -482,4 +463,4 @@ const finishClaimedContext = (props, projectId, contextMatches) => {
|
|
|
482
463
|
log.info("Dropped the local identity assertion. The next command needs `neon auth` or `neon link`.");
|
|
483
464
|
};
|
|
484
465
|
//#endregion
|
|
485
|
-
export { CLAIM_CREATE_FIELDS, CLAIM_LIST_FIELDS, aliases, builder, claimCreateFields, claimableCapabilities, command, create, describe,
|
|
466
|
+
export { CLAIM_CREATE_FIELDS, CLAIM_LIST_FIELDS, aliases, builder, claimCreateFields, claimableCapabilities, command, create, describe, handler, claim_exports as t };
|
package/dist/commands/config.js
CHANGED
|
@@ -9,6 +9,7 @@ import { CONFIG_INIT_NONE_MEANS, CONFIG_INIT_SERVICES, CONFIG_INIT_UNAVAILABLE,
|
|
|
9
9
|
import { editNeonConfig } from "../config_edit.js";
|
|
10
10
|
import { toNeonConfigView } from "../config_format.js";
|
|
11
11
|
import { declaredNeonServices } from "../config_services.js";
|
|
12
|
+
import { loadProjectConfig, neonConfigFilename, projectConfigPath, projectDir } from "../project.js";
|
|
12
13
|
import { loadEnvFileIntoProcess } from "../env_file.js";
|
|
13
14
|
import { formatInstallCommand, installArgs, resolvePackageManager, runCommand } from "../utils/package_manager.js";
|
|
14
15
|
import { writer } from "../writer.js";
|
|
@@ -24,10 +25,9 @@ import { zipBundle } from "../utils/zip.js";
|
|
|
24
25
|
import { autoPullEnvAfterPin } from "./env.js";
|
|
25
26
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
26
27
|
import { dirname, join, relative, resolve } from "node:path";
|
|
27
|
-
import { homedir } from "node:os";
|
|
28
28
|
import chalk from "chalk";
|
|
29
29
|
import { packagesToStage, previewGaWarningForConfig, resolveConfig } from "@neon/config";
|
|
30
|
-
import { PushConflictError, apply, assertZipWithinLimits, bundleAsIs, createBranch, describeNativeFinding, enforceLimits, findUndeclaredNativePackages, inspect, isPartialBranchCreateError,
|
|
30
|
+
import { PushConflictError, apply, assertZipWithinLimits, bundleAsIs, createBranch, describeNativeFinding, enforceLimits, findUndeclaredNativePackages, inspect, isPartialBranchCreateError, plan, resolveEsbuildEntry, traceNativePackages, zipFunctionBundle } from "@neon/config-runtime";
|
|
31
31
|
//#region src/commands/config.ts
|
|
32
32
|
var config_exports = /* @__PURE__ */ __exportAll({
|
|
33
33
|
ConfigInstallFailed: () => ConfigInstallFailed,
|
|
@@ -42,9 +42,7 @@ var config_exports = /* @__PURE__ */ __exportAll({
|
|
|
42
42
|
envFlag: () => envFlag,
|
|
43
43
|
envPullFlag: () => envPullFlag,
|
|
44
44
|
handler: () => handler,
|
|
45
|
-
hasNeonConfigFile: () => hasNeonConfigFile,
|
|
46
45
|
initCmd: () => initCmd,
|
|
47
|
-
neonConfigFilename: () => neonConfigFilename,
|
|
48
46
|
planCmd: () => planCmd,
|
|
49
47
|
status: () => status
|
|
50
48
|
});
|
|
@@ -117,16 +115,6 @@ const DEPENDENCY_FIELDS = [
|
|
|
117
115
|
"peerDependencies",
|
|
118
116
|
"optionalDependencies"
|
|
119
117
|
];
|
|
120
|
-
/** Config filenames the runtime loads (mirrors @neon/config's loader). */
|
|
121
|
-
const NEON_CONFIG_FILENAMES = [
|
|
122
|
-
"neon.ts",
|
|
123
|
-
"neon.mts",
|
|
124
|
-
"neon.js",
|
|
125
|
-
"neon.mjs"
|
|
126
|
-
];
|
|
127
|
-
const neonConfigFilename = (dir) => NEON_CONFIG_FILENAMES.find((name) => existsSync(join(dir, name)));
|
|
128
|
-
/** Whether `dir` already has a Neon config file the runtime would load. */
|
|
129
|
-
const hasNeonConfigFile = (dir) => neonConfigFilename(dir) !== void 0;
|
|
130
118
|
var ConfigInstallFailed = class extends Error {
|
|
131
119
|
constructor(command) {
|
|
132
120
|
super("Could not install the Neon dependencies.");
|
|
@@ -216,6 +204,14 @@ const seedFromBranch = async (props) => {
|
|
|
216
204
|
branchName: live.branch.name
|
|
217
205
|
};
|
|
218
206
|
};
|
|
207
|
+
/** The install command to print, prefixed with a `cd` when the project is not the cwd. */
|
|
208
|
+
const installCommandFrom = (dir, pm, missing) => {
|
|
209
|
+
const command = formatInstallCommand(pm, [...missing]);
|
|
210
|
+
const rel = relative(process.cwd(), dir);
|
|
211
|
+
if (rel === "") return command;
|
|
212
|
+
const target = rel.length <= dir.length ? rel : dir;
|
|
213
|
+
return `cd ${shellQuote(target)} && ${command}`;
|
|
214
|
+
};
|
|
219
215
|
/** Install the config packages the project is missing, or say how to. */
|
|
220
216
|
const ensureConfigPackages = async (cwd, props) => {
|
|
221
217
|
const run = props.run ?? runCommand;
|
|
@@ -226,16 +222,21 @@ const ensureConfigPackages = async (cwd, props) => {
|
|
|
226
222
|
}
|
|
227
223
|
const pm = props.packageManager ?? resolvePackageManager(cwd);
|
|
228
224
|
const args = installArgs(pm, missing);
|
|
225
|
+
const installHint = installCommandFrom(cwd, pm, missing);
|
|
229
226
|
if (props.install === false) {
|
|
230
|
-
if (!props.silent) log.info("Install the Neon config packages to use neon.ts: %s",
|
|
227
|
+
if (!props.silent) log.info("Install the Neon config packages to use neon.ts: %s", installHint);
|
|
231
228
|
return;
|
|
232
229
|
}
|
|
230
|
+
const pkgPath = join(cwd, "package.json");
|
|
231
|
+
if (!existsSync(pkgPath)) {
|
|
232
|
+
writeFileSync(pkgPath, "{}\n");
|
|
233
|
+
if (!props.silent) log.info("Created package.json.");
|
|
234
|
+
}
|
|
233
235
|
ensureDirectoryGitignored(join(cwd, "node_modules"));
|
|
234
236
|
if (!props.silent) log.info("Installing %s with %s…", missing.join(", "), pm);
|
|
235
237
|
if (!await run(pm, args, cwd)) {
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
log.warning("Could not install the config packages automatically. Run by hand: %s", command);
|
|
238
|
+
if (props.requireInstall) throw new ConfigInstallFailed(installHint);
|
|
239
|
+
log.warning("Could not install the config packages automatically. Run by hand: %s", installHint);
|
|
239
240
|
}
|
|
240
241
|
};
|
|
241
242
|
/**
|
|
@@ -244,7 +245,7 @@ const ensureConfigPackages = async (cwd, props) => {
|
|
|
244
245
|
* Local-only unless `--from-branch` is set (see {@link isConfigInit}).
|
|
245
246
|
*/
|
|
246
247
|
const initCmd = async (props) => {
|
|
247
|
-
const cwd = props.cwd ??
|
|
248
|
+
const cwd = props.cwd ?? projectDir(props.contextFile ? { contextFile: props.contextFile } : {});
|
|
248
249
|
const existing = neonConfigFilename(cwd);
|
|
249
250
|
if (existing) {
|
|
250
251
|
if (!props.silent) {
|
|
@@ -274,26 +275,11 @@ const initCmd = async (props) => {
|
|
|
274
275
|
const FUNCTION_SLUG_PATTERN = /^[a-z0-9]{1,20}$/;
|
|
275
276
|
const SCRIPT_EXTENSION = /\.(?:ts|mts|cts|js|mjs|cjs)$/;
|
|
276
277
|
const TYPESCRIPT_EXTENSION = /\.(?:ts|mts|cts)$/;
|
|
277
|
-
/**
|
|
278
|
-
* The nearest neon.ts, found the way the config loader finds it: walk up from `start`, stop
|
|
279
|
-
* at the first directory holding `.git` (or at the home directory).
|
|
280
|
-
*/
|
|
281
|
-
const findNeonConfigUpward = (start) => {
|
|
282
|
-
let dir = resolve(start);
|
|
283
|
-
for (;;) {
|
|
284
|
-
const name = neonConfigFilename(dir);
|
|
285
|
-
if (name) return join(dir, name);
|
|
286
|
-
if (existsSync(join(dir, ".git")) || dir === homedir()) return void 0;
|
|
287
|
-
const parent = dirname(dir);
|
|
288
|
-
if (parent === dir) return void 0;
|
|
289
|
-
dir = parent;
|
|
290
|
-
}
|
|
291
|
-
};
|
|
292
278
|
const asRelativeModulePath = (path) => path.startsWith("./") || path.startsWith("../") || path.startsWith("/") ? path : `./${path}`;
|
|
293
279
|
const shellQuote = (value) => /^[\w@%+=:,./-]+$/.test(value) ? value : `'${value.replace(/'/g, "'\\''")}'`;
|
|
294
280
|
/**
|
|
295
281
|
* `explicitConfig` is the `--config` the user passed. The commands suggested afterwards must
|
|
296
|
-
* carry it, or they would act on
|
|
282
|
+
* carry it, or they would act on the project directory's `neon.ts` instead.
|
|
297
283
|
*/
|
|
298
284
|
const planAdd = (target, configPath, explicitConfig) => {
|
|
299
285
|
const language = TYPESCRIPT_EXTENSION.test(configPath) ? "ts" : "js";
|
|
@@ -346,12 +332,16 @@ const planAdd = (target, configPath, explicitConfig) => {
|
|
|
346
332
|
*/
|
|
347
333
|
const addCmd = async (props) => {
|
|
348
334
|
const cwd = props.cwd ?? process.cwd();
|
|
335
|
+
const location = {
|
|
336
|
+
cwd,
|
|
337
|
+
...props.contextFile ? { contextFile: props.contextFile } : {}
|
|
338
|
+
};
|
|
349
339
|
let existing;
|
|
350
340
|
if (props.config) {
|
|
351
341
|
existing = resolve(cwd, props.config);
|
|
352
342
|
if (!existsSync(existing)) throw new Error(`--config ${props.config} does not exist.`);
|
|
353
|
-
} else existing =
|
|
354
|
-
const configPath = existing ?? join(
|
|
343
|
+
} else existing = projectConfigPath(location);
|
|
344
|
+
const configPath = existing ?? join(projectDir(location), "neon.ts");
|
|
355
345
|
const shown = (path) => relative(cwd, path) || path;
|
|
356
346
|
const plan = planAdd(props.target, configPath, props.config);
|
|
357
347
|
const { source, changes } = editNeonConfig(existing ? readFileSync(existing, "utf8") : renderNeonConfig([]), plan.edit);
|
|
@@ -385,7 +375,7 @@ const addSharedOptions = (yargs) => yargs.strict().options({
|
|
|
385
375
|
"project-id": { hidden: true },
|
|
386
376
|
branch: { hidden: true },
|
|
387
377
|
config: {
|
|
388
|
-
describe: "Path to the neon.ts to edit (defaults to
|
|
378
|
+
describe: "Path to the neon.ts to edit (defaults to the neon.ts next to .neon, or in cwd without one; created there when missing)",
|
|
389
379
|
type: "string"
|
|
390
380
|
},
|
|
391
381
|
install: {
|
|
@@ -398,7 +388,8 @@ const addSharedOptions = (yargs) => yargs.strict().options({
|
|
|
398
388
|
const addProps = (args, target) => ({
|
|
399
389
|
target,
|
|
400
390
|
install: args.install,
|
|
401
|
-
...args.config !== void 0 ? { config: args.config } : {}
|
|
391
|
+
...args.config !== void 0 ? { config: args.config } : {},
|
|
392
|
+
...args.contextFile !== void 0 ? { contextFile: args.contextFile } : {}
|
|
402
393
|
});
|
|
403
394
|
const command = "config";
|
|
404
395
|
const describe = "Manage a branch with a neon.ts policy";
|
|
@@ -424,13 +415,13 @@ const builder = (argv) => argv.usage("$0 config <sub-command> [options]").option
|
|
|
424
415
|
}
|
|
425
416
|
}), (args) => status(args)).command("plan", "Show what `config apply` would change (dry run)", (yargs) => yargs.options({
|
|
426
417
|
config: {
|
|
427
|
-
describe: "Path to a neon.ts policy (defaults to
|
|
418
|
+
describe: "Path to a neon.ts policy (defaults to the neon.ts next to .neon, or in cwd without one)",
|
|
428
419
|
type: "string"
|
|
429
420
|
},
|
|
430
421
|
...envFlag
|
|
431
422
|
}), (args) => planCmd(args)).command("apply", "Apply a neon.ts policy to the branch", (yargs) => yargs.options({
|
|
432
423
|
config: {
|
|
433
|
-
describe: "Path to a neon.ts policy (defaults to
|
|
424
|
+
describe: "Path to a neon.ts policy (defaults to the neon.ts next to .neon, or in cwd without one)",
|
|
434
425
|
type: "string"
|
|
435
426
|
},
|
|
436
427
|
...envFlag,
|
|
@@ -539,7 +530,10 @@ const loadEnvFileIfGiven = (env) => {
|
|
|
539
530
|
};
|
|
540
531
|
const loadConfig = async (props) => {
|
|
541
532
|
loadEnvFileIfGiven(props.env);
|
|
542
|
-
const { config } = await
|
|
533
|
+
const { config } = await loadProjectConfig({
|
|
534
|
+
contextFile: props.contextFile,
|
|
535
|
+
...props.config ? { path: props.config } : {}
|
|
536
|
+
});
|
|
543
537
|
return config;
|
|
544
538
|
};
|
|
545
539
|
const status = async (props) => {
|
|
@@ -849,7 +843,10 @@ const applyPolicyOnCreate = async (props) => {
|
|
|
849
843
|
loadEnvFileIfGiven(props.env);
|
|
850
844
|
let config;
|
|
851
845
|
try {
|
|
852
|
-
({config} = await
|
|
846
|
+
({config} = await loadProjectConfig({
|
|
847
|
+
...props.cwd ? { cwd: props.cwd } : {},
|
|
848
|
+
...props.contextFile ? { contextFile: props.contextFile } : {}
|
|
849
|
+
}));
|
|
853
850
|
} catch (err) {
|
|
854
851
|
const message = err instanceof Error ? err.message : String(err);
|
|
855
852
|
if (/Could not find a Neon config file/i.test(message)) return;
|
|
@@ -930,7 +927,10 @@ const createBranchFromPolicyOnCheckout = async (props) => {
|
|
|
930
927
|
loadEnvFileIfGiven(props.env);
|
|
931
928
|
let config;
|
|
932
929
|
try {
|
|
933
|
-
({config} = await
|
|
930
|
+
({config} = await loadProjectConfig({
|
|
931
|
+
...props.cwd ? { cwd: props.cwd } : {},
|
|
932
|
+
...props.contextFile ? { contextFile: props.contextFile } : {}
|
|
933
|
+
}));
|
|
934
934
|
} catch (err) {
|
|
935
935
|
const message = err instanceof Error ? err.message : String(err);
|
|
936
936
|
if (/Could not find a Neon config file/i.test(message)) return null;
|
|
@@ -983,4 +983,4 @@ const createBranchFromPolicyOnCheckout = async (props) => {
|
|
|
983
983
|
}
|
|
984
984
|
};
|
|
985
985
|
//#endregion
|
|
986
|
-
export { ConfigInstallFailed, addCmd, applyCmd, applyFlags, applyPolicyOnCreate, builder, command, createBranchFromPolicyOnCheckout, describe, envFlag, envPullFlag, handler,
|
|
986
|
+
export { ConfigInstallFailed, addCmd, applyCmd, applyFlags, applyPolicyOnCreate, builder, command, createBranchFromPolicyOnCheckout, describe, envFlag, envPullFlag, handler, initCmd, planCmd, status, config_exports as t };
|
package/dist/commands/deploy.js
CHANGED
|
@@ -21,7 +21,7 @@ const builder = (argv) => argv.usage("$0 deploy [options]").options({
|
|
|
21
21
|
type: "string"
|
|
22
22
|
},
|
|
23
23
|
config: {
|
|
24
|
-
describe: "Path to a neon.ts policy (defaults to
|
|
24
|
+
describe: "Path to a neon.ts policy (defaults to the neon.ts next to .neon, or in cwd without one)",
|
|
25
25
|
type: "string"
|
|
26
26
|
},
|
|
27
27
|
...envFlag,
|
package/dist/commands/dev.js
CHANGED
|
@@ -6,7 +6,7 @@ import { readEnvFile, resolveEnvFilePath } from "../env_file.js";
|
|
|
6
6
|
import { l as functionBaseUrlKey } from "../_chunks/env-N19A7qND.js";
|
|
7
7
|
import { branchIdResolve } from "../utils/enrichers.js";
|
|
8
8
|
import { bundleEntry } from "../utils/esbuild.js";
|
|
9
|
-
import { a as resolveDevEnv } from "../_chunks/env-
|
|
9
|
+
import { a as resolveDevEnv } from "../_chunks/env-BgiaRNT1.js";
|
|
10
10
|
import { findConfigFunctionBySource, resolveFunctionsFromConfig } from "../dev/functions.js";
|
|
11
11
|
import { resolveWatchInputs } from "../dev/inputs.js";
|
|
12
12
|
import { existsSync, mkdirSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
@@ -75,9 +75,13 @@ const builder = (argv) => argv.usage("$0 dev [--source <path>] [options]").examp
|
|
|
75
75
|
* reason; `dev` was the one that didn't.
|
|
76
76
|
*/
|
|
77
77
|
const devEnvContext = (props, branchId, cwd) => {
|
|
78
|
-
const
|
|
79
|
-
return {
|
|
78
|
+
const location = {
|
|
80
79
|
cwd,
|
|
80
|
+
...props.contextFile ? { contextFile: props.contextFile } : {}
|
|
81
|
+
};
|
|
82
|
+
const envFile = resolveEnvFilePath(location);
|
|
83
|
+
return {
|
|
84
|
+
...location,
|
|
81
85
|
implyAiGateway: true,
|
|
82
86
|
env: {
|
|
83
87
|
...process.env,
|
|
@@ -124,7 +128,7 @@ const runSingleSource = async (props) => {
|
|
|
124
128
|
const branchId = await resolveBranchId(props);
|
|
125
129
|
const { vars: neonEnv, skipped, credential } = await resolveDevEnv(devEnvContext(props, branchId, process.cwd()));
|
|
126
130
|
reportDevCredential(credential);
|
|
127
|
-
const match = await findConfigFunctionBySource(process.cwd(), source);
|
|
131
|
+
const match = await findConfigFunctionBySource(process.cwd(), source, props.contextFile);
|
|
128
132
|
const { overlay, port } = await sourceFunctionUrlOverlay(match, props.port);
|
|
129
133
|
const unit = {
|
|
130
134
|
slug: null,
|
|
@@ -148,7 +152,7 @@ const runSingleSource = async (props) => {
|
|
|
148
152
|
*/
|
|
149
153
|
const runFromConfig = async (props) => {
|
|
150
154
|
const branchId = await resolveBranchId(props);
|
|
151
|
-
const resolved = await resolveFunctionsFromConfig(process.cwd());
|
|
155
|
+
const resolved = await resolveFunctionsFromConfig(process.cwd(), void 0, props.contextFile);
|
|
152
156
|
if (resolved === null) throw new Error("No --source given and no neon.ts found. Pass --source <path> to run a single function, or add a neon.ts that declares functions under `functions`.");
|
|
153
157
|
const { configPath, functions } = resolved;
|
|
154
158
|
if (functions.length === 0) throw new Error("neon.ts has no functions to serve. Add at least one under `functions`, or pass --source <path>.");
|
|
@@ -156,7 +160,7 @@ const runFromConfig = async (props) => {
|
|
|
156
160
|
reportDevCredential(credential);
|
|
157
161
|
const units = await planFunctionsToUnits(functions, neonEnv, DEFAULT_PORT_BASE);
|
|
158
162
|
const replan = async (searchBase, keepPorts) => {
|
|
159
|
-
const re = await resolveFunctionsFromConfig(process.cwd());
|
|
163
|
+
const re = await resolveFunctionsFromConfig(process.cwd(), void 0, props.contextFile);
|
|
160
164
|
if (re === null) return null;
|
|
161
165
|
return planFunctionsToUnits(re.functions, neonEnv, searchBase, keepPorts);
|
|
162
166
|
};
|
package/dist/commands/env.js
CHANGED
|
@@ -10,7 +10,7 @@ import { ENV_PULL_KEY_HELP, ENV_PULL_SERVICES, envKeysForSelection, envPullFlagV
|
|
|
10
10
|
import { fillSingleProject, resolveBranchRef } from "../utils/enrichers.js";
|
|
11
11
|
import { warnAiGateway } from "../utils/ai_gateway_notice.js";
|
|
12
12
|
import { announceTargetBranch } from "../utils/branch_notice.js";
|
|
13
|
-
import { i as dropClaimableUnsupported, o as resolveNeonEnvVars } from "../_chunks/env-
|
|
13
|
+
import { i as dropClaimableUnsupported, o as resolveNeonEnvVars } from "../_chunks/env-BgiaRNT1.js";
|
|
14
14
|
import { existsSync } from "node:fs";
|
|
15
15
|
import chalk from "chalk";
|
|
16
16
|
//#region src/commands/env.ts
|
|
@@ -42,7 +42,7 @@ const builder = (argv) => argv.usage("$0 env <sub-command> [options]").options({
|
|
|
42
42
|
}
|
|
43
43
|
}).middleware(fillSingleProject).command("pull", "Write the branch's Neon env variables to a local .env file", (yargs) => yargs.usage("$0 env pull [options]").options({
|
|
44
44
|
file: {
|
|
45
|
-
describe: "Target .env file to write. Defaults to an existing .env, otherwise .env.local. Only Neon variables are updated; other lines are preserved.",
|
|
45
|
+
describe: "Target .env file to write, relative to cwd. Defaults to an existing .env, otherwise .env.local, in the project directory (next to .neon, or cwd without one). Only Neon variables are updated; other lines are preserved.",
|
|
46
46
|
type: "string"
|
|
47
47
|
},
|
|
48
48
|
service: servicesOption({
|
|
@@ -58,15 +58,16 @@ const builder = (argv) => argv.usage("$0 env <sub-command> [options]").options({
|
|
|
58
58
|
string: true
|
|
59
59
|
},
|
|
60
60
|
config: {
|
|
61
|
-
describe: "Path to a neon.ts policy (defaults to
|
|
61
|
+
describe: "Path to a neon.ts policy (defaults to the neon.ts next to .neon, or in cwd without one)",
|
|
62
62
|
type: "string"
|
|
63
63
|
}
|
|
64
64
|
}).epilogue([
|
|
65
65
|
"",
|
|
66
66
|
"What gets pulled, in precedence order:",
|
|
67
67
|
" 1. --service / --env, when given — their union, ignoring neon.ts.",
|
|
68
|
-
" 2. neon.ts, when
|
|
69
|
-
"
|
|
68
|
+
" 2. neon.ts, when the project directory (next to .neon, or cwd",
|
|
69
|
+
" without one) has one — including derived function URLs (the",
|
|
70
|
+
" function does not have to be deployed).",
|
|
70
71
|
" 3. Otherwise everything the branch has, plus the AI Gateway —",
|
|
71
72
|
" which pulls the default AI Gateway credential.",
|
|
72
73
|
"",
|
|
@@ -135,11 +136,15 @@ const pull = async (props, opts = {}) => {
|
|
|
135
136
|
const branch = await resolveBranchRef(props);
|
|
136
137
|
if (opts.announce) announceTargetBranch(props, branch, "Pulling env from branch");
|
|
137
138
|
const branchId = branch.branchId;
|
|
138
|
-
const
|
|
139
|
+
const location = {
|
|
140
|
+
cwd,
|
|
141
|
+
contextFile: props.contextFile
|
|
142
|
+
};
|
|
143
|
+
const targetPath = resolveEnvFilePath(location, props.file);
|
|
139
144
|
const fileExisted = existsSync(targetPath);
|
|
140
145
|
const existingEnv = fileExisted ? readEnvFile(targetPath) : {};
|
|
141
146
|
const { vars, credential, skipped } = await resolveNeonEnvVars({
|
|
142
|
-
|
|
147
|
+
...location,
|
|
143
148
|
projectId: props.projectId,
|
|
144
149
|
branchId,
|
|
145
150
|
env: {
|
package/dist/commands/init.js
CHANGED
|
@@ -17,7 +17,11 @@ var init_exports = /* @__PURE__ */ __exportAll({
|
|
|
17
17
|
const command = "init";
|
|
18
18
|
const describe = "Set up coding agents and this directory for Neon. -y is Recommended. -y with Custom flags applies those answers without prompts.";
|
|
19
19
|
const removedProtocol = () => `\`${getCliName()} init --data\` was removed. Run \`${getCliName()} init\` or \`${getCliName()} init -y\`.`;
|
|
20
|
-
const builder = (yargs) => yargs.usage("$0 init [options]").option("context-file", {
|
|
20
|
+
const builder = (yargs) => yargs.usage("$0 init [options]").option("context-file", {
|
|
21
|
+
hidden: true,
|
|
22
|
+
type: "string",
|
|
23
|
+
default: ".neon"
|
|
24
|
+
}).option("yes", {
|
|
21
25
|
alias: "y",
|
|
22
26
|
type: "boolean",
|
|
23
27
|
default: false,
|
package/dist/commands/link.js
CHANGED
|
@@ -7,12 +7,13 @@ import "../config.js";
|
|
|
7
7
|
import { applyContext, contextBranch, readContextFile, setContext, updateContextFile } from "../context.js";
|
|
8
8
|
import { getCliName } from "../utils/cli_name.js";
|
|
9
9
|
import { helpEpilogue } from "../utils/help_text.js";
|
|
10
|
+
import { hasNeonConfigFile, projectDir } from "../project.js";
|
|
10
11
|
import { writer } from "../writer.js";
|
|
11
12
|
import { createBranch, pickBranchInteractively } from "../utils/branch_picker.js";
|
|
12
13
|
import { looksLikeBranchId } from "../utils/formats.js";
|
|
13
14
|
import { listAllProjectBranches } from "../utils/enrichers.js";
|
|
14
15
|
import { autoPullEnvAfterPin } from "./env.js";
|
|
15
|
-
import {
|
|
16
|
+
import { initCmd } from "./config.js";
|
|
16
17
|
import { REGIONS } from "./projects.js";
|
|
17
18
|
import prompts from "prompts";
|
|
18
19
|
//#region src/commands/link.ts
|
|
@@ -848,9 +849,12 @@ const finalizeInteractiveLink = async (props, summary) => {
|
|
|
848
849
|
*/
|
|
849
850
|
const shouldOfferConfigInit = (input) => !input.hasConfig && input.offer;
|
|
850
851
|
const maybeOfferConfigInit = async (props, summary) => {
|
|
851
|
-
const
|
|
852
|
+
const dir = projectDir({
|
|
853
|
+
...props.cwd ? { cwd: props.cwd } : {},
|
|
854
|
+
contextFile: props.contextFile
|
|
855
|
+
});
|
|
852
856
|
if (!shouldOfferConfigInit({
|
|
853
|
-
hasConfig: hasNeonConfigFile(
|
|
857
|
+
hasConfig: hasNeonConfigFile(dir),
|
|
854
858
|
offer: props.config !== false
|
|
855
859
|
})) return;
|
|
856
860
|
const { value } = await prompts({
|
|
@@ -862,14 +866,13 @@ const maybeOfferConfigInit = async (props, summary) => {
|
|
|
862
866
|
});
|
|
863
867
|
if (value !== true) return;
|
|
864
868
|
await initCmd({
|
|
865
|
-
cwd,
|
|
869
|
+
cwd: dir,
|
|
866
870
|
install: true
|
|
867
871
|
});
|
|
868
872
|
if (summary.branch && summary.projectId) {
|
|
869
873
|
const { config: _offerConfig, ...rest } = props;
|
|
870
874
|
await autoPullEnvAfterPin({
|
|
871
875
|
...rest,
|
|
872
|
-
cwd,
|
|
873
876
|
projectId: summary.projectId,
|
|
874
877
|
branch: summary.branch,
|
|
875
878
|
envPull: props.envPull
|
package/dist/commands/user.js
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import { t as __exportAll } from "../_chunks/rolldown-runtime-8H4AJuhK.js";
|
|
2
|
+
import { getAuthContext } from "../auth_context.js";
|
|
2
3
|
import { writer } from "../writer.js";
|
|
3
4
|
//#region src/commands/user.ts
|
|
4
5
|
var user_exports = /* @__PURE__ */ __exportAll({
|
|
6
|
+
authenticationLabel: () => authenticationLabel,
|
|
5
7
|
builder: () => builder,
|
|
6
8
|
command: () => "me",
|
|
7
9
|
describe: () => describe,
|
|
8
|
-
handler: () => handler
|
|
10
|
+
handler: () => handler,
|
|
11
|
+
userDetails: () => userDetails
|
|
9
12
|
});
|
|
10
13
|
const command = "me";
|
|
11
14
|
const describe = "Show current user";
|
|
@@ -15,12 +18,42 @@ const handler = async (args) => {
|
|
|
15
18
|
};
|
|
16
19
|
const me = async (props) => {
|
|
17
20
|
const { data } = await props.apiClient.getCurrentUserInfo();
|
|
18
|
-
|
|
21
|
+
if (props.output === "json" || props.output === "yaml") {
|
|
22
|
+
writer(props).end(data, { fields: [
|
|
23
|
+
"login",
|
|
24
|
+
"email",
|
|
25
|
+
"name",
|
|
26
|
+
"projects_limit"
|
|
27
|
+
] });
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
writer(props).end(userDetails(data, getAuthContext()), { fields: [
|
|
19
31
|
"login",
|
|
20
32
|
"email",
|
|
21
33
|
"name",
|
|
22
|
-
"projects_limit"
|
|
34
|
+
"projects_limit",
|
|
35
|
+
"authentication"
|
|
23
36
|
] });
|
|
24
37
|
};
|
|
38
|
+
/** Table-only view; the field names become the row labels. */
|
|
39
|
+
const userDetails = (user, auth) => ({
|
|
40
|
+
login: user.login,
|
|
41
|
+
email: user.email,
|
|
42
|
+
name: [user.name, user.last_name].filter(Boolean).join(" "),
|
|
43
|
+
projects_limit: user.projects_limit === 0 ? void 0 : user.projects_limit,
|
|
44
|
+
authentication: auth ? authenticationLabel(auth) : void 0
|
|
45
|
+
});
|
|
46
|
+
const authenticationLabel = (auth) => {
|
|
47
|
+
const profile = auth.profile ? ` (profile ${auth.profile})` : "";
|
|
48
|
+
switch (auth.source) {
|
|
49
|
+
case "stored-credentials": return `OAuth${profile}`;
|
|
50
|
+
case "profile-api-key": return `API key${profile}`;
|
|
51
|
+
case "api-key":
|
|
52
|
+
if (auth.apiKeyFrom === "flag") return "API key (--api-key)";
|
|
53
|
+
if (auth.apiKeyFrom === "env") return "API key (NEON_API_KEY)";
|
|
54
|
+
return "API key";
|
|
55
|
+
case "claimable": return "Claimable project";
|
|
56
|
+
}
|
|
57
|
+
};
|
|
25
58
|
//#endregion
|
|
26
|
-
export { builder, command, describe, handler, user_exports as t };
|
|
59
|
+
export { authenticationLabel, builder, command, describe, handler, user_exports as t, userDetails };
|
package/dist/dev/env.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as resolveDevEnv, i as dropClaimableUnsupported, n as MissingBranchContextError, o as resolveNeonEnvVars, r as ServiceNotOnBranchError, t as DevEnvMismatchError } from "../_chunks/env-
|
|
1
|
+
import { a as resolveDevEnv, i as dropClaimableUnsupported, n as MissingBranchContextError, o as resolveNeonEnvVars, r as ServiceNotOnBranchError, t as DevEnvMismatchError } from "../_chunks/env-BgiaRNT1.js";
|
|
2
2
|
export { DevEnvMismatchError, MissingBranchContextError, ServiceNotOnBranchError, dropClaimableUnsupported, resolveDevEnv, resolveNeonEnvVars };
|
package/dist/dev/functions.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import { loadProjectConfig } from "../project.js";
|
|
1
2
|
import { existsSync } from "node:fs";
|
|
2
3
|
import { dirname, isAbsolute, resolve } from "node:path";
|
|
3
|
-
import {
|
|
4
|
+
import { resolveConfig } from "@neon/config";
|
|
4
5
|
//#region src/dev/functions.ts
|
|
5
6
|
/**
|
|
6
7
|
* Load `neon.ts` (if any) and resolve the list of functions it declares into
|
|
@@ -11,8 +12,8 @@ import { loadConfigFromFile, resolveConfig } from "@neon/config";
|
|
|
11
12
|
* `branchName` is used only to evaluate a policy that switches on `branch.name`; the
|
|
12
13
|
* function list is otherwise branch-independent, so a placeholder is fine when unknown.
|
|
13
14
|
*/
|
|
14
|
-
const resolveFunctionsFromConfig = async (cwd, branchName) => {
|
|
15
|
-
const loaded = await loadNeonConfig(cwd);
|
|
15
|
+
const resolveFunctionsFromConfig = async (cwd, branchName, contextFile) => {
|
|
16
|
+
const loaded = await loadNeonConfig(cwd, contextFile);
|
|
16
17
|
if (!loaded) return null;
|
|
17
18
|
const { config, configDir, configPath } = loaded;
|
|
18
19
|
return {
|
|
@@ -31,8 +32,8 @@ const resolveFunctionsFromConfig = async (cwd, branchName) => {
|
|
|
31
32
|
* The neon.ts function whose `source` is this file, if any. Sibling sources are not
|
|
32
33
|
* required to exist: `neon dev --source` is serving one file, not the whole set.
|
|
33
34
|
*/
|
|
34
|
-
const findConfigFunctionBySource = async (cwd, source) => {
|
|
35
|
-
const loaded = await loadNeonConfig(cwd);
|
|
35
|
+
const findConfigFunctionBySource = async (cwd, source, contextFile) => {
|
|
36
|
+
const loaded = await loadNeonConfig(cwd, contextFile);
|
|
36
37
|
if (!loaded) return void 0;
|
|
37
38
|
return (resolveConfig(loaded.config, {
|
|
38
39
|
name: "local",
|
|
@@ -63,9 +64,12 @@ const devPort = (dev) => dev?.port;
|
|
|
63
64
|
* to resolve each function's relative `source`). Returns `null` when no config file is
|
|
64
65
|
* found; surfaces real load errors (e.g. a syntax error).
|
|
65
66
|
*/
|
|
66
|
-
const loadNeonConfig = async (cwd) => {
|
|
67
|
+
const loadNeonConfig = async (cwd, contextFile) => {
|
|
67
68
|
try {
|
|
68
|
-
const { config, resolvedPath } = await
|
|
69
|
+
const { config, resolvedPath } = await loadProjectConfig({
|
|
70
|
+
cwd,
|
|
71
|
+
...contextFile ? { contextFile } : {}
|
|
72
|
+
});
|
|
69
73
|
return {
|
|
70
74
|
config,
|
|
71
75
|
configDir: dirname(resolvedPath),
|
package/dist/env_file.js
CHANGED
|
@@ -1,15 +1,18 @@
|
|
|
1
|
+
import { projectDir } from "./project.js";
|
|
1
2
|
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
3
|
import { join, resolve } from "node:path";
|
|
3
4
|
//#region src/env_file.ts
|
|
4
5
|
/**
|
|
5
|
-
* Default dotenv file `env pull` writes to: `.env` when one already exists in the
|
|
6
|
+
* Default dotenv file `env pull` writes to: `.env` when one already exists in the project
|
|
6
7
|
* directory (update where secrets already live), otherwise `.env.local` — matching the
|
|
7
|
-
* `vercel env pull` convention. An explicit `--file` always wins over this
|
|
8
|
+
* `vercel env pull` convention. An explicit `--file` always wins over this and is
|
|
9
|
+
* relative to `cwd`.
|
|
8
10
|
*/
|
|
9
|
-
const resolveEnvFilePath = (
|
|
10
|
-
if (file) return resolve(cwd, file);
|
|
11
|
-
|
|
12
|
-
return join(
|
|
11
|
+
const resolveEnvFilePath = (location, file) => {
|
|
12
|
+
if (file) return resolve(location.cwd, file);
|
|
13
|
+
const dir = projectDir(location);
|
|
14
|
+
if (existsSync(join(dir, ".env"))) return join(dir, ".env");
|
|
15
|
+
return join(dir, ".env.local");
|
|
13
16
|
};
|
|
14
17
|
/**
|
|
15
18
|
* Merge `updates` into the dotenv content at `path`, preserving every other line
|
package/dist/init/run.js
CHANGED
|
@@ -8,6 +8,7 @@ import { readLinkedClaimableCredentials } from "../claimable/state.js";
|
|
|
8
8
|
import { getCliName } from "../utils/cli_name.js";
|
|
9
9
|
import { deprecatedServiceMessage, parseServices, servicesFlagValue } from "../neon_services.js";
|
|
10
10
|
import { CONFIG_INIT_NONE_MEANS, CONFIG_INIT_SERVICES, CONFIG_INIT_UNAVAILABLE } from "../config_template.js";
|
|
11
|
+
import { hasNeonConfigFile, neonConfigFilename } from "../project.js";
|
|
11
12
|
import { getAgentDisplayName } from "../mcp/agents.js";
|
|
12
13
|
import { mcpInstallableAgents } from "../mcp/targets.js";
|
|
13
14
|
import { missingPluginsCommands, pluginsInstallableAgents } from "../plugins/targets.js";
|
|
@@ -16,7 +17,7 @@ import { FALLBACK_SKILLS_AGENTS, assertNamedAgentTooling, funnelAgentSetup, plan
|
|
|
16
17
|
import { inferPackageManager, resolvePackageManager } from "../utils/package_manager.js";
|
|
17
18
|
import { CANCELLED_BODY, CLAIMABLE_ACCOUNT_FLAGS, CLAIMABLE_ALREADY_LINKED, CLAIMABLE_MCP_API_KEY, CLAIMABLE_NO_LINK, MCP_SCOPED_NEEDS_PROJECT, NON_TTY_LINK_NEEDS_AUTH, NO_AGENTS_FALLBACK_STATUS, PROGRESS, YES_LINK_NEEDS_AUTH, claimableNext, claimableThenServicesNext, envPullFailedNext, existingConfigNext, extraServicesNext, installFailedNext, mcpConfigLocationSkipped, mcpConfigLocationUnavailable, namedAgentsUnavailable, skippedLinkNext, unattendedUnauthedNext } from "./copy.js";
|
|
18
19
|
import { agentsRowValue, configRowValue, formatInitDone, headingForKind, printInitBanner, printInitDone, printInitProgress, projectRowValue, shouldPrintInitBanner } from "./chrome.js";
|
|
19
|
-
import { ConfigInstallFailed,
|
|
20
|
+
import { ConfigInstallFailed, initCmd } from "../commands/config.js";
|
|
20
21
|
import { runAuthenticatedLink } from "./link.js";
|
|
21
22
|
import { pullInitEnv } from "./auth.js";
|
|
22
23
|
import { InitCancelled, raceSigint, restoreCursor } from "./cancelled.js";
|
package/dist/project.js
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { currentContextFile } from "./context.js";
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
3
|
+
import { dirname, join, resolve } from "node:path";
|
|
4
|
+
import { ConfigLoadError, loadConfigFromFile } from "@neon/config";
|
|
5
|
+
//#region src/project.ts
|
|
6
|
+
/** Config filenames the runtime loads (mirrors @neon/config's loader). */
|
|
7
|
+
const NEON_CONFIG_FILENAMES = [
|
|
8
|
+
"neon.ts",
|
|
9
|
+
"neon.mts",
|
|
10
|
+
"neon.js",
|
|
11
|
+
"neon.mjs"
|
|
12
|
+
];
|
|
13
|
+
const neonConfigFilename = (dir) => NEON_CONFIG_FILENAMES.find((name) => existsSync(join(dir, name)));
|
|
14
|
+
/** Whether `dir` already has a Neon config file the runtime would load. */
|
|
15
|
+
const hasNeonConfigFile = (dir) => neonConfigFilename(dir) !== void 0;
|
|
16
|
+
/**
|
|
17
|
+
* The project a command acts on: the directory holding its `.neon` (the nearest one at or
|
|
18
|
+
* above cwd unless `--context-file` says otherwise), else cwd. Its `neon.ts` and default
|
|
19
|
+
* `.env.local` live there and nowhere else, so a stray file in a parent directory never
|
|
20
|
+
* applies.
|
|
21
|
+
*/
|
|
22
|
+
const projectDir = ({ cwd = process.cwd(), contextFile } = {}) => dirname(contextFile ? resolve(cwd, contextFile) : currentContextFile(cwd));
|
|
23
|
+
/** Absolute path of the project's `neon.ts` (or other supported extension), if any. */
|
|
24
|
+
const projectConfigPath = (location = {}) => {
|
|
25
|
+
const dir = projectDir(location);
|
|
26
|
+
const name = neonConfigFilename(dir);
|
|
27
|
+
return name === void 0 ? void 0 : join(dir, name);
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Load the project's Neon config. An explicit `path` (`--config`) wins; otherwise only
|
|
31
|
+
* {@link projectDir} is checked. Callers match "Could not find a Neon config file" to tell
|
|
32
|
+
* a missing config from a broken one.
|
|
33
|
+
*/
|
|
34
|
+
const loadProjectConfig = async (options) => {
|
|
35
|
+
const { cwd, unsetFunctionEnv } = options;
|
|
36
|
+
const path = options.path ?? projectConfigPath(options);
|
|
37
|
+
if (path === void 0) throw new ConfigLoadError([
|
|
38
|
+
`Could not find a Neon config file in ${projectDir(options)}.`,
|
|
39
|
+
`Looked for: ${NEON_CONFIG_FILENAMES.join(", ")} next to the project's .neon (or in the current directory when there is no .neon).`,
|
|
40
|
+
"Create one with `neon config init`, or pass `--config <path>`."
|
|
41
|
+
].join("\n"));
|
|
42
|
+
return await loadConfigFromFile({
|
|
43
|
+
path,
|
|
44
|
+
...cwd !== void 0 ? { cwd } : {},
|
|
45
|
+
...unsetFunctionEnv !== void 0 ? { unsetFunctionEnv } : {}
|
|
46
|
+
});
|
|
47
|
+
};
|
|
48
|
+
//#endregion
|
|
49
|
+
export { hasNeonConfigFile, loadProjectConfig, neonConfigFilename, projectConfigPath, projectDir };
|
package/dist/utils/hooks.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { log } from "../log.js";
|
|
2
|
+
import { loadProjectConfig } from "../project.js";
|
|
2
3
|
import { s as fetchEnvKeys } from "../_chunks/env-N19A7qND.js";
|
|
3
|
-
import {
|
|
4
|
+
import { runHook } from "@neon/config-runtime";
|
|
4
5
|
//#region src/utils/hooks.ts
|
|
5
6
|
/**
|
|
6
7
|
* Load the nearest `neon.ts` for hook discovery, or `undefined` when there is none — or when
|
|
@@ -13,9 +14,12 @@ import { loadConfigFromFile, runHook } from "@neon/config-runtime";
|
|
|
13
14
|
* `.experimental?.hooks` off the result for the hooks block, and pass the whole thing to
|
|
14
15
|
* {@link resolveHookEnv} so `after` hooks resolve exactly the namespaces the policy declares.
|
|
15
16
|
*/
|
|
16
|
-
const loadHookConfig = async (cwd) => {
|
|
17
|
+
const loadHookConfig = async (cwd, contextFile) => {
|
|
17
18
|
try {
|
|
18
|
-
const { config } = await
|
|
19
|
+
const { config } = await loadProjectConfig({
|
|
20
|
+
cwd,
|
|
21
|
+
...contextFile ? { contextFile } : {}
|
|
22
|
+
});
|
|
19
23
|
return config;
|
|
20
24
|
} catch (err) {
|
|
21
25
|
const message = err instanceof Error ? err.message : String(err);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "neon",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "8.0.0",
|
|
4
4
|
"description": "CLI tool for Neon, the cloud backend primitives built around Lakebase Postgres",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"neon",
|
|
@@ -64,9 +64,9 @@
|
|
|
64
64
|
"yaml": "^2.9.0",
|
|
65
65
|
"yargs": "17.7.2",
|
|
66
66
|
"yoctocolors": "^2.1.2",
|
|
67
|
+
"@neon/sdk": "7.0.0",
|
|
67
68
|
"@neon/config-runtime": "1.7.3",
|
|
68
|
-
"@neon/config": "1.8.3"
|
|
69
|
-
"@neon/sdk": "7.0.0"
|
|
69
|
+
"@neon/config": "1.8.3"
|
|
70
70
|
},
|
|
71
71
|
"optionalDependencies": {
|
|
72
72
|
"@napi-rs/keyring": "1.3.0",
|
|
@@ -99,10 +99,10 @@
|
|
|
99
99
|
"tsx": "4.22.3",
|
|
100
100
|
"typescript": "^5.9.0",
|
|
101
101
|
"vitest": "^3.0.9",
|
|
102
|
+
"@neon-internals/cli-core": "0.0.0",
|
|
102
103
|
"@neon-internals/env-core": "0.0.26",
|
|
103
104
|
"@neon/e2e-harness": "0.0.0",
|
|
104
|
-
"@neon/functions": "0.11.0"
|
|
105
|
-
"@neon-internals/cli-core": "0.0.0"
|
|
105
|
+
"@neon/functions": "0.11.0"
|
|
106
106
|
},
|
|
107
107
|
"publishConfig": {
|
|
108
108
|
"access": "public",
|