@rocapine/community 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +118 -0
  3. package/lib/commands/adopt.d.ts +38 -0
  4. package/lib/commands/adopt.js +105 -0
  5. package/lib/commands/init.d.ts +29 -0
  6. package/lib/commands/init.js +152 -0
  7. package/lib/commands/upgrade.d.ts +33 -0
  8. package/lib/commands/upgrade.js +280 -0
  9. package/lib/index.d.ts +2 -0
  10. package/lib/index.js +123 -0
  11. package/lib/install-shared.d.ts +72 -0
  12. package/lib/install-shared.js +256 -0
  13. package/lib/manifest.d.ts +53 -0
  14. package/lib/manifest.js +74 -0
  15. package/lib/substitute.d.ts +15 -0
  16. package/lib/substitute.js +25 -0
  17. package/package.json +32 -0
  18. package/templates/functions/_shared/client.ts +20 -0
  19. package/templates/functions/_shared/config.ts +23 -0
  20. package/templates/functions/_shared/moderation.ts +102 -0
  21. package/templates/functions/_shared/push.ts +33 -0
  22. package/templates/functions/_shared/slack.ts +12 -0
  23. package/templates/functions/broadcast-post/index.ts +73 -0
  24. package/templates/functions/daily-moderation/index.ts +175 -0
  25. package/templates/functions/moderate-one/index.ts +62 -0
  26. package/templates/functions/notify-comment/index.ts +63 -0
  27. package/templates/functions/notify-like/index.ts +103 -0
  28. package/templates/functions/notify-reaction/index.ts +186 -0
  29. package/templates/functions/report-to-slack/index.ts +37 -0
  30. package/templates/functions/update-profile/index.ts +131 -0
  31. package/templates/migrations/core/001_tables.sql +177 -0
  32. package/templates/migrations/core/002_rls.sql +154 -0
  33. package/templates/migrations/core/003_moderation.sql +68 -0
  34. package/templates/migrations/core/004_dashboard.sql +102 -0
  35. package/templates/migrations/core/005_avatars.sql +19 -0
  36. package/templates/migrations/core/006_meta.sql +11 -0
  37. package/templates/migrations/inbox/001_inbox.sql +237 -0
  38. package/templates/migrations/polls/001_polls.sql +120 -0
  39. package/templates/migrations/push/001_push.sql +42 -0
  40. package/templates/migrations/push/002_triggers.sql +89 -0
  41. package/templates/migrations/reaction/001_reactions.sql +139 -0
