Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions knip.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import type { KnipConfig } from "knip";
const config: KnipConfig = {
ignoreWorkspaces: ["apps/demo"],
ignore: ["packages/cli/template/**"],
ignoreBinaries: ["op", "vercel"],
};

export default config;
5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,17 @@
"sherif": "pnpm dlx sherif@latest",
"sherif:fix": "pnpm sherif --fix",
"release": "pnpm build --filter='./packages/...' && changeset publish",
"test": "vitest"
"test": "vitest",
"knip": "knip"
},
"devDependencies": {
"@changesets/cli": "^2.29.3",
"@types/node": "^22.15.32",
"eslint": "^9.23.0",
"knip": "^5.56.0",
"prettier": "^3.5.3",
"turbo": "^2.5.4",
"typescript": "^5.8.3",
"vitest": "^3.2.3"
},
"packageManager": "[email protected]",
Expand Down
2 changes: 0 additions & 2 deletions packages/better-auth/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,14 +47,12 @@
"@babel/preset-react": "^7.27.1",
"@babel/preset-typescript": "^7.27.1",
"@commander-js/extra-typings": "^14.0.0",
"@proofkit/fmdapi": "workspace:*",
"@tanstack/vite-config": "^0.2.0",
"better-auth": "^1.2.10",
"c12": "^3.0.4",
"chalk": "5.4.1",
"commander": "^14.0.0",
"dotenv": "^16.5.0",
"execa": "^9.5.3",
"fm-odata-client": "^3.0.1",
"fs-extra": "^11.3.0",
"prompts": "^2.4.2",
Expand Down
7 changes: 7 additions & 0 deletions packages/fmdapi/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# @proofkit/fmdapi

## 5.0.1

### Patch Changes

- 2ff4cd1: Update how portal validation should be passed to the fmdapi client.
To update, simply re-run the `npx @proofkit/typegen@latest` command and your files will be updated to the correct syntax. If you still see errors, try with the "--reset-overrides" flag to also re-create your overrides files.

## 5.0.0

### Major Changes
Expand Down
2 changes: 1 addition & 1 deletion packages/fmdapi/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@proofkit/fmdapi",
"version": "5.0.0",
"version": "5.0.1",
"description": "FileMaker Data API client",
"repository": "[email protected]:proofgeist/fm-dapi.git",
"author": "Eric <[email protected]>",
Expand Down
21 changes: 13 additions & 8 deletions packages/fmdapi/src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export type ClientObjectProps = {
/**
* The schema for the portal data.
*/
portalData?: StandardSchemaV1<GenericPortalData>;
portalData?: Record<string, StandardSchemaV1<FieldData>>;
};
};

Expand All @@ -55,7 +55,11 @@ function DataApi<
: Fd;
type InferredPortalData = Opts["schema"] extends object
? Opts["schema"]["portalData"] extends object
? StandardSchemaV1.InferOutput<Opts["schema"]["portalData"]>
? {
[K in keyof Opts["schema"]["portalData"]]: StandardSchemaV1.InferOutput<
Opts["schema"]["portalData"][K]
>;
}
: Pd
: Pd;

Expand Down Expand Up @@ -473,19 +477,20 @@ function DataApi<
const validatedPortalRecords: PortalsWithIds<GenericPortalData>[] =
[];
for (const portalRecord of portalRecords) {
let portalResult = schema.portalData["~standard"].validate({
[portalName]: portalRecord,
});
let portalResult =
schema.portalData[portalName]?.["~standard"].validate(
portalRecord,
);
if (portalResult instanceof Promise)
portalResult = await portalResult;
if ("value" in portalResult) {
if (portalResult && "value" in portalResult) {
validatedPortalRecords.push({
...portalResult.value[portalName],
...portalResult.value,
recordId: portalRecord.recordId,
modId: portalRecord.modId,
});
} else {
portalDataIssues.push(...portalResult.issues);
portalDataIssues.push(...(portalResult?.issues ?? []));
}
}
// @ts-expect-error We know portalName is a valid key, but can't figure out the right assertions
Expand Down
12 changes: 12 additions & 0 deletions packages/fmdapi/src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import type { S, L, U } from "ts-toolbelt";
import type * as z3 from "zod/v3";
import type * as z4 from "zod/v4/core";

// eslint-disable-next-line @typescript-eslint/no-explicit-any
type TransformedFields<T extends Record<string, any>> = U.Merge<
Expand Down Expand Up @@ -27,3 +29,13 @@ export function removeFMTableNames<T extends Record<string, any>>(
}
return newObj;
}

