@rebasepro/cli 0.25.0 → 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 (125) hide show
  1. package/dist/{agent-setup-B5AKHOgt.js → agent-setup-BYb2Tbdz.js} +2 -2
  2. package/dist/{agent-setup-B5AKHOgt.js.map → agent-setup-BYb2Tbdz.js.map} +1 -1
  3. package/dist/{api-keys-Tm7zvAPe.js → api-keys-CSMbsmAn.js} +3 -4
  4. package/dist/{api-keys-Tm7zvAPe.js.map → api-keys-CSMbsmAn.js.map} +1 -1
  5. package/dist/{apps-BsU_ILbP.js → apps-iBXNZhVN.js} +6 -7
  6. package/dist/{apps-BsU_ILbP.js.map → apps-iBXNZhVN.js.map} +1 -1
  7. package/dist/{atlas-dev-database-OWtqnG7W.js → atlas-dev-database-BusbHP6n.js} +4 -4
  8. package/dist/{atlas-dev-database-OWtqnG7W.js.map → atlas-dev-database-BusbHP6n.js.map} +1 -1
  9. package/dist/{auth-Vl2hFKXP.js → auth-DRsTWPky.js} +6 -7
  10. package/dist/{auth-Vl2hFKXP.js.map → auth-DRsTWPky.js.map} +1 -1
  11. package/dist/{branch-pointer-9DnwkFJJ.js → branch-pointer-Bn_FjNld.js} +1 -1
  12. package/dist/{build-BBgT8EJZ.js → build-et7Rccxi.js} +6 -7
  13. package/dist/{build-BBgT8EJZ.js.map → build-et7Rccxi.js.map} +1 -1
  14. package/dist/{bundle-DtCCCr4w.js → bundle-DlqkDYSe.js} +3 -3
  15. package/dist/{bundle-DtCCCr4w.js.map → bundle-DlqkDYSe.js.map} +1 -1
  16. package/dist/{cloud-BpH0_bmV.js → cloud-B-ZIjHrA.js} +10 -11
  17. package/dist/{cloud-BpH0_bmV.js.map → cloud-B-ZIjHrA.js.map} +1 -1
  18. package/dist/commands/dev.d.ts +12 -0
  19. package/dist/commands/generate_sdk.d.ts +4 -2
  20. package/dist/commands/init.d.ts +20 -0
  21. package/dist/commands/start.d.ts +10 -0
  22. package/dist/commands/upgrade.d.ts +12 -0
  23. package/dist/{consent-ClRHmcxf.js → consent-BaB-eQKG.js} +2 -2
  24. package/dist/{consent-ClRHmcxf.js.map → consent-BaB-eQKG.js.map} +1 -1
  25. package/dist/{console-CEGm0XNc.js → console-BlD9kjFW.js} +4 -4
  26. package/dist/{console-CEGm0XNc.js.map → console-BlD9kjFW.js.map} +1 -1
  27. package/dist/{context-CyflUrKg.js → context-DHiiiJ5C.js} +3 -4
  28. package/dist/context-DHiiiJ5C.js.map +1 -0
  29. package/dist/{daemon-CknkDjd6.js → daemon-DDMRbz4T.js} +160 -5
  30. package/dist/daemon-DDMRbz4T.js.map +1 -0
  31. package/dist/{daemon-entry-CbLRrgzy.js → daemon-entry-zOC1revO.js} +6 -3
  32. package/dist/daemon-entry-zOC1revO.js.map +1 -0
  33. package/dist/{db-CRNpA5mH.js → db-9P6MnmdC.js} +90 -50
  34. package/dist/db-9P6MnmdC.js.map +1 -0
  35. package/dist/{derive-CsoFGAnK.js → derive-MTdClnkc.js} +2 -2
  36. package/dist/{derive-CsoFGAnK.js.map → derive-MTdClnkc.js.map} +1 -1
  37. package/dist/{dev-C0pbMmhS.js → dev-BDN_OKXA.js} +183 -23
  38. package/dist/dev-BDN_OKXA.js.map +1 -0
  39. package/dist/dev-db/daemon.d.ts +41 -0
  40. package/dist/dev-db/pull.d.ts +33 -5
  41. package/dist/dev-db/state.d.ts +9 -0
  42. package/dist/{dev-preflight-unHpiYO1.js → dev-preflight-FT9GbJVj.js} +73 -27
  43. package/dist/dev-preflight-FT9GbJVj.js.map +1 -0
  44. package/dist/{dev-server-B1UYifZJ.js → dev-server-CIZwAfZa.js} +14 -3
  45. package/dist/dev-server-CIZwAfZa.js.map +1 -0
  46. package/dist/{doctor-CATRdWm6.js → doctor-D8YXz7df.js} +6 -6
  47. package/dist/doctor-D8YXz7df.js.map +1 -0
  48. package/dist/{eject-DuasTr-F.js → eject-izG2KjMo.js} +3 -4
  49. package/dist/{eject-DuasTr-F.js.map → eject-izG2KjMo.js.map} +1 -1
  50. package/dist/{fold-static-hDHtDfTq.js → fold-static-CybSay_W.js} +3 -3
  51. package/dist/{fold-static-hDHtDfTq.js.map → fold-static-CybSay_W.js.map} +1 -1
  52. package/dist/{generate_sdk-Cvg5jjJF.js → generate_sdk-B4R65oUV.js} +21 -11
  53. package/dist/generate_sdk-B4R65oUV.js.map +1 -0
  54. package/dist/index.es.js +36 -37
  55. package/dist/index.es.js.map +1 -1
  56. package/dist/{init-BWAwHPtz.js → init-DYaeppEN.js} +50 -62
  57. package/dist/init-DYaeppEN.js.map +1 -0
  58. package/dist/invoked-command-CG_rIa_o.js +55 -0
  59. package/dist/invoked-command-CG_rIa_o.js.map +1 -0
  60. package/dist/last-dev-database-B_75fxZC.js +2 -0
  61. package/dist/{manifest-DRp0UVTj.js → manifest-nvPdLlJY.js} +2 -2
  62. package/dist/{manifest-DRp0UVTj.js.map → manifest-nvPdLlJY.js.map} +1 -1
  63. package/dist/{normalize-imports-BoBVh5tU.js → normalize-imports-BYw8ad87.js} +3 -3
  64. package/dist/{normalize-imports-BoBVh5tU.js.map → normalize-imports-BYw8ad87.js.map} +1 -1
  65. package/dist/{prepare-DdnU70z8.js → prepare-1rVNd2PF.js} +5 -4
  66. package/dist/{prepare-DdnU70z8.js.map → prepare-1rVNd2PF.js.map} +1 -1
  67. package/dist/project-g4K6kV1t.js +701 -0
  68. package/dist/project-g4K6kV1t.js.map +1 -0
  69. package/dist/{pull-C3PXduzl.js → pull-DDMpsZ5r.js} +131 -24
  70. package/dist/pull-DDMpsZ5r.js.map +1 -0
  71. package/dist/refusal-ReeeGP8a.js +26 -0
  72. package/dist/refusal-ReeeGP8a.js.map +1 -0
  73. package/dist/resolve-BQUAC3Wi.js +2 -0
  74. package/dist/{resources-BdNRDEAy.js → resources-CovP8AZ-.js} +4 -5
  75. package/dist/{resources-BdNRDEAy.js.map → resources-CovP8AZ-.js.map} +1 -1
  76. package/dist/{schema-B0-p6T1O.js → schema-nuxvvc50.js} +5 -6
  77. package/dist/{schema-B0-p6T1O.js.map → schema-nuxvvc50.js.map} +1 -1
  78. package/dist/{skills-DfnTeyRz.js → skills-DeUIsZcT.js} +3 -4
  79. package/dist/{skills-DfnTeyRz.js.map → skills-DeUIsZcT.js.map} +1 -1
  80. package/dist/{start-BmEZkS0n.js → start-RzZqDroW.js} +20 -7
  81. package/dist/start-RzZqDroW.js.map +1 -0
  82. package/dist/{state-CCB-FAQ9.js → state-CYdBvnwA.js} +3 -2
  83. package/dist/state-CYdBvnwA.js.map +1 -0
  84. package/dist/{status-BJ26yZOo.js → status-D9Zm88NZ.js} +8 -9
  85. package/dist/{status-BJ26yZOo.js.map → status-D9Zm88NZ.js.map} +1 -1
  86. package/dist/{storage-DKPUBTWG.js → storage-BB3umRfw.js} +3 -4
  87. package/dist/{storage-DKPUBTWG.js.map → storage-BB3umRfw.js.map} +1 -1
  88. package/dist/{telemetry-EMFRVRQi.js → telemetry-HfU5uAh1.js} +2 -2
  89. package/dist/{telemetry-EMFRVRQi.js.map → telemetry-HfU5uAh1.js.map} +1 -1
  90. package/dist/{telemetry-m9czTNGR.js → telemetry-TseNLk6X.js} +4 -4
  91. package/dist/{telemetry-m9czTNGR.js.map → telemetry-TseNLk6X.js.map} +1 -1
  92. package/dist/{upgrade-DjihWuZL.js → upgrade-apiPKKE_.js} +26 -4
  93. package/dist/{upgrade-DjihWuZL.js.map → upgrade-apiPKKE_.js.map} +1 -1
  94. package/dist/utils/args.d.ts +32 -3
  95. package/dist/utils/boot-retry.d.ts +41 -0
  96. package/dist/utils/dev-preflight.d.ts +34 -1
  97. package/dist/utils/dev-server.d.ts +11 -0
  98. package/dist/utils/project.d.ts +5 -1
  99. package/dist/utils/unknown-command.d.ts +4 -0
  100. package/package.json +7 -7
  101. package/templates/resource-graphs/blank.json +25 -0
  102. package/templates/resource-graphs/blog.json +28 -0
  103. package/templates/resource-graphs/ecommerce.json +28 -0
  104. package/templates/resource-graphs/headless.json +22 -0
  105. package/templates/template/.env.example +5 -4
  106. package/templates/template/frontend/src/App.tsx +10 -9
  107. package/dist/args-BX0YD14a.js +0 -266
  108. package/dist/args-BX0YD14a.js.map +0 -1
  109. package/dist/context-CyflUrKg.js.map +0 -1
  110. package/dist/daemon-CknkDjd6.js.map +0 -1
  111. package/dist/daemon-entry-CbLRrgzy.js.map +0 -1
  112. package/dist/db-CRNpA5mH.js.map +0 -1
  113. package/dist/dev-C0pbMmhS.js.map +0 -1
  114. package/dist/dev-preflight-unHpiYO1.js.map +0 -1
  115. package/dist/dev-server-B1UYifZJ.js.map +0 -1
  116. package/dist/doctor-CATRdWm6.js.map +0 -1
  117. package/dist/generate_sdk-Cvg5jjJF.js.map +0 -1
  118. package/dist/init-BWAwHPtz.js.map +0 -1
  119. package/dist/last-dev-database-c_dKz1S3.js +0 -2
  120. package/dist/project-Wp2zoyeN.js +0 -349
  121. package/dist/project-Wp2zoyeN.js.map +0 -1
  122. package/dist/pull-C3PXduzl.js.map +0 -1
  123. package/dist/resolve-CNQjsatH.js +0 -2
  124. package/dist/start-BmEZkS0n.js.map +0 -1
  125. package/dist/state-CCB-FAQ9.js.map +0 -1
