neon 3.0.0 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (198) hide show
  1. package/README.md +54 -0
  2. package/dist/_shared/auth_selection.js +76 -79
  3. package/dist/_shared/credentials.js +166 -187
  4. package/dist/_shared/env-core/env.js +354 -517
  5. package/dist/_shared/env-core/reuse-secrets.js +159 -203
  6. package/dist/_shared/paths.js +129 -126
  7. package/dist/_shared/profiles.js +192 -242
  8. package/dist/_shared/secure_file.js +36 -38
  9. package/dist/_virtual/_rolldown/runtime.js +13 -0
  10. package/dist/analytics.js +163 -207
  11. package/dist/api.js +815 -758
  12. package/dist/auth.js +121 -141
  13. package/dist/auth_context.js +39 -53
  14. package/dist/cli.js +4 -7
  15. package/dist/commands/api.js +220 -250
  16. package/dist/commands/api_keys.js +251 -314
  17. package/dist/commands/auth.js +283 -328
  18. package/dist/commands/bootstrap.js +372 -437
  19. package/dist/commands/branches.js +304 -455
  20. package/dist/commands/bucket.js +374 -514
  21. package/dist/commands/checkout.js +213 -298
  22. package/dist/commands/config.js +573 -690
  23. package/dist/commands/connection_string.js +137 -165
  24. package/dist/commands/data_api.js +238 -260
  25. package/dist/commands/databases.js +67 -76
  26. package/dist/commands/deploy.js +31 -25
  27. package/dist/commands/dev.js +639 -719
  28. package/dist/commands/diff.js +156 -200
  29. package/dist/commands/env.js +243 -303
  30. package/dist/commands/functions.js +275 -355
  31. package/dist/commands/index.js +70 -65
  32. package/dist/commands/init.js +84 -119
  33. package/dist/commands/inspect.js +55 -55
  34. package/dist/commands/ip_allow.js +88 -120
  35. package/dist/commands/link.js +874 -1019
  36. package/dist/commands/logs.js +291 -0
  37. package/dist/commands/neon_auth.js +725 -933
  38. package/dist/commands/operations.js +34 -25
  39. package/dist/commands/orgs.js +28 -18
  40. package/dist/commands/profile.js +614 -845
  41. package/dist/commands/projects.js +313 -373
  42. package/dist/commands/psql.js +60 -58
  43. package/dist/commands/roles.js +55 -58
  44. package/dist/commands/schema_diff.js +87 -131
  45. package/dist/commands/set_context.js +34 -26
  46. package/dist/commands/snapshots.js +288 -413
  47. package/dist/commands/status.js +41 -37
  48. package/dist/commands/user.js +21 -10
  49. package/dist/commands/vpc_endpoints.js +85 -113
  50. package/dist/config.js +7 -6
  51. package/dist/config_format.js +50 -66
  52. package/dist/config_template.js +128 -157
  53. package/dist/context.js +183 -235
  54. package/dist/current_branch_fast_path.js +40 -49
  55. package/dist/dev/env.js +313 -394
  56. package/dist/dev/functions.js +54 -68
  57. package/dist/dev/inputs.js +46 -58
  58. package/dist/dev/runtime.js +135 -164
  59. package/dist/dev/websocket.js +766 -959
  60. package/dist/env.js +27 -33
  61. package/dist/env_file.js +118 -132
  62. package/dist/env_services.js +36 -38
  63. package/dist/errors.js +57 -68
  64. package/dist/functions_api.js +45 -43
  65. package/dist/help.js +189 -140
  66. package/dist/index.js +182 -257
  67. package/dist/init/agents.js +137 -118
  68. package/dist/init/auth.js +58 -68
  69. package/dist/init/bootstrap.js +325 -396
  70. package/dist/init/build_config.js +4 -2
  71. package/dist/init/detect_agent.js +56 -101
  72. package/dist/init/editors.js +35 -52
  73. package/dist/init/enrich_output.js +51 -66
  74. package/dist/init/extension.js +134 -171
  75. package/dist/init/inspect.js +179 -266
  76. package/dist/init/interactive.js +510 -622
  77. package/dist/init/neonctl.js +117 -168
  78. package/dist/init/orchestrate.js +157 -173
  79. package/dist/init/phases/auth.js +188 -202
  80. package/dist/init/phases/cleanup.js +23 -23
  81. package/dist/init/phases/db.js +251 -277
  82. package/dist/init/phases/getting_started.js +213 -223
  83. package/dist/init/phases/mcp.js +174 -224
  84. package/dist/init/phases/migrations.js +247 -248
  85. package/dist/init/phases/neon_auth.js +114 -133
  86. package/dist/init/phases/setup.js +546 -703
  87. package/dist/init/phases/skills.js +75 -86
  88. package/dist/init/phases/status.js +72 -67
  89. package/dist/init/resolve_context.js +102 -99
  90. package/dist/init/route_command.js +91 -98
  91. package/dist/init/skills.js +174 -218
  92. package/dist/init/vsix.js +77 -99
  93. package/dist/log.js +17 -16
  94. package/dist/neon_services.js +104 -129
  95. package/dist/parameters.gen.js +481 -471
  96. package/dist/pkg.js +17 -19
  97. package/dist/profile_keys.js +44 -47
  98. package/dist/psql/cli.js +44 -47
  99. package/dist/psql/command/cmd_cond.js +231 -406
  100. package/dist/psql/command/cmd_connect.js +557 -764
  101. package/dist/psql/command/cmd_copy.js +727 -983
  102. package/dist/psql/command/cmd_describe.js +1499 -1688
  103. package/dist/psql/command/cmd_format.js +733 -905
  104. package/dist/psql/command/cmd_io.js +1293 -2082
  105. package/dist/psql/command/cmd_lo.js +297 -359
  106. package/dist/psql/command/cmd_meta.js +727 -878
  107. package/dist/psql/command/cmd_misc.js +138 -172
  108. package/dist/psql/command/cmd_pipeline.js +547 -1099
  109. package/dist/psql/command/cmd_restrict.js +119 -155
  110. package/dist/psql/command/cmd_show.js +529 -688
  111. package/dist/psql/command/dispatch.js +261 -325
  112. package/dist/psql/command/inputQueue.js +35 -33
  113. package/dist/psql/command/shared.js +49 -63
  114. package/dist/psql/complete/filenames.js +90 -133
  115. package/dist/psql/complete/index.js +59 -97
  116. package/dist/psql/complete/matcher.js +236 -300
  117. package/dist/psql/complete/psqlVars.js +218 -223
  118. package/dist/psql/complete/queries.js +159 -177
  119. package/dist/psql/complete/rules.js +1493 -2299
  120. package/dist/psql/core/common.js +762 -1180
  121. package/dist/psql/core/help.js +456 -546
  122. package/dist/psql/core/mainloop.js +692 -1302
  123. package/dist/psql/core/prompt.js +391 -408
  124. package/dist/psql/core/settings.js +429 -644
  125. package/dist/psql/core/sqlHelp.js +480 -554
  126. package/dist/psql/core/startup.js +626 -815
  127. package/dist/psql/core/syncVars.js +67 -110
  128. package/dist/psql/core/variables.js +156 -278
  129. package/dist/psql/describe/formatters.js +884 -1285
  130. package/dist/psql/describe/processNamePattern.js +173 -260
  131. package/dist/psql/describe/queries.js +1368 -2403
  132. package/dist/psql/describe/versionGate.js +32 -41
  133. package/dist/psql/index.js +1414 -1927
  134. package/dist/psql/io/history.js +232 -271
  135. package/dist/psql/io/input.js +103 -108
  136. package/dist/psql/io/lineEditor/buffer.js +238 -319
  137. package/dist/psql/io/lineEditor/complete.js +135 -213
  138. package/dist/psql/io/lineEditor/filename.js +139 -148
  139. package/dist/psql/io/lineEditor/index.js +653 -870
  140. package/dist/psql/io/lineEditor/keymap.js +544 -702
  141. package/dist/psql/io/lineEditor/vt100.js +294 -341
  142. package/dist/psql/io/pgpass.js +158 -187
  143. package/dist/psql/io/pgservice.js +146 -183
  144. package/dist/psql/io/psqlrc.js +328 -403
  145. package/dist/psql/print/aligned.js +1020 -1683
  146. package/dist/psql/print/asciidoc.js +180 -214
  147. package/dist/psql/print/crosstab.js +281 -442
  148. package/dist/psql/print/csv.js +48 -70
  149. package/dist/psql/print/html.js +195 -226
  150. package/dist/psql/print/json.js +75 -88
  151. package/dist/psql/print/latex.js +291 -364
  152. package/dist/psql/print/pager.js +171 -242
  153. package/dist/psql/print/troff.js +194 -226
  154. package/dist/psql/print/unaligned.js +69 -95
  155. package/dist/psql/print/units.js +167 -169
  156. package/dist/psql/scanner/slash.js +428 -483
  157. package/dist/psql/scanner/sql.js +445 -889
  158. package/dist/psql/scanner/stringutils.js +309 -379
  159. package/dist/psql/types/index.js +2 -7
  160. package/dist/psql/types/scanner.js +25 -22
  161. package/dist/psql/wire/connection.js +2042 -2803
  162. package/dist/psql/wire/copy.js +84 -100
  163. package/dist/psql/wire/notify.js +39 -59
  164. package/dist/psql/wire/pipeline.js +305 -518
  165. package/dist/psql/wire/protocol.js +349 -417
  166. package/dist/psql/wire/sasl.js +180 -265
  167. package/dist/psql/wire/tls.js +400 -561
  168. package/dist/storage_api.js +115 -129
  169. package/dist/test_utils/fixtures.js +94 -113
  170. package/dist/test_utils/oauth_server.js +10 -7
  171. package/dist/test_utils/project_dir.js +33 -0
  172. package/dist/utils/ai_gateway_notice.js +131 -162
  173. package/dist/utils/api_enums.js +21 -28
  174. package/dist/utils/auth.js +10 -4
  175. package/dist/utils/branch_notice.js +20 -19
  176. package/dist/utils/branch_picker.js +83 -89
  177. package/dist/utils/cli_name.js +15 -12
  178. package/dist/utils/compute_units.js +20 -27
  179. package/dist/utils/config_diff.js +127 -158
  180. package/dist/utils/enrichers.js +95 -148
  181. package/dist/utils/esbuild.js +130 -189
  182. package/dist/utils/flags.js +35 -47
  183. package/dist/utils/formats.js +8 -15
  184. package/dist/utils/git_diff.js +69 -80
  185. package/dist/utils/inspect_db.js +101 -143
  186. package/dist/utils/inspect_queries.js +179 -142
  187. package/dist/utils/middlewares.js +37 -44
  188. package/dist/utils/openapi.js +87 -99
  189. package/dist/utils/package_manager.js +312 -110
  190. package/dist/utils/point_in_time.js +49 -53
  191. package/dist/utils/psql.js +89 -106
  192. package/dist/utils/service_picker.js +55 -58
  193. package/dist/utils/string.js +5 -5
  194. package/dist/utils/ui.js +38 -55
  195. package/dist/utils/write_sync.js +26 -35
  196. package/dist/utils/zip.js +4 -3
  197. package/dist/writer.js +67 -87
  198. package/package.json +7 -5
