neon 2.47.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 +395 -0
  5. package/dist/_shared/env-core/reuse-secrets.js +179 -0
  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 +575 -658
  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 +642 -681
  28. package/dist/commands/diff.js +156 -200
  29. package/dist/commands/env.js +243 -303
  30. package/dist/commands/functions.js +275 -341
  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 -64
  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 +133 -147
  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 +9 -7
@@ -1,14 +1,17 @@
1
1
  import { basename } from "node:path";
2
+ //#region src/utils/cli_name.ts
2
3
  /**
3
- * The name this CLI was invoked as: `neon` (current) or `neonctl` (legacy alias).
4
- *
5
- * Use this for any user-facing string that suggests a command to run — help text,
6
- * error hints, and especially `--agent` `next_command_template` values, which an agent
7
- * executes verbatim. Hardcoding `neonctl` breaks those on installs of the `neon` package,
8
- * which no longer ships a `neonctl` binary (removed in `neon@2.38.0`).
9
- *
10
- * Derived from `process.argv[1]`, which is fixed for the process lifetime, so the result
11
- * is stable whether this is called at module load or per invocation. Mirrors the name used
12
- * for yargs `.scriptName()` in `index.ts`.
13
- */
14
- export const getCliName = () => basename(process.argv[1] ?? "") === "neonctl" ? "neonctl" : "neon";
4
+ * The name this CLI was invoked as: `neon` (current) or `neonctl` (legacy alias).
5
+ *
6
+ * Use this for any user-facing string that suggests a command to run — help text,
7
+ * error hints, and especially `--agent` `next_command_template` values, which an agent
8
+ * executes verbatim. Hardcoding `neonctl` breaks those on installs of the `neon` package,
9
+ * which no longer ships a `neonctl` binary (removed in `neon@2.38.0`).
10
+ *
11
+ * Derived from `process.argv[1]`, which is fixed for the process lifetime, so the result
12
+ * is stable whether this is called at module load or per invocation. Mirrors the name used
13
+ * for yargs `.scriptName()` in `index.ts`.
14
+ */
15
+ const getCliName = () => basename(process.argv[1] ?? "") === "neonctl" ? "neonctl" : "neon";
16
+ //#endregion
17
+ export { getCliName };
@@ -1,28 +1,21 @@
1
- export const getComputeUnits = (autoscaling) => {
2
- const fixedSizeAutoscaling = Number(autoscaling);
3
- if (!isNaN(fixedSizeAutoscaling)) {
4
- return {
5
- autoscaling_limit_min_cu: fixedSizeAutoscaling,
6
- autoscaling_limit_max_cu: fixedSizeAutoscaling,
7
- };
8
- }
9
- if (!autoscaling.includes("-")) {
10
- throw new Error('Autoscaling should be either fixed size (e.g. 2) or min and max sizes delimited with a dash (e.g. "0.5-1")');
11
- }
12
- const [min, max] = autoscaling.split("-");
13
- if (!min || !max) {
14
- throw new Error('Autoscaling should be either fixed size (e.g. 2) or min and max sizes delimited with a dash (e.g. "0.5-1")');
15
- }
16
- const minAutoscaling = Number(min);
17
- const maxAutoscaling = Number(max);
18
- if (isNaN(minAutoscaling)) {
19
- throw new Error("Autoscaling min should be a number");
20
- }
21
- if (isNaN(maxAutoscaling)) {
22
- throw new Error("Autoscaling max should be a number");
23
- }
24
- return {
25
- autoscaling_limit_min_cu: minAutoscaling,
26
- autoscaling_limit_max_cu: maxAutoscaling,
27
- };
1
+ //#region src/utils/compute_units.ts
2
+ const getComputeUnits = (autoscaling) => {
3
+ const fixedSizeAutoscaling = Number(autoscaling);
4
+ if (!isNaN(fixedSizeAutoscaling)) return {
5
+ autoscaling_limit_min_cu: fixedSizeAutoscaling,
6
+ autoscaling_limit_max_cu: fixedSizeAutoscaling
7
+ };
8
+ if (!autoscaling.includes("-")) throw new Error("Autoscaling should be either fixed size (e.g. 2) or min and max sizes delimited with a dash (e.g. \"0.5-1\")");
9
+ const [min, max] = autoscaling.split("-");
10
+ if (!min || !max) throw new Error("Autoscaling should be either fixed size (e.g. 2) or min and max sizes delimited with a dash (e.g. \"0.5-1\")");
11
+ const minAutoscaling = Number(min);
12
+ const maxAutoscaling = Number(max);
13
+ if (isNaN(minAutoscaling)) throw new Error("Autoscaling min should be a number");
14
+ if (isNaN(maxAutoscaling)) throw new Error("Autoscaling max should be a number");
15
+ return {
16
+ autoscaling_limit_min_cu: minAutoscaling,
17
+ autoscaling_limit_max_cu: maxAutoscaling
18
+ };
28
19
  };
20
+ //#endregion
21
+ export { getComputeUnits };
@@ -1,185 +1,154 @@
1
1
  import chalk from "chalk";
2
+ //#region src/utils/config_diff.ts
2
3
  const UNSET = "(unset)";
3
4
  /**
4
- * Shared palette for the config diff, matching `neon diff`'s `git diff` styling
5
- * (bold headers, red "before", green "after", dim connective glyphs). When
6
- * `color` is false every entry is the identity function, so the identical layout
7
- * renders without ANSI codes for `--no-color`, non-TTY pipes, and test snapshots.
8
- */
5
+ * Shared palette for the config diff, matching `neon diff`'s `git diff` styling
6
+ * (bold headers, red "before", green "after", dim connective glyphs). When
7
+ * `color` is false every entry is the identity function, so the identical layout
8
+ * renders without ANSI codes for `--no-color`, non-TTY pipes, and test snapshots.
9
+ */
9
10
  const palette = (color) => {
10
- const id = (s) => s;
11
- if (!color) {
12
- return { title: id, group: id, added: id, removed: id, arrow: id };
13
- }
14
- return {
15
- title: (s) => chalk.bold(s),
16
- group: (s) => chalk.bold(s),
17
- added: (s) => chalk.green(s),
18
- removed: (s) => chalk.red(s),
19
- arrow: (s) => chalk.dim(s),
20
- };
11
+ const id = (s) => s;
12
+ if (!color) return {
13
+ title: id,
14
+ group: id,
15
+ added: id,
16
+ removed: id,
17
+ arrow: id
18
+ };
19
+ return {
20
+ title: (s) => chalk.bold(s),
21
+ group: (s) => chalk.bold(s),
22
+ added: (s) => chalk.green(s),
23
+ removed: (s) => chalk.red(s),
24
+ arrow: (s) => chalk.dim(s)
25
+ };
21
26
  };
22
27
  const isPlainObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
23
28
  const stringifyValue = (value) => {
24
- if (value === undefined || value === null || value === "")
25
- return UNSET;
26
- if (typeof value === "string")
27
- return value;
28
- if (typeof value === "boolean" || typeof value === "number") {
29
- return String(value);
30
- }
31
- return JSON.stringify(value);
29
+ if (value === void 0 || value === null || value === "") return UNSET;
30
+ if (typeof value === "string") return value;
31
+ if (typeof value === "boolean" || typeof value === "number") return String(value);
32
+ return JSON.stringify(value);
32
33
  };
33
34
  /**
34
- * Expand one policy field into flat {@link FieldChange}s. When either side is a
35
- * plain object (compute settings, Data API settings) it splits into one
36
- * `field.key` entry per key across both sides, so the diff reads as sorted
37
- * scalar lines (`computeSettings.autoscalingLimitMaxCu 2 → 4`) rather than an
38
- * opaque JSON blob. Scalars produce a single entry.
39
- */
35
+ * Expand one policy field into flat {@link FieldChange}s. When either side is a
36
+ * plain object (compute settings, Data API settings) it splits into one
37
+ * `field.key` entry per key across both sides, so the diff reads as sorted
38
+ * scalar lines (`computeSettings.autoscalingLimitMaxCu 2 → 4`) rather than an
39
+ * opaque JSON blob. Scalars produce a single entry.
40
+ */
40
41
  const expandField = (field, current, desired) => {
41
- if (isPlainObject(current) || isPlainObject(desired)) {
42
- const cur = isPlainObject(current) ? current : {};
43
- const des = isPlainObject(desired) ? desired : {};
44
- const keys = Array.from(new Set([...Object.keys(cur), ...Object.keys(des)])).sort();
45
- return keys.map((key) => ({
46
- field: `${field}.${key}`,
47
- ...(key in cur ? { current: stringifyValue(cur[key]) } : {}),
48
- desired: stringifyValue(des[key]),
49
- }));
50
- }
51
- return [
52
- {
53
- field,
54
- ...(current !== undefined
55
- ? { current: stringifyValue(current) }
56
- : {}),
57
- desired: stringifyValue(desired),
58
- },
59
- ];
42
+ if (isPlainObject(current) || isPlainObject(desired)) {
43
+ const cur = isPlainObject(current) ? current : {};
44
+ const des = isPlainObject(desired) ? desired : {};
45
+ return Array.from(/* @__PURE__ */ new Set([...Object.keys(cur), ...Object.keys(des)])).sort().map((key) => ({
46
+ field: `${field}.${key}`,
47
+ ...key in cur ? { current: stringifyValue(cur[key]) } : {},
48
+ desired: stringifyValue(des[key])
49
+ }));
50
+ }
51
+ return [{
52
+ field,
53
+ ...current !== void 0 ? { current: stringifyValue(current) } : {},
54
+ desired: stringifyValue(desired)
55
+ }];
60
56
  };
61
57
  /** Render the indented `field current → desired` lines for one branch group. */
62
58
  const renderFieldLines = (fields, paint) => {
63
- const width = fields.reduce((max, f) => Math.max(max, f.field.length), 0);
64
- return fields.map((f) => {
65
- const name = f.field.padEnd(width);
66
- const arrow = paint.arrow("→");
67
- return f.current !== undefined
68
- ? ` ${name} ${paint.removed(f.current)} ${arrow} ${paint.added(f.desired)}`
69
- : ` ${name} ${arrow} ${paint.added(f.desired)}`;
70
- });
59
+ const width = fields.reduce((max, f) => Math.max(max, f.field.length), 0);
60
+ return fields.map((f) => {
61
+ const name = f.field.padEnd(width);
62
+ const arrow = paint.arrow("→");
63
+ return f.current !== void 0 ? ` ${name} ${paint.removed(f.current)} ${arrow} ${paint.added(f.desired)}` : ` ${name} ${arrow} ${paint.added(f.desired)}`;
64
+ });
71
65
  };
72
66
  /** Group field changes by branch, each block sorted alphabetically by field. */
73
67
  const renderBranchGroups = (byBranch, paint) => {
74
- const lines = [];
75
- const groups = [...byBranch.entries()].sort((a, b) => a[0].localeCompare(b[0]));
76
- for (const [branch, fields] of groups) {
77
- const sorted = [...fields].sort((a, b) => a.field.localeCompare(b.field));
78
- lines.push(` ${paint.group(`~ ${branch}`)}`);
79
- lines.push(...renderFieldLines(sorted, paint));
80
- }
81
- return lines;
68
+ const lines = [];
69
+ const groups = [...byBranch.entries()].sort((a, b) => a[0].localeCompare(b[0]));
70
+ for (const [branch, fields] of groups) {
71
+ const sorted = [...fields].sort((a, b) => a.field.localeCompare(b.field));
72
+ lines.push(` ${paint.group(`~ ${branch}`)}`);
73
+ lines.push(...renderFieldLines(sorted, paint));
74
+ }
75
+ return lines;
82
76
  };
83
77
  /** Friendly label for a service change identifier (`bucket:x` → `bucket x`). */
84
78
  const serviceLabel = (identifier) => {
85
- if (identifier === "auth")
86
- return "Neon Auth";
87
- if (identifier === "dataApi")
88
- return "Data API";
89
- if (identifier.startsWith("bucket:")) {
90
- return `bucket ${identifier.slice("bucket:".length)}`;
91
- }
92
- if (identifier.startsWith("function:")) {
93
- return `function ${identifier.slice("function:".length)}`;
94
- }
95
- return identifier;
79
+ if (identifier === "auth") return "Neon Auth";
80
+ if (identifier === "dataApi") return "Data API";
81
+ if (identifier.startsWith("bucket:")) return `bucket ${identifier.slice(7)}`;
82
+ if (identifier.startsWith("function:")) return `function ${identifier.slice(9)}`;
83
+ return identifier;
96
84
  };
97
85
  /**
98
- * The desired-only field changes for an applied/planned **branch** update. The
99
- * synthesized `AppliedChange.details` carry the new value keyed by `field`
100
- * (`parent`→`parent`, `ttl`→`expiresAt`, `protected`→`protected`,
101
- * `computeSettings`→`settings`); the previous value isn't threaded through in
102
- * Phase 1, so these render as `field → desired` (no red "before"). Object
103
- * settings expand into sub-fields.
104
- *
105
- * `parent` only ever arrives from a branch creation (it cannot be changed
106
- * afterwards), where it reports the parent the policy named.
107
- */
86
+ * The desired-only field changes for an applied/planned **branch** update. The
87
+ * synthesized `AppliedChange.details` carry the new value keyed by `field`
88
+ * (`parent`→`parent`, `ttl`→`expiresAt`, `protected`→`protected`,
89
+ * `computeSettings`→`settings`); the previous value isn't threaded through in
90
+ * Phase 1, so these render as `field → desired` (no red "before"). Object
91
+ * settings expand into sub-fields.
92
+ *
93
+ * `parent` only ever arrives from a branch creation (it cannot be changed
94
+ * afterwards), where it reports the parent the policy named.
95
+ */
108
96
  const appliedBranchFields = (change) => {
109
- const details = change.details ?? {};
110
- const field = typeof details.field === "string" ? details.field : "setting";
111
- switch (field) {
112
- case "parent":
113
- return expandField("parent", undefined, details.parent);
114
- case "ttl":
115
- return expandField("ttl", undefined, details.expiresAt);
116
- case "protected":
117
- return expandField("protected", undefined, details.protected);
118
- case "computeSettings":
119
- return expandField("computeSettings", undefined, details.settings);
120
- default:
121
- return expandField(field, undefined, details.settings);
122
- }
97
+ const details = change.details ?? {};
98
+ const field = typeof details.field === "string" ? details.field : "setting";
99
+ switch (field) {
100
+ case "parent": return expandField("parent", void 0, details.parent);
101
+ case "ttl": return expandField("ttl", void 0, details.expiresAt);
102
+ case "protected": return expandField("protected", void 0, details.protected);
103
+ case "computeSettings": return expandField("computeSettings", void 0, details.settings);
104
+ default: return expandField(field, void 0, details.settings);
105
+ }
123
106
  };
124
107
  /**
125
- * Render the applied (or planned) changes as a `git diff`-style list:
126
- *
127
- * - **Service changes** (auth / Data API / buckets / functions) are additions
128
- * with no meaningful "before", so they list as green `+ <label>` lines
129
- * (`~ <label>` for the lone Data API settings *update*).
130
- * - **Branch setting changes** group per branch and render as sorted
131
- * `field → value` lines under a `~ <branch>` header.
132
- *
133
- * Returns "" when there is nothing to show. Pure — callers own the heading
134
- * spacing and any surrounding summary.
135
- */
136
- export const renderAppliedChanges = (changes, title, opts) => {
137
- if (changes.length === 0)
138
- return "";
139
- const paint = palette(opts.color);
140
- const lines = [paint.title(title)];
141
- const services = changes
142
- .filter((c) => c.kind === "service")
143
- .sort((a, b) => a.identifier.localeCompare(b.identifier));
144
- for (const service of services) {
145
- const label = serviceLabel(service.identifier);
146
- lines.push(service.action === "create"
147
- ? ` ${paint.added(`+ ${label}`)}`
148
- : ` ${paint.arrow(`~ ${label}`)}`);
149
- }
150
- const byBranch = new Map();
151
- for (const change of changes.filter((c) => c.kind === "branch")) {
152
- const existing = byBranch.get(change.identifier) ?? [];
153
- byBranch.set(change.identifier, [
154
- ...existing,
155
- ...appliedBranchFields(change),
156
- ]);
157
- }
158
- lines.push(...renderBranchGroups(byBranch, paint));
159
- return lines.join("\n");
108
+ * Render the applied (or planned) changes as a `git diff`-style list:
109
+ *
110
+ * - **Service changes** (auth / Data API / buckets / functions) are additions
111
+ * with no meaningful "before", so they list as green `+ <label>` lines
112
+ * (`~ <label>` for the lone Data API settings *update*).
113
+ * - **Branch setting changes** group per branch and render as sorted
114
+ * `field → value` lines under a `~ <branch>` header.
115
+ *
116
+ * Returns "" when there is nothing to show. Pure — callers own the heading
117
+ * spacing and any surrounding summary.
118
+ */
119
+ const renderAppliedChanges = (changes, title, opts) => {
120
+ if (changes.length === 0) return "";
121
+ const paint = palette(opts.color);
122
+ const lines = [paint.title(title)];
123
+ const services = changes.filter((c) => c.kind === "service").sort((a, b) => a.identifier.localeCompare(b.identifier));
124
+ for (const service of services) {
125
+ const label = serviceLabel(service.identifier);
126
+ lines.push(service.action === "create" ? ` ${paint.added(`+ ${label}`)}` : ` ${paint.arrow(`~ ${label}`)}`);
127
+ }
128
+ const byBranch = /* @__PURE__ */ new Map();
129
+ for (const change of changes.filter((c) => c.kind === "branch")) {
130
+ const existing = byBranch.get(change.identifier) ?? [];
131
+ byBranch.set(change.identifier, [...existing, ...appliedBranchFields(change)]);
132
+ }
133
+ lines.push(...renderBranchGroups(byBranch, paint));
134
+ return lines.join("\n");
160
135
  };
161
136
  /**
162
- * Render branch-setting **conflicts** (drift the policy wants to change but that
163
- * needs `--update-existing`) as a `git diff`-style before→after report: grouped
164
- * per branch, sorted by field, `current → desired` with the old value in red and
165
- * the new in green. Conflicts already carry both sides, so this is the fullest
166
- * form of the diff. Returns "" when there are no conflicts.
167
- */
168
- export const renderBranchSettingConflicts = (conflicts, opts) => {
169
- if (conflicts.length === 0)
170
- return "";
171
- const paint = palette(opts.color);
172
- const byBranch = new Map();
173
- for (const conflict of conflicts) {
174
- const existing = byBranch.get(conflict.identifier) ?? [];
175
- byBranch.set(conflict.identifier, [
176
- ...existing,
177
- ...expandField(conflict.field, conflict.current, conflict.desired),
178
- ]);
179
- }
180
- const lines = [
181
- paint.title("Branch settings differ (re-run with --update-existing to apply)"),
182
- ...renderBranchGroups(byBranch, paint),
183
- ];
184
- return lines.join("\n");
137
+ * Render branch-setting **conflicts** (drift the policy wants to change but that
138
+ * needs `--update-existing`) as a `git diff`-style before→after report: grouped
139
+ * per branch, sorted by field, `current → desired` with the old value in red and
140
+ * the new in green. Conflicts already carry both sides, so this is the fullest
141
+ * form of the diff. Returns "" when there are no conflicts.
142
+ */
143
+ const renderBranchSettingConflicts = (conflicts, opts) => {
144
+ if (conflicts.length === 0) return "";
145
+ const paint = palette(opts.color);
146
+ const byBranch = /* @__PURE__ */ new Map();
147
+ for (const conflict of conflicts) {
148
+ const existing = byBranch.get(conflict.identifier) ?? [];
149
+ byBranch.set(conflict.identifier, [...existing, ...expandField(conflict.field, conflict.current, conflict.desired)]);
150
+ }
151
+ return [paint.title("Branch settings differ (re-run with --update-existing to apply)"), ...renderBranchGroups(byBranch, paint)].join("\n");
185
152
  };
153
+ //#endregion
154
+ export { renderAppliedChanges, renderBranchSettingConflicts };