@percher/core 0.4.10 → 0.4.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (140) hide show
  1. package/dist/ai-files-manifest.d.ts +1 -1
  2. package/dist/commands/account.d.ts +1 -1
  3. package/dist/commands/account.js +2 -2
  4. package/dist/commands/admin-reconcile-routes.d.ts +1 -1
  5. package/dist/commands/ai-files.d.ts +2 -2
  6. package/dist/commands/ai-files.js +2 -2
  7. package/dist/commands/alerts.d.ts +1 -1
  8. package/dist/commands/app-resources.d.ts +1 -1
  9. package/dist/commands/app-resources.js +2 -2
  10. package/dist/commands/app-topology.d.ts +1 -1
  11. package/dist/commands/app-topology.js +2 -2
  12. package/dist/commands/billing.d.ts +1 -1
  13. package/dist/commands/billing.js +1 -1
  14. package/dist/commands/cache.d.ts +1 -1
  15. package/dist/commands/cache.js +2 -2
  16. package/dist/commands/capabilities.d.ts +1 -1
  17. package/dist/commands/claim.d.ts +1 -1
  18. package/dist/commands/claim.js +1 -1
  19. package/dist/commands/create.d.ts +2 -2
  20. package/dist/commands/create.js +2 -2
  21. package/dist/commands/dashboard.d.ts +1 -1
  22. package/dist/commands/dashboard.js +2 -2
  23. package/dist/commands/data-export.d.ts +1 -1
  24. package/dist/commands/data-export.js +3 -3
  25. package/dist/commands/data.d.ts +1 -1
  26. package/dist/commands/data.js +2 -2
  27. package/dist/commands/delete.d.ts +1 -1
  28. package/dist/commands/delete.js +2 -2
  29. package/dist/commands/deploys.d.ts +1 -1
  30. package/dist/commands/deploys.js +2 -2
  31. package/dist/commands/dev.d.ts +1 -1
  32. package/dist/commands/dev.js +3 -3
  33. package/dist/commands/diagnose.d.ts +1 -1
  34. package/dist/commands/diagnose.js +2 -2
  35. package/dist/commands/diff.d.ts +1 -1
  36. package/dist/commands/diff.js +2 -2
  37. package/dist/commands/doctor.d.ts +3 -3
  38. package/dist/commands/doctor.js +4 -4
  39. package/dist/commands/domains.d.ts +1 -1
  40. package/dist/commands/domains.js +2 -2
  41. package/dist/commands/env.d.ts +1 -1
  42. package/dist/commands/env.js +2 -2
  43. package/dist/commands/export.d.ts +1 -1
  44. package/dist/commands/export.js +2 -2
  45. package/dist/commands/forgejo.d.ts +1 -1
  46. package/dist/commands/forgejo.js +2 -2
  47. package/dist/commands/generate.d.ts +1 -1
  48. package/dist/commands/generate.js +1 -1
  49. package/dist/commands/github.d.ts +1 -1
  50. package/dist/commands/github.js +2 -2
  51. package/dist/commands/import-project.d.ts +1 -1
  52. package/dist/commands/import-project.js +2 -2
  53. package/dist/commands/init.d.ts +2 -2
  54. package/dist/commands/init.js +3 -3
  55. package/dist/commands/insights.d.ts +1 -1
  56. package/dist/commands/insights.js +2 -2
  57. package/dist/commands/inspect-link.d.ts +1 -1
  58. package/dist/commands/inspect-link.js +2 -2
  59. package/dist/commands/login.d.ts +1 -1
  60. package/dist/commands/login.js +1 -1
  61. package/dist/commands/logs.d.ts +1 -1
  62. package/dist/commands/logs.js +2 -2
  63. package/dist/commands/mcp.d.ts +1 -1
  64. package/dist/commands/migrate-supabase-map.d.ts +1 -1
  65. package/dist/commands/migrate-supabase-recipes.d.ts +28 -0
  66. package/dist/commands/migrate-supabase-recipes.d.ts.map +1 -0
  67. package/dist/commands/migrate-supabase-recipes.js +92 -0
  68. package/dist/commands/migrate-supabase-recipes.js.map +1 -0
  69. package/dist/commands/migrate-supabase-rewrite.d.ts +8 -3
  70. package/dist/commands/migrate-supabase-rewrite.d.ts.map +1 -1
  71. package/dist/commands/migrate-supabase-rewrite.js +155 -11
  72. package/dist/commands/migrate-supabase-rewrite.js.map +1 -1
  73. package/dist/commands/migrate-supabase-scaffold.d.ts +25 -0
  74. package/dist/commands/migrate-supabase-scaffold.d.ts.map +1 -0
  75. package/dist/commands/migrate-supabase-scaffold.js +234 -0
  76. package/dist/commands/migrate-supabase-scaffold.js.map +1 -0
  77. package/dist/commands/migrate-supabase-schema.d.ts +4 -4
  78. package/dist/commands/migrate-supabase-schema.js +4 -4
  79. package/dist/commands/migrate-supabase-scripts.d.ts +1 -1
  80. package/dist/commands/migrate-supabase-sdk.d.ts +34 -0
  81. package/dist/commands/migrate-supabase-sdk.d.ts.map +1 -1
  82. package/dist/commands/migrate-supabase-sdk.js +169 -0
  83. package/dist/commands/migrate-supabase-sdk.js.map +1 -1
  84. package/dist/commands/migrate-supabase-status.d.ts +92 -0
  85. package/dist/commands/migrate-supabase-status.d.ts.map +1 -0
  86. package/dist/commands/migrate-supabase-status.js +470 -0
  87. package/dist/commands/migrate-supabase-status.js.map +1 -0
  88. package/dist/commands/migrate-supabase-walker.d.ts +15 -1
  89. package/dist/commands/migrate-supabase-walker.d.ts.map +1 -1
  90. package/dist/commands/migrate-supabase-walker.js +49 -5
  91. package/dist/commands/migrate-supabase-walker.js.map +1 -1
  92. package/dist/commands/migrate-supabase.d.ts +2 -2
  93. package/dist/commands/migrate-supabase.js +1 -1
  94. package/dist/commands/open.d.ts +1 -1
  95. package/dist/commands/open.js +2 -2
  96. package/dist/commands/preview-branch.d.ts +1 -1
  97. package/dist/commands/preview-branch.js +2 -2
  98. package/dist/commands/publish-api-error.d.ts +1 -1
  99. package/dist/commands/publish-api-error.js +1 -1
  100. package/dist/commands/publish-failure.d.ts +3 -3
  101. package/dist/commands/publish-failure.js +3 -3
  102. package/dist/commands/publish.d.ts +3 -3
  103. package/dist/commands/publish.js +13 -13
  104. package/dist/commands/push.d.ts +2 -2
  105. package/dist/commands/push.js +8 -8
  106. package/dist/commands/redeploy.d.ts +2 -2
  107. package/dist/commands/redeploy.js +4 -4
  108. package/dist/commands/rename.d.ts +1 -1
  109. package/dist/commands/rename.js +2 -2
  110. package/dist/commands/reproduce.d.ts +1 -1
  111. package/dist/commands/reproduce.js +1 -1
  112. package/dist/commands/reset-superuser.d.ts +1 -1
  113. package/dist/commands/reset-superuser.js +2 -2
  114. package/dist/commands/restore.d.ts +1 -1
  115. package/dist/commands/restore.js +2 -2
  116. package/dist/commands/resume.d.ts +1 -1
  117. package/dist/commands/resume.js +2 -2
  118. package/dist/commands/rollback.d.ts +1 -1
  119. package/dist/commands/rollback.js +2 -2
  120. package/dist/commands/sharing.d.ts +1 -1
  121. package/dist/commands/sharing.js +2 -2
  122. package/dist/commands/status.d.ts +1 -1
  123. package/dist/commands/transfers.d.ts +1 -1
  124. package/dist/commands/transfers.js +2 -2
  125. package/dist/commands/unsuspend.d.ts +1 -1
  126. package/dist/commands/versions.d.ts +1 -1
  127. package/dist/commands/versions.js +2 -2
  128. package/dist/commands/wait-deploy.d.ts +3 -3
  129. package/dist/commands/wait-deploy.js +3 -3
  130. package/dist/commands/whoami.d.ts +1 -1
  131. package/dist/detect.js +1 -1
  132. package/dist/index.d.ts +77 -75
  133. package/dist/index.d.ts.map +1 -1
  134. package/dist/index.js +76 -74
  135. package/dist/index.js.map +1 -1
  136. package/dist/poll-deployment.d.ts +1 -1
  137. package/dist/recovery.d.ts +1 -1
  138. package/dist/structured-error-codes.d.ts +1 -1
  139. package/dist/templates/ai-files/index.js +2 -2
  140. package/package.json +6 -3