@@ -1,223 +1,179 @@
1
- import { credentialScopesSatisfied, } from "@neon/config/v1";
2
- import { createApiFromOptions, credentialEnvKeys, credentialName, fetchEnvKeys, NEON_ENV_VAR_KEYS, policyEnvKeys, previewCredentialScopes, resolveBranchPolicy, toEntries, } from "./env.js";
1
+ import { NEON_ENV_VAR_KEYS, createApiFromOptions, credentialEnvKeys, credentialName, fetchEnvKeys, policyEnvKeys, previewCredentialScopes, resolveBranchPolicy, toEntries } from "./env.js";
2
+ import { credentialScopesSatisfied } from "@neon/config/v1";
3
+ //#region src/_shared/env-core/reuse-secrets.ts
3
4
  /**
4
- * Resolve a branch's env while keeping one-time secrets the caller already holds.
5
- *
6
- * {@link fetchEnvKeys} — and the public `fetchEnv` — only ever *fetch*. The Neon API returns a
7
- * credential's `api_token` / `s3_secret_access_key` exactly once, at mint time, so "fetching"
8
- * them means minting a new credential; a plain `fetchEnv` on every `neon dev` start or `env
9
- * pull` would leave a live credential behind each time. This is the wrapper that avoids that:
10
- * it looks at what the caller already has, decides what is still usable, and asks `fetchEnv`
11
- * for only the rest.
12
- *
13
- * The check is a real verification, not a presence test. A persisted secret is kept only when
14
- * it names a credential that still exists on this branch, is not revoked or expired, and
15
- * carries every scope the policy needs. A `.env.example` placeholder, a credential revoked in
16
- * the console, one copied in from another branch, or one predating a newly-enabled feature all
17
- * fail that check and get replaced.
18
- *
19
- * None of this needs local bookkeeping, because the secrets carry their own credential id:
20
- * `AWS_ACCESS_KEY_ID` **is** the credential's `tokenId` (the storage gateway authenticates
21
- * against the full id), and the AI Gateway token is minted as `nt_live_<tokenIdShort>_<secret>`,
22
- * where `tokenIdShort` is what the credentials list reports. The env source being replaced is
23
- * the record of what the last call issued.
24
- *
25
- * ```ts
26
- * import { fetchEnvReusingSecrets } from "../_shared/env-core/reuse-secrets.js";
27
- *
28
- * const { vars, credential } = await fetchEnvReusingSecrets(config, {
29
- * projectId,
30
- * branch: "main",
31
- * env: { ...process.env, ...readEnvFile(".env") },
32
- * });
33
- * if (credential.issued) console.log(`new values for ${credential.keys.join(", ")}`);
34
- * ```
35
- */
36
- export async function fetchEnvReusingSecrets(config, options) {
37
- const { env: source = process.env, revokeSuperseded = true, ...fetchOptions } = options;
38
- const api = options.api ?? createApiFromOptions(options);
39
- const { branch, desired } = await resolveBranchPolicy(config, options, api);
40
- const storageEnabled = (desired.preview?.buckets.length ?? 0) > 0;
41
- const gatewayEnabled = desired.preview?.aiGatewayEnabled ?? false;
42
- const secretKeys = credentialEnvKeys({
43
- storage: storageEnabled,
44
- aiGateway: gatewayEnabled,
45
- });
46
- // Nothing credential-backed on this branch, so there is nothing to preserve and no
47
- // credential to spend: fetch everything and skip the credentials endpoint entirely.
48
- if (secretKeys.length === 0) {
49
- const fetched = await fetchEnvKeys(config, fetchOptions, null);
50
- return {
51
- vars: preferPersisted(toEntries(fetched), source),
52
- credential: {
53
- issued: false,
54
- keys: [],
55
- revoked: [],
56
- superseded: [],
57
- },
58
- };
59
- }
60
- const persisted = readPersistedSecrets(source);
61
- const complete = (!storageEnabled ||
62
- Boolean(persisted.accessKeyId && persisted.secretAccessKey)) &&
63
- (!gatewayEnabled || Boolean(persisted.apiToken));
64
- // Look the persisted secrets up whenever there are any — not only when they're complete.
65
- // An incomplete set still names the credential a newly-enabled feature is about to
66
- // supersede (a storage-only credential on a branch that just gained the AI Gateway), and
67
- // that one should be revoked rather than left live.
68
- const named = persisted.accessKeyId !== "" || persisted.apiToken !== ""
69
- ? namedCredentials(await api.listCredentials(options.projectId, branch.id), persisted)
70
- : { storage: null, gateway: null };
71
- const reusable = complete
72
- ? reusableCredential(named, { storageEnabled, gatewayEnabled })
73
- : null;
74
- const scopes = previewCredentialScopes(desired.preview);
75
- const keep = reusable !== null && credentialScopesSatisfied(reusable.scopes, scopes);
76
- // Ask for everything the policy produces, minus the secrets we're keeping — which is what
77
- // stops `fetchEnv` from minting a credential it doesn't need.
78
- const allKeys = policyEnvKeys(desired);
79
- const fetchKeys = keep
80
- ? allKeys.filter((key) => !secretKeys.includes(key))
81
- : allKeys;
82
- const fetched = await fetchEnvKeys(config,
83
- // Pass the resolved id so `fetchEnv` targets the same branch this call verified against,
84
- // even if `options.branch` was a name that has since been reused.
85
- { ...fetchOptions, branchId: branch.id, api }, fetchKeys);
86
- const vars = preferPersisted(toEntries(fetched), source);
87
- if (keep) {
88
- for (const key of secretKeys) {
89
- const value = source[key];
90
- if (value !== undefined)
91
- vars[key] = value;
92
- }
93
- return {
94
- vars,
95
- credential: {
96
- issued: false,
97
- keys: secretKeys,
98
- revoked: [],
99
- superseded: [],
100
- },
101
- };
102
- }
103
- // A replacement was minted, so revoke what it supersedes: the credentials the old secrets
104
- // named, minus any this tool did not issue. Their secrets lived nowhere but the env source
105
- // this call replaces, so revoking them strands nothing — and it keeps a branch from
106
- // accumulating a live credential per call. Everything else on the branch is left alone: it
107
- // may belong to a teammate, another checkout, or a deployed function, and nothing
108
- // observable distinguishes those from an orphan of our own.
109
- //
110
- // Revoked *after* the fetch, so a failed fetch leaves the caller's existing secrets working.
111
- const ours = new Set();
112
- for (const meta of [named.storage, named.gateway]) {
113
- if (meta !== null &&
114
- meta.principalType === "user" &&
115
- meta.name === credentialName(branch.name)) {
116
- ours.add(meta.tokenId);
117
- }
118
- }
119
- if (revokeSuperseded) {
120
- for (const tokenId of ours) {
121
- await api.revokeCredential(options.projectId, branch.id, tokenId);
122
- }
123
- }
124
- return {
125
- vars,
126
- credential: {
127
- issued: true,
128
- keys: secretKeys,
129
- revoked: revokeSuperseded ? [...ours] : [],
130
- superseded: revokeSuperseded ? [] : [...ours],
131
- },
132
- };
5
+ * Resolve a branch's env while keeping one-time secrets the caller already holds.
6
+ *
7
+ * {@link fetchEnvKeys} — and the public `fetchEnv` — only ever *fetch*. The Neon API returns a
8
+ * credential's `api_token` / `s3_secret_access_key` exactly once, at mint time, so "fetching"
9
+ * them means minting a new credential; a plain `fetchEnv` on every `neon dev` start or `env
10
+ * pull` would leave a live credential behind each time. This is the wrapper that avoids that:
11
+ * it looks at what the caller already has, decides what is still usable, and asks `fetchEnv`
12
+ * for only the rest.
13
+ *
14
+ * The check is a real verification, not a presence test. A persisted secret is kept only when
15
+ * it names a credential that still exists on this branch, is not revoked or expired, and
16
+ * carries every scope the policy needs. A `.env.example` placeholder, a credential revoked in
17
+ * the console, one copied in from another branch, or one predating a newly-enabled feature all
18
+ * fail that check and get replaced.
19
+ *
20
+ * None of this needs local bookkeeping, because the secrets carry their own credential id:
21
+ * `AWS_ACCESS_KEY_ID` **is** the credential's `tokenId` (the storage gateway authenticates
22
+ * against the full id), and the AI Gateway token is minted as `nt_live_<tokenIdShort>_<secret>`,
23
+ * where `tokenIdShort` is what the credentials list reports. The env source being replaced is
24
+ * the record of what the last call issued.
25
+ *
26
+ * ```ts
27
+ * import { fetchEnvReusingSecrets } from "../_shared/env-core/reuse-secrets.js";
28
+ *
29
+ * const { vars, credential } = await fetchEnvReusingSecrets(config, {
30
+ * projectId,
31
+ * branch: "main",
32
+ * env: { ...process.env, ...readEnvFile(".env") },
33
+ * });
34
+ * if (credential.issued) console.log(`new values for ${credential.keys.join(", ")}`);
35
+ * ```
36
+ */
37
+ async function fetchEnvReusingSecrets(config, options) {
38
+ const { env: source = process.env, revokeSuperseded = true, ...fetchOptions } = options;
39
+ const api = options.api ?? createApiFromOptions(options);
40
+ const { branch, desired } = await resolveBranchPolicy(config, options, api);
41
+ const storageEnabled = (desired.preview?.buckets.length ?? 0) > 0;
42
+ const gatewayEnabled = desired.preview?.aiGatewayEnabled ?? false;
43
+ const secretKeys = credentialEnvKeys({
44
+ storage: storageEnabled,
45
+ aiGateway: gatewayEnabled
46
+ });
47
+ if (secretKeys.length === 0) {
48
+ const fetched = await fetchEnvKeys(config, fetchOptions, null);
49
+ return {
50
+ vars: preferPersisted(toEntries(fetched), source),
51
+ credential: {
52
+ issued: false,
53
+ keys: [],
54
+ revoked: [],
55
+ superseded: []
56
+ }
57
+ };
58
+ }
59
+ const persisted = readPersistedSecrets(source);
60
+ const complete = (!storageEnabled || Boolean(persisted.accessKeyId && persisted.secretAccessKey)) && (!gatewayEnabled || Boolean(persisted.apiToken));
61
+ const named = persisted.accessKeyId !== "" || persisted.apiToken !== "" ? namedCredentials(await api.listCredentials(options.projectId, branch.id), persisted) : {
62
+ storage: null,
63
+ gateway: null
64
+ };
65
+ const reusable = complete ? reusableCredential(named, {
66
+ storageEnabled,
67
+ gatewayEnabled
68
+ }) : null;
69
+ const scopes = previewCredentialScopes(desired.preview);
70
+ const keep = reusable !== null && credentialScopesSatisfied(reusable.scopes, scopes);
71
+ const allKeys = policyEnvKeys(desired);
72
+ const fetchKeys = keep ? allKeys.filter((key) => !secretKeys.includes(key)) : allKeys;
73
+ const fetched = await fetchEnvKeys(config, {
74
+ ...fetchOptions,
75
+ branchId: branch.id,
76
+ api
77
+ }, fetchKeys);
78
+ const vars = preferPersisted(toEntries(fetched), source);
79
+ if (keep) {
80
+ for (const key of secretKeys) {
81
+ const value = source[key];
82
+ if (value !== void 0) vars[key] = value;
83
+ }
84
+ return {
85
+ vars,
86
+ credential: {
87
+ issued: false,
88
+ keys: secretKeys,
89
+ revoked: [],
90
+ superseded: []
91
+ }
92
+ };
93
+ }
94
+ const ours = /* @__PURE__ */ new Set();
95
+ for (const meta of [named.storage, named.gateway]) if (meta !== null && meta.principalType === "user" && meta.name === credentialName(branch.name)) ours.add(meta.tokenId);
96
+ if (revokeSuperseded) for (const tokenId of ours) await api.revokeCredential(options.projectId, branch.id, tokenId);
97
+ return {
98
+ vars,
99
+ credential: {
100
+ issued: true,
101
+ keys: secretKeys,
102
+ revoked: revokeSuperseded ? [...ours] : [],
103
+ superseded: revokeSuperseded ? [] : [...ours]
104
+ }
105
+ };
133
106
  }
