@protobase/cli 0.8.0 → 0.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/dev/entry.js +3 -1
- package/dist/main.js +2 -0
- package/dist/migration/load-project.js +41 -0
- package/dist/migration/parts.js +14 -0
- package/dist/migration/register.js +22 -0
- package/dist/migration/write-migrations.js +32 -0
- package/dist/package.js +1 -1
- package/dist/serve/app.js +1 -0
- package/dist/serve/lifecycle.js +4 -3
- package/package.json +6 -6
package/dist/dev/entry.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { loadProject, projectDb } from "./project.js";
|
|
2
|
-
import { checkAuthSchema, configExports, createAdmin, hasUsers } from "@protobase/server";
|
|
2
|
+
import { checkAuthSchema, checkChanges, configExports, createAdmin, hasUsers } from "@protobase/server";
|
|
3
3
|
import { Hono } from "hono";
|
|
4
4
|
//#region src/dev/entry.ts
|
|
5
5
|
const projectDir = process.env.PROTOBASE_PROJECT;
|
|
@@ -12,6 +12,7 @@ if (project.auth && !store.__protobaseAuthSchemaChecked) {
|
|
|
12
12
|
await checkAuthSchema(project.auth);
|
|
13
13
|
store.__protobaseAuthSchemaChecked = true;
|
|
14
14
|
}
|
|
15
|
+
await checkChanges(db, resources.map((resource) => resource.toModel()), project.events);
|
|
15
16
|
const admin = createAdmin({
|
|
16
17
|
resources,
|
|
17
18
|
views,
|
|
@@ -22,6 +23,7 @@ const admin = createAdmin({
|
|
|
22
23
|
...project.auth && { auth: project.auth },
|
|
23
24
|
...project.functions && { functions: project.functions },
|
|
24
25
|
...project.files && { files: project.files },
|
|
26
|
+
...project.events && { events: project.events },
|
|
25
27
|
options: {
|
|
26
28
|
onUnhandledError: (error) => console.error(error),
|
|
27
29
|
...project.options
|
package/dist/main.js
CHANGED
|
@@ -13,6 +13,7 @@ import { serveBundle } from "./serve/serve-bundle.js";
|
|
|
13
13
|
import { serveBundleDir } from "./serve/serve-dir.js";
|
|
14
14
|
import { registerOrganizations } from "./organizations/register.js";
|
|
15
15
|
import { registerFiles } from "./files/register.js";
|
|
16
|
+
import { registerMigration } from "./migration/register.js";
|
|
16
17
|
import { registerToken, registerUsers } from "./users/register.js";
|
|
17
18
|
import { statSync } from "node:fs";
|
|
18
19
|
import path from "node:path";
|
|
@@ -89,6 +90,7 @@ registerUsers(program);
|
|
|
89
90
|
registerOrganizations(program);
|
|
90
91
|
registerToken(program);
|
|
91
92
|
registerAuth(program);
|
|
93
|
+
registerMigration(program);
|
|
92
94
|
registerFiles(program);
|
|
93
95
|
const run = (argv) => program.parseAsync(argv).catch((error) => {
|
|
94
96
|
process.stderr.write(`error: ${error.message}\n`);
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { loadNearestEnvFile } from "../dev/env.js";
|
|
2
|
+
import { importProjectFile } from "../project/import-project-file.js";
|
|
3
|
+
import { createPgDb } from "../project/pg-db.js";
|
|
4
|
+
import { existsSync } from "node:fs";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import { configExports, mergeConfig } from "@protobase/server";
|
|
7
|
+
import pg from "pg";
|
|
8
|
+
//#region src/migration/load-project.ts
|
|
9
|
+
const configOf = async (file) => (await importProjectFile(file)).default ?? {};
|
|
10
|
+
/**
|
|
11
|
+
* The project in `dir` as `protobase dev` merges it, for what its database needs: protobase.config.ts, or
|
|
12
|
+
* config/index.ts when that has no `config`, after the configs of `extensions` and the configs it extends. `close`
|
|
13
|
+
* ends the pool it opened; a `db` the project exports stays the project's.
|
|
14
|
+
*/
|
|
15
|
+
const loadMigrationProject = async (dir, extensions = []) => {
|
|
16
|
+
loadNearestEnvFile(dir);
|
|
17
|
+
const file = path.join(dir, "protobase.config.ts");
|
|
18
|
+
const custom = existsSync(file) ? await configOf(file) : {};
|
|
19
|
+
const index = path.join(dir, "config", "index.ts");
|
|
20
|
+
if (!custom.config && !existsSync(index)) throw new Error(`No config/index.ts or protobase.config.ts in ${dir}; run this inside a project`);
|
|
21
|
+
const own = {
|
|
22
|
+
...custom,
|
|
23
|
+
config: custom.config ?? await importProjectFile(index)
|
|
24
|
+
};
|
|
25
|
+
const extended = await Promise.all(extensions.flatMap((extension) => extension.config ? [configOf(extension.config)] : []));
|
|
26
|
+
const project = mergeConfig(...extended, own);
|
|
27
|
+
const url = process.env.DATABASE_URL;
|
|
28
|
+
if (!project.db && !url) throw new Error("No database: set DATABASE_URL or export `db` from protobase.config.ts");
|
|
29
|
+
const ownDb = project.db ? void 0 : createPgDb(pg, url);
|
|
30
|
+
return {
|
|
31
|
+
models: configExports(project.config ?? {}).resources.map((source) => source.toModel()),
|
|
32
|
+
db: project.db ?? ownDb,
|
|
33
|
+
...project.auth && { auth: project.auth },
|
|
34
|
+
...project.events && { events: project.events },
|
|
35
|
+
close: async () => {
|
|
36
|
+
await ownDb?.destroy();
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
};
|
|
40
|
+
//#endregion
|
|
41
|
+
export { loadMigrationProject };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { authSchemaMigration, changesMigration } from "@protobase/server";
|
|
2
|
+
//#region src/migration/parts.ts
|
|
3
|
+
/** Every feature the project's config enables, in the order their files are written. */
|
|
4
|
+
const migrationParts = (project) => [...project.auth ? [{
|
|
5
|
+
name: "auth",
|
|
6
|
+
title: "The auth schema changes this version of Protobase needs",
|
|
7
|
+
sql: () => authSchemaMigration(project.auth)
|
|
8
|
+
}] : [], {
|
|
9
|
+
name: "changes",
|
|
10
|
+
title: "The change capture the resources' events need",
|
|
11
|
+
sql: () => changesMigration(project.db, project.models, project.events)
|
|
12
|
+
}];
|
|
13
|
+
//#endregion
|
|
14
|
+
export { migrationParts };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { resolveExtensions } from "../project/extensions.js";
|
|
2
|
+
import { loadMigrationProject } from "./load-project.js";
|
|
3
|
+
import { migrationParts } from "./parts.js";
|
|
4
|
+
import { writeMigrations } from "./write-migrations.js";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
//#region src/migration/register.ts
|
|
7
|
+
const out = (text) => process.stdout.write(text);
|
|
8
|
+
const collect = (value, previous) => [...previous, value];
|
|
9
|
+
const registerMigration = (program) => {
|
|
10
|
+
program.command("migration").description("Write what the database still needs for every feature the config enables (auth, events) as the project's next SQL migrations").option("--dir <dir>", "migrations folder", "db/migrations").option("--extend <dir>", "merge the protobase.config.ts in this folder before the project's, as dev and build do, repeatable", collect, []).action(async (opts) => {
|
|
11
|
+
const project = await loadMigrationProject(process.cwd(), resolveExtensions(opts.extend));
|
|
12
|
+
try {
|
|
13
|
+
const files = await writeMigrations(migrationParts(project), path.resolve(opts.dir));
|
|
14
|
+
out(files.length > 0 ? files.map((file) => `wrote ${path.relative(process.cwd(), file)}\n`).join("") + "apply them with the project's migrations\n" : "the database is up to date; nothing to write\n");
|
|
15
|
+
} finally {
|
|
16
|
+
await project.close();
|
|
17
|
+
}
|
|
18
|
+
process.exit(0);
|
|
19
|
+
});
|
|
20
|
+
};
|
|
21
|
+
//#endregion
|
|
22
|
+
export { registerMigration };
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { nextMigrationName } from "../auth/migration-file.js";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { mkdir, readdir, writeFile } from "node:fs/promises";
|
|
4
|
+
//#region src/migration/write-migrations.ts
|
|
5
|
+
/** A part's file: what wrote it and why, then its statements. */
|
|
6
|
+
const partText = (title, sql) => `-- ${title}, written by \`protobase migration\`.\n-- Applied with the project's other migrations; edit or remove statements the project handles differently.\n\n${sql}\n`;
|
|
7
|
+
/**
|
|
8
|
+
* Writes what each part still needs as the next migrations in `dir`, one file per part, and returns their paths; none
|
|
9
|
+
* when the database is current. Every part is compared with the database before any file is written, so the
|
|
10
|
+
* project's earlier migrations must be applied to it first.
|
|
11
|
+
*/
|
|
12
|
+
const writeMigrations = async (parts, dir) => {
|
|
13
|
+
const pending = [];
|
|
14
|
+
for (const part of parts) {
|
|
15
|
+
const sql = await part.sql();
|
|
16
|
+
if (sql !== void 0) pending.push({
|
|
17
|
+
part,
|
|
18
|
+
sql
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
if (pending.length === 0) return [];
|
|
22
|
+
await mkdir(dir, { recursive: true });
|
|
23
|
+
const files = [];
|
|
24
|
+
for (const { part, sql } of pending) {
|
|
25
|
+
const file = path.join(dir, nextMigrationName(await readdir(dir), part.name));
|
|
26
|
+
await writeFile(file, partText(part.title, sql), { flag: "wx" });
|
|
27
|
+
files.push(file);
|
|
28
|
+
}
|
|
29
|
+
return files;
|
|
30
|
+
};
|
|
31
|
+
//#endregion
|
|
32
|
+
export { partText, writeMigrations };
|
package/dist/package.js
CHANGED
package/dist/serve/app.js
CHANGED
|
@@ -16,6 +16,7 @@ const createServeApp = ({ project, db, requestLog, site }) => {
|
|
|
16
16
|
...project.auth && { auth: project.auth },
|
|
17
17
|
...project.functions && { functions: project.functions },
|
|
18
18
|
...project.files && { files: project.files },
|
|
19
|
+
...project.events && { events: project.events },
|
|
19
20
|
options: {
|
|
20
21
|
onUnhandledError: (error) => console.error(error),
|
|
21
22
|
...project.options
|
package/dist/serve/lifecycle.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createPgDb } from "../project/pg-db.js";
|
|
2
2
|
import { createServeApp } from "./app.js";
|
|
3
3
|
import { startCleanupTimer } from "./cleanup-timer.js";
|
|
4
|
-
import { checkAuthSchema, configExports, createFileCleanup } from "@protobase/server";
|
|
4
|
+
import { checkAuthSchema, checkChanges, configExports, createFileCleanup } from "@protobase/server";
|
|
5
5
|
import { createAdaptorServer } from "@hono/node-server";
|
|
6
6
|
import pg from "pg";
|
|
7
7
|
//#region src/serve/lifecycle.ts
|
|
@@ -18,6 +18,7 @@ const startServe = async ({ project, env, createDb = (url) => createPgDb(pg, url
|
|
|
18
18
|
if (project.auth) await checkAuthSchema(project.auth);
|
|
19
19
|
const ownDb = project.db ? void 0 : createDb(env.databaseUrl);
|
|
20
20
|
const db = project.db ?? ownDb;
|
|
21
|
+
const { resources } = configExports(project.config);
|
|
21
22
|
const app = createServeApp({
|
|
22
23
|
project,
|
|
23
24
|
db,
|
|
@@ -25,12 +26,12 @@ const startServe = async ({ project, env, createDb = (url) => createPgDb(pg, url
|
|
|
25
26
|
site
|
|
26
27
|
});
|
|
27
28
|
const server = createAdaptorServer({ fetch: app.fetch });
|
|
28
|
-
await listen(server, env.port).catch(async (error) => {
|
|
29
|
+
await checkChanges(db, resources.map((resource) => resource.toModel()), project.events).then(() => listen(server, env.port)).catch(async (error) => {
|
|
29
30
|
await ownDb?.destroy();
|
|
30
31
|
throw error;
|
|
31
32
|
});
|
|
32
33
|
const cleanup = createFileCleanup({
|
|
33
|
-
resources
|
|
34
|
+
resources,
|
|
34
35
|
db,
|
|
35
36
|
...project.files && { files: project.files }
|
|
36
37
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@protobase/cli",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.1",
|
|
4
4
|
"description": "The protobase command: scaffold, doctor, dev, build and serve a Protobase project.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -34,11 +34,11 @@
|
|
|
34
34
|
"dependencies": {
|
|
35
35
|
"@hono/node-server": "^2.1.3",
|
|
36
36
|
"@hono/vite-dev-server": "^0.26.1",
|
|
37
|
-
"@protobase/layout": "0.8.
|
|
38
|
-
"@protobase/query": "0.8.
|
|
39
|
-
"@protobase/schema": "0.8.
|
|
40
|
-
"@protobase/server": "0.8.
|
|
41
|
-
"@protobase/ui": "0.8.
|
|
37
|
+
"@protobase/layout": "0.8.1",
|
|
38
|
+
"@protobase/query": "0.8.1",
|
|
39
|
+
"@protobase/schema": "0.8.1",
|
|
40
|
+
"@protobase/server": "0.8.1",
|
|
41
|
+
"@protobase/ui": "0.8.1",
|
|
42
42
|
"@scalar/hono-api-reference": "^0.12.10",
|
|
43
43
|
"@tailwindcss/vite": "^4.3.3",
|
|
44
44
|
"@vitejs/plugin-react": "^6.1.2",
|