@@ -0,0 +1,234 @@
1
+ import { posix } from "node:path";
2
+ import jscodeshift, {} from "jscodeshift";
3
+ /**
4
+ * Phase C — auto PocketBase client + import insertion.
5
+ *
6
+ * After the rewriter converts call sites to `pb.*`, those files reference a
7
+ * `pb` client that doesn't exist yet (the dangling-pb FAIL the status doctor
8
+ * reports). This closes that gap mechanically:
9
+ *
10
+ * 1. Generate a PARALLEL client module (`src/lib/pocketbase.<ext>`) — a
11
+ * NEW file, never a rewrite of the existing Supabase client. Other files
12
+ * (untouched storage/rpc/realtime call sites) may still import the old
13
+ * `supabase` client; clobbering it would break them and contradicts the
14
+ * hybrid model. The old module is left intact and flagged "delete once
15
+ * everything's migrated".
16
+ * 2. Insert `import { pb } from "<relative path>"` into each rewritten file
17
+ * that references `pb` without a binding.
18
+ *
19
+ * Skips creating a module silently when the project already exports a `pb`
20
+ * client (imports point at the existing one instead). Everything here is a
21
+ * "working default" — boilerplate the user may tweak — not a guess at their
22
+ * exact config.
23
+ */
24
+ const tsx = jscodeshift.withParser("tsx");
25
+ const CLIENT_FACTORIES = new Set(["createClient", "createBrowserClient", "createServerClient"]);
26
+ export function scaffoldPocketBaseClient(input) {
27
+ const pbName = input.pbIdentifier ?? "pb";
28
+ const codeFiles = input.files.filter((f) => f.kind === "code");
29
+ const flags = [];
30
+ // Flag the existing Supabase client module(s) for eventual deletion.
31
+ for (const file of codeFiles) {
32
+ if (isSupabaseClientModule(file.source)) {
33
+ flags.push({
34
+ file: file.relativePath,
35
+ kind: "manual_review",
36
+ pattern: "supabase.client_module",
37
+ note: "This module sets up the Supabase client. It's kept while any Supabase call remains; once everything is migrated to PocketBase, delete it and the imports that reference it.",
38
+ });
39
+ }
40
+ }
41
+ // If a pb client already exists, import from it instead of generating one.
42
+ const existing = codeFiles.find((f) => exportsBinding(f.rewritten, pbName));
43
+ let targetRelNoExt;
44
+ let clientModule = null;
45
+ let skippedReason;
46
+ if (existing) {
47
+ targetRelNoExt = stripExt(existing.relativePath);
48
+ skippedReason = "pb_client_exists";
49
+ }
50
+ else {
51
+ const ext = pickExtension(codeFiles);
52
+ const baseNoExt = pickModuleBase(codeFiles);
53
+ const targetRel = `${baseNoExt}.${ext}`;
54
+ // Collision with a non-pb-client file at the exact path → don't clobber.
55
+ if (input.files.some((f) => f.relativePath === targetRel)) {
56
+ flags.push({
57
+ file: targetRel,
58
+ kind: "manual_review",
59
+ pattern: "scaffold.path_taken",
60
+ note: `Couldn't auto-create a PocketBase client — \`${targetRel}\` already exists. Create one that exports \`${pbName}\` and import it in the rewritten files.`,
61
+ });
62
+ return {
63
+ clientModule: null,
64
+ importInsertions: new Map(),
65
+ flags,
66
+ skippedReason: "target_path_taken",
67
+ };
68
+ }
69
+ targetRelNoExt = baseNoExt;
70
+ clientModule = { relativePath: targetRel, content: renderClientModule(pbName, codeFiles) };
71
+ }
72
+ // Insert the import into every changed file that references pb without a binding.
73
+ const importInsertions = new Map();
74
+ const changed = new Set(input.changedRelPaths);
75
+ for (const file of codeFiles) {
76
+ if (!changed.has(file.relativePath))
77
+ continue;
78
+ if (file.relativePath === clientModule?.relativePath)
79
+ continue; // never self-import
80
+ if (!referencesUnboundIdentifier(file.rewritten, pbName))
81
+ continue;
82
+ const importPath = computeImportPath(file.relativePath, targetRelNoExt);
83
+ const next = insertNamedImport(file.rewritten, pbName, importPath);
84
+ if (next !== null)
85
+ importInsertions.set(file.relativePath, next);
86
+ }
87
+ return { clientModule, importInsertions, flags, ...(skippedReason ? { skippedReason } : {}) };
88
+ }
89
+ /** PocketBase client module body. Accessor matches the project shape. */
90
+ function renderClientModule(pbName, codeFiles) {
91
+ const usesViteEnv = codeFiles.some((f) => f.source.includes("import.meta.env"));
92
+ const accessor = usesViteEnv
93
+ ? "import.meta.env.VITE_POCKETBASE_URL"
94
+ : "process.env.POCKETBASE_URL";
95
+ return [
96
+ 'import PocketBase from "pocketbase";',
97
+ "",
98
+ "// Percher injects the PocketBase URL at deploy time. This is a working",
99
+ "// default — adjust the URL accessor to match your framework if needed.",
100
+ `export const ${pbName} = new PocketBase(${accessor});`,
101
+ "",
102
+ ].join("\n");
103
+ }
104
+ /** `ts` if the project has any TS file, else `js`. */
105
+ function pickExtension(codeFiles) {
106
+ return codeFiles.some((f) => f.relativePath.endsWith(".ts") || f.relativePath.endsWith(".tsx"))
107
+ ? "ts"
108
+ : "js";
109
+ }
110
+ /** Where to place the generated module — under src/ when the project uses it. */
111
+ function pickModuleBase(codeFiles) {
112
+ const hasSrc = codeFiles.some((f) => f.relativePath.startsWith("src/"));
113
+ return hasSrc ? "src/lib/pocketbase" : "lib/pocketbase";
114
+ }
115
+ function stripExt(relPath) {
116
+ const dot = relPath.lastIndexOf(".");
117
+ return dot > relPath.lastIndexOf("/") ? relPath.slice(0, dot) : relPath;
118
+ }
119
+ /** Relative, extensionless import specifier from one file to the module. */
120
+ function computeImportPath(fromFileRel, targetRelNoExt) {
121
+ let rel = posix.relative(posix.dirname(fromFileRel), targetRelNoExt);
122
+ if (!rel.startsWith("."))
123
+ rel = `./${rel}`;
124
+ return rel;
125
+ }
126
+ /** Insert `import { name } from "spec"` at the top of the file. Returns the
127
+ * new source, or null if it couldn't parse (caller leaves the file as-is). */
128
+ function insertNamedImport(source, name, spec) {
129
+ let root;
130
+ try {
131
+ root = tsx(source);
132
+ }
133
+ catch {
134
+ return null;
135
+ }
136
+ const decl = jscodeshift.importDeclaration([jscodeshift.importSpecifier(jscodeshift.identifier(name))], jscodeshift.literal(spec));
137
+ const program = root.get().node.program;
138
+ program.body.unshift(decl);
139
+ return root.toSource({ quote: "double" });
140
+ }
141
+ /** True when `name` is read as a member object (`name.x`) but never bound. */
142
+ function referencesUnboundIdentifier(source, name) {
143
+ let root;
144
+ try {
145
+ root = tsx(source);
146
+ }
147
+ catch {
148
+ return false;
149
+ }
150
+ let declared = false;
151
+ root.find(jscodeshift.ImportDeclaration).forEach((p) => {
152
+ for (const spec of p.node.specifiers ?? []) {
153
+ if (spec.local?.name === name)
154
+ declared = true;
155
+ }
156
+ });
157
+ root.find(jscodeshift.VariableDeclarator).forEach((p) => {
158
+ if (p.node.id.type === "Identifier" && p.node.id.name === name) {
159
+ declared = true;
160
+ }
161
+ });
162
+ let used = false;
163
+ root.find(jscodeshift.MemberExpression).forEach((p) => {
164
+ const obj = p.node.object;
165
+ if (obj.type === "Identifier" && obj.name === name)
166
+ used = true;
167
+ });
168
+ return used && !declared;
169
+ }
170
+ /** True when the module exports a binding named `name` (the existing pb client). */
171
+ function exportsBinding(source, name) {
172
+ if (!source.includes(`export`) || !source.includes(name))
173
+ return false;
174
+ let root;
175
+ try {
176
+ root = tsx(source);
177
+ }
178
+ catch {
179
+ return false;
180
+ }
181
+ let found = false;
182
+ root.find(jscodeshift.ExportNamedDeclaration).forEach((p) => {
183
+ const node = p.node;
184
+ // export const name = ...
185
+ if (node.declaration?.type === "VariableDeclaration") {
186
+ for (const d of node.declaration.declarations) {
187
+ if (d.type === "VariableDeclarator" &&
188
+ d.id.type === "Identifier" &&
189
+ d.id.name === name) {
190
+ found = true;
191
+ }
192
+ }
193
+ }
194
+ // export { name } / export { x as name }
195
+ for (const spec of node.specifiers ?? []) {
196
+ const exported = spec.exported;
197
+ if (exported?.type === "Identifier" && exported.name === name) {
198
+ found = true;
199
+ }
200
+ }
201
+ });
202
+ return found;
203
+ }
204
+ /** True when the file imports `@supabase/*` AND calls a client factory — i.e.
205
+ * it's a Supabase client-setup module, not just a consumer. */
206
+ function isSupabaseClientModule(source) {
207
+ if (!source.includes("@supabase/"))
208
+ return false;
209
+ let root;
210
+ try {
211
+ root = tsx(source);
212
+ }
213
+ catch {
214
+ return false;
215
+ }
216
+ let importsSupabase = false;
217
+ root.find(jscodeshift.ImportDeclaration).forEach((p) => {
218
+ const src = p.node.source.value;
219
+ if (typeof src === "string" && src.startsWith("@supabase/"))
220
+ importsSupabase = true;
221
+ });
222
+ if (!importsSupabase)
223
+ return false;
224
+ let callsFactory = false;
225
+ root.find(jscodeshift.CallExpression).forEach((p) => {
226
+ const callee = p.node.callee;
227
+ if (callee.type === "Identifier" &&
228
+ CLIENT_FACTORIES.has(callee.name)) {
229
+ callsFactory = true;
230
+ }
231
+ });
232
+ return callsFactory;
233
+ }
234
+ //# sourceMappingURL=migrate-supabase-scaffold.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"migrate-supabase-scaffold.js","sourceRoot":"","sources":["../../src/commands/migrate-supabase-scaffold.ts"],"names":[],"mappings":"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"}
@@ -1,8 +1,8 @@
1
1
  import { z } from "zod";
