neonctl 2.37.1 → 2.38.1

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.
Files changed (159) hide show
  1. package/README.md +15 -700
  2. package/bin/cli.js +3 -0
  3. package/package.json +8 -75
  4. package/dist/analytics.js +0 -168
  5. package/dist/api.js +0 -720
  6. package/dist/auth.js +0 -125
  7. package/dist/callback.html +0 -60
  8. package/dist/cli.js +0 -9
  9. package/dist/commands/api.js +0 -278
  10. package/dist/commands/auth.js +0 -214
  11. package/dist/commands/bootstrap.js +0 -481
  12. package/dist/commands/branches.js +0 -488
  13. package/dist/commands/bucket.js +0 -549
  14. package/dist/commands/checkout.js +0 -321
  15. package/dist/commands/config.js +0 -626
  16. package/dist/commands/connection_string.js +0 -172
  17. package/dist/commands/data_api.js +0 -284
  18. package/dist/commands/databases.js +0 -82
  19. package/dist/commands/deploy.js +0 -26
  20. package/dist/commands/dev.js +0 -698
  21. package/dist/commands/diff.js +0 -222
  22. package/dist/commands/env.js +0 -189
  23. package/dist/commands/functions.js +0 -373
  24. package/dist/commands/index.js +0 -62
  25. package/dist/commands/init.js +0 -73
  26. package/dist/commands/inspect.js +0 -65
  27. package/dist/commands/ip_allow.js +0 -137
  28. package/dist/commands/link.js +0 -1121
  29. package/dist/commands/neon_auth.js +0 -1028
  30. package/dist/commands/operations.js +0 -28
  31. package/dist/commands/orgs.js +0 -24
  32. package/dist/commands/projects.js +0 -413
  33. package/dist/commands/psql.js +0 -62
  34. package/dist/commands/roles.js +0 -65
  35. package/dist/commands/schema_diff.js +0 -151
  36. package/dist/commands/set_context.js +0 -29
  37. package/dist/commands/snapshots.js +0 -455
  38. package/dist/commands/status.js +0 -40
  39. package/dist/commands/user.js +0 -15
  40. package/dist/commands/vpc_endpoints.js +0 -134
  41. package/dist/config.js +0 -11
  42. package/dist/config_format.js +0 -72
  43. package/dist/context.js +0 -231
  44. package/dist/current_branch_fast_path.js +0 -55
  45. package/dist/dev/env.js +0 -244
  46. package/dist/dev/functions.js +0 -70
  47. package/dist/dev/inputs.js +0 -63
  48. package/dist/dev/runtime.js +0 -146
  49. package/dist/env.js +0 -36
  50. package/dist/env_file.js +0 -159
  51. package/dist/errors.js +0 -80
  52. package/dist/functions_api.js +0 -48
  53. package/dist/help.js +0 -146
  54. package/dist/index.js +0 -240
  55. package/dist/log.js +0 -18
  56. package/dist/parameters.gen.js +0 -480
  57. package/dist/pkg.js +0 -25
  58. package/dist/psql/cli.js +0 -53
  59. package/dist/psql/command/cmd_cond.js +0 -437
  60. package/dist/psql/command/cmd_connect.js +0 -820
  61. package/dist/psql/command/cmd_copy.js +0 -1035
  62. package/dist/psql/command/cmd_describe.js +0 -1815
  63. package/dist/psql/command/cmd_format.js +0 -948
  64. package/dist/psql/command/cmd_io.js +0 -2193
  65. package/dist/psql/command/cmd_lo.js +0 -393
  66. package/dist/psql/command/cmd_meta.js +0 -969
  67. package/dist/psql/command/cmd_misc.js +0 -187
  68. package/dist/psql/command/cmd_pipeline.js +0 -1148
  69. package/dist/psql/command/cmd_restrict.js +0 -171
  70. package/dist/psql/command/cmd_show.js +0 -766
  71. package/dist/psql/command/dispatch.js +0 -343
  72. package/dist/psql/command/inputQueue.js +0 -42
  73. package/dist/psql/command/shared.js +0 -71
  74. package/dist/psql/complete/filenames.js +0 -139
  75. package/dist/psql/complete/index.js +0 -104
  76. package/dist/psql/complete/matcher.js +0 -315
  77. package/dist/psql/complete/psqlVars.js +0 -249
  78. package/dist/psql/complete/queries.js +0 -493
  79. package/dist/psql/complete/rules.js +0 -2424
  80. package/dist/psql/core/common.js +0 -1253
  81. package/dist/psql/core/help.js +0 -576
  82. package/dist/psql/core/mainloop.js +0 -1360
  83. package/dist/psql/core/prompt.js +0 -439
  84. package/dist/psql/core/settings.js +0 -686
  85. package/dist/psql/core/sqlHelp.js +0 -1066
  86. package/dist/psql/core/startup.js +0 -846
  87. package/dist/psql/core/syncVars.js +0 -116
  88. package/dist/psql/core/variables.js +0 -287
  89. package/dist/psql/describe/formatters.js +0 -1330
  90. package/dist/psql/describe/processNamePattern.js +0 -270
  91. package/dist/psql/describe/queries.js +0 -2452
  92. package/dist/psql/describe/versionGate.js +0 -44
  93. package/dist/psql/index.js +0 -2030
  94. package/dist/psql/io/history.js +0 -299
  95. package/dist/psql/io/input.js +0 -120
  96. package/dist/psql/io/lineEditor/buffer.js +0 -325
  97. package/dist/psql/io/lineEditor/complete.js +0 -227
  98. package/dist/psql/io/lineEditor/filename.js +0 -159
  99. package/dist/psql/io/lineEditor/index.js +0 -893
  100. package/dist/psql/io/lineEditor/keymap.js +0 -745
  101. package/dist/psql/io/lineEditor/vt100.js +0 -363
  102. package/dist/psql/io/pgpass.js +0 -202
  103. package/dist/psql/io/pgservice.js +0 -194
  104. package/dist/psql/io/psqlrc.js +0 -422
  105. package/dist/psql/print/aligned.js +0 -1748
  106. package/dist/psql/print/asciidoc.js +0 -230
  107. package/dist/psql/print/crosstab.js +0 -463
  108. package/dist/psql/print/csv.js +0 -76
  109. package/dist/psql/print/html.js +0 -240
  110. package/dist/psql/print/json.js +0 -96
  111. package/dist/psql/print/latex.js +0 -379
  112. package/dist/psql/print/pager.js +0 -267
  113. package/dist/psql/print/troff.js +0 -240
  114. package/dist/psql/print/unaligned.js +0 -99
  115. package/dist/psql/print/units.js +0 -188
  116. package/dist/psql/scanner/slash.js +0 -515
  117. package/dist/psql/scanner/sql.js +0 -914
  118. package/dist/psql/scanner/stringutils.js +0 -394
  119. package/dist/psql/types/backslash.js +0 -1
  120. package/dist/psql/types/connection.js +0 -1
  121. package/dist/psql/types/index.js +0 -7
  122. package/dist/psql/types/printer.js +0 -1
  123. package/dist/psql/types/repl.js +0 -1
  124. package/dist/psql/types/scanner.js +0 -24
  125. package/dist/psql/types/settings.js +0 -1
  126. package/dist/psql/types/variables.js +0 -1
  127. package/dist/psql/wire/connection.js +0 -2857
  128. package/dist/psql/wire/copy.js +0 -108
  129. package/dist/psql/wire/notify.js +0 -59
  130. package/dist/psql/wire/pipeline.js +0 -521
  131. package/dist/psql/wire/protocol.js +0 -466
  132. package/dist/psql/wire/sasl.js +0 -294
  133. package/dist/psql/wire/tls.js +0 -602
  134. package/dist/storage_api.js +0 -147
  135. package/dist/test_utils/fixtures.js +0 -121
  136. package/dist/test_utils/oauth_server.js +0 -9
  137. package/dist/types.js +0 -1
  138. package/dist/utils/ai_gateway_notice.js +0 -180
  139. package/dist/utils/api_enums.js +0 -33
  140. package/dist/utils/auth.js +0 -5
  141. package/dist/utils/branch_notice.js +0 -22
  142. package/dist/utils/branch_picker.js +0 -103
  143. package/dist/utils/compute_units.js +0 -28
  144. package/dist/utils/config_diff.js +0 -185
  145. package/dist/utils/enrichers.js +0 -161
  146. package/dist/utils/esbuild.js +0 -158
  147. package/dist/utils/formats.js +0 -18
  148. package/dist/utils/git_diff.js +0 -90
  149. package/dist/utils/inspect_db.js +0 -153
  150. package/dist/utils/inspect_queries.js +0 -372
  151. package/dist/utils/middlewares.js +0 -20
  152. package/dist/utils/openapi.js +0 -114
  153. package/dist/utils/package_manager.js +0 -68
  154. package/dist/utils/point_in_time.js +0 -56
  155. package/dist/utils/psql.js +0 -120
  156. package/dist/utils/string.js +0 -5
  157. package/dist/utils/ui.js +0 -59
  158. package/dist/utils/zip.js +0 -4
  159. package/dist/writer.js +0 -97
