@hot-updater/supabase 0.35.8 → 0.35.9

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.
@@ -6276,6 +6276,112 @@ createExeca(mapNode);
6276
6276
  createExeca(mapScriptAsync, {}, deepScriptOptions, setScriptSync);
6277
6277
  const { sendMessage, getOneMessage, getEachMessage, getCancelSignal } = getIpcExport();
6278
6278
  //#endregion
6279
+ //#region iac/init/index.ts
6280
+ const SUPABASE_DATABASE_PASSWORD_PROJECT_ID_ENV_KEY = "HOT_UPDATER_SUPABASE_DB_PASSWORD_PROJECT_ID";
6281
+ const SUPABASE_REGION_VALUES = [
6282
+ "ap-east-1",
6283
+ "ap-northeast-1",
6284
+ "ap-northeast-2",
6285
+ "ap-south-1",
6286
+ "ap-southeast-1",
6287
+ "ap-southeast-2",
6288
+ "ca-central-1",
6289
+ "eu-central-1",
6290
+ "eu-central-2",
6291
+ "eu-north-1",
6292
+ "eu-west-1",
6293
+ "eu-west-2",
6294
+ "eu-west-3",
6295
+ "sa-east-1",
6296
+ "us-east-1",
6297
+ "us-east-2",
6298
+ "us-west-1",
6299
+ "us-west-2"
6300
+ ];
6301
+ const isSupabaseRegion = (value) => value !== void 0 && SUPABASE_REGION_VALUES.some((region) => region === value);
6302
+ const isSupabaseFunctionName = (value) => value !== void 0 && /^[A-Za-z][A-Za-z0-9_-]*$/.test(value);
6303
+ const initProvider = {
6304
+ label: "Supabase",
6305
+ inputs: {
6306
+ projectId: {
6307
+ envKey: "HOT_UPDATER_SUPABASE_PROJECT_ID",
6308
+ help: "Supabase project reference"
6309
+ },
6310
+ projectName: {
6311
+ envKey: "HOT_UPDATER_SUPABASE_PROJECT_NAME",
6312
+ help: "Project name used when creating a Supabase project",
6313
+ optional: true,
6314
+ prompt: {
6315
+ defaultValue: "hot-updater",
6316
+ message: "Enter a name for the new Supabase project",
6317
+ placeholder: "hot-updater",
6318
+ type: "text"
6319
+ }
6320
+ },
6321
+ organizationSlug: {
6322
+ envKey: "HOT_UPDATER_SUPABASE_ORGANIZATION_SLUG",
6323
+ help: "Organization slug used when creating a Supabase project",
6324
+ optional: true,
6325
+ prompt: {
6326
+ message: "Select a Supabase organization",
6327
+ type: "select"
6328
+ }
6329
+ },
6330
+ region: {
6331
+ envKey: "HOT_UPDATER_SUPABASE_REGION",
6332
+ help: "Region used when creating a Supabase project",
6333
+ optional: true,
6334
+ prompt: {
6335
+ defaultValue: "us-east-1",
6336
+ message: "Select a region for the new Supabase project",
6337
+ type: "select"
6338
+ },
6339
+ validate: isSupabaseRegion
6340
+ },
6341
+ accessToken: {
6342
+ envKey: "SUPABASE_ACCESS_TOKEN",
6343
+ help: "Supabase personal access token",
6344
+ persistence: "with-consent",
6345
+ preflight: false,
6346
+ prompt: {
6347
+ message: "Enter your Supabase personal access token",
6348
+ type: "password"
6349
+ }
6350
+ },
6351
+ bucketName: {
6352
+ envKey: "HOT_UPDATER_SUPABASE_BUCKET_NAME",
6353
+ help: "Storage bucket name",
6354
+ prompt: {
6355
+ defaultValue: "hot-updater-storage",
6356
+ message: "Enter a name for the new storage bucket",
6357
+ placeholder: "hot-updater-storage",
6358
+ type: "text"
6359
+ }
6360
+ },
6361
+ functionName: {
6362
+ envKey: "HOT_UPDATER_SUPABASE_FUNCTION_NAME",
6363
+ help: "Edge Function name",
6364
+ prompt: {
6365
+ defaultValue: "update-server",
6366
+ message: "Enter a name for the edge function",
6367
+ placeholder: "update-server",
6368
+ type: "text"
6369
+ },
6370
+ validate: isSupabaseFunctionName
6371
+ },
6372
+ databasePassword: {
6373
+ envKey: "HOT_UPDATER_SUPABASE_DB_PASSWORD",
6374
+ help: "Database password, when required by the linked project",
6375
+ optional: true,
6376
+ persistence: "with-consent",
6377
+ prompt: {
6378
+ message: "Enter your Supabase database password (press Enter to skip if none)",
6379
+ type: "password"
6380
+ }
6381
+ }
6382
+ }
6383
+ };
6384
+ //#endregion
6279
6385
  //#region iac/supabaseApi.ts
6280
6386
  const supabaseApi = (supabaseUrl, supabaseServiceRoleKey) => {
6281
6387
  const supabase = (0, _supabase_supabase_js.createClient)(supabaseUrl, supabaseServiceRoleKey);
@@ -6294,10 +6400,121 @@ const supabaseApi = (supabaseUrl, supabaseServiceRoleKey) => {
6294
6400
  const { data, error } = await supabase.storage.createBucket(bucketName, options);
6295
6401
  if (error) throw error;
6296
6402
  return data;
6403
+ },
6404
+ updateBucket: async (bucketId, options) => {
6405
+ const { error } = await supabase.storage.updateBucket(bucketId, options);
6406
+ if (error) throw error;
6297
6407
  }
6298
6408
  };
6299
6409
  };
6300
6410
  //#endregion