@@ -101,13 +101,33 @@ export declare function probeTcp(host: string, port: number, timeoutMs?: number)
101
101
  * forward. {@link waitForQueryable} is the check that a query will work.
102
102
  */
103
103
  export declare function waitForPort(host: string, port: number, timeoutMs?: number, intervalMs?: number): Promise<boolean>;
104
- /** Whether a database answered a query, and if not, the last reason it gave. */
104
+ /**
105
+ * Whether a database answered a query, and if not, the last reason it gave —
106
+ * and, for a database closed to new connections, its name.
107
+ */
105
108
  export type QueryReadiness = {
106
109
  ready: true;
107
110
  } | {
108
111
  ready: false;
109
112
  error: string;
113
+ closedDatabase?: string;
110
114
  };
115
+ /** What opening a closed database again did, and what to say about it. */
116
+ export interface ClosedDatabaseHeal {
117
+ /** It accepts connections again. */
118
+ reopened: boolean;
119
+ /** The driver's own lines about it (`reopenedDatabasesNotice`). */
120
+ notice: string[];
121
+ }
122
+ /**
123
+ * The database a connection was refused by because it is closed to new
124
+ * connections (`ALLOW_CONNECTIONS false`), or null.
125
+ *
126
+ * The driver's `closedDatabaseName`, for the error a bare `pg` connect gives:
127
+ * the sentence is matched, not SQLSTATE 55000, which also covers failures that
128
+ * have nothing to do with connecting.
129
+ */
130
+ export declare function closedDatabaseName(error: unknown): string | null;
111
131
  /**
112
132
  * Poll until `SELECT 1` succeeds on this connection string, or the bound runs out.
113
133
  *
@@ -118,10 +138,21 @@ export type QueryReadiness = {
118
138
  * three. Every error is retried: a reset, "the database system is starting up"
119
139
  * and a refused connection all mean "not yet", and the bound is what turns
120
140
  * "not yet" into an answer.
141
+ *
142
+ * Except one, which is not "not yet": a database closed to new connections
143
+ * (`closedDatabaseName`), which is what a `rebase db branch create --force`
144
+ * killed mid-copy leaves its source as. Waiting cannot open it, and it was
145
+ * waited on for the whole minute. It is opened again with the driver's
146
+ * `reopenClosedDatabases`, once, and then either answers or the wait ends at
147
+ * once, naming it.
121
148
  */
