@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.
- package/LICENSE +21 -0
- package/README.md +118 -0
- package/lib/commands/adopt.d.ts +38 -0
- package/lib/commands/adopt.js +105 -0
- package/lib/commands/init.d.ts +29 -0
- package/lib/commands/init.js +152 -0
- package/lib/commands/upgrade.d.ts +33 -0
- package/lib/commands/upgrade.js +280 -0
- package/lib/index.d.ts +2 -0
- package/lib/index.js +123 -0
- package/lib/install-shared.d.ts +72 -0
- package/lib/install-shared.js +256 -0
- package/lib/manifest.d.ts +53 -0
- package/lib/manifest.js +74 -0
- package/lib/substitute.d.ts +15 -0
- package/lib/substitute.js +25 -0
- package/package.json +32 -0
- package/templates/functions/_shared/client.ts +20 -0
- package/templates/functions/_shared/config.ts +23 -0
- package/templates/functions/_shared/moderation.ts +102 -0
- package/templates/functions/_shared/push.ts +33 -0
- package/templates/functions/_shared/slack.ts +12 -0
- package/templates/functions/broadcast-post/index.ts +73 -0
- package/templates/functions/daily-moderation/index.ts +175 -0
- package/templates/functions/moderate-one/index.ts +62 -0
- package/templates/functions/notify-comment/index.ts +63 -0
- package/templates/functions/notify-like/index.ts +103 -0
- package/templates/functions/notify-reaction/index.ts +186 -0
- package/templates/functions/report-to-slack/index.ts +37 -0
- package/templates/functions/update-profile/index.ts +131 -0
- package/templates/migrations/core/001_tables.sql +177 -0
- package/templates/migrations/core/002_rls.sql +154 -0
- package/templates/migrations/core/003_moderation.sql +68 -0
- package/templates/migrations/core/004_dashboard.sql +102 -0
- package/templates/migrations/core/005_avatars.sql +19 -0
- package/templates/migrations/core/006_meta.sql +11 -0
- package/templates/migrations/inbox/001_inbox.sql +237 -0
- package/templates/migrations/polls/001_polls.sql +120 -0
- package/templates/migrations/push/001_push.sql +42 -0
- package/templates/migrations/push/002_triggers.sql +89 -0
- 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
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;
|