neon 8.4.5 → 8.6.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 +18 -9
- package/dist/_chunks/{env-4WGRW2nw.js → env-BNm69Qh8.js} +2 -5
- package/dist/commands/config.js +11 -28
- package/dist/commands/dev.js +93 -27
- package/dist/commands/env.js +2 -3
- package/dist/commands/init.js +1 -2
- package/dist/commands/link.js +1 -1
- package/dist/commands/manifest.js +1 -3
- package/dist/commands/subscribe.js +16 -16
- package/dist/credential_io.js +1 -2
- package/dist/dev/env.js +1 -2
- package/dist/dev/runtime.js +8 -0
- package/dist/env_file.js +9 -1
- package/dist/index.js +1 -1
- package/dist/init/auth.js +1 -4
- package/dist/init/choices.js +21 -7
- package/dist/init/copy.js +21 -7
- package/dist/init/run.js +25 -16
- package/dist/plugins/run.js +2 -1
- package/dist/psql/command/cmd_io.js +1 -2
- package/dist/psql/command/cmd_pipeline.js +1 -2
- package/dist/psql/core/common.js +1 -2
- package/dist/psql/core/startup.js +1 -2
- package/dist/psql/index.js +1 -2
- package/dist/psql/types/index.js +1 -2
- package/dist/skills/catalog.js +5 -0
- package/dist/utils/hooks.js +0 -2
- package/dist/utils/staging.js +1 -14
- package/{LICENSE → dist/vendor/postgres-meta/LICENSE} +0 -1
- package/dist/vendor/postgres-meta/constants.js +14 -0
- package/dist/vendor/postgres-meta/lib/PostgresMeta.js +49 -0
- package/dist/vendor/postgres-meta/lib/PostgresMetaColumnPrivileges.js +85 -0
- package/dist/vendor/postgres-meta/lib/PostgresMetaColumns.js +235 -0
- package/dist/vendor/postgres-meta/lib/PostgresMetaConfig.js +16 -0
- package/dist/vendor/postgres-meta/lib/PostgresMetaExtensions.js +79 -0
- package/dist/vendor/postgres-meta/lib/PostgresMetaForeignTables.js +81 -0
- package/dist/vendor/postgres-meta/lib/PostgresMetaFunctions.js +173 -0
- package/dist/vendor/postgres-meta/lib/PostgresMetaIndexes.js +42 -0
- package/dist/vendor/postgres-meta/lib/PostgresMetaMaterializedViews.js +84 -0
- package/dist/vendor/postgres-meta/lib/PostgresMetaPolicies.js +120 -0
- package/dist/vendor/postgres-meta/lib/PostgresMetaPublications.js +193 -0
- package/dist/vendor/postgres-meta/lib/PostgresMetaRelationships.js +87 -0
- package/dist/vendor/postgres-meta/lib/PostgresMetaRoles.js +203 -0
- package/dist/vendor/postgres-meta/lib/PostgresMetaSchemas.js +105 -0
- package/dist/vendor/postgres-meta/lib/PostgresMetaTablePrivileges.js +91 -0
- package/dist/vendor/postgres-meta/lib/PostgresMetaTables.js +183 -0
- package/dist/vendor/postgres-meta/lib/PostgresMetaTriggers.js +156 -0
- package/dist/vendor/postgres-meta/lib/PostgresMetaTypes.js +22 -0
- package/dist/vendor/postgres-meta/lib/PostgresMetaVersion.js +20 -0
- package/dist/vendor/postgres-meta/lib/PostgresMetaViews.js +83 -0
- package/dist/vendor/postgres-meta/lib/constants.js +8 -0
- package/dist/vendor/postgres-meta/lib/db.js +147 -0
- package/dist/vendor/postgres-meta/lib/generators.js +106 -0
- package/dist/vendor/postgres-meta/lib/helpers.js +26 -0
- package/dist/vendor/postgres-meta/lib/index.js +1 -0
- package/dist/vendor/postgres-meta/lib/sql/column_privileges.sql.js +154 -0
- package/dist/vendor/postgres-meta/lib/sql/columns.sql.js +123 -0
- package/dist/vendor/postgres-meta/lib/sql/common.js +1 -0
- package/dist/vendor/postgres-meta/lib/sql/config.sql.js +32 -0
- package/dist/vendor/postgres-meta/lib/sql/extensions.sql.js +20 -0
- package/dist/vendor/postgres-meta/lib/sql/foreign_tables.sql.js +20 -0
- package/dist/vendor/postgres-meta/lib/sql/functions.sql.js +141 -0
- package/dist/vendor/postgres-meta/lib/sql/indexes.sql.js +51 -0
- package/dist/vendor/postgres-meta/lib/sql/materialized_views.sql.js +21 -0
- package/dist/vendor/postgres-meta/lib/sql/policies.sql.js +53 -0
- package/dist/vendor/postgres-meta/lib/sql/publications.sql.js +46 -0
- package/dist/vendor/postgres-meta/lib/sql/roles.sql.js +40 -0
- package/dist/vendor/postgres-meta/lib/sql/schemas.sql.js +26 -0
- package/dist/vendor/postgres-meta/lib/sql/table.sql.js +109 -0
- package/dist/vendor/postgres-meta/lib/sql/table_privileges.sql.js +85 -0
- package/dist/vendor/postgres-meta/lib/sql/table_relationships.sql.js +52 -0
- package/dist/vendor/postgres-meta/lib/sql/triggers.sql.js +64 -0
- package/dist/vendor/postgres-meta/lib/sql/types.sql.js +65 -0
- package/dist/vendor/postgres-meta/lib/sql/version.sql.js +15 -0
- package/dist/vendor/postgres-meta/lib/sql/views.sql.js +22 -0
- package/dist/vendor/postgres-meta/lib/sql/views_key_dependencies.sql.js +198 -0
- package/dist/vendor/postgres-meta/lib/types.js +1 -0
- package/dist/vendor/postgres-meta/templates/typescript.js +608 -0
- package/package.json +12 -5
package/README.md
CHANGED
|
@@ -316,23 +316,23 @@ neon realtime subscribe "SELECT id, title, completed FROM realtime_todos ORDER B
|
|
|
316
316
|
|
|
317
317
|
The default terminal output is append-only. It identifies the selected project,
|
|
318
318
|
branch, and database; prints the initial result once; reports connection-state
|
|
319
|
-
changes; and then prints each published
|
|
319
|
+
changes; and then prints each published change set with its transaction IDs, a compact
|
|
320
320
|
`+insert ~update -delete` summary, and the affected rows. It does not take over the
|
|
321
|
-
terminal or require keyboard navigation. Pass `--changes` to display the raw
|
|
322
|
-
and
|
|
321
|
+
terminal or require keyboard navigation. Pass `--changes` to display the raw `rows`
|
|
322
|
+
and `changes` events instead of the materialized view.
|
|
323
323
|
|
|
324
324
|
For scripts and agents, JSON output is a JSON Lines stream with one complete event
|
|
325
325
|
per line. YAML output uses one document per event:
|
|
326
326
|
|
|
327
327
|
```bash
|
|
328
328
|
neon realtime subscribe --changes --output json "SELECT * FROM realtime_todos" |
|
|
329
|
-
jq --unbuffered 'select(.type == "
|
|
329
|
+
jq --unbuffered 'select(.type == "changes")'
|
|
330
330
|
|
|
331
331
|
neon realtime subscribe --output yaml "SELECT * FROM realtime_todos"
|
|
332
332
|
```
|
|
333
333
|
|
|
334
334
|
Machine-readable output retains the event stream: without `--changes`, each event is
|
|
335
|
-
a complete `snapshot`; with `--changes`, events are raw `
|
|
335
|
+
a complete `snapshot`; with `--changes`, events are raw `rows` and `changes` objects.
|
|
336
336
|
Lifecycle labels and the target header are terminal-only.
|
|
337
337
|
|
|
338
338
|
## Configure autocompletion
|
|
@@ -623,7 +623,16 @@ Console), or drop auth from --service.
|
|
|
623
623
|
|
|
624
624
|
If you'd rather not keep env vars on disk, inject them at runtime instead with `neon-env run -- <your dev command>` (from `@neon/env`) or `neon dev`, and pass `--no-env-pull` to `link` / `checkout`.
|
|
625
625
|
|
|
626
|
-
**`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.…`).
|
|
626
|
+
**`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.…`).
|
|
627
|
+
|
|
628
|
+
Before `neon.ts` is evaluated, `dev` loads the project directory's `.env`, then `.env.local`, which overrides it, into the environment, so Function env values written as `process.env.X!` resolve the same way they do for `neon deploy --env`. A variable already set in the shell wins over both. Local Functions inherit every loaded variable, including keys `neon.ts` doesn't declare; the deployed runtime gets only declared and Neon-injected env. `--env <file>` loads only that file, resolved from the current directory, matching `neon deploy --env`:
|
|
629
|
+
|
|
630
|
+
```bash
|
|
631
|
+
neon dev # loads .env, then .env.local (overrides .env)
|
|
632
|
+
neon dev --env .env.staging # loads only .env.staging
|
|
633
|
+
```
|
|
634
|
+
|
|
635
|
+
`dev` writes nothing. To reuse the branch credential behind the AI Gateway and object storage, it reads only the file `env pull` writes (`.env` when it exists, otherwise `.env.local`), so an override in the other file cannot pair one credential's key ID with another's secret. 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.
|
|
627
636
|
|
|
628
637
|
**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.
|
|
629
638
|
|
|
@@ -890,7 +899,7 @@ Without a TTY, pass `-y` or enough flags to answer every question. `-y` alone is
|
|
|
890
899
|
|
|
891
900
|
`--project-id`, `--org-id`, `--project-name`, `--region-id`, and `--branch` are forwarded to `link`. They select linking. A linked directory is not relinked unless you pass one of those flags.
|
|
892
901
|
|
|
893
|
-
|
|
902
|
+
Init pulls the branch's env vars once, after the `neon.ts` step, so the pull includes what `neon.ts` declares. The AI Gateway and Functions need no deploy and are pulled immediately. When `neon.ts` declares Auth, the Data API, Object Storage, or Realtime, init pulls Postgres, plus the AI Gateway and the scaffolded function's URL when declared. `neon deploy` provisions the rest and pulls their env vars. An existing `neon.ts` is pulled by `link` as usual.
|
|
894
903
|
|
|
895
904
|
A failed step stops the rest. `--profile` and `--config-dir` are forwarded to each child. `--output json` and `--output yaml` are refused; the commands init runs print their own output.
|
|
896
905
|
|
|
@@ -967,7 +976,7 @@ On a TTY the command asks which agents and which skills, then installs. Detected
|
|
|
967
976
|
|
|
968
977
|
`-y` skips those questions and installs the default skills into detected agents: project-folder markers such as `.cursor`, else the agent driving the CLI. `--agent` / `-a` names coding agents and skips the agent picker. `--skill` / `-s` names specific skills and skips the skill picker; it does not select agents. `--global -y` uses installed apps, else the host CLI agent. Without a TTY, pass `-y`, or `--skill <name>` (add `--agent <name>` to name agents). If `-y` finds no agent, it exits: pass `--agent <name>`, run from a supported agent, or omit `-y` in a terminal to pick.
|
|
969
978
|
|
|
970
|
-
`--skill` names by source repo: `neondatabase/agent-skills` (`neon`, `neon-ai-gateway`, `neon-auth`, `neon-functions`, `neon-object-storage`, `neon-postgres`, `neon-postgres-branches`, `neon-postgres-egress-optimizer`); `neondatabase/neon-for-agent-platforms` (`neon-postgres-agent-platforms`).
|
|
979
|
+
`--skill` names by source repo: `neondatabase/agent-skills` (`neon`, `neon-ai-gateway`, `neon-auth`, `neon-functions`, `neon-object-storage`, `neon-postgres`, `neon-postgres-branches`, `neon-postgres-egress-optimizer`, `neon-realtime`); `neondatabase/neon-for-agent-platforms` (`neon-postgres-agent-platforms`).
|
|
971
980
|
|
|
972
981
|
Supported agents match `neon mcp`, minus agents that cannot install skills: `antigravity`, `cline`, `cline-cli`, `claude-code`, `claude-desktop`, `codex`, `cursor`, `gemini-cli`, `goose`, `github-copilot-cli`, `grok-build`, `opencode`, `vscode`, `windsurf`, `zed`. `mcporter` is a known MCP name that is then skipped. `neon skills --help` lists the same skill and agent values, and that `-y` leaves out `neon-postgres-agent-platforms`.
|
|
973
982
|
|
|
@@ -1019,7 +1028,7 @@ Default scope is `project`. `--global` is `user`. On macOS and Linux, Cursor and
|
|
|
1019
1028
|
|
|
1020
1029
|
Supported agents with a plugins mapping: `claude-code`, `claude-desktop`, `codex`, `cursor`, `github-copilot-cli`, `grok-build`, `vscode`. `claude-desktop` installs as Claude Code; detecting both produces one install and lists both names in the table. `mcporter` is a known MCP name that is then skipped.
|
|
1021
1030
|
|
|
1022
|
-
The plugins CLI installs every plugin it finds in the Neon plugin package. Today that is `neon-postgres` from `neondatabase/agent-skills`. It includes the Neon MCP server (`https://mcp.neon.tech/mcp`) and these skills: `neon`, `neon-ai-gateway`, `neon-auth`, `neon-functions`, `neon-object-storage`, `neon-postgres`, `neon-postgres-branches`, `neon-postgres-egress-optimizer`. It does not include `neon-postgres-agent-platforms`.
|
|
1031
|
+
The plugins CLI installs every plugin it finds in the Neon plugin package. Today that is `neon-postgres` from `neondatabase/agent-skills`. It includes the Neon MCP server (`https://mcp.neon.tech/mcp`) and these skills: `neon`, `neon-ai-gateway`, `neon-auth`, `neon-functions`, `neon-object-storage`, `neon-postgres`, `neon-postgres-branches`, `neon-postgres-egress-optimizer`, `neon-realtime`. It does not include `neon-postgres-agent-platforms`.
|
|
1023
1032
|
|
|
1024
1033
|
`neon plugins --help` lists the plugin, those contents, and the supported agent names.
|
|
1025
1034
|
|
|
@@ -4,8 +4,8 @@ import { declaredNeonServices } from "../config_services.js";
|
|
|
4
4
|
import { loadProjectConfig } from "../project.js";
|
|
5
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-BpP3Dqpe.js";
|
|
6
6
|
import { ENV_PULL_SERVICES, envKeysForSelection, serviceForEnvKey, servicesForEnvKeys } from "../env_services.js";
|
|
7
|
-
import { StagingOnlyError, assertPolicyAvailable, assertRealtimeAvailable, stagingFeaturesEnabled } from "../utils/staging.js";
|
|
8
7
|
import { formatInstallCommand, resolvePackageManager } from "../utils/package_manager.js";
|
|
8
|
+
import { StagingOnlyError } from "../utils/staging.js";
|
|
9
9
|
import { ErrorCode, PlatformError, createNeonApiFromOptions, isPlatformError } from "@neon/config";
|
|
10
10
|
import { credentialScopesSatisfied } from "@neon/config/v1";
|
|
11
11
|
import { plan, pullConfig } from "@neon/config-runtime";
|
|
@@ -414,8 +414,7 @@ const resolveNeonEnvVars = async (ctx) => {
|
|
|
414
414
|
const pulled = await pullConfig({
|
|
415
415
|
projectId: ctx.projectId,
|
|
416
416
|
branchId: ctx.branchId,
|
|
417
|
-
...apiOptions(ctx)
|
|
418
|
-
realtime: stagingFeaturesEnabled(ctx.apiHost)
|
|
417
|
+
...apiOptions(ctx)
|
|
419
418
|
});
|
|
420
419
|
const resolvedBranch = {
|
|
421
420
|
id: pulled.branch.id,
|
|
@@ -513,7 +512,6 @@ const resolveSelectedServices = async (ctx, directServices, envKeys) => {
|
|
|
513
512
|
const impliedServices = servicesForEnvKeys(envKeys);
|
|
514
513
|
const services = ENV_PULL_SERVICES.filter((service) => directServices.includes(service) || impliedServices.includes(service));
|
|
515
514
|
const selectedKeys = envKeysForSelection(directServices, envKeys);
|
|
516
|
-
if (services.includes("realtime")) assertRealtimeAvailable(ctx.apiHost);
|
|
517
515
|
const api = apiFor(ctx);
|
|
518
516
|
const has = (service) => services.includes(service);
|
|
519
517
|
const checkAiGateway = has("ai-gateway") && !selectedKeys.includes(NEON_ENV_VAR_KEYS.aiGateway.apiKey);
|
|
@@ -758,7 +756,6 @@ const loadNeonConfig = async (ctx) => {
|
|
|
758
756
|
...ctx.config ? { path: ctx.config } : {},
|
|
759
757
|
...ctx.omitUnsetFunctionEnv ? { unsetFunctionEnv: "omit" } : {}
|
|
760
758
|
});
|
|
761
|
-
assertPolicyAvailable(config, ctx.apiHost);
|
|
762
759
|
return config;
|
|
763
760
|
} catch (err) {
|
|
764
761
|
const message = err instanceof Error ? err.message : String(err);
|
package/dist/commands/config.js
CHANGED
|
@@ -11,7 +11,6 @@ import { toNeonConfigView } from "../config_format.js";
|
|
|
11
11
|
import { declaredNeonServices } from "../config_services.js";
|
|
12
12
|
import { loadProjectConfig, neonConfigFilename, projectConfigPath, projectDir } from "../project.js";
|
|
13
13
|
import { loadEnvFileIntoProcess } from "../env_file.js";
|
|
14
|
-
import { assertPolicyAvailable, assertRealtimeAvailable, listedServices, stagingFeaturesEnabled, stagingFeaturesEnabledForArgv } from "../utils/staging.js";
|
|
15
14
|
import { formatInstallCommand, installArgs, resolvePackageManager, runCommand } from "../utils/package_manager.js";
|
|
16
15
|
import { writer } from "../writer.js";
|
|
17
16
|
import { fillSingleProject, resolveBranchRef } from "../utils/enrichers.js";
|
|
@@ -159,11 +158,7 @@ const missingDependencies = (cwd) => {
|
|
|
159
158
|
* starter policy, which is what `config init` has always written.
|
|
160
159
|
*/
|
|
161
160
|
const resolveServices = async (props) => {
|
|
162
|
-
if (props.services !== void 0)
|
|
163
|
-
const services = parseConfigInitServices(props.services, (used, canonical) => log.warning(deprecatedServiceMessage(used, canonical)));
|
|
164
|
-
if (services.includes("realtime")) assertRealtimeAvailable(props.apiHost);
|
|
165
|
-
return services;
|
|
166
|
-
}
|
|
161
|
+
if (props.services !== void 0) return parseConfigInitServices(props.services, (used, canonical) => log.warning(deprecatedServiceMessage(used, canonical)));
|
|
167
162
|
if (props.pickServices) return props.pickServices();
|
|
168
163
|
if (isCi() || !process.stdout.isTTY) return [];
|
|
169
164
|
return pickServicesInteractively();
|
|
@@ -456,22 +451,14 @@ const builder = (argv) => argv.usage("$0 config <sub-command> [options]").option
|
|
|
456
451
|
}))).command("ai-gateway", "Enable the AI Gateway (aiGateway: true)", addSharedOptions, (args) => addCmd(addProps(args, {
|
|
457
452
|
kind: "service",
|
|
458
453
|
service: "ai-gateway"
|
|
459
|
-
}))).command({
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
handler: (args) => {
|
|
468
|
-
assertRealtimeAvailable();
|
|
469
|
-
return addCmd(addProps(args, {
|
|
470
|
-
kind: "realtime",
|
|
471
|
-
...args.allowedOrigin !== void 0 ? { allowedOrigins: args.allowedOrigin } : {}
|
|
472
|
-
}));
|
|
473
|
-
}
|
|
474
|
-
}).command("function <slug>", "Declare a Neon Function and create its handler file", (yargs) => addSharedOptions(yargs).example("$0 config add function sendemail", "Creates functions/sendemail.ts and declares it").example("$0 config add function sendemail --source src/send.ts", "Declares an existing handler").positional("slug", {
|
|
454
|
+
}))).command("realtime", "Enable Realtime (preview.realtime: true)", (yargs) => addSharedOptions(yargs).option("allowed-origin", {
|
|
455
|
+
describe: "Allowed browser origin (repeatable). \"*\" allows every origin",
|
|
456
|
+
type: "array",
|
|
457
|
+
string: true
|
|
458
|
+
}), (args) => addCmd(addProps(args, {
|
|
459
|
+
kind: "realtime",
|
|
460
|
+
...args.allowedOrigin !== void 0 ? { allowedOrigins: args.allowedOrigin } : {}
|
|
461
|
+
}))).command("function <slug>", "Declare a Neon Function and create its handler file", (yargs) => addSharedOptions(yargs).example("$0 config add function sendemail", "Creates functions/sendemail.ts and declares it").example("$0 config add function sendemail --source src/send.ts", "Declares an existing handler").positional("slug", {
|
|
475
462
|
describe: "Function slug: 1-20 lowercase letters and digits",
|
|
476
463
|
type: "string",
|
|
477
464
|
demandOption: true
|
|
@@ -510,7 +497,7 @@ const builder = (argv) => argv.usage("$0 config <sub-command> [options]").option
|
|
|
510
497
|
},
|
|
511
498
|
services: servicesOption({
|
|
512
499
|
key: "services",
|
|
513
|
-
allowed:
|
|
500
|
+
allowed: NEON_SERVICES,
|
|
514
501
|
describe: "Services the scaffolded neon.ts declares",
|
|
515
502
|
also: "postgres alone writes the starter policy; every branch has Postgres. Omitted: pick interactively on a terminal, starter policy in CI or without a TTY."
|
|
516
503
|
}),
|
|
@@ -541,8 +528,7 @@ const liveConfigView = async (opts) => {
|
|
|
541
528
|
branchId: opts.branchId,
|
|
542
529
|
...opts.apiKey ? { apiKey: opts.apiKey } : {},
|
|
543
530
|
...opts.apiHost ? { apiHost: opts.apiHost } : {},
|
|
544
|
-
...opts.runtimeApi ? { api: opts.runtimeApi } : {}
|
|
545
|
-
realtime: stagingFeaturesEnabled(opts.apiHost)
|
|
531
|
+
...opts.runtimeApi ? { api: opts.runtimeApi } : {}
|
|
546
532
|
});
|
|
547
533
|
const resolved = resolveConfig(live.config, {
|
|
548
534
|
name: live.branch.name,
|
|
@@ -569,7 +555,6 @@ const loadConfig = async (props) => {
|
|
|
569
555
|
contextFile: props.contextFile,
|
|
570
556
|
...props.config ? { path: props.config } : {}
|
|
571
557
|
});
|
|
572
|
-
assertPolicyAvailable(config, props.apiHost);
|
|
573
558
|
return config;
|
|
574
559
|
};
|
|
575
560
|
const status = async (props) => {
|
|
@@ -994,7 +979,6 @@ const applyPolicyOnCreate = async (props) => {
|
|
|
994
979
|
if (/Could not find a Neon config file/i.test(message)) return;
|
|
995
980
|
throw err;
|
|
996
981
|
}
|
|
997
|
-
assertPolicyAvailable(config, props.apiHost);
|
|
998
982
|
try {
|
|
999
983
|
warnDeprecatedPreview(config, await warningTargetForExistingBranch({
|
|
1000
984
|
projectId: props.projectId,
|
|
@@ -1079,7 +1063,6 @@ const createBranchFromPolicyOnCheckout = async (props) => {
|
|
|
1079
1063
|
if (/Could not find a Neon config file/i.test(message)) return null;
|
|
1080
1064
|
throw err;
|
|
1081
1065
|
}
|
|
1082
|
-
assertPolicyAvailable(config, props.apiHost);
|
|
1083
1066
|
await assertAiGatewayProvisionableFromCreds({
|
|
1084
1067
|
projectId: props.projectId,
|
|
1085
1068
|
...props.apiKey ? { apiKey: props.apiKey } : {},
|
package/dist/commands/dev.js
CHANGED
|
@@ -2,13 +2,14 @@ import { t as __exportAll } from "../_chunks/rolldown-runtime-8H4AJuhK.js";
|
|
|
2
2
|
import { log } from "../log.js";
|
|
3
3
|
import { isClaimableEnvTarget } from "../claimable/state.js";
|
|
4
4
|
import { getCliName } from "../utils/cli_name.js";
|
|
5
|
-
import { readEnvFile, resolveEnvFilePath } from "../env_file.js";
|
|
5
|
+
import { defaultDevEnvFiles, readEnvFile, resolveEnvFilePath } from "../env_file.js";
|
|
6
6
|
import { l as functionBaseUrlKey } from "../_chunks/env-BpP3Dqpe.js";
|
|
7
|
-
import {
|
|
8
|
-
import { bundleEntry } from "../utils/esbuild.js";
|
|
9
|
-
import { a as resolveDevEnv } from "../_chunks/env-4WGRW2nw.js";
|
|
7
|
+
import { a as resolveDevEnv } from "../_chunks/env-BNm69Qh8.js";
|
|
10
8
|
import { findConfigFunctionBySource, resolveFunctionsFromConfig } from "../dev/functions.js";
|
|
11
9
|
import { resolveWatchInputs } from "../dev/inputs.js";
|
|
10
|
+
import { branchIdResolve } from "../utils/enrichers.js";
|
|
11
|
+
import { bundleEntry } from "../utils/esbuild.js";
|
|
12
|
+
import { envFlag } from "./config.js";
|
|
12
13
|
import { existsSync, mkdirSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
13
14
|
import { basename, dirname, join, relative, resolve } from "node:path";
|
|
14
15
|
import { fileURLToPath } from "node:url";
|
|
@@ -20,13 +21,16 @@ import { createServer } from "node:net";
|
|
|
20
21
|
//#region src/commands/dev.ts
|
|
21
22
|
var dev_exports = /* @__PURE__ */ __exportAll({
|
|
22
23
|
builder: () => builder,
|
|
24
|
+
captureShellEnv: () => captureShellEnv,
|
|
23
25
|
command: () => "dev",
|
|
24
26
|
describe: () => describe,
|
|
25
27
|
devBundleDir: () => devBundleDir,
|
|
26
28
|
devEnvContext: () => devEnvContext,
|
|
27
29
|
diffUnits: () => diffUnits,
|
|
30
|
+
formatEnvFiles: () => formatEnvFiles,
|
|
28
31
|
formatEnvSummary: () => formatEnvSummary,
|
|
29
32
|
handler: () => handler,
|
|
33
|
+
loadDevEnvFiles: () => loadDevEnvFiles,
|
|
30
34
|
localFunctionUrlEnv: () => localFunctionUrlEnv,
|
|
31
35
|
planFunctionsToUnits: () => planFunctionsToUnits,
|
|
32
36
|
reportDevCredential: () => reportDevCredential,
|
|
@@ -42,7 +46,8 @@ const builder = (argv) => argv.usage("$0 dev [--source <path>] [options]").examp
|
|
|
42
46
|
port: {
|
|
43
47
|
describe: "Port to listen on (single-function mode only, with --source). Fails if taken. Without it (and without a PORT env var) a free port is chosen automatically.",
|
|
44
48
|
type: "number"
|
|
45
|
-
}
|
|
49
|
+
},
|
|
50
|
+
...envFlag
|
|
46
51
|
}).epilogue([
|
|
47
52
|
"",
|
|
48
53
|
"Functions run with the linked branch's Neon env injected, the same set the",
|
|
@@ -55,9 +60,14 @@ const builder = (argv) => argv.usage("$0 dev [--source <path>] [options]").examp
|
|
|
55
60
|
"this process is serving (every neon.ts function, or the matching slug when",
|
|
56
61
|
"`--source` names one).",
|
|
57
62
|
"",
|
|
58
|
-
"`dev`
|
|
59
|
-
"
|
|
60
|
-
"
|
|
63
|
+
"Before neon.ts is evaluated, `dev` loads the project's .env, then .env.local,",
|
|
64
|
+
"which overrides it, into the environment, so Function env values that read",
|
|
65
|
+
"process.env resolve. A variable already set in the shell wins. `--env <file>`",
|
|
66
|
+
"loads only that file instead. Functions inherit every loaded variable.",
|
|
67
|
+
"",
|
|
68
|
+
"`dev` reuses the branch credential behind the AI Gateway and object storage from",
|
|
69
|
+
"the file `env pull` writes, and never writes to it. With no such file it issues a",
|
|
70
|
+
"credential on every start, so run `env pull` once if you restart often."
|
|
61
71
|
].join("\n")).strict();
|
|
62
72
|
/**
|
|
63
73
|
* The resolver context for a `neon dev` run.
|
|
@@ -74,7 +84,7 @@ const builder = (argv) => argv.usage("$0 dev [--source <path>] [options]").examp
|
|
|
74
84
|
* last one live — one orphan per restart. `neon-env run` already reads the file for this
|
|
75
85
|
* reason; `dev` was the one that didn't.
|
|
76
86
|
*/
|
|
77
|
-
const devEnvContext = (props, branchId, cwd) => {
|
|
87
|
+
const devEnvContext = (props, branchId, cwd, baseEnv = process.env) => {
|
|
78
88
|
const location = {
|
|
79
89
|
cwd,
|
|
80
90
|
...props.contextFile ? { contextFile: props.contextFile } : {}
|
|
@@ -84,7 +94,7 @@ const devEnvContext = (props, branchId, cwd) => {
|
|
|
84
94
|
...location,
|
|
85
95
|
implyAiGateway: true,
|
|
86
96
|
env: {
|
|
87
|
-
...
|
|
97
|
+
...baseEnv,
|
|
88
98
|
...existsSync(envFile) ? readEnvFile(envFile) : {}
|
|
89
99
|
},
|
|
90
100
|
...props.projectId ? { projectId: props.projectId } : {},
|
|
@@ -114,22 +124,70 @@ const reportDevCredential = (credential) => {
|
|
|
114
124
|
log.warning(`Resolved credential secrets for this run (%s) without writing them to a file. Run \`${getCliName()} env pull\` once so restarts reuse them instead of revealing or minting again.`, credential.keys.join(", "));
|
|
115
125
|
};
|
|
116
126
|
const handler = async (props) => {
|
|
127
|
+
const shellEnv = captureShellEnv();
|
|
128
|
+
const envFiles = formatEnvFiles(loadDevEnvFiles(props, process.cwd()), process.cwd());
|
|
117
129
|
if (props.source !== void 0) {
|
|
118
|
-
await runSingleSource(props);
|
|
130
|
+
await runSingleSource(props, shellEnv, envFiles);
|
|
119
131
|
return;
|
|
120
132
|
}
|
|
121
133
|
if (props.port !== void 0) throw new Error("--port can only be used with --source. To set ports for the functions in neon.ts, give each one a `dev.port` in its config.");
|
|
122
|
-
await runFromConfig(props);
|
|
134
|
+
await runFromConfig(props, shellEnv, envFiles);
|
|
135
|
+
};
|
|
136
|
+
/**
|
|
137
|
+
* Copy of the shell env before any dotenv file loads. PORT is read through `process.env`,
|
|
138
|
+
* which is case-insensitive on Windows, so a shell `Port` still sets the port. Exported for
|
|
139
|
+
* unit testing.
|
|
140
|
+
*/
|
|
141
|
+
const captureShellEnv = () => ({
|
|
142
|
+
...process.env,
|
|
143
|
+
PORT: process.env.PORT
|
|
144
|
+
});
|
|
145
|
+
/**
|
|
146
|
+
* Load dotenv files into process.env before neon.ts is evaluated: `--env <file>` alone
|
|
147
|
+
* (relative to cwd), else the project's {@link defaultDevEnvFiles}. A later file overrides
|
|
148
|
+
* an earlier one; a key set before loading (the shell) is never overridden.
|
|
149
|
+
*/
|
|
150
|
+
const loadDevEnvFiles = (props, cwd) => {
|
|
151
|
+
const files = props.env ? [resolve(cwd, props.env)] : defaultDevEnvFiles({
|
|
152
|
+
cwd,
|
|
153
|
+
...props.contextFile ? { contextFile: props.contextFile } : {}
|
|
154
|
+
});
|
|
155
|
+
const name = (key) => process.platform === "win32" ? key.toUpperCase() : key;
|
|
156
|
+
const preset = new Set(Object.keys(process.env).filter((key) => process.env[key] !== void 0).map(name));
|
|
157
|
+
return files.map((path) => {
|
|
158
|
+
const applied = [];
|
|
159
|
+
for (const [key, value] of Object.entries(readEnvFile(path))) {
|
|
160
|
+
if (preset.has(name(key))) continue;
|
|
161
|
+
process.env[key] = value;
|
|
162
|
+
applied.push(key);
|
|
163
|
+
}
|
|
164
|
+
return {
|
|
165
|
+
path,
|
|
166
|
+
applied
|
|
167
|
+
};
|
|
168
|
+
});
|
|
169
|
+
};
|
|
170
|
+
/**
|
|
171
|
+
* The banner's account of the dotenv files, in load order, e.g.
|
|
172
|
+
* `.env.local (4 vars) → .env (2 vars)`. Counts are the keys each file set. Exported for
|
|
173
|
+
* unit testing.
|
|
174
|
+
*/
|
|
175
|
+
const formatEnvFiles = (files, cwd) => {
|
|
176
|
+
if (files.length === 0) return "none (no .env.local or .env)";
|
|
177
|
+
return files.map(({ path, applied }) => {
|
|
178
|
+
const count = `${applied.length} var${applied.length === 1 ? "" : "s"}`;
|
|
179
|
+
return `${relative(cwd, path) || path} (${count})`;
|
|
180
|
+
}).join(" → ");
|
|
123
181
|
};
|
|
124
182
|
/** Single-function mode: serve exactly the `--source` path (legacy behavior). */
|
|
125
|
-
const runSingleSource = async (props) => {
|
|
183
|
+
const runSingleSource = async (props, shellEnv, envFiles) => {
|
|
126
184
|
const source = resolve(process.cwd(), props.source);
|
|
127
185
|
if (!existsSync(source)) throw new Error(`Source file not found: ${source}`);
|
|
128
186
|
const branchId = await resolveBranchId(props);
|
|
129
|
-
const { vars: neonEnv, skipped, credential } = await resolveDevEnv(devEnvContext(props, branchId, process.cwd()));
|
|
187
|
+
const { vars: neonEnv, skipped, credential } = await resolveDevEnv(devEnvContext(props, branchId, process.cwd(), shellEnv));
|
|
130
188
|
reportDevCredential(credential);
|
|
131
189
|
const match = await findConfigFunctionBySource(process.cwd(), source, props.contextFile);
|
|
132
|
-
const { overlay, port } = await sourceFunctionUrlOverlay(match, props.port);
|
|
190
|
+
const { overlay, port } = await sourceFunctionUrlOverlay(match, props.port, shellEnv);
|
|
133
191
|
const unit = {
|
|
134
192
|
slug: null,
|
|
135
193
|
source,
|
|
@@ -144,19 +202,22 @@ const runSingleSource = async (props) => {
|
|
|
144
202
|
fn: []
|
|
145
203
|
}
|
|
146
204
|
};
|
|
147
|
-
await runSupervisor([unit], {
|
|
205
|
+
await runSupervisor([unit], {
|
|
206
|
+
envFiles,
|
|
207
|
+
...skipped ? { envNote: skipped.reason } : {}
|
|
208
|
+
});
|
|
148
209
|
};
|
|
149
210
|
/**
|
|
150
211
|
* Multi-function mode: serve every function declared in neon.ts. Requires a neon.ts
|
|
151
212
|
* (there is no single source to fall back on), one dev server per function.
|
|
152
213
|
*/
|
|
153
|
-
const runFromConfig = async (props) => {
|
|
214
|
+
const runFromConfig = async (props, shellEnv, envFiles) => {
|
|
154
215
|
const branchId = await resolveBranchId(props);
|
|
155
216
|
const resolved = await resolveFunctionsFromConfig(process.cwd(), void 0, props.contextFile);
|
|
156
217
|
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`.");
|
|
157
218
|
const { configPath, functions } = resolved;
|
|
158
219
|
if (functions.length === 0) throw new Error("neon.ts has no functions to serve. Add at least one under `functions`, or pass --source <path>.");
|
|
159
|
-
const { vars: neonEnv, skipped, credential } = await resolveDevEnv(devEnvContext(props, branchId, process.cwd()));
|
|
220
|
+
const { vars: neonEnv, skipped, credential } = await resolveDevEnv(devEnvContext(props, branchId, process.cwd(), shellEnv));
|
|
160
221
|
reportDevCredential(credential);
|
|
161
222
|
const units = await planFunctionsToUnits(functions, neonEnv, DEFAULT_PORT_BASE);
|
|
162
223
|
const replan = async (searchBase, keepPorts) => {
|
|
@@ -169,6 +230,7 @@ const runFromConfig = async (props) => {
|
|
|
169
230
|
configPath,
|
|
170
231
|
replan
|
|
171
232
|
},
|
|
233
|
+
envFiles,
|
|
172
234
|
...skipped ? { envNote: skipped.reason } : {}
|
|
173
235
|
});
|
|
174
236
|
};
|
|
@@ -231,8 +293,8 @@ const localFunctionUrlEnv = (functions) => {
|
|
|
231
293
|
* `--source` serves one file. If that file is a neon.ts function, rewrite only that
|
|
232
294
|
* slug to localhost — this process is not listening for the others.
|
|
233
295
|
*/
|
|
234
|
-
const sourceFunctionUrlOverlay = async (match, cliPort) => {
|
|
235
|
-
const portSpec = portFromProps(cliPort);
|
|
296
|
+
const sourceFunctionUrlOverlay = async (match, cliPort, shellEnv = process.env) => {
|
|
297
|
+
const portSpec = portFromProps(cliPort, shellEnv);
|
|
236
298
|
if (match === void 0) return {
|
|
237
299
|
overlay: {},
|
|
238
300
|
port: portSpec
|
|
@@ -298,14 +360,14 @@ const resolveBranchId = async (props) => {
|
|
|
298
360
|
}
|
|
299
361
|
};
|
|
300
362
|
const DEFAULT_PORT_BASE = 8787;
|
|
301
|
-
const portFromProps = (port) => {
|
|
363
|
+
const portFromProps = (port, shellEnv) => {
|
|
302
364
|
if (port !== void 0) return {
|
|
303
365
|
mode: "explicit",
|
|
304
366
|
port
|
|
305
367
|
};
|
|
306
|
-
if (
|
|
368
|
+
if (shellEnv.PORT !== void 0 && shellEnv.PORT !== "") return {
|
|
307
369
|
mode: "explicit",
|
|
308
|
-
port: Number(
|
|
370
|
+
port: Number(shellEnv.PORT)
|
|
309
371
|
};
|
|
310
372
|
return {
|
|
311
373
|
mode: "search",
|
|
@@ -387,7 +449,7 @@ const READY_PATTERN = /neon-dev:ready (\d+)/;
|
|
|
387
449
|
* in place; siblings are untouched.
|
|
388
450
|
*/
|
|
389
451
|
const runSupervisor = async (units, options = {}) => {
|
|
390
|
-
const { reload, envNote } = options;
|
|
452
|
+
const { reload, envNote, envFiles } = options;
|
|
391
453
|
const runtimePath = resolveRuntimePath();
|
|
392
454
|
let shuttingDown = false;
|
|
393
455
|
const running = units.map(makeRunningUnit);
|
|
@@ -456,7 +518,7 @@ const runSupervisor = async (units, options = {}) => {
|
|
|
456
518
|
await Promise.all(running.map((r) => stopUnit(r)));
|
|
457
519
|
throw new Error("No function started. See the output above for details.");
|
|
458
520
|
}
|
|
459
|
-
printBanner(running, envNote);
|
|
521
|
+
printBanner(running, envNote, envFiles);
|
|
460
522
|
let configWatcher = null;
|
|
461
523
|
if (reload) {
|
|
462
524
|
const ops = {
|
|
@@ -709,10 +771,14 @@ const pipeChildOutput = (child, label) => {
|
|
|
709
771
|
forward("stdout");
|
|
710
772
|
forward("stderr");
|
|
711
773
|
};
|
|
712
|
-
const printBanner = (running, envNote) => {
|
|
774
|
+
const printBanner = (running, envNote, envFiles) => {
|
|
713
775
|
log.info("");
|
|
714
776
|
log.info(chalk.green.bold(" Neon Functions dev server"));
|
|
715
777
|
log.info("");
|
|
778
|
+
if (envFiles) {
|
|
779
|
+
log.info(` ${chalk.dim("env files".padEnd(20))} ${chalk.dim(envFiles)}`);
|
|
780
|
+
log.info("");
|
|
781
|
+
}
|
|
716
782
|
for (const r of running) {
|
|
717
783
|
const name = r.unit.label ?? "function";
|
|
718
784
|
const url = urlFor(r.boundPort);
|
|
@@ -876,4 +942,4 @@ const resolveRuntimePath = () => {
|
|
|
876
942
|
return candidate;
|
|
877
943
|
};
|
|
878
944
|
//#endregion
|
|
879
|
-
export { builder, command, describe, devBundleDir, devEnvContext, diffUnits, formatEnvSummary, handler, localFunctionUrlEnv, planFunctionsToUnits, reportDevCredential, sourceFunctionUrlOverlay, dev_exports as t };
|
|
945
|
+
export { builder, captureShellEnv, command, describe, devBundleDir, devEnvContext, diffUnits, formatEnvFiles, formatEnvSummary, handler, loadDevEnvFiles, localFunctionUrlEnv, planFunctionsToUnits, reportDevCredential, sourceFunctionUrlOverlay, dev_exports as t };
|
package/dist/commands/env.js
CHANGED
|
@@ -7,10 +7,9 @@ import { deprecatedServiceMessage, parseServices, servicesFlagValue, servicesOpt
|
|
|
7
7
|
import { mergeEnvFile, readEnvFile, resolveEnvFilePath } from "../env_file.js";
|
|
8
8
|
import { t as NEON_ENV_VAR_KEYS, u as isFunctionBaseUrlKey } from "../_chunks/env-BpP3Dqpe.js";
|
|
9
9
|
import { ENV_PULL_KEY_HELP, ENV_PULL_SERVICES, envKeysForSelection, envPullFlagValue, ownedEnvServiceKeys, parseEnvPullKeys } from "../env_services.js";
|
|
10
|
-
import {
|
|
10
|
+
import { i as dropClaimableUnsupported, o as resolveNeonEnvVars } from "../_chunks/env-BNm69Qh8.js";
|
|
11
11
|
import { fillSingleProject, resolveBranchRef } from "../utils/enrichers.js";
|
|
12
12
|
import { warnAiGateway } from "../utils/ai_gateway_notice.js";
|
|
13
|
-
import { i as dropClaimableUnsupported, o as resolveNeonEnvVars } from "../_chunks/env-4WGRW2nw.js";
|
|
14
13
|
import { formatPulledEnv, pulledBranch } from "./env_output.js";
|
|
15
14
|
import { existsSync } from "node:fs";
|
|
16
15
|
import chalk from "chalk";
|
|
@@ -48,7 +47,7 @@ const builder = (argv) => argv.usage("$0 env <sub-command> [options]").options({
|
|
|
48
47
|
},
|
|
49
48
|
service: servicesOption({
|
|
50
49
|
key: "service",
|
|
51
|
-
allowed:
|
|
50
|
+
allowed: ENV_PULL_SERVICES,
|
|
52
51
|
describe: "Pull only these services' variables",
|
|
53
52
|
also: "Overrides neon.ts, and prunes only within the services you name. Combines with --env as a union."
|
|
54
53
|
}),
|
package/dist/commands/init.js
CHANGED
|
@@ -2,7 +2,6 @@ import { t as __exportAll } from "../_chunks/rolldown-runtime-8H4AJuhK.js";
|
|
|
2
2
|
import { getCliName } from "../utils/cli_name.js";
|
|
3
3
|
import { helpCsv, helpEpilogue } from "../utils/help_text.js";
|
|
4
4
|
import { NEON_SERVICES, servicesOption } from "../neon_services.js";
|
|
5
|
-
import { listedServices } from "../utils/staging.js";
|
|
6
5
|
import { coerceAgentFlag } from "../utils/agent_flag.js";
|
|
7
6
|
import { initPluginAgents, initSkillsMcpAgents } from "../init/plan.js";
|
|
8
7
|
import { listSkillIds } from "../skills/catalog.js";
|
|
@@ -52,7 +51,7 @@ const builder = (yargs) => yargs.usage("$0 init [options]").option("context-file
|
|
|
52
51
|
describe: "Create neon.ts after linking. Use --no-config to skip. Omitted in Custom: you will be asked"
|
|
53
52
|
}).option("services", servicesOption({
|
|
54
53
|
key: "services",
|
|
55
|
-
allowed:
|
|
54
|
+
allowed: NEON_SERVICES,
|
|
56
55
|
describe: "Services to declare in neon.ts",
|
|
57
56
|
also: "postgres alone writes the starter policy; every branch has Postgres. Implies creating neon.ts. Cannot be combined with --no-config."
|
|
58
57
|
})).option("org-id", {
|
package/dist/commands/link.js
CHANGED
|
@@ -864,7 +864,7 @@ const printSummary = (props, summary) => {
|
|
|
864
864
|
*/
|
|
865
865
|
const finalizeLink = async (props, summary) => {
|
|
866
866
|
printSummary(props, summary);
|
|
867
|
-
if (!summary.branch || !summary.projectId || summary.noChecks) return;
|
|
867
|
+
if (!summary.branch || !summary.projectId || summary.noChecks || props.deferEnvPull === true) return;
|
|
868
868
|
const { config: _offerConfig, ...rest } = props;
|
|
869
869
|
await autoPullEnvAfterPin({
|
|
870
870
|
...rest,
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { getCliName } from "../utils/cli_name.js";
|
|
2
|
-
import { REALTIME_COMING_SOON } from "../utils/staging.js";
|
|
3
2
|
import { Parser } from "yargs/helpers";
|
|
4
3
|
//#region src/commands/manifest.ts
|
|
5
4
|
/**
|
|
@@ -190,8 +189,7 @@ const commandManifest = [
|
|
|
190
189
|
{
|
|
191
190
|
command: "realtime",
|
|
192
191
|
describe: "Manage Neon Realtime for a branch",
|
|
193
|
-
load: () => import("./realtime.js")
|
|
194
|
-
stagingOnly: REALTIME_COMING_SOON
|
|
192
|
+
load: () => import("./realtime.js")
|
|
195
193
|
},
|
|
196
194
|
{
|
|
197
195
|
command: "functions",
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import { writer } from "../writer.js";
|
|
2
|
+
import { o as resolveNeonEnvVars } from "../_chunks/env-BNm69Qh8.js";
|
|
2
3
|
import { branchIdFromProps } from "../utils/enrichers.js";
|
|
3
|
-
import { o as resolveNeonEnvVars } from "../_chunks/env-4WGRW2nw.js";
|
|
4
4
|
import YAML from "yaml";
|
|
5
5
|
//#region src/commands/subscribe.ts
|
|
6
6
|
const builder = (argv) => argv.usage("$0 realtime subscribe \"<query>\" [options]").positional("query", {
|
|
7
7
|
describe: "SELECT query to subscribe to",
|
|
8
8
|
type: "string"
|
|
9
9
|
}).options({ changes: {
|
|
10
|
-
describe: "Show raw
|
|
10
|
+
describe: "Show raw rows and changes events",
|
|
11
11
|
type: "boolean",
|
|
12
12
|
default: false
|
|
13
|
-
} }).example("$0 realtime subscribe \"SELECT * FROM items\"", "Print the initial result followed by a readable transaction log").example("$0 realtime subscribe --changes \"SELECT * FROM items\"", "Print raw
|
|
13
|
+
} }).example("$0 realtime subscribe \"SELECT * FROM items\"", "Print the initial result followed by a readable transaction log").example("$0 realtime subscribe --changes \"SELECT * FROM items\"", "Print raw rows and changes events").epilogue("The subscription runs until interrupted. Terminal output is an append-only snapshot and transaction log; JSON output is newline-delimited; YAML output uses one document per event.").strict();
|
|
14
14
|
const handler = async (args) => {
|
|
15
15
|
const props = args;
|
|
16
16
|
const controller = new AbortController();
|
|
@@ -140,16 +140,16 @@ const createRenderer = (props, context, now) => {
|
|
|
140
140
|
return;
|
|
141
141
|
}
|
|
142
142
|
out.write("\n");
|
|
143
|
-
if (event.type === "
|
|
144
|
-
const label = hasBaseline ? "
|
|
143
|
+
if (event.type === "rows" && !props.changes) {
|
|
144
|
+
const label = hasBaseline ? "Rows" : "Initial snapshot";
|
|
145
145
|
hasBaseline = true;
|
|
146
146
|
rowsById.clear();
|
|
147
147
|
for (const { rowId, row } of event.rows) rowsById.set(rowId, row);
|
|
148
148
|
renderMaterializedRows(out, event.rows.map(({ row }) => row), `${timestamp(now)} ${label}`);
|
|
149
149
|
return;
|
|
150
150
|
}
|
|
151
|
-
if (event.type === "
|
|
152
|
-
|
|
151
|
+
if (event.type === "changes" && !props.changes) {
|
|
152
|
+
renderHumanChanges(out, event, rowsById, now);
|
|
153
153
|
return;
|
|
154
154
|
}
|
|
155
155
|
renderTableEvent(out, event, now);
|
|
@@ -160,12 +160,12 @@ const renderTableEvent = (out, event, now) => {
|
|
|
160
160
|
renderMaterializedRows(out, event.rows, `${timestamp(now)} Snapshot`);
|
|
161
161
|
return;
|
|
162
162
|
}
|
|
163
|
-
if (event.type === "
|
|
163
|
+
if (event.type === "rows") {
|
|
164
164
|
const rows = event.rows.map(({ rowId, row }) => ({
|
|
165
165
|
row_id: rowId,
|
|
166
166
|
row
|
|
167
167
|
}));
|
|
168
|
-
renderRows(out, rows, ["row_id", "row"], `${timestamp(now)}
|
|
168
|
+
renderRows(out, rows, ["row_id", "row"], `${timestamp(now)} Rows · ${rowCount(rows.length)}`, "Rows event contains no rows.");
|
|
169
169
|
return;
|
|
170
170
|
}
|
|
171
171
|
const rows = event.changes.map((change) => ({
|
|
@@ -177,7 +177,7 @@ const renderTableEvent = (out, event, now) => {
|
|
|
177
177
|
"type",
|
|
178
178
|
"row_id",
|
|
179
179
|
"row"
|
|
180
|
-
], `${timestamp(now)} ${transactionLabel(event.txids)} · ${rawChangeSummary(event.changes)}`, "
|
|
180
|
+
], `${timestamp(now)} ${transactionLabel(event.txids)} · ${rawChangeSummary(event.changes)}`, "Changes event contains no row changes.");
|
|
181
181
|
};
|
|
182
182
|
const renderHumanHeader = (out, context, rawChanges) => {
|
|
183
183
|
const target = {
|
|
@@ -205,18 +205,18 @@ const renderMaterializedRows = (out, rows, title) => {
|
|
|
205
205
|
const humanTitle = `${title} · ${rowCount(rows.length)}`;
|
|
206
206
|
renderRows(out, rows, rowFields(rows), humanTitle, "Query returned no rows.");
|
|
207
207
|
};
|
|
208
|
-
const
|
|
208
|
+
const renderHumanChanges = (out, event, rowsById, now) => {
|
|
209
209
|
const changes = classifyChanges(event.changes, rowsById);
|
|
210
210
|
const inserted = changes.filter((change) => change.operation === "+ insert");
|
|
211
211
|
const updated = changes.filter((change) => change.operation === "~ update");
|
|
212
212
|
const deleted = changes.filter((change) => change.operation === "- delete");
|
|
213
|
-
const
|
|
214
|
-
const operationField = uniqueOperationField(
|
|
213
|
+
const rowFieldsInChanges = rowFields(changes.flatMap((change) => change.row ? [change.row] : []));
|
|
214
|
+
const operationField = uniqueOperationField(rowFieldsInChanges);
|
|
215
215
|
const rows = changes.map((change) => ({
|
|
216
216
|
...change.row ?? {},
|
|
217
217
|
[operationField]: change.operation
|
|
218
218
|
}));
|
|
219
|
-
renderRows(out, rows, [operationField, ...
|
|
219
|
+
renderRows(out, rows, [operationField, ...rowFieldsInChanges], `${timestamp(now)} ${transactionLabel(event.txids)} · +${inserted.length} ~${updated.length} -${deleted.length}`, "Transaction contains no row changes.");
|
|
220
220
|
};
|
|
221
221
|
const renderRows = (out, rows, fields, title, emptyMessage) => {
|
|
222
222
|
writer({
|
|
@@ -292,11 +292,11 @@ const consumeSnapshots = async (subscription, render, signal) => consumeSubscrip
|
|
|
292
292
|
}))], render, signal);
|
|
293
293
|
const consumeChanges = async (subscription, render, signal) => consumeSubscription(subscription, (fail) => [subscription.onUpdate((update) => {
|
|
294
294
|
if (update.type === "rows") attempt(fail, () => render({
|
|
295
|
-
type: "
|
|
295
|
+
type: "rows",
|
|
296
296
|
rows: update.rows
|
|
297
297
|
}));
|
|
298
298
|
else attempt(fail, () => render({
|
|
299
|
-
type: "
|
|
299
|
+
type: "changes",
|
|
300
300
|
txids: update.batch.txids,
|
|
301
301
|
changes: update.changes
|
|
302
302
|
}));
|
package/dist/credential_io.js
CHANGED
|
@@ -1,2 +1 @@
|
|
|
1
|
-
|
|
2
|
-
export { storeFor };
|
|
1
|
+
export { t as storeFor } from "./_chunks/credential_io-ByNuojoE.js";
|