@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 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 resolve6, dirname as dirname6 } from "path";
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((resolve7) => {
346
+ const available = await new Promise((resolve6) => {
366
347
  const server = http.createServer();
367
348
  server.listen(port, () => {
368
349
  server.close();
369
- resolve7(true);
350
+ resolve6(true);
370
351
  });
371
- server.on("error", () => resolve7(false));
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((resolve7, reject) => {
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
- resolve7(code);
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
- code += `}
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
- return imports;
2321
- }
2322
-
2323
- // src/codegen/write-files.ts
2324
- import { mkdir, writeFile } from "fs/promises";
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
- await writeFile(filePath, formattedContent, "utf-8");
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/pull.ts
2355
- function registerPullCommand(program2, globalOpts) {
2356
- program2.command("pull").description(
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(
2359
- withErrorHandler(
2360
- globalOpts,
2361
- async (cmdOpts) => {
2362
- const opts = globalOpts();
2363
- const flags = {
2364
- config: cmdOpts.config,
2365
- only: cmdOpts.only,
2366
- noPrettier: cmdOpts.prettier === false,
2367
- dryRun: !!cmdOpts.dryRun,
2368
- out: cmdOpts.out
2369
- };
2370
- const config2 = await loadPullConfig(flags);
2371
- const client = await createClient(opts);
2372
- console.log(chalk4.dim("Fetching models\u2026"));
2373
- const [allModels, cpSchema] = await Promise.all([
2374
- fetchModelsForCodegen(client),
2375
- fetchCustomerProfileSchema(client)
2376
- ]);
2377
- if (allModels.length === 0 && !cpSchema) {
2378
- console.log(chalk4.yellow("No models found. Nothing to generate."));
2379
- return;
2380
- }
2381
- const models = filterModels(allModels, {
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
- files.push({
2432
- path: resolve(modelsDir, "index.ts"),
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 chalk5 from "chalk";
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(chalk5.bold(" Create Foir Extension"));
3015
- console.log(chalk5.gray(" ---------------------"));
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 ${chalk5.cyan(`"${extensionName}"`)} (${extensionType})...`
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 as writeFile2 } from "fs/promises";
3126
- import { resolve as resolve3, join as join4 } from "path";
3127
- import chalk6 from "chalk";
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 = resolve3(opts.output);
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 writeFile2(filePath, content, "utf-8");
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
- chalk6.gray(
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
- chalk6.yellow(
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
- chalk6.red(`Model "${key}" not found.`),
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 = resolve3(opts.output);
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 writeFile2(filePath, content, "utf-8");
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
- chalk6.gray(
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 chalk7 from "chalk";
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 resolve4 } from "path";
1928
+ import { resolve as resolve3 } from "path";
3337
1929
  async function loadConfig(filePath) {
3338
- const absPath = resolve4(filePath);
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(chalk7.green(`Deleted profile "${name}".`));
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 resolve5, dirname as dirname5 } from "path";
2157
+ import { resolve as resolve4, dirname as dirname4 } from "path";
3566
2158
  import { fileURLToPath } from "url";
3567
- import chalk8 from "chalk";
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 = dirname5(__filename);
3758
+ var __dirname = dirname4(__filename);
5167
3759
  function loadSchemaSDL() {
5168
- const bundledPath = resolve5(__dirname, "schema.graphql");
3760
+ const bundledPath = resolve4(__dirname, "schema.graphql");
5169
3761
  try {
5170
3762
  return readFileSync(bundledPath, "utf-8");
5171
3763
  } catch {
5172
- const monorepoPath = resolve5(
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 = resolve5(String(flags.dir));
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
- chalk8.yellow(`\u26A0 No .json/.ts/.js files found in ${dirPath}`)
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 = resolve5(dirPath, file);
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(chalk8.red(`\u2717 ${label}:`), msg2);
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(chalk8.red(`\u2717 ${label}:`), msg);
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
- chalk8.bold(
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
- chalk8.yellow(
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 = dirname6(__filename2);
5589
- config({ path: resolve6(__dirname2, "../.env.local") });
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);