@rebasepro/cli 0.25.0-canary.ge06257e → 0.25.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 (96) hide show
  1. package/bin/rebase.js +9 -5
  2. package/dist/{agent-setup-C83DXnlx.js → agent-setup-B5AKHOgt.js} +2 -2
  3. package/dist/{agent-setup-C83DXnlx.js.map → agent-setup-B5AKHOgt.js.map} +1 -1
  4. package/dist/{api-keys-Bc4FJKYp.js → api-keys-Tm7zvAPe.js} +27 -20
  5. package/dist/api-keys-Tm7zvAPe.js.map +1 -0
  6. package/dist/{apps-wG-ZTryA.js → apps-BsU_ILbP.js} +4 -4
  7. package/dist/{apps-wG-ZTryA.js.map → apps-BsU_ILbP.js.map} +1 -1
  8. package/dist/{args-CcBphFdj.js → args-BX0YD14a.js} +45 -16
  9. package/dist/args-BX0YD14a.js.map +1 -0
  10. package/dist/{atlas-dev-database-DeK3Sa4h.js → atlas-dev-database-OWtqnG7W.js} +2 -2
  11. package/dist/{atlas-dev-database-DeK3Sa4h.js.map → atlas-dev-database-OWtqnG7W.js.map} +1 -1
  12. package/dist/{auth-BL7bNQVu.js → auth-Vl2hFKXP.js} +77 -10
  13. package/dist/auth-Vl2hFKXP.js.map +1 -0
  14. package/dist/{branch-pointer-DTQNLFtb.js → branch-pointer-9DnwkFJJ.js} +1 -1
  15. package/dist/{build-h_7Re-67.js → build-BBgT8EJZ.js} +5 -5
  16. package/dist/{build-h_7Re-67.js.map → build-BBgT8EJZ.js.map} +1 -1
  17. package/dist/{bundle-CqURVN-K.js → bundle-DtCCCr4w.js} +4 -19
  18. package/dist/bundle-DtCCCr4w.js.map +1 -0
  19. package/dist/{cloud-DVV_5GPG.js → cloud-BpH0_bmV.js} +7 -7
  20. package/dist/{cloud-DVV_5GPG.js.map → cloud-BpH0_bmV.js.map} +1 -1
  21. package/dist/commands/api-keys.d.ts +4 -1
  22. package/dist/commands/auth.d.ts +3 -2
  23. package/dist/connection-banner-DbLJzWf4.js +128 -0
  24. package/dist/connection-banner-DbLJzWf4.js.map +1 -0
  25. package/dist/{console-Dw3HLdUs.js → console-CEGm0XNc.js} +4 -4
  26. package/dist/{console-Dw3HLdUs.js.map → console-CEGm0XNc.js.map} +1 -1
  27. package/dist/{context-BM-oOMNX.js → context-CyflUrKg.js} +2 -2
  28. package/dist/{context-BM-oOMNX.js.map → context-CyflUrKg.js.map} +1 -1
  29. package/dist/{daemon-entry-C6pxdily.js → daemon-entry-CbLRrgzy.js} +199 -3
  30. package/dist/daemon-entry-CbLRrgzy.js.map +1 -0
  31. package/dist/{db-Dkn9XhT-.js → db-CRNpA5mH.js} +171 -60
  32. package/dist/db-CRNpA5mH.js.map +1 -0
  33. package/dist/{derive-Dl0z2OWi.js → derive-CsoFGAnK.js} +28 -13
  34. package/dist/{derive-Dl0z2OWi.js.map → derive-CsoFGAnK.js.map} +1 -1
  35. package/dist/{dev-DAcZR4Pp.js → dev-C0pbMmhS.js} +6 -6
  36. package/dist/{dev-DAcZR4Pp.js.map → dev-C0pbMmhS.js.map} +1 -1
  37. package/dist/dev-db/connection-banner.d.ts +47 -0
  38. package/dist/dev-db/notification-proxy.d.ts +82 -1
  39. package/dist/{dev-preflight-CorMg7Ub.js → dev-preflight-unHpiYO1.js} +26 -3
  40. package/dist/dev-preflight-unHpiYO1.js.map +1 -0
  41. package/dist/{doctor-Ds_lzc9p.js → doctor-CATRdWm6.js} +4 -4
  42. package/dist/{doctor-Ds_lzc9p.js.map → doctor-CATRdWm6.js.map} +1 -1
  43. package/dist/{eject-CJHkfnat.js → eject-DuasTr-F.js} +2 -2
  44. package/dist/{eject-CJHkfnat.js.map → eject-DuasTr-F.js.map} +1 -1
  45. package/dist/{fold-static-BC9CYicw.js → fold-static-hDHtDfTq.js} +2 -2
  46. package/dist/{fold-static-BC9CYicw.js.map → fold-static-hDHtDfTq.js.map} +1 -1
  47. package/dist/{generate_sdk-kfkZmG-d.js → generate_sdk-Cvg5jjJF.js} +3 -3
  48. package/dist/{generate_sdk-kfkZmG-d.js.map → generate_sdk-Cvg5jjJF.js.map} +1 -1
  49. package/dist/index.es.js +34 -34
  50. package/dist/{init-CjoJoThg.js → init-BWAwHPtz.js} +8 -7
  51. package/dist/init-BWAwHPtz.js.map +1 -0
  52. package/dist/last-dev-database-c_dKz1S3.js +2 -0
  53. package/dist/{normalize-imports-BHxL2d05.js → normalize-imports-BoBVh5tU.js} +3 -3
  54. package/dist/{normalize-imports-BHxL2d05.js.map → normalize-imports-BoBVh5tU.js.map} +1 -1
  55. package/dist/precompress.d.ts +0 -7
  56. package/dist/{prepare-C1iaiTC4.js → prepare-DdnU70z8.js} +4 -4
  57. package/dist/prepare-DdnU70z8.js.map +1 -0
  58. package/dist/{pull-COpNJFzY.js → pull-C3PXduzl.js} +13 -40
  59. package/dist/pull-C3PXduzl.js.map +1 -0
  60. package/dist/resolve-CNQjsatH.js +2 -0
  61. package/dist/resources/derive.d.ts +9 -0
  62. package/dist/{resources-CLm6g0br.js → resources-BdNRDEAy.js} +3 -3
  63. package/dist/{resources-CLm6g0br.js.map → resources-BdNRDEAy.js.map} +1 -1
  64. package/dist/{schema-BKdh6UoE.js → schema-B0-p6T1O.js} +4 -4
  65. package/dist/{schema-BKdh6UoE.js.map → schema-B0-p6T1O.js.map} +1 -1
  66. package/dist/{skills-DEhYiPeO.js → skills-DfnTeyRz.js} +3 -3
  67. package/dist/{skills-DEhYiPeO.js.map → skills-DfnTeyRz.js.map} +1 -1
  68. package/dist/{start-BsPJedPQ.js → start-BmEZkS0n.js} +4 -4
  69. package/dist/{start-BsPJedPQ.js.map → start-BmEZkS0n.js.map} +1 -1
  70. package/dist/{status-D_B3tlDy.js → status-BJ26yZOo.js} +9 -9
  71. package/dist/{status-D_B3tlDy.js.map → status-BJ26yZOo.js.map} +1 -1
  72. package/dist/{storage-CtB8polS.js → storage-DKPUBTWG.js} +3 -3
  73. package/dist/{storage-CtB8polS.js.map → storage-DKPUBTWG.js.map} +1 -1
  74. package/dist/{telemetry-BYmPU-Ji.js → telemetry-m9czTNGR.js} +2 -2
  75. package/dist/{telemetry-BYmPU-Ji.js.map → telemetry-m9czTNGR.js.map} +1 -1
  76. package/dist/{upgrade-DbuKX9lv.js → upgrade-DjihWuZL.js} +5 -4
  77. package/dist/{upgrade-DbuKX9lv.js.map → upgrade-DjihWuZL.js.map} +1 -1
  78. package/dist/utils/args.d.ts +13 -0
  79. package/dist/utils/redact.d.ts +10 -0
  80. package/dist/utils/refusal.d.ts +18 -0
  81. package/package.json +7 -7
  82. package/templates/overlays/baas/README.md +2 -1
  83. package/templates/template/.env.example +6 -0
  84. package/templates/template/frontend/vite.config.ts +7 -73
  85. package/dist/api-keys-Bc4FJKYp.js.map +0 -1
  86. package/dist/args-CcBphFdj.js.map +0 -1
  87. package/dist/auth-BL7bNQVu.js.map +0 -1
  88. package/dist/bundle-CqURVN-K.js.map +0 -1
  89. package/dist/daemon-entry-C6pxdily.js.map +0 -1
  90. package/dist/db-Dkn9XhT-.js.map +0 -1
  91. package/dist/dev-preflight-CorMg7Ub.js.map +0 -1
  92. package/dist/init-CjoJoThg.js.map +0 -1
  93. package/dist/last-dev-database-BEBPfAMY.js +0 -2
  94. package/dist/prepare-C1iaiTC4.js.map +0 -1
  95. package/dist/pull-COpNJFzY.js.map +0 -1
  96. package/dist/resolve-b_p9bmdH.js +0 -2