122
149
  export declare function waitForQueryable(databaseUrl: string, options?: {
123
150
  timeoutMs?: number;
124
151
  intervalMs?: number;
152
+ /** Opens a closed database again. The driver's `reopenClosedDatabases` by default; injected for tests. */
153
+ reopenClosed?: (databaseUrl: string) => Promise<ClosedDatabaseHeal>;
154
+ /** Where the driver's lines about a database it opened go. */
155
+ onNotice?: (line: string) => void;
125
156
  }): Promise<QueryReadiness>;
126
157
  /**
127
158
  * What a failed schema push printed, as lines to show.
@@ -204,6 +235,8 @@ export interface EnsureDevDatabaseOptions {
204
235
  startDatabase?: (projectRoot: string) => Promise<void>;
205
236
  /** How long a started database has to answer `SELECT 1`. Defaults to 60s. */
206
237
  readyTimeoutMs?: number;
238
+ /** Opens a database closed to new connections again; see `waitForQueryable`. Injected for tests. */
239
+ reopenClosed?: (databaseUrl: string) => Promise<ClosedDatabaseHeal>;
207
240
  /**
208
241
  * Who answers on the compose port. Asks Docker by default; injected for the
209
242
  * same reason `startDatabase` is.
@@ -1,3 +1,14 @@
1
+ /**
2
+ * The address `rebase dev` is starting its backend on, handed to the
3
+ * `generate-sdk` child it runs.
4
+ *
5
+ * Dev regenerates the SDK at boot, beside the database start and before the
6
+ * backend binds — so before the backend writes `.rebase/state.json`, which is
7
+ * where `generate-sdk` otherwise reads the address for the README's example.
8
+ * Dev knows the port it hands the backend, and the child's environment is the
9
+ * only channel between them.
10
+ */
11
+ export declare const DEV_BACKEND_URL_ENV = "REBASE_DEV_BACKEND_URL";
1
12
  /**
2
13
  * The port `rebase dev` derives from the project root path.
3
14
  * Range: 3001–3999 (avoids privileged ports and common services).
@@ -158,7 +158,11 @@ export interface JsonFailureIssue {
158
158
  * failure mode of not doing it is an unparseable stream.
159
159
  */
160
160
  export declare function failAsJson(message: string, code: string, hint?: string, issues?: JsonFailureIssue[]): never;
161
- /** Did the command line ask for machine-readable output? */
161
+ /**
162
+ * Did the command line ask for machine-readable output? Read as the parser
163
+ * reads a switch: `--json`, `--json=true` and `--json=1` ask for it,
164
+ * `--json=false` and `--json=0` do not.
165
+ */
162
166
  export declare function wantsJsonOutput(argv?: readonly string[]): boolean;
163
167
  /**
164
168
  * Require the project root or exit with a helpful error.
@@ -17,6 +17,10 @@ export declare function suggestCommand(typed: string, known: readonly string[]):
17
17
  * `family` is empty for the top level: `rebase --help`, not `rebase --help`.
18
18
  * `families` are the command families below this level, by name, whose
19
19
  * commands a word may be one of.
20
+ *
21
+ * The typed word is repeated with any connection-string password masked: a URL
22
+ * pasted where the command goes (`rebase db "postgres://u:pw@h/db"`) is a
23
+ * word like any other here, and this line lands in terminals and CI logs.
20
24
  */
21
25
  export declare function unknownCommandMessage(typed: string | undefined, known: readonly string[], family?: string, families?: Readonly<Record<string, readonly string[]>>): string;
22
26
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rebasepro/cli",
3
- "version": "0.25.0",
3
+ "version": "0.25.1",
4
4
  "description": "Developer tools for Rebase projects",
5
5
  "keywords": [
6
6
  "cli",
@@ -43,12 +43,12 @@
43
43
  "inquirer": "14.0.2",
44
44
  "jiti": "^2.7.0",
45
45
  "pg": "^8.22.0",
46
- "@rebasepro/codegen": "0.25.0",
47
- "@rebasepro/client": "0.25.0",
48
- "@rebasepro/agent-skills": "0.25.0",
49
- "@rebasepro/server": "0.25.0",
50
- "@rebasepro/types": "0.25.0",
51
- "@rebasepro/server-postgres": "0.25.0"
46
+ "@rebasepro/client": "0.25.1",
47
+ "@rebasepro/codegen": "0.25.1",
48
+ "@rebasepro/server": "0.25.1",
49
+ "@rebasepro/types": "0.25.1",
50
+ "@rebasepro/agent-skills": "0.25.1",
51
+ "@rebasepro/server-postgres": "0.25.1"
52
52
  },