6411
+ //#region iac/supabaseAuthentication.ts
6412
+ const SUPABASE_AUTH_METHOD = {
6413
+ accessToken: "access-token",
6414
+ cliLogin: "cli-login"
6415
+ };
6416
+ const SUPABASE_LOGIN_URL_PATTERN = /https:\/\/supabase\.com\/dashboard\/cli\/login\?[A-Za-z0-9._~!$&'()*+,;=:@/?%-]+/;
6417
+ const openBrowser = async (url) => {
6418
+ if (process.platform === "darwin") {
6419
+ await execa("open", [url]);
6420
+ return;
6421
+ }
6422
+ if (process.platform === "win32") {
6423
+ await execa("rundll32.exe", ["url.dll,FileProtocolHandler", url]);
6424
+ return;
6425
+ }
6426
+ await execa("xdg-open", [url]);
6427
+ };
6428
+ const hasValidSupabaseCliLogin = async () => {
6429
+ return (await execa("npx", [
6430
+ "-y",
6431
+ "supabase",
6432
+ "projects",
6433
+ "list",
6434
+ "--output",
6435
+ "json",
6436
+ "--agent",
6437
+ "no"
6438
+ ], { reject: false })).exitCode === 0;
6439
+ };
6440
+ const getSupabaseCliEnv = (accessToken) => accessToken ? { [initProvider.inputs.accessToken.envKey]: accessToken } : void 0;
6441
+ const inputSupabaseAccessToken = async (accessToken) => {
6442
+ if (accessToken) return accessToken;
6443
+ const authMethod = await _hot_updater_cli_tools.p.select({
6444
+ message: "How do you want to authenticate with Supabase?",
6445
+ options: [{
6446
+ label: "Use Supabase CLI login",
6447
+ value: SUPABASE_AUTH_METHOD.cliLogin
6448
+ }, {
6449
+ label: "Enter a personal access token",
6450
+ value: SUPABASE_AUTH_METHOD.accessToken
6451
+ }]
6452
+ });
6453
+ if (_hot_updater_cli_tools.p.isCancel(authMethod)) process.exit(0);
6454
+ if (authMethod === SUPABASE_AUTH_METHOD.cliLogin) {
6455
+ if (await hasValidSupabaseCliLogin()) return;
6456
+ const loginProcess = execa("npx", [
6457
+ "-y",
6458
+ "supabase",
6459
+ "login",
6460
+ "--no-browser",
6461
+ "--agent",
6462
+ "no"
6463
+ ], {
6464
+ stdin: "inherit",
6465
+ stderr: "inherit",
6466
+ stdout: "pipe"
6467
+ });
6468
+ let output = "";
6469
+ let browserOpenPromise;
6470
+ loginProcess.stdout?.on("data", (chunk) => {
6471
+ process.stdout.write(chunk);
6472
+ output += chunk.toString();
6473
+ const loginUrl = output.match(SUPABASE_LOGIN_URL_PATTERN)?.[0];
6474
+ if (loginUrl && !browserOpenPromise) browserOpenPromise = openBrowser(loginUrl).catch(() => {
6475
+ _hot_updater_cli_tools.p.log.warn("Could not open the Supabase login page automatically.");
6476
+ });
6477
+ });
6478
+ await loginProcess;
6479
+ await browserOpenPromise;
6480
+ return;
6481
+ }
6482
+ _hot_updater_cli_tools.p.log.step(`Personal access token: ${(0, _hot_updater_cli_tools.link)("https://supabase.com/dashboard/account/tokens")}`);
6483
+ const selectedAccessToken = await _hot_updater_cli_tools.p.password({
6484
+ message: "Enter your Supabase personal access token",
6485
+ validate: (value) => value ? void 0 : "Supabase access token is required"
6486
+ });
6487
+ if (_hot_updater_cli_tools.p.isCancel(selectedAccessToken)) process.exit(0);
6488
+ return selectedAccessToken;
6489
+ };
6490
+ //#endregion
6491
+ //#region iac/supabaseBucketPrivacy.ts
6492
+ var PublicSupabaseBucketError = class extends _hot_updater_cli_tools.InitError {
6493
+ name = "PublicSupabaseBucketError";
6494
+ constructor(bucketName) {
6495
+ super([
6496
+ `Supabase bucket "${bucketName}" is public.`,
6497
+ "Make the bucket private in Supabase Storage, then rerun init.",
6498
+ "Alternatively, rerun without --env-file to approve the change interactively."
6499
+ ].join("\n"));
6500
+ this.bucketName = bucketName;
6501
+ }
6502
+ };
6503
+ const ensureSupabaseBucketPrivate = async ({ api, nonInteractive, selection }) => {
6504
+ if (selection.create || !selection.isPublic) return;
6505
+ if (nonInteractive) throw new PublicSupabaseBucketError(selection.name);
6506
+ const confirmed = await _hot_updater_cli_tools.p.confirm({
6507
+ message: `Bucket "${selection.name}" is public. Make it private?`,
6508
+ initialValue: true
6509
+ });
6510
+ if (_hot_updater_cli_tools.p.isCancel(confirmed) || !confirmed) {
6511
+ _hot_updater_cli_tools.p.log.info("Init cancelled.");
6512
+ process.exit(1);
6513
+ }
6514
+ await api.updateBucket(selection.id, { public: false });
6515
+ _hot_updater_cli_tools.p.log.success(`Bucket "${selection.name}" is now private.`);
6516
+ };
6517
+ //#endregion
6301
6518
  //#region iac/supabaseCli.ts
6302
6519
  const SUPABASE_CONFIG_TEMPLATE = `
6303
6520
  project_id = "%%projectId%%"
@@ -6312,6 +6529,10 @@ const SUPABASE_DATABASE_AUTH_ERROR_PATTERNS = [
6312
6529
  /SQLSTATE 28P01/i,
6313
6530
  /invalid SCRAM server-final-message/i
6314
6531
  ];
6532
+ const getSupabaseCommandEnv = (accessToken, dbPassword) => accessToken || dbPassword ? {
6533
+ ...getSupabaseCliEnv(accessToken),
6534
+ ...dbPassword ? { SUPABASE_DB_PASSWORD: dbPassword } : {}
6535
+ } : void 0;
6315
6536
  const isSupabaseDatabaseAuthError = (err) => {
6316
6537
  const stderr = err.stderr;
6317
6538
  return typeof stderr === "string" && SUPABASE_DATABASE_AUTH_ERROR_PATTERNS.some((pattern) => pattern.test(stderr));
@@ -6319,11 +6540,18 @@ const isSupabaseDatabaseAuthError = (err) => {
6319
6540
  const handleSupabaseDatabaseCommandError = (err, { dbPassword, stderrInherited = false }) => {
6320
6541
  if (err instanceof ExecaError) if (dbPassword && isSupabaseDatabaseAuthError(err)) _hot_updater_cli_tools.p.log.error(SUPABASE_DATABASE_CONNECTION_ERROR);
6321
6542
  else if (!stderrInherited && err.stderr) _hot_updater_cli_tools.p.log.error(err.stderr);
6322
- else console.error(err);
6543
+ else _hot_updater_cli_tools.p.log.error(err.message);
6323
6544
  else console.error(err);
6324
6545
  process.exit(1);
6325
6546
  };
6326
- const linkSupabase = async (workdir, { projectId, dbPassword }) => {
6547
+ const confirmSupabaseDatabaseMigrations = async ({ nonInteractive }) => {
6548
+ if (nonInteractive) return true;
6549
+ return await _hot_updater_cli_tools.p.confirm({
6550
+ message: "Apply Hot Updater database migrations to the selected Supabase project?",
6551
+ initialValue: true
6552
+ }) === true;
6553
+ };
6554
+ const linkSupabase = async (workdir, { accessToken, projectId, dbPassword }) => {
6327
6555
  const spinner = _hot_updater_cli_tools.p.spinner();
6328
6556
  try {
6329
6557
  await fs_promises.default.writeFile(path.default.join(workdir, "supabase", "config.toml"), (0, _hot_updater_cli_tools.transformTemplate)(SUPABASE_CONFIG_TEMPLATE, { projectId }));
@@ -6337,7 +6565,7 @@ const linkSupabase = async (workdir, { projectId, dbPassword }) => {
6337
6565
  workdir
6338
6566
  ], {
6339
6567
  cwd: workdir,
6340
- env: dbPassword ? { SUPABASE_DB_PASSWORD: dbPassword } : void 0,
6568
+ env: getSupabaseCommandEnv(accessToken, dbPassword),
6341
6569
  input: "",
6342
6570
  stdio: [
6343
6571
  "pipe",
@@ -6348,19 +6576,20 @@ const linkSupabase = async (workdir, { projectId, dbPassword }) => {
6348
6576
  spinner.stop("Supabase linked ✔");
6349
6577
  } catch (err) {
6350
6578
  spinner.stop();
6351
- handleSupabaseDatabaseCommandError(err, { dbPassword });
6579
+ handleSupabaseDatabaseCommandError(err instanceof Error ? err : new Error(String(err)), { dbPassword });
6352
6580
  }
6353
6581
  };
6354
- const pushDB = async (workdir, { dbPassword }) => {
6582
+ const pushDB = async (workdir, { accessToken, dbPassword }) => {
6355
6583
  try {
6356
6584
  const dbPush = await execa("npx", [
6357
6585
  "supabase",
6358
6586
  "db",
6359
6587
  "push",
6360
- "--include-all"
6588
+ "--include-all",
6589
+ "--yes"
6361
6590
  ], {
6362
6591
  cwd: workdir,
6363
- env: dbPassword ? { SUPABASE_DB_PASSWORD: dbPassword } : void 0,
6592
+ env: getSupabaseCommandEnv(accessToken, dbPassword),
6364
6593
  stderr: ["pipe", "inherit"],
6365
6594
  stdin: "inherit",
6366
6595
  stdout: "inherit"
@@ -6368,17 +6597,273 @@ const pushDB = async (workdir, { dbPassword }) => {
6368
6597
  _hot_updater_cli_tools.p.log.success("DB pushed ✔");
6369
6598
  return dbPush.stdout;
6370
6599
  } catch (err) {
6371
- handleSupabaseDatabaseCommandError(err, {
6600
+ handleSupabaseDatabaseCommandError(err instanceof Error ? err : new Error(String(err)), {
6372
6601
  dbPassword,
6373
6602
  stderrInherited: true
6374
6603
  });
6375
6604
  }
6376
6605
  };
6377
6606
  //#endregion
6607
+ //#region iac/supabaseDeploymentInputs.ts
6608
+ const assertSupabaseNonInteractiveInputs = async (inputs, nonInteractive, validateCliLogin = hasValidSupabaseCliLogin) => {
6609
+ (0, _hot_updater_cli_tools.assertInitProviderInputs)({
6610
+ inputs: nonInteractive && inputs.accessToken === void 0 && await validateCliLogin() ? {
6611
+ ...inputs,
6612
+ accessToken: "supabase-cli-login"
6613
+ } : inputs,
6614
+ provider: initProvider,
6615
+ strict: nonInteractive
6616
+ });
6617
+ };
6618
+ const inputSupabaseDeploymentInputs = async ({ accessToken, functionName, nonInteractive }) => {
6619
+ const { inputs } = initProvider;
6620
+ (0, _hot_updater_cli_tools.assertInitInputs)({
6621
+ inputs: { [inputs.functionName.envKey]: functionName },
6622
+ strict: nonInteractive
6623
+ });
6624
+ if (nonInteractive && functionName) return {
6625
+ accessToken,
6626
+ functionName
6627
+ };
6628
+ const savedFunctionName = isSupabaseFunctionName(functionName) ? functionName : void 0;
6629
+ return _hot_updater_cli_tools.p.group({
6630
+ accessToken: () => nonInteractive && accessToken ? Promise.resolve(accessToken) : inputSupabaseAccessToken(),
6631
+ functionName: () => nonInteractive && savedFunctionName ? Promise.resolve(savedFunctionName) : _hot_updater_cli_tools.p.text({
6632
+ ...(0, _hot_updater_cli_tools.getInitProviderTextPromptValues)(inputs.functionName.prompt, savedFunctionName),
6633
+ message: inputs.functionName.prompt.message,
6634
+ validate: (value) => isSupabaseFunctionName(value) ? void 0 : "Start with a letter and use only letters, numbers, underscores, or hyphens"
6635
+ })
6636
+ }, { onCancel: () => process.exit(0) });
6637
+ };
6638
+ //#endregion
6639
+ //#region iac/supabaseInitInputs.ts
6640
+ const resolveSupabaseInitInputs = (existingEnv, sources = {}) => {
6641
+ const { inputs } = initProvider;
6642
+ const managedEnv = sources.managedEnv ?? existingEnv;
6643
+ const projectId = (0, _hot_updater_cli_tools.resolveInitProviderInput)(existingEnv, inputs.projectId) ?? (0, _hot_updater_cli_tools.getHotUpdaterEnvValue)(existingEnv, "HOT_UPDATER_SUPABASE_URL")?.match(/^https:\/\/([^.]+)\.supabase\.co/)?.[1];
6644
+ const databasePasswordKey = inputs.databasePassword.envKey;
6645
+ const hasProcessDatabasePassword = process.env[databasePasswordKey] !== void 0;
6646
+ const hasInputDatabasePassword = sources.inputEnv !== void 0 && Object.hasOwn(sources.inputEnv, databasePasswordKey);
6647
+ const managedDatabasePasswordProjectId = managedEnv[SUPABASE_DATABASE_PASSWORD_PROJECT_ID_ENV_KEY]?.trim();
6648
+ const region = (0, _hot_updater_cli_tools.resolveInitProviderInput)(existingEnv, inputs.region);
6649
+ const databasePassword = hasProcessDatabasePassword ? process.env[databasePasswordKey]?.trim() || void 0 : hasInputDatabasePassword ? sources.inputEnv?.[databasePasswordKey]?.trim() || void 0 : managedDatabasePasswordProjectId === projectId ? managedEnv[databasePasswordKey]?.trim() || void 0 : void 0;
6650
+ return {
6651
+ accessToken: (0, _hot_updater_cli_tools.resolveInitProviderInput)(existingEnv, inputs.accessToken),
6652
+ bucketName: (0, _hot_updater_cli_tools.resolveInitProviderInput)(existingEnv, inputs.bucketName),
6653
+ databasePassword,
6654
+ functionName: (0, _hot_updater_cli_tools.resolveInitProviderInput)(existingEnv, inputs.functionName),
6655
+ organizationSlug: (0, _hot_updater_cli_tools.resolveInitProviderInput)(existingEnv, inputs.organizationSlug),
6656
+ projectId,
6657
+ projectName: (0, _hot_updater_cli_tools.resolveInitProviderInput)(existingEnv, inputs.projectName),
6658
+ region: isSupabaseRegion(region) ? region : void 0
6659
+ };
6660
+ };
6661
+ const inputSupabaseDatabasePassword = async ({ cliHandlesPrompt = false, databasePassword, nonInteractive, required = false }) => {
6662
+ if (cliHandlesPrompt) return "";
6663
+ if (nonInteractive) {
6664
+ if (required && !databasePassword) throw new _hot_updater_cli_tools.MissingInitInputsError([initProvider.inputs.databasePassword.envKey]);
6665
+ return databasePassword ?? "";
6666
+ }
6667
+ const password = await _hot_updater_cli_tools.p.password({
6668
+ message: initProvider.inputs.databasePassword.prompt.message,
6669
+ validate: (value) => required && !value ? "A database password is required to create a Supabase project" : void 0
6670
+ });
6671
+ if (_hot_updater_cli_tools.p.isCancel(password)) process.exit(0);
6672
+ return password;
6673
+ };
6674
+ const inputSupabaseProjectCreationInputs = async ({ bucketName, organizationSlug, organizations, projectName, region }) => {
6675
+ if (organizations.length === 0) throw new Error("No Supabase organization is available for project creation.");
6676
+ const savedOrganization = organizations.find((organization) => organization.slug === organizationSlug);
6677
+ if (organizationSlug && !savedOrganization) _hot_updater_cli_tools.p.log.warn("Saved Supabase organization was not found. Select an organization again.");
6678
+ const defaultRegion = initProvider.inputs.region.prompt.defaultValue;
6679
+ const selectedProjectName = await _hot_updater_cli_tools.p.text({
6680
+ ...(0, _hot_updater_cli_tools.getInitProviderTextPromptValues)(initProvider.inputs.projectName.prompt, projectName),
6681
+ message: initProvider.inputs.projectName.prompt.message,
6682
+ validate: (value) => value ? void 0 : "Supabase project name is required"
6683
+ });
6684
+ if (_hot_updater_cli_tools.p.isCancel(selectedProjectName)) process.exit(0);
6685
+ const selectedOrganizationSlug = await _hot_updater_cli_tools.p.select({
6686
+ initialValue: savedOrganization?.slug ?? organizations[0]?.slug,
6687
+ message: initProvider.inputs.organizationSlug.prompt.message,
6688
+ options: organizations.map((organization) => ({
6689
+ label: organization.name,
6690
+ value: organization.slug
6691
+ }))
6692
+ });
6693
+ if (_hot_updater_cli_tools.p.isCancel(selectedOrganizationSlug)) process.exit(0);
6694
+ const selectedRegion = await _hot_updater_cli_tools.p.select({
6695
+ message: initProvider.inputs.region.prompt.message,
6696
+ initialValue: region ?? (isSupabaseRegion(defaultRegion) ? defaultRegion : void 0),
6697
+ options: SUPABASE_REGION_VALUES.map((value) => ({
6698
+ label: value,
6699
+ value
6700
+ }))
6701
+ });
6702
+ if (_hot_updater_cli_tools.p.isCancel(selectedRegion)) process.exit(0);
6703
+ const selectedBucketName = await _hot_updater_cli_tools.p.text({
6704
+ ...(0, _hot_updater_cli_tools.getInitProviderTextPromptValues)(initProvider.inputs.bucketName.prompt, bucketName),
6705
+ message: initProvider.inputs.bucketName.prompt.message,
6706
+ validate: (value) => value ? void 0 : "Storage bucket name is required"
6707
+ });
6708
+ if (_hot_updater_cli_tools.p.isCancel(selectedBucketName)) process.exit(0);
6709
+ return {
6710
+ bucketName: selectedBucketName,
6711
+ organizationSlug: selectedOrganizationSlug,
6712
+ projectName: selectedProjectName,
6713
+ region: selectedRegion
6714
+ };
6715
+ };
6716
+ //#endregion
6717
+ //#region iac/supabaseCliManagementApi.ts
6718
+ var SupabaseCliResponseError = class extends Error {
6719
+ constructor(message) {
6720
+ super(message);
6721
+ this.name = "SupabaseCliResponseError";
6722
+ }
6723
+ };
6724
+ const isRecord$1 = (value) => typeof value === "object" && value !== null;
6725
+ const runSupabaseCli = async (args) => {
6726
+ const result = await execa("npx", [
6727
+ "-y",
6728
+ "supabase",
6729
+ ...args
6730
+ ], { env: void 0 });
6731
+ return JSON.parse(result.stdout);
6732
+ };
6733
+ const supabaseCliManagementApi = () => ({
6734
+ listOrganizations: async () => {
6735
+ const body = await runSupabaseCli([
6736
+ "orgs",
6737
+ "list",
6738
+ "--output",
6739
+ "json"
6740
+ ]);
6741
+ if (!Array.isArray(body)) throw new SupabaseCliResponseError("Supabase organizations response was invalid.");
6742
+ return body.flatMap((organization) => isRecord$1(organization) && typeof organization.id === "string" && typeof organization.name === "string" && typeof organization.slug === "string" ? [{
6743
+ id: organization.id,
6744
+ name: organization.name,
6745
+ slug: organization.slug
6746
+ }] : []);
6747
+ },
6748
+ createProject: async ({ name, organizationSlug, region }) => {
6749
+ await execa("npx", [
6750
+ "-y",
6751
+ "supabase",
6752
+ "projects",
6753
+ "create",
6754
+ name,
6755
+ "--org-id",
6756
+ organizationSlug,
6757
+ "--region",
6758
+ region,
6759
+ "--agent",
6760
+ "no"
6761
+ ], { stdio: "inherit" });
6762
+ const body = await runSupabaseCli([
6763
+ "projects",
6764
+ "list",
6765
+ "--output",
6766
+ "json"
6767
+ ]);
6768
+ if (!Array.isArray(body)) throw new SupabaseCliResponseError("Supabase projects response was invalid after project creation.");
6769
+ const project = body.find((candidate) => isRecord$1(candidate) && candidate.name === name && candidate.organization_slug === organizationSlug && candidate.region === region);
6770
+ if (!isRecord$1(project) || typeof project.id !== "string" || typeof project.name !== "string" || typeof project.region !== "string") throw new SupabaseCliResponseError("Created Supabase project was not found.");
6771
+ return {
6772
+ id: project.id,
6773
+ name: project.name,
6774
+ region: project.region
6775
+ };
6776
+ },
6777
+ getProjectStatus: async (projectId) => {
6778
+ const body = await runSupabaseCli([
6779
+ "projects",
6780
+ "list",
6781
+ "--output",
6782
+ "json"
6783
+ ]);
6784
+ if (!Array.isArray(body)) throw new SupabaseCliResponseError("Supabase projects response was invalid.");
6785
+ const project = body.find((candidate) => isRecord$1(candidate) && (candidate.id === projectId || candidate.ref === projectId));
6786
+ if (!isRecord$1(project) || typeof project.status !== "string") throw new SupabaseCliResponseError("Supabase project status was not found.");
6787
+ return project.status;
6788
+ }
6789
+ });
6790
+ //#endregion
6791
+ //#region iac/supabaseManagementApi.ts
6792
+ const SUPABASE_MANAGEMENT_API_URL = "https://api.supabase.com/v1";
6793
+ const SUPABASE_MANAGEMENT_API_TIMEOUT_MS = 3e4;
6794
+ const isRecord = (value) => typeof value === "object" && value !== null;
6795
+ var SupabaseManagementApiStatusError = class extends Error {};
6796
+ const projectCreationMayHaveSucceededError = (message = "Supabase project creation could not be confirmed.", cause) => new Error(`${message} The request may have succeeded; check the organization's projects before retrying init.`, { cause });
6797
+ const request = async (accessToken, path, init) => {
6798
+ const method = init?.method ?? "GET";
6799
+ const controller = new AbortController();
6800
+ const timeout = setTimeout(() => controller.abort(), SUPABASE_MANAGEMENT_API_TIMEOUT_MS);
6801
+ timeout.unref();
6802
+ try {
6803
+ const response = await fetch(`${SUPABASE_MANAGEMENT_API_URL}${path}`, {
6804
+ body: init?.body ? JSON.stringify(init.body) : void 0,
6805
+ headers: {
6806
+ Authorization: `Bearer ${accessToken}`,
6807
+ "Content-Type": "application/json"
6808
+ },
6809
+ method,
6810
+ signal: controller.signal
6811
+ });
6812
+ if (!response.ok) throw new SupabaseManagementApiStatusError(`Supabase Management API request failed with status ${response.status}.`);
6813
+ return await response.json();
6814
+ } catch (error) {
6815
+ if (!controller.signal.aborted) {
6816
+ if (method === "POST" && !(error instanceof SupabaseManagementApiStatusError)) throw projectCreationMayHaveSucceededError(void 0, error);
6817
+ throw error;
6818
+ }
6819
+ if (method === "POST") throw projectCreationMayHaveSucceededError("Supabase project creation timed out.", error);
6820
+ throw new Error("Supabase Management API request timed out.");
6821
+ } finally {
6822
+ clearTimeout(timeout);
6823
+ }
6824
+ };
6825
+ const supabaseManagementApi = (accessToken) => accessToken === void 0 ? supabaseCliManagementApi() : {
6826
+ listOrganizations: async () => {
6827
+ const body = await request(accessToken, "/organizations");
6828
+ if (!Array.isArray(body)) throw new Error("Supabase organizations response was invalid.");
6829
+ return body.flatMap((organization) => isRecord(organization) && typeof organization.id === "string" && typeof organization.name === "string" && typeof organization.slug === "string" ? [{
6830
+ id: organization.id,
6831
+ name: organization.name,
6832
+ slug: organization.slug
6833
+ }] : []);
6834
+ },
6835
+ createProject: async ({ databasePassword, name, organizationSlug, region }) => {
6836
+ const body = await request(accessToken, "/projects", {
6837
+ body: {
6838
+ db_pass: databasePassword,
6839
+ name,
6840
+ organization_slug: organizationSlug,
6841
+ region
6842
+ },
6843
+ method: "POST"
6844
+ });
6845
+ if (!isRecord(body) || typeof body.ref !== "string" || typeof body.name !== "string" || typeof body.region !== "string") throw projectCreationMayHaveSucceededError("Supabase project creation response was invalid.");
6846
+ return {
6847
+ id: body.ref,
6848
+ name: body.name,
6849
+ region: body.region
6850
+ };
6851
+ },
6852
+ getProjectStatus: async (projectId) => {
6853
+ const body = await request(accessToken, `/projects/${encodeURIComponent(projectId)}`);
6854
+ if (!isRecord(body) || typeof body.status !== "string") throw new Error("Supabase project response was invalid.");
6855
+ return body.status;
6856
+ }
6857
+ };
6858
+ //#endregion
6378
6859
  //#region iac/index.ts
6379
6860
  const require$1 = (0, node_module.createRequire)(require("url").pathToFileURL(__filename).href);
6380
6861
  const EDGE_VENDOR_DIR = "_hot-updater";
6381
6862
  const WORKSPACE_PACKAGE_PREFIX = "@hot-updater/";
6863
+ const SUPABASE_PROJECT_READY_STATUS = "ACTIVE_HEALTHY";
6864
+ const SUPABASE_PROJECT_PROVISIONING_STATUS = "COMING_UP";
6865
+ const SUPABASE_PROJECT_READINESS_MAX_ATTEMPTS = 300;
6866
+ const SUPABASE_PROJECT_READINESS_POLL_INTERVAL_MS = 1e3;
6382
6867
  const STATIC_IMPORT_SPECIFIER_PATTERN = /^\s*(?:import|export)\s+(?:type\s+)?(?:[^"'`]+?\s+from\s+)?["']([^"']+)["'];?/gm;
6383
6868
  const DYNAMIC_IMPORT_SPECIFIER_PATTERN = /\bimport\s*\(\s*["']([^"']+)["']\s*\)/g;
6384
6869
  const getConfigScaffold = (build) => {
@@ -6556,7 +7041,7 @@ const buildEdgeFunctionImports = async (targetDir) => {
6556
7041
  const resolveEdgeFunctionDenoConfig = async (targetDir) => {
6557
7042
  return { imports: await buildEdgeFunctionImports(targetDir) };
6558
7043
  };
6559
- const selectProject = async () => {
7044
+ const selectProject = async (preferredProjectId, nonInteractive = false, accessToken) => {
6560
7045
  const spinner = _hot_updater_cli_tools.p.spinner();
6561
7046
  spinner.start("Fetching Supabase projects...");
6562
7047
  let projectsProcess = [];
@@ -6568,16 +7053,28 @@ const selectProject = async () => {
6568
7053
  "list",
6569
7054
  "--output",
6570
7055
  "json"
6571
- ], {});
7056
+ ], { env: accessToken ? { [initProvider.inputs.accessToken.envKey]: accessToken } : void 0 });
6572
7057
  projectsProcess = listProjects.stdout === "null" ? [] : JSON.parse(listProjects?.stdout ?? "[]");
6573
7058
  } catch (err) {
6574
7059
  spinner.stop();
6575
- console.error("Failed to fetch Supabase projects:", err);
7060
+ const message = err instanceof Error ? err.message : String(err);
7061
+ console.error(`Failed to fetch Supabase projects: ${message}`);
6576
7062
  process.exit(1);
6577
7063
  }
6578
7064
  spinner.stop();
7065
+ const preferredProject = projectsProcess.find((project) => project.id === preferredProjectId);
7066
+ if (nonInteractive && preferredProject) {
7067
+ _hot_updater_cli_tools.p.log.info(`Using saved Supabase project: ${preferredProject.name}`);
7068
+ return {
7069
+ create: false,
7070
+ project: preferredProject
7071
+ };
7072
+ }
7073
+ if (preferredProjectId && !preferredProject) _hot_updater_cli_tools.p.log.warn("Saved Supabase project was not found. Select a project again.");
7074
+ if (nonInteractive) throw new _hot_updater_cli_tools.MissingInitInputsError(["HOT_UPDATER_SUPABASE_PROJECT_ID"]);
6579
7075
  const createProjectOption = `create/${Math.random().toString(36).substring(2, 15)}`;
6580
7076
  const selectedProjectId = await _hot_updater_cli_tools.p.select({
7077
+ initialValue: preferredProject?.id ?? projectsProcess[0]?.id,
6581
7078
  message: "Select a Supabase project",
6582
7079
  options: [...projectsProcess.map((project) => ({
6583
7080
  label: `${project.name} (${project.region})`,
@@ -6588,29 +7085,15 @@ const selectProject = async () => {
6588
7085
  }]
6589
7086
  });
6590
7087
  if (_hot_updater_cli_tools.p.isCancel(selectedProjectId)) process.exit(0);
6591
- if (selectedProjectId === createProjectOption) {
6592
- try {
6593
- await execa("npx", [
6594
- "-y",
6595
- "supabase",
6596
- "projects",
6597
- "create"
6598
- ], {
6599
- stdio: "inherit",
6600
- shell: true
6601
- });
6602
- } catch (err) {
6603
- if (err instanceof ExecaError) console.error(err.stderr);
6604
- else console.error(err);
6605
- process.exit(1);
6606
- }
6607
- return selectProject();
6608
- }
7088
+ if (selectedProjectId === createProjectOption) return { create: true };
6609
7089
  const selectedProject = projectsProcess.find((project) => project.id === selectedProjectId);
6610
7090
  if (!selectedProject) throw new Error("Project not found");
6611
- return selectedProject;
7091
+ return {
7092
+ create: false,
7093
+ project: selectedProject
7094
+ };
6612
7095
  };
6613
- const selectBucket = async (api) => {
7096
+ const selectBucket = async (api, preferredBucketName, nonInteractive = false) => {
6614
7097
  let buckets = [];
6615
7098
  let retryCount = 0;
6616
7099
  await _hot_updater_cli_tools.p.tasks([{
@@ -6628,15 +7111,31 @@ const selectBucket = async (api) => {
6628
7111
  process.exit(1);
6629
7112
  }
6630
7113
  }]);
7114
+ const preferredBucket = buckets.find((bucket) => bucket.name === preferredBucketName);
7115
+ if (nonInteractive && preferredBucket) {
7116
+ _hot_updater_cli_tools.p.log.info(`Using saved Supabase bucket: ${preferredBucket.name}`);
7117
+ return {
7118
+ create: false,
7119
+ id: preferredBucket.id,
7120
+ isPublic: preferredBucket.isPublic,
7121
+ name: preferredBucket.name
7122
+ };
7123
+ }
7124
+ if (preferredBucketName && !preferredBucket) {
7125
+ if (nonInteractive) return {
7126
+ create: true,
7127
+ name: preferredBucketName
7128
+ };
7129
+ _hot_updater_cli_tools.p.log.warn("Saved Supabase bucket was not found. Select a bucket again.");
7130
+ }
7131
+ if (nonInteractive) throw new _hot_updater_cli_tools.MissingInitInputsError(["HOT_UPDATER_SUPABASE_BUCKET_NAME"]);
6631
7132
  const createBucketOption = `create/${Math.random().toString(36).substring(2, 15)}`;
6632
7133
  const selectedBucketId = await _hot_updater_cli_tools.p.select({
7134
+ initialValue: preferredBucket?.id ?? buckets[0]?.id,
6633
7135
  message: "Select a storage bucket",
6634
7136
  options: [...buckets.map((bucket) => ({
6635
7137
  label: bucket.name,
6636
- value: JSON.stringify({
6637
- id: bucket.id,
6638
- name: bucket.name
6639
- })
7138
+ value: bucket.id
6640
7139
  })), {
6641
7140
  label: "Create a new private bucket",
6642
7141
  value: createBucketOption
@@ -6644,34 +7143,44 @@ const selectBucket = async (api) => {
6644
7143
  });
6645
7144
  if (_hot_updater_cli_tools.p.isCancel(selectedBucketId)) process.exit(0);
6646
7145
  if (selectedBucketId === createBucketOption) {
6647
- const bucketName = await _hot_updater_cli_tools.p.text({ message: "Enter a name for the new bucket" });
7146
+ const prompt = initProvider.inputs.bucketName.prompt;
7147
+ const bucketName = await _hot_updater_cli_tools.p.text({
7148
+ ...(0, _hot_updater_cli_tools.getInitProviderTextPromptValues)(prompt, preferredBucketName),
7149
+ message: prompt.message
7150
+ });
6648
7151
  if (_hot_updater_cli_tools.p.isCancel(bucketName)) process.exit(0);
6649
- try {
6650
- await api.createBucket(bucketName, { public: false });
6651
- _hot_updater_cli_tools.p.log.success(`Bucket "${bucketName}" created successfully.`);
6652
- const newBucket = (await api.listBuckets()).find((bucket) => bucket.name === bucketName);
6653
- if (!newBucket) throw new Error("Failed to create and select new bucket");
6654
- return {
6655
- id: newBucket.id,
6656
- name: newBucket.name
6657
- };
6658
- } catch (err) {
6659
- _hot_updater_cli_tools.p.log.error(`Failed to create new bucket: ${err}`);
6660
- process.exit(1);
6661
- }
7152
+ return {
7153
+ create: true,
7154
+ name: bucketName
7155
+ };
6662
7156
  }
6663
- return JSON.parse(selectedBucketId);
7157
+ const selectedBucket = buckets.find((bucket) => bucket.id === selectedBucketId);
7158
+ if (!selectedBucket) throw new Error("Selected Supabase bucket was not found.");
7159
+ return {
7160
+ create: false,
7161
+ id: selectedBucket.id,
7162
+ isPublic: selectedBucket.isPublic,
7163
+ name: selectedBucket.name
7164
+ };
6664
7165
  };
6665
- const deployEdgeFunction = async (workdir, projectId) => {
6666
- const functionName = await _hot_updater_cli_tools.p.text({
6667
- message: "Enter a name for the edge function",
6668
- initialValue: "update-server",
6669
- placeholder: "update-server"
6670
- });
6671
- if (_hot_updater_cli_tools.p.isCancel(functionName)) process.exit(0);
7166
+ const createSelectedBucket = async (api, selection) => {
7167
+ if (!selection.create) return selection;
7168
+ await api.createBucket(selection.name, { public: false });
7169
+ _hot_updater_cli_tools.p.log.success(`Bucket "${selection.name}" created successfully.`);
7170
+ const bucket = (await api.listBuckets()).find((item) => item.name === selection.name);
7171
+ if (!bucket) throw new Error("Failed to create and select new bucket");
7172
+ return {
7173
+ id: bucket.id,
7174
+ name: bucket.name
7175
+ };
7176
+ };
7177
+ const deployEdgeFunction = async (accessToken, workdir, projectId, functionName) => {
6672
7178
  const edgeFunctionsLibPath = path.default.join(workdir, "supabase", "edge-functions");
6673
7179
  const edgeFunctionsCode = (0, _hot_updater_cli_tools.transformEnv)(path.default.join(edgeFunctionsLibPath, "index.ts"), { FUNCTION_NAME: functionName });
6674
- const targetDir = path.default.join(workdir, "supabase", "functions", functionName);
7180
+ if (!isSupabaseFunctionName(functionName)) throw new Error("Invalid Supabase Edge Function name.");
7181
+ const functionsDir = path.default.resolve(workdir, "supabase", "functions");
7182
+ const targetDir = path.default.resolve(functionsDir, functionName);
7183
+ if (!targetDir.startsWith(`${functionsDir}${path.default.sep}`)) throw new Error("Supabase Edge Function path escaped its output directory.");
6675
7184
  await fs_promises.default.mkdir(targetDir, { recursive: true });
6676
7185
  const denoConfig = await resolveEdgeFunctionDenoConfig(targetDir);
6677
7186
  const targetPath = path.default.join(targetDir, "index.ts");
@@ -6691,21 +7200,31 @@ const deployEdgeFunction = async (workdir, projectId) => {
6691
7200
  "--no-verify-jwt",
6692
7201
  "--workdir",
6693
7202
  workdir
6694
- ], { cwd: workdir })).stdout;
7203
+ ], {
7204
+ cwd: workdir,
7205
+ env: getSupabaseCliEnv(accessToken)
7206
+ })).stdout;
6695
7207
  } catch (err) {
6696
7208
  if (err instanceof ExecaError && err.stderr) _hot_updater_cli_tools.p.log.error(err.stderr);
6697
- else console.error(err);
7209
+ else if (err instanceof Error) _hot_updater_cli_tools.p.log.error(err.message);
6698
7210
  process.exit(1);
6699
7211
  }
6700
7212
  }
6701
7213
  }]);