2
- import type { Context } from "../context";
3
- import { type AskUserRecovery, type NoneRecovery, type ToolRecovery } from "../recovery";
4
- import { type InspectSupabaseError } from "./migrate-supabase";
5
- import { type MigrationPreview } from "./migrate-supabase-map";
2
+ import type { Context } from "../context.js";
3
+ import { type AskUserRecovery, type NoneRecovery, type ToolRecovery } from "../recovery.js";
4
+ import { type InspectSupabaseError } from "./migrate-supabase.js";
5
+ import { type MigrationPreview } from "./migrate-supabase-map.js";
6
6
  /**
7
7
  * FUTURE9 Phase B chunk 2 — schema migration write step.
8
8
  *
@@ -1,10 +1,10 @@
1
1
  import { mkdir, writeFile } from "node:fs/promises";
2
2
  import { isAbsolute, join, relative, resolve } from "node:path";
3
3
  import { z } from "zod";
4
- import { recoveryAsk, recoveryNone, } from "../recovery";
5
- import { inspectSupabase, } from "./migrate-supabase";
6
- import { mapSupabaseToPocketBase } from "./migrate-supabase-map";
7
- import { generateDataImportScripts } from "./migrate-supabase-scripts";
4
+ import { recoveryAsk, recoveryNone, } from "../recovery.js";
5
+ import { inspectSupabase, } from "./migrate-supabase.js";
6
+ import { mapSupabaseToPocketBase } from "./migrate-supabase-map.js";
7
+ import { generateDataImportScripts } from "./migrate-supabase-scripts.js";
8
8
  /**
9
9
  * FUTURE9 Phase B chunk 2 — schema migration write step.
10
10
  *
@@ -1,4 +1,4 @@
1
- import type { MigrationPreview } from "./migrate-supabase-map";
1
+ import type { MigrationPreview } from "./migrate-supabase-map.js";
2
2
  /**
3
3
  * FUTURE9 Phase C — data-import script generator.
4
4
  *
@@ -121,6 +121,40 @@ export interface RewriteSupabaseSdkResult {
121
121
  * code has no Supabase calls left to match).
122
122
  */