53
53
  "devDependencies": {
54
54
  "@types/node": "^26.1.2",
@@ -0,0 +1,25 @@
1
+ {
2
+ "$generated": "Generated from config by `rebase resources`. Edit the declarations, not this file.",
3
+ "version": 1,
4
+ "resources": [
5
+ {
6
+ "kind": "database",
7
+ "key": "(default)",
8
+ "engine": "postgres",
9
+ "transport": "server",
10
+ "usedBy": [
11
+ "collection:users"
12
+ ]
13
+ },
14
+ {
15
+ "kind": "function",
16
+ "key": "hello",
17
+ "engine": "http",
18
+ "transport": "server",
19
+ "options": {
20
+ "portable": true,
21
+ "file": "backend/functions/hello.ts"
22
+ }
23
+ }
24
+ ]
25
+ }
@@ -0,0 +1,28 @@
1
+ {
2
+ "$generated": "Generated from config by `rebase resources`. Edit the declarations, not this file.",
3
+ "version": 1,
4
+ "resources": [
5
+ {
6
+ "kind": "database",
7
+ "key": "(default)",
8
+ "engine": "postgres",
9
+ "transport": "server",
10
+ "usedBy": [
11
+ "collection:authors",
12
+ "collection:posts",
13
+ "collection:tags",
14
+ "collection:users"
15
+ ]
16
+ },
17
+ {
18
+ "kind": "function",
19
+ "key": "hello",
20
+ "engine": "http",
21
+ "transport": "server",
22
+ "options": {
23
+ "portable": true,
24
+ "file": "backend/functions/hello.ts"
25
+ }
26
+ }
27
+ ]
28
+ }
@@ -0,0 +1,28 @@
1
+ {
2
+ "$generated": "Generated from config by `rebase resources`. Edit the declarations, not this file.",
3
+ "version": 1,
4
+ "resources": [
5
+ {
6
+ "kind": "database",
7
+ "key": "(default)",
8
+ "engine": "postgres",
9
+ "transport": "server",
10
+ "usedBy": [
11
+ "collection:categories",
12
+ "collection:orders",
13
+ "collection:products",
14
+ "collection:users"
15
+ ]
16
+ },
17
+ {
18
+ "kind": "function",
19
+ "key": "hello",
20
+ "engine": "http",
21
+ "transport": "server",
22
+ "options": {
23
+ "portable": true,
24
+ "file": "backend/functions/hello.ts"
25
+ }
26
+ }
27
+ ]
28
+ }
@@ -0,0 +1,22 @@
1
+ {
2
+ "$generated": "Generated from config by `rebase resources`. Edit the declarations, not this file.",
3
+ "version": 1,
4
+ "resources": [
5
+ {
6
+ "kind": "database",
7
+ "key": "(default)",
8
+ "engine": "postgres",
9
+ "transport": "server"
10
+ },
11
+ {
12
+ "kind": "function",
13
+ "key": "hello",
14
+ "engine": "http",
15
+ "transport": "server",
16
+ "options": {
17
+ "portable": true,
18
+ "file": "backend/functions/hello.ts"
19
+ }
20
+ }
21
+ ]
22
+ }
@@ -169,10 +169,11 @@ VITE_API_URL=
169
169
  # STORAGE_TYPE=local
170
170
 
171
171
  # --- Local filesystem (default — great for dev and single-server deployments) --
172
- # Unset, files land in <project>/uploads, under `rebase dev` and in the server
173
- # `rebase eject` writes alike. A relative path is read from the directory the
174
- # server runs in: the project root under `rebase dev`, backend/ after
175
- # `rebase eject`. So ./uploads names two places, and an absolute path one.
172
+ # Unset, files land in <project>/uploads, under `rebase dev`, `rebase start`
173
+ # and in the server `rebase eject` writes alike. A relative path is read from
174
+ # the directory the server runs in: the project root under `rebase dev`,
175
+ # backend/ after `rebase eject`. So ./uploads names two places, and an absolute
176
+ # path one.
176
177
  # STORAGE_PATH=/absolute/path/to/uploads
177
178
  # FORCE_LOCAL_STORAGE=true # Only with a durable volume holding STORAGE_PATH
178
179
 
@@ -1,5 +1,3 @@
1
- import React from "react";
2
-
3
1
  import "@fontsource/jetbrains-mono";
4
2
  import "@fontsource-variable/inter";
5
3
  import "@fontsource-variable/instrument-sans";
@@ -19,14 +17,17 @@ import { collections } from "virtual:rebase-collections";
19
17
  const API_URL = import.meta.env.VITE_API_URL;
20
18
  const GOOGLE_CLIENT_ID = import.meta.env.VITE_GOOGLE_CLIENT_ID;
21
19
 
22
- export function App() {
23
- const rebaseClient = React.useMemo(() => createRebaseClient({
24
- baseUrl: API_URL,
25
- // Store the refresh token in an httpOnly cookie (XSS-safe) rather than
26
- // localStorage. The backend issues it via `auth.cookieAuth`.
27
- auth: { authFlowMode: "cookie" }
28
- }), []);
20
+ // One client for the page, created when this module loads. Inside a component,
21
+ // even in a `useMemo`, `<React.StrictMode>` builds a second one in development,
22
+ // and both restore the session and open their own connections.
23
+ const rebaseClient = createRebaseClient({
24
+ baseUrl: API_URL,
25
+ // Store the refresh token in an httpOnly cookie (XSS-safe) rather than
26
+ // localStorage. The backend issues it via `auth.cookieAuth`.
27
+ auth: { authFlowMode: "cookie" }
28
+ });
29
29
 
30
+ export function App() {
30
31
  const authController = useRebaseAuthController({
31
32
  client: rebaseClient,
32
33
  googleClientId: GOOGLE_CLIENT_ID
@@ -1,266 +0,0 @@
1
- import chalk from "chalk";
2
- import arg from "arg";
3
- //#region src/utils/redact.ts
4
- /**
5
- * Connection-string passwords, replaced before a line is printed.
6
- *
7
- * A refusal that repeats what was typed repeats a password with it, and these
8
- * lines land in terminals, CI logs and issues. `user:***@` is the form every
9
- * other message uses: the driver's `redactConnectionPasswords` in
10
- * `@rebasepro/server-postgres`, which its entry does not export, and this
11
- * mirrors. Covers the user-info password and a `password=` parameter.
12
- */
13
- function redactConnectionPasswords(text) {
14
- return text.replace(/(\b[a-z][a-z0-9+.-]*:\/\/[^\s/?#@'"]*):[^\s/?#'"]*@/gi, "$1:***@").replace(/(\bpassword\s*=\s*)('[^']*'|[^\s&'"]+)/gi, "$1***");
15
- }
16
- //#endregion
17
- //#region src/utils/unknown-command.ts
18
- /**
19
- * What every command family says when it does not recognise a subcommand.
20
- *
21
- * Six families each did it differently, and the differences were not stylistic:
22
- *
23
- * rebase statsu "Unknown command: statsu" + the whole top-level help
24
- * rebase auth foo "Unknown auth command: foo" + the whole auth help
25
- * rebase apps foo "Unknown subcommand: foo" — which command's?
26
- * rebase skills foo "Unknown skills subcommand: foo" + the whole help
27
- * rebase telemetry x the help, silently, with exit code 1 and no error line
28
- * rebase cloud debug x both a JSON error *and* the human one, in JSON mode
29
- *
30
- * Dumping a screen of help after an error is the wrong shape: the one line that
31
- * says what happened scrolls off the top, and in a CI log it is buried in
32
- * forty. None of them offered the correction, which for a typo is the entire
33
- * answer — `statsu` is one transposition from `status`.
34
- *
35
- * So: one line, the near-miss when there is one, and where to read the rest.
36
- */
37
- /**
38
- * Damerau–Levenshtein distance, capped by the caller.
39
- *
40
- * The transposition is the reason this is not plain Levenshtein: `statsu` for
41
- * `status` and `craete` for `create` are the two most common typing errors
42
- * there are, and both are distance 2 without it — the same as two unrelated
43
- * edits, which is too far to suggest anything at all.
44
- */
45
- function editDistance(a, b) {
46
- const rows = a.length + 1;
47
- const cols = b.length + 1;
48
- const d = Array.from({ length: rows }, () => new Array(cols).fill(0));
49
- for (let i = 0; i < rows; i++) d[i][0] = i;
50
- for (let j = 0; j < cols; j++) d[0][j] = j;
51
- for (let i = 1; i < rows; i++) for (let j = 1; j < cols; j++) {
52
- const cost = a[i - 1] === b[j - 1] ? 0 : 1;
53
- d[i][j] = Math.min(d[i - 1][j] + 1, d[i][j - 1] + 1, d[i - 1][j - 1] + cost);
54
- if (i > 1 && j > 1 && a[i - 1] === b[j - 2] && a[i - 2] === b[j - 1]) d[i][j] = Math.min(d[i][j], d[i - 2][j - 2] + 1);
55
- }
56
- return d[a.length][b.length];
57
- }
58
- /**
59
- * The one command the user probably meant, or nothing.
60
- *
61
- * Tight on purpose. A suggestion that is wrong is worse than none: it sends
62
- * someone to read the help for a command that was never the answer, and on a
63
- * family like `db` — where `push` and `pull` are one edit apart — a loose
64
- * threshold would confidently propose the destructive neighbour of what they
65
- * typed. So: one edit for a short word, two for a long one, and a prefix match
66
- * always counts (`rebase cloud dep` for `deploy`).
67
- */
68
- function suggestCommand(typed, known) {
69
- const word = typed.toLowerCase();
70
- if (!word) return void 0;
71
- if (word.length >= 3) {
72
- const prefixed = known.filter((name) => name.toLowerCase().startsWith(word));
73
- if (prefixed.length === 1) return prefixed[0];
74
- }
75
- const budget = word.length <= 4 ? 1 : 2;
76
- let best;
77
- for (const candidate of known) {
78
- const distance = editDistance(word, candidate.toLowerCase());
79
- if (distance > budget) continue;
80
- if (!best || distance < best.distance) best = {
81
- name: candidate,
82
- distance
83
- };
84
- }
85
- return best?.name;
86
- }
87
- /**
88
- * The family command a word names exactly, as `<family> <command>`, or nothing.
89
- *
90
- * `rebase deploy` is `rebase cloud deploy`: the word is a whole command one
91
- * level down. Exact only — a near miss there is a guess about both the family
92
- * and the command.
93
- */
94
- function familyCommand(typed, families) {
95
- const word = typed.toLowerCase();
96
- for (const [family, commands] of Object.entries(families)) {
97
- const match = commands.find((name) => name.toLowerCase() === word);
98
- if (match) return `${family} ${match}`;
99
- }
100
- }
101
- /**
102
- * The line itself, without printing it — so callers that must answer in JSON
103
- * (the cloud family, whose `fail()` writes a machine-readable envelope) use the
104
- * same words as the ones that print.
105
- *
106
- * `family` is empty for the top level: `rebase --help`, not `rebase --help`.
107
- * `families` are the command families below this level, by name, whose
108
- * commands a word may be one of.
109
- */
110
- function unknownCommandMessage(typed, known, family = "", families = {}) {
111
- const what = family ? `${family} command` : "command";
112
- const suggestion = typed ? suggestCommand(typed, known) ?? familyCommand(typed, families) : void 0;
113
- const help = family ? `rebase ${family} --help` : "rebase --help";
114
- return suggestion ? `Unknown ${what} "${typed}" — did you mean \`${suggestion}\`? Run \`${help}\` for the rest.` : `Unknown ${what} "${typed ?? ""}". Run \`${help}\` for the ones there are.`;
115
- }
116
- /**
117
- * Print the line to stderr and exit 1.
118
- *
119
- * stderr, not stdout: a mistyped command is a failure, and the families with a
120
- * `--json` mode guarantee that stdout holds only their envelope.
121
- */
122
- function unknownCommand(typed, known, family = "", families = {}) {
123
- console.error(chalk.red(`✗ ${unknownCommandMessage(typed, known, family, families)}`));
124
- process.exit(1);
125
- }
126
- //#endregion
127
- //#region src/utils/args.ts
128
- /**
129
- * Argument parsing for the commands that take positional arguments.
130
- *
131
- * Every command in the small group used to parse its own line the same way:
132
- * `arg(spec, { argv: rawArgs.slice(4), permissive: true })`, then read `_[0]`
133
- * and `_[1]`. Both halves of that are wrong in a way that costs data.
134
- *
135
- * - **`permissive: true` turns an unknown flag into a positional.** `arg`
136
- * pushes an undeclared flag into `_` as a bare token, so `_[1]` is whatever
137
- * came second on the line, flag or not. `rebase auth reset-password
138
- * bob@example.com --debug` set Bob's password to the literal `--debug` — and
139
- * `--debug` is what `bin/rebase.js` prints after *every* failure as the thing
140
- * to re-run with, so the single most likely next keystroke after a failed
141
- * reset was the one that reset the account to a two-word string.
142
- * - **`slice(4)` assumes the command words are at fixed indices.** They are
143
- * not: a flag before the command shifts everything, so `rebase --debug auth
144
- * reset-password bob@example.com NewPass1!` read the email as
145
- * `reset-password` and the password as `bob@example.com`.
146
- *
147
- * So: parse the *whole* line — `rawArgs` is `process.argv` — against a spec
148
- * strictly, with no permissive mode. `arg` then consumes every flag wherever it
149
- * appears and rejects the ones nobody declared, which leaves `_` holding the
150
- * command words followed by the real positionals, in order and at a known
151
- * offset. An unrecognised flag becomes an error naming the command's help,
152
- * which is the only safe answer: the alternative is guessing that it was meant
153
- * as a value.
154
- *
155
- * `commands/cloud/index.ts` resolves its positionals against its own spec for
156
- * the same reason; this is that idea for the commands whose positionals are
157
- * credentials rather than resource names.
158
- */
159
- /**
160
- * Flags accepted on top of whatever a command declares.
161
- *
162
- * `--debug` is read by `bin/rebase.js` off `process.argv` and never by a
163
- * command, but it has to be *declared* somewhere or strict parsing rejects the
164
- * exact flag the CLI tells people to add. `--help`/`-h` are answered by each
165
- * command's dispatcher before any work happens.
166
- */
167
- var GLOBAL_COMMAND_FLAGS = {
168
- "--debug": Boolean,
169
- "--help": Boolean,
170
- "-h": "--help"
171
- };
172
- /**
173
- * The declared long flag an undeclared one was probably meant to be: one edit
174
- * or one swap away (`--form` → `--from`), or an abbreviation of exactly one
175
- * (`--anon` → `--anonymize`).
176
- */
177
- function closestDeclaredFlag(typed, declared) {
178
- if (!typed.startsWith("--")) return void 0;
179
- const near = suggestCommand(typed.slice(2), declared.filter((flag) => flag.startsWith("--")).map((flag) => flag.slice(2)));
180
- return near ? `--${near}` : void 0;
181
- }
182
- /** Past this many, a refusal names the help page rather than listing every flag. */
183
- var LISTED_FLAGS_MAX = 8;
184
- /**
185
- * The sentence an undeclared flag is refused with — the one `db backup`,
186
- * `db restore` and `db branch` say, so one CLI answers a typo one way:
187
- *
188
- * `rebase db pull` has no option --anonymise — did you mean --anonymize?
189
- * Nothing was run. See `rebase db pull --help`.
190
- *
191
- * With nothing close it lists what the command takes, when that list is short
192
- * enough to read.
193
- *
194
- * Exported for the commands that refuse a flag outside `parseCommandArgs`.
195
- */
196
- function unknownOptionMessage(command, typed, spec) {
197
- const near = closestDeclaredFlag(typed, Object.keys({
198
- ...GLOBAL_COMMAND_FLAGS,
199
- ...spec
200
- }));
201
- const own = Object.keys(spec).filter((flag) => flag.startsWith("--") && !(flag in GLOBAL_COMMAND_FLAGS));
202
- return `\`rebase ${command}\` has no option ${typed}${near ? ` — did you mean ${near}?` : own.length === 0 ? " — it takes no options." : own.length <= LISTED_FLAGS_MAX ? ` — it takes ${own.join(", ")}.` : "."} Nothing was run. See \`rebase ${command} --help\`.`;
203
- }
204
- /** Did the line ask for help? Answered before dispatch, never by a handler. */
205
- function wantsHelp(rawArgs) {
206
- return rawArgs.includes("--help") || rawArgs.includes("-h");
207
- }
208
- /**
209
- * The command line was wrong — nothing ran, and there is no stack to want.
210
- *
211
- * `bin/rebase.js` ends every failure with "Re-run with --debug for the stack
212
- * trace.", which is right when something broke inside a command and absurd for
213
- * a typo: the stack points at `arg` and this file, neither of which the person
214
- * who mistyped `--ouput` has any interest in. Worse, `--debug` is a flag, so
215
- * the suggested next command is *another* command line — and the one thing we
216
- * have just established is that the command line is what went wrong.
217
- *
218
- * Marked with a property rather than left to `instanceof`, because the reader
219
- * is `bin/rebase.js`: it imports the bundle, so the class object it would test
220
- * against is not the one this file compiled to.
221
- */
222
- var UsageError = class extends Error {
223
- isUsageError = true;
224
- constructor(message) {
225
- super(message);
226
- this.name = "UsageError";
227
- }
228
- };
229
- /**
230
- * Resolve a command's flags and positionals from the full `process.argv`.
231
- *
232
- * `commandWords` is how many words name the command itself — 2 for
233
- * `auth reset-password`, 1 for `start` — and is applied to the *parsed*
234
- * positionals rather than to `argv`, so a flag placed before the command no
235
- * longer shifts them.
236
- *
237
- * `command` names the command in error messages, e.g. `auth reset-password`.
238
- *
239
- * Throws on an unknown flag, on a positional that looks like a flag, and on
240
- * more positionals than the command takes. `bin/rebase.js` turns each into a
241
- * one-line `✗ …` and exit 1.
242
- */
243
- function parseCommandArgs({ spec, rawArgs, commandWords, command, maxPositionals }) {
244
- let parsed;
245
- try {
246
- parsed = arg({
247
- ...GLOBAL_COMMAND_FLAGS,
248
- ...spec
249
- }, { argv: rawArgs.slice(2) });
250
- } catch (err) {
251
- if (err instanceof Error && err.code === "ARG_UNKNOWN_OPTION") throw new UsageError(unknownOptionMessage(command, (/option: (\S+)$/.exec(err.message)?.[1] ?? err.message).split("=")[0], spec));
252
- throw err;
253
- }
254
- const positionals = parsed._.slice(commandWords);
255
- for (const value of positionals) if (value.startsWith("-")) throw new UsageError(`\`${value}\` looks like an option, not a value — pass it with an explicit flag (\`rebase ${command} --help\`).`);
256
- if (maxPositionals !== void 0 && positionals.length > maxPositionals) throw new UsageError(`rebase ${command} takes ${maxPositionals} argument${maxPositionals === 1 ? "" : "s"}, got ${positionals.length}: ${redactConnectionPasswords(positionals.join(" "))}`);
257
- return {
258
- flags: parsed,
259
- positionals,
260
- help: Boolean(parsed["--help"])
261
- };
262
- }
263
- //#endregion
264
- export { unknownCommand as a, suggestCommand as i, parseCommandArgs as n, unknownCommandMessage as o, wantsHelp as r, redactConnectionPasswords as s, UsageError as t };
265
-
266
- //# sourceMappingURL=args-BX0YD14a.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"args-BX0YD14a.js","names":[],"sources":["../src/utils/redact.ts","../src/utils/unknown-command.ts","../src/utils/args.ts"],"sourcesContent":["/**\n * Connection-string passwords, replaced before a line is printed.\n *\n * A refusal that repeats what was typed repeats a password with it, and these\n * lines land in terminals, CI logs and issues. `user:***@` is the form every\n * other message uses: the driver's `redactConnectionPasswords` in\n * `@rebasepro/server-postgres`, which its entry does not export, and this\n * mirrors. Covers the user-info password and a `password=` parameter.\n */\nexport function redactConnectionPasswords(text: string): string {\n return text\n .replace(/(\\b[a-z][a-z0-9+.-]*:\\/\\/[^\\s/?#@'\"]*):[^\\s/?#'\"]*@/gi, \"$1:***@\")\n .replace(/(\\bpassword\\s*=\\s*)('[^']*'|[^\\s&'\"]+)/gi, \"$1***\");\n}\n","/**\n * What every command family says when it does not recognise a subcommand.\n *\n * Six families each did it differently, and the differences were not stylistic:\n *\n * rebase statsu \"Unknown command: statsu\" + the whole top-level help\n * rebase auth foo \"Unknown auth command: foo\" + the whole auth help\n * rebase apps foo \"Unknown subcommand: foo\" — which command's?\n * rebase skills foo \"Unknown skills subcommand: foo\" + the whole help\n * rebase telemetry x the help, silently, with exit code 1 and no error line\n * rebase cloud debug x both a JSON error *and* the human one, in JSON mode\n *\n * Dumping a screen of help after an error is the wrong shape: the one line that\n * says what happened scrolls off the top, and in a CI log it is buried in\n * forty. None of them offered the correction, which for a typo is the entire\n * answer — `statsu` is one transposition from `status`.\n *\n * So: one line, the near-miss when there is one, and where to read the rest.\n */\nimport chalk from \"chalk\";\n\n/**\n * Damerau–Levenshtein distance, capped by the caller.\n *\n * The transposition is the reason this is not plain Levenshtein: `statsu` for\n * `status` and `craete` for `create` are the two most common typing errors\n * there are, and both are distance 2 without it — the same as two unrelated\n * edits, which is too far to suggest anything at all.\n */\nfunction editDistance(a: string, b: string): number {\n const rows = a.length + 1;\n const cols = b.length + 1;\n const d: number[][] = Array.from({ length: rows }, () => new Array<number>(cols).fill(0));\n\n for (let i = 0; i < rows; i++) d[i][0] = i;\n for (let j = 0; j < cols; j++) d[0][j] = j;\n\n for (let i = 1; i < rows; i++) {\n for (let j = 1; j < cols; j++) {\n const cost = a[i - 1] === b[j - 1] ? 0 : 1;\n d[i][j] = Math.min(\n d[i - 1][j] + 1,\n d[i][j - 1] + 1,\n d[i - 1][j - 1] + cost\n );\n if (i > 1 && j > 1 && a[i - 1] === b[j - 2] && a[i - 2] === b[j - 1]) {\n d[i][j] = Math.min(d[i][j], d[i - 2][j - 2] + 1);\n }\n }\n }\n return d[a.length][b.length];\n}\n\n/**\n * The one command the user probably meant, or nothing.\n *\n * Tight on purpose. A suggestion that is wrong is worse than none: it sends\n * someone to read the help for a command that was never the answer, and on a\n * family like `db` — where `push` and `pull` are one edit apart — a loose\n * threshold would confidently propose the destructive neighbour of what they\n * typed. So: one edit for a short word, two for a long one, and a prefix match\n * always counts (`rebase cloud dep` for `deploy`).\n */\nexport function suggestCommand(typed: string, known: readonly string[]): string | undefined {\n const word = typed.toLowerCase();\n if (!word) return undefined;\n\n // An abbreviation, but only when it names exactly one command. \"res\" is a\n // prefix of both `reset` and `restore`, and picking whichever comes first\n // in the list would be a coin toss dressed up as an answer.\n if (word.length >= 3) {\n const prefixed = known.filter(name => name.toLowerCase().startsWith(word));\n if (prefixed.length === 1) return prefixed[0];\n }\n\n const budget = word.length <= 4 ? 1 : 2;\n let best: { name: string; distance: number } | undefined;\n\n for (const candidate of known) {\n const distance = editDistance(word, candidate.toLowerCase());\n if (distance > budget) continue;\n if (!best || distance < best.distance) best = { name: candidate, distance };\n }\n\n return best?.name;\n}\n\n/**\n * The family command a word names exactly, as `<family> <command>`, or nothing.\n *\n * `rebase deploy` is `rebase cloud deploy`: the word is a whole command one\n * level down. Exact only — a near miss there is a guess about both the family\n * and the command.\n */\nfunction familyCommand(typed: string, families: Readonly<Record<string, readonly string[]>>): string | undefined {\n const word = typed.toLowerCase();\n for (const [family, commands] of Object.entries(families)) {\n const match = commands.find(name => name.toLowerCase() === word);\n if (match) return `${family} ${match}`;\n }\n return undefined;\n}\n\n/**\n * The line itself, without printing it — so callers that must answer in JSON\n * (the cloud family, whose `fail()` writes a machine-readable envelope) use the\n * same words as the ones that print.\n *\n * `family` is empty for the top level: `rebase --help`, not `rebase --help`.\n * `families` are the command families below this level, by name, whose\n * commands a word may be one of.\n */\nexport function unknownCommandMessage(\n typed: string | undefined,\n known: readonly string[],\n family = \"\",\n families: Readonly<Record<string, readonly string[]>> = {}\n): string {\n const what = family ? `${family} command` : \"command\";\n const suggestion = typed ? suggestCommand(typed, known) ?? familyCommand(typed, families) : undefined;\n const help = family ? `rebase ${family} --help` : \"rebase --help\";\n\n return suggestion\n ? `Unknown ${what} \"${typed}\" — did you mean \\`${suggestion}\\`? Run \\`${help}\\` for the rest.`\n : `Unknown ${what} \"${typed ?? \"\"}\". Run \\`${help}\\` for the ones there are.`;\n}\n\n/**\n * Print the line to stderr and exit 1.\n *\n * stderr, not stdout: a mistyped command is a failure, and the families with a\n * `--json` mode guarantee that stdout holds only their envelope.\n */\nexport function unknownCommand(\n typed: string | undefined,\n known: readonly string[],\n family = \"\",\n families: Readonly<Record<string, readonly string[]>> = {}\n): never {\n console.error(chalk.red(`✗ ${unknownCommandMessage(typed, known, family, families)}`));\n process.exit(1);\n}\n","/**\n * Argument parsing for the commands that take positional arguments.\n *\n * Every command in the small group used to parse its own line the same way:\n * `arg(spec, { argv: rawArgs.slice(4), permissive: true })`, then read `_[0]`\n * and `_[1]`. Both halves of that are wrong in a way that costs data.\n *\n * - **`permissive: true` turns an unknown flag into a positional.** `arg`\n * pushes an undeclared flag into `_` as a bare token, so `_[1]` is whatever\n * came second on the line, flag or not. `rebase auth reset-password\n * bob@example.com --debug` set Bob's password to the literal `--debug` — and\n * `--debug` is what `bin/rebase.js` prints after *every* failure as the thing\n * to re-run with, so the single most likely next keystroke after a failed\n * reset was the one that reset the account to a two-word string.\n * - **`slice(4)` assumes the command words are at fixed indices.** They are\n * not: a flag before the command shifts everything, so `rebase --debug auth\n * reset-password bob@example.com NewPass1!` read the email as\n * `reset-password` and the password as `bob@example.com`.\n *\n * So: parse the *whole* line — `rawArgs` is `process.argv` — against a spec\n * strictly, with no permissive mode. `arg` then consumes every flag wherever it\n * appears and rejects the ones nobody declared, which leaves `_` holding the\n * command words followed by the real positionals, in order and at a known\n * offset. An unrecognised flag becomes an error naming the command's help,\n * which is the only safe answer: the alternative is guessing that it was meant\n * as a value.\n *\n * `commands/cloud/index.ts` resolves its positionals against its own spec for\n * the same reason; this is that idea for the commands whose positionals are\n * credentials rather than resource names.\n */\nimport arg from \"arg\";\n\nimport { redactConnectionPasswords } from \"./redact\";\nimport { suggestCommand } from \"./unknown-command\";\n\n/**\n * Flags accepted on top of whatever a command declares.\n *\n * `--debug` is read by `bin/rebase.js` off `process.argv` and never by a\n * command, but it has to be *declared* somewhere or strict parsing rejects the\n * exact flag the CLI tells people to add. `--help`/`-h` are answered by each\n * command's dispatcher before any work happens.\n */\nexport const GLOBAL_COMMAND_FLAGS = {\n \"--debug\": Boolean,\n \"--help\": Boolean,\n \"-h\": \"--help\"\n} as const;\n\n/**\n * The declared long flag an undeclared one was probably meant to be: one edit\n * or one swap away (`--form` → `--from`), or an abbreviation of exactly one\n * (`--anon` → `--anonymize`).\n */\nfunction closestDeclaredFlag(typed: string, declared: readonly string[]): string | undefined {\n if (!typed.startsWith(\"--\")) return undefined;\n const near = suggestCommand(typed.slice(2), declared.filter(flag => flag.startsWith(\"--\")).map(flag => flag.slice(2)));\n return near ? `--${near}` : undefined;\n}\n\n/** Past this many, a refusal names the help page rather than listing every flag. */\nconst LISTED_FLAGS_MAX = 8;\n\n/**\n * The sentence an undeclared flag is refused with — the one `db backup`,\n * `db restore` and `db branch` say, so one CLI answers a typo one way:\n *\n * `rebase db pull` has no option --anonymise — did you mean --anonymize?\n * Nothing was run. See `rebase db pull --help`.\n *\n * With nothing close it lists what the command takes, when that list is short\n * enough to read.\n *\n * Exported for the commands that refuse a flag outside `parseCommandArgs`.\n */\nexport function unknownOptionMessage(command: string, typed: string, spec: Readonly<Record<string, unknown>>): string {\n const declared = Object.keys({ ...GLOBAL_COMMAND_FLAGS, ...spec });\n const near = closestDeclaredFlag(typed, declared);\n const own = Object.keys(spec).filter(flag => flag.startsWith(\"--\") && !(flag in GLOBAL_COMMAND_FLAGS));\n const takes = near\n ? ` — did you mean ${near}?`\n : own.length === 0\n ? \" — it takes no options.\"\n : own.length <= LISTED_FLAGS_MAX ? ` — it takes ${own.join(\", \")}.` : \".\";\n\n return `\\`rebase ${command}\\` has no option ${typed}${takes} Nothing was run. See \\`rebase ${command} --help\\`.`;\n}\n\nexport interface ParsedCommand<S extends arg.Spec> {\n /** The declared flags, as `arg` resolved them. */\n flags: arg.Result<S>;\n /** What is left after the command words — never a flag. */\n positionals: string[];\n /** `--help` or `-h` appeared anywhere on the line. */\n help: boolean;\n}\n\n/** Did the line ask for help? Answered before dispatch, never by a handler. */\nexport function wantsHelp(rawArgs: string[]): boolean {\n return rawArgs.includes(\"--help\") || rawArgs.includes(\"-h\");\n}\n\n/**\n * The command line was wrong — nothing ran, and there is no stack to want.\n *\n * `bin/rebase.js` ends every failure with \"Re-run with --debug for the stack\n * trace.\", which is right when something broke inside a command and absurd for\n * a typo: the stack points at `arg` and this file, neither of which the person\n * who mistyped `--ouput` has any interest in. Worse, `--debug` is a flag, so\n * the suggested next command is *another* command line — and the one thing we\n * have just established is that the command line is what went wrong.\n *\n * Marked with a property rather than left to `instanceof`, because the reader\n * is `bin/rebase.js`: it imports the bundle, so the class object it would test\n * against is not the one this file compiled to.\n */\nexport class UsageError extends Error {\n readonly isUsageError = true;\n\n constructor(message: string) {\n super(message);\n this.name = \"UsageError\";\n }\n}\n\n/**\n * Resolve a command's flags and positionals from the full `process.argv`.\n *\n * `commandWords` is how many words name the command itself — 2 for\n * `auth reset-password`, 1 for `start` — and is applied to the *parsed*\n * positionals rather than to `argv`, so a flag placed before the command no\n * longer shifts them.\n *\n * `command` names the command in error messages, e.g. `auth reset-password`.\n *\n * Throws on an unknown flag, on a positional that looks like a flag, and on\n * more positionals than the command takes. `bin/rebase.js` turns each into a\n * one-line `✗ …` and exit 1.\n */\nexport function parseCommandArgs<S extends arg.Spec>({\n spec,\n rawArgs,\n commandWords,\n command,\n maxPositionals\n}: {\n spec: S;\n rawArgs: string[];\n commandWords: number;\n command: string;\n maxPositionals?: number;\n}): ParsedCommand<S> {\n let parsed: arg.Result<S> & { \"--help\"?: boolean };\n try {\n const merged = {\n ...GLOBAL_COMMAND_FLAGS,\n ...spec\n };\n parsed = arg(merged, { argv: rawArgs.slice(2) }) as arg.Result<S> & { \"--help\"?: boolean };\n } catch (err) {\n if (err instanceof Error && (err as arg.ArgError).code === \"ARG_UNKNOWN_OPTION\") {\n // arg's wording: `unknown or unexpected option: --form`.\n const typed = /option: (\\S+)$/.exec(err.message)?.[1] ?? err.message;\n throw new UsageError(unknownOptionMessage(command, typed.split(\"=\")[0], spec));\n }\n throw err;\n }\n\n const positionals = parsed._.slice(commandWords);\n\n for (const value of positionals) {\n // Only reachable past a `--` separator or for a bare `-`, since anything\n // else beginning with `-` was either declared or already rejected above.\n // Still refused rather than accepted: a value that looks like a flag is\n // as likely to be a typo as an argument, and one of these positions is a\n // password.\n if (value.startsWith(\"-\")) {\n throw new UsageError(\n `\\`${value}\\` looks like an option, not a value — pass it with an explicit flag ` +\n `(\\`rebase ${command} --help\\`).`\n );\n }\n }\n\n if (maxPositionals !== undefined && positionals.length > maxPositionals) {\n // Redacted: a connection string passed where a flag's value belongs\n // (`rebase db pull \"$PROD\"`) is still a password.\n throw new UsageError(\n `rebase ${command} takes ${maxPositionals} argument${maxPositionals === 1 ? \"\" : \"s\"}, ` +\n `got ${positionals.length}: ${redactConnectionPasswords(positionals.join(\" \"))}`\n );\n }\n\n return {\n flags: parsed,\n positionals,\n help: Boolean(parsed[\"--help\"])\n };\n}\n"],"mappings":"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"}