percher 0.5.6 → 0.5.7
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/bin/percher.js +1525 -140
- package/dist/package.json +1 -1
- package/dist/src/commands/migrate.d.ts.map +1 -1
- package/dist/src/commands/migrate.js +86 -14
- package/dist/src/commands/migrate.js.map +1 -1
- package/package.json +1 -1
package/dist/bin/percher.js
CHANGED
|
@@ -5069,11 +5069,11 @@ var require_tslib = __commonJS((exports, module) => {
|
|
|
5069
5069
|
};
|
|
5070
5070
|
__awaiter = function(thisArg, _arguments, P, generator) {
|
|
5071
5071
|
function adopt(value) {
|
|
5072
|
-
return value instanceof P ? value : new P(function(
|
|
5073
|
-
|
|
5072
|
+
return value instanceof P ? value : new P(function(resolve6) {
|
|
5073
|
+
resolve6(value);
|
|
5074
5074
|
});
|
|
5075
5075
|
}
|
|
5076
|
-
return new (P || (P = Promise))(function(
|
|
5076
|
+
return new (P || (P = Promise))(function(resolve6, reject) {
|
|
5077
5077
|
function fulfilled(value) {
|
|
5078
5078
|
try {
|
|
5079
5079
|
step(generator.next(value));
|
|
@@ -5089,7 +5089,7 @@ var require_tslib = __commonJS((exports, module) => {
|
|
|
5089
5089
|
}
|
|
5090
5090
|
}
|
|
5091
5091
|
function step(result) {
|
|
5092
|
-
result.done ?
|
|
5092
|
+
result.done ? resolve6(result.value) : adopt(result.value).then(fulfilled, rejected);
|
|
5093
5093
|
}
|
|
5094
5094
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
5095
5095
|
});
|
|
@@ -5318,14 +5318,14 @@ var require_tslib = __commonJS((exports, module) => {
|
|
|
5318
5318
|
}, i);
|
|
5319
5319
|
function verb(n) {
|
|
5320
5320
|
i[n] = o[n] && function(v) {
|
|
5321
|
-
return new Promise(function(
|
|
5322
|
-
v = o[n](v), settle(
|
|
5321
|
+
return new Promise(function(resolve6, reject) {
|
|
5322
|
+
v = o[n](v), settle(resolve6, reject, v.done, v.value);
|
|
5323
5323
|
});
|
|
5324
5324
|
};
|
|
5325
5325
|
}
|
|
5326
|
-
function settle(
|
|
5326
|
+
function settle(resolve6, reject, d, v) {
|
|
5327
5327
|
Promise.resolve(v).then(function(v2) {
|
|
5328
|
-
|
|
5328
|
+
resolve6({ value: v2, done: d });
|
|
5329
5329
|
}, reject);
|
|
5330
5330
|
}
|
|
5331
5331
|
};
|
|
@@ -8824,7 +8824,7 @@ var require_util = __commonJS((exports) => {
|
|
|
8824
8824
|
}
|
|
8825
8825
|
path = url2.path;
|
|
8826
8826
|
}
|
|
8827
|
-
var
|
|
8827
|
+
var isAbsolute2 = exports.isAbsolute(path);
|
|
8828
8828
|
var parts = path.split(/\/+/);
|
|
8829
8829
|
for (var part, up = 0, i = parts.length - 1;i >= 0; i--) {
|
|
8830
8830
|
part = parts[i];
|
|
@@ -8844,7 +8844,7 @@ var require_util = __commonJS((exports) => {
|
|
|
8844
8844
|
}
|
|
8845
8845
|
path = parts.join("/");
|
|
8846
8846
|
if (path === "") {
|
|
8847
|
-
path =
|
|
8847
|
+
path = isAbsolute2 ? "/" : ".";
|
|
8848
8848
|
}
|
|
8849
8849
|
if (url2) {
|
|
8850
8850
|
url2.path = path;
|
|
@@ -8853,7 +8853,7 @@ var require_util = __commonJS((exports) => {
|
|
|
8853
8853
|
return path;
|
|
8854
8854
|
}
|
|
8855
8855
|
exports.normalize = normalize2;
|
|
8856
|
-
function
|
|
8856
|
+
function join17(aRoot, aPath) {
|
|
8857
8857
|
if (aRoot === "") {
|
|
8858
8858
|
aRoot = ".";
|
|
8859
8859
|
}
|
|
@@ -8885,11 +8885,11 @@ var require_util = __commonJS((exports) => {
|
|
|
8885
8885
|
}
|
|
8886
8886
|
return joined;
|
|
8887
8887
|
}
|
|
8888
|
-
exports.join =
|
|
8888
|
+
exports.join = join17;
|
|
8889
8889
|
exports.isAbsolute = function(aPath) {
|
|
8890
8890
|
return aPath.charAt(0) === "/" || urlRegexp.test(aPath);
|
|
8891
8891
|
};
|
|
8892
|
-
function
|
|
8892
|
+
function relative3(aRoot, aPath) {
|
|
8893
8893
|
if (aRoot === "") {
|
|
8894
8894
|
aRoot = ".";
|
|
8895
8895
|
}
|
|
@@ -8908,7 +8908,7 @@ var require_util = __commonJS((exports) => {
|
|
|
8908
8908
|
}
|
|
8909
8909
|
return Array(level + 1).join("../") + aPath.substr(aRoot.length + 1);
|
|
8910
8910
|
}
|
|
8911
|
-
exports.relative =
|
|
8911
|
+
exports.relative = relative3;
|
|
8912
8912
|
var supportsNullProto = function() {
|
|
8913
8913
|
var obj = Object.create(null);
|
|
8914
8914
|
return !("__proto__" in obj);
|
|
@@ -9058,7 +9058,7 @@ var require_util = __commonJS((exports) => {
|
|
|
9058
9058
|
parsed.path = parsed.path.substring(0, index + 1);
|
|
9059
9059
|
}
|
|
9060
9060
|
}
|
|
9061
|
-
sourceURL =
|
|
9061
|
+
sourceURL = join17(urlGenerate(parsed), sourceURL);
|
|
9062
9062
|
}
|
|
9063
9063
|
return normalize2(sourceURL);
|
|
9064
9064
|
}
|
|
@@ -89384,10 +89384,10 @@ var require_browser = __commonJS((exports) => {
|
|
|
89384
89384
|
text = canvas;
|
|
89385
89385
|
canvas = undefined;
|
|
89386
89386
|
}
|
|
89387
|
-
return new Promise(function(
|
|
89387
|
+
return new Promise(function(resolve9, reject) {
|
|
89388
89388
|
try {
|
|
89389
89389
|
const data2 = QRCode.create(text, opts);
|
|
89390
|
-
|
|
89390
|
+
resolve9(renderFunc(data2, canvas, opts));
|
|
89391
89391
|
} catch (e) {
|
|
89392
89392
|
reject(e);
|
|
89393
89393
|
}
|
|
@@ -89466,11 +89466,11 @@ var require_server = __commonJS((exports) => {
|
|
|
89466
89466
|
}
|
|
89467
89467
|
function render(renderFunc, text, params) {
|
|
89468
89468
|
if (!params.cb) {
|
|
89469
|
-
return new Promise(function(
|
|
89469
|
+
return new Promise(function(resolve9, reject) {
|
|
89470
89470
|
try {
|
|
89471
89471
|
const data2 = QRCode.create(text, params.opts);
|
|
89472
89472
|
return renderFunc(data2, params.opts, function(err, data3) {
|
|
89473
|
-
return err ? reject(err) :
|
|
89473
|
+
return err ? reject(err) : resolve9(data3);
|
|
89474
89474
|
});
|
|
89475
89475
|
} catch (e) {
|
|
89476
89476
|
reject(e);
|
|
@@ -89565,15 +89565,15 @@ var require_windows = __commonJS((exports, module) => {
|
|
|
89565
89565
|
}
|
|
89566
89566
|
return false;
|
|
89567
89567
|
}
|
|
89568
|
-
function checkStat(
|
|
89569
|
-
if (!
|
|
89568
|
+
function checkStat(stat2, path, options) {
|
|
89569
|
+
if (!stat2.isSymbolicLink() && !stat2.isFile()) {
|
|
89570
89570
|
return false;
|
|
89571
89571
|
}
|
|
89572
89572
|
return checkPathExt(path, options);
|
|
89573
89573
|
}
|
|
89574
89574
|
function isexe(path, options, cb) {
|
|
89575
|
-
fs.stat(path, function(er,
|
|
89576
|
-
cb(er, er ? false : checkStat(
|
|
89575
|
+
fs.stat(path, function(er, stat2) {
|
|
89576
|
+
cb(er, er ? false : checkStat(stat2, path, options));
|
|
89577
89577
|
});
|
|
89578
89578
|
}
|
|
89579
89579
|
function sync(path, options) {
|
|
@@ -89587,20 +89587,20 @@ var require_mode2 = __commonJS((exports, module) => {
|
|
|
89587
89587
|
isexe.sync = sync;
|
|
89588
89588
|
var fs = __require("fs");
|
|
89589
89589
|
function isexe(path, options, cb) {
|
|
89590
|
-
fs.stat(path, function(er,
|
|
89591
|
-
cb(er, er ? false : checkStat(
|
|
89590
|
+
fs.stat(path, function(er, stat2) {
|
|
89591
|
+
cb(er, er ? false : checkStat(stat2, options));
|
|
89592
89592
|
});
|
|
89593
89593
|
}
|
|
89594
89594
|
function sync(path, options) {
|
|
89595
89595
|
return checkStat(fs.statSync(path), options);
|
|
89596
89596
|
}
|
|
89597
|
-
function checkStat(
|
|
89598
|
-
return
|
|
89597
|
+
function checkStat(stat2, options) {
|
|
89598
|
+
return stat2.isFile() && checkMode(stat2, options);
|
|
89599
89599
|
}
|
|
89600
|
-
function checkMode(
|
|
89601
|
-
var mod =
|
|
89602
|
-
var uid =
|
|
89603
|
-
var gid =
|
|
89600
|
+
function checkMode(stat2, options) {
|
|
89601
|
+
var mod = stat2.mode;
|
|
89602
|
+
var uid = stat2.uid;
|
|
89603
|
+
var gid = stat2.gid;
|
|
89604
89604
|
var myUid = options.uid !== undefined ? options.uid : process.getuid && process.getuid();
|
|
89605
89605
|
var myGid = options.gid !== undefined ? options.gid : process.getgid && process.getgid();
|
|
89606
89606
|
var u = parseInt("100", 8);
|
|
@@ -89632,12 +89632,12 @@ var require_isexe = __commonJS((exports, module) => {
|
|
|
89632
89632
|
if (typeof Promise !== "function") {
|
|
89633
89633
|
throw new TypeError("callback not provided");
|
|
89634
89634
|
}
|
|
89635
|
-
return new Promise(function(
|
|
89635
|
+
return new Promise(function(resolve9, reject) {
|
|
89636
89636
|
isexe(path, options || {}, function(er, is) {
|
|
89637
89637
|
if (er) {
|
|
89638
89638
|
reject(er);
|
|
89639
89639
|
} else {
|
|
89640
|
-
|
|
89640
|
+
resolve9(is);
|
|
89641
89641
|
}
|
|
89642
89642
|
});
|
|
89643
89643
|
});
|
|
@@ -89699,27 +89699,27 @@ var require_which = __commonJS((exports, module) => {
|
|
|
89699
89699
|
opt = {};
|
|
89700
89700
|
const { pathEnv, pathExt, pathExtExe } = getPathInfo(cmd, opt);
|
|
89701
89701
|
const found = [];
|
|
89702
|
-
const step = (i) => new Promise((
|
|
89702
|
+
const step = (i) => new Promise((resolve9, reject) => {
|
|
89703
89703
|
if (i === pathEnv.length)
|
|
89704
|
-
return opt.all && found.length ?
|
|
89704
|
+
return opt.all && found.length ? resolve9(found) : reject(getNotFoundError(cmd));
|
|
89705
89705
|
const ppRaw = pathEnv[i];
|
|
89706
89706
|
const pathPart = /^".*"$/.test(ppRaw) ? ppRaw.slice(1, -1) : ppRaw;
|
|
89707
89707
|
const pCmd = path.join(pathPart, cmd);
|
|
89708
89708
|
const p = !pathPart && /^\.[\\\/]/.test(cmd) ? cmd.slice(0, 2) + pCmd : pCmd;
|
|
89709
|
-
|
|
89709
|
+
resolve9(subStep(p, i, 0));
|
|
89710
89710
|
});
|
|
89711
|
-
const subStep = (p, i, ii) => new Promise((
|
|
89711
|
+
const subStep = (p, i, ii) => new Promise((resolve9, reject) => {
|
|
89712
89712
|
if (ii === pathExt.length)
|
|
89713
|
-
return
|
|
89713
|
+
return resolve9(step(i + 1));
|
|
89714
89714
|
const ext = pathExt[ii];
|
|
89715
89715
|
isexe(p + ext, { pathExt: pathExtExe }, (er, is) => {
|
|
89716
89716
|
if (!er && is) {
|
|
89717
89717
|
if (opt.all)
|
|
89718
89718
|
found.push(p + ext);
|
|
89719
89719
|
else
|
|
89720
|
-
return
|
|
89720
|
+
return resolve9(p + ext);
|
|
89721
89721
|
}
|
|
89722
|
-
return
|
|
89722
|
+
return resolve9(subStep(p, i, ii + 1));
|
|
89723
89723
|
});
|
|
89724
89724
|
});
|
|
89725
89725
|
return cb ? step(0).then((res) => cb(null, res), cb) : step(0);
|
|
@@ -90181,7 +90181,7 @@ __export(exports_prompt_hidden, {
|
|
|
90181
90181
|
});
|
|
90182
90182
|
import { stdin, stdout } from "node:process";
|
|
90183
90183
|
function promptHiddenWithStreams(question, input, output) {
|
|
90184
|
-
return new Promise((
|
|
90184
|
+
return new Promise((resolve9, reject) => {
|
|
90185
90185
|
output.write(`${question} `);
|
|
90186
90186
|
let buf = "";
|
|
90187
90187
|
let done = false;
|
|
@@ -90197,7 +90197,7 @@ function promptHiddenWithStreams(question, input, output) {
|
|
|
90197
90197
|
input.removeListener("error", onError);
|
|
90198
90198
|
output.write(`
|
|
90199
90199
|
`);
|
|
90200
|
-
|
|
90200
|
+
resolve9(buf.replace(/\r$/, ""));
|
|
90201
90201
|
return;
|
|
90202
90202
|
}
|
|
90203
90203
|
buf += ch;
|
|
@@ -90219,7 +90219,7 @@ function promptHiddenWithStreams(question, input, output) {
|
|
|
90219
90219
|
input.removeListener("error", onError);
|
|
90220
90220
|
output.write(`
|
|
90221
90221
|
`);
|
|
90222
|
-
|
|
90222
|
+
resolve9(buf.replace(/\r$/, ""));
|
|
90223
90223
|
};
|
|
90224
90224
|
input.on("data", onData);
|
|
90225
90225
|
input.on("error", onError);
|
|
@@ -90237,7 +90237,7 @@ async function promptHidden(question) {
|
|
|
90237
90237
|
ttyStdin.resume();
|
|
90238
90238
|
ttyStdin.setEncoding("utf8");
|
|
90239
90239
|
try {
|
|
90240
|
-
return await new Promise((
|
|
90240
|
+
return await new Promise((resolve9, reject) => {
|
|
90241
90241
|
stdout.write(`${question} `);
|
|
90242
90242
|
let buf = "";
|
|
90243
90243
|
const onData = (key) => {
|
|
@@ -90247,7 +90247,7 @@ async function promptHidden(question) {
|
|
|
90247
90247
|
ttyStdin.removeListener("data", onData);
|
|
90248
90248
|
stdout.write(`
|
|
90249
90249
|
`);
|
|
90250
|
-
|
|
90250
|
+
resolve9(buf);
|
|
90251
90251
|
return;
|
|
90252
90252
|
}
|
|
90253
90253
|
if (ch === "\x03") {
|
|
@@ -105486,10 +105486,17 @@ function loadConfig() {
|
|
|
105486
105486
|
const envToken = process.env.PERCHER_TOKEN;
|
|
105487
105487
|
return {
|
|
105488
105488
|
apiUrl: envUrl ?? file2.apiUrl ?? DEFAULT_API_URL,
|
|
105489
|
-
token: envToken ?? file2.token ?? null
|
|
105490
|
-
anonClaim: file2.anonClaim ?? null
|
|
105489
|
+
token: envToken ?? file2.token ?? null
|
|
105491
105490
|
};
|
|
105492
105491
|
}
|
|
105492
|
+
function readAnonClaims() {
|
|
105493
|
+
const file2 = readFile2();
|
|
105494
|
+
const claims = { ...file2.anonClaims ?? {} };
|
|
105495
|
+
if (file2.anonClaim?.app && !claims[file2.anonClaim.app]) {
|
|
105496
|
+
claims[file2.anonClaim.app] = file2.anonClaim;
|
|
105497
|
+
}
|
|
105498
|
+
return claims;
|
|
105499
|
+
}
|
|
105493
105500
|
function saveConfig(partial2) {
|
|
105494
105501
|
const p = configPath();
|
|
105495
105502
|
mkdirSync3(dirname3(p), { recursive: true });
|
|
@@ -105499,6 +105506,21 @@ function saveConfig(partial2) {
|
|
|
105499
105506
|
writeFileSync3(tmp, JSON.stringify(merged, null, 2), "utf8");
|
|
105500
105507
|
renameSync(tmp, p);
|
|
105501
105508
|
}
|
|
105509
|
+
function getAnonClaim(app) {
|
|
105510
|
+
return readAnonClaims()[app] ?? null;
|
|
105511
|
+
}
|
|
105512
|
+
function setAnonClaim(state) {
|
|
105513
|
+
const claims = { ...readAnonClaims(), [state.app]: state };
|
|
105514
|
+
saveConfig({ anonClaims: claims, anonClaim: null });
|
|
105515
|
+
}
|
|
105516
|
+
function clearAnonClaim(app) {
|
|
105517
|
+
const claims = { ...readAnonClaims() };
|
|
105518
|
+
delete claims[app];
|
|
105519
|
+
saveConfig({ anonClaims: claims, anonClaim: null });
|
|
105520
|
+
}
|
|
105521
|
+
function listAnonClaimApps() {
|
|
105522
|
+
return Object.keys(readAnonClaims());
|
|
105523
|
+
}
|
|
105502
105524
|
|
|
105503
105525
|
// ../client/dist/errors.js
|
|
105504
105526
|
class PercherApiError extends Error {
|
|
@@ -106534,13 +106556,14 @@ var claimInputSchema = exports_external.object({
|
|
|
106534
106556
|
claimToken: exports_external.string().optional().describe("The one-time claim token returned by the anonymous publish (pcl_…)")
|
|
106535
106557
|
});
|
|
106536
106558
|
async function claim(ctx, input = {}) {
|
|
106537
|
-
const
|
|
106538
|
-
const
|
|
106539
|
-
const
|
|
106559
|
+
const tomlApp = readPercherTomlAppName(ctx.cwd) ?? undefined;
|
|
106560
|
+
const remembered = listAnonClaimApps();
|
|
106561
|
+
const app = input.app ?? tomlApp ?? (remembered.length === 1 ? remembered[0] : undefined);
|
|
106562
|
+
const claimToken = input.claimToken ?? (app ? getAnonClaim(app)?.claimToken ?? undefined : undefined);
|
|
106540
106563
|
if (!app) {
|
|
106541
106564
|
return {
|
|
106542
106565
|
status: "needs_token",
|
|
106543
|
-
summary: "No app to claim. Pass --app (or run from the project that published it) and the claim token."
|
|
106566
|
+
summary: remembered.length > 1 ? `Multiple anonymous apps are remembered (${remembered.join(", ")}). Pass --app to choose one.` : "No app to claim. Pass --app (or run from the project that published it) and the claim token."
|
|
106544
106567
|
};
|
|
106545
106568
|
}
|
|
106546
106569
|
if (!claimToken) {
|
|
@@ -106550,8 +106573,7 @@ async function claim(ctx, input = {}) {
|
|
|
106550
106573
|
};
|
|
106551
106574
|
}
|
|
106552
106575
|
const result = await ctx.client.apps.claim(app, { claimToken });
|
|
106553
|
-
|
|
106554
|
-
saveConfig({ anonClaim: null });
|
|
106576
|
+
clearAnonClaim(app);
|
|
106555
106577
|
return {
|
|
106556
106578
|
status: "claimed",
|
|
106557
106579
|
app: result.name,
|
|
@@ -110866,9 +110888,1332 @@ function collectWarnings2(collections, rlsPolicies, unsupported) {
|
|
|
110866
110888
|
}
|
|
110867
110889
|
return warnings;
|
|
110868
110890
|
}
|
|
110891
|
+
// ../core/dist/commands/migrate-supabase-rewrite.js
|
|
110892
|
+
import { mkdir as mkdir3, writeFile as writeFile3 } from "node:fs/promises";
|
|
110893
|
+
import { dirname as dirname5, join as join18, relative as relative4, resolve as resolve7 } from "node:path";
|
|
110894
|
+
|
|
110895
|
+
// ../core/dist/commands/migrate-supabase-walker.js
|
|
110896
|
+
var import_ignore3 = __toESM(require_ignore(), 1);
|
|
110897
|
+
import { mkdir as mkdir2, readdir, readFile as readFile3, stat, writeFile as writeFile2 } from "node:fs/promises";
|
|
110898
|
+
import { dirname as dirname4, isAbsolute as isAbsolute2, join as join17, relative as relative3, resolve as resolve6, sep as sep3 } from "node:path";
|
|
110899
|
+
|
|
110900
|
+
// ../core/dist/commands/migrate-supabase-sdk.js
|
|
110901
|
+
var import_jscodeshift = __toESM(require_core3(), 1);
|
|
110902
|
+
var tsx = import_jscodeshift.default.withParser("tsx");
|
|
110903
|
+
function rewriteSupabaseSdk(input) {
|
|
110904
|
+
const pbName = input.pbIdentifier ?? "pb";
|
|
110905
|
+
const supabaseNames = new Set(input.supabaseIdentifiers ?? ["supabase"]);
|
|
110906
|
+
if (!sourceMentionsSupabase(input.source, supabaseNames)) {
|
|
110907
|
+
return { rewritten: input.source, flags: [], changed: false };
|
|
110908
|
+
}
|
|
110909
|
+
let root;
|
|
110910
|
+
try {
|
|
110911
|
+
root = tsx(input.source);
|
|
110912
|
+
} catch (err) {
|
|
110913
|
+
return {
|
|
110914
|
+
rewritten: input.source,
|
|
110915
|
+
flags: [],
|
|
110916
|
+
changed: false,
|
|
110917
|
+
parseError: err instanceof Error ? err.message : String(err)
|
|
110918
|
+
};
|
|
110919
|
+
}
|
|
110920
|
+
const flags = [];
|
|
110921
|
+
const state = { mutated: false };
|
|
110922
|
+
rewriteAuthCalls(root, supabaseNames, pbName, flags, state);
|
|
110923
|
+
rewriteFromCalls(root, supabaseNames, pbName, flags, state);
|
|
110924
|
+
flagUnsupportedPatterns(root, supabaseNames, flags);
|
|
110925
|
+
if (!state.mutated) {
|
|
110926
|
+
return { rewritten: input.source, flags, changed: false };
|
|
110927
|
+
}
|
|
110928
|
+
return {
|
|
110929
|
+
rewritten: root.toSource({ quote: "double" }),
|
|
110930
|
+
flags,
|
|
110931
|
+
changed: true
|
|
110932
|
+
};
|
|
110933
|
+
}
|
|
110934
|
+
function consumesDataErrorEnvelope(path) {
|
|
110935
|
+
let outer = path.node;
|
|
110936
|
+
let outerParent = path.parent?.node;
|
|
110937
|
+
if (outerParent?.type === "AwaitExpression") {
|
|
110938
|
+
const aw = outerParent;
|
|
110939
|
+
if (aw.argument === path.node) {
|
|
110940
|
+
outer = outerParent;
|
|
110941
|
+
outerParent = path.parent.parent?.node;
|
|
110942
|
+
}
|
|
110943
|
+
}
|
|
110944
|
+
if (!outerParent)
|
|
110945
|
+
return false;
|
|
110946
|
+
if (outerParent.type === "MemberExpression") {
|
|
110947
|
+
const m = outerParent;
|
|
110948
|
+
if (m.object === outer && m.property.type === "Identifier") {
|
|
110949
|
+
const propName = m.property.name;
|
|
110950
|
+
if (propName === "data" || propName === "error")
|
|
110951
|
+
return true;
|
|
110952
|
+
}
|
|
110953
|
+
return false;
|
|
110954
|
+
}
|
|
110955
|
+
if (outerParent.type === "VariableDeclarator") {
|
|
110956
|
+
const v = outerParent;
|
|
110957
|
+
if (v.init === outer && v.id.type === "ObjectPattern") {
|
|
110958
|
+
return objectPatternHasDataOrError(v.id);
|
|
110959
|
+
}
|
|
110960
|
+
return false;
|
|
110961
|
+
}
|
|
110962
|
+
if (outerParent.type === "AssignmentExpression") {
|
|
110963
|
+
const a = outerParent;
|
|
110964
|
+
if (a.right === outer && a.left.type === "ObjectPattern") {
|
|
110965
|
+
return objectPatternHasDataOrError(a.left);
|
|
110966
|
+
}
|
|
110967
|
+
return false;
|
|
110968
|
+
}
|
|
110969
|
+
return false;
|
|
110970
|
+
}
|
|
110971
|
+
function objectPatternHasDataOrError(pattern) {
|
|
110972
|
+
for (const prop of pattern.properties) {
|
|
110973
|
+
if ((prop.type === "Property" || prop.type === "ObjectProperty") && "key" in prop && prop.key.type === "Identifier") {
|
|
110974
|
+
const name = prop.key.name;
|
|
110975
|
+
if (name === "data" || name === "error")
|
|
110976
|
+
return true;
|
|
110977
|
+
}
|
|
110978
|
+
}
|
|
110979
|
+
return false;
|
|
110980
|
+
}
|
|
110981
|
+
function flagEnvelopeManualReview(path, pattern, flags) {
|
|
110982
|
+
flags.push(makeFlag(path, "manual_review", `${pattern}.envelope`, `Call site consumes the Supabase \`{ data, error }\` envelope — PocketBase returns plain values. Skipping rewrite; unwrap the destructure (or .data/.error access) manually, then re-run the migrator.`));
|
|
110983
|
+
}
|
|
110984
|
+
function sourceMentionsSupabase(source, supabaseNames) {
|
|
110985
|
+
if (source.includes("@supabase/supabase-js"))
|
|
110986
|
+
return true;
|
|
110987
|
+
for (const name of supabaseNames) {
|
|
110988
|
+
if (source.includes(name))
|
|
110989
|
+
return true;
|
|
110990
|
+
}
|
|
110991
|
+
return false;
|
|
110992
|
+
}
|
|
110993
|
+
function rewriteAuthCalls(root, supabaseNames, pbName, flags, state) {
|
|
110994
|
+
root.find(import_jscodeshift.default.CallExpression).filter((path) => isSupabaseAuthCall(path, supabaseNames)).forEach((path) => {
|
|
110995
|
+
const callee = path.node.callee;
|
|
110996
|
+
const method = callee.property.name;
|
|
110997
|
+
switch (method) {
|
|
110998
|
+
case "signInWithPassword":
|
|
110999
|
+
replaceAuthSignIn(path, pbName, flags, state);
|
|
111000
|
+
return;
|
|
111001
|
+
case "signUp":
|
|
111002
|
+
replaceAuthSignUp(path, pbName, flags, state);
|
|
111003
|
+
return;
|
|
111004
|
+
case "signOut":
|
|
111005
|
+
replaceAuthSignOut(path, pbName, flags, state);
|
|
111006
|
+
return;
|
|
111007
|
+
case "getUser":
|
|
111008
|
+
replaceAuthGetUser(path, pbName, flags, state);
|
|
111009
|
+
return;
|
|
111010
|
+
case "onAuthStateChange":
|
|
111011
|
+
replaceAuthOnChange(path, pbName, flags, state);
|
|
111012
|
+
return;
|
|
111013
|
+
}
|
|
111014
|
+
});
|
|
111015
|
+
}
|
|
111016
|
+
function replaceAuthOnChange(path, pbName, flags, state) {
|
|
111017
|
+
const callback = path.node.arguments[0];
|
|
111018
|
+
if (!callback) {
|
|
111019
|
+
flags.push(makeFlag(path, "manual_review", "auth.onAuthStateChange", "onAuthStateChange called with no args — manual review."));
|
|
111020
|
+
return;
|
|
111021
|
+
}
|
|
111022
|
+
const replacement = import_jscodeshift.default.callExpression(import_jscodeshift.default.memberExpression(import_jscodeshift.default.memberExpression(import_jscodeshift.default.identifier(pbName), import_jscodeshift.default.identifier("authStore")), import_jscodeshift.default.identifier("onChange")), [callback]);
|
|
111023
|
+
applyRewrite(path, replacement, "manual_review", "auth.onAuthStateChange", "onAuthStateChange → pb.authStore.onChange — the callback signature changes: Supabase passes `(event, session)`, PB passes `(token, model)`. Rewrite the callback body.", flags, state);
|
|
111024
|
+
}
|
|
111025
|
+
function isSupabaseAuthCall(path, supabaseNames) {
|
|
111026
|
+
const callee = path.node.callee;
|
|
111027
|
+
if (callee.type !== "MemberExpression")
|
|
111028
|
+
return false;
|
|
111029
|
+
if (callee.property.type !== "Identifier")
|
|
111030
|
+
return false;
|
|
111031
|
+
const object2 = callee.object;
|
|
111032
|
+
if (object2.type !== "MemberExpression")
|
|
111033
|
+
return false;
|
|
111034
|
+
if (object2.property.type !== "Identifier" || object2.property.name !== "auth")
|
|
111035
|
+
return false;
|
|
111036
|
+
if (object2.object.type !== "Identifier")
|
|
111037
|
+
return false;
|
|
111038
|
+
return supabaseNames.has(object2.object.name);
|
|
111039
|
+
}
|
|
111040
|
+
function replaceAuthSignIn(path, pbName, flags, state) {
|
|
111041
|
+
const arg = path.node.arguments[0];
|
|
111042
|
+
if (arg?.type !== "ObjectExpression") {
|
|
111043
|
+
flags.push(makeFlag(path, "manual_review", "auth.signInWithPassword", "auth.signInWithPassword called with non-object argument — manual review needed."));
|
|
111044
|
+
return;
|
|
111045
|
+
}
|
|
111046
|
+
const emailExpr = findObjectProp(arg, "email");
|
|
111047
|
+
const passwordExpr = findObjectProp(arg, "password");
|
|
111048
|
+
if (!emailExpr || !passwordExpr) {
|
|
111049
|
+
flags.push(makeFlag(path, "manual_review", "auth.signInWithPassword", "auth.signInWithPassword object missing email or password — manual review needed."));
|
|
111050
|
+
return;
|
|
111051
|
+
}
|
|
111052
|
+
if (consumesDataErrorEnvelope(path)) {
|
|
111053
|
+
flagEnvelopeManualReview(path, "auth.signInWithPassword", flags);
|
|
111054
|
+
return;
|
|
111055
|
+
}
|
|
111056
|
+
const replacement = import_jscodeshift.default.callExpression(import_jscodeshift.default.memberExpression(import_jscodeshift.default.callExpression(import_jscodeshift.default.memberExpression(import_jscodeshift.default.identifier(pbName), import_jscodeshift.default.identifier("collection")), [import_jscodeshift.default.literal("users")]), import_jscodeshift.default.identifier("authWithPassword")), [emailExpr, passwordExpr]);
|
|
111057
|
+
applyRewrite(path, replacement, "rewritten", "auth.signInWithPassword", 'signInWithPassword → pb.collection("users").authWithPassword', flags, state);
|
|
111058
|
+
}
|
|
111059
|
+
function replaceAuthSignUp(path, pbName, flags, state) {
|
|
111060
|
+
const arg = path.node.arguments[0];
|
|
111061
|
+
if (arg?.type !== "ObjectExpression") {
|
|
111062
|
+
flags.push(makeFlag(path, "manual_review", "auth.signUp", "auth.signUp called with non-object argument — manual review needed."));
|
|
111063
|
+
return;
|
|
111064
|
+
}
|
|
111065
|
+
const emailExpr = findObjectProp(arg, "email");
|
|
111066
|
+
const passwordExpr = findObjectProp(arg, "password");
|
|
111067
|
+
if (!emailExpr || !passwordExpr) {
|
|
111068
|
+
flags.push(makeFlag(path, "manual_review", "auth.signUp", "auth.signUp object missing email or password — manual review needed."));
|
|
111069
|
+
return;
|
|
111070
|
+
}
|
|
111071
|
+
if (consumesDataErrorEnvelope(path)) {
|
|
111072
|
+
flagEnvelopeManualReview(path, "auth.signUp", flags);
|
|
111073
|
+
return;
|
|
111074
|
+
}
|
|
111075
|
+
const replacement = import_jscodeshift.default.callExpression(import_jscodeshift.default.memberExpression(import_jscodeshift.default.callExpression(import_jscodeshift.default.memberExpression(import_jscodeshift.default.identifier(pbName), import_jscodeshift.default.identifier("collection")), [import_jscodeshift.default.literal("users")]), import_jscodeshift.default.identifier("create")), [
|
|
111076
|
+
import_jscodeshift.default.objectExpression([
|
|
111077
|
+
import_jscodeshift.default.property("init", import_jscodeshift.default.identifier("email"), emailExpr),
|
|
111078
|
+
import_jscodeshift.default.property("init", import_jscodeshift.default.identifier("password"), passwordExpr),
|
|
111079
|
+
import_jscodeshift.default.property("init", import_jscodeshift.default.identifier("passwordConfirm"), passwordExpr)
|
|
111080
|
+
])
|
|
111081
|
+
]);
|
|
111082
|
+
applyRewrite(path, replacement, "rewritten", "auth.signUp", 'signUp → pb.collection("users").create({...passwordConfirm})', flags, state);
|
|
111083
|
+
}
|
|
111084
|
+
function replaceAuthSignOut(path, pbName, flags, state) {
|
|
111085
|
+
if (consumesDataErrorEnvelope(path)) {
|
|
111086
|
+
flagEnvelopeManualReview(path, "auth.signOut", flags);
|
|
111087
|
+
return;
|
|
111088
|
+
}
|
|
111089
|
+
const replacement = import_jscodeshift.default.callExpression(import_jscodeshift.default.memberExpression(import_jscodeshift.default.memberExpression(import_jscodeshift.default.identifier(pbName), import_jscodeshift.default.identifier("authStore")), import_jscodeshift.default.identifier("clear")), []);
|
|
111090
|
+
applyRewrite(path, replacement, "rewritten", "auth.signOut", "signOut → pb.authStore.clear()", flags, state);
|
|
111091
|
+
}
|
|
111092
|
+
function replaceAuthGetUser(path, pbName, flags, state) {
|
|
111093
|
+
if (consumesDataErrorEnvelope(path)) {
|
|
111094
|
+
flagEnvelopeManualReview(path, "auth.getUser", flags);
|
|
111095
|
+
return;
|
|
111096
|
+
}
|
|
111097
|
+
const replacement = import_jscodeshift.default.memberExpression(import_jscodeshift.default.memberExpression(import_jscodeshift.default.identifier(pbName), import_jscodeshift.default.identifier("authStore")), import_jscodeshift.default.identifier("model"));
|
|
111098
|
+
applyRewrite(path, replacement, "manual_review", "auth.getUser", "getUser → pb.authStore.model — note Supabase returned Promise<{ data: { user } }>, PB returns the record sync; remove awaits + .data.user access.", flags, state);
|
|
111099
|
+
}
|
|
111100
|
+
function rewriteFromCalls(root, supabaseNames, pbName, flags, state) {
|
|
111101
|
+
root.find(import_jscodeshift.default.CallExpression).filter((path) => isSupabaseFromTerminal(path, supabaseNames)).forEach((path) => {
|
|
111102
|
+
rewriteFromChain(path, supabaseNames, pbName, flags, state);
|
|
111103
|
+
});
|
|
111104
|
+
}
|
|
111105
|
+
function isSupabaseFromTerminal(path, supabaseNames) {
|
|
111106
|
+
let fromCall = null;
|
|
111107
|
+
let cursor = path.node;
|
|
111108
|
+
while (cursor.type === "CallExpression") {
|
|
111109
|
+
const call = cursor;
|
|
111110
|
+
if (call.callee.type !== "MemberExpression")
|
|
111111
|
+
break;
|
|
111112
|
+
const callee = call.callee;
|
|
111113
|
+
if (callee.property.type === "Identifier" && callee.property.name === "from") {
|
|
111114
|
+
fromCall = call;
|
|
111115
|
+
break;
|
|
111116
|
+
}
|
|
111117
|
+
cursor = callee.object;
|
|
111118
|
+
}
|
|
111119
|
+
if (!fromCall)
|
|
111120
|
+
return false;
|
|
111121
|
+
const fromCallee = fromCall.callee;
|
|
111122
|
+
if (fromCallee.object.type !== "Identifier")
|
|
111123
|
+
return false;
|
|
111124
|
+
if (!supabaseNames.has(fromCallee.object.name))
|
|
111125
|
+
return false;
|
|
111126
|
+
const parent = path.parent?.node;
|
|
111127
|
+
if (parent?.type === "MemberExpression") {
|
|
111128
|
+
const parentMember = parent;
|
|
111129
|
+
if (parentMember.object === path.node)
|
|
111130
|
+
return false;
|
|
111131
|
+
}
|
|
111132
|
+
return true;
|
|
111133
|
+
}
|
|
111134
|
+
function rewriteFromChain(path, _supabaseNames, pbName, flags, state) {
|
|
111135
|
+
const decomposed = decomposeFromChain(path.node);
|
|
111136
|
+
if (!decomposed) {
|
|
111137
|
+
flags.push(makeFlag(path, "manual_review", "from.chain", "Couldn't decompose .from() chain shape — manual review."));
|
|
111138
|
+
return;
|
|
111139
|
+
}
|
|
111140
|
+
const { table, steps } = decomposed;
|
|
111141
|
+
const terminal = steps[0];
|
|
111142
|
+
if (!terminal) {
|
|
111143
|
+
return;
|
|
111144
|
+
}
|
|
111145
|
+
switch (terminal.method) {
|
|
111146
|
+
case "select":
|
|
111147
|
+
rewriteSelect(path, pbName, table, steps, flags, state);
|
|
111148
|
+
return;
|
|
111149
|
+
case "insert":
|
|
111150
|
+
rewriteInsert(path, pbName, table, terminal, flags, state);
|
|
111151
|
+
return;
|
|
111152
|
+
case "update":
|
|
111153
|
+
rewriteUpdate(path, pbName, table, steps, flags, state);
|
|
111154
|
+
return;
|
|
111155
|
+
case "delete":
|
|
111156
|
+
rewriteDelete(path, pbName, table, steps, flags, state);
|
|
111157
|
+
return;
|
|
111158
|
+
case "upsert":
|
|
111159
|
+
flags.push(makeFlag(path, "unsupported", "from.upsert", `from(${JSON.stringify(table)}).upsert(...) — PB has no upsert primitive. Rewrite as getOne (catch 404) → update-or-create.`));
|
|
111160
|
+
return;
|
|
111161
|
+
default:
|
|
111162
|
+
flags.push(makeFlag(path, "manual_review", `from.${terminal.method}`, `from(${JSON.stringify(table)}).${terminal.method}(...) has no chunk-1 rewrite — manual review.`));
|
|
111163
|
+
}
|
|
111164
|
+
}
|
|
111165
|
+
function decomposeFromChain(outer) {
|
|
111166
|
+
const reversedSteps = [];
|
|
111167
|
+
let node = outer;
|
|
111168
|
+
while (node.type === "CallExpression") {
|
|
111169
|
+
const call = node;
|
|
111170
|
+
if (call.callee.type !== "MemberExpression")
|
|
111171
|
+
break;
|
|
111172
|
+
const callee = call.callee;
|
|
111173
|
+
const method = callee.property.type === "Identifier" ? callee.property.name : null;
|
|
111174
|
+
if (method === null)
|
|
111175
|
+
return null;
|
|
111176
|
+
if (method === "from") {
|
|
111177
|
+
const arg = call.arguments[0];
|
|
111178
|
+
if (!arg || arg.type !== "Literal" && arg.type !== "StringLiteral")
|
|
111179
|
+
return null;
|
|
111180
|
+
const table = arg.value;
|
|
111181
|
+
if (typeof table !== "string")
|
|
111182
|
+
return null;
|
|
111183
|
+
return { table, steps: reversedSteps.reverse() };
|
|
111184
|
+
}
|
|
111185
|
+
reversedSteps.push({ method, args: call.arguments });
|
|
111186
|
+
node = callee.object;
|
|
111187
|
+
}
|
|
111188
|
+
return null;
|
|
111189
|
+
}
|
|
111190
|
+
function rewriteSelect(path, pbName, table, steps, flags, state) {
|
|
111191
|
+
if (consumesDataErrorEnvelope(path)) {
|
|
111192
|
+
flagEnvelopeManualReview(path, "from.select", flags);
|
|
111193
|
+
return;
|
|
111194
|
+
}
|
|
111195
|
+
const select = steps[0];
|
|
111196
|
+
if (!select)
|
|
111197
|
+
return;
|
|
111198
|
+
const tail = steps.slice(1);
|
|
111199
|
+
const opts = {};
|
|
111200
|
+
let needsReview = false;
|
|
111201
|
+
const reviewNotes = [];
|
|
111202
|
+
let usePagination = false;
|
|
111203
|
+
let paginationPage = 1;
|
|
111204
|
+
let paginationPerPage = 0;
|
|
111205
|
+
const fieldsValue = extractSelectFields(select);
|
|
111206
|
+
if (fieldsValue !== null)
|
|
111207
|
+
opts.fields = import_jscodeshift.default.literal(fieldsValue);
|
|
111208
|
+
for (const step of tail) {
|
|
111209
|
+
switch (step.method) {
|
|
111210
|
+
case "eq": {
|
|
111211
|
+
const result = buildEqFilter(step);
|
|
111212
|
+
if (!result) {
|
|
111213
|
+
needsReview = true;
|
|
111214
|
+
reviewNotes.push(`.eq() arguments not in the (column-literal, value) shape — manual review.`);
|
|
111215
|
+
continue;
|
|
111216
|
+
}
|
|
111217
|
+
opts.filter = result.identifierValue ? combineFilterDynamic(opts.filter, result.expression) : combineFilterStatic(opts.filter, result.expression);
|
|
111218
|
+
if (result.identifierValue) {
|
|
111219
|
+
needsReview = true;
|
|
111220
|
+
reviewNotes.push(`.eq(${JSON.stringify(result.column)}, <identifier>) — verify the value is escape-safe for PB filter strings (no double-quotes / backslashes).`);
|
|
111221
|
+
}
|
|
111222
|
+
break;
|
|
111223
|
+
}
|
|
111224
|
+
case "order": {
|
|
111225
|
+
const sortFrag = buildSortFragment(step);
|
|
111226
|
+
if (!sortFrag) {
|
|
111227
|
+
needsReview = true;
|
|
111228
|
+
reviewNotes.push(`.order() arguments not in the recognised shape — manual review.`);
|
|
111229
|
+
continue;
|
|
111230
|
+
}
|
|
111231
|
+
opts.sort = import_jscodeshift.default.literal(opts.sort ? `${literalString(opts.sort)},${sortFrag}` : sortFrag);
|
|
111232
|
+
break;
|
|
111233
|
+
}
|
|
111234
|
+
case "limit": {
|
|
111235
|
+
const arg = step.args[0];
|
|
111236
|
+
const n = extractNumber(arg);
|
|
111237
|
+
if (n === null) {
|
|
111238
|
+
needsReview = true;
|
|
111239
|
+
reviewNotes.push(`.limit() argument isn't a literal number — manual review.`);
|
|
111240
|
+
continue;
|
|
111241
|
+
}
|
|
111242
|
+
if (n <= 0) {
|
|
111243
|
+
flags.push(makeFlag(path, "manual_review", "from.select.limit_invalid", `from(${JSON.stringify(table)}).select(...).limit(${n}) — non-positive limit doesn't translate to PB pagination. Manual review.`));
|
|
111244
|
+
return;
|
|
111245
|
+
}
|
|
111246
|
+
usePagination = true;
|
|
111247
|
+
paginationPerPage = Math.max(paginationPerPage, n);
|
|
111248
|
+
break;
|
|
111249
|
+
}
|
|
111250
|
+
case "range": {
|
|
111251
|
+
const startArg = step.args[0];
|
|
111252
|
+
const endArg = step.args[1];
|
|
111253
|
+
const startN = extractNumber(startArg);
|
|
111254
|
+
const endN = extractNumber(endArg);
|
|
111255
|
+
if (startN === null || endN === null) {
|
|
111256
|
+
needsReview = true;
|
|
111257
|
+
reviewNotes.push(`.range() arguments aren't literal numbers — manual review.`);
|
|
111258
|
+
continue;
|
|
111259
|
+
}
|
|
111260
|
+
const perPage = endN - startN + 1;
|
|
111261
|
+
if (perPage <= 0) {
|
|
111262
|
+
flags.push(makeFlag(path, "manual_review", "from.select.range_invalid", `from(${JSON.stringify(table)}).select(...).range(${startN}, ${endN}) — end < start; PB pagination has no equivalent. Manual review.`));
|
|
111263
|
+
return;
|
|
111264
|
+
}
|
|
111265
|
+
usePagination = true;
|
|
111266
|
+
paginationPerPage = Math.max(paginationPerPage, perPage);
|
|
111267
|
+
if (startN === 0) {
|
|
111268
|
+
paginationPage = 1;
|
|
111269
|
+
} else if (startN % perPage === 0) {
|
|
111270
|
+
paginationPage = Math.floor(startN / perPage) + 1;
|
|
111271
|
+
} else {
|
|
111272
|
+
needsReview = true;
|
|
111273
|
+
reviewNotes.push(`.range(${startN}, ${endN}) — start isn't on a page boundary; PB's page math doesn't translate cleanly. Manual review.`);
|
|
111274
|
+
}
|
|
111275
|
+
break;
|
|
111276
|
+
}
|
|
111277
|
+
case "single": {
|
|
111278
|
+
flags.push(makeFlag(path, "manual_review", "from.select.single", `from(${JSON.stringify(table)}).select(...).single() — PB's getFullList returns an array; getFirstListItem(filter) is the single-record equivalent but takes a filter string. Rewrite this chain by hand: pick getFirstListItem with the right filter OR [0]-index the array result.`));
|
|
111279
|
+
return;
|
|
111280
|
+
}
|
|
111281
|
+
default: {
|
|
111282
|
+
flags.push(makeFlag(path, "manual_review", `from.select.${step.method}`, `from(${JSON.stringify(table)}).select(...).${step.method}(...) has no chunk-2 rewrite — manual review.`));
|
|
111283
|
+
return;
|
|
111284
|
+
}
|
|
111285
|
+
}
|
|
111286
|
+
}
|
|
111287
|
+
let methodName;
|
|
111288
|
+
let methodArgs;
|
|
111289
|
+
if (usePagination) {
|
|
111290
|
+
methodName = "getList";
|
|
111291
|
+
const trailingOpts = buildOptionsObject(opts);
|
|
111292
|
+
methodArgs = [import_jscodeshift.default.literal(paginationPage), import_jscodeshift.default.literal(paginationPerPage)];
|
|
111293
|
+
if (trailingOpts)
|
|
111294
|
+
methodArgs.push(trailingOpts);
|
|
111295
|
+
} else {
|
|
111296
|
+
methodName = "getFullList";
|
|
111297
|
+
const optsObj = buildOptionsObject(opts);
|
|
111298
|
+
methodArgs = optsObj ? [optsObj] : [];
|
|
111299
|
+
}
|
|
111300
|
+
const replacement = import_jscodeshift.default.callExpression(import_jscodeshift.default.memberExpression(import_jscodeshift.default.callExpression(import_jscodeshift.default.memberExpression(import_jscodeshift.default.identifier(pbName), import_jscodeshift.default.identifier("collection")), [import_jscodeshift.default.literal(table)]), import_jscodeshift.default.identifier(methodName)), methodArgs);
|
|
111301
|
+
const kind = needsReview ? "manual_review" : "rewritten";
|
|
111302
|
+
const baseNote = `from(${JSON.stringify(table)}).select(...) → pb.collection(...).${methodName}(...)`;
|
|
111303
|
+
const note = needsReview ? `${baseNote} — ${reviewNotes.join(" · ")}` : baseNote;
|
|
111304
|
+
applyRewrite(path, replacement, kind, "from.select", note, flags, state);
|
|
111305
|
+
}
|
|
111306
|
+
function extractSelectFields(select) {
|
|
111307
|
+
const first = select.args[0];
|
|
111308
|
+
if (!first)
|
|
111309
|
+
return null;
|
|
111310
|
+
if ((first.type === "Literal" || first.type === "StringLiteral") && typeof first.value === "string") {
|
|
111311
|
+
const val = first.value;
|
|
111312
|
+
if (val === "*" || val.length === 0)
|
|
111313
|
+
return null;
|
|
111314
|
+
return val;
|
|
111315
|
+
}
|
|
111316
|
+
return null;
|
|
111317
|
+
}
|
|
111318
|
+
function buildEqFilter(step) {
|
|
111319
|
+
const colArg = step.args[0];
|
|
111320
|
+
const valArg = step.args[1];
|
|
111321
|
+
if (!colArg || !valArg)
|
|
111322
|
+
return null;
|
|
111323
|
+
if (!(colArg.type === "Literal" || colArg.type === "StringLiteral") || typeof colArg.value !== "string") {
|
|
111324
|
+
return null;
|
|
111325
|
+
}
|
|
111326
|
+
const column = colArg.value;
|
|
111327
|
+
if (valArg.type === "Literal" || valArg.type === "StringLiteral" || valArg.type === "NumericLiteral" || valArg.type === "BooleanLiteral") {
|
|
111328
|
+
const v = valArg.value;
|
|
111329
|
+
if (typeof v === "string") {
|
|
111330
|
+
return {
|
|
111331
|
+
expression: import_jscodeshift.default.literal(`${column} = "${escapeFilterString(v)}"`),
|
|
111332
|
+
column,
|
|
111333
|
+
identifierValue: false
|
|
111334
|
+
};
|
|
111335
|
+
}
|
|
111336
|
+
if (typeof v === "number" || typeof v === "boolean") {
|
|
111337
|
+
return {
|
|
111338
|
+
expression: import_jscodeshift.default.literal(`${column} = ${v}`),
|
|
111339
|
+
column,
|
|
111340
|
+
identifierValue: false
|
|
111341
|
+
};
|
|
111342
|
+
}
|
|
111343
|
+
return null;
|
|
111344
|
+
}
|
|
111345
|
+
const template = import_jscodeshift.default.templateLiteral([
|
|
111346
|
+
import_jscodeshift.default.templateElement({ raw: `${column} = "`, cooked: `${column} = "` }, false),
|
|
111347
|
+
import_jscodeshift.default.templateElement({ raw: `"`, cooked: `"` }, true)
|
|
111348
|
+
], [valArg]);
|
|
111349
|
+
return {
|
|
111350
|
+
expression: template,
|
|
111351
|
+
column,
|
|
111352
|
+
identifierValue: true
|
|
111353
|
+
};
|
|
111354
|
+
}
|
|
111355
|
+
function escapeFilterString(s) {
|
|
111356
|
+
return s.replace(/\\/g, "\\\\").replace(/"/g, "\\\"");
|
|
111357
|
+
}
|
|
111358
|
+
function combineFilterStatic(current, next) {
|
|
111359
|
+
if (!current)
|
|
111360
|
+
return next;
|
|
111361
|
+
const a = literalString(current);
|
|
111362
|
+
const b = literalString(next);
|
|
111363
|
+
if (a !== null && b !== null) {
|
|
111364
|
+
return import_jscodeshift.default.literal(`(${a}) && (${b})`);
|
|
111365
|
+
}
|
|
111366
|
+
return combineFilterDynamic(current, next);
|
|
111367
|
+
}
|
|
111368
|
+
function combineFilterDynamic(current, next) {
|
|
111369
|
+
if (!current)
|
|
111370
|
+
return next;
|
|
111371
|
+
return import_jscodeshift.default.templateLiteral([
|
|
111372
|
+
import_jscodeshift.default.templateElement({ raw: "(", cooked: "(" }, false),
|
|
111373
|
+
import_jscodeshift.default.templateElement({ raw: ") && (", cooked: ") && (" }, false),
|
|
111374
|
+
import_jscodeshift.default.templateElement({ raw: ")", cooked: ")" }, true)
|
|
111375
|
+
], [current, next]);
|
|
111376
|
+
}
|
|
111377
|
+
function literalString(node) {
|
|
111378
|
+
if (node?.type === "Literal" || node?.type === "StringLiteral") {
|
|
111379
|
+
const v = node.value;
|
|
111380
|
+
if (typeof v === "string")
|
|
111381
|
+
return v;
|
|
111382
|
+
}
|
|
111383
|
+
return null;
|
|
111384
|
+
}
|
|
111385
|
+
function buildSortFragment(step) {
|
|
111386
|
+
const colArg = step.args[0];
|
|
111387
|
+
if (!colArg)
|
|
111388
|
+
return null;
|
|
111389
|
+
if (!(colArg.type === "Literal" || colArg.type === "StringLiteral") || typeof colArg.value !== "string") {
|
|
111390
|
+
return null;
|
|
111391
|
+
}
|
|
111392
|
+
const column = colArg.value;
|
|
111393
|
+
let ascending = true;
|
|
111394
|
+
if (step.args.length >= 2) {
|
|
111395
|
+
const opts = step.args[1];
|
|
111396
|
+
if (opts?.type === "ObjectExpression") {
|
|
111397
|
+
const ascProp = findObjectProp(opts, "ascending");
|
|
111398
|
+
if (ascProp?.type === "Literal" || ascProp?.type === "BooleanLiteral") {
|
|
111399
|
+
ascending = ascProp.value === true;
|
|
111400
|
+
} else if (ascProp) {
|
|
111401
|
+
return null;
|
|
111402
|
+
}
|
|
111403
|
+
}
|
|
111404
|
+
}
|
|
111405
|
+
return ascending ? column : `-${column}`;
|
|
111406
|
+
}
|
|
111407
|
+
function extractNumber(node) {
|
|
111408
|
+
if (!node)
|
|
111409
|
+
return null;
|
|
111410
|
+
if (node.type === "Literal" || node.type === "NumericLiteral") {
|
|
111411
|
+
const v = node.value;
|
|
111412
|
+
if (typeof v === "number")
|
|
111413
|
+
return v;
|
|
111414
|
+
}
|
|
111415
|
+
if (node.type === "UnaryExpression") {
|
|
111416
|
+
const u = node;
|
|
111417
|
+
if (u.operator === "-") {
|
|
111418
|
+
const inner = extractNumber(u.argument);
|
|
111419
|
+
if (inner !== null)
|
|
111420
|
+
return -inner;
|
|
111421
|
+
}
|
|
111422
|
+
}
|
|
111423
|
+
return null;
|
|
111424
|
+
}
|
|
111425
|
+
function buildOptionsObject(opts) {
|
|
111426
|
+
const props = [];
|
|
111427
|
+
if (opts.fields !== undefined) {
|
|
111428
|
+
props.push(import_jscodeshift.default.property("init", import_jscodeshift.default.identifier("fields"), opts.fields));
|
|
111429
|
+
}
|
|
111430
|
+
if (opts.filter !== undefined) {
|
|
111431
|
+
props.push(import_jscodeshift.default.property("init", import_jscodeshift.default.identifier("filter"), opts.filter));
|
|
111432
|
+
}
|
|
111433
|
+
if (opts.sort !== undefined) {
|
|
111434
|
+
props.push(import_jscodeshift.default.property("init", import_jscodeshift.default.identifier("sort"), opts.sort));
|
|
111435
|
+
}
|
|
111436
|
+
if (props.length === 0)
|
|
111437
|
+
return null;
|
|
111438
|
+
return import_jscodeshift.default.objectExpression(props);
|
|
111439
|
+
}
|
|
111440
|
+
function rewriteInsert(path, pbName, table, insert, flags, state) {
|
|
111441
|
+
if (consumesDataErrorEnvelope(path)) {
|
|
111442
|
+
flagEnvelopeManualReview(path, "from.insert", flags);
|
|
111443
|
+
return;
|
|
111444
|
+
}
|
|
111445
|
+
const arg = insert.args[0];
|
|
111446
|
+
if (!arg) {
|
|
111447
|
+
flags.push(makeFlag(path, "manual_review", "from.insert", "from(...).insert() called with no args — manual review."));
|
|
111448
|
+
return;
|
|
111449
|
+
}
|
|
111450
|
+
if (arg.type === "ArrayExpression") {
|
|
111451
|
+
flags.push(makeFlag(path, "manual_review", "from.insert.array", `from(${JSON.stringify(table)}).insert([...]) — PB's create takes one record at a time; wrap in a loop.`));
|
|
111452
|
+
return;
|
|
111453
|
+
}
|
|
111454
|
+
const replacement = import_jscodeshift.default.callExpression(import_jscodeshift.default.memberExpression(import_jscodeshift.default.callExpression(import_jscodeshift.default.memberExpression(import_jscodeshift.default.identifier(pbName), import_jscodeshift.default.identifier("collection")), [import_jscodeshift.default.literal(table)]), import_jscodeshift.default.identifier("create")), [arg]);
|
|
111455
|
+
applyRewrite(path, replacement, "rewritten", "from.insert", `from(${JSON.stringify(table)}).insert({...}) → pb.collection(...).create({...})`, flags, state);
|
|
111456
|
+
}
|
|
111457
|
+
function rewriteUpdate(path, pbName, table, steps, flags, state) {
|
|
111458
|
+
if (consumesDataErrorEnvelope(path)) {
|
|
111459
|
+
flagEnvelopeManualReview(path, "from.update", flags);
|
|
111460
|
+
return;
|
|
111461
|
+
}
|
|
111462
|
+
const update = steps[0];
|
|
111463
|
+
const eq = steps[1];
|
|
111464
|
+
if (steps.length !== 2 || !update || !eq || eq.method !== "eq") {
|
|
111465
|
+
flags.push(makeFlag(path, "manual_review", "from.update.shape", `from(${JSON.stringify(table)}).update(...) chain isn't the .update({...}).eq("id", v) shape — manual review.`));
|
|
111466
|
+
return;
|
|
111467
|
+
}
|
|
111468
|
+
const eqCol = eq.args[0];
|
|
111469
|
+
const eqVal = eq.args[1];
|
|
111470
|
+
if (!eqCol || !eqVal) {
|
|
111471
|
+
flags.push(makeFlag(path, "manual_review", "from.update.eq", "update().eq(...) call missing args — manual review."));
|
|
111472
|
+
return;
|
|
111473
|
+
}
|
|
111474
|
+
if (!((eqCol.type === "Literal" || eqCol.type === "StringLiteral") && eqCol.value === "id")) {
|
|
111475
|
+
flags.push(makeFlag(path, "manual_review", "from.update.eq_non_id", `from(${JSON.stringify(table)}).update().eq() filters on a non-id column — PB's update needs an id; chunk 2 will handle filter-based updates.`));
|
|
111476
|
+
return;
|
|
111477
|
+
}
|
|
111478
|
+
const updateArg = update.args[0];
|
|
111479
|
+
if (!updateArg) {
|
|
111480
|
+
flags.push(makeFlag(path, "manual_review", "from.update", "update() called with no args — manual review."));
|
|
111481
|
+
return;
|
|
111482
|
+
}
|
|
111483
|
+
const replacement = import_jscodeshift.default.callExpression(import_jscodeshift.default.memberExpression(import_jscodeshift.default.callExpression(import_jscodeshift.default.memberExpression(import_jscodeshift.default.identifier(pbName), import_jscodeshift.default.identifier("collection")), [import_jscodeshift.default.literal(table)]), import_jscodeshift.default.identifier("update")), [eqVal, updateArg]);
|
|
111484
|
+
applyRewrite(path, replacement, "rewritten", "from.update", `from(${JSON.stringify(table)}).update({...}).eq("id", v) → pb.collection(...).update(v, {...})`, flags, state);
|
|
111485
|
+
}
|
|
111486
|
+
function rewriteDelete(path, pbName, table, steps, flags, state) {
|
|
111487
|
+
if (consumesDataErrorEnvelope(path)) {
|
|
111488
|
+
flagEnvelopeManualReview(path, "from.delete", flags);
|
|
111489
|
+
return;
|
|
111490
|
+
}
|
|
111491
|
+
const eq = steps[1];
|
|
111492
|
+
if (steps.length !== 2 || !eq || eq.method !== "eq") {
|
|
111493
|
+
flags.push(makeFlag(path, "manual_review", "from.delete.shape", `from(${JSON.stringify(table)}).delete() chain isn't the .delete().eq("id", v) shape — manual review.`));
|
|
111494
|
+
return;
|
|
111495
|
+
}
|
|
111496
|
+
const eqCol = eq.args[0];
|
|
111497
|
+
const eqVal = eq.args[1];
|
|
111498
|
+
if (!eqCol || !eqVal) {
|
|
111499
|
+
flags.push(makeFlag(path, "manual_review", "from.delete.eq", "delete().eq(...) call missing args — manual review."));
|
|
111500
|
+
return;
|
|
111501
|
+
}
|
|
111502
|
+
if (!((eqCol.type === "Literal" || eqCol.type === "StringLiteral") && eqCol.value === "id")) {
|
|
111503
|
+
flags.push(makeFlag(path, "manual_review", "from.delete.eq_non_id", `from(${JSON.stringify(table)}).delete().eq() filters on a non-id column — PB's delete needs an id; chunk 2 will handle filter-based deletes.`));
|
|
111504
|
+
return;
|
|
111505
|
+
}
|
|
111506
|
+
const replacement = import_jscodeshift.default.callExpression(import_jscodeshift.default.memberExpression(import_jscodeshift.default.callExpression(import_jscodeshift.default.memberExpression(import_jscodeshift.default.identifier(pbName), import_jscodeshift.default.identifier("collection")), [import_jscodeshift.default.literal(table)]), import_jscodeshift.default.identifier("delete")), [eqVal]);
|
|
111507
|
+
applyRewrite(path, replacement, "rewritten", "from.delete", `from(${JSON.stringify(table)}).delete().eq("id", v) → pb.collection(...).delete(v)`, flags, state);
|
|
111508
|
+
}
|
|
111509
|
+
function flagUnsupportedPatterns(root, supabaseNames, flags) {
|
|
111510
|
+
root.find(import_jscodeshift.default.CallExpression).forEach((path) => {
|
|
111511
|
+
flagOneUnsupported(path, supabaseNames, flags);
|
|
111512
|
+
});
|
|
111513
|
+
}
|
|
111514
|
+
function flagOneUnsupported(path, supabaseNames, flags) {
|
|
111515
|
+
const root = findChainRoot(path.node);
|
|
111516
|
+
if (!root)
|
|
111517
|
+
return;
|
|
111518
|
+
if (root.type !== "Identifier")
|
|
111519
|
+
return;
|
|
111520
|
+
if (!supabaseNames.has(root.name))
|
|
111521
|
+
return;
|
|
111522
|
+
const parent = path.parent?.node;
|
|
111523
|
+
if (parent?.type === "MemberExpression") {
|
|
111524
|
+
const m = parent;
|
|
111525
|
+
if (m.object === path.node)
|
|
111526
|
+
return;
|
|
111527
|
+
}
|
|
111528
|
+
const steps = readChainSteps(path.node);
|
|
111529
|
+
const first = steps[0];
|
|
111530
|
+
if (!first)
|
|
111531
|
+
return;
|
|
111532
|
+
if (first.method === "storage") {
|
|
111533
|
+
const opStep = findStorageOperation(steps);
|
|
111534
|
+
const opName = opStep?.method ?? "from";
|
|
111535
|
+
flags.push(makeFlag(path, "unsupported", `storage.${opName}`, `supabase.storage.<bucket>.${opName}(...) — PocketBase models files as record-attached fields, not standalone buckets. Rewrite by hand: create a collection with a \`file\` field, then use pb.collection(c).create({ file: blob }) for uploads and pb.files.getURL(record, record.file) for URLs.`));
|
|
111536
|
+
return;
|
|
111537
|
+
}
|
|
111538
|
+
if (first.method === "channel") {
|
|
111539
|
+
flags.push(makeFlag(path, "unsupported", "channel.subscribe", `supabase.channel(...).on(...).subscribe(...) — PB has realtime via pb.collection(c).subscribe("*", cb) but the event shape and filter model differ from Supabase channels. Rewrite by hand.`));
|
|
111540
|
+
return;
|
|
111541
|
+
}
|
|
111542
|
+
if (first.method === "rpc") {
|
|
111543
|
+
const fnName = first.call ? readFirstStringArg(first.call) ?? "<unknown>" : "<unknown>";
|
|
111544
|
+
flags.push(makeFlag(path, "unsupported", "rpc", `supabase.rpc(${JSON.stringify(fnName)}, ...) — Postgres function calls don't have a PocketBase equivalent. Move the logic into your app server code OR a PB hook (.pb_hooks/).`));
|
|
111545
|
+
return;
|
|
111546
|
+
}
|
|
111547
|
+
const invokeStep = steps[1];
|
|
111548
|
+
if (first.method === "functions" && invokeStep?.method === "invoke") {
|
|
111549
|
+
const fnName = invokeStep.call ? readFirstStringArg(invokeStep.call) ?? "<unknown>" : "<unknown>";
|
|
111550
|
+
flags.push(makeFlag(path, "unsupported", "functions.invoke", `supabase.functions.invoke(${JSON.stringify(fnName)}, ...) — Edge Functions don't run on Percher. Move the function code into the Percher app server, or host it on a separate platform (Deno Deploy, Cloudflare Workers) and call it via fetch.`));
|
|
111551
|
+
return;
|
|
111552
|
+
}
|
|
111553
|
+
}
|
|
111554
|
+
var STORAGE_OPERATIONS = new Set([
|
|
111555
|
+
"upload",
|
|
111556
|
+
"uploadToSignedUrl",
|
|
111557
|
+
"download",
|
|
111558
|
+
"list",
|
|
111559
|
+
"remove",
|
|
111560
|
+
"move",
|
|
111561
|
+
"copy",
|
|
111562
|
+
"createSignedUrl",
|
|
111563
|
+
"createSignedUrls",
|
|
111564
|
+
"createSignedUploadUrl",
|
|
111565
|
+
"getPublicUrl",
|
|
111566
|
+
"update",
|
|
111567
|
+
"exists",
|
|
111568
|
+
"info"
|
|
111569
|
+
]);
|
|
111570
|
+
var CHAIN_CONTINUATIONS = new Set(["then", "catch", "finally"]);
|
|
111571
|
+
function findStorageOperation(steps) {
|
|
111572
|
+
for (const step of steps) {
|
|
111573
|
+
if (STORAGE_OPERATIONS.has(step.method))
|
|
111574
|
+
return step;
|
|
111575
|
+
}
|
|
111576
|
+
for (let i = steps.length - 1;i >= 0; i--) {
|
|
111577
|
+
const step = steps[i];
|
|
111578
|
+
if (!step)
|
|
111579
|
+
continue;
|
|
111580
|
+
if (step.method === "storage" || step.method === "from")
|
|
111581
|
+
continue;
|
|
111582
|
+
if (CHAIN_CONTINUATIONS.has(step.method))
|
|
111583
|
+
continue;
|
|
111584
|
+
return step;
|
|
111585
|
+
}
|
|
111586
|
+
return null;
|
|
111587
|
+
}
|
|
111588
|
+
function findChainRoot(node) {
|
|
111589
|
+
let cursor = node;
|
|
111590
|
+
while (true) {
|
|
111591
|
+
if (cursor.type === "CallExpression") {
|
|
111592
|
+
const c = cursor;
|
|
111593
|
+
if (c.callee.type !== "MemberExpression")
|
|
111594
|
+
return null;
|
|
111595
|
+
cursor = c.callee.object;
|
|
111596
|
+
continue;
|
|
111597
|
+
}
|
|
111598
|
+
if (cursor.type === "MemberExpression") {
|
|
111599
|
+
cursor = cursor.object;
|
|
111600
|
+
continue;
|
|
111601
|
+
}
|
|
111602
|
+
return cursor;
|
|
111603
|
+
}
|
|
111604
|
+
}
|
|
111605
|
+
function readChainSteps(outer) {
|
|
111606
|
+
const reversed = [];
|
|
111607
|
+
let cursor = outer;
|
|
111608
|
+
while (true) {
|
|
111609
|
+
if (cursor.type === "CallExpression") {
|
|
111610
|
+
const call = cursor;
|
|
111611
|
+
if (call.callee.type !== "MemberExpression")
|
|
111612
|
+
break;
|
|
111613
|
+
const callee = call.callee;
|
|
111614
|
+
if (callee.property.type === "Identifier") {
|
|
111615
|
+
reversed.push({
|
|
111616
|
+
method: callee.property.name,
|
|
111617
|
+
call
|
|
111618
|
+
});
|
|
111619
|
+
}
|
|
111620
|
+
cursor = callee.object;
|
|
111621
|
+
continue;
|
|
111622
|
+
}
|
|
111623
|
+
if (cursor.type === "MemberExpression") {
|
|
111624
|
+
const m = cursor;
|
|
111625
|
+
if (m.property.type === "Identifier") {
|
|
111626
|
+
reversed.push({
|
|
111627
|
+
method: m.property.name,
|
|
111628
|
+
call: null
|
|
111629
|
+
});
|
|
111630
|
+
}
|
|
111631
|
+
cursor = m.object;
|
|
111632
|
+
continue;
|
|
111633
|
+
}
|
|
111634
|
+
break;
|
|
111635
|
+
}
|
|
111636
|
+
return reversed.reverse();
|
|
111637
|
+
}
|
|
111638
|
+
function readFirstStringArg(call) {
|
|
111639
|
+
const a = call.arguments[0];
|
|
111640
|
+
if (!a)
|
|
111641
|
+
return null;
|
|
111642
|
+
if (a.type === "Literal" || a.type === "StringLiteral") {
|
|
111643
|
+
const v = a.value;
|
|
111644
|
+
if (typeof v === "string")
|
|
111645
|
+
return v;
|
|
111646
|
+
}
|
|
111647
|
+
return null;
|
|
111648
|
+
}
|
|
111649
|
+
function findObjectProp(obj, name) {
|
|
111650
|
+
for (const prop of obj.properties) {
|
|
111651
|
+
if ((prop.type === "Property" || prop.type === "ObjectProperty") && "key" in prop && prop.key.type === "Identifier" && prop.key.name === name) {
|
|
111652
|
+
if (prop.shorthand === true) {
|
|
111653
|
+
return import_jscodeshift.default.identifier(name);
|
|
111654
|
+
}
|
|
111655
|
+
if ("value" in prop) {
|
|
111656
|
+
const val = prop.value;
|
|
111657
|
+
if (val.type !== "SpreadElement" && val.type !== "RestElement") {
|
|
111658
|
+
return val;
|
|
111659
|
+
}
|
|
111660
|
+
}
|
|
111661
|
+
}
|
|
111662
|
+
}
|
|
111663
|
+
return null;
|
|
111664
|
+
}
|
|
111665
|
+
function makeFlag(pathOrLoc, kind, pattern, note) {
|
|
111666
|
+
let line;
|
|
111667
|
+
let column;
|
|
111668
|
+
if ("node" in pathOrLoc) {
|
|
111669
|
+
const loc = pathOrLoc.node.loc;
|
|
111670
|
+
line = loc?.start.line;
|
|
111671
|
+
column = loc?.start.column;
|
|
111672
|
+
} else {
|
|
111673
|
+
line = pathOrLoc.line;
|
|
111674
|
+
column = pathOrLoc.column;
|
|
111675
|
+
}
|
|
111676
|
+
return { kind, pattern, line, column, note };
|
|
111677
|
+
}
|
|
111678
|
+
function captureLoc(path) {
|
|
111679
|
+
const loc = path.node.loc;
|
|
111680
|
+
return { line: loc?.start.line, column: loc?.start.column };
|
|
111681
|
+
}
|
|
111682
|
+
function applyRewrite(path, replacement, kind, pattern, note, flags, state) {
|
|
111683
|
+
const loc = captureLoc(path);
|
|
111684
|
+
path.replace(replacement);
|
|
111685
|
+
flags.push(makeFlag(loc, kind, pattern, note));
|
|
111686
|
+
state.mutated = true;
|
|
111687
|
+
}
|
|
111688
|
+
|
|
111689
|
+
// ../core/dist/commands/migrate-supabase-walker.js
|
|
111690
|
+
var REWRITABLE_EXTENSIONS = new Set([".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"]);
|
|
111691
|
+
var DEFAULT_EXCLUDES = [
|
|
111692
|
+
"node_modules",
|
|
111693
|
+
".git",
|
|
111694
|
+
"dist",
|
|
111695
|
+
"build",
|
|
111696
|
+
".next",
|
|
111697
|
+
".svelte-kit",
|
|
111698
|
+
".astro",
|
|
111699
|
+
".nuxt",
|
|
111700
|
+
".output",
|
|
111701
|
+
"coverage",
|
|
111702
|
+
".bun",
|
|
111703
|
+
".cache",
|
|
111704
|
+
".turbo",
|
|
111705
|
+
"migration-preview",
|
|
111706
|
+
"pb_data",
|
|
111707
|
+
"pb_migrations"
|
|
111708
|
+
];
|
|
111709
|
+
async function walkSupabaseProject(input) {
|
|
111710
|
+
const rootAbs = resolve6(input.rootDir);
|
|
111711
|
+
const ig = import_ignore3.default().add(DEFAULT_EXCLUDES);
|
|
111712
|
+
if (input.extraExcludes && input.extraExcludes.length > 0) {
|
|
111713
|
+
ig.add(input.extraExcludes);
|
|
111714
|
+
}
|
|
111715
|
+
const files = [];
|
|
111716
|
+
const skipped = [];
|
|
111717
|
+
for await (const absPath of walkTree(rootAbs, rootAbs, ig)) {
|
|
111718
|
+
const rel = relative3(rootAbs, absPath).split(sep3).join("/");
|
|
111719
|
+
const kind = classifyFile2(rel);
|
|
111720
|
+
if (kind === null)
|
|
111721
|
+
continue;
|
|
111722
|
+
let source;
|
|
111723
|
+
try {
|
|
111724
|
+
source = await readFile3(absPath, "utf8");
|
|
111725
|
+
} catch (err) {
|
|
111726
|
+
skipped.push({
|
|
111727
|
+
relativePath: rel,
|
|
111728
|
+
reason: "read_error",
|
|
111729
|
+
detail: err instanceof Error ? err.message : String(err)
|
|
111730
|
+
});
|
|
111731
|
+
continue;
|
|
111732
|
+
}
|
|
111733
|
+
if (looksBinary(source)) {
|
|
111734
|
+
skipped.push({ relativePath: rel, reason: "binary" });
|
|
111735
|
+
continue;
|
|
111736
|
+
}
|
|
111737
|
+
if (kind === "code") {
|
|
111738
|
+
const sdkResult = rewriteSupabaseSdk({
|
|
111739
|
+
source,
|
|
111740
|
+
filePath: rel,
|
|
111741
|
+
pbIdentifier: input.pbIdentifier,
|
|
111742
|
+
supabaseIdentifiers: input.supabaseIdentifiers
|
|
111743
|
+
});
|
|
111744
|
+
if (sdkResult.parseError !== undefined) {
|
|
111745
|
+
skipped.push({
|
|
111746
|
+
relativePath: rel,
|
|
111747
|
+
reason: "parse_error",
|
|
111748
|
+
detail: sdkResult.parseError
|
|
111749
|
+
});
|
|
111750
|
+
continue;
|
|
111751
|
+
}
|
|
111752
|
+
files.push({
|
|
111753
|
+
relativePath: rel,
|
|
111754
|
+
kind,
|
|
111755
|
+
source,
|
|
111756
|
+
rewritten: sdkResult.rewritten,
|
|
111757
|
+
changed: sdkResult.changed,
|
|
111758
|
+
flags: sdkResult.flags
|
|
111759
|
+
});
|
|
111760
|
+
continue;
|
|
111761
|
+
}
|
|
111762
|
+
const configResult = kind === "package_json" ? rewritePackageJson(source) : rewriteEnvFile(source);
|
|
111763
|
+
files.push({
|
|
111764
|
+
relativePath: rel,
|
|
111765
|
+
kind,
|
|
111766
|
+
source,
|
|
111767
|
+
rewritten: configResult.rewritten,
|
|
111768
|
+
changed: configResult.changed,
|
|
111769
|
+
flags: configResult.flags
|
|
111770
|
+
});
|
|
111771
|
+
}
|
|
111772
|
+
const flagsByPattern = {};
|
|
111773
|
+
let flagCount = 0;
|
|
111774
|
+
let changedFiles = 0;
|
|
111775
|
+
let packageJsonChanged = false;
|
|
111776
|
+
let envFilesChanged = 0;
|
|
111777
|
+
for (const file2 of files) {
|
|
111778
|
+
if (file2.changed) {
|
|
111779
|
+
changedFiles += 1;
|
|
111780
|
+
if (file2.kind === "package_json")
|
|
111781
|
+
packageJsonChanged = true;
|
|
111782
|
+
if (file2.kind === "env")
|
|
111783
|
+
envFilesChanged += 1;
|
|
111784
|
+
}
|
|
111785
|
+
for (const flag of file2.flags) {
|
|
111786
|
+
flagCount += 1;
|
|
111787
|
+
flagsByPattern[flag.pattern] = (flagsByPattern[flag.pattern] ?? 0) + 1;
|
|
111788
|
+
}
|
|
111789
|
+
}
|
|
111790
|
+
return {
|
|
111791
|
+
files,
|
|
111792
|
+
skipped,
|
|
111793
|
+
flagsByPattern,
|
|
111794
|
+
summary: {
|
|
111795
|
+
totalFiles: files.length,
|
|
111796
|
+
changedFiles,
|
|
111797
|
+
flagCount,
|
|
111798
|
+
packageJsonChanged,
|
|
111799
|
+
envFilesChanged
|
|
111800
|
+
}
|
|
111801
|
+
};
|
|
111802
|
+
}
|
|
111803
|
+
async function* walkTree(dir, rootAbs, ig) {
|
|
111804
|
+
let entries;
|
|
111805
|
+
try {
|
|
111806
|
+
entries = await readdir(dir);
|
|
111807
|
+
} catch {
|
|
111808
|
+
return;
|
|
111809
|
+
}
|
|
111810
|
+
for (const name of entries) {
|
|
111811
|
+
const abs = join17(dir, name);
|
|
111812
|
+
const rel = relative3(rootAbs, abs).split(sep3).join("/");
|
|
111813
|
+
if (rel.length > 0 && ig.ignores(rel))
|
|
111814
|
+
continue;
|
|
111815
|
+
let st;
|
|
111816
|
+
try {
|
|
111817
|
+
st = await stat(abs);
|
|
111818
|
+
} catch {
|
|
111819
|
+
continue;
|
|
111820
|
+
}
|
|
111821
|
+
if (st.isDirectory()) {
|
|
111822
|
+
if (ig.ignores(`${rel}/`))
|
|
111823
|
+
continue;
|
|
111824
|
+
yield* walkTree(abs, rootAbs, ig);
|
|
111825
|
+
continue;
|
|
111826
|
+
}
|
|
111827
|
+
if (st.isFile()) {
|
|
111828
|
+
yield abs;
|
|
111829
|
+
}
|
|
111830
|
+
}
|
|
111831
|
+
}
|
|
111832
|
+
function classifyFile2(relPath) {
|
|
111833
|
+
const base = relPath.split("/").pop() ?? relPath;
|
|
111834
|
+
if (base === "package.json")
|
|
111835
|
+
return "package_json";
|
|
111836
|
+
if (base === ".env" || base.startsWith(".env."))
|
|
111837
|
+
return "env";
|
|
111838
|
+
const dot = base.lastIndexOf(".");
|
|
111839
|
+
if (dot < 0)
|
|
111840
|
+
return null;
|
|
111841
|
+
const ext = base.slice(dot);
|
|
111842
|
+
if (REWRITABLE_EXTENSIONS.has(ext))
|
|
111843
|
+
return "code";
|
|
111844
|
+
return null;
|
|
111845
|
+
}
|
|
111846
|
+
function looksBinary(source) {
|
|
111847
|
+
const limit = Math.min(source.length, 8192);
|
|
111848
|
+
for (let i = 0;i < limit; i++) {
|
|
111849
|
+
if (source.charCodeAt(i) === 0)
|
|
111850
|
+
return true;
|
|
111851
|
+
}
|
|
111852
|
+
return false;
|
|
111853
|
+
}
|
|
111854
|
+
function rewritePackageJson(source) {
|
|
111855
|
+
let pkg;
|
|
111856
|
+
try {
|
|
111857
|
+
pkg = JSON.parse(source);
|
|
111858
|
+
} catch {
|
|
111859
|
+
return { rewritten: source, changed: false, flags: [] };
|
|
111860
|
+
}
|
|
111861
|
+
const depSections = [
|
|
111862
|
+
"dependencies",
|
|
111863
|
+
"devDependencies",
|
|
111864
|
+
"peerDependencies",
|
|
111865
|
+
"optionalDependencies"
|
|
111866
|
+
];
|
|
111867
|
+
let changed = false;
|
|
111868
|
+
const flags = [];
|
|
111869
|
+
for (const section of depSections) {
|
|
111870
|
+
const deps = pkg[section];
|
|
111871
|
+
if (!deps || typeof deps !== "object" || Array.isArray(deps))
|
|
111872
|
+
continue;
|
|
111873
|
+
const depsObj = deps;
|
|
111874
|
+
if ("@supabase/supabase-js" in depsObj) {
|
|
111875
|
+
delete depsObj["@supabase/supabase-js"];
|
|
111876
|
+
if (!("pocketbase" in depsObj)) {
|
|
111877
|
+
depsObj.pocketbase = "^0.21.0";
|
|
111878
|
+
}
|
|
111879
|
+
changed = true;
|
|
111880
|
+
flags.push({
|
|
111881
|
+
kind: "rewritten",
|
|
111882
|
+
pattern: `package.${section}`,
|
|
111883
|
+
note: `${section}: @supabase/supabase-js → pocketbase`
|
|
111884
|
+
});
|
|
111885
|
+
}
|
|
111886
|
+
}
|
|
111887
|
+
if (!changed) {
|
|
111888
|
+
return { rewritten: source, changed: false, flags: [] };
|
|
111889
|
+
}
|
|
111890
|
+
const rewritten = `${JSON.stringify(pkg, null, 2)}
|
|
111891
|
+
`;
|
|
111892
|
+
return { rewritten, changed: true, flags };
|
|
111893
|
+
}
|
|
111894
|
+
function rewriteEnvFile(source) {
|
|
111895
|
+
const lines = source.split(/\r?\n/);
|
|
111896
|
+
const flags = [];
|
|
111897
|
+
const seenPrefixes = new Set;
|
|
111898
|
+
const outLines = [];
|
|
111899
|
+
let changed = false;
|
|
111900
|
+
const HEADER_MARKER = "# === Percher migration: Supabase → PocketBase ===";
|
|
111901
|
+
if (source.includes(HEADER_MARKER)) {
|
|
111902
|
+
return { rewritten: source, changed: false, flags: [] };
|
|
111903
|
+
}
|
|
111904
|
+
for (const [i, line] of lines.entries()) {
|
|
111905
|
+
const match = line.match(/^\s*([A-Z][A-Z0-9_]*)\s*=(.*)$/);
|
|
111906
|
+
if (!match) {
|
|
111907
|
+
outLines.push(line);
|
|
111908
|
+
continue;
|
|
111909
|
+
}
|
|
111910
|
+
const key = match[1] ?? "";
|
|
111911
|
+
if (!key.includes("SUPABASE")) {
|
|
111912
|
+
outLines.push(line);
|
|
111913
|
+
continue;
|
|
111914
|
+
}
|
|
111915
|
+
const prefix = extractPublicPrefix(key);
|
|
111916
|
+
seenPrefixes.add(prefix);
|
|
111917
|
+
outLines.push(`# was: ${line.trim()}`);
|
|
111918
|
+
flags.push({
|
|
111919
|
+
kind: "rewritten",
|
|
111920
|
+
pattern: "env.supabase_key",
|
|
111921
|
+
line: i + 1,
|
|
111922
|
+
note: `${key} commented out — replace with the appropriate POCKETBASE_URL value.`
|
|
111923
|
+
});
|
|
111924
|
+
changed = true;
|
|
111925
|
+
}
|
|
111926
|
+
if (!changed) {
|
|
111927
|
+
return { rewritten: source, changed: false, flags: [] };
|
|
111928
|
+
}
|
|
111929
|
+
const stubLines = [];
|
|
111930
|
+
stubLines.push(HEADER_MARKER);
|
|
111931
|
+
stubLines.push("# Old SUPABASE_* keys are kept above as `# was:` comments.");
|
|
111932
|
+
stubLines.push("# Fill in the POCKETBASE_URL values below and remove the comments");
|
|
111933
|
+
stubLines.push("# once you've verified the migration.");
|
|
111934
|
+
for (const prefix of [...seenPrefixes].sort()) {
|
|
111935
|
+
const pbKey = prefix ? `${prefix}POCKETBASE_URL` : "POCKETBASE_URL";
|
|
111936
|
+
stubLines.push(`${pbKey}=`);
|
|
111937
|
+
}
|
|
111938
|
+
stubLines.push("");
|
|
111939
|
+
const rewritten = `${stubLines.join(`
|
|
111940
|
+
`)}
|
|
111941
|
+
${outLines.join(`
|
|
111942
|
+
`)}`;
|
|
111943
|
+
return { rewritten, changed: true, flags };
|
|
111944
|
+
}
|
|
111945
|
+
function extractPublicPrefix(key) {
|
|
111946
|
+
for (const p of [
|
|
111947
|
+
"NEXT_PUBLIC_",
|
|
111948
|
+
"VITE_",
|
|
111949
|
+
"NUXT_PUBLIC_",
|
|
111950
|
+
"PUBLIC_",
|
|
111951
|
+
"REACT_APP_",
|
|
111952
|
+
"VUE_APP_",
|
|
111953
|
+
"EXPO_PUBLIC_"
|
|
111954
|
+
]) {
|
|
111955
|
+
if (key.startsWith(p))
|
|
111956
|
+
return p;
|
|
111957
|
+
}
|
|
111958
|
+
return "";
|
|
111959
|
+
}
|
|
111960
|
+
async function writeWalkResult(input) {
|
|
111961
|
+
const targetAbs = resolve6(input.targetDir);
|
|
111962
|
+
const changedOnly = input.changedOnly !== false;
|
|
111963
|
+
const written = [];
|
|
111964
|
+
for (const file2 of input.result.files) {
|
|
111965
|
+
if (changedOnly && !file2.changed)
|
|
111966
|
+
continue;
|
|
111967
|
+
const abs = resolve6(targetAbs, file2.relativePath);
|
|
111968
|
+
const rel = relative3(targetAbs, abs);
|
|
111969
|
+
const isParentTraversal = rel === ".." || rel.startsWith(`..${sep3}`) || rel.startsWith("../");
|
|
111970
|
+
if (rel.length === 0 || isParentTraversal || isAbsolute2(rel)) {
|
|
111971
|
+
throw new Error(`writeWalkResult refused path-traversing file "${file2.relativePath}" → "${abs}" (would escape targetDir "${targetAbs}").`);
|
|
111972
|
+
}
|
|
111973
|
+
await mkdir2(dirname4(abs), { recursive: true });
|
|
111974
|
+
await writeFile2(abs, file2.rewritten, "utf8");
|
|
111975
|
+
written.push(file2.relativePath);
|
|
111976
|
+
}
|
|
111977
|
+
return { written };
|
|
111978
|
+
}
|
|
111979
|
+
|
|
111980
|
+
// ../core/dist/commands/migrate-supabase-rewrite.js
|
|
111981
|
+
var rewriteSupabaseClientInputSchema = exports_external.object({
|
|
111982
|
+
rootDir: exports_external.string().optional().describe("Project root to rewrite. Relative paths resolve against ctx.cwd. Defaults to ctx.cwd."),
|
|
111983
|
+
outputDir: exports_external.string().optional().describe("Where preview-mode writes the rewritten tree. Ignored when `apply` is true. Defaults to `<root>/migration-preview/rewritten`."),
|
|
111984
|
+
apply: exports_external.boolean().optional().describe("When true, overwrite changed files in place (originals backed up to `migration-preview/backup/` first). Default false = preview only."),
|
|
111985
|
+
pbIdentifier: exports_external.string().optional().describe("PocketBase client variable name to emit. Defaults to `pb`."),
|
|
111986
|
+
supabaseIdentifiers: exports_external.array(exports_external.string()).optional().describe("Extra Supabase client variable names to recognise (beyond `supabase`).")
|
|
111987
|
+
});
|
|
111988
|
+
async function rewriteSupabaseClient(ctx, input) {
|
|
111989
|
+
const rootAbs = input.rootDir ? resolve7(ctx.cwd, input.rootDir) : resolve7(ctx.cwd);
|
|
111990
|
+
const apply = input.apply === true;
|
|
111991
|
+
const mode = apply ? "apply" : "preview";
|
|
111992
|
+
ctx.status(`Scanning ${relative4(ctx.cwd, rootAbs) || "."} for Supabase SDK calls...`);
|
|
111993
|
+
let walk2;
|
|
111994
|
+
try {
|
|
111995
|
+
walk2 = await walkSupabaseProject({
|
|
111996
|
+
rootDir: rootAbs,
|
|
111997
|
+
pbIdentifier: input.pbIdentifier,
|
|
111998
|
+
supabaseIdentifiers: input.supabaseIdentifiers
|
|
111999
|
+
});
|
|
112000
|
+
} catch (err) {
|
|
112001
|
+
return failure2(rootAbs, mode, err, `Couldn't scan ${relative4(ctx.cwd, rootAbs) || "."}: check the path exists and is readable, then re-run.`);
|
|
112002
|
+
}
|
|
112003
|
+
const changed = walk2.files.filter((f) => f.changed);
|
|
112004
|
+
const flags = collectManualFlags(walk2);
|
|
112005
|
+
if (changed.length === 0 && flags.length === 0 && walk2.skipped.length === 0) {
|
|
112006
|
+
return {
|
|
112007
|
+
status: "ok",
|
|
112008
|
+
mode,
|
|
112009
|
+
rootDir: rootAbs,
|
|
112010
|
+
writtenTo: rootAbs,
|
|
112011
|
+
backupDir: null,
|
|
112012
|
+
filesWritten: [],
|
|
112013
|
+
changedCount: 0,
|
|
112014
|
+
flags: [],
|
|
112015
|
+
flagsByPattern: {},
|
|
112016
|
+
packageJsonChanged: false,
|
|
112017
|
+
envFilesChanged: 0,
|
|
112018
|
+
skipped: [],
|
|
112019
|
+
recovery: recoveryNone(),
|
|
112020
|
+
summary: "No Supabase SDK calls found to rewrite. If you intend to keep Supabase as your backend, " + 'set `[data] mode = "supabase"` in percher.toml instead.'
|
|
112021
|
+
};
|
|
112022
|
+
}
|
|
112023
|
+
const reportNeeded = flags.length > 0 || walk2.skipped.length > 0;
|
|
112024
|
+
const backupDir = apply && changed.length > 0 ? join18(rootAbs, "migration-preview", "backup") : null;
|
|
112025
|
+
const rewriteTarget = apply ? rootAbs : input.outputDir ? resolve7(ctx.cwd, input.outputDir) : join18(rootAbs, "migration-preview", "rewritten");
|
|
112026
|
+
const filesWritten = [];
|
|
112027
|
+
try {
|
|
112028
|
+
if (changed.length > 0) {
|
|
112029
|
+
if (apply && backupDir) {
|
|
112030
|
+
ctx.status(`Backing up ${changed.length} original file(s) to migration-preview/backup/...`);
|
|
112031
|
+
await writeWalkResult({
|
|
112032
|
+
result: { ...walk2, files: changed.map((f) => ({ ...f, rewritten: f.source })) },
|
|
112033
|
+
targetDir: backupDir,
|
|
112034
|
+
changedOnly: false
|
|
112035
|
+
});
|
|
112036
|
+
}
|
|
112037
|
+
ctx.status(apply ? `Rewriting ${changed.length} file(s) in place...` : `Writing ${changed.length} rewritten file(s) to migration-preview/rewritten/...`);
|
|
112038
|
+
const out = await writeWalkResult({
|
|
112039
|
+
result: walk2,
|
|
112040
|
+
targetDir: rewriteTarget,
|
|
112041
|
+
changedOnly: true
|
|
112042
|
+
});
|
|
112043
|
+
for (const rel of out.written) {
|
|
112044
|
+
filesWritten.push(relative4(ctx.cwd, join18(rewriteTarget, rel)));
|
|
112045
|
+
}
|
|
112046
|
+
}
|
|
112047
|
+
if (reportNeeded) {
|
|
112048
|
+
const reportAbs = join18(rootAbs, "migration-preview", "REWRITE_NOTES.md");
|
|
112049
|
+
await mkdir3(dirname5(reportAbs), { recursive: true });
|
|
112050
|
+
await writeFile3(reportAbs, renderReport({ flags, skipped: walk2.skipped }), "utf8");
|
|
112051
|
+
filesWritten.push(relative4(ctx.cwd, reportAbs));
|
|
112052
|
+
}
|
|
112053
|
+
} catch (err) {
|
|
112054
|
+
return failure2(rootAbs, mode, err, apply ? "The in-place rewrite failed partway. Your originals are safe — restore from `migration-preview/backup/` and re-run." : "Couldn't write the rewritten preview. Check the output directory is writeable and re-run.");
|
|
112055
|
+
}
|
|
112056
|
+
const flagsByPattern = {};
|
|
112057
|
+
for (const flag of flags) {
|
|
112058
|
+
flagsByPattern[flag.pattern] = (flagsByPattern[flag.pattern] ?? 0) + 1;
|
|
112059
|
+
}
|
|
112060
|
+
const recovery = reportNeeded ? recoveryAsk({ prompt: buildPrompt(flags, walk2.skipped.length), reasonCode: "unknown" }) : recoveryNone();
|
|
112061
|
+
return {
|
|
112062
|
+
status: "ok",
|
|
112063
|
+
mode,
|
|
112064
|
+
rootDir: rootAbs,
|
|
112065
|
+
writtenTo: changed.length > 0 ? rewriteTarget : rootAbs,
|
|
112066
|
+
backupDir,
|
|
112067
|
+
filesWritten,
|
|
112068
|
+
changedCount: changed.length,
|
|
112069
|
+
flags,
|
|
112070
|
+
flagsByPattern,
|
|
112071
|
+
packageJsonChanged: walk2.summary.packageJsonChanged,
|
|
112072
|
+
envFilesChanged: walk2.summary.envFilesChanged,
|
|
112073
|
+
skipped: walk2.skipped,
|
|
112074
|
+
recovery,
|
|
112075
|
+
summary: renderSummary2({ mode, changed: changed.length, walk: walk2, flagCount: flags.length })
|
|
112076
|
+
};
|
|
112077
|
+
}
|
|
112078
|
+
function collectManualFlags(walk2) {
|
|
112079
|
+
const out = [];
|
|
112080
|
+
for (const file2 of walk2.files) {
|
|
112081
|
+
for (const flag of file2.flags) {
|
|
112082
|
+
if (flag.kind === "manual_review" || flag.kind === "unsupported") {
|
|
112083
|
+
out.push({
|
|
112084
|
+
file: file2.relativePath,
|
|
112085
|
+
line: flag.line,
|
|
112086
|
+
column: flag.column,
|
|
112087
|
+
kind: flag.kind,
|
|
112088
|
+
pattern: flag.pattern,
|
|
112089
|
+
note: flag.note
|
|
112090
|
+
});
|
|
112091
|
+
}
|
|
112092
|
+
}
|
|
112093
|
+
}
|
|
112094
|
+
return out;
|
|
112095
|
+
}
|
|
112096
|
+
function buildPrompt(flags, skippedCount) {
|
|
112097
|
+
const rewritten = flags.filter((f) => f.kind === "manual_review").length;
|
|
112098
|
+
const untouched = flags.filter((f) => f.kind === "unsupported").length;
|
|
112099
|
+
const bits = [];
|
|
112100
|
+
if (rewritten > 0) {
|
|
112101
|
+
bits.push(`${rewritten} rewritten to PocketBase but with semantics that differ from Supabase`);
|
|
112102
|
+
}
|
|
112103
|
+
if (untouched > 0) {
|
|
112104
|
+
bits.push(`${untouched} left untouched (no PocketBase equivalent — storage, realtime, rpc, or edge functions)`);
|
|
112105
|
+
}
|
|
112106
|
+
if (skippedCount > 0) {
|
|
112107
|
+
bits.push(`${skippedCount} file${skippedCount === 1 ? "" : "s"} couldn't be parsed`);
|
|
112108
|
+
}
|
|
112109
|
+
return `Some locations need review: ${bits.join("; ")}. Each is listed with its file and line in migration-preview/REWRITE_NOTES.md — check them before deploying.`;
|
|
112110
|
+
}
|
|
112111
|
+
function renderSummary2(args) {
|
|
112112
|
+
const parts = [];
|
|
112113
|
+
if (args.changed > 0) {
|
|
112114
|
+
const where = args.mode === "apply" ? "rewritten in place (originals in `migration-preview/backup/`)" : "written to `migration-preview/rewritten/` for review";
|
|
112115
|
+
parts.push(`${args.changed} file${args.changed === 1 ? "" : "s"} ${where}.`);
|
|
112116
|
+
} else {
|
|
112117
|
+
parts.push("No files needed automatic rewriting.");
|
|
112118
|
+
}
|
|
112119
|
+
if (args.walk.summary.packageJsonChanged) {
|
|
112120
|
+
parts.push("`package.json` swapped `@supabase/supabase-js` → `pocketbase`.");
|
|
112121
|
+
}
|
|
112122
|
+
if (args.walk.summary.envFilesChanged > 0) {
|
|
112123
|
+
parts.push(`${args.walk.summary.envFilesChanged} .env file${args.walk.summary.envFilesChanged === 1 ? "" : "s"} stubbed with PocketBase keys.`);
|
|
112124
|
+
}
|
|
112125
|
+
if (args.flagCount > 0) {
|
|
112126
|
+
parts.push(`${args.flagCount} pattern${args.flagCount === 1 ? "" : "s"} need manual review — see \`migration-preview/REWRITE_NOTES.md\`.`);
|
|
112127
|
+
}
|
|
112128
|
+
if (args.walk.skipped.length > 0) {
|
|
112129
|
+
parts.push(`${args.walk.skipped.length} file${args.walk.skipped.length === 1 ? "" : "s"} couldn't be parsed (listed in \`REWRITE_NOTES.md\`).`);
|
|
112130
|
+
}
|
|
112131
|
+
return parts.join(" ");
|
|
112132
|
+
}
|
|
112133
|
+
function renderReport(args) {
|
|
112134
|
+
const lines = [];
|
|
112135
|
+
lines.push("# Supabase → PocketBase — review notes");
|
|
112136
|
+
lines.push("");
|
|
112137
|
+
lines.push("The automated rewrite handled auth and CRUD. The locations below still need a human: some were rewritten but their semantics differ from Supabase, others have no PocketBase equivalent and were left untouched. There are no inline markers — work from this list.");
|
|
112138
|
+
lines.push("");
|
|
112139
|
+
const rewritten = args.flags.filter((f) => f.kind === "manual_review");
|
|
112140
|
+
const untouched = args.flags.filter((f) => f.kind === "unsupported");
|
|
112141
|
+
if (rewritten.length > 0) {
|
|
112142
|
+
lines.push("## Rewritten — verify the semantics");
|
|
112143
|
+
lines.push("");
|
|
112144
|
+
lines.push("These calls were converted to PocketBase, but the behaviour isn't identical (event shapes, filter escaping, pagination). Confirm each still does what you intend.");
|
|
112145
|
+
lines.push("");
|
|
112146
|
+
appendFlagsByFile(lines, rewritten);
|
|
112147
|
+
}
|
|
112148
|
+
if (untouched.length > 0) {
|
|
112149
|
+
lines.push("## Left untouched — rewrite by hand");
|
|
112150
|
+
lines.push("");
|
|
112151
|
+
lines.push("These have no clean PocketBase equivalent, so they remain as Supabase calls in your source. Replace each one yourself.");
|
|
112152
|
+
lines.push("");
|
|
112153
|
+
appendFlagsByFile(lines, untouched);
|
|
112154
|
+
}
|
|
112155
|
+
if (args.skipped.length > 0) {
|
|
112156
|
+
lines.push("## Files that couldn't be parsed");
|
|
112157
|
+
lines.push("");
|
|
112158
|
+
lines.push("These were skipped — review them manually for Supabase calls the rewriter missed.");
|
|
112159
|
+
lines.push("");
|
|
112160
|
+
for (const file2 of args.skipped) {
|
|
112161
|
+
lines.push(`- \`${file2.relativePath}\` — ${file2.reason}${file2.detail ? `: ${file2.detail}` : ""}`);
|
|
112162
|
+
}
|
|
112163
|
+
lines.push("");
|
|
112164
|
+
}
|
|
112165
|
+
return lines.join(`
|
|
112166
|
+
`);
|
|
112167
|
+
}
|
|
112168
|
+
function appendFlagsByFile(lines, flags) {
|
|
112169
|
+
const byFile = new Map;
|
|
112170
|
+
for (const flag of flags) {
|
|
112171
|
+
const list = byFile.get(flag.file) ?? [];
|
|
112172
|
+
list.push(flag);
|
|
112173
|
+
byFile.set(flag.file, list);
|
|
112174
|
+
}
|
|
112175
|
+
for (const [file2, fileFlags] of byFile) {
|
|
112176
|
+
lines.push(`### \`${file2}\``);
|
|
112177
|
+
lines.push("");
|
|
112178
|
+
for (const flag of fileFlags) {
|
|
112179
|
+
const loc = flag.line !== undefined ? `L${flag.line}${flag.column !== undefined ? `:${flag.column}` : ""}` : "(location unknown)";
|
|
112180
|
+
lines.push(`- ${loc} \`${flag.pattern}\` — ${flag.note}`);
|
|
112181
|
+
}
|
|
112182
|
+
lines.push("");
|
|
112183
|
+
}
|
|
112184
|
+
}
|
|
112185
|
+
function failure2(rootAbs, mode, err, suggestion) {
|
|
112186
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
112187
|
+
return {
|
|
112188
|
+
status: "failed",
|
|
112189
|
+
mode,
|
|
112190
|
+
rootDir: rootAbs,
|
|
112191
|
+
writtenTo: rootAbs,
|
|
112192
|
+
backupDir: null,
|
|
112193
|
+
filesWritten: [],
|
|
112194
|
+
changedCount: 0,
|
|
112195
|
+
flags: [],
|
|
112196
|
+
flagsByPattern: {},
|
|
112197
|
+
packageJsonChanged: false,
|
|
112198
|
+
envFilesChanged: 0,
|
|
112199
|
+
skipped: [],
|
|
112200
|
+
recovery: recoveryAsk({
|
|
112201
|
+
prompt: `${suggestion} (${message})`,
|
|
112202
|
+
reasonCode: "unknown",
|
|
112203
|
+
retryable: true
|
|
112204
|
+
}),
|
|
112205
|
+
summary: `Client rewrite failed: ${message}`,
|
|
112206
|
+
error: {
|
|
112207
|
+
code: "rewrite_failed",
|
|
112208
|
+
title: "Supabase client rewrite failed",
|
|
112209
|
+
explanation: message,
|
|
112210
|
+
suggestion
|
|
112211
|
+
}
|
|
112212
|
+
};
|
|
112213
|
+
}
|
|
110869
112214
|
// ../core/dist/commands/migrate-supabase-schema.js
|
|
110870
|
-
import { mkdir as
|
|
110871
|
-
import { isAbsolute as
|
|
112215
|
+
import { mkdir as mkdir4, writeFile as writeFile4 } from "node:fs/promises";
|
|
112216
|
+
import { isAbsolute as isAbsolute3, join as join19, relative as relative5, resolve as resolve8 } from "node:path";
|
|
110872
112217
|
|
|
110873
112218
|
// ../core/dist/commands/migrate-supabase-scripts.js
|
|
110874
112219
|
function generateDataImportScripts(preview) {
|
|
@@ -111316,7 +112661,7 @@ var migrateSupabaseSchemaInputSchema = exports_external.object({
|
|
|
111316
112661
|
});
|
|
111317
112662
|
async function migrateSupabaseSchema(ctx, input) {
|
|
111318
112663
|
const outputDirAbs = resolveOutputDir(ctx.cwd, input.outputDir);
|
|
111319
|
-
const outputDirRel =
|
|
112664
|
+
const outputDirRel = relative5(ctx.cwd, outputDirAbs) || ".";
|
|
111320
112665
|
ctx.status(`Inspecting Supabase project ${input.projectRef}...`);
|
|
111321
112666
|
const inspection = await inspectSupabase(ctx, {
|
|
111322
112667
|
projectRef: input.projectRef,
|
|
@@ -111362,7 +112707,7 @@ async function migrateSupabaseSchema(ctx, input) {
|
|
|
111362
112707
|
}
|
|
111363
112708
|
};
|
|
111364
112709
|
}
|
|
111365
|
-
const summary =
|
|
112710
|
+
const summary = renderSummary3({
|
|
111366
112711
|
projectRef: input.projectRef,
|
|
111367
112712
|
outputDirRel,
|
|
111368
112713
|
inspection,
|
|
@@ -111380,28 +112725,28 @@ async function migrateSupabaseSchema(ctx, input) {
|
|
|
111380
112725
|
}
|
|
111381
112726
|
function resolveOutputDir(cwd, requested) {
|
|
111382
112727
|
const raw = requested ?? "./migration-preview";
|
|
111383
|
-
return
|
|
112728
|
+
return isAbsolute3(raw) ? raw : resolve8(cwd, raw);
|
|
111384
112729
|
}
|
|
111385
112730
|
async function writePreviewFiles(ctx, outputDir, preview) {
|
|
111386
|
-
await
|
|
112731
|
+
await mkdir4(outputDir, { recursive: true });
|
|
111387
112732
|
const schemaJson = renderSchemaJson(preview);
|
|
111388
112733
|
const notesMd = renderMigrationNotes(preview);
|
|
111389
112734
|
const scripts = generateDataImportScripts(preview);
|
|
111390
|
-
const schemaPath =
|
|
111391
|
-
const notesPath =
|
|
111392
|
-
const migratePath =
|
|
111393
|
-
const storagePath =
|
|
111394
|
-
await
|
|
111395
|
-
await
|
|
111396
|
-
await
|
|
112735
|
+
const schemaPath = join19(outputDir, "pb_schema.json");
|
|
112736
|
+
const notesPath = join19(outputDir, "MIGRATION_NOTES.md");
|
|
112737
|
+
const migratePath = join19(outputDir, "pb_migrate.js");
|
|
112738
|
+
const storagePath = join19(outputDir, "pb_migrate_storage.js");
|
|
112739
|
+
await writeFile4(schemaPath, schemaJson, "utf8");
|
|
112740
|
+
await writeFile4(notesPath, notesMd, "utf8");
|
|
112741
|
+
await writeFile4(migratePath, scripts.pbMigrate, "utf8");
|
|
111397
112742
|
const written = [
|
|
111398
|
-
|
|
111399
|
-
|
|
111400
|
-
|
|
112743
|
+
relative5(ctx.cwd, schemaPath),
|
|
112744
|
+
relative5(ctx.cwd, notesPath),
|
|
112745
|
+
relative5(ctx.cwd, migratePath)
|
|
111401
112746
|
];
|
|
111402
112747
|
if (scripts.pbMigrateStorage !== null) {
|
|
111403
|
-
await
|
|
111404
|
-
written.push(
|
|
112748
|
+
await writeFile4(storagePath, scripts.pbMigrateStorage, "utf8");
|
|
112749
|
+
written.push(relative5(ctx.cwd, storagePath));
|
|
111405
112750
|
}
|
|
111406
112751
|
return written;
|
|
111407
112752
|
}
|
|
@@ -111550,7 +112895,7 @@ function renderMigrationNotes(preview) {
|
|
|
111550
112895
|
return lines.join(`
|
|
111551
112896
|
`);
|
|
111552
112897
|
}
|
|
111553
|
-
function
|
|
112898
|
+
function renderSummary3(args) {
|
|
111554
112899
|
const counts = args.preview.summary;
|
|
111555
112900
|
return [
|
|
111556
112901
|
`Wrote migration preview for Supabase project ${args.projectRef} to ${args.outputDirRel}/.`,
|
|
@@ -111560,29 +112905,6 @@ function renderSummary2(args) {
|
|
|
111560
112905
|
"Review `MIGRATION_NOTES.md` before running the import step."
|
|
111561
112906
|
].join(" ");
|
|
111562
112907
|
}
|
|
111563
|
-
// ../core/dist/commands/migrate-supabase-sdk.js
|
|
111564
|
-
var import_jscodeshift = __toESM(require_core3(), 1);
|
|
111565
|
-
var tsx = import_jscodeshift.default.withParser("tsx");
|
|
111566
|
-
var STORAGE_OPERATIONS = new Set([
|
|
111567
|
-
"upload",
|
|
111568
|
-
"uploadToSignedUrl",
|
|
111569
|
-
"download",
|
|
111570
|
-
"list",
|
|
111571
|
-
"remove",
|
|
111572
|
-
"move",
|
|
111573
|
-
"copy",
|
|
111574
|
-
"createSignedUrl",
|
|
111575
|
-
"createSignedUrls",
|
|
111576
|
-
"createSignedUploadUrl",
|
|
111577
|
-
"getPublicUrl",
|
|
111578
|
-
"update",
|
|
111579
|
-
"exists",
|
|
111580
|
-
"info"
|
|
111581
|
-
]);
|
|
111582
|
-
var CHAIN_CONTINUATIONS = new Set(["then", "catch", "finally"]);
|
|
111583
|
-
// ../core/dist/commands/migrate-supabase-walker.js
|
|
111584
|
-
var import_ignore3 = __toESM(require_ignore(), 1);
|
|
111585
|
-
var REWRITABLE_EXTENSIONS = new Set([".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"]);
|
|
111586
112908
|
// ../core/dist/commands/open.js
|
|
111587
112909
|
var openInputSchema = exports_external.object({
|
|
111588
112910
|
app: exports_external.string().optional()
|
|
@@ -111640,7 +112962,7 @@ async function previewBranchShow(ctx, input = {}) {
|
|
|
111640
112962
|
}
|
|
111641
112963
|
// ../core/dist/commands/publish.js
|
|
111642
112964
|
import { existsSync as existsSync14 } from "node:fs";
|
|
111643
|
-
import { join as
|
|
112965
|
+
import { join as join22 } from "node:path";
|
|
111644
112966
|
|
|
111645
112967
|
// ../shared/dist/pow.js
|
|
111646
112968
|
import { createHash as createHash3 } from "node:crypto";
|
|
@@ -112017,7 +113339,7 @@ async function runDeployWithRetry(opts) {
|
|
|
112017
113339
|
|
|
112018
113340
|
// ../core/dist/commands/env-scan.js
|
|
112019
113341
|
import { readdirSync as readdirSync3, readFileSync as readFileSync10, statSync as statSync3 } from "node:fs";
|
|
112020
|
-
import { join as
|
|
113342
|
+
import { join as join20 } from "node:path";
|
|
112021
113343
|
var SOURCE_EXTS = new Set([
|
|
112022
113344
|
".ts",
|
|
112023
113345
|
".tsx",
|
|
@@ -112054,7 +113376,7 @@ function scanForMissingBuildEnvRefs(cwd, existingKeys) {
|
|
|
112054
113376
|
continue;
|
|
112055
113377
|
let content;
|
|
112056
113378
|
try {
|
|
112057
|
-
content = readFileSync10(
|
|
113379
|
+
content = readFileSync10(join20(cwd, rel), "utf8");
|
|
112058
113380
|
} catch {
|
|
112059
113381
|
continue;
|
|
112060
113382
|
}
|
|
@@ -112086,7 +113408,7 @@ function* walkDir(dir, base) {
|
|
|
112086
113408
|
for (const name of entries) {
|
|
112087
113409
|
if (SKIP_DIRS.has(name))
|
|
112088
113410
|
continue;
|
|
112089
|
-
const full =
|
|
113411
|
+
const full = join20(dir, name);
|
|
112090
113412
|
let st;
|
|
112091
113413
|
try {
|
|
112092
113414
|
st = statSync3(full);
|
|
@@ -112447,9 +113769,9 @@ function titleFromCode(code) {
|
|
|
112447
113769
|
|
|
112448
113770
|
// ../core/dist/commands/publish-node.js
|
|
112449
113771
|
import { existsSync as existsSync13, readFileSync as readFileSync11 } from "node:fs";
|
|
112450
|
-
import { join as
|
|
113772
|
+
import { join as join21 } from "node:path";
|
|
112451
113773
|
function resolveNodeVersion(cwd) {
|
|
112452
|
-
const nvmrc =
|
|
113774
|
+
const nvmrc = join21(cwd, ".nvmrc");
|
|
112453
113775
|
if (existsSync13(nvmrc)) {
|
|
112454
113776
|
try {
|
|
112455
113777
|
const v = readFileSync11(nvmrc, "utf8").trim();
|
|
@@ -112457,7 +113779,7 @@ function resolveNodeVersion(cwd) {
|
|
|
112457
113779
|
return { value: v, source: ".nvmrc" };
|
|
112458
113780
|
} catch {}
|
|
112459
113781
|
}
|
|
112460
|
-
const pkgPath =
|
|
113782
|
+
const pkgPath = join21(cwd, "package.json");
|
|
112461
113783
|
if (!existsSync13(pkgPath))
|
|
112462
113784
|
return null;
|
|
112463
113785
|
try {
|
|
@@ -112483,7 +113805,7 @@ var publishInputSchema = exports_external.object({
|
|
|
112483
113805
|
var SUSPICIOUS_BYTES = 50 * 1024 * 1024;
|
|
112484
113806
|
var SUSPICIOUS_FILES = 1e4;
|
|
112485
113807
|
function tomlPathFor2(cwd) {
|
|
112486
|
-
return
|
|
113808
|
+
return join22(cwd, "percher.toml");
|
|
112487
113809
|
}
|
|
112488
113810
|
async function buildReplacedResult2(opts) {
|
|
112489
113811
|
const { ctx, app, deployment, tarball, cwd } = opts;
|
|
@@ -113103,13 +114425,11 @@ async function handleAnonymousPublish(ctx, config2, input, tarball) {
|
|
|
113103
114425
|
bundle: { fileCount: tarball.fileCount, bytes: tarball.bytes }
|
|
113104
114426
|
};
|
|
113105
114427
|
}
|
|
113106
|
-
saveConfig({
|
|
113107
|
-
|
|
113108
|
-
|
|
113109
|
-
|
|
113110
|
-
|
|
113111
|
-
expiresAt: created.expiresAt
|
|
113112
|
-
}
|
|
114428
|
+
saveConfig({ token: created.token });
|
|
114429
|
+
setAnonClaim({
|
|
114430
|
+
app: created.app.name,
|
|
114431
|
+
claimToken: created.claimToken,
|
|
114432
|
+
expiresAt: created.expiresAt
|
|
113113
114433
|
});
|
|
113114
114434
|
const newClient = new PercherClient({
|
|
113115
114435
|
apiUrl: ctx.client.apiUrl,
|
|
@@ -113150,7 +114470,7 @@ async function handleAnonymousPublish(ctx, config2, input, tarball) {
|
|
|
113150
114470
|
};
|
|
113151
114471
|
}
|
|
113152
114472
|
async function loadOrGenerate(ctx) {
|
|
113153
|
-
const tomlPath =
|
|
114473
|
+
const tomlPath = join22(ctx.cwd, "percher.toml");
|
|
113154
114474
|
if (!existsSync14(tomlPath)) {
|
|
113155
114475
|
ctx.status("No percher.toml found — detecting framework...");
|
|
113156
114476
|
await init(ctx);
|
|
@@ -113201,7 +114521,7 @@ function emitPreflight(ctx, config2, tarball) {
|
|
|
113201
114521
|
}
|
|
113202
114522
|
// ../core/dist/commands/push.js
|
|
113203
114523
|
import { existsSync as existsSync15 } from "node:fs";
|
|
113204
|
-
import { join as
|
|
114524
|
+
import { join as join23 } from "node:path";
|
|
113205
114525
|
var pushInputSchema = exports_external.object({
|
|
113206
114526
|
dryRun: exports_external.boolean().optional(),
|
|
113207
114527
|
force: exports_external.boolean().optional(),
|
|
@@ -113212,7 +114532,7 @@ var SUSPICIOUS_BYTES2 = 50 * 1024 * 1024;
|
|
|
113212
114532
|
var SUSPICIOUS_FILES2 = 1e4;
|
|
113213
114533
|
var PLACEHOLDER_PREFIXES = ["add-your-", "your-key-here"];
|
|
113214
114534
|
async function loadOrGenerate2(ctx) {
|
|
113215
|
-
const tomlPath =
|
|
114535
|
+
const tomlPath = join23(ctx.cwd, "percher.toml");
|
|
113216
114536
|
if (!existsSync15(tomlPath)) {
|
|
113217
114537
|
ctx.status("No percher.toml found — detecting framework...");
|
|
113218
114538
|
const result = await init(ctx);
|
|
@@ -113324,7 +114644,7 @@ async function push(ctx, input = {}, internals = {}) {
|
|
|
113324
114644
|
fileCount: tarball.fileCount,
|
|
113325
114645
|
recovery: RECOVERY_NONE,
|
|
113326
114646
|
summary: `Dry run for ${config2.app.name}: ${tarball.fileCount} files, ${(tarball.bytes / 1024).toFixed(1)} KB — no deploy performed.`,
|
|
113327
|
-
configPath:
|
|
114647
|
+
configPath: join23(ctx.cwd, "percher.toml"),
|
|
113328
114648
|
bundle: { fileCount: tarball.fileCount, bytes: tarball.bytes }
|
|
113329
114649
|
};
|
|
113330
114650
|
}
|
|
@@ -113507,7 +114827,7 @@ ${deployment.errorMessage}` : "";
|
|
|
113507
114827
|
fileCount: tarball.fileCount,
|
|
113508
114828
|
recovery: RECOVERY_NONE,
|
|
113509
114829
|
summary,
|
|
113510
|
-
configPath:
|
|
114830
|
+
configPath: join23(ctx.cwd, "percher.toml"),
|
|
113511
114831
|
bundle: { fileCount: tarball.fileCount, bytes: tarball.bytes }
|
|
113512
114832
|
};
|
|
113513
114833
|
}
|
|
@@ -113837,7 +115157,7 @@ function failureResult(error48) {
|
|
|
113837
115157
|
}
|
|
113838
115158
|
// ../core/dist/commands/rename.js
|
|
113839
115159
|
import { existsSync as existsSync16, readFileSync as readFileSync12, writeFileSync as writeFileSync8 } from "node:fs";
|
|
113840
|
-
import { join as
|
|
115160
|
+
import { join as join24 } from "node:path";
|
|
113841
115161
|
var renameInputSchema = exports_external.object({
|
|
113842
115162
|
app: exports_external.string().optional(),
|
|
113843
115163
|
name: exports_external.string().min(3).max(40)
|
|
@@ -113857,7 +115177,7 @@ async function rename2(ctx, input) {
|
|
|
113857
115177
|
};
|
|
113858
115178
|
}
|
|
113859
115179
|
function updateLocalTomlName(cwd, oldName, newName) {
|
|
113860
|
-
const p =
|
|
115180
|
+
const p = join24(cwd, "percher.toml");
|
|
113861
115181
|
if (!existsSync16(p))
|
|
113862
115182
|
return false;
|
|
113863
115183
|
try {
|
|
@@ -113876,16 +115196,16 @@ function updateLocalTomlName(cwd, oldName, newName) {
|
|
|
113876
115196
|
// ../core/dist/commands/reproduce.js
|
|
113877
115197
|
import { mkdtempSync, writeFileSync as writeFileSync9 } from "node:fs";
|
|
113878
115198
|
import { tmpdir } from "node:os";
|
|
113879
|
-
import { join as
|
|
115199
|
+
import { join as join25 } from "node:path";
|
|
113880
115200
|
var import_cross_spawn = __toESM(require_cross_spawn(), 1);
|
|
113881
115201
|
function runProcessExit(cmd, args, opts = {}) {
|
|
113882
|
-
return new Promise((
|
|
115202
|
+
return new Promise((resolve9, reject) => {
|
|
113883
115203
|
const child = import_cross_spawn.default(cmd, args, {
|
|
113884
115204
|
cwd: opts.cwd,
|
|
113885
115205
|
stdio: opts.inherit ? "inherit" : "ignore"
|
|
113886
115206
|
});
|
|
113887
115207
|
child.on("error", reject);
|
|
113888
|
-
child.on("close", (code) =>
|
|
115208
|
+
child.on("close", (code) => resolve9(code ?? 1));
|
|
113889
115209
|
});
|
|
113890
115210
|
}
|
|
113891
115211
|
var reproduceInputSchema = exports_external.object({
|
|
@@ -113971,8 +115291,8 @@ async function reproduce(ctx, input) {
|
|
|
113971
115291
|
const envMap = await ctx.client.env.list(app.id);
|
|
113972
115292
|
currentAppEnvKeys = Object.keys(envMap).sort();
|
|
113973
115293
|
} catch {}
|
|
113974
|
-
const tmpDir = mkdtempSync(
|
|
113975
|
-
const tarballPath =
|
|
115294
|
+
const tmpDir = mkdtempSync(join25(tmpdir(), `percher-reproduce-${input.deployId}-`));
|
|
115295
|
+
const tarballPath = join25(tmpDir, "source.tar.gz");
|
|
113976
115296
|
writeFileSync9(tarballPath, bytes);
|
|
113977
115297
|
let extracted = false;
|
|
113978
115298
|
try {
|
|
@@ -115312,8 +116632,8 @@ function registerAppsCommands(cli, ctx) {
|
|
|
115312
116632
|
deployId,
|
|
115313
116633
|
timeoutSeconds: Math.min(Math.ceil(remainingMs / 1000), 30)
|
|
115314
116634
|
}, { signal: callController.signal }),
|
|
115315
|
-
new Promise((
|
|
115316
|
-
raceTimer = setTimeout(() =>
|
|
116635
|
+
new Promise((resolve9) => {
|
|
116636
|
+
raceTimer = setTimeout(() => resolve9(null), remainingMs);
|
|
115317
116637
|
})
|
|
115318
116638
|
]);
|
|
115319
116639
|
} finally {
|
|
@@ -115779,8 +117099,8 @@ function registerDeploysCommands(cli, ctx, argv) {
|
|
|
115779
117099
|
const { summary, files } = result;
|
|
115780
117100
|
await writeLine(ctx, `${summary.filesChanged} file${summary.filesChanged === 1 ? "" : "s"} changed · ${import_picocolors7.default.green(`+${summary.additions}`)} ${import_picocolors7.default.red(`−${summary.deletions}`)} (${result.base.slice(0, 10)} → ${result.head.slice(0, 10)})`);
|
|
115781
117101
|
for (const f of files) {
|
|
115782
|
-
const
|
|
115783
|
-
await writeLine(ctx, ` ${f.status} ${f.path} ${
|
|
117102
|
+
const stat2 = f.binary ? import_picocolors7.default.dim("binary") : `${import_picocolors7.default.green(`+${f.additions}`)} ${import_picocolors7.default.red(`−${f.deletions}`)}`;
|
|
117103
|
+
await writeLine(ctx, ` ${f.status} ${f.path} ${stat2}${f.truncated ? import_picocolors7.default.dim(" (truncated)") : ""}`);
|
|
115784
117104
|
if (options.patch && f.patch) {
|
|
115785
117105
|
await writeLine(ctx, f.patch.replace(/\n$/, ""));
|
|
115786
117106
|
}
|
|
@@ -115852,7 +117172,7 @@ function registerEnvCommands(cli, ctx) {
|
|
|
115852
117172
|
if (action === "set") {
|
|
115853
117173
|
const { resolveEnvSetInputs: resolveEnvSetInputs2, EnvSetInputError: EnvSetInputError2 } = await Promise.resolve().then(() => (init_env_set_input(), exports_env_set_input));
|
|
115854
117174
|
const { promptHidden: promptHidden2 } = await Promise.resolve().then(() => (init_prompt_hidden(), exports_prompt_hidden));
|
|
115855
|
-
const { readFile:
|
|
117175
|
+
const { readFile: readFile4 } = await import("node:fs/promises");
|
|
115856
117176
|
try {
|
|
115857
117177
|
const values = await resolveEnvSetInputs2({
|
|
115858
117178
|
args,
|
|
@@ -115864,7 +117184,7 @@ function registerEnvCommands(cli, ctx) {
|
|
|
115864
117184
|
chunks.push(c);
|
|
115865
117185
|
return Buffer.concat(chunks).toString("utf8");
|
|
115866
117186
|
},
|
|
115867
|
-
readFile: async (p) =>
|
|
117187
|
+
readFile: async (p) => readFile4(p, "utf8"),
|
|
115868
117188
|
promptHidden: promptHidden2
|
|
115869
117189
|
});
|
|
115870
117190
|
await envSet(ctx, { app: options.app, values });
|
|
@@ -115978,9 +117298,13 @@ function registerGithubCommands(cli, ctx) {
|
|
|
115978
117298
|
// src/commands/migrate.ts
|
|
115979
117299
|
var import_picocolors11 = __toESM(require_picocolors(), 1);
|
|
115980
117300
|
function registerMigrateCommands(cli, ctx) {
|
|
115981
|
-
cli.command("migrate-from-supabase", "Migrate a Supabase project to PocketBase
|
|
117301
|
+
cli.command("migrate-from-supabase", "Migrate a Supabase project to PocketBase: schema + data-import script, optionally rewrite SDK calls").option("--project <ref>", "Supabase project reference (the 20-char id from the project URL)").option("--token <token>", "Supabase personal access token (sbp_...). Read from SUPABASE_ACCESS_TOKEN if omitted.").option("--inspect-only", "Only print the schema that would migrate — read-only, no file writes").option("--output-dir <dir>", "Where to write the preview files (default: ./migration-preview/)").option("--rewrite-client", "Also convert your Supabase SDK calls to PocketBase across the project").option("--apply", "With --rewrite-client: overwrite files in place (originals backed up first). Default: preview only.").option("--root <dir>", "Project root to rewrite with --rewrite-client (default: current directory)").option("--json", "Output the structured result as JSON").action(async (options) => {
|
|
115982
117302
|
const projectRef = options.project;
|
|
115983
117303
|
const token = options.token ?? ctx.env.SUPABASE_ACCESS_TOKEN;
|
|
117304
|
+
if (options.apply && !options.rewriteClient) {
|
|
117305
|
+
await writeLine(ctx, `${import_picocolors11.default.red("✗")} --apply only applies to the client rewrite. Pass --rewrite-client --apply together.`);
|
|
117306
|
+
throw new SilentExitError(1);
|
|
117307
|
+
}
|
|
115984
117308
|
if (!projectRef) {
|
|
115985
117309
|
await writeLine(ctx, `${import_picocolors11.default.red("✗")} Missing --project. Pass your Supabase project ref (e.g. --project abcdefghijklmnopqrst).`);
|
|
115986
117310
|
throw new SilentExitError(1);
|
|
@@ -116007,15 +117331,28 @@ function registerMigrateCommands(cli, ctx) {
|
|
|
116007
117331
|
token,
|
|
116008
117332
|
outputDir: options.outputDir
|
|
116009
117333
|
});
|
|
117334
|
+
let rewrite;
|
|
117335
|
+
if (options.rewriteClient && result.status === "ok") {
|
|
117336
|
+
rewrite = await rewriteSupabaseClient(ctx, {
|
|
117337
|
+
rootDir: options.root,
|
|
117338
|
+
apply: options.apply === true
|
|
117339
|
+
});
|
|
117340
|
+
}
|
|
116010
117341
|
if (options.json) {
|
|
116011
|
-
await writeLine(ctx, JSON.stringify(result, null, 2));
|
|
116012
|
-
if (result.status === "failed")
|
|
117342
|
+
await writeLine(ctx, JSON.stringify(rewrite ? { schema: result, rewrite } : result, null, 2));
|
|
117343
|
+
if (result.status === "failed" || rewrite?.status === "failed") {
|
|
116013
117344
|
throw new SilentExitError(1);
|
|
117345
|
+
}
|
|
116014
117346
|
return;
|
|
116015
117347
|
}
|
|
116016
|
-
await renderSchemaHuman(ctx, result
|
|
116017
|
-
|
|
117348
|
+
await renderSchemaHuman(ctx, result, {
|
|
117349
|
+
clientRewriteRequested: options.rewriteClient === true
|
|
117350
|
+
});
|
|
117351
|
+
if (rewrite)
|
|
117352
|
+
await renderRewriteHuman(ctx, rewrite);
|
|
117353
|
+
if (result.status === "failed" || rewrite?.status === "failed") {
|
|
116018
117354
|
throw new SilentExitError(1);
|
|
117355
|
+
}
|
|
116019
117356
|
});
|
|
116020
117357
|
}
|
|
116021
117358
|
async function renderInspectHuman(ctx, result) {
|
|
@@ -116061,9 +117398,9 @@ async function renderInspectHuman(ctx, result) {
|
|
|
116061
117398
|
}
|
|
116062
117399
|
}
|
|
116063
117400
|
await writeLine(ctx, "");
|
|
116064
|
-
await writeLine(ctx, import_picocolors11.default.dim("--inspect-only is read-only. Drop the flag to also write the
|
|
117401
|
+
await writeLine(ctx, import_picocolors11.default.dim("--inspect-only is read-only. Drop the flag to also write the PocketBase schema preview + pb_migrate.js data-import script to ./migration-preview/."));
|
|
116065
117402
|
}
|
|
116066
|
-
async function renderSchemaHuman(ctx, result) {
|
|
117403
|
+
async function renderSchemaHuman(ctx, result, opts) {
|
|
116067
117404
|
if (result.status === "failed") {
|
|
116068
117405
|
const err = result.error;
|
|
116069
117406
|
await writeLine(ctx, `${import_picocolors11.default.red("✗")} ${err?.title ?? "Migration preview failed"}`);
|
|
@@ -116102,7 +117439,55 @@ async function renderSchemaHuman(ctx, result) {
|
|
|
116102
117439
|
}
|
|
116103
117440
|
}
|
|
116104
117441
|
await writeLine(ctx, "");
|
|
116105
|
-
await writeLine(ctx, import_picocolors11.default.dim("
|
|
117442
|
+
await writeLine(ctx, import_picocolors11.default.dim("Preview only — nothing has touched your PocketBase yet. ./migration-preview/ includes pb_migrate.js, the data-import script: review MIGRATION_NOTES.md, then run it against your app's PocketBase to copy your rows over."));
|
|
117443
|
+
if (!opts.clientRewriteRequested) {
|
|
117444
|
+
await writeLine(ctx, import_picocolors11.default.dim("Add --rewrite-client to also convert your Supabase SDK calls to PocketBase (preview first; --apply writes in place)."));
|
|
117445
|
+
}
|
|
117446
|
+
}
|
|
117447
|
+
async function renderRewriteHuman(ctx, result) {
|
|
117448
|
+
await writeLine(ctx, "");
|
|
117449
|
+
if (result.status === "failed") {
|
|
117450
|
+
const err = result.error;
|
|
117451
|
+
await writeLine(ctx, `${import_picocolors11.default.red("✗")} ${err?.title ?? "Client rewrite failed"}`);
|
|
117452
|
+
if (err?.explanation)
|
|
117453
|
+
await writeLine(ctx, ` ${err.explanation}`);
|
|
117454
|
+
if (err?.suggestion)
|
|
117455
|
+
await writeLine(ctx, import_picocolors11.default.dim(` ${err.suggestion}`));
|
|
117456
|
+
return;
|
|
117457
|
+
}
|
|
117458
|
+
await writeLine(ctx, `${import_picocolors11.default.green("✓")} ${result.summary}`);
|
|
117459
|
+
if (result.filesWritten.length > 0) {
|
|
117460
|
+
await writeLine(ctx, "");
|
|
117461
|
+
await writeLine(ctx, import_picocolors11.default.bold("Files written"));
|
|
117462
|
+
for (const path of result.filesWritten.slice(0, 20)) {
|
|
117463
|
+
await writeLine(ctx, ` ${import_picocolors11.default.cyan(path)}`);
|
|
117464
|
+
}
|
|
117465
|
+
if (result.filesWritten.length > 20) {
|
|
117466
|
+
await writeLine(ctx, import_picocolors11.default.dim(` …and ${result.filesWritten.length - 20} more`));
|
|
117467
|
+
}
|
|
117468
|
+
}
|
|
117469
|
+
if (result.flags.length > 0) {
|
|
117470
|
+
await writeLine(ctx, "");
|
|
117471
|
+
await writeLine(ctx, import_picocolors11.default.bold("Needs review (see migration-preview/REWRITE_NOTES.md)"));
|
|
117472
|
+
for (const flag of result.flags.slice(0, 15)) {
|
|
117473
|
+
const loc = flag.line !== undefined ? `:${flag.line}` : "";
|
|
117474
|
+
const tag = flag.kind === "manual_review" ? "rewritten, verify semantics" : "left untouched";
|
|
117475
|
+
await writeLine(ctx, ` ${import_picocolors11.default.yellow("⚠")} ${import_picocolors11.default.cyan(`${flag.file}${loc}`)} ${flag.pattern} ${import_picocolors11.default.dim(`(${tag}) — ${flag.note}`)}`);
|
|
117476
|
+
}
|
|
117477
|
+
if (result.flags.length > 15) {
|
|
117478
|
+
await writeLine(ctx, import_picocolors11.default.dim(` …and ${result.flags.length - 15} more — see REWRITE_NOTES.md`));
|
|
117479
|
+
}
|
|
117480
|
+
}
|
|
117481
|
+
if (result.skipped.length > 0) {
|
|
117482
|
+
await writeLine(ctx, import_picocolors11.default.dim(`
|
|
117483
|
+
${result.skipped.length} file(s) couldn't be parsed and were skipped — listed in REWRITE_NOTES.md.`));
|
|
117484
|
+
}
|
|
117485
|
+
await writeLine(ctx, "");
|
|
117486
|
+
if (result.mode === "apply") {
|
|
117487
|
+
await writeLine(ctx, import_picocolors11.default.dim(result.changedCount > 0 ? "Files rewritten in place. Originals are in ./migration-preview/backup/ — diff against them (or git) before committing." : "No files were rewritten — the work left is the manual review above."));
|
|
117488
|
+
} else {
|
|
117489
|
+
await writeLine(ctx, import_picocolors11.default.dim(result.changedCount > 0 ? "Preview only — diff ./migration-preview/rewritten/ against your project, then re-run with --rewrite-client --apply to write in place." : "Preview only — no files needed automatic rewriting; work through the manual review above."));
|
|
117490
|
+
}
|
|
116106
117491
|
}
|
|
116107
117492
|
|
|
116108
117493
|
// src/commands/sharing.ts
|
|
@@ -116443,7 +117828,7 @@ async function renderError(stderr, error48) {
|
|
|
116443
117828
|
// package.json
|
|
116444
117829
|
var package_default = {
|
|
116445
117830
|
name: "percher",
|
|
116446
|
-
version: "0.5.
|
|
117831
|
+
version: "0.5.7",
|
|
116447
117832
|
description: "CLI for the Percher hosting platform — publish apps with one command",
|
|
116448
117833
|
type: "module",
|
|
116449
117834
|
bin: {
|
|
@@ -116525,9 +117910,9 @@ async function showTelemetryNotice() {
|
|
|
116525
117910
|
return;
|
|
116526
117911
|
const { homedir: homedir2 } = await import("node:os");
|
|
116527
117912
|
const { existsSync: existsSync17, mkdirSync: mkdirSync6, writeFileSync: writeFileSync10 } = await import("node:fs");
|
|
116528
|
-
const { join:
|
|
116529
|
-
const configDir =
|
|
116530
|
-
const markerFile =
|
|
117913
|
+
const { join: join26 } = await import("node:path");
|
|
117914
|
+
const configDir = join26(homedir2(), ".percher");
|
|
117915
|
+
const markerFile = join26(configDir, ".telemetry-notice-shown");
|
|
116531
117916
|
if (existsSync17(markerFile))
|
|
116532
117917
|
return;
|
|
116533
117918
|
console.log("Percher sends non-account-linked usage stats (command, result, error type, OS, Bun version).");
|