@@ -0,0 +1,47 @@
1
+ /**
2
+ * The boxed banner the database commands print when a connection fails.
3
+ *
4
+ * ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
5
+ * ❌ Cannot connect to PostgreSQL at 127.0.0.1:5508
6
+ * ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
7
+ *
8
+ * The driver said: connect ECONNREFUSED 127.0.0.1:5508
9
+ * …
10
+ *
11
+ * The driver prints it for the commands it runs (`cli-errors.ts` in
12
+ * `@rebasepro/server-postgres`, which its entry does not export); this is the
13
+ * same box for the commands the CLI answers in its own process — `db pull`
14
+ * and `auth reset-password`'s database fallback — as plain lines for the
15
+ * caller to print. Never with the connection string in it: it carries the
16
+ * password.
17
+ */
18
+ import type { DevDatabaseSource } from "./resolve.js";
19
+ /** The banner, as lines: a title, what the driver or tool said, then the body. */
20
+ export declare function banner(title: string, said: string | null, body: readonly string[]): string[];
21
+ /** `host:port` of a connection string, for a message: never the string, which carries the password. */
22
+ export declare function hostOf(connectionString: string): string;
23
+ /**
24
+ * A connection error's own sentence. A refused connection on a dual-stack
25
+ * host is an `AggregateError` with an empty message, and its sentence —
26
+ * `connect ECONNREFUSED 127.0.0.1:5432` — is in the attempts it holds.
27
+ */
28
+ export declare function connectionErrorText(error: unknown): string;
29
+ /**
30
+ * What to print when opening `url` failed with `error`: the banner, in the
31
+ * case it is — the server not answering, credentials refused, a database
32
+ * closed to new connections, or a database of that name not there — or `null`
33
+ * when it is none of those four.
34
+ *
35
+ * `consequence` is the line saying what did not happen ("The password was not
36
+ * reset."); `fixes` is what to do about a server that does not answer, which
37
+ * depends on what named the database.
38
+ */
39
+ export declare function connectionFailureBanner(url: string, error: unknown, options: {
40
+ consequence: string;
41
+ fixes: readonly string[];
42
+ }): string[] | null;
43
+ /**
44
+ * What to do about a database that does not answer, for what named it — the
45
+ * list the driver's banner gives for the same `REBASE_DATABASE_SOURCE`.
46
+ */
47
+ export declare function connectionRefusedFixes(source: DevDatabaseSource): string[];
@@ -31,10 +31,17 @@
31
31
  * has subscribed to, which costs nothing. A client that *did* ask now always
32
32
  * gets it, which is the whole point.
33
33
  *
34
+ * It corrects one more defect on the way: PGlite answers a failed
35
+ * extended-protocol message with a ReadyForQuery of its own and the Sync after
36
+ * it with another, and the proxy drops the extra one (`ReadyForQueryLedger`),
37
+ * because a client that takes it for the next query's answer reads that
38
+ * query's rows as empty and every answer after it one request late.
39
+ *
34
40
  * Two properties make this safe rather than clever:
35
41
  *
36
42
  * - **It never parses SQL and never rewrites a byte.** Frames are forwarded
37
- * verbatim; the only edit is delivering a copy of one to more sockets.
43
+ * verbatim; the only edits are delivering a copy of one to more sockets and
44
+ * holding back that extra ReadyForQuery.
38
45
  * - **Injection only happens on a message boundary.** The server→client stream
39
46
  * is reassembled into whole protocol messages before anything is written on,
40
47
  * so an injected frame can never land inside another message.
@@ -61,6 +68,61 @@ export declare class BackendMessageParser {
61
68
  /** Bytes held back because they are not yet a whole message. */
62
69
  get pending(): number;
63
70
  }