123
123
  export declare function rewriteSupabaseSdk(input: RewriteSupabaseSdkInput): RewriteSupabaseSdkResult;
124
+ export interface SupabaseUsageScan {
125
+ /** File imports from `@supabase/*` (static import, dynamic import, or require). */
126
+ importsSupabase: boolean;
127
+ /** `supabase.auth.*(...)` call sites. */
128
+ authCalls: number;
129
+ /** `supabase.from(...)` CRUD chains. Reported, but NOT part of
130
+ * migrationComplete — a remaining `.from()` keeps the file importing
131
+ * the client, so `importsSupabase` is the backstop. */
132
+ fromCalls: number;
133
+ /** `supabase.storage.*` chains. */
134
+ storage: number;
135
+ /** `supabase.rpc(...)` sites. */
136
+ rpc: number;
137
+ /** `supabase.channel(...)` realtime chains. */
138
+ realtime: number;
139
+ /** `supabase.functions.invoke(...)` sites. */
140
+ edgeFunctions: number;
141
+ /** File reads `pb.<x>` as a member at all (whether or not `pb` is bound). */
142
+ usesPb: boolean;
143
+ /** File reads `pb.<x>` but has no `pb` binding (import / declaration /
144
+ * param) in scope — the dangling reference a rewrite leaves until the
145
+ * PocketBase client is wired. */
146
+ danglingPb: boolean;
147
+ /** Set when the source failed to parse; all counts are 0. */
148
+ parseError?: string;
149
+ }
150
+ export declare function scanSupabaseUsage(input: {
151
+ source: string;
152
+ supabaseIdentifiers?: string[];
153
+ /** PocketBase identifier to check for usage/dangling refs. Defaults to `pb`.
154
+ * MUST match the `pbIdentifier` a rewrite emitted, or the dangling-pb check
155
+ * silently passes (e.g. a project rewritten with pbIdentifier "db"). */
156
+ pbIdentifier?: string;
157
+ }): SupabaseUsageScan;
124
158
  /**
125
159
  * Entry point matching jscodeshift's `transform` signature, in case
126
160
  * a caller wants to run this as a standalone codemod via the
@@ -1 +1 @@
1
- {"version":3,"file":"migrate-supabase-sdk.d.ts","sourceRoot":"","sources":["../../src/commands/migrate-supabase-sdk.ts"],"names":[],"mappings":"AAAA,OAAoB,EAClB,KAAK,GAAG,EAGR,KAAK,QAAQ,EACb,KAAK,OAAO,EACb,MAAM,aAAa,CAAC;AAErB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AAEH,MAAM,MAAM,eAAe;AACzB,sDAAsD;AACpD,WAAW;AACb,sEAAsE;GACpE,eAAe;AACjB,uHAAuH;GACrH,aAAa,CAAC;AAElB,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,eAAe,CAAC;IACtB,0EAA0E;IAC1E,OAAO,EAAE,MAAM,CAAC;IAChB,0FAA0F;IAC1F,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,uEAAuE;IACvE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,gEAAgE;IAChE,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,uBAAuB;IACtC,0BAA0B;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,8EAA8E;IAC9E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;;OAKG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;;OAKG;IACH,mBAAmB,CAAC,EAAE,MAAM,EAAE,CAAC;CAChC;AAED,MAAM,WAAW,wBAAwB;IACvC,uFAAuF;IACvF,SAAS,EAAE,MAAM,CAAC;IAClB,uGAAuG;IACvG,KAAK,EAAE,WAAW,EAAE,CAAC;IACrB;;;;;;;;;;;;OAYG;IACH,OAAO,EAAE,OAAO,CAAC;IACjB;;;;;;;;OAQG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAKD;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,uBAAuB,GAAG,wBAAwB,CAsD3F;AAimDD;;;;;;;GAOG;AACH,MAAM,CAAC,OAAO,UAAU,SAAS,CAAC,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,GAAG,EAAE,OAAO,GAAE,OAAY,GAAG,MAAM,GAAG,IAAI,CAOjG"}
1
+ {"version":3,"file":"migrate-supabase-sdk.d.ts","sourceRoot":"","sources":["../../src/commands/migrate-supabase-sdk.ts"],"names":[],"mappings":"AAAA,OAAoB,EAClB,KAAK,GAAG,EAGR,KAAK,QAAQ,EACb,KAAK,OAAO,EACb,MAAM,aAAa,CAAC;AAGrB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AAEH,MAAM,MAAM,eAAe;AACzB,sDAAsD;AACpD,WAAW;AACb,sEAAsE;GACpE,eAAe;AACjB,uHAAuH;GACrH,aAAa,CAAC;AAElB,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,eAAe,CAAC;IACtB,0EAA0E;IAC1E,OAAO,EAAE,MAAM,CAAC;IAChB,0FAA0F;IAC1F,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,uEAAuE;IACvE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,gEAAgE;IAChE,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,uBAAuB;IACtC,0BAA0B;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,8EAA8E;IAC9E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;;OAKG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;;OAKG;IACH,mBAAmB,CAAC,EAAE,MAAM,EAAE,CAAC;CAChC;AAED,MAAM,WAAW,wBAAwB;IACvC,uFAAuF;IACvF,SAAS,EAAE,MAAM,CAAC;IAClB,uGAAuG;IACvG,KAAK,EAAE,WAAW,EAAE,CAAC;IACrB;;;;;;;;;;;;OAYG;IACH,OAAO,EAAE,OAAO,CAAC;IACjB;;;;;;;;OAQG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAKD;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,uBAAuB,GAAG,wBAAwB,CAsD3F;AAyiDD,MAAM,WAAW,iBAAiB;IAChC,mFAAmF;IACnF,eAAe,EAAE,OAAO,CAAC;IACzB,yCAAyC;IACzC,SAAS,EAAE,MAAM,CAAC;IAClB;;4DAEwD;IACxD,SAAS,EAAE,MAAM,CAAC;IAClB,mCAAmC;IACnC,OAAO,EAAE,MAAM,CAAC;IAChB,iCAAiC;IACjC,GAAG,EAAE,MAAM,CAAC;IACZ,+CAA+C;IAC/C,QAAQ,EAAE,MAAM,CAAC;IACjB,8CAA8C;IAC9C,aAAa,EAAE,MAAM,CAAC;IACtB,6EAA6E;IAC7E,MAAM,EAAE,OAAO,CAAC;IAChB;;sCAEkC;IAClC,UAAU,EAAE,OAAO,CAAC;IACpB,6DAA6D;IAC7D,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAcD,wBAAgB,iBAAiB,CAAC,KAAK,EAAE;IACvC,MAAM,EAAE,MAAM,CAAC;IACf,mBAAmB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC/B;;6EAEyE;IACzE,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,GAAG,iBAAiB,CA4DpB;AAqLD;;;;;;;GAOG;AACH,MAAM,CAAC,OAAO,UAAU,SAAS,CAAC,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,GAAG,EAAE,OAAO,GAAE,OAAY,GAAG,MAAM,GAAG,IAAI,CAOjG"}
@@ -1,4 +1,5 @@
1
1
  import jscodeshift, {} from "jscodeshift";
2
+ import { recipeFor } from "./migrate-supabase-recipes.js";
2
3
  /** AST library pre-bound to the TypeScript parser via jscodeshift's `.withParser`. */
