@rebasepro/cli 0.25.0-canary.ge06257e → 0.25.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (142) hide show
  1. package/bin/rebase.js +9 -5
  2. package/dist/{agent-setup-C83DXnlx.js → agent-setup-BYb2Tbdz.js} +2 -2
  3. package/dist/{agent-setup-C83DXnlx.js.map → agent-setup-BYb2Tbdz.js.map} +1 -1
  4. package/dist/{api-keys-Bc4FJKYp.js → api-keys-CSMbsmAn.js} +28 -22
  5. package/dist/api-keys-CSMbsmAn.js.map +1 -0
  6. package/dist/{apps-wG-ZTryA.js → apps-iBXNZhVN.js} +6 -7
  7. package/dist/{apps-wG-ZTryA.js.map → apps-iBXNZhVN.js.map} +1 -1
  8. package/dist/{atlas-dev-database-DeK3Sa4h.js → atlas-dev-database-BusbHP6n.js} +4 -4
  9. package/dist/{atlas-dev-database-DeK3Sa4h.js.map → atlas-dev-database-BusbHP6n.js.map} +1 -1
  10. package/dist/{auth-BL7bNQVu.js → auth-DRsTWPky.js} +77 -11
  11. package/dist/auth-DRsTWPky.js.map +1 -0
  12. package/dist/{branch-pointer-DTQNLFtb.js → branch-pointer-Bn_FjNld.js} +1 -1
  13. package/dist/{build-h_7Re-67.js → build-et7Rccxi.js} +6 -7
  14. package/dist/{build-h_7Re-67.js.map → build-et7Rccxi.js.map} +1 -1
  15. package/dist/{bundle-CqURVN-K.js → bundle-DlqkDYSe.js} +6 -21
  16. package/dist/bundle-DlqkDYSe.js.map +1 -0
  17. package/dist/{cloud-DVV_5GPG.js → cloud-B-ZIjHrA.js} +10 -11
  18. package/dist/{cloud-DVV_5GPG.js.map → cloud-B-ZIjHrA.js.map} +1 -1
  19. package/dist/commands/api-keys.d.ts +4 -1
  20. package/dist/commands/auth.d.ts +3 -2
  21. package/dist/commands/dev.d.ts +12 -0
  22. package/dist/commands/generate_sdk.d.ts +4 -2
  23. package/dist/commands/init.d.ts +20 -0
  24. package/dist/commands/start.d.ts +10 -0
  25. package/dist/commands/upgrade.d.ts +12 -0
  26. package/dist/connection-banner-DbLJzWf4.js +128 -0
  27. package/dist/connection-banner-DbLJzWf4.js.map +1 -0
  28. package/dist/{consent-ClRHmcxf.js → consent-BaB-eQKG.js} +2 -2
  29. package/dist/{consent-ClRHmcxf.js.map → consent-BaB-eQKG.js.map} +1 -1
  30. package/dist/{console-Dw3HLdUs.js → console-BlD9kjFW.js} +4 -4
  31. package/dist/{console-Dw3HLdUs.js.map → console-BlD9kjFW.js.map} +1 -1
  32. package/dist/{context-BM-oOMNX.js → context-DHiiiJ5C.js} +3 -4
  33. package/dist/context-DHiiiJ5C.js.map +1 -0
  34. package/dist/{daemon-CknkDjd6.js → daemon-DDMRbz4T.js} +160 -5
  35. package/dist/daemon-DDMRbz4T.js.map +1 -0
  36. package/dist/{daemon-entry-C6pxdily.js → daemon-entry-zOC1revO.js} +204 -5
  37. package/dist/daemon-entry-zOC1revO.js.map +1 -0
  38. package/dist/{db-Dkn9XhT-.js → db-9P6MnmdC.js} +237 -86
  39. package/dist/db-9P6MnmdC.js.map +1 -0
  40. package/dist/{derive-Dl0z2OWi.js → derive-MTdClnkc.js} +29 -14
  41. package/dist/{derive-Dl0z2OWi.js.map → derive-MTdClnkc.js.map} +1 -1
  42. package/dist/{dev-DAcZR4Pp.js → dev-BDN_OKXA.js} +183 -23
  43. package/dist/dev-BDN_OKXA.js.map +1 -0
  44. package/dist/dev-db/connection-banner.d.ts +47 -0
  45. package/dist/dev-db/daemon.d.ts +41 -0
  46. package/dist/dev-db/notification-proxy.d.ts +82 -1
  47. package/dist/dev-db/pull.d.ts +33 -5
  48. package/dist/dev-db/state.d.ts +9 -0
  49. package/dist/{dev-preflight-CorMg7Ub.js → dev-preflight-FT9GbJVj.js} +73 -4
  50. package/dist/dev-preflight-FT9GbJVj.js.map +1 -0
  51. package/dist/{dev-server-B1UYifZJ.js → dev-server-CIZwAfZa.js} +14 -3
  52. package/dist/dev-server-CIZwAfZa.js.map +1 -0
  53. package/dist/{doctor-Ds_lzc9p.js → doctor-D8YXz7df.js} +6 -6
  54. package/dist/doctor-D8YXz7df.js.map +1 -0
  55. package/dist/{eject-CJHkfnat.js → eject-izG2KjMo.js} +3 -4
  56. package/dist/{eject-CJHkfnat.js.map → eject-izG2KjMo.js.map} +1 -1
  57. package/dist/{fold-static-BC9CYicw.js → fold-static-CybSay_W.js} +3 -3
  58. package/dist/{fold-static-BC9CYicw.js.map → fold-static-CybSay_W.js.map} +1 -1
  59. package/dist/{generate_sdk-kfkZmG-d.js → generate_sdk-B4R65oUV.js} +22 -12
  60. package/dist/generate_sdk-B4R65oUV.js.map +1 -0
  61. package/dist/index.es.js +36 -37
  62. package/dist/index.es.js.map +1 -1
  63. package/dist/{init-CjoJoThg.js → init-DYaeppEN.js} +52 -63
  64. package/dist/init-DYaeppEN.js.map +1 -0
  65. package/dist/invoked-command-CG_rIa_o.js +55 -0
  66. package/dist/invoked-command-CG_rIa_o.js.map +1 -0
  67. package/dist/last-dev-database-B_75fxZC.js +2 -0
  68. package/dist/{manifest-DRp0UVTj.js → manifest-nvPdLlJY.js} +2 -2
  69. package/dist/{manifest-DRp0UVTj.js.map → manifest-nvPdLlJY.js.map} +1 -1
  70. package/dist/{normalize-imports-BHxL2d05.js → normalize-imports-BYw8ad87.js} +3 -3
  71. package/dist/{normalize-imports-BHxL2d05.js.map → normalize-imports-BYw8ad87.js.map} +1 -1
  72. package/dist/precompress.d.ts +0 -7
  73. package/dist/{prepare-C1iaiTC4.js → prepare-1rVNd2PF.js} +7 -6
  74. package/dist/prepare-1rVNd2PF.js.map +1 -0
  75. package/dist/project-g4K6kV1t.js +701 -0
  76. package/dist/project-g4K6kV1t.js.map +1 -0
  77. package/dist/{pull-COpNJFzY.js → pull-DDMpsZ5r.js} +143 -63
  78. package/dist/pull-DDMpsZ5r.js.map +1 -0
  79. package/dist/refusal-ReeeGP8a.js +26 -0
  80. package/dist/refusal-ReeeGP8a.js.map +1 -0
  81. package/dist/resolve-BQUAC3Wi.js +2 -0
  82. package/dist/resources/derive.d.ts +9 -0
  83. package/dist/{resources-CLm6g0br.js → resources-CovP8AZ-.js} +4 -5
  84. package/dist/{resources-CLm6g0br.js.map → resources-CovP8AZ-.js.map} +1 -1
  85. package/dist/{schema-BKdh6UoE.js → schema-nuxvvc50.js} +5 -6
  86. package/dist/{schema-BKdh6UoE.js.map → schema-nuxvvc50.js.map} +1 -1
  87. package/dist/{skills-DEhYiPeO.js → skills-DeUIsZcT.js} +3 -4
  88. package/dist/{skills-DEhYiPeO.js.map → skills-DeUIsZcT.js.map} +1 -1
  89. package/dist/{start-BsPJedPQ.js → start-RzZqDroW.js} +20 -7
  90. package/dist/start-RzZqDroW.js.map +1 -0
  91. package/dist/{state-CCB-FAQ9.js → state-CYdBvnwA.js} +3 -2
  92. package/dist/state-CYdBvnwA.js.map +1 -0
  93. package/dist/{status-D_B3tlDy.js → status-D9Zm88NZ.js} +11 -12
  94. package/dist/{status-D_B3tlDy.js.map → status-D9Zm88NZ.js.map} +1 -1
  95. package/dist/{storage-CtB8polS.js → storage-BB3umRfw.js} +3 -4
  96. package/dist/{storage-CtB8polS.js.map → storage-BB3umRfw.js.map} +1 -1
  97. package/dist/{telemetry-EMFRVRQi.js → telemetry-HfU5uAh1.js} +2 -2
  98. package/dist/{telemetry-EMFRVRQi.js.map → telemetry-HfU5uAh1.js.map} +1 -1
  99. package/dist/{telemetry-BYmPU-Ji.js → telemetry-TseNLk6X.js} +4 -4
  100. package/dist/{telemetry-BYmPU-Ji.js.map → telemetry-TseNLk6X.js.map} +1 -1
  101. package/dist/{upgrade-DbuKX9lv.js → upgrade-apiPKKE_.js} +29 -6
  102. package/dist/{upgrade-DbuKX9lv.js.map → upgrade-apiPKKE_.js.map} +1 -1
  103. package/dist/utils/args.d.ts +45 -3
  104. package/dist/utils/boot-retry.d.ts +41 -0
  105. package/dist/utils/dev-preflight.d.ts +34 -1
  106. package/dist/utils/dev-server.d.ts +11 -0
  107. package/dist/utils/project.d.ts +5 -1
  108. package/dist/utils/redact.d.ts +10 -0
  109. package/dist/utils/refusal.d.ts +18 -0
  110. package/dist/utils/unknown-command.d.ts +4 -0
  111. package/package.json +7 -7
  112. package/templates/overlays/baas/README.md +2 -1
  113. package/templates/resource-graphs/blank.json +25 -0
  114. package/templates/resource-graphs/blog.json +28 -0
  115. package/templates/resource-graphs/ecommerce.json +28 -0
  116. package/templates/resource-graphs/headless.json +22 -0
  117. package/templates/template/.env.example +11 -4
  118. package/templates/template/frontend/src/App.tsx +10 -9
  119. package/templates/template/frontend/vite.config.ts +7 -73
  120. package/dist/api-keys-Bc4FJKYp.js.map +0 -1
  121. package/dist/args-CcBphFdj.js +0 -237
  122. package/dist/args-CcBphFdj.js.map +0 -1
  123. package/dist/auth-BL7bNQVu.js.map +0 -1
  124. package/dist/bundle-CqURVN-K.js.map +0 -1
  125. package/dist/context-BM-oOMNX.js.map +0 -1
  126. package/dist/daemon-CknkDjd6.js.map +0 -1
  127. package/dist/daemon-entry-C6pxdily.js.map +0 -1
  128. package/dist/db-Dkn9XhT-.js.map +0 -1
  129. package/dist/dev-DAcZR4Pp.js.map +0 -1
  130. package/dist/dev-preflight-CorMg7Ub.js.map +0 -1
  131. package/dist/dev-server-B1UYifZJ.js.map +0 -1
  132. package/dist/doctor-Ds_lzc9p.js.map +0 -1
  133. package/dist/generate_sdk-kfkZmG-d.js.map +0 -1
  134. package/dist/init-CjoJoThg.js.map +0 -1
  135. package/dist/last-dev-database-BEBPfAMY.js +0 -2
  136. package/dist/prepare-C1iaiTC4.js.map +0 -1
  137. package/dist/project-Wp2zoyeN.js +0 -349
  138. package/dist/project-Wp2zoyeN.js.map +0 -1
  139. package/dist/pull-COpNJFzY.js.map +0 -1
  140. package/dist/resolve-b_p9bmdH.js +0 -2
  141. package/dist/start-BsPJedPQ.js.map +0 -1
  142. package/dist/state-CCB-FAQ9.js.map +0 -1
