@proofkit/better-auth 0.3.1-beta.0 → 0.4.0-beta.10
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/bin/intent.js +20 -0
- package/dist/esm/adapter.d.ts +6 -10
- package/dist/esm/adapter.js +58 -93
- package/dist/esm/adapter.js.map +1 -1
- package/dist/esm/better-auth-cli/utils/add-svelte-kit-env-modules.js.map +1 -1
- package/dist/esm/better-auth-cli/utils/get-config.js.map +1 -1
- package/dist/esm/better-auth-cli/utils/get-tsconfig-info.js.map +1 -1
- package/dist/esm/cli/index.js +66 -23
- package/dist/esm/cli/index.js.map +1 -1
- package/dist/esm/migrate.d.ts +26 -84
- package/dist/esm/migrate.js +92 -100
- package/dist/esm/migrate.js.map +1 -1
- package/package.json +24 -19
- package/skills/better-auth-setup/SKILL.md +233 -0
- package/src/adapter.ts +84 -107
- package/src/cli/index.ts +88 -28
- package/src/migrate.ts +143 -157
- package/dist/esm/odata/index.d.ts +0 -29
- package/dist/esm/odata/index.js +0 -157
- package/dist/esm/odata/index.js.map +0 -1
- package/src/odata/index.ts +0 -219
package/package.json
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@proofkit/better-auth",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0-beta.10",
|
|
4
4
|
"description": "FileMaker adapter for Better Auth",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/esm/index.js",
|
|
7
7
|
"bin": {
|
|
8
|
-
"better-auth": "./dist/esm/cli/index.js"
|
|
8
|
+
"better-auth": "./dist/esm/cli/index.js",
|
|
9
|
+
"intent": "./bin/intent.js"
|
|
9
10
|
},
|
|
10
11
|
"exports": {
|
|
11
12
|
".": {
|
|
@@ -22,11 +23,15 @@
|
|
|
22
23
|
"fmdapi",
|
|
23
24
|
"proofgeist",
|
|
24
25
|
"filemaker",
|
|
25
|
-
"fmrest"
|
|
26
|
+
"fmrest",
|
|
27
|
+
"tanstack-intent"
|
|
26
28
|
],
|
|
27
29
|
"files": [
|
|
28
30
|
"dist",
|
|
29
|
-
"src"
|
|
31
|
+
"src",
|
|
32
|
+
"skills",
|
|
33
|
+
"bin",
|
|
34
|
+
"!skills/_artifacts"
|
|
30
35
|
],
|
|
31
36
|
"author": "",
|
|
32
37
|
"license": "ISC",
|
|
@@ -36,34 +41,34 @@
|
|
|
36
41
|
"url": "git+https://github.com/proofgeist/proofkit.git"
|
|
37
42
|
},
|
|
38
43
|
"dependencies": {
|
|
39
|
-
"@babel/preset-react": "^7.
|
|
40
|
-
"@babel/preset-typescript": "^7.
|
|
44
|
+
"@babel/preset-react": "^7.28.5",
|
|
45
|
+
"@babel/preset-typescript": "^7.28.5",
|
|
41
46
|
"@commander-js/extra-typings": "^14.0.0",
|
|
42
|
-
"@tanstack/vite-config": "^0.2.
|
|
43
|
-
"better-auth": "^1.
|
|
44
|
-
"c12": "^3.
|
|
47
|
+
"@tanstack/vite-config": "^0.2.1",
|
|
48
|
+
"better-auth": "^1.5.4",
|
|
49
|
+
"c12": "^3.3.3",
|
|
45
50
|
"chalk": "5.4.1",
|
|
46
|
-
"commander": "^14.0.
|
|
47
|
-
"dotenv": "^16.
|
|
48
|
-
"fs-extra": "^11.3.
|
|
49
|
-
"neverthrow": "^8.2.0",
|
|
50
|
-
"odata-query": "^8.0.4",
|
|
51
|
+
"commander": "^14.0.2",
|
|
52
|
+
"dotenv": "^16.6.1",
|
|
53
|
+
"fs-extra": "^11.3.3",
|
|
51
54
|
"prompts": "^2.4.2",
|
|
52
|
-
"vite": "^6.
|
|
53
|
-
"
|
|
55
|
+
"vite": "^6.4.1",
|
|
56
|
+
"@proofkit/fmodata": "0.1.0-beta.34"
|
|
54
57
|
},
|
|
55
58
|
"devDependencies": {
|
|
59
|
+
"@tanstack/intent": "^0.0.19",
|
|
56
60
|
"@types/fs-extra": "^11.0.4",
|
|
57
61
|
"@types/prompts": "^2.4.9",
|
|
58
62
|
"@vitest/ui": "^3.2.4",
|
|
59
|
-
"
|
|
60
|
-
"publint": "^0.3.12",
|
|
63
|
+
"publint": "^0.3.16",
|
|
61
64
|
"typescript": "^5.9.3",
|
|
62
|
-
"vitest": "^4.0.
|
|
65
|
+
"vitest": "^4.0.17"
|
|
63
66
|
},
|
|
64
67
|
"scripts": {
|
|
65
68
|
"dev": "pnpm build:watch",
|
|
66
69
|
"test": "vitest run",
|
|
70
|
+
"test:e2e": "doppler run -c test_betterauth -- vitest run tests/e2e",
|
|
71
|
+
"typecheck": "tsc --noEmit",
|
|
67
72
|
"build": "vite build && publint --strict",
|
|
68
73
|
"build:watch": "vite build --watch",
|
|
69
74
|
"ci": "pnpm run build && pnpm run test",
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: better-auth-setup
|
|
3
|
+
description: >
|
|
4
|
+
Set up self-hosted authentication with better-auth using FileMaker as the
|
|
5
|
+
database backend. Covers FileMakerAdapter, FMServerConnection, betterAuth
|
|
6
|
+
config, migration via npx @proofkit/better-auth migrate, OData prerequisites,
|
|
7
|
+
fmodata privilege, Full Access credentials for schema modification, plugin
|
|
8
|
+
migration workflow, troubleshooting "filemaker is not supported" errors.
|
|
9
|
+
type: core
|
|
10
|
+
library: proofkit
|
|
11
|
+
library_version: "0.4.0-beta.7"
|
|
12
|
+
requires:
|
|
13
|
+
- fmodata-client
|
|
14
|
+
sources:
|
|
15
|
+
- "proofgeist/proofkit:packages/better-auth/src/*.ts"
|
|
16
|
+
- "proofgeist/proofkit:apps/docs/content/docs/better-auth/*.mdx"
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Setup
|
|
20
|
+
|
|
21
|
+
### Prerequisites
|
|
22
|
+
|
|
23
|
+
- OData enabled on FileMaker Server
|
|
24
|
+
- API credentials with `fmodata` privilege enabled
|
|
25
|
+
- Read/write access to the better-auth tables
|
|
26
|
+
- Full Access credentials available for schema migration (can differ from runtime credentials)
|
|
27
|
+
|
|
28
|
+
### Install packages
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
pnpm add @proofkit/better-auth @proofkit/fmodata
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### Configure auth.ts
|
|
35
|
+
|
|
36
|
+
```ts
|
|
37
|
+
import { betterAuth } from "better-auth";
|
|
38
|
+
import { FMServerConnection } from "@proofkit/fmodata";
|
|
39
|
+
import { FileMakerAdapter } from "@proofkit/better-auth";
|
|
40
|
+
|
|
41
|
+
const connection = new FMServerConnection({
|
|
42
|
+
serverUrl: process.env.FM_SERVER_URL!,
|
|
43
|
+
auth: {
|
|
44
|
+
username: process.env.FM_USERNAME!,
|
|
45
|
+
password: process.env.FM_PASSWORD!,
|
|
46
|
+
},
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
const db = connection.database(process.env.FM_DATABASE!);
|
|
50
|
+
|
|
51
|
+
export const auth = betterAuth({
|
|
52
|
+
database: FileMakerAdapter({ database: db }),
|
|
53
|
+
// add plugins, social providers, etc.
|
|
54
|
+
});
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
`FileMakerAdapter` accepts a `FileMakerAdapterConfig`:
|
|
58
|
+
|
|
59
|
+
- `database` (required) -- an fmodata `Database` instance
|
|
60
|
+
- `debugLogs` (optional) -- enable adapter debug logging
|
|
61
|
+
- `usePlural` (optional) -- set `true` if table names are plural
|
|
62
|
+
|
|
63
|
+
The adapter maps Better Auth operations (create, findOne, findMany, update, delete, count) to OData requests via `db._makeRequest`. It does not support JSON columns, native dates, or native booleans -- all values are stored as strings/numbers.
|
|
64
|
+
|
|
65
|
+
### Alternative: Data API key (OttoFMS 4.11+)
|
|
66
|
+
|
|
67
|
+
```ts
|
|
68
|
+
const connection = new FMServerConnection({
|
|
69
|
+
serverUrl: process.env.FM_SERVER_URL!,
|
|
70
|
+
auth: {
|
|
71
|
+
apiKey: process.env.OTTO_API_KEY!,
|
|
72
|
+
},
|
|
73
|
+
});
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
OData must be enabled for the key.
|
|
77
|
+
|
|
78
|
+
## Core Patterns
|
|
79
|
+
|
|
80
|
+
### 1. Initial migration
|
|
81
|
+
|
|
82
|
+
After configuring `auth.ts`, run the migration CLI to create tables and fields in FileMaker:
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
npx @proofkit/better-auth migrate
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
The CLI:
|
|
89
|
+
|
|
90
|
+
1. Loads your `auth.ts` config (auto-detected or via `--config <path>`)
|
|
91
|
+
2. Calls `getSchema()` from `better-auth/db` to determine required tables/fields
|
|
92
|
+
3. Fetches current OData metadata via `db.getMetadata()`
|
|
93
|
+
4. Computes a diff: tables to create, fields to add to existing tables
|
|
94
|
+
5. Prints the migration plan and prompts for confirmation
|
|
95
|
+
6. Executes via `db.schema.createTable()` and `db.schema.addFields()`
|
|
96
|
+
|
|
97
|
+
Only schema is modified. No layouts or relationships are created.
|
|
98
|
+
|
|
99
|
+
If your runtime credentials lack Full Access, override for migration only:
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
npx @proofkit/better-auth migrate --username "admin" --password "admin_pass"
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Skip confirmation with `-y`:
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
npx @proofkit/better-auth migrate -y
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### 2. Adding plugins and re-migrating
|
|
112
|
+
|
|
113
|
+
When you add a Better Auth plugin (e.g. `twoFactor`, `organization`), it declares additional tables/fields. After updating `auth.ts`:
|
|
114
|
+
|
|
115
|
+
```ts
|
|
116
|
+
import { betterAuth } from "better-auth";
|
|
117
|
+
import { twoFactor } from "better-auth/plugins";
|
|
118
|
+
import { FMServerConnection } from "@proofkit/fmodata";
|
|
119
|
+
import { FileMakerAdapter } from "@proofkit/better-auth";
|
|
120
|
+
|
|
121
|
+
const connection = new FMServerConnection({
|
|
122
|
+
serverUrl: process.env.FM_SERVER_URL!,
|
|
123
|
+
auth: {
|
|
124
|
+
username: process.env.FM_USERNAME!,
|
|
125
|
+
password: process.env.FM_PASSWORD!,
|
|
126
|
+
},
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
const db = connection.database(process.env.FM_DATABASE!);
|
|
130
|
+
|
|
131
|
+
export const auth = betterAuth({
|
|
132
|
+
database: FileMakerAdapter({ database: db }),
|
|
133
|
+
plugins: [twoFactor()],
|
|
134
|
+
});
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Re-run migration:
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
npx @proofkit/better-auth migrate
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
The planner diffs against existing metadata, so only new tables/fields are added. Existing tables are left untouched.
|
|
144
|
+
|
|
145
|
+
### 3. Troubleshooting privilege errors
|
|
146
|
+
|
|
147
|
+
When migration fails with OData error code `207`, the account lacks schema modification privileges. The CLI outputs:
|
|
148
|
+
|
|
149
|
+
```
|
|
150
|
+
Failed to create table "tableName": Cannot modify schema.
|
|
151
|
+
The account used does not have schema modification privileges.
|
|
152
|
+
Use --username and --password to provide Full Access credentials.
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
Fix: provide Full Access credentials via CLI flags. These are only used for migration, not at runtime.
|
|
156
|
+
|
|
157
|
+
## Common Mistakes
|
|
158
|
+
|
|
159
|
+
### [CRITICAL] Using better-auth CLI instead of @proofkit/better-auth
|
|
160
|
+
|
|
161
|
+
Wrong:
|
|
162
|
+
```bash
|
|
163
|
+
npx better-auth migrate
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
Correct:
|
|
167
|
+
```bash
|
|
168
|
+
npx @proofkit/better-auth migrate
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
The standard better-auth CLI does not know about the FileMaker adapter and produces: `ERROR [Better Auth]: filemaker is not supported. If it is a custom adapter, please request the maintainer to implement createSchema`. The `@proofkit/better-auth` CLI loads your auth config, extracts the `Database` instance from the adapter, and handles migration via fmodata's schema API.
|
|
172
|
+
|
|
173
|
+
Source: `apps/docs/content/docs/better-auth/troubleshooting.mdx`
|
|
174
|
+
|
|
175
|
+
### [HIGH] Missing Full Access credentials for schema migration
|
|
176
|
+
|
|
177
|
+
Wrong:
|
|
178
|
+
```bash
|
|
179
|
+
# Using runtime credentials that only have fmodata privilege
|
|
180
|
+
npx @proofkit/better-auth migrate
|
|
181
|
+
# Fails with OData error 207: Cannot modify schema
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
Correct:
|
|
185
|
+
```bash
|
|
186
|
+
npx @proofkit/better-auth migrate --username "full_access_user" --password "full_access_pass"
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
Schema modification (`db.schema.createTable`, `db.schema.addFields`) requires [Full Access] privileges. Standard API accounts with `fmodata` privilege can read/write data but cannot alter schema. The CLI accepts `--username` and `--password` flags to override credentials for migration only.
|
|
190
|
+
|
|
191
|
+
Source: `packages/better-auth/src/cli/index.ts`, `packages/better-auth/src/migrate.ts`
|
|
192
|
+
|
|
193
|
+
### [HIGH] Removing fields added by migration
|
|
194
|
+
|
|
195
|
+
Wrong:
|
|
196
|
+
```
|
|
197
|
+
Manually deleting "unused" fields from better-auth tables in FileMaker
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
Correct:
|
|
201
|
+
```
|
|
202
|
+
Keep all fields created by migration, even if you don't plan to use them
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
Better Auth expects all schema fields to exist at runtime. The adapter issues OData requests that reference these fields. Removing them causes runtime errors when Better Auth attempts to read or write those columns.
|
|
206
|
+
|
|
207
|
+
Source: `apps/docs/content/docs/better-auth/installation.mdx`
|
|
208
|
+
|
|
209
|
+
### [HIGH] Forgetting to re-run migration after adding plugins
|
|
210
|
+
|
|
211
|
+
Wrong:
|
|
212
|
+
```ts
|
|
213
|
+
// Added twoFactor() plugin to auth.ts but did not re-run migration
|
|
214
|
+
export const auth = betterAuth({
|
|
215
|
+
database: FileMakerAdapter({ database: db }),
|
|
216
|
+
plugins: [twoFactor()],
|
|
217
|
+
});
|
|
218
|
+
// Runtime errors: tables/fields for twoFactor don't exist
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
Correct:
|
|
222
|
+
```bash
|
|
223
|
+
# After adding any plugin to auth.ts, always re-run:
|
|
224
|
+
npx @proofkit/better-auth migrate
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
Each plugin declares additional tables and fields via `getSchema()`. The migration planner diffs the full schema (including plugins) against current OData metadata. Without re-running, the new tables/fields don't exist and Better Auth throws at runtime.
|
|
228
|
+
|
|
229
|
+
Source: `apps/docs/content/docs/better-auth/installation.mdx`
|
|
230
|
+
|
|
231
|
+
## References
|
|
232
|
+
|
|
233
|
+
- **fmodata-client** -- Better Auth uses fmodata `Database` under the hood for all OData requests. `FMServerConnection` and `database()` must be configured before `FileMakerAdapter` can work. The adapter calls `db._makeRequest()` for CRUD and `db.schema.*` for migrations.
|
package/src/adapter.ts
CHANGED
|
@@ -1,50 +1,23 @@
|
|
|
1
1
|
/** biome-ignore-all lint/suspicious/noExplicitAny: library code */
|
|
2
|
+
import type { Database } from "@proofkit/fmodata";
|
|
2
3
|
import { logger } from "better-auth";
|
|
3
|
-
import { type
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
import { createRawFetch, type FmOdataConfig } from "./odata";
|
|
7
|
-
|
|
8
|
-
const configSchema = z.object({
|
|
9
|
-
debugLogs: z.unknown().optional(),
|
|
10
|
-
usePlural: z.boolean().optional(),
|
|
11
|
-
odata: z.object({
|
|
12
|
-
serverUrl: z.url(),
|
|
13
|
-
auth: z.union([z.object({ username: z.string(), password: z.string() }), z.object({ apiKey: z.string() })]),
|
|
14
|
-
database: z.string().endsWith(".fmp12"),
|
|
15
|
-
}),
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
interface FileMakerAdapterConfig {
|
|
4
|
+
import { type CleanedWhere, createAdapterFactory, type DBAdapterDebugLogOption } from "better-auth/adapters";
|
|
5
|
+
|
|
6
|
+
export interface FileMakerAdapterConfig {
|
|
19
7
|
/**
|
|
20
8
|
* Helps you debug issues with the adapter.
|
|
21
9
|
*/
|
|
22
|
-
debugLogs?:
|
|
10
|
+
debugLogs?: DBAdapterDebugLogOption;
|
|
23
11
|
/**
|
|
24
12
|
* If the table names in the schema are plural.
|
|
25
13
|
*/
|
|
26
14
|
usePlural?: boolean;
|
|
27
|
-
|
|
28
15
|
/**
|
|
29
|
-
*
|
|
16
|
+
* The fmodata Database instance to use for all OData requests.
|
|
30
17
|
*/
|
|
31
|
-
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export interface AdapterOptions {
|
|
35
|
-
config: FileMakerAdapterConfig;
|
|
18
|
+
database: Database;
|
|
36
19
|
}
|
|
37
20
|
|
|
38
|
-
const defaultConfig: Required<FileMakerAdapterConfig> = {
|
|
39
|
-
debugLogs: false,
|
|
40
|
-
usePlural: false,
|
|
41
|
-
odata: {
|
|
42
|
-
serverUrl: "",
|
|
43
|
-
auth: { username: "", password: "" },
|
|
44
|
-
database: "",
|
|
45
|
-
},
|
|
46
|
-
};
|
|
47
|
-
|
|
48
21
|
// Regex patterns for field validation and ISO date detection
|
|
49
22
|
const FIELD_SPECIAL_CHARS_REGEX = /[\s_]/;
|
|
50
23
|
const ISO_DATE_REGEX = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d{3})?Z?$/;
|
|
@@ -155,41 +128,59 @@ export function parseWhere(where?: CleanedWhere[]): string {
|
|
|
155
128
|
return clauses.join(" ");
|
|
156
129
|
}
|
|
157
130
|
|
|
158
|
-
|
|
159
|
-
|
|
131
|
+
/**
|
|
132
|
+
* Build an OData query string from parameters.
|
|
133
|
+
*/
|
|
134
|
+
function buildQueryString(params: {
|
|
135
|
+
top?: number;
|
|
136
|
+
skip?: number;
|
|
137
|
+
filter?: string;
|
|
138
|
+
orderBy?: string;
|
|
139
|
+
select?: string[];
|
|
140
|
+
}): string {
|
|
141
|
+
const parts: string[] = [];
|
|
142
|
+
if (params.top !== undefined) {
|
|
143
|
+
parts.push(`$top=${params.top}`);
|
|
144
|
+
}
|
|
145
|
+
if (params.skip !== undefined) {
|
|
146
|
+
parts.push(`$skip=${params.skip}`);
|
|
147
|
+
}
|
|
148
|
+
if (params.filter) {
|
|
149
|
+
parts.push(`$filter=${encodeURIComponent(params.filter)}`);
|
|
150
|
+
}
|
|
151
|
+
if (params.orderBy) {
|
|
152
|
+
parts.push(`$orderby=${encodeURIComponent(params.orderBy)}`);
|
|
153
|
+
}
|
|
154
|
+
if (params.select?.length) {
|
|
155
|
+
parts.push(`$select=${params.select.map(encodeURIComponent).join(",")}`);
|
|
156
|
+
}
|
|
157
|
+
return parts.length > 0 ? `?${parts.join("&")}` : "";
|
|
158
|
+
}
|
|
160
159
|
|
|
161
|
-
|
|
162
|
-
|
|
160
|
+
export const FileMakerAdapter = (config: FileMakerAdapterConfig) => {
|
|
161
|
+
if (!config.database || typeof config.database !== "object") {
|
|
162
|
+
throw new Error("FileMakerAdapter requires a `database` (fmodata Database instance).");
|
|
163
163
|
}
|
|
164
|
-
const config = parsed.data;
|
|
165
164
|
|
|
166
|
-
const
|
|
167
|
-
...config.odata,
|
|
168
|
-
logging: config.debugLogs ? "verbose" : "none",
|
|
169
|
-
});
|
|
165
|
+
const db = config.database;
|
|
170
166
|
|
|
171
|
-
|
|
167
|
+
const adapterFactory = createAdapterFactory({
|
|
172
168
|
config: {
|
|
173
169
|
adapterId: "filemaker",
|
|
174
170
|
adapterName: "FileMaker",
|
|
175
|
-
usePlural: config.usePlural ?? false,
|
|
176
|
-
debugLogs: config.debugLogs ?? false,
|
|
177
|
-
supportsJSON: false,
|
|
178
|
-
supportsDates: false,
|
|
179
|
-
supportsBooleans: false,
|
|
180
|
-
supportsNumericIds: false,
|
|
171
|
+
usePlural: config.usePlural ?? false,
|
|
172
|
+
debugLogs: config.debugLogs ?? false,
|
|
173
|
+
supportsJSON: false,
|
|
174
|
+
supportsDates: false,
|
|
175
|
+
supportsBooleans: false,
|
|
176
|
+
supportsNumericIds: false,
|
|
181
177
|
},
|
|
182
178
|
adapter: () => {
|
|
183
179
|
return {
|
|
184
180
|
create: async ({ data, model }) => {
|
|
185
|
-
|
|
186
|
-
console.log("session", data);
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
const result = await fetch(`/${model}`, {
|
|
181
|
+
const result = await db._makeRequest<Record<string, any>>(`/${model}`, {
|
|
190
182
|
method: "POST",
|
|
191
|
-
body: data,
|
|
192
|
-
output: z.looseObject({ id: z.string() }),
|
|
183
|
+
body: JSON.stringify(data),
|
|
193
184
|
});
|
|
194
185
|
|
|
195
186
|
if (result.error) {
|
|
@@ -202,15 +193,12 @@ export const FileMakerAdapter = (_config: FileMakerAdapterConfig = defaultConfig
|
|
|
202
193
|
const filter = parseWhere(where);
|
|
203
194
|
logger.debug("$filter", filter);
|
|
204
195
|
|
|
205
|
-
const query =
|
|
196
|
+
const query = buildQueryString({
|
|
206
197
|
filter: filter.length > 0 ? filter : undefined,
|
|
207
198
|
});
|
|
208
199
|
|
|
209
|
-
const result = await
|
|
210
|
-
|
|
211
|
-
output: z.object({ value: z.number() }),
|
|
212
|
-
});
|
|
213
|
-
if (!result.data) {
|
|
200
|
+
const result = await db._makeRequest<{ value: number }>(`/${model}/$count${query}`);
|
|
201
|
+
if (result.error) {
|
|
214
202
|
throw new Error("Failed to count records");
|
|
215
203
|
}
|
|
216
204
|
return (result.data?.value as any) ?? 0;
|
|
@@ -219,15 +207,12 @@ export const FileMakerAdapter = (_config: FileMakerAdapterConfig = defaultConfig
|
|
|
219
207
|
const filter = parseWhere(where);
|
|
220
208
|
logger.debug("$filter", filter);
|
|
221
209
|
|
|
222
|
-
const query =
|
|
210
|
+
const query = buildQueryString({
|
|
223
211
|
top: 1,
|
|
224
212
|
filter: filter.length > 0 ? filter : undefined,
|
|
225
213
|
});
|
|
226
214
|
|
|
227
|
-
const result = await
|
|
228
|
-
method: "GET",
|
|
229
|
-
output: z.object({ value: z.array(z.any()) }),
|
|
230
|
-
});
|
|
215
|
+
const result = await db._makeRequest<{ value: any[] }>(`/${model}${query}`);
|
|
231
216
|
if (result.error) {
|
|
232
217
|
throw new Error("Failed to find record");
|
|
233
218
|
}
|
|
@@ -237,7 +222,7 @@ export const FileMakerAdapter = (_config: FileMakerAdapterConfig = defaultConfig
|
|
|
237
222
|
const filter = parseWhere(where);
|
|
238
223
|
logger.debug("FIND MANY", { where, filter });
|
|
239
224
|
|
|
240
|
-
const query =
|
|
225
|
+
const query = buildQueryString({
|
|
241
226
|
top: limit,
|
|
242
227
|
skip: offset,
|
|
243
228
|
orderBy: sortBy ? `${sortBy.field} ${sortBy.direction ?? "asc"}` : undefined,
|
|
@@ -245,10 +230,7 @@ export const FileMakerAdapter = (_config: FileMakerAdapterConfig = defaultConfig
|
|
|
245
230
|
});
|
|
246
231
|
logger.debug("QUERY", query);
|
|
247
232
|
|
|
248
|
-
const result = await
|
|
249
|
-
method: "GET",
|
|
250
|
-
output: z.object({ value: z.array(z.any()) }),
|
|
251
|
-
});
|
|
233
|
+
const result = await db._makeRequest<{ value: any[] }>(`/${model}${query}`);
|
|
252
234
|
logger.debug("RESULT", result);
|
|
253
235
|
|
|
254
236
|
if (result.error) {
|
|
@@ -259,54 +241,44 @@ export const FileMakerAdapter = (_config: FileMakerAdapterConfig = defaultConfig
|
|
|
259
241
|
},
|
|
260
242
|
delete: async ({ model, where }) => {
|
|
261
243
|
const filter = parseWhere(where);
|
|
262
|
-
console.log("DELETE", { model, where, filter });
|
|
263
244
|
logger.debug("$filter", filter);
|
|
264
245
|
|
|
265
246
|
// Find a single id matching the filter
|
|
266
|
-
const query =
|
|
247
|
+
const query = buildQueryString({
|
|
267
248
|
top: 1,
|
|
268
249
|
select: [`"id"`],
|
|
269
250
|
filter: filter.length > 0 ? filter : undefined,
|
|
270
251
|
});
|
|
271
252
|
|
|
272
|
-
const toDelete = await
|
|
273
|
-
method: "GET",
|
|
274
|
-
output: z.object({ value: z.array(z.object({ id: z.string() })) }),
|
|
275
|
-
});
|
|
253
|
+
const toDelete = await db._makeRequest<{ value: { id: string }[] }>(`/${model}${query}`);
|
|
276
254
|
|
|
277
255
|
const id = toDelete.data?.value?.[0]?.id;
|
|
278
256
|
if (!id) {
|
|
279
|
-
// Nothing to delete
|
|
280
257
|
return;
|
|
281
258
|
}
|
|
282
259
|
|
|
283
|
-
const result = await
|
|
260
|
+
const result = await db._makeRequest(`/${model}('${id}')`, {
|
|
284
261
|
method: "DELETE",
|
|
285
262
|
});
|
|
286
263
|
if (result.error) {
|
|
287
|
-
console.log("DELETE ERROR", result.error);
|
|
288
264
|
throw new Error("Failed to delete record");
|
|
289
265
|
}
|
|
290
266
|
},
|
|
291
267
|
deleteMany: async ({ model, where }) => {
|
|
292
268
|
const filter = parseWhere(where);
|
|
293
|
-
console.log("DELETE MANY", { model, where, filter });
|
|
294
269
|
|
|
295
270
|
// Find all ids matching the filter
|
|
296
|
-
const query =
|
|
271
|
+
const query = buildQueryString({
|
|
297
272
|
select: [`"id"`],
|
|
298
273
|
filter: filter.length > 0 ? filter : undefined,
|
|
299
274
|
});
|
|
300
275
|
|
|
301
|
-
const rows = await
|
|
302
|
-
method: "GET",
|
|
303
|
-
output: z.object({ value: z.array(z.object({ id: z.string() })) }),
|
|
304
|
-
});
|
|
276
|
+
const rows = await db._makeRequest<{ value: { id: string }[] }>(`/${model}${query}`);
|
|
305
277
|
|
|
306
278
|
const ids = rows.data?.value?.map((r: any) => r.id) ?? [];
|
|
307
279
|
let deleted = 0;
|
|
308
280
|
for (const id of ids) {
|
|
309
|
-
const res = await
|
|
281
|
+
const res = await db._makeRequest(`/${model}('${id}')`, {
|
|
310
282
|
method: "DELETE",
|
|
311
283
|
});
|
|
312
284
|
if (!res.error) {
|
|
@@ -319,16 +291,14 @@ export const FileMakerAdapter = (_config: FileMakerAdapterConfig = defaultConfig
|
|
|
319
291
|
const filter = parseWhere(where);
|
|
320
292
|
logger.debug("UPDATE", { model, where, update });
|
|
321
293
|
logger.debug("$filter", filter);
|
|
294
|
+
|
|
322
295
|
// Find one id to update
|
|
323
|
-
const query =
|
|
296
|
+
const query = buildQueryString({
|
|
324
297
|
select: [`"id"`],
|
|
325
298
|
filter: filter.length > 0 ? filter : undefined,
|
|
326
299
|
});
|
|
327
300
|
|
|
328
|
-
const existing = await
|
|
329
|
-
method: "GET",
|
|
330
|
-
output: z.object({ value: z.array(z.object({ id: z.string() })) }),
|
|
331
|
-
});
|
|
301
|
+
const existing = await db._makeRequest<{ value: { id: string }[] }>(`/${model}${query}`);
|
|
332
302
|
logger.debug("EXISTING", existing.data);
|
|
333
303
|
|
|
334
304
|
const id = existing.data?.value?.[0]?.id;
|
|
@@ -336,9 +306,9 @@ export const FileMakerAdapter = (_config: FileMakerAdapterConfig = defaultConfig
|
|
|
336
306
|
return null;
|
|
337
307
|
}
|
|
338
308
|
|
|
339
|
-
const patchRes = await
|
|
309
|
+
const patchRes = await db._makeRequest(`/${model}('${id}')`, {
|
|
340
310
|
method: "PATCH",
|
|
341
|
-
body: update,
|
|
311
|
+
body: JSON.stringify(update),
|
|
342
312
|
});
|
|
343
313
|
logger.debug("PATCH RES", patchRes.data);
|
|
344
314
|
if (patchRes.error) {
|
|
@@ -346,32 +316,27 @@ export const FileMakerAdapter = (_config: FileMakerAdapterConfig = defaultConfig
|
|
|
346
316
|
}
|
|
347
317
|
|
|
348
318
|
// Read back the updated record
|
|
349
|
-
const readBack = await
|
|
350
|
-
method: "GET",
|
|
351
|
-
output: z.record(z.string(), z.unknown()),
|
|
352
|
-
});
|
|
319
|
+
const readBack = await db._makeRequest<Record<string, unknown>>(`/${model}('${id}')`);
|
|
353
320
|
logger.debug("READ BACK", readBack.data);
|
|
354
321
|
return (readBack.data as any) ?? null;
|
|
355
322
|
},
|
|
356
323
|
updateMany: async ({ model, where, update }) => {
|
|
357
324
|
const filter = parseWhere(where);
|
|
325
|
+
|
|
358
326
|
// Find all ids matching the filter
|
|
359
|
-
const query =
|
|
327
|
+
const query = buildQueryString({
|
|
360
328
|
select: [`"id"`],
|
|
361
329
|
filter: filter.length > 0 ? filter : undefined,
|
|
362
330
|
});
|
|
363
331
|
|
|
364
|
-
const rows = await
|
|
365
|
-
method: "GET",
|
|
366
|
-
output: z.object({ value: z.array(z.object({ id: z.string() })) }),
|
|
367
|
-
});
|
|
332
|
+
const rows = await db._makeRequest<{ value: { id: string }[] }>(`/${model}${query}`);
|
|
368
333
|
|
|
369
334
|
const ids = rows.data?.value?.map((r: any) => r.id) ?? [];
|
|
370
335
|
let updated = 0;
|
|
371
336
|
for (const id of ids) {
|
|
372
|
-
const res = await
|
|
337
|
+
const res = await db._makeRequest(`/${model}('${id}')`, {
|
|
373
338
|
method: "PATCH",
|
|
374
|
-
body: update,
|
|
339
|
+
body: JSON.stringify(update),
|
|
375
340
|
});
|
|
376
341
|
if (!res.error) {
|
|
377
342
|
updated++;
|
|
@@ -382,4 +347,16 @@ export const FileMakerAdapter = (_config: FileMakerAdapterConfig = defaultConfig
|
|
|
382
347
|
};
|
|
383
348
|
},
|
|
384
349
|
});
|
|
350
|
+
|
|
351
|
+
// Expose the Database instance for CLI access.
|
|
352
|
+
// Set on both the factory function (for pre-getAdapter extraction)
|
|
353
|
+
// and the returned adapter (for post-getAdapter extraction).
|
|
354
|
+
const originalFactory = adapterFactory;
|
|
355
|
+
const wrappedFactory = ((options: unknown) => {
|
|
356
|
+
const adapter = (originalFactory as (opts: unknown) => Record<string, unknown>)(options);
|
|
357
|
+
adapter.database = db;
|
|
358
|
+
return adapter;
|
|
359
|
+
}) as typeof adapterFactory;
|
|
360
|
+
(wrappedFactory as unknown as { database: Database }).database = db;
|
|
361
|
+
return wrappedFactory;
|
|
385
362
|
};
|