@protobase/cli 0.66.6 → 0.66.8
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/build/config-bundle.js +11 -1
- package/dist/build/first-party-code.js +22 -0
- package/dist/build/plugin-build.js +3 -1
- package/dist/build/shell.js +3 -1
- package/dist/cli/package.js +1 -1
- package/dist/dev/entry.js +1 -1
- package/dist/dev/run.js +2 -1
- package/dist/dev/server-package.js +8 -6
- package/dist/doctor/built-in-models.js +26 -0
- package/dist/doctor/run.js +11 -4
- package/dist/main.js +3 -2
- package/dist/mobile/android-signing.js +8 -1
- package/dist/mobile/load-project.js +4 -1
- package/dist/project/cli-modules.js +30 -0
- package/dist/project/import-project-file.js +9 -9
- package/package.json +8 -7
- package/skills/protobase/references/features/ai.md +10 -7
- package/skills/protobase/references/features/build-and-run.md +1 -0
- package/skills/protobase/references/features/compatibility.md +6 -6
- package/skills/protobase/references/features/data-model.md +1 -1
- package/skills/protobase/references/features/email-and-public-forms.md +2 -0
- package/skills/protobase/references/features/events-webhooks-and-jobs.md +2 -0
- package/skills/protobase/references/features/files-and-images.md +1 -0
- package/skills/protobase/references/features/history-comments-and-live-updates.md +1 -0
- package/skills/protobase/references/features/look-and-language.md +1 -0
- package/skills/protobase/references/features/navigation.md +5 -4
- package/skills/protobase/references/features/sign-in-and-accounts.md +7 -0
- package/skills/protobase/references/features/start-a-project.md +2 -1
- package/skills/protobase/references/features/your-own-code.md +4 -2
- package/skills/protobase/references/getting-started.md +10 -2
- package/skills/protobase/references/guides/api-functions.md +2 -2
- package/skills/protobase/references/guides/mobile-app.md +2 -2
- package/skills/protobase/references/reference/api.md +2 -2
- package/skills/protobase/references/reference/app-identity.md +1 -1
- package/skills/protobase/references/reference/assistant.md +22 -2
- package/skills/protobase/references/reference/auth.md +42 -11
- package/skills/protobase/references/reference/cli.md +9 -5
- package/skills/protobase/references/reference/client.md +1 -1
- package/skills/protobase/references/reference/comments.md +2 -2
- package/skills/protobase/references/reference/custom-components.md +7 -1
- package/skills/protobase/references/reference/data-config.md +1 -1
- package/skills/protobase/references/reference/email.md +1 -1
- package/skills/protobase/references/reference/files.md +4 -0
- package/skills/protobase/references/reference/history.md +1 -1
- package/skills/protobase/references/reference/jobs.md +3 -1
- package/skills/protobase/references/reference/languages.md +2 -2
- package/skills/protobase/references/reference/live.md +4 -0
- package/skills/protobase/references/reference/mcp.md +22 -2
- package/skills/protobase/references/reference/plugins.md +6 -4
- package/skills/protobase/references/reference/sidebar.md +6 -0
- package/skills/protobase/references/reference/supabase-realtime.md +5 -1
- package/skills/protobase/references/reference/supabase-rest.md +5 -1
- package/skills/protobase/references/reference/supabase-storage.md +5 -1
- package/skills/protobase/references/reference/theme.md +1 -1
- package/skills/protobase/references/reference/ui-config.md +6 -2
- package/skills/protobase/references/reference/versioning.md +5 -2
- package/skills/protobase/references/reference/webhooks.md +2 -0
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import { findPackageDir, packageName } from "./packages/package-dir.js";
|
|
2
2
|
import { hostModuleIds } from "../serve/host-module-ids.js";
|
|
3
|
+
import { cliPackages } from "../dev/server-package.js";
|
|
4
|
+
import { importProjectFiles } from "../project/import-project-file.js";
|
|
3
5
|
import { loadSourceConfig } from "../config/source-config.js";
|
|
4
6
|
import { layoutCheck } from "./layout-check.js";
|
|
5
7
|
import { extensionImports } from "../project/extensions.js";
|
|
6
8
|
import { viteBundle } from "./vite-bundle.js";
|
|
9
|
+
import { cliProtobaseIds, provideCliModules } from "../project/cli-modules.js";
|
|
7
10
|
import { collectComponents } from "./component-names.js";
|
|
8
11
|
import { bundleEntryCode } from "./entry-code.js";
|
|
9
12
|
import { hostPackages } from "./packages/host-packages.js";
|
|
@@ -67,9 +70,16 @@ const servedImports = (native) => {
|
|
|
67
70
|
};
|
|
68
71
|
};
|
|
69
72
|
const buildConfigBundle = async ({ projectDir, outFile, bun, extensions = [], components = /* @__PURE__ */ new Set() }) => {
|
|
73
|
+
if (process.versions.bun !== void 0) provideCliModules();
|
|
74
|
+
const cli = cliPackages();
|
|
75
|
+
const packages = {
|
|
76
|
+
...cli,
|
|
77
|
+
dedupe: [.../* @__PURE__ */ new Set([...cli.dedupe, ...cliProtobaseIds().map((id) => packageName(id))])]
|
|
78
|
+
};
|
|
70
79
|
const config = await loadSourceConfig(projectDir, {
|
|
71
80
|
command: "build",
|
|
72
|
-
extensions
|
|
81
|
+
extensions,
|
|
82
|
+
importModules: (files) => importProjectFiles(files, projectDir, packages)
|
|
73
83
|
});
|
|
74
84
|
const file = path.join(projectDir, "protobase-bundle-entry.js");
|
|
75
85
|
const native = /* @__PURE__ */ new Map();
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { firstPartyCodeKey } from "@protobase/schema/internal";
|
|
2
|
+
import { sentryVitePlugin } from "@sentry/vite-plugin";
|
|
3
|
+
//#region src/build/first-party-code.ts
|
|
4
|
+
/**
|
|
5
|
+
* Marks every script of a browser build as the app's own (see `firstPartyCodeKey`), with the error SDK's bundler
|
|
6
|
+
* plugin and nothing else of it: no release, no source map upload and no telemetry. Silent, since it still warns that
|
|
7
|
+
* it has no token to create the release it is told not to create.
|
|
8
|
+
*/
|
|
9
|
+
const firstPartyCode = () => sentryVitePlugin({
|
|
10
|
+
applicationKey: firstPartyCodeKey,
|
|
11
|
+
telemetry: false,
|
|
12
|
+
silent: true,
|
|
13
|
+
sourcemaps: { disable: true },
|
|
14
|
+
release: {
|
|
15
|
+
inject: false,
|
|
16
|
+
create: false,
|
|
17
|
+
finalize: false,
|
|
18
|
+
setCommits: false
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
//#endregion
|
|
22
|
+
export { firstPartyCode };
|
|
@@ -6,6 +6,7 @@ import { bundledImports } from "../project/bundled-imports.js";
|
|
|
6
6
|
import { sharedModules } from "../project/shared-modules.js";
|
|
7
7
|
import { pluginFieldTypes } from "./plugin-fields.js";
|
|
8
8
|
import { layerPluginUtilities } from "./plugin-styles.js";
|
|
9
|
+
import { firstPartyCode } from "./first-party-code.js";
|
|
9
10
|
import { createRequire } from "node:module";
|
|
10
11
|
import { existsSync } from "node:fs";
|
|
11
12
|
import path from "node:path";
|
|
@@ -120,7 +121,8 @@ const buildPlugin = async ({ pluginDir, outDir, name, projectDir, ignore = [], s
|
|
|
120
121
|
pluginStyles(styles, styleCode(pluginDir, target.theme, [outDir, ...ignore])),
|
|
121
122
|
react(),
|
|
122
123
|
tailwindcss(),
|
|
123
|
-
layeredStyles()
|
|
124
|
+
layeredStyles(),
|
|
125
|
+
firstPartyCode()
|
|
124
126
|
],
|
|
125
127
|
worker: { rolldownOptions: { output: { sourcemapDebugIds: sourceMaps } } },
|
|
126
128
|
build: {
|
package/dist/build/shell.js
CHANGED
|
@@ -2,6 +2,7 @@ import { readPackageJson } from "./packages/package-dir.js";
|
|
|
2
2
|
import { adminAppDir, uiEntry, uiPackageDir } from "../project/admin-app.js";
|
|
3
3
|
import { projectUiPlugin } from "../project/project-ui.js";
|
|
4
4
|
import { sharedModulesPlugin } from "../project/shared-modules.js";
|
|
5
|
+
import { firstPartyCode } from "./first-party-code.js";
|
|
5
6
|
import { pinnedNative } from "../mobile/pinned-packages.js";
|
|
6
7
|
import { faviconLink } from "../project/favicon-link.js";
|
|
7
8
|
import { themeLink } from "../project/theme-link.js";
|
|
@@ -69,7 +70,8 @@ const buildShell = async (outDir, sourceMapsDir) => {
|
|
|
69
70
|
faviconLink(),
|
|
70
71
|
serviceWorker(),
|
|
71
72
|
react(),
|
|
72
|
-
tailwindcss()
|
|
73
|
+
tailwindcss(),
|
|
74
|
+
firstPartyCode()
|
|
73
75
|
],
|
|
74
76
|
worker: { rolldownOptions: { output: { sourcemapDebugIds: sourceMaps } } },
|
|
75
77
|
build: {
|
package/dist/cli/package.js
CHANGED
package/dist/dev/entry.js
CHANGED
|
@@ -55,7 +55,7 @@ const ui = {
|
|
|
55
55
|
icons: projectIcons(projectDir)
|
|
56
56
|
};
|
|
57
57
|
const { admin, jobs } = projectAdmin(project, db, ui, reporter, upgradeWebSocket);
|
|
58
|
-
await devJobs(db, jobs, admin.decryptField, project.mailer);
|
|
58
|
+
if (process.env.PROTOBASE_DEV_JOBS !== "false") await devJobs(db, jobs, admin.decryptField, project.mailer);
|
|
59
59
|
devRealtimePrune(admin);
|
|
60
60
|
var entry_default = withDevServiceWorker(new Hono(), Boolean(project.notifications)).route("/", admin);
|
|
61
61
|
/** Whether the auth store has a user yet, for the banner's sign-in line; undefined without one. */
|
package/dist/dev/run.js
CHANGED
|
@@ -25,6 +25,7 @@ const runDev = async (options, out) => {
|
|
|
25
25
|
process.env.PROTOBASE_PROJECT = options.projectDir;
|
|
26
26
|
process.env.PROTOBASE_EXTENSIONS = JSON.stringify(extensions);
|
|
27
27
|
process.env.PROTOBASE_DATABASE_URL = url;
|
|
28
|
+
process.env.PROTOBASE_DEV_JOBS = String(options.jobs ?? true);
|
|
28
29
|
const onApiExit = (code) => {
|
|
29
30
|
process.stderr.write(`error: the API process of protobase dev ended with exit code ${code}\n`);
|
|
30
31
|
server.close().then(() => process.exit(code || 1));
|
|
@@ -63,7 +64,7 @@ const runDev = async (options, out) => {
|
|
|
63
64
|
const started = await apiChild.start(`http://localhost:${port}`).catch(closeAndThrow);
|
|
64
65
|
apiReady(started);
|
|
65
66
|
const { anyUsers, authDrift } = started;
|
|
66
|
-
out(`protobase dev ready at http://localhost:${port}\n project ${options.projectDir}\n api http://localhost:${port}/api/v1 (docs at /api/docs)\n` + loginLine(anyUsers, process.env.npm_config_user_agent) + authDriftLines(authDrift));
|
|
67
|
+
out(`protobase dev ready at http://localhost:${port}\n project ${options.projectDir}\n api http://localhost:${port}/api/v1 (docs at /api/docs)\n` + (options.jobs === false ? " jobs off: no jobs run and no events are delivered (--no-jobs)\n" : "") + loginLine(anyUsers, process.env.npm_config_user_agent) + authDriftLines(authDrift));
|
|
67
68
|
return server;
|
|
68
69
|
};
|
|
69
70
|
//#endregion
|
|
@@ -6,6 +6,12 @@ import { fileURLToPath } from "node:url";
|
|
|
6
6
|
const server = "@protobase/server";
|
|
7
7
|
const supplied = [...new Set(hostModuleIds.map((id) => packageName(id)))];
|
|
8
8
|
const cliDir = fileURLToPath(new URL("../..", import.meta.url));
|
|
9
|
+
/** The CLI's own copies of the packages the serve runtime supplies, as a bundled project gets them. */
|
|
10
|
+
const cliPackages = (ownDir = cliDir) => ({
|
|
11
|
+
from: "cli",
|
|
12
|
+
root: ownDir,
|
|
13
|
+
dedupe: supplied
|
|
14
|
+
});
|
|
9
15
|
/**
|
|
10
16
|
* Where the API of `protobase dev` gets @protobase/server: from the project's node_modules when the project is locked,
|
|
11
17
|
* so one that is not installed fails as any missing package does; otherwise, bundled, the CLI's own, with the other
|
|
@@ -17,11 +23,7 @@ const serverPackage = (projectDir, ownDir = cliDir) => {
|
|
|
17
23
|
root: projectDir,
|
|
18
24
|
dedupe: [server]
|
|
19
25
|
};
|
|
20
|
-
return
|
|
21
|
-
from: "cli",
|
|
22
|
-
root: ownDir,
|
|
23
|
-
dedupe: supplied
|
|
24
|
-
};
|
|
26
|
+
return cliPackages(ownDir);
|
|
25
27
|
};
|
|
26
28
|
//#endregion
|
|
27
|
-
export { serverPackage };
|
|
29
|
+
export { cliPackages, serverPackage };
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { builtInResources, configExports, webhooksModule } from "@protobase/server/config-exports";
|
|
2
|
+
//#region src/doctor/built-in-models.ts
|
|
3
|
+
/**
|
|
4
|
+
* The resources and views Protobase adds to an app by what its config enables, as the API serves them: the webhook ones
|
|
5
|
+
* of `webhooks`, then Failed jobs, Activity and Comments by what the app's resources use. Doctor checks them as it checks
|
|
6
|
+
* the app's own.
|
|
7
|
+
*/
|
|
8
|
+
const builtInModels = (config, models) => {
|
|
9
|
+
const webhooks = config.webhooks ? configExports(webhooksModule(config.webhooks)) : {
|
|
10
|
+
resources: [],
|
|
11
|
+
views: []
|
|
12
|
+
};
|
|
13
|
+
const webhookModels = webhooks.resources.map((resource) => resource.toModel());
|
|
14
|
+
const added = builtInResources({
|
|
15
|
+
models: [...webhookModels, ...models],
|
|
16
|
+
jobs: Object.keys(config.jobs).length > 0,
|
|
17
|
+
notifications: config.notifications !== void 0,
|
|
18
|
+
...config.events && { events: config.events }
|
|
19
|
+
});
|
|
20
|
+
return {
|
|
21
|
+
models: [...webhookModels, ...added.resources.map((resource) => resource.toModel())],
|
|
22
|
+
views: [...webhooks.views, ...added.views].map((view) => view.toModel())
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
//#endregion
|
|
26
|
+
export { builtInModels };
|
package/dist/doctor/run.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
import { loadSourceConfig } from "../config/source-config.js";
|
|
1
2
|
import { connect } from "../db/connect.js";
|
|
2
3
|
import { introspect } from "../db/introspect.js";
|
|
3
4
|
import { loadDataConfig } from "../project/load-data-config.js";
|
|
4
5
|
import { readOnlyDb } from "../db/read-only-db.js";
|
|
6
|
+
import { builtInModels } from "./built-in-models.js";
|
|
5
7
|
import { checkResource } from "./check.js";
|
|
6
8
|
import { indexIssues } from "./index-issues.js";
|
|
7
9
|
import { readShapes } from "./read-shapes.js";
|
|
@@ -15,12 +17,17 @@ const format = (issue) => ` ${issue.severity.padEnd(7)} ${issue.resource}: ${is
|
|
|
15
17
|
const runDoctor = async (options, out) => {
|
|
16
18
|
if (!existsSync(path.join(options.configDir, "index.ts"))) throw new Error(`No config in ${options.configDir}: write it with \`protobase scaffold\`, or pass --config <dir>`);
|
|
17
19
|
const config = await loadDataConfig(options.configDir, options.projectDir);
|
|
18
|
-
|
|
19
|
-
|
|
20
|
+
if (config.models.length === 0) throw new Error(`No resources exported from ${options.configDir}/index.ts`);
|
|
21
|
+
const builtIn = builtInModels(await loadSourceConfig(options.projectDir, { command: "tool" }), config.models);
|
|
22
|
+
const models = [...builtIn.models, ...config.models];
|
|
20
23
|
const sql = connect(options.url);
|
|
21
24
|
const tables = await introspect(sql, { views: true }).finally(() => sql.end());
|
|
22
25
|
const db = readOnlyDb(options.url);
|
|
23
|
-
const shapes = readShapes(
|
|
26
|
+
const shapes = readShapes({
|
|
27
|
+
models,
|
|
28
|
+
views: [...builtIn.views, ...config.views],
|
|
29
|
+
pages: config.pages
|
|
30
|
+
});
|
|
24
31
|
const indexes = await Promise.all(models.map(async (model) => indexIssues(model, await tableIndexes(db, model), shapes))).finally(() => db.destroy());
|
|
25
32
|
const issues = models.flatMap((model, i) => [
|
|
26
33
|
...checkResource(model, tables),
|
|
@@ -30,7 +37,7 @@ const runDoctor = async (options, out) => {
|
|
|
30
37
|
]);
|
|
31
38
|
for (const issue of issues) out(format(issue));
|
|
32
39
|
const errors = issues.filter((i) => i.severity === "error").length;
|
|
33
|
-
out(`Checked ${models.length} resources: ${errors} errors, ${issues.length - errors} warnings.\n`);
|
|
40
|
+
out(`Checked ${builtIn.models.length > 0 ? `${config.models.length} resources and ${builtIn.models.length} built-in ones` : `${models.length} resources`}: ${errors} errors, ${issues.length - errors} warnings.\n`);
|
|
34
41
|
return {
|
|
35
42
|
errors,
|
|
36
43
|
warnings: issues.length - errors
|
package/dist/main.js
CHANGED
|
@@ -60,7 +60,7 @@ program.command("doctor").description("Check a config folder against the databas
|
|
|
60
60
|
}, out);
|
|
61
61
|
if (errors > 0) process.exitCode = 1;
|
|
62
62
|
});
|
|
63
|
-
program.command("dev").description("Serve the admin app and API for the project in the current directory").option("--port <port>", "port for the app and API", "5173").option("--env <variable>", "environment variable holding the database URL", "DATABASE_URL").option("--allowed-hosts <hosts>", "extra Host headers to accept, comma separated; a leading dot matches subdomains (.trycloudflare.com)").option("--cache-dir <dir>", "Vite's dependency cache, so parallel dev servers do not share one").option("--extend <dir>", "merge the protobase.config.ts and protobase.ui.tsx in this folder before the project's, repeatable", collect, []).action(async (opts) => {
|
|
63
|
+
program.command("dev").description("Serve the admin app and API for the project in the current directory").option("--port <port>", "port for the app and API", "5173").option("--env <variable>", "environment variable holding the database URL", "DATABASE_URL").option("--allowed-hosts <hosts>", "extra Host headers to accept, comma separated; a leading dot matches subdomains (.trycloudflare.com)").option("--cache-dir <dir>", "Vite's dependency cache, so parallel dev servers do not share one").option("--extend <dir>", "merge the protobase.config.ts and protobase.ui.tsx in this folder before the project's, repeatable", collect, []).option("--no-jobs", "run none of the app's jobs and deliver none of its events, such as against its hosted database").action(async (opts) => {
|
|
64
64
|
const [{ runDev }, { resolveExtensions }, { writeProjectTypes }] = await Promise.all([
|
|
65
65
|
import("./dev/run.js"),
|
|
66
66
|
import("./project/extensions.js"),
|
|
@@ -73,7 +73,8 @@ program.command("dev").description("Serve the admin app and API for the project
|
|
|
73
73
|
envVar: opts.env,
|
|
74
74
|
projectDir: process.cwd(),
|
|
75
75
|
cacheDir: opts.cacheDir && path.resolve(opts.cacheDir),
|
|
76
|
-
allowedHosts: parseHosts(opts.allowedHosts)
|
|
76
|
+
allowedHosts: parseHosts(opts.allowedHosts),
|
|
77
|
+
jobs: opts.jobs
|
|
77
78
|
}, out);
|
|
78
79
|
warn(placeholderSecretWarning(process.env));
|
|
79
80
|
process.once("SIGINT", () => void server.close().then(() => process.exit(0)));
|
|
@@ -12,6 +12,13 @@ const signingNames = new Set(androidSigningVariables);
|
|
|
12
12
|
const isSigningVariable = (key) => signingNames.has(key.toUpperCase());
|
|
13
13
|
/** `env` without the signing variables, in any letter case. */
|
|
14
14
|
const withoutAndroidSigning = (env) => Object.fromEntries(Object.entries(env).filter(([key]) => !isSigningVariable(key)));
|
|
15
|
+
/**
|
|
16
|
+
* Deletes from `env`, in place, every variable named `PROTOBASE_ANDROID_*` in any letter case: wider than the four, so
|
|
17
|
+
* no such variable is left for the project's code to find, while Gradle, which reads the four by name, gets them apart.
|
|
18
|
+
*/
|
|
19
|
+
const dropAndroidSigning = (env) => {
|
|
20
|
+
for (const key of Object.keys(env)) if (key.toUpperCase().startsWith("PROTOBASE_ANDROID_")) delete env[key];
|
|
21
|
+
};
|
|
15
22
|
const isFile = (file) => {
|
|
16
23
|
try {
|
|
17
24
|
return statSync(file).isFile();
|
|
@@ -50,4 +57,4 @@ const withReleaseSigning = (gradle) => {
|
|
|
50
57
|
return gradle.replace(releaseAnchor, `${signingConfig}${releaseAnchor} signingConfig signingConfigs.release\n`);
|
|
51
58
|
};
|
|
52
59
|
//#endregion
|
|
53
|
-
export { androidSigning, androidSigningVariables, withReleaseSigning, withoutAndroidSigning };
|
|
60
|
+
export { androidSigning, androidSigningVariables, dropAndroidSigning, withReleaseSigning, withoutAndroidSigning };
|
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
import { loadNearestEnvFile } from "../dev/env.js";
|
|
2
2
|
import { loadSourceConfig } from "../config/source-config.js";
|
|
3
|
+
import { dropAndroidSigning } from "./android-signing.js";
|
|
3
4
|
import { appNameOver } from "@protobase/server";
|
|
4
5
|
//#region src/mobile/load-project.ts
|
|
5
6
|
/**
|
|
6
7
|
* What a mobile build needs of the project in `dir`, merged as `protobase dev` merges it after the configs of
|
|
7
8
|
* `extensions`: its mobile section, unchecked, and with sign-in on the app's URL, its tokens' issuer, and its name.
|
|
8
|
-
* The project's .env is loaded first, as other commands do
|
|
9
|
+
* The project's .env is loaded first, as other commands do, and no `PROTOBASE_ANDROID_*` variable is left in the
|
|
10
|
+
* environment; nothing but the configs runs.
|
|
9
11
|
*/
|
|
10
12
|
const loadMobileProject = async (dir, extensions) => {
|
|
11
13
|
loadNearestEnvFile(dir);
|
|
14
|
+
dropAndroidSigning(process.env);
|
|
12
15
|
const { mobile, auth, url, name } = await loadSourceConfig(dir, {
|
|
13
16
|
command: "tool",
|
|
14
17
|
extensions
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { cliTypePaths } from "./project-types.js";
|
|
2
|
+
import { fileURLToPath } from "node:url";
|
|
3
|
+
//#region src/project/cli-modules.ts
|
|
4
|
+
const cliDir = fileURLToPath(new URL("../..", import.meta.url));
|
|
5
|
+
/** Every entry point of the @protobase packages the CLI carries, such as `@protobase/server/pdf`. */
|
|
6
|
+
const cliProtobaseIds = () => Object.keys(cliTypePaths()).filter((id) => id.startsWith("@protobase/"));
|
|
7
|
+
/**
|
|
8
|
+
* Under Bun, makes every later import of an entry point of the @protobase packages load the CLI's own copy, whatever
|
|
9
|
+
* the importer installed (Bun's virtual modules). Each loads when it is first imported, so the CLI loads nothing it did
|
|
10
|
+
* not already load or the project does not import. Only the @protobase packages: a virtual module that loads lazily
|
|
11
|
+
* cannot be `require`d, as the CLI requires React. Bun only: under Node the project's files load through Vite's module
|
|
12
|
+
* runner, which resolves them from the CLI itself. Each build calls it again, which Bun takes: a plugin of the same name
|
|
13
|
+
* registered again replaces nothing it needs and throws nothing.
|
|
14
|
+
*/
|
|
15
|
+
const provideCliModules = () => {
|
|
16
|
+
const bun = globalThis.Bun;
|
|
17
|
+
if (!bun) throw new Error("provideCliModules runs under Bun only");
|
|
18
|
+
const ids = cliProtobaseIds();
|
|
19
|
+
bun.plugin({
|
|
20
|
+
name: "protobase-cli-modules",
|
|
21
|
+
setup: (build) => {
|
|
22
|
+
for (const id of ids) build.module(id, async () => ({
|
|
23
|
+
exports: await import(bun.resolveSync(id, cliDir)),
|
|
24
|
+
loader: "object"
|
|
25
|
+
}));
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
};
|
|
29
|
+
//#endregion
|
|
30
|
+
export { cliProtobaseIds, provideCliModules };
|
|
@@ -3,10 +3,9 @@ import { writeProjectTypes } from "./project-types.js";
|
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { pathToFileURL } from "node:url";
|
|
5
5
|
//#region src/project/import-project-file.ts
|
|
6
|
-
const runnerImport = async (file, projectDir, plugins = []) => {
|
|
6
|
+
const runnerImport = async (file, projectDir, resolution, plugins = []) => {
|
|
7
7
|
await writeProjectTypes(projectDir);
|
|
8
8
|
const vite = await import("vite");
|
|
9
|
-
const resolution = serverPackage(projectDir);
|
|
10
9
|
const packages = resolution.from === "cli" ? {
|
|
11
10
|
root: resolution.root,
|
|
12
11
|
resolve: { dedupe: resolution.dedupe }
|
|
@@ -23,29 +22,30 @@ const inPackage = (file) => file.split(path.sep).includes("node_modules");
|
|
|
23
22
|
const bunLoads = (projectDir) => process.versions.bun !== void 0 && serverPackage(projectDir).from === "project";
|
|
24
23
|
/**
|
|
25
24
|
* Imports one of the files of the project in `projectDir`, TypeScript: Bun loads it itself, Node through Vite's module
|
|
26
|
-
* runner, or a package's file as it is. A bundled project's file always goes through the runner. `
|
|
27
|
-
*
|
|
25
|
+
* runner, or a package's file as it is. A bundled project's file always goes through the runner. `resolution` says whose
|
|
26
|
+
* packages the runner resolves, by default as `serverPackage` says. `protobase dev` loads this module through Vite,
|
|
27
|
+
* which leaves these imports of a path as they are.
|
|
28
28
|
*/
|
|
29
|
-
const importProjectFile = async (file, projectDir) => inPackage(file) ? import(
|
|
29
|
+
const importProjectFile = async (file, projectDir, resolution = serverPackage(projectDir)) => inPackage(file) ? import(
|
|
30
30
|
/* @vite-ignore */
|
|
31
31
|
pathToFileURL(file).href
|
|
32
32
|
) : bunLoads(projectDir) ? import(
|
|
33
33
|
/* @vite-ignore */
|
|
34
34
|
file
|
|
35
|
-
) : runnerImport(file, projectDir);
|
|
35
|
+
) : runnerImport(file, projectDir, resolution);
|
|
36
36
|
/**
|
|
37
37
|
* Imports several of the project's files in order, so that the modules they share are loaded once. Bun's module cache
|
|
38
38
|
* does that itself; Vite's runner gets them as the imports of one module, which need not exist, beside the first file.
|
|
39
39
|
*/
|
|
40
|
-
const importProjectFiles = async (files, projectDir) => {
|
|
40
|
+
const importProjectFiles = async (files, projectDir, resolution = serverPackage(projectDir)) => {
|
|
41
41
|
if (bunLoads(projectDir) || files.length === 1) {
|
|
42
42
|
const modules = [];
|
|
43
|
-
for (const file of files) modules.push(await importProjectFile(file, projectDir));
|
|
43
|
+
for (const file of files) modules.push(await importProjectFile(file, projectDir, resolution));
|
|
44
44
|
return modules;
|
|
45
45
|
}
|
|
46
46
|
const id = path.join(path.dirname(files[0]), "protobase-project-files.js");
|
|
47
47
|
const code = [...files.map((file, i) => `import * as m${i} from ${JSON.stringify(file)}`), `export const modules = [${files.map((_, i) => `m${i}`).join(", ")}]`].join("\n");
|
|
48
|
-
return (await runnerImport(id, projectDir, [{
|
|
48
|
+
return (await runnerImport(id, projectDir, resolution, [{
|
|
49
49
|
name: "protobase-project-files",
|
|
50
50
|
resolveId: (source) => source === id ? `\0${id}` : void 0,
|
|
51
51
|
load: (source) => source === `\0${id}` ? code : void 0
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@protobase/cli",
|
|
3
|
-
"version": "0.66.
|
|
3
|
+
"version": "0.66.8",
|
|
4
4
|
"description": "The protobase command: scaffold, doctor, dev, build and serve a Protobase project.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -43,14 +43,15 @@
|
|
|
43
43
|
"dependencies": {
|
|
44
44
|
"@better-auth/core": "^1.7.12",
|
|
45
45
|
"@hono/node-server": "^2.1.3",
|
|
46
|
-
"@protobase/config": "0.66.
|
|
47
|
-
"@protobase/layout": "0.66.
|
|
48
|
-
"@protobase/query": "0.66.
|
|
49
|
-
"@protobase/schema": "0.66.
|
|
50
|
-
"@protobase/server": "0.66.
|
|
51
|
-
"@protobase/ui": "0.66.
|
|
46
|
+
"@protobase/config": "0.66.8",
|
|
47
|
+
"@protobase/layout": "0.66.8",
|
|
48
|
+
"@protobase/query": "0.66.8",
|
|
49
|
+
"@protobase/schema": "0.66.8",
|
|
50
|
+
"@protobase/server": "0.66.8",
|
|
51
|
+
"@protobase/ui": "0.66.8",
|
|
52
52
|
"@react-email/render": "^2.1.0",
|
|
53
53
|
"@scalar/hono-api-reference": "^0.12.10",
|
|
54
|
+
"@sentry/vite-plugin": "^11.10.0",
|
|
54
55
|
"@tailwindcss/vite": "^4.3.3",
|
|
55
56
|
"@vitejs/plugin-react": "^6.1.2",
|
|
56
57
|
"aip-parsers": "^1.0.0",
|
|
@@ -6,16 +6,19 @@ Fields and actions filled by a model, the assistant beside the admin, and MCP.
|
|
|
6
6
|
|
|
7
7
|
- [AI fields](../reference/ai.md#ai-fields): A model fills a field from a prompt that uses the record's other fields, after each change.
|
|
8
8
|
- [AI actions](../reference/ai.md#ai-actions): A button asks a model to write a field now, such as a suggested priority or a translation.
|
|
9
|
-
- [Assistant](../reference/assistant.md): A chat beside the admin that answers questions about the app, reads its data and proposes changes for approval.
|
|
9
|
+
- [Assistant](../reference/assistant.md) (beta): A chat beside the admin that answers questions about the app, reads its data and proposes changes for approval.
|
|
10
10
|
- [Choice of model](../reference/assistant.md#which-backend): The assistant runs on OpenRouter or any OpenAI-compatible endpoint, also a local one.
|
|
11
|
-
- [Assistant tools](../reference/assistant.md#the-apps-own-tools): Your own tools for the assistant, acting on records as the user.
|
|
12
|
-
- [
|
|
11
|
+
- [Assistant tools](../reference/assistant.md#the-apps-own-tools) (beta): Your own tools for the assistant, acting on records as the user.
|
|
12
|
+
- [Assistant widgets](../reference/assistant.md#widget-parts): Your own React components in the chat, showing a record as it is now and taking decisions on it, from last month's chat too.
|
|
13
|
+
- [Assistant backend of your own](../reference/assistant.md#another-backend): The chat beside the admin talks to a backend elsewhere, with its own prompt and tools, built from the same pieces as the built-in one.
|
|
14
|
+
- [MCP server](../reference/mcp.md#the-tools) (beta): AI clients connect to the app over MCP as the signed-in person: they list the resources in a small first call, describe one resource's fields when they need them, and find and read records within that person's access, soft-deleted ones too when asked.
|
|
13
15
|
- [MCP read fields](../reference/mcp.md#reading-only-what-is-needed): AI clients read only the fields they name, a plain list returns the columns of the app's table, and the server tells clients how to keep reads small.
|
|
14
|
-
- [MCP counts](../reference/mcp.md#the-tools): AI clients count the records a filter finds, in all or per value of a field such as a status, without reading the records.
|
|
15
|
-
- [MCP app icon](../reference/mcp.md#the-apps-icon): AI clients show the app's own icon beside its name when someone adds its MCP endpoint.
|
|
16
|
-
- [MCP
|
|
16
|
+
- [MCP counts](../reference/mcp.md#the-tools) (beta): AI clients count the records a filter finds, in all or per value of a field such as a status, without reading the records.
|
|
17
|
+
- [MCP app icon](../reference/mcp.md#the-apps-icon) (beta): AI clients show the app's own icon beside its name when someone adds its MCP endpoint.
|
|
18
|
+
- [MCP SQL for admins](../reference/mcp.md#sql-for-admins): People with the `admin` or `ai` role run read-only SQL over MCP when you turn it on.
|
|
19
|
+
- [MCP writes](../reference/mcp.md#writes) (beta): When turned on, for every resource or only those you list, AI clients create, update and delete records after the person approves each write.
|
|
17
20
|
- [Small MCP write results](../reference/mcp.md#what-a-write-returns): A write over MCP returns the record's key, new ETag, the fields written and the read-only fields that changed, not the whole record; `fields` asks for more.
|
|
18
|
-
- [MCP writes without approval](../reference/mcp.md#writes-without-approval): Roles you name, such as service accounts, write over MCP without being asked to approve each write; everyone else is still asked.
|
|
21
|
+
- [MCP writes without approval](../reference/mcp.md#writes-without-approval) (beta): Roles you name, such as service accounts, write over MCP without being asked to approve each write; everyone else is still asked.
|
|
19
22
|
- [MCP comments](../reference/mcp.md#comments): AI clients read a record's comments, and with MCP writes on add one after the person approves it.
|
|
20
23
|
- [MCP batch writes](../reference/mcp.md#batch-writes): When MCP writes are on, AI clients make many creates, updates and deletes across resources in one transaction, after the person approves the batch.
|
|
21
24
|
- [Connected apps](../reference/auth.md#oauth-for-mcp-clients): People allow an AI client to sign in, and disconnect it later.
|
|
@@ -6,6 +6,7 @@ Develop, build and run an app, on servers, in containers and on phones.
|
|
|
6
6
|
|
|
7
7
|
- [Node and Bun](../reference/cli.md#how-it-runs): The `protobase` command runs on Node 22.12 or later or on Bun, and a bundle runs on both unless it is built for Bun only.
|
|
8
8
|
- [Dev server](../reference/cli.md#dev): The admin app and the API on one port, with changes shown without a restart.
|
|
9
|
+
- [Dev server without jobs](../reference/cli.md#dev): `protobase dev --no-jobs` runs no jobs and delivers no events, webhooks or pushes, to develop against a hosted app's database without doing its work twice.
|
|
9
10
|
- [Editor types](../reference/cli.md#types): `protobase types` gives the editor and `tsc` the types of the CLI that builds the app, with nothing installed.
|
|
10
11
|
- [Project scripts](../reference/cli.md#run): `protobase run` runs a seed or a migration script on the CLI's packages.
|
|
11
12
|
- [Migrate command](../reference/cli.md#migrate): `protobase migrate` applies the project's SQL migrations in one transaction, with nothing installed.
|
|
@@ -6,10 +6,10 @@ Apps written for other systems that keep working on Protobase, and moving their
|
|
|
6
6
|
|
|
7
7
|
## Supabase
|
|
8
8
|
|
|
9
|
-
- [Supabase import](../reference/cli.md#import-supabase): Moves a Supabase project into Protobase, with schema, data, users, functions, storage and scheduled jobs.
|
|
10
|
-
- [Create a project on a Supabase database](../getting-started.md#on-
|
|
11
|
-
- [Supabase client API](../reference/supabase-rest.md): supabase-js `from()` and `rpc()` calls keep working, under the tables' RLS policies.
|
|
12
|
-
- [Supabase Auth](../reference/auth.md#supabase-js-and-supabasessr): `supabase.auth` and `@supabase/ssr` calls keep working.
|
|
13
|
-
- [Supabase Storage](../reference/supabase-storage.md): supabase-js storage calls keep working, over the app's file storage.
|
|
14
|
-
- [Supabase Realtime](../reference/supabase-realtime.md): Database changes, broadcast and presence through supabase-js channels, private ones authorized by the policies on `realtime.messages`, broadcasts sent from a server without joining a channel, and broadcasts from the database with `realtime.send()` and `realtime.broadcast_changes()`, across [several processes](../reference/supabase-realtime.md#several-processes) serving one app.
|
|
9
|
+
- [Supabase import](../reference/cli.md#import-supabase) (beta): Moves a Supabase project into Protobase, with schema, data, users, functions, storage and scheduled jobs, and a coding agent's skill finishes what its report leaves.
|
|
10
|
+
- [Create a project on a Supabase database](../getting-started.md#on-a-supabase-database) (beta): your Supabase users sign in to the new project with their own passwords, and Supabase's own auth schema stays as Supabase set it up.
|
|
11
|
+
- [Supabase client API](../reference/supabase-rest.md) (beta): supabase-js `from()` and `rpc()` calls keep working, under the tables' RLS policies.
|
|
12
|
+
- [Supabase Auth](../reference/auth.md#supabase-js-and-supabasessr) (beta): `supabase.auth` and `@supabase/ssr` calls keep working.
|
|
13
|
+
- [Supabase Storage](../reference/supabase-storage.md) (beta): supabase-js storage calls keep working, over the app's file storage.
|
|
14
|
+
- [Supabase Realtime](../reference/supabase-realtime.md) (beta): Database changes, broadcast and presence through supabase-js channels, private ones authorized by the policies on `realtime.messages`, broadcasts sent from a server without joining a channel, and broadcasts from the database with `realtime.send()` and `realtime.broadcast_changes()`, across [several processes](../reference/supabase-realtime.md#several-processes) serving one app.
|
|
15
15
|
- [Edge Functions](../guides/api-functions.md#functions-moved-from-supabase): Functions moved from Supabase answer at their old addresses.
|
|
@@ -18,7 +18,7 @@ Describe the tables of an existing Postgres database as resources, with their fi
|
|
|
18
18
|
- [Record validation](../reference/data-config.md#resource): A rule over the whole record runs on every create and update and reports each problem on the field it concerns.
|
|
19
19
|
- [Rollups and formulas](../reference/data-config.md#rollups-and-formulas) (beta): Fields that hold counts, sums, averages, minimums or maximums of related records, or values calculated from the record's own numbers, kept current by the database.
|
|
20
20
|
- [Sensitive fields](../reference/data-config.md#sensitive-fields): Values such as bank account numbers are left out of every response and shown masked. A permitted user reveals one value at a time, and each reveal is audited.
|
|
21
|
-
- [Recent strong sign-in](../reference/data-config.md#recent-strong-sign-in): A sensitive field can ask for a passkey or a second step in the last minutes before it is revealed. The record page asks the person to confirm it's them, then shows it.
|
|
21
|
+
- [Recent strong sign-in](../reference/data-config.md#recent-strong-sign-in): A sensitive field can ask for a passkey or a second step in the last minutes before it is revealed, or an emailed code for an account with neither. The record page asks the person to confirm it's them, then shows it.
|
|
22
22
|
- [Computed reveals](../reference/data-config.md#computed-reveals) (beta): A secret kept elsewhere, or built from several values, is revealed like a sensitive field, with the same access, audit and recent sign-in check, but made when it is asked for.
|
|
23
23
|
- [Encrypted fields](../reference/data-config.md#encrypted-fields): A sensitive text field is encrypted with the app's own key before it is stored, so dumps, backups and SQL readers see only ciphertext. A command encrypts the values a column already holds.
|
|
24
24
|
- [Encrypted fields in jobs and functions](../reference/data-config.md#reading-in-jobs-and-functions) (beta): A job or an API function reads an encrypted value the app must use itself, such as a share's password, and each read is audited like a reveal.
|
|
@@ -7,6 +7,8 @@ Mail the app sends, the PDF documents it makes, and forms for people without an
|
|
|
7
7
|
- [Email](../reference/email.md#configuration): The app sends mail through one SMTP server or a mailer you supply.
|
|
8
8
|
- [Built-in emails](../reference/email.md#protobases-own-mail): Sign-in codes, password resets, invitations and mentions are sent in the app's name.
|
|
9
9
|
- [Email templates](../reference/email.md#templates): Your own emails are React components with typed data, sent from jobs, hooks or functions.
|
|
10
|
+
- [Protobase's mail look](../reference/email.md#the-look-of-protobases-own-mail): Your emails use the layout, button, code and note that Protobase's own mail is built from.
|
|
11
|
+
- [Unsubscribe headers](../reference/email.md#templates): Your emails carry header fields of their own, such as a one-click unsubscribe link for bulk mail.
|
|
10
12
|
- [Attachments](../reference/email.md#attachments): A message carries files, such as an invoice's PDF, through the app's mail or someone's own mailbox.
|
|
11
13
|
- [PDF documents](../reference/pdf.md#documents): PDF files such as invoices and quotes are React components with typed data, rendered from jobs, hooks or functions and attached to mail as they are.
|
|
12
14
|
- [Mail from your own mailbox](../reference/email.md#from-someones-own-mailbox) (beta): People link their Gmail on the account page, and the app's functions send mail from it, with a clear error when the link is missing or expired.
|
|
@@ -10,10 +10,12 @@ React to changes, deliver them to other systems, and run work in the background.
|
|
|
10
10
|
- [Signed webhooks](../reference/webhooks.md#what-a-receiver-gets): Deliveries are signed under the Standard Webhooks spec.
|
|
11
11
|
- [Delivery retries](../reference/webhooks.md#delivery-and-retries): Failed deliveries are retried for about three days.
|
|
12
12
|
- [Webhooks in the admin app](../reference/webhooks.md#subscriptions-in-the-admin-app): Users create subscriptions, send test events and resend failures in the app.
|
|
13
|
+
- [Webhook delivery log](../reference/webhooks.md#subscriptions-in-the-admin-app): Every try of the admin app's subscriptions, successes too, is kept for 30 days with its status and timing, when the app turns it on.
|
|
13
14
|
- [Background jobs](../reference/jobs.md#enqueueing): Jobs are kept in the app's own Postgres and can be queued in the same transaction as a write, also from SQL.
|
|
14
15
|
- [Typed jobs](../reference/jobs.md#typed-names-and-payloads): Code that queues a job, and the job itself, are checked against the names and arguments the config declares as they compile.
|
|
15
16
|
- [Retries](../reference/jobs.md#failures): Failed jobs are retried with growing waits, and duplicates can be prevented with a key.
|
|
16
17
|
- [Ordered groups](../reference/jobs.md#ordered-groups): Jobs in one group run one at a time, in order.
|
|
17
18
|
- [Recurring jobs](../reference/jobs.md#recurring-jobs): Jobs run on a cron schedule in a chosen time zone.
|
|
18
19
|
- [Failed jobs page](../reference/jobs.md#failed-jobs-page): Admins see jobs that failed for good and retry or delete them.
|
|
20
|
+
- [Worker process](../reference/jobs.md#where-jobs-run): Jobs and event deliveries run beside the API, or in a worker process of their own from the same bundle.
|
|
19
21
|
- [Jobs on hosts that sleep](../reference/jobs.md#hosts-that-sleep): A host that stops idle apps can wake them when a job is due.
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
Uploaded files and images, where they are kept and how they are served.
|
|
6
6
|
|
|
7
7
|
- [File fields](../reference/files.md#what-the-column-holds): A text column holds an uploaded file, with limits on type and size.
|
|
8
|
+
- [File uploads in forms](../reference/files.md#in-forms): Forms upload a dropped or chosen file at once with a progress bar, and replace or remove it; tables show its name and a thumbnail.
|
|
8
9
|
- [File storage](../reference/files.md#providers): Files are kept on disk, or in a bucket of your own.
|
|
9
10
|
- [S3-compatible buckets](../reference/files.md#s3-compatible-buckets) (beta): Files are kept in any bucket that speaks the S3 API, when the app runs on Bun.
|
|
10
11
|
- [Files from a seed](../reference/files.md#writing-files-from-a-seed): A seed or another script writes files into the app's providers, read from its config and environment as the server reads them.
|
|
@@ -12,5 +12,6 @@ The changes of each record, comments on it, and pages that refresh as records ch
|
|
|
12
12
|
- [Comment moderation](../reference/comments.md#moderation): Admins find and delete comments across the app.
|
|
13
13
|
- [Live updates](../reference/live.md#what-a-page-hears): Open lists and record pages refresh within seconds when a record changes, including changes made outside the API.
|
|
14
14
|
- [Change stream](../reference/live.md#the-stream): Apps follow record changes over a stream that catches up after a reconnect.
|
|
15
|
+
- [Polling, then a socket](../reference/live.md#polling-then-a-socket): Apps and Node scripts poll for changes first and switch to a WebSocket after 30 seconds, or at once, falling back to polling while the socket is down.
|
|
15
16
|
- [Presence](../reference/channels.md#presence) (beta): Apps see who else has a record open and what each of them tracks, such as online or away, for whoever may read the record.
|
|
16
17
|
- [Broadcasts](../reference/channels.md#broadcasts) (beta): Apps on a record send each other short-lived events, such as typing, which are never stored, as JSON or as bytes, such as a chunk of audio or a drawing stroke; the app's own code sends them too, from an API function, over HTTP or [from SQL](../reference/channels.md#from-sql) with `channels.send()`.
|
|
@@ -8,6 +8,7 @@ The app's colours, name, icon, language and installation.
|
|
|
8
8
|
- [Dark mode](../reference/theme.md#light-dark-or-the-system): Each user picks light, dark or the system setting.
|
|
9
9
|
- [App name, icon and colours](../reference/app-identity.md): The app's own name, icon and colours in the sidebar, browser tabs, `/favicon.ico`, home-screen icon and mail.
|
|
10
10
|
- [Logo and favicon](../reference/theme.md#logo-and-favicon): Your own favicon or logo replaces Protobase's.
|
|
11
|
+
- [Named icons](../reference/layouts.md#named-actions): Actions, key facts and card meta show an icon picked by name from a set, such as `play`, `bed` or `cpu`.
|
|
11
12
|
- [English and Dutch](../reference/languages.md): The built-in screens come in English and Dutch, with numbers and dates written to match.
|
|
12
13
|
- [Installable app](../reference/installable-app.md): The admin installs as an app on phones and computers, and says when the server cannot be reached.
|
|
13
14
|
- Reduced motion: When the system asks for less motion, animations and pulsing dots stop.
|
|
@@ -4,14 +4,15 @@
|
|
|
4
4
|
|
|
5
5
|
The sidebar, the user menu, the bottom bar on phones and the global search.
|
|
6
6
|
|
|
7
|
-
- [Sidebar](../reference/sidebar.md): Resources and pages in groups, in the order you set.
|
|
7
|
+
- [Sidebar](../reference/sidebar.md): Resources and pages in groups, in the order you set, and a drawer on phones.
|
|
8
8
|
- [Sidebar icons](../reference/sidebar.md#sidebar-icons): Each resource shows the icon its view names in the sidebar, bottom bar and profile menu.
|
|
9
|
-
- [Recent records](../reference/sidebar.md#recent-records): A sidebar entry lists a few records under it, with status dots or as titles alone, and marked per reader, such as unread ones in bold with a count.
|
|
9
|
+
- [Recent records](../reference/sidebar.md#recent-records) (beta): A sidebar entry lists a few records under it, with status dots or as titles alone, and marked per reader, such as unread ones in bold with a count.
|
|
10
10
|
- [Create link](../reference/sidebar.md#create-link): A sidebar entry links to its create form, such as "New message" under Direct messages, for whoever may create.
|
|
11
11
|
- [The current organization](../reference/sidebar.md#the-current-organization): A sidebar entry opens its list on the organization you work in, which you can clear to see every organization you are a member of.
|
|
12
12
|
- [User menu](../reference/user-menu.md): Account pages, help links, security settings, setting a status, the theme switch and Sign out.
|
|
13
13
|
- [Bottom bar](../reference/bottom-bar.md): On phones, tabs along the bottom for each role's main screens.
|
|
14
14
|
- [Global search](../reference/client.md#global-search): A search box, also on Cmd+K or Ctrl+K, finds records in every resource and jumps to pages and New forms.
|
|
15
|
-
- Breadcrumbs: The top bar shows the group, list and record you are on, each a link back.
|
|
16
|
-
-
|
|
15
|
+
- [Breadcrumbs](../reference/ui-config.md#breadcrumbs): The top bar shows the group, list and record you are on, each a link back, and on a phone a back button to the one before.
|
|
16
|
+
- [Tab titles](../reference/ui-config.md#names-and-columns): Browser tabs name the record, list or page they show, then the app.
|
|
17
|
+
- [Update notice](../reference/versioning.md#a-new-version-under-an-open-page): When the server runs a newer Protobase version, open pages offer a reload so the user can save their work first.
|
|
17
18
|
- Config refresh: When the config or the user's roles change, open pages load the new menus, columns and permissions without a reload.
|
|
@@ -5,12 +5,15 @@
|
|
|
5
5
|
Sign-in methods, users, roles, organizations and tokens.
|
|
6
6
|
|
|
7
7
|
- [Built-in sign-in](../reference/auth.md): Password sign-in, sessions and short-lived tokens, with no separate identity service.
|
|
8
|
+
- [Sign-in page](../reference/client.md#the-admin-app): The app signs people in at its own address, where the browser's Back returns to the step before, and signing in comes back to the page they were on.
|
|
8
9
|
- [First admin](../reference/auth.md#first-run): No account can be made over the web while the app has no users. You create the first admin from the terminal, and the last active admin cannot be deleted, demoted or blocked.
|
|
9
10
|
- [Users from a script](../reference/auth.md#first-run): A seed or another script of the project creates and lists users, and mints their tokens, on the app's own sign-in.
|
|
10
11
|
- [Versioned sign-in tables](../reference/auth.md#migrations): The sign-in tables come from numbered migrations that ship with Protobase, written as the project's own and recorded in the database, so a deploy checks them without comparing tables. Every feature has its tables from the start, so turning one on or off needs no migration.
|
|
11
12
|
- [Existing users table](../reference/auth.md#an-existing-users-table): Sign-in can use the users table another system keeps in the same database.
|
|
12
13
|
- [Users page](../reference/auth.md#users-page): Admins find people, set roles, block them, add accounts and invite people by email.
|
|
14
|
+
- [Account page](../reference/auth.md#account-page): Everyone signed in has an account page for their profile, sign-in and security, devices and connected apps, and you add, hide or reorder its tabs.
|
|
13
15
|
- [Password reset](../reference/auth.md#password-reset): People reset a forgotten password through an emailed link.
|
|
16
|
+
- [Password changes](../reference/auth.md#name-password-and-devices): Changing the password signs out every other device and MCP client, and emails the account that its password changed. Guesses at the current password are limited for each account.
|
|
14
17
|
- [Leaked passwords](../reference/auth.md#leaked-passwords): New passwords that appear in a known data breach are refused, when the app turns the check on.
|
|
15
18
|
- [Emailed sign-in codes](../reference/auth.md#emailed-sign-in-codes): People sign in with a code sent to their email. An address with no account is emailed that it has none, so the person tries the one they signed up with. An app can have the sign-in page lead with the code and put the password behind "Use a password".
|
|
16
19
|
- [Changing the address](../reference/auth.md#changing-the-address): People move their account to a new email address from the account page, once they enter the code sent there.
|
|
@@ -18,8 +21,12 @@ Sign-in methods, users, roles, organizations and tokens.
|
|
|
18
21
|
- [Open sign-up](../reference/auth.md#open-sign-up): Anyone can create an account after confirming an emailed code.
|
|
19
22
|
- [Passkeys](../reference/auth.md#passkeys): People sign in with their device's face or fingerprint unlock, or with a security key.
|
|
20
23
|
- [Two-factor authentication](../reference/auth.md#two-factor-authentication): A second step with an authenticator app, backup codes or emailed codes.
|
|
24
|
+
- [Emailed confirmation code](../reference/auth.md#emailed-confirmation-code): Someone without a passkey or a second step confirms it's them with a code emailed on request, where they are, such as before a secret is shown.
|
|
25
|
+
- [Confirming an account change](../reference/auth.md#confirming-an-account-change): Adding a passkey, unlinking a provider or deleting the account from an older session asks to confirm it's them in place, never to sign out and sign in again.
|
|
21
26
|
- [Sign-in policy](../reference/auth.md#sign-in-policy): Admins choose which sign-in methods are on, off or required, and the server refuses a policy that would lock people out.
|
|
22
27
|
- [GitHub and social sign-in](../reference/auth.md#sign-in-with-github): People sign in with GitHub or another social provider.
|
|
28
|
+
- [Sign-in links](../reference/auth.md#sign-in-links-and-connected-accounts): A link signs someone in with a sign-in provider at once, without the sign-in page.
|
|
29
|
+
- [Connected accounts](../reference/auth.md#sign-in-links-and-connected-accounts): People link GitHub or another sign-in provider to their account from the account page.
|
|
23
30
|
- [SAML sign-in](../reference/auth.md#sign-in-with-saml): People sign in through any SAML provider.
|
|
24
31
|
- [OpenID Connect provider](../reference/auth.md#platform-sign-in-provider): A host adds an OpenID Connect provider for people who already have an account.
|
|
25
32
|
- [Staff sign-in](../reference/auth.md#staff-sign-in): The team that runs an app signs in as a person to help them, with a reason and a log entry.
|
|
@@ -19,7 +19,8 @@ Start a new app, or an admin over an existing Postgres database, and keep the co
|
|
|
19
19
|
- [Scaffold from a database](../reference/cli.md#scaffold): Reads your Postgres tables and writes the data config for them, with field types, keys, relations, enums, soft delete, tenants and search filled in, and with `--ui` a first view of each.
|
|
20
20
|
- [Safe re-scaffolding](../reference/cli.md#re-running): Running scaffold again adds only the columns that are not declared yet and leaves your own config code as it is.
|
|
21
21
|
- [Schema drift report](../reference/cli.md#drift-report): After each scaffold, lists declared fields whose column is gone, whose type changed or whose enum values differ, with a suggested fix.
|
|
22
|
-
- [Doctor](../reference/cli.md#doctor): Checks every resource and view against the database,
|
|
22
|
+
- [Doctor](../reference/cli.md#doctor): Checks every resource and view against the database, the ones Protobase adds such as webhook subscriptions included, with the reads that lack an index, and prints the fix for each problem.
|
|
23
23
|
- [Admin without a view](../guides/table-to-admin.md#1-the-table-as-it-is): A table with an empty view already gives a working admin with every column, sorting, paging, a record page and a New button.
|
|
24
24
|
- [Agent skills](../reference/cli.md#skill): `protobase skill install` writes agent skills that match the installed version into the project, for the coding agents that work on it.
|
|
25
25
|
- [Docs as Markdown](../getting-started.md#docs-as-markdown): Every docs page is also Markdown at its address plus `.md`, for coding agents and chat assistants.
|
|
26
|
+
- [llms.txt](../getting-started.md#docs-as-markdown): The docs site serves `/llms.txt`, which links every page's Markdown, and the protobase agent skill at `/skills/protobase/SKILL.md`, for chat assistants.
|