6702
7214
  };
6703
- const runInit = async ({ build }) => {
6704
- const project = await selectProject();
6705
- const spinner = _hot_updater_cli_tools.p.spinner();
6706
- spinner.start(`Getting API keys for ${project.name}...`);
6707
- let apiKeys = [];
6708
- try {
7215
+ const waitForSupabaseProjectReady = async ({ getProjectStatus, maxAttempts = SUPABASE_PROJECT_READINESS_MAX_ATTEMPTS, onLongWait, pollIntervalMs = SUPABASE_PROJECT_READINESS_POLL_INTERVAL_MS }) => {
7216
+ let lastStatus;
7217
+ for (let attempt = 0; attempt < maxAttempts; attempt++) {
7218
+ lastStatus = await getProjectStatus();
7219
+ if (lastStatus === SUPABASE_PROJECT_READY_STATUS) return;
7220
+ if (lastStatus !== SUPABASE_PROJECT_PROVISIONING_STATUS) throw new Error(`Supabase project entered unexpected status: ${lastStatus}.`);
7221
+ if (attempt === 5) onLongWait();
7222
+ if (attempt < maxAttempts - 1) await delay(pollIntervalMs);
7223
+ }
7224
+ throw new Error(`Timed out while waiting for the Supabase project. Last status: ${lastStatus ?? "unknown"}.`);
7225
+ };
7226
+ const getSupabaseProjectAccess = async ({ accessToken, managementApi, project, waitForProject }) => {
7227
+ const getServiceRoleApiKey = async () => {
6709
7228
  const keysProcess = await execa("npx", [
6710
7229
  "-y",
6711
7230
  "supabase",
@@ -6715,17 +7234,146 @@ const runInit = async ({ build }) => {
6715
7234
  project.id,
6716
7235
  "--output",
6717
7236
  "json"
6718
- ]);
6719
- apiKeys = JSON.parse(keysProcess.stdout ?? "[]");
6720
- } catch (err) {
6721
- spinner.stop();
6722
- console.error("Failed to get API keys:", err);
6723
- process.exit(1);
7237
+ ], { env: getSupabaseCliEnv(accessToken) });
7238
+ return JSON.parse(keysProcess.stdout ?? "[]").find((key) => key.name === "service_role")?.api_key;
7239
+ };
7240
+ let serviceRoleApiKey;
7241
+ if (waitForProject) await _hot_updater_cli_tools.p.tasks([{
7242
+ title: `Waiting for ${project.name} to become ready...`,
7243
+ task: async (message) => {
7244
+ await waitForSupabaseProjectReady({
7245
+ getProjectStatus: () => managementApi.getProjectStatus(project.id),
7246
+ onLongWait: () => {
7247
+ message("Supabase project is still provisioning. This might take a few minutes.");
7248
+ }
7249
+ });
7250
+ serviceRoleApiKey = await getServiceRoleApiKey();
7251
+ return "Supabase project is ready.";
7252
+ }
7253
+ }]);
7254
+ else {
7255
+ const spinner = _hot_updater_cli_tools.p.spinner();
7256
+ spinner.start(`Getting API keys for ${project.name}...`);
7257
+ try {
7258
+ serviceRoleApiKey = await getServiceRoleApiKey();
7259
+ } catch (error) {
7260
+ const message = error instanceof Error ? error.message : String(error);
7261
+ throw new Error(`Failed to get Supabase API keys: ${message}`);
7262
+ } finally {
7263
+ spinner.stop();
7264
+ }
6724
7265
  }
6725
- spinner.stop();
6726
- const serviceRoleApiKey = apiKeys.find((key) => key.name === "service_role");
6727
7266
  if (!serviceRoleApiKey) throw new Error("Service role key not found, is your project paused?");
6728
- const bucket = await selectBucket(supabaseApi(`https://${project.id}.supabase.co`, serviceRoleApiKey.api_key));
7267
+ return {
7268
+ api: supabaseApi(`https://${project.id}.supabase.co`, serviceRoleApiKey),
7269
+ serviceRoleApiKey
7270
+ };
7271
+ };
7272
+ const runInit = async ({ build, envFile }) => {
7273
+ const nonInteractive = envFile !== void 0;
7274
+ const initEnvSources = await (0, _hot_updater_cli_tools.readHotUpdaterInitEnv)(process.cwd(), envFile);
7275
+ const { inputEnv, managedEnv } = initEnvSources;
7276
+ const savedInputs = resolveSupabaseInitInputs((0, _hot_updater_cli_tools.getHotUpdaterInitInputEnv)(initEnvSources, nonInteractive), {
7277
+ inputEnv,
7278
+ managedEnv
7279
+ });
7280
+ await assertSupabaseNonInteractiveInputs(savedInputs, nonInteractive);
7281
+ const initInputs = await inputSupabaseDeploymentInputs({
7282
+ ...savedInputs,
7283
+ nonInteractive
7284
+ });
7285
+ const { accessToken, functionName } = initInputs;
7286
+ const projectSelection = await selectProject(savedInputs.projectId, nonInteractive, accessToken);
7287
+ let project = projectSelection.create ? void 0 : projectSelection.project;
7288
+ const dbPassword = await inputSupabaseDatabasePassword({
7289
+ cliHandlesPrompt: projectSelection.create && initInputs.accessToken === void 0,
7290
+ databasePassword: savedInputs.databasePassword,
7291
+ nonInteractive,
7292
+ required: projectSelection.create
7293
+ });
7294
+ const managementApi = supabaseManagementApi(accessToken);
7295
+ const projectCreationInputs = projectSelection.create ? await inputSupabaseProjectCreationInputs({
7296
+ bucketName: savedInputs.bucketName,
7297
+ organizationSlug: savedInputs.organizationSlug,
7298
+ organizations: await managementApi.listOrganizations(),
7299
+ projectName: savedInputs.projectName,
7300
+ region: savedInputs.region
7301
+ }) : void 0;
7302
+ let projectAccess = project === void 0 ? void 0 : await getSupabaseProjectAccess({
7303
+ accessToken,
7304
+ managementApi,
7305
+ project,
7306
+ waitForProject: false
7307
+ });
7308
+ let bucketSelection;
7309
+ if (project && projectAccess) bucketSelection = await selectBucket(projectAccess.api, savedInputs.bucketName, nonInteractive);
7310
+ else if (projectCreationInputs) bucketSelection = {
7311
+ create: true,
7312
+ name: projectCreationInputs.bucketName
7313
+ };
7314
+ else throw new Error("Failed to plan the Supabase storage bucket.");
7315
+ if (projectAccess) await ensureSupabaseBucketPrivate({
7316
+ api: projectAccess.api,
7317
+ nonInteractive,
7318
+ selection: bucketSelection
7319
+ });
7320
+ const inputsBeforeProvisioning = {
7321
+ ...savedInputs,
7322
+ ...projectCreationInputs,
7323
+ accessToken,
7324
+ bucketName: bucketSelection.name,
7325
+ databasePassword: dbPassword,
7326
+ functionName,
7327
+ projectId: project?.id
7328
+ };
7329
+ const databasePasswordKey = initProvider.inputs.databasePassword.envKey;
7330
+ const persistCredentialInputs = await (0, _hot_updater_cli_tools.confirmInitInputPersistence)({
7331
+ existingEnv: dbPassword !== "" && (projectSelection.create || managedEnv["HOT_UPDATER_SUPABASE_DB_PASSWORD_PROJECT_ID"] !== project?.id) ? {
7332
+ ...managedEnv,
7333
+ [databasePasswordKey]: ""
7334
+ } : managedEnv,
7335
+ inputs: inputsBeforeProvisioning,
7336
+ nonInteractive,
7337
+ provider: initProvider
7338
+ });
7339
+ if (!await confirmSupabaseDatabaseMigrations({ nonInteractive })) {
7340
+ _hot_updater_cli_tools.p.log.info("Init cancelled.");
7341
+ process.exit(1);
7342
+ }
7343
+ if (projectSelection.create) {
7344
+ if (!projectCreationInputs) throw new Error("Supabase project creation inputs were not resolved.");
7345
+ project = await managementApi.createProject({
7346
+ databasePassword: dbPassword,
7347
+ name: projectCreationInputs.projectName,
7348
+ organizationSlug: projectCreationInputs.organizationSlug,
7349
+ region: projectCreationInputs.region
7350
+ });
7351
+ projectAccess = await getSupabaseProjectAccess({
7352
+ accessToken,
7353
+ managementApi,
7354
+ project,
7355
+ waitForProject: true
7356
+ });
7357
+ }
7358
+ if (!project || !projectAccess) throw new Error("Failed to resolve the Supabase project.");
7359
+ const providerEnv = (0, _hot_updater_cli_tools.getInitProviderEnvVars)({
7360
+ includeConsentInputs: persistCredentialInputs,
7361
+ inputs: {
7362
+ ...inputsBeforeProvisioning,
7363
+ projectId: project.id
7364
+ },
7365
+ provider: initProvider
7366
+ });
7367
+ const persistDatabasePassword = persistCredentialInputs && dbPassword !== "";
7368
+ if (persistDatabasePassword) providerEnv[SUPABASE_DATABASE_PASSWORD_PROJECT_ID_ENV_KEY] = project.id;
7369
+ await (0, _hot_updater_cli_tools.makeEnv)(providerEnv, ".env.hotupdater", { removeKeys: persistDatabasePassword ? [] : [databasePasswordKey, SUPABASE_DATABASE_PASSWORD_PROJECT_ID_ENV_KEY] });
7370
+ const bucket = await createSelectedBucket(projectAccess.api, bucketSelection);
7371
+ await (0, _hot_updater_cli_tools.makeEnv)({
7372
+ [initProvider.inputs.projectId.envKey]: project.id,
7373
+ HOT_UPDATER_SUPABASE_SERVICE_ROLE_KEY: projectAccess.serviceRoleApiKey,
7374
+ [initProvider.inputs.bucketName.envKey]: bucket.name,
7375
+ HOT_UPDATER_SUPABASE_URL: `https://${project.id}.supabase.co`
7376
+ });
6729
7377
  const { tmpDir, removeTmpDir } = await (0, _hot_updater_cli_tools.copyDirToTmp)(path.default.dirname(path.default.resolve(require$1.resolve("@hot-updater/supabase/scaffold"))), "supabase");
6730
7378
  const migrationPath = await path.default.join(tmpDir, "supabase", "migrations");
6731
7379
  const migrationFiles = await fs_promises.default.readdir(migrationPath);
@@ -6734,33 +7382,33 @@ const runInit = async ({ build }) => {
6734
7382
  const content = await fs_promises.default.readFile(filePath, "utf-8");
6735
7383
  await fs_promises.default.writeFile(filePath, (0, _hot_updater_cli_tools.transformTemplate)(content, { BUCKET_NAME: bucket.name }));
6736
7384
  }
6737
- const dbPassword = await _hot_updater_cli_tools.p.password({ message: "Enter your Supabase database password (press Enter to skip if none)" });
6738
- if (_hot_updater_cli_tools.p.isCancel(dbPassword)) process.exit(0);
6739
7385
  await linkSupabase(tmpDir, {
7386
+ accessToken,
6740
7387
  projectId: project.id,
6741
7388
  dbPassword
6742
7389
  });
6743
- await pushDB(tmpDir, { dbPassword });
6744
- await deployEdgeFunction(tmpDir, project.id);
7390
+ await pushDB(tmpDir, {
7391
+ accessToken,
7392
+ dbPassword
7393
+ });
7394
+ await deployEdgeFunction(accessToken, tmpDir, project.id, functionName);
6745
7395
  await removeTmpDir();
6746
7396
  const configWriteResult = await (0, _hot_updater_cli_tools.writeHotUpdaterConfig)(getConfigScaffold(build));
6747
7397
  await assertSkippedConfigDoesNotUseLegacySupabaseKey(configWriteResult);
6748
- await (0, _hot_updater_cli_tools.makeEnv)({
6749
- HOT_UPDATER_SUPABASE_SERVICE_ROLE_KEY: serviceRoleApiKey.api_key,
6750
- HOT_UPDATER_SUPABASE_BUCKET_NAME: bucket.name,
6751
- HOT_UPDATER_SUPABASE_URL: `https://${project.id}.supabase.co`
6752
- });
6753
7398
  _hot_updater_cli_tools.p.log.success("Generated '.env.hotupdater' file with Supabase settings.");
6754
7399
  if (configWriteResult.status === "created") _hot_updater_cli_tools.p.log.success("Generated 'hot-updater.config.ts' file with Supabase settings.");
6755
7400
  else if (configWriteResult.status === "merged") _hot_updater_cli_tools.p.log.success("Updated 'hot-updater.config.ts' file with Supabase settings.");
6756
7401
  else _hot_updater_cli_tools.p.log.warn(`Kept existing 'hot-updater.config.ts' unchanged: ${configWriteResult.reason}`);
6757
- _hot_updater_cli_tools.p.note((0, _hot_updater_cli_tools.transformTemplate)(SOURCE_TEMPLATE, { source: `https://${project.id}.supabase.co/functions/v1/update-server` }));
7402
+ _hot_updater_cli_tools.p.note((0, _hot_updater_cli_tools.transformTemplate)(SOURCE_TEMPLATE, { source: `https://${project.id}.supabase.co/functions/v1/${functionName}` }));
6758
7403
  _hot_updater_cli_tools.p.log.message(`Next step: ${(0, _hot_updater_cli_tools.link)("https://hot-updater.dev/docs/managed/supabase#step-4-add-hotupdater-to-your-project")}`);
6759
7404
  _hot_updater_cli_tools.p.log.success("Done! 🎉");
6760
7405
  };
6761
7406
  //#endregion
7407
+ exports.createSelectedBucket = createSelectedBucket;
6762
7408
  exports.getLegacySupabaseConfigReference = getLegacySupabaseConfigReference;
7409
+ exports.getSupabaseProjectAccess = getSupabaseProjectAccess;
6763
7410
  exports.resolveEdgeFunctionDenoConfig = resolveEdgeFunctionDenoConfig;
6764
7411
  exports.runInit = runInit;
6765
7412
  exports.selectBucket = selectBucket;
6766
7413
  exports.selectProject = selectProject;
7414
+ exports.waitForSupabaseProjectReady = waitForSupabaseProjectReady;