71
+ /**
72
+ * Reassembles the client→server stream into whole messages, so the proxy
73
+ * knows what each answer is an answer to.
74
+ *
75
+ * The connection opens untyped — an `SSLRequest` or `GSSENCRequest` (each
76
+ * answered with one byte) and then the startup packet, each a bare `Int32
77
+ * length` and a code — and is `type + Int32 length` after it. Each message is
78
+ * returned as its type character, `"startup"` for the startup packet, and the
79
+ * negotiation requests not at all.
80
+ */
81
+ export declare class FrontendMessageParser {
82
+ private buffered;
83
+ private started;
84
+ push(chunk: Buffer): string[];
85
+ }
86
+ /**
87
+ * Keeps a connection's ReadyForQuery messages one to a request.
88
+ *
89
+ * PGlite (0.5.6) answers an extended-protocol message that fails with an
90
+ * ErrorResponse *and* a ReadyForQuery, then answers the Sync after it with a
91
+ * second ReadyForQuery. Real Postgres sends one: after an error it ignores
92
+ * everything up to the Sync and answers the Sync. `pg` absorbs the extra one
93
+ * when both arrive in one read. When they arrive apart — under load, a script
94
+ * beside a running `rebase dev` — it takes the second as the answer to the
95
+ * next query it sent:
96
+ *
97
+ * - a refused write's ROLLBACK "finished" on it, the pool saw the client
98
+ * released in a failed transaction (status 'E'), and the real ROLLBACK's
99
+ * answer arrived as "Received unexpected commandComplete message from
100
+ * backend" — two `[pg-pool]` ERROR lines on every refused write;
101
+ * - on a connection held across queries, the next query "finished" with no
102
+ * rows: a `SELECT` that answered `[]`.
103
+ *
104
+ * So the proxy follows each request and drops the ReadyForQuery that answers
105
+ * a failed message before its Sync. An error raised *by* the Sync — a deferred
106
+ * constraint failing as the implicit transaction commits — comes with the
107
+ * one ReadyForQuery, and that is kept, as is every simple Query's.
108
+ */
109
+ export declare class ReadyForQueryLedger {
110
+ private readonly pending;
111
+ /** Note a message the client sent, before it is forwarded. */
112
+ sent(type: string): void;
113
+ /** Whether a message the backend sent goes on to the client. */
114
+ deliver(message: Buffer): boolean;
115
+ }
116
+ /** What a client over the managed database's limit is told, and what to do. */
117
+ export declare const NO_FREE_CONNECTION: {
118
+ message: string;
119
+ hint: string;
120
+ };
121
+ /**
122
+ * The refusal as Postgres sends one to a client over `max_connections`: a
123
+ * FATAL ErrorResponse with SQLSTATE 53300 (too_many_connections).
124
+ */
125
+ export declare function noFreeConnectionResponse(): Buffer;
64
126
  export declare function isNotificationFrame(message: Buffer): boolean;
65
127
  /** Channel and payload of a NotificationResponse, for logging and tests. */
