@rebasepro/cli 0.24.0 → 0.24.1-canary.gcb918fc

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 (122) hide show
  1. package/bin/rebase.js +3 -0
  2. package/dist/agent-setup-B31F00DC.js +324 -0
  3. package/dist/agent-setup-B31F00DC.js.map +1 -0
  4. package/dist/api-keys-BPBi5nOF.js +524 -0
  5. package/dist/api-keys-BPBi5nOF.js.map +1 -0
  6. package/dist/apps-CPXNElX4.js +256 -0
  7. package/dist/apps-CPXNElX4.js.map +1 -0
  8. package/dist/args-BnmsEXvu.js +109 -0
  9. package/dist/args-BnmsEXvu.js.map +1 -0
  10. package/dist/atlas-dev-database-DTMuEScS.js +62 -0
  11. package/dist/atlas-dev-database-DTMuEScS.js.map +1 -0
  12. package/dist/auth-DvY94ZuL.js +395 -0
  13. package/dist/auth-DvY94ZuL.js.map +1 -0
  14. package/dist/branch-pointer-DIhKbQLp.js +2 -0
  15. package/dist/build-BHvC6Bjs.js +336 -0
  16. package/dist/build-BHvC6Bjs.js.map +1 -0
  17. package/dist/bundle-BQhP_xiA.js +1617 -0
  18. package/dist/bundle-BQhP_xiA.js.map +1 -0
  19. package/dist/cli.d.ts +2 -2
  20. package/dist/cloud-K6P5IHUb.js +9667 -0
  21. package/dist/cloud-K6P5IHUb.js.map +1 -0
  22. package/dist/collection-drift-BKj_BoMq.js +106 -0
  23. package/dist/collection-drift-BKj_BoMq.js.map +1 -0
  24. package/dist/commands/api-keys.d.ts +2 -0
  25. package/dist/commands/console.d.ts +12 -0
  26. package/dist/commands/db.d.ts +64 -24
  27. package/dist/commands/dev.d.ts +103 -0
  28. package/dist/commands/generate_sdk.d.ts +26 -0
  29. package/dist/commands/skills.d.ts +22 -7
  30. package/dist/commands/storage.d.ts +46 -0
  31. package/dist/consent-BWHaYx5p.js +76 -0
  32. package/dist/consent-BWHaYx5p.js.map +1 -0
  33. package/dist/console-Cs00E-dt.js +102 -0
  34. package/dist/console-Cs00E-dt.js.map +1 -0
  35. package/dist/context-BqhRydBg.js +1234 -0
  36. package/dist/context-BqhRydBg.js.map +1 -0
  37. package/dist/daemon-DBn-shxX.js +2 -0
  38. package/dist/{daemon-entry-LTFKtpHy.js → daemon-entry-Da-D3rmH.js} +3 -4
  39. package/dist/daemon-entry-Da-D3rmH.js.map +1 -0
  40. package/dist/db-YbPMFY4Y.js +1252 -0
  41. package/dist/db-YbPMFY4Y.js.map +1 -0
  42. package/dist/derive-QMPMcQZJ.js +484 -0
  43. package/dist/derive-QMPMcQZJ.js.map +1 -0
  44. package/dist/dev-CJ_dd9uP.js +1593 -0
  45. package/dist/dev-CJ_dd9uP.js.map +1 -0
  46. package/dist/dev-db/atlas-dev-database.d.ts +32 -0
  47. package/dist/dev-db/daemon-entry.d.ts +13 -0
  48. package/dist/dev-preflight-N1evJL3S.js +2 -0
  49. package/dist/doctor-CIcpWl_L.js +771 -0
  50. package/dist/doctor-CIcpWl_L.js.map +1 -0
  51. package/dist/eject-BT6t37D0.js +335 -0
  52. package/dist/eject-BT6t37D0.js.map +1 -0
  53. package/dist/fold-static-C1EtHlfV.js +201 -0
  54. package/dist/fold-static-C1EtHlfV.js.map +1 -0
  55. package/dist/function-portability-BIvlvR9q.js +265 -0
  56. package/dist/function-portability-BIvlvR9q.js.map +1 -0
  57. package/dist/generate_sdk-UV635zSe.js +477 -0
  58. package/dist/generate_sdk-UV635zSe.js.map +1 -0
  59. package/dist/index.es.js +150 -24674
  60. package/dist/index.es.js.map +1 -1
  61. package/dist/init-fXeBcxuN.js +1042 -0
  62. package/dist/init-fXeBcxuN.js.map +1 -0
  63. package/dist/manifest-DzlC60WX.js +871 -0
  64. package/dist/manifest-DzlC60WX.js.map +1 -0
  65. package/dist/normalize-imports-CmVdE9iZ.js +100 -0
  66. package/dist/normalize-imports-CmVdE9iZ.js.map +1 -0
  67. package/dist/{package-manager-CXQevsE1.js → package-manager-DSEOwTqM.js} +2 -2
  68. package/dist/{package-manager-CXQevsE1.js.map → package-manager-DSEOwTqM.js.map} +1 -1
  69. package/dist/path-flags-DUBuI2i1.js +77 -0
  70. package/dist/path-flags-DUBuI2i1.js.map +1 -0
  71. package/dist/precompress.d.ts +17 -0
  72. package/dist/prepare-Btn9Ewbn.js +1052 -0
  73. package/dist/prepare-Btn9Ewbn.js.map +1 -0
  74. package/dist/{project-DmaGLmH2.js → project-Wp2zoyeN.js} +33 -4
  75. package/dist/project-Wp2zoyeN.js.map +1 -0
  76. package/dist/resolve-CJLkfFV0.js +2 -0
  77. package/dist/resources-3J3UQg5r.js +161 -0
  78. package/dist/resources-3J3UQg5r.js.map +1 -0
  79. package/dist/schema-ovO8qJDw.js +125 -0
  80. package/dist/schema-ovO8qJDw.js.map +1 -0
  81. package/dist/skills-DTFibT1m.js +642 -0
  82. package/dist/skills-DTFibT1m.js.map +1 -0
  83. package/dist/start-rU2e3Kp5.js +228 -0
  84. package/dist/start-rU2e3Kp5.js.map +1 -0
  85. package/dist/{state-C59Elrnt.js → state-Do7ZJYkF.js} +49 -4
  86. package/dist/state-Do7ZJYkF.js.map +1 -0
  87. package/dist/status-Cfy0TtSb.js +351 -0
  88. package/dist/status-Cfy0TtSb.js.map +1 -0
  89. package/dist/storage-DyvrSTH3.js +303 -0
  90. package/dist/storage-DyvrSTH3.js.map +1 -0
  91. package/dist/telemetry-CAWNCugs.js +338 -0
  92. package/dist/telemetry-CAWNCugs.js.map +1 -0
  93. package/dist/telemetry-DbLqGIBW.js +129 -0
  94. package/dist/telemetry-DbLqGIBW.js.map +1 -0
  95. package/dist/unknown-command-DNwDcX3w.js +98 -0
  96. package/dist/unknown-command-DNwDcX3w.js.map +1 -0
  97. package/dist/upgrade-D-5-Od96.js +1265 -0
  98. package/dist/upgrade-D-5-Od96.js.map +1 -0
  99. package/dist/utils/project.d.ts +16 -0
  100. package/dist/utils/temp-archive.d.ts +9 -0
  101. package/dist/{version-DsgBmP22.js → version-Buyyo1ut.js} +2 -2
  102. package/dist/{version-DsgBmP22.js.map → version-Buyyo1ut.js.map} +1 -1
  103. package/package.json +7 -7
  104. package/templates/eject/backend/src/env.ts +1 -1
  105. package/templates/eject/backend/src/index.ts +10 -2
  106. package/templates/template/README.md +1 -1
  107. package/templates/template/ai-instructions.md +2 -1
  108. package/templates/template/frontend/vite.config.ts +13 -3
  109. package/templates/template/scripts/example.ts +2 -1
  110. package/dist/branch-pointer-kk9F5FBO.js +0 -111
  111. package/dist/branch-pointer-kk9F5FBO.js.map +0 -1
  112. package/dist/constraints-DKGbNfUW.js +0 -48
  113. package/dist/constraints-DKGbNfUW.js.map +0 -1
  114. package/dist/daemon-C8p32i-n.js +0 -357
  115. package/dist/daemon-C8p32i-n.js.map +0 -1
  116. package/dist/daemon-entry-LTFKtpHy.js.map +0 -1
  117. package/dist/dev-preflight-CNLl4rdo.js +0 -367
  118. package/dist/dev-preflight-CNLl4rdo.js.map +0 -1
  119. package/dist/project-DmaGLmH2.js.map +0 -1
  120. package/dist/resolve-BAeRUBhg.js +0 -74
  121. package/dist/resolve-BAeRUBhg.js.map +0 -1
  122. package/dist/state-C59Elrnt.js.map +0 -1
