@rebasepro/cli 0.13.1-canary.gc77922a → 0.13.1-canary.gc9afce1
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.
- package/dist/commands/auth.d.ts +31 -0
- package/dist/index.es.js +215 -52
- package/dist/index.es.js.map +1 -1
- package/dist/telemetry/payload.d.ts +1 -1
- package/dist/utils/collection-drift.d.ts +27 -0
- package/dist/utils/project.d.ts +20 -0
- package/package.json +7 -7
package/dist/commands/auth.d.ts
CHANGED
|
@@ -1 +1,32 @@
|
|
|
1
|
+
/** A user as the admin API returns it, reduced to what this command needs. */
|
|
2
|
+
export interface ResolvedUser {
|
|
3
|
+
id: string;
|
|
4
|
+
email: string;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Pick the user with exactly this email out of a search response.
|
|
8
|
+
*
|
|
9
|
+
* `/api/admin/users?search=` is an `ILIKE '%…%'` over email **or display
|
|
10
|
+
* name**, ordered by role count descending. This used to take row `[0]` and
|
|
11
|
+
* reset it, then print the email it had been *given* as confirmation — so two
|
|
12
|
+
* ordinary situations ended in a successful-looking reset of somebody else's
|
|
13
|
+
* account:
|
|
14
|
+
*
|
|
15
|
+
* - a substring collision: `bob@example.com` also matches
|
|
16
|
+
* `robert.bob@example.com`;
|
|
17
|
+
* - a display name, which is user-controlled and accepted up to 255
|
|
18
|
+
* characters with no constraint on its content, containing an address
|
|
19
|
+
* belonging to someone else.
|
|
20
|
+
*
|
|
21
|
+
* The ordering makes it worse rather than better — `array_length(roles) DESC
|
|
22
|
+
* NULLS LAST` puts the most privileged match first, so the account most likely
|
|
23
|
+
* to be reset by mistake is an admin's.
|
|
24
|
+
*
|
|
25
|
+
* Returns `undefined` when nothing matched exactly, which the caller reports
|
|
26
|
+
* rather than falling through to a guess. The direct-database fallback below
|
|
27
|
+
* has always matched with `eq(usersTable.email, email)`; this is the same
|
|
28
|
+
* definition, so the command no longer resets different accounts depending on
|
|
29
|
+
* whether the backend happened to be running.
|
|
30
|
+
*/
|
|
31
|
+
export declare function selectUserForEmail(payload: unknown, email: string): ResolvedUser | undefined;
|
|
1
32
|
export declare function authCommand(subcommand: string | undefined, rawArgs: string[]): Promise<void>;
|
package/dist/index.es.js
CHANGED
|
@@ -12,8 +12,9 @@ import crypto from "crypto";
|
|
|
12
12
|
import { execSync, spawn, spawnSync } from "child_process";
|
|
13
13
|
import os from "os";
|
|
14
14
|
import { createRebaseClient } from "@rebasepro/client";
|
|
15
|
+
import dotenv from "dotenv";
|
|
15
16
|
import { createRequire } from "module";
|
|
16
|
-
import { BUNDLE_FORMAT_VERSION, RUNTIME_CONTRACT_VERSION, computeSchemaVersion, deserializeCollections, findStorageSuffixCollision, normalizeStorageSources, storageEnvSuffix } from "@rebasepro/types";
|
|
17
|
+
import { BUNDLE_FORMAT_VERSION, DEFAULT_DATA_SOURCE_KEY, RUNTIME_CONTRACT_VERSION, computeSchemaVersion, deserializeCollections, findStorageSuffixCollision, getDataSourceCapabilities, normalizeStorageSources, storageEnvSuffix } from "@rebasepro/types";
|
|
17
18
|
import { generateSDK } from "@rebasepro/codegen";
|
|
18
19
|
//#region src/utils/package-manager.ts
|
|
19
20
|
/**
|
|
@@ -289,6 +290,34 @@ function findEnvFile(projectRoot) {
|
|
|
289
290
|
return null;
|
|
290
291
|
}
|
|
291
292
|
/**
|
|
293
|
+
* Read the project's `.env` into a plain object.
|
|
294
|
+
*
|
|
295
|
+
* One reader, because there were four: `dotenv` in `start`, a hand-rolled
|
|
296
|
+
* `indexOf("=")` loop in `api-keys`, a single-key regex in `auth`, and its own
|
|
297
|
+
* splitting in `cloud env`. `dotenv` is a declared dependency of this package,
|
|
298
|
+
* so the other three existed for no reason and disagreed with the correct one
|
|
299
|
+
* on the two things people actually write in a `.env`:
|
|
300
|
+
*
|
|
301
|
+
* - `export KEY=value`, which the hand-rolled parser keyed as
|
|
302
|
+
* `export KEY` — so the command reported the key as unset while it was
|
|
303
|
+
* right there in the file;
|
|
304
|
+
* - `KEY=value # comment`, whose comment travelled into the value and then
|
|
305
|
+
* into an `Authorization` header, coming back as a 401 with nothing
|
|
306
|
+
* pointing at the cause.
|
|
307
|
+
*
|
|
308
|
+
* Returns `{}` when the project has no `.env`, so callers can treat "absent"
|
|
309
|
+
* and "empty" alike.
|
|
310
|
+
*/
|
|
311
|
+
function readEnvFile(projectRoot) {
|
|
312
|
+
const envFile = findEnvFile(projectRoot);
|
|
313
|
+
if (!envFile || !fs.existsSync(envFile)) return {};
|
|
314
|
+
try {
|
|
315
|
+
return dotenv.parse(fs.readFileSync(envFile, "utf-8"));
|
|
316
|
+
} catch {
|
|
317
|
+
return {};
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
292
321
|
* Resolve a binary from the project's node_modules/.bin.
|
|
293
322
|
* Checks backend, root, parent monorepo root, then falls back to PATH.
|
|
294
323
|
*/
|
|
@@ -2394,7 +2423,7 @@ async function schemaCommand(subcommand, rawArgs) {
|
|
|
2394
2423
|
return;
|
|
2395
2424
|
}
|
|
2396
2425
|
const projectRoot = requireProjectRoot();
|
|
2397
|
-
recordEvent("cli.
|
|
2426
|
+
recordEvent("cli.schema", { subcommand: subcommand ?? "none" }, { projectRoot });
|
|
2398
2427
|
const backendDir = requireBackendDir(projectRoot);
|
|
2399
2428
|
const activePlugin = getActiveBackendPlugin(backendDir);
|
|
2400
2429
|
if (!activePlugin) {
|
|
@@ -2463,7 +2492,7 @@ async function dbCommand(subcommand, rawArgs) {
|
|
|
2463
2492
|
return;
|
|
2464
2493
|
}
|
|
2465
2494
|
const projectRoot = requireProjectRoot();
|
|
2466
|
-
recordEvent("cli.
|
|
2495
|
+
recordEvent("cli.db", { subcommand: subcommand ?? "none" }, { projectRoot });
|
|
2467
2496
|
const backendDir = requireBackendDir(projectRoot);
|
|
2468
2497
|
const activePlugin = getActiveBackendPlugin(backendDir);
|
|
2469
2498
|
if (!activePlugin) {
|
|
@@ -3101,6 +3130,106 @@ function resolveBackendPaths(app, projectRoot) {
|
|
|
3101
3130
|
};
|
|
3102
3131
|
}
|
|
3103
3132
|
//#endregion
|
|
3133
|
+
//#region src/utils/collection-drift.ts
|
|
3134
|
+
/**
|
|
3135
|
+
* Which edits under `config/collections` can put the *SQL* schema out of date.
|
|
3136
|
+
*
|
|
3137
|
+
* `rebase dev` watches that directory and, on any change, either tells the
|
|
3138
|
+
* developer to run `rebase schema generate` / `rebase db push` or runs them.
|
|
3139
|
+
* The watcher is recursive and knows nothing about what it is watching, so it
|
|
3140
|
+
* said that about every file under the directory — including a
|
|
3141
|
+
* `collections/firestore/exercises.ts`, whose documents live in Firestore and
|
|
3142
|
+
* for which there is no Drizzle schema to regenerate and no database to push
|
|
3143
|
+
* to. Advice that is wrong on every edit is advice a developer learns to
|
|
3144
|
+
* ignore, which is worse than none: the same box is the only warning for the
|
|
3145
|
+
* Postgres collection next to it, where it is real.
|
|
3146
|
+
*
|
|
3147
|
+
* Two independent reasons a change cannot affect the SQL schema, both checked
|
|
3148
|
+
* here:
|
|
3149
|
+
*
|
|
3150
|
+
* 1. **The loader would never read the file.** `loadCollectionsFromDirectory`
|
|
3151
|
+
* reads the top level of the collections directory only — no recursion, no
|
|
3152
|
+
* `index`, no tests, no declarations. A file it does not read cannot change
|
|
3153
|
+
* what it returns, and the watcher must not claim otherwise.
|
|
3154
|
+
* 2. **Every collection in it is served by another engine.** A Firestore or
|
|
3155
|
+
* MongoDB collection has no table, no migration and no policies.
|
|
3156
|
+
*
|
|
3157
|
+
* The engine check reads the source text rather than importing the module: the
|
|
3158
|
+
* CLI runs as plain Node and cannot evaluate a project's TypeScript, and a
|
|
3159
|
+
* watcher must answer in the time between two keystrokes. Anything it cannot
|
|
3160
|
+
* read confidently counts as SQL-affecting — a spurious warning is a nuisance,
|
|
3161
|
+
* a suppressed one hides real drift.
|
|
3162
|
+
*/
|
|
3163
|
+
/**
|
|
3164
|
+
* Would `loadCollectionsFromDirectory` load this file?
|
|
3165
|
+
*
|
|
3166
|
+
* Mirrors that loader's own `isCollectionFile` plus its flat (non-recursive)
|
|
3167
|
+
* scan. `relativePath` is relative to the collections directory, as `fs.watch`
|
|
3168
|
+
* reports it.
|
|
3169
|
+
*/
|
|
3170
|
+
function isLoadedCollectionFile(relativePath) {
|
|
3171
|
+
const normalized = relativePath.split(path.sep).join("/");
|
|
3172
|
+
if (normalized.includes("/")) return false;
|
|
3173
|
+
const file = normalized;
|
|
3174
|
+
if (!file.endsWith(".ts") && !file.endsWith(".js")) return false;
|
|
3175
|
+
if (file.startsWith(".")) return false;
|
|
3176
|
+
if (file.includes(".test.")) return false;
|
|
3177
|
+
if (file.endsWith(".d.ts")) return false;
|
|
3178
|
+
if (file === "index.ts" || file === "index.js") return false;
|
|
3179
|
+
return true;
|
|
3180
|
+
}
|
|
3181
|
+
var ENGINE_LITERAL = /\bengine\s*:\s*["'`]([^"'`]+)["'`]/g;
|
|
3182
|
+
var DATA_SOURCE_LITERAL = /\bdataSource\s*:\s*["'`]([^"'`]+)["'`]/g;
|
|
3183
|
+
/**
|
|
3184
|
+
* Drop comments, so a `// engine: "firestore"` in a docblock cannot silence a
|
|
3185
|
+
* warning about the Postgres collection the file actually declares.
|
|
3186
|
+
*
|
|
3187
|
+
* Deliberately naive — it does not understand strings, so a `//` inside one
|
|
3188
|
+
* (a URL in a default value) eats the rest of that line. That only ever removes
|
|
3189
|
+
* text, and removing an engine literal makes this file *more* likely to warn,
|
|
3190
|
+
* which is the side to be wrong on.
|
|
3191
|
+
*/
|
|
3192
|
+
function stripComments(source) {
|
|
3193
|
+
return source.replace(/\/\*[\s\S]*?\*\//g, " ").replace(/\/\/[^\n]*/g, " ");
|
|
3194
|
+
}
|
|
3195
|
+
function literals(source, pattern) {
|
|
3196
|
+
const found = [];
|
|
3197
|
+
for (const match of source.matchAll(pattern)) found.push(match[1]);
|
|
3198
|
+
return found;
|
|
3199
|
+
}
|
|
3200
|
+
/**
|
|
3201
|
+
* Does this collection source declare anything a SQL toolchain would own?
|
|
3202
|
+
*
|
|
3203
|
+
* The fallback order matches `resolveDataSource`: an explicit `engine` wins,
|
|
3204
|
+
* and a collection that only names a `dataSource` is resolved as if the key
|
|
3205
|
+
* were the engine — which is what that function does when no registry is
|
|
3206
|
+
* available, and the CLI has none. An engine nobody recognises counts as
|
|
3207
|
+
* relational, for the reason `isRelationalCollection` gives.
|
|
3208
|
+
*
|
|
3209
|
+
* A file declaring several collections is SQL-affecting if *any* of them is.
|
|
3210
|
+
*/
|
|
3211
|
+
function declaresRelationalCollection(rawSource) {
|
|
3212
|
+
const source = stripComments(rawSource);
|
|
3213
|
+
const engines = literals(source, ENGINE_LITERAL);
|
|
3214
|
+
const declared = engines.length > 0 ? engines : literals(source, DATA_SOURCE_LITERAL).filter((key) => key !== DEFAULT_DATA_SOURCE_KEY);
|
|
3215
|
+
if (declared.length === 0) return true;
|
|
3216
|
+
return declared.some((engine) => getDataSourceCapabilities(engine).supportsRelations);
|
|
3217
|
+
}
|
|
3218
|
+
/**
|
|
3219
|
+
* Can this edit have changed the generated SQL schema?
|
|
3220
|
+
*
|
|
3221
|
+
* Answers `true` when it cannot tell — an unreadable file is a reason to warn,
|
|
3222
|
+
* not a reason to go quiet.
|
|
3223
|
+
*/
|
|
3224
|
+
function affectsSqlSchema(collectionsDir, relativePath) {
|
|
3225
|
+
if (!isLoadedCollectionFile(relativePath)) return false;
|
|
3226
|
+
try {
|
|
3227
|
+
return declaresRelationalCollection(fs.readFileSync(path.join(collectionsDir, relativePath), "utf8"));
|
|
3228
|
+
} catch {
|
|
3229
|
+
return true;
|
|
3230
|
+
}
|
|
3231
|
+
}
|
|
3232
|
+
//#endregion
|
|
3104
3233
|
//#region src/commands/dev.ts
|
|
3105
3234
|
/**
|
|
3106
3235
|
* CLI command: rebase dev
|
|
@@ -3413,14 +3542,18 @@ async function devCommand(rawArgs) {
|
|
|
3413
3542
|
const collectionsDir = path.join(projectRoot, "config", "collections");
|
|
3414
3543
|
if (fs.existsSync(collectionsDir)) {
|
|
3415
3544
|
let watchDebounce = null;
|
|
3545
|
+
let sqlSchemaAffected = false;
|
|
3416
3546
|
fs.watch(collectionsDir, { recursive: true }, (eventType, filename) => {
|
|
3417
3547
|
if (!filename || filename.startsWith(".") || filename.endsWith(".tmp")) return;
|
|
3548
|
+
sqlSchemaAffected = sqlSchemaAffected || affectsSqlSchema(collectionsDir, filename);
|
|
3418
3549
|
if (watchDebounce) clearTimeout(watchDebounce);
|
|
3419
3550
|
watchDebounce = setTimeout(async () => {
|
|
3420
|
-
|
|
3551
|
+
const regenerateSchema = sqlSchemaAffected;
|
|
3552
|
+
sqlSchemaAffected = false;
|
|
3553
|
+
console.log(chalk.yellow(`\n 🔄 Collection change detected (${filename}). Regenerating ${regenerateSchema ? "schema & SDK" : "SDK"}...`));
|
|
3421
3554
|
try {
|
|
3422
3555
|
const activePlugin = getActiveBackendPlugin(backendDir);
|
|
3423
|
-
const pluginCli = activePlugin ? resolvePluginCliScript(backendDir, activePlugin) : null;
|
|
3556
|
+
const pluginCli = regenerateSchema && activePlugin ? resolvePluginCliScript(backendDir, activePlugin) : null;
|
|
3424
3557
|
if (pluginCli) await execa(tsxBin, [
|
|
3425
3558
|
pluginCli,
|
|
3426
3559
|
"schema",
|
|
@@ -3436,7 +3569,7 @@ async function devCommand(rawArgs) {
|
|
|
3436
3569
|
stdio: "inherit",
|
|
3437
3570
|
env
|
|
3438
3571
|
});
|
|
3439
|
-
console.log(chalk.green(
|
|
3572
|
+
console.log(chalk.green(` ✓ ${regenerateSchema ? "Schema & SDK" : "SDK"} regenerated successfully. Hono will reload.`));
|
|
3440
3573
|
} catch (err) {
|
|
3441
3574
|
console.error(chalk.red(` ✗ Failed to regenerate schema/SDK: ${err instanceof Error ? err.message : err}`));
|
|
3442
3575
|
}
|
|
@@ -3461,12 +3594,14 @@ async function devCommand(rawArgs) {
|
|
|
3461
3594
|
let driftDebounce = null;
|
|
3462
3595
|
fs.watch(collectionsDir, { recursive: true }, (_eventType, filename) => {
|
|
3463
3596
|
if (!filename || filename.startsWith(".") || filename.endsWith(".tmp")) return;
|
|
3597
|
+
if (!affectsSqlSchema(collectionsDir, filename)) return;
|
|
3464
3598
|
if (driftDebounce) clearTimeout(driftDebounce);
|
|
3465
3599
|
driftDebounce = setTimeout(() => {
|
|
3600
|
+
const shown = filename.length > 31 ? `…${filename.slice(-30)}` : filename.padEnd(31);
|
|
3466
3601
|
console.log([
|
|
3467
3602
|
"",
|
|
3468
3603
|
chalk.yellow(" ┌──────────────────────────────────────────────────────────────┐"),
|
|
3469
|
-
chalk.yellow(" │ ⚠️ Collection file changed: ") + chalk.white(
|
|
3604
|
+
chalk.yellow(" │ ⚠️ Collection file changed: ") + chalk.white(shown) + chalk.yellow("│"),
|
|
3470
3605
|
chalk.yellow(" │ │"),
|
|
3471
3606
|
chalk.yellow(" │ Your schema may be out of sync. Run: │"),
|
|
3472
3607
|
chalk.yellow(" │ ") + chalk.cyan("rebase schema generate") + chalk.yellow(" regenerate Drizzle schema │"),
|
|
@@ -5327,6 +5462,48 @@ async function startWorkspaceBackend(projectRoot, env) {
|
|
|
5327
5462
|
* Subcommands:
|
|
5328
5463
|
* reset-password — Reset a user's password
|
|
5329
5464
|
*/
|
|
5465
|
+
/**
|
|
5466
|
+
* Pick the user with exactly this email out of a search response.
|
|
5467
|
+
*
|
|
5468
|
+
* `/api/admin/users?search=` is an `ILIKE '%…%'` over email **or display
|
|
5469
|
+
* name**, ordered by role count descending. This used to take row `[0]` and
|
|
5470
|
+
* reset it, then print the email it had been *given* as confirmation — so two
|
|
5471
|
+
* ordinary situations ended in a successful-looking reset of somebody else's
|
|
5472
|
+
* account:
|
|
5473
|
+
*
|
|
5474
|
+
* - a substring collision: `bob@example.com` also matches
|
|
5475
|
+
* `robert.bob@example.com`;
|
|
5476
|
+
* - a display name, which is user-controlled and accepted up to 255
|
|
5477
|
+
* characters with no constraint on its content, containing an address
|
|
5478
|
+
* belonging to someone else.
|
|
5479
|
+
*
|
|
5480
|
+
* The ordering makes it worse rather than better — `array_length(roles) DESC
|
|
5481
|
+
* NULLS LAST` puts the most privileged match first, so the account most likely
|
|
5482
|
+
* to be reset by mistake is an admin's.
|
|
5483
|
+
*
|
|
5484
|
+
* Returns `undefined` when nothing matched exactly, which the caller reports
|
|
5485
|
+
* rather than falling through to a guess. The direct-database fallback below
|
|
5486
|
+
* has always matched with `eq(usersTable.email, email)`; this is the same
|
|
5487
|
+
* definition, so the command no longer resets different accounts depending on
|
|
5488
|
+
* whether the backend happened to be running.
|
|
5489
|
+
*/
|
|
5490
|
+
function selectUserForEmail(payload, email) {
|
|
5491
|
+
const wanted = email.trim().toLowerCase();
|
|
5492
|
+
if (!wanted) return void 0;
|
|
5493
|
+
const rows = Array.isArray(payload) ? payload : payload && typeof payload === "object" && Array.isArray(payload.users) ? payload.users : [];
|
|
5494
|
+
for (const row of rows) {
|
|
5495
|
+
if (!row || typeof row !== "object") continue;
|
|
5496
|
+
const record = row;
|
|
5497
|
+
const rowEmail = typeof record.email === "string" ? record.email.trim().toLowerCase() : void 0;
|
|
5498
|
+
if (!rowEmail || rowEmail !== wanted) continue;
|
|
5499
|
+
const id = typeof record.id === "string" ? record.id : typeof record.uid === "string" ? record.uid : void 0;
|
|
5500
|
+
if (!id) continue;
|
|
5501
|
+
return {
|
|
5502
|
+
id,
|
|
5503
|
+
email: record.email
|
|
5504
|
+
};
|
|
5505
|
+
}
|
|
5506
|
+
}
|
|
5330
5507
|
async function authCommand(subcommand, rawArgs) {
|
|
5331
5508
|
if (!subcommand || subcommand === "--help") {
|
|
5332
5509
|
printAuthHelp();
|
|
@@ -5363,12 +5540,8 @@ async function resetPassword(rawArgs) {
|
|
|
5363
5540
|
process.exit(1);
|
|
5364
5541
|
}
|
|
5365
5542
|
const projectRoot = requireProjectRoot();
|
|
5366
|
-
let envServiceKey;
|
|
5367
5543
|
const envFile = findEnvFile(projectRoot);
|
|
5368
|
-
|
|
5369
|
-
const match = fs.readFileSync(envFile, "utf8").match(/^\s*REBASE_SERVICE_KEY\s*=\s*['"]?(.*?)['"]?\s*$/m);
|
|
5370
|
-
if (match && match[1]) envServiceKey = match[1];
|
|
5371
|
-
} catch {}
|
|
5544
|
+
const envServiceKey = readEnvFile(projectRoot).REBASE_SERVICE_KEY;
|
|
5372
5545
|
let baseUrl = process.env.REBASE_BASE_URL;
|
|
5373
5546
|
let serviceKey = process.env.REBASE_SERVICE_KEY || envServiceKey;
|
|
5374
5547
|
const statePath = path.join(projectRoot, ".rebase", "state.json");
|
|
@@ -5388,7 +5561,7 @@ async function resetPassword(rawArgs) {
|
|
|
5388
5561
|
try {
|
|
5389
5562
|
const finalPass = newPassword || "NewPassword123!";
|
|
5390
5563
|
const cleanBaseUrl = baseUrl.replace(/\/+$/, "");
|
|
5391
|
-
const searchUrl = `${cleanBaseUrl}/api/admin/users?search=${encodeURIComponent(email)}&limit=
|
|
5564
|
+
const searchUrl = `${cleanBaseUrl}/api/admin/users?search=${encodeURIComponent(email)}&limit=50`;
|
|
5392
5565
|
const searchRes = await fetch(searchUrl, { headers: {
|
|
5393
5566
|
"Authorization": `Bearer ${serviceKey}`,
|
|
5394
5567
|
"Accept": "application/json"
|
|
@@ -5396,18 +5569,9 @@ async function resetPassword(rawArgs) {
|
|
|
5396
5569
|
if (!searchRes.ok) throw new Error(`Failed to list users: ${searchRes.statusText}`);
|
|
5397
5570
|
const searchData = await searchRes.json();
|
|
5398
5571
|
if (!searchData || typeof searchData !== "object") throw new Error("Invalid response format from user search API.");
|
|
5399
|
-
|
|
5400
|
-
if (
|
|
5401
|
-
|
|
5402
|
-
if (firstUser && typeof firstUser === "object" && "id" in firstUser && typeof firstUser.id === "string") userId = firstUser.id;
|
|
5403
|
-
else if (firstUser && typeof firstUser === "object" && "uid" in firstUser && typeof firstUser.uid === "string") userId = firstUser.uid;
|
|
5404
|
-
} else if ("users" in searchData && Array.isArray(searchData.users)) {
|
|
5405
|
-
const firstUser = searchData.users[0];
|
|
5406
|
-
if (firstUser && typeof firstUser === "object" && "id" in firstUser && typeof firstUser.id === "string") userId = firstUser.id;
|
|
5407
|
-
else if (firstUser && typeof firstUser === "object" && "uid" in firstUser && typeof firstUser.uid === "string") userId = firstUser.uid;
|
|
5408
|
-
}
|
|
5409
|
-
if (!userId) throw new Error(`User not found with email: ${email}`);
|
|
5410
|
-
const resetUrl = `${cleanBaseUrl}/api/admin/users/${userId}/reset-password`;
|
|
5572
|
+
const matched = selectUserForEmail(searchData, email);
|
|
5573
|
+
if (!matched) throw new Error(`No user has the email ${email}.`);
|
|
5574
|
+
const resetUrl = `${cleanBaseUrl}/api/admin/users/${matched.id}/reset-password`;
|
|
5411
5575
|
const resetRes = await fetch(resetUrl, {
|
|
5412
5576
|
method: "POST",
|
|
5413
5577
|
headers: {
|
|
@@ -5424,7 +5588,7 @@ async function resetPassword(rawArgs) {
|
|
|
5424
5588
|
console.log("API reset successful.");
|
|
5425
5589
|
console.log(chalk.bold(" 🔑 Rebase Auth — Reset Password (via API)"));
|
|
5426
5590
|
console.log("");
|
|
5427
|
-
console.log(` ${chalk.gray("Email:")} ${email}`);
|
|
5591
|
+
console.log(` ${chalk.gray("Email:")} ${matched.email}`);
|
|
5428
5592
|
console.log(` ${chalk.gray("Password:")} ${finalPass}`);
|
|
5429
5593
|
console.log("");
|
|
5430
5594
|
return;
|
|
@@ -5492,10 +5656,12 @@ async function resetPassword() {
|
|
|
5492
5656
|
if (result.length > 0) {
|
|
5493
5657
|
console.log("✅ Password reset for: " + result[0].email);
|
|
5494
5658
|
${!newPassword ? "console.log(\" New password: \" + newPassword);" : ""}
|
|
5495
|
-
|
|
5496
|
-
console.log("✗ User not found: " + email);
|
|
5659
|
+
process.exit(0);
|
|
5497
5660
|
}
|
|
5498
|
-
|
|
5661
|
+
// Nothing was updated, so nothing was reset. Exiting 0 here reported
|
|
5662
|
+
// success for a no-op, which is what a script would have believed.
|
|
5663
|
+
console.error("✗ User not found: " + email);
|
|
5664
|
+
process.exit(1);
|
|
5499
5665
|
}
|
|
5500
5666
|
|
|
5501
5667
|
resetPassword().catch(console.error);
|
|
@@ -5513,11 +5679,20 @@ resetPassword().catch(console.error);
|
|
|
5513
5679
|
stdio: "inherit",
|
|
5514
5680
|
env
|
|
5515
5681
|
});
|
|
5682
|
+
const cleanup = () => {
|
|
5683
|
+
try {
|
|
5684
|
+
fs.unlinkSync(tmpScriptPath);
|
|
5685
|
+
} catch {}
|
|
5686
|
+
};
|
|
5516
5687
|
return new Promise((resolve) => {
|
|
5688
|
+
child.on("error", (err) => {
|
|
5689
|
+
cleanup();
|
|
5690
|
+
console.error(chalk.red("✗ Could not run the reset script."));
|
|
5691
|
+
console.error(chalk.gray(` ${err.message}`));
|
|
5692
|
+
process.exit(1);
|
|
5693
|
+
});
|
|
5517
5694
|
child.on("close", (code) => {
|
|
5518
|
-
|
|
5519
|
-
fs.unlinkSync(tmpScriptPath);
|
|
5520
|
-
} catch {}
|
|
5695
|
+
cleanup();
|
|
5521
5696
|
if (code !== 0) process.exit(code ?? 1);
|
|
5522
5697
|
resolve();
|
|
5523
5698
|
});
|
|
@@ -5813,25 +5988,13 @@ ${chalk.green.bold("Examples")}
|
|
|
5813
5988
|
* create — Create a new API key
|
|
5814
5989
|
* revoke — Revoke an existing API key
|
|
5815
5990
|
*/
|
|
5816
|
-
|
|
5817
|
-
|
|
5818
|
-
|
|
5819
|
-
|
|
5820
|
-
|
|
5821
|
-
|
|
5822
|
-
|
|
5823
|
-
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
5824
|
-
const idx = trimmed.indexOf("=");
|
|
5825
|
-
if (idx > 0) {
|
|
5826
|
-
const key = trimmed.slice(0, idx).trim();
|
|
5827
|
-
let value = trimmed.slice(idx + 1).trim();
|
|
5828
|
-
if (value.startsWith("\"") && value.endsWith("\"") || value.startsWith("'") && value.endsWith("'")) value = value.slice(1, -1);
|
|
5829
|
-
env[key] = value;
|
|
5830
|
-
}
|
|
5831
|
-
}
|
|
5832
|
-
}
|
|
5833
|
-
return env;
|
|
5834
|
-
}
|
|
5991
|
+
/**
|
|
5992
|
+
* Was a hand-rolled `indexOf("=")` loop. It keyed `export KEY=value` as
|
|
5993
|
+
* `export KEY` and carried a trailing `# comment` into the value — so a key
|
|
5994
|
+
* that was present read as absent, or reached an `Authorization` header with a
|
|
5995
|
+
* comment attached and came back 401. See `readEnvFile`.
|
|
5996
|
+
*/
|
|
5997
|
+
var loadEnv = readEnvFile;
|
|
5835
5998
|
function resolveBaseUrl(env, projectRoot) {
|
|
5836
5999
|
if (env.REBASE_BASE_URL) return env.REBASE_BASE_URL;
|
|
5837
6000
|
if (projectRoot) try {
|
|
@@ -10940,6 +11103,6 @@ function telemetryNotice() {
|
|
|
10940
11103
|
return chalk.gray(`Usage sharing: ${sharing ? "on" : "off"} — ${chalk.cyan("rebase telemetry")} to inspect or change\n`);
|
|
10941
11104
|
}
|
|
10942
11105
|
//#endregion
|
|
10943
|
-
export { CURRENT_RUNTIME_RANGE, DEFAULT_BUNDLE_DIR, DEFAULT_CONFIG_DIR, DEFAULT_CRONS_DIR, DEFAULT_FUNCTIONS_DIR, DEFAULT_SCHEMA_FILE, DEV_PORT_FILENAME, MANIFEST_FILENAME, ManifestError, TEMPLATE_PLACEHOLDER_FILES, appsCommand, assessManagedCompatibility, authCommand, buildBundle, buildCommand, buildInitQuestions, buildStaticBundle, buildableApps, cloudCommand, collectDeclaredDependencies, configureEnvFile, createRebaseApp, dbCommand, detectFrameworkDepDrift, detectNativeDependencies, detectPackageManager, detectStorageAuthorize, devCommand, doctorCommand, ejectCommand, entry, findBackendApp, findBackendDir, findEnvFile, findFrontendDir, findProjectRoot, findUnusedServerEntry, foldStaticIntoBundle, formatCdTarget, generateSdkCommand, getActiveBackendPlugin, getPMCommands, getProjectPort, isIdentifierLike, isPnpmAvailable, loadManifest, manifestExists, manifestPath, normalizeEsmSpecifiers, pnpmAvailabilityFromProbe, positionals, printInitHelp, requireBackendDir, requireProjectRoot, resetPnpmAvailabilityCache, resolveBackendPaths, resolveCliVersion, resolveExampleBaseUrl, resolveLocalBin, resolvePluginCliScript, resolveRuntimeImageTag, resolveStartPort, resolveTsx, schemaCommand, startCommand, synthesizeManifest, validateManifest, validateProjectName, validateTsxInstallation, writeManifest };
|
|
11106
|
+
export { CURRENT_RUNTIME_RANGE, DEFAULT_BUNDLE_DIR, DEFAULT_CONFIG_DIR, DEFAULT_CRONS_DIR, DEFAULT_FUNCTIONS_DIR, DEFAULT_SCHEMA_FILE, DEV_PORT_FILENAME, MANIFEST_FILENAME, ManifestError, TEMPLATE_PLACEHOLDER_FILES, appsCommand, assessManagedCompatibility, authCommand, buildBundle, buildCommand, buildInitQuestions, buildStaticBundle, buildableApps, cloudCommand, collectDeclaredDependencies, configureEnvFile, createRebaseApp, dbCommand, detectFrameworkDepDrift, detectNativeDependencies, detectPackageManager, detectStorageAuthorize, devCommand, doctorCommand, ejectCommand, entry, findBackendApp, findBackendDir, findEnvFile, findFrontendDir, findProjectRoot, findUnusedServerEntry, foldStaticIntoBundle, formatCdTarget, generateSdkCommand, getActiveBackendPlugin, getPMCommands, getProjectPort, isIdentifierLike, isPnpmAvailable, loadManifest, manifestExists, manifestPath, normalizeEsmSpecifiers, pnpmAvailabilityFromProbe, positionals, printInitHelp, readEnvFile, requireBackendDir, requireProjectRoot, resetPnpmAvailabilityCache, resolveBackendPaths, resolveCliVersion, resolveExampleBaseUrl, resolveLocalBin, resolvePluginCliScript, resolveRuntimeImageTag, resolveStartPort, resolveTsx, schemaCommand, selectUserForEmail, startCommand, synthesizeManifest, validateManifest, validateProjectName, validateTsxInstallation, writeManifest };
|
|
10944
11107
|
|
|
10945
11108
|
//# sourceMappingURL=index.es.js.map
|