134
107
  /** Read the branch credential's secrets out of an env source. */
135
108
  function readPersistedSecrets(source) {
136
- const storage = NEON_ENV_VAR_KEYS.storage;
137
- const gateway = NEON_ENV_VAR_KEYS.aiGateway;
138
- return {
139
- accessKeyId: source[storage.accessKeyId] ?? "",
140
- secretAccessKey: source[storage.secretAccessKey] ?? "",
141
- apiToken: source[gateway.apiKey] ?? "",
142
- };
109
+ const storage = NEON_ENV_VAR_KEYS.storage;
110
+ const gateway = NEON_ENV_VAR_KEYS.aiGateway;
111
+ return {
112
+ accessKeyId: source[storage.accessKeyId] ?? "",
113
+ secretAccessKey: source[storage.secretAccessKey] ?? "",
114
+ apiToken: source[gateway.apiKey] ?? ""
115
+ };
143
116
  }
144
117
  /**
145
- * Keep a persisted value rather than overwriting it with an empty fetched one.
146
- *
147
- * Neon Auth's `base_url` is the case that needs this: integrations created before the API
148
- * returned it answer with an empty string, and the persisted copy is the only one left. An
149
- * empty fetched value never carries more information than a non-empty persisted one, so
150
- * preferring the latter is safe for every var — and it keeps a pull from blanking a working
151
- * line in someone's `.env`.
152
- */
118
+ * Keep a persisted value rather than overwriting it with an empty fetched one.
119
+ *
120
+ * Neon Auth's `base_url` is the case that needs this: integrations created before the API
121
+ * returned it answer with an empty string, and the persisted copy is the only one left. An
122
+ * empty fetched value never carries more information than a non-empty persisted one, so
123
+ * preferring the latter is safe for every var — and it keeps a pull from blanking a working
124
+ * line in someone's `.env`.
125
+ */
153
126
  function preferPersisted(vars, source) {
154
- const out = { ...vars };
155
- for (const [key, value] of Object.entries(out)) {
156
- if (value !== "")
157
- continue;
158
- const persisted = source[key];
159
- if (persisted !== undefined && persisted !== "")
160
- out[key] = persisted;
161
- }
162
- return out;
127
+ const out = { ...vars };
128
+ for (const [key, value] of Object.entries(out)) {
129
+ if (value !== "") continue;
130
+ const persisted = source[key];
131
+ if (persisted !== void 0 && persisted !== "") out[key] = persisted;
132
+ }
133
+ return out;
163
134
  }