@@ -41,6 +41,8 @@ export declare function resolveBackendAddress(env: Record<string, string>, proje
41
41
  * Exits before any request is made: the refusal is the point.
42
42
  */
43
43
  export declare function requireBackendUrl(env: Record<string, string>, projectRoot: string): string;
44
+ /** Where a service key is read from: the name a scaffold writes, or the older one. */
45
+ type ServiceKeyVariable = "REBASE_SERVICE_KEY" | "SERVICE_KEY";
44
46
  /**
45
47
  * What to print when a request fails: the server's error code and message,
46
48
  * which is what says why — `SCOPE_EXCEEDS_CREATOR`, `INVALID_SCOPES`,
@@ -48,7 +50,7 @@ export declare function requireBackendUrl(env: Record<string, string>, projectRo
48
50
  *
49
51
  * Exported so its tests can read the lines without a server.
50
52
  */
51
- export declare function describeFailure(action: string, error: unknown, baseUrl: string): string[];
53
+ export declare function describeFailure(action: string, error: unknown, baseUrl: string, keyVariable?: ServiceKeyVariable): string[];
52
54
  export declare function apiKeysCommand(subcommand: string | undefined, rawArgs: string[]): Promise<void>;
53
55
  /**
54
56
  * The lines that describe one key: what it is, what it may do and whom it
@@ -153,3 +155,4 @@ export declare const SCOPES_LIST_FLAGS: {};
153
155
  * Exported so its tests can read the lines without a server.
154
156
  */
155
157
  export declare function describeScopes(scopes: readonly ScopeSummary[], held: readonly string[]): string[];
158
+ export {};
@@ -88,8 +88,9 @@ export declare function generatePassword(): string;
88
88
  * attacker's refresh token minting access tokens did not recover anything.
89
89
  *
90
90
  * It exits 0 only when a row was updated: a missing user and a thrown error are
91
- * both exit 1. `echoPassword` prints the new password, for a generated one.
91
+ * both exit 1. `echoPassword` prints the new password, for a generated one;
92
+ * `showStack` prints a failure's stack rather than its sentence (`--debug`).
92
93
  *
93
94
  * Exported so its tests can run it.
94
95
  */
95
- export declare function resetPasswordScript(echoPassword: boolean): string;
96
+ export declare function resetPasswordScript(echoPassword: boolean, showStack?: boolean): string;
@@ -86,6 +86,18 @@ export declare function devWatchIncludes(projectRoot: string, paths: {
86
86
  REBASE_DEV_FUNCTIONS?: string;
87
87
  REBASE_DEV_CRONS?: string;
88
88
  }): string[];
89
+ /**
90
+ * Call `onAppear` once, when `target` exists, and return what stops waiting.
91
+ *
92
+ * Watches the nearest directory above `target` that exists (no higher than
93
+ * `root`), and follows the path down as each part of it appears — `fs.watch`
94
+ * cannot watch a directory that is not there yet. A headless project has no
95
+ * `config/collections` until `rebase schema introspect` writes one, and dev
96
+ * watched it only when it was there at start: the backend kept serving the
97
+ * API it derived from the database, and edits to the new files did nothing,
98
+ * until dev was restarted by hand.
99
+ */
100
+ export declare function whenDirectoryAppears(target: string, root: string, onAppear: () => void): () => void;
89
101
  /**
90
102
  * The file dev writes to restart the backend, or `null` when tsx would not see it.
91
103
  *
@@ -57,8 +57,10 @@ export declare function registrationDir(collectionsDir: string): string;
57
57
  * the one it actually got to `.rebase/state.json`. Printing a hardcoded
58
58
  * `localhost:3001` sent people to a port nothing was listening on — or, with
59
59
  * several projects on one machine, to a different project's backend. Prefer the
60
- * port this project last ran on; fall back to the literal only when the project
61
- * has never been started.
60
+ * port `rebase dev` says it is starting the backend on (`DEV_BACKEND_URL_ENV`:
61
+ * it regenerates the SDK before the backend binds), then the port this project
62
+ * last ran on; fall back to the literal only when the project has never been
63
+ * started.
62
64
  */
63
65
  export declare function resolveExampleBaseUrl(cwd: string): string;
64
66
  /** Whether a slug can be written as `rebase.data.<slug>` rather than a lookup. */
@@ -247,6 +247,26 @@ export declare function initGitRepository(targetDirectory: string): Promise<bool
247
247
  * copied into a project that chose another preset.
248
248
  */
249
249
  export declare function shippedSchemaPath(preset: TemplatePreset): string;
250
+ /** A scaffold's shape, for the resource graph it ships with: a preset, or headless. */
251
+ export type ScaffoldShape = TemplatePreset | "headless";
252
+ /**
253
+ * Where each scaffold shape's `rebase.resources.json` ships, beside the template.
254
+ *
255
+ * `rebase resources --check` is the CI gate for that file, and a fresh
256
+ * scaffold failed it before anything had changed: `config/resources.ts`
257
+ * declares `main = database()`, `backend/functions/hello.ts` is a function,
258
+ * and nothing had written the graph — `rebase build` does, on its first run.
259
+ * So `init` writes it, and a fresh project starts consistent with its own
260
+ * gate, with the file in its first commit.
261
+ *
262
+ * It cannot be derived here for the reason the schema cannot (see
263
+ * `shippedSchemaPath`): deriving evaluates the collection files, which import
264
+ * `@rebasepro/cms-types`, and the project has none until it is installed. So
265
+ * each shape's graph is derived in this repository and shipped, and
266
+ * `init-resource-graph.test.ts` fails the moment one differs from what
267
+ * `rebase resources --write` writes for that shape.
268
+ */
269
+ export declare function shippedResourceGraphPath(shape: ScaffoldShape): string;
250
270
  /**
251
271
  * Whether `port` is free — on the wildcard address *and* on both loopback addresses.
252
272
  *
@@ -2,6 +2,16 @@ import { type PMCommands } from "../utils/package-manager.js";
2
2
  import type { RebaseBundleStatic } from "@rebasepro/types";
3
3
  import type { DevDatabase } from "../dev-db/resolve.js";
4
4
  import type { DaemonState } from "../dev-db/state.js";
5
+ /**
6
+ * The help's line that runs `start` on the database `rebase dev` uses, spelled
7
+ * as the reader can type it.
8
+ *
9
+ * In a project, the project's package manager: a scaffold has `rebase` in
10
+ * `node_modules/.bin`, not on PATH, so a bare `rebase` is "command not found" —
11
+ * `pnpm exec rebase …`, as `missingDatabaseUrl` and the CLI's other hints say
12
+ * it. Outside one, the way this CLI was started (`invokedRebaseCommand`).
13
+ */
14
+ export declare function startHelpRunLine(projectRoot?: string | null): string;
5
15
  /** The NODE_ENV the runtime will see, and where it comes from. */
6
16
  export interface StartNodeEnv {
7
17
  value: string | undefined;
@@ -49,3 +49,15 @@ export interface UpgradeIo {
49
49
  */
50
50
  export declare function npmFailure(err: unknown): string;
51
51
  export declare function upgradeCommand(rawArgs: string[], io?: UpgradeIo): Promise<void>;
52
+ /**
53
+ * What to say about this project's managed database when an older CLI started
54
+ * it, or nothing.
55
+ *
56
+ * The daemon outlives every command and the database proxy runs inside it, so
57
+ * moving the pins does not move the proxy: the old one, with whatever the
58
+ * upgrade fixed in it, kept serving until `rebase db stop` or thirty idle
59
+ * minutes — and under a running `rebase dev` it never idles. The upgraded CLI
60
+ * restarts it when nothing is connected (`daemonOutdated`); this says so, and
61
+ * what to do while something is.
62
+ */
63
+ export declare function runningDatabaseNotice(projectRoot: string, target: string): Promise<string[]>;
@@ -0,0 +1,128 @@
1
+ import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
2
+ //#region src/dev-db/connection-banner.ts
3
+ var connection_banner_exports = /* @__PURE__ */ __exportAll({
4
+ banner: () => banner,
5
+ connectionErrorText: () => connectionErrorText,
6
+ connectionFailureBanner: () => connectionFailureBanner,
7
+ connectionRefusedFixes: () => connectionRefusedFixes,
8
+ hostOf: () => hostOf
9
+ });
10
+ var RULE = "━".repeat(60);
11
+ /** The banner, as lines: a title, what the driver or tool said, then the body. */
12
+ function banner(title, said, body) {
13
+ return [
14
+ "",
15
+ RULE,
16
+ ` ❌ ${title}`,
17
+ RULE,
18
+ "",
19
+ ...said ? [` ${said}`, ""] : [],
20
+ ...body.map((line) => ` ${line}`),
21
+ "",
22
+ RULE
23
+ ];
24
+ }
25
+ /** `host:port` of a connection string, for a message: never the string, which carries the password. */
26
+ function hostOf(connectionString) {
27
+ try {
28
+ const url = new URL(connectionString);
29
+ return `${url.hostname || "localhost"}:${url.port || "5432"}`;
30
+ } catch {
31
+ return "(unparseable connection string)";
32
+ }
33
+ }
34
+ /**
35
+ * A connection error's own sentence. A refused connection on a dual-stack
36
+ * host is an `AggregateError` with an empty message, and its sentence —
37
+ * `connect ECONNREFUSED 127.0.0.1:5432` — is in the attempts it holds.
38
+ */
39
+ function connectionErrorText(error) {
40
+ if (error instanceof AggregateError && error.errors.length > 0 && !error.message) return connectionErrorText(error.errors[0]);
41
+ return error instanceof Error && error.message ? error.message : String(error);
42
+ }
43
+ /** The `code` a `pg` or `net` error carries, looking inside an `AggregateError`'s attempts. */
44
+ function codeOf(error) {
45
+ if (error instanceof AggregateError && error.errors.length > 0) for (const inner of error.errors) {
46
+ const code = codeOf(inner);
47
+ if (code) return code;
48
+ }
49
+ if (error && typeof error === "object" && "code" in error && typeof error.code === "string") return error.code;
50
+ }
51
+ /** A server that is not there, or not reachable from here. */
52
+ var UNREACHABLE = /* @__PURE__ */ new Set([
53
+ "ECONNREFUSED",
54
+ "ENOTFOUND",
55
+ "EAI_AGAIN",
56
+ "ETIMEDOUT",
57
+ "EHOSTUNREACH",
58
+ "ENETUNREACH",
59
+ "ECONNRESET"
60
+ ]);
61
+ /**
62
+ * What to print when opening `url` failed with `error`: the banner, in the
63
+ * case it is — the server not answering, credentials refused, a database
64
+ * closed to new connections, or a database of that name not there — or `null`
65
+ * when it is none of those four.
66
+ *
67
+ * `consequence` is the line saying what did not happen ("The password was not
68
+ * reset."); `fixes` is what to do about a server that does not answer, which
69
+ * depends on what named the database.
70
+ */
71
+ function connectionFailureBanner(url, error, options) {
72
+ const code = codeOf(error);
73
+ const text = connectionErrorText(error);
74
+ const said = `The driver said: ${text}${code && !text.includes(code) ? ` (${code})` : ""}`;
75
+ const host = hostOf(url);
76
+ if (code && UNREACHABLE.has(code) || /timeout expired|Connection terminated due to connection timeout/i.test(text)) return banner(`Cannot connect to PostgreSQL at ${host}`, said, [
77
+ "The database server is not running or is not accepting connections.",
78
+ options.consequence,
79
+ "",
80
+ ...options.fixes.map((fix) => ` • ${fix}`)
81
+ ]);
82
+ if (code === "28P01" || code === "28000") {
83
+ let user = "unknown";
84
+ try {
85
+ user = decodeURIComponent(new URL(url).username) || "unknown";
86
+ } catch {}
87
+ return banner(`Authentication failed for user "${user}" at ${host}`, said, ["PostgreSQL rejected the credentials in the connection string.", options.consequence]);
88
+ }
89
+ const closed = /database "(.+)" is not currently accepting connections/.exec(text)?.[1];
90
+ if (closed) return banner(`Database "${closed}" at ${host} is closed to new connections`, said, [
91
+ "It is set to refuse every new connection (ALLOW_CONNECTIONS false).",
92
+ "`rebase db branch create --force` closes its source for the copy, and one",
93
+ "stopped before it finished (a closed terminal, a kill) leaves it closed.",
94
+ options.consequence,
95
+ "",
96
+ "Nothing in it is lost. Open it again with:",
97
+ " rebase db branch list (every rebase db branch command opens it first)",
98
+ "or run this, connected to the server's postgres database:",
99
+ ` ALTER DATABASE "${closed.replace(/"/g, "\"\"")}" ALLOW_CONNECTIONS true;`
100
+ ]);
101
+ if (code === "3D000") {
102
+ let database = "(default)";
103
+ try {
104
+ database = decodeURIComponent(new URL(url).pathname.replace(/^\//, "")) || database;
105
+ } catch {}
106
+ return banner(`Database "${database}" does not exist at ${host}`, said, ["The server answered, and has no database of that name.", options.consequence]);
107
+ }
108
+ return null;
109
+ }
110
+ /**
111
+ * What to do about a database that does not answer, for what named it — the
112
+ * list the driver's banner gives for the same `REBASE_DATABASE_SOURCE`.
113
+ */
114
+ function connectionRefusedFixes(source) {
115
+ switch (source) {
116
+ case "flag": return ["Check the URL passed with --database-url, and that its server is running"];
117
+ case "environment": return ["Check DATABASE_URL in this shell's environment, and that its server is running"];
118
+ case "env-file": return ["Check DATABASE_URL in .env, and that its server is running"];
119
+ case "branch": return ["The branch's database is on the server DATABASE_URL names: start that server", "Or leave the branch: rebase db branch switch --off"];
120
+ case "docker":
121
+ case "last-dev": return ["docker compose up -d db (the project's Postgres in Docker)"];
122
+ case "managed": return ["Restart the managed development database: rebase db stop, then run the command again"];
123
+ }
124
+ }
125
+ //#endregion
126
+ export { hostOf as i, connectionErrorText as n, connection_banner_exports as r, banner as t };
127
+
128
+ //# sourceMappingURL=connection-banner-DbLJzWf4.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connection-banner-DbLJzWf4.js","names":[],"sources":["../src/dev-db/connection-banner.ts"],"sourcesContent":["/**\n * The boxed banner the database commands print when a connection fails.\n *\n * ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n * ❌ Cannot connect to PostgreSQL at 127.0.0.1:5508\n * ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n *\n * The driver said: connect ECONNREFUSED 127.0.0.1:5508\n * …\n *\n * The driver prints it for the commands it runs (`cli-errors.ts` in\n * `@rebasepro/server-postgres`, which its entry does not export); this is the\n * same box for the commands the CLI answers in its own process — `db pull`\n * and `auth reset-password`'s database fallback — as plain lines for the\n * caller to print. Never with the connection string in it: it carries the\n * password.\n */\nimport type { DevDatabaseSource } from \"./resolve\";\n\nconst RULE = \"━\".repeat(60);\n\n/** The banner, as lines: a title, what the driver or tool said, then the body. */\nexport function banner(title: string, said: string | null, body: readonly string[]): string[] {\n return [\"\", RULE, ` ❌ ${title}`, RULE, \"\", ...(said ? [` ${said}`, \"\"] : []), ...body.map(line => ` ${line}`), \"\", RULE];\n}\n\n/** `host:port` of a connection string, for a message: never the string, which carries the password. */\nexport function hostOf(connectionString: string): string {\n try {\n const url = new URL(connectionString);\n\n return `${url.hostname || \"localhost\"}:${url.port || \"5432\"}`;\n } catch {\n return \"(unparseable connection string)\";\n }\n}\n\n/**\n * A connection error's own sentence. A refused connection on a dual-stack\n * host is an `AggregateError` with an empty message, and its sentence —\n * `connect ECONNREFUSED 127.0.0.1:5432` — is in the attempts it holds.\n */\nexport function connectionErrorText(error: unknown): string {\n if (error instanceof AggregateError && error.errors.length > 0 && !error.message) {\n return connectionErrorText(error.errors[0]);\n }\n\n return error instanceof Error && error.message ? error.message : String(error);\n}\n\n/** The `code` a `pg` or `net` error carries, looking inside an `AggregateError`'s attempts. */\nfunction codeOf(error: unknown): string | undefined {\n if (error instanceof AggregateError && error.errors.length > 0) {\n for (const inner of error.errors) {\n const code = codeOf(inner);\n if (code) return code;\n }\n }\n if (error && typeof error === \"object\" && \"code\" in error && typeof error.code === \"string\") return error.code;\n\n return undefined;\n}\n\n/** A server that is not there, or not reachable from here. */\nconst UNREACHABLE = new Set([\"ECONNREFUSED\", \"ENOTFOUND\", \"EAI_AGAIN\", \"ETIMEDOUT\", \"EHOSTUNREACH\", \"ENETUNREACH\", \"ECONNRESET\"]);\n\n/**\n * What to print when opening `url` failed with `error`: the banner, in the\n * case it is — the server not answering, credentials refused, a database\n * closed to new connections, or a database of that name not there — or `null`\n * when it is none of those four.\n *\n * `consequence` is the line saying what did not happen (\"The password was not\n * reset.\"); `fixes` is what to do about a server that does not answer, which\n * depends on what named the database.\n */\nexport function connectionFailureBanner(\n url: string,\n error: unknown,\n options: { consequence: string; fixes: readonly string[] }\n): string[] | null {\n const code = codeOf(error);\n const text = connectionErrorText(error);\n const said = `The driver said: ${text}${code && !text.includes(code) ? ` (${code})` : \"\"}`;\n const host = hostOf(url);\n\n if ((code && UNREACHABLE.has(code)) || /timeout expired|Connection terminated due to connection timeout/i.test(text)) {\n return banner(`Cannot connect to PostgreSQL at ${host}`, said, [\n \"The database server is not running or is not accepting connections.\",\n options.consequence,\n \"\",\n ...options.fixes.map(fix => ` • ${fix}`)\n ]);\n }\n if (code === \"28P01\" || code === \"28000\") {\n let user = \"unknown\";\n try {\n user = decodeURIComponent(new URL(url).username) || \"unknown\";\n } catch { /* named as unknown */ }\n return banner(`Authentication failed for user \"${user}\" at ${host}`, said, [\n \"PostgreSQL rejected the credentials in the connection string.\",\n options.consequence\n ]);\n }\n // 55000 covers more than this, so the sentence is matched — as the driver's banner does.\n const closed = /database \"(.+)\" is not currently accepting connections/.exec(text)?.[1];\n if (closed) {\n return banner(`Database \"${closed}\" at ${host} is closed to new connections`, said, [\n \"It is set to refuse every new connection (ALLOW_CONNECTIONS false).\",\n \"`rebase db branch create --force` closes its source for the copy, and one\",\n \"stopped before it finished (a closed terminal, a kill) leaves it closed.\",\n options.consequence,\n \"\",\n \"Nothing in it is lost. Open it again with:\",\n \" rebase db branch list (every rebase db branch command opens it first)\",\n \"or run this, connected to the server's postgres database:\",\n ` ALTER DATABASE \"${closed.replace(/\"/g, '\"\"')}\" ALLOW_CONNECTIONS true;`\n ]);\n }\n if (code === \"3D000\") {\n let database = \"(default)\";\n try {\n database = decodeURIComponent(new URL(url).pathname.replace(/^\\//, \"\")) || database;\n } catch { /* named as the default */ }\n return banner(`Database \"${database}\" does not exist at ${host}`, said, [\n \"The server answered, and has no database of that name.\",\n options.consequence\n ]);\n }\n\n return null;\n}\n\n/**\n * What to do about a database that does not answer, for what named it — the\n * list the driver's banner gives for the same `REBASE_DATABASE_SOURCE`.\n */\nexport function connectionRefusedFixes(source: DevDatabaseSource): string[] {\n switch (source) {\n case \"flag\": return [\"Check the URL passed with --database-url, and that its server is running\"];\n case \"environment\": return [\"Check DATABASE_URL in this shell's environment, and that its server is running\"];\n case \"env-file\": return [\"Check DATABASE_URL in .env, and that its server is running\"];\n case \"branch\": return [\n \"The branch's database is on the server DATABASE_URL names: start that server\",\n \"Or leave the branch: rebase db branch switch --off\"\n ];\n case \"docker\":\n case \"last-dev\": return [\"docker compose up -d db (the project's Postgres in Docker)\"];\n case \"managed\": return [\"Restart the managed development database: rebase db stop, then run the command again\"];\n }\n}\n"],"mappings":";;;;;;;;;AAmBA,IAAM,OAAO,IAAI,OAAO,EAAE;;AAG1B,SAAgB,OAAO,OAAe,MAAqB,MAAmC;CAC1F,OAAO;EAAC;EAAI;EAAM,QAAQ;EAAS;EAAM;EAAI,GAAI,OAAO,CAAC,KAAK,QAAQ,EAAE,IAAI,CAAC;EAAI,GAAG,KAAK,KAAI,SAAQ,KAAK,MAAM;EAAG;EAAI;CAAI;AAC/H;;AAGA,SAAgB,OAAO,kBAAkC;CACrD,IAAI;EACA,MAAM,MAAM,IAAI,IAAI,gBAAgB;EAEpC,OAAO,GAAG,IAAI,YAAY,YAAY,GAAG,IAAI,QAAQ;CACzD,QAAQ;EACJ,OAAO;CACX;AACJ;;;;;;AAOA,SAAgB,oBAAoB,OAAwB;CACxD,IAAI,iBAAiB,kBAAkB,MAAM,OAAO,SAAS,KAAK,CAAC,MAAM,SACrE,OAAO,oBAAoB,MAAM,OAAO,EAAE;CAG9C,OAAO,iBAAiB,SAAS,MAAM,UAAU,MAAM,UAAU,OAAO,KAAK;AACjF;;AAGA,SAAS,OAAO,OAAoC;CAChD,IAAI,iBAAiB,kBAAkB,MAAM,OAAO,SAAS,GACzD,KAAK,MAAM,SAAS,MAAM,QAAQ;EAC9B,MAAM,OAAO,OAAO,KAAK;EACzB,IAAI,MAAM,OAAO;CACrB;CAEJ,IAAI,SAAS,OAAO,UAAU,YAAY,UAAU,SAAS,OAAO,MAAM,SAAS,UAAU,OAAO,MAAM;AAG9G;;AAGA,IAAM,8BAAc,IAAI,IAAI;CAAC;CAAgB;CAAa;CAAa;CAAa;CAAgB;CAAe;AAAY,CAAC;;;;;;;;;;;AAYhI,SAAgB,wBACZ,KACA,OACA,SACe;CACf,MAAM,OAAO,OAAO,KAAK;CACzB,MAAM,OAAO,oBAAoB,KAAK;CACtC,MAAM,OAAO,oBAAoB,OAAO,QAAQ,CAAC,KAAK,SAAS,IAAI,IAAI,KAAK,KAAK,KAAK;CACtF,MAAM,OAAO,OAAO,GAAG;CAEvB,IAAK,QAAQ,YAAY,IAAI,IAAI,KAAM,mEAAmE,KAAK,IAAI,GAC/G,OAAO,OAAO,mCAAmC,QAAQ,MAAM;EAC3D;EACA,QAAQ;EACR;EACA,GAAG,QAAQ,MAAM,KAAI,QAAO,OAAO,KAAK;CAC5C,CAAC;CAEL,IAAI,SAAS,WAAW,SAAS,SAAS;EACtC,IAAI,OAAO;EACX,IAAI;GACA,OAAO,mBAAmB,IAAI,IAAI,GAAG,CAAC,CAAC,QAAQ,KAAK;EACxD,QAAQ,CAAyB;EACjC,OAAO,OAAO,mCAAmC,KAAK,OAAO,QAAQ,MAAM,CACvE,iEACA,QAAQ,WACZ,CAAC;CACL;CAEA,MAAM,SAAS,yDAAyD,KAAK,IAAI,CAAC,GAAG;CACrF,IAAI,QACA,OAAO,OAAO,aAAa,OAAO,OAAO,KAAK,gCAAgC,MAAM;EAChF;EACA;EACA;EACA,QAAQ;EACR;EACA;EACA;EACA;EACA,qBAAqB,OAAO,QAAQ,MAAM,MAAI,EAAE;CACpD,CAAC;CAEL,IAAI,SAAS,SAAS;EAClB,IAAI,WAAW;EACf,IAAI;GACA,WAAW,mBAAmB,IAAI,IAAI,GAAG,CAAC,CAAC,SAAS,QAAQ,OAAO,EAAE,CAAC,KAAK;EAC/E,QAAQ,CAA6B;EACrC,OAAO,OAAO,aAAa,SAAS,sBAAsB,QAAQ,MAAM,CACpE,0DACA,QAAQ,WACZ,CAAC;CACL;CAEA,OAAO;AACX;;;;;AAMA,SAAgB,uBAAuB,QAAqC;CACxE,QAAQ,QAAR;EACI,KAAK,QAAQ,OAAO,CAAC,0EAA0E;EAC/F,KAAK,eAAe,OAAO,CAAC,gFAAgF;EAC5G,KAAK,YAAY,OAAO,CAAC,4DAA4D;EACrF,KAAK,UAAU,OAAO,CAClB,gFACA,oDACJ;EACA,KAAK;EACL,KAAK,YAAY,OAAO,CAAC,qEAAqE;EAC9F,KAAK,WAAW,OAAO,CAAC,sFAAsF;CAClH;AACJ"}
@@ -1,4 +1,4 @@
1
- import { a as setConsent, c as buildEvent, f as readConfig, o as suppressionReason } from "./telemetry-EMFRVRQi.js";
1
+ import { a as setConsent, c as buildEvent, f as readConfig, o as suppressionReason } from "./telemetry-HfU5uAh1.js";
2
2
  import chalk from "chalk";
3
3
  import inquirer from "inquirer";
4
4
  //#region src/telemetry/consent.ts
@@ -73,4 +73,4 @@ function describeState(env = process.env) {
73
73
  //#endregion
74
74
  export { promptForConsent as n, renderPreview as r, describeState as t };
75
75
 
76
- //# sourceMappingURL=consent-ClRHmcxf.js.map
76
+ //# sourceMappingURL=consent-BaB-eQKG.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"consent-ClRHmcxf.js","names":[],"sources":["../src/telemetry/consent.ts"],"sourcesContent":["import chalk from \"chalk\";\nimport inquirer from \"inquirer\";\n\nimport { buildEvent, TELEMETRY_SCHEMA_VERSION, TelemetryEventName } from \"./payload\";\nimport { readConfig } from \"./identity\";\nimport { setConsent, suppressionReason } from \"./index\";\n\n/**\n * Asking, and what the question looks like.\n *\n * ## Why the prompt comes *after* the work\n *\n * The first `rebase init` on a machine is the event most worth having, and it\n * is the one where no consent exists yet. Asking before scaffolding puts a\n * privacy negotiation in front of someone who has not yet seen the tool do\n * anything — the worst possible moment, and a reliable way to get a reflexive\n * no.\n *\n * So the question is asked once the project exists and the user has seen it\n * work. The event's data is still in memory at that point, so nothing is lost\n * by waiting, and — this is the part that matters — **nothing has been\n * transmitted or written**. Declining leaves no id, no file, no record.\n *\n * ## Why the payload is shown rather than described\n *\n * \"Anonymous usage data\" is a phrase that has been used to mean almost\n * anything. Printing the exact JSON costs four lines of output and replaces a\n * claim the user has to take on faith with something they can read. It is also\n * the same builder the sender uses, so it cannot drift into a comfortable\n * fiction.\n */\n\n/** Where the question is being asked from, which decides how often it may be. */\nexport interface PromptOptions {\n /**\n * Ask again even though this machine already declined.\n *\n * Set by `rebase init`, and only there. Scaffolding a project is a\n * deliberate, occasional act with a natural pause in it — not something\n * anyone does in a loop — so asking each time is an offer rather than\n * nagging, and it reaches the person whose first answer was a reflex before\n * they had seen the tool do anything.\n *\n * It does **not** re-ask someone who accepted: they are already sharing, so\n * the only thing another prompt could do is talk them out of it.\n *\n * Nothing else changes. `DO_NOT_TRACK`, `REBASE_TELEMETRY_DISABLED`, `CI`\n * and a project's `\"telemetry\": false` are answers, not questions, and they\n * still suppress the prompt entirely — a re-ask that could override a\n * committed repository policy would be exactly the consent-by-proxy the\n * policy exists to prevent.\n */\n reAskDeclined?: boolean;\n}\n\n/** True when we may ask: nothing forbids it, and there is a question to ask. */\nexport function shouldPrompt(\n env: NodeJS.ProcessEnv = process.env,\n { reAskDeclined = false }: PromptOptions = {}\n): boolean {\n if (!process.stdin.isTTY) return false;\n\n const reason = suppressionReason(env);\n // `not_asked` is the only reason that is a question rather than an answer.\n // A user who set DO_NOT_TRACK, or is on CI, has already told us — and so\n // has one who is already sharing (`null`), whom we never re-ask.\n if (reason === \"not_asked\") return true;\n return reAskDeclined && reason === \"declined\";\n}\n\nexport function renderPreview(event: TelemetryEventName, properties: Record<string, unknown>): string {\n const preview = buildEvent(event, properties, {\n machineId: \"<random uuid, generated only if you say yes>\",\n projectId: \"<random uuid, per checkout>\"\n });\n return JSON.stringify(preview, null, 2);\n}\n\n/**\n * Ask, record the answer, and report it.\n *\n * Never throws and never blocks a non-interactive run: `rebase init --yes` in\n * CI must behave exactly as it does today, which means not asking and not\n * sending.\n */\nexport async function promptForConsent(options: PromptOptions = {}): Promise<boolean> {\n if (!shouldPrompt(process.env, options)) return false;\n\n // Only true when this is a second (or later) asking, which the closing line\n // needs to know: \"you will not be asked again\" is false at `init`.\n const askedBefore = suppressionReason(process.env) === \"declined\";\n\n try {\n console.log(\"\");\n console.log(chalk.bold(\"Help improve Rebase?\"));\n console.log(\"\");\n console.log(chalk.gray(\" Rebase is self-hosted, so we only learn what works if you tell us.\"));\n console.log(chalk.gray(\" Anonymous: random ids, the CLI version, your OS, and which template and\"));\n console.log(chalk.gray(\" package manager you used. Never project names, paths, schemas, URLs or\"));\n console.log(chalk.gray(\" error messages.\"));\n console.log(\"\");\n console.log(chalk.gray(` Print the exact payload with ${chalk.cyan(\"rebase telemetry show\")}; change your mind`));\n console.log(chalk.gray(` any time with ${chalk.cyan(\"rebase telemetry disable\")}.`));\n if (askedBefore) {\n console.log(\"\");\n console.log(chalk.gray(\" You declined before; that stands unless you change it here.\"));\n }\n console.log(\"\");\n\n const { accepted } = await inquirer.prompt([\n {\n type: \"confirm\",\n name: \"accepted\",\n message: \"Share anonymous usage data?\",\n // Defaults to no. Telemetry is off until somebody says yes, and\n // a bare Enter is the commonest answer to any prompt — so a\n // pre-selected yes would make pressing through `init` the\n // consent. `y` is one keystroke, and `rebase telemetry enable`\n // is the same answer later.\n default: false\n }\n ] as Parameters<typeof inquirer.prompt>[0]) as { accepted: boolean };\n\n setConsent(Boolean(accepted));\n\n console.log(\n accepted\n ? chalk.green(\" Thank you — sharing enabled.\")\n : chalk.gray(\n \" Nothing will be sent.\"\n + (options.reAskDeclined\n ? \" You will be asked again the next time you scaffold a project.\"\n : \" You will not be asked again.\")\n )\n );\n console.log(\"\");\n return Boolean(accepted);\n } catch {\n // A prompt that fails (piped stdin, a terminal that went away) is a\n // decline, and specifically a decline we do not persist — the user\n // never actually saw the question, so they should still get to answer\n // it another day.\n return false;\n }\n}\n\n/** Human-readable current state, for `rebase telemetry status`. */\nexport function describeState(env: NodeJS.ProcessEnv = process.env): string {\n const reason = suppressionReason(env);\n const config = readConfig();\n switch (reason) {\n case null:\n return `${chalk.green(\"enabled\")} — schema v${TELEMETRY_SCHEMA_VERSION}, machine id ${chalk.gray(config.machineId ?? \"unset\")}`;\n case \"not_asked\":\n return `${chalk.yellow(\"not configured\")} — nothing has been sent, and you have not been asked yet`;\n case \"declined\":\n return `${chalk.gray(\"disabled\")} — you declined${config.decidedAt ? ` on ${config.decidedAt.slice(0, 10)}` : \"\"}`;\n case \"do_not_track\":\n return `${chalk.gray(\"disabled\")} — the ${chalk.cyan(\"DO_NOT_TRACK\")} environment variable is set`;\n case \"rebase_telemetry_disabled\":\n return `${chalk.gray(\"disabled\")} — the ${chalk.cyan(\"REBASE_TELEMETRY_DISABLED\")} environment variable is set`;\n case \"ci\":\n return `${chalk.gray(\"disabled\")} — this looks like CI (${chalk.cyan(\"CI\")} is set), which is never counted`;\n case \"project_opt_out\":\n return `${chalk.gray(\"disabled\")} — this project's ${chalk.cyan(\"rebase.json\")} sets ${chalk.cyan('\"telemetry\": false')}`;\n }\n}\n"],"mappings":";;;;;AAwDA,SAAgB,aACZ,MAAyB,QAAQ,KACjC,EAAE,gBAAgB,UAAyB,CAAC,GACrC;CACP,IAAI,CAAC,QAAQ,MAAM,OAAO,OAAO;CAEjC,MAAM,SAAS,kBAAkB,GAAG;CAIpC,IAAI,WAAW,aAAa,OAAO;CACnC,OAAO,iBAAiB,WAAW;AACvC;AAEA,SAAgB,cAAc,OAA2B,YAA6C;CAClG,MAAM,UAAU,WAAW,OAAO,YAAY;EAC1C,WAAW;EACX,WAAW;CACf,CAAC;CACD,OAAO,KAAK,UAAU,SAAS,MAAM,CAAC;AAC1C;;;;;;;;AASA,eAAsB,iBAAiB,UAAyB,CAAC,GAAqB;CAClF,IAAI,CAAC,aAAa,QAAQ,KAAK,OAAO,GAAG,OAAO;CAIhD,MAAM,cAAc,kBAAkB,QAAQ,GAAG,MAAM;CAEvD,IAAI;EACA,QAAQ,IAAI,EAAE;EACd,QAAQ,IAAI,MAAM,KAAK,sBAAsB,CAAC;EAC9C,QAAQ,IAAI,EAAE;EACd,QAAQ,IAAI,MAAM,KAAK,sEAAsE,CAAC;EAC9F,QAAQ,IAAI,MAAM,KAAK,2EAA2E,CAAC;EACnG,QAAQ,IAAI,MAAM,KAAK,0EAA0E,CAAC;EAClG,QAAQ,IAAI,MAAM,KAAK,mBAAmB,CAAC;EAC3C,QAAQ,IAAI,EAAE;EACd,QAAQ,IAAI,MAAM,KAAK,kCAAkC,MAAM,KAAK,uBAAuB,EAAE,mBAAmB,CAAC;EACjH,QAAQ,IAAI,MAAM,KAAK,mBAAmB,MAAM,KAAK,0BAA0B,EAAE,EAAE,CAAC;EACpF,IAAI,aAAa;GACb,QAAQ,IAAI,EAAE;GACd,QAAQ,IAAI,MAAM,KAAK,+DAA+D,CAAC;EAC3F;EACA,QAAQ,IAAI,EAAE;EAEd,MAAM,EAAE,aAAa,MAAM,SAAS,OAAO,CACvC;GACI,MAAM;GACN,MAAM;GACN,SAAS;GAMT,SAAS;EACb,CACJ,CAA0C;EAE1C,WAAW,QAAQ,QAAQ,CAAC;EAE5B,QAAQ,IACJ,WACM,MAAM,MAAM,gCAAgC,IAC5C,MAAM,KACJ,6BACG,QAAQ,gBACL,mEACA,gCACV,CACR;EACA,QAAQ,IAAI,EAAE;EACd,OAAO,QAAQ,QAAQ;CAC3B,QAAQ;EAKJ,OAAO;CACX;AACJ;;AAGA,SAAgB,cAAc,MAAyB,QAAQ,KAAa;CACxE,MAAM,SAAS,kBAAkB,GAAG;CACpC,MAAM,SAAS,WAAW;CAC1B,QAAQ,QAAR;EACI,KAAK,MACD,OAAO,GAAG,MAAM,MAAM,SAAS,EAAE,2BAAqD,MAAM,KAAK,OAAO,aAAa,OAAO;EAChI,KAAK,aACD,OAAO,GAAG,MAAM,OAAO,gBAAgB,EAAE;EAC7C,KAAK,YACD,OAAO,GAAG,MAAM,KAAK,UAAU,EAAE,iBAAiB,OAAO,YAAY,OAAO,OAAO,UAAU,MAAM,GAAG,EAAE,MAAM;EAClH,KAAK,gBACD,OAAO,GAAG,MAAM,KAAK,UAAU,EAAE,SAAS,MAAM,KAAK,cAAc,EAAE;EACzE,KAAK,6BACD,OAAO,GAAG,MAAM,KAAK,UAAU,EAAE,SAAS,MAAM,KAAK,2BAA2B,EAAE;EACtF,KAAK,MACD,OAAO,GAAG,MAAM,KAAK,UAAU,EAAE,yBAAyB,MAAM,KAAK,IAAI,EAAE;EAC/E,KAAK,mBACD,OAAO,GAAG,MAAM,KAAK,UAAU,EAAE,oBAAoB,MAAM,KAAK,aAAa,EAAE,QAAQ,MAAM,KAAK,sBAAoB;CAC9H;AACJ"}
1
+ {"version":3,"file":"consent-BaB-eQKG.js","names":[],"sources":["../src/telemetry/consent.ts"],"sourcesContent":["import chalk from \"chalk\";\nimport inquirer from \"inquirer\";\n\nimport { buildEvent, TELEMETRY_SCHEMA_VERSION, TelemetryEventName } from \"./payload\";\nimport { readConfig } from \"./identity\";\nimport { setConsent, suppressionReason } from \"./index\";\n\n/**\n * Asking, and what the question looks like.\n *\n * ## Why the prompt comes *after* the work\n *\n * The first `rebase init` on a machine is the event most worth having, and it\n * is the one where no consent exists yet. Asking before scaffolding puts a\n * privacy negotiation in front of someone who has not yet seen the tool do\n * anything — the worst possible moment, and a reliable way to get a reflexive\n * no.\n *\n * So the question is asked once the project exists and the user has seen it\n * work. The event's data is still in memory at that point, so nothing is lost\n * by waiting, and — this is the part that matters — **nothing has been\n * transmitted or written**. Declining leaves no id, no file, no record.\n *\n * ## Why the payload is shown rather than described\n *\n * \"Anonymous usage data\" is a phrase that has been used to mean almost\n * anything. Printing the exact JSON costs four lines of output and replaces a\n * claim the user has to take on faith with something they can read. It is also\n * the same builder the sender uses, so it cannot drift into a comfortable\n * fiction.\n */\n\n/** Where the question is being asked from, which decides how often it may be. */\nexport interface PromptOptions {\n /**\n * Ask again even though this machine already declined.\n *\n * Set by `rebase init`, and only there. Scaffolding a project is a\n * deliberate, occasional act with a natural pause in it — not something\n * anyone does in a loop — so asking each time is an offer rather than\n * nagging, and it reaches the person whose first answer was a reflex before\n * they had seen the tool do anything.\n *\n * It does **not** re-ask someone who accepted: they are already sharing, so\n * the only thing another prompt could do is talk them out of it.\n *\n * Nothing else changes. `DO_NOT_TRACK`, `REBASE_TELEMETRY_DISABLED`, `CI`\n * and a project's `\"telemetry\": false` are answers, not questions, and they\n * still suppress the prompt entirely — a re-ask that could override a\n * committed repository policy would be exactly the consent-by-proxy the\n * policy exists to prevent.\n */\n reAskDeclined?: boolean;\n}\n\n/** True when we may ask: nothing forbids it, and there is a question to ask. */\nexport function shouldPrompt(\n env: NodeJS.ProcessEnv = process.env,\n { reAskDeclined = false }: PromptOptions = {}\n): boolean {\n if (!process.stdin.isTTY) return false;\n\n const reason = suppressionReason(env);\n // `not_asked` is the only reason that is a question rather than an answer.\n // A user who set DO_NOT_TRACK, or is on CI, has already told us — and so\n // has one who is already sharing (`null`), whom we never re-ask.\n if (reason === \"not_asked\") return true;\n return reAskDeclined && reason === \"declined\";\n}\n\nexport function renderPreview(event: TelemetryEventName, properties: Record<string, unknown>): string {\n const preview = buildEvent(event, properties, {\n machineId: \"<random uuid, generated only if you say yes>\",\n projectId: \"<random uuid, per checkout>\"\n });\n return JSON.stringify(preview, null, 2);\n}\n\n/**\n * Ask, record the answer, and report it.\n *\n * Never throws and never blocks a non-interactive run: `rebase init --yes` in\n * CI must behave exactly as it does today, which means not asking and not\n * sending.\n */\nexport async function promptForConsent(options: PromptOptions = {}): Promise<boolean> {\n if (!shouldPrompt(process.env, options)) return false;\n\n // Only true when this is a second (or later) asking, which the closing line\n // needs to know: \"you will not be asked again\" is false at `init`.\n const askedBefore = suppressionReason(process.env) === \"declined\";\n\n try {\n console.log(\"\");\n console.log(chalk.bold(\"Help improve Rebase?\"));\n console.log(\"\");\n console.log(chalk.gray(\" Rebase is self-hosted, so we only learn what works if you tell us.\"));\n console.log(chalk.gray(\" Anonymous: random ids, the CLI version, your OS, and which template and\"));\n console.log(chalk.gray(\" package manager you used. Never project names, paths, schemas, URLs or\"));\n console.log(chalk.gray(\" error messages.\"));\n console.log(\"\");\n console.log(chalk.gray(` Print the exact payload with ${chalk.cyan(\"rebase telemetry show\")}; change your mind`));\n console.log(chalk.gray(` any time with ${chalk.cyan(\"rebase telemetry disable\")}.`));\n if (askedBefore) {\n console.log(\"\");\n console.log(chalk.gray(\" You declined before; that stands unless you change it here.\"));\n }\n console.log(\"\");\n\n const { accepted } = await inquirer.prompt([\n {\n type: \"confirm\",\n name: \"accepted\",\n message: \"Share anonymous usage data?\",\n // Defaults to no. Telemetry is off until somebody says yes, and\n // a bare Enter is the commonest answer to any prompt — so a\n // pre-selected yes would make pressing through `init` the\n // consent. `y` is one keystroke, and `rebase telemetry enable`\n // is the same answer later.\n default: false\n }\n ] as Parameters<typeof inquirer.prompt>[0]) as { accepted: boolean };\n\n setConsent(Boolean(accepted));\n\n console.log(\n accepted\n ? chalk.green(\" Thank you — sharing enabled.\")\n : chalk.gray(\n \" Nothing will be sent.\"\n + (options.reAskDeclined\n ? \" You will be asked again the next time you scaffold a project.\"\n : \" You will not be asked again.\")\n )\n );\n console.log(\"\");\n return Boolean(accepted);\n } catch {\n // A prompt that fails (piped stdin, a terminal that went away) is a\n // decline, and specifically a decline we do not persist — the user\n // never actually saw the question, so they should still get to answer\n // it another day.\n return false;\n }\n}\n\n/** Human-readable current state, for `rebase telemetry status`. */\nexport function describeState(env: NodeJS.ProcessEnv = process.env): string {\n const reason = suppressionReason(env);\n const config = readConfig();\n switch (reason) {\n case null:\n return `${chalk.green(\"enabled\")} — schema v${TELEMETRY_SCHEMA_VERSION}, machine id ${chalk.gray(config.machineId ?? \"unset\")}`;\n case \"not_asked\":\n return `${chalk.yellow(\"not configured\")} — nothing has been sent, and you have not been asked yet`;\n case \"declined\":\n return `${chalk.gray(\"disabled\")} — you declined${config.decidedAt ? ` on ${config.decidedAt.slice(0, 10)}` : \"\"}`;\n case \"do_not_track\":\n return `${chalk.gray(\"disabled\")} — the ${chalk.cyan(\"DO_NOT_TRACK\")} environment variable is set`;\n case \"rebase_telemetry_disabled\":\n return `${chalk.gray(\"disabled\")} — the ${chalk.cyan(\"REBASE_TELEMETRY_DISABLED\")} environment variable is set`;\n case \"ci\":\n return `${chalk.gray(\"disabled\")} — this looks like CI (${chalk.cyan(\"CI\")} is set), which is never counted`;\n case \"project_opt_out\":\n return `${chalk.gray(\"disabled\")} — this project's ${chalk.cyan(\"rebase.json\")} sets ${chalk.cyan('\"telemetry\": false')}`;\n }\n}\n"],"mappings":";;;;;AAwDA,SAAgB,aACZ,MAAyB,QAAQ,KACjC,EAAE,gBAAgB,UAAyB,CAAC,GACrC;CACP,IAAI,CAAC,QAAQ,MAAM,OAAO,OAAO;CAEjC,MAAM,SAAS,kBAAkB,GAAG;CAIpC,IAAI,WAAW,aAAa,OAAO;CACnC,OAAO,iBAAiB,WAAW;AACvC;AAEA,SAAgB,cAAc,OAA2B,YAA6C;CAClG,MAAM,UAAU,WAAW,OAAO,YAAY;EAC1C,WAAW;EACX,WAAW;CACf,CAAC;CACD,OAAO,KAAK,UAAU,SAAS,MAAM,CAAC;AAC1C;;;;;;;;AASA,eAAsB,iBAAiB,UAAyB,CAAC,GAAqB;CAClF,IAAI,CAAC,aAAa,QAAQ,KAAK,OAAO,GAAG,OAAO;CAIhD,MAAM,cAAc,kBAAkB,QAAQ,GAAG,MAAM;CAEvD,IAAI;EACA,QAAQ,IAAI,EAAE;EACd,QAAQ,IAAI,MAAM,KAAK,sBAAsB,CAAC;EAC9C,QAAQ,IAAI,EAAE;EACd,QAAQ,IAAI,MAAM,KAAK,sEAAsE,CAAC;EAC9F,QAAQ,IAAI,MAAM,KAAK,2EAA2E,CAAC;EACnG,QAAQ,IAAI,MAAM,KAAK,0EAA0E,CAAC;EAClG,QAAQ,IAAI,MAAM,KAAK,mBAAmB,CAAC;EAC3C,QAAQ,IAAI,EAAE;EACd,QAAQ,IAAI,MAAM,KAAK,kCAAkC,MAAM,KAAK,uBAAuB,EAAE,mBAAmB,CAAC;EACjH,QAAQ,IAAI,MAAM,KAAK,mBAAmB,MAAM,KAAK,0BAA0B,EAAE,EAAE,CAAC;EACpF,IAAI,aAAa;GACb,QAAQ,IAAI,EAAE;GACd,QAAQ,IAAI,MAAM,KAAK,+DAA+D,CAAC;EAC3F;EACA,QAAQ,IAAI,EAAE;EAEd,MAAM,EAAE,aAAa,MAAM,SAAS,OAAO,CACvC;GACI,MAAM;GACN,MAAM;GACN,SAAS;GAMT,SAAS;EACb,CACJ,CAA0C;EAE1C,WAAW,QAAQ,QAAQ,CAAC;EAE5B,QAAQ,IACJ,WACM,MAAM,MAAM,gCAAgC,IAC5C,MAAM,KACJ,6BACG,QAAQ,gBACL,mEACA,gCACV,CACR;EACA,QAAQ,IAAI,EAAE;EACd,OAAO,QAAQ,QAAQ;CAC3B,QAAQ;EAKJ,OAAO;CACX;AACJ;;AAGA,SAAgB,cAAc,MAAyB,QAAQ,KAAa;CACxE,MAAM,SAAS,kBAAkB,GAAG;CACpC,MAAM,SAAS,WAAW;CAC1B,QAAQ,QAAR;EACI,KAAK,MACD,OAAO,GAAG,MAAM,MAAM,SAAS,EAAE,2BAAqD,MAAM,KAAK,OAAO,aAAa,OAAO;EAChI,KAAK,aACD,OAAO,GAAG,MAAM,OAAO,gBAAgB,EAAE;EAC7C,KAAK,YACD,OAAO,GAAG,MAAM,KAAK,UAAU,EAAE,iBAAiB,OAAO,YAAY,OAAO,OAAO,UAAU,MAAM,GAAG,EAAE,MAAM;EAClH,KAAK,gBACD,OAAO,GAAG,MAAM,KAAK,UAAU,EAAE,SAAS,MAAM,KAAK,cAAc,EAAE;EACzE,KAAK,6BACD,OAAO,GAAG,MAAM,KAAK,UAAU,EAAE,SAAS,MAAM,KAAK,2BAA2B,EAAE;EACtF,KAAK,MACD,OAAO,GAAG,MAAM,KAAK,UAAU,EAAE,yBAAyB,MAAM,KAAK,IAAI,EAAE;EAC/E,KAAK,mBACD,OAAO,GAAG,MAAM,KAAK,UAAU,EAAE,oBAAoB,MAAM,KAAK,aAAa,EAAE,QAAQ,MAAM,KAAK,sBAAoB;CAC9H;AACJ"}
@@ -1,7 +1,7 @@
1
- import { n as parseCommandArgs, r as wantsHelp } from "./args-CcBphFdj.js";
1
+ import { C as wantsHelp, b as parseCommandArgs } from "./project-g4K6kV1t.js";
2
2
  import { r as refuseMissingCollectionsPath, t as COLLECTIONS_FLAG_HELP } from "./path-flags-BFgG9V6y.js";
3
- import { DEV_DATABASE_KIND_ENV, managedNotices, prepareDatabaseEnv } from "./prepare-C1iaiTC4.js";
4
- import { absolutizeLocalPathArgs, ensureDockerDatabase, n as argsFromCommand, resolveDriverCli, spawnDriverCli, t as reportSpawnFailure } from "./db-Dkn9XhT-.js";
3
+ import { DEV_DATABASE_KIND_ENV, managedNotices, prepareDatabaseEnv } from "./prepare-1rVNd2PF.js";
4
+ import { absolutizeLocalPathArgs, ensureDockerDatabase, n as argsFromCommand, resolveDriverCli, spawnDriverCli, t as reportSpawnFailure } from "./db-9P6MnmdC.js";
5
5
  import chalk from "chalk";
6
6
  import path from "path";
7
7
  //#region src/commands/console.ts
@@ -100,4 +100,4 @@ async function consoleCommand(rawArgs) {
100
100
  //#endregion
101
101
  export { consoleCommand };
102
102
 
103
- //# sourceMappingURL=console-Dw3HLdUs.js.map
103
+ //# sourceMappingURL=console-BlD9kjFW.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"console-Dw3HLdUs.js","names":[],"sources":["../src/commands/console.ts"],"sourcesContent":["/**\n * CLI command: rebase console\n *\n * A Node REPL with the project open — `rebase` (what `openRebase` from\n * `@rebasepro/server-postgres` returns: `dataAsAdmin`, `sql`, `withAuth`) and\n * `collections` in scope — or, with `--eval`, one piece of code run and its\n * result printed as JSON.\n *\n * Resolved like `rebase db push`: the same project and `.env`, the same\n * database (the managed one started if that is what the project uses), the\n * same `--collections` loader — the driver's, spawned in `backend/`. Opening\n * the database changes nothing in it.\n */\nimport chalk from \"chalk\";\nimport path from \"path\";\nimport { parseCommandArgs, wantsHelp } from \"../utils/args\";\nimport { argsFromCommand } from \"../utils/command-words\";\nimport { COLLECTIONS_FLAG_HELP, refuseMissingCollectionsPath } from \"../utils/path-flags\";\nimport { reportSpawnFailure } from \"../utils/spawn-error\";\nimport { DEV_DATABASE_KIND_ENV, managedNotices, prepareDatabaseEnv } from \"../dev-db/prepare\";\nimport { absolutizeLocalPathArgs, ensureDockerDatabase, resolveDriverCli, spawnDriverCli } from \"./db\";\n\nexport const CONSOLE_FLAGS = {\n \"--eval\": String,\n \"-e\": \"--eval\",\n \"--collections\": String,\n \"-c\": \"--collections\",\n \"--database-url\": String,\n \"--docker\": Boolean\n} as const;\n\n/** What each long flag above does, for the Options block. */\nconst CONSOLE_FLAG_HELP: Record<string, string> = {\n \"--eval\": \"Run this code instead of the REPL, print its awaited result as JSON, and exit 1 if it throws\",\n \"--collections\": COLLECTIONS_FLAG_HELP,\n \"--database-url\": \"Open this database instead of the project's\",\n \"--docker\": \"Use the database from docker compose, starting it if needed\"\n};\n\n/** Where the REPL keeps its history: the project's own `.rebase/`, which the scaffold ignores. */\nexport function consoleHistoryPath(projectRoot: string): string {\n return path.join(projectRoot, \".rebase\", \"console_history\");\n}\n\nfunction flagLabel(flag: string): string {\n const aliases = Object.entries(CONSOLE_FLAGS)\n .filter(([, target]) => target === flag)\n .map(([alias]) => alias);\n return [flag, ...aliases].join(\", \");\n}\n\nexport function printConsoleHelp(): void {\n console.log(`\n${chalk.bold(\"rebase console\")} — Explore the project's data from a REPL\n\n${chalk.green.bold(\"Usage\")}\n rebase console [options]\n\n${chalk.green.bold(\"Options\")}\n${Object.keys(CONSOLE_FLAG_HELP)\n .map(flag => ` ${chalk.blue(flagLabel(flag).padEnd(22))} ${CONSOLE_FLAG_HELP[flag]}`)\n .join(\"\\n\")}\n\nIn scope:\n rebase dataAsAdmin, sql(query, { params }), withAuth(user) → { data }, close()\n collections the project's collections\n\nTop-level await works. Opening the database changes nothing in it: no table,\nrole or trigger is created. withAuth runs as the user under row-level security.\n\n${chalk.green.bold(\"Examples\")}\n rebase console\n rebase console -e \"rebase.dataAsAdmin.posts.find({ limit: 5 })\"\n rebase console -e \"(await rebase.withAuth({ uid: 'u1', roles: ['user'] })).data.posts.find()\"\n rebase console -e \"rebase.sql('select count(*) from posts')\"\n`);\n}\n\nexport async function consoleCommand(rawArgs: string[]): Promise<void> {\n if (wantsHelp(rawArgs)) {\n printConsoleHelp();\n return;\n }\n\n // Strictly, before the line is relayed: the driver would otherwise be the\n // first to see a mistyped flag, after a database had been started for it.\n const { flags } = parseCommandArgs({\n spec: CONSOLE_FLAGS,\n rawArgs,\n commandWords: 1,\n command: \"console\",\n maxPositionals: 0\n });\n\n const resolved = await resolveDriverCli();\n const { projectRoot, backendDir } = resolved;\n refuseMissingCollectionsPath(rawArgs, process.cwd(), backendDir, \"console\");\n await ensureDockerDatabase(projectRoot, rawArgs);\n\n // With --eval, stdout is the result and nothing else, so it can be piped\n // into `jq`; piped into the REPL, it is the REPL's answers. Either way what\n // the database resolution has to say goes to stderr.\n const say = flags[\"--eval\"] === undefined && process.stdin.isTTY\n ? (line: string) => console.log(chalk.gray(` ${line}`))\n : (line: string) => console.error(chalk.gray(` ${line}`));\n const prepared = await prepareDatabaseEnv(projectRoot, {\n flagUrl: flags[\"--database-url\"] ?? null,\n flagDocker: flags[\"--docker\"] === true,\n onProgress: say\n });\n Object.assign(resolved.env, prepared.env);\n // The database this run resolved, flags included, so the driver words a\n // failure to open it for that one — a managed database that has no free\n // connection is not \"is your Postgres running?\".\n resolved.env[DEV_DATABASE_KIND_ENV] = prepared.database.kind;\n for (const line of managedNotices(prepared)) say(line);\n\n const childArgs = [\n ...absolutizeLocalPathArgs(argsFromCommand(rawArgs, \"console\"), process.cwd()),\n `--history=${consoleHistoryPath(projectRoot)}`\n ];\n\n try {\n await spawnDriverCli(resolved, childArgs);\n } catch (error) {\n // A child that exited non-zero has already said why, on the terminal it\n // shares — an `--eval` that threw, a database that refused.\n reportSpawnFailure(error);\n process.exit(1);\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAsBA,IAAa,gBAAgB;CACzB,UAAU;CACV,MAAM;CACN,iBAAiB;CACjB,MAAM;CACN,kBAAkB;CAClB,YAAY;AAChB;;AAGA,IAAM,oBAA4C;CAC9C,UAAU;CACV,iBAAiB;CACjB,kBAAkB;CAClB,YAAY;AAChB;;AAGA,SAAgB,mBAAmB,aAA6B;CAC5D,OAAO,KAAK,KAAK,aAAa,WAAW,iBAAiB;AAC9D;AAEA,SAAS,UAAU,MAAsB;CAIrC,OAAO,CAAC,MAAM,GAHE,OAAO,QAAQ,aAAa,CAAC,CACxC,QAAQ,GAAG,YAAY,WAAW,IAAI,CAAC,CACvC,KAAK,CAAC,WAAW,KACL,CAAO,CAAC,CAAC,KAAK,IAAI;AACvC;AAEA,SAAgB,mBAAyB;CACrC,QAAQ,IAAI;EACd,MAAM,KAAK,gBAAgB,EAAE;;EAE7B,MAAM,MAAM,KAAK,OAAO,EAAE;;;EAG1B,MAAM,MAAM,KAAK,SAAS,EAAE;EAC5B,OAAO,KAAK,iBAAiB,CAAC,CACvB,KAAI,SAAQ,KAAK,MAAM,KAAK,UAAU,IAAI,CAAC,CAAC,OAAO,EAAE,CAAC,EAAE,GAAG,kBAAkB,OAAO,CAAC,CACrF,KAAK,IAAI,EAAE;;;;;;;;;EASlB,MAAM,MAAM,KAAK,UAAU,EAAE;;;;;CAK9B;AACD;AAEA,eAAsB,eAAe,SAAkC;CACnE,IAAI,UAAU,OAAO,GAAG;EACpB,iBAAiB;EACjB;CACJ;CAIA,MAAM,EAAE,UAAU,iBAAiB;EAC/B,MAAM;EACN;EACA,cAAc;EACd,SAAS;EACT,gBAAgB;CACpB,CAAC;CAED,MAAM,WAAW,MAAM,iBAAiB;CACxC,MAAM,EAAE,aAAa,eAAe;CACpC,6BAA6B,SAAS,QAAQ,IAAI,GAAG,YAAY,SAAS;CAC1E,MAAM,qBAAqB,aAAa,OAAO;CAK/C,MAAM,MAAM,MAAM,cAAc,KAAA,KAAa,QAAQ,MAAM,SACpD,SAAiB,QAAQ,IAAI,MAAM,KAAK,KAAK,MAAM,CAAC,KACpD,SAAiB,QAAQ,MAAM,MAAM,KAAK,KAAK,MAAM,CAAC;CAC7D,MAAM,WAAW,MAAM,mBAAmB,aAAa;EACnD,SAAS,MAAM,qBAAqB;EACpC,YAAY,MAAM,gBAAgB;EAClC,YAAY;CAChB,CAAC;CACD,OAAO,OAAO,SAAS,KAAK,SAAS,GAAG;CAIxC,SAAS,IAAI,yBAAyB,SAAS,SAAS;CACxD,KAAK,MAAM,QAAQ,eAAe,QAAQ,GAAG,IAAI,IAAI;CAErD,MAAM,YAAY,CACd,GAAG,wBAAwB,gBAAgB,SAAS,SAAS,GAAG,QAAQ,IAAI,CAAC,GAC7E,aAAa,mBAAmB,WAAW,GAC/C;CAEA,IAAI;EACA,MAAM,eAAe,UAAU,SAAS;CAC5C,SAAS,OAAO;EAGZ,mBAAmB,KAAK;EACxB,QAAQ,KAAK,CAAC;CAClB;AACJ"}
1
+ {"version":3,"file":"console-BlD9kjFW.js","names":[],"sources":["../src/commands/console.ts"],"sourcesContent":["/**\n * CLI command: rebase console\n *\n * A Node REPL with the project open — `rebase` (what `openRebase` from\n * `@rebasepro/server-postgres` returns: `dataAsAdmin`, `sql`, `withAuth`) and\n * `collections` in scope — or, with `--eval`, one piece of code run and its\n * result printed as JSON.\n *\n * Resolved like `rebase db push`: the same project and `.env`, the same\n * database (the managed one started if that is what the project uses), the\n * same `--collections` loader — the driver's, spawned in `backend/`. Opening\n * the database changes nothing in it.\n */\nimport chalk from \"chalk\";\nimport path from \"path\";\nimport { parseCommandArgs, wantsHelp } from \"../utils/args\";\nimport { argsFromCommand } from \"../utils/command-words\";\nimport { COLLECTIONS_FLAG_HELP, refuseMissingCollectionsPath } from \"../utils/path-flags\";\nimport { reportSpawnFailure } from \"../utils/spawn-error\";\nimport { DEV_DATABASE_KIND_ENV, managedNotices, prepareDatabaseEnv } from \"../dev-db/prepare\";\nimport { absolutizeLocalPathArgs, ensureDockerDatabase, resolveDriverCli, spawnDriverCli } from \"./db\";\n\nexport const CONSOLE_FLAGS = {\n \"--eval\": String,\n \"-e\": \"--eval\",\n \"--collections\": String,\n \"-c\": \"--collections\",\n \"--database-url\": String,\n \"--docker\": Boolean\n} as const;\n\n/** What each long flag above does, for the Options block. */\nconst CONSOLE_FLAG_HELP: Record<string, string> = {\n \"--eval\": \"Run this code instead of the REPL, print its awaited result as JSON, and exit 1 if it throws\",\n \"--collections\": COLLECTIONS_FLAG_HELP,\n \"--database-url\": \"Open this database instead of the project's\",\n \"--docker\": \"Use the database from docker compose, starting it if needed\"\n};\n\n/** Where the REPL keeps its history: the project's own `.rebase/`, which the scaffold ignores. */\nexport function consoleHistoryPath(projectRoot: string): string {\n return path.join(projectRoot, \".rebase\", \"console_history\");\n}\n\nfunction flagLabel(flag: string): string {\n const aliases = Object.entries(CONSOLE_FLAGS)\n .filter(([, target]) => target === flag)\n .map(([alias]) => alias);\n return [flag, ...aliases].join(\", \");\n}\n\nexport function printConsoleHelp(): void {\n console.log(`\n${chalk.bold(\"rebase console\")} — Explore the project's data from a REPL\n\n${chalk.green.bold(\"Usage\")}\n rebase console [options]\n\n${chalk.green.bold(\"Options\")}\n${Object.keys(CONSOLE_FLAG_HELP)\n .map(flag => ` ${chalk.blue(flagLabel(flag).padEnd(22))} ${CONSOLE_FLAG_HELP[flag]}`)\n .join(\"\\n\")}\n\nIn scope:\n rebase dataAsAdmin, sql(query, { params }), withAuth(user) → { data }, close()\n collections the project's collections\n\nTop-level await works. Opening the database changes nothing in it: no table,\nrole or trigger is created. withAuth runs as the user under row-level security.\n\n${chalk.green.bold(\"Examples\")}\n rebase console\n rebase console -e \"rebase.dataAsAdmin.posts.find({ limit: 5 })\"\n rebase console -e \"(await rebase.withAuth({ uid: 'u1', roles: ['user'] })).data.posts.find()\"\n rebase console -e \"rebase.sql('select count(*) from posts')\"\n`);\n}\n\nexport async function consoleCommand(rawArgs: string[]): Promise<void> {\n if (wantsHelp(rawArgs)) {\n printConsoleHelp();\n return;\n }\n\n // Strictly, before the line is relayed: the driver would otherwise be the\n // first to see a mistyped flag, after a database had been started for it.\n const { flags } = parseCommandArgs({\n spec: CONSOLE_FLAGS,\n rawArgs,\n commandWords: 1,\n command: \"console\",\n maxPositionals: 0\n });\n\n const resolved = await resolveDriverCli();\n const { projectRoot, backendDir } = resolved;\n refuseMissingCollectionsPath(rawArgs, process.cwd(), backendDir, \"console\");\n await ensureDockerDatabase(projectRoot, rawArgs);\n\n // With --eval, stdout is the result and nothing else, so it can be piped\n // into `jq`; piped into the REPL, it is the REPL's answers. Either way what\n // the database resolution has to say goes to stderr.\n const say = flags[\"--eval\"] === undefined && process.stdin.isTTY\n ? (line: string) => console.log(chalk.gray(` ${line}`))\n : (line: string) => console.error(chalk.gray(` ${line}`));\n const prepared = await prepareDatabaseEnv(projectRoot, {\n flagUrl: flags[\"--database-url\"] ?? null,\n flagDocker: flags[\"--docker\"] === true,\n onProgress: say\n });\n Object.assign(resolved.env, prepared.env);\n // The database this run resolved, flags included, so the driver words a\n // failure to open it for that one — a managed database that has no free\n // connection is not \"is your Postgres running?\".\n resolved.env[DEV_DATABASE_KIND_ENV] = prepared.database.kind;\n for (const line of managedNotices(prepared)) say(line);\n\n const childArgs = [\n ...absolutizeLocalPathArgs(argsFromCommand(rawArgs, \"console\"), process.cwd()),\n `--history=${consoleHistoryPath(projectRoot)}`\n ];\n\n try {\n await spawnDriverCli(resolved, childArgs);\n } catch (error) {\n // A child that exited non-zero has already said why, on the terminal it\n // shares — an `--eval` that threw, a database that refused.\n reportSpawnFailure(error);\n process.exit(1);\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAsBA,IAAa,gBAAgB;CACzB,UAAU;CACV,MAAM;CACN,iBAAiB;CACjB,MAAM;CACN,kBAAkB;CAClB,YAAY;AAChB;;AAGA,IAAM,oBAA4C;CAC9C,UAAU;CACV,iBAAiB;CACjB,kBAAkB;CAClB,YAAY;AAChB;;AAGA,SAAgB,mBAAmB,aAA6B;CAC5D,OAAO,KAAK,KAAK,aAAa,WAAW,iBAAiB;AAC9D;AAEA,SAAS,UAAU,MAAsB;CAIrC,OAAO,CAAC,MAAM,GAHE,OAAO,QAAQ,aAAa,CAAC,CACxC,QAAQ,GAAG,YAAY,WAAW,IAAI,CAAC,CACvC,KAAK,CAAC,WAAW,KACL,CAAO,CAAC,CAAC,KAAK,IAAI;AACvC;AAEA,SAAgB,mBAAyB;CACrC,QAAQ,IAAI;EACd,MAAM,KAAK,gBAAgB,EAAE;;EAE7B,MAAM,MAAM,KAAK,OAAO,EAAE;;;EAG1B,MAAM,MAAM,KAAK,SAAS,EAAE;EAC5B,OAAO,KAAK,iBAAiB,CAAC,CACvB,KAAI,SAAQ,KAAK,MAAM,KAAK,UAAU,IAAI,CAAC,CAAC,OAAO,EAAE,CAAC,EAAE,GAAG,kBAAkB,OAAO,CAAC,CACrF,KAAK,IAAI,EAAE;;;;;;;;;EASlB,MAAM,MAAM,KAAK,UAAU,EAAE;;;;;CAK9B;AACD;AAEA,eAAsB,eAAe,SAAkC;CACnE,IAAI,UAAU,OAAO,GAAG;EACpB,iBAAiB;EACjB;CACJ;CAIA,MAAM,EAAE,UAAU,iBAAiB;EAC/B,MAAM;EACN;EACA,cAAc;EACd,SAAS;EACT,gBAAgB;CACpB,CAAC;CAED,MAAM,WAAW,MAAM,iBAAiB;CACxC,MAAM,EAAE,aAAa,eAAe;CACpC,6BAA6B,SAAS,QAAQ,IAAI,GAAG,YAAY,SAAS;CAC1E,MAAM,qBAAqB,aAAa,OAAO;CAK/C,MAAM,MAAM,MAAM,cAAc,KAAA,KAAa,QAAQ,MAAM,SACpD,SAAiB,QAAQ,IAAI,MAAM,KAAK,KAAK,MAAM,CAAC,KACpD,SAAiB,QAAQ,MAAM,MAAM,KAAK,KAAK,MAAM,CAAC;CAC7D,MAAM,WAAW,MAAM,mBAAmB,aAAa;EACnD,SAAS,MAAM,qBAAqB;EACpC,YAAY,MAAM,gBAAgB;EAClC,YAAY;CAChB,CAAC;CACD,OAAO,OAAO,SAAS,KAAK,SAAS,GAAG;CAIxC,SAAS,IAAI,yBAAyB,SAAS,SAAS;CACxD,KAAK,MAAM,QAAQ,eAAe,QAAQ,GAAG,IAAI,IAAI;CAErD,MAAM,YAAY,CACd,GAAG,wBAAwB,gBAAgB,SAAS,SAAS,GAAG,QAAQ,IAAI,CAAC,GAC7E,aAAa,mBAAmB,WAAW,GAC/C;CAEA,IAAI;EACA,MAAM,eAAe,UAAU,SAAS;CAC5C,SAAS,OAAO;EAGZ,mBAAmB,KAAK;EACxB,QAAQ,KAAK,CAAC;CAClB;AACJ"}
@@ -1,5 +1,4 @@
1
- import { c as findProjectRoot } from "./project-Wp2zoyeN.js";
2
- import { n as parseCommandArgs } from "./args-CcBphFdj.js";
1
+ import { S as switchOn, b as parseCommandArgs, c as findProjectRoot } from "./project-g4K6kV1t.js";
3
2
  import { t as cliUserAgent } from "./version-Buyyo1ut.js";
4
3
  import chalk from "chalk";
5
4
  import arg from "arg";
@@ -164,7 +163,7 @@ function truncate(text) {
164
163
  * printed the fallback.
165
164
  */
166
165
  function wantsRawError(argv = process.argv) {
167
- return argv.includes("--debug") || parseEnvBoolean(process.env.REBASE_DEBUG) === true;
166
+ return switchOn(argv, "--debug") || parseEnvBoolean(process.env.REBASE_DEBUG) === true;
168
167
  }
169
168
  //#endregion
170
169
  //#region src/commands/cloud/context.ts
@@ -1231,4 +1230,4 @@ function requireCloudRow(row, what) {
1231
1230
  //#endregion
1232
1231
  export { projectHost as A, requireProject as B, lookupProjectId as C, parseCloudArgs as D, openUrl as E, reportError as F, resolveProjectRef as G, requireSessionClient as H, requireClient as I, setCurrentContext as J, resolveTimeoutMs as K, requireCloudRow as L, readLink as M, refuseDirectLink as N, printGroupHelp as O, removeLink as P, wantsRawError as Q, requireInteractive as R, keyValues as S, noteBlank as T, resolveCloudTarget as U, requireProjectRef as V, resolveCloudUrl as W, warn as X, success as Y, writeLink as Z, freshAccessToken as _, cloudRow as a, isJsonMode as b, confirmDestructive as c, emit as d, emitHelp as f, fetchTenantBaseDomain as g, fetchDeployTargets as h, billingAccountIdOf as i, projectLinkPath as j, printJson as k, createCloudClient as l, fail as m, GLOBAL_HELP_FLAGS as n, cloudRows as o, envToken as p, setContextOrg as q, TOKEN_ENV as r, colorStatus as s, GLOBAL_CLOUD_FLAGS as t, displayProjectRef as u, getContextOrg as v, note as w, isKnownControlPlane as x, initOutputMode as y, requireKnownAction as z };
1233
1232
 
1234
- //# sourceMappingURL=context-BM-oOMNX.js.map
1233
+ //# sourceMappingURL=context-DHiiiJ5C.js.map