export type InferZodPortals<T extends Record<string, any>> = {
[K in keyof T]: T[K] extends { _def: any; parse: (...args: any[]) => any }
? ReturnType<T[K]["parse"]>
: T[K] extends { _def: any; safeParse: (...args: any[]) => any }
? T[K] extends { parse: (...args: any[]) => any }
? ReturnType<T[K]["parse"]>
: any
: never;
};
11 changes: 7 additions & 4 deletions packages/fmdapi/tests/zod.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ const ZPortalTable = z.object({
"related::related_field": z.string(),
});

const ZCustomerPortals = z.object({
const ZCustomerPortals = {
PortalTable: ZPortalTable,
});
};

const client = DataApi({
adapter: new OttoAdapter({
Expand Down Expand Up @@ -79,19 +79,22 @@ describe("zod transformation", () => {
booleanField: z.coerce.boolean(),
CreationTimestamp: z.coerce.date(), // this does not convert the date properly, but does test the transformation
}),
portalData: z.object({
portalData: {
test: z.object({
"related::related_field": z.string(),
"related::recordId": z.coerce.string(), // it's actually a number field, this tests the transformation
}),
}),
},
},
});
const data = await customClient.listAll();
expect(typeof data[0].fieldData.booleanField).toBe("boolean");
console.log(data[0].fieldData.CreationTimestamp);
expect(typeof data[0].fieldData.CreationTimestamp).toBe("object");
const firstPortalRecord = data[0].portalData.test[0];
// @ts-expect-error - notAPortal is not a portal
data[0].portalData.notAPortal;

console.log("test portal data, first record", firstPortalRecord);
expect(typeof firstPortalRecord["related::related_field"]).toBe("string");
expect(typeof firstPortalRecord["related::recordId"]).toBe("string");
Expand Down
9 changes: 9 additions & 0 deletions packages/typegen/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
# @proofkit/typegen

## 1.0.9

### Patch Changes

- 2ff4cd1: Update how portal validation should be passed to the fmdapi client.
To update, simply re-run the `npx @proofkit/typegen@latest` command and your files will be updated to the correct syntax. If you still see errors, try with the "--reset-overrides" flag to also re-create your overrides files.
- Updated dependencies [2ff4cd1]
- @proofkit/[email protected]

## 1.0.8

### Patch Changes
Expand Down
5 changes: 4 additions & 1 deletion packages/typegen/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@proofkit/typegen",
"version": "1.0.8",
"version": "1.0.9",
"description": "",
"type": "module",
"main": "dist/esm/index.js",
Expand Down Expand Up @@ -59,14 +59,17 @@
"fs-extra": "^11.3.0",
"jsonc-parser": "^3.3.1",
"prettier": "^3.5.3",
"semver": "^7.7.2",
"ts-morph": "^26.0.0",
"ts-toolbelt": "^9.6.0",
"vite": "^6.3.4",
"zod": "3.25.64"
},
"devDependencies": {
"@types/fs-extra": "^11.0.4",
"@types/semver": "^7.7.0",
"publint": "^0.3.12",
"type-fest": "^3.13.1",
"typescript": "^5.8.3",
"vitest": "^3.2.3"
}
Expand Down
4 changes: 2 additions & 2 deletions packages/typegen/src/buildLayoutClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export function buildLayoutClient(
}

