@hot-updater/supabase 0.36.7 → 1.0.0-rc.0

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 (37) hide show
  1. package/dist/edge.cjs +6 -3
  2. package/dist/edge.d.cts +14 -2
  3. package/dist/edge.d.mts +14 -2
  4. package/dist/edge.mjs +5 -2
  5. package/dist/iac/index.cjs +607 -50
  6. package/dist/iac/index.d.cts +26 -8
  7. package/dist/iac/index.d.mts +26 -8
  8. package/dist/iac/index.mjs +604 -52
  9. package/dist/index.cjs +3 -145
  10. package/dist/index.d.cts +7 -24
  11. package/dist/index.d.mts +7 -24
  12. package/dist/index.mjs +2 -117
  13. package/dist/init/index.cjs +5 -2
  14. package/dist/init/index.d.cts +2 -2
  15. package/dist/init/index.d.mts +2 -2
  16. package/dist/init/index.mjs +5 -2
  17. package/dist/supabaseDatabase-BDIBsXpz.d.cts +13 -0
  18. package/dist/supabaseDatabase-CP9WalBi.d.mts +13 -0
  19. package/dist/supabaseStorage-Cw294tR4.mjs +557 -0
  20. package/dist/supabaseStorage-yP7u4m7E.cjs +568 -0
  21. package/package.json +9 -9
  22. package/supabase/edge-functions/index.ts +9 -13
  23. package/supabase/edge-functions/runtime.docker.integration.spec.ts +417 -202
  24. package/supabase/migrations/20260818000000_hot-updater_1.0.0.sql +583 -0
  25. package/supabase/migrations.spec.ts +127 -46
  26. package/dist/supabaseEdgeFunctionStorage-BRxGvt-r.d.mts +0 -19
  27. package/dist/supabaseEdgeFunctionStorage-BSLlUeqi.cjs +0 -386
  28. package/dist/supabaseEdgeFunctionStorage-CU396KO3.d.cts +0 -19
  29. package/dist/supabaseEdgeFunctionStorage-pV0s_mTF.mjs +0 -356
  30. package/supabase/migrations/20250103114225_init.sql +0 -228
  31. package/supabase/migrations/20250314000000_hot-updater_0.13.0.sql +0 -134
  32. package/supabase/migrations/20250516000000_hot-updater_0.18.0.sql +0 -194
  33. package/supabase/migrations/20251014000000_hot-updater_0.21.0.sql +0 -184
  34. package/supabase/migrations/20260401000000_hot-updater_0.29.0.sql +0 -503
  35. package/supabase/migrations/20260414000000_hot-updater_0.30.0.sql +0 -50
  36. package/supabase/migrations/20260422000000_hot-updater_0.31.0.sql +0 -20
  37. package/supabase/migrations/20260520014100_hot-updater_rls.sql +0 -48
@@ -1,7 +1,8 @@
1
1
  import { createRequire } from "node:module";
2
2
  import fs from "fs/promises";
3
3
  import path from "path";
4
- import { ConfigBuilder, InitError, MissingInitInputsError, assertInitInputs, assertInitProviderInputs, confirmInitInputPersistence, copyDirToTmp, createHotUpdaterConfigScaffoldFromBuilder, getHotUpdaterEnvValue, getHotUpdaterInitInputEnv, getInitProviderEnvVars, getInitProviderTextPromptValues, link, makeEnv, p, readHotUpdaterInitEnv, resolveInitProviderInput, resolvePackageVersion, transformEnv, transformTemplate, writeHotUpdaterConfig } from "@hot-updater/cli-tools";
4
+ import { ConfigBuilder, InitError, MissingInitInputsError, assertInfrastructureGenerationAtUrl, assertInitInputs, assertInitProviderInputs, confirmInitInputPersistence, copyDirToTmp, createHotUpdaterConfigScaffoldFromBuilder, getHotUpdaterEnvValue, getHotUpdaterInitInputEnv, getInitProviderEnvVars, getInitProviderTextPromptValues, link, makeEnv, p, readHotUpdaterInitEnv, resolveInitProviderInput, resolvePackageVersion, transformEnv, transformTemplate, writeHotUpdaterConfig } from "@hot-updater/cli-tools";
5
+ import { provisionApiKey } from "@hot-updater/server";
5
6
  import { fileURLToPath } from "node:url";
6
7
  import { ChildProcess, execFile, spawn, spawnSync } from "node:child_process";
7
8
  import { StringDecoder } from "node:string_decoder";