66
128
  export declare function decodeNotification(message: Buffer): {
@@ -75,6 +137,12 @@ export interface NotificationProxyOptions {
75
137
  host?: string;
76
138
  /** Called for every notification broadcast. For diagnostics and tests. */
77
139
  onNotification?: (channel: string, payload: string, copies: number) => void;
140
+ /**
141
+ * Connections the socket server behind accepts. One more is refused here,
142
+ * with a message, rather than dropped there without one. Default
143
+ * {@link MANAGED_SERVER_MAX_CONNECTIONS}, what the daemon gives it.
144
+ */
145
+ maxConnections?: number;
78
146
  }
79
147
  /**
80
148
  * The proxy itself.
@@ -90,6 +158,19 @@ export declare class NotificationProxy {
90
158
  get connectionCount(): number;
91
159
  start(): Promise<void>;
92
160
  private accept;
161
+ /**
162
+ * Turn a client away, in words, when every connection the socket server
163
+ * accepts is taken.
164
+ *
165
+ * The socket server sets its limit on the listening socket, so Node closes
166
+ * a connection over it the moment it is accepted, before a byte is read:
167
+ * every client read that as "Connection terminated unexpectedly", which
168
+ * says the database is down. Each client has its own connection upstream,
169
+ * so the proxy's count is the server's, and it answers as Postgres answers
170
+ * a client over `max_connections`, once the client has said hello: "no TLS"
171
+ * to an SSLRequest, and the refusal to the startup packet.
172
+ */
173
+ private refuse;
93
174
  /**
94
175
  * Copy a notification to every other client.
95
176
  *
@@ -5,6 +5,29 @@ import fs from "fs";
5
5
  import path from "path";
6
6
  import { ExecaError, execa } from "execa";
7
7
  import net from "net";
8
+ //#region src/utils/refusal.ts
9
+ /**
10
+ * A refusal the CLI raised itself, having said why: nothing broke.
11
+ *
12
+ * `bin/rebase.js` ends every failure with "Re-run with --debug for the stack
13
+ * trace." That is right when something broke inside a command, and noise after
14
+ * a check that decided not to run — the compose-port refusal prints the
15
+ * container holding the port and how to move, and its stack points at the line
16
+ * that decided to refuse. A `UsageError` (`utils/args.ts`) is the same case for
17
+ * a command line that was wrong; this is the one for everything else.
18
+ *
19
+ * Marked with a property rather than left to `instanceof`, for `UsageError`'s
20
+ * reason: the reader is `bin/rebase.js`, which imports the bundle, so the class
21
+ * it would test against is not the one this file compiled to.
22
+ */
23
+ var Refusal = class extends Error {
24
+ isRefusal = true;
25
+ constructor(message) {
26
+ super(message);
27
+ this.name = "Refusal";
28
+ }
29
+ };
30
+ //#endregion
8
31
  //#region src/utils/dev-preflight.ts
9
32
  /**
10
33
  * Make `rebase dev` the only command a new project needs.
@@ -526,7 +549,7 @@ async function refuseForeignListener(options) {
526
549
  `Give this project its own port: change "${port}:5432" under db: in docker-compose.yml to ${suggestion ? `a free one, such as "${suggestion}:5432"` : "one nothing else uses"}` + (envSetsUrl ? ", and the port in DATABASE_URL in .env." : "."),
527
550
  holder.kind === "container" ? `Or stop ${holder.name} first: docker stop ${holder.name}` : "Or stop what is listening on it."
528
551
  ]) log(` ${chalk.gray(line)}`);
529
- throw new Error(holder.kind === "container" ? `Port ${port} belongs to ${holder.name}, not to this project's db service. Nothing was started.` : `Port ${port} belongs to a server outside Docker, not to this project's db service. Nothing was started.`);
552
+ throw new Refusal(holder.kind === "container" ? `Port ${port} belongs to ${holder.name}, not to this project's db service. Nothing was started.` : `Port ${port} belongs to a server outside Docker, not to this project's db service. Nothing was started.`);
530
553
  }
531
554
  /**
532
555
  * Start the project's development database if it is not running, and give it a
@@ -655,6 +678,6 @@ async function ensureDevDatabase(options) {
655
678
  }
656
679
  }
657
680
  //#endregion
658
- export { parseLoopbackDsn as a, ensureDevDatabase as i, dev_preflight_exports as n, probeTcp as o, dockerClaimedPorts as r, composeDatabaseUrl as t };
681
+ export { parseLoopbackDsn as a, ensureDevDatabase as i, dev_preflight_exports as n, probeTcp as o, dockerClaimedPorts as r, Refusal as s, composeDatabaseUrl as t };
659
682
 
660
- //# sourceMappingURL=dev-preflight-CorMg7Ub.js.map
683
+ //# sourceMappingURL=dev-preflight-unHpiYO1.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dev-preflight-unHpiYO1.js","names":[],"sources":["../src/utils/refusal.ts","../src/utils/dev-preflight.ts"],"sourcesContent":["/**\n * A refusal the CLI raised itself, having said why: nothing broke.\n *\n * `bin/rebase.js` ends every failure with \"Re-run with --debug for the stack\n * trace.\" That is right when something broke inside a command, and noise after\n * a check that decided not to run — the compose-port refusal prints the\n * container holding the port and how to move, and its stack points at the line\n * that decided to refuse. A `UsageError` (`utils/args.ts`) is the same case for\n * a command line that was wrong; this is the one for everything else.\n *\n * Marked with a property rather than left to `instanceof`, for `UsageError`'s\n * reason: the reader is `bin/rebase.js`, which imports the bundle, so the class\n * it would test against is not the one this file compiled to.\n */\nexport class Refusal extends Error {\n readonly isRefusal = true;\n\n constructor(message: string) {\n super(message);\n this.name = \"Refusal\";\n }\n}\n","/**\n * Make `rebase dev` the only command a new project needs.\n *\n * The scaffolded project prints three steps — start the database container,\n * push the schema, start the dev server — and a first-time reader has to\n * perform all three correctly before anything works. Two of them are\n * mechanical, and both fail in ways that do not name themselves: skipping the\n * container gives `ECONNREFUSED` from a stack the reader has never seen, and\n * skipping the push gives a server that starts cleanly and then 500s on the\n * first request because the tables are not there.\n *\n * So `dev` does them. Not always, and not silently — under four conditions that\n * together mean \"this is a throwaway development database on this laptop, and\n * it is not running\":\n *\n * 1. The DSN resolves to a loopback address. A remote database is somebody's\n * real data and is never touched here, whatever else is true.\n * 2. Nothing is listening on it. A database that answers is left completely\n * alone — no push, no inspection, no connection.\n * 3. The project has a `docker-compose.yml` declaring a `db` service, i.e.\n * the one `rebase init` wrote. Without it there is nothing to start and\n * the backend's own error is the better message.\n * 4. Docker is available. If it is not, the manual steps are printed and dev\n * continues — a preflight must never be the reason the command fails.\n *\n * The schema push happens only when this function actually started the\n * container, which is the one moment a local database is known to be new or\n * stale relative to the collections. A `dev` against an already-running\n * database never mutates its schema.\n *\n * One case stops the command instead of stepping aside: the URL is this\n * project's compose database, and Docker says what answers on its port is\n * somebody else's. Carrying on would connect this project to that database,\n * and the best case is a failed login whose advice (\"ALTER USER …\") is for the\n * wrong one. See {@link classifyPortPublishers}.\n */\nimport fs from \"fs\";\nimport net from \"net\";\nimport path from \"path\";\nimport chalk from \"chalk\";\nimport { execa, ExecaError } from \"execa\";\n\nimport { readEnvFile } from \"./project\";\nimport { Refusal } from \"./refusal\";\n\n/** Where the preflight stopped, and why. Returned so tests can assert it. */\nexport type PreflightOutcome =\n | { action: \"disabled\" }\n | { action: \"no-dsn\" }\n | { action: \"remote-dsn\"; host: string }\n | { action: \"already-running\"; host: string; port: number }\n | { action: \"no-compose\" }\n | { action: \"no-docker\"; hint: string }\n | { action: \"start-failed\"; hint: string }\n | { action: \"wrong-port\"; hint: string; port: number; composePort: number }\n | { action: \"started\"; port: number; pushed: boolean };\n\n/**\n * Hosts that mean \"this machine\", and so may be provisioned automatically.\n *\n * An exact-match set, never a prefix or substring test: `localhost.example.com`\n * is a perfectly resolvable name that somebody else controls.\n */\nconst LOOPBACK = new Set([\"localhost\", \"127.0.0.1\", \"::1\", \"0.0.0.0\"]);\n\n/**\n * The host and port of a Postgres DSN, but only when it points at this machine.\n *\n * Returns null for anything else — a remote host, an unparseable string, a\n * non-postgres scheme. Callers treat null as \"do nothing\", so every parse\n * failure fails closed onto the safe behaviour rather than onto a guess.\n */\nexport function parseLoopbackDsn(dsn: string | undefined): { host: string; port: number } | null {\n if (!dsn) return null;\n\n let url: URL;\n try {\n url = new URL(dsn);\n } catch {\n return null;\n }\n\n if (!/^postgres(ql)?:$/.test(url.protocol)) return null;\n\n // `url.hostname` KEEPS the brackets an IPv6 literal carries in a URL, and\n // `net.connect` rejects them. Normalising here rather than at each use is\n // what stops the two representations diverging: everything downstream gets\n // a host it can dial.\n const host = url.hostname.replace(/^\\[(.+)\\]$/, \"$1\");\n if (!LOOPBACK.has(host)) return null;\n\n const port = url.port ? Number(url.port) : 5432;\n if (!Number.isInteger(port) || port <= 0 || port > 65535) return null;\n\n return { host, port };\n}\n\n/**\n * Whether a compose file declares a `db` service.\n *\n * Deliberately a text scan and not a YAML parse: the CLI has no YAML\n * dependency, and the question is only ever asked of a file this tool wrote.\n * A false negative costs the automation and nothing else — the reader gets the\n * manual steps, which are correct.\n */\nexport function composeDeclaresDbService(yamlText: string): boolean {\n let inServices = false;\n for (const rawLine of yamlText.split(/\\r?\\n/)) {\n const line = rawLine.replace(/\\t/g, \" \");\n if (/^\\s*#/.test(line) || line.trim() === \"\") continue;\n\n if (/^services:\\s*$/.test(line)) {\n inServices = true;\n continue;\n }\n // Any other zero-indent key ends the services block.\n if (inServices && /^\\S/.test(line)) {\n inServices = false;\n continue;\n }\n if (inServices && /^\\s{2,}db:\\s*$/.test(line)) return true;\n }\n return false;\n}\n\n/**\n * The connection string the compose file's `db` service will answer on.\n *\n * `--docker` used to be a flag that changed a banner and nothing else: it\n * resolved to `kind: \"docker\"`, and every caller then had no URL to reach the\n * container with, so the backend booted with no `DATABASE_URL` at all and died\n * on the message a project with no database gets. The container it had just\n * been asked for was never started, because the preflight that starts it needs\n * a DSN to decide the database is local and not running.\n *\n * So the URL is derived rather than required: the compose file names the user,\n * the database and the published host port, and `.env` holds the password\n * compose itself interpolates. That is the same string `rebase init` writes as\n * the commented-out `DATABASE_URL`, which is what makes uncommenting that line\n * and passing `--docker` reach the same database.\n *\n * Returns null when the file declares no `db` service, or declares one this\n * scan cannot read — a `--docker` that cannot be honoured has to say so, and\n * a guess would point at somebody else's Postgres on 5432.\n *\n * Same text scan as {@link composeDeclaresDbService}, for the same reason.\n */\nexport function composeDatabaseUrl(\n yamlText: string,\n env: Record<string, string | undefined> = {}\n): string | null {\n const found = scanComposeDbService(yamlText);\n\n const user = expandComposeValue(found.POSTGRES_USER, env);\n const password = expandComposeValue(found.POSTGRES_PASSWORD, env);\n const database = expandComposeValue(found.POSTGRES_DB, env);\n if (!user || !password || !database || !found.hostPort) return null;\n\n return `postgresql://${encodeURIComponent(user)}:${encodeURIComponent(password)}` +\n `@127.0.0.1:${found.hostPort}/${database}?options=-c%20search_path%3Dpublic&sslmode=disable`;\n}\n\n/**\n * The host port the compose `db` service publishes, or null.\n *\n * Separate from {@link composeDatabaseUrl} because the port is answerable on\n * its own: the URL needs the user, the password and the database name too, and\n * a compose file missing any of them still says which port a container would\n * listen on. That is the question \"would starting this container answer the\n * DSN in `.env`?\" — see the `wrong-port` branch of {@link ensureDevDatabase},\n * where getting it wrong meant starting a container nobody asked for.\n */\nexport function composeHostPort(yamlText: string): number | null {\n const port = scanComposeDbService(yamlText).hostPort;\n\n return port ? Number(port) : null;\n}\n\n/** The raw values of the compose `db` service, before any expansion. */\nfunction scanComposeDbService(yamlText: string): Record<string, string> & { hostPort?: string } {\n let inServices = false;\n let inDb = false;\n let dbIndent = 0;\n let section: \"environment\" | \"ports\" | null = null;\n const found: Record<string, string> = {};\n let hostPort: string | null = null;\n\n for (const rawLine of yamlText.split(/\\r?\\n/)) {\n const line = rawLine.replace(/\\t/g, \" \");\n if (/^\\s*#/.test(line) || line.trim() === \"\") continue;\n\n if (/^services:\\s*$/.test(line)) { inServices = true; continue; }\n if (inServices && /^\\S/.test(line)) { inServices = false; inDb = false; continue; }\n if (!inServices) continue;\n\n const indent = line.length - line.trimStart().length;\n const dbHeader = line.match(/^(\\s{2,})db:\\s*$/);\n if (dbHeader) { inDb = true; dbIndent = dbHeader[1].length; section = null; continue; }\n // A sibling service at the same indent as `db:` ends it.\n if (inDb && indent <= dbIndent) { inDb = false; section = null; continue; }\n if (!inDb) continue;\n\n const key = line.match(/^\\s+(environment|ports):\\s*$/);\n if (key) { section = key[1] as \"environment\" | \"ports\"; continue; }\n // Any other key of the service ends the block we were reading.\n if (/^\\s+[A-Za-z_][\\w-]*:\\s*\\S/.test(line) && !/^\\s+POSTGRES_/.test(line)) {\n if (!/^\\s*-\\s/.test(line.trimStart())) section = null;\n }\n\n if (section === \"environment\") {\n const pair = line.match(/^\\s+(POSTGRES_(?:USER|PASSWORD|DB)):\\s*(.+?)\\s*$/);\n if (pair) found[pair[1]] = pair[2].replace(/^[\"']|[\"']$/g, \"\");\n continue;\n }\n\n if (section === \"ports\" && hostPort === null) {\n // `- \"5435:5432\"`. Only a mapping whose container side is Postgres's\n // own port is the one to dial; anything else is a different service.\n const mapping = line.match(/^\\s*-\\s*[\"']?(?:[\\d.]+:)?(\\d+):5432(?:\\/tcp)?[\"']?\\s*$/);\n if (mapping) hostPort = mapping[1];\n }\n }\n\n return hostPort === null ? found : { ...found, hostPort };\n}\n\n/**\n * `${VAR:-default}`, `${VAR}` and a literal, resolved the way compose resolves\n * them — against the project's `.env`, which is the file compose itself reads.\n */\nfunction expandComposeValue(\n raw: string | undefined,\n env: Record<string, string | undefined>\n): string | null {\n if (!raw) return null;\n const interpolated = raw.replace(/\\$\\{([A-Za-z_][\\w]*)(?::?-([^}]*))?\\}/g, (_all, name, fallback) => {\n const value = env[name];\n if (value !== undefined && value !== \"\") return value;\n return fallback ?? \"\";\n });\n const trimmed = interpolated.trim();\n return trimmed.length > 0 ? trimmed : null;\n}\n\n/**\n * Is something accepting connections there right now?\n *\n * A reset counts as yes: a refused connection means nothing listens, a reset\n * means something accepted it and dropped it — Docker's port proxy, while the\n * Postgres behind it is still starting. On macOS that reset arrives after\n * `connect`; on Linux it can arrive instead of it, and read as \"not listening\"\n * the port wait spun for its whole minute before readiness was ever asked.\n */\nexport function probeTcp(host: string, port: number, timeoutMs = 700): Promise<boolean> {\n return new Promise(resolve => {\n const socket = new net.Socket();\n let settled = false;\n\n const done = (open: boolean): void => {\n if (settled) return;\n settled = true;\n socket.destroy();\n resolve(open);\n };\n\n socket.setTimeout(timeoutMs);\n socket.once(\"connect\", () => done(true));\n socket.once(\"timeout\", () => done(false));\n socket.once(\"error\", (error: Error) => done(\"code\" in error && error.code === \"ECONNRESET\"));\n socket.connect(port, host);\n });\n}\n\n/**\n * Poll until the port answers, or give up.\n *\n * This only answers \"has the container got as far as listening\". It is not\n * readiness: Docker's port proxy accepts on the host port while Postgres inside\n * the container is still running initdb, and resets every connection it cannot\n * forward. {@link waitForQueryable} is the check that a query will work.\n */\nexport async function waitForPort(\n host: string,\n port: number,\n timeoutMs = 60_000,\n intervalMs = 500\n): Promise<boolean> {\n const deadline = Date.now() + timeoutMs;\n while (Date.now() < deadline) {\n if (await probeTcp(host, port, 700)) return true;\n await new Promise(resolve => setTimeout(resolve, intervalMs));\n }\n return false;\n}\n\n/** Whether a database answered a query, and if not, the last reason it gave. */\nexport type QueryReadiness = { ready: true } | { ready: false; error: string };\n\n/**\n * Poll until `SELECT 1` succeeds on this connection string, or the bound runs out.\n *\n * The readiness that matters to everything after it. A fresh compose volume\n * answers the TCP probe seconds before it answers a query — the official image\n * runs initdb on a socket-only server, then restarts on TCP — and the schema\n * push that ran in that gap failed on `read ECONNRESET`, three runs out of\n * three. Every error is retried: a reset, \"the database system is starting up\"\n * and a refused connection all mean \"not yet\", and the bound is what turns\n * \"not yet\" into an answer.\n */\nexport async function waitForQueryable(\n databaseUrl: string,\n options: { timeoutMs?: number; intervalMs?: number } = {}\n): Promise<QueryReadiness> {\n const { timeoutMs = 60_000, intervalMs = 500 } = options;\n const { Client } = await import(\"pg\");\n const deadline = Date.now() + timeoutMs;\n let lastError = \"no attempt was made\";\n\n do {\n const client = new Client({\n connectionString: databaseUrl,\n connectionTimeoutMillis: Math.max(250, Math.min(2_000, deadline - Date.now()))\n });\n // A reset can arrive after `connect` settles; unhandled, it would take\n // the process down rather than count as one more \"not yet\".\n client.on(\"error\", () => undefined);\n try {\n await client.connect();\n await client.query(\"SELECT 1\");\n return { ready: true };\n } catch (error) {\n lastError = error instanceof Error && error.message ? error.message : String(error);\n } finally {\n await client.end().catch(() => undefined);\n }\n if (Date.now() + intervalMs >= deadline) break;\n await new Promise(resolve => setTimeout(resolve, intervalMs));\n } while (Date.now() < deadline);\n\n return { ready: false, error: lastError };\n}\n\n/**\n * What a failed schema push printed, as lines to show.\n *\n * `rebase dev` runs the push with its output captured, so a successful start\n * stays quiet. On a failure that captured output is the diagnosis, and it was\n * being dropped: the warning said \"fix the error above\" over nothing at all.\n * The child's stdout and stderr are what is shown — not execa's own message,\n * which leads with the command line, and that carries the database password.\n */\nexport function pushFailureOutput(error: unknown): string[] {\n if (error instanceof ExecaError) {\n const captured = [streamText(error.stdout), streamText(error.stderr)]\n .map(stream => stream.trim())\n .filter(stream => stream.length > 0);\n if (captured.length > 0) return captured.join(\"\\n\").split(/\\r?\\n/);\n return [error.originalMessage || `The push exited with code ${error.exitCode ?? \"unknown\"}.`];\n }\n return [error instanceof Error ? error.message : String(error)];\n}\n\n/** A captured stream as text, whichever form execa's options gave it. */\nfunction streamText(stream: unknown): string {\n if (typeof stream === \"string\") return stream;\n if (stream instanceof Uint8Array) return Buffer.from(stream).toString(\"utf8\");\n if (Array.isArray(stream)) return stream.map(String).join(\"\\n\");\n return \"\";\n}\n\n/** The user and password the compose `db` service creates, expanded against `.env`. */\nfunction composeCredentials(\n yamlText: string,\n env: Record<string, string | undefined>\n): { user: string; password: string } | null {\n const found = scanComposeDbService(yamlText);\n const user = expandComposeValue(found.POSTGRES_USER, env);\n const password = expandComposeValue(found.POSTGRES_PASSWORD, env);\n return user && password ? { user, password } : null;\n}\n\n/**\n * The compose project a file belongs to, named the way `docker compose` names\n * it: the file's top-level `name:`, else the directory, lowercased with\n * everything but `[a-z0-9_-]` dropped.\n *\n * A project name set with `-p` or in the environment is not read: a container\n * started from this directory is recognised by its working directory anyway,\n * and the name is only the fallback for a project that has moved since.\n */\nexport function composeProjectName(yamlText: string, projectRoot: string): string {\n const named = /^name:\\s*[\"']?([^\"'#\\s]+)[\"']?\\s*(?:#.*)?$/m.exec(yamlText)?.[1];\n const raw = named || path.basename(projectRoot);\n return raw.toLowerCase().replace(/[^a-z0-9_-]/g, \"\");\n}\n\n/** Who answers on a host port, as far as Docker can say. */\nexport type PortHolder =\n | { kind: \"this-project\" }\n | { kind: \"container\"; name: string; project?: string; directory?: string }\n | { kind: \"outside-docker\" }\n | { kind: \"unknown\" };\n\n/** One `docker ps` row per container publishing the port: name, then its compose labels. */\nconst PUBLISHER_FORMAT = [\n \"{{.Names}}\",\n \"{{.Label \\\"com.docker.compose.project\\\"}}\",\n \"{{.Label \\\"com.docker.compose.project.working_dir\\\"}}\",\n \"{{.Label \\\"com.docker.compose.service\\\"}}\"\n].join(\"\\t\");\n\n/**\n * Whose container `docker ps --filter publish=<port>` listed, from its rows.\n *\n * This project's when one of them is a `db` service of this compose project:\n * started from this directory, or carrying the project's name (compose treats\n * two directories with one `name:` as one project, so this does too). Any other\n * container is named. No rows at all, from a Docker that answered, means the\n * listener is not a container: a Postgres installed on this machine, say.\n */\nexport function classifyPortPublishers(output: string, projectRoot: string, composeProject: string | null): PortHolder {\n const rows = output.split(/\\r?\\n/)\n .map(line => line.split(\"\\t\").map(field => field.trim()))\n .filter(([name]) => Boolean(name));\n if (rows.length === 0) return { kind: \"outside-docker\" };\n\n const here = canonicalPath(projectRoot);\n for (const [, project, directory, service] of rows) {\n if (service !== \"db\") continue;\n if ((directory && canonicalPath(directory) === here) || (composeProject && project === composeProject)) {\n return { kind: \"this-project\" };\n }\n }\n const [name, project, directory] = rows[0];\n return { kind: \"container\", name, ...(project ? { project } : {}), ...(directory ? { directory } : {}) };\n}\n\nfunction canonicalPath(dir: string): string {\n try {\n return fs.realpathSync(dir);\n } catch {\n return path.resolve(dir);\n }\n}\n\n/** Ask Docker who publishes `port`. `unknown` when Docker cannot be asked. */\nexport async function dockerPortHolder(projectRoot: string, composeProject: string | null, port: number): Promise<PortHolder> {\n try {\n const { stdout } = await execa(\"docker\", [\"ps\", \"--filter\", `publish=${port}`, \"--format\", PUBLISHER_FORMAT], {\n stdio: \"pipe\",\n timeout: 5_000\n });\n return classifyPortPublishers(stdout, projectRoot, composeProject);\n } catch {\n return { kind: \"unknown\" };\n }\n}\n\n/**\n * Every host port in a list of `docker inspect --format '{{json .HostConfig.PortBindings}}'` lines.\n *\n * The configured bindings, which a stopped container keeps: it takes its port\n * back the moment it starts, so the port is not free to give to a new project.\n * `HostPort: \"0\"` asks for any free port and claims none.\n */\nexport function parsePortBindings(output: string): Set<number> {\n const ports = new Set<number>();\n for (const line of output.split(/\\r?\\n/)) {\n if (!line.trim()) continue;\n let parsed: unknown;\n try {\n parsed = JSON.parse(line);\n } catch {\n continue;\n }\n if (!parsed || typeof parsed !== \"object\") continue;\n const bindingLists: unknown[] = Object.values(parsed);\n for (const bindings of bindingLists) {\n if (!Array.isArray(bindings)) continue;\n for (const binding of bindings) {\n if (!binding || typeof binding !== \"object\" || !(\"HostPort\" in binding)) continue;\n const hostPort = Number(binding.HostPort);\n if (Number.isInteger(hostPort) && hostPort > 0 && hostPort < 65536) ports.add(hostPort);\n }\n }\n }\n return ports;\n}\n\n/**\n * The host ports every container on this machine is bound to, running or\n * stopped — `null` when Docker cannot be asked.\n *\n * A TCP probe sees only what is listening now, and a stopped project's `db`\n * container is not; `docker ps` shows a stopped container's ports only\n * sometimes. The bindings in its config are what it will take back.\n */\nexport async function dockerClaimedPorts(): Promise<Set<number> | null> {\n try {\n const { stdout: ids } = await execa(\"docker\", [\"ps\", \"-aq\"], { stdio: \"pipe\", timeout: 5_000 });\n const containers = ids.split(/\\s+/).filter(Boolean);\n if (containers.length === 0) return new Set();\n // `reject: false`: a container removed since `ps` fails its own entry and\n // exits 1, and every other container's bindings are still on stdout.\n const { stdout } = await execa(\"docker\", [\"inspect\", \"--format\", \"{{json .HostConfig.PortBindings}}\", ...containers], {\n stdio: \"pipe\",\n timeout: 10_000,\n reject: false\n });\n return parsePortBindings(stdout);\n } catch {\n return null;\n }\n}\n\n/**\n * Whether `databaseUrl` is this project's compose database: the port the `db`\n * service publishes, with the user and password it creates.\n *\n * The credentials matter. One Postgres container shared by several projects is\n * an ordinary setup, and such a URL names that container's password — a\n * database somebody chose, which is theirs to point at.\n */\nfunction isComposeDatabaseUrl(databaseUrl: string, port: number, composeText: string, env: Record<string, string | undefined>): boolean {\n if (composeHostPort(composeText) !== port) return false;\n const credentials = composeCredentials(composeText, env);\n if (!credentials) return false;\n try {\n const url = new URL(databaseUrl);\n return decodeURIComponent(url.username) === credentials.user\n && decodeURIComponent(url.password) === credentials.password;\n } catch {\n return false;\n }\n}\n\n/** The first port after `port` that nothing on loopback answers on, for a suggestion. */\nasync function nextQuietPort(port: number): Promise<number | null> {\n for (let candidate = port + 1; candidate < Math.min(port + 50, 65536); candidate++) {\n if (!(await probeTcp(\"127.0.0.1\", candidate, 300))) return candidate;\n }\n return null;\n}\n\n/**\n * Said, then thrown: the compose port answers, and not with this project's\n * database. Returns quietly when the listener is ours, or when Docker cannot\n * say — the behaviour from before this check.\n */\nasync function refuseForeignListener(options: {\n projectRoot: string;\n databaseUrl: string;\n port: number;\n composeText: string;\n whoHoldsPort?: (port: number) => Promise<PortHolder>;\n log: (message: string) => void;\n}): Promise<void> {\n const { projectRoot, databaseUrl, port, composeText, log } = options;\n const env = readEnvFile(projectRoot);\n if (!isComposeDatabaseUrl(databaseUrl, port, composeText, env)) return;\n\n const whoHoldsPort = options.whoHoldsPort\n ?? ((p: number) => dockerPortHolder(projectRoot, composeProjectName(composeText, projectRoot), p));\n const holder = await whoHoldsPort(port);\n if (holder.kind === \"this-project\" || holder.kind === \"unknown\") return;\n\n const who = holder.kind === \"container\"\n ? `another container is listening there: ${holder.name}`\n + (holder.project || holder.directory\n ? ` (${[holder.project ? `compose project ${holder.project}` : \"\", holder.directory ? `in ${holder.directory}` : \"\"]\n .filter(Boolean).join(\", \")})`\n : \"\")\n : \"something outside Docker is listening there, such as a Postgres installed on this machine\";\n const suggestion = await nextQuietPort(port);\n const envSetsUrl = Boolean(env.DATABASE_URL?.trim());\n\n log(\"\");\n log(` ${chalk.yellow(\"⚠\")} ${chalk.bold(`Port ${port} is not this project's database.`)}`);\n for (const line of [\n `docker-compose.yml publishes this project's db service on ${port}, and ${who}.`,\n \"Nothing was started and nothing connected: a login there fails on this project's password, and the fix that failure suggests is for that database.\",\n `Give this project its own port: change \"${port}:5432\" under db: in docker-compose.yml`\n + ` to ${suggestion ? `a free one, such as \"${suggestion}:5432\"` : \"one nothing else uses\"}`\n + (envSetsUrl ? \", and the port in DATABASE_URL in .env.\" : \".\"),\n holder.kind === \"container\"\n ? `Or stop ${holder.name} first: docker stop ${holder.name}`\n : \"Or stop what is listening on it.\"\n ]) {\n log(` ${chalk.gray(line)}`);\n }\n\n // Said in full above, so a `Refusal`: `bin/rebase.js` adds no stack hint.\n throw new Refusal(\n holder.kind === \"container\"\n ? `Port ${port} belongs to ${holder.name}, not to this project's db service. Nothing was started.`\n : `Port ${port} belongs to a server outside Docker, not to this project's db service. Nothing was started.`\n );\n}\n\nexport interface EnsureDevDatabaseOptions {\n projectRoot: string;\n /** The DSN from the project's env file, if it has one. */\n databaseUrl: string | undefined;\n /** Set by `--no-db` or REBASE_DEV_NO_DB. */\n disabled: boolean;\n /** Whether the project has collections worth pushing. */\n hasCollections: boolean;\n /** Runs `rebase db push` for this project. Injected so tests need no database. */\n pushSchema: () => Promise<void>;\n /**\n * Brings the compose `db` service up. Injected for the same reason\n * `pushSchema` is: what happens *after* a start — a push that fails without\n * taking the dev server with it — is the part worth asserting, and it was\n * untestable while the only way in was a real `docker compose up`.\n */\n startDatabase?: (projectRoot: string) => Promise<void>;\n /** How long a started database has to answer `SELECT 1`. Defaults to 60s. */\n readyTimeoutMs?: number;\n /**\n * Who answers on the compose port. Asks Docker by default; injected for the\n * same reason `startDatabase` is.\n */\n whoHoldsPort?: (port: number) => Promise<PortHolder>;\n log?: (message: string) => void;\n}\n\n/**\n * Start the project's development database if it is not running, and give it a\n * schema if this call is what started it.\n */\nexport async function ensureDevDatabase(options: EnsureDevDatabaseOptions): Promise<PreflightOutcome> {\n const { projectRoot, databaseUrl, disabled, hasCollections, pushSchema } = options;\n const log = options.log ?? ((message: string) => console.log(message));\n\n if (disabled) return { action: \"disabled\" };\n\n const target = parseLoopbackDsn(databaseUrl);\n if (!databaseUrl) return { action: \"no-dsn\" };\n if (!target) {\n // Not an error, and not worth a warning either: pointing DATABASE_URL at\n // a real database is a completely ordinary thing to do, and the only\n // correct response is to keep away from it.\n let host = \"the configured host\";\n try {\n host = new URL(databaseUrl).hostname || host;\n } catch { /* keep the placeholder */ }\n return { action: \"remote-dsn\", host };\n }\n\n const composePath = path.join(projectRoot, \"docker-compose.yml\");\n const composeText = fs.existsSync(composePath) ? fs.readFileSync(composePath, \"utf8\") : null;\n\n if (await probeTcp(target.host, target.port)) {\n // Something answers. Whatever it is stays untouched; but if this URL is\n // the compose database, it has to be the compose container.\n if (composeText !== null) {\n await refuseForeignListener({\n projectRoot,\n databaseUrl,\n port: target.port,\n composeText,\n whoHoldsPort: options.whoHoldsPort,\n log\n });\n }\n return { action: \"already-running\", host: target.host, port: target.port };\n }\n\n if (composeText === null || !composeDeclaresDbService(composeText)) return { action: \"no-compose\" };\n\n const manualSteps =\n `${chalk.cyan(\"docker compose up -d db\")} then ${chalk.cyan(\"rebase db push\")}`;\n\n // Would starting that container answer *this* DSN? A `.env` naming a port\n // the compose file does not publish is the commonest way to get here, and\n // starting the container then told the reader the exact opposite of the\n // truth: `⚠ The database container did not begin listening on port 3139.\n // Run docker compose up -d db then rebase db push to see why.` The\n // container was up and healthy on 5436; the port in `.env` was what was\n // wrong. And a container nobody asked for was left running.\n const composePort = composeHostPort(composeText);\n if (composePort !== null && composePort !== target.port) {\n const hint =\n `DATABASE_URL points at ${target.host}:${target.port} and nothing listens there. `\n + `The compose file's db service publishes ${composePort}, so starting it would not `\n + `answer that URL — nothing was started. Either correct the port in .env, or point `\n + `DATABASE_URL at 127.0.0.1:${composePort}.`;\n log(\"\");\n log(` ${chalk.yellow(\"⚠\")} ${chalk.gray(hint)}`);\n return { action: \"wrong-port\", hint, port: target.port, composePort };\n }\n\n log(\"\");\n log(` ${chalk.gray(\"Database not running — starting it…\")}`);\n\n const startDatabase = options.startDatabase ?? (async (root: string) => {\n await execa(\"docker\", [\"compose\", \"up\", \"-d\", \"db\"], { cwd: root, stdio: \"pipe\" });\n });\n\n try {\n await startDatabase(projectRoot);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n // Two very different failures land here and the reader needs to know\n // which: Docker not being installed at all, and Docker being installed\n // but unable to start this container.\n if (/ENOENT|not found|command not found/i.test(message)) {\n const hint = `Docker was not found. Start a Postgres yourself, then run ${manualSteps}.`;\n log(` ${chalk.yellow(\"⚠\")} ${chalk.gray(hint)}`);\n return { action: \"no-docker\", hint };\n }\n const hint = `Could not start the database container. Run ${manualSteps} to see why.`;\n log(` ${chalk.yellow(\"⚠\")} ${chalk.gray(hint)}`);\n return { action: \"start-failed\", hint };\n }\n\n if (!(await waitForPort(target.host, target.port))) {\n const hint = `The database container did not begin listening on port ${target.port}. Run ${manualSteps} to see why.`;\n log(` ${chalk.yellow(\"⚠\")} ${chalk.gray(hint)}`);\n return { action: \"start-failed\", hint };\n }\n\n // Listening is not ready. Nothing is pushed into a database that has not\n // yet answered a query; see `waitForQueryable`.\n const readiness = await waitForQueryable(databaseUrl, { timeoutMs: options.readyTimeoutMs });\n if (!readiness.ready) {\n const seconds = Math.round((options.readyTimeoutMs ?? 60_000) / 1000);\n const hint = `The database container is listening on port ${target.port} but did not answer a query `\n + `within ${seconds}s (${readiness.error}). Run ${chalk.cyan(\"docker compose logs db\")} to see why, `\n + `then ${chalk.cyan(\"rebase db push\")}.`;\n log(` ${chalk.yellow(\"⚠\")} ${chalk.gray(hint)}`);\n return { action: \"start-failed\", hint };\n }\n\n log(` ${chalk.green(\"✓\")} ${chalk.gray(`Database ready on port ${target.port}`)}`);\n\n if (!hasCollections) return { action: \"started\", port: target.port, pushed: false };\n\n log(` ${chalk.gray(\"Pushing the schema…\")}`);\n try {\n await pushSchema();\n log(` ${chalk.green(\"✓\")} ${chalk.gray(\"Schema pushed\")}`);\n return { action: \"started\", port: target.port, pushed: true };\n } catch (error) {\n // The push runs with its output captured, so what it printed is shown\n // here, before the line that refers to it. Then the one thing the\n // reader needs next: the server is still starting, and against what.\n for (const line of pushFailureOutput(error)) log(` ${chalk.gray(line)}`);\n log(` ${chalk.yellow(\"⚠\")} ${chalk.gray(`Schema push failed. The database is running; fix the error above and run ${chalk.cyan(\"rebase db push\")}.`)}`);\n return { action: \"started\", port: target.port, pushed: false };\n }\n}\n"],"mappings":"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"}
@@ -1,10 +1,10 @@
1
1
  import { f as requireBackendDir, g as resolveTsx, h as resolvePluginCliScript, l as getActiveBackendPlugin, o as findEnvFile, p as requireProjectRoot, r as exitDependenciesNotInstalled } from "./project-Wp2zoyeN.js";
2
- import { n as parseCommandArgs, r as wantsHelp } from "./args-CcBphFdj.js";
2
+ import { n as parseCommandArgs, r as wantsHelp } from "./args-BX0YD14a.js";
3
3
  import { d as findBackendApp, g as resolveBackendPaths, p as loadManifest } from "./manifest-DRp0UVTj.js";
4
4
  import { n as summarisePortability, t as analyseFunctionsDirectory } from "./function-portability-BIvlvR9q.js";
5
5
  import { r as refuseMissingCollectionsPath, t as COLLECTIONS_FLAG_HELP } from "./path-flags-BFgG9V6y.js";
6
- import { DEV_DATABASE_KIND_ENV, devDatabaseKind, managedNotices, prepareDatabaseEnv } from "./prepare-C1iaiTC4.js";
7
- import { absolutizeLocalPathArgs, n as argsFromCommand, t as reportSpawnFailure } from "./db-Dkn9XhT-.js";
6
+ import { DEV_DATABASE_KIND_ENV, devDatabaseKind, managedNotices, prepareDatabaseEnv } from "./prepare-DdnU70z8.js";
7
+ import { absolutizeLocalPathArgs, n as argsFromCommand, t as reportSpawnFailure } from "./db-CRNpA5mH.js";
8
8
  import chalk from "chalk";
9
9
  import fs from "fs";
10
10
  import path from "path";
@@ -790,4 +790,4 @@ async function doctorCommand(rawArgs) {
790
790
  //#endregion
791
791
  export { doctorCommand };
792
792
 
793
- //# sourceMappingURL=doctor-Ds_lzc9p.js.map
793
+ //# sourceMappingURL=doctor-CATRdWm6.js.map