@protobase/cli 0.8.2 → 0.10.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/dist/dev/entry.js CHANGED
@@ -1,12 +1,13 @@
1
1
  import { devJobs } from "./dev-jobs.js";
2
+ import { projectAdmin } from "../project/project-admin.js";
2
3
  import { loadProject, projectDb } from "./project.js";
3
- import { checkAuthSchema, checkChanges, configExports, createAdmin, hasUsers, jobDefinitions } from "@protobase/server";
4
+ import { checkAuthSchema, checkChanges, checkWebhooksSchema, configExports, hasUsers } from "@protobase/server";
4
5
  import { Hono } from "hono";
5
6
  //#region src/dev/entry.ts
6
7
  const projectDir = process.env.PROTOBASE_PROJECT;
7
8
  const project = await loadProject(projectDir, JSON.parse(process.env.PROTOBASE_EXTENSIONS ?? "[]"));
8
9
  const db = projectDb(project, projectDir);
9
- const { resources, views, pages, userMenu } = configExports(project.exports);
10
+ const { resources } = configExports(project.exports);
10
11
  if (!project.authenticate) throw new Error("No authenticator: export `authenticate` (and `auth`) from protobase.config.ts; see https://docs.protobase.net/reference/auth/");
11
12
  const store = globalThis;
