neon 4.19.0 → 4.21.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 -4
- package/dist/_chunks/{env-B75xufe_.js → env-DRpFX-_d.js} +17 -19
- package/dist/_chunks/{env_services-CbARRnq9.js → env_services-DlXECBPi.js} +1 -1
- package/dist/commands/checkout.js +1 -0
- package/dist/commands/config.js +97 -3
- package/dist/commands/dev.js +4 -4
- package/dist/commands/env.js +2 -2
- package/dist/commands/triggers.js +59 -14
- package/dist/config_services.js +4 -3
- package/dist/config_template.js +18 -18
- package/dist/dev/env.js +1 -1
- package/dist/env_services.js +1 -1
- package/dist/utils/ai_gateway_notice.js +2 -2
- package/dist/utils/enrichers.js +10 -2
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -583,7 +583,7 @@ If you'd rather not keep env vars on disk, inject them at runtime instead with `
|
|
|
583
583
|
|
|
584
584
|
## Config as code (`config` / `deploy`)
|
|
585
585
|
|
|
586
|
-
Describe a branch's desired state in a `neon.ts` policy and reconcile it from the CLI — the Neon equivalent of `terraform status` / `plan` / `apply`. A policy splits into a **static** existential set — top-level `auth` / `dataApi`
|
|
586
|
+
Describe a branch's desired state in a `neon.ts` policy and reconcile it from the CLI — the Neon equivalent of `terraform status` / `plan` / `apply`. A policy splits into a **static** existential set — top-level `auth` / `dataApi` / `aiGateway` / `functions` / `buckets` / `triggers` (`aiGateway`, `functions`, and `buckets` still work under deprecated `preview`) that decide what _exists_ — and a **dynamic** `branch` closure that tunes each branch (compute settings, TTL, protection, `parent`) based on the branch it's evaluated for (`name`, `isDefault`, …):
|
|
587
587
|
|
|
588
588
|
```ts
|
|
589
589
|
// neon.ts
|
|
@@ -729,7 +729,7 @@ neon config plan --project-id polished-snowflake-12345678 --output json
|
|
|
729
729
|
neon deploy --branch my-feature --update-existing
|
|
730
730
|
```
|
|
731
731
|
|
|
732
|
-
Function deploys declared under `
|
|
732
|
+
Function deploys declared under `functions` are bundled with esbuild by default. A directory `source` is discovered as `index.ts`, then `index.js`, then `index.mjs`. Set `bundler: "none"` to ship a prebuilt directory as-is. `neon function deploy --no-bundle` is the same switch without a `neon.ts`.
|
|
733
733
|
|
|
734
734
|
Point a domain you already own at a function with `neon function domains` (alias `domain`):
|
|
735
735
|
|
|
@@ -967,21 +967,23 @@ All sub-commands honor the [global options](#global-options), including `--outpu
|
|
|
967
967
|
|
|
968
968
|
## Function triggers (`triggers`)
|
|
969
969
|
|
|
970
|
-
`neon triggers` (alias `neon trigger`) manages
|
|
970
|
+
`neon triggers` (alias `neon trigger`) manages Function triggers on a branch. Types are `schedule` (a five-field UTC cron expression) and `storage_object_created` (an object-storage bucket, with an optional object-key prefix).
|
|
971
971
|
|
|
972
972
|
Every sub-command resolves the project through the standard chain (`--project-id`, then the `.neon` context file, then a single-project auto-detect) and the branch through `--branch <id|name>`, the `.neon` pin, or the project's default branch.
|
|
973
973
|
|
|
974
974
|
```bash
|
|
975
975
|
neon triggers create --function-slug uptime --name uptime-check --cron '*/15 * * * *'
|
|
976
|
+
neon triggers create --function-slug ingest --name on-upload --bucket assets --prefix 'logos/' --function-path /object
|
|
976
977
|
neon triggers list
|
|
977
978
|
neon triggers get trigger-test-123
|
|
978
979
|
neon triggers update trigger-test-123 --cron '0 3 * * *'
|
|
980
|
+
neon triggers update trigger-storage-123 --bucket assets --prefix 'incoming/'
|
|
979
981
|
neon triggers enable trigger-test-123
|
|
980
982
|
neon triggers disable trigger-test-123
|
|
981
983
|
neon triggers delete trigger-test-123
|
|
982
984
|
```
|
|
983
985
|
|
|
984
|
-
`enable` / `disable` are wrappers over `update --enabled`. List shows `inherited` when the effective config was authored on an ancestor branch.
|
|
986
|
+
`enable` / `disable` are wrappers over `update --enabled`. List shows `inherited` when the effective config was authored on an ancestor branch. `create` takes `--cron` or `--bucket`, not both.
|
|
985
987
|
|
|
986
988
|
## Branch credentials (`credentials`)
|
|
987
989
|
|
|
@@ -1,10 +1,10 @@
|
|
|
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 { C as resolveBranchPolicy, S as policyEnvKeys, _ as defaultStorageCredential, a as envKeysForSelection, b as isFunctionBaseUrlKey, d as servicesForEnvKeys, f as NEON_ENV_VAR_KEYS, g as defaultAiGatewayCredential, h as credentialName, i as ENV_PULL_SERVICES, m as credentialEnvKeys, p as createApiFromOptions, u as serviceForEnvKey, v as fetchEnvKeysState, w as toEntries, x as isLiveCredential, y as functionBaseUrlKey } from "./env_services-
|
|
4
|
+
import { C as resolveBranchPolicy, S as policyEnvKeys, _ as defaultStorageCredential, a as envKeysForSelection, b as isFunctionBaseUrlKey, d as servicesForEnvKeys, f as NEON_ENV_VAR_KEYS, g as defaultAiGatewayCredential, h as credentialName, i as ENV_PULL_SERVICES, m as credentialEnvKeys, p as createApiFromOptions, u as serviceForEnvKey, v as fetchEnvKeysState, w as toEntries, x as isLiveCredential, y as functionBaseUrlKey } from "./env_services-DlXECBPi.js";
|
|
5
5
|
import { formatInstallCommand, resolvePackageManager } from "../utils/package_manager.js";
|
|
6
|
+
import { ErrorCode, createNeonApiFromOptions, isPlatformError, loadConfigFromFile } from "@neon/config";
|
|
6
7
|
import { credentialScopesSatisfied } from "@neon/config/v1";
|
|
7
|
-
import { ErrorCode as ErrorCode$1, createNeonApiFromOptions as createNeonApiFromOptions$1, isPlatformError as isPlatformError$1, loadConfigFromFile } from "@neon/config";
|
|
8
8
|
import { plan, pullConfig } from "@neon/config-runtime";
|
|
9
9
|
//#region ../../internals/env-core/dist/reuse-secrets.js
|
|
10
10
|
/**
|
|
@@ -375,7 +375,7 @@ const resolveClaimableNeonEnvVars = async (ctx) => {
|
|
|
375
375
|
* buckets) into a config, then `fetchEnv` resolves what is actually enabled —
|
|
376
376
|
* so a branch with a bucket gets its `AWS_*` storage vars pulled with no policy.
|
|
377
377
|
* Function invocation URLs are listed live (`functionUrls: "all-live"`) because
|
|
378
|
-
* `pullConfig` cannot round-trip them into `
|
|
378
|
+
* `pullConfig` cannot round-trip them into `functions`. With
|
|
379
379
|
* {@link DevEnvContext.implyAiGateway}, the AI Gateway is added on top, since
|
|
380
380
|
* `pullConfig` cannot read it back.
|
|
381
381
|
* 3. otherwise -> throw {@link MissingBranchContextError}.
|
|
@@ -394,7 +394,7 @@ const resolveNeonEnvVars = async (ctx) => {
|
|
|
394
394
|
const config = await loadNeonConfig(ctx);
|
|
395
395
|
if (config) {
|
|
396
396
|
if (!ctx.projectId || !ctx.branchId) throw new MissingBranchContextError(`Found a neon.ts but could not resolve the project/branch. Run \`${getCliName()} link\` and \`${getCliName()} checkout <branch>\`, or pass --project-id / --branch.`);
|
|
397
|
-
await assertPolicyMatchesBranch(
|
|
397
|
+
await assertPolicyMatchesBranch(withoutDeclaredFunctions(config), ctx);
|
|
398
398
|
return await fetchAndProject(config, ctx);
|
|
399
399
|
}
|
|
400
400
|
if (ctx.projectId && ctx.branchId) {
|
|
@@ -465,7 +465,7 @@ const credentialsUnreachable = async (ctx, branch) => {
|
|
|
465
465
|
}
|
|
466
466
|
};
|
|
467
467
|
/** The adapter for direct branch reads: the injected one in tests, else built from options. */
|
|
468
|
-
const apiFor = (ctx) => ctx.api ?? createNeonApiFromOptions
|
|
468
|
+
const apiFor = (ctx) => ctx.api ?? createNeonApiFromOptions("neon env", {
|
|
469
469
|
...ctx.apiKey ? { apiKey: ctx.apiKey } : {},
|
|
470
470
|
...ctx.apiHost ? { apiHost: ctx.apiHost } : {}
|
|
471
471
|
});
|
|
@@ -535,7 +535,7 @@ const readAiGatewayAvailable = async (api, projectId, branchId) => {
|
|
|
535
535
|
await api.listCredentials(projectId, branchId);
|
|
536
536
|
return true;
|
|
537
537
|
} catch (error) {
|
|
538
|
-
if (isPlatformError
|
|
538
|
+
if (isPlatformError(error) && error.code === ErrorCode.FeatureUnavailable && (error.details.status === 404 || error.details.status === 501)) return false;
|
|
539
539
|
throw error;
|
|
540
540
|
}
|
|
541
541
|
};
|
|
@@ -576,16 +576,14 @@ const configForServices = (services, branchId, branch, selection) => {
|
|
|
576
576
|
if (branch.dataApiEnabled === false) notOnBranch("data-api", "Data API integration");
|
|
577
577
|
config.dataApi = true;
|
|
578
578
|
}
|
|
579
|
-
const preview = {};
|
|
580
579
|
if (services.includes("object-storage")) {
|
|
581
580
|
if (branch.buckets.length === 0) notOnBranch("object-storage", "object-storage buckets");
|
|
582
|
-
|
|
581
|
+
config.buckets = Object.fromEntries(branch.buckets.map((bucket) => [bucket.name, { access: bucket.accessLevel }]));
|
|
583
582
|
}
|
|
584
583
|
if (services.includes("ai-gateway")) {
|
|
585
584
|
if (!branch.aiGatewayAvailable) aiGatewayUnavailable();
|
|
586
|
-
|
|
585
|
+
config.aiGateway = true;
|
|
587
586
|
}
|
|
588
|
-
if (Object.keys(preview).length > 0) config.preview = preview;
|
|
589
587
|
return config;
|
|
590
588
|
};
|
|
591
589
|
/**
|
|
@@ -624,15 +622,15 @@ const resolveDevEnv = async (ctx) => {
|
|
|
624
622
|
}
|
|
625
623
|
};
|
|
626
624
|
/** Functions are omitted because `plan` treats undeployed functions as resources to create. */
|
|
627
|
-
const
|
|
628
|
-
const
|
|
629
|
-
if (
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
625
|
+
const withoutDeclaredFunctions = (config) => {
|
|
626
|
+
const next = { ...config };
|
|
627
|
+
if (next.functions !== void 0) delete next.functions;
|
|
628
|
+
if (next.preview?.functions) {
|
|
629
|
+
const previewWithoutFunctions = { ...next.preview };
|
|
630
|
+
delete previewWithoutFunctions.functions;
|
|
631
|
+
next.preview = previewWithoutFunctions;
|
|
632
|
+
}
|
|
633
|
+
return next;
|
|
636
634
|
};
|
|
637
635
|
/**
|
|
638
636
|
* Tier-1 guard. Dry-run the policy against the branch's live state and stop if
|
|
@@ -185,7 +185,7 @@ async function fetchEnvKeysState(config, options, keys) {
|
|
|
185
185
|
let storage = null;
|
|
186
186
|
if (wantsStorage) {
|
|
187
187
|
storage = await api.getProjectBranchStorage(projectId, branch.id);
|
|
188
|
-
if (!storage) throw new PlatformError(ErrorCode.NotFound, [`fetchEnv: branch policy declares object storage (
|
|
188
|
+
if (!storage) throw new PlatformError(ErrorCode.NotFound, [`fetchEnv: branch policy declares object storage (buckets) but storage is not enabled on branch ${branch.name} (${branch.id}).`, "Enable it via `apply(config, { projectId, branchId })` (or in the Neon Console) — then re-run fetchEnv. Or remove buckets."].join(" "), { details: {
|
|
189
189
|
projectId,
|
|
190
190
|
branchId: branch.id
|
|
191
191
|
} });
|
|
@@ -77,6 +77,7 @@ const handler = async (props) => {
|
|
|
77
77
|
const failure = created && !policyApplied ? await applyPolicyOrDescribeFailure({
|
|
78
78
|
projectId,
|
|
79
79
|
branchId,
|
|
80
|
+
branchName,
|
|
80
81
|
...props.apiKey ? { apiKey: props.apiKey } : {},
|
|
81
82
|
...props.apiHost ? { apiHost: props.apiHost } : {},
|
|
82
83
|
...props.color !== void 0 ? { color: props.color } : {},
|
package/dist/commands/config.js
CHANGED
|
@@ -21,8 +21,8 @@ import { zipBundle } from "../utils/zip.js";
|
|
|
21
21
|
import { autoPullEnvAfterPin } from "./env.js";
|
|
22
22
|
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
23
23
|
import { dirname, join } from "node:path";
|
|
24
|
+
import { packagesToStage, previewGaWarningForConfig, resolveConfig } from "@neon/config";
|
|
24
25
|
import chalk from "chalk";
|
|
25
|
-
import { packagesToStage, resolveConfig } from "@neon/config";
|
|
26
26
|
import { PushConflictError, apply, assertZipWithinLimits, bundleAsIs, createBranch, describeNativeFinding, enforceLimits, findUndeclaredNativePackages, inspect, isPartialBranchCreateError, loadConfigFromFile as loadConfigFromFile$1, plan, resolveEsbuildEntry, traceNativePackages, zipFunctionBundle } from "@neon/config-runtime";
|
|
27
27
|
//#region src/commands/config.ts
|
|
28
28
|
var config_exports = /* @__PURE__ */ __exportAll({
|
|
@@ -161,7 +161,7 @@ const resolveServices = async (props) => {
|
|
|
161
161
|
return pickServicesInteractively();
|
|
162
162
|
};
|
|
163
163
|
/**
|
|
164
|
-
* Write the hello-world handler the scaffolded `
|
|
164
|
+
* Write the hello-world handler the scaffolded `functions` entry points at. An
|
|
165
165
|
* existing `hello.ts` is left alone: the declared function keeps pointing at it, which is the
|
|
166
166
|
* better outcome than overwriting a file the user wrote.
|
|
167
167
|
*/
|
|
@@ -389,10 +389,76 @@ const planCmd = async (props) => {
|
|
|
389
389
|
branchId
|
|
390
390
|
});
|
|
391
391
|
};
|
|
392
|
+
const warnDeprecatedPreview = (config, target) => {
|
|
393
|
+
const message = previewGaWarningForConfig(config, target);
|
|
394
|
+
if (message) log.warning("%s", message);
|
|
395
|
+
};
|
|
396
|
+
const warningTargetFromResolved = (branch) => ({
|
|
397
|
+
name: branch.branchName,
|
|
398
|
+
id: branch.branchId,
|
|
399
|
+
exists: true,
|
|
400
|
+
...branch.isDefault !== void 0 ? { isDefault: branch.isDefault } : {},
|
|
401
|
+
...branch.isProtected !== void 0 ? { isProtected: branch.isProtected } : {},
|
|
402
|
+
...branch.parentId ? { parentId: branch.parentId } : {},
|
|
403
|
+
...branch.expiresAt ? { expiresAt: branch.expiresAt } : {}
|
|
404
|
+
});
|
|
405
|
+
const warningTargetForExistingBranch = async (props) => {
|
|
406
|
+
if (props.runtimeApi) {
|
|
407
|
+
const branch = (await props.runtimeApi.listBranches(props.projectId)).find((listed) => listed.id === props.branchId);
|
|
408
|
+
if (branch) return {
|
|
409
|
+
name: branch.name,
|
|
410
|
+
id: branch.id,
|
|
411
|
+
exists: true,
|
|
412
|
+
isDefault: branch.isDefault,
|
|
413
|
+
isProtected: branch.protected,
|
|
414
|
+
...branch.parentId ? { parentId: branch.parentId } : {},
|
|
415
|
+
...branch.expiresAt ? { expiresAt: branch.expiresAt } : {}
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
if (props.apiKey) {
|
|
419
|
+
const { data } = await getApiClient({
|
|
420
|
+
apiKey: props.apiKey,
|
|
421
|
+
...props.apiHost ? { apiHost: props.apiHost } : {}
|
|
422
|
+
}).listProjectBranches({ projectId: props.projectId });
|
|
423
|
+
const found = data.branches.find((listed) => listed.id === props.branchId);
|
|
424
|
+
if (found) return {
|
|
425
|
+
name: found.name ?? props.branchName ?? props.branchId,
|
|
426
|
+
id: found.id,
|
|
427
|
+
exists: true,
|
|
428
|
+
isDefault: found.default === true,
|
|
429
|
+
isProtected: found.protected === true,
|
|
430
|
+
...found.parent_id ? { parentId: found.parent_id } : {},
|
|
431
|
+
...found.expires_at ? { expiresAt: found.expires_at } : {}
|
|
432
|
+
};
|
|
433
|
+
}
|
|
434
|
+
return {
|
|
435
|
+
name: props.branchName ?? props.branchId,
|
|
436
|
+
id: props.branchId,
|
|
437
|
+
exists: true,
|
|
438
|
+
isDefault: false
|
|
439
|
+
};
|
|
440
|
+
};
|
|
441
|
+
const warnDeprecatedPreviewOnCreate = async (config, props) => {
|
|
442
|
+
try {
|
|
443
|
+
const listed = await warningTargetForExistingBranch(props);
|
|
444
|
+
warnDeprecatedPreview(config, {
|
|
445
|
+
...listed,
|
|
446
|
+
exists: false
|
|
447
|
+
});
|
|
448
|
+
} catch {
|
|
449
|
+
warnDeprecatedPreview(config, {
|
|
450
|
+
name: props.branchName,
|
|
451
|
+
id: props.branchId,
|
|
452
|
+
exists: false,
|
|
453
|
+
isDefault: false
|
|
454
|
+
});
|
|
455
|
+
}
|
|
456
|
+
};
|
|
392
457
|
const applyCmd = async (props) => {
|
|
393
458
|
const config = await loadConfig(props);
|
|
394
459
|
const branch = await resolveBranchRef(props);
|
|
395
460
|
announceTargetBranch(props, branch, "Applying to branch");
|
|
461
|
+
warnDeprecatedPreview(config, warningTargetFromResolved(branch));
|
|
396
462
|
const branchId = branch.branchId;
|
|
397
463
|
if (utilizedServices(config).includes("AI Gateway")) await assertAiGatewayProvisionable({
|
|
398
464
|
apiClient: props.apiClient,
|
|
@@ -429,7 +495,7 @@ const applyCmd = async (props) => {
|
|
|
429
495
|
* the plan/apply table. Postgres is always present (every branch has it); the rest are listed
|
|
430
496
|
* only when the policy declares them. This deliberately surfaces services that produce **no**
|
|
431
497
|
* plan step — notably the AI Gateway, which is always available and only needs a scoped branch
|
|
432
|
-
* credential (not a provisioning step) — so adding `
|
|
498
|
+
* credential (not a provisioning step) — so adding `aiGateway` to a neon.ts isn't
|
|
433
499
|
* mistaken for being silently dropped. Service enablement is static top-level config (it never
|
|
434
500
|
* lives in the per-branch closure), so reading it straight off `config` is accurate.
|
|
435
501
|
*/
|
|
@@ -547,6 +613,18 @@ const applyPolicyOnCreate = async (props) => {
|
|
|
547
613
|
if (/Could not find a Neon config file/i.test(message)) return;
|
|
548
614
|
throw err;
|
|
549
615
|
}
|
|
616
|
+
try {
|
|
617
|
+
warnDeprecatedPreview(config, await warningTargetForExistingBranch({
|
|
618
|
+
projectId: props.projectId,
|
|
619
|
+
branchId: props.branchId,
|
|
620
|
+
...props.branchName ? { branchName: props.branchName } : {},
|
|
621
|
+
...props.apiKey ? { apiKey: props.apiKey } : {},
|
|
622
|
+
...props.apiHost ? { apiHost: props.apiHost } : {},
|
|
623
|
+
...props.runtimeApi ? { runtimeApi: props.runtimeApi } : {}
|
|
624
|
+
}));
|
|
625
|
+
} catch {
|
|
626
|
+
warnDeprecatedPreview(config);
|
|
627
|
+
}
|
|
550
628
|
await assertAiGatewayProvisionableFromCreds({
|
|
551
629
|
projectId: props.projectId,
|
|
552
630
|
...props.apiKey ? { apiKey: props.apiKey } : {},
|
|
@@ -634,10 +712,26 @@ const createBranchFromPolicyOnCheckout = async (props) => {
|
|
|
634
712
|
});
|
|
635
713
|
log.info("Created branch %s (%s) from neon.ts policy.", branchName, branchId);
|
|
636
714
|
logPolicyResult(result, { color: props.color !== false });
|
|
715
|
+
await warnDeprecatedPreviewOnCreate(config, {
|
|
716
|
+
projectId: props.projectId,
|
|
717
|
+
branchId,
|
|
718
|
+
branchName,
|
|
719
|
+
...props.apiKey ? { apiKey: props.apiKey } : {},
|
|
720
|
+
...props.apiHost ? { apiHost: props.apiHost } : {},
|
|
721
|
+
...props.runtimeApi ? { runtimeApi: props.runtimeApi } : {}
|
|
722
|
+
});
|
|
637
723
|
return { branchId };
|
|
638
724
|
} catch (err) {
|
|
639
725
|
if (isPartialBranchCreateError(err)) {
|
|
640
726
|
log.info("Created branch %s (%s) from neon.ts policy.", err.branchName, err.branchId);
|
|
727
|
+
await warnDeprecatedPreviewOnCreate(config, {
|
|
728
|
+
projectId: props.projectId,
|
|
729
|
+
branchId: err.branchId,
|
|
730
|
+
branchName: err.branchName,
|
|
731
|
+
...props.apiKey ? { apiKey: props.apiKey } : {},
|
|
732
|
+
...props.apiHost ? { apiHost: props.apiHost } : {},
|
|
733
|
+
...props.runtimeApi ? { runtimeApi: props.runtimeApi } : {}
|
|
734
|
+
});
|
|
641
735
|
return {
|
|
642
736
|
branchId: err.branchId,
|
|
643
737
|
policyFailure: err.reason
|
package/dist/commands/dev.js
CHANGED
|
@@ -3,10 +3,10 @@ import { log } from "../log.js";
|
|
|
3
3
|
import { isClaimableEnvTarget } from "../claimable/state.js";
|
|
4
4
|
import { getCliName } from "../utils/cli_name.js";
|
|
5
5
|
import { readEnvFile, resolveEnvFilePath } from "../env_file.js";
|
|
6
|
-
import { y as functionBaseUrlKey } from "../_chunks/env_services-
|
|
6
|
+
import { y as functionBaseUrlKey } from "../_chunks/env_services-DlXECBPi.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-DRpFX-_d.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";
|
|
@@ -149,9 +149,9 @@ const runSingleSource = async (props) => {
|
|
|
149
149
|
const runFromConfig = async (props) => {
|
|
150
150
|
const branchId = await resolveBranchId(props);
|
|
151
151
|
const resolved = await resolveFunctionsFromConfig(process.cwd());
|
|
152
|
-
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 `
|
|
152
|
+
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
153
|
const { configPath, functions } = resolved;
|
|
154
|
-
if (functions.length === 0) throw new Error("neon.ts has no functions to serve. Add at least one under `
|
|
154
|
+
if (functions.length === 0) throw new Error("neon.ts has no functions to serve. Add at least one under `functions`, or pass --source <path>.");
|
|
155
155
|
const { vars: neonEnv, skipped, credential } = await resolveDevEnv(devEnvContext(props, branchId, process.cwd()));
|
|
156
156
|
reportDevCredential(credential);
|
|
157
157
|
const units = await planFunctionsToUnits(functions, neonEnv, DEFAULT_PORT_BASE);
|
package/dist/commands/env.js
CHANGED
|
@@ -5,11 +5,11 @@ import { isClaimableEnvTarget } from "../claimable/state.js";
|
|
|
5
5
|
import { getCliName } from "../utils/cli_name.js";
|
|
6
6
|
import { deprecatedServiceMessage, parseServices, servicesFlagValue, servicesOption } from "../neon_services.js";
|
|
7
7
|
import { mergeEnvFile, readEnvFile, resolveEnvFilePath } from "../env_file.js";
|
|
8
|
-
import { a as envKeysForSelection, b as isFunctionBaseUrlKey, c as ownedEnvServiceKeys, f as NEON_ENV_VAR_KEYS, i as ENV_PULL_SERVICES, l as parseEnvPullKeys, o as envPullFlagValue, r as ENV_PULL_KEY_HELP } from "../_chunks/env_services-
|
|
8
|
+
import { a as envKeysForSelection, b as isFunctionBaseUrlKey, c as ownedEnvServiceKeys, f as NEON_ENV_VAR_KEYS, i as ENV_PULL_SERVICES, l as parseEnvPullKeys, o as envPullFlagValue, r as ENV_PULL_KEY_HELP } from "../_chunks/env_services-DlXECBPi.js";
|
|
9
9
|
import { warnAiGateway } from "../utils/ai_gateway_notice.js";
|
|
10
10
|
import { announceTargetBranch } from "../utils/branch_notice.js";
|
|
11
11
|
import { fillSingleProject, resolveBranchRef } from "../utils/enrichers.js";
|
|
12
|
-
import { i as dropClaimableUnsupported, o as resolveNeonEnvVars } from "../_chunks/env-
|
|
12
|
+
import { i as dropClaimableUnsupported, o as resolveNeonEnvVars } from "../_chunks/env-DRpFX-_d.js";
|
|
13
13
|
import { existsSync } from "node:fs";
|
|
14
14
|
import chalk from "chalk";
|
|
15
15
|
//#region src/commands/env.ts
|
|
@@ -100,8 +100,8 @@ function enabledPatch(type, enabled) {
|
|
|
100
100
|
enabled
|
|
101
101
|
};
|
|
102
102
|
}
|
|
103
|
-
function updatePatch(
|
|
104
|
-
if (type === "schedule") {
|
|
103
|
+
function updatePatch(existing, props) {
|
|
104
|
+
if (existing.type === "schedule") {
|
|
105
105
|
const body = { type: "schedule" };
|
|
106
106
|
if (props["function-slug"] !== void 0) body.function_slug = props["function-slug"];
|
|
107
107
|
if (props.name !== void 0) body.name = props.name;
|
|
@@ -115,6 +115,10 @@ function updatePatch(type, props) {
|
|
|
115
115
|
if (props.name !== void 0) body.name = props.name;
|
|
116
116
|
if (props["function-path"] !== void 0) body.function_path = props["function-path"];
|
|
117
117
|
if (props.enabled !== void 0) body.enabled = props.enabled;
|
|
118
|
+
if (props.bucket !== void 0 || props.prefix !== void 0) body.storage_object_created = {
|
|
119
|
+
bucket_name: props.bucket ?? existing.storage_object_created.bucket_name,
|
|
120
|
+
...props.prefix !== void 0 ? { prefix: props.prefix } : existing.storage_object_created.prefix !== void 0 ? { prefix: existing.storage_object_created.prefix } : {}
|
|
121
|
+
};
|
|
118
122
|
return body;
|
|
119
123
|
}
|
|
120
124
|
function noFieldsToUpdateMessage(type) {
|
|
@@ -127,6 +131,8 @@ function noFieldsToUpdateMessage(type) {
|
|
|
127
131
|
] : [
|
|
128
132
|
"--function-slug",
|
|
129
133
|
"--name",
|
|
134
|
+
"--bucket",
|
|
135
|
+
"--prefix",
|
|
130
136
|
"--function-path",
|
|
131
137
|
"--enabled"
|
|
132
138
|
];
|
|
@@ -148,7 +154,7 @@ const builder = (argv) => argv.usage("$0 triggers <sub-command> [options]").opti
|
|
|
148
154
|
}).middleware(fillSingleProject).command("list", "List triggers on the branch", (yargs) => yargs, (args) => list(args)).command("get <id>", "Show a trigger", (yargs) => yargs.positional("id", {
|
|
149
155
|
describe: "Trigger ID",
|
|
150
156
|
type: "string"
|
|
151
|
-
}), (args) => get(args)).command("create", "Create a trigger that invokes a function on a cron schedule", (yargs) => yargs.options({
|
|
157
|
+
}), (args) => get(args)).command("create", "Create a trigger that invokes a function on a cron schedule or when an object is created", (yargs) => yargs.options({
|
|
152
158
|
"function-slug": {
|
|
153
159
|
describe: "Slug of the function to invoke",
|
|
154
160
|
type: "string",
|
|
@@ -161,8 +167,15 @@ const builder = (argv) => argv.usage("$0 triggers <sub-command> [options]").opti
|
|
|
161
167
|
},
|
|
162
168
|
cron: {
|
|
163
169
|
describe: "Five-field UTC cron expression, e.g. '*/15 * * * *'",
|
|
164
|
-
type: "string"
|
|
165
|
-
|
|
170
|
+
type: "string"
|
|
171
|
+
},
|
|
172
|
+
bucket: {
|
|
173
|
+
describe: "Object-storage bucket to watch (storage_object_created)",
|
|
174
|
+
type: "string"
|
|
175
|
+
},
|
|
176
|
+
prefix: {
|
|
177
|
+
describe: "Object-key prefix to match (requires --bucket on create)",
|
|
178
|
+
type: "string"
|
|
166
179
|
},
|
|
167
180
|
"function-path": {
|
|
168
181
|
describe: "Path the invocation is sent to (default '/')",
|
|
@@ -188,6 +201,14 @@ const builder = (argv) => argv.usage("$0 triggers <sub-command> [options]").opti
|
|
|
188
201
|
describe: "Five-field UTC cron expression",
|
|
189
202
|
type: "string"
|
|
190
203
|
},
|
|
204
|
+
bucket: {
|
|
205
|
+
describe: "Object-storage bucket to watch (storage_object_created)",
|
|
206
|
+
type: "string"
|
|
207
|
+
},
|
|
208
|
+
prefix: {
|
|
209
|
+
describe: "Object-key prefix to match",
|
|
210
|
+
type: "string"
|
|
211
|
+
},
|
|
191
212
|
"function-path": {
|
|
192
213
|
describe: "Path the invocation is sent to",
|
|
193
214
|
type: "string"
|
|
@@ -221,23 +242,47 @@ const get = async (props) => {
|
|
|
221
242
|
};
|
|
222
243
|
const create = async (props) => {
|
|
223
244
|
const branchId = await branchIdFromProps(props);
|
|
224
|
-
const body =
|
|
225
|
-
type: "schedule",
|
|
226
|
-
function_slug: props["function-slug"],
|
|
227
|
-
name: props.name,
|
|
228
|
-
schedule: { cron: props.cron }
|
|
229
|
-
};
|
|
230
|
-
if (props["function-path"] !== void 0) body.function_path = props["function-path"];
|
|
231
|
-
if (props.enabled !== void 0) body.enabled = props.enabled;
|
|
245
|
+
const body = createBody(props);
|
|
232
246
|
const { data } = await retryOnLock(() => props.apiClient.createProjectBranchTrigger(props.projectId, branchId, body));
|
|
233
247
|
writeTrigger(props, data.trigger);
|
|
234
248
|
};
|
|
249
|
+
function createBody(props) {
|
|
250
|
+
if (props.cron !== void 0 && props.bucket !== void 0) throw new Error("Pass --cron or --bucket, not both.");
|
|
251
|
+
if (props.prefix !== void 0 && props.bucket === void 0) throw new Error("--prefix requires --bucket.");
|
|
252
|
+
if (props.cron !== void 0) {
|
|
253
|
+
const body = {
|
|
254
|
+
type: "schedule",
|
|
255
|
+
function_slug: props["function-slug"],
|
|
256
|
+
name: props.name,
|
|
257
|
+
schedule: { cron: props.cron }
|
|
258
|
+
};
|
|
259
|
+
if (props["function-path"] !== void 0) body.function_path = props["function-path"];
|
|
260
|
+
if (props.enabled !== void 0) body.enabled = props.enabled;
|
|
261
|
+
return body;
|
|
262
|
+
}
|
|
263
|
+
if (props.bucket !== void 0) {
|
|
264
|
+
const body = {
|
|
265
|
+
type: "storage_object_created",
|
|
266
|
+
function_slug: props["function-slug"],
|
|
267
|
+
name: props.name,
|
|
268
|
+
storage_object_created: {
|
|
269
|
+
bucket_name: props.bucket,
|
|
270
|
+
...props.prefix !== void 0 ? { prefix: props.prefix } : {}
|
|
271
|
+
}
|
|
272
|
+
};
|
|
273
|
+
if (props["function-path"] !== void 0) body.function_path = props["function-path"];
|
|
274
|
+
if (props.enabled !== void 0) body.enabled = props.enabled;
|
|
275
|
+
return body;
|
|
276
|
+
}
|
|
277
|
+
throw new Error("Pass --cron for a schedule trigger, or --bucket for a storage_object_created trigger.");
|
|
278
|
+
}
|
|
235
279
|
const update = async (props) => {
|
|
236
280
|
const branchId = await branchIdFromProps(props);
|
|
237
281
|
const { data: existing } = await withTriggerNotFound(props.id, branchId, () => props.apiClient.getProjectBranchTrigger(props.projectId, branchId, props.id));
|
|
238
282
|
const trigger = existing.trigger;
|
|
239
283
|
if (props.cron !== void 0 && trigger.type !== "schedule") throw new Error(`Trigger ${props.id} is type ${trigger.type}; --cron applies to schedule triggers.`);
|
|
240
|
-
|
|
284
|
+
if ((props.bucket !== void 0 || props.prefix !== void 0) && trigger.type !== "storage_object_created") throw new Error(`Trigger ${props.id} is type ${trigger.type}; --bucket and --prefix apply to storage_object_created triggers.`);
|
|
285
|
+
const body = updatePatch(trigger, props);
|
|
241
286
|
if (Object.keys(body).filter((k) => k !== "type").length === 0) throw new Error(noFieldsToUpdateMessage(trigger.type));
|
|
242
287
|
const { data } = await withTriggerNotFound(props.id, branchId, () => retryOnLock(() => props.apiClient.updateProjectBranchTrigger(props.projectId, branchId, props.id, body)));
|
|
243
288
|
writeTrigger(props, data.trigger);
|
package/dist/config_services.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { authoredAiGateway, authoredBuckets, authoredFunctions } from "@neon/config";
|
|
1
2
|
//#region src/config_services.ts
|
|
2
3
|
const isToggleEnabled = (toggle) => {
|
|
3
4
|
if (toggle === void 0) return false;
|
|
@@ -9,9 +10,9 @@ const declaredNeonServices = (config) => {
|
|
|
9
10
|
const services = [];
|
|
10
11
|
if (isToggleEnabled(config.auth)) services.push("auth");
|
|
11
12
|
if (isToggleEnabled(config.dataApi)) services.push("data-api");
|
|
12
|
-
if (Object.keys(config
|
|
13
|
-
if (Object.keys(config
|
|
14
|
-
if (isToggleEnabled(config
|
|
13
|
+
if (Object.keys(authoredBuckets(config) ?? {}).length > 0) services.push("object-storage");
|
|
14
|
+
if (Object.keys(authoredFunctions(config) ?? {}).length > 0) services.push("functions");
|
|
15
|
+
if (isToggleEnabled(authoredAiGateway(config))) services.push("ai-gateway");
|
|
15
16
|
return services;
|
|
16
17
|
};
|
|
17
18
|
const dataApiSettingsToSnake = (settings) => {
|
package/dist/config_template.js
CHANGED
|
@@ -15,7 +15,7 @@ const REQUIRED_PACKAGES = [CONFIG_PACKAGE, ENV_PACKAGE];
|
|
|
15
15
|
/**
|
|
16
16
|
* The services `config init` can declare in the `neon.ts` it scaffolds — the subset of
|
|
17
17
|
* {@link NEON_SERVICES} a policy has a field for. {@link renderNeonConfig} owns the mapping
|
|
18
|
-
* from these names to the `neon.ts` fields (`aiGateway`, `buckets`, `dataApi`).
|
|
18
|
+
* from these names to the `neon.ts` fields (`aiGateway`, `functions`, `buckets`, `dataApi`).
|
|
19
19
|
*
|
|
20
20
|
* Postgres is absent because every branch has it, so there is nothing to declare. Selecting
|
|
21
21
|
* `data-api` also writes `auth: true`: the default Data API provider requires Neon Auth, and
|
|
@@ -53,18 +53,18 @@ const block = (level, key, body) => [
|
|
|
53
53
|
...body,
|
|
54
54
|
...at(level, "},")
|
|
55
55
|
];
|
|
56
|
-
/**
|
|
57
|
-
const
|
|
56
|
+
/** GA service keys for the selected init services, or "" when none of them is selected. */
|
|
57
|
+
const renderGaServices = (services) => {
|
|
58
58
|
const lines = [];
|
|
59
|
-
if (services.includes("ai-gateway")) lines.push(...at(
|
|
60
|
-
if (services.includes("functions")) lines.push(...block(
|
|
61
|
-
if (services.includes("object-storage")) lines.push(...block(
|
|
59
|
+
if (services.includes("ai-gateway")) lines.push(...at(1, "aiGateway: true,"));
|
|
60
|
+
if (services.includes("functions")) lines.push(...block(1, "functions", [...at(2, `${FUNCTION_SLUG}: { name: "${FUNCTION_NAME}", source: "./${FUNCTION_FILENAME}" },`)]));
|
|
61
|
+
if (services.includes("object-storage")) lines.push(...block(1, "buckets", [...at(2, `// "private" is the default; use "public_read" for anonymous reads`, `${BUCKET_NAME}: { access: "private" },`)]));
|
|
62
62
|
if (lines.length === 0) return "";
|
|
63
|
-
return `${
|
|
63
|
+
return `${lines.join("\n")}\n`;
|
|
64
64
|
};
|
|
65
65
|
/**
|
|
66
66
|
* Render the `neon.ts` policy `config init` writes. With no services this is the starter
|
|
67
|
-
* policy — an explicit `auth: false`, no
|
|
67
|
+
* policy — an explicit `auth: false`, no service keys beyond that, and a `branch` closure that gives
|
|
68
68
|
* new non-default branches a 7-day TTL — so the picker's "skip everything" answer and a
|
|
69
69
|
* non-interactive run produce the identical file.
|
|
70
70
|
*
|
|
@@ -79,7 +79,7 @@ const renderNeonConfig = (services) => {
|
|
|
79
79
|
export default defineConfig({
|
|
80
80
|
// Declare your Neon services here
|
|
81
81
|
auth: ${auth},
|
|
82
|
-
${dataApi}${
|
|
82
|
+
${dataApi}${renderGaServices(services)} // Branch policy: per-branch tuning
|
|
83
83
|
branch: (branch) => {
|
|
84
84
|
if (branch.isDefault) {
|
|
85
85
|
// Default branch: no overrides, uses project defaults
|
|
@@ -125,15 +125,15 @@ const renderSeededBranch = (view) => {
|
|
|
125
125
|
...at(1, "}),")
|
|
126
126
|
].join("\n")}\n`;
|
|
127
127
|
};
|
|
128
|
-
/**
|
|
129
|
-
const
|
|
128
|
+
/** GA service keys for a policy seeded from live state. */
|
|
129
|
+
const renderSeededGaServices = (view, branchName) => {
|
|
130
130
|
const lines = [];
|
|
131
131
|
const buckets = Object.entries(view.preview?.buckets ?? {});
|
|
132
|
-
if (buckets.length > 0) lines.push(...block(
|
|
132
|
+
if (buckets.length > 0) lines.push(...block(1, "buckets", buckets.flatMap(([name, bucket]) => at(2, `${renderKey(name)}: { access: "${bucket.access}" },`))));
|
|
133
133
|
const functions = Object.entries(view.preview?.functions ?? {});
|
|
134
|
-
if (functions.length > 0) lines.push(...at(
|
|
134
|
+
if (functions.length > 0) lines.push(...at(1, `// ${branchName} has ${functions.length} deployed function${functions.length === 1 ? "" : "s"}.`, "// Declaring one needs the local source path, which the branch does not know:", "// functions: {", ...functions.map(([slug, fn]) => `// ${renderKey(slug)}: { name: "${fn.name}", source: "./${slug}.ts" },`), "// },"));
|
|
135
135
|
if (lines.length === 0) return "";
|
|
136
|
-
return `${
|
|
136
|
+
return `${lines.join("\n")}\n`;
|
|
137
137
|
};
|
|
138
138
|
/**
|
|
139
139
|
* Render a `neon.ts` from a branch's live state (`config init --from-branch`).
|
|
@@ -152,16 +152,16 @@ const renderSeededPreview = (view, branchName) => {
|
|
|
152
152
|
*/
|
|
153
153
|
const renderNeonConfigFromView = (view, branchName) => {
|
|
154
154
|
const services = [view.auth ? " auth: true," : "", view.dataApi ? " dataApi: true," : ""].filter((line) => line !== "");
|
|
155
|
-
const
|
|
155
|
+
const gaServices = renderSeededGaServices(view, branchName);
|
|
156
156
|
const branch = renderSeededBranch(view);
|
|
157
|
-
if (services.length === 0 &&
|
|
157
|
+
if (services.length === 0 && gaServices === "" && branch === "") return {
|
|
158
158
|
source: renderNeonConfig([]),
|
|
159
159
|
seeded: false
|
|
160
160
|
};
|
|
161
161
|
const protectedNote = view.branch?.protected ? `// ${branchName} is protected on Neon. Not declared here: a policy \`protected\` would\n// apply to every branch this policy is applied to.\n` : "";
|
|
162
162
|
const body = [
|
|
163
163
|
...services,
|
|
164
|
-
...
|
|
164
|
+
...gaServices === "" ? [] : [gaServices.trimEnd()],
|
|
165
165
|
...branch === "" ? [] : [branch.trimEnd()]
|
|
166
166
|
].join("\n");
|
|
167
167
|
return {
|
|
@@ -169,7 +169,7 @@ const renderNeonConfigFromView = (view, branchName) => {
|
|
|
169
169
|
|
|
170
170
|
// Seeded by \`neon config init --from-branch\` from ${branchName}.
|
|
171
171
|
// The AI Gateway is not readable from a branch (always available, credential-gated), so add
|
|
172
|
-
// \`
|
|
172
|
+
// \`aiGateway: true\` if the policy should declare it.
|
|
173
173
|
${protectedNote}export default defineConfig({
|
|
174
174
|
${body}
|
|
175
175
|
});
|
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-DRpFX-_d.js";
|
|
2
2
|
export { DevEnvMismatchError, MissingBranchContextError, ServiceNotOnBranchError, dropClaimableUnsupported, resolveDevEnv, resolveNeonEnvVars };
|
package/dist/env_services.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as envKeysForSelection, c as ownedEnvServiceKeys, d as servicesForEnvKeys, i as ENV_PULL_SERVICES, l as parseEnvPullKeys, n as ENV_PULL_KEYS, o as envPullFlagValue, r as ENV_PULL_KEY_HELP, s as envServiceKeys, t as BRANCH_ENV_KEY, u as serviceForEnvKey } from "./_chunks/env_services-
|
|
1
|
+
import { a as envKeysForSelection, c as ownedEnvServiceKeys, d as servicesForEnvKeys, i as ENV_PULL_SERVICES, l as parseEnvPullKeys, n as ENV_PULL_KEYS, o as envPullFlagValue, r as ENV_PULL_KEY_HELP, s as envServiceKeys, t as BRANCH_ENV_KEY, u as serviceForEnvKey } from "./_chunks/env_services-DlXECBPi.js";
|
|
2
2
|
export { BRANCH_ENV_KEY, ENV_PULL_KEYS, ENV_PULL_KEY_HELP, ENV_PULL_SERVICES, envKeysForSelection, envPullFlagValue, envServiceKeys, ownedEnvServiceKeys, parseEnvPullKeys, serviceForEnvKey, servicesForEnvKeys };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { log } from "../log.js";
|
|
2
2
|
//#region src/utils/ai_gateway_notice.ts
|
|
3
3
|
/**
|
|
4
|
-
* Friendly guidance shown when a branch enables the AI Gateway (`
|
|
4
|
+
* Friendly guidance shown when a branch enables the AI Gateway (`aiGateway`).
|
|
5
5
|
*
|
|
6
6
|
* The gateway is credential-gated, not provisioned: enabling it always mints a working
|
|
7
7
|
* branch credential, so `apply` / `checkout` / `env pull` succeed regardless of plan. The
|
|
@@ -50,7 +50,7 @@ const hasFlagshipModels = (modelIds) => modelIds.some((id) => id.includes("opus"
|
|
|
50
50
|
* gateway won't serve model requests until the account is on a paid plan. `upgradeUrl` is the
|
|
51
51
|
* org-scoped Console billing page (see {@link aiGatewayUpgradeUrl}).
|
|
52
52
|
*/
|
|
53
|
-
const freePlanBlockMessage = (upgradeUrl) => `This neon.ts enables the AI Gateway, which isn't available on the Free plan — the gateway won't serve model requests. Upgrade to a paid plan and re-run, or remove \`
|
|
53
|
+
const freePlanBlockMessage = (upgradeUrl) => `This neon.ts enables the AI Gateway, which isn't available on the Free plan — the gateway won't serve model requests. Upgrade to a paid plan and re-run, or remove \`aiGateway\` from neon.ts. Upgrade here: ${upgradeUrl}`;
|
|
54
54
|
/**
|
|
55
55
|
* Build the AI Gateway courtesy notice for an account's plan and (optionally) its live model
|
|
56
56
|
* catalog, or `null` when nothing needs saying.
|
package/dist/utils/enrichers.js
CHANGED
|
@@ -30,13 +30,20 @@ const resolveBranchRef = async (props) => {
|
|
|
30
30
|
const branch = typeof props.branch === "string" ? props.branch : props.id;
|
|
31
31
|
const { data } = await props.apiClient.listProjectBranches({ projectId: props.projectId });
|
|
32
32
|
const branches = data.branches;
|
|
33
|
+
const listingFields = (listed) => ({
|
|
34
|
+
isDefault: listed.default === true,
|
|
35
|
+
isProtected: listed.protected === true,
|
|
36
|
+
...listed.parent_id ? { parentId: listed.parent_id } : {},
|
|
37
|
+
...listed.expires_at ? { expiresAt: listed.expires_at } : {}
|
|
38
|
+
});
|
|
33
39
|
if (branch) {
|
|
34
40
|
const ref = branch.toString();
|
|
35
41
|
const found = looksLikeBranchId(ref) ? branches.find((b) => b.id === ref) : branches.find((b) => b.name === ref);
|
|
36
42
|
if (found) return {
|
|
37
43
|
branchId: found.id,
|
|
38
44
|
branchName: found.name ?? found.id,
|
|
39
|
-
usedDefault: false
|
|
45
|
+
usedDefault: false,
|
|
46
|
+
...listingFields(found)
|
|
40
47
|
};
|
|
41
48
|
if (looksLikeBranchId(ref)) return {
|
|
42
49
|
branchId: ref,
|
|
@@ -50,7 +57,8 @@ const resolveBranchRef = async (props) => {
|
|
|
50
57
|
return {
|
|
51
58
|
branchId: defaultBranch.id,
|
|
52
59
|
branchName: defaultBranch.name ?? defaultBranch.id,
|
|
53
|
-
usedDefault: true
|
|
60
|
+
usedDefault: true,
|
|
61
|
+
...listingFields(defaultBranch)
|
|
54
62
|
};
|
|
55
63
|
};
|
|
56
64
|
const resolveSingleDatabase = async (props) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "neon",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.21.0",
|
|
4
4
|
"description": "CLI tool for Neon, the cloud backend primitives built around Lakebase Postgres",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"neon",
|
|
@@ -63,9 +63,9 @@
|
|
|
63
63
|
"yaml": "^2.9.0",
|
|
64
64
|
"yargs": "17.7.2",
|
|
65
65
|
"yoctocolors": "^2.1.2",
|
|
66
|
-
"@neon/config
|
|
67
|
-
"@neon/
|
|
68
|
-
"@neon/
|
|
66
|
+
"@neon/config": "1.7.0",
|
|
67
|
+
"@neon/config-runtime": "1.6.0",
|
|
68
|
+
"@neon/sdk": "6.0.0"
|
|
69
69
|
},
|
|
70
70
|
"optionalDependencies": {
|
|
71
71
|
"@napi-rs/keyring": "1.3.0",
|
|
@@ -98,7 +98,7 @@
|
|
|
98
98
|
"typescript": "^5.9.0",
|
|
99
99
|
"vitest": "^3.0.9",
|
|
100
100
|
"@neon-internals/cli-core": "0.0.0",
|
|
101
|
-
"@neon-internals/env-core": "0.0.
|
|
101
|
+
"@neon-internals/env-core": "0.0.19",
|
|
102
102
|
"@neon/e2e-harness": "0.0.0",
|
|
103
103
|
"@neon/functions": "0.11.0"
|
|
104
104
|
},
|