package/dist/dev/env.js DELETED
@@ -1,244 +0,0 @@
1
- import { loadConfigFromFile } from "@neon/config";
2
- import { plan, pullConfig } from "@neon/config-runtime";
3
- import { fetchEnv, toEntries } from "@neon/env";
4
- import { log } from "../log.js";
5
- /** The API-targeting options every runtime call forwards from the context. */
6
- const apiOptions = (ctx) => ({
7
- ...(ctx.apiKey ? { apiKey: ctx.apiKey } : {}),
8
- ...(ctx.apiHost ? { apiHost: ctx.apiHost } : {}),
9
- ...(ctx.api ? { api: ctx.api } : {}),
10
- });
11
- /**
12
- * Thrown when a `neon.ts` policy declares a branch-level resource (Neon Auth,
13
- * Data API, a bucket, the AI Gateway) that the linked remote branch does not
14
- * have yet. Unlike every other failure in {@link resolveDevEnv} — which degrades
15
- * to "run without injection" — this is a hard stop: the user's intent (a policy)
16
- * cannot be honored, and silently dropping the secret would be more confusing
17
- * than refusing to start. The fix is to provision the resource first.
18
- */
19
- export class DevEnvMismatchError extends Error {
20
- constructor() {
21
- super(...arguments);
22
- this.name = "DevEnvMismatchError";
23
- }
24
- }
25
- /**
26
- * Signals that no project/branch context could be resolved, so there is nothing to
27
- * resolve env from. `resolveDevEnv` degrades on this (dev runs without injection);
28
- * `env pull` surfaces it (an explicit pull needs a branch).
29
- */
30
- export class MissingBranchContextError extends Error {
31
- constructor() {
32
- super(...arguments);
33
- this.name = "MissingBranchContextError";
34
- }
35
- }
36
- /**
37
- * Resolve the branch's Neon env vars (pooled / direct `DATABASE_URL`, plus Auth /
38
- * Data API when enabled) into a `{ KEY: value }` map. Shared by `neon dev` (which
39
- * injects them) and `neon env pull` (which writes them to a `.env` file).
40
- *
41
- * Tiered:
42
- *
43
- * 1. a `neon.ts` policy is found -> the policy is the source of truth. We first
44
- * check it against the branch's live state (`plan`); if it declares a resource
45
- * the branch is missing, we stop with a {@link DevEnvMismatchError} pointing at
46
- * `neonctl deploy`. Otherwise `fetchEnv` evaluates the policy.
47
- * 2. no `neon.ts`, but a project + branch are known -> `pullConfig` reads the
48
- * branch's live state (Auth / Data API enablement plus any object-storage
49
- * buckets) into a config, then `fetchEnv` resolves what is actually enabled —
50
- * so a branch with a bucket gets its `AWS_*` storage vars pulled with no policy.
51
- * 3. otherwise -> throw {@link MissingBranchContextError}.
52
- *
53
- * Unlike {@link resolveDevEnv}, this never swallows errors — callers decide how to
54
- * handle them.
55
- */
56
- export const resolveNeonEnvVars = async (ctx) => {
57
- const config = await loadNeonConfig(ctx.cwd);
58
- if (config) {
59
- if (!ctx.projectId || !ctx.branchId) {
60
- throw new MissingBranchContextError("Found a neon.ts but could not resolve the project/branch. " +
61
- "Run `neonctl link` and `neonctl checkout <branch>`, or pass " +
62
- "--project-id / --branch.");
63
- }
64
- // Resolve env from the policy with its `preview.functions` removed. Functions carry no
65
- // branch-level secrets — their env comes from the local `neon.ts` `functions.<slug>.env`,
66
- // layered per-function by the dev server — so env resolution never needs the functions
67
- // API. Probing it (via `plan`/`fetchEnv`) only adds a failure mode: an undeployed
68
- // function, or a project where the Functions Preview isn't enabled, would error and sink
69
- // ALL injection (including DATABASE_URL). Stripping functions keeps env resolution honest
70
- // while leaving buckets / AI Gateway / Auth / Data API fully checked — those DO carry
71
- // secrets, so a declared-but-missing one still hard-stops (see assertPolicyMatchesBranch).
72
- const envConfig = withoutPreviewFunctions(config);
73
- await assertPolicyMatchesBranch(envConfig, ctx);
74
- return await fetchAndProject(envConfig, ctx);
75
- }
76
- if (ctx.projectId && ctx.branchId) {
77
- const pulled = await pullConfig({
78
- projectId: ctx.projectId,
79
- branchId: ctx.branchId,
80
- ...apiOptions(ctx),
81
- });
82
- // `pulled.config` is already a `Config` (static auth/dataApi toggles, any
83
- // object-storage `preview.buckets`, and a branch tuning closure), so it feeds
84
- // straight into fetchEnv — no wrapping needed. pullConfig excludes functions and
85
- // the AI Gateway (neither can be faithfully read back), so fetchEnv never probes
86
- // the functions API here and only mints a storage credential when a bucket exists.
87
- return await fetchAndProject(pulled.config, ctx);
88
- }
89
- throw new MissingBranchContextError("No project/branch context found. Link a branch (`neonctl link` / " +
90
- "`neonctl checkout`) or pass --project-id and --branch.");
91
- };
92
- /**
93
- * `neon dev`'s env resolver: {@link resolveNeonEnvVars} with graceful degradation.
94
- *
95
- * - Success → `{ vars }` (possibly just the always-present Postgres URLs).
96
- * - No linked branch / project → `{ vars: {}, skipped }` with a "link a branch" hint; the
97
- * function still runs locally, just without Neon env.
98
- * - Any other failure (offline, transient API error) → `{ vars: {}, skipped }` naming the
99
- * cause; again non-fatal.
100
- * - {@link DevEnvMismatchError} (policy declares a secret-bearing service the branch lacks)
101
- * is the one hard stop and is re-thrown for the caller to surface.
102
- */
103
- export const resolveDevEnv = async (ctx) => {
104
- try {
105
- return { vars: await resolveNeonEnvVars(ctx) };
106
- }
107
- catch (err) {
108
- if (err instanceof DevEnvMismatchError)
109
- throw err;
110
- if (err instanceof MissingBranchContextError) {
111
- log.debug("dev: %s; skipping env injection", err.message);
112
- return {
113
- vars: {},
114
- skipped: {
115
- reason: "no linked Neon branch — run `neonctl link`, then " +
116
- "`neonctl checkout <branch>`, to inject DATABASE_URL and friends",
117
- },
118
- };
119
- }
120
- const detail = err instanceof Error ? err.message : String(err);
121
- log.debug("dev: env resolution failed: %s", detail);
122
- return {
123
- vars: {},
124
- skipped: {
125
- reason: `could not reach Neon (${detail}); running without Neon env`,
126
- },
127
- };
128
- }
129
- };
130
- /**
131
- * Return the policy with its `preview.functions` removed, so the env path never enumerates
132
- * functions against the Neon API. Functions are local-source-bundled and produce no
133
- * branch-level secrets, so they are irrelevant to env resolution; probing them only risks
134
- * failing the whole resolve (undeployed function, or Functions Preview disabled on the
135
- * project). Buckets / AI Gateway and the top-level Auth / Data API toggles are preserved —
136
- * they DO carry env, so they must still be checked and resolved. Returns the config
137
- * unchanged when it declares no functions.
138
- */
139
- const withoutPreviewFunctions = (config) => {
140
- const preview = config.preview;
141
- if (!preview?.functions)
142
- return config;
143
- const previewWithoutFunctions = { ...preview };
144
- delete previewWithoutFunctions.functions;
145
- return { ...config, preview: previewWithoutFunctions };
146
- };
147
- /**
148
- * Tier-1 guard. Dry-run the policy against the branch's live state and stop if
149
- * it declares a branch-level resource the branch is missing. Built on `plan` so
150
- * it covers every present and future provisionable resource for free: any
151
- * `create` action is a resource `neonctl deploy` would provision.
152
- *
153
- * Called with functions already stripped (see {@link withoutPreviewFunctions}), so the
154
- * `plan` probe never enumerates the functions API — an undeployed function, or a project
155
- * without the Functions Preview, must never block local dev or sink env injection.
156
- */
157
- const assertPolicyMatchesBranch = async (config, ctx) => {
158
- const result = await plan(config, {
159
- projectId: ctx.projectId,
160
- branchId: ctx.branchId,
161
- ...apiOptions(ctx),
162
- });
163
- const missing = result.applied.filter(isMissingResource);
164
- if (missing.length === 0)
165
- return;
166
- const names = missing.map((change) => change.identifier).join(", ");
167
- throw new DevEnvMismatchError(`Your neon.ts declares ${names} for branch ${ctx.branchId}, but the branch ` +
168
- "does not have it yet, so the matching env vars cannot be injected. " +
169
- "Provision it first with `neonctl deploy` (or `neonctl config apply`), " +
170
- "then re-run `neonctl dev`.");
171
- };
172
- /**
173
- * A planned change that provisions a branch-level resource the branch lacks: a
174
- * `create` on a service (Neon Auth, Data API, a bucket, the AI Gateway). Branch
175
- * setting drift (`update`) and `noop`s are ignored — they don't block local dev
176
- * — and functions are excluded (see {@link assertPolicyMatchesBranch}).
177
- */
178
- const isMissingResource = (change) => change.kind === "service" &&
179
- change.action === "create" &&
180
- !change.identifier.startsWith("function:");
181
- const fetchAndProject = async (config, ctx) => {
182
- const env = await fetchEnv(config, {
183
- projectId: ctx.projectId,
184
- branch: ctx.branchId,
185
- ...apiOptions(ctx),
186
- ...(ctx.env ? { env: ctx.env } : {}),
187
- });
188
- return toEntries(env);
189
- };
190
- /**
191
- * Load a `neon.ts` policy if one exists on the path from `cwd` up to the repo
192
- * root. Returns `null` when there is none (the common "no config" case), and
193
- * surfaces real load errors (e.g. a syntax error in an existing file).
194
- */
195
- /**
196
- * Substrings that mark a module-resolution failure while loading `neon.ts` —
197
- * almost always because the project's dependencies aren't installed yet (the
198
- * config imports `@neon/config` & friends). Deliberately specific:
199
- * the generic "…or a missing dependency…" hint the loader always appends is
200
- * NOT in here, so a real syntax/runtime error doesn't get mislabeled.
201
- */
202
- const MISSING_DEPENDENCY_HINTS = [
203
- "cannot find module",
204
- "cannot find package",
205
- "err_module_not_found",
206
- "failed to resolve",
207
- "could not resolve",
208
- "module not found",
209
- ];
210
- /** Flatten an error and its `cause` chain to one lowercased string for matching. */
211
- const errorChainText = (err) => {
212
- const parts = [];
213
- let current = err;
214
- for (let depth = 0; current instanceof Error && depth < 6; depth++) {
215
- parts.push(current.message);
216
- current = current.cause;
217
- }
218
- return parts.join("\n").toLowerCase();
219
- };
220
- const looksLikeMissingDependency = (err) => {
221
- const text = errorChainText(err);
222
- return MISSING_DEPENDENCY_HINTS.some((hint) => text.includes(hint));
223
- };
224
- const loadNeonConfig = async (cwd) => {
225
- try {
226
- const { config } = await loadConfigFromFile({ cwd });
227
- return config;
228
- }
229
- catch (err) {
230
- const message = err instanceof Error ? err.message : String(err);
231
- if (/Could not find a Neon config file/i.test(message)) {
232
- return null;
233
- }
234
- // A neon.ts that imports a package which isn't installed fails here with a
235
- // cryptic "Cannot find module …". Turn that into the actionable thing to do.
236
- if (looksLikeMissingDependency(err)) {
237
- throw new Error("Could not load neon.ts: a package it imports is not installed. " +
238
- "Did you run `npm install`? Install your dependencies " +
239
- "(npm / pnpm / yarn / bun), then try again.\n" +
240
- `Original error: ${message}`);
241
- }
242
- throw err;
243
- }
244
- };
@@ -1,70 +0,0 @@
1
- import { existsSync } from "node:fs";
2
- import { dirname, isAbsolute, resolve } from "node:path";
3
- import { loadConfigFromFile, resolveConfig, } from "@neon/config";
4
- /**
5
- * Load `neon.ts` (if any) and resolve the list of functions it declares into
6
- * {@link PlannedFunction}s for `neon dev` to serve. Returns `null` when there is no
7
- * `neon.ts` on the path from `cwd` up to the repo root — the caller turns that into a
8
- * "no --source and no neon.ts" error.
9
- *
10
- * `branchName` is used only to evaluate a policy that switches on `branch.name`; the
11
- * function list is otherwise branch-independent, so a placeholder is fine when unknown.
12
- */
13
- export const resolveFunctionsFromConfig = async (cwd, branchName) => {
14
- const loaded = await loadNeonConfig(cwd);
15
- if (!loaded)
16
- return null;
17
- const { config, configDir, configPath } = loaded;
18
- const resolved = resolveConfig(config, {
19
- name: branchName ?? "local",
20
- exists: branchName !== undefined,
21
- });
22
- const functions = resolved.preview?.functions ?? [];
23
- const planned = functions.map((fn) => {
24
- const source = isAbsolute(fn.source)
25
- ? fn.source
26
- : resolve(configDir, fn.source);
27
- if (!existsSync(source)) {
28
- throw new Error(`Function "${fn.slug}" points at a source that does not exist: ${source} ` +
29
- `(from neon.ts "${fn.source}"). Fix the source path and re-run.`);
30
- }
31
- return {
32
- slug: fn.slug,
33
- name: fn.name,
34
- source,
35
- ...(devPort(fn.dev) !== undefined
36
- ? { port: devPort(fn.dev) }
37
- : {}),
38
- env: { ...fn.env },
39
- };
40
- });
41
- return { configPath, functions: planned };
42
- };
43
- /**
44
- * Read the `port` off a {@link FunctionDevConfig}. `undefined` when no `dev.port` is set
45
- * (the supervisor then searches for a free port).
46
- */
47
- const devPort = (dev) => dev?.port;
48
- /**
49
- * Load a `neon.ts` policy if one exists, returning the loaded config, the resolved path to
50
- * the config file (used by the dev server to watch it), and the directory it lives in (used
51
- * to resolve each function's relative `source`). Returns `null` when no config file is
52
- * found; surfaces real load errors (e.g. a syntax error).
53
- */
54
- const loadNeonConfig = async (cwd) => {
55
- try {
56
- const { config, resolvedPath } = await loadConfigFromFile({ cwd });
57
- return {
58
- config,
59
- configDir: dirname(resolvedPath),
60
- configPath: resolvedPath,
61
- };
62
- }
63
- catch (err) {
64
- const message = err instanceof Error ? err.message : String(err);
65
- if (/Could not find a Neon config file/i.test(message)) {
66
- return null;
67
- }
68
- throw err;
69
- }
70
- };
@@ -1,63 +0,0 @@
1
- import { resolve } from "node:path";
2
- const defaultDeps = {
3
- isPackaged: () => process.pkg !== undefined,
4
- loadEsbuild: (name) => import(name),
5
- };
6
- /**
7
- * Resolve the exact set of files esbuild reads to produce the bundle for
8
- * `source` — the entry plus every local module it imports (npm deps are left
9
- * external, so they never appear). These are the files the dev watcher should
10
- * watch, so a single edit triggers exactly one rebuild.
11
- *
12
- * Returns absolute paths, or `null` when the precise set cannot be computed —
13
- * either inside the packaged binary (which cannot import esbuild as a module;
14
- * it shells out to a binary that has no JSON-metafile equivalent here) or on a
15
- * platform where the esbuild module won't load. Callers fall back to a coarser
16
- * watch in that case.
17
- *
18
- * This performs a metafile-only pass (`write:false`, `metafile:true`) so it
19
- * never emits output; the actual bundle bytes still come from `bundleEntry`.
20
- */
21
- export const resolveWatchInputs = async (source, deps = defaultDeps) => {
22
- if (deps.isPackaged())
23
- return null;
24
- // esbuild is resolved by a COMPUTED specifier, never the literal string
25
- // 'esbuild', for the same reason as src/utils/esbuild.ts: rollup and
26
- // @yao-pkg/pkg statically scan for literal import()/require() and would pull
27
- // esbuild's native Go binary into the bundle/snapshot. Keep it invisible.
28
- const name = ["es", "build"].join("");
29
- let esbuild;
30
- try {
31
- esbuild = await deps.loadEsbuild(name);
32
- }
33
- catch {
34
- return null;
35
- }
36
- let metafile;
37
- try {
38
- // Mirrors bundleEntry's flags so the resolved input graph matches the real
39
- // bundle. metafile:true + write:false makes this a pure analysis pass.
40
- const result = await esbuild.build({
41
- entryPoints: [source],
42
- bundle: true,
43
- write: false,
44
- metafile: true,
45
- format: "esm",
46
- platform: "node",
47
- packages: "external",
48
- logLevel: "silent",
49
- });
50
- metafile = result.metafile;
51
- }
52
- catch {
53
- // A bundle error here is non-fatal for watching: bundleEntry surfaces the
54
- // real diagnostic. Fall back to the coarser watch so edits still rebuild.
55
- return null;
56
- }
57
- const inputs = metafile?.inputs;
58
- if (!inputs)
59
- return null;
60
- // metafile input keys are paths relative to esbuild's cwd; resolve to absolute
61
- // so they compare cleanly against chokidar's watched paths.
62
- return Object.keys(inputs).map((p) => resolve(process.cwd(), p));
63
- };
@@ -1,146 +0,0 @@
1
- import { createServer } from "node:http";
2
- import { resolve } from "node:path";
3
- import { pathToFileURL } from "node:url";
4
- import { getRequestListener } from "@hono/node-server";
5
- const isFunction = (value) => typeof value === "function";
6
- const hasFetchMethod = (value) => typeof value === "object" &&
7
- value !== null &&
8
- "fetch" in value &&
9
- typeof value.fetch === "function";
10
- /**
11
- * Resolve the user's exported handler to a single fetch callback.
12
- *
13
- * Resolution order (first match wins):
14
- * 1. `export default { fetch }` — Workers / Neon Functions style
15
- * 2. `export default function (req)` — bare (async) default function
16
- */
17
- export const resolveFetchHandler = (mod) => {
18
- const defaultExport = mod.default;
19
- if (hasFetchMethod(defaultExport)) {
20
- const target = defaultExport;
21
- return (req) => target.fetch(req);
22
- }
23
- if (isFunction(defaultExport)) {
24
- return defaultExport;
25
- }
26
- throw new Error("No request handler found in the source module. Export one of:\n" +
27
- " export default { fetch(req) { /* ... */ } }\n" +
28
- " export default function (req) { /* ... */ }");
29
- };
30
- /**
31
- * Wrap a fetch handler so user errors become a 500 response (with the message
32
- * in the body during dev) instead of crashing the child process.
33
- */
34
- export const withErrorBoundary = (handler) => {
35
- return async (req) => {
36
- try {
37
- return await handler(req);
38
- }
39
- catch (err) {
40
- const message = err instanceof Error ? (err.stack ?? err.message) : String(err);
41
- process.stderr.write(`Request handler threw an error:\n${message}\n`);
42
- return new Response(`Internal Server Error\n\n${message}`, {
43
- status: 500,
44
- headers: { "content-type": "text/plain; charset=utf-8" },
45
- });
46
- }
47
- };
48
- };
49
- const isAddressInUse = (err) => typeof err === "object" &&
50
- err !== null &&
51
- err.code === "EADDRINUSE";
52
- const DEFAULT_SEARCH_BASE = 8787;
53
- const MAX_SEARCH_STEPS = 100;
54
- const bindPort = async (server, selection, hostname) => {
55
- if (selection.mode === "explicit") {
56
- return listen(server, selection.port, hostname);
57
- }
58
- for (let step = 0; step < MAX_SEARCH_STEPS; step++) {
59
- try {
60
- return await listen(server, selection.from + step, hostname);
61
- }
62
- catch (err) {
63
- if (!isAddressInUse(err))
64
- throw err;
65
- }
66
- }
67
- throw new Error(`Could not find a free port in ${selection.from}-${selection.from + MAX_SEARCH_STEPS - 1}`);
68
- };
69
- const listen = (server, port, hostname) => new Promise((resolveListen, rejectListen) => {
70
- const onError = (err) => {
71
- server.off("listening", onListening);
72
- rejectListen(err);
73
- };
74
- const onListening = () => {
75
- server.off("error", onError);
76
- resolveListen(server.address().port);
77
- };
78
- server.once("error", onError);
79
- server.once("listening", onListening);
80
- server.listen(port, hostname);
81
- });
82
- /**
83
- * Load the (already-bundled) user module, build the listener, and start an HTTP
84
- * server. Announces the bound port on stdout as `neon-dev:ready <port>` so the
85
- * parent can render the URL. Resolves with the bound port.
86
- */
87
- export const startRuntime = async ({ source, port, hostname, }) => {
88
- const absoluteSource = resolve(process.cwd(), source);
89
- const mod = (await import(pathToFileURL(absoluteSource).href));
90
- const handler = withErrorBoundary(resolveFetchHandler(mod));
91
- const listener = getRequestListener(handler, { hostname });
92
- const server = createServer((incoming, outgoing) => {
93
- void listener(incoming, outgoing);
94
- });
95
- const boundPort = await bindPort(server, port, hostname);
96
- process.stdout.write(`neon-dev:ready ${boundPort}\n`);
97
- return boundPort;
98
- };
99
- /**
100
- * Build a {@link PortSelection} from the environment. Precedence:
101
- * 1. `NEON_DEV_PORT` -> explicit bind (crash if taken). Set by `neon dev` from an
102
- * explicit `--port` / `dev.port`.
103
- * 2. `PORT` -> explicit bind. A bare `PORT=3000 neon dev` sets this, so the
104
- * runtime binds the port chosen for it.
105
- * 3. otherwise -> search upward from `NEON_DEV_PORT_BASE` (or the default base).
106
- */
107
- export const portSelectionFromEnv = (env) => {
108
- const explicit = env.NEON_DEV_PORT;
109
- if (explicit !== undefined && explicit !== "") {
110
- return { mode: "explicit", port: parsePort(explicit, "NEON_DEV_PORT") };
111
- }
112
- const injected = env.PORT;
113
- if (injected !== undefined && injected !== "") {
114
- return { mode: "explicit", port: parsePort(injected, "PORT") };
115
- }
116
- const base = Number(env.NEON_DEV_PORT_BASE ?? DEFAULT_SEARCH_BASE);
117
- return {
118
- mode: "search",
119
- from: Number.isInteger(base) ? base : DEFAULT_SEARCH_BASE,
120
- };
121
- };
122
- const parsePort = (value, varName) => {
123
- const port = Number(value);
124
- if (!Number.isInteger(port) || port < 0 || port > 65535) {
125
- throw new Error(`Invalid ${varName}: "${value}"`);
126
- }
127
- return port;
128
- };
129
- const isDirectExecution = () => {
130
- const entry = process.argv[1];
131
- if (!entry)
132
- return false;
133
- return import.meta.url === pathToFileURL(entry).href;
134
- };
135
- if (isDirectExecution()) {
136
- const source = process.env.NEON_DEV_SOURCE ?? process.argv[2];
137
- if (!source) {
138
- process.stderr.write("neon-dev runtime: missing source path\n");
139
- process.exit(1);
140
- }
141
- startRuntime({ source, port: portSelectionFromEnv(process.env) }).catch((err) => {
142
- const msg = err instanceof Error ? err.message : String(err);
143
- process.stderr.write(`neon-dev runtime failed to start: ${msg}\n`);
144
- process.exit(1);
145
- });
146
- }
package/dist/env.js DELETED
@@ -1,36 +0,0 @@
1
- export const isCi = () => {
2
- return process.env.CI !== "false" && Boolean(process.env.CI);
3
- };
4
- export const isDebug = () => {
5
- return Boolean(process.env.DEBUG);
6
- };
7
- export const getGithubEnvVars = (env) => {
8
- const vars = [
9
- // github action info
10
- "GITHUB_ACTION_PATH",
11
- // source github repository
12
- "GITHUB_REPOSITORY",
13
- // environment info
14
- "GITHUB_RUN_ID",
15
- "GITHUB_RUN_NUMBER",
16
- "GITHUB_SERVER_URL",
17
- "GITHUB_WORKFLOW_REF",
18
- "RUNNER_ARCH",
19
- "RUNNER_ENVIRONMENT",
20
- "RUNNER_OS",
21
- ];
22
- const map = new Map();
23
- vars.forEach((v) => {
24
- let value = env[v];
25
- if (value === undefined || value === "") {
26
- return;
27
- }
28
- if (v === "GITHUB_ACTION_PATH") {
29
- value = value.includes("actions/")
30
- ? value.replace(/^.*actions\/(.+)$/, "$1")
31
- : value;
32
- }
33
- map.set(v, value);
34
- });
35
- return Object.fromEntries(map);
36
- };