@eide/foir-cli 0.1.43 → 0.1.44
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/cli.js +981 -3286
- package/dist/config/types.d.ts +19 -30
- package/dist/schema.graphql +29 -10
- package/package.json +20 -19
package/dist/cli.js
CHANGED
|
@@ -48,8 +48,8 @@ async function getCredentials() {
|
|
|
48
48
|
}
|
|
49
49
|
async function writeCredentials(credentials) {
|
|
50
50
|
await ensureDir(getCredentialsDir());
|
|
51
|
-
const
|
|
52
|
-
await fs.writeFile(
|
|
51
|
+
const path3 = getCredentialsPath();
|
|
52
|
+
await fs.writeFile(path3, JSON.stringify(credentials, null, 2), {
|
|
53
53
|
mode: 384
|
|
54
54
|
});
|
|
55
55
|
}
|
|
@@ -606,95 +606,98 @@ async function provisionApiKey(apiUrl, accessToken, projectId, tenantId) {
|
|
|
606
606
|
}
|
|
607
607
|
function registerSelectProjectCommand(program2, globalOpts) {
|
|
608
608
|
program2.command("select-project").description("Choose which project to work with").option("--project-id <id>", "Project ID to select directly").option("--save-as <name>", "Save as a named profile").action(
|
|
609
|
-
withErrorHandler(
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
const sessionContext = await fetchSessionContext(
|
|
619
|
-
apiUrl,
|
|
620
|
-
credentials.accessToken
|
|
621
|
-
);
|
|
622
|
-
const { availableTenants: tenants, availableProjects: projects } = sessionContext;
|
|
623
|
-
if (projects.length === 0) {
|
|
624
|
-
console.log("No projects found. Create one in the platform first.");
|
|
625
|
-
throw new Error("No projects available");
|
|
626
|
-
}
|
|
627
|
-
const tenantNameMap = new Map(tenants.map((t) => [t.id, t.name]));
|
|
628
|
-
let selectedProject;
|
|
629
|
-
if (cmdOpts.projectId) {
|
|
630
|
-
const found = projects.find((p) => p.id === cmdOpts.projectId);
|
|
631
|
-
if (!found) {
|
|
632
|
-
console.log(`Project with ID "${cmdOpts.projectId}" not found.`);
|
|
633
|
-
console.log("Available projects:");
|
|
634
|
-
for (const p of projects) {
|
|
635
|
-
console.log(` - ${p.name} (${p.id})`);
|
|
636
|
-
}
|
|
637
|
-
throw new Error("Project not found");
|
|
609
|
+
withErrorHandler(
|
|
610
|
+
globalOpts,
|
|
611
|
+
async (cmdOpts) => {
|
|
612
|
+
const opts = globalOpts();
|
|
613
|
+
const apiUrl = getApiUrl(opts);
|
|
614
|
+
const credentials = await getCredentials();
|
|
615
|
+
if (!credentials) {
|
|
616
|
+
console.log("Not logged in. Run `foir login` first.");
|
|
617
|
+
throw new Error("Not authenticated");
|
|
638
618
|
}
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
const key = tenantNameMap.get(p.tenantId) ?? "Unknown";
|
|
644
|
-
if (!acc[key]) acc[key] = [];
|
|
645
|
-
acc[key].push(p);
|
|
646
|
-
return acc;
|
|
647
|
-
},
|
|
648
|
-
{}
|
|
619
|
+
console.log("Fetching your projects...\n");
|
|
620
|
+
const sessionContext = await fetchSessionContext(
|
|
621
|
+
apiUrl,
|
|
622
|
+
credentials.accessToken
|
|
649
623
|
);
|
|
650
|
-
const
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
624
|
+
const { availableTenants: tenants, availableProjects: projects } = sessionContext;
|
|
625
|
+
if (projects.length === 0) {
|
|
626
|
+
console.log("No projects found. Create one in the platform first.");
|
|
627
|
+
throw new Error("No projects available");
|
|
628
|
+
}
|
|
629
|
+
const tenantNameMap = new Map(tenants.map((t) => [t.id, t.name]));
|
|
630
|
+
let selectedProject;
|
|
631
|
+
if (cmdOpts.projectId) {
|
|
632
|
+
const found = projects.find((p) => p.id === cmdOpts.projectId);
|
|
633
|
+
if (!found) {
|
|
634
|
+
console.log(`Project with ID "${cmdOpts.projectId}" not found.`);
|
|
635
|
+
console.log("Available projects:");
|
|
636
|
+
for (const p of projects) {
|
|
637
|
+
console.log(` - ${p.name} (${p.id})`);
|
|
638
|
+
}
|
|
639
|
+
throw new Error("Project not found");
|
|
640
|
+
}
|
|
641
|
+
selectedProject = found;
|
|
642
|
+
} else {
|
|
643
|
+
const byTenant = projects.reduce(
|
|
644
|
+
(acc, p) => {
|
|
645
|
+
const key = tenantNameMap.get(p.tenantId) ?? "Unknown";
|
|
646
|
+
if (!acc[key]) acc[key] = [];
|
|
647
|
+
acc[key].push(p);
|
|
648
|
+
return acc;
|
|
649
|
+
},
|
|
650
|
+
{}
|
|
651
|
+
);
|
|
652
|
+
const choices = Object.entries(byTenant).flatMap(
|
|
653
|
+
([tenantName, tenantProjects]) => [
|
|
654
|
+
new inquirer.Separator(`\u2500\u2500 ${tenantName} \u2500\u2500`),
|
|
655
|
+
...tenantProjects.map((p) => ({
|
|
656
|
+
name: ` ${p.name}`,
|
|
657
|
+
value: p.id,
|
|
658
|
+
short: p.name
|
|
659
|
+
}))
|
|
660
|
+
]
|
|
661
|
+
);
|
|
662
|
+
const { projectId } = await inquirer.prompt([
|
|
663
|
+
{
|
|
664
|
+
type: "list",
|
|
665
|
+
name: "projectId",
|
|
666
|
+
message: "Select a project:",
|
|
667
|
+
choices
|
|
668
|
+
}
|
|
669
|
+
]);
|
|
670
|
+
selectedProject = projects.find((p) => p.id === projectId);
|
|
671
|
+
}
|
|
672
|
+
console.log("\nProvisioning API key for CLI access...");
|
|
673
|
+
const { apiKey, apiKeyId } = await provisionApiKey(
|
|
674
|
+
apiUrl,
|
|
675
|
+
credentials.accessToken,
|
|
676
|
+
selectedProject.id,
|
|
677
|
+
selectedProject.tenantId
|
|
659
678
|
);
|
|
660
|
-
|
|
679
|
+
await writeProjectContext(
|
|
661
680
|
{
|
|
662
|
-
|
|
663
|
-
name:
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
}
|
|
670
|
-
console.log("\nProvisioning API key for CLI access...");
|
|
671
|
-
const { apiKey, apiKeyId } = await provisionApiKey(
|
|
672
|
-
apiUrl,
|
|
673
|
-
credentials.accessToken,
|
|
674
|
-
selectedProject.id,
|
|
675
|
-
selectedProject.tenantId
|
|
676
|
-
);
|
|
677
|
-
await writeProjectContext(
|
|
678
|
-
{
|
|
679
|
-
id: selectedProject.id,
|
|
680
|
-
name: selectedProject.name,
|
|
681
|
-
tenantId: selectedProject.tenantId,
|
|
682
|
-
apiKey,
|
|
683
|
-
apiKeyId
|
|
684
|
-
},
|
|
685
|
-
cmdOpts.saveAs
|
|
686
|
-
);
|
|
687
|
-
console.log(`
|
|
688
|
-
\u2713 Selected project: ${selectedProject.name}`);
|
|
689
|
-
console.log("\u2713 API key provisioned for CLI access");
|
|
690
|
-
if (cmdOpts.saveAs) {
|
|
691
|
-
console.log(
|
|
692
|
-
` Saved as profile "${cmdOpts.saveAs}". Use --project ${cmdOpts.saveAs} or set as default with \`foir profiles default ${cmdOpts.saveAs}\``
|
|
681
|
+
id: selectedProject.id,
|
|
682
|
+
name: selectedProject.name,
|
|
683
|
+
tenantId: selectedProject.tenantId,
|
|
684
|
+
apiKey,
|
|
685
|
+
apiKeyId
|
|
686
|
+
},
|
|
687
|
+
cmdOpts.saveAs
|
|
693
688
|
);
|
|
694
|
-
|
|
695
|
-
|
|
689
|
+
console.log(`
|
|
690
|
+
\u2713 Selected project: ${selectedProject.name}`);
|
|
691
|
+
console.log("\u2713 API key provisioned for CLI access");
|
|
692
|
+
if (cmdOpts.saveAs) {
|
|
693
|
+
console.log(
|
|
694
|
+
` Saved as profile "${cmdOpts.saveAs}". Use --project ${cmdOpts.saveAs} or set as default with \`foir profiles default ${cmdOpts.saveAs}\``
|
|
695
|
+
);
|
|
696
|
+
} else {
|
|
697
|
+
console.log(" (stored in .foir/project.json for this repository)");
|
|
698
|
+
}
|
|
696
699
|
}
|
|
697
|
-
|
|
700
|
+
)
|
|
698
701
|
);
|
|
699
702
|
}
|
|
700
703
|
|
|
@@ -942,20 +945,7 @@ import { resolve } from "path";
|
|
|
942
945
|
import chalk4 from "chalk";
|
|
943
946
|
|
|
944
947
|
// src/config/pull-config.ts
|
|
945
|
-
var
|
|
946
|
-
var DEFAULT_DOCS_DIR = "./src/generated/documents";
|
|
947
|
-
var ALL_DOMAINS = {
|
|
948
|
-
auth: true,
|
|
949
|
-
authProviders: true,
|
|
950
|
-
files: true,
|
|
951
|
-
sync: true,
|
|
952
|
-
notifications: true,
|
|
953
|
-
operations: true,
|
|
954
|
-
schedules: true,
|
|
955
|
-
sharing: true,
|
|
956
|
-
embeddings: true,
|
|
957
|
-
analytics: true
|
|
958
|
-
};
|
|
948
|
+
var DEFAULT_OUTPUT_DIR = "./src/generated";
|
|
959
949
|
async function loadPullConfig(flags) {
|
|
960
950
|
let fileConfig = {};
|
|
961
951
|
const configPath = findConfigFile(flags.config);
|
|
@@ -963,52 +953,386 @@ async function loadPullConfig(flags) {
|
|
|
963
953
|
const full = await loadConfigFile(configPath);
|
|
964
954
|
fileConfig = full.pull ?? {};
|
|
965
955
|
}
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
const output = {
|
|
975
|
-
types,
|
|
976
|
-
documents,
|
|
977
|
-
operations,
|
|
978
|
-
...hooks ? { hooks } : {},
|
|
979
|
-
...loaders ? { loaders } : {},
|
|
980
|
-
...swift ? { swift } : {}
|
|
981
|
-
};
|
|
982
|
-
let domains;
|
|
983
|
-
if (fileConfig.domains === false) {
|
|
984
|
-
domains = {
|
|
985
|
-
auth: false,
|
|
986
|
-
authProviders: false,
|
|
987
|
-
files: false,
|
|
988
|
-
sync: false,
|
|
989
|
-
notifications: false,
|
|
990
|
-
operations: false,
|
|
991
|
-
schedules: false,
|
|
992
|
-
sharing: false,
|
|
993
|
-
embeddings: false,
|
|
994
|
-
analytics: false
|
|
995
|
-
};
|
|
996
|
-
} else if (typeof fileConfig.domains === "object") {
|
|
997
|
-
domains = { ...ALL_DOMAINS, ...fileConfig.domains };
|
|
956
|
+
let outputDir;
|
|
957
|
+
if (flags.out) {
|
|
958
|
+
outputDir = flags.out;
|
|
959
|
+
} else if (typeof fileConfig.output === "string") {
|
|
960
|
+
outputDir = fileConfig.output;
|
|
961
|
+
} else if (typeof fileConfig.output === "object" && fileConfig.output?.types) {
|
|
962
|
+
const legacyTypes = fileConfig.output.types;
|
|
963
|
+
outputDir = legacyTypes.replace(/\/types\/?$/, "") || DEFAULT_OUTPUT_DIR;
|
|
998
964
|
} else {
|
|
999
|
-
|
|
965
|
+
outputDir = DEFAULT_OUTPUT_DIR;
|
|
1000
966
|
}
|
|
1001
967
|
const only = flags.only ? flags.only.split(",").map((s) => s.trim()) : fileConfig.only ?? [];
|
|
1002
968
|
const includeInline = fileConfig.includeInline ?? true;
|
|
1003
969
|
const prettier = flags.noPrettier ? false : fileConfig.prettier ?? true;
|
|
1004
970
|
const dryRun = flags.dryRun ?? false;
|
|
1005
|
-
return {
|
|
971
|
+
return {
|
|
972
|
+
output: { types: outputDir },
|
|
973
|
+
only,
|
|
974
|
+
includeInline,
|
|
975
|
+
prettier,
|
|
976
|
+
dryRun
|
|
977
|
+
};
|
|
1006
978
|
}
|
|
1007
979
|
|
|
1008
980
|
// src/graphql/generated.ts
|
|
1009
|
-
var GetCustomerProfileSchemaDocument = {
|
|
1010
|
-
|
|
1011
|
-
|
|
981
|
+
var GetCustomerProfileSchemaDocument = {
|
|
982
|
+
kind: "Document",
|
|
983
|
+
definitions: [
|
|
984
|
+
{
|
|
985
|
+
kind: "OperationDefinition",
|
|
986
|
+
operation: "query",
|
|
987
|
+
name: { kind: "Name", value: "GetCustomerProfileSchema" },
|
|
988
|
+
selectionSet: {
|
|
989
|
+
kind: "SelectionSet",
|
|
990
|
+
selections: [
|
|
991
|
+
{
|
|
992
|
+
kind: "Field",
|
|
993
|
+
name: { kind: "Name", value: "customerProfileSchema" },
|
|
994
|
+
selectionSet: {
|
|
995
|
+
kind: "SelectionSet",
|
|
996
|
+
selections: [
|
|
997
|
+
{ kind: "Field", name: { kind: "Name", value: "id" } },
|
|
998
|
+
{
|
|
999
|
+
kind: "Field",
|
|
1000
|
+
name: { kind: "Name", value: "fields" },
|
|
1001
|
+
selectionSet: {
|
|
1002
|
+
kind: "SelectionSet",
|
|
1003
|
+
selections: [
|
|
1004
|
+
{ kind: "Field", name: { kind: "Name", value: "key" } },
|
|
1005
|
+
{ kind: "Field", name: { kind: "Name", value: "type" } },
|
|
1006
|
+
{ kind: "Field", name: { kind: "Name", value: "label" } },
|
|
1007
|
+
{
|
|
1008
|
+
kind: "Field",
|
|
1009
|
+
name: { kind: "Name", value: "required" }
|
|
1010
|
+
},
|
|
1011
|
+
{
|
|
1012
|
+
kind: "Field",
|
|
1013
|
+
name: { kind: "Name", value: "helpText" }
|
|
1014
|
+
},
|
|
1015
|
+
{
|
|
1016
|
+
kind: "Field",
|
|
1017
|
+
name: { kind: "Name", value: "defaultValue" }
|
|
1018
|
+
},
|
|
1019
|
+
{
|
|
1020
|
+
kind: "Field",
|
|
1021
|
+
name: { kind: "Name", value: "config" }
|
|
1022
|
+
},
|
|
1023
|
+
{
|
|
1024
|
+
kind: "Field",
|
|
1025
|
+
name: { kind: "Name", value: "validation" },
|
|
1026
|
+
selectionSet: {
|
|
1027
|
+
kind: "SelectionSet",
|
|
1028
|
+
selections: [
|
|
1029
|
+
{
|
|
1030
|
+
kind: "Field",
|
|
1031
|
+
name: { kind: "Name", value: "rule" }
|
|
1032
|
+
},
|
|
1033
|
+
{
|
|
1034
|
+
kind: "Field",
|
|
1035
|
+
name: { kind: "Name", value: "value" }
|
|
1036
|
+
},
|
|
1037
|
+
{
|
|
1038
|
+
kind: "Field",
|
|
1039
|
+
name: { kind: "Name", value: "message" }
|
|
1040
|
+
}
|
|
1041
|
+
]
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
1044
|
+
]
|
|
1045
|
+
}
|
|
1046
|
+
},
|
|
1047
|
+
{
|
|
1048
|
+
kind: "Field",
|
|
1049
|
+
name: { kind: "Name", value: "publicFields" }
|
|
1050
|
+
},
|
|
1051
|
+
{ kind: "Field", name: { kind: "Name", value: "version" } },
|
|
1052
|
+
{ kind: "Field", name: { kind: "Name", value: "createdAt" } },
|
|
1053
|
+
{ kind: "Field", name: { kind: "Name", value: "updatedAt" } }
|
|
1054
|
+
]
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
1057
|
+
]
|
|
1058
|
+
}
|
|
1059
|
+
}
|
|
1060
|
+
]
|
|
1061
|
+
};
|
|
1062
|
+
var ModelsForCodegenDocument = {
|
|
1063
|
+
kind: "Document",
|
|
1064
|
+
definitions: [
|
|
1065
|
+
{
|
|
1066
|
+
kind: "OperationDefinition",
|
|
1067
|
+
operation: "query",
|
|
1068
|
+
name: { kind: "Name", value: "ModelsForCodegen" },
|
|
1069
|
+
variableDefinitions: [
|
|
1070
|
+
{
|
|
1071
|
+
kind: "VariableDefinition",
|
|
1072
|
+
variable: {
|
|
1073
|
+
kind: "Variable",
|
|
1074
|
+
name: { kind: "Name", value: "search" }
|
|
1075
|
+
},
|
|
1076
|
+
type: { kind: "NamedType", name: { kind: "Name", value: "String" } }
|
|
1077
|
+
},
|
|
1078
|
+
{
|
|
1079
|
+
kind: "VariableDefinition",
|
|
1080
|
+
variable: {
|
|
1081
|
+
kind: "Variable",
|
|
1082
|
+
name: { kind: "Name", value: "limit" }
|
|
1083
|
+
},
|
|
1084
|
+
type: { kind: "NamedType", name: { kind: "Name", value: "Int" } }
|
|
1085
|
+
},
|
|
1086
|
+
{
|
|
1087
|
+
kind: "VariableDefinition",
|
|
1088
|
+
variable: {
|
|
1089
|
+
kind: "Variable",
|
|
1090
|
+
name: { kind: "Name", value: "offset" }
|
|
1091
|
+
},
|
|
1092
|
+
type: { kind: "NamedType", name: { kind: "Name", value: "Int" } }
|
|
1093
|
+
}
|
|
1094
|
+
],
|
|
1095
|
+
selectionSet: {
|
|
1096
|
+
kind: "SelectionSet",
|
|
1097
|
+
selections: [
|
|
1098
|
+
{
|
|
1099
|
+
kind: "Field",
|
|
1100
|
+
name: { kind: "Name", value: "models" },
|
|
1101
|
+
arguments: [
|
|
1102
|
+
{
|
|
1103
|
+
kind: "Argument",
|
|
1104
|
+
name: { kind: "Name", value: "search" },
|
|
1105
|
+
value: {
|
|
1106
|
+
kind: "Variable",
|
|
1107
|
+
name: { kind: "Name", value: "search" }
|
|
1108
|
+
}
|
|
1109
|
+
},
|
|
1110
|
+
{
|
|
1111
|
+
kind: "Argument",
|
|
1112
|
+
name: { kind: "Name", value: "limit" },
|
|
1113
|
+
value: {
|
|
1114
|
+
kind: "Variable",
|
|
1115
|
+
name: { kind: "Name", value: "limit" }
|
|
1116
|
+
}
|
|
1117
|
+
},
|
|
1118
|
+
{
|
|
1119
|
+
kind: "Argument",
|
|
1120
|
+
name: { kind: "Name", value: "offset" },
|
|
1121
|
+
value: {
|
|
1122
|
+
kind: "Variable",
|
|
1123
|
+
name: { kind: "Name", value: "offset" }
|
|
1124
|
+
}
|
|
1125
|
+
}
|
|
1126
|
+
],
|
|
1127
|
+
selectionSet: {
|
|
1128
|
+
kind: "SelectionSet",
|
|
1129
|
+
selections: [
|
|
1130
|
+
{
|
|
1131
|
+
kind: "Field",
|
|
1132
|
+
name: { kind: "Name", value: "items" },
|
|
1133
|
+
selectionSet: {
|
|
1134
|
+
kind: "SelectionSet",
|
|
1135
|
+
selections: [
|
|
1136
|
+
{ kind: "Field", name: { kind: "Name", value: "id" } },
|
|
1137
|
+
{ kind: "Field", name: { kind: "Name", value: "key" } },
|
|
1138
|
+
{ kind: "Field", name: { kind: "Name", value: "name" } },
|
|
1139
|
+
{
|
|
1140
|
+
kind: "Field",
|
|
1141
|
+
name: { kind: "Name", value: "pluralName" }
|
|
1142
|
+
},
|
|
1143
|
+
{
|
|
1144
|
+
kind: "Field",
|
|
1145
|
+
name: { kind: "Name", value: "description" }
|
|
1146
|
+
},
|
|
1147
|
+
{
|
|
1148
|
+
kind: "Field",
|
|
1149
|
+
name: { kind: "Name", value: "category" }
|
|
1150
|
+
},
|
|
1151
|
+
{
|
|
1152
|
+
kind: "Field",
|
|
1153
|
+
name: { kind: "Name", value: "fields" }
|
|
1154
|
+
},
|
|
1155
|
+
{
|
|
1156
|
+
kind: "Field",
|
|
1157
|
+
name: { kind: "Name", value: "config" }
|
|
1158
|
+
},
|
|
1159
|
+
{ kind: "Field", name: { kind: "Name", value: "hooks" } },
|
|
1160
|
+
{
|
|
1161
|
+
kind: "Field",
|
|
1162
|
+
name: { kind: "Name", value: "createdAt" }
|
|
1163
|
+
},
|
|
1164
|
+
{
|
|
1165
|
+
kind: "Field",
|
|
1166
|
+
name: { kind: "Name", value: "updatedAt" }
|
|
1167
|
+
}
|
|
1168
|
+
]
|
|
1169
|
+
}
|
|
1170
|
+
},
|
|
1171
|
+
{ kind: "Field", name: { kind: "Name", value: "total" } }
|
|
1172
|
+
]
|
|
1173
|
+
}
|
|
1174
|
+
}
|
|
1175
|
+
]
|
|
1176
|
+
}
|
|
1177
|
+
}
|
|
1178
|
+
]
|
|
1179
|
+
};
|
|
1180
|
+
var GlobalSearchDocument = {
|
|
1181
|
+
kind: "Document",
|
|
1182
|
+
definitions: [
|
|
1183
|
+
{
|
|
1184
|
+
kind: "OperationDefinition",
|
|
1185
|
+
operation: "query",
|
|
1186
|
+
name: { kind: "Name", value: "GlobalSearch" },
|
|
1187
|
+
variableDefinitions: [
|
|
1188
|
+
{
|
|
1189
|
+
kind: "VariableDefinition",
|
|
1190
|
+
variable: {
|
|
1191
|
+
kind: "Variable",
|
|
1192
|
+
name: { kind: "Name", value: "query" }
|
|
1193
|
+
},
|
|
1194
|
+
type: {
|
|
1195
|
+
kind: "NonNullType",
|
|
1196
|
+
type: {
|
|
1197
|
+
kind: "NamedType",
|
|
1198
|
+
name: { kind: "Name", value: "String" }
|
|
1199
|
+
}
|
|
1200
|
+
}
|
|
1201
|
+
},
|
|
1202
|
+
{
|
|
1203
|
+
kind: "VariableDefinition",
|
|
1204
|
+
variable: {
|
|
1205
|
+
kind: "Variable",
|
|
1206
|
+
name: { kind: "Name", value: "limit" }
|
|
1207
|
+
},
|
|
1208
|
+
type: { kind: "NamedType", name: { kind: "Name", value: "Int" } }
|
|
1209
|
+
},
|
|
1210
|
+
{
|
|
1211
|
+
kind: "VariableDefinition",
|
|
1212
|
+
variable: {
|
|
1213
|
+
kind: "Variable",
|
|
1214
|
+
name: { kind: "Name", value: "modelKeys" }
|
|
1215
|
+
},
|
|
1216
|
+
type: {
|
|
1217
|
+
kind: "ListType",
|
|
1218
|
+
type: {
|
|
1219
|
+
kind: "NonNullType",
|
|
1220
|
+
type: {
|
|
1221
|
+
kind: "NamedType",
|
|
1222
|
+
name: { kind: "Name", value: "String" }
|
|
1223
|
+
}
|
|
1224
|
+
}
|
|
1225
|
+
}
|
|
1226
|
+
},
|
|
1227
|
+
{
|
|
1228
|
+
kind: "VariableDefinition",
|
|
1229
|
+
variable: {
|
|
1230
|
+
kind: "Variable",
|
|
1231
|
+
name: { kind: "Name", value: "includeMedia" }
|
|
1232
|
+
},
|
|
1233
|
+
type: { kind: "NamedType", name: { kind: "Name", value: "Boolean" } }
|
|
1234
|
+
}
|
|
1235
|
+
],
|
|
1236
|
+
selectionSet: {
|
|
1237
|
+
kind: "SelectionSet",
|
|
1238
|
+
selections: [
|
|
1239
|
+
{
|
|
1240
|
+
kind: "Field",
|
|
1241
|
+
name: { kind: "Name", value: "globalSearch" },
|
|
1242
|
+
arguments: [
|
|
1243
|
+
{
|
|
1244
|
+
kind: "Argument",
|
|
1245
|
+
name: { kind: "Name", value: "query" },
|
|
1246
|
+
value: {
|
|
1247
|
+
kind: "Variable",
|
|
1248
|
+
name: { kind: "Name", value: "query" }
|
|
1249
|
+
}
|
|
1250
|
+
},
|
|
1251
|
+
{
|
|
1252
|
+
kind: "Argument",
|
|
1253
|
+
name: { kind: "Name", value: "limit" },
|
|
1254
|
+
value: {
|
|
1255
|
+
kind: "Variable",
|
|
1256
|
+
name: { kind: "Name", value: "limit" }
|
|
1257
|
+
}
|
|
1258
|
+
},
|
|
1259
|
+
{
|
|
1260
|
+
kind: "Argument",
|
|
1261
|
+
name: { kind: "Name", value: "modelKeys" },
|
|
1262
|
+
value: {
|
|
1263
|
+
kind: "Variable",
|
|
1264
|
+
name: { kind: "Name", value: "modelKeys" }
|
|
1265
|
+
}
|
|
1266
|
+
},
|
|
1267
|
+
{
|
|
1268
|
+
kind: "Argument",
|
|
1269
|
+
name: { kind: "Name", value: "includeMedia" },
|
|
1270
|
+
value: {
|
|
1271
|
+
kind: "Variable",
|
|
1272
|
+
name: { kind: "Name", value: "includeMedia" }
|
|
1273
|
+
}
|
|
1274
|
+
}
|
|
1275
|
+
],
|
|
1276
|
+
selectionSet: {
|
|
1277
|
+
kind: "SelectionSet",
|
|
1278
|
+
selections: [
|
|
1279
|
+
{
|
|
1280
|
+
kind: "Field",
|
|
1281
|
+
name: { kind: "Name", value: "records" },
|
|
1282
|
+
selectionSet: {
|
|
1283
|
+
kind: "SelectionSet",
|
|
1284
|
+
selections: [
|
|
1285
|
+
{ kind: "Field", name: { kind: "Name", value: "id" } },
|
|
1286
|
+
{
|
|
1287
|
+
kind: "Field",
|
|
1288
|
+
name: { kind: "Name", value: "modelKey" }
|
|
1289
|
+
},
|
|
1290
|
+
{ kind: "Field", name: { kind: "Name", value: "title" } },
|
|
1291
|
+
{
|
|
1292
|
+
kind: "Field",
|
|
1293
|
+
name: { kind: "Name", value: "naturalKey" }
|
|
1294
|
+
},
|
|
1295
|
+
{
|
|
1296
|
+
kind: "Field",
|
|
1297
|
+
name: { kind: "Name", value: "subtitle" }
|
|
1298
|
+
},
|
|
1299
|
+
{
|
|
1300
|
+
kind: "Field",
|
|
1301
|
+
name: { kind: "Name", value: "updatedAt" }
|
|
1302
|
+
}
|
|
1303
|
+
]
|
|
1304
|
+
}
|
|
1305
|
+
},
|
|
1306
|
+
{
|
|
1307
|
+
kind: "Field",
|
|
1308
|
+
name: { kind: "Name", value: "media" },
|
|
1309
|
+
selectionSet: {
|
|
1310
|
+
kind: "SelectionSet",
|
|
1311
|
+
selections: [
|
|
1312
|
+
{ kind: "Field", name: { kind: "Name", value: "id" } },
|
|
1313
|
+
{
|
|
1314
|
+
kind: "Field",
|
|
1315
|
+
name: { kind: "Name", value: "fileName" }
|
|
1316
|
+
},
|
|
1317
|
+
{
|
|
1318
|
+
kind: "Field",
|
|
1319
|
+
name: { kind: "Name", value: "altText" }
|
|
1320
|
+
},
|
|
1321
|
+
{
|
|
1322
|
+
kind: "Field",
|
|
1323
|
+
name: { kind: "Name", value: "fileUrl" }
|
|
1324
|
+
}
|
|
1325
|
+
]
|
|
1326
|
+
}
|
|
1327
|
+
}
|
|
1328
|
+
]
|
|
1329
|
+
}
|
|
1330
|
+
}
|
|
1331
|
+
]
|
|
1332
|
+
}
|
|
1333
|
+
}
|
|
1334
|
+
]
|
|
1335
|
+
};
|
|
1012
1336
|
|
|
1013
1337
|
// src/codegen/fetch-models.ts
|
|
1014
1338
|
function normalizeConfig(raw) {
|
|
@@ -1252,10 +1576,6 @@ function toPascalCase(str) {
|
|
|
1252
1576
|
const camel = toCamelCase(str);
|
|
1253
1577
|
return camel.charAt(0).toUpperCase() + camel.slice(1);
|
|
1254
1578
|
}
|
|
1255
|
-
function toUpperSnakeCase(str) {
|
|
1256
|
-
if (!str) return "UNKNOWN";
|
|
1257
|
-
return str.replace(/([a-z])([A-Z])/g, "$1_$2").replace(/[-\s]+/g, "_").toUpperCase();
|
|
1258
|
-
}
|
|
1259
1579
|
function sanitizeFieldName(key) {
|
|
1260
1580
|
if (!key) return "unknown";
|
|
1261
1581
|
const camel = toCamelCase(key);
|
|
@@ -1317,438 +1637,6 @@ function generateFieldDef(field) {
|
|
|
1317
1637
|
return `{ ${parts.join(", ")} }`;
|
|
1318
1638
|
}
|
|
1319
1639
|
|
|
1320
|
-
// src/codegen/generators/field-types.ts
|
|
1321
|
-
function generateFieldTypesFile() {
|
|
1322
|
-
return `/**
|
|
1323
|
-
* Field Types and Definitions
|
|
1324
|
-
*
|
|
1325
|
-
* Value types, filter types, and field definition types.
|
|
1326
|
-
*
|
|
1327
|
-
* @generated by foir \u2014 DO NOT EDIT MANUALLY
|
|
1328
|
-
*/
|
|
1329
|
-
|
|
1330
|
-
// =============================================================================
|
|
1331
|
-
// JSON-SAFE BASE TYPE
|
|
1332
|
-
// =============================================================================
|
|
1333
|
-
|
|
1334
|
-
/** Recursive JSON-serializable value type. Safe for TanStack Router, Remix, RSC, and other serialization boundaries. */
|
|
1335
|
-
export type JsonValue = string | number | boolean | null | JsonValue[] | { [key: string]: JsonValue };
|
|
1336
|
-
|
|
1337
|
-
// =============================================================================
|
|
1338
|
-
// VALUE TYPES
|
|
1339
|
-
// =============================================================================
|
|
1340
|
-
|
|
1341
|
-
/** Rich text content (Lexical JSON format) */
|
|
1342
|
-
export type RichtextValue = JsonValue;
|
|
1343
|
-
|
|
1344
|
-
/** Currency value with amount and ISO 4217 code */
|
|
1345
|
-
export interface CurrencyValue {
|
|
1346
|
-
amount: number;
|
|
1347
|
-
currency: string;
|
|
1348
|
-
}
|
|
1349
|
-
|
|
1350
|
-
/** Image reference with metadata */
|
|
1351
|
-
export interface ImageValue {
|
|
1352
|
-
id: string;
|
|
1353
|
-
url: string;
|
|
1354
|
-
alt?: string;
|
|
1355
|
-
width?: number;
|
|
1356
|
-
height?: number;
|
|
1357
|
-
}
|
|
1358
|
-
|
|
1359
|
-
/** Video reference with metadata */
|
|
1360
|
-
export interface VideoValue {
|
|
1361
|
-
id: string;
|
|
1362
|
-
url: string;
|
|
1363
|
-
thumbnail?: string;
|
|
1364
|
-
duration?: number;
|
|
1365
|
-
}
|
|
1366
|
-
|
|
1367
|
-
/** File reference with metadata */
|
|
1368
|
-
export interface FileValue {
|
|
1369
|
-
id: string;
|
|
1370
|
-
url: string;
|
|
1371
|
-
name: string;
|
|
1372
|
-
size: number;
|
|
1373
|
-
mimeType: string;
|
|
1374
|
-
}
|
|
1375
|
-
|
|
1376
|
-
/** Link value (internal reference or external URL) */
|
|
1377
|
-
export interface LinkValue {
|
|
1378
|
-
type: 'entity' | 'url';
|
|
1379
|
-
entity?: LinkRecordReference;
|
|
1380
|
-
url?: string;
|
|
1381
|
-
target?: '_self' | '_blank';
|
|
1382
|
-
}
|
|
1383
|
-
|
|
1384
|
-
/** Record reference for internal links */
|
|
1385
|
-
export interface LinkRecordReference {
|
|
1386
|
-
modelKey: string;
|
|
1387
|
-
naturalKey: string;
|
|
1388
|
-
}
|
|
1389
|
-
|
|
1390
|
-
/** Record reference value (generic TPreview for typed preview data when reference target is known) */
|
|
1391
|
-
export interface ReferenceValue<TPreview = Record<string, JsonValue>> {
|
|
1392
|
-
_type: 'reference';
|
|
1393
|
-
_schema: string;
|
|
1394
|
-
naturalKey: string;
|
|
1395
|
-
_preview?: TPreview;
|
|
1396
|
-
}
|
|
1397
|
-
|
|
1398
|
-
/** Composite/inline value */
|
|
1399
|
-
export interface CompositeValue {
|
|
1400
|
-
_type: 'composite';
|
|
1401
|
-
_schema: string;
|
|
1402
|
-
fields: Record<string, JsonValue>;
|
|
1403
|
-
}
|
|
1404
|
-
|
|
1405
|
-
/** A single item in a flexible field array */
|
|
1406
|
-
export interface FlexibleFieldItem {
|
|
1407
|
-
_id: string;
|
|
1408
|
-
_key: string;
|
|
1409
|
-
_type: string;
|
|
1410
|
-
_label: string;
|
|
1411
|
-
_required?: boolean;
|
|
1412
|
-
_helpText?: string;
|
|
1413
|
-
_config?: Record<string, JsonValue>;
|
|
1414
|
-
value: JsonValue;
|
|
1415
|
-
}
|
|
1416
|
-
|
|
1417
|
-
// =============================================================================
|
|
1418
|
-
// FILTER TYPES
|
|
1419
|
-
// =============================================================================
|
|
1420
|
-
|
|
1421
|
-
export interface TextFilter {
|
|
1422
|
-
eq?: string;
|
|
1423
|
-
ne?: string;
|
|
1424
|
-
contains?: string;
|
|
1425
|
-
startsWith?: string;
|
|
1426
|
-
endsWith?: string;
|
|
1427
|
-
in?: string[];
|
|
1428
|
-
notIn?: string[];
|
|
1429
|
-
isNull?: boolean;
|
|
1430
|
-
}
|
|
1431
|
-
|
|
1432
|
-
export interface NumberFilter {
|
|
1433
|
-
eq?: number;
|
|
1434
|
-
ne?: number;
|
|
1435
|
-
gt?: number;
|
|
1436
|
-
gte?: number;
|
|
1437
|
-
lt?: number;
|
|
1438
|
-
lte?: number;
|
|
1439
|
-
in?: number[];
|
|
1440
|
-
notIn?: number[];
|
|
1441
|
-
isNull?: boolean;
|
|
1442
|
-
}
|
|
1443
|
-
|
|
1444
|
-
export interface BooleanFilter {
|
|
1445
|
-
eq?: boolean;
|
|
1446
|
-
ne?: boolean;
|
|
1447
|
-
isNull?: boolean;
|
|
1448
|
-
}
|
|
1449
|
-
|
|
1450
|
-
export interface DateFilter {
|
|
1451
|
-
eq?: string;
|
|
1452
|
-
ne?: string;
|
|
1453
|
-
gt?: string;
|
|
1454
|
-
gte?: string;
|
|
1455
|
-
lt?: string;
|
|
1456
|
-
lte?: string;
|
|
1457
|
-
isNull?: boolean;
|
|
1458
|
-
}
|
|
1459
|
-
|
|
1460
|
-
export interface SelectFilter<T extends string = string> {
|
|
1461
|
-
eq?: T;
|
|
1462
|
-
ne?: T;
|
|
1463
|
-
in?: T[];
|
|
1464
|
-
notIn?: T[];
|
|
1465
|
-
isNull?: boolean;
|
|
1466
|
-
}
|
|
1467
|
-
|
|
1468
|
-
export interface MultiselectFilter<T extends string = string> {
|
|
1469
|
-
contains?: T;
|
|
1470
|
-
containsAny?: T[];
|
|
1471
|
-
containsAll?: T[];
|
|
1472
|
-
isNull?: boolean;
|
|
1473
|
-
}
|
|
1474
|
-
|
|
1475
|
-
export interface ReferenceFilter {
|
|
1476
|
-
eq?: string;
|
|
1477
|
-
ne?: string;
|
|
1478
|
-
in?: string[];
|
|
1479
|
-
notIn?: string[];
|
|
1480
|
-
isNull?: boolean;
|
|
1481
|
-
}
|
|
1482
|
-
|
|
1483
|
-
export interface FilterInput {
|
|
1484
|
-
field: string;
|
|
1485
|
-
operator: string;
|
|
1486
|
-
value: JsonValue;
|
|
1487
|
-
}
|
|
1488
|
-
|
|
1489
|
-
export interface SortInput {
|
|
1490
|
-
field: string;
|
|
1491
|
-
direction: 'ASC' | 'DESC';
|
|
1492
|
-
}
|
|
1493
|
-
|
|
1494
|
-
// =============================================================================
|
|
1495
|
-
// RESOLVE TYPES
|
|
1496
|
-
// =============================================================================
|
|
1497
|
-
|
|
1498
|
-
/** Variant context for record resolution */
|
|
1499
|
-
export interface VariantContext {
|
|
1500
|
-
locale?: string;
|
|
1501
|
-
device?: string;
|
|
1502
|
-
region?: string;
|
|
1503
|
-
contexts?: Record<string, JsonValue>;
|
|
1504
|
-
}
|
|
1505
|
-
|
|
1506
|
-
/** Reference resolution options */
|
|
1507
|
-
export interface ReferenceResolutionOptions {
|
|
1508
|
-
maxDepth?: number;
|
|
1509
|
-
resolveMedia?: boolean;
|
|
1510
|
-
resolveReferences?: boolean;
|
|
1511
|
-
}
|
|
1512
|
-
|
|
1513
|
-
/** Resolved record metadata */
|
|
1514
|
-
export interface ResolvedRecord {
|
|
1515
|
-
id: string;
|
|
1516
|
-
modelKey: string;
|
|
1517
|
-
naturalKey: string | null;
|
|
1518
|
-
metadata?: Record<string, JsonValue>;
|
|
1519
|
-
}
|
|
1520
|
-
|
|
1521
|
-
/** Resolved variant info */
|
|
1522
|
-
export interface ResolvedVariant {
|
|
1523
|
-
id: string;
|
|
1524
|
-
variantKey: string;
|
|
1525
|
-
}
|
|
1526
|
-
|
|
1527
|
-
/** Resolved field with value */
|
|
1528
|
-
export interface ResolvedField {
|
|
1529
|
-
key: string;
|
|
1530
|
-
type: string;
|
|
1531
|
-
label?: string;
|
|
1532
|
-
required?: boolean;
|
|
1533
|
-
value: JsonValue;
|
|
1534
|
-
}
|
|
1535
|
-
|
|
1536
|
-
/** Resolved content */
|
|
1537
|
-
export interface ResolvedContent {
|
|
1538
|
-
fields: ResolvedField[];
|
|
1539
|
-
}
|
|
1540
|
-
|
|
1541
|
-
/** Resolution context output */
|
|
1542
|
-
export interface ResolutionContext {
|
|
1543
|
-
locale: string;
|
|
1544
|
-
contexts: Record<string, JsonValue>;
|
|
1545
|
-
}
|
|
1546
|
-
|
|
1547
|
-
/** Base resolved record content */
|
|
1548
|
-
export interface ResolvedRecordContentBase {
|
|
1549
|
-
record: ResolvedRecord;
|
|
1550
|
-
variant: ResolvedVariant;
|
|
1551
|
-
content: ResolvedContent;
|
|
1552
|
-
resolvedWith: ResolutionContext;
|
|
1553
|
-
}
|
|
1554
|
-
|
|
1555
|
-
// =============================================================================
|
|
1556
|
-
// FIELD DEFINITION TYPES
|
|
1557
|
-
// =============================================================================
|
|
1558
|
-
|
|
1559
|
-
export interface BaseFieldDef {
|
|
1560
|
-
key: string;
|
|
1561
|
-
label: string;
|
|
1562
|
-
required?: boolean;
|
|
1563
|
-
helpText?: string;
|
|
1564
|
-
defaultValue?: JsonValue;
|
|
1565
|
-
}
|
|
1566
|
-
|
|
1567
|
-
export interface TextFieldDef extends BaseFieldDef {
|
|
1568
|
-
type: 'text';
|
|
1569
|
-
maxLength?: number;
|
|
1570
|
-
minLength?: number;
|
|
1571
|
-
pattern?: string;
|
|
1572
|
-
}
|
|
1573
|
-
|
|
1574
|
-
export interface NumberFieldDef extends BaseFieldDef {
|
|
1575
|
-
type: 'number';
|
|
1576
|
-
min?: number;
|
|
1577
|
-
max?: number;
|
|
1578
|
-
step?: number;
|
|
1579
|
-
}
|
|
1580
|
-
|
|
1581
|
-
export interface BooleanFieldDef extends BaseFieldDef {
|
|
1582
|
-
type: 'boolean';
|
|
1583
|
-
}
|
|
1584
|
-
|
|
1585
|
-
export interface DateFieldDef extends BaseFieldDef {
|
|
1586
|
-
type: 'date';
|
|
1587
|
-
}
|
|
1588
|
-
|
|
1589
|
-
export interface RichtextFieldDef extends BaseFieldDef {
|
|
1590
|
-
type: 'richtext';
|
|
1591
|
-
}
|
|
1592
|
-
|
|
1593
|
-
export interface ImageFieldDef extends BaseFieldDef {
|
|
1594
|
-
type: 'image';
|
|
1595
|
-
allowedTypes?: string[];
|
|
1596
|
-
maxSize?: number;
|
|
1597
|
-
}
|
|
1598
|
-
|
|
1599
|
-
export interface VideoFieldDef extends BaseFieldDef {
|
|
1600
|
-
type: 'video';
|
|
1601
|
-
allowedTypes?: string[];
|
|
1602
|
-
maxSize?: number;
|
|
1603
|
-
}
|
|
1604
|
-
|
|
1605
|
-
export interface FileFieldDef extends BaseFieldDef {
|
|
1606
|
-
type: 'file';
|
|
1607
|
-
allowedTypes?: string[];
|
|
1608
|
-
maxSize?: number;
|
|
1609
|
-
}
|
|
1610
|
-
|
|
1611
|
-
export interface SelectFieldDef extends BaseFieldDef {
|
|
1612
|
-
type: 'select';
|
|
1613
|
-
options: Array<{ label: string; value: string }>;
|
|
1614
|
-
}
|
|
1615
|
-
|
|
1616
|
-
export interface MultiselectFieldDef extends BaseFieldDef {
|
|
1617
|
-
type: 'multiselect';
|
|
1618
|
-
options: Array<{ label: string; value: string }>;
|
|
1619
|
-
}
|
|
1620
|
-
|
|
1621
|
-
export interface LinkFieldDef extends BaseFieldDef {
|
|
1622
|
-
type: 'link';
|
|
1623
|
-
}
|
|
1624
|
-
|
|
1625
|
-
export interface ReferenceFieldDef extends BaseFieldDef {
|
|
1626
|
-
type: 'reference';
|
|
1627
|
-
referenceTypes?: string[];
|
|
1628
|
-
multiple?: boolean;
|
|
1629
|
-
}
|
|
1630
|
-
|
|
1631
|
-
export interface ListFieldDef extends BaseFieldDef {
|
|
1632
|
-
type: 'list';
|
|
1633
|
-
itemType?: string;
|
|
1634
|
-
minItems?: number;
|
|
1635
|
-
maxItems?: number;
|
|
1636
|
-
}
|
|
1637
|
-
|
|
1638
|
-
export interface JsonFieldDef extends BaseFieldDef {
|
|
1639
|
-
type: 'json';
|
|
1640
|
-
}
|
|
1641
|
-
|
|
1642
|
-
export interface FlexibleFieldDef extends BaseFieldDef {
|
|
1643
|
-
type: 'flexible';
|
|
1644
|
-
}
|
|
1645
|
-
|
|
1646
|
-
/** Field def for inline model types (type is the model's key, e.g. 'seo', 'hero-banner') */
|
|
1647
|
-
export interface InlineModelFieldDef extends BaseFieldDef {
|
|
1648
|
-
type: string;
|
|
1649
|
-
}
|
|
1650
|
-
|
|
1651
|
-
export type FieldDef =
|
|
1652
|
-
| TextFieldDef
|
|
1653
|
-
| NumberFieldDef
|
|
1654
|
-
| BooleanFieldDef
|
|
1655
|
-
| DateFieldDef
|
|
1656
|
-
| RichtextFieldDef
|
|
1657
|
-
| ImageFieldDef
|
|
1658
|
-
| VideoFieldDef
|
|
1659
|
-
| FileFieldDef
|
|
1660
|
-
| SelectFieldDef
|
|
1661
|
-
| MultiselectFieldDef
|
|
1662
|
-
| LinkFieldDef
|
|
1663
|
-
| ReferenceFieldDef
|
|
1664
|
-
| ListFieldDef
|
|
1665
|
-
| JsonFieldDef
|
|
1666
|
-
| FlexibleFieldDef
|
|
1667
|
-
| InlineModelFieldDef;
|
|
1668
|
-
`;
|
|
1669
|
-
}
|
|
1670
|
-
|
|
1671
|
-
// src/codegen/generators/config.ts
|
|
1672
|
-
function generateConfigFile() {
|
|
1673
|
-
return `/**
|
|
1674
|
-
* Model Configuration Type
|
|
1675
|
-
*
|
|
1676
|
-
* Strongly-typed model definitions for the unified data layer.
|
|
1677
|
-
*
|
|
1678
|
-
* @generated by foir \u2014 DO NOT EDIT MANUALLY
|
|
1679
|
-
*/
|
|
1680
|
-
|
|
1681
|
-
import type { FieldDef, JsonValue } from './field-types.js';
|
|
1682
|
-
|
|
1683
|
-
/**
|
|
1684
|
-
* Model configuration
|
|
1685
|
-
*
|
|
1686
|
-
* Defines the complete configuration for a model including
|
|
1687
|
-
* its schema, capabilities, and lifecycle hooks.
|
|
1688
|
-
*
|
|
1689
|
-
* @example
|
|
1690
|
-
* export const blogPostConfig = {
|
|
1691
|
-
* key: 'blog-post',
|
|
1692
|
-
* name: 'Blog Post',
|
|
1693
|
-
* records: true,
|
|
1694
|
-
* inline: false,
|
|
1695
|
-
* publicApi: true,
|
|
1696
|
-
* versioning: true,
|
|
1697
|
-
* publishing: true,
|
|
1698
|
-
* variants: false,
|
|
1699
|
-
* customerScoped: false,
|
|
1700
|
-
* fieldDefs: [
|
|
1701
|
-
* { key: 'title', type: 'text', label: 'Title', required: true },
|
|
1702
|
-
* { key: 'content', type: 'richtext', label: 'Content' },
|
|
1703
|
-
* ],
|
|
1704
|
-
* } as const satisfies ModelConfig;
|
|
1705
|
-
*/
|
|
1706
|
-
export interface ModelConfig {
|
|
1707
|
-
/** Unique identifier (kebab-case) */
|
|
1708
|
-
key: string;
|
|
1709
|
-
/** Display name */
|
|
1710
|
-
name: string;
|
|
1711
|
-
/** Description */
|
|
1712
|
-
description?: string;
|
|
1713
|
-
|
|
1714
|
-
// Capability flags (from model config)
|
|
1715
|
-
/** Can create standalone records */
|
|
1716
|
-
records: boolean;
|
|
1717
|
-
/** Available as inline field type in other models */
|
|
1718
|
-
inline: boolean;
|
|
1719
|
-
/** Exposed via public GraphQL API */
|
|
1720
|
-
publicApi: boolean;
|
|
1721
|
-
/** Version history enabled */
|
|
1722
|
-
versioning: boolean;
|
|
1723
|
-
/** Publishing workflow enabled */
|
|
1724
|
-
publishing: boolean;
|
|
1725
|
-
/** Market/device/locale variants enabled */
|
|
1726
|
-
variants: boolean;
|
|
1727
|
-
/** Customer-level record isolation */
|
|
1728
|
-
customerScoped: boolean;
|
|
1729
|
-
|
|
1730
|
-
/** Embedding configuration */
|
|
1731
|
-
embeddings?: {
|
|
1732
|
-
enabled: boolean;
|
|
1733
|
-
fields: Array<{ fieldPath: string; weight?: number }>;
|
|
1734
|
-
};
|
|
1735
|
-
|
|
1736
|
-
/** Lifecycle hooks configuration */
|
|
1737
|
-
hooks?: Record<string, JsonValue>;
|
|
1738
|
-
|
|
1739
|
-
/** Field definitions */
|
|
1740
|
-
fieldDefs: readonly FieldDef[];
|
|
1741
|
-
}
|
|
1742
|
-
|
|
1743
|
-
/**
|
|
1744
|
-
* Helper to create a type-safe model config
|
|
1745
|
-
*/
|
|
1746
|
-
export function defineModel<T extends ModelConfig>(config: T): T {
|
|
1747
|
-
return config;
|
|
1748
|
-
}
|
|
1749
|
-
`;
|
|
1750
|
-
}
|
|
1751
|
-
|
|
1752
1640
|
// src/codegen/generators/model-types.ts
|
|
1753
1641
|
function isInlineOnlyModel(model) {
|
|
1754
1642
|
return model.config.inline && !model.config.records;
|
|
@@ -1778,12 +1666,9 @@ function generateModelTypes(model, allModels) {
|
|
|
1778
1666
|
}
|
|
1779
1667
|
function buildImportStatements(model, fieldTypeImports, inlineSchemaRefs, referenceModelRefs, allModels) {
|
|
1780
1668
|
const imports = [];
|
|
1781
|
-
if (!isInlineOnlyModel(model)) {
|
|
1782
|
-
imports.push("import type { ModelConfig } from '../config.js';");
|
|
1783
|
-
}
|
|
1784
1669
|
if (fieldTypeImports.size > 0) {
|
|
1785
1670
|
const types = Array.from(fieldTypeImports).sort().join(", ");
|
|
1786
|
-
imports.push(`import type { ${types} } from '
|
|
1671
|
+
imports.push(`import type { ${types} } from '@eide/foir-client';`);
|
|
1787
1672
|
}
|
|
1788
1673
|
const allModelRefKeys = /* @__PURE__ */ new Set([
|
|
1789
1674
|
...inlineSchemaRefs,
|
|
@@ -1851,7 +1736,7 @@ function generateConfigObject(model, fields, configName) {
|
|
|
1851
1736
|
}
|
|
1852
1737
|
lines.push(" ],");
|
|
1853
1738
|
}
|
|
1854
|
-
lines.push("} as const
|
|
1739
|
+
lines.push("} as const;");
|
|
1855
1740
|
return lines.join("\n") + "\n";
|
|
1856
1741
|
}
|
|
1857
1742
|
function generateDataInterface(model, fields, interfaceName, allModels) {
|
|
@@ -1940,2563 +1825,499 @@ function generateModelIndex(models) {
|
|
|
1940
1825
|
`;
|
|
1941
1826
|
code += `export type { ${typeName}Data } from './${model.key}.js';
|
|
1942
1827
|
`;
|
|
1828
|
+
if (model.config.publicApi) {
|
|
1829
|
+
code += `export type { ${typeName}Where, ${typeName}SortField } from './${model.key}.filters.js';
|
|
1830
|
+
`;
|
|
1831
|
+
}
|
|
1943
1832
|
}
|
|
1944
1833
|
}
|
|
1945
1834
|
return code;
|
|
1946
1835
|
}
|
|
1947
1836
|
|
|
1948
|
-
// src/codegen/generators/
|
|
1949
|
-
function
|
|
1950
|
-
const
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
return `# Generated GraphQL operations for ${displayName}
|
|
1954
|
-
# @generated by foir \u2014 DO NOT EDIT MANUALLY
|
|
1955
|
-
|
|
1956
|
-
fragment ${typeName}Fields on Record {
|
|
1957
|
-
id
|
|
1958
|
-
modelKey
|
|
1959
|
-
naturalKey
|
|
1960
|
-
data
|
|
1961
|
-
metadata
|
|
1962
|
-
publishedVersionNumber
|
|
1963
|
-
publishedAt
|
|
1964
|
-
versionNumber
|
|
1965
|
-
changeDescription
|
|
1966
|
-
createdAt
|
|
1967
|
-
updatedAt
|
|
1968
|
-
}
|
|
1969
|
-
|
|
1970
|
-
query Get${typeName}($id: ID!, $locale: String, $preview: Boolean, $fields: FieldSelectionInput) {
|
|
1971
|
-
record(id: $id) {
|
|
1972
|
-
...${typeName}Fields
|
|
1973
|
-
resolved(locale: $locale, preview: $preview, fields: $fields) {
|
|
1974
|
-
content
|
|
1975
|
-
record { id modelKey naturalKey }
|
|
1976
|
-
version { id versionNumber }
|
|
1977
|
-
}
|
|
1978
|
-
}
|
|
1979
|
-
}
|
|
1980
|
-
|
|
1981
|
-
query Get${typeName}ByKey($naturalKey: String!, $locale: String, $preview: Boolean, $fields: FieldSelectionInput) {
|
|
1982
|
-
recordByKey(modelKey: "${model.key}", naturalKey: $naturalKey) {
|
|
1983
|
-
...${typeName}Fields
|
|
1984
|
-
resolved(locale: $locale, preview: $preview, fields: $fields) {
|
|
1985
|
-
content
|
|
1986
|
-
record { id modelKey naturalKey }
|
|
1987
|
-
version { id versionNumber }
|
|
1988
|
-
}
|
|
1989
|
-
}
|
|
1990
|
-
}
|
|
1991
|
-
|
|
1992
|
-
query List${pluralName}(
|
|
1993
|
-
$limit: Int
|
|
1994
|
-
$offset: Int
|
|
1995
|
-
$filters: [FilterInput!]
|
|
1996
|
-
$sort: SortInput
|
|
1997
|
-
$locale: String
|
|
1998
|
-
$preview: Boolean
|
|
1999
|
-
$fields: FieldSelectionInput
|
|
2000
|
-
) {
|
|
2001
|
-
records(
|
|
2002
|
-
modelKey: "${model.key}"
|
|
2003
|
-
limit: $limit
|
|
2004
|
-
offset: $offset
|
|
2005
|
-
filters: $filters
|
|
2006
|
-
sort: $sort
|
|
2007
|
-
) {
|
|
2008
|
-
items {
|
|
2009
|
-
...${typeName}Fields
|
|
2010
|
-
resolved(locale: $locale, preview: $preview, fields: $fields) {
|
|
2011
|
-
content
|
|
2012
|
-
record { id modelKey naturalKey }
|
|
2013
|
-
version { id versionNumber }
|
|
2014
|
-
}
|
|
2015
|
-
}
|
|
2016
|
-
total
|
|
2017
|
-
}
|
|
2018
|
-
}
|
|
2019
|
-
|
|
2020
|
-
mutation Create${typeName}($input: CreateRecordInput!) {
|
|
2021
|
-
createRecord(input: $input) {
|
|
2022
|
-
record {
|
|
2023
|
-
...${typeName}Fields
|
|
2024
|
-
}
|
|
2025
|
-
}
|
|
2026
|
-
}
|
|
2027
|
-
|
|
2028
|
-
mutation Update${typeName}($input: UpdateRecordInput!) {
|
|
2029
|
-
updateRecord(input: $input) {
|
|
2030
|
-
record {
|
|
2031
|
-
...${typeName}Fields
|
|
2032
|
-
}
|
|
2033
|
-
matched
|
|
2034
|
-
}
|
|
2035
|
-
}
|
|
2036
|
-
|
|
2037
|
-
mutation Delete${typeName}($id: ID!) {
|
|
2038
|
-
deleteRecord(id: $id) {
|
|
2039
|
-
id
|
|
2040
|
-
}
|
|
2041
|
-
}
|
|
2042
|
-
|
|
2043
|
-
mutation Publish${typeName}Version($versionId: ID!) {
|
|
2044
|
-
publishVersion(versionId: $versionId)
|
|
2045
|
-
}
|
|
2046
|
-
|
|
2047
|
-
mutation Unpublish${typeName}($id: ID!) {
|
|
2048
|
-
unpublishRecord(id: $id)
|
|
2049
|
-
}
|
|
2050
|
-
${model.config.sharing?.enabled ? generateSharingOperations(model.key, typeName, pluralName) : ""}`;
|
|
2051
|
-
}
|
|
2052
|
-
function generateSharedFragments() {
|
|
2053
|
-
return `# Shared fragments used across multiple model documents
|
|
2054
|
-
# @generated by foir \u2014 DO NOT EDIT MANUALLY
|
|
2055
|
-
|
|
2056
|
-
fragment ShareFields on Share {
|
|
2057
|
-
id
|
|
2058
|
-
resourceType
|
|
2059
|
-
recordId
|
|
2060
|
-
fileId
|
|
2061
|
-
permission
|
|
2062
|
-
status
|
|
2063
|
-
sharedWithCustomerId
|
|
2064
|
-
acceptedAt
|
|
2065
|
-
declinedAt
|
|
2066
|
-
expiresAt
|
|
2067
|
-
createdAt
|
|
2068
|
-
createdBy
|
|
2069
|
-
revokedAt
|
|
2070
|
-
}
|
|
2071
|
-
`;
|
|
2072
|
-
}
|
|
2073
|
-
function generateSharingOperations(modelKey, typeName, pluralName) {
|
|
2074
|
-
return `# Sharing operations
|
|
2075
|
-
|
|
2076
|
-
mutation Share${typeName}($recordId: ID!, $sharedWithCustomerId: ID!, $permission: SharePermission!) {
|
|
2077
|
-
shareRecord(recordId: $recordId, sharedWithCustomerId: $sharedWithCustomerId, permission: $permission) {
|
|
2078
|
-
...ShareFields
|
|
2079
|
-
}
|
|
2080
|
-
}
|
|
2081
|
-
|
|
2082
|
-
mutation Accept${typeName}Share($shareId: ID!) {
|
|
2083
|
-
acceptShare(shareId: $shareId) {
|
|
2084
|
-
...ShareFields
|
|
2085
|
-
}
|
|
2086
|
-
}
|
|
2087
|
-
|
|
2088
|
-
mutation Decline${typeName}Share($shareId: ID!) {
|
|
2089
|
-
declineShare(shareId: $shareId) {
|
|
2090
|
-
...ShareFields
|
|
2091
|
-
}
|
|
2092
|
-
}
|
|
2093
|
-
|
|
2094
|
-
mutation Revoke${typeName}Share($shareId: ID!) {
|
|
2095
|
-
revokeShare(shareId: $shareId) {
|
|
2096
|
-
...ShareFields
|
|
2097
|
-
}
|
|
2098
|
-
}
|
|
2099
|
-
|
|
2100
|
-
query ${typeName}Shares($resourceId: ID!, $status: ShareStatus) {
|
|
2101
|
-
shares(resourceType: RECORD, resourceId: $resourceId, status: $status) {
|
|
2102
|
-
...ShareFields
|
|
2103
|
-
}
|
|
2104
|
-
}
|
|
2105
|
-
|
|
2106
|
-
query ${pluralName}SharedWithMe($status: ShareStatus) {
|
|
2107
|
-
sharedWithMe(resourceType: RECORD, modelKey: "${modelKey}", status: $status) {
|
|
2108
|
-
...ShareFields
|
|
2109
|
-
record {
|
|
2110
|
-
...${typeName}Fields
|
|
2111
|
-
}
|
|
2112
|
-
}
|
|
2113
|
-
}
|
|
2114
|
-
`;
|
|
2115
|
-
}
|
|
2116
|
-
|
|
2117
|
-
// src/codegen/swift-field-mapping.ts
|
|
2118
|
-
var SWIFT_FIELD_TYPE_MAPPING = {
|
|
2119
|
-
text: {
|
|
2120
|
-
type: "String",
|
|
2121
|
-
alwaysOptional: false,
|
|
2122
|
-
defaultValue: '""',
|
|
2123
|
-
castExpression: "as? String"
|
|
2124
|
-
},
|
|
2125
|
-
richtext: {
|
|
2126
|
-
type: "String",
|
|
2127
|
-
alwaysOptional: true,
|
|
2128
|
-
defaultValue: '""',
|
|
2129
|
-
castExpression: "as? String"
|
|
2130
|
-
},
|
|
2131
|
-
number: {
|
|
2132
|
-
type: "Double",
|
|
2133
|
-
alwaysOptional: true,
|
|
2134
|
-
defaultValue: "0",
|
|
2135
|
-
castExpression: "as? Double"
|
|
2136
|
-
},
|
|
2137
|
-
boolean: {
|
|
2138
|
-
type: "Bool",
|
|
2139
|
-
alwaysOptional: true,
|
|
2140
|
-
defaultValue: "false",
|
|
2141
|
-
castExpression: "as? Bool"
|
|
2142
|
-
},
|
|
2143
|
-
email: {
|
|
2144
|
-
type: "String",
|
|
2145
|
-
alwaysOptional: true,
|
|
2146
|
-
defaultValue: '""',
|
|
2147
|
-
castExpression: "as? String"
|
|
2148
|
-
},
|
|
2149
|
-
phone: {
|
|
2150
|
-
type: "String",
|
|
2151
|
-
alwaysOptional: true,
|
|
2152
|
-
defaultValue: '""',
|
|
2153
|
-
castExpression: "as? String"
|
|
2154
|
-
},
|
|
2155
|
-
url: {
|
|
2156
|
-
type: "String",
|
|
2157
|
-
alwaysOptional: true,
|
|
2158
|
-
defaultValue: '""',
|
|
2159
|
-
castExpression: "as? String"
|
|
2160
|
-
},
|
|
2161
|
-
date: {
|
|
2162
|
-
type: "String",
|
|
2163
|
-
alwaysOptional: true,
|
|
2164
|
-
defaultValue: '""',
|
|
2165
|
-
castExpression: "as? String"
|
|
2166
|
-
},
|
|
2167
|
-
image: {
|
|
2168
|
-
type: "ImageValue",
|
|
2169
|
-
alwaysOptional: true,
|
|
2170
|
-
defaultValue: 'ImageValue(id: "", url: "")',
|
|
2171
|
-
castExpression: "as? [String: Any]",
|
|
2172
|
-
needsSharedType: true
|
|
2173
|
-
},
|
|
2174
|
-
video: {
|
|
2175
|
-
type: "VideoValue",
|
|
2176
|
-
alwaysOptional: true,
|
|
2177
|
-
defaultValue: 'VideoValue(id: "", url: "")',
|
|
2178
|
-
castExpression: "as? [String: Any]",
|
|
2179
|
-
needsSharedType: true
|
|
2180
|
-
},
|
|
2181
|
-
file: {
|
|
2182
|
-
type: "FileValue",
|
|
2183
|
-
alwaysOptional: true,
|
|
2184
|
-
defaultValue: 'FileValue(id: "", url: "", name: "", size: 0, mimeType: "")',
|
|
2185
|
-
// fromSyncData handles fileId→id mapping
|
|
2186
|
-
castExpression: "as? [String: Any]",
|
|
2187
|
-
needsSharedType: true
|
|
2188
|
-
},
|
|
2189
|
-
currency: {
|
|
2190
|
-
type: "CurrencyValue",
|
|
2191
|
-
alwaysOptional: true,
|
|
2192
|
-
defaultValue: 'CurrencyValue(amount: 0, currency: "")',
|
|
2193
|
-
castExpression: "as? [String: Any]",
|
|
2194
|
-
needsSharedType: true
|
|
2195
|
-
},
|
|
2196
|
-
select: {
|
|
2197
|
-
type: "String",
|
|
2198
|
-
alwaysOptional: true,
|
|
2199
|
-
defaultValue: '""',
|
|
2200
|
-
castExpression: "as? String"
|
|
2201
|
-
},
|
|
2202
|
-
multiselect: {
|
|
2203
|
-
type: "[String]",
|
|
2204
|
-
alwaysOptional: true,
|
|
2205
|
-
defaultValue: "[]",
|
|
2206
|
-
castExpression: "as? [String]"
|
|
2207
|
-
},
|
|
2208
|
-
json: {
|
|
2209
|
-
type: "Any",
|
|
2210
|
-
alwaysOptional: true,
|
|
2211
|
-
defaultValue: "nil",
|
|
2212
|
-
castExpression: ""
|
|
2213
|
-
},
|
|
2214
|
-
list: {
|
|
2215
|
-
type: "[Any]",
|
|
2216
|
-
alwaysOptional: true,
|
|
2217
|
-
defaultValue: "[]",
|
|
2218
|
-
castExpression: "as? [Any]"
|
|
2219
|
-
},
|
|
2220
|
-
flexible: {
|
|
2221
|
-
type: "[[String: Any]]",
|
|
2222
|
-
alwaysOptional: true,
|
|
2223
|
-
defaultValue: "[]",
|
|
2224
|
-
castExpression: "as? [[String: Any]]"
|
|
2225
|
-
},
|
|
2226
|
-
reference: {
|
|
2227
|
-
type: "String",
|
|
2228
|
-
alwaysOptional: true,
|
|
2229
|
-
defaultValue: '""',
|
|
2230
|
-
castExpression: "as? String"
|
|
2231
|
-
},
|
|
2232
|
-
link: {
|
|
2233
|
-
type: "LinkValue",
|
|
2234
|
-
alwaysOptional: true,
|
|
2235
|
-
defaultValue: 'LinkValue(type: "")',
|
|
2236
|
-
castExpression: "as? [String: Any]",
|
|
2237
|
-
needsSharedType: true
|
|
2238
|
-
},
|
|
2239
|
-
model: {
|
|
2240
|
-
type: "String",
|
|
2241
|
-
alwaysOptional: true,
|
|
2242
|
-
defaultValue: '""',
|
|
2243
|
-
castExpression: "as? String"
|
|
2244
|
-
}
|
|
2245
|
-
};
|
|
2246
|
-
function getSwiftFieldType(field) {
|
|
2247
|
-
const mapping = SWIFT_FIELD_TYPE_MAPPING[field.type];
|
|
2248
|
-
if (!mapping) {
|
|
2249
|
-
return {
|
|
2250
|
-
type: "Any",
|
|
2251
|
-
isOptional: true,
|
|
2252
|
-
mapping: void 0
|
|
2253
|
-
};
|
|
2254
|
-
}
|
|
2255
|
-
const isOptional = mapping.alwaysOptional || !field.required;
|
|
2256
|
-
return { type: mapping.type, isOptional, mapping };
|
|
2257
|
-
}
|
|
2258
|
-
|
|
2259
|
-
// src/codegen/generators/swift-types.ts
|
|
2260
|
-
function generateSwiftModelFile(model) {
|
|
2261
|
-
const typeName = toPascalCase(model.key);
|
|
2262
|
-
const fields = model.fields ?? [];
|
|
2263
|
-
const lines = [];
|
|
2264
|
-
lines.push("//");
|
|
2265
|
-
lines.push(`// ${typeName}.swift`);
|
|
2266
|
-
lines.push("//");
|
|
2267
|
-
lines.push(`// Generated from model '${model.key}'`);
|
|
2268
|
-
lines.push("//");
|
|
2269
|
-
lines.push("// @generated by foir \u2014 DO NOT EDIT MANUALLY");
|
|
2270
|
-
lines.push("//");
|
|
2271
|
-
lines.push("");
|
|
2272
|
-
lines.push("import Foundation");
|
|
2273
|
-
lines.push("");
|
|
2274
|
-
lines.push(generateFieldsEnum(typeName, fields));
|
|
2275
|
-
lines.push("");
|
|
2276
|
-
lines.push(generateDataStruct(typeName, fields));
|
|
2277
|
-
lines.push("");
|
|
2278
|
-
lines.push(generateSerializationExtension(typeName, fields));
|
|
2279
|
-
lines.push("");
|
|
2280
|
-
lines.push(generateConfigEnum(typeName, model));
|
|
2281
|
-
return lines.join("\n");
|
|
2282
|
-
}
|
|
2283
|
-
function generateFieldsEnum(typeName, fields) {
|
|
2284
|
-
const lines = [];
|
|
2285
|
-
lines.push(`// MARK: - ${typeName} Field Keys`);
|
|
2286
|
-
lines.push("");
|
|
2287
|
-
lines.push(`enum ${typeName}Fields {`);
|
|
2288
|
-
for (const field of fields) {
|
|
2289
|
-
lines.push(` static let ${field.key} = "${field.key}"`);
|
|
2290
|
-
}
|
|
2291
|
-
lines.push("}");
|
|
2292
|
-
return lines.join("\n");
|
|
2293
|
-
}
|
|
2294
|
-
function generateDataStruct(typeName, fields) {
|
|
2295
|
-
const lines = [];
|
|
2296
|
-
lines.push(`// MARK: - ${typeName} Data`);
|
|
2297
|
-
lines.push("");
|
|
2298
|
-
lines.push(`struct ${typeName}Data {`);
|
|
2299
|
-
for (const field of fields) {
|
|
2300
|
-
const { type, isOptional } = getSwiftFieldType(field);
|
|
2301
|
-
const optionalSuffix = isOptional ? "?" : "";
|
|
2302
|
-
lines.push(` var ${field.key}: ${type}${optionalSuffix}`);
|
|
2303
|
-
}
|
|
2304
|
-
lines.push("}");
|
|
2305
|
-
return lines.join("\n");
|
|
2306
|
-
}
|
|
2307
|
-
function generateSerializationExtension(typeName, fields) {
|
|
2308
|
-
const lines = [];
|
|
2309
|
-
lines.push(`// MARK: - ${typeName} Serialization`);
|
|
2310
|
-
lines.push("");
|
|
2311
|
-
lines.push(`extension ${typeName}Data {`);
|
|
2312
|
-
lines.push(" func toSyncData() -> [String: Any] {");
|
|
2313
|
-
const requiredFields = fields.filter((f) => {
|
|
2314
|
-
const { isOptional } = getSwiftFieldType(f);
|
|
2315
|
-
return !isOptional;
|
|
2316
|
-
});
|
|
2317
|
-
const optionalFields = fields.filter((f) => {
|
|
2318
|
-
const { isOptional } = getSwiftFieldType(f);
|
|
2319
|
-
return isOptional;
|
|
2320
|
-
});
|
|
2321
|
-
if (requiredFields.length > 0) {
|
|
2322
|
-
if (optionalFields.length === 0) {
|
|
2323
|
-
lines.push(` return [`);
|
|
2324
|
-
requiredFields.forEach((f, i) => {
|
|
2325
|
-
const comma = i < requiredFields.length - 1 ? "," : "";
|
|
2326
|
-
lines.push(
|
|
2327
|
-
` ${typeName}Fields.${f.key}: ${toSyncValueExpr(f)}${comma}`
|
|
2328
|
-
);
|
|
2329
|
-
});
|
|
2330
|
-
lines.push(" ]");
|
|
2331
|
-
} else {
|
|
2332
|
-
lines.push(` var data: [String: Any] = [`);
|
|
2333
|
-
requiredFields.forEach((f, i) => {
|
|
2334
|
-
const comma = i < requiredFields.length - 1 ? "," : "";
|
|
2335
|
-
lines.push(
|
|
2336
|
-
` ${typeName}Fields.${f.key}: ${toSyncValueExpr(f)}${comma}`
|
|
2337
|
-
);
|
|
2338
|
-
});
|
|
2339
|
-
lines.push(" ]");
|
|
2340
|
-
for (const f of optionalFields) {
|
|
2341
|
-
lines.push(
|
|
2342
|
-
` if let ${f.key} { data[${typeName}Fields.${f.key}] = ${toSyncValueExprForOptional(f)} }`
|
|
2343
|
-
);
|
|
2344
|
-
}
|
|
2345
|
-
lines.push(" return data");
|
|
2346
|
-
}
|
|
2347
|
-
} else {
|
|
2348
|
-
lines.push(" var data: [String: Any] = [:]");
|
|
2349
|
-
for (const f of optionalFields) {
|
|
2350
|
-
lines.push(
|
|
2351
|
-
` if let ${f.key} { data[${typeName}Fields.${f.key}] = ${toSyncValueExprForOptional(f)} }`
|
|
2352
|
-
);
|
|
2353
|
-
}
|
|
2354
|
-
lines.push(" return data");
|
|
2355
|
-
}
|
|
2356
|
-
lines.push(" }");
|
|
2357
|
-
lines.push("");
|
|
2358
|
-
lines.push(
|
|
2359
|
-
" static func fromSyncData(_ data: [String: Any]) -> " + typeName + "Data {"
|
|
2360
|
-
);
|
|
2361
|
-
lines.push(` ${typeName}Data(`);
|
|
2362
|
-
fields.forEach((field, i) => {
|
|
2363
|
-
const comma = i < fields.length - 1 ? "," : "";
|
|
2364
|
-
const { isOptional, mapping } = getSwiftFieldType(field);
|
|
2365
|
-
lines.push(
|
|
2366
|
-
` ${field.key}: ${fromSyncDataExpr(field, typeName, isOptional, mapping)}${comma}`
|
|
2367
|
-
);
|
|
2368
|
-
});
|
|
2369
|
-
lines.push(" )");
|
|
2370
|
-
lines.push(" }");
|
|
2371
|
-
lines.push("}");
|
|
2372
|
-
return lines.join("\n");
|
|
2373
|
-
}
|
|
2374
|
-
function toSyncValueExpr(field) {
|
|
2375
|
-
const mapping = SWIFT_FIELD_TYPE_MAPPING[field.type];
|
|
2376
|
-
if (mapping?.needsSharedType) {
|
|
2377
|
-
return `${field.key}.toSyncData()`;
|
|
2378
|
-
}
|
|
2379
|
-
return field.key;
|
|
2380
|
-
}
|
|
2381
|
-
function toSyncValueExprForOptional(field) {
|
|
2382
|
-
const mapping = SWIFT_FIELD_TYPE_MAPPING[field.type];
|
|
2383
|
-
if (mapping?.needsSharedType) {
|
|
2384
|
-
return `${field.key}.toSyncData()`;
|
|
2385
|
-
}
|
|
2386
|
-
return field.key;
|
|
2387
|
-
}
|
|
2388
|
-
function fromSyncDataExpr(field, typeName, isOptional, mapping) {
|
|
2389
|
-
const accessor = `data[${typeName}Fields.${field.key}]`;
|
|
2390
|
-
if (!mapping) {
|
|
2391
|
-
return isOptional ? `${accessor}` : `${accessor} ?? nil`;
|
|
2392
|
-
}
|
|
2393
|
-
if (mapping.needsSharedType) {
|
|
2394
|
-
const dictCast = `${accessor} as? [String: Any]`;
|
|
2395
|
-
if (isOptional) {
|
|
2396
|
-
return `(${dictCast}).map { ${mapping.type}.fromSyncData($0) }`;
|
|
2397
|
-
}
|
|
2398
|
-
return `${mapping.type}.fromSyncData(${dictCast} ?? [:])`;
|
|
2399
|
-
}
|
|
2400
|
-
if (field.type === "json") {
|
|
2401
|
-
return isOptional ? accessor : `${accessor}`;
|
|
2402
|
-
}
|
|
2403
|
-
if (isOptional) {
|
|
2404
|
-
return `${accessor} ${mapping.castExpression}`;
|
|
2405
|
-
}
|
|
2406
|
-
return `${accessor} ${mapping.castExpression} ?? ${mapping.defaultValue}`;
|
|
2407
|
-
}
|
|
2408
|
-
function generateConfigEnum(typeName, model) {
|
|
2409
|
-
const lines = [];
|
|
2410
|
-
lines.push(`// MARK: - ${typeName} Config`);
|
|
2411
|
-
lines.push("");
|
|
2412
|
-
lines.push(`enum ${typeName}Config {`);
|
|
2413
|
-
const escapedName = (model.name ?? model.key).replace(/"/g, '\\"');
|
|
2414
|
-
lines.push(` static let key = "${model.key}"`);
|
|
2415
|
-
lines.push(` static let name = "${escapedName}"`);
|
|
2416
|
-
lines.push(` static let customerScoped = ${model.config.customerScoped}`);
|
|
2417
|
-
lines.push(` static let publicApi = ${model.config.publicApi}`);
|
|
2418
|
-
lines.push(` static let versioning = ${model.config.versioning}`);
|
|
2419
|
-
lines.push(` static let publishing = ${model.config.publishing}`);
|
|
2420
|
-
lines.push(` static let variants = ${model.config.variants}`);
|
|
2421
|
-
lines.push(
|
|
2422
|
-
` static let sharingEnabled = ${model.config.sharing?.enabled ?? false}`
|
|
2423
|
-
);
|
|
2424
|
-
lines.push(
|
|
2425
|
-
` static let sharingRequireAcceptance = ${model.config.sharing?.requireAcceptance ?? true}`
|
|
2426
|
-
);
|
|
2427
|
-
lines.push("}");
|
|
2428
|
-
lines.push("");
|
|
2429
|
-
return lines.join("\n");
|
|
2430
|
-
}
|
|
2431
|
-
|
|
2432
|
-
// src/codegen/generators/swift-field-types.ts
|
|
2433
|
-
function generateSwiftFieldTypesFile() {
|
|
2434
|
-
return `//
|
|
2435
|
-
// FieldTypes.swift
|
|
2436
|
-
//
|
|
2437
|
-
// Shared value types for platform sync data.
|
|
2438
|
-
//
|
|
2439
|
-
// @generated by foir \u2014 DO NOT EDIT MANUALLY
|
|
2440
|
-
//
|
|
2441
|
-
|
|
2442
|
-
import Foundation
|
|
2443
|
-
|
|
2444
|
-
// MARK: - Image
|
|
2445
|
-
|
|
2446
|
-
struct ImageValue {
|
|
2447
|
-
let id: String
|
|
2448
|
-
let url: String
|
|
2449
|
-
var alt: String?
|
|
2450
|
-
var width: Int?
|
|
2451
|
-
var height: Int?
|
|
2452
|
-
var dominantColor: String?
|
|
2453
|
-
var blurhash: String?
|
|
2454
|
-
|
|
2455
|
-
func toSyncData() -> [String: Any] {
|
|
2456
|
-
var data: [String: Any] = ["fileId": id, "source": "internal"]
|
|
2457
|
-
if let alt { data["altText"] = alt }
|
|
2458
|
-
if let width { data["width"] = width }
|
|
2459
|
-
if let height { data["height"] = height }
|
|
2460
|
-
if let dominantColor { data["dominantColor"] = dominantColor }
|
|
2461
|
-
if let blurhash { data["blurhash"] = blurhash }
|
|
2462
|
-
return data
|
|
2463
|
-
}
|
|
2464
|
-
|
|
2465
|
-
static func fromSyncData(_ data: [String: Any]) -> ImageValue {
|
|
2466
|
-
ImageValue(
|
|
2467
|
-
id: data["fileId"] as? String ?? data["id"] as? String ?? "",
|
|
2468
|
-
url: data["url"] as? String ?? "",
|
|
2469
|
-
alt: data["altText"] as? String ?? data["alt"] as? String,
|
|
2470
|
-
width: data["width"] as? Int,
|
|
2471
|
-
height: data["height"] as? Int,
|
|
2472
|
-
dominantColor: data["dominantColor"] as? String,
|
|
2473
|
-
blurhash: data["blurhash"] as? String
|
|
2474
|
-
)
|
|
2475
|
-
}
|
|
2476
|
-
}
|
|
2477
|
-
|
|
2478
|
-
// MARK: - Video
|
|
2479
|
-
|
|
2480
|
-
struct VideoValue {
|
|
2481
|
-
let id: String
|
|
2482
|
-
let url: String
|
|
2483
|
-
var thumbnail: String?
|
|
2484
|
-
var duration: Double?
|
|
2485
|
-
|
|
2486
|
-
func toSyncData() -> [String: Any] {
|
|
2487
|
-
var data: [String: Any] = ["fileId": id, "source": "internal"]
|
|
2488
|
-
if let thumbnail { data["thumbnailUrl"] = thumbnail }
|
|
2489
|
-
if let duration { data["duration"] = duration }
|
|
2490
|
-
return data
|
|
2491
|
-
}
|
|
2492
|
-
|
|
2493
|
-
static func fromSyncData(_ data: [String: Any]) -> VideoValue {
|
|
2494
|
-
VideoValue(
|
|
2495
|
-
id: data["fileId"] as? String ?? data["id"] as? String ?? "",
|
|
2496
|
-
url: data["url"] as? String ?? "",
|
|
2497
|
-
thumbnail: data["thumbnailUrl"] as? String ?? data["thumbnail"] as? String,
|
|
2498
|
-
duration: data["duration"] as? Double
|
|
2499
|
-
)
|
|
2500
|
-
}
|
|
2501
|
-
}
|
|
2502
|
-
|
|
2503
|
-
// MARK: - File
|
|
2504
|
-
|
|
2505
|
-
struct FileValue {
|
|
2506
|
-
let id: String
|
|
2507
|
-
let url: String
|
|
2508
|
-
let name: String
|
|
2509
|
-
let size: Int
|
|
2510
|
-
let mimeType: String
|
|
2511
|
-
|
|
2512
|
-
func toSyncData() -> [String: Any] {
|
|
2513
|
-
[
|
|
2514
|
-
"fileId": id,
|
|
2515
|
-
"source": "internal",
|
|
2516
|
-
"filename": name,
|
|
2517
|
-
"fileSize": size,
|
|
2518
|
-
"mimeType": mimeType,
|
|
2519
|
-
]
|
|
2520
|
-
}
|
|
2521
|
-
|
|
2522
|
-
static func fromSyncData(_ data: [String: Any]) -> FileValue {
|
|
2523
|
-
FileValue(
|
|
2524
|
-
id: data["fileId"] as? String ?? data["id"] as? String ?? "",
|
|
2525
|
-
url: data["url"] as? String ?? "",
|
|
2526
|
-
name: data["filename"] as? String ?? data["name"] as? String ?? "",
|
|
2527
|
-
size: data["fileSize"] as? Int ?? data["size"] as? Int ?? 0,
|
|
2528
|
-
mimeType: data["mimeType"] as? String ?? ""
|
|
2529
|
-
)
|
|
2530
|
-
}
|
|
2531
|
-
}
|
|
2532
|
-
|
|
2533
|
-
// MARK: - Currency
|
|
2534
|
-
|
|
2535
|
-
struct CurrencyValue {
|
|
2536
|
-
let amount: Double
|
|
2537
|
-
let currency: String
|
|
2538
|
-
|
|
2539
|
-
func toSyncData() -> [String: Any] {
|
|
2540
|
-
["amount": amount, "currency": currency]
|
|
2541
|
-
}
|
|
2542
|
-
|
|
2543
|
-
static func fromSyncData(_ data: [String: Any]) -> CurrencyValue {
|
|
2544
|
-
CurrencyValue(
|
|
2545
|
-
amount: data["amount"] as? Double ?? 0,
|
|
2546
|
-
currency: data["currency"] as? String ?? ""
|
|
2547
|
-
)
|
|
2548
|
-
}
|
|
2549
|
-
}
|
|
2550
|
-
|
|
2551
|
-
// MARK: - Link
|
|
2552
|
-
|
|
2553
|
-
struct LinkValue {
|
|
2554
|
-
let type: String
|
|
2555
|
-
var entityModelKey: String?
|
|
2556
|
-
var entityNaturalKey: String?
|
|
2557
|
-
var url: String?
|
|
2558
|
-
var target: String?
|
|
2559
|
-
|
|
2560
|
-
func toSyncData() -> [String: Any] {
|
|
2561
|
-
var data: [String: Any] = ["type": type]
|
|
2562
|
-
if let entityModelKey { data["entityModelKey"] = entityModelKey }
|
|
2563
|
-
if let entityNaturalKey { data["entityNaturalKey"] = entityNaturalKey }
|
|
2564
|
-
if let url { data["url"] = url }
|
|
2565
|
-
if let target { data["target"] = target }
|
|
2566
|
-
return data
|
|
2567
|
-
}
|
|
2568
|
-
|
|
2569
|
-
static func fromSyncData(_ data: [String: Any]) -> LinkValue {
|
|
2570
|
-
LinkValue(
|
|
2571
|
-
type: data["type"] as? String ?? "",
|
|
2572
|
-
entityModelKey: data["entityModelKey"] as? String,
|
|
2573
|
-
entityNaturalKey: data["entityNaturalKey"] as? String,
|
|
2574
|
-
url: data["url"] as? String,
|
|
2575
|
-
target: data["target"] as? String
|
|
2576
|
-
)
|
|
2577
|
-
}
|
|
2578
|
-
}
|
|
2579
|
-
`;
|
|
2580
|
-
}
|
|
2581
|
-
|
|
2582
|
-
// src/codegen/generators/swift-model-keys.ts
|
|
2583
|
-
function generateSwiftModelKeys(models) {
|
|
2584
|
-
const lines = [];
|
|
2585
|
-
lines.push("//");
|
|
2586
|
-
lines.push("// ModelKeys.swift");
|
|
2587
|
-
lines.push("//");
|
|
2588
|
-
lines.push("// All model key constants.");
|
|
2589
|
-
lines.push("//");
|
|
2590
|
-
lines.push("// @generated by foir \u2014 DO NOT EDIT MANUALLY");
|
|
2591
|
-
lines.push("//");
|
|
2592
|
-
lines.push("");
|
|
2593
|
-
lines.push("import Foundation");
|
|
2594
|
-
lines.push("");
|
|
2595
|
-
lines.push("enum ModelKeys {");
|
|
2596
|
-
for (const model of models) {
|
|
2597
|
-
const propName = toCamelCase(model.key);
|
|
2598
|
-
lines.push(` static let ${propName} = "${model.key}"`);
|
|
2599
|
-
}
|
|
2600
|
-
lines.push("}");
|
|
2601
|
-
lines.push("");
|
|
2602
|
-
return lines.join("\n");
|
|
2603
|
-
}
|
|
2604
|
-
|
|
2605
|
-
// src/codegen/generators/customer-profile-types.ts
|
|
2606
|
-
function generateCustomerProfileTypes(schema) {
|
|
2607
|
-
const fields = schema.fields ?? [];
|
|
2608
|
-
const fieldTypeImports = getFieldTypeImportsForFields2(fields);
|
|
2609
|
-
let code = `/**
|
|
2610
|
-
* Customer Profile Types
|
|
2611
|
-
*
|
|
2612
|
-
* @generated by foir from customer profile schema (version ${schema.version}) \u2014 DO NOT EDIT MANUALLY
|
|
2613
|
-
*/
|
|
2614
|
-
|
|
2615
|
-
`;
|
|
2616
|
-
if (fieldTypeImports.size > 0) {
|
|
2617
|
-
const types = Array.from(fieldTypeImports).sort().join(", ");
|
|
2618
|
-
code += `import type { ${types} } from '../field-types.js';
|
|
2619
|
-
|
|
2620
|
-
`;
|
|
2621
|
-
}
|
|
2622
|
-
code += `/**
|
|
2623
|
-
* Customer Profile Data
|
|
2624
|
-
* Typed field values for customer profiles
|
|
2625
|
-
*
|
|
2626
|
-
* @generated from customer profile schema (version ${schema.version})
|
|
2627
|
-
*/
|
|
2628
|
-
`;
|
|
2629
|
-
code += `export interface CustomerProfileData {
|
|
2630
|
-
`;
|
|
2631
|
-
for (const field of fields) {
|
|
2632
|
-
const fieldName = sanitizeFieldName(field.key);
|
|
2633
|
-
const fieldType = getFieldType(field, "output");
|
|
2634
|
-
const optional = field.required ? "" : "?";
|
|
2635
|
-
if (field.helpText) {
|
|
2636
|
-
code += ` /** ${field.helpText} */
|
|
2637
|
-
`;
|
|
2638
|
-
}
|
|
2639
|
-
code += ` ${fieldName}${optional}: ${fieldType};
|
|
2640
|
-
`;
|
|
2641
|
-
}
|
|
2642
|
-
code += `}
|
|
2643
|
-
`;
|
|
2644
|
-
return code;
|
|
2645
|
-
}
|
|
2646
|
-
function getFieldTypeImportsForFields2(fields) {
|
|
2647
|
-
const imports = /* @__PURE__ */ new Set();
|
|
2648
|
-
for (const field of fields) {
|
|
2649
|
-
const mapping = FIELD_TYPE_MAPPING[field.type];
|
|
2650
|
-
if (mapping?.needsImport === "field-types") {
|
|
2651
|
-
imports.add(mapping.outputType);
|
|
2652
|
-
}
|
|
2653
|
-
}
|
|
2654
|
-
return imports;
|
|
2655
|
-
}
|
|
2656
|
-
|
|
2657
|
-
// src/codegen/generators/swift-customer-profile.ts
|
|
2658
|
-
function generateSwiftCustomerProfileFile(schema) {
|
|
2659
|
-
const typeName = "CustomerProfile";
|
|
2660
|
-
const fields = schema.fields ?? [];
|
|
2661
|
-
const lines = [];
|
|
2662
|
-
lines.push("//");
|
|
2663
|
-
lines.push("// CustomerProfile.swift");
|
|
2664
|
-
lines.push("//");
|
|
2665
|
-
lines.push(
|
|
2666
|
-
`// Generated from customer profile schema (version ${schema.version})`
|
|
2667
|
-
);
|
|
2668
|
-
lines.push("//");
|
|
2669
|
-
lines.push("// @generated by foir \u2014 DO NOT EDIT MANUALLY");
|
|
2670
|
-
lines.push("//");
|
|
2671
|
-
lines.push("");
|
|
2672
|
-
lines.push("import Foundation");
|
|
2673
|
-
lines.push("");
|
|
2674
|
-
lines.push(generateFieldsEnum2(typeName, fields));
|
|
2675
|
-
lines.push("");
|
|
2676
|
-
lines.push(generateDataStruct2(typeName, fields));
|
|
2677
|
-
lines.push("");
|
|
2678
|
-
lines.push(generateSerializationExtension2(typeName, fields));
|
|
2679
|
-
return lines.join("\n");
|
|
2680
|
-
}
|
|
2681
|
-
function generateFieldsEnum2(typeName, fields) {
|
|
2682
|
-
const lines = [];
|
|
2683
|
-
lines.push(`// MARK: - ${typeName} Field Keys`);
|
|
2684
|
-
lines.push("");
|
|
2685
|
-
lines.push(`enum ${typeName}Fields {`);
|
|
2686
|
-
for (const field of fields) {
|
|
2687
|
-
lines.push(` static let ${field.key} = "${field.key}"`);
|
|
2688
|
-
}
|
|
2689
|
-
lines.push("}");
|
|
2690
|
-
return lines.join("\n");
|
|
2691
|
-
}
|
|
2692
|
-
function generateDataStruct2(typeName, fields) {
|
|
2693
|
-
const lines = [];
|
|
2694
|
-
lines.push(`// MARK: - ${typeName} Data`);
|
|
2695
|
-
lines.push("");
|
|
2696
|
-
lines.push(`struct ${typeName}Data {`);
|
|
2697
|
-
for (const field of fields) {
|
|
2698
|
-
const { type, isOptional } = getSwiftFieldType(field);
|
|
2699
|
-
const optionalSuffix = isOptional ? "?" : "";
|
|
2700
|
-
lines.push(` var ${field.key}: ${type}${optionalSuffix}`);
|
|
2701
|
-
}
|
|
2702
|
-
lines.push("}");
|
|
2703
|
-
return lines.join("\n");
|
|
2704
|
-
}
|
|
2705
|
-
function generateSerializationExtension2(typeName, fields) {
|
|
2706
|
-
const lines = [];
|
|
2707
|
-
lines.push(`// MARK: - ${typeName} Serialization`);
|
|
2708
|
-
lines.push("");
|
|
2709
|
-
lines.push(`extension ${typeName}Data {`);
|
|
2710
|
-
lines.push(" func toSyncData() -> [String: Any] {");
|
|
2711
|
-
const requiredFields = fields.filter((f) => {
|
|
2712
|
-
const { isOptional } = getSwiftFieldType(f);
|
|
2713
|
-
return !isOptional;
|
|
2714
|
-
});
|
|
2715
|
-
const optionalFields = fields.filter((f) => {
|
|
2716
|
-
const { isOptional } = getSwiftFieldType(f);
|
|
2717
|
-
return isOptional;
|
|
2718
|
-
});
|
|
2719
|
-
if (requiredFields.length > 0) {
|
|
2720
|
-
if (optionalFields.length === 0) {
|
|
2721
|
-
lines.push(" return [");
|
|
2722
|
-
requiredFields.forEach((f, i) => {
|
|
2723
|
-
const comma = i < requiredFields.length - 1 ? "," : "";
|
|
2724
|
-
lines.push(
|
|
2725
|
-
` ${typeName}Fields.${f.key}: ${toSyncValueExpr2(f)}${comma}`
|
|
2726
|
-
);
|
|
2727
|
-
});
|
|
2728
|
-
lines.push(" ]");
|
|
2729
|
-
} else {
|
|
2730
|
-
lines.push(" var data: [String: Any] = [");
|
|
2731
|
-
requiredFields.forEach((f, i) => {
|
|
2732
|
-
const comma = i < requiredFields.length - 1 ? "," : "";
|
|
2733
|
-
lines.push(
|
|
2734
|
-
` ${typeName}Fields.${f.key}: ${toSyncValueExpr2(f)}${comma}`
|
|
2735
|
-
);
|
|
2736
|
-
});
|
|
2737
|
-
lines.push(" ]");
|
|
2738
|
-
for (const f of optionalFields) {
|
|
2739
|
-
lines.push(
|
|
2740
|
-
` if let ${f.key} { data[${typeName}Fields.${f.key}] = ${toSyncValueExprForOptional2(f)} }`
|
|
2741
|
-
);
|
|
2742
|
-
}
|
|
2743
|
-
lines.push(" return data");
|
|
2744
|
-
}
|
|
2745
|
-
} else {
|
|
2746
|
-
lines.push(" var data: [String: Any] = [:]");
|
|
2747
|
-
for (const f of optionalFields) {
|
|
2748
|
-
lines.push(
|
|
2749
|
-
` if let ${f.key} { data[${typeName}Fields.${f.key}] = ${toSyncValueExprForOptional2(f)} }`
|
|
2750
|
-
);
|
|
2751
|
-
}
|
|
2752
|
-
lines.push(" return data");
|
|
2753
|
-
}
|
|
2754
|
-
lines.push(" }");
|
|
2755
|
-
lines.push("");
|
|
2756
|
-
lines.push(
|
|
2757
|
-
" static func fromSyncData(_ data: [String: Any]) -> " + typeName + "Data {"
|
|
2758
|
-
);
|
|
2759
|
-
lines.push(` ${typeName}Data(`);
|
|
2760
|
-
fields.forEach((field, i) => {
|
|
2761
|
-
const comma = i < fields.length - 1 ? "," : "";
|
|
2762
|
-
const { isOptional, mapping } = getSwiftFieldType(field);
|
|
2763
|
-
lines.push(
|
|
2764
|
-
` ${field.key}: ${fromSyncDataExpr2(field, typeName, isOptional, mapping)}${comma}`
|
|
2765
|
-
);
|
|
2766
|
-
});
|
|
2767
|
-
lines.push(" )");
|
|
2768
|
-
lines.push(" }");
|
|
2769
|
-
lines.push("}");
|
|
2770
|
-
lines.push("");
|
|
2771
|
-
return lines.join("\n");
|
|
2772
|
-
}
|
|
2773
|
-
function toSyncValueExpr2(field) {
|
|
2774
|
-
const mapping = SWIFT_FIELD_TYPE_MAPPING[field.type];
|
|
2775
|
-
if (mapping?.needsSharedType) {
|
|
2776
|
-
return `${field.key}.toSyncData()`;
|
|
2777
|
-
}
|
|
2778
|
-
return field.key;
|
|
2779
|
-
}
|
|
2780
|
-
function toSyncValueExprForOptional2(field) {
|
|
2781
|
-
const mapping = SWIFT_FIELD_TYPE_MAPPING[field.type];
|
|
2782
|
-
if (mapping?.needsSharedType) {
|
|
2783
|
-
return `${field.key}.toSyncData()`;
|
|
2784
|
-
}
|
|
2785
|
-
return field.key;
|
|
2786
|
-
}
|
|
2787
|
-
function fromSyncDataExpr2(field, typeName, isOptional, mapping) {
|
|
2788
|
-
const accessor = `data[${typeName}Fields.${field.key}]`;
|
|
2789
|
-
if (!mapping) {
|
|
2790
|
-
return isOptional ? `${accessor}` : `${accessor} ?? nil`;
|
|
2791
|
-
}
|
|
2792
|
-
if (mapping.needsSharedType) {
|
|
2793
|
-
const dictCast = `${accessor} as? [String: Any]`;
|
|
2794
|
-
if (isOptional) {
|
|
2795
|
-
return `(${dictCast}).map { ${mapping.type}.fromSyncData($0) }`;
|
|
2796
|
-
}
|
|
2797
|
-
return `${mapping.type}.fromSyncData(${dictCast} ?? [:])`;
|
|
2798
|
-
}
|
|
2799
|
-
if (field.type === "json") {
|
|
2800
|
-
return isOptional ? accessor : `${accessor}`;
|
|
2801
|
-
}
|
|
2802
|
-
if (isOptional) {
|
|
2803
|
-
return `${accessor} ${mapping.castExpression}`;
|
|
2804
|
-
}
|
|
2805
|
-
return `${accessor} ${mapping.castExpression} ?? ${mapping.defaultValue}`;
|
|
2806
|
-
}
|
|
2807
|
-
|
|
2808
|
-
// src/codegen/generators/customer-profile-documents.ts
|
|
2809
|
-
function generateCustomerProfileDocuments() {
|
|
2810
|
-
return `# Generated GraphQL operations for Customer Profiles
|
|
2811
|
-
# @generated by foir \u2014 DO NOT EDIT MANUALLY
|
|
2812
|
-
|
|
2813
|
-
fragment CustomerProfileFields on CustomerProfile {
|
|
2814
|
-
id
|
|
2815
|
-
customerId
|
|
2816
|
-
data
|
|
2817
|
-
createdAt
|
|
2818
|
-
updatedAt
|
|
2819
|
-
}
|
|
2820
|
-
|
|
2821
|
-
query GetMyProfile {
|
|
2822
|
-
myProfile {
|
|
2823
|
-
...CustomerProfileFields
|
|
2824
|
-
resolved
|
|
2825
|
-
}
|
|
2826
|
-
}
|
|
2827
|
-
|
|
2828
|
-
query GetCustomerProfile($customerId: ID!) {
|
|
2829
|
-
customerProfile(customerId: $customerId) {
|
|
2830
|
-
...CustomerProfileFields
|
|
2831
|
-
resolved
|
|
2832
|
-
}
|
|
2833
|
-
}
|
|
2834
|
-
|
|
2835
|
-
mutation SetMyProfile($data: JSON!) {
|
|
2836
|
-
setMyProfile(data: $data) {
|
|
2837
|
-
...CustomerProfileFields
|
|
2838
|
-
}
|
|
2839
|
-
}
|
|
2840
|
-
|
|
2841
|
-
mutation UpdateCustomerProfile($customerId: ID, $data: JSON!) {
|
|
2842
|
-
updateCustomerProfile(customerId: $customerId, input: { data: $data }) {
|
|
2843
|
-
...CustomerProfileFields
|
|
2844
|
-
}
|
|
2845
|
-
}
|
|
2846
|
-
`;
|
|
2847
|
-
}
|
|
2848
|
-
|
|
2849
|
-
// src/codegen/generators/static-documents.ts
|
|
2850
|
-
var HEADER = "# @generated by foir \u2014 DO NOT EDIT MANUALLY";
|
|
2851
|
-
function authDocument() {
|
|
2852
|
-
return `# Customer authentication operations
|
|
2853
|
-
${HEADER}
|
|
2854
|
-
|
|
2855
|
-
mutation CustomerLogin($email: String!, $password: String!) {
|
|
2856
|
-
customerLogin(email: $email, password: $password) {
|
|
2857
|
-
success
|
|
2858
|
-
accessToken
|
|
2859
|
-
refreshToken
|
|
2860
|
-
user { id email status }
|
|
2861
|
-
}
|
|
2862
|
-
}
|
|
2863
|
-
|
|
2864
|
-
mutation CustomerRegister($email: String!, $password: String!) {
|
|
2865
|
-
customerRegister(email: $email, password: $password) {
|
|
2866
|
-
success
|
|
2867
|
-
accessToken
|
|
2868
|
-
refreshToken
|
|
2869
|
-
user { id email status }
|
|
2870
|
-
emailVerificationRequired
|
|
2871
|
-
}
|
|
2872
|
-
}
|
|
2873
|
-
|
|
2874
|
-
mutation CustomerRequestOTP($email: String!) {
|
|
2875
|
-
customerRequestOTP(email: $email) {
|
|
2876
|
-
success
|
|
2877
|
-
expiresAt
|
|
2878
|
-
message
|
|
2879
|
-
}
|
|
2880
|
-
}
|
|
2881
|
-
|
|
2882
|
-
mutation CustomerLoginOTP($email: String!, $otp: String!) {
|
|
2883
|
-
customerLoginOTP(email: $email, otp: $otp) {
|
|
2884
|
-
success
|
|
2885
|
-
accessToken
|
|
2886
|
-
refreshToken
|
|
2887
|
-
user { id email status }
|
|
2888
|
-
}
|
|
2889
|
-
}
|
|
2890
|
-
|
|
2891
|
-
mutation CustomerRefreshToken($refreshToken: String!) {
|
|
2892
|
-
customerRefreshToken(refreshToken: $refreshToken) {
|
|
2893
|
-
success
|
|
2894
|
-
accessToken
|
|
2895
|
-
refreshToken
|
|
2896
|
-
}
|
|
2897
|
-
}
|
|
2898
|
-
|
|
2899
|
-
mutation CustomerRequestPasswordReset($email: String!) {
|
|
2900
|
-
customerRequestPasswordReset(email: $email) {
|
|
2901
|
-
success
|
|
2902
|
-
message
|
|
2903
|
-
}
|
|
2904
|
-
}
|
|
2905
|
-
|
|
2906
|
-
mutation CustomerResetPassword($token: String!, $newPassword: String!) {
|
|
2907
|
-
customerResetPassword(token: $token, newPassword: $newPassword) {
|
|
2908
|
-
success
|
|
2909
|
-
message
|
|
2910
|
-
}
|
|
2911
|
-
}
|
|
2912
|
-
|
|
2913
|
-
mutation CustomerUpdatePassword($currentPassword: String!, $newPassword: String!) {
|
|
2914
|
-
customerUpdatePassword(currentPassword: $currentPassword, newPassword: $newPassword) {
|
|
2915
|
-
success
|
|
2916
|
-
message
|
|
2917
|
-
}
|
|
2918
|
-
}
|
|
2919
|
-
|
|
2920
|
-
mutation CustomerVerifyEmail($token: String!) {
|
|
2921
|
-
customerVerifyEmail(token: $token) {
|
|
2922
|
-
success
|
|
2923
|
-
user { id email }
|
|
2924
|
-
message
|
|
2925
|
-
}
|
|
2926
|
-
}
|
|
2927
|
-
|
|
2928
|
-
mutation CustomerResendVerificationEmail {
|
|
2929
|
-
customerResendVerificationEmail {
|
|
2930
|
-
success
|
|
2931
|
-
message
|
|
2932
|
-
}
|
|
2933
|
-
}
|
|
2934
|
-
|
|
2935
|
-
mutation CustomerLogout {
|
|
2936
|
-
customerLogout {
|
|
2937
|
-
success
|
|
2938
|
-
message
|
|
2939
|
-
}
|
|
2940
|
-
}
|
|
2941
|
-
|
|
2942
|
-
query AuthConfig($tenantId: ID) {
|
|
2943
|
-
authConfig(tenantId: $tenantId) {
|
|
2944
|
-
authMethods
|
|
2945
|
-
passwordPolicy {
|
|
2946
|
-
minLength
|
|
2947
|
-
requireUppercase
|
|
2948
|
-
requireLowercase
|
|
2949
|
-
requireNumbers
|
|
2950
|
-
requireSpecialChars
|
|
2951
|
-
requireSpecial
|
|
2952
|
-
}
|
|
2953
|
-
publicRegistrationEnabled
|
|
2954
|
-
}
|
|
2955
|
-
}
|
|
2956
|
-
|
|
2957
|
-
query CurrentUser {
|
|
2958
|
-
currentUser {
|
|
2959
|
-
id
|
|
2960
|
-
email
|
|
2961
|
-
emailVerified
|
|
2962
|
-
status
|
|
2963
|
-
userType
|
|
2964
|
-
}
|
|
2965
|
-
}
|
|
2966
|
-
`;
|
|
2967
|
-
}
|
|
2968
|
-
function authProvidersDocument() {
|
|
2969
|
-
return `# Auth provider operations
|
|
2970
|
-
${HEADER}
|
|
2971
|
-
|
|
2972
|
-
query AuthProviders {
|
|
2973
|
-
authProviders {
|
|
2974
|
-
id
|
|
2975
|
-
key
|
|
2976
|
-
name
|
|
2977
|
-
type
|
|
2978
|
-
enabled
|
|
2979
|
-
isDefault
|
|
2980
|
-
priority
|
|
2981
|
-
}
|
|
2982
|
-
}
|
|
2983
|
-
|
|
2984
|
-
query DefaultAuthProvider {
|
|
2985
|
-
defaultAuthProvider {
|
|
2986
|
-
id
|
|
2987
|
-
key
|
|
2988
|
-
name
|
|
2989
|
-
type
|
|
2990
|
-
enabled
|
|
2991
|
-
isDefault
|
|
2992
|
-
priority
|
|
2993
|
-
}
|
|
2994
|
-
}
|
|
2995
|
-
|
|
2996
|
-
mutation CustomerLoginWithProvider($input: ProviderLoginInput!) {
|
|
2997
|
-
customerLoginWithProvider(input: $input) {
|
|
2998
|
-
method
|
|
2999
|
-
providerId
|
|
3000
|
-
providerKey
|
|
3001
|
-
redirectUrl
|
|
3002
|
-
accessToken
|
|
3003
|
-
refreshToken
|
|
3004
|
-
user { id email userType }
|
|
3005
|
-
otpSent
|
|
3006
|
-
email
|
|
3007
|
-
expiresAt
|
|
3008
|
-
state
|
|
3009
|
-
}
|
|
3010
|
-
}
|
|
3011
|
-
|
|
3012
|
-
mutation CustomerProviderCallback($input: ProviderCallbackInput!) {
|
|
3013
|
-
customerProviderCallback(input: $input) {
|
|
3014
|
-
accessToken
|
|
3015
|
-
refreshToken
|
|
3016
|
-
user { id email userType }
|
|
3017
|
-
isNewCustomer
|
|
3018
|
-
providerAccessToken
|
|
3019
|
-
providerAccessTokenExpiresIn
|
|
3020
|
-
}
|
|
3021
|
-
}
|
|
3022
|
-
|
|
3023
|
-
mutation CustomerProviderVerifyOTP($input: ProviderOTPVerifyInput!) {
|
|
3024
|
-
customerProviderVerifyOTP(input: $input) {
|
|
3025
|
-
accessToken
|
|
3026
|
-
refreshToken
|
|
3027
|
-
user { id email userType }
|
|
3028
|
-
isNewCustomer
|
|
3029
|
-
providerAccessToken
|
|
3030
|
-
providerAccessTokenExpiresIn
|
|
3031
|
-
}
|
|
3032
|
-
}
|
|
3033
|
-
`;
|
|
3034
|
-
}
|
|
3035
|
-
function filesDocument() {
|
|
3036
|
-
return `# File management operations
|
|
3037
|
-
${HEADER}
|
|
3038
|
-
|
|
3039
|
-
query GetFile($id: ID!) {
|
|
3040
|
-
file(id: $id) {
|
|
3041
|
-
id
|
|
3042
|
-
filename
|
|
3043
|
-
mimeType
|
|
3044
|
-
size
|
|
3045
|
-
url
|
|
3046
|
-
source
|
|
3047
|
-
status
|
|
3048
|
-
metadata
|
|
3049
|
-
width
|
|
3050
|
-
height
|
|
3051
|
-
blurhash
|
|
3052
|
-
dominantColor
|
|
3053
|
-
duration
|
|
3054
|
-
thumbnailUrl
|
|
3055
|
-
previewUrl
|
|
3056
|
-
altText
|
|
3057
|
-
caption
|
|
3058
|
-
description
|
|
3059
|
-
isImage
|
|
3060
|
-
isVideo
|
|
3061
|
-
createdAt
|
|
3062
|
-
updatedAt
|
|
3063
|
-
}
|
|
3064
|
-
}
|
|
3065
|
-
|
|
3066
|
-
mutation CreateFileUpload(
|
|
3067
|
-
$filename: String!
|
|
3068
|
-
$mimeType: String!
|
|
3069
|
-
$size: Int!
|
|
3070
|
-
$folder: String
|
|
3071
|
-
$metadata: JSON
|
|
3072
|
-
) {
|
|
3073
|
-
createFileUpload(
|
|
3074
|
-
filename: $filename
|
|
3075
|
-
mimeType: $mimeType
|
|
3076
|
-
size: $size
|
|
3077
|
-
folder: $folder
|
|
3078
|
-
metadata: $metadata
|
|
3079
|
-
) {
|
|
3080
|
-
uploadId
|
|
3081
|
-
uploadUrl
|
|
3082
|
-
expiresAt
|
|
3083
|
-
}
|
|
3084
|
-
}
|
|
3085
|
-
|
|
3086
|
-
mutation ConfirmFileUpload($uploadId: ID!) {
|
|
3087
|
-
confirmFileUpload(uploadId: $uploadId) {
|
|
3088
|
-
id
|
|
3089
|
-
filename
|
|
3090
|
-
mimeType
|
|
3091
|
-
size
|
|
3092
|
-
url
|
|
3093
|
-
source
|
|
3094
|
-
status
|
|
3095
|
-
createdAt
|
|
3096
|
-
}
|
|
3097
|
-
}
|
|
3098
|
-
`;
|
|
3099
|
-
}
|
|
3100
|
-
function syncDocument() {
|
|
3101
|
-
return `# Sync engine operations (Layer 1: delta sync protocol)
|
|
3102
|
-
${HEADER}
|
|
3103
|
-
|
|
3104
|
-
query SyncPull($modelKey: String!, $since: String!, $limit: Int) {
|
|
3105
|
-
syncPull(modelKey: $modelKey, since: $since, limit: $limit) {
|
|
3106
|
-
items {
|
|
3107
|
-
id
|
|
3108
|
-
modelKey
|
|
3109
|
-
naturalKey
|
|
3110
|
-
data
|
|
3111
|
-
metadata
|
|
3112
|
-
syncVersion
|
|
3113
|
-
updatedAt
|
|
3114
|
-
deleted
|
|
3115
|
-
}
|
|
3116
|
-
cursor
|
|
3117
|
-
hasMore
|
|
3118
|
-
}
|
|
3119
|
-
}
|
|
3120
|
-
|
|
3121
|
-
mutation SyncPush($items: [SyncPushItemInput!]!) {
|
|
3122
|
-
syncPush(items: $items) {
|
|
3123
|
-
items {
|
|
3124
|
-
clientId
|
|
3125
|
-
serverId
|
|
3126
|
-
syncVersion
|
|
3127
|
-
status
|
|
3128
|
-
serverData
|
|
3129
|
-
serverSyncVersion
|
|
3130
|
-
error
|
|
3131
|
-
}
|
|
3132
|
-
}
|
|
3133
|
-
}
|
|
3134
|
-
|
|
3135
|
-
subscription RecordChanged($modelKey: String!) {
|
|
3136
|
-
recordChanged(modelKey: $modelKey) {
|
|
3137
|
-
type
|
|
3138
|
-
recordId
|
|
3139
|
-
modelKey
|
|
3140
|
-
naturalKey
|
|
3141
|
-
syncVersion
|
|
3142
|
-
data
|
|
3143
|
-
updatedBy
|
|
3144
|
-
timestamp
|
|
3145
|
-
}
|
|
3146
|
-
}
|
|
3147
|
-
`;
|
|
3148
|
-
}
|
|
3149
|
-
function notificationsDocument() {
|
|
3150
|
-
return `# Customer notification operations
|
|
3151
|
-
${HEADER}
|
|
3152
|
-
|
|
3153
|
-
query CustomerNotifications(
|
|
3154
|
-
$unreadOnly: Boolean
|
|
3155
|
-
$category: String
|
|
3156
|
-
$limit: Int
|
|
3157
|
-
$offset: Int
|
|
3158
|
-
) {
|
|
3159
|
-
customerNotifications(
|
|
3160
|
-
unreadOnly: $unreadOnly
|
|
3161
|
-
category: $category
|
|
3162
|
-
limit: $limit
|
|
3163
|
-
offset: $offset
|
|
3164
|
-
) {
|
|
3165
|
-
items {
|
|
3166
|
-
id
|
|
3167
|
-
type
|
|
3168
|
-
category
|
|
3169
|
-
title
|
|
3170
|
-
message
|
|
3171
|
-
actionUrl
|
|
3172
|
-
imageUrl
|
|
3173
|
-
metadata
|
|
3174
|
-
alertChannels
|
|
3175
|
-
isRead
|
|
3176
|
-
readAt
|
|
3177
|
-
createdAt
|
|
3178
|
-
}
|
|
3179
|
-
total
|
|
3180
|
-
unreadCount
|
|
3181
|
-
hasMore
|
|
3182
|
-
}
|
|
3183
|
-
}
|
|
3184
|
-
|
|
3185
|
-
query CustomerUnreadCount($category: String) {
|
|
3186
|
-
customerUnreadCount(category: $category)
|
|
3187
|
-
}
|
|
3188
|
-
|
|
3189
|
-
query NotificationPreferences {
|
|
3190
|
-
notificationPreferences {
|
|
3191
|
-
id
|
|
3192
|
-
category
|
|
3193
|
-
channel
|
|
3194
|
-
enabled
|
|
3195
|
-
}
|
|
3196
|
-
}
|
|
3197
|
-
|
|
3198
|
-
mutation SendNotification($input: SendNotificationInput!) {
|
|
3199
|
-
sendNotification(input: $input) {
|
|
3200
|
-
id
|
|
3201
|
-
type
|
|
3202
|
-
category
|
|
3203
|
-
title
|
|
3204
|
-
message
|
|
3205
|
-
actionUrl
|
|
3206
|
-
imageUrl
|
|
3207
|
-
metadata
|
|
3208
|
-
alertChannels
|
|
3209
|
-
isRead
|
|
3210
|
-
readAt
|
|
3211
|
-
createdAt
|
|
3212
|
-
}
|
|
3213
|
-
}
|
|
3214
|
-
|
|
3215
|
-
mutation SendBulkNotifications($input: SendBulkNotificationsInput!) {
|
|
3216
|
-
sendBulkNotifications(input: $input) {
|
|
3217
|
-
sent
|
|
3218
|
-
failed
|
|
3219
|
-
}
|
|
3220
|
-
}
|
|
3221
|
-
|
|
3222
|
-
mutation MarkCustomerNotificationRead($id: ID!) {
|
|
3223
|
-
markCustomerNotificationRead(id: $id) {
|
|
3224
|
-
id
|
|
3225
|
-
isRead
|
|
3226
|
-
readAt
|
|
3227
|
-
}
|
|
3228
|
-
}
|
|
3229
|
-
|
|
3230
|
-
mutation MarkAllCustomerNotificationsRead($category: String) {
|
|
3231
|
-
markAllCustomerNotificationsRead(category: $category)
|
|
3232
|
-
}
|
|
3233
|
-
|
|
3234
|
-
mutation RegisterDeviceToken($input: RegisterDeviceTokenInput!) {
|
|
3235
|
-
registerDeviceToken(input: $input) {
|
|
3236
|
-
id
|
|
3237
|
-
platform
|
|
3238
|
-
token
|
|
3239
|
-
deviceName
|
|
3240
|
-
isActive
|
|
3241
|
-
createdAt
|
|
3242
|
-
}
|
|
3243
|
-
}
|
|
3244
|
-
|
|
3245
|
-
mutation UnregisterDeviceToken($token: String!) {
|
|
3246
|
-
unregisterDeviceToken(token: $token)
|
|
3247
|
-
}
|
|
3248
|
-
|
|
3249
|
-
mutation UpdateNotificationPreference($input: UpdateNotificationPreferenceInput!) {
|
|
3250
|
-
updateNotificationPreference(input: $input) {
|
|
3251
|
-
id
|
|
3252
|
-
category
|
|
3253
|
-
channel
|
|
3254
|
-
enabled
|
|
3255
|
-
}
|
|
3256
|
-
}
|
|
3257
|
-
`;
|
|
3258
|
-
}
|
|
3259
|
-
function operationsDocument() {
|
|
3260
|
-
return `# Operation execution operations
|
|
3261
|
-
${HEADER}
|
|
3262
|
-
|
|
3263
|
-
query GetOperationExecution($id: ID!) {
|
|
3264
|
-
operationExecution(id: $id) {
|
|
3265
|
-
id
|
|
3266
|
-
operationKey
|
|
3267
|
-
status
|
|
3268
|
-
result
|
|
3269
|
-
error
|
|
3270
|
-
startedAt
|
|
3271
|
-
completedAt
|
|
3272
|
-
durationMs
|
|
3273
|
-
metadata
|
|
3274
|
-
createdAt
|
|
3275
|
-
}
|
|
3276
|
-
}
|
|
3277
|
-
|
|
3278
|
-
query ListOperationExecutions(
|
|
3279
|
-
$operationKey: String
|
|
3280
|
-
$status: OperationExecutionStatus
|
|
3281
|
-
$limit: Int
|
|
3282
|
-
$offset: Int
|
|
3283
|
-
) {
|
|
3284
|
-
operationExecutions(
|
|
3285
|
-
operationKey: $operationKey
|
|
3286
|
-
status: $status
|
|
3287
|
-
limit: $limit
|
|
3288
|
-
offset: $offset
|
|
3289
|
-
) {
|
|
3290
|
-
items {
|
|
3291
|
-
id
|
|
3292
|
-
operationKey
|
|
3293
|
-
status
|
|
3294
|
-
durationMs
|
|
3295
|
-
startedAt
|
|
3296
|
-
completedAt
|
|
3297
|
-
metadata
|
|
3298
|
-
createdAt
|
|
3299
|
-
}
|
|
3300
|
-
total
|
|
3301
|
-
}
|
|
3302
|
-
}
|
|
3303
|
-
|
|
3304
|
-
mutation ExecuteOperation($input: ExecuteOperationInput!) {
|
|
3305
|
-
executeOperation(input: $input) {
|
|
3306
|
-
success
|
|
3307
|
-
result
|
|
3308
|
-
error {
|
|
3309
|
-
code
|
|
3310
|
-
message
|
|
3311
|
-
}
|
|
3312
|
-
executionId
|
|
3313
|
-
durationMs
|
|
3314
|
-
metadata
|
|
3315
|
-
}
|
|
3316
|
-
}
|
|
3317
|
-
|
|
3318
|
-
mutation CancelOperationExecution($id: ID!) {
|
|
3319
|
-
cancelOperationExecution(id: $id) {
|
|
3320
|
-
id
|
|
3321
|
-
status
|
|
3322
|
-
}
|
|
3323
|
-
}
|
|
3324
|
-
`;
|
|
3325
|
-
}
|
|
3326
|
-
function schedulesDocument() {
|
|
3327
|
-
return `# Schedule management operations
|
|
3328
|
-
${HEADER}
|
|
3329
|
-
|
|
3330
|
-
query GetSchedule($key: String!) {
|
|
3331
|
-
schedule(key: $key) {
|
|
3332
|
-
id
|
|
3333
|
-
key
|
|
3334
|
-
name
|
|
3335
|
-
description
|
|
3336
|
-
cron
|
|
3337
|
-
cronDescription
|
|
3338
|
-
timezone
|
|
3339
|
-
targetType
|
|
3340
|
-
isActive
|
|
3341
|
-
lastRunAt
|
|
3342
|
-
lastRunStatus
|
|
3343
|
-
nextRunAt
|
|
3344
|
-
runCount
|
|
3345
|
-
failureCount
|
|
3346
|
-
createdAt
|
|
3347
|
-
updatedAt
|
|
3348
|
-
}
|
|
3349
|
-
}
|
|
3350
|
-
|
|
3351
|
-
query ListSchedules(
|
|
3352
|
-
$targetType: ScheduleTargetType
|
|
3353
|
-
$isActive: Boolean
|
|
3354
|
-
$limit: Int
|
|
3355
|
-
$offset: Int
|
|
3356
|
-
) {
|
|
3357
|
-
schedules(
|
|
3358
|
-
targetType: $targetType
|
|
3359
|
-
isActive: $isActive
|
|
3360
|
-
limit: $limit
|
|
3361
|
-
offset: $offset
|
|
3362
|
-
) {
|
|
3363
|
-
items {
|
|
3364
|
-
id
|
|
3365
|
-
key
|
|
3366
|
-
name
|
|
3367
|
-
cron
|
|
3368
|
-
cronDescription
|
|
3369
|
-
timezone
|
|
3370
|
-
isActive
|
|
3371
|
-
lastRunAt
|
|
3372
|
-
lastRunStatus
|
|
3373
|
-
nextRunAt
|
|
3374
|
-
runCount
|
|
3375
|
-
failureCount
|
|
3376
|
-
createdAt
|
|
3377
|
-
}
|
|
3378
|
-
total
|
|
3379
|
-
}
|
|
3380
|
-
}
|
|
3381
|
-
|
|
3382
|
-
mutation CreateSchedule($input: CreateScheduleInput!) {
|
|
3383
|
-
createSchedule(input: $input) {
|
|
3384
|
-
id
|
|
3385
|
-
key
|
|
3386
|
-
name
|
|
3387
|
-
cron
|
|
3388
|
-
isActive
|
|
3389
|
-
createdAt
|
|
3390
|
-
}
|
|
3391
|
-
}
|
|
3392
|
-
|
|
3393
|
-
mutation UpdateSchedule($key: String!, $input: UpdateScheduleInput!) {
|
|
3394
|
-
updateSchedule(key: $key, input: $input) {
|
|
3395
|
-
id
|
|
3396
|
-
key
|
|
3397
|
-
name
|
|
3398
|
-
cron
|
|
3399
|
-
isActive
|
|
3400
|
-
updatedAt
|
|
3401
|
-
}
|
|
3402
|
-
}
|
|
3403
|
-
|
|
3404
|
-
mutation DeleteSchedule($key: String!) {
|
|
3405
|
-
deleteSchedule(key: $key)
|
|
3406
|
-
}
|
|
3407
|
-
|
|
3408
|
-
mutation PauseSchedule($key: String!) {
|
|
3409
|
-
pauseSchedule(key: $key) {
|
|
3410
|
-
id
|
|
3411
|
-
key
|
|
3412
|
-
isActive
|
|
3413
|
-
}
|
|
3414
|
-
}
|
|
3415
|
-
|
|
3416
|
-
mutation ResumeSchedule($key: String!) {
|
|
3417
|
-
resumeSchedule(key: $key) {
|
|
3418
|
-
id
|
|
3419
|
-
key
|
|
3420
|
-
isActive
|
|
3421
|
-
}
|
|
3422
|
-
}
|
|
3423
|
-
|
|
3424
|
-
mutation TriggerSchedule($key: String!) {
|
|
3425
|
-
triggerSchedule(key: $key) {
|
|
3426
|
-
success
|
|
3427
|
-
jobId
|
|
3428
|
-
error
|
|
3429
|
-
}
|
|
3430
|
-
}
|
|
3431
|
-
`;
|
|
3432
|
-
}
|
|
3433
|
-
function sharingDocument() {
|
|
3434
|
-
return `# Sharing operations
|
|
3435
|
-
${HEADER}
|
|
3436
|
-
|
|
3437
|
-
fragment ShareFields on Share {
|
|
3438
|
-
id
|
|
3439
|
-
resourceType
|
|
3440
|
-
recordId
|
|
3441
|
-
fileId
|
|
3442
|
-
permission
|
|
3443
|
-
status
|
|
3444
|
-
sharedWithCustomerId
|
|
3445
|
-
acceptedAt
|
|
3446
|
-
declinedAt
|
|
3447
|
-
expiresAt
|
|
3448
|
-
createdAt
|
|
3449
|
-
createdBy
|
|
3450
|
-
revokedAt
|
|
3451
|
-
}
|
|
3452
|
-
|
|
3453
|
-
query GetShares($resourceType: ShareResourceType!, $resourceId: ID!, $status: ShareStatus) {
|
|
3454
|
-
shares(resourceType: $resourceType, resourceId: $resourceId, status: $status) {
|
|
3455
|
-
...ShareFields
|
|
3456
|
-
}
|
|
3457
|
-
}
|
|
3458
|
-
|
|
3459
|
-
query SharedWithMe(
|
|
3460
|
-
$resourceType: ShareResourceType
|
|
3461
|
-
$modelKey: String
|
|
3462
|
-
$status: ShareStatus
|
|
3463
|
-
$limit: Int
|
|
3464
|
-
$offset: Int
|
|
3465
|
-
) {
|
|
3466
|
-
sharedWithMe(
|
|
3467
|
-
resourceType: $resourceType
|
|
3468
|
-
modelKey: $modelKey
|
|
3469
|
-
status: $status
|
|
3470
|
-
limit: $limit
|
|
3471
|
-
offset: $offset
|
|
3472
|
-
) {
|
|
3473
|
-
...ShareFields
|
|
3474
|
-
}
|
|
3475
|
-
}
|
|
3476
|
-
|
|
3477
|
-
mutation ShareRecord(
|
|
3478
|
-
$recordId: ID!
|
|
3479
|
-
$sharedWithCustomerId: ID!
|
|
3480
|
-
$permission: SharePermission!
|
|
3481
|
-
$expiresAt: DateTime
|
|
3482
|
-
) {
|
|
3483
|
-
shareRecord(
|
|
3484
|
-
recordId: $recordId
|
|
3485
|
-
sharedWithCustomerId: $sharedWithCustomerId
|
|
3486
|
-
permission: $permission
|
|
3487
|
-
expiresAt: $expiresAt
|
|
3488
|
-
) {
|
|
3489
|
-
...ShareFields
|
|
3490
|
-
}
|
|
3491
|
-
}
|
|
3492
|
-
|
|
3493
|
-
mutation ShareFile(
|
|
3494
|
-
$fileId: ID!
|
|
3495
|
-
$sharedWithCustomerId: ID!
|
|
3496
|
-
$permission: SharePermission!
|
|
3497
|
-
$expiresAt: DateTime
|
|
3498
|
-
) {
|
|
3499
|
-
shareFile(
|
|
3500
|
-
fileId: $fileId
|
|
3501
|
-
sharedWithCustomerId: $sharedWithCustomerId
|
|
3502
|
-
permission: $permission
|
|
3503
|
-
expiresAt: $expiresAt
|
|
3504
|
-
) {
|
|
3505
|
-
...ShareFields
|
|
3506
|
-
}
|
|
3507
|
-
}
|
|
3508
|
-
|
|
3509
|
-
mutation AcceptShare($shareId: ID!) {
|
|
3510
|
-
acceptShare(shareId: $shareId) {
|
|
3511
|
-
...ShareFields
|
|
3512
|
-
}
|
|
3513
|
-
}
|
|
3514
|
-
|
|
3515
|
-
mutation DeclineShare($shareId: ID!) {
|
|
3516
|
-
declineShare(shareId: $shareId) {
|
|
3517
|
-
...ShareFields
|
|
3518
|
-
}
|
|
3519
|
-
}
|
|
3520
|
-
|
|
3521
|
-
mutation RevokeShare($shareId: ID!) {
|
|
3522
|
-
revokeShare(shareId: $shareId) {
|
|
3523
|
-
...ShareFields
|
|
3524
|
-
}
|
|
3525
|
-
}
|
|
3526
|
-
|
|
3527
|
-
mutation UpdateSharePermission($shareId: ID!, $permission: SharePermission!) {
|
|
3528
|
-
updateSharePermission(shareId: $shareId, permission: $permission) {
|
|
3529
|
-
...ShareFields
|
|
3530
|
-
}
|
|
3531
|
-
}
|
|
3532
|
-
`;
|
|
3533
|
-
}
|
|
3534
|
-
function embeddingsDocument() {
|
|
3535
|
-
return `# Vector embedding operations (search, write, delete)
|
|
3536
|
-
${HEADER}
|
|
3537
|
-
|
|
3538
|
-
query SearchEmbeddings($input: SearchEmbeddingsInput!) {
|
|
3539
|
-
searchEmbeddings(input: $input) {
|
|
3540
|
-
recordId
|
|
3541
|
-
modelKey
|
|
3542
|
-
naturalKey
|
|
3543
|
-
key
|
|
3544
|
-
similarity
|
|
3545
|
-
metadata
|
|
3546
|
-
}
|
|
3547
|
-
}
|
|
3548
|
-
|
|
3549
|
-
mutation WriteEmbeddings($input: WriteEmbeddingsInput!) {
|
|
3550
|
-
writeEmbeddings(input: $input) {
|
|
3551
|
-
written
|
|
3552
|
-
errors {
|
|
3553
|
-
recordId
|
|
3554
|
-
key
|
|
3555
|
-
message
|
|
3556
|
-
}
|
|
3557
|
-
}
|
|
3558
|
-
}
|
|
3559
|
-
|
|
3560
|
-
mutation DeleteEmbeddings($input: DeleteEmbeddingsInput!) {
|
|
3561
|
-
deleteEmbeddings(input: $input) {
|
|
3562
|
-
deleted
|
|
3563
|
-
}
|
|
3564
|
-
}
|
|
3565
|
-
`;
|
|
3566
|
-
}
|
|
3567
|
-
function analyticsDocument() {
|
|
3568
|
-
return `# Analytics & conversion tracking operations
|
|
3569
|
-
${HEADER}
|
|
3570
|
-
|
|
3571
|
-
mutation RecordConversion($input: RecordConversionInput!) {
|
|
3572
|
-
recordConversion(input: $input) {
|
|
3573
|
-
success
|
|
3574
|
-
}
|
|
3575
|
-
}
|
|
3576
|
-
`;
|
|
3577
|
-
}
|
|
3578
|
-
function generateStaticDocuments(domains) {
|
|
3579
|
-
const files = [];
|
|
3580
|
-
if (domains.auth)
|
|
3581
|
-
files.push({ filename: "auth.graphql", content: authDocument() });
|
|
3582
|
-
if (domains.authProviders)
|
|
3583
|
-
files.push({
|
|
3584
|
-
filename: "auth-providers.graphql",
|
|
3585
|
-
content: authProvidersDocument()
|
|
3586
|
-
});
|
|
3587
|
-
if (domains.files)
|
|
3588
|
-
files.push({ filename: "files.graphql", content: filesDocument() });
|
|
3589
|
-
if (domains.sync)
|
|
3590
|
-
files.push({ filename: "sync.graphql", content: syncDocument() });
|
|
3591
|
-
if (domains.notifications)
|
|
3592
|
-
files.push({
|
|
3593
|
-
filename: "notifications.graphql",
|
|
3594
|
-
content: notificationsDocument()
|
|
3595
|
-
});
|
|
3596
|
-
if (domains.operations)
|
|
3597
|
-
files.push({
|
|
3598
|
-
filename: "operations.graphql",
|
|
3599
|
-
content: operationsDocument()
|
|
3600
|
-
});
|
|
3601
|
-
if (domains.schedules)
|
|
3602
|
-
files.push({ filename: "schedules.graphql", content: schedulesDocument() });
|
|
3603
|
-
if (domains.sharing)
|
|
3604
|
-
files.push({ filename: "sharing.graphql", content: sharingDocument() });
|
|
3605
|
-
if (domains.embeddings)
|
|
3606
|
-
files.push({
|
|
3607
|
-
filename: "embeddings.graphql",
|
|
3608
|
-
content: embeddingsDocument()
|
|
3609
|
-
});
|
|
3610
|
-
if (domains.analytics)
|
|
3611
|
-
files.push({ filename: "analytics.graphql", content: analyticsDocument() });
|
|
3612
|
-
return files;
|
|
3613
|
-
}
|
|
3614
|
-
|
|
3615
|
-
// src/codegen/generators/typed-operations-common.ts
|
|
3616
|
-
function generateTypedOperationsCommon(typesRelPath) {
|
|
3617
|
-
return `/**
|
|
3618
|
-
* Shared types for typed GraphQL operations.
|
|
3619
|
-
*
|
|
3620
|
-
* @generated by foir \u2014 DO NOT EDIT MANUALLY
|
|
3621
|
-
*/
|
|
3622
|
-
|
|
3623
|
-
import type { JsonValue } from '${typesRelPath}/field-types.js';
|
|
3624
|
-
|
|
3625
|
-
/** A record with strongly-typed data. */
|
|
3626
|
-
export interface BaseRecord<T> {
|
|
3627
|
-
id: string;
|
|
3628
|
-
modelKey: string;
|
|
3629
|
-
naturalKey: string | null;
|
|
3630
|
-
data: T;
|
|
3631
|
-
metadata: Record<string, JsonValue> | null;
|
|
3632
|
-
publishedVersionNumber: number | null;
|
|
3633
|
-
publishedAt: string | null;
|
|
3634
|
-
versionNumber: number | null;
|
|
3635
|
-
changeDescription: string | null;
|
|
3636
|
-
createdAt: string;
|
|
3637
|
-
updatedAt: string;
|
|
3638
|
-
}
|
|
3639
|
-
|
|
3640
|
-
/** Resolved content wrapping strongly-typed data. */
|
|
3641
|
-
export interface ResolvedContent<T> {
|
|
3642
|
-
content: T;
|
|
3643
|
-
record: { id: string; modelKey: string; naturalKey: string | null };
|
|
3644
|
-
version: { id: string; versionNumber: number } | null;
|
|
3645
|
-
}
|
|
3646
|
-
|
|
3647
|
-
/** Paginated list result. */
|
|
3648
|
-
export interface PaginatedResult<T> {
|
|
3649
|
-
items: (BaseRecord<T> & { resolved: ResolvedContent<T> | null })[];
|
|
3650
|
-
total: number;
|
|
3651
|
-
}
|
|
3652
|
-
|
|
3653
|
-
/** Result of a createRecord mutation. */
|
|
3654
|
-
export interface CreateRecordResult<T> {
|
|
3655
|
-
record: BaseRecord<T>;
|
|
3656
|
-
}
|
|
3657
|
-
|
|
3658
|
-
/** Result of an updateRecord mutation. */
|
|
3659
|
-
export interface UpdateRecordResult<T> {
|
|
3660
|
-
record: BaseRecord<T>;
|
|
3661
|
-
matched: boolean;
|
|
3662
|
-
}
|
|
3663
|
-
|
|
3664
|
-
/** Result of a deleteRecord mutation. */
|
|
3665
|
-
export interface DeleteRecordResult {
|
|
3666
|
-
id: string;
|
|
3667
|
-
}
|
|
3668
|
-
|
|
3669
|
-
/** Share resource type. */
|
|
3670
|
-
export interface ShareResult {
|
|
3671
|
-
id: string;
|
|
3672
|
-
resourceType: string;
|
|
3673
|
-
permission: string;
|
|
3674
|
-
status: string;
|
|
3675
|
-
acceptedAt: string | null;
|
|
3676
|
-
declinedAt: string | null;
|
|
3677
|
-
expiresAt: string | null;
|
|
3678
|
-
createdAt: string;
|
|
3679
|
-
revokedAt: string | null;
|
|
3680
|
-
}
|
|
3681
|
-
|
|
3682
|
-
/** A share that includes the shared record. */
|
|
3683
|
-
export interface ShareWithRecord<T> extends ShareResult {
|
|
3684
|
-
record: BaseRecord<T>;
|
|
3685
|
-
}
|
|
3686
|
-
|
|
3687
|
-
/** Field selection for resolved content \u2014 pick or omit specific fields. */
|
|
3688
|
-
export interface FieldSelection<T = Record<string, unknown>> {
|
|
3689
|
-
/** Include only these field keys (mutually exclusive with omit) */
|
|
3690
|
-
pick?: (keyof T & string)[];
|
|
3691
|
-
/** Exclude these field keys (mutually exclusive with omit) */
|
|
3692
|
-
omit?: (keyof T & string)[];
|
|
3693
|
-
}
|
|
3694
|
-
`;
|
|
3695
|
-
}
|
|
3696
|
-
|
|
3697
|
-
// src/codegen/generators/typed-operations.ts
|
|
3698
|
-
import path from "path";
|
|
3699
|
-
function generateTypedOperations(model, typesRelPath) {
|
|
3700
|
-
const typeName = toPascalCase(model.key);
|
|
3701
|
-
const upperSnake = toUpperSnakeCase(model.key);
|
|
3702
|
-
const pluralName = model.pluralName ? toPascalCase(model.pluralName.replace(/\s+/g, "")) : `${typeName}s`;
|
|
3703
|
-
const pluralUpperSnake = model.pluralName ? toUpperSnakeCase(model.pluralName.replace(/\s+/g, "")) : `${upperSnake}S`;
|
|
3704
|
-
const dataType = `${typeName}Data`;
|
|
1837
|
+
// src/codegen/generators/client-factory.ts
|
|
1838
|
+
function generateClientFactory(models, hasCustomerProfile) {
|
|
1839
|
+
const publicModels = models.filter(
|
|
1840
|
+
(m) => m.config.publicApi && m.config.records
|
|
1841
|
+
);
|
|
3705
1842
|
const lines = [];
|
|
3706
1843
|
lines.push(`/**
|
|
3707
|
-
* Typed
|
|
1844
|
+
* Typed Foir client for this project.
|
|
3708
1845
|
*
|
|
3709
1846
|
* @generated by foir \u2014 DO NOT EDIT MANUALLY
|
|
3710
1847
|
*/
|
|
3711
1848
|
|
|
3712
|
-
import
|
|
3713
|
-
|
|
3714
|
-
|
|
3715
|
-
|
|
3716
|
-
|
|
3717
|
-
|
|
3718
|
-
|
|
3719
|
-
|
|
3720
|
-
|
|
3721
|
-
|
|
3722
|
-
|
|
1849
|
+
import {
|
|
1850
|
+
createBaseClient,
|
|
1851
|
+
createModelAccessor,
|
|
1852
|
+
createAuthClient,
|
|
1853
|
+
createFilesClient,
|
|
1854
|
+
createNotificationsClient,
|
|
1855
|
+
createSharingClient,
|
|
1856
|
+
createSearchClient,
|
|
1857
|
+
createProfileClient,
|
|
1858
|
+
createOperationsClient,
|
|
1859
|
+
type ClientConfig,
|
|
1860
|
+
type ModelAccessor,
|
|
1861
|
+
type FoirClient,
|
|
1862
|
+
} from '@eide/foir-client';
|
|
3723
1863
|
`);
|
|
3724
|
-
|
|
3725
|
-
|
|
3726
|
-
|
|
3727
|
-
|
|
3728
|
-
|
|
3729
|
-
|
|
3730
|
-
|
|
3731
|
-
|
|
3732
|
-
resolved(locale: $locale, preview: $preview, fields: $fields) {
|
|
3733
|
-
content
|
|
3734
|
-
record { id modelKey naturalKey }
|
|
3735
|
-
version { id versionNumber }
|
|
3736
|
-
}
|
|
3737
|
-
}
|
|
3738
|
-
}
|
|
3739
|
-
\`;`);
|
|
3740
|
-
lines.push("");
|
|
3741
|
-
lines.push(`export interface Get${typeName}Variables {
|
|
3742
|
-
id: string;
|
|
3743
|
-
locale?: string;
|
|
3744
|
-
preview?: boolean;
|
|
3745
|
-
fields?: FieldSelection<${dataType}>;
|
|
3746
|
-
}`);
|
|
3747
|
-
lines.push("");
|
|
3748
|
-
lines.push(`export interface Get${typeName}Result {
|
|
3749
|
-
record: ${typeName}Record & {
|
|
3750
|
-
resolved: ResolvedContent<${dataType}> | null;
|
|
3751
|
-
} | null;
|
|
3752
|
-
}`);
|
|
3753
|
-
lines.push("");
|
|
3754
|
-
lines.push(`export const GET_${upperSnake}_BY_KEY = \`
|
|
3755
|
-
query Get${typeName}ByKey($naturalKey: String!, $locale: String, $preview: Boolean, $fields: FieldSelectionInput) {
|
|
3756
|
-
recordByKey(modelKey: "${model.key}", naturalKey: $naturalKey) {
|
|
3757
|
-
id modelKey naturalKey data metadata
|
|
3758
|
-
publishedVersionNumber publishedAt versionNumber changeDescription
|
|
3759
|
-
createdAt updatedAt
|
|
3760
|
-
resolved(locale: $locale, preview: $preview, fields: $fields) {
|
|
3761
|
-
content
|
|
3762
|
-
record { id modelKey naturalKey }
|
|
3763
|
-
version { id versionNumber }
|
|
3764
|
-
}
|
|
3765
|
-
}
|
|
1864
|
+
for (const model of publicModels) {
|
|
1865
|
+
const typeName = toPascalCase(model.key);
|
|
1866
|
+
const dataType = `${typeName}Data`;
|
|
1867
|
+
const whereType = `${typeName}Where`;
|
|
1868
|
+
const sortType = `${typeName}SortField`;
|
|
1869
|
+
lines.push(
|
|
1870
|
+
`import type { ${dataType}, ${whereType}, ${sortType} } from './models/${model.key}.js';`
|
|
1871
|
+
);
|
|
3766
1872
|
}
|
|
3767
|
-
|
|
3768
|
-
|
|
3769
|
-
|
|
3770
|
-
|
|
3771
|
-
locale?: string;
|
|
3772
|
-
preview?: boolean;
|
|
3773
|
-
fields?: FieldSelection<${dataType}>;
|
|
3774
|
-
}`);
|
|
3775
|
-
lines.push("");
|
|
3776
|
-
lines.push(`export interface Get${typeName}ByKeyResult {
|
|
3777
|
-
recordByKey: ${typeName}Record & {
|
|
3778
|
-
resolved: ResolvedContent<${dataType}> | null;
|
|
3779
|
-
} | null;
|
|
3780
|
-
}`);
|
|
3781
|
-
lines.push("");
|
|
3782
|
-
lines.push(`export const LIST_${pluralUpperSnake} = \`
|
|
3783
|
-
query List${pluralName}($limit: Int, $offset: Int, $filters: [FilterInput!], $sort: SortInput, $locale: String, $preview: Boolean, $fields: FieldSelectionInput) {
|
|
3784
|
-
records(modelKey: "${model.key}", limit: $limit, offset: $offset, filters: $filters, sort: $sort) {
|
|
3785
|
-
items {
|
|
3786
|
-
id modelKey naturalKey data metadata
|
|
3787
|
-
publishedVersionNumber publishedAt versionNumber changeDescription
|
|
3788
|
-
createdAt updatedAt
|
|
3789
|
-
resolved(locale: $locale, preview: $preview, fields: $fields) {
|
|
3790
|
-
content
|
|
3791
|
-
record { id modelKey naturalKey }
|
|
3792
|
-
version { id versionNumber }
|
|
3793
|
-
}
|
|
3794
|
-
}
|
|
3795
|
-
total
|
|
3796
|
-
}
|
|
1873
|
+
if (hasCustomerProfile) {
|
|
1874
|
+
lines.push(
|
|
1875
|
+
`import type { CustomerProfileData } from './models/customer-profile.js';`
|
|
1876
|
+
);
|
|
3797
1877
|
}
|
|
3798
|
-
\`;`);
|
|
3799
|
-
lines.push("");
|
|
3800
|
-
lines.push(`export interface List${pluralName}Variables {
|
|
3801
|
-
limit?: number;
|
|
3802
|
-
offset?: number;
|
|
3803
|
-
filters?: Array<{ field: string; operator: string; value: JsonValue }>;
|
|
3804
|
-
sort?: { field: string; direction: 'ASC' | 'DESC' };
|
|
3805
|
-
locale?: string;
|
|
3806
|
-
preview?: boolean;
|
|
3807
|
-
fields?: FieldSelection<${dataType}>;
|
|
3808
|
-
}`);
|
|
3809
1878
|
lines.push("");
|
|
3810
|
-
lines.push(
|
|
3811
|
-
|
|
3812
|
-
|
|
3813
|
-
|
|
3814
|
-
|
|
3815
|
-
|
|
3816
|
-
|
|
3817
|
-
|
|
3818
|
-
id modelKey naturalKey data metadata createdAt updatedAt
|
|
3819
|
-
}
|
|
3820
|
-
}
|
|
1879
|
+
lines.push("export interface TypedClient extends FoirClient {");
|
|
1880
|
+
for (const model of publicModels) {
|
|
1881
|
+
const typeName = toPascalCase(model.key);
|
|
1882
|
+
const accessorName = toCamelCase(model.key);
|
|
1883
|
+
const dataType = `${typeName}Data`;
|
|
1884
|
+
const whereType = `${typeName}Where`;
|
|
1885
|
+
const sortType = `${typeName}SortField`;
|
|
1886
|
+
lines.push(` ${accessorName}: ModelAccessor<${dataType}, ${whereType}, ${sortType}>;`);
|
|
3821
1887
|
}
|
|
3822
|
-
|
|
3823
|
-
|
|
3824
|
-
|
|
3825
|
-
|
|
3826
|
-
modelKey: string;
|
|
3827
|
-
naturalKey?: string;
|
|
3828
|
-
data: Partial<${dataType}>;
|
|
3829
|
-
metadata?: Record<string, JsonValue>;
|
|
3830
|
-
};
|
|
3831
|
-
}`);
|
|
3832
|
-
lines.push("");
|
|
3833
|
-
lines.push(`export interface Create${typeName}Result {
|
|
3834
|
-
createRecord: CreateRecordResult<${dataType}>;
|
|
3835
|
-
}`);
|
|
3836
|
-
lines.push("");
|
|
3837
|
-
lines.push(`export const UPDATE_${upperSnake} = \`
|
|
3838
|
-
mutation Update${typeName}($input: UpdateRecordInput!) {
|
|
3839
|
-
updateRecord(input: $input) {
|
|
3840
|
-
record {
|
|
3841
|
-
id modelKey naturalKey data metadata createdAt updatedAt
|
|
3842
|
-
}
|
|
3843
|
-
matched
|
|
3844
|
-
}
|
|
1888
|
+
if (hasCustomerProfile) {
|
|
1889
|
+
lines.push(
|
|
1890
|
+
` profile: ReturnType<typeof createProfileClient<CustomerProfileData>>;`
|
|
1891
|
+
);
|
|
3845
1892
|
}
|
|
3846
|
-
|
|
3847
|
-
lines.push("");
|
|
3848
|
-
lines.push(`export interface Update${typeName}Variables {
|
|
3849
|
-
input: {
|
|
3850
|
-
id: string;
|
|
3851
|
-
data?: Partial<${dataType}>;
|
|
3852
|
-
metadata?: Record<string, JsonValue>;
|
|
3853
|
-
changeDescription?: string;
|
|
3854
|
-
};
|
|
3855
|
-
}`);
|
|
1893
|
+
lines.push("}");
|
|
3856
1894
|
lines.push("");
|
|
3857
|
-
lines.push(
|
|
3858
|
-
|
|
3859
|
-
}`);
|
|
1895
|
+
lines.push("export function createClient(config: ClientConfig): TypedClient {");
|
|
1896
|
+
lines.push(" const base = createBaseClient(config);");
|
|
3860
1897
|
lines.push("");
|
|
3861
|
-
lines.push(
|
|
3862
|
-
|
|
3863
|
-
|
|
1898
|
+
lines.push(" return {");
|
|
1899
|
+
lines.push(" auth: createAuthClient(base),");
|
|
1900
|
+
lines.push(" files: createFilesClient(base),");
|
|
1901
|
+
lines.push(" notifications: createNotificationsClient(base),");
|
|
1902
|
+
lines.push(" sharing: createSharingClient(base),");
|
|
1903
|
+
lines.push(" search: createSearchClient(base),");
|
|
1904
|
+
if (hasCustomerProfile) {
|
|
1905
|
+
lines.push(" profile: createProfileClient<CustomerProfileData>(base),");
|
|
1906
|
+
} else {
|
|
1907
|
+
lines.push(" profile: createProfileClient(base),");
|
|
3864
1908
|
}
|
|
3865
|
-
|
|
3866
|
-
|
|
3867
|
-
|
|
3868
|
-
|
|
3869
|
-
}
|
|
3870
|
-
|
|
3871
|
-
|
|
3872
|
-
|
|
3873
|
-
}
|
|
3874
|
-
|
|
3875
|
-
lines.push(`export const PUBLISH_${upperSnake}_VERSION = \`
|
|
3876
|
-
mutation Publish${typeName}Version($versionId: ID!) {
|
|
3877
|
-
publishVersion(versionId: $versionId)
|
|
1909
|
+
lines.push(" operations: createOperationsClient(base),");
|
|
1910
|
+
for (const model of publicModels) {
|
|
1911
|
+
const typeName = toPascalCase(model.key);
|
|
1912
|
+
const accessorName = toCamelCase(model.key);
|
|
1913
|
+
const dataType = `${typeName}Data`;
|
|
1914
|
+
const whereType = `${typeName}Where`;
|
|
1915
|
+
const sortType = `${typeName}SortField`;
|
|
1916
|
+
lines.push(
|
|
1917
|
+
` ${accessorName}: createModelAccessor<${dataType}, ${whereType}, ${sortType}>(base, '${model.key}'),`
|
|
1918
|
+
);
|
|
3878
1919
|
}
|
|
3879
|
-
|
|
1920
|
+
lines.push(` model<TData = unknown>(modelKey: string) {`);
|
|
1921
|
+
lines.push(` return createModelAccessor<TData>(base, modelKey);`);
|
|
1922
|
+
lines.push(` },`);
|
|
1923
|
+
lines.push(" request: base.request,");
|
|
1924
|
+
lines.push(" setCustomerToken: base.setCustomerToken,");
|
|
1925
|
+
lines.push(" setLocale: base.setLocale,");
|
|
1926
|
+
lines.push(" setPreview: base.setPreview,");
|
|
1927
|
+
lines.push(" } as TypedClient;");
|
|
1928
|
+
lines.push("}");
|
|
3880
1929
|
lines.push("");
|
|
3881
|
-
lines.push(
|
|
3882
|
-
|
|
3883
|
-
|
|
3884
|
-
|
|
3885
|
-
|
|
1930
|
+
lines.push("// Re-export types from @eide/foir-client for convenience");
|
|
1931
|
+
lines.push(`export type {
|
|
1932
|
+
ClientConfig,
|
|
1933
|
+
TypedRecord,
|
|
1934
|
+
TypedList,
|
|
1935
|
+
ModelAccessor,
|
|
1936
|
+
JsonValue,
|
|
1937
|
+
ImageValue,
|
|
1938
|
+
VideoValue,
|
|
1939
|
+
FileValue,
|
|
1940
|
+
LinkValue,
|
|
1941
|
+
ReferenceValue,
|
|
1942
|
+
FlexibleFieldItem,
|
|
1943
|
+
RichtextValue,
|
|
1944
|
+
CurrencyValue,
|
|
1945
|
+
SelectMap,
|
|
1946
|
+
ApplySelect,
|
|
1947
|
+
FoirClient,
|
|
1948
|
+
} from '@eide/foir-client';`);
|
|
3886
1949
|
lines.push("");
|
|
3887
|
-
|
|
3888
|
-
|
|
3889
|
-
|
|
3890
|
-
|
|
3891
|
-
|
|
3892
|
-
|
|
3893
|
-
|
|
3894
|
-
|
|
3895
|
-
}
|
|
3896
|
-
\`;`);
|
|
3897
|
-
lines.push("");
|
|
3898
|
-
lines.push(`export interface Share${typeName}Variables {
|
|
3899
|
-
recordId: string;
|
|
3900
|
-
sharedWithCustomerId: string;
|
|
3901
|
-
permission: 'VIEW' | 'EDIT' | 'ADMIN';
|
|
3902
|
-
}`);
|
|
3903
|
-
lines.push("");
|
|
3904
|
-
lines.push(`export interface Share${typeName}Result {
|
|
3905
|
-
shareRecord: ShareResult;
|
|
3906
|
-
}`);
|
|
3907
|
-
lines.push("");
|
|
3908
|
-
lines.push(`export const ${upperSnake}_SHARES = \`
|
|
3909
|
-
query ${typeName}Shares($resourceId: ID!, $status: ShareStatus) {
|
|
3910
|
-
shares(resourceType: RECORD, resourceId: $resourceId, status: $status) {
|
|
3911
|
-
id resourceType permission status acceptedAt declinedAt expiresAt createdAt revokedAt
|
|
3912
|
-
}
|
|
3913
|
-
}
|
|
3914
|
-
\`;`);
|
|
3915
|
-
lines.push("");
|
|
3916
|
-
lines.push(`export const ${pluralUpperSnake}_SHARED_WITH_ME = \`
|
|
3917
|
-
query ${pluralName}SharedWithMe($status: ShareStatus) {
|
|
3918
|
-
sharedWithMe(resourceType: RECORD, modelKey: "${model.key}", status: $status) {
|
|
3919
|
-
id resourceType permission status acceptedAt declinedAt expiresAt createdAt revokedAt
|
|
3920
|
-
record {
|
|
3921
|
-
id modelKey naturalKey data metadata
|
|
3922
|
-
publishedVersionNumber publishedAt versionNumber changeDescription
|
|
3923
|
-
createdAt updatedAt
|
|
3924
|
-
}
|
|
3925
|
-
}
|
|
3926
|
-
}
|
|
3927
|
-
\`;`);
|
|
3928
|
-
lines.push("");
|
|
3929
|
-
lines.push(`export interface ${pluralName}SharedWithMeResult {
|
|
3930
|
-
sharedWithMe: ShareWithRecord<${dataType}>[];
|
|
3931
|
-
}`);
|
|
3932
|
-
lines.push("");
|
|
3933
|
-
}
|
|
3934
|
-
return lines.join("\n");
|
|
3935
|
-
}
|
|
3936
|
-
function computeTypesRelPath(opsDir, typesDir) {
|
|
3937
|
-
const rel = path.relative(opsDir, typesDir).replace(/\\/g, "/");
|
|
3938
|
-
return rel.startsWith(".") ? rel : `./${rel}`;
|
|
3939
|
-
}
|
|
3940
|
-
|
|
3941
|
-
// src/codegen/generators/typed-operations-index.ts
|
|
3942
|
-
function generateTypedOperationsIndex(models, hasCustomerProfile) {
|
|
3943
|
-
const lines = [];
|
|
3944
|
-
lines.push(`/**
|
|
3945
|
-
* Typed GraphQL operations.
|
|
3946
|
-
*
|
|
3947
|
-
* @generated by foir \u2014 DO NOT EDIT MANUALLY
|
|
3948
|
-
*/
|
|
3949
|
-
|
|
3950
|
-
export * from './_common.js';
|
|
3951
|
-
`);
|
|
3952
|
-
for (const model of models) {
|
|
3953
|
-
lines.push(`export * from './${model.key}.js';`);
|
|
1950
|
+
for (const model of publicModels) {
|
|
1951
|
+
const typeName = toPascalCase(model.key);
|
|
1952
|
+
const dataType = `${typeName}Data`;
|
|
1953
|
+
const whereType = `${typeName}Where`;
|
|
1954
|
+
const sortType = `${typeName}SortField`;
|
|
1955
|
+
lines.push(
|
|
1956
|
+
`export type { ${dataType}, ${whereType}, ${sortType} } from './models/${model.key}.js';`
|
|
1957
|
+
);
|
|
3954
1958
|
}
|
|
3955
1959
|
if (hasCustomerProfile) {
|
|
3956
|
-
lines.push(
|
|
3957
|
-
|
|
3958
|
-
|
|
3959
|
-
return lines.join("\n");
|
|
3960
|
-
}
|
|
3961
|
-
|
|
3962
|
-
// src/codegen/generators/customer-profile-operations.ts
|
|
3963
|
-
function generateCustomerProfileOperations(typesRelPath) {
|
|
3964
|
-
return `/**
|
|
3965
|
-
* Typed operations for Customer Profiles
|
|
3966
|
-
*
|
|
3967
|
-
* @generated by foir \u2014 DO NOT EDIT MANUALLY
|
|
3968
|
-
*/
|
|
3969
|
-
|
|
3970
|
-
import type { CustomerProfileData } from '${typesRelPath}/customer-profile.js';
|
|
3971
|
-
|
|
3972
|
-
export const GET_MY_PROFILE = \`
|
|
3973
|
-
query GetMyProfile {
|
|
3974
|
-
myProfile {
|
|
3975
|
-
id
|
|
3976
|
-
customerId
|
|
3977
|
-
data
|
|
3978
|
-
createdAt
|
|
3979
|
-
updatedAt
|
|
3980
|
-
}
|
|
3981
|
-
}
|
|
3982
|
-
\`;
|
|
3983
|
-
|
|
3984
|
-
export interface GetMyProfileResult {
|
|
3985
|
-
myProfile: {
|
|
3986
|
-
id: string;
|
|
3987
|
-
customerId: string;
|
|
3988
|
-
data: CustomerProfileData;
|
|
3989
|
-
createdAt: string;
|
|
3990
|
-
updatedAt: string;
|
|
3991
|
-
} | null;
|
|
3992
|
-
}
|
|
3993
|
-
|
|
3994
|
-
export const GET_CUSTOMER_PROFILE = \`
|
|
3995
|
-
query GetCustomerProfile($customerId: ID!) {
|
|
3996
|
-
customerProfile(customerId: $customerId) {
|
|
3997
|
-
id
|
|
3998
|
-
customerId
|
|
3999
|
-
data
|
|
4000
|
-
createdAt
|
|
4001
|
-
updatedAt
|
|
4002
|
-
}
|
|
4003
|
-
}
|
|
4004
|
-
\`;
|
|
4005
|
-
|
|
4006
|
-
export interface GetCustomerProfileVariables {
|
|
4007
|
-
customerId: string;
|
|
4008
|
-
}
|
|
4009
|
-
|
|
4010
|
-
export interface GetCustomerProfileResult {
|
|
4011
|
-
customerProfile: {
|
|
4012
|
-
id: string;
|
|
4013
|
-
customerId: string;
|
|
4014
|
-
data: CustomerProfileData;
|
|
4015
|
-
createdAt: string;
|
|
4016
|
-
updatedAt: string;
|
|
4017
|
-
} | null;
|
|
4018
|
-
}
|
|
4019
|
-
|
|
4020
|
-
export const SET_MY_PROFILE = \`
|
|
4021
|
-
mutation SetMyProfile($data: JSON!) {
|
|
4022
|
-
setMyProfile(data: $data) {
|
|
4023
|
-
id
|
|
4024
|
-
customerId
|
|
4025
|
-
data
|
|
4026
|
-
createdAt
|
|
4027
|
-
updatedAt
|
|
4028
|
-
}
|
|
4029
|
-
}
|
|
4030
|
-
\`;
|
|
4031
|
-
|
|
4032
|
-
export interface SetMyProfileVariables {
|
|
4033
|
-
data: Partial<CustomerProfileData>;
|
|
4034
|
-
}
|
|
4035
|
-
|
|
4036
|
-
export interface SetMyProfileResult {
|
|
4037
|
-
setMyProfile: {
|
|
4038
|
-
id: string;
|
|
4039
|
-
customerId: string;
|
|
4040
|
-
data: CustomerProfileData;
|
|
4041
|
-
createdAt: string;
|
|
4042
|
-
updatedAt: string;
|
|
4043
|
-
};
|
|
4044
|
-
}
|
|
4045
|
-
|
|
4046
|
-
export const UPDATE_CUSTOMER_PROFILE = \`
|
|
4047
|
-
mutation UpdateCustomerProfile($customerId: ID, $input: CustomerProfileInput!) {
|
|
4048
|
-
updateCustomerProfile(customerId: $customerId, input: $input) {
|
|
4049
|
-
id
|
|
4050
|
-
customerId
|
|
4051
|
-
data
|
|
4052
|
-
createdAt
|
|
4053
|
-
updatedAt
|
|
4054
|
-
}
|
|
1960
|
+
lines.push(
|
|
1961
|
+
`export type { CustomerProfileData } from './models/customer-profile.js';`
|
|
1962
|
+
);
|
|
4055
1963
|
}
|
|
4056
|
-
|
|
4057
|
-
|
|
4058
|
-
export interface UpdateCustomerProfileVariables {
|
|
4059
|
-
customerId?: string;
|
|
4060
|
-
input: {
|
|
4061
|
-
data: Partial<CustomerProfileData>;
|
|
4062
|
-
};
|
|
1964
|
+
return lines.join("\n") + "\n";
|
|
4063
1965
|
}
|
|
4064
1966
|
|
|
4065
|
-
|
|
4066
|
-
|
|
4067
|
-
|
|
4068
|
-
|
|
4069
|
-
|
|
4070
|
-
|
|
4071
|
-
updatedAt: string;
|
|
1967
|
+
// src/codegen/generators/schema-manifest.ts
|
|
1968
|
+
function generateSchemaManifest(models, cpSchema) {
|
|
1969
|
+
const manifest = {
|
|
1970
|
+
$schema: "https://foir.io/schema/v1.json",
|
|
1971
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1972
|
+
models: {}
|
|
4072
1973
|
};
|
|
4073
|
-
}
|
|
4074
|
-
|
|
4075
|
-
export const DELETE_MY_PROFILE = \`
|
|
4076
|
-
mutation DeleteMyProfile {
|
|
4077
|
-
deleteMyProfile
|
|
4078
|
-
}
|
|
4079
|
-
\`;
|
|
4080
|
-
|
|
4081
|
-
export interface DeleteMyProfileResult {
|
|
4082
|
-
deleteMyProfile: boolean;
|
|
4083
|
-
}
|
|
4084
|
-
`;
|
|
4085
|
-
}
|
|
4086
|
-
|
|
4087
|
-
// src/codegen/generators/react-hooks.ts
|
|
4088
|
-
function generateReactHooks(model) {
|
|
4089
|
-
const typeName = toPascalCase(model.key);
|
|
4090
|
-
const upperSnake = toUpperSnakeCase(model.key);
|
|
4091
|
-
const pluralName = model.pluralName ? toPascalCase(model.pluralName.replace(/\s+/g, "")) : `${typeName}s`;
|
|
4092
|
-
const pluralUpperSnake = model.pluralName ? toUpperSnakeCase(model.pluralName.replace(/\s+/g, "")) : `${upperSnake}S`;
|
|
4093
|
-
const lines = [];
|
|
4094
|
-
lines.push(`/**
|
|
4095
|
-
* React Apollo hooks for ${model.name ?? model.key}
|
|
4096
|
-
*
|
|
4097
|
-
* @generated by foir \u2014 DO NOT EDIT MANUALLY
|
|
4098
|
-
*/
|
|
4099
|
-
|
|
4100
|
-
import { useQuery, useMutation } from '@apollo/client';
|
|
4101
|
-
import type { QueryHookOptions, MutationHookOptions } from '@apollo/client';
|
|
4102
|
-
import { gql } from '@apollo/client';
|
|
4103
|
-
import {
|
|
4104
|
-
GET_${upperSnake},
|
|
4105
|
-
GET_${upperSnake}_BY_KEY,
|
|
4106
|
-
LIST_${pluralUpperSnake},
|
|
4107
|
-
CREATE_${upperSnake},
|
|
4108
|
-
UPDATE_${upperSnake},
|
|
4109
|
-
DELETE_${upperSnake},
|
|
4110
|
-
PUBLISH_${upperSnake}_VERSION,
|
|
4111
|
-
UNPUBLISH_${upperSnake},
|
|
4112
|
-
} from '../operations/${model.key}.js';
|
|
4113
|
-
import type {
|
|
4114
|
-
${typeName}Record,
|
|
4115
|
-
Get${typeName}Variables,
|
|
4116
|
-
Get${typeName}Result,
|
|
4117
|
-
Get${typeName}ByKeyVariables,
|
|
4118
|
-
Get${typeName}ByKeyResult,
|
|
4119
|
-
List${pluralName}Variables,
|
|
4120
|
-
List${pluralName}Result,
|
|
4121
|
-
Create${typeName}Variables,
|
|
4122
|
-
Create${typeName}Result,
|
|
4123
|
-
Update${typeName}Variables,
|
|
4124
|
-
Update${typeName}Result,
|
|
4125
|
-
Delete${typeName}Variables,
|
|
4126
|
-
Delete${typeName}Result,
|
|
4127
|
-
} from '../operations/${model.key}.js';
|
|
4128
|
-
`);
|
|
4129
|
-
lines.push(`export type { ${typeName}Record };`);
|
|
4130
|
-
lines.push("");
|
|
4131
|
-
lines.push(`export function useGet${typeName}(
|
|
4132
|
-
id: string | null | undefined,
|
|
4133
|
-
options?: Omit<QueryHookOptions<Get${typeName}Result, Get${typeName}Variables>, 'variables' | 'skip'>
|
|
4134
|
-
) {
|
|
4135
|
-
return useQuery<Get${typeName}Result, Get${typeName}Variables>(
|
|
4136
|
-
gql\`\${GET_${upperSnake}}\`,
|
|
4137
|
-
{ ...options, variables: { id: id! }, skip: !id }
|
|
4138
|
-
);
|
|
4139
|
-
}`);
|
|
4140
|
-
lines.push("");
|
|
4141
|
-
lines.push(`export function useGet${typeName}ByKey(
|
|
4142
|
-
naturalKey: string | null | undefined,
|
|
4143
|
-
options?: Omit<QueryHookOptions<Get${typeName}ByKeyResult, Get${typeName}ByKeyVariables>, 'variables' | 'skip'>
|
|
4144
|
-
) {
|
|
4145
|
-
return useQuery<Get${typeName}ByKeyResult, Get${typeName}ByKeyVariables>(
|
|
4146
|
-
gql\`\${GET_${upperSnake}_BY_KEY}\`,
|
|
4147
|
-
{ ...options, variables: { naturalKey: naturalKey! }, skip: !naturalKey }
|
|
4148
|
-
);
|
|
4149
|
-
}`);
|
|
4150
|
-
lines.push("");
|
|
4151
|
-
lines.push(`export function useList${pluralName}(
|
|
4152
|
-
variables?: List${pluralName}Variables,
|
|
4153
|
-
options?: Omit<QueryHookOptions<List${pluralName}Result, List${pluralName}Variables>, 'variables'>
|
|
4154
|
-
) {
|
|
4155
|
-
return useQuery<List${pluralName}Result, List${pluralName}Variables>(
|
|
4156
|
-
gql\`\${LIST_${pluralUpperSnake}}\`,
|
|
4157
|
-
{ ...options, variables }
|
|
4158
|
-
);
|
|
4159
|
-
}`);
|
|
4160
|
-
lines.push("");
|
|
4161
|
-
lines.push(`export function useCreate${typeName}(
|
|
4162
|
-
options?: MutationHookOptions<Create${typeName}Result, Create${typeName}Variables>
|
|
4163
|
-
) {
|
|
4164
|
-
return useMutation<Create${typeName}Result, Create${typeName}Variables>(
|
|
4165
|
-
gql\`\${CREATE_${upperSnake}}\`,
|
|
4166
|
-
options
|
|
4167
|
-
);
|
|
4168
|
-
}`);
|
|
4169
|
-
lines.push("");
|
|
4170
|
-
lines.push(`export function useUpdate${typeName}(
|
|
4171
|
-
options?: MutationHookOptions<Update${typeName}Result, Update${typeName}Variables>
|
|
4172
|
-
) {
|
|
4173
|
-
return useMutation<Update${typeName}Result, Update${typeName}Variables>(
|
|
4174
|
-
gql\`\${UPDATE_${upperSnake}}\`,
|
|
4175
|
-
options
|
|
4176
|
-
);
|
|
4177
|
-
}`);
|
|
4178
|
-
lines.push("");
|
|
4179
|
-
lines.push(`export function useDelete${typeName}(
|
|
4180
|
-
options?: MutationHookOptions<Delete${typeName}Result, Delete${typeName}Variables>
|
|
4181
|
-
) {
|
|
4182
|
-
return useMutation<Delete${typeName}Result, Delete${typeName}Variables>(
|
|
4183
|
-
gql\`\${DELETE_${upperSnake}}\`,
|
|
4184
|
-
options
|
|
4185
|
-
);
|
|
4186
|
-
}`);
|
|
4187
|
-
lines.push("");
|
|
4188
|
-
lines.push(`export function usePublish${typeName}Version(
|
|
4189
|
-
options?: MutationHookOptions<{ publishVersion: boolean }, { versionId: string }>
|
|
4190
|
-
) {
|
|
4191
|
-
return useMutation<{ publishVersion: boolean }, { versionId: string }>(
|
|
4192
|
-
gql\`\${PUBLISH_${upperSnake}_VERSION}\`,
|
|
4193
|
-
options
|
|
4194
|
-
);
|
|
4195
|
-
}`);
|
|
4196
|
-
lines.push("");
|
|
4197
|
-
lines.push(`export function useUnpublish${typeName}(
|
|
4198
|
-
options?: MutationHookOptions<{ unpublishRecord: boolean }, { id: string }>
|
|
4199
|
-
) {
|
|
4200
|
-
return useMutation<{ unpublishRecord: boolean }, { id: string }>(
|
|
4201
|
-
gql\`\${UNPUBLISH_${upperSnake}}\`,
|
|
4202
|
-
options
|
|
4203
|
-
);
|
|
4204
|
-
}`);
|
|
4205
|
-
lines.push("");
|
|
4206
|
-
return lines.join("\n");
|
|
4207
|
-
}
|
|
4208
|
-
|
|
4209
|
-
// src/codegen/generators/react-hooks-index.ts
|
|
4210
|
-
function generateReactHooksIndex(models, hasCustomerProfile) {
|
|
4211
|
-
const lines = [];
|
|
4212
|
-
lines.push(`/**
|
|
4213
|
-
* React Apollo hooks for all models.
|
|
4214
|
-
*
|
|
4215
|
-
* @generated by foir \u2014 DO NOT EDIT MANUALLY
|
|
4216
|
-
*/
|
|
4217
|
-
`);
|
|
4218
1974
|
for (const model of models) {
|
|
4219
|
-
|
|
1975
|
+
manifest.models[model.key] = {
|
|
1976
|
+
name: model.name,
|
|
1977
|
+
pluralName: model.pluralName,
|
|
1978
|
+
description: model.description,
|
|
1979
|
+
category: model.category,
|
|
1980
|
+
config: model.config,
|
|
1981
|
+
fields: model.fields.map((f) => ({
|
|
1982
|
+
key: f.key,
|
|
1983
|
+
type: f.type,
|
|
1984
|
+
label: f.label,
|
|
1985
|
+
required: f.required,
|
|
1986
|
+
helpText: f.helpText,
|
|
1987
|
+
options: f.options
|
|
1988
|
+
}))
|
|
1989
|
+
};
|
|
4220
1990
|
}
|
|
4221
|
-
if (
|
|
4222
|
-
|
|
1991
|
+
if (cpSchema && cpSchema.fields.length > 0) {
|
|
1992
|
+
manifest.customerProfile = {
|
|
1993
|
+
fields: cpSchema.fields.map((f) => ({
|
|
1994
|
+
key: f.key,
|
|
1995
|
+
type: f.type,
|
|
1996
|
+
label: f.label,
|
|
1997
|
+
required: f.required,
|
|
1998
|
+
helpText: f.helpText,
|
|
1999
|
+
options: f.options
|
|
2000
|
+
}))
|
|
2001
|
+
};
|
|
4223
2002
|
}
|
|
4224
|
-
|
|
4225
|
-
return lines.join("\n");
|
|
4226
|
-
}
|
|
4227
|
-
|
|
4228
|
-
// src/codegen/generators/customer-profile-hooks.ts
|
|
4229
|
-
function generateCustomerProfileHooks() {
|
|
4230
|
-
return `/**
|
|
4231
|
-
* React Apollo hooks for Customer Profiles
|
|
4232
|
-
*
|
|
4233
|
-
* @generated by foir \u2014 DO NOT EDIT MANUALLY
|
|
4234
|
-
*/
|
|
4235
|
-
|
|
4236
|
-
import { useQuery, useMutation } from '@apollo/client';
|
|
4237
|
-
import type { QueryHookOptions, MutationHookOptions } from '@apollo/client';
|
|
4238
|
-
import { gql } from '@apollo/client';
|
|
4239
|
-
import {
|
|
4240
|
-
GET_MY_PROFILE,
|
|
4241
|
-
GET_CUSTOMER_PROFILE,
|
|
4242
|
-
SET_MY_PROFILE,
|
|
4243
|
-
UPDATE_CUSTOMER_PROFILE,
|
|
4244
|
-
DELETE_MY_PROFILE,
|
|
4245
|
-
} from '../operations/customer-profile.js';
|
|
4246
|
-
import type {
|
|
4247
|
-
GetMyProfileResult,
|
|
4248
|
-
GetCustomerProfileVariables,
|
|
4249
|
-
GetCustomerProfileResult,
|
|
4250
|
-
SetMyProfileVariables,
|
|
4251
|
-
SetMyProfileResult,
|
|
4252
|
-
UpdateCustomerProfileVariables,
|
|
4253
|
-
UpdateCustomerProfileResult,
|
|
4254
|
-
DeleteMyProfileResult,
|
|
4255
|
-
} from '../operations/customer-profile.js';
|
|
4256
|
-
|
|
4257
|
-
export function useMyProfile(
|
|
4258
|
-
options?: QueryHookOptions<GetMyProfileResult>
|
|
4259
|
-
) {
|
|
4260
|
-
return useQuery<GetMyProfileResult>(
|
|
4261
|
-
gql\`\${GET_MY_PROFILE}\`,
|
|
4262
|
-
options
|
|
4263
|
-
);
|
|
4264
|
-
}
|
|
4265
|
-
|
|
4266
|
-
export function useCustomerProfile(
|
|
4267
|
-
customerId: string | null | undefined,
|
|
4268
|
-
options?: Omit<QueryHookOptions<GetCustomerProfileResult, GetCustomerProfileVariables>, 'variables' | 'skip'>
|
|
4269
|
-
) {
|
|
4270
|
-
return useQuery<GetCustomerProfileResult, GetCustomerProfileVariables>(
|
|
4271
|
-
gql\`\${GET_CUSTOMER_PROFILE}\`,
|
|
4272
|
-
{ ...options, variables: { customerId: customerId! }, skip: !customerId }
|
|
4273
|
-
);
|
|
4274
|
-
}
|
|
4275
|
-
|
|
4276
|
-
export function useSetMyProfile(
|
|
4277
|
-
options?: MutationHookOptions<SetMyProfileResult, SetMyProfileVariables>
|
|
4278
|
-
) {
|
|
4279
|
-
return useMutation<SetMyProfileResult, SetMyProfileVariables>(
|
|
4280
|
-
gql\`\${SET_MY_PROFILE}\`,
|
|
4281
|
-
options
|
|
4282
|
-
);
|
|
4283
|
-
}
|
|
4284
|
-
|
|
4285
|
-
export function useUpdateCustomerProfile(
|
|
4286
|
-
options?: MutationHookOptions<UpdateCustomerProfileResult, UpdateCustomerProfileVariables>
|
|
4287
|
-
) {
|
|
4288
|
-
return useMutation<UpdateCustomerProfileResult, UpdateCustomerProfileVariables>(
|
|
4289
|
-
gql\`\${UPDATE_CUSTOMER_PROFILE}\`,
|
|
4290
|
-
options
|
|
4291
|
-
);
|
|
4292
|
-
}
|
|
4293
|
-
|
|
4294
|
-
export function useDeleteMyProfile(
|
|
4295
|
-
options?: MutationHookOptions<DeleteMyProfileResult>
|
|
4296
|
-
) {
|
|
4297
|
-
return useMutation<DeleteMyProfileResult>(
|
|
4298
|
-
gql\`\${DELETE_MY_PROFILE}\`,
|
|
4299
|
-
options
|
|
4300
|
-
);
|
|
4301
|
-
}
|
|
4302
|
-
`;
|
|
4303
|
-
}
|
|
4304
|
-
|
|
4305
|
-
// src/codegen/generators/remix-loaders.ts
|
|
4306
|
-
function generateRemixLoaders(model) {
|
|
4307
|
-
const typeName = toPascalCase(model.key);
|
|
4308
|
-
const upperSnake = toUpperSnakeCase(model.key);
|
|
4309
|
-
const pluralName = model.pluralName ? toPascalCase(model.pluralName.replace(/\s+/g, "")) : `${typeName}s`;
|
|
4310
|
-
const pluralUpperSnake = model.pluralName ? toUpperSnakeCase(model.pluralName.replace(/\s+/g, "")) : `${upperSnake}S`;
|
|
4311
|
-
return `/**
|
|
4312
|
-
* Remix / server-side loader functions for ${model.name ?? model.key}
|
|
4313
|
-
*
|
|
4314
|
-
* @generated by foir \u2014 DO NOT EDIT MANUALLY
|
|
4315
|
-
*/
|
|
4316
|
-
|
|
4317
|
-
import {
|
|
4318
|
-
GET_${upperSnake},
|
|
4319
|
-
GET_${upperSnake}_BY_KEY,
|
|
4320
|
-
LIST_${pluralUpperSnake},
|
|
4321
|
-
CREATE_${upperSnake},
|
|
4322
|
-
UPDATE_${upperSnake},
|
|
4323
|
-
DELETE_${upperSnake},
|
|
4324
|
-
PUBLISH_${upperSnake}_VERSION,
|
|
4325
|
-
UNPUBLISH_${upperSnake},
|
|
4326
|
-
} from '../operations/${model.key}.js';
|
|
4327
|
-
import type {
|
|
4328
|
-
Get${typeName}Variables,
|
|
4329
|
-
Get${typeName}Result,
|
|
4330
|
-
Get${typeName}ByKeyVariables,
|
|
4331
|
-
Get${typeName}ByKeyResult,
|
|
4332
|
-
List${pluralName}Variables,
|
|
4333
|
-
List${pluralName}Result,
|
|
4334
|
-
Create${typeName}Variables,
|
|
4335
|
-
Create${typeName}Result,
|
|
4336
|
-
Update${typeName}Variables,
|
|
4337
|
-
Update${typeName}Result,
|
|
4338
|
-
Delete${typeName}Variables,
|
|
4339
|
-
Delete${typeName}Result,
|
|
4340
|
-
} from '../operations/${model.key}.js';
|
|
4341
|
-
|
|
4342
|
-
/** A minimal GraphQL client interface (works with graphql-request, urql, or custom). */
|
|
4343
|
-
export interface GraphQLClient {
|
|
4344
|
-
request<T>(query: string, variables?: Record<string, unknown>): Promise<T>;
|
|
2003
|
+
return JSON.stringify(manifest, null, 2) + "\n";
|
|
4345
2004
|
}
|
|
4346
2005
|
|
|
4347
|
-
|
|
4348
|
-
|
|
4349
|
-
|
|
4350
|
-
): Promise<Get${typeName}Result> {
|
|
4351
|
-
return client.request<Get${typeName}Result>(GET_${upperSnake}, variables as Record<string, unknown>);
|
|
2006
|
+
// src/codegen/generators/model-filters.ts
|
|
2007
|
+
function isInlineOnlyModel3(model) {
|
|
2008
|
+
return model.config.inline && !model.config.records;
|
|
4352
2009
|
}
|
|
4353
|
-
|
|
4354
|
-
|
|
4355
|
-
|
|
4356
|
-
|
|
4357
|
-
|
|
4358
|
-
|
|
2010
|
+
function getFilterType(fieldType) {
|
|
2011
|
+
switch (fieldType) {
|
|
2012
|
+
case "text":
|
|
2013
|
+
case "richtext":
|
|
2014
|
+
case "email":
|
|
2015
|
+
case "phone":
|
|
2016
|
+
case "url":
|
|
2017
|
+
case "select":
|
|
2018
|
+
return "StringFilter";
|
|
2019
|
+
case "number":
|
|
2020
|
+
case "currency":
|
|
2021
|
+
return "NumberFilter";
|
|
2022
|
+
case "boolean":
|
|
2023
|
+
return "BooleanFilter";
|
|
2024
|
+
case "date":
|
|
2025
|
+
return "DateFilter";
|
|
2026
|
+
default:
|
|
2027
|
+
return null;
|
|
2028
|
+
}
|
|
4359
2029
|
}
|
|
4360
|
-
|
|
4361
|
-
|
|
4362
|
-
client: GraphQLClient,
|
|
4363
|
-
variables?: List${pluralName}Variables
|
|
4364
|
-
): Promise<List${pluralName}Result> {
|
|
4365
|
-
return client.request<List${pluralName}Result>(LIST_${pluralUpperSnake}, variables as Record<string, unknown>);
|
|
2030
|
+
function isSortable(fieldType) {
|
|
2031
|
+
return ["text", "number", "date", "boolean", "email", "url", "select"].includes(fieldType);
|
|
4366
2032
|
}
|
|
4367
|
-
|
|
4368
|
-
|
|
4369
|
-
|
|
4370
|
-
|
|
4371
|
-
|
|
4372
|
-
|
|
2033
|
+
function generateModelWhere(model) {
|
|
2034
|
+
if (isInlineOnlyModel3(model)) return "";
|
|
2035
|
+
const typeName = toPascalCase(model.key);
|
|
2036
|
+
const whereName = `${typeName}Where`;
|
|
2037
|
+
const fields = model.fields ?? [];
|
|
2038
|
+
const filterImports = /* @__PURE__ */ new Set();
|
|
2039
|
+
const filterFields = [];
|
|
2040
|
+
for (const field of fields) {
|
|
2041
|
+
const filterType = getFilterType(field.type);
|
|
2042
|
+
if (filterType) {
|
|
2043
|
+
filterImports.add(filterType);
|
|
2044
|
+
const fieldName = sanitizeFieldName(field.key);
|
|
2045
|
+
filterFields.push(` ${fieldName}?: ${filterType};`);
|
|
2046
|
+
}
|
|
2047
|
+
}
|
|
2048
|
+
filterImports.add("DateFilter");
|
|
2049
|
+
filterFields.push(" createdAt?: DateFilter;");
|
|
2050
|
+
filterFields.push(" updatedAt?: DateFilter;");
|
|
2051
|
+
const imports = Array.from(filterImports).sort().join(", ");
|
|
2052
|
+
const lines = [];
|
|
2053
|
+
lines.push(`import type { ${imports} } from '@eide/foir-client';`);
|
|
2054
|
+
lines.push("");
|
|
2055
|
+
lines.push(`export interface ${whereName} {`);
|
|
2056
|
+
lines.push(...filterFields);
|
|
2057
|
+
lines.push(` AND?: ${whereName}[];`);
|
|
2058
|
+
lines.push(` OR?: ${whereName}[];`);
|
|
2059
|
+
lines.push(` NOT?: ${whereName};`);
|
|
2060
|
+
lines.push("}");
|
|
2061
|
+
return lines.join("\n") + "\n";
|
|
4373
2062
|
}
|
|
4374
|
-
|
|
4375
|
-
|
|
4376
|
-
|
|
4377
|
-
|
|
4378
|
-
|
|
4379
|
-
|
|
2063
|
+
function generateModelSortField(model) {
|
|
2064
|
+
if (isInlineOnlyModel3(model)) return "";
|
|
2065
|
+
const typeName = toPascalCase(model.key);
|
|
2066
|
+
const sortName = `${typeName}SortField`;
|
|
2067
|
+
const fields = model.fields ?? [];
|
|
2068
|
+
const sortableFields = [];
|
|
2069
|
+
for (const field of fields) {
|
|
2070
|
+
if (isSortable(field.type)) {
|
|
2071
|
+
sortableFields.push(`'${field.key}'`);
|
|
2072
|
+
}
|
|
2073
|
+
}
|
|
2074
|
+
sortableFields.push(`'createdAt'`);
|
|
2075
|
+
sortableFields.push(`'updatedAt'`);
|
|
2076
|
+
const unique = [...new Set(sortableFields)];
|
|
2077
|
+
return `export type ${sortName} = ${unique.join(" | ")};
|
|
2078
|
+
`;
|
|
4380
2079
|
}
|
|
4381
2080
|
|
|
4382
|
-
|
|
4383
|
-
|
|
4384
|
-
|
|
4385
|
-
): Promise<Delete${typeName}Result> {
|
|
4386
|
-
return client.request<Delete${typeName}Result>(DELETE_${upperSnake}, variables as Record<string, unknown>);
|
|
2081
|
+
// src/codegen/generators/model-zod.ts
|
|
2082
|
+
function isInlineOnlyModel4(model) {
|
|
2083
|
+
return model.config.inline && !model.config.records;
|
|
4387
2084
|
}
|
|
4388
|
-
|
|
4389
|
-
|
|
4390
|
-
|
|
4391
|
-
|
|
4392
|
-
|
|
4393
|
-
|
|
2085
|
+
function getZodExpression(field, allModels) {
|
|
2086
|
+
const imports = /* @__PURE__ */ new Set();
|
|
2087
|
+
let expr;
|
|
2088
|
+
switch (field.type) {
|
|
2089
|
+
case "text":
|
|
2090
|
+
case "email":
|
|
2091
|
+
case "phone":
|
|
2092
|
+
case "url": {
|
|
2093
|
+
imports.add("textField");
|
|
2094
|
+
const opts = [];
|
|
2095
|
+
if (field.options?.maxLength) opts.push(`maxLength: ${field.options.maxLength}`);
|
|
2096
|
+
if (field.options?.minLength) opts.push(`minLength: ${field.options.minLength}`);
|
|
2097
|
+
if (field.options?.pattern) opts.push(`pattern: '${field.options.pattern}'`);
|
|
2098
|
+
expr = opts.length > 0 ? `textField({ ${opts.join(", ")} })` : `textField()`;
|
|
2099
|
+
break;
|
|
2100
|
+
}
|
|
2101
|
+
case "number": {
|
|
2102
|
+
imports.add("numberField");
|
|
2103
|
+
const opts = [];
|
|
2104
|
+
if (field.options?.min !== void 0) opts.push(`min: ${field.options.min}`);
|
|
2105
|
+
if (field.options?.max !== void 0) opts.push(`max: ${field.options.max}`);
|
|
2106
|
+
expr = opts.length > 0 ? `numberField({ ${opts.join(", ")} })` : `numberField()`;
|
|
2107
|
+
break;
|
|
2108
|
+
}
|
|
2109
|
+
case "boolean":
|
|
2110
|
+
imports.add("booleanField");
|
|
2111
|
+
expr = "booleanField()";
|
|
2112
|
+
break;
|
|
2113
|
+
case "date":
|
|
2114
|
+
imports.add("dateField");
|
|
2115
|
+
expr = "dateField()";
|
|
2116
|
+
break;
|
|
2117
|
+
case "richtext":
|
|
2118
|
+
imports.add("richtextValueSchema");
|
|
2119
|
+
expr = "richtextValueSchema";
|
|
2120
|
+
break;
|
|
2121
|
+
case "image":
|
|
2122
|
+
imports.add("imageValueSchema");
|
|
2123
|
+
expr = "imageValueSchema";
|
|
2124
|
+
break;
|
|
2125
|
+
case "video":
|
|
2126
|
+
imports.add("videoValueSchema");
|
|
2127
|
+
expr = "videoValueSchema";
|
|
2128
|
+
break;
|
|
2129
|
+
case "file":
|
|
2130
|
+
imports.add("fileValueSchema");
|
|
2131
|
+
expr = "fileValueSchema";
|
|
2132
|
+
break;
|
|
2133
|
+
case "currency":
|
|
2134
|
+
imports.add("currencyValueSchema");
|
|
2135
|
+
expr = "currencyValueSchema";
|
|
2136
|
+
break;
|
|
2137
|
+
case "link":
|
|
2138
|
+
imports.add("linkValueSchema");
|
|
2139
|
+
expr = "linkValueSchema";
|
|
2140
|
+
break;
|
|
2141
|
+
case "reference":
|
|
2142
|
+
imports.add("referenceValueSchema");
|
|
2143
|
+
expr = "referenceValueSchema";
|
|
2144
|
+
break;
|
|
2145
|
+
case "select": {
|
|
2146
|
+
if (field.options?.options) {
|
|
2147
|
+
imports.add("selectField");
|
|
2148
|
+
const options = field.options.options;
|
|
2149
|
+
const values = options.map((o) => `'${o.value}'`).join(", ");
|
|
2150
|
+
expr = `selectField([${values}])`;
|
|
2151
|
+
} else {
|
|
2152
|
+
expr = "z.string()";
|
|
2153
|
+
}
|
|
2154
|
+
break;
|
|
2155
|
+
}
|
|
2156
|
+
case "multiselect": {
|
|
2157
|
+
if (field.options?.options) {
|
|
2158
|
+
imports.add("multiselectField");
|
|
2159
|
+
const options = field.options.options;
|
|
2160
|
+
const values = options.map((o) => `'${o.value}'`).join(", ");
|
|
2161
|
+
expr = `multiselectField([${values}])`;
|
|
2162
|
+
} else {
|
|
2163
|
+
expr = "z.array(z.string())";
|
|
2164
|
+
}
|
|
2165
|
+
break;
|
|
2166
|
+
}
|
|
2167
|
+
case "flexible":
|
|
2168
|
+
imports.add("flexibleFieldItemSchema");
|
|
2169
|
+
expr = "z.array(flexibleFieldItemSchema)";
|
|
2170
|
+
break;
|
|
2171
|
+
case "json":
|
|
2172
|
+
imports.add("jsonValueSchema");
|
|
2173
|
+
expr = "jsonValueSchema";
|
|
2174
|
+
break;
|
|
2175
|
+
case "list": {
|
|
2176
|
+
if (field.options?.itemType) {
|
|
2177
|
+
const itemType = field.options.itemType;
|
|
2178
|
+
const refModel = allModels.find((m) => m.key === itemType);
|
|
2179
|
+
if (refModel) {
|
|
2180
|
+
const refSchemaName = `${toPascalCase(itemType)}${isInlineOnlyModel4(refModel) ? "" : "Data"}Schema`;
|
|
2181
|
+
expr = `z.array(${refSchemaName})`;
|
|
2182
|
+
} else {
|
|
2183
|
+
imports.add("jsonValueSchema");
|
|
2184
|
+
expr = "z.array(jsonValueSchema)";
|
|
2185
|
+
}
|
|
2186
|
+
} else {
|
|
2187
|
+
imports.add("jsonValueSchema");
|
|
2188
|
+
expr = "z.array(jsonValueSchema)";
|
|
2189
|
+
}
|
|
2190
|
+
break;
|
|
2191
|
+
}
|
|
2192
|
+
default: {
|
|
2193
|
+
const refModel = allModels.find((m) => m.key === field.type);
|
|
2194
|
+
if (refModel) {
|
|
2195
|
+
const refSchemaName = `${toPascalCase(field.type)}${isInlineOnlyModel4(refModel) ? "" : "Data"}Schema`;
|
|
2196
|
+
expr = refSchemaName;
|
|
2197
|
+
} else {
|
|
2198
|
+
imports.add("jsonValueSchema");
|
|
2199
|
+
expr = "jsonValueSchema";
|
|
2200
|
+
}
|
|
2201
|
+
}
|
|
2202
|
+
}
|
|
2203
|
+
if (!field.required) {
|
|
2204
|
+
expr = `${expr}.optional()`;
|
|
2205
|
+
}
|
|
2206
|
+
return { expression: expr, imports };
|
|
4394
2207
|
}
|
|
2208
|
+
function generateModelZodSchema(model, allModels) {
|
|
2209
|
+
const typeName = toPascalCase(model.key);
|
|
2210
|
+
const schemaName = isInlineOnlyModel4(model) ? `${typeName}Schema` : `${typeName}DataSchema`;
|
|
2211
|
+
const fields = model.fields ?? [];
|
|
2212
|
+
if (fields.length === 0) {
|
|
2213
|
+
return `import { z } from '@eide/foir-client/validation';
|
|
4395
2214
|
|
|
4396
|
-
export
|
|
4397
|
-
client: GraphQLClient,
|
|
4398
|
-
id: string
|
|
4399
|
-
): Promise<{ unpublishRecord: boolean }> {
|
|
4400
|
-
return client.request<{ unpublishRecord: boolean }>(UNPUBLISH_${upperSnake}, { id });
|
|
4401
|
-
}
|
|
2215
|
+
export const ${schemaName} = z.object({});
|
|
4402
2216
|
`;
|
|
4403
|
-
}
|
|
4404
|
-
|
|
4405
|
-
|
|
4406
|
-
|
|
2217
|
+
}
|
|
2218
|
+
const allImports = /* @__PURE__ */ new Set();
|
|
2219
|
+
const fieldLines = [];
|
|
2220
|
+
const inlineSchemaImports = [];
|
|
2221
|
+
for (const field of fields) {
|
|
2222
|
+
const { expression, imports } = getZodExpression(field, allModels);
|
|
2223
|
+
for (const imp of imports) allImports.add(imp);
|
|
2224
|
+
const fieldName = sanitizeFieldName(field.key);
|
|
2225
|
+
fieldLines.push(` ${fieldName}: ${expression},`);
|
|
2226
|
+
if (!isPrimitiveFieldType(field.type) && field.type !== "list") {
|
|
2227
|
+
const refModel = allModels.find((m) => m.key === field.type);
|
|
2228
|
+
if (refModel && refModel.key !== model.key) {
|
|
2229
|
+
const refSchemaName = `${toPascalCase(field.type)}${isInlineOnlyModel4(refModel) ? "" : "Data"}Schema`;
|
|
2230
|
+
inlineSchemaImports.push(
|
|
2231
|
+
`import { ${refSchemaName} } from './${field.type}.zod.js';`
|
|
2232
|
+
);
|
|
2233
|
+
}
|
|
2234
|
+
}
|
|
2235
|
+
if (field.type === "list" && field.options?.itemType) {
|
|
2236
|
+
const itemType = field.options.itemType;
|
|
2237
|
+
const refModel = allModels.find((m) => m.key === itemType);
|
|
2238
|
+
if (refModel && refModel.key !== model.key) {
|
|
2239
|
+
const refSchemaName = `${toPascalCase(itemType)}${isInlineOnlyModel4(refModel) ? "" : "Data"}Schema`;
|
|
2240
|
+
inlineSchemaImports.push(
|
|
2241
|
+
`import { ${refSchemaName} } from './${itemType}.zod.js';`
|
|
2242
|
+
);
|
|
2243
|
+
}
|
|
2244
|
+
}
|
|
2245
|
+
}
|
|
4407
2246
|
const lines = [];
|
|
4408
|
-
lines.push(
|
|
4409
|
-
*
|
|
4410
|
-
|
|
4411
|
-
* @generated by foir \u2014 DO NOT EDIT MANUALLY
|
|
4412
|
-
|
|
4413
|
-
|
|
4414
|
-
|
|
4415
|
-
|
|
2247
|
+
lines.push(`/**`);
|
|
2248
|
+
lines.push(` * Zod validation schema for ${model.name}`);
|
|
2249
|
+
lines.push(` *`);
|
|
2250
|
+
lines.push(` * @generated by foir \u2014 DO NOT EDIT MANUALLY`);
|
|
2251
|
+
lines.push(` */`);
|
|
2252
|
+
lines.push("");
|
|
2253
|
+
lines.push(`import { z } from '@eide/foir-client/validation';`);
|
|
2254
|
+
if (allImports.size > 0) {
|
|
2255
|
+
const sorted = Array.from(allImports).sort();
|
|
2256
|
+
lines.push(`import { ${sorted.join(", ")} } from '@eide/foir-client/validation';`);
|
|
4416
2257
|
}
|
|
4417
|
-
|
|
4418
|
-
|
|
2258
|
+
const uniqueInlineImports = [...new Set(inlineSchemaImports)];
|
|
2259
|
+
for (const imp of uniqueInlineImports) {
|
|
2260
|
+
lines.push(imp);
|
|
4419
2261
|
}
|
|
4420
2262
|
lines.push("");
|
|
4421
|
-
|
|
2263
|
+
lines.push(`export const ${schemaName} = z.object({`);
|
|
2264
|
+
lines.push(...fieldLines);
|
|
2265
|
+
lines.push("});");
|
|
2266
|
+
lines.push("");
|
|
2267
|
+
lines.push(`export type ${isInlineOnlyModel4(model) ? typeName : typeName + "Data"}Validated = z.infer<typeof ${schemaName}>;`);
|
|
2268
|
+
return lines.join("\n") + "\n";
|
|
4422
2269
|
}
|
|
4423
2270
|
|
|
4424
|
-
// src/codegen/generators/customer-profile-
|
|
4425
|
-
function
|
|
4426
|
-
|
|
4427
|
-
|
|
2271
|
+
// src/codegen/generators/customer-profile-types.ts
|
|
2272
|
+
function generateCustomerProfileTypes(schema) {
|
|
2273
|
+
const fields = schema.fields ?? [];
|
|
2274
|
+
const fieldTypeImports = getFieldTypeImportsForFields2(fields);
|
|
2275
|
+
let code = `/**
|
|
2276
|
+
* Customer Profile Types
|
|
4428
2277
|
*
|
|
4429
|
-
* @generated by foir \u2014 DO NOT EDIT MANUALLY
|
|
2278
|
+
* @generated by foir from customer profile schema (version ${schema.version}) \u2014 DO NOT EDIT MANUALLY
|
|
4430
2279
|
*/
|
|
4431
2280
|
|
|
4432
|
-
|
|
4433
|
-
|
|
4434
|
-
|
|
4435
|
-
|
|
4436
|
-
UPDATE_CUSTOMER_PROFILE,
|
|
4437
|
-
DELETE_MY_PROFILE,
|
|
4438
|
-
} from '../operations/customer-profile.js';
|
|
4439
|
-
import type {
|
|
4440
|
-
GetMyProfileResult,
|
|
4441
|
-
GetCustomerProfileVariables,
|
|
4442
|
-
GetCustomerProfileResult,
|
|
4443
|
-
SetMyProfileVariables,
|
|
4444
|
-
SetMyProfileResult,
|
|
4445
|
-
UpdateCustomerProfileVariables,
|
|
4446
|
-
UpdateCustomerProfileResult,
|
|
4447
|
-
DeleteMyProfileResult,
|
|
4448
|
-
} from '../operations/customer-profile.js';
|
|
4449
|
-
|
|
4450
|
-
/** A minimal GraphQL client interface (works with graphql-request, urql, or custom). */
|
|
4451
|
-
export interface GraphQLClient {
|
|
4452
|
-
request<T>(query: string, variables?: Record<string, unknown>): Promise<T>;
|
|
4453
|
-
}
|
|
4454
|
-
|
|
4455
|
-
export async function getMyProfile(
|
|
4456
|
-
client: GraphQLClient
|
|
4457
|
-
): Promise<GetMyProfileResult> {
|
|
4458
|
-
return client.request<GetMyProfileResult>(GET_MY_PROFILE);
|
|
4459
|
-
}
|
|
4460
|
-
|
|
4461
|
-
export async function getCustomerProfile(
|
|
4462
|
-
client: GraphQLClient,
|
|
4463
|
-
variables: GetCustomerProfileVariables
|
|
4464
|
-
): Promise<GetCustomerProfileResult> {
|
|
4465
|
-
return client.request<GetCustomerProfileResult>(GET_CUSTOMER_PROFILE, variables as Record<string, unknown>);
|
|
4466
|
-
}
|
|
4467
|
-
|
|
4468
|
-
export async function setMyProfile(
|
|
4469
|
-
client: GraphQLClient,
|
|
4470
|
-
variables: SetMyProfileVariables
|
|
4471
|
-
): Promise<SetMyProfileResult> {
|
|
4472
|
-
return client.request<SetMyProfileResult>(SET_MY_PROFILE, variables as Record<string, unknown>);
|
|
4473
|
-
}
|
|
4474
|
-
|
|
4475
|
-
export async function updateCustomerProfile(
|
|
4476
|
-
client: GraphQLClient,
|
|
4477
|
-
variables: UpdateCustomerProfileVariables
|
|
4478
|
-
): Promise<UpdateCustomerProfileResult> {
|
|
4479
|
-
return client.request<UpdateCustomerProfileResult>(UPDATE_CUSTOMER_PROFILE, variables as Record<string, unknown>);
|
|
4480
|
-
}
|
|
2281
|
+
`;
|
|
2282
|
+
if (fieldTypeImports.size > 0) {
|
|
2283
|
+
const types = Array.from(fieldTypeImports).sort().join(", ");
|
|
2284
|
+
code += `import type { ${types} } from '../field-types.js';
|
|
4481
2285
|
|
|
4482
|
-
export async function deleteMyProfile(
|
|
4483
|
-
client: GraphQLClient
|
|
4484
|
-
): Promise<DeleteMyProfileResult> {
|
|
4485
|
-
return client.request<DeleteMyProfileResult>(DELETE_MY_PROFILE);
|
|
4486
|
-
}
|
|
4487
2286
|
`;
|
|
2287
|
+
}
|
|
2288
|
+
code += `/**
|
|
2289
|
+
* Customer Profile Data
|
|
2290
|
+
* Typed field values for customer profiles
|
|
2291
|
+
*
|
|
2292
|
+
* @generated from customer profile schema (version ${schema.version})
|
|
2293
|
+
*/
|
|
2294
|
+
`;
|
|
2295
|
+
code += `export interface CustomerProfileData {
|
|
2296
|
+
`;
|
|
2297
|
+
for (const field of fields) {
|
|
2298
|
+
const fieldName = sanitizeFieldName(field.key);
|
|
2299
|
+
const fieldType = getFieldType(field, "output");
|
|
2300
|
+
const optional = field.required ? "" : "?";
|
|
2301
|
+
if (field.helpText) {
|
|
2302
|
+
code += ` /** ${field.helpText} */
|
|
2303
|
+
`;
|
|
2304
|
+
}
|
|
2305
|
+
code += ` ${fieldName}${optional}: ${fieldType};
|
|
2306
|
+
`;
|
|
2307
|
+
}
|
|
2308
|
+
code += `}
|
|
2309
|
+
`;
|
|
2310
|
+
return code;
|
|
4488
2311
|
}
|
|
4489
|
-
|
|
4490
|
-
|
|
4491
|
-
|
|
4492
|
-
|
|
4493
|
-
|
|
4494
|
-
|
|
4495
|
-
|
|
4496
|
-
return res._service?.sdl ?? null;
|
|
4497
|
-
} catch {
|
|
4498
|
-
return null;
|
|
2312
|
+
function getFieldTypeImportsForFields2(fields) {
|
|
2313
|
+
const imports = /* @__PURE__ */ new Set();
|
|
2314
|
+
for (const field of fields) {
|
|
2315
|
+
const mapping = FIELD_TYPE_MAPPING[field.type];
|
|
2316
|
+
if (mapping?.needsImport === "field-types") {
|
|
2317
|
+
imports.add(mapping.outputType);
|
|
2318
|
+
}
|
|
4499
2319
|
}
|
|
2320
|
+
return imports;
|
|
4500
2321
|
}
|
|
4501
2322
|
|
|
4502
2323
|
// src/codegen/write-files.ts
|
|
@@ -4533,8 +2354,8 @@ async function writeFiles(files, usePrettier = true) {
|
|
|
4533
2354
|
// src/commands/pull.ts
|
|
4534
2355
|
function registerPullCommand(program2, globalOpts) {
|
|
4535
2356
|
program2.command("pull").description(
|
|
4536
|
-
"Generate
|
|
4537
|
-
).option("--config <path>", "Path to config file").option("--only <models>", "Comma-separated model keys to generate").option("--no-prettier", "Skip Prettier formatting").option("--dry-run", "Show what would be generated without writing").option("--out <dir>", "Override output directory
|
|
2357
|
+
"Generate typed client, model types, and validation schemas from platform models"
|
|
2358
|
+
).option("--config <path>", "Path to config file").option("--only <models>", "Comma-separated model keys to generate").option("--no-prettier", "Skip Prettier formatting").option("--dry-run", "Show what would be generated without writing").option("--out <dir>", "Override output directory").action(
|
|
4538
2359
|
withErrorHandler(
|
|
4539
2360
|
globalOpts,
|
|
4540
2361
|
async (cmdOpts) => {
|
|
@@ -4544,16 +2365,14 @@ function registerPullCommand(program2, globalOpts) {
|
|
|
4544
2365
|
only: cmdOpts.only,
|
|
4545
2366
|
noPrettier: cmdOpts.prettier === false,
|
|
4546
2367
|
dryRun: !!cmdOpts.dryRun,
|
|
4547
|
-
out: cmdOpts.out
|
|
4548
|
-
swift: cmdOpts.swift
|
|
2368
|
+
out: cmdOpts.out
|
|
4549
2369
|
};
|
|
4550
2370
|
const config2 = await loadPullConfig(flags);
|
|
4551
2371
|
const client = await createClient(opts);
|
|
4552
2372
|
console.log(chalk4.dim("Fetching models\u2026"));
|
|
4553
|
-
const [allModels, cpSchema
|
|
2373
|
+
const [allModels, cpSchema] = await Promise.all([
|
|
4554
2374
|
fetchModelsForCodegen(client),
|
|
4555
|
-
fetchCustomerProfileSchema(client)
|
|
4556
|
-
fetchPublicSchema(client)
|
|
2375
|
+
fetchCustomerProfileSchema(client)
|
|
4557
2376
|
]);
|
|
4558
2377
|
if (allModels.length === 0 && !cpSchema) {
|
|
4559
2378
|
console.log(chalk4.yellow("No models found. Nothing to generate."));
|
|
@@ -4569,166 +2388,62 @@ function registerPullCommand(program2, globalOpts) {
|
|
|
4569
2388
|
)
|
|
4570
2389
|
);
|
|
4571
2390
|
const cwd = process.cwd();
|
|
4572
|
-
const
|
|
4573
|
-
const
|
|
4574
|
-
const opsDir = resolve(cwd, config2.output.operations);
|
|
4575
|
-
const hooksDir = config2.output.hooks ? resolve(cwd, config2.output.hooks) : null;
|
|
4576
|
-
const loadersDir = config2.output.loaders ? resolve(cwd, config2.output.loaders) : null;
|
|
2391
|
+
const outDir = resolve(cwd, config2.output.types);
|
|
2392
|
+
const modelsDir = resolve(outDir, "models");
|
|
4577
2393
|
const files = [];
|
|
4578
2394
|
const hasCustomerProfile = !!(cpSchema && cpSchema.fields.length > 0);
|
|
4579
2395
|
const publicModels = models.filter(
|
|
4580
2396
|
(m) => m.config.publicApi && m.config.records
|
|
4581
2397
|
);
|
|
4582
|
-
files.push({
|
|
4583
|
-
path: resolve(typesDir, "field-types.ts"),
|
|
4584
|
-
content: generateFieldTypesFile()
|
|
4585
|
-
});
|
|
4586
|
-
files.push({
|
|
4587
|
-
path: resolve(typesDir, "config.ts"),
|
|
4588
|
-
content: generateConfigFile()
|
|
4589
|
-
});
|
|
4590
2398
|
for (const model of models) {
|
|
4591
2399
|
files.push({
|
|
4592
|
-
path: resolve(
|
|
2400
|
+
path: resolve(modelsDir, `${model.key}.ts`),
|
|
4593
2401
|
content: generateModelTypes(model, models)
|
|
4594
2402
|
});
|
|
4595
|
-
}
|
|
4596
|
-
files.push({
|
|
4597
|
-
path: resolve(typesDir, "models", "index.ts"),
|
|
4598
|
-
content: generateModelIndex(models)
|
|
4599
|
-
});
|
|
4600
|
-
if (hasCustomerProfile) {
|
|
4601
2403
|
files.push({
|
|
4602
|
-
path: resolve(
|
|
4603
|
-
content:
|
|
2404
|
+
path: resolve(modelsDir, `${model.key}.zod.ts`),
|
|
2405
|
+
content: generateModelZodSchema(model, models)
|
|
4604
2406
|
});
|
|
4605
2407
|
}
|
|
4606
|
-
files.push({
|
|
4607
|
-
path: resolve(typesDir, "index.ts"),
|
|
4608
|
-
content: generateMainIndex(hasCustomerProfile)
|
|
4609
|
-
});
|
|
4610
2408
|
for (const model of publicModels) {
|
|
4611
|
-
|
|
4612
|
-
|
|
4613
|
-
|
|
4614
|
-
|
|
2409
|
+
const whereCode = generateModelWhere(model);
|
|
2410
|
+
const sortCode = generateModelSortField(model);
|
|
2411
|
+
if (whereCode || sortCode) {
|
|
2412
|
+
files.push({
|
|
2413
|
+
path: resolve(modelsDir, `${model.key}.filters.ts`),
|
|
2414
|
+
content: `/**
|
|
2415
|
+
* Filter & sort types for ${model.name}
|
|
2416
|
+
*
|
|
2417
|
+
* @generated by foir \u2014 DO NOT EDIT MANUALLY
|
|
2418
|
+
*/
|
|
2419
|
+
|
|
2420
|
+
${whereCode}
|
|
2421
|
+
${sortCode}`
|
|
2422
|
+
});
|
|
2423
|
+
}
|
|
4615
2424
|
}
|
|
4616
|
-
|
|
4617
|
-
(m) => m.config.sharing?.enabled
|
|
4618
|
-
);
|
|
4619
|
-
if (hasSharingModels) {
|
|
2425
|
+
if (hasCustomerProfile) {
|
|
4620
2426
|
files.push({
|
|
4621
|
-
path: resolve(
|
|
4622
|
-
content:
|
|
2427
|
+
path: resolve(modelsDir, "customer-profile.ts"),
|
|
2428
|
+
content: generateCustomerProfileTypes(cpSchema)
|
|
4623
2429
|
});
|
|
4624
2430
|
}
|
|
4625
2431
|
files.push({
|
|
4626
|
-
path: resolve(
|
|
4627
|
-
content:
|
|
2432
|
+
path: resolve(modelsDir, "index.ts"),
|
|
2433
|
+
content: generateModelIndex(models)
|
|
4628
2434
|
});
|
|
4629
|
-
const staticDocs = generateStaticDocuments(config2.domains);
|
|
4630
|
-
for (const doc of staticDocs) {
|
|
4631
|
-
files.push({
|
|
4632
|
-
path: resolve(docsDir, doc.filename),
|
|
4633
|
-
content: doc.content
|
|
4634
|
-
});
|
|
4635
|
-
}
|
|
4636
|
-
if (publicSchema) {
|
|
4637
|
-
files.push({
|
|
4638
|
-
path: resolve(docsDir, "public-schema.graphql"),
|
|
4639
|
-
content: publicSchema
|
|
4640
|
-
});
|
|
4641
|
-
}
|
|
4642
|
-
const typesRelPath = computeTypesRelPath(opsDir, typesDir);
|
|
4643
2435
|
files.push({
|
|
4644
|
-
path: resolve(
|
|
4645
|
-
content:
|
|
2436
|
+
path: resolve(outDir, "client.ts"),
|
|
2437
|
+
content: generateClientFactory(publicModels, hasCustomerProfile)
|
|
4646
2438
|
});
|
|
4647
|
-
for (const model of publicModels) {
|
|
4648
|
-
files.push({
|
|
4649
|
-
path: resolve(opsDir, `${model.key}.ts`),
|
|
4650
|
-
content: generateTypedOperations(model, typesRelPath)
|
|
4651
|
-
});
|
|
4652
|
-
}
|
|
4653
|
-
if (hasCustomerProfile) {
|
|
4654
|
-
files.push({
|
|
4655
|
-
path: resolve(opsDir, "customer-profile.ts"),
|
|
4656
|
-
content: generateCustomerProfileOperations(typesRelPath)
|
|
4657
|
-
});
|
|
4658
|
-
}
|
|
4659
2439
|
files.push({
|
|
4660
|
-
path: resolve(
|
|
4661
|
-
content:
|
|
4662
|
-
|
|
4663
|
-
|
|
4664
|
-
)
|
|
2440
|
+
path: resolve(outDir, "schema.json"),
|
|
2441
|
+
content: generateSchemaManifest(models, cpSchema)
|
|
2442
|
+
});
|
|
2443
|
+
files.push({
|
|
2444
|
+
path: resolve(outDir, "index.ts"),
|
|
2445
|
+
content: generateRootIndex(hasCustomerProfile)
|
|
4665
2446
|
});
|
|
4666
|
-
if (hooksDir) {
|
|
4667
|
-
for (const model of publicModels) {
|
|
4668
|
-
files.push({
|
|
4669
|
-
path: resolve(hooksDir, `${model.key}.ts`),
|
|
4670
|
-
content: generateReactHooks(model)
|
|
4671
|
-
});
|
|
4672
|
-
}
|
|
4673
|
-
if (hasCustomerProfile) {
|
|
4674
|
-
files.push({
|
|
4675
|
-
path: resolve(hooksDir, "customer-profile.ts"),
|
|
4676
|
-
content: generateCustomerProfileHooks()
|
|
4677
|
-
});
|
|
4678
|
-
}
|
|
4679
|
-
files.push({
|
|
4680
|
-
path: resolve(hooksDir, "index.ts"),
|
|
4681
|
-
content: generateReactHooksIndex(
|
|
4682
|
-
publicModels,
|
|
4683
|
-
hasCustomerProfile
|
|
4684
|
-
)
|
|
4685
|
-
});
|
|
4686
|
-
}
|
|
4687
|
-
if (loadersDir) {
|
|
4688
|
-
for (const model of publicModels) {
|
|
4689
|
-
files.push({
|
|
4690
|
-
path: resolve(loadersDir, `${model.key}.ts`),
|
|
4691
|
-
content: generateRemixLoaders(model)
|
|
4692
|
-
});
|
|
4693
|
-
}
|
|
4694
|
-
if (hasCustomerProfile) {
|
|
4695
|
-
files.push({
|
|
4696
|
-
path: resolve(loadersDir, "customer-profile.ts"),
|
|
4697
|
-
content: generateCustomerProfileLoaders()
|
|
4698
|
-
});
|
|
4699
|
-
}
|
|
4700
|
-
files.push({
|
|
4701
|
-
path: resolve(loadersDir, "index.ts"),
|
|
4702
|
-
content: generateRemixLoadersIndex(
|
|
4703
|
-
publicModels,
|
|
4704
|
-
hasCustomerProfile
|
|
4705
|
-
)
|
|
4706
|
-
});
|
|
4707
|
-
}
|
|
4708
|
-
if (config2.output.swift) {
|
|
4709
|
-
const swiftDir = resolve(cwd, config2.output.swift);
|
|
4710
|
-
files.push({
|
|
4711
|
-
path: resolve(swiftDir, "FieldTypes.swift"),
|
|
4712
|
-
content: generateSwiftFieldTypesFile()
|
|
4713
|
-
});
|
|
4714
|
-
files.push({
|
|
4715
|
-
path: resolve(swiftDir, "ModelKeys.swift"),
|
|
4716
|
-
content: generateSwiftModelKeys(models)
|
|
4717
|
-
});
|
|
4718
|
-
for (const model of models) {
|
|
4719
|
-
const swiftTypeName = toPascalCase(model.key);
|
|
4720
|
-
files.push({
|
|
4721
|
-
path: resolve(swiftDir, `${swiftTypeName}.swift`),
|
|
4722
|
-
content: generateSwiftModelFile(model)
|
|
4723
|
-
});
|
|
4724
|
-
}
|
|
4725
|
-
if (hasCustomerProfile) {
|
|
4726
|
-
files.push({
|
|
4727
|
-
path: resolve(swiftDir, "CustomerProfile.swift"),
|
|
4728
|
-
content: generateSwiftCustomerProfileFile(cpSchema)
|
|
4729
|
-
});
|
|
4730
|
-
}
|
|
4731
|
-
}
|
|
4732
2447
|
if (config2.dryRun) {
|
|
4733
2448
|
console.log(
|
|
4734
2449
|
chalk4.bold("\nDry run \u2014 files that would be generated:\n")
|
|
@@ -4742,50 +2457,31 @@ ${chalk4.dim(`${files.length} file(s) total`)}`);
|
|
|
4742
2457
|
return;
|
|
4743
2458
|
}
|
|
4744
2459
|
await writeFiles(files, config2.prettier);
|
|
4745
|
-
const
|
|
4746
|
-
const docCount = publicModels.length + staticDocs.length;
|
|
4747
|
-
const opsCount = publicModels.length + (hasCustomerProfile ? 1 : 0) + 2;
|
|
4748
|
-
const hookCount = hooksDir ? publicModels.length + (hasCustomerProfile ? 1 : 0) + 1 : 0;
|
|
4749
|
-
const loaderCount = loadersDir ? publicModels.length + (hasCustomerProfile ? 1 : 0) + 1 : 0;
|
|
4750
|
-
const swiftCount = config2.output.swift ? models.length + 2 : 0;
|
|
4751
|
-
const cpSuffix = hasCustomerProfile ? ", customer profile" : "";
|
|
2460
|
+
const cpSuffix = hasCustomerProfile ? " + customer profile" : "";
|
|
4752
2461
|
console.log(
|
|
4753
2462
|
chalk4.green(`
|
|
4754
|
-
Generated ${files.length} file(s)`) + chalk4.dim(
|
|
4755
|
-
` (${
|
|
2463
|
+
\u2713 Generated ${files.length} file(s)`) + chalk4.dim(
|
|
2464
|
+
` (${models.length} model(s), ${publicModels.length} typed accessor(s)${cpSuffix})`
|
|
4756
2465
|
)
|
|
4757
2466
|
);
|
|
4758
|
-
console.log(chalk4.dim(`
|
|
4759
|
-
console.log(chalk4.dim(` Documents: ${docsDir}`));
|
|
4760
|
-
console.log(chalk4.dim(` Operations: ${opsDir}`));
|
|
4761
|
-
if (hooksDir) {
|
|
4762
|
-
console.log(chalk4.dim(` Hooks: ${hooksDir}`));
|
|
4763
|
-
}
|
|
4764
|
-
if (loadersDir) {
|
|
4765
|
-
console.log(chalk4.dim(` Loaders: ${loadersDir}`));
|
|
4766
|
-
}
|
|
4767
|
-
if (config2.output.swift) {
|
|
4768
|
-
console.log(
|
|
4769
|
-
chalk4.dim(` Swift: ${resolve(cwd, config2.output.swift)}`)
|
|
4770
|
-
);
|
|
4771
|
-
}
|
|
2467
|
+
console.log(chalk4.dim(` Output: ${outDir}`));
|
|
4772
2468
|
}
|
|
4773
2469
|
)
|
|
4774
2470
|
);
|
|
4775
2471
|
}
|
|
4776
|
-
function
|
|
2472
|
+
function generateRootIndex(includeCustomerProfile) {
|
|
4777
2473
|
let code = `/**
|
|
4778
|
-
* Generated
|
|
2474
|
+
* Generated Foir client and model types.
|
|
4779
2475
|
*
|
|
4780
2476
|
* @generated by foir \u2014 DO NOT EDIT MANUALLY
|
|
4781
2477
|
*/
|
|
4782
2478
|
|
|
4783
|
-
export
|
|
4784
|
-
export
|
|
2479
|
+
export { createClient } from './client.js';
|
|
2480
|
+
export type { TypedClient } from './client.js';
|
|
4785
2481
|
export * from './models/index.js';
|
|
4786
2482
|
`;
|
|
4787
2483
|
if (includeCustomerProfile) {
|
|
4788
|
-
code += `export
|
|
2484
|
+
code += `export type { CustomerProfileData } from './models/customer-profile.js';
|
|
4789
2485
|
`;
|
|
4790
2486
|
}
|
|
4791
2487
|
return code;
|
|
@@ -4797,11 +2493,11 @@ import inquirer2 from "inquirer";
|
|
|
4797
2493
|
|
|
4798
2494
|
// src/scaffold/scaffold.ts
|
|
4799
2495
|
import * as fs4 from "fs";
|
|
4800
|
-
import * as
|
|
2496
|
+
import * as path2 from "path";
|
|
4801
2497
|
|
|
4802
2498
|
// src/scaffold/package-manager.ts
|
|
4803
2499
|
import * as fs3 from "fs";
|
|
4804
|
-
import * as
|
|
2500
|
+
import * as path from "path";
|
|
4805
2501
|
function detectPackageManager() {
|
|
4806
2502
|
const lockFiles = [
|
|
4807
2503
|
{ file: "pnpm-lock.yaml", manager: "pnpm" },
|
|
@@ -4811,11 +2507,11 @@ function detectPackageManager() {
|
|
|
4811
2507
|
let dir = process.cwd();
|
|
4812
2508
|
while (true) {
|
|
4813
2509
|
for (const { file, manager } of lockFiles) {
|
|
4814
|
-
if (fs3.existsSync(
|
|
2510
|
+
if (fs3.existsSync(path.join(dir, file))) {
|
|
4815
2511
|
return getManagerInfo(manager);
|
|
4816
2512
|
}
|
|
4817
2513
|
}
|
|
4818
|
-
const parentDir =
|
|
2514
|
+
const parentDir = path.dirname(dir);
|
|
4819
2515
|
if (parentDir === dir) {
|
|
4820
2516
|
break;
|
|
4821
2517
|
}
|
|
@@ -4848,7 +2544,7 @@ function getManagerInfo(manager) {
|
|
|
4848
2544
|
|
|
4849
2545
|
// src/scaffold/scaffold.ts
|
|
4850
2546
|
async function scaffold(projectName, extensionType, apiUrl) {
|
|
4851
|
-
const projectDir =
|
|
2547
|
+
const projectDir = path2.resolve(process.cwd(), projectName);
|
|
4852
2548
|
if (fs4.existsSync(projectDir)) {
|
|
4853
2549
|
throw new Error(
|
|
4854
2550
|
`Directory "${projectName}" already exists. Choose a different name or remove the existing directory.`
|
|
@@ -4856,8 +2552,8 @@ async function scaffold(projectName, extensionType, apiUrl) {
|
|
|
4856
2552
|
}
|
|
4857
2553
|
const files = getFiles(projectName, extensionType, apiUrl);
|
|
4858
2554
|
for (const [filePath, content] of Object.entries(files)) {
|
|
4859
|
-
const fullPath =
|
|
4860
|
-
const dir =
|
|
2555
|
+
const fullPath = path2.join(projectDir, filePath);
|
|
2556
|
+
const dir = path2.dirname(fullPath);
|
|
4861
2557
|
if (!fs4.existsSync(dir)) {
|
|
4862
2558
|
fs4.mkdirSync(dir, { recursive: true });
|
|
4863
2559
|
}
|
|
@@ -5769,9 +3465,7 @@ function registerProfilesCommand(program2, globalOpts) {
|
|
|
5769
3465
|
console.log(`Default profile: ${current}`);
|
|
5770
3466
|
} else {
|
|
5771
3467
|
console.log("No default profile set.");
|
|
5772
|
-
console.log(
|
|
5773
|
-
"Use `foir profiles default <name>` to set one."
|
|
5774
|
-
);
|
|
3468
|
+
console.log("Use `foir profiles default <name>` to set one.");
|
|
5775
3469
|
}
|
|
5776
3470
|
}
|
|
5777
3471
|
})
|
|
@@ -5799,7 +3493,9 @@ function registerProfilesCommand(program2, globalOpts) {
|
|
|
5799
3493
|
opts
|
|
5800
3494
|
);
|
|
5801
3495
|
} else {
|
|
5802
|
-
console.log(
|
|
3496
|
+
console.log(
|
|
3497
|
+
`Profile: ${name}${name === defaultName ? " (default)" : ""}`
|
|
3498
|
+
);
|
|
5803
3499
|
console.log("\u2500".repeat(40));
|
|
5804
3500
|
console.log(`Name: ${project.name}`);
|
|
5805
3501
|
console.log(`ID: ${project.id}`);
|
|
@@ -5809,9 +3505,7 @@ function registerProfilesCommand(program2, globalOpts) {
|
|
|
5809
3505
|
const resolved = await resolveProjectContext(opts);
|
|
5810
3506
|
if (!resolved) {
|
|
5811
3507
|
console.log("No active project context.");
|
|
5812
|
-
console.log(
|
|
5813
|
-
"Run `foir select-project` to choose a project."
|
|
5814
|
-
);
|
|
3508
|
+
console.log("Run `foir select-project` to choose a project.");
|
|
5815
3509
|
return;
|
|
5816
3510
|
}
|
|
5817
3511
|
if (opts.json || opts.jsonl) {
|
|
@@ -5837,31 +3531,32 @@ function registerProfilesCommand(program2, globalOpts) {
|
|
|
5837
3531
|
})
|
|
5838
3532
|
);
|
|
5839
3533
|
profiles.command("delete <name>").description("Delete a named profile").option("--confirm", "Skip confirmation prompt").action(
|
|
5840
|
-
withErrorHandler(
|
|
5841
|
-
|
|
5842
|
-
|
|
5843
|
-
|
|
5844
|
-
|
|
5845
|
-
|
|
5846
|
-
|
|
5847
|
-
|
|
5848
|
-
|
|
5849
|
-
|
|
5850
|
-
|
|
5851
|
-
|
|
5852
|
-
|
|
5853
|
-
console.log("Aborted.");
|
|
5854
|
-
return;
|
|
5855
|
-
}
|
|
5856
|
-
await deleteProfile(name);
|
|
5857
|
-
if (opts.json || opts.jsonl) {
|
|
5858
|
-
formatOutput({ deleted: name }, opts);
|
|
5859
|
-
} else {
|
|
5860
|
-
console.log(
|
|
5861
|
-
chalk7.green(`Deleted profile "${name}".`)
|
|
3534
|
+
withErrorHandler(
|
|
3535
|
+
globalOpts,
|
|
3536
|
+
async (name, cmdOpts) => {
|
|
3537
|
+
const opts = globalOpts();
|
|
3538
|
+
const project = await getProjectContext(name);
|
|
3539
|
+
if (!project) {
|
|
3540
|
+
throw new Error(
|
|
3541
|
+
`Profile "${name}" not found. Run \`foir profiles list\` to see available profiles.`
|
|
3542
|
+
);
|
|
3543
|
+
}
|
|
3544
|
+
const confirmed = await confirmAction(
|
|
3545
|
+
`Delete profile "${name}" (${project.name})?`,
|
|
3546
|
+
{ confirm: !!cmdOpts.confirm }
|
|
5862
3547
|
);
|
|
3548
|
+
if (!confirmed) {
|
|
3549
|
+
console.log("Aborted.");
|
|
3550
|
+
return;
|
|
3551
|
+
}
|
|
3552
|
+
await deleteProfile(name);
|
|
3553
|
+
if (opts.json || opts.jsonl) {
|
|
3554
|
+
formatOutput({ deleted: name }, opts);
|
|
3555
|
+
} else {
|
|
3556
|
+
console.log(chalk7.green(`Deleted profile "${name}".`));
|
|
3557
|
+
}
|
|
5863
3558
|
}
|
|
5864
|
-
|
|
3559
|
+
)
|
|
5865
3560
|
);
|
|
5866
3561
|
}
|
|
5867
3562
|
|