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