164
135
  /**
165
- * The credential id embedded in an AI Gateway token. The API mints them as
166
- * `nt_live_<tokenIdShort>_<secret>`, and `tokenIdShort` is the public identifier the credentials
167
- * list reports — so a persisted token names the credential that issued it. Returns `null` for
168
- * anything not in that shape (a `.env.example` placeholder, a hand-typed value), which callers
169
- * treat as unverifiable.
170
- */
136
+ * The credential id embedded in an AI Gateway token. The API mints them as
137
+ * `nt_live_<tokenIdShort>_<secret>`, and `tokenIdShort` is the public identifier the credentials
138
+ * list reports — so a persisted token names the credential that issued it. Returns `null` for
139
+ * anything not in that shape (a `.env.example` placeholder, a hand-typed value), which callers
140
+ * treat as unverifiable.
141
+ */
171
142
  function gatewayTokenIdShort(apiToken) {
172
- return /^nt_live_([^_]+)_.+$/.exec(apiToken)?.[1] ?? null;
143
+ return /^nt_live_([^_]+)_.+$/.exec(apiToken)?.[1] ?? null;
173
144
  }
174
145
  /** Whether an issued credential can still be used: not revoked, not past its expiry. */
175
146
  function isLiveCredential(meta, now) {
176
- if (meta.revokedAt !== undefined)
177
- return false;
178
- if (meta.expiresAt === undefined)
179
- return true;
180
- const expiresAt = Date.parse(meta.expiresAt);
181
- return Number.isNaN(expiresAt) || expiresAt > now;
147
+ if (meta.revokedAt !== void 0) return false;
148
+ if (meta.expiresAt === void 0) return true;
149
+ const expiresAt = Date.parse(meta.expiresAt);
150
+ return Number.isNaN(expiresAt) || expiresAt > now;
182
151
  }
