Skip to content

Commit 2892fe2

Browse files
committed
Tighten plugin FumaDB table typing
1 parent f3cb52e commit 2892fe2

10 files changed

Lines changed: 180 additions & 239 deletions

File tree

packages/core/sdk/src/fuma-runtime.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { Cause, Context, Data, Effect, Exit, Layer, Predicate } from "effect";
22
import type { AbstractQuery } from "fumadb/query";
3-
import type { AnySchema, AnyTable } from "fumadb/schema";
3+
import type { AnySchema, AnyTable, Schema as FumaSchema } from "fumadb/schema";
44

55
export class StorageError extends Data.TaggedError("StorageError")<{
66
readonly message: string;
@@ -14,6 +14,9 @@ export class UniqueViolationError extends Data.TaggedError("UniqueViolationError
1414
export type StorageFailure = StorageError | UniqueViolationError;
1515

1616
export type FumaTables = Record<string, AnyTable>;
17+
export type TablesToFumaSchema<TTables extends FumaTables | undefined> = TTables extends FumaTables
18+
? FumaSchema<"latest", TTables>
19+
: AnySchema;
1720
export type FumaDb<TSchema extends AnySchema = AnySchema> = AbstractQuery<TSchema>;
1821
export type FumaRow<TTable extends AnyTable> = Omit<
1922
{
@@ -83,9 +86,12 @@ class TransactionEffectDefect {
8386
constructor(readonly cause: unknown) {}
8487
}
8588

86-
export type IFumaClient = Readonly<{
87-
db: FumaDb;
88-
use: <A>(label: string, fn: (db: FumaDb) => Promise<A>) => Effect.Effect<A, StorageFailure>;
89+
export type IFumaClient<TSchema extends AnySchema = AnySchema> = Readonly<{
90+
db: FumaDb<TSchema>;
91+
use: <A>(
92+
label: string,
93+
fn: (db: FumaDb<TSchema>) => Promise<A>,
94+
) => Effect.Effect<A, StorageFailure>;
8995
transaction: <A, E>(effect: Effect.Effect<A, E>) => Effect.Effect<A, E | StorageFailure>;
9096
}>;
9197

packages/core/sdk/src/index.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -298,7 +298,6 @@ export {
298298
type SecretListEntry,
299299
type Elicit,
300300
definePlugin,
301-
defineSchema,
302301
tool,
303302
} from "./plugin";
304303

packages/core/sdk/src/plugin.ts

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,13 @@ import type { Context, Layer } from "effect";
33
import type { HttpClient } from "effect/unstable/http";
44
import type { HttpApiGroup } from "effect/unstable/httpapi";
55
import type { StandardJSONSchemaV1, StandardSchemaV1 } from "@standard-schema/spec";
6-
import type { FumaDb, FumaTables, IFumaClient, StorageFailure } from "./fuma-runtime";
6+
import type {
7+
FumaDb,
8+
FumaTables,
9+
IFumaClient,
10+
StorageFailure,
11+
TablesToFumaSchema,
12+
} from "./fuma-runtime";
713

814
import type { PluginBlobStore } from "./blob";
915
import type {
@@ -44,7 +50,7 @@ import type { Usage, UsagesForConnectionInput, UsagesForSecretInput } from "./us
4450
// and writes stamp an explicit `scope_id`.
4551
// ---------------------------------------------------------------------------
4652

47-
export interface StorageDeps<_TTables extends FumaTables | undefined = undefined> {
53+
export interface StorageDeps<TTables extends FumaTables | undefined = undefined> {
4854
/**
4955
* Precedence-ordered scope stack visible to this executor. Innermost
5056
* first. Reads on scoped tables walk every scope; writes require the
@@ -53,19 +59,12 @@ export interface StorageDeps<_TTables extends FumaTables | undefined = undefined
5359
*/
5460
readonly scopes: readonly Scope[];
5561
/** Plugin-facing FumaDB query handle. Plugins call FumaDB directly. */
56-
readonly db: FumaDb;
62+
readonly db: FumaDb<TablesToFumaSchema<TTables>>;
5763
/** Effect boundary around the same FumaDB handle for stores implemented in Effect. */
58-
readonly fuma: IFumaClient;
64+
readonly fuma: IFumaClient<TablesToFumaSchema<TTables>>;
5965
readonly blobs: PluginBlobStore;
6066
}
6167

62-
// ---------------------------------------------------------------------------
63-
// defineSchema — compatibility name for plugin tables. It returns FumaDB table
64-
// definitions directly; there is no Executor-specific schema DSL anymore.
65-
// ---------------------------------------------------------------------------
66-
67-
export const defineSchema = <const TTables extends FumaTables>(tables: TTables): TTables => tables;
68-
6968
// ---------------------------------------------------------------------------
7069
// Elicit — suspends the fiber, calls the invoke-time elicitation
7170
// handler, resumes with the user's response. Available on both static

packages/core/sdk/src/policies.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import {
1212
matchPattern,
1313
resolveToolPolicy,
1414
} from "./policies";
15-
import { definePlugin, defineSchema } from "./plugin";
15+
import { definePlugin } from "./plugin";
1616
import { makeTestExecutor } from "./testing";
1717

1818
// ---------------------------------------------------------------------------
@@ -268,9 +268,9 @@ const recordingHandler = (calls: { count: number }): ElicitationHandler =>
268268
const decliningHandler: ElicitationHandler = () =>
269269
Effect.succeed(ElicitationResponse.make({ action: "decline" }));
270270

271-
const policyTestSchema = defineSchema({
271+
const policyTestSchema = {
272272
ptest_marker: scopedExecutorTable("ptest_marker", {}),
273-
});
273+
};
274274

275275
const policyTestPlugin = definePlugin(() => ({
276276
id: "ptest" as const,

packages/core/sdk/src/promise.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { describe, expect, it } from "@effect/vitest";
22

33
import { createExecutor } from "./promise";
44
import { collectTables } from "./executor";
5-
import { definePlugin, defineSchema, tool } from "./plugin";
5+
import { definePlugin, tool } from "./plugin";
66
import { createSqliteTestFumaDb } from "./sqlite-test-db";
77
import { Effect, Schema } from "effect";
88

@@ -11,7 +11,7 @@ import { Effect, Schema } from "effect";
1111
// nested methods (executor.tools.*) and plugin extensions.
1212
const echoPlugin = definePlugin(() => ({
1313
id: "echo" as const,
14-
schema: defineSchema({}),
14+
schema: {},
1515
storage: () => ({}),
1616
staticSources: () => [
1717
{
@@ -84,7 +84,7 @@ describe("promise/createExecutor", () => {
8484
// wrapped invocation error.
8585
const approvedPlugin = definePlugin(() => ({
8686
id: "ap" as const,
87-
schema: defineSchema({}),
87+
schema: {},
8888
storage: () => ({}),
8989
staticSources: () => [
9090
{

packages/plugins/google-discovery/src/sdk/binding-store.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import { Effect, Option, Schema } from "effect";
1616

1717
import {
1818
dateColumn,
19-
defineSchema,
19+
type FumaTables,
2020
jsonColumn,
2121
nullableTextColumn,
2222
scopedExecutorTable,
@@ -46,7 +46,7 @@ export const GOOGLE_DISCOVERY_OAUTH_SESSION_TTL_MS = 15 * 60 * 1000;
4646
// Schema — plugin-declared tables merged with coreSchema at executor start.
4747
// ---------------------------------------------------------------------------
4848

49-
export const googleDiscoverySchema = defineSchema({
49+
export const googleDiscoverySchema = {
5050
google_discovery_source: scopedExecutorTable("google_discovery_source", {
5151
name: textColumn("name"),
5252
// Plugin-private structural config minus auth/credentials —
@@ -89,7 +89,7 @@ export const googleDiscoverySchema = defineSchema({
8989
binding: jsonColumn("binding"),
9090
created_at: dateColumn("created_at"),
9191
}),
92-
});
92+
} satisfies FumaTables;
9393

9494
export type GoogleDiscoverySchema = typeof googleDiscoverySchema;
9595

packages/plugins/graphql/src/sdk/store.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { Effect, Schema } from "effect";
22

33
import {
44
ConfiguredCredentialBinding,
5-
defineSchema,
5+
type FumaTables,
66
jsonColumn,
77
nullableTextColumn,
88
scopedExecutorTable,
@@ -35,7 +35,7 @@ import {
3535
// internal opaque data).
3636
// ---------------------------------------------------------------------------
3737

38-
export const graphqlSchema = defineSchema({
38+
export const graphqlSchema = {
3939
graphql_source: scopedExecutorTable("graphql_source", {
4040
name: textColumn("name"),
4141
endpoint: textColumn("endpoint"),
@@ -62,7 +62,7 @@ export const graphqlSchema = defineSchema({
6262
source_id: textColumn("source_id"),
6363
binding: jsonColumn("binding"),
6464
}),
65-
});
65+
} satisfies FumaTables;
6666

6767
export type GraphqlSchema = typeof graphqlSchema;
6868

packages/plugins/mcp/src/sdk/binding-store.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import { Effect, Option, Schema } from "effect";
2121
import {
2222
ConfiguredCredentialBinding,
2323
dateColumn,
24-
defineSchema,
24+
type FumaTables,
2525
jsonColumn,
2626
nullableTextColumn,
2727
scopedExecutorTable,
@@ -44,7 +44,7 @@ import {
4444
// Schema
4545
// ---------------------------------------------------------------------------
4646

47-
export const mcpSchema = defineSchema({
47+
export const mcpSchema = {
4848
mcp_source: scopedExecutorTable("mcp_source", {
4949
name: textColumn("name"),
5050
// Plugin-private structural data minus the ref-bearing fields
@@ -84,7 +84,7 @@ export const mcpSchema = defineSchema({
8484
binding: jsonColumn("binding"),
8585
created_at: dateColumn("created_at"),
8686
}),
87-
});
87+
} satisfies FumaTables;
8888

8989
export type McpSchema = typeof mcpSchema;
9090

0 commit comments

Comments
 (0)