12
13
  if (project.auth && !store.__protobaseAuthSchemaChecked) {
@@ -14,24 +15,12 @@ if (project.auth && !store.__protobaseAuthSchemaChecked) {
14
15
  store.__protobaseAuthSchemaChecked = true;
15
16
  }
16
17
  await checkChanges(db, resources.map((resource) => resource.toModel()), project.events);
17
- await devJobs(db, jobDefinitions(project.jobs ?? {}));
18
- const admin = createAdmin({
19
- resources,
20
- views,
21
- pages,
22
- ...userMenu && { userMenu },
23
- db,
24
- authenticate: project.authenticate,
25
- ...project.auth && { auth: project.auth },
26
- ...project.functions && { functions: project.functions },
27
- ...project.files && { files: project.files },
28
- ...project.events && { events: project.events },
29
- jobs: jobDefinitions(project.jobs ?? {}),
30
- options: {
31
- onUnhandledError: (error) => console.error(error),
32
- ...project.options
33
- }
34
- });
18
+ if (project.webhooks) await checkWebhooksSchema(db, project.webhooks);
19
+ const { admin, jobs } = projectAdmin({
20
+ ...project,
21
+ authenticate: project.authenticate
22
+ }, project.exports, db);
23
+ await devJobs(db, jobs);
35
24
  var entry_default = new Hono().route("/", admin);
36
25
  if (project.auth && !store.__protobaseNoUsersShown) {
37
26
  store.__protobaseNoUsersShown = true;
package/dist/dev/run.js CHANGED
@@ -79,7 +79,7 @@ const runDev = async (options, out) => {
79
79
  tailwindcss(),
80
80
  devServer({
81
81
  entry,
82
- exclude: [/^(?!\/api(\/|\?|$)).*/],
82
+ exclude: [/^(?!\/api(\/|\?|$)|\/\.well-known\/webauthn(\?|$)).*/],
83
83
  injectClientScript: false
84
84
  })
85
85
  ]
@@ -3,6 +3,11 @@ const words = (name) => name.split(/[^A-Za-z0-9]+/).filter(Boolean);
3
3
  const capitalize = (word) => word.charAt(0).toUpperCase() + word.slice(1);
4
4
  const lowerFirst = (word) => word.charAt(0).toLowerCase() + word.slice(1);
5
5
  const identifier = (name) => /^[0-9]/.test(name) ? `c${name}` : name;
6
+ const snakeName = (name) => {
7
+ const parts = words(name.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/([A-Z])([A-Z][a-z])/g, "$1_$2"));
8
+ if (parts.length === 0) throw new Error(`Cannot derive a field name from "${name}"`);
9
+ return identifier(parts.map((part) => part.toLowerCase()).join("_"));
10
+ };
6
11
  const camelCase = (name) => {
7
12
  const parts = words(name);
8
13
  if (parts.length === 0) throw new Error(`Cannot derive a field name from "${name}"`);
@@ -25,4 +30,4 @@ const humanizeSingular = (name) => {
25
30
  return [...parts.slice(0, -1), singular(parts.at(-1).toLowerCase())].join(" ").replace(/^./, (c) => c.toUpperCase());
26
31
  };
27
32
  //#endregion
28
- export { camelCase, humanize, humanizeSingular, singular };
33
+ export { camelCase, humanize, humanizeSingular, singular, snakeName };
@@ -3,7 +3,7 @@ import { checkEnumValues } from "./check-values.js";
3
3
  import { columnDefault } from "./defaults.js";
4
4
  import { inferKind } from "./type-map.js";
5
5
  import { defaultColumn } from "./default-column.js";
6
- import { camelCase } from "./names.js";
6
+ import { snakeName } from "./names.js";
7
7
  import { isTenantColumn } from "./tenant.js";
8
8
  //#region src/infer/proposal.ts
9
9
  const columnIndexing = (table, column) => {
@@ -41,7 +41,7 @@ const defaultsOf = (table, column, kind) => {
41
41
  const proposeField = (table, column, context) => {
42
42
  const kind = inferKind(column, checkEnumValues(table.checks, column.name));
43
43
  const relation = relationOf(table, column.name, context);
44
- const name = camelCase(column.name);
44
+ const name = snakeName(column.name);
45
45
  const defaults = defaultsOf(table, column, kind);
46
46
  return {
47
47
  name,
@@ -32,6 +32,7 @@ const loadMigrationProject = async (dir, extensions = []) => {
32
32
  db: project.db ?? ownDb,
33
33
  ...project.auth && { auth: project.auth },
34
34
  ...project.events && { events: project.events },
35
+ ...project.webhooks && { webhooks: project.webhooks },
35
36
  jobs: jobDefinitions(project.jobs ?? {}),
36
37
  close: async () => {
37
38
  await ownDb?.destroy();
@@ -1,4 +1,4 @@
1
- import { authSchemaMigration, changesMigration, jobsSchemaMigration } from "@protobase/server";
1
+ import { authSchemaMigration, changesMigration, jobsSchemaMigration, webhooksMigration } from "@protobase/server";
2
2
  //#region src/migration/parts.ts
3
3
  /** Every feature the project's config enables, in the order their files are written. */
4
4
  const migrationParts = (project) => [
@@ -7,7 +7,7 @@ const migrationParts = (project) => [
7
7
  title: "The auth schema changes this version of Protobase needs",
8
8
  sql: () => authSchemaMigration(project.auth)
9
9
  }] : [],
10
- ...project.jobs.length > 0 ? [{
10
+ ...project.jobs.length > 0 || project.models.some((model) => model.events) || project.webhooks ? [{
11
11
  name: "jobs",
12
12
  title: "The jobs schema this version of Protobase needs",
13
13
  sql: () => jobsSchemaMigration(project.db)
@@ -16,7 +16,12 @@ const migrationParts = (project) => [
16
16
  name: "changes",
17
17
  title: "The change capture the resources' events need",
18
18
  sql: () => changesMigration(project.db, project.models, project.events)
19
- }
19
+ },
20
+ ...project.webhooks ? [{
21
+ name: "webhooks",
22
+ title: "The webhook subscriptions of webhooks()",
23
+ sql: () => webhooksMigration(project.db, project.webhooks, project.events)
24
+ }] : []
20
25
  ];
21
26
  //#endregion
22
27
  export { migrationParts };
package/dist/package.js CHANGED
@@ -1,4 +1,4 @@
1
1
  //#region package.json
2
- var version = "0.8.2";
2
+ var version = "0.10.0";
3
3
  //#endregion
4
4
  export { version };
@@ -0,0 +1,36 @@
1
+ import { configExports, createAdmin, jobDefinitions } from "@protobase/server";
2
+ //#region src/project/project-admin.ts
3
+ /**
4
+ * The admin API of a project over `db`, as `dev`, `serve` and `worker` build it: its config module's resources, views,
5
+ * pages and subscriptions, and the rest of its config. `jobs` are the project's own and the delivery of its events.
6
+ */
7
+ const projectAdmin = (project, config, db) => {
8
+ const { resources, views, pages, userMenu, bottomBars, subscriptions } = configExports(config);
9
+ const own = jobDefinitions(project.jobs ?? {});
10
+ const admin = createAdmin({
11
+ resources,
12
+ views,
13
+ pages,
14
+ ...userMenu && { userMenu },
15
+ bottomBars,
16
+ db,
17
+ authenticate: project.authenticate,
18
+ ...project.auth && { auth: project.auth },
19
+ ...project.functions && { functions: project.functions },
20
+ ...project.files && { files: project.files },
21
+ ...project.events && { events: project.events },
22
+ ...project.webhooks && { webhooks: project.webhooks },
23
+ jobs: own,
24
+ subscriptions,
25
+ options: {
26
+ onUnhandledError: (error) => console.error(error),
27
+ ...project.options
28
+ }
29
+ });
30
+ return {
31
+ admin,
32
+ jobs: [...own, ...admin.jobs]
33
+ };
34
+ };
35
+ //#endregion
36
+ export { projectAdmin };
@@ -1,4 +1,5 @@
1
1
  import { tableKey } from "../db/model.js";
2
+ import { camelCase } from "../infer/names.js";
2
3
  import { parseSource } from "./project.js";
3
4
  import { fieldsObject, resourceChain } from "./existing-config.js";
4
5
  import { renderField } from "../infer/render-field.js";
@@ -19,7 +20,7 @@ const renderDataFile = (plan) => {
19
20
  return [
20
21
  `import { f, resource } from '@protobase/schema'`,
21
22
  "",
22
- `${docComment(plan)}export const ${plan.name} = resource('${plan.name}')`,
23
+ `${docComment(plan)}export const ${camelCase(plan.name)} = resource('${plan.name}')`,
23
24
  ` .table('${tableKey(plan.table)}')`,
24
25
  ` .fields({`,
25
26
  properties,
@@ -1,5 +1,5 @@
1
1
  import { defaultColumn } from "../infer/default-column.js";
2
- import { camelCase } from "../infer/names.js";
2
+ import { camelCase, snakeName } from "../infer/names.js";
3
3
  import { parseSource } from "./project.js";
4
4
  import path from "node:path";
5
5
  import { readFile, readdir } from "node:fs/promises";
@@ -65,9 +65,11 @@ const parseResource = (text, dir) => {
65
65
  const tableCall = methodCalls(chainTop, "table")[0];
66
66
  const table = tableCall && stringArg(tableCall);
67
67
  const fields = fieldsObject(chainTop);
68
- if (!name || !table || !fields) return void 0;
68
+ const identifier = root.getFirstAncestorByKind(SyntaxKind.VariableDeclaration)?.getName();
69
+ if (!name || !table || !fields || !identifier) return void 0;
69
70
  return {
70
71
  name,
72
+ identifier,
71
73
  table,
72
74
  dir,
73
75
  text,
@@ -77,7 +79,7 @@ const parseResource = (text, dir) => {
77
79
  })
78
80
  };
79
81
  };
80
- const findField = (resource, column) => resource.fields.find((field) => field.ignored ? field.column.toLowerCase() === column.toLowerCase() || field.name === camelCase(column) : field.column === column);
82
+ const findField = (resource, column) => resource.fields.find((field) => field.ignored ? field.column.toLowerCase() === column.toLowerCase() || field.name === snakeName(column) || field.name === camelCase(column) : field.column === column);
81
83
  const readExistingResources = async (configDir) => {
82
84
  const entries = await readdir(configDir, { withFileTypes: true }).catch((error) => {
83
85
  if (error.code === "ENOENT") return [];
@@ -1,5 +1,6 @@
1
1
  import { tableKey } from "../db/model.js";
2
2
  import { keyColumns } from "../db/keys.js";
3
+ import { camelCase } from "../infer/names.js";
3
4
  import { findField } from "./existing-config.js";
4
5
  import { proposeField } from "../infer/proposal.js";
5
6
  import { addFieldsToDataFile, renderDataFile } from "./data-file.js";
@@ -49,19 +50,20 @@ const planScaffold = async (input) => {
49
50
  const drift = [];
50
51
  const warnings = [];
51
52
  const registrations = [];
52
- const writeUi = async (dir, resource, table, current, decisions) => {
53
+ const writeUi = async (dir, resource, identifier, table, current, decisions) => {
53
54
  if (ui.length === 0) return;
54
55
  const file = path.join(configDir, dir, "ui.ts");
55
56
  const before = await input.readText(file);
56
57
  const target = {
57
58
  resource,
59
+ identifier,
58
60
  tableName: table.name,
59
61
  fields: uiFields(table, current, decisions)
60
62
  };
61
63
  changes.push(...change(file, before, before === void 0 ? renderUiFile(target, ui) : addUiSections(before, target, ui)));
62
64
  if (before === void 0) registrations.push({
63
65
  from: `./${dir}/ui`,
64
- names: [viewName(resource)]
66
+ names: [viewName(identifier)]
65
67
  });
66
68
  };
67
69
  for (const table of targets) {
@@ -75,7 +77,7 @@ const planScaffold = async (input) => {
75
77
  const file = path.join(configDir, current.dir, "data.ts");
76
78
  changes.push(...change(file, current.text, addFieldsToDataFile(current.text, decisions)));
77
79
  drift.push(...detectDrift(current, table, context(table)));
78
- await writeUi(current.dir, current.name, table, current, decisions);
80
+ await writeUi(current.dir, current.name, current.identifier, table, current, decisions);
79
81
  continue;
80
82
  }
81
83
  const name = names.get(key);
@@ -93,9 +95,9 @@ const planScaffold = async (input) => {
93
95
  });
94
96
  registrations.push({
95
97
  from: `./${name}/data`,
96
- names: [name]
98
+ names: [camelCase(name)]
97
99
  });
98
- await writeUi(name, name, table, void 0, decisions);
100
+ await writeUi(name, name, camelCase(name), table, void 0, decisions);
99
101
  }
100
102
  const indexFile = path.join(configDir, "index.ts");
101
103
  const indexBefore = await input.readText(indexFile);
@@ -114,12 +116,12 @@ const registerExisting = async (input, planned) => {
114
116
  const data = `./${resource.dir}/data`;
115
117
  if (!known.has(data)) registrations.push({
116
118
  from: data,
117
- names: [resource.name]
119
+ names: [resource.identifier]
118
120
  });
119
121
  const ui = `./${resource.dir}/ui`;
120
122
  if (await input.readText(path.join(input.configDir, resource.dir, "ui.ts")) !== void 0 && !known.has(ui)) registrations.push({
121
123
  from: ui,
122
- names: [viewName(resource.name)]
124
+ names: [viewName(resource.identifier)]
123
125
  });
124
126
  }
125
127
  return registrations;
@@ -1,15 +1,15 @@
1
1
  import { tableKey } from "../db/model.js";
2
- import { camelCase } from "../infer/names.js";
2
+ import { snakeName } from "../infer/names.js";
3
3
  //#region src/scaffold/resource-names.ts
4
4
  const assignNames = (tables, existing) => {
5
5
  const names = new Map(existing.map((resource) => [resource.table, resource.name]));
6
6
  const taken = new Set(names.values());
7
7
  const fresh = tables.filter((table) => !names.has(tableKey(table)));
8
8
  const counts = /* @__PURE__ */ new Map();
9
- for (const table of tables) counts.set(camelCase(table.name), (counts.get(camelCase(table.name)) ?? 0) + 1);
9
+ for (const table of tables) counts.set(snakeName(table.name), (counts.get(snakeName(table.name)) ?? 0) + 1);
10
10
  for (const table of fresh) {
11
- const plain = camelCase(table.name);
12
- const name = counts.get(plain) === 1 && !taken.has(plain) ? plain : camelCase(`${table.schema}_${table.name}`);
11
+ const plain = snakeName(table.name);
12
+ const name = counts.get(plain) === 1 && !taken.has(plain) ? plain : snakeName(`${table.schema}_${table.name}`);
13
13
  names.set(tableKey(table), name);
14
14
  taken.add(name);
15
15
  }
@@ -3,7 +3,7 @@ import { parseSource } from "./project.js";
3
3
  import { methodCalls } from "./existing-config.js";
4
4
  import { Node, SyntaxKind } from "ts-morph";
5
5
  //#region src/scaffold/ui-file.ts
6
- const auditField = /^(created|updated)(At|On)$/;
6
+ const auditField = /^(created|updated)(At|On|_at|_on)$/;
7
7
  const longText = /^(notes?|description|body|comment|comments)$/;
8
8
  const listSize = 5;
9
9
  const listColumns = (fields) => {
@@ -13,7 +13,7 @@ const listColumns = (fields) => {
13
13
  const picked = [...scalars.slice(0, listSize), ...scalars.length < 3 ? relations : []].slice(0, listSize);
14
14
  return (picked.length > 0 ? picked : fields.filter((f) => f.role === "key")).map((f) => f.name);
15
15
  };
16
- const viewName = (resource) => `${resource}View`;
16
+ const viewName = (identifier) => `${identifier}View`;
17
17
  const sectionText = {
18
18
  names: (t) => `.names({ singular: '${humanizeSingular(t.tableName)}', plural: '${humanize(t.tableName)}' })`,
19
19
  list: (t) => `.list((r) => ({ columns: [${listColumns(t.fields).map((n) => `r.${n}`).join(", ")}] }))`
@@ -21,9 +21,9 @@ const sectionText = {
21
21
  const lines = (sections, target) => sections.map((s) => ` ${sectionText[s](target)}`);
22
22
  const renderUiFile = (target, sections) => [
23
23
  `import { view } from '@protobase/schema'`,
24
- `import type { ${target.resource} } from './data'`,
24
+ `import type { ${target.identifier} } from './data'`,
25
25
  "",
26
- `export const ${viewName(target.resource)} = view<typeof ${target.resource}>('${target.resource}')`,
26
+ `export const ${viewName(target.identifier)} = view<typeof ${target.identifier}>('${target.resource}')`,
27
27
  ...lines(sections, target),
28
28
  ""
29
29
  ].join("\n");
package/dist/serve/app.js CHANGED
@@ -1,32 +1,14 @@
1
1
  import { isApiPath } from "../bundle/manifest.js";
2
2
  import { serveStaticSite } from "./static-site.js";
3
- import { configExports, createAdmin, jobDefinitions, jobsDrainApp } from "@protobase/server";
3
+ import { jobsDrainApp, webauthnPath } from "@protobase/server";
4
4
  import { Hono } from "hono";
5
5
  import { logger } from "hono/logger";
6
6
  //#region src/serve/app.ts
7
- const createServeApp = ({ project, db, requestLog, site, jobs, drainToken }) => {
8
- const { resources, views, pages, userMenu } = configExports(project.config);
9
- const admin = createAdmin({
10
- resources,
11
- views,
12
- pages,
13
- ...userMenu && { userMenu },
14
- db,
15
- authenticate: project.authenticate,
16
- ...project.auth && { auth: project.auth },
17
- ...project.functions && { functions: project.functions },
18
- ...project.files && { files: project.files },
19
- ...project.events && { events: project.events },
20
- jobs: jobDefinitions(project.jobs ?? {}),
21
- options: {
22
- onUnhandledError: (error) => console.error(error),
23
- ...project.options
24
- }
25
- });
7
+ const createServeApp = ({ admin, requestLog, site, jobs, drainToken }) => {
26
8
  const app = new Hono();
27
9
  if (requestLog) app.use(logger());
28
10
  app.get("/health", (c) => c.json({ status: "ok" }));
29
- if (site) app.use("*", (c, next) => isApiPath(site.api, c.req.path) ? next() : serveStaticSite(site, c.req.raw));
11
+ if (site) app.use("*", (c, next) => isApiPath(site.api, c.req.path) || c.req.path === webauthnPath ? next() : serveStaticSite(site, c.req.raw));
30
12
  if (jobs && drainToken) app.route("/", jobsDrainApp({
31
13
  runtime: jobs,
32
14
  token: drainToken
@@ -1,7 +1,8 @@
1
+ import { projectAdmin } from "../project/project-admin.js";
1
2
  import { createPgDb } from "../project/pg-db.js";
2
3
  import { createServeApp } from "./app.js";
3
4
  import { startCleanupTimer } from "./cleanup-timer.js";
4
- import { checkAuthSchema, checkChanges, checkJobsSchema, configExports, createFileCleanup, jobDefinitions, startJobs } from "@protobase/server";
5
+ import { checkAuthSchema, checkChanges, checkJobsSchema, checkWebhooksSchema, configExports, createFileCleanup, startJobs } from "@protobase/server";
5
6
  import { createAdaptorServer } from "@hono/node-server";
6
7
  import pg from "pg";
7
8
  //#region src/serve/lifecycle.ts
@@ -19,9 +20,10 @@ const startServe = async ({ project, env, createDb = (url) => createPgDb(pg, url
19
20
  const ownDb = project.db ? void 0 : createDb(env.databaseUrl);
20
21
  const db = project.db ?? ownDb;
21
22
  const { resources } = configExports(project.config);
22
- const definitions = jobDefinitions(project.jobs ?? {});
23
23
  const startJobsAndListen = async () => {
24
24
  await checkChanges(db, resources.map((resource) => resource.toModel()), project.events);
25
+ if (project.webhooks) await checkWebhooksSchema(db, project.webhooks);
26
+ const { admin, jobs: definitions } = projectAdmin(project, project.config, db);
25
27
  if (definitions.length > 0) await checkJobsSchema(db);
26
28
  const jobs = definitions.length > 0 ? await startJobs({
27
29
  db,
@@ -30,8 +32,7 @@ const startServe = async ({ project, env, createDb = (url) => createPgDb(pg, url
30
32
  onError: (error) => console.error(error)
31
33
  }) : void 0;
32
34
  const app = createServeApp({
33
- project,
34
- db,
35
+ admin,
35
36
  requestLog: env.requestLog,
36
37
  site,
37
38
  ...jobs && { jobs },
@@ -1,10 +1,11 @@
1
1
  import { readManifest } from "../bundle/manifest.js";
2
2
  import { checkBundleVersion } from "../serve/bundle-version.js";
3
+ import { projectAdmin } from "../project/project-admin.js";
3
4
  import { createPgDb } from "../project/pg-db.js";
4
5
  import { loadBundle } from "../serve/load-bundle.js";
5
6
  import { statSync } from "node:fs";
6
7
  import path from "node:path";
7
- import { checkJobsSchema, jobDefinitions, startJobs } from "@protobase/server";
8
+ import { checkJobsSchema, configExports, jobDefinitions, startJobs } from "@protobase/server";
8
9
  import pg from "pg";
9
10
  //#region src/worker/start-worker.ts
10
11
  const bundleConfigFile = async (bundle) => {
@@ -16,20 +17,24 @@ const bundleConfigFile = async (bundle) => {
16
17
  * their own. It reads the jobs schema at start and refuses one that is missing or behind, like `serve`.
17
18
  */
18
19
  const startWorker = async ({ project, databaseUrl, createDb = (url) => createPgDb(pg, url) }) => {
19
- const definitions = jobDefinitions(project.jobs ?? {});
20
- if (definitions.length === 0) throw new Error("The bundle has no jobs: export them from protobase.config.ts as `jobs`");
20
+ const withEvents = configExports(project.config).resources.some((resource) => resource.toModel().events);
21
+ if (jobDefinitions(project.jobs ?? {}).length === 0 && !withEvents) throw new Error("The bundle has no jobs: export them from protobase.config.ts as `jobs`, or give a resource events");
21
22
  if (!project.db && !databaseUrl) throw new Error("No database: set DATABASE_URL or export `db` from protobase.config.ts");
22
23
  const ownDb = project.db ? void 0 : createDb(databaseUrl);
23
24
  const db = project.db ?? ownDb;
24
25
  const start = async () => {
26
+ const { jobs: definitions } = projectAdmin(project, project.config, db);
25
27
  await checkJobsSchema(db);
26
- return startJobs({
27
- db,
28
- jobs: definitions,
29
- onError: (error) => console.error(error)
30
- });
28
+ return {
29
+ definitions,
30
+ runtime: await startJobs({
31
+ db,
32
+ jobs: definitions,
33
+ onError: (error) => console.error(error)
34
+ })
35
+ };
31
36
  };
32
- const jobs = await start().catch(async (error) => {
37
+ const { definitions, runtime: jobs } = await start().catch(async (error) => {
33
38
  await ownDb?.destroy();
34
39
  throw error;
35
40
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@protobase/cli",
3
- "version": "0.8.2",
3
+ "version": "0.10.0",
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.2",
38
- "@protobase/query": "0.8.2",
39
- "@protobase/schema": "0.8.2",
40
- "@protobase/server": "0.8.2",
41
- "@protobase/ui": "0.8.2",
37
+ "@protobase/layout": "0.10.0",
38
+ "@protobase/query": "0.10.0",
39
+ "@protobase/schema": "0.10.0",
40
+ "@protobase/server": "0.10.0",
41
+ "@protobase/ui": "0.10.0",
42
42
  "@scalar/hono-api-reference": "^0.12.10",
43
43
  "@tailwindcss/vite": "^4.3.3",
44
44
  "@vitejs/plugin-react": "^6.1.2",