183
152
  /**
184
- * The live credentials the persisted secrets name — at most one per half. A half that names
185
- * nothing contributes nothing, which is what a placeholder, a credential revoked in the
186
- * console, and one copied in from another branch all look like from here.
187
- */
153
+ * The live credentials the persisted secrets name — at most one per half. A half that names
154
+ * nothing contributes nothing, which is what a placeholder, a credential revoked in the
155
+ * console, and one copied in from another branch all look like from here.
156
+ */
188
157
  function namedCredentials(live, persisted) {
189
- const usable = live.filter((meta) => isLiveCredential(meta, Date.now()));
190
- const shortId = persisted.apiToken
191
- ? gatewayTokenIdShort(persisted.apiToken)
192
- : null;
193
- return {
194
- storage: persisted.accessKeyId
195
- ? (usable.find((meta) => meta.tokenId === persisted.accessKeyId) ??
196
- null)
197
- : null,
198
- gateway: shortId
199
- ? (usable.find((meta) => meta.tokenIdShort === shortId) ?? null)
200
- : null,
201
- };
158
+ const usable = live.filter((meta) => isLiveCredential(meta, Date.now()));
159
+ const shortId = persisted.apiToken ? gatewayTokenIdShort(persisted.apiToken) : null;
160
+ return {
161
+ storage: persisted.accessKeyId ? usable.find((meta) => meta.tokenId === persisted.accessKeyId) ?? null : null,
162
+ gateway: shortId ? usable.find((meta) => meta.tokenIdShort === shortId) ?? null : null
163
+ };
202
164
  }