@@ -0,0 +1,280 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.NOT_INITIALIZED_MESSAGE = void 0;
37
+ exports.runUpgrade = runUpgrade;
38
+ const fs = __importStar(require("node:fs"));
39
+ const os = __importStar(require("node:os"));
40
+ const path = __importStar(require("node:path"));
41
+ const substitute_1 = require("../substitute");
42
+ const manifest_1 = require("../manifest");
43
+ const install_shared_1 = require("../install-shared");
44
+ exports.NOT_INITIALIZED_MESSAGE = "community-sdk: not initialized, run init (or adopt)";
45
+ async function runUpgrade(options = {}) {
46
+ const cwd = options.cwd ?? process.cwd();
47
+ const log = options.log ?? ((message) => console.log(message));
48
+ const onWarn = options.onWarn ?? ((message) => console.warn(message));
49
+ const manifest = (0, manifest_1.readManifest)(cwd);
50
+ if (!manifest) {
51
+ throw new Error(exports.NOT_INITIALIZED_MESSAGE);
52
+ }
53
+ const dirRel = options.dir ?? "supabase";
54
+ const dir = (0, install_shared_1.resolveContainedDir)(cwd, dirRel);
55
+ const templatesDir = options.templatesDir ?? (0, install_shared_1.defaultTemplatesDir)();
56
+ const { migrationsSrcRoot, functionsSrcRoot } = (0, install_shared_1.resolveTemplateRoots)(templatesDir);
57
+ // Only modules the app actually has installed are diffed/re-synced —
58
+ // upgrade never adds or removes modules (that's what re-running init on a
59
+ // fresh dir, or a future dedicated command, would be for).
60
+ const canonicalModules = install_shared_1.MODULE_ORDER.filter((m) => manifest.modules.includes(m));
61
+ const installedTemplateIds = new Set(manifest.installedTemplates ?? reconstructInstalledTemplateIds(manifest.installedFiles));
62
+ const pending = [];
63
+ for (const moduleName of canonicalModules) {
64
+ const moduleSrcDir = path.join(migrationsSrcRoot, moduleName);
65
+ if (!fs.existsSync(moduleSrcDir))
66
+ continue;
67
+ const filenames = fs
68
+ .readdirSync(moduleSrcDir)
69
+ .filter((f) => f.endsWith(".sql"))
70
+ .sort();
71
+ for (const filename of filenames) {
72
+ const id = (0, manifest_1.migrationTemplateId)(moduleName, (0, install_shared_1.templateBaseName)(filename));
73
+ if (installedTemplateIds.has(id))
74
+ continue;
75
+ pending.push({
76
+ moduleName,
77
+ templateFilename: filename,
78
+ rawSql: fs.readFileSync(path.join(moduleSrcDir, filename), "utf8"),
79
+ });
80
+ }
81
+ }
82
+ // ---- functions: new (not on disk yet) vs changed (content differs) ----
83
+ const functionsDestRoot = path.join(dir, "functions");
84
+ const newFunctions = [];
85
+ const changedFunctions = [];
86
+ for (const fnName of (0, install_shared_1.functionsForModules)(canonicalModules)) {
87
+ const srcDir = path.join(functionsSrcRoot, fnName);
88
+ if (!fs.existsSync(srcDir))
89
+ continue;
90
+ const destDir = path.join(functionsDestRoot, fnName);
91
+ if (!fs.existsSync(destDir)) {
92
+ newFunctions.push(fnName);
93
+ }
94
+ else if (functionDirDiffers(srcDir, destDir)) {
95
+ changedFunctions.push(fnName);
96
+ }
97
+ }
98
+ if (pending.length === 0 && newFunctions.length === 0 && changedFunctions.length === 0) {
99
+ log("community-sdk: already up to date");
100
+ return {
101
+ manifest,
102
+ dir,
103
+ upToDate: true,
104
+ addedMigrations: [],
105
+ newFunctions: [],
106
+ overwrittenFunctions: [],
107
+ };
108
+ }
109
+ // ---- credentials, only if a pending migration actually needs them ----
110
+ const needsCredentials = pending.some((p) => p.rawSql.includes("__SUPABASE"));
111
+ let projectUrl;
112
+ let anonKey;
113
+ if (needsCredentials) {
114
+ const creds = await (0, install_shared_1.resolveProjectCredentials)({
115
+ dir,
116
+ projectUrl: options.projectUrl,
117
+ anonKey: options.anonKey,
118
+ prompt: options.prompt,
119
+ });
120
+ projectUrl = creds.projectUrl;
121
+ anonKey = creds.anonKey;
122
+ }
123
+ const migrationsDestRoot = path.join(dir, "migrations");
124
+ const writtenMigrationPaths = [];
125
+ const addedMigrations = [];
126
+ const newInstalledTemplateIds = new Set(installedTemplateIds);
127
+ const createdFunctionDirs = [];
128
+ const backups = [];
129
+ try {
130
+ // ---- prepare every migration's final content up front: a placeholder
131
+ // error here must throw before a single byte is written to disk. ----
132
+ const now = options.now ?? new Date();
133
+ const prepared = pending.map((p) => {
134
+ if (!p.rawSql.includes("__SUPABASE"))
135
+ return { ...p, content: p.rawSql };
136
+ try {
137
+ return {
138
+ ...p,
139
+ content: (0, substitute_1.substitutePlaceholders)(p.rawSql, { projectUrl: projectUrl, anonKey: anonKey }),
140
+ };
141
+ }
142
+ catch (err) {
143
+ const message = err instanceof Error ? err.message : String(err);
144
+ throw new Error(`${message} (source: ${p.moduleName}/${p.templateFilename})`);
145
+ }
146
+ });
147
+ if (prepared.length > 0) {
148
+ fs.mkdirSync(migrationsDestRoot, { recursive: true });
149
+ prepared.forEach((p, index) => {
150
+ const timestamp = (0, install_shared_1.formatTimestamp)((0, install_shared_1.addSeconds)(now, index));
151
+ const name = (0, install_shared_1.templateBaseName)(p.templateFilename);
152
+ const destFilename = (0, install_shared_1.migrationDestFilename)(timestamp, p.moduleName, name);
153
+ const destPath = path.join(migrationsDestRoot, destFilename);
154
+ fs.writeFileSync(destPath, p.content, "utf8");
155
+ writtenMigrationPaths.push(destPath);
156
+ addedMigrations.push((0, install_shared_1.toRelativePosix)(cwd, destPath));
157
+ newInstalledTemplateIds.add((0, manifest_1.migrationTemplateId)(p.moduleName, name));
158
+ });
159
+ }
160
+ if (newFunctions.length > 0) {
161
+ fs.mkdirSync(functionsDestRoot, { recursive: true });
162
+ }
163
+ for (const fnName of newFunctions) {
164
+ const destDir = path.join(functionsDestRoot, fnName);
165
+ fs.cpSync(path.join(functionsSrcRoot, fnName), destDir, { recursive: true });
166
+ createdFunctionDirs.push(destDir);
167
+ }
168
+ for (const fnName of changedFunctions) {
169
+ const destDir = path.join(functionsDestRoot, fnName);
170
+ const backupDir = fs.mkdtempSync(path.join(os.tmpdir(), "community-sdk-upgrade-backup-"));
171
+ fs.cpSync(destDir, backupDir, { recursive: true });
172
+ backups.push({ destDir, backupDir });
173
+ fs.rmSync(destDir, { recursive: true, force: true });
174
+ fs.cpSync(path.join(functionsSrcRoot, fnName), destDir, { recursive: true });
175
+ }
176
+ }
177
+ catch (err) {
178
+ for (const p of writtenMigrationPaths)
179
+ fs.rmSync(p, { force: true });
180
+ for (const d of createdFunctionDirs)
181
+ fs.rmSync(d, { recursive: true, force: true });
182
+ for (const b of backups) {
183
+ fs.rmSync(b.destDir, { recursive: true, force: true });
184
+ fs.cpSync(b.backupDir, b.destDir, { recursive: true });
185
+ }
186
+ throw err;
187
+ }
188
+ finally {
189
+ for (const b of backups)
190
+ fs.rmSync(b.backupDir, { recursive: true, force: true });
191
+ }
192
+ // ---- manifest: fold in the newly added migrations + refreshed function
193
+ // file listings, dropping any stale entries for files a touched function
194
+ // no longer ships (e.g. one removed from a newer template). ----
195
+ const touchedFunctionDirs = [
196
+ ...createdFunctionDirs,
197
+ ...changedFunctions.map((fnName) => path.join(functionsDestRoot, fnName)),
198
+ ];
199
+ const touchedFunctionDirPrefixes = touchedFunctionDirs.map((d) => `${(0, install_shared_1.toRelativePosix)(cwd, d)}/`);
200
+ const touchedFunctionFiles = touchedFunctionDirs.flatMap((d) => (0, install_shared_1.listFilesRecursive)(d).map((f) => (0, install_shared_1.toRelativePosix)(cwd, f)));
201
+ const survivingOldFiles = manifest.installedFiles.filter((f) => !touchedFunctionDirPrefixes.some((prefix) => f.startsWith(prefix)));
202
+ const installedFiles = [
203
+ ...new Set([...survivingOldFiles, ...touchedFunctionFiles, ...addedMigrations]),
204
+ ].sort();
205
+ const updatedManifest = {
206
+ schemaVersion: manifest_1.CURRENT_SCHEMA_VERSION,
207
+ sdkVersion: (0, install_shared_1.readOwnPackageVersion)(),
208
+ modules: manifest.modules,
209
+ installedFiles,
210
+ installedTemplates: [...newInstalledTemplateIds].sort(),
211
+ };
212
+ (0, manifest_1.writeManifest)(cwd, updatedManifest);
213
+ printSummary(log, onWarn, {
214
+ addedMigrations,
215
+ newFunctions,
216
+ overwrittenFunctions: changedFunctions,
217
+ });
218
+ return {
219
+ manifest: updatedManifest,
220
+ dir,
221
+ upToDate: false,
222
+ addedMigrations,
223
+ newFunctions,
224
+ overwrittenFunctions: changedFunctions,
225
+ };
226
+ }
227
+ /**
228
+ * Fallback for a manifest written before `installedTemplates` existed
229
+ * (schemaVersion 1 `init`, Task 18): recovers the same `<module>/<name>`
230
+ * identity from the timestamped filenames already recorded in
231
+ * `installedFiles`. Non-migration entries (function files) don't match the
232
+ * pattern and are silently skipped.
233
+ */
234
+ function reconstructInstalledTemplateIds(installedFiles) {
235
+ const ids = [];
236
+ for (const f of installedFiles) {
237
+ const parsed = (0, install_shared_1.parseMigrationDestFilename)(path.basename(f));
238
+ if (!parsed)
239
+ continue;
240
+ ids.push((0, manifest_1.migrationTemplateId)(parsed.moduleName, parsed.name));
241
+ }
242
+ return ids;
243
+ }
244
+ function functionDirDiffers(srcDir, destDir) {
245
+ const srcRel = new Set((0, install_shared_1.listFilesRecursive)(srcDir).map((f) => path.relative(srcDir, f)));
246
+ const destRel = new Set((0, install_shared_1.listFilesRecursive)(destDir).map((f) => path.relative(destDir, f)));
247
+ if (srcRel.size !== destRel.size)
248
+ return true;
249
+ for (const rel of srcRel) {
250
+ if (!destRel.has(rel))
251
+ return true;
252
+ const a = fs.readFileSync(path.join(srcDir, rel));
253
+ const b = fs.readFileSync(path.join(destDir, rel));
254
+ if (!a.equals(b))
255
+ return true;
256
+ }
257
+ return false;
258
+ }
259
+ function printSummary(log, onWarn, summary) {
260
+ log("");
261
+ log("community-sdk upgraded.");
262
+ if (summary.addedMigrations.length > 0) {
263
+ log(`Added ${summary.addedMigrations.length} migration(s):`);
264
+ for (const f of summary.addedMigrations)
265
+ log(` ${f}`);
266
+ }
267
+ if (summary.newFunctions.length > 0) {
268
+ log(`Added ${summary.newFunctions.length} function(s): ${summary.newFunctions.join(", ")}`);
269
+ }
270
+ if (summary.overwrittenFunctions.length > 0) {
271
+ onWarn(`community-sdk: overwrote ${summary.overwrittenFunctions.length} function(s) with new template code — review before deploying: ${summary.overwrittenFunctions.join(", ")}`);
272
+ }
273
+ log("Next steps:");
274
+ if (summary.addedMigrations.length > 0) {
275
+ log(" 1. Review the new migrations, then: supabase db push");
276
+ }
277
+ if (summary.newFunctions.length > 0 || summary.overwrittenFunctions.length > 0) {
278
+ log(" 2. Review the function changes, then: supabase functions deploy");
279
+ }
280
+ }
package/lib/index.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/lib/index.js ADDED
@@ -0,0 +1,123 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
4
+ if (k2 === undefined) k2 = k;
5
+ var desc = Object.getOwnPropertyDescriptor(m, k);
6
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
7
+ desc = { enumerable: true, get: function() { return m[k]; } };
8
+ }
9
+ Object.defineProperty(o, k2, desc);
10
+ }) : (function(o, m, k, k2) {
11
+ if (k2 === undefined) k2 = k;
12
+ o[k2] = m[k];
13
+ }));
14
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
15
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
16
+ }) : function(o, v) {
17
+ o["default"] = v;
18
+ });
19
+ var __importStar = (this && this.__importStar) || (function () {
20
+ var ownKeys = function(o) {
21
+ ownKeys = Object.getOwnPropertyNames || function (o) {
22
+ var ar = [];
23
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
24
+ return ar;
25
+ };
26
+ return ownKeys(o);
27
+ };
28
+ return function (mod) {
29
+ if (mod && mod.__esModule) return mod;
30
+ var result = {};
31
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
32
+ __setModuleDefault(result, mod);
33
+ return result;
34
+ };
35
+ })();
36
+ Object.defineProperty(exports, "__esModule", { value: true });
37
+ const fs = __importStar(require("node:fs"));
38
+ const path = __importStar(require("node:path"));
39
+ const commander_1 = require("commander");
40
+ const init_1 = require("./commands/init");
41
+ const upgrade_1 = require("./commands/upgrade");
42
+ const adopt_1 = require("./commands/adopt");
43
+ function readOwnVersion() {
44
+ const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, "..", "package.json"), "utf8"));
45
+ return pkg.version;
46
+ }
47
+ function parseModulesFlag(modules) {
48
+ return modules
49
+ ? modules
50
+ .split(",")
51
+ .map((m) => m.trim())
52
+ .filter(Boolean)
53
+ : undefined;
54
+ }
55
+ const program = new commander_1.Command();
56
+ program
57
+ .name("community")
58
+ .description("Install and manage the Rocapine Community SDK backend (Supabase migrations + Edge Functions)")
59
+ .version(readOwnVersion());
60
+ program
61
+ .command("init")
62
+ .description("Copy the community backend into a Supabase project: migrations, Edge Functions, and a community-sdk.json manifest")
63
+ .option("--modules <modules>", "comma-separated module list (core,push,polls,reaction,inbox) — core is always implied; defaults to all modules")
64
+ .option("--project-url <url>", "Supabase project URL, e.g. https://<ref>.supabase.co")
65
+ .option("--anon-key <key>", "Supabase anon key")
66
+ .option("--dir <dir>", "target Supabase directory, relative to the current directory", "supabase")
67
+ .action(async (opts) => {
68
+ try {
69
+ await (0, init_1.runInit)({
70
+ modules: parseModulesFlag(opts.modules),
71
+ projectUrl: opts.projectUrl,
72
+ anonKey: opts.anonKey,
73
+ dir: opts.dir,
74
+ });
75
+ }
76
+ catch (err) {
77
+ console.error(err instanceof Error ? err.message : String(err));
78
+ process.exitCode = 1;
79
+ }
80
+ });
81
+ program
82
+ .command("upgrade")
83
+ .description("Copy any new migrations/Edge Functions shipped since this repo's community-sdk.json was last written, and overwrite changed function code (with a warning)")
84
+ .option("--project-url <url>", "Supabase project URL, e.g. https://<ref>.supabase.co (only needed if a new migration carries a placeholder)")
85
+ .option("--anon-key <key>", "Supabase anon key (only needed if a new migration carries a placeholder)")
86
+ .option("--dir <dir>", "target Supabase directory, relative to the current directory", "supabase")
87
+ .action(async (opts) => {
88
+ try {
89
+ await (0, upgrade_1.runUpgrade)({
90
+ projectUrl: opts.projectUrl,
91
+ anonKey: opts.anonKey,
92
+ dir: opts.dir,
93
+ });
94
+ }
95
+ catch (err) {
96
+ console.error(err instanceof Error ? err.message : String(err));
97
+ process.exitCode = 1;
98
+ }
99
+ });
100
+ program
101
+ .command("adopt")
102
+ .description("Register an already-live community backend (e.g. an app that hand-rolled its own before this SDK existed) with community-sdk.json, without copying any files")
103
+ .requiredOption("--schema-version <n>", "the community-sdk schema version this app's live backend corresponds to")
104
+ .requiredOption("--modules <modules>", "comma-separated module list (core,push,polls,reaction,inbox) — core is always implied")
105
+ .option("--dir <dir>", "Supabase directory this app would use, relative to the current directory", "supabase")
106
+ .action(async (opts) => {
107
+ try {
108
+ const schemaVersion = Number(opts.schemaVersion);
109
+ await (0, adopt_1.runAdopt)({
110
+ schemaVersion,
111
+ modules: parseModulesFlag(opts.modules) ?? [],
112
+ dir: opts.dir,
113
+ });
114
+ }
115
+ catch (err) {
116
+ console.error(err instanceof Error ? err.message : String(err));
117
+ process.exitCode = 1;
118
+ }
119
+ });
120
+ program.parseAsync(process.argv).catch((err) => {
121
+ console.error(err instanceof Error ? err.message : String(err));
122
+ process.exitCode = 1;
123
+ });
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Ruled install order (controller ruling, task 18): core -> push -> polls ->
3
+ * reaction -> inbox. Reaction MUST land before inbox — inbox/001's reaction
4
+ * trigger is guarded with `to_regclass('public.post_reactions')` at install
5
+ * time and is never retroactively wired if the reaction module shows up
6
+ * later, so `reaction` needs to already exist when `inbox` runs.
7
+ *
8
+ * Shared between `init` (task 18) and `upgrade`/`adopt` (task 19) — the
9
+ * single source of truth for module names, order, and the function sets
10
+ * each module pulls in.
11
+ */
12
+ export declare const MODULE_ORDER: readonly ["core", "push", "polls", "reaction", "inbox"];
13
+ export type Module = (typeof MODULE_ORDER)[number];
14
+ export declare function functionsForModules(modules: readonly Module[]): string[];
15
+ /**
16
+ * Validates and canonicalizes a requested module list: rejects unknown
17
+ * names, always implies `core`, orders the result per MODULE_ORDER, and
18
+ * warns (non-fatally) when `inbox` is picked without `reaction`.
19
+ */
20
+ export declare function resolveModules(requested: string[] | undefined, onWarn: (message: string) => void): Module[];
21
+ /**
22
+ * Strips a template migration's leading numeric ordering prefix and `.sql`
23
+ * extension, e.g. "001_tables.sql" -> "tables". This is both the middle
24
+ * segment of the timestamped destination filename init/upgrade write
25
+ * (`<ts>_community_<module>_<name>.sql`) AND — paired with the module name —
26
+ * the stable identity `upgrade` diffs against (see manifest.ts
27
+ * `migrationTemplateId`).
28
+ */
29
+ export declare function templateBaseName(templateFilename: string): string;
30
+ export declare function migrationDestFilename(timestamp: string, moduleName: string, name: string): string;
31
+ /**
32
+ * Parses a filename previously written by init/upgrade
33
+ * (`<ts>_community_<module>_<name>.sql`) back into its module + base name.
34
+ * Returns null for anything else (e.g. a function file, or a migration a
35
+ * user hand-wrote outside the SDK) — used by `upgrade` as a fallback when
36
+ * reconstructing template identity from a manifest that predates the
37
+ * `installedTemplates` field.
38
+ */
39
+ export declare function parseMigrationDestFilename(filename: string): {
40
+ moduleName: string;
41
+ name: string;
42
+ } | null;
43
+ export declare function defaultTemplatesDir(): string;
44
+ /** Asserts `templatesDir` looks like a real template tree and returns its two roots. */
45
+ export declare function resolveTemplateRoots(templatesDir: string): {
46
+ migrationsSrcRoot: string;
47
+ functionsSrcRoot: string;
48
+ };
49
+ export declare function readOwnPackageVersion(): string;
50
+ export declare function deriveProjectUrlFromConfig(dir: string): string | null;
51
+ /**
52
+ * Resolves the Supabase project URL + anon key from (in order) explicit
53
+ * flags, `<dir>/config.toml` (URL only), or an interactive prompt — failing
54
+ * loudly instead of hanging when stdin is not a TTY and no `prompt`
55
+ * override was injected. Shared by `init` and `upgrade`, both of which only
56
+ * need this when a placeholder-bearing file is about to be written.
57
+ */
58
+ export declare function resolveProjectCredentials(options: {
59
+ dir: string;
60
+ projectUrl?: string;
61
+ anonKey?: string;
62
+ prompt?: (question: string) => Promise<string>;
63
+ }): Promise<{
64
+ projectUrl: string;
65
+ anonKey: string;
66
+ }>;
67
+ export declare function formatTimestamp(date: Date): string;
68
+ export declare function addSeconds(date: Date, seconds: number): Date;
69
+ export declare function listFilesRecursive(dir: string): string[];
70
+ export declare function toRelativePosix(base: string, target: string): string;
71
+ /** Resolves `dirRel` against `cwd`, throwing if it would escape the project root. */
72
+ export declare function resolveContainedDir(cwd: string, dirRel: string): string;