@protobase/server 0.35.0 → 0.37.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/admin-options.d.ts +7 -0
- package/dist/aggregate-groups.js +1 -1
- package/dist/aggregate-service.js +3 -3
- package/dist/aggregate-splits.js +1 -1
- package/dist/assistant/built-in-assistant.js +3 -11
- package/dist/assistant/checked-tools.js +14 -0
- package/dist/assistant/query-tools.js +9 -0
- package/dist/assistant/readable-resources.d.ts +7 -0
- package/dist/assistant/readable-resources.js +13 -0
- package/dist/assistant/record-tools.d.ts +11 -0
- package/dist/assistant/record-tools.js +140 -0
- package/dist/assistant/records.d.ts +27 -0
- package/dist/assistant/records.js +4 -0
- package/dist/assistant/tools.d.ts +19 -2
- package/dist/assistant/turn.d.ts +3 -1
- package/dist/assistant/turn.js +6 -1
- package/dist/comments/mention-job.d.ts +15 -2
- package/dist/comments/mentionable.js +1 -1
- package/dist/comments/read-comments.js +1 -1
- package/dist/create-admin.js +36 -1
- package/dist/deps.d.ts +79 -10
- package/dist/encryption/apply.js +1 -1
- package/dist/encryption/binding.d.ts +1 -1
- package/dist/encryption/keyring.d.ts +12 -0
- package/dist/encryption/runtime.d.ts +8 -1
- package/dist/encryption/scan.d.ts +1 -1
- package/dist/encryption/status.d.ts +1 -1
- package/dist/encryption/status.js +1 -1
- package/dist/files/cleanup-runner.d.ts +5 -0
- package/dist/files/runtime.d.ts +28 -3
- package/dist/files/signer.d.ts +7 -0
- package/dist/functions/records.d.ts +2 -29
- package/dist/functions/records.js +11 -20
- package/dist/history/read-history.js +1 -1
- package/dist/index.d.ts +12 -4
- package/dist/index.js +9 -1
- package/dist/list-service.js +3 -3
- package/dist/map-service.js +1 -1
- package/dist/mcp/mcp-authenticator.js +14 -0
- package/dist/mcp/mcp-context.d.ts +11 -0
- package/dist/mcp/mcp-context.js +16 -0
- package/dist/mcp/mcp-gate.d.ts +19 -0
- package/dist/mcp/mcp-gate.js +52 -0
- package/dist/mcp/mcp-handler.d.ts +15 -0
- package/dist/mcp/mcp-handler.js +22 -0
- package/dist/mcp/mcp-routes.d.ts +12 -0
- package/dist/mcp/mcp-routes.js +15 -0
- package/dist/mcp/mcp-server.d.ts +19 -0
- package/dist/mcp/mcp-server.js +35 -0
- package/dist/mcp/mcp-tools.d.ts +12 -0
- package/dist/mcp/mcp-tools.js +8 -0
- package/dist/meta.d.ts +76 -0
- package/dist/records.js +1 -1
- package/dist/registry.d.ts +22 -2
- package/dist/request-access.d.ts +2 -2
- package/dist/routes/collection.js +1 -1
- package/dist/routes/record.js +1 -1
- package/dist/row-permissions.js +1 -1
- package/dist/scan-guard.d.ts +21 -3
- package/dist/sql-parts.js +1 -1
- package/dist/webhooks/subscriber-view.js +23 -7
- package/dist/webhooks/webhooks.js +36 -9
- package/dist/write-shared.js +1 -1
- package/package.json +5 -4
package/dist/meta.d.ts
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { Session } from "./types.js";
|
|
2
|
+
import { ErrorReportingSettings } from "./error-reporting/error-reporting-settings.js";
|
|
3
|
+
import { MapSettings } from "./maps/map-settings.js";
|
|
4
|
+
import { PluginRef } from "./plugins/plugin-settings.js";
|
|
5
|
+
import { SourceRef } from "./sources/source-settings.js";
|
|
6
|
+
import { Deps } from "./deps.js";
|
|
7
|
+
import { PageModel } from "@protobase/layout";
|
|
8
|
+
import { Locale } from "@protobase/schema/internal";
|
|
9
|
+
import { BottomBarItemModel, ResourceModel, SignInViewModel, UserMenuModel, ViewModel } from "@protobase/schema";
|
|
10
|
+
//#region src/meta.d.ts
|
|
11
|
+
export type ResourcePermissions = {
|
|
12
|
+
read: boolean;
|
|
13
|
+
create: boolean;
|
|
14
|
+
update: boolean;
|
|
15
|
+
delete: boolean;
|
|
16
|
+
/** Operations allowed only for some records: a row filter (`.own`) or a record-level rule decides. */
|
|
17
|
+
conditional: Array<'create' | 'update' | 'delete'>;
|
|
18
|
+
};
|
|
19
|
+
export type CallerMeta = {
|
|
20
|
+
/** The app's name, which ends the admin app's tab titles. */
|
|
21
|
+
appName: string;
|
|
22
|
+
/** The app's own icon, its favicon's path, when the project has one: the sidebar's logo draws it beside the name. */
|
|
23
|
+
appIcon?: string;
|
|
24
|
+
resources: ResourceModel[];
|
|
25
|
+
views: ViewModel[];
|
|
26
|
+
/** The composed pages the caller may open, without the elements that name what they cannot see or do. */
|
|
27
|
+
pages: PageModel[];
|
|
28
|
+
permissions: Record<string, ResourcePermissions>;
|
|
29
|
+
/** The user menu with the resources the caller cannot see left out. */
|
|
30
|
+
userMenu?: UserMenuModel;
|
|
31
|
+
/** For a visitor who is not signed in, the resources they cannot see whose view tells them what signing in gives. */
|
|
32
|
+
signIn?: SignInViewModel[];
|
|
33
|
+
/** The bottom bar naming the caller's active role, without the resources and pages they cannot see. */
|
|
34
|
+
bottomBar?: {
|
|
35
|
+
label: string;
|
|
36
|
+
items: BottomBarItemModel[];
|
|
37
|
+
};
|
|
38
|
+
/** The assistant backend, for a caller with the `admin` or `ai` role when there is one: an absolute URL, or the built-in one's path. */
|
|
39
|
+
assistant?: {
|
|
40
|
+
url: string;
|
|
41
|
+
};
|
|
42
|
+
/** The runtime updates endpoint, for a caller with the `admin` role when the app has one. */
|
|
43
|
+
runtime?: {
|
|
44
|
+
url: string;
|
|
45
|
+
};
|
|
46
|
+
/** Where the admin app sends its errors, when the app reports them: the DSN is public, as in any browser app. */
|
|
47
|
+
errorReporting?: ErrorReportingSettings;
|
|
48
|
+
/** The map style the admin app draws its maps with, and the layers they may switch to, when the app has one: public, as the tiles are. */
|
|
49
|
+
maps?: MapSettings;
|
|
50
|
+
/** The language the admin app's screens are in, when the app sets one for everyone. */
|
|
51
|
+
locale?: Locale;
|
|
52
|
+
/** The hosts besides the app's own whose images the admin app draws with the page's referrer, when there are any. */
|
|
53
|
+
files?: {
|
|
54
|
+
trustedHosts: string[];
|
|
55
|
+
};
|
|
56
|
+
/** The plugins the admin app loads for the caller: those for everyone, and those for one of their roles. */
|
|
57
|
+
plugins?: PluginRef[];
|
|
58
|
+
/** The other APIs whose resources the admin app draws for the caller: those for everyone, and those for one of their roles. */
|
|
59
|
+
sources?: SourceRef[];
|
|
60
|
+
};
|
|
61
|
+
/**
|
|
62
|
+
* `/meta` as one caller sees it: the resources they can do anything with, each as the restricted model (hidden fields
|
|
63
|
+
* absent, `readOnly` unless writable), the view for their roles with hidden fields pruned, what they may do, and the
|
|
64
|
+
* user menu and their bottom bar without the resources they cannot see. A visitor also gets the prompts of the resources
|
|
65
|
+
* they could use once signed in, which keep their place in the bottom bar and the user menu.
|
|
66
|
+
* The ETag hashes all of it, so it differs per user and per roles.
|
|
67
|
+
*/
|
|
68
|
+
export declare const createMeta: (deps: Deps, appName: string, appIcon?: string) => {
|
|
69
|
+
forCaller: (session: Session) => Promise<{
|
|
70
|
+
body: CallerMeta;
|
|
71
|
+
etag: string;
|
|
72
|
+
}>;
|
|
73
|
+
version: (roles: readonly string[]) => Promise<string>;
|
|
74
|
+
};
|
|
75
|
+
export type Meta = ReturnType<typeof createMeta>;
|
|
76
|
+
//#endregion
|
package/dist/records.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { storedValue } from "./encryption/stored-value.js";
|
|
2
1
|
import { columnId, filterCondition, keyConditions, returningList, scopeConditions, tableId, whereClause } from "./sql-parts.js";
|
|
3
2
|
import { etagOf } from "./etag.js";
|
|
3
|
+
import { storedValue } from "./encryption/stored-value.js";
|
|
4
4
|
import { exposeRow, pickFields } from "./rows.js";
|
|
5
5
|
import { sql } from "kysely";
|
|
6
6
|
//#region src/records.ts
|
package/dist/registry.d.ts
CHANGED
|
@@ -1,2 +1,22 @@
|
|
|
1
|
-
import "./resource-source.js";
|
|
2
|
-
import { KeyType, ResourceModel } from "@protobase/schema";
|
|
1
|
+
import { ResourceSource } from "./resource-source.js";
|
|
2
|
+
import { FieldModel, KeyType, ResourceModel } from "@protobase/schema";
|
|
3
|
+
//#region src/registry.d.ts
|
|
4
|
+
export type EtagSource = {
|
|
5
|
+
field: FieldModel;
|
|
6
|
+
kind: 'version' | 'timestamp';
|
|
7
|
+
};
|
|
8
|
+
export type Entry = {
|
|
9
|
+
name: string;
|
|
10
|
+
source: ResourceSource;
|
|
11
|
+
model: ResourceModel;
|
|
12
|
+
keyTypes: KeyType[];
|
|
13
|
+
/** The type of the user ids its `.owner()` field holds: the field's own, or the key type of the record it points at. */
|
|
14
|
+
ownerType?: KeyType;
|
|
15
|
+
/** A database-managed `version` or `updated_at` column, when the table has one; otherwise ETags hash the row. */
|
|
16
|
+
etag?: EtagSource;
|
|
17
|
+
};
|
|
18
|
+
export type Registry = {
|
|
19
|
+
entries: Entry[];
|
|
20
|
+
find: (name: string) => Entry | undefined;
|
|
21
|
+
};
|
|
22
|
+
//#endregion
|
package/dist/request-access.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { runWrite } from "../write-pipeline.js";
|
|
2
1
|
import { listRecords } from "../list-service.js";
|
|
2
|
+
import { runWrite } from "../write-pipeline.js";
|
|
3
3
|
import { facets, numericHistogram, timeSeries } from "../aggregate-service.js";
|
|
4
4
|
import { aggregate } from "../aggregate-groups.js";
|
|
5
5
|
import { mapRecords } from "../map-service.js";
|
package/dist/routes/record.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { notFound } from "../problem.js";
|
|
2
2
|
import { keyFromPath } from "../key.js";
|
|
3
|
+
import { deletedRows, selectedFields } from "../params.js";
|
|
3
4
|
import { pickFields } from "../rows.js";
|
|
4
5
|
import { readRecord } from "../read-service.js";
|
|
5
6
|
import { runWrite } from "../write-pipeline.js";
|
|
6
7
|
import { auditOrigin } from "../audit/origin.js";
|
|
7
|
-
import { deletedRows, selectedFields } from "../params.js";
|
|
8
8
|
import { returnPreference } from "./preferences.js";
|
|
9
9
|
import { resolveTarget } from "./target.js";
|
|
10
10
|
import { revealField } from "../reveal-service.js";
|
package/dist/row-permissions.js
CHANGED
|
@@ -3,8 +3,8 @@ import { recordDecision } from "./request-access.js";
|
|
|
3
3
|
import { aliasedColumn, columnId, scopeConditions, tableId, whereClause } from "./sql-parts.js";
|
|
4
4
|
import { fetchRecord } from "./records.js";
|
|
5
5
|
import { parentGuard } from "./parent-access.js";
|
|
6
|
-
import { sql } from "kysely";
|
|
7
6
|
import { compileFilter } from "@protobase/query";
|
|
7
|
+
import { sql } from "kysely";
|
|
8
8
|
//#region src/row-permissions.ts
|
|
9
9
|
const keyOf = (access, row) => JSON.stringify(access.full.primaryKey.map((name) => String(row[name])));
|
|
10
10
|
const matching = async (trx, access, rows, filter) => {
|
package/dist/scan-guard.d.ts
CHANGED
|
@@ -1,4 +1,22 @@
|
|
|
1
|
-
import "./types.js";
|
|
2
|
-
import "./registry.js";
|
|
1
|
+
import { ScanGuardMode } from "./types.js";
|
|
2
|
+
import { Entry } from "./registry.js";
|
|
3
3
|
import { ResourceModel } from "@protobase/schema";
|
|
4
|
-
import { Db } from "@protobase/query";
|
|
4
|
+
import { CoverageQuery, Db, Scope, TableIndexes } from "@protobase/query";
|
|
5
|
+
//#region src/scan-guard.d.ts
|
|
6
|
+
/** The model the query runs against (as the caller sees it) and its scope (tenant, row filter, full model). */
|
|
7
|
+
export type GuardContext = {
|
|
8
|
+
model: ResourceModel;
|
|
9
|
+
scope: Scope;
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Checks that an index serves every column a read filters, sorts or groups by, from the table's indexes in the catalog
|
|
13
|
+
* (read once per table and kept for the process lifetime), so a read is never refused or allowed by how many rows match.
|
|
14
|
+
*/
|
|
15
|
+
export declare const createScanGuard: (db: Db, options?: {
|
|
16
|
+
mode?: ScanGuardMode;
|
|
17
|
+
}) => ((entry: Entry, query: CoverageQuery, context: GuardContext) => Promise<string | undefined>) & {
|
|
18
|
+
mode: ScanGuardMode;
|
|
19
|
+
indexesOf: (entry: Entry) => Promise<TableIndexes>;
|
|
20
|
+
};
|
|
21
|
+
export type ScanGuard = ReturnType<typeof createScanGuard>;
|
|
22
|
+
//#endregion
|
package/dist/sql-parts.js
CHANGED
|
@@ -40,20 +40,30 @@ const subscriptionEvent = (change, event, { fields, thin = false } = {}) => ({
|
|
|
40
40
|
transaction: change.transaction
|
|
41
41
|
}
|
|
42
42
|
});
|
|
43
|
-
const
|
|
44
|
-
const fields = Object.values(model.fields).filter((field) => field.
|
|
45
|
-
if (fields.length === 0) return record;
|
|
43
|
+
const withUnknownFields = (model, record) => {
|
|
44
|
+
const fields = Object.values(model.fields).filter((field) => field.sensitive || !Object.hasOwn(record, field.name));
|
|
46
45
|
const unknown = { ...record };
|
|
46
|
+
const read = [];
|
|
47
|
+
const error = (name) => {
|
|
48
|
+
const field = model.fields[name];
|
|
49
|
+
return /* @__PURE__ */ new Error(`a subscription's access check cannot tell whether the ${field.sensitive ? "sensitive field" : "field"} "${name}" is set${field.sensitive ? "" : ", which the captured change does not hold"}`);
|
|
50
|
+
};
|
|
47
51
|
for (const field of fields) Object.defineProperty(unknown, field.name, {
|
|
48
52
|
enumerable: true,
|
|
49
53
|
get: () => {
|
|
50
|
-
|
|
54
|
+
read.push(field.name);
|
|
55
|
+
throw error(field.name);
|
|
51
56
|
}
|
|
52
57
|
});
|
|
53
|
-
|
|
58
|
+
const check = () => {
|
|
59
|
+
if (read.length > 0) throw error(read[0]);
|
|
60
|
+
};
|
|
61
|
+
return {
|
|
62
|
+
record: unknown,
|
|
63
|
+
check
|
|
64
|
+
};
|
|
54
65
|
};
|
|
55
|
-
const
|
|
56
|
-
const record = withUnknownEncrypted(access.full, captured);
|
|
66
|
+
const decide = async (access, record) => {
|
|
57
67
|
const { resolved, session, entry } = access;
|
|
58
68
|
if (!resolved.operations.read) return false;
|
|
59
69
|
if (resolved.rowFilter.read && evaluateFilter(resolved.rowFilter.read, record) !== true) return false;
|
|
@@ -64,6 +74,12 @@ const mayRead = async (access, captured) => {
|
|
|
64
74
|
};
|
|
65
75
|
return checkRecord(entry.source, context, "read", record, void 0, access.deps.accessOptions);
|
|
66
76
|
};
|
|
77
|
+
const mayRead = async (access, captured) => {
|
|
78
|
+
const { record, check } = withUnknownFields(access.full, captured);
|
|
79
|
+
const allowed = await decide(access, record);
|
|
80
|
+
check();
|
|
81
|
+
return allowed;
|
|
82
|
+
};
|
|
67
83
|
/**
|
|
68
84
|
* Builds each subscriber's event, under the access rules of the session it delivers as, resolved once per subscription
|
|
69
85
|
* and resource: `undefined` when it may not read the record, after the change or before a delete.
|
|
@@ -47,7 +47,9 @@ const deliveryGroup = ({ subscription, change }) => `${subscription.name}:${chan
|
|
|
47
47
|
* before that commits leaves the rows, and the job's retry delivers them again, under the same `webhook-id`.
|
|
48
48
|
* - `webhooks.dispatch-change`, one change whose app code threw in dispatch: an event's `match` or filter, a
|
|
49
49
|
* subscription's `when`, or an access rule of its roles. Kept as failed, so dispatch moves on to later changes; a
|
|
50
|
-
* retry after the fix queues the change's deliveries.
|
|
50
|
+
* retry after the fix queues the change's deliveries. An access rule's failure keeps the change for that subscription
|
|
51
|
+
* only: the others still receive it, and the retry delivers to that subscription alone. Any other error fails the
|
|
52
|
+
* whole dispatch, which is retried.
|
|
51
53
|
* - `webhooks.deliver`, one try of an unordered delivery; a failure queues the next try on the Standard Webhooks
|
|
52
54
|
* schedule, or Retry-After, and the last one is kept as failed.
|
|
53
55
|
* - `webhooks.deliver-ordered`, the ordered deliveries of one dispatch for one subscription and record, retried with
|
|
@@ -148,17 +150,32 @@ const createWebhooks = ({ deps, subscriptions, functions, schema, log, table })
|
|
|
148
150
|
const changeDeliveries = async (db) => {
|
|
149
151
|
const route = subscriptionRouter(await current(db), models);
|
|
150
152
|
const eventOf = subscriberEvents(deps);
|
|
151
|
-
return async (change) => {
|
|
153
|
+
return async (change, only) => {
|
|
154
|
+
const routes = route(change, matchers.get(change.resource)(change)).filter(({ subscription }) => only === void 0 || subscription.name === only);
|
|
152
155
|
const deliveries = [];
|
|
153
|
-
|
|
154
|
-
|
|
156
|
+
const failures = [];
|
|
157
|
+
const failed = /* @__PURE__ */ new Set();
|
|
158
|
+
for (const { subscription, event } of routes) {
|
|
159
|
+
if (failed.has(subscription.name)) continue;
|
|
160
|
+
const shaped = await eventOf(subscription, change, event).catch((error) => {
|
|
161
|
+
if (!(error instanceof EventCodeError)) throw error;
|
|
162
|
+
failed.add(subscription.name);
|
|
163
|
+
failures.push({
|
|
164
|
+
change,
|
|
165
|
+
subscription: subscription.name,
|
|
166
|
+
error
|
|
167
|
+
});
|
|
168
|
+
});
|
|
155
169
|
if (shaped) deliveries.push({
|
|
156
170
|
subscription,
|
|
157
171
|
event: shaped,
|
|
158
172
|
change
|
|
159
173
|
});
|
|
160
174
|
}
|
|
161
|
-
return
|
|
175
|
+
return {
|
|
176
|
+
deliveries: deliveries.filter(({ subscription }) => !failed.has(subscription.name)),
|
|
177
|
+
failures
|
|
178
|
+
};
|
|
162
179
|
};
|
|
163
180
|
};
|
|
164
181
|
const queueOrdered = async (db, deliveries) => {
|
|
@@ -178,8 +195,9 @@ const createWebhooks = ({ deps, subscriptions, functions, schema, log, table })
|
|
|
178
195
|
name: "webhooks.dispatch-change",
|
|
179
196
|
maxAttempts: 1,
|
|
180
197
|
timeout: "5 minutes",
|
|
181
|
-
run: async ({ change }, { db }) => {
|
|
182
|
-
const deliveries = await (await changeDeliveries(db))(change);
|
|
198
|
+
run: async ({ change, subscription: only }, { db }) => {
|
|
199
|
+
const { deliveries, failures } = await (await changeDeliveries(db))(change, only);
|
|
200
|
+
if (only !== void 0 && failures.length > 0) throw failures[0].error;
|
|
183
201
|
await db.transaction().execute(async (trx) => {
|
|
184
202
|
for (const { subscription, event } of deliveries.filter(({ subscription }) => !subscription.options.ordered)) {
|
|
185
203
|
const payload = {
|
|
@@ -190,6 +208,10 @@ const createWebhooks = ({ deps, subscriptions, functions, schema, log, table })
|
|
|
190
208
|
await deliver.enqueue(trx, payload, { key: deliveryKey(payload) });
|
|
191
209
|
}
|
|
192
210
|
await queueOrdered(trx, deliveries);
|
|
211
|
+
for (const { subscription } of failures) await dispatchChange.enqueue(trx, {
|
|
212
|
+
change,
|
|
213
|
+
subscription
|
|
214
|
+
});
|
|
193
215
|
});
|
|
194
216
|
}
|
|
195
217
|
});
|
|
@@ -204,7 +226,9 @@ const createWebhooks = ({ deps, subscriptions, functions, schema, log, table })
|
|
|
204
226
|
const deliveries = [];
|
|
205
227
|
const failures = [];
|
|
206
228
|
for (const change of changes) try {
|
|
207
|
-
|
|
229
|
+
const routed = await deliveriesOf(change);
|
|
230
|
+
deliveries.push(...routed.deliveries);
|
|
231
|
+
failures.push(...routed.failures);
|
|
208
232
|
} catch (error) {
|
|
209
233
|
if (!(error instanceof EventCodeError)) throw error;
|
|
210
234
|
failures.push({
|
|
@@ -232,7 +256,10 @@ const createWebhooks = ({ deps, subscriptions, functions, schema, log, table })
|
|
|
232
256
|
});
|
|
233
257
|
}
|
|
234
258
|
await queueOrdered(trx, deliveries);
|
|
235
|
-
for (const { change } of failures) await dispatchChange.enqueue(trx, {
|
|
259
|
+
for (const { change, subscription } of failures) await dispatchChange.enqueue(trx, {
|
|
260
|
+
change,
|
|
261
|
+
...subscription !== void 0 && { subscription }
|
|
262
|
+
});
|
|
236
263
|
await writeHistory(trx, schema, changes.filter((change) => deps.registry.find(change.resource)?.model.history));
|
|
237
264
|
await writeNotices(trx, schema, changes.filter((change) => deps.registry.find(change.resource)?.model.live));
|
|
238
265
|
await deleteOutbox(trx, schema, rows.map((row) => row.id));
|
package/dist/write-shared.js
CHANGED
|
@@ -2,8 +2,8 @@ import { maskEncrypted } from "./encryption/mask-stored.js";
|
|
|
2
2
|
import { forbidden, notFound, preconditionFailed, preconditionRequired } from "./problem.js";
|
|
3
3
|
import { tenantScope } from "./tenant.js";
|
|
4
4
|
import { andFilters, recordDecision } from "./request-access.js";
|
|
5
|
-
import { presentFiles } from "./files/present.js";
|
|
6
5
|
import { ifMatchSatisfied } from "./etag.js";
|
|
6
|
+
import { presentFiles } from "./files/present.js";
|
|
7
7
|
import { pickFields } from "./rows.js";
|
|
8
8
|
import { fetchRecord } from "./records.js";
|
|
9
9
|
import { parentGuard } from "./parent-access.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@protobase/server",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.37.0",
|
|
4
4
|
"description": "The Protobase REST API as a Hono app, with Better Auth sign-in.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -35,9 +35,10 @@
|
|
|
35
35
|
"@better-auth/oauth-provider": "^1.7.12",
|
|
36
36
|
"@better-auth/passkey": "^1.7.12",
|
|
37
37
|
"@better-auth/sso": "^1.7.12",
|
|
38
|
-
"@
|
|
39
|
-
"@protobase/
|
|
40
|
-
"@protobase/
|
|
38
|
+
"@modelcontextprotocol/server": "^2.3.1",
|
|
39
|
+
"@protobase/layout": "0.37.0",
|
|
40
|
+
"@protobase/query": "0.37.0",
|
|
41
|
+
"@protobase/schema": "0.37.0",
|
|
41
42
|
"@react-email/render": "^2.1.0",
|
|
42
43
|
"@scalar/hono-api-reference": "^0.12.10",
|
|
43
44
|
"@sentry/core": "^11.7.0",
|