203
165
  /**
204
- * The credential the persisted secrets can be *reused* as, or `null`.
205
- *
206
- * Strict on purpose: every half the policy enables has to name a live credential, and when both
207
- * features are enabled they must name the *same* one — they share a single credential, so
208
- * halves that disagree came from two different calls and neither can be trusted.
209
- */
166
+ * The credential the persisted secrets can be *reused* as, or `null`.
167
+ *
168
+ * Strict on purpose: every half the policy enables has to name a live credential, and when both
169
+ * features are enabled they must name the *same* one — they share a single credential, so
170
+ * halves that disagree came from two different calls and neither can be trusted.
171
+ */
210
172
  function reusableCredential(named, enabled) {
211
- if (enabled.storageEnabled && enabled.gatewayEnabled) {
212
- return named.storage &&
213
- named.gateway &&
214
- named.storage.tokenId === named.gateway.tokenId
215
- ? named.storage
216
- : null;
217
- }
218
- if (enabled.storageEnabled)
219
- return named.storage;
220
- if (enabled.gatewayEnabled)
221
- return named.gateway;
222
- return null;
173
+ if (enabled.storageEnabled && enabled.gatewayEnabled) return named.storage && named.gateway && named.storage.tokenId === named.gateway.tokenId ? named.storage : null;
174
+ if (enabled.storageEnabled) return named.storage;
175
+ if (enabled.gatewayEnabled) return named.gateway;
176
+ return null;
223
177
  }
178
+ //#endregion
179
+ export { fetchEnvReusingSecrets };