3
4
  const tsx = jscodeshift.withParser("tsx");
4
5
  /**
@@ -179,6 +180,21 @@ function rewriteAuthCalls(root, supabaseNames, pbName, flags, state) {
179
180
  case "onAuthStateChange":
180
181
  replaceAuthOnChange(path, pbName, flags, state);
181
182
  return;
183
+ default: {
184
+ // Any auth method we don't map (getSession, getClaims,
185
+ // refreshSession, signInWithOtp, verifyOtp,
186
+ // resetPasswordForEmail, updateUser, …). NEVER pass it
187
+ // through silently — left untouched it still calls Supabase,
188
+ // so flag it or the report under-reports the remaining work.
189
+ // When we have a concrete PB recipe, fold it into the note so
190
+ // the flag is actionable, not just "do it yourself".
191
+ const recipe = recipeFor(`auth.${method}`);
192
+ const note = recipe
193
+ ? `supabase.auth.${method}(...) isn't auto-translated (the PB equivalent diverges) — it still calls Supabase. ${recipe.recipe}`
194
+ : `supabase.auth.${method}(...) isn't auto-translated — it still calls Supabase. Map it to PocketBase's auth API by hand (https://pocketbase.io/docs/authentication/).`;
195
+ flags.push(makeFlag(path, "unsupported", `auth.${method}`, note));
196
+ return;
197
+ }
182
198
  }
183
199
  });
184
200
  }
@@ -1047,6 +1063,159 @@ function readFirstStringArg(call) {
1047
1063
  }
1048
1064
  return null;
1049
1065
  }
1066
+ const EMPTY_SCAN = {
1067
+ importsSupabase: false,
1068
+ authCalls: 0,
1069
+ fromCalls: 0,
1070
+ storage: 0,
1071
+ rpc: 0,
1072
+ realtime: 0,
1073
+ edgeFunctions: 0,
1074
+ usesPb: false,
1075
+ danglingPb: false,
1076
+ };
1077
+ export function scanSupabaseUsage(input) {
1078
+ const supabaseNames = new Set(input.supabaseIdentifiers ?? ["supabase"]);
1079
+ const pbName = input.pbIdentifier ?? "pb";
1080
+ const mentionsSupabase = sourceMentionsSupabase(input.source, supabaseNames);
1081
+ // includes() (not a word-boundary regex) so a custom identifier with regex-
1082
+ // special chars can't break the fast-path; a false positive only costs a parse.
1083
+ const mentionsPb = input.source.includes(pbName);
1084
+ if (!mentionsSupabase && !mentionsPb)
1085
+ return { ...EMPTY_SCAN };
1086
+ let root;
1087
+ try {
1088
+ root = tsx(input.source);
1089
+ }
1090
+ catch (err) {
1091
+ return { ...EMPTY_SCAN, parseError: err instanceof Error ? err.message : String(err) };
1092
+ }
1093
+ const scan = { ...EMPTY_SCAN };
1094
+ scan.importsSupabase = importsSupabasePackage(root);
1095
+ root.find(jscodeshift.CallExpression).forEach((path) => {
1096
+ const chainRoot = findChainRoot(path.node);
1097
+ if (chainRoot?.type !== "Identifier")
1098
+ return;
1099
+ if (!supabaseNames.has(chainRoot.name))
1100
+ return;
1101
+ // Count once per chain — only at the outermost call.
1102
+ const parent = path.parent?.node;
1103
+ if (parent?.type === "MemberExpression" &&
1104
+ parent.object === path.node) {
1105
+ return;
1106
+ }
1107
+ const steps = readChainSteps(path.node);
1108
+ const first = steps[0];
1109
+ if (!first)
1110
+ return;
1111
+ switch (first.method) {
1112
+ case "auth":
1113
+ scan.authCalls += 1;
1114
+ break;
1115
+ case "from":
1116
+ scan.fromCalls += 1;
1117
+ break;
1118
+ case "storage":
1119
+ scan.storage += 1;
1120
+ break;
1121
+ case "rpc":
1122
+ scan.rpc += 1;
1123
+ break;
1124
+ case "channel":
1125
+ scan.realtime += 1;
1126
+ break;
1127
+ case "functions":
1128
+ if (steps[1]?.method === "invoke")
1129
+ scan.edgeFunctions += 1;
1130
+ break;
1131
+ }
1132
+ });
1133
+ const pb = analyzePbBinding(root, pbName);
1134
+ scan.usesPb = pb.usesPb;
1135
+ scan.danglingPb = pb.usesPb && !pb.declared;
1136
+ return scan;
1137
+ }
1138
+ /** True when the program imports/requires any `@supabase/*` package. */
1139
+ function importsSupabasePackage(root) {
1140
+ let found = false;
1141
+ root.find(jscodeshift.ImportDeclaration).forEach((p) => {
1142
+ const src = p.node.source.value;
1143
+ if (typeof src === "string" && src.startsWith("@supabase/"))
1144
+ found = true;
1145
+ });
1146
+ if (found)
1147
+ return true;
1148
+ // require("@supabase/...") and dynamic import("@supabase/...")
1149
+ root.find(jscodeshift.CallExpression).forEach((p) => {
1150
+ const callee = p.node.callee;
1151
+ const isRequire = callee.type === "Identifier" && callee.name === "require";
1152
+ const isDynImport = callee.type === "Import";
1153
+ if (!isRequire && !isDynImport)
1154
+ return;
1155
+ const arg = p.node.arguments[0];
1156
+ if ((arg?.type === "Literal" || arg?.type === "StringLiteral") &&
1157
+ typeof arg.value === "string" &&
1158
+ arg.value.startsWith("@supabase/")) {
1159
+ found = true;
1160
+ }
1161
+ });
1162
+ return found;
1163
+ }
1164
+ /** Whether `pbName` is read as an object (`pbName.x`) and whether it's bound. */
1165
+ function analyzePbBinding(root, pbName) {
1166
+ let declared = false;
1167
+ root.find(jscodeshift.ImportDeclaration).forEach((p) => {
1168
+ for (const spec of p.node.specifiers ?? []) {
1169
+ if (spec.local?.name === pbName)
1170
+ declared = true;
1171
+ }
1172
+ });
1173
+ root.find(jscodeshift.VariableDeclarator).forEach((p) => {
1174
+ const id = p.node.id;
1175
+ if (id.type === "Identifier" && id.name === pbName)
1176
+ declared = true;
1177
+ if (id.type === "ObjectPattern" &&
1178
+ objectPatternBindsName(id, pbName)) {
1179
+ declared = true;
1180
+ }
1181
+ });
1182
+ // Function params named pbName (any function kind). A param is itself a
1183
+ // binding, so its body's `pbName.x` isn't dangling.
1184
+ const checkParams = (params) => {
1185
+ for (const param of params) {
1186
+ if (param.type === "Identifier" && param.name === pbName) {
1187
+ declared = true;
1188
+ }
1189
+ }
1190
+ };
1191
+ root.find(jscodeshift.FunctionDeclaration).forEach((p) => {
1192
+ checkParams(p.node.params);
1193
+ });
1194
+ root.find(jscodeshift.FunctionExpression).forEach((p) => {
1195
+ checkParams(p.node.params);
1196
+ });
1197
+ root.find(jscodeshift.ArrowFunctionExpression).forEach((p) => {
1198
+ checkParams(p.node.params);
1199
+ });
1200
+ let usedAsObject = false;
1201
+ root.find(jscodeshift.MemberExpression).forEach((p) => {
1202
+ const obj = p.node.object;
1203
+ if (obj.type === "Identifier" && obj.name === pbName)
1204
+ usedAsObject = true;
1205
+ });
1206
+ return { usesPb: usedAsObject, declared };
1207
+ }
1208
+ function objectPatternBindsName(pattern, name) {
1209
+ for (const prop of pattern.properties) {
1210
+ if ((prop.type === "Property" || prop.type === "ObjectProperty") &&
1211
+ "value" in prop &&
1212
+ prop.value.type === "Identifier" &&
1213
+ prop.value.name === name) {
1214
+ return true;
1215
+ }
1216
+ }
1217
+ return false;
1218
+ }
1050
1219
  function findObjectProp(obj, name) {
1051
1220
  for (const prop of obj.properties) {
1052
1221
  if ((prop.type === "Property" || prop.type === "ObjectProperty") &&