@@ -17,6 +18,8 @@ import { appendFileSync, createReadStream, createWriteStream, readFileSync, stat
17
18
  import { finished } from "node:stream/promises";
18
19
  import { Duplex, PassThrough, Readable, Transform, Writable, getDefaultHighWaterMark } from "node:stream";
19
20
  import { Buffer as Buffer$1 } from "node:buffer";
21
+ import { DatabasePluginInputError, createDatabasePlugin } from "@hot-updater/plugin-core";
22
+ import { DatabaseRowReferencedError, createDatabasePluginAdapter } from "@hot-updater/plugin-core/internal";
20
23
  import { createClient } from "@supabase/supabase-js";
21
24
  //#region \0rolldown/runtime.js
22
25
  var __create = Object.create;
@@ -6271,6 +6274,437 @@ createExeca(mapNode);
6271
6274
  createExeca(mapScriptAsync, {}, deepScriptOptions, setScriptSync);
6272
6275
  const { sendMessage, getOneMessage, getEachMessage, getCancelSignal } = getIpcExport();
6273
6276
  //#endregion
6277
+ //#region src/supabaseConfig.ts
6278
+ const resolveSupabaseServiceRoleKey = (config) => {
6279
+ if (!config.supabaseServiceRoleKey) throw new Error("Supabase service role key is required. Set supabaseServiceRoleKey.");
6280
+ return config.supabaseServiceRoleKey;
6281
+ };
6282
+ //#endregion
6283
+ //#region src/supabaseFilter.ts
6284
+ var InvalidSupabasePredicateError = class extends Error {
6285
+ name = "InvalidSupabasePredicateError";
6286
+ };
6287
+ const encodeValue = (value) => {
6288
+ if (typeof value !== "string") return String(value);
6289
+ return `"${value.replaceAll("\\", "\\\\").replaceAll("\"", "\\\"")}"`;
6290
+ };
6291
+ const encodePattern = (value) => encodeValue(value.replaceAll("*", "\\*").replaceAll("%", "\\%").replaceAll("_", "\\_"));
6292
+ const predicate = (condition) => {
6293
+ const operator = condition.operator ?? "eq";
6294
+ const field = condition.field;
6295
+ switch (operator) {
6296
+ case "eq":
6297
+ case "ne":
6298
+ if (condition.value === null) return `${field}.${operator === "eq" ? "is" : "not.is"}.null`;
6299
+ if (typeof condition.value !== "boolean" && typeof condition.value !== "number" && typeof condition.value !== "string") throw new InvalidSupabasePredicateError();
6300
+ if ("mode" in condition && condition.mode === "insensitive") {
6301
+ const comparison = encodePattern(condition.value);
6302
+ return `${field}.${operator === "eq" ? "ilike" : "not.ilike"}.${comparison}`;
6303
+ }
6304
+ return `${field}.${operator === "eq" ? "eq" : "neq"}.${encodeValue(condition.value)}`;
6305
+ case "gt":
6306
+ case "gte":
6307
+ case "lt":
6308
+ case "lte":
6309
+ if (typeof condition.value !== "number" && typeof condition.value !== "string") throw new InvalidSupabasePredicateError();
6310
+ return `${field}.${operator}.${encodeValue(condition.value)}`;
6311
+ case "in":
6312
+ case "not_in": {
6313
+ if (!Array.isArray(condition.value)) throw new InvalidSupabasePredicateError();
6314
+ if (condition.value.length === 0) return operator === "in" ? `and(${field}.is.null,${field}.not.is.null)` : `or(${field}.is.null,${field}.not.is.null)`;
6315
+ const values = condition.value.map(encodeValue).join(",");
6316
+ return `${field}.${operator === "in" ? "in" : "not.in"}.(${values})`;
6317
+ }
6318
+ case "contains":
6319
+ case "starts_with":
6320
+ case "ends_with": {
6321
+ if (typeof condition.value !== "string") throw new InvalidSupabasePredicateError();
6322
+ const value = encodePattern(condition.value).slice(1, -1);
6323
+ const pattern = operator === "contains" ? `"*${value}*"` : operator === "starts_with" ? `"${value}*"` : `"*${value}"`;
6324
+ return `${field}.${"mode" in condition && condition.mode === "insensitive" ? "ilike" : "like"}.${pattern}`;
6325
+ }
6326
+ }
6327
+ };
6328
+ const buildSupabaseFilter = (where) => {
6329
+ const [first, ...rest] = where ?? [];
6330
+ if (first === void 0) return;
6331
+ let expression = predicate(first);
6332
+ for (const condition of rest) {
6333
+ const next = predicate(condition);
6334
+ expression = condition.connector === "OR" ? `or(${expression},${next})` : `and(${expression},${next})`;
6335
+ }
6336
+ return expression;
6337
+ };
6338
+ //#endregion
6339
+ //#region src/supabaseInfrastructureNames.ts
6340
+ const SUPABASE_V1_FUNCTION_NAME = "hot-updater-v1";
6341
+ const SUPABASE_V1_TABLE_NAMES = {
6342
+ apiKeys: "hot_updater_v1_api_keys",
6343
+ bundleEvents: "hot_updater_v1_bundle_events",
6344
+ bundlePatches: "hot_updater_v1_bundle_patches",
6345
+ bundles: "hot_updater_v1_bundles",
6346
+ channels: "hot_updater_v1_channels",
6347
+ releaseCatalogs: "hot_updater_v1_release_catalogs",
6348
+ releases: "hot_updater_v1_releases",
6349
+ settings: "hot_updater_v1_private_settings"
6350
+ };
6351
+ const SUPABASE_V1_FUNCTION_NAMES = {
6352
+ commit: "hot_updater_v1_commit",
6353
+ deleteChannel: "hot_updater_v1_delete_channel"
6354
+ };
6355
+ //#endregion
6356
+ //#region src/supabaseResult.ts
6357
+ var SupabaseDatabaseError = class extends Error {
6358
+ name = "SupabaseDatabaseError";
6359
+ constructor(operation, cause) {
6360
+ super(`Supabase database operation failed: ${operation}`, { cause });
6361
+ this.operation = operation;
6362
+ }
6363
+ };
6364
+ var SupabaseMissingDataError = class extends Error {
6365
+ name = "SupabaseMissingDataError";
6366
+ constructor(operation) {
6367
+ super(`Supabase database operation returned no row: ${operation}`);
6368
+ this.operation = operation;
6369
+ }
6370
+ };
6371
+ const throwSupabaseError = (operation, error) => {
6372
+ if (error !== null) throw new SupabaseDatabaseError(operation, error);
6373
+ };
6374
+ //#endregion
6375
+ //#region src/supabaseDatabase.ts
6376
+ const isForeignKeyViolation = (error) => typeof error === "object" && error !== null && Reflect.get(error, "code") === "23503";
6377
+ const createSupabaseImplementation = (supabase) => {
6378
+ const implementation = {
6379
+ async create(input) {
6380
+ switch (input.model) {
6381
+ case "bundles": {
6382
+ const { data, error } = await supabase.from(SUPABASE_V1_TABLE_NAMES.bundles).insert(input.data).select("*").single();
6383
+ throwSupabaseError("create bundles", error);
6384
+ if (data === null) throw new SupabaseMissingDataError("create bundles");
6385
+ return data;
6386
+ }
6387
+ case "bundle_patches": {
6388
+ const { data, error } = await supabase.from(SUPABASE_V1_TABLE_NAMES.bundlePatches).insert(input.data).select("*").single();
6389
+ throwSupabaseError("create bundle_patches", error);
6390
+ if (data === null) throw new SupabaseMissingDataError("create bundle_patches");
6391
+ return data;
6392
+ }
6393
+ case "channels": {
6394
+ if (input.onConflict === "ignore") {
6395
+ const { data, error } = await supabase.from(SUPABASE_V1_TABLE_NAMES.channels).upsert(input.data, {
6396
+ onConflict: "name",
6397
+ ignoreDuplicates: true
6398
+ }).select("*").maybeSingle();
6399
+ throwSupabaseError("create channels", error);
6400
+ return data ?? input.data;
6401
+ }
6402
+ const { data, error } = await supabase.from(SUPABASE_V1_TABLE_NAMES.channels).insert(input.data).select("*").single();
6403
+ throwSupabaseError("create channels", error);
6404
+ if (data === null) throw new SupabaseMissingDataError("create channels");
6405
+ return data;
6406
+ }
6407
+ case "bundle_events": {
6408
+ const { data, error } = await supabase.from(SUPABASE_V1_TABLE_NAMES.bundleEvents).insert(input.data).select("*").single();
6409
+ throwSupabaseError("create bundle_events", error);
6410
+ if (data === null) throw new SupabaseMissingDataError("create bundle_events");
6411
+ return data;
6412
+ }
6413
+ case "releases": {
6414
+ const { data, error } = await supabase.from(SUPABASE_V1_TABLE_NAMES.releases).insert(input.data).select("*").single();
6415
+ throwSupabaseError("create releases", error);
6416
+ if (data === null) throw new SupabaseMissingDataError("create releases");
6417
+ return data;
6418
+ }
6419
+ case "release_catalogs": {
6420
+ const { data, error } = await supabase.from(SUPABASE_V1_TABLE_NAMES.releaseCatalogs).insert(input.data).select("*").single();
6421
+ throwSupabaseError("create release_catalogs", error);
6422
+ if (data === null) throw new SupabaseMissingDataError("create release_catalogs");
6423
+ return data;
6424
+ }
6425
+ case "api_keys": {
6426
+ const { data, error } = await (input.onConflict === "ignore" ? supabase.from(SUPABASE_V1_TABLE_NAMES.apiKeys).upsert(input.data, {
6427
+ onConflict: "hash",
6428
+ ignoreDuplicates: true
6429
+ }) : supabase.from(SUPABASE_V1_TABLE_NAMES.apiKeys).insert(input.data)).select("*").maybeSingle();
6430
+ throwSupabaseError("create api_keys", error);
6431
+ if (data === null && input.onConflict !== "ignore") throw new SupabaseMissingDataError("create api_keys");
6432
+ return data ?? input.data;
6433
+ }
6434
+ }
6435
+ },
6436
+ async update(input) {
6437
+ const filter = buildSupabaseFilter(input.where);
6438
+ if (input.model === "api_keys") {
6439
+ let query = supabase.from(SUPABASE_V1_TABLE_NAMES.apiKeys).update(input.update);
6440
+ if (filter !== void 0) query = query.or(filter);
6441
+ const { data, error } = await query.select("*").maybeSingle();
6442
+ throwSupabaseError("update api_keys", error);
6443
+ return data;
6444
+ }
6445
+ if (input.model === "releases") {
6446
+ let query = supabase.from(SUPABASE_V1_TABLE_NAMES.releases).update(input.update);
6447
+ if (filter !== void 0) query = query.or(filter);
6448
+ const { data, error } = await query.select("*").maybeSingle();
6449
+ throwSupabaseError("update releases", error);
6450
+ return data;
6451
+ }
6452
+ if (input.model === "release_catalogs") {
6453
+ let query = supabase.from(SUPABASE_V1_TABLE_NAMES.releaseCatalogs).update(input.update);
6454
+ if (filter !== void 0) query = query.or(filter);
6455
+ const { data, error } = await query.select("*").maybeSingle();
6456
+ throwSupabaseError("update release_catalogs", error);
6457
+ return data;
6458
+ }
6459
+ let query = supabase.from(SUPABASE_V1_TABLE_NAMES.bundles).update(input.update);
6460
+ if (filter !== void 0) query = query.or(filter);
6461
+ const { data, error } = await query.select("*").maybeSingle();
6462
+ throwSupabaseError("update bundles", error);
6463
+ return data;
6464
+ },
6465
+ async delete(input) {
6466
+ const filter = buildSupabaseFilter(input.where);
6467
+ switch (input.model) {
6468
+ case "bundles": {
6469
+ let query = supabase.from(SUPABASE_V1_TABLE_NAMES.bundles).delete();
6470
+ if (filter !== void 0) query = query.or(filter);
6471
+ const { error } = await query;
6472
+ throwSupabaseError("delete bundles", error);
6473
+ return;
6474
+ }
6475
+ case "bundle_patches": {
6476
+ let query = supabase.from(SUPABASE_V1_TABLE_NAMES.bundlePatches).delete();
6477
+ if (filter !== void 0) query = query.or(filter);
6478
+ const { error } = await query;
6479
+ throwSupabaseError("delete bundle_patches", error);
6480
+ return;
6481
+ }
6482
+ case "channels": {
6483
+ let query = supabase.from(SUPABASE_V1_TABLE_NAMES.channels).delete();
6484
+ if (filter !== void 0) query = query.or(filter);
6485
+ const { error } = await query;
6486
+ if (isForeignKeyViolation(error)) throw new DatabaseRowReferencedError();
6487
+ throwSupabaseError("delete channels", error);
6488
+ }
6489
+ case "releases": {
6490
+ let query = supabase.from(SUPABASE_V1_TABLE_NAMES.releases).delete();
6491
+ if (filter !== void 0) query = query.or(filter);
6492
+ const { error } = await query;
6493
+ throwSupabaseError("delete releases", error);
6494
+ }
6495
+ }
6496
+ },
6497
+ async count(input) {
6498
+ if (input.distinct !== void 0) throw new DatabasePluginInputError("invalid-operation");
6499
+ const filter = buildSupabaseFilter(input.where);
6500
+ switch (input.model) {
6501
+ case "bundles": {
6502
+ let query = supabase.from(SUPABASE_V1_TABLE_NAMES.bundles).select("*", {
6503
+ count: "exact",
6504
+ head: true
6505
+ });
6506
+ if (filter !== void 0) query = query.or(filter);
6507
+ const { count, error } = await query;
6508
+ throwSupabaseError("count bundles", error);
6509
+ return count ?? 0;
6510
+ }
6511
+ case "bundle_patches": {
6512
+ let query = supabase.from(SUPABASE_V1_TABLE_NAMES.bundlePatches).select("*", {
6513
+ count: "exact",
6514
+ head: true
6515
+ });
6516
+ if (filter !== void 0) query = query.or(filter);
6517
+ const { count, error } = await query;
6518
+ throwSupabaseError("count bundle_patches", error);
6519
+ return count ?? 0;
6520
+ }
6521
+ case "releases": {
6522
+ let query = supabase.from(SUPABASE_V1_TABLE_NAMES.releases).select("*", {
6523
+ count: "exact",
6524
+ head: true
6525
+ });
6526
+ if (filter !== void 0) query = query.or(filter);
6527
+ const { count, error } = await query;
6528
+ throwSupabaseError("count releases", error);
6529
+ return count ?? 0;
6530
+ }
6531
+ }
6532
+ },
6533
+ async findOne(input) {
6534
+ const filter = buildSupabaseFilter(input.where);
6535
+ switch (input.model) {
6536
+ case "bundles": {
6537
+ let query = supabase.from(SUPABASE_V1_TABLE_NAMES.bundles).select("*");
6538
+ if (filter !== void 0) query = query.or(filter);
6539
+ const { data, error } = await query.limit(1).maybeSingle();
6540
+ throwSupabaseError("findOne bundles", error);
6541
+ return data;
6542
+ }
6543
+ case "api_keys": {
6544
+ let query = supabase.from(SUPABASE_V1_TABLE_NAMES.apiKeys).select("*");
6545
+ if (filter !== void 0) query = query.or(filter);
6546
+ const { data, error } = await query.limit(1).maybeSingle();
6547
+ throwSupabaseError("findOne api_keys", error);
6548
+ return data;
6549
+ }
6550
+ case "channels": {
6551
+ let query = supabase.from(SUPABASE_V1_TABLE_NAMES.channels).select("*");
6552
+ if (filter !== void 0) query = query.or(filter);
6553
+ const { data, error } = await query.limit(1).maybeSingle();
6554
+ throwSupabaseError("findOne channels", error);
6555
+ return data;
6556
+ }
6557
+ case "bundle_patches": {
6558
+ let query = supabase.from(SUPABASE_V1_TABLE_NAMES.bundlePatches).select("*");
6559
+ if (filter !== void 0) query = query.or(filter);
6560
+ const { data, error } = await query.limit(1).maybeSingle();
6561
+ throwSupabaseError("findOne bundle_patches", error);
6562
+ return data;
6563
+ }
6564
+ case "releases": {
6565
+ let query = supabase.from(SUPABASE_V1_TABLE_NAMES.releases).select("*");
6566
+ if (filter !== void 0) query = query.or(filter);
6567
+ const { data, error } = await query.limit(1).maybeSingle();
6568
+ throwSupabaseError("findOne releases", error);
6569
+ return data;
6570
+ }
6571
+ case "release_catalogs": {
6572
+ let query = supabase.from(SUPABASE_V1_TABLE_NAMES.releaseCatalogs).select("*");
6573
+ if (filter !== void 0) query = query.or(filter);
6574
+ const { data, error } = await query.limit(1).maybeSingle();
6575
+ throwSupabaseError("findOne release_catalogs", error);
6576
+ return data;
6577
+ }
6578
+ }
6579
+ },
6580
+ async findMany(input) {
6581
+ if (input.distinctOn !== void 0) throw new DatabasePluginInputError("invalid-operation");
6582
+ if (input.limit === 0) return [];
6583
+ const filter = buildSupabaseFilter(input.where);
6584
+ const rangeEnd = input.offset + input.limit - 1;
6585
+ const orderBy = input.orderBy ?? [];
6586
+ switch (input.model) {
6587
+ case "bundles": {
6588
+ let query = supabase.from(SUPABASE_V1_TABLE_NAMES.bundles).select("*");
6589
+ if (filter !== void 0) query = query.or(filter);
6590
+ for (const clause of orderBy) query = query.order(clause.field, {
6591
+ ascending: clause.direction === "asc",
6592
+ ...clause.nulls ? { nullsFirst: clause.nulls === "first" } : {}
6593
+ });
6594
+ const { data, error } = await query.range(input.offset, rangeEnd);
6595
+ throwSupabaseError("findMany bundles", error);
6596
+ return data ?? [];
6597
+ }
6598
+ case "bundle_events": {
6599
+ let query = supabase.from(SUPABASE_V1_TABLE_NAMES.bundleEvents).select("*");
6600
+ if (filter !== void 0) query = query.or(filter);
6601
+ for (const clause of orderBy) query = query.order(clause.field, {
6602
+ ascending: clause.direction === "asc",
6603
+ ...clause.nulls ? { nullsFirst: clause.nulls === "first" } : {}
6604
+ });
6605
+ const { data, error } = await query.range(input.offset, rangeEnd);
6606
+ throwSupabaseError("findMany bundle_events", error);
6607
+ return data ?? [];
6608
+ }
6609
+ case "api_keys": {
6610
+ let query = supabase.from(SUPABASE_V1_TABLE_NAMES.apiKeys).select("*");
6611
+ if (filter !== void 0) query = query.or(filter);
6612
+ for (const clause of orderBy) query = query.order(clause.field, {
6613
+ ascending: clause.direction === "asc",
6614
+ ...clause.nulls ? { nullsFirst: clause.nulls === "first" } : {}
6615
+ });
6616
+ const { data, error } = await query.range(input.offset, rangeEnd);
6617
+ throwSupabaseError("findMany api_keys", error);
6618
+ return data ?? [];
6619
+ }
6620
+ case "bundle_patches": {
6621
+ let query = supabase.from(SUPABASE_V1_TABLE_NAMES.bundlePatches).select("*");
6622
+ if (filter !== void 0) query = query.or(filter);
6623
+ for (const clause of orderBy) query = query.order(clause.field, {
6624
+ ascending: clause.direction === "asc",
6625
+ ...clause.nulls ? { nullsFirst: clause.nulls === "first" } : {}
6626
+ });
6627
+ const { data, error } = await query.range(input.offset, rangeEnd);
6628
+ throwSupabaseError("findMany bundle_patches", error);
6629
+ return data ?? [];
6630
+ }
6631
+ case "channels": {
6632
+ let query = supabase.from(SUPABASE_V1_TABLE_NAMES.channels).select("*");
6633
+ if (filter !== void 0) query = query.or(filter);
6634
+ for (const clause of orderBy) query = query.order(clause.field, {
6635
+ ascending: clause.direction === "asc",
6636
+ ...clause.nulls ? { nullsFirst: clause.nulls === "first" } : {}
6637
+ });
6638
+ const { data, error } = await query.range(input.offset, rangeEnd);
6639
+ throwSupabaseError("findMany channels", error);
6640
+ return data ?? [];
6641
+ }
6642
+ case "releases": {
6643
+ let query = supabase.from(SUPABASE_V1_TABLE_NAMES.releases).select("*");
6644
+ if (filter !== void 0) query = query.or(filter);
6645
+ for (const clause of orderBy) query = query.order(clause.field, {
6646
+ ascending: clause.direction === "asc",
6647
+ ...clause.nulls ? { nullsFirst: clause.nulls === "first" } : {}
6648
+ });
6649
+ const { data, error } = await query.range(input.offset, rangeEnd);
6650
+ throwSupabaseError("findMany releases", error);
6651
+ return data ?? [];
6652
+ }
6653
+ case "release_catalogs": {
6654
+ let query = supabase.from(SUPABASE_V1_TABLE_NAMES.releaseCatalogs).select("*");
6655
+ if (filter !== void 0) query = query.or(filter);
6656
+ for (const clause of orderBy) query = query.order(clause.field, {
6657
+ ascending: clause.direction === "asc",
6658
+ ...clause.nulls ? { nullsFirst: clause.nulls === "first" } : {}
6659
+ });
6660
+ const { data, error } = await query.range(input.offset, rangeEnd);
6661
+ throwSupabaseError("findMany release_catalogs", error);
6662
+ return data ?? [];
6663
+ }
6664
+ }
6665
+ },
6666
+ async insertChannel(input) {
6667
+ const { data: inserted, error: insertError } = await supabase.from(SUPABASE_V1_TABLE_NAMES.channels).upsert(input.row, {
6668
+ onConflict: "name",
6669
+ ignoreDuplicates: true
6670
+ }).select("*").maybeSingle();
6671
+ throwSupabaseError("insert channel", insertError);
6672
+ if (inserted !== null) return {
6673
+ row: inserted,
6674
+ inserted: true
6675
+ };
6676
+ const { data: existing, error: findError } = await supabase.from(SUPABASE_V1_TABLE_NAMES.channels).select("*").eq("name", input.row.name).single();
6677
+ throwSupabaseError("find canonical channel", findError);
6678
+ if (existing === null) throw new SupabaseMissingDataError("find canonical channel");
6679
+ return {
6680
+ row: existing,
6681
+ inserted: false
6682
+ };
6683
+ },
6684
+ async deleteChannel(input) {
6685
+ const { data, error } = await supabase.rpc(SUPABASE_V1_FUNCTION_NAMES.deleteChannel, { p_id: input.id });
6686
+ throwSupabaseError("delete channel", error);
6687
+ if (data === null || typeof data !== "object" || !("deleted" in data) || typeof data.deleted !== "boolean") throw new SupabaseMissingDataError("delete channel");
6688
+ return data;
6689
+ }
6690
+ };
6691
+ implementation.commit = async (input) => {
6692
+ const { data, error } = await supabase.rpc(SUPABASE_V1_FUNCTION_NAMES.commit, { p_commit: input });
6693
+ throwSupabaseError("commit database changes", error);
6694
+ if (data === null || typeof data !== "object" || !("committed" in data) || typeof data.committed !== "boolean") throw new SupabaseMissingDataError("commit database changes");
6695
+ return data;
6696
+ };
6697
+ return implementation;
6698
+ };
6699
+ const supabaseDatabase = (config) => {
6700
+ const adapter = createDatabasePluginAdapter("supabaseDatabase", createSupabaseImplementation(createClient(config.supabaseUrl, resolveSupabaseServiceRoleKey(config))));
6701
+ return createDatabasePlugin({
6702
+ name: "supabaseDatabase",
6703
+ models: adapter.models,
6704
+ commit: adapter.commit
6705
+ });
6706
+ };
6707
+ //#endregion
6274
6708
  //#region iac/init/index.ts
6275
6709
  const SUPABASE_DATABASE_PASSWORD_PROJECT_ID_ENV_KEY = "HOT_UPDATER_SUPABASE_DB_PASSWORD_PROJECT_ID";
6276
6710
  const SUPABASE_REGION_VALUES = [
@@ -6357,9 +6791,9 @@ const initProvider = {
6357
6791
  envKey: "HOT_UPDATER_SUPABASE_FUNCTION_NAME",
6358
6792
  help: "Edge Function name",
6359
6793
  prompt: {
6360
- defaultValue: "update-server",
6794
+ defaultValue: SUPABASE_V1_FUNCTION_NAME,
6361
6795
  message: "Enter a name for the edge function",
6362
- placeholder: "update-server",
6796
+ placeholder: SUPABASE_V1_FUNCTION_NAME,
6363
6797
  type: "text"
6364
6798
  },
6365
6799
  validate: isSupabaseFunctionName
@@ -6381,6 +6815,12 @@ const initProvider = {
6381
6815
  const supabaseApi = (supabaseUrl, supabaseServiceRoleKey) => {
6382
6816
  const supabase = createClient(supabaseUrl, supabaseServiceRoleKey);
6383
6817
  return {
6818
+ getInfrastructureState: async () => {
6819
+ const marker = await supabase.from(SUPABASE_V1_TABLE_NAMES.settings).select("value").eq("key", "schema.core").maybeSingle();
6820
+ if (!marker.error) return marker.data?.value === "1.0.0" ? "v1" : "incompatible";
6821
+ if (marker.error.code === "42P01" || marker.error.code === "PGRST205") return "fresh";
6822
+ throw marker.error;
6823
+ },
6384
6824
  listBuckets: async () => {
6385
6825
  const { data, error } = await supabase.storage.listBuckets();
6386
6826
  if (error) throw error;
@@ -6395,10 +6835,6 @@ const supabaseApi = (supabaseUrl, supabaseServiceRoleKey) => {
6395
6835
  const { data, error } = await supabase.storage.createBucket(bucketName, options);
6396
6836
  if (error) throw error;
6397
6837
  return data;
6398
- },
6399
- updateBucket: async (bucketId, options) => {
6400
- const { error } = await supabase.storage.updateBucket(bucketId, options);
6401
- if (error) throw error;
6402
6838
  }
6403
6839
  };
6404
6840
  };
@@ -6484,30 +6920,9 @@ const inputSupabaseAccessToken = async (accessToken) => {
6484
6920
  };
6485
6921
  //#endregion
6486
6922
  //#region iac/supabaseBucketPrivacy.ts
6487
- var PublicSupabaseBucketError = class extends InitError {
6488
- name = "PublicSupabaseBucketError";
6489
- constructor(bucketName) {
6490
- super([
6491
- `Supabase bucket "${bucketName}" is public.`,
6492
- "Make the bucket private in Supabase Storage, then rerun init.",
6493
- "Alternatively, rerun without --from-env-file to approve the change interactively."
6494
- ].join("\n"));
6495
- this.bucketName = bucketName;
6496
- }
6497
- };
6498
- const ensureSupabaseBucketPrivate = async ({ api, nonInteractive, selection }) => {
6923
+ const preserveSupabaseBucketPrivacy = ({ selection }) => {
6499
6924
  if (selection.create || !selection.isPublic) return;
6500
- if (nonInteractive) throw new PublicSupabaseBucketError(selection.name);
6501
- const confirmed = await p.confirm({
6502
- message: `Bucket "${selection.name}" is public. Make it private?`,
6503
- initialValue: true
6504
- });
6505
- if (p.isCancel(confirmed) || !confirmed) {
6506
- p.log.info("Init cancelled.");
6507
- process.exit(1);
6508
- }
6509
- await api.updateBucket(selection.id, { public: false });
6510
- p.log.success(`Bucket "${selection.name}" is now private.`);
6925
+ p.log.warn(`Bucket "${selection.name}" is public. Its access level will be preserved for existing apps.`);
6511
6926
  };
6512
6927
  //#endregion
6513
6928
  //#region iac/supabaseCli.ts
@@ -6576,12 +6991,26 @@ const linkSupabase = async (workdir, { accessToken, projectId, dbPassword }) =>
6576
6991
  };
6577
6992
  const pushDB = async (workdir, { accessToken, dbPassword }) => {
6578
6993
  try {
6994
+ await execa("npx", [
6995
+ "supabase",
6996
+ "migration",
6997
+ "fetch",
6998
+ "--linked",
6999
+ "--yes",
7000
+ "--workdir",
7001
+ workdir
7002
+ ], {
7003
+ cwd: workdir,
7004
+ env: getSupabaseCommandEnv(accessToken, dbPassword)
7005
+ });
6579
7006
  const dbPush = await execa("npx", [
6580
7007
  "supabase",
6581
7008
  "db",
6582
7009
  "push",
6583
7010
  "--include-all",
6584
- "--yes"
7011
+ "--yes",
7012
+ "--workdir",
7013
+ workdir
6585
7014
  ], {
6586
7015
  cwd: workdir,
6587
7016
  env: getSupabaseCommandEnv(accessToken, dbPassword),
@@ -6599,6 +7028,20 @@ const pushDB = async (workdir, { accessToken, dbPassword }) => {
6599
7028
  }
6600
7029
  };
6601
7030
  //#endregion
7031
+ //#region iac/supabaseInfrastructureState.ts
7032
+ const assertSupabaseInfrastructureCanInitialize = async (api, projectId) => {
7033
+ if (await api.getInfrastructureState() === "incompatible") throw new InitError(`Supabase v1 infrastructure in project ${projectId} is incomplete or uses an unsupported database version.`);
7034
+ };
7035
+ const assertSupabaseFunctionCanInitialize = async ({ fetchImpl, functionName, functionSlugs, projectId }) => {
7036
+ if (!functionSlugs.includes(functionName)) return;
7037
+ await assertInfrastructureGenerationAtUrl({
7038
+ fetchImpl,
7039
+ provider: "Supabase",
7040
+ resource: `Edge Function ${functionName}`,
7041
+ versionUrl: `https://${projectId}.supabase.co/functions/v1/${encodeURIComponent(functionName)}/version`
7042
+ });
7043
+ };
7044
+ //#endregion
6602
7045
  //#region iac/supabaseDeploymentInputs.ts
6603
7046
  const assertSupabaseNonInteractiveInputs = async (inputs, nonInteractive, validateCliLogin = hasValidSupabaseCliLogin) => {
6604
7047
  assertInitProviderInputs({
@@ -6740,6 +7183,14 @@ const supabaseCliManagementApi = () => ({
6740
7183
  slug: organization.slug
6741
7184
  }] : []);
6742
7185
  },
7186
+ listFunctions: async (projectId) => parseSupabaseFunctionSlugs(await runSupabaseCli([
7187
+ "functions",
7188
+ "list",
7189
+ "--project-ref",
7190
+ projectId,
7191
+ "--output",
7192
+ "json"
7193
+ ])),
6743
7194
  createProject: async ({ name, organizationSlug, region }) => {
6744
7195
  await execa("npx", [
6745
7196
  "-y",
@@ -6787,6 +7238,13 @@ const supabaseCliManagementApi = () => ({
6787
7238
  const SUPABASE_MANAGEMENT_API_URL = "https://api.supabase.com/v1";
6788
7239
  const SUPABASE_MANAGEMENT_API_TIMEOUT_MS = 3e4;
6789
7240
  const isRecord = (value) => typeof value === "object" && value !== null;
7241
+ const parseSupabaseFunctionSlugs = (body) => {
7242
+ if (!Array.isArray(body)) throw new Error("Supabase functions response was invalid.");
7243
+ return body.map((item) => {
7244
+ if (!isRecord(item) || typeof item.slug !== "string") throw new Error("Supabase functions response was invalid.");
7245
+ return item.slug;
7246
+ });
7247
+ };
6790
7248
  var SupabaseManagementApiStatusError = class extends Error {};
6791
7249
  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 });
6792
7250
  const request = async (accessToken, path, init) => {
@@ -6827,6 +7285,7 @@ const supabaseManagementApi = (accessToken) => accessToken === void 0 ? supabase
6827
7285
  slug: organization.slug
6828
7286
  }] : []);
6829
7287
  },
7288
+ listFunctions: async (projectId) => parseSupabaseFunctionSlugs(await request(accessToken, `/projects/${encodeURIComponent(projectId)}/functions`)),
6830
7289
  createProject: async ({ databasePassword, name, organizationSlug, region }) => {
6831
7290
  const body = await request(accessToken, "/projects", {
6832
7291
  body: {
@@ -6859,6 +7318,11 @@ const SUPABASE_PROJECT_READY_STATUS = "ACTIVE_HEALTHY";
6859
7318
  const SUPABASE_PROJECT_PROVISIONING_STATUS = "COMING_UP";
6860
7319
  const SUPABASE_PROJECT_READINESS_MAX_ATTEMPTS = 300;
6861
7320
  const SUPABASE_PROJECT_READINESS_POLL_INTERVAL_MS = 1e3;
7321
+ const SUPABASE_STORAGE_READINESS_MAX_ATTEMPTS = 300;
7322
+ const SUPABASE_STORAGE_READINESS_POLL_INTERVAL_MS = 1e3;
7323
+ const SUPABASE_SCHEMA_READINESS_MAX_ATTEMPTS = 60;
7324
+ const SUPABASE_SCHEMA_READINESS_POLL_INTERVAL_MS = 1e3;
7325
+ const LEGACY_SUPABASE_CATALOG_CDN_URL_ENV_KEY = "HOT_UPDATER_SUPABASE_CATALOG_CDN_URL";
6862
7326
  const STATIC_IMPORT_SPECIFIER_PATTERN = /^\s*(?:import|export)\s+(?:type\s+)?(?:[^"'`]+?\s+from\s+)?["']([^"']+)["'];?/gm;
6863
7327
  const DYNAMIC_IMPORT_SPECIFIER_PATTERN = /\bimport\s*\(\s*["']([^"']+)["']\s*\)/g;
6864
7328
  const getConfigScaffold = (build) => {
@@ -6907,10 +7371,28 @@ function App() {
6907
7371
  return null; // Replace with your app root
6908
7372
  }
6909
7373
 
6910
- export default HotUpdater.wrap({
7374
+ HotUpdater.init({
6911
7375
  baseURL: "%%source%%",
6912
- updateStrategy: "appVersion", // or "fingerprint"
6913
- })(App);`;
7376
+ requestHeaders: {
7377
+ "x-api-key": %%apiKey%%,
7378
+ },
7379
+ });
7380
+
7381
+ // Call HotUpdater.checkForUpdate({ updateStrategy: "appVersion" })
7382
+ // when your app is ready to check.
7383
+ export default App;`;
7384
+ const getSupabaseReactNativeSource = ({ apiKey, functionName, projectId }) => transformTemplate(SOURCE_TEMPLATE, {
7385
+ apiKey: JSON.stringify(apiKey),
7386
+ source: `https://${projectId}.supabase.co/functions/v1/${functionName}`
7387
+ });
7388
+ const reportSupabaseOriginCatalogReady = () => {
7389
+ p.log.success("Release catalog endpoint is ready in origin-only mode.");
7390
+ p.log.info("Catalog checks still invoke the Supabase Edge Function.");
7391
+ };
7392
+ const reportSupabaseApiKey = (apiKey) => {
7393
+ p.note(apiKey, "API Key");
7394
+ p.log.message("Store this API key separately in a secure place.");
7395
+ };
6914
7396
  const resolvePackageExportPath = async (packageName, exportName) => {
6915
7397
  const packageJsonPath = require$1.resolve(`${packageName}/package.json`);
6916
7398
  const exportTarget = JSON.parse(await fs.readFile(packageJsonPath, "utf-8")).exports?.[exportName];
@@ -7012,10 +7494,11 @@ const buildEdgeFunctionImports = async (targetDir) => {
7012
7494
  for (const nestedSpecifier of nestedSpecifiers) {
7013
7495
  if (imports[nestedSpecifier]) continue;
7014
7496
  if (nestedSpecifier.startsWith(WORKSPACE_PACKAGE_PREFIX)) {
7497
+ const [scope, name, ...subpath] = nestedSpecifier.split("/");
7015
7498
  await addWorkspacePackage({
7016
7499
  importSpecifier: nestedSpecifier,
7017
- packageName: nestedSpecifier,
7018
- exportName: "."
7500
+ packageName: `${scope}/${name}`,
7501
+ exportName: subpath.length === 0 ? "." : `./${subpath.join("/")}`
7019
7502
  });
7020
7503
  continue;
7021
7504
  }
@@ -7029,7 +7512,7 @@ const buildEdgeFunctionImports = async (targetDir) => {
7029
7512
  exportName: "."
7030
7513
  });
7031
7514
  const supabasePackage = await addWorkspacePackage({
7032
- importSpecifier: "@hot-updater/supabase",
7515
+ importSpecifier: "@hot-updater/supabase/edge",
7033
7516
  packageName: "@hot-updater/supabase",
7034
7517
  exportName: "./edge"
7035
7518
  });
@@ -7098,6 +7581,17 @@ const selectProject = async (preferredProjectId, nonInteractive = false, accessT
7098
7581
  project: selectedProject
7099
7582
  };
7100
7583
  };
7584
+ const isMissingSupabaseStorageTenantError = (error) => error instanceof Error && /Missing tenant config for tenant/u.test(error.message) && (Reflect.get(error, "status") === 400 || Reflect.get(error, "statusCode") === "400");
7585
+ const retryWhileSupabaseStorageTenantIsProvisioning = async (operation) => {
7586
+ for (let attempt = 1; attempt <= SUPABASE_STORAGE_READINESS_MAX_ATTEMPTS; attempt++) try {
7587
+ return await operation();
7588
+ } catch (error) {
7589
+ if (!isMissingSupabaseStorageTenantError(error) || attempt === SUPABASE_STORAGE_READINESS_MAX_ATTEMPTS) throw error;
7590
+ if (attempt === 1) p.log.info("Waiting for Supabase Storage to become ready...");
7591
+ await delay(SUPABASE_STORAGE_READINESS_POLL_INTERVAL_MS);
7592
+ }
7593
+ throw new Error("Supabase Storage did not become ready.");
7594
+ };
7101
7595
  const selectBucket = async (api, preferredBucketName, nonInteractive = false) => {
7102
7596
  let buckets = [];
7103
7597
  let retryCount = 0;
@@ -7170,18 +7664,21 @@ const selectBucket = async (api, preferredBucketName, nonInteractive = false) =>
7170
7664
  };
7171
7665
  const createSelectedBucket = async (api, selection) => {
7172
7666
  if (!selection.create) return selection;
7173
- await api.createBucket(selection.name, { public: false });
7667
+ await retryWhileSupabaseStorageTenantIsProvisioning(() => api.createBucket(selection.name, { public: false }));
7174
7668
  p.log.success(`Bucket "${selection.name}" created successfully.`);
7175
- const bucket = (await api.listBuckets()).find((item) => item.name === selection.name);
7669
+ const bucket = (await retryWhileSupabaseStorageTenantIsProvisioning(() => api.listBuckets())).find((item) => item.name === selection.name);
7176
7670
  if (!bucket) throw new Error("Failed to create and select new bucket");
7177
7671
  return {
7178
7672
  id: bucket.id,
7179
7673
  name: bucket.name
7180
7674
  };
7181
7675
  };
7182
- const deployEdgeFunction = async (accessToken, workdir, projectId, functionName) => {
7676
+ const deployEdgeFunction = async (accessToken, workdir, projectId, functionName, bucketName) => {
7183
7677
  const edgeFunctionsLibPath = path.join(workdir, "supabase", "edge-functions");
7184
- const edgeFunctionsCode = transformEnv(path.join(edgeFunctionsLibPath, "index.ts"), { FUNCTION_NAME: functionName });
7678
+ const edgeFunctionsCode = transformEnv(path.join(edgeFunctionsLibPath, "index.ts"), {
7679
+ BUCKET_NAME: bucketName,
7680
+ FUNCTION_NAME: functionName
7681
+ });
7185
7682
  if (!isSupabaseFunctionName(functionName)) throw new Error("Invalid Supabase Edge Function name.");
7186
7683
  const functionsDir = path.resolve(workdir, "supabase", "functions");
7187
7684
  const targetDir = path.resolve(functionsDir, functionName);
@@ -7228,6 +7725,15 @@ const waitForSupabaseProjectReady = async ({ getProjectStatus, maxAttempts = SUP
7228
7725
  }
7229
7726
  throw new Error(`Timed out while waiting for the Supabase project. Last status: ${lastStatus ?? "unknown"}.`);
7230
7727
  };
7728
+ const waitForSupabaseSchemaReady = async ({ getInfrastructureState, maxAttempts = SUPABASE_SCHEMA_READINESS_MAX_ATTEMPTS, pollIntervalMs = SUPABASE_SCHEMA_READINESS_POLL_INTERVAL_MS }) => {
7729
+ for (let attempt = 0; attempt < maxAttempts; attempt++) {
7730
+ const state = await getInfrastructureState();
7731
+ if (state === "v1") return;
7732
+ if (state === "incompatible") throw new Error("Supabase database schema is incompatible after migration.");
7733
+ if (attempt < maxAttempts - 1) await delay(pollIntervalMs);
7734
+ }
7735
+ throw new Error("Timed out while waiting for the Supabase database schema cache.");
7736
+ };
7231
7737
  const getSupabaseProjectAccess = async ({ accessToken, managementApi, project, waitForProject }) => {
7232
7738
  const getServiceRoleApiKey = async () => {
7233
7739
  const keysProcess = await execa("npx", [
@@ -7274,11 +7780,30 @@ const getSupabaseProjectAccess = async ({ accessToken, managementApi, project, w
7274
7780
  serviceRoleApiKey
7275
7781
  };
7276
7782
  };
7277
- const runInit = async ({ build, envFile }) => {
7783
+ const withSupabaseCliMetadataCleanup = async (cwd, operation) => {
7784
+ const cliDirectory = path.join(cwd, "supabase");
7785
+ let shouldCleanup = false;
7786
+ try {
7787
+ await fs.access(cliDirectory);
7788
+ } catch (error) {
7789
+ if (!(error instanceof Error) || Reflect.get(error, "code") !== "ENOENT") throw error;
7790
+ shouldCleanup = true;
7791
+ }
7792
+ try {
7793
+ return await operation();
7794
+ } finally {
7795
+ if (shouldCleanup) await fs.rm(cliDirectory, {
7796
+ recursive: true,
7797
+ force: true
7798
+ });
7799
+ }
7800
+ };
7801
+ const runInitWithoutCliMetadata = async ({ build, envFile }) => {
7278
7802
  const nonInteractive = envFile !== void 0;
7279
7803
  const initEnvSources = await readHotUpdaterInitEnv(process.cwd(), envFile);
7280
7804
  const { inputEnv, managedEnv } = initEnvSources;
7281
- const savedInputs = resolveSupabaseInitInputs(getHotUpdaterInitInputEnv(initEnvSources, nonInteractive), {
7805
+ const initInputEnv = getHotUpdaterInitInputEnv(initEnvSources, nonInteractive);
7806
+ const savedInputs = resolveSupabaseInitInputs(initInputEnv, {
7282
7807
  inputEnv,
7283
7808
  managedEnv
7284
7809
  });
@@ -7317,11 +7842,15 @@ const runInit = async ({ build, envFile }) => {
7317
7842
  name: projectCreationInputs.bucketName
7318
7843
  };
7319
7844
  else throw new Error("Failed to plan the Supabase storage bucket.");
7320
- if (projectAccess) await ensureSupabaseBucketPrivate({
7321
- api: projectAccess.api,
7322
- nonInteractive,
7323
- selection: bucketSelection
7324
- });
7845
+ if (projectAccess) {
7846
+ await assertSupabaseInfrastructureCanInitialize(projectAccess.api, project.id);
7847
+ await assertSupabaseFunctionCanInitialize({
7848
+ functionName,
7849
+ functionSlugs: await managementApi.listFunctions(project.id),
7850
+ projectId: project.id
7851
+ });
7852
+ preserveSupabaseBucketPrivacy({ selection: bucketSelection });
7853
+ }
7325
7854
  const inputsBeforeProvisioning = {
7326
7855
  ...savedInputs,
7327
7856
  ...projectCreationInputs,
@@ -7371,7 +7900,7 @@ const runInit = async ({ build, envFile }) => {
7371
7900
  });
7372
7901
  const persistDatabasePassword = persistCredentialInputs && dbPassword !== "";
7373
7902
  if (persistDatabasePassword) providerEnv[SUPABASE_DATABASE_PASSWORD_PROJECT_ID_ENV_KEY] = project.id;
7374
- await makeEnv(providerEnv, ".env.hotupdater", { removeKeys: persistDatabasePassword ? [] : [databasePasswordKey, SUPABASE_DATABASE_PASSWORD_PROJECT_ID_ENV_KEY] });
7903
+ await makeEnv(providerEnv, ".env.hotupdater", { removeKeys: [LEGACY_SUPABASE_CATALOG_CDN_URL_ENV_KEY, ...persistDatabasePassword ? [] : [databasePasswordKey, SUPABASE_DATABASE_PASSWORD_PROJECT_ID_ENV_KEY]] });
7375
7904
  const bucket = await createSelectedBucket(projectAccess.api, bucketSelection);
7376
7905
  await makeEnv({
7377
7906
  [initProvider.inputs.projectId.envKey]: project.id,
@@ -7396,7 +7925,23 @@ const runInit = async ({ build, envFile }) => {
7396
7925
  accessToken,
7397
7926
  dbPassword
7398
7927
  });
7399
- await deployEdgeFunction(accessToken, tmpDir, project.id, functionName);
7928
+ await waitForSupabaseSchemaReady({ getInfrastructureState: projectAccess.api.getInfrastructureState });
7929
+ const databasePlugin = supabaseDatabase({
7930
+ supabaseServiceRoleKey: projectAccess.serviceRoleApiKey,
7931
+ supabaseUrl: `https://${project.id}.supabase.co`
7932
+ });
7933
+ let apiKey;
7934
+ try {
7935
+ apiKey = (await provisionApiKey({
7936
+ apiKeys: databasePlugin.models.apiKeys,
7937
+ existingApiKey: initInputEnv.HOT_UPDATER_API_KEY,
7938
+ name: "Supabase init"
7939
+ })).apiKey;
7940
+ await makeEnv({ HOT_UPDATER_API_KEY: apiKey });
7941
+ } finally {
7942
+ await databasePlugin.dispose?.();
7943
+ }
7944
+ await deployEdgeFunction(accessToken, tmpDir, project.id, functionName, bucket.name);
7400
7945
  await removeTmpDir();
7401
7946
  const configWriteResult = await writeHotUpdaterConfig(getConfigScaffold(build));
7402
7947
  await assertSkippedConfigDoesNotUseLegacySupabaseKey(configWriteResult);
@@ -7404,9 +7949,16 @@ const runInit = async ({ build, envFile }) => {
7404
7949
  if (configWriteResult.status === "created") p.log.success("Generated 'hot-updater.config.ts' file with Supabase settings.");
7405
7950
  else if (configWriteResult.status === "merged") p.log.success("Updated 'hot-updater.config.ts' file with Supabase settings.");
7406
7951
  else p.log.warn(`Kept existing 'hot-updater.config.ts' unchanged: ${configWriteResult.reason}`);
7407
- p.note(transformTemplate(SOURCE_TEMPLATE, { source: `https://${project.id}.supabase.co/functions/v1/${functionName}` }));
7952
+ p.note(getSupabaseReactNativeSource({
7953
+ apiKey,
7954
+ functionName,
7955
+ projectId: project.id
7956
+ }));
7957
+ reportSupabaseApiKey(apiKey);
7958
+ reportSupabaseOriginCatalogReady();
7408
7959
  p.log.message(`Next step: ${link("https://hot-updater.dev/docs/managed/supabase#step-4-add-hotupdater-to-your-project")}`);
7409
7960
  p.log.success("Done! 🎉");
7410
7961
  };
7962
+ const runInit = (options) => withSupabaseCliMetadataCleanup(process.cwd(), () => runInitWithoutCliMetadata(options));
7411
7963
  //#endregion
7412
- export { createSelectedBucket, getLegacySupabaseConfigReference, getSupabaseProjectAccess, resolveEdgeFunctionDenoConfig, runInit, selectBucket, selectProject, waitForSupabaseProjectReady };
7964
+ export { createSelectedBucket, getLegacySupabaseConfigReference, getSupabaseProjectAccess, getSupabaseReactNativeSource, reportSupabaseApiKey, reportSupabaseOriginCatalogReady, resolveEdgeFunctionDenoConfig, runInit, selectBucket, selectProject, waitForSupabaseProjectReady, waitForSupabaseSchemaReady, withSupabaseCliMetadataCleanup };