// import the types
if (type === "zod/v4" || type === "zod/v3") {
if (type === "zod" || type === "zod/v4" || type === "zod/v3") {
const schemaImport = sourceFile.addImportDeclaration({
moduleSpecifier: `../${schemaName}`,
namedImports: [{ name: `Z${schemaName}` }],
Expand Down Expand Up @@ -101,7 +101,7 @@ export function buildLayoutClient(
buildAdapter(writer, args);
writer.write(",").newLine();
writer.write(`layout: `).quote(layoutName).write(`,`).newLine();
if (type === "zod/v4" || type === "zod/v3") {
if (type === "zod" || type === "zod/v4" || type === "zod/v3") {
writer.writeLine(
`schema: { fieldData: Z${schemaName}${
hasPortals ? `, portalData: Z${schemaName}Portals` : ""
Expand Down
50 changes: 37 additions & 13 deletions packages/typegen/src/buildSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,19 @@ export function buildSchema(
strictNumbers = false,
} = args;

if (type === "zod/v4" || type === "zod/v3") {
const hasPortals = portalSchema.length > 0;

if (type === "zod" || type === "zod/v4" || type === "zod/v3") {
schemaFile.addImportDeclaration({
moduleSpecifier: type,
namedImports: ["z"],
});
if (hasPortals) {
schemaFile.addImportDeclaration({
moduleSpecifier: "@proofkit/fmdapi",
namedImports: ["InferZodPortals"],
});
}
}

// build the portals
Expand Down Expand Up @@ -103,24 +111,26 @@ export function buildSchema(
name: `Z${varname(schemaName)}Portals`,
initializer: (writer) => {
writer
.write(`z.object(`)
.inlineBlock(() => {
.write(`{`)
.newLine()
.indent(() => {
portalSchema.forEach((p, i) => {
writer
.quote(p.schemaName)
.write(": ")
.write(`Z${varname(p.schemaName)}`);
writer.conditionalWrite(i !== portalSchema.length - 1, ",");
writer.newLine();
});
})
.write(")");
.write(`}`);
},
},
],
});
schemaFile.addTypeAlias({
name: `T${varname(schemaName)}Portals`,
type: `z.infer<typeof Z${varname(schemaName)}Portals>`,
type: `InferZodPortals<typeof Z${varname(schemaName)}Portals>`,
isExported: true,
});
}
Expand Down Expand Up @@ -286,7 +296,7 @@ export function buildOverrideFile(
schemaFile: SourceFile,
{ type, ...args }: BuildSchemaArgs,
) {
if (type === "zod/v4" || type === "zod/v3") {
if (type === "zod" || type === "zod/v4" || type === "zod/v3") {
overrideFile.addImportDeclaration({
moduleSpecifier: type,
namedImports: ["z"],
Expand All @@ -299,7 +309,7 @@ export function buildOverrideFile(
.getExportSymbols()
.map((symbol) => symbol.getName())
.filter((name) => {
if (type === "zod/v4" || type === "zod/v3") {
if (type === "zod" || type === "zod/v4" || type === "zod/v3") {
return name.startsWith("Z");
} else {
return name.startsWith("T");
Expand All @@ -316,8 +326,20 @@ export function buildOverrideFile(
})),
});

const hasPortals = portalSchema.length > 0;

if (
hasPortals &&
(type === "zod" || type === "zod/v4" || type === "zod/v3")
) {
overrideFile.addImportDeclaration({
moduleSpecifier: "@proofkit/fmdapi",
namedImports: ["InferZodPortals"],
});
}

namedExportNames.forEach((name) => {
if (type === "zod/v4" || type === "zod/v3") {
if (type === "zod" || type === "zod/v4" || type === "zod/v3") {
overrideFile.addVariableStatement({
isExported: true,
declarationKind: VariableDeclarationKind.Const,
Expand Down Expand Up @@ -346,7 +368,7 @@ export function buildOverrideFile(
});

// build the final portals object
if (portalSchema.length > 0) {
if (hasPortals) {
if (type === "ts") {
overrideFile.addTypeAlias({
name: `T${varname(schemaName)}Portals`,
Expand All @@ -368,24 +390,26 @@ export function buildOverrideFile(
name: `Z${varname(schemaName)}Portals`,
initializer: (writer) => {
writer
.write(`z.object(`)
.inlineBlock(() => {
.write(`{`)
.newLine()
.indent(() => {
portalSchema.forEach((p, i) => {
writer
.quote(p.schemaName)
.write(": ")
.write(`Z${varname(p.schemaName)}`);
writer.conditionalWrite(i !== portalSchema.length - 1, ",");
writer.newLine();
});
})
.write(")");
.write(`}`);
},
},
],
});
overrideFile.addTypeAlias({
name: `T${varname(schemaName)}Portals`,
type: `z.infer<typeof Z${varname(schemaName)}Portals>`,
type: `InferZodPortals<typeof Z${varname(schemaName)}Portals>`,
isExported: true,
});
}
Expand Down
Loading