@eide/foir-cli 0.1.44 → 0.1.45
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 +237 -1646
- package/package.json +2 -7
- package/dist/chunk-L642MYIL.js +0 -47
- package/dist/config/types.d.ts +0 -48
- package/dist/config/types.js +0 -7
- package/dist/loader-7VE4OF73.js +0 -10
package/dist/cli.js
CHANGED
|
@@ -1,12 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
findConfigFile,
|
|
4
|
-
loadConfigFile
|
|
5
|
-
} from "./chunk-L642MYIL.js";
|
|
6
2
|
|
|
7
3
|
// src/cli.ts
|
|
8
4
|
import { config } from "dotenv";
|
|
9
|
-
import { resolve as
|
|
5
|
+
import { resolve as resolve5, dirname as dirname5 } from "path";
|
|
10
6
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
11
7
|
import { createRequire } from "module";
|
|
12
8
|
import { Command } from "commander";
|
|
@@ -218,21 +214,6 @@ async function resolveProjectContext(options) {
|
|
|
218
214
|
};
|
|
219
215
|
}
|
|
220
216
|
}
|
|
221
|
-
try {
|
|
222
|
-
const { loadConfigProject } = await import("./loader-7VE4OF73.js");
|
|
223
|
-
const configProfile = await loadConfigProject();
|
|
224
|
-
if (configProfile) {
|
|
225
|
-
const project2 = await getProjectContext(configProfile);
|
|
226
|
-
if (project2) {
|
|
227
|
-
return {
|
|
228
|
-
project: project2,
|
|
229
|
-
source: "foir.config.ts",
|
|
230
|
-
profileName: configProfile
|
|
231
|
-
};
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
} catch {
|
|
235
|
-
}
|
|
236
217
|
const defaultProfile = await getDefaultProfile();
|
|
237
218
|
if (defaultProfile) {
|
|
238
219
|
const project2 = await getProjectContext(defaultProfile);
|
|
@@ -362,13 +343,13 @@ function withErrorHandler(optsFn, fn) {
|
|
|
362
343
|
// src/commands/login.ts
|
|
363
344
|
async function findAvailablePort(start, end) {
|
|
364
345
|
for (let port = start; port <= end; port++) {
|
|
365
|
-
const available = await new Promise((
|
|
346
|
+
const available = await new Promise((resolve6) => {
|
|
366
347
|
const server = http.createServer();
|
|
367
348
|
server.listen(port, () => {
|
|
368
349
|
server.close();
|
|
369
|
-
|
|
350
|
+
resolve6(true);
|
|
370
351
|
});
|
|
371
|
-
server.on("error", () =>
|
|
352
|
+
server.on("error", () => resolve6(false));
|
|
372
353
|
});
|
|
373
354
|
if (available) return port;
|
|
374
355
|
}
|
|
@@ -406,7 +387,7 @@ async function loginAction(globalOpts) {
|
|
|
406
387
|
const state = crypto.randomBytes(16).toString("hex");
|
|
407
388
|
const port = await findAvailablePort(9876, 9900);
|
|
408
389
|
const redirectUri = `http://localhost:${port}/callback`;
|
|
409
|
-
const authCode = await new Promise((
|
|
390
|
+
const authCode = await new Promise((resolve6, reject) => {
|
|
410
391
|
const server = http.createServer((req, res) => {
|
|
411
392
|
const url = new URL(req.url, `http://localhost:${port}`);
|
|
412
393
|
if (url.pathname === "/callback") {
|
|
@@ -439,7 +420,7 @@ async function loginAction(globalOpts) {
|
|
|
439
420
|
`<html><head><meta http-equiv="refresh" content="2;url=${mainUrl}"></head><body style="font-family:system-ui;text-align:center;padding:50px"><h1>Authentication successful!</h1><p>You can close this window.</p></body></html>`
|
|
440
421
|
);
|
|
441
422
|
server.close();
|
|
442
|
-
|
|
423
|
+
resolve6(code);
|
|
443
424
|
}
|
|
444
425
|
});
|
|
445
426
|
server.listen(port);
|
|
@@ -889,1606 +870,59 @@ async function getRestAuth(options) {
|
|
|
889
870
|
}
|
|
890
871
|
const credentials = await getCredentials();
|
|
891
872
|
if (!credentials) {
|
|
892
|
-
throw new Error("Not authenticated. Run `foir login` or set FOIR_API_KEY.");
|
|
893
|
-
}
|
|
894
|
-
if (isTokenExpired(credentials)) {
|
|
895
|
-
throw new Error("Session expired. Run `foir login` to re-authenticate.");
|
|
896
|
-
}
|
|
897
|
-
headers["Authorization"] = `Bearer ${credentials.accessToken}`;
|
|
898
|
-
const resolved = await resolveProjectContext(options);
|
|
899
|
-
if (resolved) {
|
|
900
|
-
headers["x-tenant-id"] = resolved.project.tenantId;
|
|
901
|
-
headers["x-project-id"] = resolved.project.id;
|
|
902
|
-
}
|
|
903
|
-
return { apiUrl, headers };
|
|
904
|
-
}
|
|
905
|
-
|
|
906
|
-
// src/commands/media.ts
|
|
907
|
-
function registerMediaCommands(program2, globalOpts) {
|
|
908
|
-
const media = program2.command("media").description("Media file operations");
|
|
909
|
-
media.command("upload <filepath>").description("Upload a file").action(
|
|
910
|
-
withErrorHandler(globalOpts, async (filepath) => {
|
|
911
|
-
const opts = globalOpts();
|
|
912
|
-
const { apiUrl, headers } = await getRestAuth(opts);
|
|
913
|
-
const fileBuffer = await fs2.readFile(filepath);
|
|
914
|
-
const fileName = basename(filepath);
|
|
915
|
-
const formData = new FormData();
|
|
916
|
-
formData.append("file", new Blob([fileBuffer]), fileName);
|
|
917
|
-
const uploadUrl = `${apiUrl.replace(/\/$/, "")}/api/files/upload`;
|
|
918
|
-
const response = await fetch(uploadUrl, {
|
|
919
|
-
method: "POST",
|
|
920
|
-
headers,
|
|
921
|
-
body: formData
|
|
922
|
-
});
|
|
923
|
-
if (!response.ok) {
|
|
924
|
-
const errorText = await response.text();
|
|
925
|
-
throw new Error(`Upload failed (${response.status}): ${errorText}`);
|
|
926
|
-
}
|
|
927
|
-
const result = await response.json();
|
|
928
|
-
if (opts.json || opts.jsonl) {
|
|
929
|
-
formatOutput(result, opts);
|
|
930
|
-
} else {
|
|
931
|
-
success(`Uploaded ${fileName}`);
|
|
932
|
-
if (result.url) {
|
|
933
|
-
console.log(chalk3.bold(` URL: ${result.url}`));
|
|
934
|
-
}
|
|
935
|
-
if (result.storageKey) {
|
|
936
|
-
console.log(chalk3.gray(` Key: ${result.storageKey}`));
|
|
937
|
-
}
|
|
938
|
-
}
|
|
939
|
-
})
|
|
940
|
-
);
|
|
941
|
-
}
|
|
942
|
-
|
|
943
|
-
// src/commands/pull.ts
|
|
944
|
-
import { resolve } from "path";
|
|
945
|
-
import chalk4 from "chalk";
|
|
946
|
-
|
|
947
|
-
// src/config/pull-config.ts
|
|
948
|
-
var DEFAULT_OUTPUT_DIR = "./src/generated";
|
|
949
|
-
async function loadPullConfig(flags) {
|
|
950
|
-
let fileConfig = {};
|
|
951
|
-
const configPath = findConfigFile(flags.config);
|
|
952
|
-
if (configPath) {
|
|
953
|
-
const full = await loadConfigFile(configPath);
|
|
954
|
-
fileConfig = full.pull ?? {};
|
|
955
|
-
}
|
|
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;
|
|
964
|
-
} else {
|
|
965
|
-
outputDir = DEFAULT_OUTPUT_DIR;
|
|
966
|
-
}
|
|
967
|
-
const only = flags.only ? flags.only.split(",").map((s) => s.trim()) : fileConfig.only ?? [];
|
|
968
|
-
const includeInline = fileConfig.includeInline ?? true;
|
|
969
|
-
const prettier = flags.noPrettier ? false : fileConfig.prettier ?? true;
|
|
970
|
-
const dryRun = flags.dryRun ?? false;
|
|
971
|
-
return {
|
|
972
|
-
output: { types: outputDir },
|
|
973
|
-
only,
|
|
974
|
-
includeInline,
|
|
975
|
-
prettier,
|
|
976
|
-
dryRun
|
|
977
|
-
};
|
|
978
|
-
}
|
|
979
|
-
|
|
980
|
-
// src/graphql/generated.ts
|
|
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
|
-
};
|
|
1336
|
-
|
|
1337
|
-
// src/codegen/fetch-models.ts
|
|
1338
|
-
function normalizeConfig(raw) {
|
|
1339
|
-
return {
|
|
1340
|
-
records: Boolean(raw.records ?? true),
|
|
1341
|
-
inline: Boolean(raw.inline ?? false),
|
|
1342
|
-
publicApi: Boolean(raw.publicApi ?? false),
|
|
1343
|
-
versioning: Boolean(raw.versioning ?? false),
|
|
1344
|
-
publishing: Boolean(raw.publishing ?? false),
|
|
1345
|
-
variants: Boolean(raw.variants ?? false),
|
|
1346
|
-
customerScoped: Boolean(raw.customerScoped ?? false),
|
|
1347
|
-
sharing: raw.sharing ? {
|
|
1348
|
-
enabled: Boolean(
|
|
1349
|
-
raw.sharing.enabled ?? false
|
|
1350
|
-
),
|
|
1351
|
-
requireAcceptance: Boolean(
|
|
1352
|
-
raw.sharing.requireAcceptance ?? true
|
|
1353
|
-
)
|
|
1354
|
-
} : void 0,
|
|
1355
|
-
embeddings: raw.embeddings
|
|
1356
|
-
};
|
|
1357
|
-
}
|
|
1358
|
-
function normalizeField(raw) {
|
|
1359
|
-
const field = raw;
|
|
1360
|
-
const options = { ...field.options ?? {} };
|
|
1361
|
-
const resolvedType = field.type === "composite" && field.config?.schema ? field.config.schema : field.type;
|
|
1362
|
-
if (!options.itemType) {
|
|
1363
|
-
const resolved = field.itemType ?? field.config?.itemType ?? field.config?.itemSchema;
|
|
1364
|
-
if (resolved) {
|
|
1365
|
-
options.itemType = resolved;
|
|
1366
|
-
}
|
|
1367
|
-
}
|
|
1368
|
-
if (field.config?.minItems !== void 0 && options.minItems === void 0) {
|
|
1369
|
-
options.minItems = field.config.minItems;
|
|
1370
|
-
}
|
|
1371
|
-
if (field.config?.maxItems !== void 0 && options.maxItems === void 0) {
|
|
1372
|
-
options.maxItems = field.config.maxItems;
|
|
1373
|
-
}
|
|
1374
|
-
return {
|
|
1375
|
-
key: field.key,
|
|
1376
|
-
type: resolvedType,
|
|
1377
|
-
label: field.label,
|
|
1378
|
-
required: field.required,
|
|
1379
|
-
helpText: field.helpText,
|
|
1380
|
-
options: Object.keys(options).length > 0 ? options : void 0
|
|
1381
|
-
};
|
|
1382
|
-
}
|
|
1383
|
-
async function fetchModelsForCodegen(client) {
|
|
1384
|
-
const data = await client.request(ModelsForCodegenDocument, {
|
|
1385
|
-
limit: 500
|
|
1386
|
-
});
|
|
1387
|
-
return data.models.items.map((item) => ({
|
|
1388
|
-
key: item.key,
|
|
1389
|
-
name: item.name,
|
|
1390
|
-
pluralName: item.pluralName ?? void 0,
|
|
1391
|
-
description: item.description ?? void 0,
|
|
1392
|
-
category: item.category ?? void 0,
|
|
1393
|
-
fields: (item.fields ?? []).map(
|
|
1394
|
-
(f) => normalizeField(f)
|
|
1395
|
-
),
|
|
1396
|
-
config: normalizeConfig(item.config ?? {}),
|
|
1397
|
-
hooks: item.hooks ?? void 0
|
|
1398
|
-
}));
|
|
1399
|
-
}
|
|
1400
|
-
function filterModels(models, options) {
|
|
1401
|
-
let filtered = models;
|
|
1402
|
-
if (options.only && options.only.length > 0) {
|
|
1403
|
-
const onlySet = new Set(options.only);
|
|
1404
|
-
filtered = filtered.filter((m) => onlySet.has(m.key));
|
|
1405
|
-
}
|
|
1406
|
-
if (!options.includeInline) {
|
|
1407
|
-
filtered = filtered.filter((m) => m.config.records);
|
|
1408
|
-
}
|
|
1409
|
-
return filtered;
|
|
1410
|
-
}
|
|
1411
|
-
|
|
1412
|
-
// src/codegen/fetch-customer-profile-schema.ts
|
|
1413
|
-
async function fetchCustomerProfileSchema(client) {
|
|
1414
|
-
const data = await client.request(GetCustomerProfileSchemaDocument);
|
|
1415
|
-
if (!data.customerProfileSchema) return null;
|
|
1416
|
-
return {
|
|
1417
|
-
id: data.customerProfileSchema.id,
|
|
1418
|
-
version: data.customerProfileSchema.version,
|
|
1419
|
-
fields: (data.customerProfileSchema.fields ?? []).map((f) => ({
|
|
1420
|
-
key: f.key,
|
|
1421
|
-
type: f.type,
|
|
1422
|
-
label: f.label,
|
|
1423
|
-
required: f.required ?? void 0,
|
|
1424
|
-
helpText: f.helpText ?? void 0,
|
|
1425
|
-
options: f.config
|
|
1426
|
-
}))
|
|
1427
|
-
};
|
|
1428
|
-
}
|
|
1429
|
-
|
|
1430
|
-
// src/codegen/field-mapping.ts
|
|
1431
|
-
var PRIMITIVE_FIELD_TYPES = /* @__PURE__ */ new Set([
|
|
1432
|
-
"text",
|
|
1433
|
-
"richtext",
|
|
1434
|
-
"number",
|
|
1435
|
-
"boolean",
|
|
1436
|
-
"email",
|
|
1437
|
-
"phone",
|
|
1438
|
-
"url",
|
|
1439
|
-
"date",
|
|
1440
|
-
"image",
|
|
1441
|
-
"video",
|
|
1442
|
-
"file",
|
|
1443
|
-
"currency",
|
|
1444
|
-
"select",
|
|
1445
|
-
"multiselect",
|
|
1446
|
-
"json",
|
|
1447
|
-
"list",
|
|
1448
|
-
"reference",
|
|
1449
|
-
"link",
|
|
1450
|
-
"flexible",
|
|
1451
|
-
"model"
|
|
1452
|
-
]);
|
|
1453
|
-
function isPrimitiveFieldType(type) {
|
|
1454
|
-
return PRIMITIVE_FIELD_TYPES.has(type);
|
|
1455
|
-
}
|
|
1456
|
-
var FIELD_TYPE_MAPPING = {
|
|
1457
|
-
text: { outputType: "string", inputType: "string" },
|
|
1458
|
-
richtext: {
|
|
1459
|
-
outputType: "RichtextValue",
|
|
1460
|
-
inputType: "RichtextValue",
|
|
1461
|
-
needsImport: "field-types"
|
|
1462
|
-
},
|
|
1463
|
-
number: { outputType: "number", inputType: "number" },
|
|
1464
|
-
boolean: { outputType: "boolean", inputType: "boolean" },
|
|
1465
|
-
email: { outputType: "string", inputType: "string" },
|
|
1466
|
-
phone: { outputType: "string", inputType: "string" },
|
|
1467
|
-
url: { outputType: "string", inputType: "string" },
|
|
1468
|
-
date: { outputType: "Date", inputType: "string" },
|
|
1469
|
-
image: {
|
|
1470
|
-
outputType: "ImageValue",
|
|
1471
|
-
inputType: "string",
|
|
1472
|
-
needsImport: "field-types"
|
|
1473
|
-
},
|
|
1474
|
-
video: {
|
|
1475
|
-
outputType: "VideoValue",
|
|
1476
|
-
inputType: "string",
|
|
1477
|
-
needsImport: "field-types"
|
|
1478
|
-
},
|
|
1479
|
-
file: {
|
|
1480
|
-
outputType: "FileValue",
|
|
1481
|
-
inputType: "string",
|
|
1482
|
-
needsImport: "field-types"
|
|
1483
|
-
},
|
|
1484
|
-
currency: {
|
|
1485
|
-
outputType: "CurrencyValue",
|
|
1486
|
-
inputType: "CurrencyValue",
|
|
1487
|
-
needsImport: "field-types"
|
|
1488
|
-
},
|
|
1489
|
-
select: { outputType: "string", inputType: "string" },
|
|
1490
|
-
multiselect: { outputType: "string[]", inputType: "string[]" },
|
|
1491
|
-
json: { outputType: "unknown", inputType: "unknown" },
|
|
1492
|
-
list: { outputType: "unknown[]", inputType: "unknown[]" },
|
|
1493
|
-
flexible: {
|
|
1494
|
-
outputType: "FlexibleFieldItem[]",
|
|
1495
|
-
inputType: "FlexibleFieldItem[]",
|
|
1496
|
-
needsImport: "field-types"
|
|
1497
|
-
},
|
|
1498
|
-
reference: {
|
|
1499
|
-
outputType: "ReferenceValue",
|
|
1500
|
-
inputType: "ReferenceValue",
|
|
1501
|
-
needsImport: "field-types"
|
|
1502
|
-
},
|
|
1503
|
-
link: {
|
|
1504
|
-
outputType: "LinkValue",
|
|
1505
|
-
inputType: "LinkValue",
|
|
1506
|
-
needsImport: "field-types"
|
|
1507
|
-
},
|
|
1508
|
-
model: { outputType: "string", inputType: "string" }
|
|
1509
|
-
};
|
|
1510
|
-
function getFieldType(field, mode = "output") {
|
|
1511
|
-
if (!field?.type) return "unknown";
|
|
1512
|
-
const mapping = FIELD_TYPE_MAPPING[field.type];
|
|
1513
|
-
if (!mapping) {
|
|
1514
|
-
if (isPrimitiveFieldType(field.type)) return "unknown";
|
|
1515
|
-
return toPascalCase(field.type);
|
|
1516
|
-
}
|
|
1517
|
-
let tsType = mode === "output" ? mapping.outputType : mapping.inputType;
|
|
1518
|
-
if (field.type === "select" && field.options?.options) {
|
|
1519
|
-
const options = field.options.options;
|
|
1520
|
-
if (options.length > 0) {
|
|
1521
|
-
tsType = options.map((o) => `'${o.value}'`).join(" | ");
|
|
1522
|
-
}
|
|
1523
|
-
}
|
|
1524
|
-
if (field.type === "multiselect" && field.options?.options) {
|
|
1525
|
-
const options = field.options.options;
|
|
1526
|
-
if (options.length > 0) {
|
|
1527
|
-
tsType = `(${options.map((o) => `'${o.value}'`).join(" | ")})[]`;
|
|
1528
|
-
}
|
|
1529
|
-
}
|
|
1530
|
-
if (field.type === "list" && field.options?.itemType) {
|
|
1531
|
-
const itemType = field.options.itemType;
|
|
1532
|
-
const itemMapping = FIELD_TYPE_MAPPING[itemType];
|
|
1533
|
-
if (itemMapping) {
|
|
1534
|
-
tsType = `${mode === "output" ? itemMapping.outputType : itemMapping.inputType}[]`;
|
|
1535
|
-
} else if (!isPrimitiveFieldType(itemType)) {
|
|
1536
|
-
tsType = `${toPascalCase(itemType)}[]`;
|
|
1537
|
-
}
|
|
1538
|
-
}
|
|
1539
|
-
return tsType;
|
|
1540
|
-
}
|
|
1541
|
-
function getReferenceTypeModelRefs(fields) {
|
|
1542
|
-
const refs = /* @__PURE__ */ new Set();
|
|
1543
|
-
for (const field of fields) {
|
|
1544
|
-
if (field.type === "reference" && field.options?.referenceTypes) {
|
|
1545
|
-
for (const rt of field.options.referenceTypes) {
|
|
1546
|
-
refs.add(rt);
|
|
1547
|
-
}
|
|
1548
|
-
}
|
|
1549
|
-
}
|
|
1550
|
-
return refs;
|
|
1551
|
-
}
|
|
1552
|
-
function getInlineSchemaReferences(fields) {
|
|
1553
|
-
const refs = /* @__PURE__ */ new Set();
|
|
1554
|
-
for (const field of fields) {
|
|
1555
|
-
if (!isPrimitiveFieldType(field.type) && !FIELD_TYPE_MAPPING[field.type]) {
|
|
1556
|
-
refs.add(field.type);
|
|
1557
|
-
}
|
|
1558
|
-
if (field.type === "list" && field.options?.itemType) {
|
|
1559
|
-
const itemType = field.options.itemType;
|
|
1560
|
-
if (!isPrimitiveFieldType(itemType) && !FIELD_TYPE_MAPPING[itemType]) {
|
|
1561
|
-
refs.add(itemType);
|
|
1562
|
-
}
|
|
1563
|
-
}
|
|
1564
|
-
}
|
|
1565
|
-
return refs;
|
|
1566
|
-
}
|
|
1567
|
-
function toCamelCase(str) {
|
|
1568
|
-
if (!str) return "unknown";
|
|
1569
|
-
return str.replace(
|
|
1570
|
-
/[-_]([a-z])/g,
|
|
1571
|
-
(_, letter) => letter.toUpperCase()
|
|
1572
|
-
);
|
|
1573
|
-
}
|
|
1574
|
-
function toPascalCase(str) {
|
|
1575
|
-
if (!str) return "Unknown";
|
|
1576
|
-
const camel = toCamelCase(str);
|
|
1577
|
-
return camel.charAt(0).toUpperCase() + camel.slice(1);
|
|
1578
|
-
}
|
|
1579
|
-
function sanitizeFieldName(key) {
|
|
1580
|
-
if (!key) return "unknown";
|
|
1581
|
-
const camel = toCamelCase(key);
|
|
1582
|
-
if (/^[0-9]/.test(camel)) return `_${camel}`;
|
|
1583
|
-
return camel;
|
|
1584
|
-
}
|
|
1585
|
-
function generateFieldDef(field) {
|
|
1586
|
-
const parts = [];
|
|
1587
|
-
parts.push(`key: '${field.key ?? "unknown"}'`);
|
|
1588
|
-
parts.push(`type: '${field.type ?? "text"}'`);
|
|
1589
|
-
parts.push(
|
|
1590
|
-
`label: '${(field.label ?? field.key ?? "Unknown").replace(/'/g, "\\'")}'`
|
|
1591
|
-
);
|
|
1592
|
-
if (field.required) parts.push("required: true");
|
|
1593
|
-
if (field.helpText)
|
|
1594
|
-
parts.push(`helpText: '${field.helpText.replace(/'/g, "\\'")}'`);
|
|
1595
|
-
if (field.type === "text" && field.options) {
|
|
1596
|
-
if (field.options.maxLength)
|
|
1597
|
-
parts.push(`maxLength: ${field.options.maxLength}`);
|
|
1598
|
-
if (field.options.minLength)
|
|
1599
|
-
parts.push(`minLength: ${field.options.minLength}`);
|
|
1600
|
-
}
|
|
1601
|
-
if (field.type === "number" && field.options) {
|
|
1602
|
-
if (field.options.min !== void 0)
|
|
1603
|
-
parts.push(`min: ${field.options.min}`);
|
|
1604
|
-
if (field.options.max !== void 0)
|
|
1605
|
-
parts.push(`max: ${field.options.max}`);
|
|
1606
|
-
if (field.options.step !== void 0)
|
|
1607
|
-
parts.push(`step: ${field.options.step}`);
|
|
1608
|
-
}
|
|
1609
|
-
if ((field.type === "select" || field.type === "multiselect") && field.options?.options) {
|
|
1610
|
-
const options = field.options.options;
|
|
1611
|
-
const optionsStr = options.filter((o) => o.value !== void 0).map((o) => {
|
|
1612
|
-
const label = (o.label ?? o.value ?? "").replace(/'/g, "\\'");
|
|
1613
|
-
const value = (o.value ?? "").replace(/'/g, "\\'");
|
|
1614
|
-
return `{ label: '${label}', value: '${value}' }`;
|
|
1615
|
-
}).join(", ");
|
|
1616
|
-
parts.push(`options: [${optionsStr}]`);
|
|
1617
|
-
}
|
|
1618
|
-
if (field.type === "reference" && field.options?.referenceTypes) {
|
|
1619
|
-
const refTypes = field.options.referenceTypes;
|
|
1620
|
-
parts.push(`referenceTypes: [${refTypes.map((t) => `'${t}'`).join(", ")}]`);
|
|
1621
|
-
if (field.options.multiple) parts.push("multiple: true");
|
|
1622
|
-
}
|
|
1623
|
-
if (field.type === "list" && field.options?.itemType) {
|
|
1624
|
-
parts.push(`itemType: '${field.options.itemType}'`);
|
|
1625
|
-
if (field.options.minItems !== void 0)
|
|
1626
|
-
parts.push(`minItems: ${field.options.minItems}`);
|
|
1627
|
-
if (field.options.maxItems !== void 0)
|
|
1628
|
-
parts.push(`maxItems: ${field.options.maxItems}`);
|
|
1629
|
-
}
|
|
1630
|
-
if ((field.type === "image" || field.type === "video" || field.type === "file") && field.options) {
|
|
1631
|
-
if (field.options.allowedTypes) {
|
|
1632
|
-
const types = field.options.allowedTypes;
|
|
1633
|
-
parts.push(`allowedTypes: [${types.map((t) => `'${t}'`).join(", ")}]`);
|
|
1634
|
-
}
|
|
1635
|
-
if (field.options.maxSize) parts.push(`maxSize: ${field.options.maxSize}`);
|
|
1636
|
-
}
|
|
1637
|
-
return `{ ${parts.join(", ")} }`;
|
|
1638
|
-
}
|
|
1639
|
-
|
|
1640
|
-
// src/codegen/generators/model-types.ts
|
|
1641
|
-
function isInlineOnlyModel(model) {
|
|
1642
|
-
return model.config.inline && !model.config.records;
|
|
1643
|
-
}
|
|
1644
|
-
function generateModelTypes(model, allModels) {
|
|
1645
|
-
const typeName = toPascalCase(model.key);
|
|
1646
|
-
const configName = toCamelCase(model.key) + "Config";
|
|
1647
|
-
const fields = model.fields ?? [];
|
|
1648
|
-
const fieldTypeImports = getFieldTypeImportsForFields(fields);
|
|
1649
|
-
const inlineSchemaRefs = getInlineSchemaReferences(fields);
|
|
1650
|
-
const referenceModelRefs = getReferenceTypeModelRefs(fields);
|
|
1651
|
-
let code = buildImportStatements(
|
|
1652
|
-
model,
|
|
1653
|
-
fieldTypeImports,
|
|
1654
|
-
inlineSchemaRefs,
|
|
1655
|
-
referenceModelRefs,
|
|
1656
|
-
allModels
|
|
1657
|
-
);
|
|
1658
|
-
if (isInlineOnlyModel(model)) {
|
|
1659
|
-
code += generateDataInterface(model, fields, typeName, allModels);
|
|
1660
|
-
return code;
|
|
1661
|
-
}
|
|
1662
|
-
code += generateConfigObject(model, fields, configName);
|
|
1663
|
-
code += "\n";
|
|
1664
|
-
code += generateDataInterface(model, fields, typeName + "Data", allModels);
|
|
1665
|
-
return code;
|
|
1666
|
-
}
|
|
1667
|
-
function buildImportStatements(model, fieldTypeImports, inlineSchemaRefs, referenceModelRefs, allModels) {
|
|
1668
|
-
const imports = [];
|
|
1669
|
-
if (fieldTypeImports.size > 0) {
|
|
1670
|
-
const types = Array.from(fieldTypeImports).sort().join(", ");
|
|
1671
|
-
imports.push(`import type { ${types} } from '@eide/foir-client';`);
|
|
1672
|
-
}
|
|
1673
|
-
const allModelRefKeys = /* @__PURE__ */ new Set([
|
|
1674
|
-
...inlineSchemaRefs,
|
|
1675
|
-
...referenceModelRefs
|
|
1676
|
-
]);
|
|
1677
|
-
for (const refKey of allModelRefKeys) {
|
|
1678
|
-
if (refKey === model.key) continue;
|
|
1679
|
-
const refModel = allModels.find((m) => m.key === refKey);
|
|
1680
|
-
if (refModel) {
|
|
1681
|
-
const refTypeName = isInlineOnlyModel(refModel) ? toPascalCase(refKey) : toPascalCase(refKey) + "Data";
|
|
1682
|
-
imports.push(`import type { ${refTypeName} } from './${refKey}.js';`);
|
|
1683
|
-
}
|
|
1684
|
-
}
|
|
1685
|
-
return imports.length > 0 ? imports.join("\n") + "\n\n" : "";
|
|
1686
|
-
}
|
|
1687
|
-
function generateConfigObject(model, fields, configName) {
|
|
1688
|
-
const lines = [];
|
|
1689
|
-
lines.push("/**");
|
|
1690
|
-
lines.push(` * ${model.name} Configuration`);
|
|
1691
|
-
if (model.description) lines.push(` * ${model.description}`);
|
|
1692
|
-
lines.push(` *`);
|
|
1693
|
-
lines.push(` * @generated from model '${model.key}'`);
|
|
1694
|
-
lines.push(" */");
|
|
1695
|
-
const escapedName = (model.name ?? model.key).replace(/'/g, "\\'");
|
|
1696
|
-
lines.push(`export const ${configName} = {`);
|
|
1697
|
-
lines.push(` key: '${model.key}',`);
|
|
1698
|
-
lines.push(` name: '${escapedName}',`);
|
|
1699
|
-
if (model.description) {
|
|
1700
|
-
lines.push(` description: '${model.description.replace(/'/g, "\\'")}',`);
|
|
1701
|
-
}
|
|
1702
|
-
lines.push("");
|
|
1703
|
-
lines.push(" // Capability flags");
|
|
1704
|
-
lines.push(` records: ${model.config.records},`);
|
|
1705
|
-
lines.push(` inline: ${model.config.inline},`);
|
|
1706
|
-
lines.push(` publicApi: ${model.config.publicApi},`);
|
|
1707
|
-
lines.push(` versioning: ${model.config.versioning},`);
|
|
1708
|
-
lines.push(` publishing: ${model.config.publishing},`);
|
|
1709
|
-
lines.push(` variants: ${model.config.variants},`);
|
|
1710
|
-
lines.push(` customerScoped: ${model.config.customerScoped},`);
|
|
1711
|
-
if (model.config.embeddings?.enabled) {
|
|
1712
|
-
lines.push("");
|
|
1713
|
-
lines.push(" // Embeddings");
|
|
1714
|
-
lines.push(
|
|
1715
|
-
` embeddings: ${JSON.stringify(model.config.embeddings, null, 4).replace(/\n/g, "\n ")},`
|
|
1716
|
-
);
|
|
1717
|
-
}
|
|
1718
|
-
if (model.hooks && Object.keys(model.hooks).length > 0) {
|
|
1719
|
-
lines.push("");
|
|
1720
|
-
lines.push(" // Lifecycle hooks");
|
|
1721
|
-
lines.push(
|
|
1722
|
-
` hooks: ${JSON.stringify(model.hooks, null, 4).replace(/\n/g, "\n ")},`
|
|
1723
|
-
);
|
|
1724
|
-
} else {
|
|
1725
|
-
lines.push("");
|
|
1726
|
-
lines.push(" hooks: {},");
|
|
1727
|
-
}
|
|
1728
|
-
lines.push("");
|
|
1729
|
-
lines.push(" // Field definitions");
|
|
1730
|
-
if (fields.length === 0) {
|
|
1731
|
-
lines.push(" fieldDefs: [],");
|
|
1732
|
-
} else {
|
|
1733
|
-
lines.push(" fieldDefs: [");
|
|
1734
|
-
for (const field of fields) {
|
|
1735
|
-
lines.push(` ${generateFieldDef(field)},`);
|
|
1736
|
-
}
|
|
1737
|
-
lines.push(" ],");
|
|
1738
|
-
}
|
|
1739
|
-
lines.push("} as const;");
|
|
1740
|
-
return lines.join("\n") + "\n";
|
|
1741
|
-
}
|
|
1742
|
-
function generateDataInterface(model, fields, interfaceName, allModels) {
|
|
1743
|
-
const lines = [];
|
|
1744
|
-
lines.push("/**");
|
|
1745
|
-
lines.push(` * ${model.name} Data`);
|
|
1746
|
-
lines.push(` * Field values only \u2014 no system fields`);
|
|
1747
|
-
lines.push(` *`);
|
|
1748
|
-
lines.push(` * @generated from model '${model.key}'`);
|
|
1749
|
-
lines.push(" */");
|
|
1750
|
-
lines.push(`export interface ${interfaceName} {`);
|
|
1751
|
-
for (const field of fields) {
|
|
1752
|
-
const fieldName = sanitizeFieldName(field.key);
|
|
1753
|
-
let fieldType = getFieldType(field, "output");
|
|
1754
|
-
const refModel = allModels.find((m) => m.key === field.type);
|
|
1755
|
-
if (refModel && !isInlineOnlyModel(refModel)) {
|
|
1756
|
-
fieldType = toPascalCase(field.type) + "Data";
|
|
1757
|
-
}
|
|
1758
|
-
if (field.type === "list" && field.options?.itemType) {
|
|
1759
|
-
const itemRefModel = allModels.find(
|
|
1760
|
-
(m) => m.key === field.options.itemType
|
|
1761
|
-
);
|
|
1762
|
-
if (itemRefModel && !isInlineOnlyModel(itemRefModel)) {
|
|
1763
|
-
fieldType = toPascalCase(field.options.itemType) + "Data[]";
|
|
1764
|
-
}
|
|
1765
|
-
}
|
|
1766
|
-
if (field.type === "reference" && field.options?.referenceTypes) {
|
|
1767
|
-
const refTypes = field.options.referenceTypes;
|
|
1768
|
-
const resolvedPreviewTypes = [];
|
|
1769
|
-
for (const refKey of refTypes) {
|
|
1770
|
-
const targetModel = allModels.find((m) => m.key === refKey);
|
|
1771
|
-
if (targetModel) {
|
|
1772
|
-
const targetTypeName = isInlineOnlyModel(targetModel) ? toPascalCase(refKey) : toPascalCase(refKey) + "Data";
|
|
1773
|
-
resolvedPreviewTypes.push(`Partial<${targetTypeName}>`);
|
|
1774
|
-
}
|
|
1775
|
-
}
|
|
1776
|
-
if (resolvedPreviewTypes.length === refTypes.length && resolvedPreviewTypes.length > 0) {
|
|
1777
|
-
fieldType = `ReferenceValue<${resolvedPreviewTypes.join(" | ")}>`;
|
|
1778
|
-
}
|
|
1779
|
-
}
|
|
1780
|
-
const optional = field.required ? "" : "?";
|
|
1781
|
-
const comment = field.helpText ? ` /** ${field.helpText} */
|
|
1782
|
-
` : "";
|
|
1783
|
-
lines.push(comment + ` ${fieldName}${optional}: ${fieldType};`);
|
|
1784
|
-
}
|
|
1785
|
-
lines.push("}");
|
|
1786
|
-
return lines.join("\n") + "\n";
|
|
1787
|
-
}
|
|
1788
|
-
function getFieldTypeImportsForFields(fields) {
|
|
1789
|
-
const imports = /* @__PURE__ */ new Set();
|
|
1790
|
-
for (const field of fields) {
|
|
1791
|
-
const mapping = FIELD_TYPE_MAPPING[field.type];
|
|
1792
|
-
if (mapping?.needsImport === "field-types") {
|
|
1793
|
-
imports.add(mapping.outputType.replace(/\[\]$/, ""));
|
|
1794
|
-
}
|
|
1795
|
-
if (field.type === "list" && field.options?.itemType) {
|
|
1796
|
-
const itemMapping = FIELD_TYPE_MAPPING[field.options.itemType];
|
|
1797
|
-
if (itemMapping?.needsImport === "field-types") {
|
|
1798
|
-
imports.add(itemMapping.outputType.replace(/\[\]$/, ""));
|
|
1799
|
-
}
|
|
1800
|
-
}
|
|
1801
|
-
}
|
|
1802
|
-
return imports;
|
|
1803
|
-
}
|
|
1804
|
-
|
|
1805
|
-
// src/codegen/generators/model-index.ts
|
|
1806
|
-
function isInlineOnlyModel2(model) {
|
|
1807
|
-
return model.config.inline && !model.config.records;
|
|
1808
|
-
}
|
|
1809
|
-
function generateModelIndex(models) {
|
|
1810
|
-
let code = `/**
|
|
1811
|
-
* Model Types and Configs \u2014 Generated re-exports
|
|
1812
|
-
*
|
|
1813
|
-
* @generated by foir \u2014 DO NOT EDIT MANUALLY
|
|
1814
|
-
*/
|
|
1815
|
-
|
|
1816
|
-
`;
|
|
1817
|
-
for (const model of models) {
|
|
1818
|
-
const typeName = toPascalCase(model.key);
|
|
1819
|
-
const configName = toCamelCase(model.key) + "Config";
|
|
1820
|
-
if (isInlineOnlyModel2(model)) {
|
|
1821
|
-
code += `export type { ${typeName} } from './${model.key}.js';
|
|
1822
|
-
`;
|
|
1823
|
-
} else {
|
|
1824
|
-
code += `export { ${configName} } from './${model.key}.js';
|
|
1825
|
-
`;
|
|
1826
|
-
code += `export type { ${typeName}Data } from './${model.key}.js';
|
|
1827
|
-
`;
|
|
1828
|
-
if (model.config.publicApi) {
|
|
1829
|
-
code += `export type { ${typeName}Where, ${typeName}SortField } from './${model.key}.filters.js';
|
|
1830
|
-
`;
|
|
1831
|
-
}
|
|
1832
|
-
}
|
|
1833
|
-
}
|
|
1834
|
-
return code;
|
|
1835
|
-
}
|
|
1836
|
-
|
|
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
|
-
);
|
|
1842
|
-
const lines = [];
|
|
1843
|
-
lines.push(`/**
|
|
1844
|
-
* Typed Foir client for this project.
|
|
1845
|
-
*
|
|
1846
|
-
* @generated by foir \u2014 DO NOT EDIT MANUALLY
|
|
1847
|
-
*/
|
|
1848
|
-
|
|
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';
|
|
1863
|
-
`);
|
|
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
|
-
);
|
|
1872
|
-
}
|
|
1873
|
-
if (hasCustomerProfile) {
|
|
1874
|
-
lines.push(
|
|
1875
|
-
`import type { CustomerProfileData } from './models/customer-profile.js';`
|
|
1876
|
-
);
|
|
1877
|
-
}
|
|
1878
|
-
lines.push("");
|
|
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}>;`);
|
|
1887
|
-
}
|
|
1888
|
-
if (hasCustomerProfile) {
|
|
1889
|
-
lines.push(
|
|
1890
|
-
` profile: ReturnType<typeof createProfileClient<CustomerProfileData>>;`
|
|
1891
|
-
);
|
|
1892
|
-
}
|
|
1893
|
-
lines.push("}");
|
|
1894
|
-
lines.push("");
|
|
1895
|
-
lines.push("export function createClient(config: ClientConfig): TypedClient {");
|
|
1896
|
-
lines.push(" const base = createBaseClient(config);");
|
|
1897
|
-
lines.push("");
|
|
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),");
|
|
1908
|
-
}
|
|
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
|
-
);
|
|
1919
|
-
}
|
|
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("}");
|
|
1929
|
-
lines.push("");
|
|
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';`);
|
|
1949
|
-
lines.push("");
|
|
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
|
-
);
|
|
1958
|
-
}
|
|
1959
|
-
if (hasCustomerProfile) {
|
|
1960
|
-
lines.push(
|
|
1961
|
-
`export type { CustomerProfileData } from './models/customer-profile.js';`
|
|
1962
|
-
);
|
|
1963
|
-
}
|
|
1964
|
-
return lines.join("\n") + "\n";
|
|
1965
|
-
}
|
|
1966
|
-
|
|
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: {}
|
|
1973
|
-
};
|
|
1974
|
-
for (const model of models) {
|
|
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
|
-
};
|
|
1990
|
-
}
|
|
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
|
-
};
|
|
2002
|
-
}
|
|
2003
|
-
return JSON.stringify(manifest, null, 2) + "\n";
|
|
2004
|
-
}
|
|
2005
|
-
|
|
2006
|
-
// src/codegen/generators/model-filters.ts
|
|
2007
|
-
function isInlineOnlyModel3(model) {
|
|
2008
|
-
return model.config.inline && !model.config.records;
|
|
2009
|
-
}
|
|
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
|
-
}
|
|
2029
|
-
}
|
|
2030
|
-
function isSortable(fieldType) {
|
|
2031
|
-
return ["text", "number", "date", "boolean", "email", "url", "select"].includes(fieldType);
|
|
2032
|
-
}
|
|
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";
|
|
2062
|
-
}
|
|
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
|
-
`;
|
|
2079
|
-
}
|
|
2080
|
-
|
|
2081
|
-
// src/codegen/generators/model-zod.ts
|
|
2082
|
-
function isInlineOnlyModel4(model) {
|
|
2083
|
-
return model.config.inline && !model.config.records;
|
|
2084
|
-
}
|
|
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 };
|
|
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';
|
|
2214
|
-
|
|
2215
|
-
export const ${schemaName} = z.object({});
|
|
2216
|
-
`;
|
|
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
|
-
}
|
|
2246
|
-
const lines = [];
|
|
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';`);
|
|
2257
|
-
}
|
|
2258
|
-
const uniqueInlineImports = [...new Set(inlineSchemaImports)];
|
|
2259
|
-
for (const imp of uniqueInlineImports) {
|
|
2260
|
-
lines.push(imp);
|
|
2261
|
-
}
|
|
2262
|
-
lines.push("");
|
|
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";
|
|
2269
|
-
}
|
|
2270
|
-
|
|
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
|
|
2277
|
-
*
|
|
2278
|
-
* @generated by foir from customer profile schema (version ${schema.version}) \u2014 DO NOT EDIT MANUALLY
|
|
2279
|
-
*/
|
|
2280
|
-
|
|
2281
|
-
`;
|
|
2282
|
-
if (fieldTypeImports.size > 0) {
|
|
2283
|
-
const types = Array.from(fieldTypeImports).sort().join(", ");
|
|
2284
|
-
code += `import type { ${types} } from '../field-types.js';
|
|
2285
|
-
|
|
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
|
-
`;
|
|
873
|
+
throw new Error("Not authenticated. Run `foir login` or set FOIR_API_KEY.");
|
|
2307
874
|
}
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
return code;
|
|
2311
|
-
}
|
|
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
|
-
}
|
|
875
|
+
if (isTokenExpired(credentials)) {
|
|
876
|
+
throw new Error("Session expired. Run `foir login` to re-authenticate.");
|
|
2319
877
|
}
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
import { dirname as dirname2 } from "path";
|
|
2326
|
-
async function writeGeneratedFile(filePath, content, usePrettier = true) {
|
|
2327
|
-
await mkdir(dirname2(filePath), { recursive: true });
|
|
2328
|
-
let formattedContent = content;
|
|
2329
|
-
const isSwift = filePath.endsWith(".swift");
|
|
2330
|
-
if (usePrettier && !isSwift) {
|
|
2331
|
-
try {
|
|
2332
|
-
const prettier = await import("prettier");
|
|
2333
|
-
const parser = filePath.endsWith(".graphql") ? "graphql" : "typescript";
|
|
2334
|
-
formattedContent = await prettier.format(content, {
|
|
2335
|
-
parser,
|
|
2336
|
-
semi: true,
|
|
2337
|
-
singleQuote: true,
|
|
2338
|
-
trailingComma: "es5",
|
|
2339
|
-
printWidth: 100
|
|
2340
|
-
});
|
|
2341
|
-
} catch {
|
|
2342
|
-
}
|
|
878
|
+
headers["Authorization"] = `Bearer ${credentials.accessToken}`;
|
|
879
|
+
const resolved = await resolveProjectContext(options);
|
|
880
|
+
if (resolved) {
|
|
881
|
+
headers["x-tenant-id"] = resolved.project.tenantId;
|
|
882
|
+
headers["x-project-id"] = resolved.project.id;
|
|
2343
883
|
}
|
|
2344
|
-
|
|
2345
|
-
}
|
|
2346
|
-
async function writeFiles(files, usePrettier = true) {
|
|
2347
|
-
await Promise.all(
|
|
2348
|
-
files.map(
|
|
2349
|
-
(file) => writeGeneratedFile(file.path, file.content, usePrettier)
|
|
2350
|
-
)
|
|
2351
|
-
);
|
|
884
|
+
return { apiUrl, headers };
|
|
2352
885
|
}
|
|
2353
886
|
|
|
2354
|
-
// src/commands/
|
|
2355
|
-
function
|
|
2356
|
-
program2.command("
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
only: config2.only.length > 0 ? config2.only : void 0,
|
|
2383
|
-
includeInline: config2.includeInline
|
|
2384
|
-
});
|
|
2385
|
-
console.log(
|
|
2386
|
-
chalk4.dim(
|
|
2387
|
-
`Found ${allModels.length} model(s), generating for ${models.length}.`
|
|
2388
|
-
)
|
|
2389
|
-
);
|
|
2390
|
-
const cwd = process.cwd();
|
|
2391
|
-
const outDir = resolve(cwd, config2.output.types);
|
|
2392
|
-
const modelsDir = resolve(outDir, "models");
|
|
2393
|
-
const files = [];
|
|
2394
|
-
const hasCustomerProfile = !!(cpSchema && cpSchema.fields.length > 0);
|
|
2395
|
-
const publicModels = models.filter(
|
|
2396
|
-
(m) => m.config.publicApi && m.config.records
|
|
2397
|
-
);
|
|
2398
|
-
for (const model of models) {
|
|
2399
|
-
files.push({
|
|
2400
|
-
path: resolve(modelsDir, `${model.key}.ts`),
|
|
2401
|
-
content: generateModelTypes(model, models)
|
|
2402
|
-
});
|
|
2403
|
-
files.push({
|
|
2404
|
-
path: resolve(modelsDir, `${model.key}.zod.ts`),
|
|
2405
|
-
content: generateModelZodSchema(model, models)
|
|
2406
|
-
});
|
|
2407
|
-
}
|
|
2408
|
-
for (const model of publicModels) {
|
|
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
|
-
}
|
|
2424
|
-
}
|
|
2425
|
-
if (hasCustomerProfile) {
|
|
2426
|
-
files.push({
|
|
2427
|
-
path: resolve(modelsDir, "customer-profile.ts"),
|
|
2428
|
-
content: generateCustomerProfileTypes(cpSchema)
|
|
2429
|
-
});
|
|
887
|
+
// src/commands/media.ts
|
|
888
|
+
function registerMediaCommands(program2, globalOpts) {
|
|
889
|
+
const media = program2.command("media").description("Media file operations");
|
|
890
|
+
media.command("upload <filepath>").description("Upload a file").action(
|
|
891
|
+
withErrorHandler(globalOpts, async (filepath) => {
|
|
892
|
+
const opts = globalOpts();
|
|
893
|
+
const { apiUrl, headers } = await getRestAuth(opts);
|
|
894
|
+
const fileBuffer = await fs2.readFile(filepath);
|
|
895
|
+
const fileName = basename(filepath);
|
|
896
|
+
const formData = new FormData();
|
|
897
|
+
formData.append("file", new Blob([fileBuffer]), fileName);
|
|
898
|
+
const uploadUrl = `${apiUrl.replace(/\/$/, "")}/api/files/upload`;
|
|
899
|
+
const response = await fetch(uploadUrl, {
|
|
900
|
+
method: "POST",
|
|
901
|
+
headers,
|
|
902
|
+
body: formData
|
|
903
|
+
});
|
|
904
|
+
if (!response.ok) {
|
|
905
|
+
const errorText = await response.text();
|
|
906
|
+
throw new Error(`Upload failed (${response.status}): ${errorText}`);
|
|
907
|
+
}
|
|
908
|
+
const result = await response.json();
|
|
909
|
+
if (opts.json || opts.jsonl) {
|
|
910
|
+
formatOutput(result, opts);
|
|
911
|
+
} else {
|
|
912
|
+
success(`Uploaded ${fileName}`);
|
|
913
|
+
if (result.url) {
|
|
914
|
+
console.log(chalk3.bold(` URL: ${result.url}`));
|
|
2430
915
|
}
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
content: generateModelIndex(models)
|
|
2434
|
-
});
|
|
2435
|
-
files.push({
|
|
2436
|
-
path: resolve(outDir, "client.ts"),
|
|
2437
|
-
content: generateClientFactory(publicModels, hasCustomerProfile)
|
|
2438
|
-
});
|
|
2439
|
-
files.push({
|
|
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)
|
|
2446
|
-
});
|
|
2447
|
-
if (config2.dryRun) {
|
|
2448
|
-
console.log(
|
|
2449
|
-
chalk4.bold("\nDry run \u2014 files that would be generated:\n")
|
|
2450
|
-
);
|
|
2451
|
-
for (const file of files) {
|
|
2452
|
-
const rel = file.path.replace(cwd + "/", "");
|
|
2453
|
-
console.log(` ${chalk4.green("+")} ${rel}`);
|
|
2454
|
-
}
|
|
2455
|
-
console.log(`
|
|
2456
|
-
${chalk4.dim(`${files.length} file(s) total`)}`);
|
|
2457
|
-
return;
|
|
916
|
+
if (result.storageKey) {
|
|
917
|
+
console.log(chalk3.gray(` Key: ${result.storageKey}`));
|
|
2458
918
|
}
|
|
2459
|
-
await writeFiles(files, config2.prettier);
|
|
2460
|
-
const cpSuffix = hasCustomerProfile ? " + customer profile" : "";
|
|
2461
|
-
console.log(
|
|
2462
|
-
chalk4.green(`
|
|
2463
|
-
\u2713 Generated ${files.length} file(s)`) + chalk4.dim(
|
|
2464
|
-
` (${models.length} model(s), ${publicModels.length} typed accessor(s)${cpSuffix})`
|
|
2465
|
-
)
|
|
2466
|
-
);
|
|
2467
|
-
console.log(chalk4.dim(` Output: ${outDir}`));
|
|
2468
919
|
}
|
|
2469
|
-
)
|
|
920
|
+
})
|
|
2470
921
|
);
|
|
2471
922
|
}
|
|
2472
|
-
function generateRootIndex(includeCustomerProfile) {
|
|
2473
|
-
let code = `/**
|
|
2474
|
-
* Generated Foir client and model types.
|
|
2475
|
-
*
|
|
2476
|
-
* @generated by foir \u2014 DO NOT EDIT MANUALLY
|
|
2477
|
-
*/
|
|
2478
|
-
|
|
2479
|
-
export { createClient } from './client.js';
|
|
2480
|
-
export type { TypedClient } from './client.js';
|
|
2481
|
-
export * from './models/index.js';
|
|
2482
|
-
`;
|
|
2483
|
-
if (includeCustomerProfile) {
|
|
2484
|
-
code += `export type { CustomerProfileData } from './models/customer-profile.js';
|
|
2485
|
-
`;
|
|
2486
|
-
}
|
|
2487
|
-
return code;
|
|
2488
|
-
}
|
|
2489
923
|
|
|
2490
924
|
// src/commands/create-extension.ts
|
|
2491
|
-
import
|
|
925
|
+
import chalk4 from "chalk";
|
|
2492
926
|
import inquirer2 from "inquirer";
|
|
2493
927
|
|
|
2494
928
|
// src/scaffold/scaffold.ts
|
|
@@ -3011,8 +1445,8 @@ function registerCreateExtensionCommand(program2, globalOpts) {
|
|
|
3011
1445
|
globalOpts,
|
|
3012
1446
|
async (name, cmdOpts) => {
|
|
3013
1447
|
console.log();
|
|
3014
|
-
console.log(
|
|
3015
|
-
console.log(
|
|
1448
|
+
console.log(chalk4.bold(" Create Foir Extension"));
|
|
1449
|
+
console.log(chalk4.gray(" ---------------------"));
|
|
3016
1450
|
console.log();
|
|
3017
1451
|
let extensionName = name;
|
|
3018
1452
|
if (!extensionName) {
|
|
@@ -3044,7 +1478,7 @@ function registerCreateExtensionCommand(program2, globalOpts) {
|
|
|
3044
1478
|
const apiUrl = cmdOpts?.apiUrl ?? "http://localhost:4000/graphql";
|
|
3045
1479
|
console.log();
|
|
3046
1480
|
console.log(
|
|
3047
|
-
` Scaffolding ${
|
|
1481
|
+
` Scaffolding ${chalk4.cyan(`"${extensionName}"`)} (${extensionType})...`
|
|
3048
1482
|
);
|
|
3049
1483
|
console.log();
|
|
3050
1484
|
await scaffold(extensionName, extensionType, apiUrl);
|
|
@@ -3053,6 +1487,164 @@ function registerCreateExtensionCommand(program2, globalOpts) {
|
|
|
3053
1487
|
);
|
|
3054
1488
|
}
|
|
3055
1489
|
|
|
1490
|
+
// src/graphql/generated.ts
|
|
1491
|
+
var GlobalSearchDocument = {
|
|
1492
|
+
kind: "Document",
|
|
1493
|
+
definitions: [
|
|
1494
|
+
{
|
|
1495
|
+
kind: "OperationDefinition",
|
|
1496
|
+
operation: "query",
|
|
1497
|
+
name: { kind: "Name", value: "GlobalSearch" },
|
|
1498
|
+
variableDefinitions: [
|
|
1499
|
+
{
|
|
1500
|
+
kind: "VariableDefinition",
|
|
1501
|
+
variable: {
|
|
1502
|
+
kind: "Variable",
|
|
1503
|
+
name: { kind: "Name", value: "query" }
|
|
1504
|
+
},
|
|
1505
|
+
type: {
|
|
1506
|
+
kind: "NonNullType",
|
|
1507
|
+
type: {
|
|
1508
|
+
kind: "NamedType",
|
|
1509
|
+
name: { kind: "Name", value: "String" }
|
|
1510
|
+
}
|
|
1511
|
+
}
|
|
1512
|
+
},
|
|
1513
|
+
{
|
|
1514
|
+
kind: "VariableDefinition",
|
|
1515
|
+
variable: {
|
|
1516
|
+
kind: "Variable",
|
|
1517
|
+
name: { kind: "Name", value: "limit" }
|
|
1518
|
+
},
|
|
1519
|
+
type: { kind: "NamedType", name: { kind: "Name", value: "Int" } }
|
|
1520
|
+
},
|
|
1521
|
+
{
|
|
1522
|
+
kind: "VariableDefinition",
|
|
1523
|
+
variable: {
|
|
1524
|
+
kind: "Variable",
|
|
1525
|
+
name: { kind: "Name", value: "modelKeys" }
|
|
1526
|
+
},
|
|
1527
|
+
type: {
|
|
1528
|
+
kind: "ListType",
|
|
1529
|
+
type: {
|
|
1530
|
+
kind: "NonNullType",
|
|
1531
|
+
type: {
|
|
1532
|
+
kind: "NamedType",
|
|
1533
|
+
name: { kind: "Name", value: "String" }
|
|
1534
|
+
}
|
|
1535
|
+
}
|
|
1536
|
+
}
|
|
1537
|
+
},
|
|
1538
|
+
{
|
|
1539
|
+
kind: "VariableDefinition",
|
|
1540
|
+
variable: {
|
|
1541
|
+
kind: "Variable",
|
|
1542
|
+
name: { kind: "Name", value: "includeMedia" }
|
|
1543
|
+
},
|
|
1544
|
+
type: { kind: "NamedType", name: { kind: "Name", value: "Boolean" } }
|
|
1545
|
+
}
|
|
1546
|
+
],
|
|
1547
|
+
selectionSet: {
|
|
1548
|
+
kind: "SelectionSet",
|
|
1549
|
+
selections: [
|
|
1550
|
+
{
|
|
1551
|
+
kind: "Field",
|
|
1552
|
+
name: { kind: "Name", value: "globalSearch" },
|
|
1553
|
+
arguments: [
|
|
1554
|
+
{
|
|
1555
|
+
kind: "Argument",
|
|
1556
|
+
name: { kind: "Name", value: "query" },
|
|
1557
|
+
value: {
|
|
1558
|
+
kind: "Variable",
|
|
1559
|
+
name: { kind: "Name", value: "query" }
|
|
1560
|
+
}
|
|
1561
|
+
},
|
|
1562
|
+
{
|
|
1563
|
+
kind: "Argument",
|
|
1564
|
+
name: { kind: "Name", value: "limit" },
|
|
1565
|
+
value: {
|
|
1566
|
+
kind: "Variable",
|
|
1567
|
+
name: { kind: "Name", value: "limit" }
|
|
1568
|
+
}
|
|
1569
|
+
},
|
|
1570
|
+
{
|
|
1571
|
+
kind: "Argument",
|
|
1572
|
+
name: { kind: "Name", value: "modelKeys" },
|
|
1573
|
+
value: {
|
|
1574
|
+
kind: "Variable",
|
|
1575
|
+
name: { kind: "Name", value: "modelKeys" }
|
|
1576
|
+
}
|
|
1577
|
+
},
|
|
1578
|
+
{
|
|
1579
|
+
kind: "Argument",
|
|
1580
|
+
name: { kind: "Name", value: "includeMedia" },
|
|
1581
|
+
value: {
|
|
1582
|
+
kind: "Variable",
|
|
1583
|
+
name: { kind: "Name", value: "includeMedia" }
|
|
1584
|
+
}
|
|
1585
|
+
}
|
|
1586
|
+
],
|
|
1587
|
+
selectionSet: {
|
|
1588
|
+
kind: "SelectionSet",
|
|
1589
|
+
selections: [
|
|
1590
|
+
{
|
|
1591
|
+
kind: "Field",
|
|
1592
|
+
name: { kind: "Name", value: "records" },
|
|
1593
|
+
selectionSet: {
|
|
1594
|
+
kind: "SelectionSet",
|
|
1595
|
+
selections: [
|
|
1596
|
+
{ kind: "Field", name: { kind: "Name", value: "id" } },
|
|
1597
|
+
{
|
|
1598
|
+
kind: "Field",
|
|
1599
|
+
name: { kind: "Name", value: "modelKey" }
|
|
1600
|
+
},
|
|
1601
|
+
{ kind: "Field", name: { kind: "Name", value: "title" } },
|
|
1602
|
+
{
|
|
1603
|
+
kind: "Field",
|
|
1604
|
+
name: { kind: "Name", value: "naturalKey" }
|
|
1605
|
+
},
|
|
1606
|
+
{
|
|
1607
|
+
kind: "Field",
|
|
1608
|
+
name: { kind: "Name", value: "subtitle" }
|
|
1609
|
+
},
|
|
1610
|
+
{
|
|
1611
|
+
kind: "Field",
|
|
1612
|
+
name: { kind: "Name", value: "updatedAt" }
|
|
1613
|
+
}
|
|
1614
|
+
]
|
|
1615
|
+
}
|
|
1616
|
+
},
|
|
1617
|
+
{
|
|
1618
|
+
kind: "Field",
|
|
1619
|
+
name: { kind: "Name", value: "media" },
|
|
1620
|
+
selectionSet: {
|
|
1621
|
+
kind: "SelectionSet",
|
|
1622
|
+
selections: [
|
|
1623
|
+
{ kind: "Field", name: { kind: "Name", value: "id" } },
|
|
1624
|
+
{
|
|
1625
|
+
kind: "Field",
|
|
1626
|
+
name: { kind: "Name", value: "fileName" }
|
|
1627
|
+
},
|
|
1628
|
+
{
|
|
1629
|
+
kind: "Field",
|
|
1630
|
+
name: { kind: "Name", value: "altText" }
|
|
1631
|
+
},
|
|
1632
|
+
{
|
|
1633
|
+
kind: "Field",
|
|
1634
|
+
name: { kind: "Name", value: "fileUrl" }
|
|
1635
|
+
}
|
|
1636
|
+
]
|
|
1637
|
+
}
|
|
1638
|
+
}
|
|
1639
|
+
]
|
|
1640
|
+
}
|
|
1641
|
+
}
|
|
1642
|
+
]
|
|
1643
|
+
}
|
|
1644
|
+
}
|
|
1645
|
+
]
|
|
1646
|
+
};
|
|
1647
|
+
|
|
3056
1648
|
// src/commands/search.ts
|
|
3057
1649
|
function registerSearchCommands(program2, globalOpts) {
|
|
3058
1650
|
program2.command("search <query>").description("Search across all records and media").option(
|
|
@@ -3122,9 +1714,9 @@ Media (${data.globalSearch.media.length}):`);
|
|
|
3122
1714
|
|
|
3123
1715
|
// src/commands/init.ts
|
|
3124
1716
|
import { existsSync as existsSync3, mkdirSync as mkdirSync2 } from "fs";
|
|
3125
|
-
import { writeFile
|
|
3126
|
-
import { resolve as
|
|
3127
|
-
import
|
|
1717
|
+
import { writeFile } from "fs/promises";
|
|
1718
|
+
import { resolve as resolve2, join as join4 } from "path";
|
|
1719
|
+
import chalk5 from "chalk";
|
|
3128
1720
|
import inquirer3 from "inquirer";
|
|
3129
1721
|
var FIELD_DEFAULTS = {
|
|
3130
1722
|
text: "",
|
|
@@ -3214,18 +1806,18 @@ function registerInitCommands(program2, globalOpts) {
|
|
|
3214
1806
|
async (key, opts) => {
|
|
3215
1807
|
const globalFlags = globalOpts();
|
|
3216
1808
|
const template = generateModelTemplate(key);
|
|
3217
|
-
const outDir =
|
|
1809
|
+
const outDir = resolve2(opts.output);
|
|
3218
1810
|
if (!existsSync3(outDir)) {
|
|
3219
1811
|
mkdirSync2(outDir, { recursive: true });
|
|
3220
1812
|
}
|
|
3221
1813
|
const ext = opts.ts ? "ts" : "json";
|
|
3222
1814
|
const filePath = join4(outDir, `${key}.${ext}`);
|
|
3223
1815
|
const content = opts.ts ? formatAsTypeScript(template) : JSON.stringify(template, null, 2) + "\n";
|
|
3224
|
-
await
|
|
1816
|
+
await writeFile(filePath, content, "utf-8");
|
|
3225
1817
|
if (!(globalFlags.json || globalFlags.jsonl || globalFlags.quiet)) {
|
|
3226
1818
|
success(`Created ${filePath}`);
|
|
3227
1819
|
console.log(
|
|
3228
|
-
|
|
1820
|
+
chalk5.gray(
|
|
3229
1821
|
`
|
|
3230
1822
|
Edit the file, then run:
|
|
3231
1823
|
foir models create -f ${filePath}`
|
|
@@ -3251,7 +1843,7 @@ Edit the file, then run:
|
|
|
3251
1843
|
const models = result.models.items;
|
|
3252
1844
|
if (models.length === 0) {
|
|
3253
1845
|
console.log(
|
|
3254
|
-
|
|
1846
|
+
chalk5.yellow(
|
|
3255
1847
|
"No models found. Create models first with `foir models create`."
|
|
3256
1848
|
)
|
|
3257
1849
|
);
|
|
@@ -3264,7 +1856,7 @@ Edit the file, then run:
|
|
|
3264
1856
|
const found = models.find((m) => m.key === key);
|
|
3265
1857
|
if (!found) {
|
|
3266
1858
|
console.error(
|
|
3267
|
-
|
|
1859
|
+
chalk5.red(`Model "${key}" not found.`),
|
|
3268
1860
|
"Available:",
|
|
3269
1861
|
models.map((m) => m.key).join(", ")
|
|
3270
1862
|
);
|
|
@@ -3292,7 +1884,7 @@ Edit the file, then run:
|
|
|
3292
1884
|
console.log("No models selected.");
|
|
3293
1885
|
return;
|
|
3294
1886
|
}
|
|
3295
|
-
const outDir =
|
|
1887
|
+
const outDir = resolve2(opts.output);
|
|
3296
1888
|
if (!existsSync3(outDir)) {
|
|
3297
1889
|
mkdirSync2(outDir, { recursive: true });
|
|
3298
1890
|
}
|
|
@@ -3302,7 +1894,7 @@ Edit the file, then run:
|
|
|
3302
1894
|
const ext = opts.ts ? "ts" : "json";
|
|
3303
1895
|
const filePath = join4(outDir, `${model.key}.${ext}`);
|
|
3304
1896
|
const content = opts.ts ? formatAsTypeScript(seed) : JSON.stringify(seed, null, 2) + "\n";
|
|
3305
|
-
await
|
|
1897
|
+
await writeFile(filePath, content, "utf-8");
|
|
3306
1898
|
createdFiles.push(filePath);
|
|
3307
1899
|
if (!(globalFlags.json || globalFlags.jsonl || globalFlags.quiet)) {
|
|
3308
1900
|
success(`Created ${filePath}`);
|
|
@@ -3310,7 +1902,7 @@ Edit the file, then run:
|
|
|
3310
1902
|
}
|
|
3311
1903
|
if (!(globalFlags.json || globalFlags.jsonl || globalFlags.quiet)) {
|
|
3312
1904
|
console.log(
|
|
3313
|
-
|
|
1905
|
+
chalk5.gray(
|
|
3314
1906
|
`
|
|
3315
1907
|
Edit the files, then run:
|
|
3316
1908
|
foir records create --dir ${outDir} --publish`
|
|
@@ -3325,7 +1917,7 @@ Edit the files, then run:
|
|
|
3325
1917
|
}
|
|
3326
1918
|
|
|
3327
1919
|
// src/commands/profiles.ts
|
|
3328
|
-
import
|
|
1920
|
+
import chalk6 from "chalk";
|
|
3329
1921
|
|
|
3330
1922
|
// src/lib/input.ts
|
|
3331
1923
|
import inquirer4 from "inquirer";
|
|
@@ -3333,9 +1925,9 @@ import inquirer4 from "inquirer";
|
|
|
3333
1925
|
// src/lib/config-loader.ts
|
|
3334
1926
|
import { readFile } from "fs/promises";
|
|
3335
1927
|
import { pathToFileURL } from "url";
|
|
3336
|
-
import { resolve as
|
|
1928
|
+
import { resolve as resolve3 } from "path";
|
|
3337
1929
|
async function loadConfig(filePath) {
|
|
3338
|
-
const absPath =
|
|
1930
|
+
const absPath = resolve3(filePath);
|
|
3339
1931
|
if (filePath.endsWith(".ts")) {
|
|
3340
1932
|
const configModule = await import(pathToFileURL(absPath).href);
|
|
3341
1933
|
return configModule.default;
|
|
@@ -3553,7 +2145,7 @@ function registerProfilesCommand(program2, globalOpts) {
|
|
|
3553
2145
|
if (opts.json || opts.jsonl) {
|
|
3554
2146
|
formatOutput({ deleted: name }, opts);
|
|
3555
2147
|
} else {
|
|
3556
|
-
console.log(
|
|
2148
|
+
console.log(chalk6.green(`Deleted profile "${name}".`));
|
|
3557
2149
|
}
|
|
3558
2150
|
}
|
|
3559
2151
|
)
|
|
@@ -3562,9 +2154,9 @@ function registerProfilesCommand(program2, globalOpts) {
|
|
|
3562
2154
|
|
|
3563
2155
|
// src/commands/register-commands.ts
|
|
3564
2156
|
import { readFileSync, readdirSync } from "fs";
|
|
3565
|
-
import { resolve as
|
|
2157
|
+
import { resolve as resolve4, dirname as dirname4 } from "path";
|
|
3566
2158
|
import { fileURLToPath } from "url";
|
|
3567
|
-
import
|
|
2159
|
+
import chalk7 from "chalk";
|
|
3568
2160
|
|
|
3569
2161
|
// ../command-registry/src/command-map.ts
|
|
3570
2162
|
var COMMANDS = [
|
|
@@ -5163,13 +3755,13 @@ function createSchemaEngine(sdl) {
|
|
|
5163
3755
|
|
|
5164
3756
|
// src/commands/register-commands.ts
|
|
5165
3757
|
var __filename = fileURLToPath(import.meta.url);
|
|
5166
|
-
var __dirname =
|
|
3758
|
+
var __dirname = dirname4(__filename);
|
|
5167
3759
|
function loadSchemaSDL() {
|
|
5168
|
-
const bundledPath =
|
|
3760
|
+
const bundledPath = resolve4(__dirname, "schema.graphql");
|
|
5169
3761
|
try {
|
|
5170
3762
|
return readFileSync(bundledPath, "utf-8");
|
|
5171
3763
|
} catch {
|
|
5172
|
-
const monorepoPath =
|
|
3764
|
+
const monorepoPath = resolve4(
|
|
5173
3765
|
__dirname,
|
|
5174
3766
|
"../../../graphql-core/schema.graphql"
|
|
5175
3767
|
);
|
|
@@ -5325,11 +3917,11 @@ function registerDynamicCommands(program2, globalOpts) {
|
|
|
5325
3917
|
);
|
|
5326
3918
|
Object.assign(variables, coerced);
|
|
5327
3919
|
if (flags.dir && entry.acceptsInput) {
|
|
5328
|
-
const dirPath =
|
|
3920
|
+
const dirPath = resolve4(String(flags.dir));
|
|
5329
3921
|
const files = readdirSync(dirPath).filter((f) => /\.(json|ts|js|mjs)$/.test(f)).sort();
|
|
5330
3922
|
if (files.length === 0) {
|
|
5331
3923
|
console.error(
|
|
5332
|
-
|
|
3924
|
+
chalk7.yellow(`\u26A0 No .json/.ts/.js files found in ${dirPath}`)
|
|
5333
3925
|
);
|
|
5334
3926
|
return;
|
|
5335
3927
|
}
|
|
@@ -5337,7 +3929,7 @@ function registerDynamicCommands(program2, globalOpts) {
|
|
|
5337
3929
|
let updated = 0;
|
|
5338
3930
|
let failed = 0;
|
|
5339
3931
|
for (const file of files) {
|
|
5340
|
-
const filePath =
|
|
3932
|
+
const filePath = resolve4(dirPath, file);
|
|
5341
3933
|
const fileData = await parseInputData({ file: filePath });
|
|
5342
3934
|
const argName = entry.inputArgName ?? "input";
|
|
5343
3935
|
const fileVars = { ...variables, [argName]: fileData };
|
|
@@ -5374,19 +3966,19 @@ function registerDynamicCommands(program2, globalOpts) {
|
|
|
5374
3966
|
} catch (updateErr) {
|
|
5375
3967
|
failed++;
|
|
5376
3968
|
const msg2 = updateErr instanceof Error ? updateErr.message : String(updateErr);
|
|
5377
|
-
console.error(
|
|
3969
|
+
console.error(chalk7.red(`\u2717 ${label}:`), msg2);
|
|
5378
3970
|
continue;
|
|
5379
3971
|
}
|
|
5380
3972
|
}
|
|
5381
3973
|
failed++;
|
|
5382
3974
|
const msg = err instanceof Error ? err.message : String(err);
|
|
5383
|
-
console.error(
|
|
3975
|
+
console.error(chalk7.red(`\u2717 ${label}:`), msg);
|
|
5384
3976
|
}
|
|
5385
3977
|
}
|
|
5386
3978
|
if (!(opts.json || opts.jsonl || opts.quiet)) {
|
|
5387
3979
|
console.log("");
|
|
5388
3980
|
console.log(
|
|
5389
|
-
|
|
3981
|
+
chalk7.bold(
|
|
5390
3982
|
`Done: ${created} created${updated ? `, ${updated} updated` : ""}${failed ? `, ${failed} failed` : ""}`
|
|
5391
3983
|
)
|
|
5392
3984
|
);
|
|
@@ -5562,7 +4154,7 @@ function registerDynamicCommands(program2, globalOpts) {
|
|
|
5562
4154
|
}
|
|
5563
4155
|
} else if (!(opts.json || opts.jsonl || opts.quiet)) {
|
|
5564
4156
|
console.error(
|
|
5565
|
-
|
|
4157
|
+
chalk7.yellow(
|
|
5566
4158
|
"\u26A0 Could not auto-publish: no version found in response"
|
|
5567
4159
|
)
|
|
5568
4160
|
);
|
|
@@ -5585,8 +4177,8 @@ function autoColumns(items) {
|
|
|
5585
4177
|
|
|
5586
4178
|
// src/cli.ts
|
|
5587
4179
|
var __filename2 = fileURLToPath2(import.meta.url);
|
|
5588
|
-
var __dirname2 =
|
|
5589
|
-
config({ path:
|
|
4180
|
+
var __dirname2 = dirname5(__filename2);
|
|
4181
|
+
config({ path: resolve5(__dirname2, "../.env.local") });
|
|
5590
4182
|
var require2 = createRequire(import.meta.url);
|
|
5591
4183
|
var { version } = require2("../package.json");
|
|
5592
4184
|
var program = new Command();
|
|
@@ -5608,7 +4200,6 @@ registerWhoamiCommand(program, getGlobalOpts);
|
|
|
5608
4200
|
registerProfilesCommand(program, getGlobalOpts);
|
|
5609
4201
|
registerMediaCommands(program, getGlobalOpts);
|
|
5610
4202
|
registerSearchCommands(program, getGlobalOpts);
|
|
5611
|
-
registerPullCommand(program, getGlobalOpts);
|
|
5612
4203
|
registerCreateExtensionCommand(program, getGlobalOpts);
|
|
5613
4204
|
registerInitCommands(program, getGlobalOpts);
|
|
5614
4205
|
registerDynamicCommands(program, getGlobalOpts);
|