-
Notifications
You must be signed in to change notification settings - Fork 2
refactor(better-auth): replace raw OData fetch with fmodata Database #112
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
refactor(better-auth): replace raw OData fetch with fmodata Database #112
Conversation
BREAKING: Config now takes `database` (fmodata Database instance)
instead of `odata: { serverUrl, auth, database }`.
- Delete custom OData fetch wrapper (src/odata/index.ts)
- All CRUD ops delegate to db._makeRequest()
- Migrate uses db.schema.createTable/addFields
- Add _getDatabaseName getter and _makeRequest to fmodata Database
- Drop neverthrow, odata-query, zod deps
- Update tests to mock Database instead of global fetch
- Update docs with FMServerConnection pattern
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
This stack of pull requests is managed by Graphite. Learn more about stacking. |
✅ Actions performedReview triggered.
|
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
📝 WalkthroughWalkthroughThis pull request refactors the better-auth FileMaker adapter from a fetch-based OData configuration approach to using the fmodata Database instance directly. The adapter's public API now requires a Database object, the odata module is removed entirely, and all requests are replaced with Database._makeRequest calls. Migrations, CLI, tests, and dependencies are updated accordingly. Changes
Sequence DiagramsequenceDiagram
participant App as Application Code
participant FSC as FMServerConnection
participant DB as Database Instance
participant HTTP as HTTP Layer
App->>FSC: new FMServerConnection({ serverUrl, auth, ... })
FSC->>DB: database(databaseName)
DB-->>App: Database instance
App->>App: FileMakerAdapter({ database: db })
App->>DB: query via _makeRequest()
DB->>HTTP: fetch with path & options
HTTP-->>DB: Response
DB-->>App: Result<T> { data or error }
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
🤖 Fix all issues with AI agents
In `@packages/better-auth/src/adapter.ts`:
- Around line 131-158: The buildQueryString function must percent-encode OData
parameter values: when adding $filter and $orderby use
encodeURIComponent(params.filter) and encodeURIComponent(params.orderBy)
respectively, and for $select map over params.select and encode each identifier
with encodeURIComponent before joining with commas (e.g.,
params.select.map(encodeURIComponent).join(",")) so special characters (spaces,
&, =, { } [ ] ", etc.) are safely encoded; update the buildQueryString function
to apply these encodings for params.filter, params.orderBy, and params.select.
In `@packages/better-auth/src/cli/index.ts`:
- Around line 55-91: Replace the unsafe "as any" casts when accessing internal
fmodata properties on configDb by using "as unknown as { ... }" shaped types:
cast configDb to a shape that exposes _getDatabaseName to read dbName
(referenced as dbName) and cast configDb to a shape with optional context and
_getBaseUrl() to read baseUrl; keep the rest of the logic (building serverUrl,
creating new FMServerConnection and calling connection.database(dbName))
unchanged so you still create a connection and assign db when options.username
and options.password are provided.
In `@packages/better-auth/src/migrate.ts`:
- Around line 18-27: The catch block in getMetadata discards the thrown error;
change the catch to capture the error (e.g., catch (err)) and include the error
details in the log so failures from db.getMetadata are visible—update the
console.error call in getMetadata to log a descriptive message plus the error
(and optionally the db or format context) to aid debugging.
🧹 Nitpick comments (9)
packages/better-auth/tests/utils/mock-fetch.ts (1)
26-41: Consider adding path/options matching for more specific test assertions.The current implementation ignores
pathandoptionsparameters, which works for simple mocks but limits the ability to verify that correct endpoints are being called.💡 Optional: Add path tracking for test assertions
export function createMockDatabase(response: MockResponse): MockDatabase { + const calls: Array<{ path: string; options?: RequestInit }> = []; return { _makeRequest: <T>(path: string, options?: RequestInit): Promise<MakeRequestResult<T>> => { + calls.push({ path, options }); if (response.status >= 200 && response.status < 300) { return Promise.resolve({ data: response.response as T, error: undefined }); } return Promise.resolve({ data: undefined, error: new Error(`HTTP ${response.status}`) }); }, schema: { createTable: async () => ({}), addFields: async () => ({}), }, getMetadata: async () => ({}), _getDatabaseName: "test.fmp12", + // Expose for assertions + _calls: calls, }; }packages/better-auth/src/adapter.ts (1)
267-289: Sequential deletes may be slow for large datasets.The
deleteManyoperation issues individual DELETE requests in sequence. For large record sets, this could be slow compared to batch operations.Consider documenting this behavior or evaluating if the fmodata Database supports batch delete operations that could improve performance. This is acceptable for typical Better Auth use cases with small record counts.
packages/better-auth/src/migrate.ts (2)
67-84: Type narrowing for field type could be more explicit.The
typevariable is assigned as a string but represents a union of"string" | "numeric" | "timestamp". Consider using a typed variable or themapFieldTypehelper for consistency.💡 Optional: Use explicit type for consistency
.map(([fieldKey, fieldValue]) => { - let type = "string"; + let type: "string" | "numeric" | "timestamp" = "string"; if (fieldValue.$Type === "Edm.String") { type = "string"; } else if (fieldValue.$Type === "Edm.DateTimeOffset") {
169-182: Preserve error stack trace when re-throwing.Wrapping the error with string interpolation loses the original stack trace, making debugging harder.
🔧 Proposed fix to preserve error context
try { await db.schema.createTable(step.tableName, fmodataFields); } catch (error) { console.error(`Failed to create table ${step.tableName}:`, error); - throw new Error(`Migration failed: ${error}`); + throw error instanceof Error + ? new Error(`Migration failed for table ${step.tableName}: ${error.message}`, { cause: error }) + : new Error(`Migration failed for table ${step.tableName}: ${error}`); } } else if (step.operation === "update") { console.log("Adding fields to table:", step.tableName); try { await db.schema.addFields(step.tableName, fmodataFields); } catch (error) { console.error(`Failed to update table ${step.tableName}:`, error); - throw new Error(`Migration failed: ${error}`); + throw error instanceof Error + ? new Error(`Migration failed for table ${step.tableName}: ${error.message}`, { cause: error }) + : new Error(`Migration failed for table ${step.tableName}: ${error}`); }packages/better-auth/tests/e2e/migrate.test.ts (1)
44-48: Consider logging unexpected errors during cleanup.The empty catch block is fine for the expected "table doesn't exist" scenario, but it may silently swallow unexpected errors (e.g., permission issues, network failures). Consider differentiating error types or adding a debug log.
🔧 Suggested improvement
// Delete table if it exists (cleanup) try { await db.schema.deleteTable(tableName); - } catch { - // Table might not exist + } catch (error) { + // Table might not exist - ignore 404-like errors + // Log other errors for debugging + if (process.env.DEBUG) { + console.log("Cleanup error (may be expected):", error); + } }packages/better-auth/tests/e2e/adapter.test.ts (4)
6-17: Remove duplicate environment variable checks.The environment variable validation at lines 6-17 is duplicated inside the
describeblock at lines 52-63. Since the module-level checks already throw before the describe block runs, the inner checks are unreachable.♻️ Suggested fix
Remove lines 52-63 as they are redundant:
await runAdapterTest({ // biome-ignore lint/suspicious/useAwait: must be an async function getAdapter: async (betterAuthOptions = {}) => { return adapter(betterAuthOptions); }, }); - - if (!process.env.FM_SERVER) { - throw new Error("FM_SERVER is not set"); - } - if (!process.env.FM_DATABASE) { - throw new Error("FM_DATABASE is not set"); - } - if (!process.env.FM_USERNAME) { - throw new Error("FM_USERNAME is not set"); - } - if (!process.env.FM_PASSWORD) { - throw new Error("FM_PASSWORD is not set"); - }Also applies to: 52-63
93-94: Consider removing debug console.log statement.This appears to be a debug artifact. Consider removing it or using a conditional debug flag to reduce test output noise.
123-123: Consider removing debug console.log statement.Same as above - appears to be a leftover debug statement.
97-139: Test is outside the main describe block.The
"should properly filter by dates"test is defined at the module level rather than inside the"My Adapter Tests"describe block. While this works in vitest, consider moving it inside the describe block for better organization and consistent test output grouping.
| /** | ||
| * Build an OData query string from parameters. | ||
| */ | ||
| function buildQueryString(params: { | ||
| top?: number; | ||
| skip?: number; | ||
| filter?: string; | ||
| orderBy?: string; | ||
| select?: string[]; | ||
| }): string { | ||
| const parts: string[] = []; | ||
| if (params.top !== undefined) { | ||
| parts.push(`$top=${params.top}`); | ||
| } | ||
| if (params.skip !== undefined) { | ||
| parts.push(`$skip=${params.skip}`); | ||
| } | ||
| if (params.filter) { | ||
| parts.push(`$filter=${params.filter}`); | ||
| } | ||
| if (params.orderBy) { | ||
| parts.push(`$orderby=${params.orderBy}`); | ||
| } | ||
| if (params.select?.length) { | ||
| parts.push(`$select=${params.select.join(",")}`); | ||
| } | ||
| return parts.length > 0 ? `?${parts.join("&")}` : ""; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🌐 Web query:
OData $filter parameter URL encoding requirements specification
💡 Result:
What spec governs $filter URL encoding?
OData URLs (including the $filter query option value) follow RFC 3986 URI rules plus OData’s URL Conventions + OData-ABNF rules. OData explicitly assumes inputs are percent-encoding normalized per RFC 3986 §6.2.2.2 before applying the grammar. [1]
Practical encoding requirements for $filter
When placing a $filter expression into a URL query string, you must percent-encode characters that are not allowed in a URI query component or that would change query parsing:
- Spaces are not allowed unencoded in a URI query; encode as
%20(not+). (+is only a space substitute inapplication/x-www-form-urlencoded, not in generic URI syntax.) [3] %must be encoded as%25if it is data (because%introduces percent-encoding). [2]- Query-string delimiters
&and=must be encoded as%26and%3Dif they appear inside the$filterexpression, otherwise they will be interpreted as separating query options / key-value pairs. (This follows RFC 3986 reserved-character rules.) [2] - OData-specific note: for JSON-style literals used in URLs (e.g., via parameter aliases), characters like
{}[]"MUST be percent-encoded (even if some clients/browsers tolerate them). [4] - OData examples show that some characters (like quotes/parentheses) may appear percent-encoded and still represent the same OData URL (e.g., encoding
'as%27,(as%28). [1]
Example
Readable:
?$filter=Discontinued eq false
Correctly percent-encoded (at least the spaces):
?$filter=Discontinued%20eq%20false
Sources: OData URL Conventions (v4.02) on percent-encoding normalization + examples [1], RFC 3986 on percent-encoding and reserved characters [2], RFC 3986 query validity (space not allowed) [3], OData URL Conventions (v4.02) rule that { } [ ] " must be percent-encoded in URLs [4].
Citations:
[1] (docs.oasis-open.org)
[2] (datatracker.ietf.org)
[3] (stackoverflow.com)
[4] (docs.oasis-open.org)
Citations:
- 1: https://docs.oasis-open.org/odata/odata/v4.02/csd01/part2-url-conventions/odata-v4.02-csd01-part2-url-conventions.html?utm_source=openai
- 2: https://datatracker.ietf.org/doc/rfc3986/?utm_source=openai
- 3: https://stackoverflow.com/questions/2366260/whats-valid-and-whats-not-in-a-uri-query?utm_source=openai
- 4: https://docs.oasis-open.org/odata/odata/v4.02/csd01/part2-url-conventions/odata-v4.02-csd01-part2-url-conventions.html?utm_source=openai
🏁 Script executed:
# Search for how buildQueryString is used and where parameters come from
rg "buildQueryString" packages/better-auth/src/adapter.ts -A 3 -B 3Repository: proofgeist/proofkit
Length of output: 1891
🏁 Script executed:
# Look for existing encoding patterns in the codebase
rg "encodeURIComponent|decodeURIComponent" packages/ -t ts -t tsxRepository: proofgeist/proofkit
Length of output: 90
🏁 Script executed:
# Check the broader context of how filter/select parameters are created
rg "filter.*:" packages/better-auth/src/adapter.ts -A 2 -B 2Repository: proofgeist/proofkit
Length of output: 1232
URL-encode OData query parameters to handle special characters.
The filter, orderBy, and select values must be percent-encoded per the OData specification (RFC 3986 + OData URL Conventions). Spaces, reserved characters (&, =), and special characters ({, }, [, ], ") in these parameter values will cause parsing errors or security issues if not encoded.
🛡️ Proposed fix to encode query parameters
function buildQueryString(params: {
top?: number;
skip?: number;
filter?: string;
orderBy?: string;
select?: string[];
}): string {
const parts: string[] = [];
if (params.top !== undefined) {
parts.push(`$top=${params.top}`);
}
if (params.skip !== undefined) {
parts.push(`$skip=${params.skip}`);
}
if (params.filter) {
- parts.push(`$filter=${params.filter}`);
+ parts.push(`$filter=${encodeURIComponent(params.filter)}`);
}
if (params.orderBy) {
- parts.push(`$orderby=${params.orderBy}`);
+ parts.push(`$orderby=${encodeURIComponent(params.orderBy)}`);
}
if (params.select?.length) {
- parts.push(`$select=${params.select.join(",")}`);
+ parts.push(`$select=${encodeURIComponent(params.select.join(","))}`);
}
return parts.length > 0 ? `?${parts.join("&")}` : "";
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| /** | |
| * Build an OData query string from parameters. | |
| */ | |
| function buildQueryString(params: { | |
| top?: number; | |
| skip?: number; | |
| filter?: string; | |
| orderBy?: string; | |
| select?: string[]; | |
| }): string { | |
| const parts: string[] = []; | |
| if (params.top !== undefined) { | |
| parts.push(`$top=${params.top}`); | |
| } | |
| if (params.skip !== undefined) { | |
| parts.push(`$skip=${params.skip}`); | |
| } | |
| if (params.filter) { | |
| parts.push(`$filter=${params.filter}`); | |
| } | |
| if (params.orderBy) { | |
| parts.push(`$orderby=${params.orderBy}`); | |
| } | |
| if (params.select?.length) { | |
| parts.push(`$select=${params.select.join(",")}`); | |
| } | |
| return parts.length > 0 ? `?${parts.join("&")}` : ""; | |
| } | |
| /** | |
| * Build an OData query string from parameters. | |
| */ | |
| function buildQueryString(params: { | |
| top?: number; | |
| skip?: number; | |
| filter?: string; | |
| orderBy?: string; | |
| select?: string[]; | |
| }): string { | |
| const parts: string[] = []; | |
| if (params.top !== undefined) { | |
| parts.push(`$top=${params.top}`); | |
| } | |
| if (params.skip !== undefined) { | |
| parts.push(`$skip=${params.skip}`); | |
| } | |
| if (params.filter) { | |
| parts.push(`$filter=${encodeURIComponent(params.filter)}`); | |
| } | |
| if (params.orderBy) { | |
| parts.push(`$orderby=${encodeURIComponent(params.orderBy)}`); | |
| } | |
| if (params.select?.length) { | |
| parts.push(`$select=${encodeURIComponent(params.select.join(","))}`); | |
| } | |
| return parts.length > 0 ? `?${parts.join("&")}` : ""; | |
| } |
🤖 Prompt for AI Agents
In `@packages/better-auth/src/adapter.ts` around lines 131 - 158, The
buildQueryString function must percent-encode OData parameter values: when
adding $filter and $orderby use encodeURIComponent(params.filter) and
encodeURIComponent(params.orderBy) respectively, and for $select map over
params.select and encode each identifier with encodeURIComponent before joining
with commas (e.g., params.select.map(encodeURIComponent).join(",")) so special
characters (spaces, &, =, { } [ ] ", etc.) are safely encoded; update the
buildQueryString function to apply these encodings for params.filter,
params.orderBy, and params.select.
| // Extract Database from the adapter | ||
| const configDb = (adapter as unknown as { database: Database }).database; | ||
| let db: Database = configDb; | ||
|
|
||
| // If CLI credential overrides are provided, construct a new connection | ||
| if (options.username && options.password) { | ||
| // biome-ignore lint/suspicious/noExplicitAny: accessing internal getter | ||
| const dbName: string = (configDb as any)._getDatabaseName; | ||
| if (!dbName) { | ||
| logger.error("Could not determine database filename from adapter config."); | ||
| process.exit(1); | ||
| } | ||
|
|
||
| // Build the server URL from the existing db's context | ||
| // biome-ignore lint/suspicious/noExplicitAny: accessing internal method | ||
| const baseUrl: string | undefined = (configDb as any).context?._getBaseUrl?.(); | ||
|
|
||
| if (!baseUrl) { | ||
| logger.error( | ||
| "Could not determine server URL from adapter config. Ensure your auth.ts uses FMServerConnection.", | ||
| ); | ||
| process.exit(1); | ||
| } | ||
|
|
||
| // Extract server origin from base URL (e.g. "https://myserver.com/fmi/odata/v4" -> "https://myserver.com") | ||
| const serverUrl = new URL(baseUrl).origin; | ||
|
|
||
| const connection = new FMServerConnection({ | ||
| serverUrl, | ||
| auth: { | ||
| username: options.username, | ||
| password: options.password, | ||
| }, | ||
| }); | ||
|
|
||
| db = connection.database(dbName); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
# Check the file in question for context
head -100 packages/better-auth/src/cli/index.ts | tail -50Repository: proofgeist/proofkit
Length of output: 1796
🏁 Script executed:
# Search for references to _getDatabaseName and _getBaseUrl in the codebase
rg '_getDatabaseName|_getBaseUrl' -A 2 -B 2Repository: proofgeist/proofkit
Length of output: 4001
🏁 Script executed:
# Check the fmodata package to see if these are documented as internal or stable
fd 'package.json' | xargs grep -l '@proofkit/fmodata' 2>/dev/nullRepository: proofgeist/proofkit
Length of output: 156
🏁 Script executed:
# Look at the imports and type definitions in the file
head -30 packages/better-auth/src/cli/index.tsRepository: proofgeist/proofkit
Length of output: 1248
Replace as any with typed unknown to align with TypeScript guidelines.
Lines 62 and 70 access internal APIs from fmodata using as any, which violates the guideline to prefer unknown over any. While these APIs are intentionally exposed as internal (marked with @internal in fmodata), the type assertion should follow the pattern already used in line 56: as unknown as TypeShape.
Consider:
const dbName: string = (configDb as unknown as { _getDatabaseName: string })._getDatabaseName;
const baseUrl: string | undefined = (configDb as unknown as { context?: { _getBaseUrl?(): string } }).context?._getBaseUrl?.();This maintains access to the documented internal APIs while adhering to the TypeScript typing guidelines.
🤖 Prompt for AI Agents
In `@packages/better-auth/src/cli/index.ts` around lines 55 - 91, Replace the
unsafe "as any" casts when accessing internal fmodata properties on configDb by
using "as unknown as { ... }" shaped types: cast configDb to a shape that
exposes _getDatabaseName to read dbName (referenced as dbName) and cast configDb
to a shape with optional context and _getBaseUrl() to read baseUrl; keep the
rest of the logic (building serverUrl, creating new FMServerConnection and
calling connection.database(dbName)) unchanged so you still create a connection
and assign db when options.username and options.password are provided.
| export async function getMetadata(db: Database): Promise<Metadata | null> { | ||
| console.log("getting metadata..."); | ||
| const result = await fetch("/$metadata", { | ||
| method: "GET", | ||
| headers: { accept: "application/json" }, | ||
| output: z | ||
| .looseObject({ | ||
| $Version: z.string(), | ||
| "@ServerVersion": z.string(), | ||
| }) | ||
| .or(z.null()) | ||
| .catch(null), | ||
| }); | ||
|
|
||
| if (result.error) { | ||
| console.error("Failed to get metadata:", result.error); | ||
| try { | ||
| const metadata = await db.getMetadata({ format: "json" }); | ||
| return metadata; | ||
| } catch { | ||
| console.error("Failed to get metadata"); | ||
| return null; | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Log the actual error for better debugging.
The catch block discards the error details, which makes debugging connection or metadata issues difficult.
🔧 Proposed fix to include error details
export async function getMetadata(db: Database): Promise<Metadata | null> {
console.log("getting metadata...");
try {
const metadata = await db.getMetadata({ format: "json" });
return metadata;
- } catch {
- console.error("Failed to get metadata");
+ } catch (error) {
+ console.error("Failed to get metadata:", error);
return null;
}
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| export async function getMetadata(db: Database): Promise<Metadata | null> { | |
| console.log("getting metadata..."); | |
| const result = await fetch("/$metadata", { | |
| method: "GET", | |
| headers: { accept: "application/json" }, | |
| output: z | |
| .looseObject({ | |
| $Version: z.string(), | |
| "@ServerVersion": z.string(), | |
| }) | |
| .or(z.null()) | |
| .catch(null), | |
| }); | |
| if (result.error) { | |
| console.error("Failed to get metadata:", result.error); | |
| try { | |
| const metadata = await db.getMetadata({ format: "json" }); | |
| return metadata; | |
| } catch { | |
| console.error("Failed to get metadata"); | |
| return null; | |
| } | |
| } | |
| export async function getMetadata(db: Database): Promise<Metadata | null> { | |
| console.log("getting metadata..."); | |
| try { | |
| const metadata = await db.getMetadata({ format: "json" }); | |
| return metadata; | |
| } catch (error) { | |
| console.error("Failed to get metadata:", error); | |
| return null; | |
| } | |
| } |
🤖 Prompt for AI Agents
In `@packages/better-auth/src/migrate.ts` around lines 18 - 27, The catch block in
getMetadata discards the thrown error; change the catch to capture the error
(e.g., catch (err)) and include the error details in the log so failures from
db.getMetadata are visible—update the console.error call in getMetadata to log a
descriptive message plus the error (and optionally the db or format context) to
aid debugging.
@proofkit/better-auth
@proofkit/cli
create-proofkit
@proofkit/fmdapi
@proofkit/fmodata
@proofkit/typegen
@proofkit/webviewer
commit: |
…rors Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cursor Bugbot has reviewed your changes and found 3 potential issues.
Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.
… in CLI - Updated the CLI to extract the Database instance from both the adapter factory and the resolved adapter. - Improved error logging for metadata retrieval and migration operations, providing clearer feedback on failures. - Added a new `formatError` function to standardize error messages and enhance user experience during migrations. - Modified the `prettyPrintMigrationPlan` function to include target server URL and file name for better context during migrations.

BREAKING: Config now takes
database(fmodata Database instance)instead of
odata: { serverUrl, auth, database }.Note
High Risk
Breaking change replaces the adapter’s request/migration plumbing and CLI integration, affecting all Better Auth FileMaker DB reads/writes and schema migrations. Misconfigured
Databaseinstances or differences in request behavior could break authentication flows or migration execution.Overview
BREAKING:
FileMakerAdapternow requires adatabase(an@proofkit/fmodataDatabaseinstance) instead of anodata: { serverUrl, auth, database }config, and docs are updated to show creating it viaFMServerConnection.All adapter CRUD operations now delegate to
db._makeRequest()and the custom raw OData fetch wrapper (src/odata/index.ts) plus related deps (zod,odata-query,neverthrow,fm-odata-client) are removed. Migrations/CLI are refactored to usedb.getMetadata()anddb.schema.createTable/addFields, including better CLI target display and optional Full Access credential overrides by constructing a newFMServerConnection.@proofkit/fmodata’sDatabasegains internal helpers (_getDatabaseName,_makeRequest) to support adapter/CLI needs; tests are rewritten to mock aDatabaseinstead of globalfetch, and a small TS type fix is made in the typegen data grid.Written by Cursor Bugbot for commit fba5e73. This will update automatically on new commits. Configure here.