@hot-updater/supabase 0.36.7 → 1.0.0-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/edge.cjs +6 -3
- package/dist/edge.d.cts +14 -2
- package/dist/edge.d.mts +14 -2
- package/dist/edge.mjs +5 -2
- package/dist/iac/index.cjs +607 -50
- package/dist/iac/index.d.cts +26 -8
- package/dist/iac/index.d.mts +26 -8
- package/dist/iac/index.mjs +604 -52
- package/dist/index.cjs +3 -145
- package/dist/index.d.cts +7 -24
- package/dist/index.d.mts +7 -24
- package/dist/index.mjs +2 -117
- package/dist/init/index.cjs +5 -2
- package/dist/init/index.d.cts +2 -2
- package/dist/init/index.d.mts +2 -2
- package/dist/init/index.mjs +5 -2
- package/dist/supabaseDatabase-BDIBsXpz.d.cts +13 -0
- package/dist/supabaseDatabase-CP9WalBi.d.mts +13 -0
- package/dist/supabaseStorage-Cw294tR4.mjs +557 -0
- package/dist/supabaseStorage-yP7u4m7E.cjs +568 -0
- package/package.json +9 -9
- package/supabase/edge-functions/index.ts +9 -13
- package/supabase/edge-functions/runtime.docker.integration.spec.ts +417 -202
- package/supabase/migrations/20260818000000_hot-updater_1.0.0.sql +583 -0
- package/supabase/migrations.spec.ts +127 -46
- package/dist/supabaseEdgeFunctionStorage-BRxGvt-r.d.mts +0 -19
- package/dist/supabaseEdgeFunctionStorage-BSLlUeqi.cjs +0 -386
- package/dist/supabaseEdgeFunctionStorage-CU396KO3.d.cts +0 -19
- package/dist/supabaseEdgeFunctionStorage-pV0s_mTF.mjs +0 -356
- package/supabase/migrations/20250103114225_init.sql +0 -228
- package/supabase/migrations/20250314000000_hot-updater_0.13.0.sql +0 -134
- package/supabase/migrations/20250516000000_hot-updater_0.18.0.sql +0 -194
- package/supabase/migrations/20251014000000_hot-updater_0.21.0.sql +0 -184
- package/supabase/migrations/20260401000000_hot-updater_0.29.0.sql +0 -503
- package/supabase/migrations/20260414000000_hot-updater_0.30.0.sql +0 -50
- package/supabase/migrations/20260422000000_hot-updater_0.31.0.sql +0 -20
- package/supabase/migrations/20260520014100_hot-updater_rls.sql +0 -48
package/dist/iac/index.cjs
CHANGED
|
@@ -28,6 +28,7 @@ let node_module = require("node:module");
|
|
|
28
28
|
let path = require("path");
|
|
29
29
|
path = __toESM(path);
|
|
30
30
|
let _hot_updater_cli_tools = require("@hot-updater/cli-tools");
|
|
31
|
+
let _hot_updater_server = require("@hot-updater/server");
|
|
31
32
|
let node_url = require("node:url");
|
|
32
33
|
let node_child_process = require("node:child_process");
|
|
33
34
|
let node_string_decoder = require("node:string_decoder");
|
|
@@ -46,6 +47,8 @@ let node_fs = require("node:fs");
|
|
|
46
47
|
let node_stream_promises = require("node:stream/promises");
|
|
47
48
|
let node_stream = require("node:stream");
|
|
48
49
|
let node_buffer = require("node:buffer");
|
|
50
|
+
let _hot_updater_plugin_core = require("@hot-updater/plugin-core");
|
|
51
|
+
let _hot_updater_plugin_core_internal = require("@hot-updater/plugin-core/internal");
|
|
49
52
|
let _supabase_supabase_js = require("@supabase/supabase-js");
|
|
50
53
|
//#region ../../node_modules/.pnpm/es-toolkit@1.47.0/node_modules/es-toolkit/dist/_internal/globalThis.mjs
|
|
51
54
|
const globalThis_ = typeof globalThis === "object" && globalThis || typeof window === "object" && window || typeof self === "object" && self || typeof global === "object" && global || (function() {
|
|
@@ -6276,6 +6279,437 @@ createExeca(mapNode);
|
|
|
6276
6279
|
createExeca(mapScriptAsync, {}, deepScriptOptions, setScriptSync);
|
|
6277
6280
|
const { sendMessage, getOneMessage, getEachMessage, getCancelSignal } = getIpcExport();
|
|
6278
6281
|
//#endregion
|
|
6282
|
+
//#region src/supabaseConfig.ts
|
|
6283
|
+
const resolveSupabaseServiceRoleKey = (config) => {
|
|
6284
|
+
if (!config.supabaseServiceRoleKey) throw new Error("Supabase service role key is required. Set supabaseServiceRoleKey.");
|
|
6285
|
+
return config.supabaseServiceRoleKey;
|
|
6286
|
+
};
|
|
6287
|
+
//#endregion
|
|
6288
|
+
//#region src/supabaseFilter.ts
|
|
6289
|
+
var InvalidSupabasePredicateError = class extends Error {
|
|
6290
|
+
name = "InvalidSupabasePredicateError";
|
|
6291
|
+
};
|
|
6292
|
+
const encodeValue = (value) => {
|
|
6293
|
+
if (typeof value !== "string") return String(value);
|
|
6294
|
+
return `"${value.replaceAll("\\", "\\\\").replaceAll("\"", "\\\"")}"`;
|
|
6295
|
+
};
|
|
6296
|
+
const encodePattern = (value) => encodeValue(value.replaceAll("*", "\\*").replaceAll("%", "\\%").replaceAll("_", "\\_"));
|
|
6297
|
+
const predicate = (condition) => {
|
|
6298
|
+
const operator = condition.operator ?? "eq";
|
|
6299
|
+
const field = condition.field;
|
|
6300
|
+
switch (operator) {
|
|
6301
|
+
case "eq":
|
|
6302
|
+
case "ne":
|
|
6303
|
+
if (condition.value === null) return `${field}.${operator === "eq" ? "is" : "not.is"}.null`;
|
|
6304
|
+
if (typeof condition.value !== "boolean" && typeof condition.value !== "number" && typeof condition.value !== "string") throw new InvalidSupabasePredicateError();
|
|
6305
|
+
if ("mode" in condition && condition.mode === "insensitive") {
|
|
6306
|
+
const comparison = encodePattern(condition.value);
|
|
6307
|
+
return `${field}.${operator === "eq" ? "ilike" : "not.ilike"}.${comparison}`;
|
|
6308
|
+
}
|
|
6309
|
+
return `${field}.${operator === "eq" ? "eq" : "neq"}.${encodeValue(condition.value)}`;
|
|
6310
|
+
case "gt":
|
|
6311
|
+
case "gte":
|
|
6312
|
+
case "lt":
|
|
6313
|
+
case "lte":
|
|
6314
|
+
if (typeof condition.value !== "number" && typeof condition.value !== "string") throw new InvalidSupabasePredicateError();
|
|
6315
|
+
return `${field}.${operator}.${encodeValue(condition.value)}`;
|
|
6316
|
+
case "in":
|
|
6317
|
+
case "not_in": {
|
|
6318
|
+
if (!Array.isArray(condition.value)) throw new InvalidSupabasePredicateError();
|
|
6319
|
+
if (condition.value.length === 0) return operator === "in" ? `and(${field}.is.null,${field}.not.is.null)` : `or(${field}.is.null,${field}.not.is.null)`;
|
|
6320
|
+
const values = condition.value.map(encodeValue).join(",");
|
|
6321
|
+
return `${field}.${operator === "in" ? "in" : "not.in"}.(${values})`;
|
|
6322
|
+
}
|
|
6323
|
+
case "contains":
|
|
6324
|
+
case "starts_with":
|
|
6325
|
+
case "ends_with": {
|
|
6326
|
+
if (typeof condition.value !== "string") throw new InvalidSupabasePredicateError();
|
|
6327
|
+
const value = encodePattern(condition.value).slice(1, -1);
|
|
6328
|
+
const pattern = operator === "contains" ? `"*${value}*"` : operator === "starts_with" ? `"${value}*"` : `"*${value}"`;
|
|
6329
|
+
return `${field}.${"mode" in condition && condition.mode === "insensitive" ? "ilike" : "like"}.${pattern}`;
|
|
6330
|
+
}
|
|
6331
|
+
}
|
|
6332
|
+
};
|
|
6333
|
+
const buildSupabaseFilter = (where) => {
|
|
6334
|
+
const [first, ...rest] = where ?? [];
|
|
6335
|
+
if (first === void 0) return;
|
|
6336
|
+
let expression = predicate(first);
|
|
6337
|
+
for (const condition of rest) {
|
|
6338
|
+
const next = predicate(condition);
|
|
6339
|
+
expression = condition.connector === "OR" ? `or(${expression},${next})` : `and(${expression},${next})`;
|
|
6340
|
+
}
|
|
6341
|
+
return expression;
|
|
6342
|
+
};
|
|
6343
|
+
//#endregion
|
|
6344
|
+
//#region src/supabaseInfrastructureNames.ts
|
|
6345
|
+
const SUPABASE_V1_FUNCTION_NAME = "hot-updater-v1";
|
|
6346
|
+
const SUPABASE_V1_TABLE_NAMES = {
|
|
6347
|
+
apiKeys: "hot_updater_v1_api_keys",
|
|
6348
|
+
bundleEvents: "hot_updater_v1_bundle_events",
|
|
6349
|
+
bundlePatches: "hot_updater_v1_bundle_patches",
|
|
6350
|
+
bundles: "hot_updater_v1_bundles",
|
|
6351
|
+
channels: "hot_updater_v1_channels",
|
|
6352
|
+
releaseCatalogs: "hot_updater_v1_release_catalogs",
|
|
6353
|
+
releases: "hot_updater_v1_releases",
|
|
6354
|
+
settings: "hot_updater_v1_private_settings"
|
|
6355
|
+
};
|
|
6356
|
+
const SUPABASE_V1_FUNCTION_NAMES = {
|
|
6357
|
+
commit: "hot_updater_v1_commit",
|
|
6358
|
+
deleteChannel: "hot_updater_v1_delete_channel"
|
|
6359
|
+
};
|
|
6360
|
+
//#endregion
|
|
6361
|
+
//#region src/supabaseResult.ts
|
|
6362
|
+
var SupabaseDatabaseError = class extends Error {
|
|
6363
|
+
name = "SupabaseDatabaseError";
|
|
6364
|
+
constructor(operation, cause) {
|
|
6365
|
+
super(`Supabase database operation failed: ${operation}`, { cause });
|
|
6366
|
+
this.operation = operation;
|
|
6367
|
+
}
|
|
6368
|
+
};
|
|
6369
|
+
var SupabaseMissingDataError = class extends Error {
|
|
6370
|
+
name = "SupabaseMissingDataError";
|
|
6371
|
+
constructor(operation) {
|
|
6372
|
+
super(`Supabase database operation returned no row: ${operation}`);
|
|
6373
|
+
this.operation = operation;
|
|
6374
|
+
}
|
|
6375
|
+
};
|
|
6376
|
+
const throwSupabaseError = (operation, error) => {
|
|
6377
|
+
if (error !== null) throw new SupabaseDatabaseError(operation, error);
|
|
6378
|
+
};
|
|
6379
|
+
//#endregion
|
|
6380
|
+
//#region src/supabaseDatabase.ts
|
|
6381
|
+
const isForeignKeyViolation = (error) => typeof error === "object" && error !== null && Reflect.get(error, "code") === "23503";
|
|
6382
|
+
const createSupabaseImplementation = (supabase) => {
|
|
6383
|
+
const implementation = {
|
|
6384
|
+
async create(input) {
|
|
6385
|
+
switch (input.model) {
|
|
6386
|
+
case "bundles": {
|
|
6387
|
+
const { data, error } = await supabase.from(SUPABASE_V1_TABLE_NAMES.bundles).insert(input.data).select("*").single();
|
|
6388
|
+
throwSupabaseError("create bundles", error);
|
|
6389
|
+
if (data === null) throw new SupabaseMissingDataError("create bundles");
|
|
6390
|
+
return data;
|
|
6391
|
+
}
|
|
6392
|
+
case "bundle_patches": {
|
|
6393
|
+
const { data, error } = await supabase.from(SUPABASE_V1_TABLE_NAMES.bundlePatches).insert(input.data).select("*").single();
|
|
6394
|
+
throwSupabaseError("create bundle_patches", error);
|
|
6395
|
+
if (data === null) throw new SupabaseMissingDataError("create bundle_patches");
|
|
6396
|
+
return data;
|
|
6397
|
+
}
|
|
6398
|
+
case "channels": {
|
|
6399
|
+
if (input.onConflict === "ignore") {
|
|
6400
|
+
const { data, error } = await supabase.from(SUPABASE_V1_TABLE_NAMES.channels).upsert(input.data, {
|
|
6401
|
+
onConflict: "name",
|
|
6402
|
+
ignoreDuplicates: true
|
|
6403
|
+
}).select("*").maybeSingle();
|
|
6404
|
+
throwSupabaseError("create channels", error);
|
|
6405
|
+
return data ?? input.data;
|
|
6406
|
+
}
|
|
6407
|
+
const { data, error } = await supabase.from(SUPABASE_V1_TABLE_NAMES.channels).insert(input.data).select("*").single();
|
|
6408
|
+
throwSupabaseError("create channels", error);
|
|
6409
|
+
if (data === null) throw new SupabaseMissingDataError("create channels");
|
|
6410
|
+
return data;
|
|
6411
|
+
}
|
|
6412
|
+
case "bundle_events": {
|
|
6413
|
+
const { data, error } = await supabase.from(SUPABASE_V1_TABLE_NAMES.bundleEvents).insert(input.data).select("*").single();
|
|
6414
|
+
throwSupabaseError("create bundle_events", error);
|
|
6415
|
+
if (data === null) throw new SupabaseMissingDataError("create bundle_events");
|
|
6416
|
+
return data;
|
|
6417
|
+
}
|
|
6418
|
+
case "releases": {
|
|
6419
|
+
const { data, error } = await supabase.from(SUPABASE_V1_TABLE_NAMES.releases).insert(input.data).select("*").single();
|
|
6420
|
+
throwSupabaseError("create releases", error);
|
|
6421
|
+
if (data === null) throw new SupabaseMissingDataError("create releases");
|
|
6422
|
+
return data;
|
|
6423
|
+
}
|
|
6424
|
+
case "release_catalogs": {
|
|
6425
|
+
const { data, error } = await supabase.from(SUPABASE_V1_TABLE_NAMES.releaseCatalogs).insert(input.data).select("*").single();
|
|
6426
|
+
throwSupabaseError("create release_catalogs", error);
|
|
6427
|
+
if (data === null) throw new SupabaseMissingDataError("create release_catalogs");
|
|
6428
|
+
return data;
|
|
6429
|
+
}
|
|
6430
|
+
case "api_keys": {
|
|
6431
|
+
const { data, error } = await (input.onConflict === "ignore" ? supabase.from(SUPABASE_V1_TABLE_NAMES.apiKeys).upsert(input.data, {
|
|
6432
|
+
onConflict: "hash",
|
|
6433
|
+
ignoreDuplicates: true
|
|
6434
|
+
}) : supabase.from(SUPABASE_V1_TABLE_NAMES.apiKeys).insert(input.data)).select("*").maybeSingle();
|
|
6435
|
+
throwSupabaseError("create api_keys", error);
|
|
6436
|
+
if (data === null && input.onConflict !== "ignore") throw new SupabaseMissingDataError("create api_keys");
|
|
6437
|
+
return data ?? input.data;
|
|
6438
|
+
}
|
|
6439
|
+
}
|
|
6440
|
+
},
|
|
6441
|
+
async update(input) {
|
|
6442
|
+
const filter = buildSupabaseFilter(input.where);
|
|
6443
|
+
if (input.model === "api_keys") {
|
|
6444
|
+
let query = supabase.from(SUPABASE_V1_TABLE_NAMES.apiKeys).update(input.update);
|
|
6445
|
+
if (filter !== void 0) query = query.or(filter);
|
|
6446
|
+
const { data, error } = await query.select("*").maybeSingle();
|
|
6447
|
+
throwSupabaseError("update api_keys", error);
|
|
6448
|
+
return data;
|
|
6449
|
+
}
|
|
6450
|
+
if (input.model === "releases") {
|
|
6451
|
+
let query = supabase.from(SUPABASE_V1_TABLE_NAMES.releases).update(input.update);
|
|
6452
|
+
if (filter !== void 0) query = query.or(filter);
|
|
6453
|
+
const { data, error } = await query.select("*").maybeSingle();
|
|
6454
|
+
throwSupabaseError("update releases", error);
|
|
6455
|
+
return data;
|
|
6456
|
+
}
|
|
6457
|
+
if (input.model === "release_catalogs") {
|
|
6458
|
+
let query = supabase.from(SUPABASE_V1_TABLE_NAMES.releaseCatalogs).update(input.update);
|
|
6459
|
+
if (filter !== void 0) query = query.or(filter);
|
|
6460
|
+
const { data, error } = await query.select("*").maybeSingle();
|
|
6461
|
+
throwSupabaseError("update release_catalogs", error);
|
|
6462
|
+
return data;
|
|
6463
|
+
}
|
|
6464
|
+
let query = supabase.from(SUPABASE_V1_TABLE_NAMES.bundles).update(input.update);
|
|
6465
|
+
if (filter !== void 0) query = query.or(filter);
|
|
6466
|
+
const { data, error } = await query.select("*").maybeSingle();
|
|
6467
|
+
throwSupabaseError("update bundles", error);
|
|
6468
|
+
return data;
|
|
6469
|
+
},
|
|
6470
|
+
async delete(input) {
|
|
6471
|
+
const filter = buildSupabaseFilter(input.where);
|
|
6472
|
+
switch (input.model) {
|
|
6473
|
+
case "bundles": {
|
|
6474
|
+
let query = supabase.from(SUPABASE_V1_TABLE_NAMES.bundles).delete();
|
|
6475
|
+
if (filter !== void 0) query = query.or(filter);
|
|
6476
|
+
const { error } = await query;
|
|
6477
|
+
throwSupabaseError("delete bundles", error);
|
|
6478
|
+
return;
|
|
6479
|
+
}
|
|
6480
|
+
case "bundle_patches": {
|
|
6481
|
+
let query = supabase.from(SUPABASE_V1_TABLE_NAMES.bundlePatches).delete();
|
|
6482
|
+
if (filter !== void 0) query = query.or(filter);
|
|
6483
|
+
const { error } = await query;
|
|
6484
|
+
throwSupabaseError("delete bundle_patches", error);
|
|
6485
|
+
return;
|
|
6486
|
+
}
|
|
6487
|
+
case "channels": {
|
|
6488
|
+
let query = supabase.from(SUPABASE_V1_TABLE_NAMES.channels).delete();
|
|
6489
|
+
if (filter !== void 0) query = query.or(filter);
|
|
6490
|
+
const { error } = await query;
|
|
6491
|
+
if (isForeignKeyViolation(error)) throw new _hot_updater_plugin_core_internal.DatabaseRowReferencedError();
|
|
6492
|
+
throwSupabaseError("delete channels", error);
|
|
6493
|
+
}
|
|
6494
|
+
case "releases": {
|
|
6495
|
+
let query = supabase.from(SUPABASE_V1_TABLE_NAMES.releases).delete();
|
|
6496
|
+
if (filter !== void 0) query = query.or(filter);
|
|
6497
|
+
const { error } = await query;
|
|
6498
|
+
throwSupabaseError("delete releases", error);
|
|
6499
|
+
}
|
|
6500
|
+
}
|
|
6501
|
+
},
|
|
6502
|
+
async count(input) {
|
|
6503
|
+
if (input.distinct !== void 0) throw new _hot_updater_plugin_core.DatabasePluginInputError("invalid-operation");
|
|
6504
|
+
const filter = buildSupabaseFilter(input.where);
|
|
6505
|
+
switch (input.model) {
|
|
6506
|
+
case "bundles": {
|
|
6507
|
+
let query = supabase.from(SUPABASE_V1_TABLE_NAMES.bundles).select("*", {
|
|
6508
|
+
count: "exact",
|
|
6509
|
+
head: true
|
|
6510
|
+
});
|
|
6511
|
+
if (filter !== void 0) query = query.or(filter);
|
|
6512
|
+
const { count, error } = await query;
|
|
6513
|
+
throwSupabaseError("count bundles", error);
|
|
6514
|
+
return count ?? 0;
|
|
6515
|
+
}
|
|
6516
|
+
case "bundle_patches": {
|
|
6517
|
+
let query = supabase.from(SUPABASE_V1_TABLE_NAMES.bundlePatches).select("*", {
|
|
6518
|
+
count: "exact",
|
|
6519
|
+
head: true
|
|
6520
|
+
});
|
|
6521
|
+
if (filter !== void 0) query = query.or(filter);
|
|
6522
|
+
const { count, error } = await query;
|
|
6523
|
+
throwSupabaseError("count bundle_patches", error);
|
|
6524
|
+
return count ?? 0;
|
|
6525
|
+
}
|
|
6526
|
+
case "releases": {
|
|
6527
|
+
let query = supabase.from(SUPABASE_V1_TABLE_NAMES.releases).select("*", {
|
|
6528
|
+
count: "exact",
|
|
6529
|
+
head: true
|
|
6530
|
+
});
|
|
6531
|
+
if (filter !== void 0) query = query.or(filter);
|
|
6532
|
+
const { count, error } = await query;
|
|
6533
|
+
throwSupabaseError("count releases", error);
|
|
6534
|
+
return count ?? 0;
|
|
6535
|
+
}
|
|
6536
|
+
}
|
|
6537
|
+
},
|
|
6538
|
+
async findOne(input) {
|
|
6539
|
+
const filter = buildSupabaseFilter(input.where);
|
|
6540
|
+
switch (input.model) {
|
|
6541
|
+
case "bundles": {
|
|
6542
|
+
let query = supabase.from(SUPABASE_V1_TABLE_NAMES.bundles).select("*");
|
|
6543
|
+
if (filter !== void 0) query = query.or(filter);
|
|
6544
|
+
const { data, error } = await query.limit(1).maybeSingle();
|
|
6545
|
+
throwSupabaseError("findOne bundles", error);
|
|
6546
|
+
return data;
|
|
6547
|
+
}
|
|
6548
|
+
case "api_keys": {
|
|
6549
|
+
let query = supabase.from(SUPABASE_V1_TABLE_NAMES.apiKeys).select("*");
|
|
6550
|
+
if (filter !== void 0) query = query.or(filter);
|
|
6551
|
+
const { data, error } = await query.limit(1).maybeSingle();
|
|
6552
|
+
throwSupabaseError("findOne api_keys", error);
|
|
6553
|
+
return data;
|
|
6554
|
+
}
|
|
6555
|
+
case "channels": {
|
|
6556
|
+
let query = supabase.from(SUPABASE_V1_TABLE_NAMES.channels).select("*");
|
|
6557
|
+
if (filter !== void 0) query = query.or(filter);
|
|
6558
|
+
const { data, error } = await query.limit(1).maybeSingle();
|
|
6559
|
+
throwSupabaseError("findOne channels", error);
|
|
6560
|
+
return data;
|
|
6561
|
+
}
|
|
6562
|
+
case "bundle_patches": {
|
|
6563
|
+
let query = supabase.from(SUPABASE_V1_TABLE_NAMES.bundlePatches).select("*");
|
|
6564
|
+
if (filter !== void 0) query = query.or(filter);
|
|
6565
|
+
const { data, error } = await query.limit(1).maybeSingle();
|
|
6566
|
+
throwSupabaseError("findOne bundle_patches", error);
|
|
6567
|
+
return data;
|
|
6568
|
+
}
|
|
6569
|
+
case "releases": {
|
|
6570
|
+
let query = supabase.from(SUPABASE_V1_TABLE_NAMES.releases).select("*");
|
|
6571
|
+
if (filter !== void 0) query = query.or(filter);
|
|
6572
|
+
const { data, error } = await query.limit(1).maybeSingle();
|
|
6573
|
+
throwSupabaseError("findOne releases", error);
|
|
6574
|
+
return data;
|
|
6575
|
+
}
|
|
6576
|
+
case "release_catalogs": {
|
|
6577
|
+
let query = supabase.from(SUPABASE_V1_TABLE_NAMES.releaseCatalogs).select("*");
|
|
6578
|
+
if (filter !== void 0) query = query.or(filter);
|
|
6579
|
+
const { data, error } = await query.limit(1).maybeSingle();
|
|
6580
|
+
throwSupabaseError("findOne release_catalogs", error);
|
|
6581
|
+
return data;
|
|
6582
|
+
}
|
|
6583
|
+
}
|
|
6584
|
+
},
|
|
6585
|
+
async findMany(input) {
|
|
6586
|
+
if (input.distinctOn !== void 0) throw new _hot_updater_plugin_core.DatabasePluginInputError("invalid-operation");
|
|
6587
|
+
if (input.limit === 0) return [];
|
|
6588
|
+
const filter = buildSupabaseFilter(input.where);
|
|
6589
|
+
const rangeEnd = input.offset + input.limit - 1;
|
|
6590
|
+
const orderBy = input.orderBy ?? [];
|
|
6591
|
+
switch (input.model) {
|
|
6592
|
+
case "bundles": {
|
|
6593
|
+
let query = supabase.from(SUPABASE_V1_TABLE_NAMES.bundles).select("*");
|
|
6594
|
+
if (filter !== void 0) query = query.or(filter);
|
|
6595
|
+
for (const clause of orderBy) query = query.order(clause.field, {
|
|
6596
|
+
ascending: clause.direction === "asc",
|
|
6597
|
+
...clause.nulls ? { nullsFirst: clause.nulls === "first" } : {}
|
|
6598
|
+
});
|
|
6599
|
+
const { data, error } = await query.range(input.offset, rangeEnd);
|
|
6600
|
+
throwSupabaseError("findMany bundles", error);
|
|
6601
|
+
return data ?? [];
|
|
6602
|
+
}
|
|
6603
|
+
case "bundle_events": {
|
|
6604
|
+
let query = supabase.from(SUPABASE_V1_TABLE_NAMES.bundleEvents).select("*");
|
|
6605
|
+
if (filter !== void 0) query = query.or(filter);
|
|
6606
|
+
for (const clause of orderBy) query = query.order(clause.field, {
|
|
6607
|
+
ascending: clause.direction === "asc",
|
|
6608
|
+
...clause.nulls ? { nullsFirst: clause.nulls === "first" } : {}
|
|
6609
|
+
});
|
|
6610
|
+
const { data, error } = await query.range(input.offset, rangeEnd);
|
|
6611
|
+
throwSupabaseError("findMany bundle_events", error);
|
|
6612
|
+
return data ?? [];
|
|
6613
|
+
}
|
|
6614
|
+
case "api_keys": {
|
|
6615
|
+
let query = supabase.from(SUPABASE_V1_TABLE_NAMES.apiKeys).select("*");
|
|
6616
|
+
if (filter !== void 0) query = query.or(filter);
|
|
6617
|
+
for (const clause of orderBy) query = query.order(clause.field, {
|
|
6618
|
+
ascending: clause.direction === "asc",
|
|
6619
|
+
...clause.nulls ? { nullsFirst: clause.nulls === "first" } : {}
|
|
6620
|
+
});
|
|
6621
|
+
const { data, error } = await query.range(input.offset, rangeEnd);
|
|
6622
|
+
throwSupabaseError("findMany api_keys", error);
|
|
6623
|
+
return data ?? [];
|
|
6624
|
+
}
|
|
6625
|
+
case "bundle_patches": {
|
|
6626
|
+
let query = supabase.from(SUPABASE_V1_TABLE_NAMES.bundlePatches).select("*");
|
|
6627
|
+
if (filter !== void 0) query = query.or(filter);
|
|
6628
|
+
for (const clause of orderBy) query = query.order(clause.field, {
|
|
6629
|
+
ascending: clause.direction === "asc",
|
|
6630
|
+
...clause.nulls ? { nullsFirst: clause.nulls === "first" } : {}
|
|
6631
|
+
});
|
|
6632
|
+
const { data, error } = await query.range(input.offset, rangeEnd);
|
|
6633
|
+
throwSupabaseError("findMany bundle_patches", error);
|
|
6634
|
+
return data ?? [];
|
|
6635
|
+
}
|
|
6636
|
+
case "channels": {
|
|
6637
|
+
let query = supabase.from(SUPABASE_V1_TABLE_NAMES.channels).select("*");
|
|
6638
|
+
if (filter !== void 0) query = query.or(filter);
|
|
6639
|
+
for (const clause of orderBy) query = query.order(clause.field, {
|
|
6640
|
+
ascending: clause.direction === "asc",
|
|
6641
|
+
...clause.nulls ? { nullsFirst: clause.nulls === "first" } : {}
|
|
6642
|
+
});
|
|
6643
|
+
const { data, error } = await query.range(input.offset, rangeEnd);
|
|
6644
|
+
throwSupabaseError("findMany channels", error);
|
|
6645
|
+
return data ?? [];
|
|
6646
|
+
}
|
|
6647
|
+
case "releases": {
|
|
6648
|
+
let query = supabase.from(SUPABASE_V1_TABLE_NAMES.releases).select("*");
|
|
6649
|
+
if (filter !== void 0) query = query.or(filter);
|
|
6650
|
+
for (const clause of orderBy) query = query.order(clause.field, {
|
|
6651
|
+
ascending: clause.direction === "asc",
|
|
6652
|
+
...clause.nulls ? { nullsFirst: clause.nulls === "first" } : {}
|
|
6653
|
+
});
|
|
6654
|
+
const { data, error } = await query.range(input.offset, rangeEnd);
|
|
6655
|
+
throwSupabaseError("findMany releases", error);
|
|
6656
|
+
return data ?? [];
|
|
6657
|
+
}
|
|
6658
|
+
case "release_catalogs": {
|
|
6659
|
+
let query = supabase.from(SUPABASE_V1_TABLE_NAMES.releaseCatalogs).select("*");
|
|
6660
|
+
if (filter !== void 0) query = query.or(filter);
|
|
6661
|
+
for (const clause of orderBy) query = query.order(clause.field, {
|
|
6662
|
+
ascending: clause.direction === "asc",
|
|
6663
|
+
...clause.nulls ? { nullsFirst: clause.nulls === "first" } : {}
|
|
6664
|
+
});
|
|
6665
|
+
const { data, error } = await query.range(input.offset, rangeEnd);
|
|
6666
|
+
throwSupabaseError("findMany release_catalogs", error);
|
|
6667
|
+
return data ?? [];
|
|
6668
|
+
}
|
|
6669
|
+
}
|
|
6670
|
+
},
|
|
6671
|
+
async insertChannel(input) {
|
|
6672
|
+
const { data: inserted, error: insertError } = await supabase.from(SUPABASE_V1_TABLE_NAMES.channels).upsert(input.row, {
|
|
6673
|
+
onConflict: "name",
|
|
6674
|
+
ignoreDuplicates: true
|
|
6675
|
+
}).select("*").maybeSingle();
|
|
6676
|
+
throwSupabaseError("insert channel", insertError);
|
|
6677
|
+
if (inserted !== null) return {
|
|
6678
|
+
row: inserted,
|
|
6679
|
+
inserted: true
|
|
6680
|
+
};
|
|
6681
|
+
const { data: existing, error: findError } = await supabase.from(SUPABASE_V1_TABLE_NAMES.channels).select("*").eq("name", input.row.name).single();
|
|
6682
|
+
throwSupabaseError("find canonical channel", findError);
|
|
6683
|
+
if (existing === null) throw new SupabaseMissingDataError("find canonical channel");
|
|
6684
|
+
return {
|
|
6685
|
+
row: existing,
|
|
6686
|
+
inserted: false
|
|
6687
|
+
};
|
|
6688
|
+
},
|
|
6689
|
+
async deleteChannel(input) {
|
|
6690
|
+
const { data, error } = await supabase.rpc(SUPABASE_V1_FUNCTION_NAMES.deleteChannel, { p_id: input.id });
|
|
6691
|
+
throwSupabaseError("delete channel", error);
|
|
6692
|
+
if (data === null || typeof data !== "object" || !("deleted" in data) || typeof data.deleted !== "boolean") throw new SupabaseMissingDataError("delete channel");
|
|
6693
|
+
return data;
|
|
6694
|
+
}
|
|
6695
|
+
};
|
|
6696
|
+
implementation.commit = async (input) => {
|
|
6697
|
+
const { data, error } = await supabase.rpc(SUPABASE_V1_FUNCTION_NAMES.commit, { p_commit: input });
|
|
6698
|
+
throwSupabaseError("commit database changes", error);
|
|
6699
|
+
if (data === null || typeof data !== "object" || !("committed" in data) || typeof data.committed !== "boolean") throw new SupabaseMissingDataError("commit database changes");
|
|
6700
|
+
return data;
|
|
6701
|
+
};
|
|
6702
|
+
return implementation;
|
|
6703
|
+
};
|
|
6704
|
+
const supabaseDatabase = (config) => {
|
|
6705
|
+
const adapter = (0, _hot_updater_plugin_core_internal.createDatabasePluginAdapter)("supabaseDatabase", createSupabaseImplementation((0, _supabase_supabase_js.createClient)(config.supabaseUrl, resolveSupabaseServiceRoleKey(config))));
|
|
6706
|
+
return (0, _hot_updater_plugin_core.createDatabasePlugin)({
|
|
6707
|
+
name: "supabaseDatabase",
|
|
6708
|
+
models: adapter.models,
|
|
6709
|
+
commit: adapter.commit
|
|
6710
|
+
});
|
|
6711
|
+
};
|
|
6712
|
+
//#endregion
|
|
6279
6713
|
//#region iac/init/index.ts
|
|
6280
6714
|
const SUPABASE_DATABASE_PASSWORD_PROJECT_ID_ENV_KEY = "HOT_UPDATER_SUPABASE_DB_PASSWORD_PROJECT_ID";
|
|
6281
6715
|
const SUPABASE_REGION_VALUES = [
|
|
@@ -6362,9 +6796,9 @@ const initProvider = {
|
|
|
6362
6796
|
envKey: "HOT_UPDATER_SUPABASE_FUNCTION_NAME",
|
|
6363
6797
|
help: "Edge Function name",
|
|
6364
6798
|
prompt: {
|
|
6365
|
-
defaultValue:
|
|
6799
|
+
defaultValue: SUPABASE_V1_FUNCTION_NAME,
|
|
6366
6800
|
message: "Enter a name for the edge function",
|
|
6367
|
-
placeholder:
|
|
6801
|
+
placeholder: SUPABASE_V1_FUNCTION_NAME,
|
|
6368
6802
|
type: "text"
|
|
6369
6803
|
},
|
|
6370
6804
|
validate: isSupabaseFunctionName
|
|
@@ -6386,6 +6820,12 @@ const initProvider = {
|
|
|
6386
6820
|
const supabaseApi = (supabaseUrl, supabaseServiceRoleKey) => {
|
|
6387
6821
|
const supabase = (0, _supabase_supabase_js.createClient)(supabaseUrl, supabaseServiceRoleKey);
|
|
6388
6822
|
return {
|
|
6823
|
+
getInfrastructureState: async () => {
|
|
6824
|
+
const marker = await supabase.from(SUPABASE_V1_TABLE_NAMES.settings).select("value").eq("key", "schema.core").maybeSingle();
|
|
6825
|
+
if (!marker.error) return marker.data?.value === "1.0.0" ? "v1" : "incompatible";
|
|
6826
|
+
if (marker.error.code === "42P01" || marker.error.code === "PGRST205") return "fresh";
|
|
6827
|
+
throw marker.error;
|
|
6828
|
+
},
|
|
6389
6829
|
listBuckets: async () => {
|
|
6390
6830
|
const { data, error } = await supabase.storage.listBuckets();
|
|
6391
6831
|
if (error) throw error;
|
|
@@ -6400,10 +6840,6 @@ const supabaseApi = (supabaseUrl, supabaseServiceRoleKey) => {
|
|
|
6400
6840
|
const { data, error } = await supabase.storage.createBucket(bucketName, options);
|
|
6401
6841
|
if (error) throw error;
|
|
6402
6842
|
return data;
|
|
6403
|
-
},
|
|
6404
|
-
updateBucket: async (bucketId, options) => {
|
|
6405
|
-
const { error } = await supabase.storage.updateBucket(bucketId, options);
|
|
6406
|
-
if (error) throw error;
|
|
6407
6843
|
}
|
|
6408
6844
|
};
|
|
6409
6845
|
};
|
|
@@ -6489,30 +6925,9 @@ const inputSupabaseAccessToken = async (accessToken) => {
|
|
|
6489
6925
|
};
|
|
6490
6926
|
//#endregion
|
|
6491
6927
|
//#region iac/supabaseBucketPrivacy.ts
|
|
6492
|
-
|
|
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 --from-env-file to approve the change interactively."
|
|
6499
|
-
].join("\n"));
|
|
6500
|
-
this.bucketName = bucketName;
|
|
6501
|
-
}
|
|
6502
|
-
};
|
|
6503
|
-
const ensureSupabaseBucketPrivate = async ({ api, nonInteractive, selection }) => {
|
|
6928
|
+
const preserveSupabaseBucketPrivacy = ({ selection }) => {
|
|
6504
6929
|
if (selection.create || !selection.isPublic) return;
|
|
6505
|
-
|
|
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.`);
|
|
6930
|
+
_hot_updater_cli_tools.p.log.warn(`Bucket "${selection.name}" is public. Its access level will be preserved for existing apps.`);
|
|
6516
6931
|
};
|
|
6517
6932
|
//#endregion
|
|
6518
6933
|
//#region iac/supabaseCli.ts
|
|
@@ -6581,12 +6996,26 @@ const linkSupabase = async (workdir, { accessToken, projectId, dbPassword }) =>
|
|
|
6581
6996
|
};
|
|
6582
6997
|
const pushDB = async (workdir, { accessToken, dbPassword }) => {
|
|
6583
6998
|
try {
|
|
6999
|
+
await execa("npx", [
|
|
7000
|
+
"supabase",
|
|
7001
|
+
"migration",
|
|
7002
|
+
"fetch",
|
|
7003
|
+
"--linked",
|
|
7004
|
+
"--yes",
|
|
7005
|
+
"--workdir",
|
|
7006
|
+
workdir
|
|
7007
|
+
], {
|
|
7008
|
+
cwd: workdir,
|
|
7009
|
+
env: getSupabaseCommandEnv(accessToken, dbPassword)
|
|
7010
|
+
});
|
|
6584
7011
|
const dbPush = await execa("npx", [
|
|
6585
7012
|
"supabase",
|
|
6586
7013
|
"db",
|
|
6587
7014
|
"push",
|
|
6588
7015
|
"--include-all",
|
|
6589
|
-
"--yes"
|
|
7016
|
+
"--yes",
|
|
7017
|
+
"--workdir",
|
|
7018
|
+
workdir
|
|
6590
7019
|
], {
|
|
6591
7020
|
cwd: workdir,
|
|
6592
7021
|
env: getSupabaseCommandEnv(accessToken, dbPassword),
|
|
@@ -6604,6 +7033,20 @@ const pushDB = async (workdir, { accessToken, dbPassword }) => {
|
|
|
6604
7033
|
}
|
|
6605
7034
|
};
|
|
6606
7035
|
//#endregion
|
|
7036
|
+
//#region iac/supabaseInfrastructureState.ts
|
|
7037
|
+
const assertSupabaseInfrastructureCanInitialize = async (api, projectId) => {
|
|
7038
|
+
if (await api.getInfrastructureState() === "incompatible") throw new _hot_updater_cli_tools.InitError(`Supabase v1 infrastructure in project ${projectId} is incomplete or uses an unsupported database version.`);
|
|
7039
|
+
};
|
|
7040
|
+
const assertSupabaseFunctionCanInitialize = async ({ fetchImpl, functionName, functionSlugs, projectId }) => {
|
|
7041
|
+
if (!functionSlugs.includes(functionName)) return;
|
|
7042
|
+
await (0, _hot_updater_cli_tools.assertInfrastructureGenerationAtUrl)({
|
|
7043
|
+
fetchImpl,
|
|
7044
|
+
provider: "Supabase",
|
|
7045
|
+
resource: `Edge Function ${functionName}`,
|
|
7046
|
+
versionUrl: `https://${projectId}.supabase.co/functions/v1/${encodeURIComponent(functionName)}/version`
|
|
7047
|
+
});
|
|
7048
|
+
};
|
|
7049
|
+
//#endregion
|
|
6607
7050
|
//#region iac/supabaseDeploymentInputs.ts
|
|
6608
7051
|
const assertSupabaseNonInteractiveInputs = async (inputs, nonInteractive, validateCliLogin = hasValidSupabaseCliLogin) => {
|
|
6609
7052
|
(0, _hot_updater_cli_tools.assertInitProviderInputs)({
|
|
@@ -6745,6 +7188,14 @@ const supabaseCliManagementApi = () => ({
|
|
|
6745
7188
|
slug: organization.slug
|
|
6746
7189
|
}] : []);
|
|
6747
7190
|
},
|
|
7191
|
+
listFunctions: async (projectId) => parseSupabaseFunctionSlugs(await runSupabaseCli([
|
|
7192
|
+
"functions",
|
|
7193
|
+
"list",
|
|
7194
|
+
"--project-ref",
|
|
7195
|
+
projectId,
|
|
7196
|
+
"--output",
|
|
7197
|
+
"json"
|
|
7198
|
+
])),
|
|
6748
7199
|
createProject: async ({ name, organizationSlug, region }) => {
|
|
6749
7200
|
await execa("npx", [
|
|
6750
7201
|
"-y",
|
|
@@ -6792,6 +7243,13 @@ const supabaseCliManagementApi = () => ({
|
|
|
6792
7243
|
const SUPABASE_MANAGEMENT_API_URL = "https://api.supabase.com/v1";
|
|
6793
7244
|
const SUPABASE_MANAGEMENT_API_TIMEOUT_MS = 3e4;
|
|
6794
7245
|
const isRecord = (value) => typeof value === "object" && value !== null;
|
|
7246
|
+
const parseSupabaseFunctionSlugs = (body) => {
|
|
7247
|
+
if (!Array.isArray(body)) throw new Error("Supabase functions response was invalid.");
|
|
7248
|
+
return body.map((item) => {
|
|
7249
|
+
if (!isRecord(item) || typeof item.slug !== "string") throw new Error("Supabase functions response was invalid.");
|
|
7250
|
+
return item.slug;
|
|
7251
|
+
});
|
|
7252
|
+
};
|
|
6795
7253
|
var SupabaseManagementApiStatusError = class extends Error {};
|
|
6796
7254
|
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
7255
|
const request = async (accessToken, path, init) => {
|
|
@@ -6832,6 +7290,7 @@ const supabaseManagementApi = (accessToken) => accessToken === void 0 ? supabase
|
|
|
6832
7290
|
slug: organization.slug
|
|
6833
7291
|
}] : []);
|
|
6834
7292
|
},
|
|
7293
|
+
listFunctions: async (projectId) => parseSupabaseFunctionSlugs(await request(accessToken, `/projects/${encodeURIComponent(projectId)}/functions`)),
|
|
6835
7294
|
createProject: async ({ databasePassword, name, organizationSlug, region }) => {
|
|
6836
7295
|
const body = await request(accessToken, "/projects", {
|
|
6837
7296
|
body: {
|
|
@@ -6864,6 +7323,11 @@ const SUPABASE_PROJECT_READY_STATUS = "ACTIVE_HEALTHY";
|
|
|
6864
7323
|
const SUPABASE_PROJECT_PROVISIONING_STATUS = "COMING_UP";
|
|
6865
7324
|
const SUPABASE_PROJECT_READINESS_MAX_ATTEMPTS = 300;
|
|
6866
7325
|
const SUPABASE_PROJECT_READINESS_POLL_INTERVAL_MS = 1e3;
|
|
7326
|
+
const SUPABASE_STORAGE_READINESS_MAX_ATTEMPTS = 300;
|
|
7327
|
+
const SUPABASE_STORAGE_READINESS_POLL_INTERVAL_MS = 1e3;
|
|
7328
|
+
const SUPABASE_SCHEMA_READINESS_MAX_ATTEMPTS = 60;
|
|
7329
|
+
const SUPABASE_SCHEMA_READINESS_POLL_INTERVAL_MS = 1e3;
|
|
7330
|
+
const LEGACY_SUPABASE_CATALOG_CDN_URL_ENV_KEY = "HOT_UPDATER_SUPABASE_CATALOG_CDN_URL";
|
|
6867
7331
|
const STATIC_IMPORT_SPECIFIER_PATTERN = /^\s*(?:import|export)\s+(?:type\s+)?(?:[^"'`]+?\s+from\s+)?["']([^"']+)["'];?/gm;
|
|
6868
7332
|
const DYNAMIC_IMPORT_SPECIFIER_PATTERN = /\bimport\s*\(\s*["']([^"']+)["']\s*\)/g;
|
|
6869
7333
|
const getConfigScaffold = (build) => {
|
|
@@ -6912,10 +7376,28 @@ function App() {
|
|
|
6912
7376
|
return null; // Replace with your app root
|
|
6913
7377
|
}
|
|
6914
7378
|
|
|
6915
|
-
|
|
7379
|
+
HotUpdater.init({
|
|
6916
7380
|
baseURL: "%%source%%",
|
|
6917
|
-
|
|
6918
|
-
|
|
7381
|
+
requestHeaders: {
|
|
7382
|
+
"x-api-key": %%apiKey%%,
|
|
7383
|
+
},
|
|
7384
|
+
});
|
|
7385
|
+
|
|
7386
|
+
// Call HotUpdater.checkForUpdate({ updateStrategy: "appVersion" })
|
|
7387
|
+
// when your app is ready to check.
|
|
7388
|
+
export default App;`;
|
|
7389
|
+
const getSupabaseReactNativeSource = ({ apiKey, functionName, projectId }) => (0, _hot_updater_cli_tools.transformTemplate)(SOURCE_TEMPLATE, {
|
|
7390
|
+
apiKey: JSON.stringify(apiKey),
|
|
7391
|
+
source: `https://${projectId}.supabase.co/functions/v1/${functionName}`
|
|
7392
|
+
});
|
|
7393
|
+
const reportSupabaseOriginCatalogReady = () => {
|
|
7394
|
+
_hot_updater_cli_tools.p.log.success("Release catalog endpoint is ready in origin-only mode.");
|
|
7395
|
+
_hot_updater_cli_tools.p.log.info("Catalog checks still invoke the Supabase Edge Function.");
|
|
7396
|
+
};
|
|
7397
|
+
const reportSupabaseApiKey = (apiKey) => {
|
|
7398
|
+
_hot_updater_cli_tools.p.note(apiKey, "API Key");
|
|
7399
|
+
_hot_updater_cli_tools.p.log.message("Store this API key separately in a secure place.");
|
|
7400
|
+
};
|
|
6919
7401
|
const resolvePackageExportPath = async (packageName, exportName) => {
|
|
6920
7402
|
const packageJsonPath = require$1.resolve(`${packageName}/package.json`);
|
|
6921
7403
|
const exportTarget = JSON.parse(await fs_promises.default.readFile(packageJsonPath, "utf-8")).exports?.[exportName];
|
|
@@ -7017,10 +7499,11 @@ const buildEdgeFunctionImports = async (targetDir) => {
|
|
|
7017
7499
|
for (const nestedSpecifier of nestedSpecifiers) {
|
|
7018
7500
|
if (imports[nestedSpecifier]) continue;
|
|
7019
7501
|
if (nestedSpecifier.startsWith(WORKSPACE_PACKAGE_PREFIX)) {
|
|
7502
|
+
const [scope, name, ...subpath] = nestedSpecifier.split("/");
|
|
7020
7503
|
await addWorkspacePackage({
|
|
7021
7504
|
importSpecifier: nestedSpecifier,
|
|
7022
|
-
packageName:
|
|
7023
|
-
exportName: "."
|
|
7505
|
+
packageName: `${scope}/${name}`,
|
|
7506
|
+
exportName: subpath.length === 0 ? "." : `./${subpath.join("/")}`
|
|
7024
7507
|
});
|
|
7025
7508
|
continue;
|
|
7026
7509
|
}
|
|
@@ -7034,7 +7517,7 @@ const buildEdgeFunctionImports = async (targetDir) => {
|
|
|
7034
7517
|
exportName: "."
|
|
7035
7518
|
});
|
|
7036
7519
|
const supabasePackage = await addWorkspacePackage({
|
|
7037
|
-
importSpecifier: "@hot-updater/supabase",
|
|
7520
|
+
importSpecifier: "@hot-updater/supabase/edge",
|
|
7038
7521
|
packageName: "@hot-updater/supabase",
|
|
7039
7522
|
exportName: "./edge"
|
|
7040
7523
|
});
|
|
@@ -7103,6 +7586,17 @@ const selectProject = async (preferredProjectId, nonInteractive = false, accessT
|
|
|
7103
7586
|
project: selectedProject
|
|
7104
7587
|
};
|
|
7105
7588
|
};
|
|
7589
|
+
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");
|
|
7590
|
+
const retryWhileSupabaseStorageTenantIsProvisioning = async (operation) => {
|
|
7591
|
+
for (let attempt = 1; attempt <= SUPABASE_STORAGE_READINESS_MAX_ATTEMPTS; attempt++) try {
|
|
7592
|
+
return await operation();
|
|
7593
|
+
} catch (error) {
|
|
7594
|
+
if (!isMissingSupabaseStorageTenantError(error) || attempt === SUPABASE_STORAGE_READINESS_MAX_ATTEMPTS) throw error;
|
|
7595
|
+
if (attempt === 1) _hot_updater_cli_tools.p.log.info("Waiting for Supabase Storage to become ready...");
|
|
7596
|
+
await delay(SUPABASE_STORAGE_READINESS_POLL_INTERVAL_MS);
|
|
7597
|
+
}
|
|
7598
|
+
throw new Error("Supabase Storage did not become ready.");
|
|
7599
|
+
};
|
|
7106
7600
|
const selectBucket = async (api, preferredBucketName, nonInteractive = false) => {
|
|
7107
7601
|
let buckets = [];
|
|
7108
7602
|
let retryCount = 0;
|
|
@@ -7175,18 +7669,21 @@ const selectBucket = async (api, preferredBucketName, nonInteractive = false) =>
|
|
|
7175
7669
|
};
|
|
7176
7670
|
const createSelectedBucket = async (api, selection) => {
|
|
7177
7671
|
if (!selection.create) return selection;
|
|
7178
|
-
await api.createBucket(selection.name, { public: false });
|
|
7672
|
+
await retryWhileSupabaseStorageTenantIsProvisioning(() => api.createBucket(selection.name, { public: false }));
|
|
7179
7673
|
_hot_updater_cli_tools.p.log.success(`Bucket "${selection.name}" created successfully.`);
|
|
7180
|
-
const bucket = (await api.listBuckets()).find((item) => item.name === selection.name);
|
|
7674
|
+
const bucket = (await retryWhileSupabaseStorageTenantIsProvisioning(() => api.listBuckets())).find((item) => item.name === selection.name);
|
|
7181
7675
|
if (!bucket) throw new Error("Failed to create and select new bucket");
|
|
7182
7676
|
return {
|
|
7183
7677
|
id: bucket.id,
|
|
7184
7678
|
name: bucket.name
|
|
7185
7679
|
};
|
|
7186
7680
|
};
|
|
7187
|
-
const deployEdgeFunction = async (accessToken, workdir, projectId, functionName) => {
|
|
7681
|
+
const deployEdgeFunction = async (accessToken, workdir, projectId, functionName, bucketName) => {
|
|
7188
7682
|
const edgeFunctionsLibPath = path.default.join(workdir, "supabase", "edge-functions");
|
|
7189
|
-
const edgeFunctionsCode = (0, _hot_updater_cli_tools.transformEnv)(path.default.join(edgeFunctionsLibPath, "index.ts"), {
|
|
7683
|
+
const edgeFunctionsCode = (0, _hot_updater_cli_tools.transformEnv)(path.default.join(edgeFunctionsLibPath, "index.ts"), {
|
|
7684
|
+
BUCKET_NAME: bucketName,
|
|
7685
|
+
FUNCTION_NAME: functionName
|
|
7686
|
+
});
|
|
7190
7687
|
if (!isSupabaseFunctionName(functionName)) throw new Error("Invalid Supabase Edge Function name.");
|
|
7191
7688
|
const functionsDir = path.default.resolve(workdir, "supabase", "functions");
|
|
7192
7689
|
const targetDir = path.default.resolve(functionsDir, functionName);
|
|
@@ -7233,6 +7730,15 @@ const waitForSupabaseProjectReady = async ({ getProjectStatus, maxAttempts = SUP
|
|
|
7233
7730
|
}
|
|
7234
7731
|
throw new Error(`Timed out while waiting for the Supabase project. Last status: ${lastStatus ?? "unknown"}.`);
|
|
7235
7732
|
};
|
|
7733
|
+
const waitForSupabaseSchemaReady = async ({ getInfrastructureState, maxAttempts = SUPABASE_SCHEMA_READINESS_MAX_ATTEMPTS, pollIntervalMs = SUPABASE_SCHEMA_READINESS_POLL_INTERVAL_MS }) => {
|
|
7734
|
+
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
7735
|
+
const state = await getInfrastructureState();
|
|
7736
|
+
if (state === "v1") return;
|
|
7737
|
+
if (state === "incompatible") throw new Error("Supabase database schema is incompatible after migration.");
|
|
7738
|
+
if (attempt < maxAttempts - 1) await delay(pollIntervalMs);
|
|
7739
|
+
}
|
|
7740
|
+
throw new Error("Timed out while waiting for the Supabase database schema cache.");
|
|
7741
|
+
};
|
|
7236
7742
|
const getSupabaseProjectAccess = async ({ accessToken, managementApi, project, waitForProject }) => {
|
|
7237
7743
|
const getServiceRoleApiKey = async () => {
|
|
7238
7744
|
const keysProcess = await execa("npx", [
|
|
@@ -7279,11 +7785,30 @@ const getSupabaseProjectAccess = async ({ accessToken, managementApi, project, w
|
|
|
7279
7785
|
serviceRoleApiKey
|
|
7280
7786
|
};
|
|
7281
7787
|
};
|
|
7282
|
-
const
|
|
7788
|
+
const withSupabaseCliMetadataCleanup = async (cwd, operation) => {
|
|
7789
|
+
const cliDirectory = path.default.join(cwd, "supabase");
|
|
7790
|
+
let shouldCleanup = false;
|
|
7791
|
+
try {
|
|
7792
|
+
await fs_promises.default.access(cliDirectory);
|
|
7793
|
+
} catch (error) {
|
|
7794
|
+
if (!(error instanceof Error) || Reflect.get(error, "code") !== "ENOENT") throw error;
|
|
7795
|
+
shouldCleanup = true;
|
|
7796
|
+
}
|
|
7797
|
+
try {
|
|
7798
|
+
return await operation();
|
|
7799
|
+
} finally {
|
|
7800
|
+
if (shouldCleanup) await fs_promises.default.rm(cliDirectory, {
|
|
7801
|
+
recursive: true,
|
|
7802
|
+
force: true
|
|
7803
|
+
});
|
|
7804
|
+
}
|
|
7805
|
+
};
|
|
7806
|
+
const runInitWithoutCliMetadata = async ({ build, envFile }) => {
|
|
7283
7807
|
const nonInteractive = envFile !== void 0;
|
|
7284
7808
|
const initEnvSources = await (0, _hot_updater_cli_tools.readHotUpdaterInitEnv)(process.cwd(), envFile);
|
|
7285
7809
|
const { inputEnv, managedEnv } = initEnvSources;
|
|
7286
|
-
const
|
|
7810
|
+
const initInputEnv = (0, _hot_updater_cli_tools.getHotUpdaterInitInputEnv)(initEnvSources, nonInteractive);
|
|
7811
|
+
const savedInputs = resolveSupabaseInitInputs(initInputEnv, {
|
|
7287
7812
|
inputEnv,
|
|
7288
7813
|
managedEnv
|
|
7289
7814
|
});
|
|
@@ -7322,11 +7847,15 @@ const runInit = async ({ build, envFile }) => {
|
|
|
7322
7847
|
name: projectCreationInputs.bucketName
|
|
7323
7848
|
};
|
|
7324
7849
|
else throw new Error("Failed to plan the Supabase storage bucket.");
|
|
7325
|
-
if (projectAccess)
|
|
7326
|
-
|
|
7327
|
-
|
|
7328
|
-
|
|
7329
|
-
|
|
7850
|
+
if (projectAccess) {
|
|
7851
|
+
await assertSupabaseInfrastructureCanInitialize(projectAccess.api, project.id);
|
|
7852
|
+
await assertSupabaseFunctionCanInitialize({
|
|
7853
|
+
functionName,
|
|
7854
|
+
functionSlugs: await managementApi.listFunctions(project.id),
|
|
7855
|
+
projectId: project.id
|
|
7856
|
+
});
|
|
7857
|
+
preserveSupabaseBucketPrivacy({ selection: bucketSelection });
|
|
7858
|
+
}
|
|
7330
7859
|
const inputsBeforeProvisioning = {
|
|
7331
7860
|
...savedInputs,
|
|
7332
7861
|
...projectCreationInputs,
|
|
@@ -7376,7 +7905,7 @@ const runInit = async ({ build, envFile }) => {
|
|
|
7376
7905
|
});
|
|
7377
7906
|
const persistDatabasePassword = persistCredentialInputs && dbPassword !== "";
|
|
7378
7907
|
if (persistDatabasePassword) providerEnv[SUPABASE_DATABASE_PASSWORD_PROJECT_ID_ENV_KEY] = project.id;
|
|
7379
|
-
await (0, _hot_updater_cli_tools.makeEnv)(providerEnv, ".env.hotupdater", { removeKeys: persistDatabasePassword ? [] : [databasePasswordKey, SUPABASE_DATABASE_PASSWORD_PROJECT_ID_ENV_KEY] });
|
|
7908
|
+
await (0, _hot_updater_cli_tools.makeEnv)(providerEnv, ".env.hotupdater", { removeKeys: [LEGACY_SUPABASE_CATALOG_CDN_URL_ENV_KEY, ...persistDatabasePassword ? [] : [databasePasswordKey, SUPABASE_DATABASE_PASSWORD_PROJECT_ID_ENV_KEY]] });
|
|
7380
7909
|
const bucket = await createSelectedBucket(projectAccess.api, bucketSelection);
|
|
7381
7910
|
await (0, _hot_updater_cli_tools.makeEnv)({
|
|
7382
7911
|
[initProvider.inputs.projectId.envKey]: project.id,
|
|
@@ -7401,7 +7930,23 @@ const runInit = async ({ build, envFile }) => {
|
|
|
7401
7930
|
accessToken,
|
|
7402
7931
|
dbPassword
|
|
7403
7932
|
});
|
|
7404
|
-
await
|
|
7933
|
+
await waitForSupabaseSchemaReady({ getInfrastructureState: projectAccess.api.getInfrastructureState });
|
|
7934
|
+
const databasePlugin = supabaseDatabase({
|
|
7935
|
+
supabaseServiceRoleKey: projectAccess.serviceRoleApiKey,
|
|
7936
|
+
supabaseUrl: `https://${project.id}.supabase.co`
|
|
7937
|
+
});
|
|
7938
|
+
let apiKey;
|
|
7939
|
+
try {
|
|
7940
|
+
apiKey = (await (0, _hot_updater_server.provisionApiKey)({
|
|
7941
|
+
apiKeys: databasePlugin.models.apiKeys,
|
|
7942
|
+
existingApiKey: initInputEnv.HOT_UPDATER_API_KEY,
|
|
7943
|
+
name: "Supabase init"
|
|
7944
|
+
})).apiKey;
|
|
7945
|
+
await (0, _hot_updater_cli_tools.makeEnv)({ HOT_UPDATER_API_KEY: apiKey });
|
|
7946
|
+
} finally {
|
|
7947
|
+
await databasePlugin.dispose?.();
|
|
7948
|
+
}
|
|
7949
|
+
await deployEdgeFunction(accessToken, tmpDir, project.id, functionName, bucket.name);
|
|
7405
7950
|
await removeTmpDir();
|
|
7406
7951
|
const configWriteResult = await (0, _hot_updater_cli_tools.writeHotUpdaterConfig)(getConfigScaffold(build));
|
|
7407
7952
|
await assertSkippedConfigDoesNotUseLegacySupabaseKey(configWriteResult);
|
|
@@ -7409,16 +7954,28 @@ const runInit = async ({ build, envFile }) => {
|
|
|
7409
7954
|
if (configWriteResult.status === "created") _hot_updater_cli_tools.p.log.success("Generated 'hot-updater.config.ts' file with Supabase settings.");
|
|
7410
7955
|
else if (configWriteResult.status === "merged") _hot_updater_cli_tools.p.log.success("Updated 'hot-updater.config.ts' file with Supabase settings.");
|
|
7411
7956
|
else _hot_updater_cli_tools.p.log.warn(`Kept existing 'hot-updater.config.ts' unchanged: ${configWriteResult.reason}`);
|
|
7412
|
-
_hot_updater_cli_tools.p.note((
|
|
7957
|
+
_hot_updater_cli_tools.p.note(getSupabaseReactNativeSource({
|
|
7958
|
+
apiKey,
|
|
7959
|
+
functionName,
|
|
7960
|
+
projectId: project.id
|
|
7961
|
+
}));
|
|
7962
|
+
reportSupabaseApiKey(apiKey);
|
|
7963
|
+
reportSupabaseOriginCatalogReady();
|
|
7413
7964
|
_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")}`);
|
|
7414
7965
|
_hot_updater_cli_tools.p.log.success("Done! 🎉");
|
|
7415
7966
|
};
|
|
7967
|
+
const runInit = (options) => withSupabaseCliMetadataCleanup(process.cwd(), () => runInitWithoutCliMetadata(options));
|
|
7416
7968
|
//#endregion
|
|
7417
7969
|
exports.createSelectedBucket = createSelectedBucket;
|
|
7418
7970
|
exports.getLegacySupabaseConfigReference = getLegacySupabaseConfigReference;
|
|
7419
7971
|
exports.getSupabaseProjectAccess = getSupabaseProjectAccess;
|
|
7972
|
+
exports.getSupabaseReactNativeSource = getSupabaseReactNativeSource;
|
|
7973
|
+
exports.reportSupabaseApiKey = reportSupabaseApiKey;
|
|
7974
|
+
exports.reportSupabaseOriginCatalogReady = reportSupabaseOriginCatalogReady;
|
|
7420
7975
|
exports.resolveEdgeFunctionDenoConfig = resolveEdgeFunctionDenoConfig;
|
|
7421
7976
|
exports.runInit = runInit;
|
|
7422
7977
|
exports.selectBucket = selectBucket;
|
|
7423
7978
|
exports.selectProject = selectProject;
|
|
7424
7979
|
exports.waitForSupabaseProjectReady = waitForSupabaseProjectReady;
|
|
7980
|
+
exports.waitForSupabaseSchemaReady = waitForSupabaseSchemaReady;
|
|
7981
|
+
exports.withSupabaseCliMetadataCleanup = withSupabaseCliMetadataCleanup;
|