@@ -0,0 +1,106 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ import { DEFAULT_DATA_SOURCE_KEY, getDataSourceCapabilities } from "@rebasepro/types";
4
+ //#region src/utils/collection-drift.ts
5
+ /**
6
+ * Which edits under `config/collections` can put the *SQL* schema out of date.
7
+ *
8
+ * `rebase dev` watches that directory and, on any change, either tells the
9
+ * developer to run `rebase schema generate` / `rebase db push` or runs them.
10
+ * The watcher is recursive and knows nothing about what it is watching, so it
11
+ * said that about every file under the directory — including a
12
+ * `collections/firestore/exercises.ts`, whose documents live in Firestore and
13
+ * for which there is no Drizzle schema to regenerate and no database to push
14
+ * to. Advice that is wrong on every edit is advice a developer learns to
15
+ * ignore, which is worse than none: the same box is the only warning for the
16
+ * Postgres collection next to it, where it is real.
17
+ *
18
+ * Two independent reasons a change cannot affect the SQL schema, both checked
19
+ * here:
20
+ *
21
+ * 1. **The loader would never read the file.** `loadCollectionsFromDirectory`
22
+ * reads the top level of the collections directory only — no recursion, no
23
+ * `index`, no tests, no declarations. A file it does not read cannot change
24
+ * what it returns, and the watcher must not claim otherwise.
25
+ * 2. **Every collection in it is served by another engine.** A Firestore or
26
+ * MongoDB collection has no table, no migration and no policies.
27
+ *
28
+ * The engine check reads the source text rather than importing the module: the
29
+ * CLI runs as plain Node and cannot evaluate a project's TypeScript, and a
30
+ * watcher must answer in the time between two keystrokes. Anything it cannot
31
+ * read confidently counts as SQL-affecting — a spurious warning is a nuisance,
32
+ * a suppressed one hides real drift.
33
+ */
34
+ /**
35
+ * Would `loadCollectionsFromDirectory` load this file?
36
+ *
37
+ * Mirrors that loader's own `isCollectionFile` plus its flat (non-recursive)
38
+ * scan. `relativePath` is relative to the collections directory, as `fs.watch`
39
+ * reports it.
40
+ */
41
+ function isLoadedCollectionFile(relativePath) {
42
+ const normalized = relativePath.split(path.sep).join("/");
43
+ if (normalized.includes("/")) return false;
44
+ const file = normalized;
45
+ if (!file.endsWith(".ts") && !file.endsWith(".js")) return false;
46
+ if (file.startsWith(".")) return false;
47
+ if (file.includes(".test.")) return false;
48
+ if (file.endsWith(".d.ts")) return false;
49
+ if (file === "index.ts" || file === "index.js") return false;
50
+ return true;
51
+ }
52
+ var ENGINE_LITERAL = /\bengine\s*:\s*["'`]([^"'`]+)["'`]/g;
53
+ var DATA_SOURCE_LITERAL = /\bdataSource\s*:\s*["'`]([^"'`]+)["'`]/g;
54
+ /**
55
+ * Drop comments, so a `// engine: "firestore"` in a docblock cannot silence a
56
+ * warning about the Postgres collection the file actually declares.
57
+ *
58
+ * Deliberately naive — it does not understand strings, so a `//` inside one
59
+ * (a URL in a default value) eats the rest of that line. That only ever removes
60
+ * text, and removing an engine literal makes this file *more* likely to warn,
61
+ * which is the side to be wrong on.
62
+ */
63
+ function stripComments(source) {
64
+ return source.replace(/\/\*[\s\S]*?\*\//g, " ").replace(/\/\/[^\n]*/g, " ");
65
+ }
66
+ function literals(source, pattern) {
67
+ const found = [];
68
+ for (const match of source.matchAll(pattern)) found.push(match[1]);
69
+ return found;
70
+ }
71
+ /**
72
+ * Does this collection source declare anything a SQL toolchain would own?
73
+ *
74
+ * The fallback order matches `resolveDataSource`: an explicit `engine` wins,
75
+ * and a collection that only names a `dataSource` is resolved as if the key
76
+ * were the engine — which is what that function does when no registry is
77
+ * available, and the CLI has none. An engine nobody recognises counts as
78
+ * relational, for the reason `isRelationalCollection` gives.
79
+ *
80
+ * A file declaring several collections is SQL-affecting if *any* of them is.
81
+ */
82
+ function declaresRelationalCollection(rawSource) {
83
+ const source = stripComments(rawSource);
84
+ const engines = literals(source, ENGINE_LITERAL);
85
+ const declared = engines.length > 0 ? engines : literals(source, DATA_SOURCE_LITERAL).filter((key) => key !== DEFAULT_DATA_SOURCE_KEY);
86
+ if (declared.length === 0) return true;
87
+ return declared.some((engine) => getDataSourceCapabilities(engine).supportsRelations);
88
+ }
89
+ /**
90
+ * Can this edit have changed the generated SQL schema?
91
+ *
92
+ * Answers `true` when it cannot tell — an unreadable file is a reason to warn,
93
+ * not a reason to go quiet.
94
+ */
95
+ function affectsSqlSchema(collectionsDir, relativePath) {
96
+ if (!isLoadedCollectionFile(relativePath)) return false;
97
+ try {
98
+ return declaresRelationalCollection(fs.readFileSync(path.join(collectionsDir, relativePath), "utf8"));
99
+ } catch {
100
+ return true;
101
+ }
102
+ }
103
+ //#endregion
104
+ export { isLoadedCollectionFile as n, affectsSqlSchema as t };
105
+
106
+ //# sourceMappingURL=collection-drift-BKj_BoMq.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"collection-drift-BKj_BoMq.js","names":[],"sources":["../src/utils/collection-drift.ts"],"sourcesContent":["/**\n * Which edits under `config/collections` can put the *SQL* schema out of date.\n *\n * `rebase dev` watches that directory and, on any change, either tells the\n * developer to run `rebase schema generate` / `rebase db push` or runs them.\n * The watcher is recursive and knows nothing about what it is watching, so it\n * said that about every file under the directory — including a\n * `collections/firestore/exercises.ts`, whose documents live in Firestore and\n * for which there is no Drizzle schema to regenerate and no database to push\n * to. Advice that is wrong on every edit is advice a developer learns to\n * ignore, which is worse than none: the same box is the only warning for the\n * Postgres collection next to it, where it is real.\n *\n * Two independent reasons a change cannot affect the SQL schema, both checked\n * here:\n *\n * 1. **The loader would never read the file.** `loadCollectionsFromDirectory`\n * reads the top level of the collections directory only — no recursion, no\n * `index`, no tests, no declarations. A file it does not read cannot change\n * what it returns, and the watcher must not claim otherwise.\n * 2. **Every collection in it is served by another engine.** A Firestore or\n * MongoDB collection has no table, no migration and no policies.\n *\n * The engine check reads the source text rather than importing the module: the\n * CLI runs as plain Node and cannot evaluate a project's TypeScript, and a\n * watcher must answer in the time between two keystrokes. Anything it cannot\n * read confidently counts as SQL-affecting — a spurious warning is a nuisance,\n * a suppressed one hides real drift.\n */\nimport fs from \"fs\";\nimport path from \"path\";\nimport { DEFAULT_DATA_SOURCE_KEY, getDataSourceCapabilities } from \"@rebasepro/types\";\n\n/**\n * Would `loadCollectionsFromDirectory` load this file?\n *\n * Mirrors that loader's own `isCollectionFile` plus its flat (non-recursive)\n * scan. `relativePath` is relative to the collections directory, as `fs.watch`\n * reports it.\n */\nexport function isLoadedCollectionFile(relativePath: string): boolean {\n const normalized = relativePath.split(path.sep).join(\"/\");\n // A nested path is a subdirectory, which the loader does not descend into.\n if (normalized.includes(\"/\")) return false;\n\n const file = normalized;\n if (!file.endsWith(\".ts\") && !file.endsWith(\".js\")) return false;\n if (file.startsWith(\".\")) return false;\n if (file.includes(\".test.\")) return false;\n if (file.endsWith(\".d.ts\")) return false;\n // The directory's own module: defaults live there, not a collection.\n if (file === \"index.ts\" || file === \"index.js\") return false;\n return true;\n}\n\nconst ENGINE_LITERAL = /\\bengine\\s*:\\s*[\"'`]([^\"'`]+)[\"'`]/g;\nconst DATA_SOURCE_LITERAL = /\\bdataSource\\s*:\\s*[\"'`]([^\"'`]+)[\"'`]/g;\n\n/**\n * Drop comments, so a `// engine: \"firestore\"` in a docblock cannot silence a\n * warning about the Postgres collection the file actually declares.\n *\n * Deliberately naive — it does not understand strings, so a `//` inside one\n * (a URL in a default value) eats the rest of that line. That only ever removes\n * text, and removing an engine literal makes this file *more* likely to warn,\n * which is the side to be wrong on.\n */\nfunction stripComments(source: string): string {\n return source.replace(/\\/\\*[\\s\\S]*?\\*\\//g, \" \").replace(/\\/\\/[^\\n]*/g, \" \");\n}\n\nfunction literals(source: string, pattern: RegExp): string[] {\n const found: string[] = [];\n for (const match of source.matchAll(pattern)) found.push(match[1]);\n return found;\n}\n\n/**\n * Does this collection source declare anything a SQL toolchain would own?\n *\n * The fallback order matches `resolveDataSource`: an explicit `engine` wins,\n * and a collection that only names a `dataSource` is resolved as if the key\n * were the engine — which is what that function does when no registry is\n * available, and the CLI has none. An engine nobody recognises counts as\n * relational, for the reason `isRelationalCollection` gives.\n *\n * A file declaring several collections is SQL-affecting if *any* of them is.\n */\nexport function declaresRelationalCollection(rawSource: string): boolean {\n const source = stripComments(rawSource);\n const engines = literals(source, ENGINE_LITERAL);\n const declared = engines.length > 0\n ? engines\n : literals(source, DATA_SOURCE_LITERAL).filter(key => key !== DEFAULT_DATA_SOURCE_KEY);\n\n if (declared.length === 0) return true;\n return declared.some(engine => getDataSourceCapabilities(engine).supportsRelations);\n}\n\n/**\n * Can this edit have changed the generated SQL schema?\n *\n * Answers `true` when it cannot tell — an unreadable file is a reason to warn,\n * not a reason to go quiet.\n */\nexport function affectsSqlSchema(collectionsDir: string, relativePath: string): boolean {\n if (!isLoadedCollectionFile(relativePath)) return false;\n try {\n return declaresRelationalCollection(\n fs.readFileSync(path.join(collectionsDir, relativePath), \"utf8\")\n );\n } catch {\n // Deleted between the event and the read, or unreadable. Both leave the\n // schema genuinely in question.\n return true;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwCA,SAAgB,uBAAuB,cAA+B;CAClE,MAAM,aAAa,aAAa,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,GAAG;CAExD,IAAI,WAAW,SAAS,GAAG,GAAG,OAAO;CAErC,MAAM,OAAO;CACb,IAAI,CAAC,KAAK,SAAS,KAAK,KAAK,CAAC,KAAK,SAAS,KAAK,GAAG,OAAO;CAC3D,IAAI,KAAK,WAAW,GAAG,GAAG,OAAO;CACjC,IAAI,KAAK,SAAS,QAAQ,GAAG,OAAO;CACpC,IAAI,KAAK,SAAS,OAAO,GAAG,OAAO;CAEnC,IAAI,SAAS,cAAc,SAAS,YAAY,OAAO;CACvD,OAAO;AACX;AAEA,IAAM,iBAAiB;AACvB,IAAM,sBAAsB;;;;;;;;;;AAW5B,SAAS,cAAc,QAAwB;CAC3C,OAAO,OAAO,QAAQ,qBAAqB,GAAG,CAAC,CAAC,QAAQ,eAAe,GAAG;AAC9E;AAEA,SAAS,SAAS,QAAgB,SAA2B;CACzD,MAAM,QAAkB,CAAC;CACzB,KAAK,MAAM,SAAS,OAAO,SAAS,OAAO,GAAG,MAAM,KAAK,MAAM,EAAE;CACjE,OAAO;AACX;;;;;;;;;;;;AAaA,SAAgB,6BAA6B,WAA4B;CACrE,MAAM,SAAS,cAAc,SAAS;CACtC,MAAM,UAAU,SAAS,QAAQ,cAAc;CAC/C,MAAM,WAAW,QAAQ,SAAS,IAC5B,UACA,SAAS,QAAQ,mBAAmB,CAAC,CAAC,QAAO,QAAO,QAAQ,uBAAuB;CAEzF,IAAI,SAAS,WAAW,GAAG,OAAO;CAClC,OAAO,SAAS,MAAK,WAAU,0BAA0B,MAAM,CAAC,CAAC,iBAAiB;AACtF;;;;;;;AAQA,SAAgB,iBAAiB,gBAAwB,cAA+B;CACpF,IAAI,CAAC,uBAAuB,YAAY,GAAG,OAAO;CAClD,IAAI;EACA,OAAO,6BACH,GAAG,aAAa,KAAK,KAAK,gBAAgB,YAAY,GAAG,MAAM,CACnE;CACJ,QAAQ;EAGJ,OAAO;CACX;AACJ"}
@@ -1,6 +1,8 @@
1
1
  import { type ApiKeyMasked, type ScopeSummary } from "@rebasepro/types";
2
2
  /** Everything the switch below dispatches, for the did-you-mean. */
3
3
  export declare const API_KEYS_SUBCOMMANDS: readonly ["list", "get", "create", "revoke", "scopes"];
4
+ /** The running backend's URL. Shared with `rebase storage`. */
5
+ export declare function resolveBaseUrl(env: Record<string, string>, projectRoot?: string): string;
4
6
  /**
5
7
  * What to print when a request fails: the server's error code and message,
6
8
  * which is what says why — `SCOPE_EXCEEDS_CREATOR`, `INVALID_SCOPES`,
@@ -0,0 +1,12 @@
1
+ export declare const CONSOLE_FLAGS: {
2
+ readonly "--eval": StringConstructor;
3
+ readonly "-e": "--eval";
4
+ readonly "--collections": StringConstructor;
5
+ readonly "-c": "--collections";
6
+ readonly "--database-url": StringConstructor;
7
+ readonly "--docker": BooleanConstructor;
8
+ };
9
+ /** Where the REPL keeps its history: the project's own `.rebase/`, which the scaffold ignores. */
10
+ export declare function consoleHistoryPath(projectRoot: string): string;
11
+ export declare function printConsoleHelp(): void;
12
+ export declare function consoleCommand(rawArgs: string[]): Promise<void>;
@@ -1,5 +1,6 @@
1
1
  import { type PreparedDatabase } from "../dev-db/prepare.js";
2
2
  import { type DevDatabase } from "../dev-db/resolve.js";
3
+ import type { AtlasDevDatabase } from "../dev-db/atlas-dev-database.js";
3
4
  /**
4
5
  * The connection string a prepared database actually answers on.
5
6
  *
@@ -69,30 +70,35 @@ export declare class ManagedDatabaseRefusal extends Error {
69
70
  /** Print a refusal exactly as the guards used to print it themselves. */
70
71
  export declare function printManagedDatabaseRefusal(refusal: ManagedDatabaseRefusal): void;
71
72
  /**
72
- * Stop an Atlas-backed subcommand before it fails inside Atlas on the managed
73
+ * The variable the driver reads for Atlas's dev database
74
+ * (`ATLAS_DEV_URL_ENV` in `@rebasepro/server-postgres`, which this CLI does not
75
+ * depend on).
76
+ */
77
+ export declare const ATLAS_DEV_URL_ENV = "REBASE_ATLAS_DEV_URL";
78
+ /**
79
+ * The empty database an Atlas-backed subcommand plans against, on the managed
73
80
  * development database.
74
81
  *
75
- * **PGlite serves exactly one database.** `CREATE DATABASE "postgres_dev_diff"`
76
- * against it reports success and creates nothing, so Atlas connects its dev-url
77
- * straight back to the database it is meant to be comparing against, finds the
78
- * project's own tables there, and stops with
79
- * `connected database is not clean: found schema "public"`. Verified on a
80
- * completely fresh scaffold, so this is not a leftover-state problem that a
81
- * reset would fix — the model does not fit the engine.
82
- *
83
- * Before this guard the failure was worse than unhelpful. The first thing the
84
- * reader hit was `pq: SSL is not enabled on the server`, whose remedy box said
85
- * to append `sslmode=disable` to `DATABASE_URL` — a variable `rebase init`
86
- * deliberately leaves unset, which is the very reason the managed database was
87
- * in use. Two errors deep, about a variable that does not exist, for a command
88
- * the quickstart told them to run.
89
- *
90
- * There is nothing to fix by trying harder here: Atlas's other dev-url option
91
- * is `docker://`, and needing Docker is precisely what the managed database
92
- * exists to avoid. So this says what the managed database can and cannot do,
93
- * and names the two things that work.
82
+ * **PGlite serves exactly one database.** Atlas computes a change by replaying
83
+ * the desired schema into a second, empty one; on a real server the driver
84
+ * makes it as `<db>_dev_diff`, but `CREATE DATABASE` against PGlite reports
85
+ * success and creates nothing, so Atlas connected straight back to the
86
+ * database it was comparing and stopped with `connected database is not
87
+ * clean`. These commands were refused outright on the managed database — and
88
+ * with them every way to drop, rename or narrow a column there, since boot
89
+ * only adds.
90
+ *
91
+ * So a throwaway second PGlite is started for the command instead — in
92
+ * memory, in this process, gone when the command ends — and handed to the
93
+ * driver as `REBASE_ATLAS_DEV_URL`. Nothing else about the push changes: the
94
+ * destructive gate is the driver's, and holds as it does everywhere.
95
+ *
96
+ * `undefined` when none is needed: another database kind (the driver makes its
97
+ * own), a subcommand that is not Atlas's, or a `REBASE_ATLAS_DEV_URL` the
98
+ * developer set. When one cannot be started — PGlite is an optional
99
+ * dependency — the old refusal stands, with the reason.
94
100
  */
95
- export declare function refuseAtlasOnManagedDatabase(rawArgs: string[], kind: string): void;
101
+ export declare function atlasDevDatabaseFor(rawArgs: string[], kind: string, env: Readonly<Record<string, string | undefined>>, start?: () => Promise<AtlasDevDatabase>): Promise<AtlasDevDatabase | undefined>;
96
102
  /**
97
103
  * Stop `rebase db branch` before it reports a branch that is not one.
98
104
  *
@@ -121,11 +127,25 @@ export declare function refuseAtlasOnManagedDatabase(rawArgs: string[], kind: st
121
127
  * an invitation to do the broken thing, and `info` would confirm a size for a
122
128
  * database that was never made.
123
129
  *
124
- * `refuseAtlasOnManagedDatabase` above is the same shape for a different
125
- * reason, and its comment named `branch` as one of the subcommands it does not
126
- * cover. It does now.
130
+ * Push, generate and migrate had the same problem — Atlas's scratch database
131
+ * is a second database too — and get a throwaway PGlite of their own instead
132
+ * (`atlasDevDatabaseFor` above). A branch cannot: it is meant to keep the
133
+ * project's data, and a throwaway instance has none.
127
134
  */
128
135
  export declare function refuseBranchOnManagedDatabase(rawArgs: string[], kind: string): void;
136
+ /**
137
+ * Resolve the active driver's CLI and the environment it should run in.
138
+ *
139
+ * Shared by the two runners below. Deliberately does NOT resolve a database:
140
+ * `schema generate` reads collection files and writes one file, and starting a
141
+ * database to run it would be a side effect nobody asked for.
142
+ */
143
+ export declare function resolveDriverCli(): Promise<{
144
+ projectRoot: string;
145
+ backendDir: string;
146
+ pluginCli: string;
147
+ env: Record<string, string>;
148
+ }>;
129
149
  /**
130
150
  * The one place this CLI spawns the driver's own CLI.
131
151
  *
@@ -147,6 +167,12 @@ export declare function refuseBranchOnManagedDatabase(rawArgs: string[], kind: s
147
167
  * tsx whenever it is installed — a devDependency of every scaffold, for exactly
148
168
  * this reason. Without it a `.ts` entry cannot run at all; a built entry still
149
169
  * runs, and the subcommands that never touch the project's TypeScript still work.
170
+ *
171
+ * tsx's loader in one Node (`node --import`) rather than the `tsx` binary,
172
+ * which is a shell shim and a relay Node in front of the same loader: one Node
173
+ * start instead of two, ~50–100 ms on each of the driver runs every `rebase
174
+ * dev` start and every collection save makes. The binary stays the fallback for
175
+ * a tsx whose loader does not resolve as a package — a global install.
150
176
  */
151
177
  export declare function spawnDriverCli(resolved: {
152
178
  projectRoot: string;
@@ -171,6 +197,20 @@ export declare function runDriverDbCommand(rawArgs: string[], options?: {
171
197
  quiet?: boolean;
172
198
  }): Promise<void>;
173
199
  export declare function dbCommand(subcommand: string | undefined, rawArgs: string[]): Promise<void>;
200
+ /**
201
+ * Start the compose database a `db … --docker` was asked to talk to.
202
+ *
203
+ * `--docker` resolved the compose URL and stopped there, so every `db`
204
+ * subcommand carrying it reached a container nobody had started —
205
+ * `ECONNREFUSED 127.0.0.1:5436`, two lines, no diagnosis — including the
206
+ * commands the managed-database refusals send readers to `--docker` for.
207
+ *
208
+ * `rebase dev --docker`'s preflight, called with the compose URL and nothing
209
+ * to push: this is not a schema step, and `rebase db push --docker` is the
210
+ * push. A failure here is reported and not fatal — the command runs and its
211
+ * own connection error follows, which is the same contract `dev` keeps.
212
+ */
213
+ export declare function ensureDockerDatabase(projectRoot: string, rawArgs: readonly string[]): Promise<void>;
174
214
  /** Every subcommand the family dispatches — the help table's keys. */
175
215
  export declare const DB_SUBCOMMANDS: string[];
176
216
  /**
@@ -30,6 +30,21 @@ import { type PreparedDatabase } from "../dev-db/prepare.js";
30
30
  * Exported so `dev-crash-marker.test.ts` can hold the running tsx to it.
31
31
  */
32
32
  export declare const WATCHER_RESTART_MARKER: RegExp;
33
+ /**
34
+ * Calls `onLine` with each line a child process writes to `stream`.
35
+ *
36
+ * A pipe hands over whatever has been written so far, so one line can arrive
37
+ * in two chunks. Split chunk by chunk, "Server running at http://localhost:3001"
38
+ * could read as two fragments that neither matched, and `rebase dev` then
39
+ * never announced the backend or started the admin panel. So the piece after
40
+ * the last newline is held for the next chunk, decoded as UTF-8 across chunk
41
+ * boundaries. A held piece is handed over at the end of the stream, or after
42
+ * `idleFlushMs` with nothing more written, so a prompt or a progress line
43
+ * still shows when it is written.
44
+ *
45
+ * Exported for its test.
46
+ */
47
+ export declare function forEachOutputLine(stream: NodeJS.ReadableStream | null | undefined, onLine: (line: string) => void, idleFlushMs?: number): void;
33
48
  /**
34
49
  * The corrected banner line, when the backend says it moved port.
35
50
  *
@@ -95,6 +110,16 @@ export declare function devRestartTrigger(projectRoot: string): string | null;
95
110
  * collections directory alone (`--exclude` wins over its dependency tracking),
96
111
  * and dev restarts the backend itself, once, after the regeneration — through
97
112
  * `restartTrigger`, which tsx watches.
113
+ *
114
+ * Every included directory also gets tsx's own default ignores, made absolute.
115
+ * tsx resolves those defaults (`**\/node_modules/**`, `**\/.*`, …) against its
116
+ * cwd, which is `backend/`, so they never reached `config/`, a sibling: chokidar
117
+ * walked `config/node_modules` — following symlinks — before tsx spawned the
118
+ * backend at all, and kept every file in it watched. In the monorepo's example
119
+ * app that was 17 s before the first backend line and 1,400 open files, and any
120
+ * install (or another checkout's Vite build writing `.vite-temp` deep inside
121
+ * that tree) restarted the backend. A `.DS_Store` or an editor swap file in
122
+ * `config/` restarted it too.
98
123
  */
99
124
  export declare function devWatchArgs(projectRoot: string, paths: {
100
125
  REBASE_DEV_FUNCTIONS?: string;
@@ -294,6 +319,21 @@ export declare const DEV_FLAGS: {
294
319
  readonly "-P": "--port";
295
320
  readonly "--no-db": BooleanConstructor;
296
321
  };
322
+ /** What {@link ensureGeneratedSchema} did at startup. */
323
+ export type SchemaGeneration = "generated" | "skipped" | "failed";
324
+ /**
325
+ * Whether dev must still run the driver's `schema stale --fix` before the
326
+ * backend boots.
327
+ *
328
+ * Not after a regeneration that succeeded: that wrote `schema.generated.ts`
329
+ * from these collections with this driver, moments earlier, and `stale`
330
+ * compares the file against the same generator — it can only answer "nothing
331
+ * stale", and it cost a driver process (~0.6 s) on every start. The repair it
332
+ * exists for, a library upgrade that changed what the generator derives, is
333
+ * exactly what the regeneration already did. When generation was skipped or
334
+ * failed, the preflight is the remaining safety net and still runs.
335
+ */
336
+ export declare function needsStaleSchemaPreflight(generation: SchemaGeneration): boolean;
297
337
  /**
298
338
  * One variable's value out of `.env` text, or undefined when it has none.
299
339
  *
@@ -322,4 +362,67 @@ export declare function readEnvValue(envText: string, key: string): string | und
322
362
  * it is invisible in any assertion about the push's *outcome*.
323
363
  */
324
364
  export declare function schemaPushArgv(databaseUrl: string | undefined): string[];
365
+ /**
366
+ * Start-up's independent halves, overlapped rather than run one after another.
367
+ *
368
+ * `rebase dev` used to regenerate the schema and the SDK, then derive the
369
+ * resource graph (every collection evaluated again, in this process), then
370
+ * start the managed database and wait for it — about a second when it was not
371
+ * already running — and only then spawn the backend. None of the three reads
372
+ * what another writes: the generators read `config/` and write
373
+ * `backend/src/schema.generated.ts` and `generated/sdk/`; the derivation reads
374
+ * `config/resources.ts` and the collections; the database needs neither, and
375
+ * `schema generate` never connects to one. So `database` starts first and is
376
+ * collected last.
377
+ *
378
+ * `preflight` is the exception, and the reason this is not one `Promise.all`:
379
+ * on the `--docker` path it pushes the schema the generators have just written,
380
+ * so it still runs after them.
381
+ *
382
+ * A database that fails to start is reported where it always was — after the
383
+ * generators' and the preflight's output — rather than ahead of it, and it is
384
+ * never an unhandled rejection while they run.
385
+ *
386
+ * Exported for its test.
387
+ */
388
+ export declare function overlapDevStartup<T>(steps: {
389
+ generate: () => Promise<SchemaGeneration>;
390
+ preflight: () => Promise<void>;
391
+ /** Resolve and, for the managed database, start it. `null` when this run starts none. */
392
+ database: (() => Promise<T>) | null;
393
+ }): Promise<{
394
+ schemaGeneration: SchemaGeneration;
395
+ database: T | null;
396
+ }>;
397
+ /**
398
+ * Whether the admin panel can start beside the backend instead of after it.
399
+ *
400
+ * Vite has to be told the backend's URL when it starts (`VITE_API_URL`), and
401
+ * the backend's port is only certain once it has bound — so the admin panel
402
+ * used to wait for "Server running at", and the time to it was the backend's
403
+ * whole boot plus Vite's start. It can start beside the backend whenever the
404
+ * port the backend will bind is known in advance:
405
+ *
406
+ * - **named with `--port`**: the server binds it or refuses to start, never
407
+ * walks past it (`REBASE_DEV_PORT_EXPLICIT`), and `pinnedPortRefusal` has
408
+ * already checked that nothing answers there;
409
+ * - **free, and the port the backend tries first**: the server tries the port
410
+ * its last run landed on before the one it is given (`.rebase-dev-port`), so
411
+ * a remembered port must be this one — the "kept from the last run" rung —
412
+ * or absent, which it is after every clean exit, and the derived port is
413
+ * then the one tried. A port something already answers on would make the
414
+ * backend move, so that case keeps the old order.
415
+ *
416
+ * Nothing here can rule out another process taking the port in the second the
417
+ * backend boots, so `devCommand` still compares the announced port with the
418
+ * one Vite was given and restarts the admin panel when they differ.
419
+ *
420
+ * Exported for its test.
421
+ */
422
+ export declare function frontendCanStartWithBackend(options: {
423
+ projectRoot: string;
424
+ port: number;
425
+ pinned: boolean;
426
+ probe?: (host: string, port: number) => Promise<boolean>;
427
+ }): Promise<boolean>;
325
428
  export declare function devCommand(rawArgs: string[]): Promise<void>;
@@ -22,8 +22,34 @@ interface GenerateSDKArgs {
22
22
  from?: string;
23
23
  /** Bearer token for the contract endpoint. Falls back to REBASE_SERVICE_KEY. */
24
24
  token?: string;
25
+ /**
26
+ * Also register the generated `Database` for server-side code: write the
27
+ * types and the `Register` augmentation into `config/generated/`. See
28
+ * {@link writeRegistration}.
29
+ */
30
+ register?: boolean;
31
+ /**
32
+ * The directory holding `rebase.json`, where `--register` is remembered.
33
+ * Found from `cwd` when not given.
34
+ */
35
+ projectRoot?: string;
25
36
  help?: boolean;
26
37
  }
38
+ /** What `--register` writes, and what marks a project as having opted in. */
39
+ export declare const REGISTER_FILE = "register.ts";
40
+ /**
41
+ * Where `--register` writes: `generated/` beside the collections directory,
42
+ * which is `config/generated/` in a scaffolded project.
43
+ *
44
+ * Inside `config/` because that is the one directory both compiles of a
45
+ * collection file include: the backend's program (it includes
46
+ * `../config/**`) and the `config` package's own `tsc`. A registration
47
+ * there that imported `../generated/sdk/database.types` pulled a file from
48
+ * outside `config/` into that package's program, which TypeScript refuses
49
+ * (the file is not under its `rootDir`) — so the types are copied in beside
50
+ * the registration rather than imported from the SDK.
51
+ */
52
+ export declare function registrationDir(collectionsDir: string): string;
27
53
  /**
28
54
  * The base URL to show in the printed usage example.
29
55
  *
@@ -215,15 +215,27 @@ export interface InstalledAgent {
215
215
  */
216
216
  export declare function detectInstalledAgents(home?: string): InstalledAgent[];
217
217
  /** Install skills for a specific agent into the project directory. */
218
- export declare function installForAgent(agentKey: AgentKey, skills: LoadedSkill[], projectDir: string): {
218
+ /**
219
+ * The file in each target directory that records what the installer wrote:
220
+ * relative path → sha256 of the bytes written. A dotfile, so no agent loads it
221
+ * as a rule.
222
+ */
223
+ export declare const SKILLS_MANIFEST = ".rebase-skills.json";
224
+ /** What one agent's install did. */
225
+ export interface AgentInstallResult {
219
226
  skills: number;
220
227
  assets: number;
221
- };
228
+ /** Files left as they are because they were edited since the last install (relative to the target dir). */
229
+ kept: string[];
230
+ /** Files of skills the bundle no longer ships, removed because nobody edited them. */
231
+ removed: string[];
232
+ }
233
+ export declare function installForAgent(agentKey: AgentKey, skills: LoadedSkill[], projectDir: string, options?: {
234
+ force?: boolean;
235
+ }): AgentInstallResult;
222
236
  /** What `installSkills` did for one agent. */
223
- export interface SkillsInstallResult {
237
+ export interface SkillsInstallResult extends AgentInstallResult {
224
238
  agent: AgentKey;
225
- skills: number;
226
- assets: number;
227
239
  /** An earlier agent in the same run that already wrote this directory. */
228
240
  sharedWith?: AgentKey;
229
241
  }
@@ -233,7 +245,9 @@ export interface SkillsInstallResult {
233
245
  * Gemini and Codex both read `.agents/skills`; installing twice would write the
234
246
  * same 21 files twice and report two installs of one directory.
235
247
  */
236
- export declare function installSkills(agents: readonly AgentKey[], skills: LoadedSkill[], projectDir: string): SkillsInstallResult[];
248
+ export declare function installSkills(agents: readonly AgentKey[], skills: LoadedSkill[], projectDir: string, options?: {
249
+ force?: boolean;
250
+ }): SkillsInstallResult[];
237
251
  /** Everything the switch below dispatches, for the did-you-mean. */
238
252
  export declare const SKILLS_SUBCOMMANDS: readonly ["install"];
239
253
  export declare function skillsCommand(subcommand: string | undefined, rawArgs: string[]): Promise<void>;
@@ -243,10 +257,11 @@ export declare function skillsCommand(subcommand: string | undefined, rawArgs: s
243
257
  * given.
244
258
  */
245
259
  export declare function parseAgentFlags(rawArgs: string[]): AgentKey[] | null;
246
- /** `rebase skills install`'s whole line: the agents it names, and `--mcp`. */
260
+ /** `rebase skills install`'s whole line: the agents it names, `--mcp` and `--force`. */
247
261
  export declare function parseSkillsInstallLine(rawArgs: string[]): {
248
262
  agents: AgentKey[] | null;
249
263
  mcp: boolean;
264
+ force: boolean;
250
265
  };
251
266
  /**
252
267
  * The agents a list of `--agent` values names, or null when it names none.
@@ -0,0 +1,46 @@
1
+ /** Everything the switch below dispatches, for the did-you-mean. */
2
+ export declare const STORAGE_SUBCOMMANDS: readonly ["orphans", "move"];
3
+ /** The flags `rebase storage orphans` takes. */
4
+ export declare const ORPHANS_FLAGS: {
5
+ readonly "--source": StringConstructor;
6
+ readonly "--older-than": NumberConstructor;
7
+ readonly "--delete": BooleanConstructor;
8
+ readonly "--yes": BooleanConstructor;
9
+ readonly "-y": "--yes";
10
+ readonly "--json": BooleanConstructor;
11
+ };
12
+ /** The flags `rebase storage move` takes. */
13
+ export declare const MOVE_FLAGS: {
14
+ readonly "--from": StringConstructor;
15
+ readonly "--to": StringConstructor;
16
+ readonly "--prefix": StringConstructor;
17
+ readonly "--delete-source": BooleanConstructor;
18
+ readonly "--json": BooleanConstructor;
19
+ };
20
+ /** One stored object, as the backend lists it. */
21
+ interface StoredObjectEntry {
22
+ key: string;
23
+ size?: number;
24
+ updated?: string;
25
+ }
26
+ /** What `GET /admin/storage/orphans` answers. */
27
+ export interface OrphanReport {
28
+ storageId: string;
29
+ objects: number;
30
+ orphans: StoredObjectEntry[];
31
+ recent: number;
32
+ scanned: string[];
33
+ unscanned: {
34
+ collection: string;
35
+ reason: string;
36
+ }[];
37
+ }
38
+ /** Bytes, as a person reads them. */
39
+ export declare function formatBytes(bytes: number): string;
40
+ /**
41
+ * The lines that describe an orphan scan. Exported so its tests can read them
42
+ * without a server.
43
+ */
44
+ export declare function describeOrphanReport(report: OrphanReport, olderThanHours: number): string[];
45
+ export declare function storageCommand(subcommand: string | undefined, rawArgs: string[]): Promise<void>;
46
+ export {};
@@ -0,0 +1,76 @@
1
+ import { a as setConsent, c as buildEvent, f as readConfig, o as suppressionReason } from "./telemetry-CAWNCugs.js";
2
+ import chalk from "chalk";
3
+ import inquirer from "inquirer";
4
+ //#region src/telemetry/consent.ts
5
+ /** True when we may ask: nothing forbids it, and there is a question to ask. */
6
+ function shouldPrompt(env = process.env, { reAskDeclined = false } = {}) {
7
+ if (!process.stdin.isTTY) return false;
8
+ const reason = suppressionReason(env);
9
+ if (reason === "not_asked") return true;
10
+ return reAskDeclined && reason === "declined";
11
+ }
12
+ function renderPreview(event, properties) {
13
+ const preview = buildEvent(event, properties, {
14
+ machineId: "<random uuid, generated only if you say yes>",
15
+ projectId: "<random uuid, per checkout>"
16
+ });
17
+ return JSON.stringify(preview, null, 2);
18
+ }
19
+ /**
20
+ * Ask, record the answer, and report it.
21
+ *
22
+ * Never throws and never blocks a non-interactive run: `rebase init --yes` in
23
+ * CI must behave exactly as it does today, which means not asking and not
24
+ * sending.
25
+ */
26
+ async function promptForConsent(options = {}) {
27
+ if (!shouldPrompt(process.env, options)) return false;
28
+ const askedBefore = suppressionReason(process.env) === "declined";
29
+ try {
30
+ console.log("");
31
+ console.log(chalk.bold("Help improve Rebase?"));
32
+ console.log("");
33
+ console.log(chalk.gray(" Rebase is self-hosted, so we only learn what works if you tell us."));
34
+ console.log(chalk.gray(" Anonymous: random ids, the CLI version, your OS, and which template and"));
35
+ console.log(chalk.gray(" package manager you used. Never project names, paths, schemas, URLs or"));
36
+ console.log(chalk.gray(" error messages."));
37
+ console.log("");
38
+ console.log(chalk.gray(` Print the exact payload with ${chalk.cyan("rebase telemetry show")}; change your mind`));
39
+ console.log(chalk.gray(` any time with ${chalk.cyan("rebase telemetry disable")}.`));
40
+ if (askedBefore) {
41
+ console.log("");
42
+ console.log(chalk.gray(" You declined before; that stands unless you change it here."));
43
+ }
44
+ console.log("");
45
+ const { accepted } = await inquirer.prompt([{
46
+ type: "confirm",
47
+ name: "accepted",
48
+ message: "Share anonymous usage data?",
49
+ default: true
50
+ }]);
51
+ setConsent(Boolean(accepted));
52
+ console.log(accepted ? chalk.green(" Thank you — sharing enabled.") : chalk.gray(" Nothing will be sent." + (options.reAskDeclined ? " You will be asked again the next time you scaffold a project." : " You will not be asked again.")));
53
+ console.log("");
54
+ return Boolean(accepted);
55
+ } catch {
56
+ return false;
57
+ }
58
+ }
59
+ /** Human-readable current state, for `rebase telemetry status`. */
60
+ function describeState(env = process.env) {
61
+ const reason = suppressionReason(env);
62
+ const config = readConfig();
63
+ switch (reason) {
64
+ case null: return `${chalk.green("enabled")} — schema v1, machine id ${chalk.gray(config.machineId ?? "unset")}`;
65
+ case "not_asked": return `${chalk.yellow("not configured")} — nothing has been sent, and you have not been asked yet`;
66
+ case "declined": return `${chalk.gray("disabled")} — you declined${config.decidedAt ? ` on ${config.decidedAt.slice(0, 10)}` : ""}`;
67
+ case "do_not_track": return `${chalk.gray("disabled")} — the ${chalk.cyan("DO_NOT_TRACK")} environment variable is set`;
68
+ case "rebase_telemetry_disabled": return `${chalk.gray("disabled")} — the ${chalk.cyan("REBASE_TELEMETRY_DISABLED")} environment variable is set`;
69
+ case "ci": return `${chalk.gray("disabled")} — this looks like CI (${chalk.cyan("CI")} is set), which is never counted`;
70
+ case "project_opt_out": return `${chalk.gray("disabled")} — this project's ${chalk.cyan("rebase.json")} sets ${chalk.cyan("\"telemetry\": false")}`;
71
+ }
72
+ }
73
+ //#endregion
74
+ export { promptForConsent as n, renderPreview as r, describeState as t };
75
+
76
+ //# sourceMappingURL=consent-BWHaYx5p.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"consent-BWHaYx5p.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 yes: this is the question the project most needs\n // answered, and a bare Enter is the commonest answer to any\n // prompt. It is still a question — nothing is sent until it is\n // answered, `n` is one keystroke, and the decision is\n // reversible with the command named two lines above. What it\n // is NOT is \"off by default\" any more; anything that used to\n // say so had to change with it.\n default: true\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;GAQT,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"}