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.
@@ -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(resolve7) {
5073
- resolve7(value);
5072
+ return value instanceof P ? value : new P(function(resolve6) {
5073
+ resolve6(value);
5074
5074
  });
5075
5075
  }
5076
- return new (P || (P = Promise))(function(resolve7, reject) {
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 ? resolve7(result.value) : adopt(result.value).then(fulfilled, rejected);
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(resolve7, reject) {
5322
- v = o[n](v), settle(resolve7, reject, v.done, v.value);
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(resolve7, reject, d, v) {
5326
+ function settle(resolve6, reject, d, v) {
5327
5327
  Promise.resolve(v).then(function(v2) {
5328
- resolve7({ value: v2, done: d });
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 isAbsolute3 = exports.isAbsolute(path);
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 = isAbsolute3 ? "/" : ".";
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 join18(aRoot, aPath) {
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 = join18;
8888
+ exports.join = join17;
8889
8889
  exports.isAbsolute = function(aPath) {
8890
8890
  return aPath.charAt(0) === "/" || urlRegexp.test(aPath);
8891
8891
  };
8892
- function relative4(aRoot, aPath) {
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 = relative4;
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 = join18(urlGenerate(parsed), 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(resolve7, reject) {
89387
+ return new Promise(function(resolve9, reject) {
89388
89388
  try {
89389
89389
  const data2 = QRCode.create(text, opts);
89390
- resolve7(renderFunc(data2, canvas, opts));
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(resolve7, reject) {
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) : resolve7(data3);
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(stat, path, options) {
89569
- if (!stat.isSymbolicLink() && !stat.isFile()) {
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, stat) {
89576
- cb(er, er ? false : checkStat(stat, path, options));
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, stat) {
89591
- cb(er, er ? false : checkStat(stat, options));
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(stat, options) {
89598
- return stat.isFile() && checkMode(stat, options);
89597
+ function checkStat(stat2, options) {
89598
+ return stat2.isFile() && checkMode(stat2, options);
89599
89599
  }
89600
- function checkMode(stat, options) {
89601
- var mod = stat.mode;
89602
- var uid = stat.uid;
89603
- var gid = stat.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(resolve7, reject) {
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
- resolve7(is);
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((resolve7, reject) => {
89702
+ const step = (i) => new Promise((resolve9, reject) => {
89703
89703
  if (i === pathEnv.length)
89704
- return opt.all && found.length ? resolve7(found) : reject(getNotFoundError(cmd));
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
- resolve7(subStep(p, i, 0));
89709
+ resolve9(subStep(p, i, 0));
89710
89710
  });
89711
- const subStep = (p, i, ii) => new Promise((resolve7, reject) => {
89711
+ const subStep = (p, i, ii) => new Promise((resolve9, reject) => {
89712
89712
  if (ii === pathExt.length)
89713
- return resolve7(step(i + 1));
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 resolve7(p + ext);
89720
+ return resolve9(p + ext);
89721
89721
  }
89722
- return resolve7(subStep(p, i, ii + 1));
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((resolve7, reject) => {
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
- resolve7(buf.replace(/\r$/, ""));
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
- resolve7(buf.replace(/\r$/, ""));
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((resolve7, reject) => {
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
- resolve7(buf);
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 state = loadConfig().anonClaim;
106538
- const app = input.app ?? state?.app ?? readPercherTomlAppName(ctx.cwd) ?? undefined;
106539
- const claimToken = input.claimToken ?? state?.claimToken;
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
- if (state)
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 mkdir2, writeFile as writeFile2 } from "node:fs/promises";
110871
- import { isAbsolute as isAbsolute2, join as join17, relative as relative3, resolve as resolve6 } from "node:path";
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 = relative3(ctx.cwd, outputDirAbs) || ".";
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 = renderSummary2({
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 isAbsolute2(raw) ? raw : resolve6(cwd, raw);
112728
+ return isAbsolute3(raw) ? raw : resolve8(cwd, raw);
111384
112729
  }
111385
112730
  async function writePreviewFiles(ctx, outputDir, preview) {
111386
- await mkdir2(outputDir, { recursive: true });
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 = join17(outputDir, "pb_schema.json");
111391
- const notesPath = join17(outputDir, "MIGRATION_NOTES.md");
111392
- const migratePath = join17(outputDir, "pb_migrate.js");
111393
- const storagePath = join17(outputDir, "pb_migrate_storage.js");
111394
- await writeFile2(schemaPath, schemaJson, "utf8");
111395
- await writeFile2(notesPath, notesMd, "utf8");
111396
- await writeFile2(migratePath, scripts.pbMigrate, "utf8");
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
- relative3(ctx.cwd, schemaPath),
111399
- relative3(ctx.cwd, notesPath),
111400
- relative3(ctx.cwd, migratePath)
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 writeFile2(storagePath, scripts.pbMigrateStorage, "utf8");
111404
- written.push(relative3(ctx.cwd, storagePath));
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 renderSummary2(args) {
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 join20 } from "node:path";
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 join18 } from "node:path";
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(join18(cwd, rel), "utf8");
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 = join18(dir, name);
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 join19 } from "node:path";
113772
+ import { join as join21 } from "node:path";
112451
113773
  function resolveNodeVersion(cwd) {
112452
- const nvmrc = join19(cwd, ".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 = join19(cwd, "package.json");
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 join20(cwd, "percher.toml");
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
- token: created.token,
113108
- anonClaim: {
113109
- app: created.app.name,
113110
- claimToken: created.claimToken,
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 = join20(ctx.cwd, "percher.toml");
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 join21 } from "node:path";
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 = join21(ctx.cwd, "percher.toml");
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: join21(ctx.cwd, "percher.toml"),
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: join21(ctx.cwd, "percher.toml"),
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 join22 } from "node:path";
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 = join22(cwd, "percher.toml");
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 join23 } from "node:path";
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((resolve7, reject) => {
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) => resolve7(code ?? 1));
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(join23(tmpdir(), `percher-reproduce-${input.deployId}-`));
113975
- const tarballPath = join23(tmpDir, "source.tar.gz");
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((resolve7) => {
115316
- raceTimer = setTimeout(() => resolve7(null), remainingMs);
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 stat = f.binary ? import_picocolors7.default.dim("binary") : `${import_picocolors7.default.green(`+${f.additions}`)} ${import_picocolors7.default.red(`−${f.deletions}`)}`;
115783
- await writeLine(ctx, ` ${f.status} ${f.path} ${stat}${f.truncated ? import_picocolors7.default.dim(" (truncated)") : ""}`);
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: readFile3 } = await import("node:fs/promises");
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) => readFile3(p, "utf8"),
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 (FUTURE9 Phase A read-only + Phase B preview writes)").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", "Run schema introspection only (Phase A — read-only, no file writes)").option("--output-dir <dir>", "Where to write the Phase B preview files (default: ./migration-preview/)").option("--json", "Output the structured result as JSON").action(async (options) => {
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
- if (result.status === "failed")
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 Phase B PocketBase-schema preview."));
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("Phase B is a preview. Phase C (data import) and Phase D (client rewrite) ship in later releases. Review MIGRATION_NOTES.md before any import."));
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.6",
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: join24 } = await import("node:path");
116529
- const configDir = join24(homedir2(), ".percher");
116530
- const markerFile = join24(configDir, ".telemetry-notice-shown");
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).");