@eide/foir-cli 0.1.44 → 0.1.46
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 +616 -1848
- package/dist/{generated-xEC9Cfzw.d.ts → lib/config-helpers.d.ts} +95 -77
- package/dist/lib/config-helpers.js +41 -0
- package/dist/schema.graphql +93 -63
- package/package.json +5 -18
- package/dist/chunk-L642MYIL.js +0 -47
- package/dist/config/types.d.ts +0 -48
- package/dist/config/types.js +0 -7
- package/dist/lib/extension-helpers.d.ts +0 -100
- package/dist/lib/extension-helpers.js +0 -23
- package/dist/lib/hook-helpers.d.ts +0 -108
- package/dist/lib/hook-helpers.js +0 -15
- package/dist/lib/seed-helpers.d.ts +0 -124
- package/dist/lib/seed-helpers.js +0 -23
- 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 resolve6, dirname as
|
|
5
|
+
import { resolve as resolve6, dirname as dirname5 } from "path";
|
|
10
6
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
11
7
|
import { createRequire } from "module";
|
|
12
8
|
import { Command } from "commander";
|
|
@@ -218,21 +214,6 @@ async function resolveProjectContext(options) {
|
|
|
218
214
|
};
|
|
219
215
|
}
|
|
220
216
|
}
|
|
221
|
-
try {
|
|
222
|
-
const { loadConfigProject } = await import("./loader-7VE4OF73.js");
|
|
223
|
-
const configProfile = await loadConfigProject();
|
|
224
|
-
if (configProfile) {
|
|
225
|
-
const project2 = await getProjectContext(configProfile);
|
|
226
|
-
if (project2) {
|
|
227
|
-
return {
|
|
228
|
-
project: project2,
|
|
229
|
-
source: "foir.config.ts",
|
|
230
|
-
profileName: configProfile
|
|
231
|
-
};
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
} catch {
|
|
235
|
-
}
|
|
236
217
|
const defaultProfile = await getDefaultProfile();
|
|
237
218
|
if (defaultProfile) {
|
|
238
219
|
const project2 = await getProjectContext(defaultProfile);
|
|
@@ -516,7 +497,7 @@ var CLI_API_KEY_SCOPES = [
|
|
|
516
497
|
"records:publish",
|
|
517
498
|
"files:read",
|
|
518
499
|
"files:write",
|
|
519
|
-
"
|
|
500
|
+
"configs:read",
|
|
520
501
|
"operations:read",
|
|
521
502
|
"operations:execute"
|
|
522
503
|
];
|
|
@@ -940,1717 +921,181 @@ function registerMediaCommands(program2, globalOpts) {
|
|
|
940
921
|
);
|
|
941
922
|
}
|
|
942
923
|
|
|
943
|
-
// src/commands/
|
|
944
|
-
import { resolve } from "path";
|
|
924
|
+
// src/commands/create-config.ts
|
|
945
925
|
import chalk4 from "chalk";
|
|
926
|
+
import inquirer2 from "inquirer";
|
|
946
927
|
|
|
947
|
-
// src/
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
928
|
+
// src/scaffold/scaffold.ts
|
|
929
|
+
import * as fs4 from "fs";
|
|
930
|
+
import * as path2 from "path";
|
|
931
|
+
|
|
932
|
+
// src/scaffold/package-manager.ts
|
|
933
|
+
import * as fs3 from "fs";
|
|
934
|
+
import * as path from "path";
|
|
935
|
+
function detectPackageManager() {
|
|
936
|
+
const lockFiles = [
|
|
937
|
+
{ file: "pnpm-lock.yaml", manager: "pnpm" },
|
|
938
|
+
{ file: "yarn.lock", manager: "yarn" },
|
|
939
|
+
{ file: "package-lock.json", manager: "npm" }
|
|
940
|
+
];
|
|
941
|
+
let dir = process.cwd();
|
|
942
|
+
while (true) {
|
|
943
|
+
for (const { file, manager } of lockFiles) {
|
|
944
|
+
if (fs3.existsSync(path.join(dir, file))) {
|
|
945
|
+
return getManagerInfo(manager);
|
|
946
|
+
}
|
|
947
|
+
}
|
|
948
|
+
const parentDir = path.dirname(dir);
|
|
949
|
+
if (parentDir === dir) {
|
|
950
|
+
break;
|
|
951
|
+
}
|
|
952
|
+
dir = parentDir;
|
|
955
953
|
}
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
954
|
+
return getManagerInfo("npm");
|
|
955
|
+
}
|
|
956
|
+
function getManagerInfo(manager) {
|
|
957
|
+
switch (manager) {
|
|
958
|
+
case "pnpm":
|
|
959
|
+
return {
|
|
960
|
+
name: "pnpm",
|
|
961
|
+
installCommand: "pnpm install",
|
|
962
|
+
execCommand: "pnpm dlx"
|
|
963
|
+
};
|
|
964
|
+
case "yarn":
|
|
965
|
+
return {
|
|
966
|
+
name: "yarn",
|
|
967
|
+
installCommand: "yarn install",
|
|
968
|
+
execCommand: "yarn dlx"
|
|
969
|
+
};
|
|
970
|
+
case "npm":
|
|
971
|
+
return {
|
|
972
|
+
name: "npm",
|
|
973
|
+
installCommand: "npm install",
|
|
974
|
+
execCommand: "npx"
|
|
975
|
+
};
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
// src/scaffold/scaffold.ts
|
|
980
|
+
async function scaffold(projectName, configType, apiUrl) {
|
|
981
|
+
const projectDir = path2.resolve(process.cwd(), projectName);
|
|
982
|
+
if (fs4.existsSync(projectDir)) {
|
|
983
|
+
throw new Error(
|
|
984
|
+
`Directory "${projectName}" already exists. Choose a different name or remove the existing directory.`
|
|
985
|
+
);
|
|
986
|
+
}
|
|
987
|
+
const files = getFiles(projectName, configType, apiUrl);
|
|
988
|
+
for (const [filePath, content] of Object.entries(files)) {
|
|
989
|
+
const fullPath = path2.join(projectDir, filePath);
|
|
990
|
+
const dir = path2.dirname(fullPath);
|
|
991
|
+
if (!fs4.existsSync(dir)) {
|
|
992
|
+
fs4.mkdirSync(dir, { recursive: true });
|
|
993
|
+
}
|
|
994
|
+
fs4.writeFileSync(fullPath, content, "utf-8");
|
|
995
|
+
}
|
|
996
|
+
const pm = detectPackageManager();
|
|
997
|
+
console.log(" Files created:");
|
|
998
|
+
console.log();
|
|
999
|
+
for (const filePath of Object.keys(files)) {
|
|
1000
|
+
console.log(` ${filePath}`);
|
|
966
1001
|
}
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
1002
|
+
console.log();
|
|
1003
|
+
console.log(" Done! Next steps:");
|
|
1004
|
+
console.log();
|
|
1005
|
+
console.log(` cd ${projectName}`);
|
|
1006
|
+
console.log(` ${pm.installCommand}`);
|
|
1007
|
+
console.log(` cp ui/.env.example ui/.env.local`);
|
|
1008
|
+
console.log(` cp api/.env.example api/.env.local`);
|
|
1009
|
+
console.log(` ${pm.name === "npm" ? "npm run" : pm.name} dev`);
|
|
1010
|
+
console.log();
|
|
1011
|
+
}
|
|
1012
|
+
function getFiles(projectName, configType, apiUrl) {
|
|
971
1013
|
return {
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
1014
|
+
// Root
|
|
1015
|
+
"package.json": getRootPackageJson(projectName),
|
|
1016
|
+
// Config manifest
|
|
1017
|
+
"foir.config.ts": getFoirConfig(projectName, configType),
|
|
1018
|
+
// UI (Vite SPA)
|
|
1019
|
+
"ui/package.json": getUiPackageJson(projectName),
|
|
1020
|
+
"ui/tsconfig.json": getUiTsconfig(),
|
|
1021
|
+
"ui/vite.config.ts": getUiViteConfig(),
|
|
1022
|
+
"ui/index.html": getUiIndexHtml(projectName),
|
|
1023
|
+
"ui/.env.example": getUiEnvExample(apiUrl),
|
|
1024
|
+
"ui/.gitignore": getUiGitignore(),
|
|
1025
|
+
"ui/src/main.tsx": getUiMain(),
|
|
1026
|
+
"ui/src/App.tsx": getUiApp(configType),
|
|
1027
|
+
"ui/src/index.css": getUiCss(),
|
|
1028
|
+
"ui/src/vite-env.d.ts": '/// <reference types="vite/client" />\n',
|
|
1029
|
+
// API (Hono)
|
|
1030
|
+
"api/package.json": getApiPackageJson(projectName),
|
|
1031
|
+
"api/tsconfig.json": getApiTsconfig(),
|
|
1032
|
+
"api/.env.example": getApiEnvExample(apiUrl),
|
|
1033
|
+
"api/.gitignore": "node_modules\ndist\n.env\n.env.local\n",
|
|
1034
|
+
"api/src/index.ts": getApiIndex(),
|
|
1035
|
+
"api/src/routes/webhooks.ts": getApiWebhooks(),
|
|
1036
|
+
"api/src/routes/health.ts": getApiHealth()
|
|
1037
|
+
};
|
|
1038
|
+
}
|
|
1039
|
+
function getRootPackageJson(name) {
|
|
1040
|
+
const pkg = {
|
|
1041
|
+
name,
|
|
1042
|
+
version: "0.1.0",
|
|
1043
|
+
private: true,
|
|
1044
|
+
scripts: {
|
|
1045
|
+
dev: 'concurrently "pnpm --filter ./ui dev" "pnpm --filter ./api dev"',
|
|
1046
|
+
build: "pnpm --filter ./ui build && pnpm --filter ./api build"
|
|
1047
|
+
},
|
|
1048
|
+
devDependencies: {
|
|
1049
|
+
concurrently: "^9.0.0",
|
|
1050
|
+
"@eide/foir-cli": "^0.1.0"
|
|
1051
|
+
}
|
|
977
1052
|
};
|
|
1053
|
+
return JSON.stringify(pkg, null, 2) + "\n";
|
|
978
1054
|
}
|
|
1055
|
+
function getFoirConfig(name, configType) {
|
|
1056
|
+
const displayName = name.replace(/-/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
1057
|
+
return `import { defineConfig } from '@eide/foir-cli/configs';
|
|
979
1058
|
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
kind: "Field",
|
|
1021
|
-
name: { kind: "Name", value: "config" }
|
|
1022
|
-
},
|
|
1023
|
-
{
|
|
1024
|
-
kind: "Field",
|
|
1025
|
-
name: { kind: "Name", value: "validation" },
|
|
1026
|
-
selectionSet: {
|
|
1027
|
-
kind: "SelectionSet",
|
|
1028
|
-
selections: [
|
|
1029
|
-
{
|
|
1030
|
-
kind: "Field",
|
|
1031
|
-
name: { kind: "Name", value: "rule" }
|
|
1032
|
-
},
|
|
1033
|
-
{
|
|
1034
|
-
kind: "Field",
|
|
1035
|
-
name: { kind: "Name", value: "value" }
|
|
1036
|
-
},
|
|
1037
|
-
{
|
|
1038
|
-
kind: "Field",
|
|
1039
|
-
name: { kind: "Name", value: "message" }
|
|
1040
|
-
}
|
|
1041
|
-
]
|
|
1042
|
-
}
|
|
1043
|
-
}
|
|
1044
|
-
]
|
|
1045
|
-
}
|
|
1046
|
-
},
|
|
1047
|
-
{
|
|
1048
|
-
kind: "Field",
|
|
1049
|
-
name: { kind: "Name", value: "publicFields" }
|
|
1050
|
-
},
|
|
1051
|
-
{ kind: "Field", name: { kind: "Name", value: "version" } },
|
|
1052
|
-
{ kind: "Field", name: { kind: "Name", value: "createdAt" } },
|
|
1053
|
-
{ kind: "Field", name: { kind: "Name", value: "updatedAt" } }
|
|
1054
|
-
]
|
|
1055
|
-
}
|
|
1056
|
-
}
|
|
1057
|
-
]
|
|
1058
|
-
}
|
|
1059
|
-
}
|
|
1060
|
-
]
|
|
1061
|
-
};
|
|
1062
|
-
var ModelsForCodegenDocument = {
|
|
1063
|
-
kind: "Document",
|
|
1064
|
-
definitions: [
|
|
1065
|
-
{
|
|
1066
|
-
kind: "OperationDefinition",
|
|
1067
|
-
operation: "query",
|
|
1068
|
-
name: { kind: "Name", value: "ModelsForCodegen" },
|
|
1069
|
-
variableDefinitions: [
|
|
1070
|
-
{
|
|
1071
|
-
kind: "VariableDefinition",
|
|
1072
|
-
variable: {
|
|
1073
|
-
kind: "Variable",
|
|
1074
|
-
name: { kind: "Name", value: "search" }
|
|
1075
|
-
},
|
|
1076
|
-
type: { kind: "NamedType", name: { kind: "Name", value: "String" } }
|
|
1077
|
-
},
|
|
1078
|
-
{
|
|
1079
|
-
kind: "VariableDefinition",
|
|
1080
|
-
variable: {
|
|
1081
|
-
kind: "Variable",
|
|
1082
|
-
name: { kind: "Name", value: "limit" }
|
|
1083
|
-
},
|
|
1084
|
-
type: { kind: "NamedType", name: { kind: "Name", value: "Int" } }
|
|
1085
|
-
},
|
|
1086
|
-
{
|
|
1087
|
-
kind: "VariableDefinition",
|
|
1088
|
-
variable: {
|
|
1089
|
-
kind: "Variable",
|
|
1090
|
-
name: { kind: "Name", value: "offset" }
|
|
1091
|
-
},
|
|
1092
|
-
type: { kind: "NamedType", name: { kind: "Name", value: "Int" } }
|
|
1093
|
-
}
|
|
1094
|
-
],
|
|
1095
|
-
selectionSet: {
|
|
1096
|
-
kind: "SelectionSet",
|
|
1097
|
-
selections: [
|
|
1098
|
-
{
|
|
1099
|
-
kind: "Field",
|
|
1100
|
-
name: { kind: "Name", value: "models" },
|
|
1101
|
-
arguments: [
|
|
1102
|
-
{
|
|
1103
|
-
kind: "Argument",
|
|
1104
|
-
name: { kind: "Name", value: "search" },
|
|
1105
|
-
value: {
|
|
1106
|
-
kind: "Variable",
|
|
1107
|
-
name: { kind: "Name", value: "search" }
|
|
1108
|
-
}
|
|
1109
|
-
},
|
|
1110
|
-
{
|
|
1111
|
-
kind: "Argument",
|
|
1112
|
-
name: { kind: "Name", value: "limit" },
|
|
1113
|
-
value: {
|
|
1114
|
-
kind: "Variable",
|
|
1115
|
-
name: { kind: "Name", value: "limit" }
|
|
1116
|
-
}
|
|
1117
|
-
},
|
|
1118
|
-
{
|
|
1119
|
-
kind: "Argument",
|
|
1120
|
-
name: { kind: "Name", value: "offset" },
|
|
1121
|
-
value: {
|
|
1122
|
-
kind: "Variable",
|
|
1123
|
-
name: { kind: "Name", value: "offset" }
|
|
1124
|
-
}
|
|
1125
|
-
}
|
|
1126
|
-
],
|
|
1127
|
-
selectionSet: {
|
|
1128
|
-
kind: "SelectionSet",
|
|
1129
|
-
selections: [
|
|
1130
|
-
{
|
|
1131
|
-
kind: "Field",
|
|
1132
|
-
name: { kind: "Name", value: "items" },
|
|
1133
|
-
selectionSet: {
|
|
1134
|
-
kind: "SelectionSet",
|
|
1135
|
-
selections: [
|
|
1136
|
-
{ kind: "Field", name: { kind: "Name", value: "id" } },
|
|
1137
|
-
{ kind: "Field", name: { kind: "Name", value: "key" } },
|
|
1138
|
-
{ kind: "Field", name: { kind: "Name", value: "name" } },
|
|
1139
|
-
{
|
|
1140
|
-
kind: "Field",
|
|
1141
|
-
name: { kind: "Name", value: "pluralName" }
|
|
1142
|
-
},
|
|
1143
|
-
{
|
|
1144
|
-
kind: "Field",
|
|
1145
|
-
name: { kind: "Name", value: "description" }
|
|
1146
|
-
},
|
|
1147
|
-
{
|
|
1148
|
-
kind: "Field",
|
|
1149
|
-
name: { kind: "Name", value: "category" }
|
|
1150
|
-
},
|
|
1151
|
-
{
|
|
1152
|
-
kind: "Field",
|
|
1153
|
-
name: { kind: "Name", value: "fields" }
|
|
1154
|
-
},
|
|
1155
|
-
{
|
|
1156
|
-
kind: "Field",
|
|
1157
|
-
name: { kind: "Name", value: "config" }
|
|
1158
|
-
},
|
|
1159
|
-
{ kind: "Field", name: { kind: "Name", value: "hooks" } },
|
|
1160
|
-
{
|
|
1161
|
-
kind: "Field",
|
|
1162
|
-
name: { kind: "Name", value: "createdAt" }
|
|
1163
|
-
},
|
|
1164
|
-
{
|
|
1165
|
-
kind: "Field",
|
|
1166
|
-
name: { kind: "Name", value: "updatedAt" }
|
|
1167
|
-
}
|
|
1168
|
-
]
|
|
1169
|
-
}
|
|
1170
|
-
},
|
|
1171
|
-
{ kind: "Field", name: { kind: "Name", value: "total" } }
|
|
1172
|
-
]
|
|
1173
|
-
}
|
|
1174
|
-
}
|
|
1175
|
-
]
|
|
1176
|
-
}
|
|
1177
|
-
}
|
|
1178
|
-
]
|
|
1179
|
-
};
|
|
1180
|
-
var GlobalSearchDocument = {
|
|
1181
|
-
kind: "Document",
|
|
1182
|
-
definitions: [
|
|
1183
|
-
{
|
|
1184
|
-
kind: "OperationDefinition",
|
|
1185
|
-
operation: "query",
|
|
1186
|
-
name: { kind: "Name", value: "GlobalSearch" },
|
|
1187
|
-
variableDefinitions: [
|
|
1188
|
-
{
|
|
1189
|
-
kind: "VariableDefinition",
|
|
1190
|
-
variable: {
|
|
1191
|
-
kind: "Variable",
|
|
1192
|
-
name: { kind: "Name", value: "query" }
|
|
1193
|
-
},
|
|
1194
|
-
type: {
|
|
1195
|
-
kind: "NonNullType",
|
|
1196
|
-
type: {
|
|
1197
|
-
kind: "NamedType",
|
|
1198
|
-
name: { kind: "Name", value: "String" }
|
|
1199
|
-
}
|
|
1200
|
-
}
|
|
1201
|
-
},
|
|
1202
|
-
{
|
|
1203
|
-
kind: "VariableDefinition",
|
|
1204
|
-
variable: {
|
|
1205
|
-
kind: "Variable",
|
|
1206
|
-
name: { kind: "Name", value: "limit" }
|
|
1207
|
-
},
|
|
1208
|
-
type: { kind: "NamedType", name: { kind: "Name", value: "Int" } }
|
|
1209
|
-
},
|
|
1210
|
-
{
|
|
1211
|
-
kind: "VariableDefinition",
|
|
1212
|
-
variable: {
|
|
1213
|
-
kind: "Variable",
|
|
1214
|
-
name: { kind: "Name", value: "modelKeys" }
|
|
1215
|
-
},
|
|
1216
|
-
type: {
|
|
1217
|
-
kind: "ListType",
|
|
1218
|
-
type: {
|
|
1219
|
-
kind: "NonNullType",
|
|
1220
|
-
type: {
|
|
1221
|
-
kind: "NamedType",
|
|
1222
|
-
name: { kind: "Name", value: "String" }
|
|
1223
|
-
}
|
|
1224
|
-
}
|
|
1225
|
-
}
|
|
1226
|
-
},
|
|
1227
|
-
{
|
|
1228
|
-
kind: "VariableDefinition",
|
|
1229
|
-
variable: {
|
|
1230
|
-
kind: "Variable",
|
|
1231
|
-
name: { kind: "Name", value: "includeMedia" }
|
|
1232
|
-
},
|
|
1233
|
-
type: { kind: "NamedType", name: { kind: "Name", value: "Boolean" } }
|
|
1234
|
-
}
|
|
1235
|
-
],
|
|
1236
|
-
selectionSet: {
|
|
1237
|
-
kind: "SelectionSet",
|
|
1238
|
-
selections: [
|
|
1239
|
-
{
|
|
1240
|
-
kind: "Field",
|
|
1241
|
-
name: { kind: "Name", value: "globalSearch" },
|
|
1242
|
-
arguments: [
|
|
1243
|
-
{
|
|
1244
|
-
kind: "Argument",
|
|
1245
|
-
name: { kind: "Name", value: "query" },
|
|
1246
|
-
value: {
|
|
1247
|
-
kind: "Variable",
|
|
1248
|
-
name: { kind: "Name", value: "query" }
|
|
1249
|
-
}
|
|
1250
|
-
},
|
|
1251
|
-
{
|
|
1252
|
-
kind: "Argument",
|
|
1253
|
-
name: { kind: "Name", value: "limit" },
|
|
1254
|
-
value: {
|
|
1255
|
-
kind: "Variable",
|
|
1256
|
-
name: { kind: "Name", value: "limit" }
|
|
1257
|
-
}
|
|
1258
|
-
},
|
|
1259
|
-
{
|
|
1260
|
-
kind: "Argument",
|
|
1261
|
-
name: { kind: "Name", value: "modelKeys" },
|
|
1262
|
-
value: {
|
|
1263
|
-
kind: "Variable",
|
|
1264
|
-
name: { kind: "Name", value: "modelKeys" }
|
|
1265
|
-
}
|
|
1266
|
-
},
|
|
1267
|
-
{
|
|
1268
|
-
kind: "Argument",
|
|
1269
|
-
name: { kind: "Name", value: "includeMedia" },
|
|
1270
|
-
value: {
|
|
1271
|
-
kind: "Variable",
|
|
1272
|
-
name: { kind: "Name", value: "includeMedia" }
|
|
1273
|
-
}
|
|
1274
|
-
}
|
|
1275
|
-
],
|
|
1276
|
-
selectionSet: {
|
|
1277
|
-
kind: "SelectionSet",
|
|
1278
|
-
selections: [
|
|
1279
|
-
{
|
|
1280
|
-
kind: "Field",
|
|
1281
|
-
name: { kind: "Name", value: "records" },
|
|
1282
|
-
selectionSet: {
|
|
1283
|
-
kind: "SelectionSet",
|
|
1284
|
-
selections: [
|
|
1285
|
-
{ kind: "Field", name: { kind: "Name", value: "id" } },
|
|
1286
|
-
{
|
|
1287
|
-
kind: "Field",
|
|
1288
|
-
name: { kind: "Name", value: "modelKey" }
|
|
1289
|
-
},
|
|
1290
|
-
{ kind: "Field", name: { kind: "Name", value: "title" } },
|
|
1291
|
-
{
|
|
1292
|
-
kind: "Field",
|
|
1293
|
-
name: { kind: "Name", value: "naturalKey" }
|
|
1294
|
-
},
|
|
1295
|
-
{
|
|
1296
|
-
kind: "Field",
|
|
1297
|
-
name: { kind: "Name", value: "subtitle" }
|
|
1298
|
-
},
|
|
1299
|
-
{
|
|
1300
|
-
kind: "Field",
|
|
1301
|
-
name: { kind: "Name", value: "updatedAt" }
|
|
1302
|
-
}
|
|
1303
|
-
]
|
|
1304
|
-
}
|
|
1305
|
-
},
|
|
1306
|
-
{
|
|
1307
|
-
kind: "Field",
|
|
1308
|
-
name: { kind: "Name", value: "media" },
|
|
1309
|
-
selectionSet: {
|
|
1310
|
-
kind: "SelectionSet",
|
|
1311
|
-
selections: [
|
|
1312
|
-
{ kind: "Field", name: { kind: "Name", value: "id" } },
|
|
1313
|
-
{
|
|
1314
|
-
kind: "Field",
|
|
1315
|
-
name: { kind: "Name", value: "fileName" }
|
|
1316
|
-
},
|
|
1317
|
-
{
|
|
1318
|
-
kind: "Field",
|
|
1319
|
-
name: { kind: "Name", value: "altText" }
|
|
1320
|
-
},
|
|
1321
|
-
{
|
|
1322
|
-
kind: "Field",
|
|
1323
|
-
name: { kind: "Name", value: "fileUrl" }
|
|
1324
|
-
}
|
|
1325
|
-
]
|
|
1326
|
-
}
|
|
1327
|
-
}
|
|
1328
|
-
]
|
|
1329
|
-
}
|
|
1330
|
-
}
|
|
1331
|
-
]
|
|
1332
|
-
}
|
|
1333
|
-
}
|
|
1334
|
-
]
|
|
1335
|
-
};
|
|
1336
|
-
|
|
1337
|
-
// src/codegen/fetch-models.ts
|
|
1338
|
-
function normalizeConfig(raw) {
|
|
1339
|
-
return {
|
|
1340
|
-
records: Boolean(raw.records ?? true),
|
|
1341
|
-
inline: Boolean(raw.inline ?? false),
|
|
1342
|
-
publicApi: Boolean(raw.publicApi ?? false),
|
|
1343
|
-
versioning: Boolean(raw.versioning ?? false),
|
|
1344
|
-
publishing: Boolean(raw.publishing ?? false),
|
|
1345
|
-
variants: Boolean(raw.variants ?? false),
|
|
1346
|
-
customerScoped: Boolean(raw.customerScoped ?? false),
|
|
1347
|
-
sharing: raw.sharing ? {
|
|
1348
|
-
enabled: Boolean(
|
|
1349
|
-
raw.sharing.enabled ?? false
|
|
1350
|
-
),
|
|
1351
|
-
requireAcceptance: Boolean(
|
|
1352
|
-
raw.sharing.requireAcceptance ?? true
|
|
1353
|
-
)
|
|
1354
|
-
} : void 0,
|
|
1355
|
-
embeddings: raw.embeddings
|
|
1356
|
-
};
|
|
1357
|
-
}
|
|
1358
|
-
function normalizeField(raw) {
|
|
1359
|
-
const field = raw;
|
|
1360
|
-
const options = { ...field.options ?? {} };
|
|
1361
|
-
const resolvedType = field.type === "composite" && field.config?.schema ? field.config.schema : field.type;
|
|
1362
|
-
if (!options.itemType) {
|
|
1363
|
-
const resolved = field.itemType ?? field.config?.itemType ?? field.config?.itemSchema;
|
|
1364
|
-
if (resolved) {
|
|
1365
|
-
options.itemType = resolved;
|
|
1366
|
-
}
|
|
1367
|
-
}
|
|
1368
|
-
if (field.config?.minItems !== void 0 && options.minItems === void 0) {
|
|
1369
|
-
options.minItems = field.config.minItems;
|
|
1370
|
-
}
|
|
1371
|
-
if (field.config?.maxItems !== void 0 && options.maxItems === void 0) {
|
|
1372
|
-
options.maxItems = field.config.maxItems;
|
|
1373
|
-
}
|
|
1374
|
-
return {
|
|
1375
|
-
key: field.key,
|
|
1376
|
-
type: resolvedType,
|
|
1377
|
-
label: field.label,
|
|
1378
|
-
required: field.required,
|
|
1379
|
-
helpText: field.helpText,
|
|
1380
|
-
options: Object.keys(options).length > 0 ? options : void 0
|
|
1381
|
-
};
|
|
1382
|
-
}
|
|
1383
|
-
async function fetchModelsForCodegen(client) {
|
|
1384
|
-
const data = await client.request(ModelsForCodegenDocument, {
|
|
1385
|
-
limit: 500
|
|
1386
|
-
});
|
|
1387
|
-
return data.models.items.map((item) => ({
|
|
1388
|
-
key: item.key,
|
|
1389
|
-
name: item.name,
|
|
1390
|
-
pluralName: item.pluralName ?? void 0,
|
|
1391
|
-
description: item.description ?? void 0,
|
|
1392
|
-
category: item.category ?? void 0,
|
|
1393
|
-
fields: (item.fields ?? []).map(
|
|
1394
|
-
(f) => normalizeField(f)
|
|
1395
|
-
),
|
|
1396
|
-
config: normalizeConfig(item.config ?? {}),
|
|
1397
|
-
hooks: item.hooks ?? void 0
|
|
1398
|
-
}));
|
|
1399
|
-
}
|
|
1400
|
-
function filterModels(models, options) {
|
|
1401
|
-
let filtered = models;
|
|
1402
|
-
if (options.only && options.only.length > 0) {
|
|
1403
|
-
const onlySet = new Set(options.only);
|
|
1404
|
-
filtered = filtered.filter((m) => onlySet.has(m.key));
|
|
1405
|
-
}
|
|
1406
|
-
if (!options.includeInline) {
|
|
1407
|
-
filtered = filtered.filter((m) => m.config.records);
|
|
1408
|
-
}
|
|
1409
|
-
return filtered;
|
|
1410
|
-
}
|
|
1411
|
-
|
|
1412
|
-
// src/codegen/fetch-customer-profile-schema.ts
|
|
1413
|
-
async function fetchCustomerProfileSchema(client) {
|
|
1414
|
-
const data = await client.request(GetCustomerProfileSchemaDocument);
|
|
1415
|
-
if (!data.customerProfileSchema) return null;
|
|
1416
|
-
return {
|
|
1417
|
-
id: data.customerProfileSchema.id,
|
|
1418
|
-
version: data.customerProfileSchema.version,
|
|
1419
|
-
fields: (data.customerProfileSchema.fields ?? []).map((f) => ({
|
|
1420
|
-
key: f.key,
|
|
1421
|
-
type: f.type,
|
|
1422
|
-
label: f.label,
|
|
1423
|
-
required: f.required ?? void 0,
|
|
1424
|
-
helpText: f.helpText ?? void 0,
|
|
1425
|
-
options: f.config
|
|
1426
|
-
}))
|
|
1427
|
-
};
|
|
1428
|
-
}
|
|
1429
|
-
|
|
1430
|
-
// src/codegen/field-mapping.ts
|
|
1431
|
-
var PRIMITIVE_FIELD_TYPES = /* @__PURE__ */ new Set([
|
|
1432
|
-
"text",
|
|
1433
|
-
"richtext",
|
|
1434
|
-
"number",
|
|
1435
|
-
"boolean",
|
|
1436
|
-
"email",
|
|
1437
|
-
"phone",
|
|
1438
|
-
"url",
|
|
1439
|
-
"date",
|
|
1440
|
-
"image",
|
|
1441
|
-
"video",
|
|
1442
|
-
"file",
|
|
1443
|
-
"currency",
|
|
1444
|
-
"select",
|
|
1445
|
-
"multiselect",
|
|
1446
|
-
"json",
|
|
1447
|
-
"list",
|
|
1448
|
-
"reference",
|
|
1449
|
-
"link",
|
|
1450
|
-
"flexible",
|
|
1451
|
-
"model"
|
|
1452
|
-
]);
|
|
1453
|
-
function isPrimitiveFieldType(type) {
|
|
1454
|
-
return PRIMITIVE_FIELD_TYPES.has(type);
|
|
1455
|
-
}
|
|
1456
|
-
var FIELD_TYPE_MAPPING = {
|
|
1457
|
-
text: { outputType: "string", inputType: "string" },
|
|
1458
|
-
richtext: {
|
|
1459
|
-
outputType: "RichtextValue",
|
|
1460
|
-
inputType: "RichtextValue",
|
|
1461
|
-
needsImport: "field-types"
|
|
1462
|
-
},
|
|
1463
|
-
number: { outputType: "number", inputType: "number" },
|
|
1464
|
-
boolean: { outputType: "boolean", inputType: "boolean" },
|
|
1465
|
-
email: { outputType: "string", inputType: "string" },
|
|
1466
|
-
phone: { outputType: "string", inputType: "string" },
|
|
1467
|
-
url: { outputType: "string", inputType: "string" },
|
|
1468
|
-
date: { outputType: "Date", inputType: "string" },
|
|
1469
|
-
image: {
|
|
1470
|
-
outputType: "ImageValue",
|
|
1471
|
-
inputType: "string",
|
|
1472
|
-
needsImport: "field-types"
|
|
1473
|
-
},
|
|
1474
|
-
video: {
|
|
1475
|
-
outputType: "VideoValue",
|
|
1476
|
-
inputType: "string",
|
|
1477
|
-
needsImport: "field-types"
|
|
1478
|
-
},
|
|
1479
|
-
file: {
|
|
1480
|
-
outputType: "FileValue",
|
|
1481
|
-
inputType: "string",
|
|
1482
|
-
needsImport: "field-types"
|
|
1483
|
-
},
|
|
1484
|
-
currency: {
|
|
1485
|
-
outputType: "CurrencyValue",
|
|
1486
|
-
inputType: "CurrencyValue",
|
|
1487
|
-
needsImport: "field-types"
|
|
1488
|
-
},
|
|
1489
|
-
select: { outputType: "string", inputType: "string" },
|
|
1490
|
-
multiselect: { outputType: "string[]", inputType: "string[]" },
|
|
1491
|
-
json: { outputType: "unknown", inputType: "unknown" },
|
|
1492
|
-
list: { outputType: "unknown[]", inputType: "unknown[]" },
|
|
1493
|
-
flexible: {
|
|
1494
|
-
outputType: "FlexibleFieldItem[]",
|
|
1495
|
-
inputType: "FlexibleFieldItem[]",
|
|
1496
|
-
needsImport: "field-types"
|
|
1497
|
-
},
|
|
1498
|
-
reference: {
|
|
1499
|
-
outputType: "ReferenceValue",
|
|
1500
|
-
inputType: "ReferenceValue",
|
|
1501
|
-
needsImport: "field-types"
|
|
1502
|
-
},
|
|
1503
|
-
link: {
|
|
1504
|
-
outputType: "LinkValue",
|
|
1505
|
-
inputType: "LinkValue",
|
|
1506
|
-
needsImport: "field-types"
|
|
1507
|
-
},
|
|
1508
|
-
model: { outputType: "string", inputType: "string" }
|
|
1509
|
-
};
|
|
1510
|
-
function getFieldType(field, mode = "output") {
|
|
1511
|
-
if (!field?.type) return "unknown";
|
|
1512
|
-
const mapping = FIELD_TYPE_MAPPING[field.type];
|
|
1513
|
-
if (!mapping) {
|
|
1514
|
-
if (isPrimitiveFieldType(field.type)) return "unknown";
|
|
1515
|
-
return toPascalCase(field.type);
|
|
1516
|
-
}
|
|
1517
|
-
let tsType = mode === "output" ? mapping.outputType : mapping.inputType;
|
|
1518
|
-
if (field.type === "select" && field.options?.options) {
|
|
1519
|
-
const options = field.options.options;
|
|
1520
|
-
if (options.length > 0) {
|
|
1521
|
-
tsType = options.map((o) => `'${o.value}'`).join(" | ");
|
|
1522
|
-
}
|
|
1523
|
-
}
|
|
1524
|
-
if (field.type === "multiselect" && field.options?.options) {
|
|
1525
|
-
const options = field.options.options;
|
|
1526
|
-
if (options.length > 0) {
|
|
1527
|
-
tsType = `(${options.map((o) => `'${o.value}'`).join(" | ")})[]`;
|
|
1528
|
-
}
|
|
1529
|
-
}
|
|
1530
|
-
if (field.type === "list" && field.options?.itemType) {
|
|
1531
|
-
const itemType = field.options.itemType;
|
|
1532
|
-
const itemMapping = FIELD_TYPE_MAPPING[itemType];
|
|
1533
|
-
if (itemMapping) {
|
|
1534
|
-
tsType = `${mode === "output" ? itemMapping.outputType : itemMapping.inputType}[]`;
|
|
1535
|
-
} else if (!isPrimitiveFieldType(itemType)) {
|
|
1536
|
-
tsType = `${toPascalCase(itemType)}[]`;
|
|
1537
|
-
}
|
|
1538
|
-
}
|
|
1539
|
-
return tsType;
|
|
1540
|
-
}
|
|
1541
|
-
function getReferenceTypeModelRefs(fields) {
|
|
1542
|
-
const refs = /* @__PURE__ */ new Set();
|
|
1543
|
-
for (const field of fields) {
|
|
1544
|
-
if (field.type === "reference" && field.options?.referenceTypes) {
|
|
1545
|
-
for (const rt of field.options.referenceTypes) {
|
|
1546
|
-
refs.add(rt);
|
|
1547
|
-
}
|
|
1548
|
-
}
|
|
1549
|
-
}
|
|
1550
|
-
return refs;
|
|
1551
|
-
}
|
|
1552
|
-
function getInlineSchemaReferences(fields) {
|
|
1553
|
-
const refs = /* @__PURE__ */ new Set();
|
|
1554
|
-
for (const field of fields) {
|
|
1555
|
-
if (!isPrimitiveFieldType(field.type) && !FIELD_TYPE_MAPPING[field.type]) {
|
|
1556
|
-
refs.add(field.type);
|
|
1557
|
-
}
|
|
1558
|
-
if (field.type === "list" && field.options?.itemType) {
|
|
1559
|
-
const itemType = field.options.itemType;
|
|
1560
|
-
if (!isPrimitiveFieldType(itemType) && !FIELD_TYPE_MAPPING[itemType]) {
|
|
1561
|
-
refs.add(itemType);
|
|
1562
|
-
}
|
|
1563
|
-
}
|
|
1564
|
-
}
|
|
1565
|
-
return refs;
|
|
1566
|
-
}
|
|
1567
|
-
function toCamelCase(str) {
|
|
1568
|
-
if (!str) return "unknown";
|
|
1569
|
-
return str.replace(
|
|
1570
|
-
/[-_]([a-z])/g,
|
|
1571
|
-
(_, letter) => letter.toUpperCase()
|
|
1572
|
-
);
|
|
1573
|
-
}
|
|
1574
|
-
function toPascalCase(str) {
|
|
1575
|
-
if (!str) return "Unknown";
|
|
1576
|
-
const camel = toCamelCase(str);
|
|
1577
|
-
return camel.charAt(0).toUpperCase() + camel.slice(1);
|
|
1578
|
-
}
|
|
1579
|
-
function sanitizeFieldName(key) {
|
|
1580
|
-
if (!key) return "unknown";
|
|
1581
|
-
const camel = toCamelCase(key);
|
|
1582
|
-
if (/^[0-9]/.test(camel)) return `_${camel}`;
|
|
1583
|
-
return camel;
|
|
1584
|
-
}
|
|
1585
|
-
function generateFieldDef(field) {
|
|
1586
|
-
const parts = [];
|
|
1587
|
-
parts.push(`key: '${field.key ?? "unknown"}'`);
|
|
1588
|
-
parts.push(`type: '${field.type ?? "text"}'`);
|
|
1589
|
-
parts.push(
|
|
1590
|
-
`label: '${(field.label ?? field.key ?? "Unknown").replace(/'/g, "\\'")}'`
|
|
1591
|
-
);
|
|
1592
|
-
if (field.required) parts.push("required: true");
|
|
1593
|
-
if (field.helpText)
|
|
1594
|
-
parts.push(`helpText: '${field.helpText.replace(/'/g, "\\'")}'`);
|
|
1595
|
-
if (field.type === "text" && field.options) {
|
|
1596
|
-
if (field.options.maxLength)
|
|
1597
|
-
parts.push(`maxLength: ${field.options.maxLength}`);
|
|
1598
|
-
if (field.options.minLength)
|
|
1599
|
-
parts.push(`minLength: ${field.options.minLength}`);
|
|
1600
|
-
}
|
|
1601
|
-
if (field.type === "number" && field.options) {
|
|
1602
|
-
if (field.options.min !== void 0)
|
|
1603
|
-
parts.push(`min: ${field.options.min}`);
|
|
1604
|
-
if (field.options.max !== void 0)
|
|
1605
|
-
parts.push(`max: ${field.options.max}`);
|
|
1606
|
-
if (field.options.step !== void 0)
|
|
1607
|
-
parts.push(`step: ${field.options.step}`);
|
|
1608
|
-
}
|
|
1609
|
-
if ((field.type === "select" || field.type === "multiselect") && field.options?.options) {
|
|
1610
|
-
const options = field.options.options;
|
|
1611
|
-
const optionsStr = options.filter((o) => o.value !== void 0).map((o) => {
|
|
1612
|
-
const label = (o.label ?? o.value ?? "").replace(/'/g, "\\'");
|
|
1613
|
-
const value = (o.value ?? "").replace(/'/g, "\\'");
|
|
1614
|
-
return `{ label: '${label}', value: '${value}' }`;
|
|
1615
|
-
}).join(", ");
|
|
1616
|
-
parts.push(`options: [${optionsStr}]`);
|
|
1617
|
-
}
|
|
1618
|
-
if (field.type === "reference" && field.options?.referenceTypes) {
|
|
1619
|
-
const refTypes = field.options.referenceTypes;
|
|
1620
|
-
parts.push(`referenceTypes: [${refTypes.map((t) => `'${t}'`).join(", ")}]`);
|
|
1621
|
-
if (field.options.multiple) parts.push("multiple: true");
|
|
1622
|
-
}
|
|
1623
|
-
if (field.type === "list" && field.options?.itemType) {
|
|
1624
|
-
parts.push(`itemType: '${field.options.itemType}'`);
|
|
1625
|
-
if (field.options.minItems !== void 0)
|
|
1626
|
-
parts.push(`minItems: ${field.options.minItems}`);
|
|
1627
|
-
if (field.options.maxItems !== void 0)
|
|
1628
|
-
parts.push(`maxItems: ${field.options.maxItems}`);
|
|
1629
|
-
}
|
|
1630
|
-
if ((field.type === "image" || field.type === "video" || field.type === "file") && field.options) {
|
|
1631
|
-
if (field.options.allowedTypes) {
|
|
1632
|
-
const types = field.options.allowedTypes;
|
|
1633
|
-
parts.push(`allowedTypes: [${types.map((t) => `'${t}'`).join(", ")}]`);
|
|
1634
|
-
}
|
|
1635
|
-
if (field.options.maxSize) parts.push(`maxSize: ${field.options.maxSize}`);
|
|
1636
|
-
}
|
|
1637
|
-
return `{ ${parts.join(", ")} }`;
|
|
1638
|
-
}
|
|
1639
|
-
|
|
1640
|
-
// src/codegen/generators/model-types.ts
|
|
1641
|
-
function isInlineOnlyModel(model) {
|
|
1642
|
-
return model.config.inline && !model.config.records;
|
|
1643
|
-
}
|
|
1644
|
-
function generateModelTypes(model, allModels) {
|
|
1645
|
-
const typeName = toPascalCase(model.key);
|
|
1646
|
-
const configName = toCamelCase(model.key) + "Config";
|
|
1647
|
-
const fields = model.fields ?? [];
|
|
1648
|
-
const fieldTypeImports = getFieldTypeImportsForFields(fields);
|
|
1649
|
-
const inlineSchemaRefs = getInlineSchemaReferences(fields);
|
|
1650
|
-
const referenceModelRefs = getReferenceTypeModelRefs(fields);
|
|
1651
|
-
let code = buildImportStatements(
|
|
1652
|
-
model,
|
|
1653
|
-
fieldTypeImports,
|
|
1654
|
-
inlineSchemaRefs,
|
|
1655
|
-
referenceModelRefs,
|
|
1656
|
-
allModels
|
|
1657
|
-
);
|
|
1658
|
-
if (isInlineOnlyModel(model)) {
|
|
1659
|
-
code += generateDataInterface(model, fields, typeName, allModels);
|
|
1660
|
-
return code;
|
|
1661
|
-
}
|
|
1662
|
-
code += generateConfigObject(model, fields, configName);
|
|
1663
|
-
code += "\n";
|
|
1664
|
-
code += generateDataInterface(model, fields, typeName + "Data", allModels);
|
|
1665
|
-
return code;
|
|
1666
|
-
}
|
|
1667
|
-
function buildImportStatements(model, fieldTypeImports, inlineSchemaRefs, referenceModelRefs, allModels) {
|
|
1668
|
-
const imports = [];
|
|
1669
|
-
if (fieldTypeImports.size > 0) {
|
|
1670
|
-
const types = Array.from(fieldTypeImports).sort().join(", ");
|
|
1671
|
-
imports.push(`import type { ${types} } from '@eide/foir-client';`);
|
|
1672
|
-
}
|
|
1673
|
-
const allModelRefKeys = /* @__PURE__ */ new Set([
|
|
1674
|
-
...inlineSchemaRefs,
|
|
1675
|
-
...referenceModelRefs
|
|
1676
|
-
]);
|
|
1677
|
-
for (const refKey of allModelRefKeys) {
|
|
1678
|
-
if (refKey === model.key) continue;
|
|
1679
|
-
const refModel = allModels.find((m) => m.key === refKey);
|
|
1680
|
-
if (refModel) {
|
|
1681
|
-
const refTypeName = isInlineOnlyModel(refModel) ? toPascalCase(refKey) : toPascalCase(refKey) + "Data";
|
|
1682
|
-
imports.push(`import type { ${refTypeName} } from './${refKey}.js';`);
|
|
1683
|
-
}
|
|
1684
|
-
}
|
|
1685
|
-
return imports.length > 0 ? imports.join("\n") + "\n\n" : "";
|
|
1686
|
-
}
|
|
1687
|
-
function generateConfigObject(model, fields, configName) {
|
|
1688
|
-
const lines = [];
|
|
1689
|
-
lines.push("/**");
|
|
1690
|
-
lines.push(` * ${model.name} Configuration`);
|
|
1691
|
-
if (model.description) lines.push(` * ${model.description}`);
|
|
1692
|
-
lines.push(` *`);
|
|
1693
|
-
lines.push(` * @generated from model '${model.key}'`);
|
|
1694
|
-
lines.push(" */");
|
|
1695
|
-
const escapedName = (model.name ?? model.key).replace(/'/g, "\\'");
|
|
1696
|
-
lines.push(`export const ${configName} = {`);
|
|
1697
|
-
lines.push(` key: '${model.key}',`);
|
|
1698
|
-
lines.push(` name: '${escapedName}',`);
|
|
1699
|
-
if (model.description) {
|
|
1700
|
-
lines.push(` description: '${model.description.replace(/'/g, "\\'")}',`);
|
|
1701
|
-
}
|
|
1702
|
-
lines.push("");
|
|
1703
|
-
lines.push(" // Capability flags");
|
|
1704
|
-
lines.push(` records: ${model.config.records},`);
|
|
1705
|
-
lines.push(` inline: ${model.config.inline},`);
|
|
1706
|
-
lines.push(` publicApi: ${model.config.publicApi},`);
|
|
1707
|
-
lines.push(` versioning: ${model.config.versioning},`);
|
|
1708
|
-
lines.push(` publishing: ${model.config.publishing},`);
|
|
1709
|
-
lines.push(` variants: ${model.config.variants},`);
|
|
1710
|
-
lines.push(` customerScoped: ${model.config.customerScoped},`);
|
|
1711
|
-
if (model.config.embeddings?.enabled) {
|
|
1712
|
-
lines.push("");
|
|
1713
|
-
lines.push(" // Embeddings");
|
|
1714
|
-
lines.push(
|
|
1715
|
-
` embeddings: ${JSON.stringify(model.config.embeddings, null, 4).replace(/\n/g, "\n ")},`
|
|
1716
|
-
);
|
|
1717
|
-
}
|
|
1718
|
-
if (model.hooks && Object.keys(model.hooks).length > 0) {
|
|
1719
|
-
lines.push("");
|
|
1720
|
-
lines.push(" // Lifecycle hooks");
|
|
1721
|
-
lines.push(
|
|
1722
|
-
` hooks: ${JSON.stringify(model.hooks, null, 4).replace(/\n/g, "\n ")},`
|
|
1723
|
-
);
|
|
1724
|
-
} else {
|
|
1725
|
-
lines.push("");
|
|
1726
|
-
lines.push(" hooks: {},");
|
|
1727
|
-
}
|
|
1728
|
-
lines.push("");
|
|
1729
|
-
lines.push(" // Field definitions");
|
|
1730
|
-
if (fields.length === 0) {
|
|
1731
|
-
lines.push(" fieldDefs: [],");
|
|
1732
|
-
} else {
|
|
1733
|
-
lines.push(" fieldDefs: [");
|
|
1734
|
-
for (const field of fields) {
|
|
1735
|
-
lines.push(` ${generateFieldDef(field)},`);
|
|
1736
|
-
}
|
|
1737
|
-
lines.push(" ],");
|
|
1738
|
-
}
|
|
1739
|
-
lines.push("} as const;");
|
|
1740
|
-
return lines.join("\n") + "\n";
|
|
1741
|
-
}
|
|
1742
|
-
function generateDataInterface(model, fields, interfaceName, allModels) {
|
|
1743
|
-
const lines = [];
|
|
1744
|
-
lines.push("/**");
|
|
1745
|
-
lines.push(` * ${model.name} Data`);
|
|
1746
|
-
lines.push(` * Field values only \u2014 no system fields`);
|
|
1747
|
-
lines.push(` *`);
|
|
1748
|
-
lines.push(` * @generated from model '${model.key}'`);
|
|
1749
|
-
lines.push(" */");
|
|
1750
|
-
lines.push(`export interface ${interfaceName} {`);
|
|
1751
|
-
for (const field of fields) {
|
|
1752
|
-
const fieldName = sanitizeFieldName(field.key);
|
|
1753
|
-
let fieldType = getFieldType(field, "output");
|
|
1754
|
-
const refModel = allModels.find((m) => m.key === field.type);
|
|
1755
|
-
if (refModel && !isInlineOnlyModel(refModel)) {
|
|
1756
|
-
fieldType = toPascalCase(field.type) + "Data";
|
|
1757
|
-
}
|
|
1758
|
-
if (field.type === "list" && field.options?.itemType) {
|
|
1759
|
-
const itemRefModel = allModels.find(
|
|
1760
|
-
(m) => m.key === field.options.itemType
|
|
1761
|
-
);
|
|
1762
|
-
if (itemRefModel && !isInlineOnlyModel(itemRefModel)) {
|
|
1763
|
-
fieldType = toPascalCase(field.options.itemType) + "Data[]";
|
|
1764
|
-
}
|
|
1765
|
-
}
|
|
1766
|
-
if (field.type === "reference" && field.options?.referenceTypes) {
|
|
1767
|
-
const refTypes = field.options.referenceTypes;
|
|
1768
|
-
const resolvedPreviewTypes = [];
|
|
1769
|
-
for (const refKey of refTypes) {
|
|
1770
|
-
const targetModel = allModels.find((m) => m.key === refKey);
|
|
1771
|
-
if (targetModel) {
|
|
1772
|
-
const targetTypeName = isInlineOnlyModel(targetModel) ? toPascalCase(refKey) : toPascalCase(refKey) + "Data";
|
|
1773
|
-
resolvedPreviewTypes.push(`Partial<${targetTypeName}>`);
|
|
1774
|
-
}
|
|
1775
|
-
}
|
|
1776
|
-
if (resolvedPreviewTypes.length === refTypes.length && resolvedPreviewTypes.length > 0) {
|
|
1777
|
-
fieldType = `ReferenceValue<${resolvedPreviewTypes.join(" | ")}>`;
|
|
1778
|
-
}
|
|
1779
|
-
}
|
|
1780
|
-
const optional = field.required ? "" : "?";
|
|
1781
|
-
const comment = field.helpText ? ` /** ${field.helpText} */
|
|
1782
|
-
` : "";
|
|
1783
|
-
lines.push(comment + ` ${fieldName}${optional}: ${fieldType};`);
|
|
1784
|
-
}
|
|
1785
|
-
lines.push("}");
|
|
1786
|
-
return lines.join("\n") + "\n";
|
|
1787
|
-
}
|
|
1788
|
-
function getFieldTypeImportsForFields(fields) {
|
|
1789
|
-
const imports = /* @__PURE__ */ new Set();
|
|
1790
|
-
for (const field of fields) {
|
|
1791
|
-
const mapping = FIELD_TYPE_MAPPING[field.type];
|
|
1792
|
-
if (mapping?.needsImport === "field-types") {
|
|
1793
|
-
imports.add(mapping.outputType.replace(/\[\]$/, ""));
|
|
1794
|
-
}
|
|
1795
|
-
if (field.type === "list" && field.options?.itemType) {
|
|
1796
|
-
const itemMapping = FIELD_TYPE_MAPPING[field.options.itemType];
|
|
1797
|
-
if (itemMapping?.needsImport === "field-types") {
|
|
1798
|
-
imports.add(itemMapping.outputType.replace(/\[\]$/, ""));
|
|
1799
|
-
}
|
|
1800
|
-
}
|
|
1801
|
-
}
|
|
1802
|
-
return imports;
|
|
1803
|
-
}
|
|
1804
|
-
|
|
1805
|
-
// src/codegen/generators/model-index.ts
|
|
1806
|
-
function isInlineOnlyModel2(model) {
|
|
1807
|
-
return model.config.inline && !model.config.records;
|
|
1808
|
-
}
|
|
1809
|
-
function generateModelIndex(models) {
|
|
1810
|
-
let code = `/**
|
|
1811
|
-
* Model Types and Configs \u2014 Generated re-exports
|
|
1812
|
-
*
|
|
1813
|
-
* @generated by foir \u2014 DO NOT EDIT MANUALLY
|
|
1814
|
-
*/
|
|
1815
|
-
|
|
1816
|
-
`;
|
|
1817
|
-
for (const model of models) {
|
|
1818
|
-
const typeName = toPascalCase(model.key);
|
|
1819
|
-
const configName = toCamelCase(model.key) + "Config";
|
|
1820
|
-
if (isInlineOnlyModel2(model)) {
|
|
1821
|
-
code += `export type { ${typeName} } from './${model.key}.js';
|
|
1822
|
-
`;
|
|
1823
|
-
} else {
|
|
1824
|
-
code += `export { ${configName} } from './${model.key}.js';
|
|
1825
|
-
`;
|
|
1826
|
-
code += `export type { ${typeName}Data } from './${model.key}.js';
|
|
1827
|
-
`;
|
|
1828
|
-
if (model.config.publicApi) {
|
|
1829
|
-
code += `export type { ${typeName}Where, ${typeName}SortField } from './${model.key}.filters.js';
|
|
1830
|
-
`;
|
|
1831
|
-
}
|
|
1832
|
-
}
|
|
1833
|
-
}
|
|
1834
|
-
return code;
|
|
1835
|
-
}
|
|
1836
|
-
|
|
1837
|
-
// src/codegen/generators/client-factory.ts
|
|
1838
|
-
function generateClientFactory(models, hasCustomerProfile) {
|
|
1839
|
-
const publicModels = models.filter(
|
|
1840
|
-
(m) => m.config.publicApi && m.config.records
|
|
1841
|
-
);
|
|
1842
|
-
const lines = [];
|
|
1843
|
-
lines.push(`/**
|
|
1844
|
-
* Typed Foir client for this project.
|
|
1845
|
-
*
|
|
1846
|
-
* @generated by foir \u2014 DO NOT EDIT MANUALLY
|
|
1847
|
-
*/
|
|
1848
|
-
|
|
1849
|
-
import {
|
|
1850
|
-
createBaseClient,
|
|
1851
|
-
createModelAccessor,
|
|
1852
|
-
createAuthClient,
|
|
1853
|
-
createFilesClient,
|
|
1854
|
-
createNotificationsClient,
|
|
1855
|
-
createSharingClient,
|
|
1856
|
-
createSearchClient,
|
|
1857
|
-
createProfileClient,
|
|
1858
|
-
createOperationsClient,
|
|
1859
|
-
type ClientConfig,
|
|
1860
|
-
type ModelAccessor,
|
|
1861
|
-
type FoirClient,
|
|
1862
|
-
} from '@eide/foir-client';
|
|
1863
|
-
`);
|
|
1864
|
-
for (const model of publicModels) {
|
|
1865
|
-
const typeName = toPascalCase(model.key);
|
|
1866
|
-
const dataType = `${typeName}Data`;
|
|
1867
|
-
const whereType = `${typeName}Where`;
|
|
1868
|
-
const sortType = `${typeName}SortField`;
|
|
1869
|
-
lines.push(
|
|
1870
|
-
`import type { ${dataType}, ${whereType}, ${sortType} } from './models/${model.key}.js';`
|
|
1871
|
-
);
|
|
1872
|
-
}
|
|
1873
|
-
if (hasCustomerProfile) {
|
|
1874
|
-
lines.push(
|
|
1875
|
-
`import type { CustomerProfileData } from './models/customer-profile.js';`
|
|
1876
|
-
);
|
|
1877
|
-
}
|
|
1878
|
-
lines.push("");
|
|
1879
|
-
lines.push("export interface TypedClient extends FoirClient {");
|
|
1880
|
-
for (const model of publicModels) {
|
|
1881
|
-
const typeName = toPascalCase(model.key);
|
|
1882
|
-
const accessorName = toCamelCase(model.key);
|
|
1883
|
-
const dataType = `${typeName}Data`;
|
|
1884
|
-
const whereType = `${typeName}Where`;
|
|
1885
|
-
const sortType = `${typeName}SortField`;
|
|
1886
|
-
lines.push(` ${accessorName}: ModelAccessor<${dataType}, ${whereType}, ${sortType}>;`);
|
|
1887
|
-
}
|
|
1888
|
-
if (hasCustomerProfile) {
|
|
1889
|
-
lines.push(
|
|
1890
|
-
` profile: ReturnType<typeof createProfileClient<CustomerProfileData>>;`
|
|
1891
|
-
);
|
|
1892
|
-
}
|
|
1893
|
-
lines.push("}");
|
|
1894
|
-
lines.push("");
|
|
1895
|
-
lines.push("export function createClient(config: ClientConfig): TypedClient {");
|
|
1896
|
-
lines.push(" const base = createBaseClient(config);");
|
|
1897
|
-
lines.push("");
|
|
1898
|
-
lines.push(" return {");
|
|
1899
|
-
lines.push(" auth: createAuthClient(base),");
|
|
1900
|
-
lines.push(" files: createFilesClient(base),");
|
|
1901
|
-
lines.push(" notifications: createNotificationsClient(base),");
|
|
1902
|
-
lines.push(" sharing: createSharingClient(base),");
|
|
1903
|
-
lines.push(" search: createSearchClient(base),");
|
|
1904
|
-
if (hasCustomerProfile) {
|
|
1905
|
-
lines.push(" profile: createProfileClient<CustomerProfileData>(base),");
|
|
1906
|
-
} else {
|
|
1907
|
-
lines.push(" profile: createProfileClient(base),");
|
|
1908
|
-
}
|
|
1909
|
-
lines.push(" operations: createOperationsClient(base),");
|
|
1910
|
-
for (const model of publicModels) {
|
|
1911
|
-
const typeName = toPascalCase(model.key);
|
|
1912
|
-
const accessorName = toCamelCase(model.key);
|
|
1913
|
-
const dataType = `${typeName}Data`;
|
|
1914
|
-
const whereType = `${typeName}Where`;
|
|
1915
|
-
const sortType = `${typeName}SortField`;
|
|
1916
|
-
lines.push(
|
|
1917
|
-
` ${accessorName}: createModelAccessor<${dataType}, ${whereType}, ${sortType}>(base, '${model.key}'),`
|
|
1918
|
-
);
|
|
1919
|
-
}
|
|
1920
|
-
lines.push(` model<TData = unknown>(modelKey: string) {`);
|
|
1921
|
-
lines.push(` return createModelAccessor<TData>(base, modelKey);`);
|
|
1922
|
-
lines.push(` },`);
|
|
1923
|
-
lines.push(" request: base.request,");
|
|
1924
|
-
lines.push(" setCustomerToken: base.setCustomerToken,");
|
|
1925
|
-
lines.push(" setLocale: base.setLocale,");
|
|
1926
|
-
lines.push(" setPreview: base.setPreview,");
|
|
1927
|
-
lines.push(" } as TypedClient;");
|
|
1928
|
-
lines.push("}");
|
|
1929
|
-
lines.push("");
|
|
1930
|
-
lines.push("// Re-export types from @eide/foir-client for convenience");
|
|
1931
|
-
lines.push(`export type {
|
|
1932
|
-
ClientConfig,
|
|
1933
|
-
TypedRecord,
|
|
1934
|
-
TypedList,
|
|
1935
|
-
ModelAccessor,
|
|
1936
|
-
JsonValue,
|
|
1937
|
-
ImageValue,
|
|
1938
|
-
VideoValue,
|
|
1939
|
-
FileValue,
|
|
1940
|
-
LinkValue,
|
|
1941
|
-
ReferenceValue,
|
|
1942
|
-
FlexibleFieldItem,
|
|
1943
|
-
RichtextValue,
|
|
1944
|
-
CurrencyValue,
|
|
1945
|
-
SelectMap,
|
|
1946
|
-
ApplySelect,
|
|
1947
|
-
FoirClient,
|
|
1948
|
-
} from '@eide/foir-client';`);
|
|
1949
|
-
lines.push("");
|
|
1950
|
-
for (const model of publicModels) {
|
|
1951
|
-
const typeName = toPascalCase(model.key);
|
|
1952
|
-
const dataType = `${typeName}Data`;
|
|
1953
|
-
const whereType = `${typeName}Where`;
|
|
1954
|
-
const sortType = `${typeName}SortField`;
|
|
1955
|
-
lines.push(
|
|
1956
|
-
`export type { ${dataType}, ${whereType}, ${sortType} } from './models/${model.key}.js';`
|
|
1957
|
-
);
|
|
1958
|
-
}
|
|
1959
|
-
if (hasCustomerProfile) {
|
|
1960
|
-
lines.push(
|
|
1961
|
-
`export type { CustomerProfileData } from './models/customer-profile.js';`
|
|
1962
|
-
);
|
|
1963
|
-
}
|
|
1964
|
-
return lines.join("\n") + "\n";
|
|
1965
|
-
}
|
|
1966
|
-
|
|
1967
|
-
// src/codegen/generators/schema-manifest.ts
|
|
1968
|
-
function generateSchemaManifest(models, cpSchema) {
|
|
1969
|
-
const manifest = {
|
|
1970
|
-
$schema: "https://foir.io/schema/v1.json",
|
|
1971
|
-
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1972
|
-
models: {}
|
|
1973
|
-
};
|
|
1974
|
-
for (const model of models) {
|
|
1975
|
-
manifest.models[model.key] = {
|
|
1976
|
-
name: model.name,
|
|
1977
|
-
pluralName: model.pluralName,
|
|
1978
|
-
description: model.description,
|
|
1979
|
-
category: model.category,
|
|
1980
|
-
config: model.config,
|
|
1981
|
-
fields: model.fields.map((f) => ({
|
|
1982
|
-
key: f.key,
|
|
1983
|
-
type: f.type,
|
|
1984
|
-
label: f.label,
|
|
1985
|
-
required: f.required,
|
|
1986
|
-
helpText: f.helpText,
|
|
1987
|
-
options: f.options
|
|
1988
|
-
}))
|
|
1989
|
-
};
|
|
1990
|
-
}
|
|
1991
|
-
if (cpSchema && cpSchema.fields.length > 0) {
|
|
1992
|
-
manifest.customerProfile = {
|
|
1993
|
-
fields: cpSchema.fields.map((f) => ({
|
|
1994
|
-
key: f.key,
|
|
1995
|
-
type: f.type,
|
|
1996
|
-
label: f.label,
|
|
1997
|
-
required: f.required,
|
|
1998
|
-
helpText: f.helpText,
|
|
1999
|
-
options: f.options
|
|
2000
|
-
}))
|
|
2001
|
-
};
|
|
2002
|
-
}
|
|
2003
|
-
return JSON.stringify(manifest, null, 2) + "\n";
|
|
2004
|
-
}
|
|
2005
|
-
|
|
2006
|
-
// src/codegen/generators/model-filters.ts
|
|
2007
|
-
function isInlineOnlyModel3(model) {
|
|
2008
|
-
return model.config.inline && !model.config.records;
|
|
2009
|
-
}
|
|
2010
|
-
function getFilterType(fieldType) {
|
|
2011
|
-
switch (fieldType) {
|
|
2012
|
-
case "text":
|
|
2013
|
-
case "richtext":
|
|
2014
|
-
case "email":
|
|
2015
|
-
case "phone":
|
|
2016
|
-
case "url":
|
|
2017
|
-
case "select":
|
|
2018
|
-
return "StringFilter";
|
|
2019
|
-
case "number":
|
|
2020
|
-
case "currency":
|
|
2021
|
-
return "NumberFilter";
|
|
2022
|
-
case "boolean":
|
|
2023
|
-
return "BooleanFilter";
|
|
2024
|
-
case "date":
|
|
2025
|
-
return "DateFilter";
|
|
2026
|
-
default:
|
|
2027
|
-
return null;
|
|
2028
|
-
}
|
|
2029
|
-
}
|
|
2030
|
-
function isSortable(fieldType) {
|
|
2031
|
-
return ["text", "number", "date", "boolean", "email", "url", "select"].includes(fieldType);
|
|
2032
|
-
}
|
|
2033
|
-
function generateModelWhere(model) {
|
|
2034
|
-
if (isInlineOnlyModel3(model)) return "";
|
|
2035
|
-
const typeName = toPascalCase(model.key);
|
|
2036
|
-
const whereName = `${typeName}Where`;
|
|
2037
|
-
const fields = model.fields ?? [];
|
|
2038
|
-
const filterImports = /* @__PURE__ */ new Set();
|
|
2039
|
-
const filterFields = [];
|
|
2040
|
-
for (const field of fields) {
|
|
2041
|
-
const filterType = getFilterType(field.type);
|
|
2042
|
-
if (filterType) {
|
|
2043
|
-
filterImports.add(filterType);
|
|
2044
|
-
const fieldName = sanitizeFieldName(field.key);
|
|
2045
|
-
filterFields.push(` ${fieldName}?: ${filterType};`);
|
|
2046
|
-
}
|
|
2047
|
-
}
|
|
2048
|
-
filterImports.add("DateFilter");
|
|
2049
|
-
filterFields.push(" createdAt?: DateFilter;");
|
|
2050
|
-
filterFields.push(" updatedAt?: DateFilter;");
|
|
2051
|
-
const imports = Array.from(filterImports).sort().join(", ");
|
|
2052
|
-
const lines = [];
|
|
2053
|
-
lines.push(`import type { ${imports} } from '@eide/foir-client';`);
|
|
2054
|
-
lines.push("");
|
|
2055
|
-
lines.push(`export interface ${whereName} {`);
|
|
2056
|
-
lines.push(...filterFields);
|
|
2057
|
-
lines.push(` AND?: ${whereName}[];`);
|
|
2058
|
-
lines.push(` OR?: ${whereName}[];`);
|
|
2059
|
-
lines.push(` NOT?: ${whereName};`);
|
|
2060
|
-
lines.push("}");
|
|
2061
|
-
return lines.join("\n") + "\n";
|
|
2062
|
-
}
|
|
2063
|
-
function generateModelSortField(model) {
|
|
2064
|
-
if (isInlineOnlyModel3(model)) return "";
|
|
2065
|
-
const typeName = toPascalCase(model.key);
|
|
2066
|
-
const sortName = `${typeName}SortField`;
|
|
2067
|
-
const fields = model.fields ?? [];
|
|
2068
|
-
const sortableFields = [];
|
|
2069
|
-
for (const field of fields) {
|
|
2070
|
-
if (isSortable(field.type)) {
|
|
2071
|
-
sortableFields.push(`'${field.key}'`);
|
|
2072
|
-
}
|
|
2073
|
-
}
|
|
2074
|
-
sortableFields.push(`'createdAt'`);
|
|
2075
|
-
sortableFields.push(`'updatedAt'`);
|
|
2076
|
-
const unique = [...new Set(sortableFields)];
|
|
2077
|
-
return `export type ${sortName} = ${unique.join(" | ")};
|
|
2078
|
-
`;
|
|
2079
|
-
}
|
|
2080
|
-
|
|
2081
|
-
// src/codegen/generators/model-zod.ts
|
|
2082
|
-
function isInlineOnlyModel4(model) {
|
|
2083
|
-
return model.config.inline && !model.config.records;
|
|
2084
|
-
}
|
|
2085
|
-
function getZodExpression(field, allModels) {
|
|
2086
|
-
const imports = /* @__PURE__ */ new Set();
|
|
2087
|
-
let expr;
|
|
2088
|
-
switch (field.type) {
|
|
2089
|
-
case "text":
|
|
2090
|
-
case "email":
|
|
2091
|
-
case "phone":
|
|
2092
|
-
case "url": {
|
|
2093
|
-
imports.add("textField");
|
|
2094
|
-
const opts = [];
|
|
2095
|
-
if (field.options?.maxLength) opts.push(`maxLength: ${field.options.maxLength}`);
|
|
2096
|
-
if (field.options?.minLength) opts.push(`minLength: ${field.options.minLength}`);
|
|
2097
|
-
if (field.options?.pattern) opts.push(`pattern: '${field.options.pattern}'`);
|
|
2098
|
-
expr = opts.length > 0 ? `textField({ ${opts.join(", ")} })` : `textField()`;
|
|
2099
|
-
break;
|
|
2100
|
-
}
|
|
2101
|
-
case "number": {
|
|
2102
|
-
imports.add("numberField");
|
|
2103
|
-
const opts = [];
|
|
2104
|
-
if (field.options?.min !== void 0) opts.push(`min: ${field.options.min}`);
|
|
2105
|
-
if (field.options?.max !== void 0) opts.push(`max: ${field.options.max}`);
|
|
2106
|
-
expr = opts.length > 0 ? `numberField({ ${opts.join(", ")} })` : `numberField()`;
|
|
2107
|
-
break;
|
|
2108
|
-
}
|
|
2109
|
-
case "boolean":
|
|
2110
|
-
imports.add("booleanField");
|
|
2111
|
-
expr = "booleanField()";
|
|
2112
|
-
break;
|
|
2113
|
-
case "date":
|
|
2114
|
-
imports.add("dateField");
|
|
2115
|
-
expr = "dateField()";
|
|
2116
|
-
break;
|
|
2117
|
-
case "richtext":
|
|
2118
|
-
imports.add("richtextValueSchema");
|
|
2119
|
-
expr = "richtextValueSchema";
|
|
2120
|
-
break;
|
|
2121
|
-
case "image":
|
|
2122
|
-
imports.add("imageValueSchema");
|
|
2123
|
-
expr = "imageValueSchema";
|
|
2124
|
-
break;
|
|
2125
|
-
case "video":
|
|
2126
|
-
imports.add("videoValueSchema");
|
|
2127
|
-
expr = "videoValueSchema";
|
|
2128
|
-
break;
|
|
2129
|
-
case "file":
|
|
2130
|
-
imports.add("fileValueSchema");
|
|
2131
|
-
expr = "fileValueSchema";
|
|
2132
|
-
break;
|
|
2133
|
-
case "currency":
|
|
2134
|
-
imports.add("currencyValueSchema");
|
|
2135
|
-
expr = "currencyValueSchema";
|
|
2136
|
-
break;
|
|
2137
|
-
case "link":
|
|
2138
|
-
imports.add("linkValueSchema");
|
|
2139
|
-
expr = "linkValueSchema";
|
|
2140
|
-
break;
|
|
2141
|
-
case "reference":
|
|
2142
|
-
imports.add("referenceValueSchema");
|
|
2143
|
-
expr = "referenceValueSchema";
|
|
2144
|
-
break;
|
|
2145
|
-
case "select": {
|
|
2146
|
-
if (field.options?.options) {
|
|
2147
|
-
imports.add("selectField");
|
|
2148
|
-
const options = field.options.options;
|
|
2149
|
-
const values = options.map((o) => `'${o.value}'`).join(", ");
|
|
2150
|
-
expr = `selectField([${values}])`;
|
|
2151
|
-
} else {
|
|
2152
|
-
expr = "z.string()";
|
|
2153
|
-
}
|
|
2154
|
-
break;
|
|
2155
|
-
}
|
|
2156
|
-
case "multiselect": {
|
|
2157
|
-
if (field.options?.options) {
|
|
2158
|
-
imports.add("multiselectField");
|
|
2159
|
-
const options = field.options.options;
|
|
2160
|
-
const values = options.map((o) => `'${o.value}'`).join(", ");
|
|
2161
|
-
expr = `multiselectField([${values}])`;
|
|
2162
|
-
} else {
|
|
2163
|
-
expr = "z.array(z.string())";
|
|
2164
|
-
}
|
|
2165
|
-
break;
|
|
2166
|
-
}
|
|
2167
|
-
case "flexible":
|
|
2168
|
-
imports.add("flexibleFieldItemSchema");
|
|
2169
|
-
expr = "z.array(flexibleFieldItemSchema)";
|
|
2170
|
-
break;
|
|
2171
|
-
case "json":
|
|
2172
|
-
imports.add("jsonValueSchema");
|
|
2173
|
-
expr = "jsonValueSchema";
|
|
2174
|
-
break;
|
|
2175
|
-
case "list": {
|
|
2176
|
-
if (field.options?.itemType) {
|
|
2177
|
-
const itemType = field.options.itemType;
|
|
2178
|
-
const refModel = allModels.find((m) => m.key === itemType);
|
|
2179
|
-
if (refModel) {
|
|
2180
|
-
const refSchemaName = `${toPascalCase(itemType)}${isInlineOnlyModel4(refModel) ? "" : "Data"}Schema`;
|
|
2181
|
-
expr = `z.array(${refSchemaName})`;
|
|
2182
|
-
} else {
|
|
2183
|
-
imports.add("jsonValueSchema");
|
|
2184
|
-
expr = "z.array(jsonValueSchema)";
|
|
2185
|
-
}
|
|
2186
|
-
} else {
|
|
2187
|
-
imports.add("jsonValueSchema");
|
|
2188
|
-
expr = "z.array(jsonValueSchema)";
|
|
2189
|
-
}
|
|
2190
|
-
break;
|
|
2191
|
-
}
|
|
2192
|
-
default: {
|
|
2193
|
-
const refModel = allModels.find((m) => m.key === field.type);
|
|
2194
|
-
if (refModel) {
|
|
2195
|
-
const refSchemaName = `${toPascalCase(field.type)}${isInlineOnlyModel4(refModel) ? "" : "Data"}Schema`;
|
|
2196
|
-
expr = refSchemaName;
|
|
2197
|
-
} else {
|
|
2198
|
-
imports.add("jsonValueSchema");
|
|
2199
|
-
expr = "jsonValueSchema";
|
|
2200
|
-
}
|
|
2201
|
-
}
|
|
2202
|
-
}
|
|
2203
|
-
if (!field.required) {
|
|
2204
|
-
expr = `${expr}.optional()`;
|
|
2205
|
-
}
|
|
2206
|
-
return { expression: expr, imports };
|
|
2207
|
-
}
|
|
2208
|
-
function generateModelZodSchema(model, allModels) {
|
|
2209
|
-
const typeName = toPascalCase(model.key);
|
|
2210
|
-
const schemaName = isInlineOnlyModel4(model) ? `${typeName}Schema` : `${typeName}DataSchema`;
|
|
2211
|
-
const fields = model.fields ?? [];
|
|
2212
|
-
if (fields.length === 0) {
|
|
2213
|
-
return `import { z } from '@eide/foir-client/validation';
|
|
2214
|
-
|
|
2215
|
-
export const ${schemaName} = z.object({});
|
|
2216
|
-
`;
|
|
2217
|
-
}
|
|
2218
|
-
const allImports = /* @__PURE__ */ new Set();
|
|
2219
|
-
const fieldLines = [];
|
|
2220
|
-
const inlineSchemaImports = [];
|
|
2221
|
-
for (const field of fields) {
|
|
2222
|
-
const { expression, imports } = getZodExpression(field, allModels);
|
|
2223
|
-
for (const imp of imports) allImports.add(imp);
|
|
2224
|
-
const fieldName = sanitizeFieldName(field.key);
|
|
2225
|
-
fieldLines.push(` ${fieldName}: ${expression},`);
|
|
2226
|
-
if (!isPrimitiveFieldType(field.type) && field.type !== "list") {
|
|
2227
|
-
const refModel = allModels.find((m) => m.key === field.type);
|
|
2228
|
-
if (refModel && refModel.key !== model.key) {
|
|
2229
|
-
const refSchemaName = `${toPascalCase(field.type)}${isInlineOnlyModel4(refModel) ? "" : "Data"}Schema`;
|
|
2230
|
-
inlineSchemaImports.push(
|
|
2231
|
-
`import { ${refSchemaName} } from './${field.type}.zod.js';`
|
|
2232
|
-
);
|
|
2233
|
-
}
|
|
2234
|
-
}
|
|
2235
|
-
if (field.type === "list" && field.options?.itemType) {
|
|
2236
|
-
const itemType = field.options.itemType;
|
|
2237
|
-
const refModel = allModels.find((m) => m.key === itemType);
|
|
2238
|
-
if (refModel && refModel.key !== model.key) {
|
|
2239
|
-
const refSchemaName = `${toPascalCase(itemType)}${isInlineOnlyModel4(refModel) ? "" : "Data"}Schema`;
|
|
2240
|
-
inlineSchemaImports.push(
|
|
2241
|
-
`import { ${refSchemaName} } from './${itemType}.zod.js';`
|
|
2242
|
-
);
|
|
2243
|
-
}
|
|
2244
|
-
}
|
|
2245
|
-
}
|
|
2246
|
-
const lines = [];
|
|
2247
|
-
lines.push(`/**`);
|
|
2248
|
-
lines.push(` * Zod validation schema for ${model.name}`);
|
|
2249
|
-
lines.push(` *`);
|
|
2250
|
-
lines.push(` * @generated by foir \u2014 DO NOT EDIT MANUALLY`);
|
|
2251
|
-
lines.push(` */`);
|
|
2252
|
-
lines.push("");
|
|
2253
|
-
lines.push(`import { z } from '@eide/foir-client/validation';`);
|
|
2254
|
-
if (allImports.size > 0) {
|
|
2255
|
-
const sorted = Array.from(allImports).sort();
|
|
2256
|
-
lines.push(`import { ${sorted.join(", ")} } from '@eide/foir-client/validation';`);
|
|
2257
|
-
}
|
|
2258
|
-
const uniqueInlineImports = [...new Set(inlineSchemaImports)];
|
|
2259
|
-
for (const imp of uniqueInlineImports) {
|
|
2260
|
-
lines.push(imp);
|
|
2261
|
-
}
|
|
2262
|
-
lines.push("");
|
|
2263
|
-
lines.push(`export const ${schemaName} = z.object({`);
|
|
2264
|
-
lines.push(...fieldLines);
|
|
2265
|
-
lines.push("});");
|
|
2266
|
-
lines.push("");
|
|
2267
|
-
lines.push(`export type ${isInlineOnlyModel4(model) ? typeName : typeName + "Data"}Validated = z.infer<typeof ${schemaName}>;`);
|
|
2268
|
-
return lines.join("\n") + "\n";
|
|
2269
|
-
}
|
|
2270
|
-
|
|
2271
|
-
// src/codegen/generators/customer-profile-types.ts
|
|
2272
|
-
function generateCustomerProfileTypes(schema) {
|
|
2273
|
-
const fields = schema.fields ?? [];
|
|
2274
|
-
const fieldTypeImports = getFieldTypeImportsForFields2(fields);
|
|
2275
|
-
let code = `/**
|
|
2276
|
-
* Customer Profile Types
|
|
2277
|
-
*
|
|
2278
|
-
* @generated by foir from customer profile schema (version ${schema.version}) \u2014 DO NOT EDIT MANUALLY
|
|
2279
|
-
*/
|
|
2280
|
-
|
|
2281
|
-
`;
|
|
2282
|
-
if (fieldTypeImports.size > 0) {
|
|
2283
|
-
const types = Array.from(fieldTypeImports).sort().join(", ");
|
|
2284
|
-
code += `import type { ${types} } from '../field-types.js';
|
|
2285
|
-
|
|
2286
|
-
`;
|
|
2287
|
-
}
|
|
2288
|
-
code += `/**
|
|
2289
|
-
* Customer Profile Data
|
|
2290
|
-
* Typed field values for customer profiles
|
|
2291
|
-
*
|
|
2292
|
-
* @generated from customer profile schema (version ${schema.version})
|
|
2293
|
-
*/
|
|
2294
|
-
`;
|
|
2295
|
-
code += `export interface CustomerProfileData {
|
|
2296
|
-
`;
|
|
2297
|
-
for (const field of fields) {
|
|
2298
|
-
const fieldName = sanitizeFieldName(field.key);
|
|
2299
|
-
const fieldType = getFieldType(field, "output");
|
|
2300
|
-
const optional = field.required ? "" : "?";
|
|
2301
|
-
if (field.helpText) {
|
|
2302
|
-
code += ` /** ${field.helpText} */
|
|
2303
|
-
`;
|
|
2304
|
-
}
|
|
2305
|
-
code += ` ${fieldName}${optional}: ${fieldType};
|
|
2306
|
-
`;
|
|
2307
|
-
}
|
|
2308
|
-
code += `}
|
|
2309
|
-
`;
|
|
2310
|
-
return code;
|
|
2311
|
-
}
|
|
2312
|
-
function getFieldTypeImportsForFields2(fields) {
|
|
2313
|
-
const imports = /* @__PURE__ */ new Set();
|
|
2314
|
-
for (const field of fields) {
|
|
2315
|
-
const mapping = FIELD_TYPE_MAPPING[field.type];
|
|
2316
|
-
if (mapping?.needsImport === "field-types") {
|
|
2317
|
-
imports.add(mapping.outputType);
|
|
2318
|
-
}
|
|
2319
|
-
}
|
|
2320
|
-
return imports;
|
|
2321
|
-
}
|
|
2322
|
-
|
|
2323
|
-
// src/codegen/write-files.ts
|
|
2324
|
-
import { mkdir, writeFile } from "fs/promises";
|
|
2325
|
-
import { dirname as dirname2 } from "path";
|
|
2326
|
-
async function writeGeneratedFile(filePath, content, usePrettier = true) {
|
|
2327
|
-
await mkdir(dirname2(filePath), { recursive: true });
|
|
2328
|
-
let formattedContent = content;
|
|
2329
|
-
const isSwift = filePath.endsWith(".swift");
|
|
2330
|
-
if (usePrettier && !isSwift) {
|
|
2331
|
-
try {
|
|
2332
|
-
const prettier = await import("prettier");
|
|
2333
|
-
const parser = filePath.endsWith(".graphql") ? "graphql" : "typescript";
|
|
2334
|
-
formattedContent = await prettier.format(content, {
|
|
2335
|
-
parser,
|
|
2336
|
-
semi: true,
|
|
2337
|
-
singleQuote: true,
|
|
2338
|
-
trailingComma: "es5",
|
|
2339
|
-
printWidth: 100
|
|
2340
|
-
});
|
|
2341
|
-
} catch {
|
|
2342
|
-
}
|
|
2343
|
-
}
|
|
2344
|
-
await writeFile(filePath, formattedContent, "utf-8");
|
|
2345
|
-
}
|
|
2346
|
-
async function writeFiles(files, usePrettier = true) {
|
|
2347
|
-
await Promise.all(
|
|
2348
|
-
files.map(
|
|
2349
|
-
(file) => writeGeneratedFile(file.path, file.content, usePrettier)
|
|
2350
|
-
)
|
|
2351
|
-
);
|
|
2352
|
-
}
|
|
2353
|
-
|
|
2354
|
-
// src/commands/pull.ts
|
|
2355
|
-
function registerPullCommand(program2, globalOpts) {
|
|
2356
|
-
program2.command("pull").description(
|
|
2357
|
-
"Generate typed client, model types, and validation schemas from platform models"
|
|
2358
|
-
).option("--config <path>", "Path to config file").option("--only <models>", "Comma-separated model keys to generate").option("--no-prettier", "Skip Prettier formatting").option("--dry-run", "Show what would be generated without writing").option("--out <dir>", "Override output directory").action(
|
|
2359
|
-
withErrorHandler(
|
|
2360
|
-
globalOpts,
|
|
2361
|
-
async (cmdOpts) => {
|
|
2362
|
-
const opts = globalOpts();
|
|
2363
|
-
const flags = {
|
|
2364
|
-
config: cmdOpts.config,
|
|
2365
|
-
only: cmdOpts.only,
|
|
2366
|
-
noPrettier: cmdOpts.prettier === false,
|
|
2367
|
-
dryRun: !!cmdOpts.dryRun,
|
|
2368
|
-
out: cmdOpts.out
|
|
2369
|
-
};
|
|
2370
|
-
const config2 = await loadPullConfig(flags);
|
|
2371
|
-
const client = await createClient(opts);
|
|
2372
|
-
console.log(chalk4.dim("Fetching models\u2026"));
|
|
2373
|
-
const [allModels, cpSchema] = await Promise.all([
|
|
2374
|
-
fetchModelsForCodegen(client),
|
|
2375
|
-
fetchCustomerProfileSchema(client)
|
|
2376
|
-
]);
|
|
2377
|
-
if (allModels.length === 0 && !cpSchema) {
|
|
2378
|
-
console.log(chalk4.yellow("No models found. Nothing to generate."));
|
|
2379
|
-
return;
|
|
2380
|
-
}
|
|
2381
|
-
const models = filterModels(allModels, {
|
|
2382
|
-
only: config2.only.length > 0 ? config2.only : void 0,
|
|
2383
|
-
includeInline: config2.includeInline
|
|
2384
|
-
});
|
|
2385
|
-
console.log(
|
|
2386
|
-
chalk4.dim(
|
|
2387
|
-
`Found ${allModels.length} model(s), generating for ${models.length}.`
|
|
2388
|
-
)
|
|
2389
|
-
);
|
|
2390
|
-
const cwd = process.cwd();
|
|
2391
|
-
const outDir = resolve(cwd, config2.output.types);
|
|
2392
|
-
const modelsDir = resolve(outDir, "models");
|
|
2393
|
-
const files = [];
|
|
2394
|
-
const hasCustomerProfile = !!(cpSchema && cpSchema.fields.length > 0);
|
|
2395
|
-
const publicModels = models.filter(
|
|
2396
|
-
(m) => m.config.publicApi && m.config.records
|
|
2397
|
-
);
|
|
2398
|
-
for (const model of models) {
|
|
2399
|
-
files.push({
|
|
2400
|
-
path: resolve(modelsDir, `${model.key}.ts`),
|
|
2401
|
-
content: generateModelTypes(model, models)
|
|
2402
|
-
});
|
|
2403
|
-
files.push({
|
|
2404
|
-
path: resolve(modelsDir, `${model.key}.zod.ts`),
|
|
2405
|
-
content: generateModelZodSchema(model, models)
|
|
2406
|
-
});
|
|
2407
|
-
}
|
|
2408
|
-
for (const model of publicModels) {
|
|
2409
|
-
const whereCode = generateModelWhere(model);
|
|
2410
|
-
const sortCode = generateModelSortField(model);
|
|
2411
|
-
if (whereCode || sortCode) {
|
|
2412
|
-
files.push({
|
|
2413
|
-
path: resolve(modelsDir, `${model.key}.filters.ts`),
|
|
2414
|
-
content: `/**
|
|
2415
|
-
* Filter & sort types for ${model.name}
|
|
2416
|
-
*
|
|
2417
|
-
* @generated by foir \u2014 DO NOT EDIT MANUALLY
|
|
2418
|
-
*/
|
|
2419
|
-
|
|
2420
|
-
${whereCode}
|
|
2421
|
-
${sortCode}`
|
|
2422
|
-
});
|
|
2423
|
-
}
|
|
2424
|
-
}
|
|
2425
|
-
if (hasCustomerProfile) {
|
|
2426
|
-
files.push({
|
|
2427
|
-
path: resolve(modelsDir, "customer-profile.ts"),
|
|
2428
|
-
content: generateCustomerProfileTypes(cpSchema)
|
|
2429
|
-
});
|
|
2430
|
-
}
|
|
2431
|
-
files.push({
|
|
2432
|
-
path: resolve(modelsDir, "index.ts"),
|
|
2433
|
-
content: generateModelIndex(models)
|
|
2434
|
-
});
|
|
2435
|
-
files.push({
|
|
2436
|
-
path: resolve(outDir, "client.ts"),
|
|
2437
|
-
content: generateClientFactory(publicModels, hasCustomerProfile)
|
|
2438
|
-
});
|
|
2439
|
-
files.push({
|
|
2440
|
-
path: resolve(outDir, "schema.json"),
|
|
2441
|
-
content: generateSchemaManifest(models, cpSchema)
|
|
2442
|
-
});
|
|
2443
|
-
files.push({
|
|
2444
|
-
path: resolve(outDir, "index.ts"),
|
|
2445
|
-
content: generateRootIndex(hasCustomerProfile)
|
|
2446
|
-
});
|
|
2447
|
-
if (config2.dryRun) {
|
|
2448
|
-
console.log(
|
|
2449
|
-
chalk4.bold("\nDry run \u2014 files that would be generated:\n")
|
|
2450
|
-
);
|
|
2451
|
-
for (const file of files) {
|
|
2452
|
-
const rel = file.path.replace(cwd + "/", "");
|
|
2453
|
-
console.log(` ${chalk4.green("+")} ${rel}`);
|
|
2454
|
-
}
|
|
2455
|
-
console.log(`
|
|
2456
|
-
${chalk4.dim(`${files.length} file(s) total`)}`);
|
|
2457
|
-
return;
|
|
2458
|
-
}
|
|
2459
|
-
await writeFiles(files, config2.prettier);
|
|
2460
|
-
const cpSuffix = hasCustomerProfile ? " + customer profile" : "";
|
|
2461
|
-
console.log(
|
|
2462
|
-
chalk4.green(`
|
|
2463
|
-
\u2713 Generated ${files.length} file(s)`) + chalk4.dim(
|
|
2464
|
-
` (${models.length} model(s), ${publicModels.length} typed accessor(s)${cpSuffix})`
|
|
2465
|
-
)
|
|
2466
|
-
);
|
|
2467
|
-
console.log(chalk4.dim(` Output: ${outDir}`));
|
|
2468
|
-
}
|
|
2469
|
-
)
|
|
2470
|
-
);
|
|
2471
|
-
}
|
|
2472
|
-
function generateRootIndex(includeCustomerProfile) {
|
|
2473
|
-
let code = `/**
|
|
2474
|
-
* Generated Foir client and model types.
|
|
2475
|
-
*
|
|
2476
|
-
* @generated by foir \u2014 DO NOT EDIT MANUALLY
|
|
2477
|
-
*/
|
|
2478
|
-
|
|
2479
|
-
export { createClient } from './client.js';
|
|
2480
|
-
export type { TypedClient } from './client.js';
|
|
2481
|
-
export * from './models/index.js';
|
|
2482
|
-
`;
|
|
2483
|
-
if (includeCustomerProfile) {
|
|
2484
|
-
code += `export type { CustomerProfileData } from './models/customer-profile.js';
|
|
2485
|
-
`;
|
|
2486
|
-
}
|
|
2487
|
-
return code;
|
|
2488
|
-
}
|
|
2489
|
-
|
|
2490
|
-
// src/commands/create-extension.ts
|
|
2491
|
-
import chalk5 from "chalk";
|
|
2492
|
-
import inquirer2 from "inquirer";
|
|
2493
|
-
|
|
2494
|
-
// src/scaffold/scaffold.ts
|
|
2495
|
-
import * as fs4 from "fs";
|
|
2496
|
-
import * as path2 from "path";
|
|
2497
|
-
|
|
2498
|
-
// src/scaffold/package-manager.ts
|
|
2499
|
-
import * as fs3 from "fs";
|
|
2500
|
-
import * as path from "path";
|
|
2501
|
-
function detectPackageManager() {
|
|
2502
|
-
const lockFiles = [
|
|
2503
|
-
{ file: "pnpm-lock.yaml", manager: "pnpm" },
|
|
2504
|
-
{ file: "yarn.lock", manager: "yarn" },
|
|
2505
|
-
{ file: "package-lock.json", manager: "npm" }
|
|
2506
|
-
];
|
|
2507
|
-
let dir = process.cwd();
|
|
2508
|
-
while (true) {
|
|
2509
|
-
for (const { file, manager } of lockFiles) {
|
|
2510
|
-
if (fs3.existsSync(path.join(dir, file))) {
|
|
2511
|
-
return getManagerInfo(manager);
|
|
2512
|
-
}
|
|
2513
|
-
}
|
|
2514
|
-
const parentDir = path.dirname(dir);
|
|
2515
|
-
if (parentDir === dir) {
|
|
2516
|
-
break;
|
|
2517
|
-
}
|
|
2518
|
-
dir = parentDir;
|
|
2519
|
-
}
|
|
2520
|
-
return getManagerInfo("npm");
|
|
2521
|
-
}
|
|
2522
|
-
function getManagerInfo(manager) {
|
|
2523
|
-
switch (manager) {
|
|
2524
|
-
case "pnpm":
|
|
2525
|
-
return {
|
|
2526
|
-
name: "pnpm",
|
|
2527
|
-
installCommand: "pnpm install",
|
|
2528
|
-
execCommand: "pnpm dlx"
|
|
2529
|
-
};
|
|
2530
|
-
case "yarn":
|
|
2531
|
-
return {
|
|
2532
|
-
name: "yarn",
|
|
2533
|
-
installCommand: "yarn install",
|
|
2534
|
-
execCommand: "yarn dlx"
|
|
2535
|
-
};
|
|
2536
|
-
case "npm":
|
|
2537
|
-
return {
|
|
2538
|
-
name: "npm",
|
|
2539
|
-
installCommand: "npm install",
|
|
2540
|
-
execCommand: "npx"
|
|
2541
|
-
};
|
|
2542
|
-
}
|
|
2543
|
-
}
|
|
2544
|
-
|
|
2545
|
-
// src/scaffold/scaffold.ts
|
|
2546
|
-
async function scaffold(projectName, extensionType, apiUrl) {
|
|
2547
|
-
const projectDir = path2.resolve(process.cwd(), projectName);
|
|
2548
|
-
if (fs4.existsSync(projectDir)) {
|
|
2549
|
-
throw new Error(
|
|
2550
|
-
`Directory "${projectName}" already exists. Choose a different name or remove the existing directory.`
|
|
2551
|
-
);
|
|
2552
|
-
}
|
|
2553
|
-
const files = getFiles(projectName, extensionType, apiUrl);
|
|
2554
|
-
for (const [filePath, content] of Object.entries(files)) {
|
|
2555
|
-
const fullPath = path2.join(projectDir, filePath);
|
|
2556
|
-
const dir = path2.dirname(fullPath);
|
|
2557
|
-
if (!fs4.existsSync(dir)) {
|
|
2558
|
-
fs4.mkdirSync(dir, { recursive: true });
|
|
2559
|
-
}
|
|
2560
|
-
fs4.writeFileSync(fullPath, content, "utf-8");
|
|
2561
|
-
}
|
|
2562
|
-
const pm = detectPackageManager();
|
|
2563
|
-
console.log(" Files created:");
|
|
2564
|
-
console.log();
|
|
2565
|
-
for (const filePath of Object.keys(files)) {
|
|
2566
|
-
console.log(` ${filePath}`);
|
|
2567
|
-
}
|
|
2568
|
-
console.log();
|
|
2569
|
-
console.log(" Done! Next steps:");
|
|
2570
|
-
console.log();
|
|
2571
|
-
console.log(` cd ${projectName}`);
|
|
2572
|
-
console.log(` ${pm.installCommand}`);
|
|
2573
|
-
console.log(` cp ui/.env.example ui/.env.local`);
|
|
2574
|
-
console.log(` cp api/.env.example api/.env.local`);
|
|
2575
|
-
console.log(` ${pm.name === "npm" ? "npm run" : pm.name} dev`);
|
|
2576
|
-
console.log();
|
|
2577
|
-
}
|
|
2578
|
-
function getFiles(projectName, extensionType, apiUrl) {
|
|
2579
|
-
return {
|
|
2580
|
-
// Root
|
|
2581
|
-
"package.json": getRootPackageJson(projectName),
|
|
2582
|
-
"extension.manifest.json": getManifest(projectName, extensionType),
|
|
2583
|
-
// UI (Vite SPA)
|
|
2584
|
-
"ui/package.json": getUiPackageJson(projectName),
|
|
2585
|
-
"ui/tsconfig.json": getUiTsconfig(),
|
|
2586
|
-
"ui/vite.config.ts": getUiViteConfig(),
|
|
2587
|
-
"ui/index.html": getUiIndexHtml(projectName),
|
|
2588
|
-
"ui/.env.example": getUiEnvExample(apiUrl),
|
|
2589
|
-
"ui/.gitignore": getUiGitignore(),
|
|
2590
|
-
"ui/src/main.tsx": getUiMain(),
|
|
2591
|
-
"ui/src/App.tsx": getUiApp(extensionType),
|
|
2592
|
-
"ui/src/index.css": getUiCss(),
|
|
2593
|
-
"ui/src/vite-env.d.ts": '/// <reference types="vite/client" />\n',
|
|
2594
|
-
// API (Hono)
|
|
2595
|
-
"api/package.json": getApiPackageJson(projectName),
|
|
2596
|
-
"api/tsconfig.json": getApiTsconfig(),
|
|
2597
|
-
"api/.env.example": getApiEnvExample(apiUrl),
|
|
2598
|
-
"api/.gitignore": "node_modules\ndist\n.env\n.env.local\n",
|
|
2599
|
-
"api/src/index.ts": getApiIndex(),
|
|
2600
|
-
"api/src/routes/webhooks.ts": getApiWebhooks(),
|
|
2601
|
-
"api/src/routes/health.ts": getApiHealth(),
|
|
2602
|
-
"api/src/lib/platform.ts": getApiPlatform()
|
|
2603
|
-
};
|
|
2604
|
-
}
|
|
2605
|
-
function getRootPackageJson(name) {
|
|
2606
|
-
const pkg = {
|
|
2607
|
-
name,
|
|
2608
|
-
version: "0.1.0",
|
|
2609
|
-
private: true,
|
|
2610
|
-
scripts: {
|
|
2611
|
-
dev: 'concurrently "pnpm --filter ./ui dev" "pnpm --filter ./api dev"',
|
|
2612
|
-
build: "pnpm --filter ./ui build && pnpm --filter ./api build"
|
|
2613
|
-
},
|
|
2614
|
-
devDependencies: {
|
|
2615
|
-
concurrently: "^9.0.0"
|
|
2616
|
-
}
|
|
2617
|
-
};
|
|
2618
|
-
return JSON.stringify(pkg, null, 2) + "\n";
|
|
2619
|
-
}
|
|
2620
|
-
function getManifest(name, extensionType) {
|
|
2621
|
-
const manifest = {
|
|
2622
|
-
name: name.replace(/-/g, " ").replace(/\b\w/g, (c) => c.toUpperCase()),
|
|
2623
|
-
version: "0.1.0",
|
|
2624
|
-
type: extensionType,
|
|
2625
|
-
description: `${extensionType} extension`,
|
|
2626
|
-
entityTypes: []
|
|
2627
|
-
};
|
|
2628
|
-
return JSON.stringify(manifest, null, 2) + "\n";
|
|
2629
|
-
}
|
|
2630
|
-
function getUiPackageJson(name) {
|
|
2631
|
-
const pkg = {
|
|
2632
|
-
name: `${name}-ui`,
|
|
2633
|
-
version: "0.1.0",
|
|
2634
|
-
private: true,
|
|
2635
|
-
type: "module",
|
|
2636
|
-
scripts: {
|
|
2637
|
-
dev: "vite",
|
|
2638
|
-
build: "tsc && vite build",
|
|
2639
|
-
preview: "vite preview"
|
|
2640
|
-
},
|
|
2641
|
-
dependencies: {
|
|
2642
|
-
"@eide/extension-sdk": "^0.1.0",
|
|
2643
|
-
react: "^19.0.0",
|
|
2644
|
-
"react-dom": "^19.0.0"
|
|
2645
|
-
},
|
|
2646
|
-
devDependencies: {
|
|
2647
|
-
"@tailwindcss/vite": "^4.0.6",
|
|
2648
|
-
"@types/react": "^19.0.0",
|
|
2649
|
-
"@types/react-dom": "^19.0.0",
|
|
2650
|
-
"@vitejs/plugin-react": "^4.3.4",
|
|
2651
|
-
tailwindcss: "^4.0.0",
|
|
2652
|
-
typescript: "^5.0.0",
|
|
2653
|
-
vite: "^6.0.7"
|
|
1059
|
+
export default defineConfig({
|
|
1060
|
+
key: '${name}',
|
|
1061
|
+
name: '${displayName}',
|
|
1062
|
+
configType: '${configType}',
|
|
1063
|
+
|
|
1064
|
+
// Uncomment and configure as needed:
|
|
1065
|
+
// models: [],
|
|
1066
|
+
// operations: [],
|
|
1067
|
+
// schedules: [],
|
|
1068
|
+
// hooks: [],
|
|
1069
|
+
// segments: [],
|
|
1070
|
+
// authProviders: [],
|
|
1071
|
+
// placements: [],
|
|
1072
|
+
});
|
|
1073
|
+
`;
|
|
1074
|
+
}
|
|
1075
|
+
function getUiPackageJson(name) {
|
|
1076
|
+
const pkg = {
|
|
1077
|
+
name: `${name}-ui`,
|
|
1078
|
+
version: "0.1.0",
|
|
1079
|
+
private: true,
|
|
1080
|
+
type: "module",
|
|
1081
|
+
scripts: {
|
|
1082
|
+
dev: "vite",
|
|
1083
|
+
build: "tsc && vite build",
|
|
1084
|
+
preview: "vite preview"
|
|
1085
|
+
},
|
|
1086
|
+
dependencies: {
|
|
1087
|
+
"@eide/foir-editor-sdk": "^0.1.0",
|
|
1088
|
+
react: "^19.0.0",
|
|
1089
|
+
"react-dom": "^19.0.0"
|
|
1090
|
+
},
|
|
1091
|
+
devDependencies: {
|
|
1092
|
+
"@tailwindcss/vite": "^4.0.6",
|
|
1093
|
+
"@types/react": "^19.0.0",
|
|
1094
|
+
"@types/react-dom": "^19.0.0",
|
|
1095
|
+
"@vitejs/plugin-react": "^4.3.4",
|
|
1096
|
+
tailwindcss: "^4.0.0",
|
|
1097
|
+
typescript: "^5.0.0",
|
|
1098
|
+
vite: "^6.0.7"
|
|
2654
1099
|
}
|
|
2655
1100
|
};
|
|
2656
1101
|
return JSON.stringify(pkg, null, 2) + "\n";
|
|
@@ -2736,13 +1181,13 @@ dist
|
|
|
2736
1181
|
function getUiMain() {
|
|
2737
1182
|
return `import { StrictMode } from 'react';
|
|
2738
1183
|
import { createRoot } from 'react-dom/client';
|
|
2739
|
-
import {
|
|
1184
|
+
import { EditorProvider, useEditor } from '@eide/foir-editor-sdk';
|
|
2740
1185
|
import { useEffect } from 'react';
|
|
2741
1186
|
import { App } from './App';
|
|
2742
1187
|
import './index.css';
|
|
2743
1188
|
|
|
2744
1189
|
function ThemeSync({ children }: { children: React.ReactNode }) {
|
|
2745
|
-
const { theme } =
|
|
1190
|
+
const { theme } = useEditor();
|
|
2746
1191
|
|
|
2747
1192
|
useEffect(() => {
|
|
2748
1193
|
const root = document.documentElement;
|
|
@@ -2755,17 +1200,17 @@ function ThemeSync({ children }: { children: React.ReactNode }) {
|
|
|
2755
1200
|
|
|
2756
1201
|
createRoot(document.getElementById('root')!).render(
|
|
2757
1202
|
<StrictMode>
|
|
2758
|
-
<
|
|
1203
|
+
<EditorProvider>
|
|
2759
1204
|
<ThemeSync>
|
|
2760
1205
|
<App />
|
|
2761
1206
|
</ThemeSync>
|
|
2762
|
-
</
|
|
1207
|
+
</EditorProvider>
|
|
2763
1208
|
</StrictMode>
|
|
2764
1209
|
);
|
|
2765
1210
|
`;
|
|
2766
1211
|
}
|
|
2767
|
-
function getUiApp(
|
|
2768
|
-
switch (
|
|
1212
|
+
function getUiApp(configType) {
|
|
1213
|
+
switch (configType) {
|
|
2769
1214
|
case "custom-editor":
|
|
2770
1215
|
return getCustomEditorApp();
|
|
2771
1216
|
case "widget":
|
|
@@ -2775,10 +1220,10 @@ function getUiApp(extensionType) {
|
|
|
2775
1220
|
}
|
|
2776
1221
|
}
|
|
2777
1222
|
function getCustomEditorApp() {
|
|
2778
|
-
return `import {
|
|
1223
|
+
return `import { useEditor, useAutoResize } from '@eide/foir-editor-sdk';
|
|
2779
1224
|
|
|
2780
1225
|
export function App() {
|
|
2781
|
-
const { isReady, init, updateField, setDirty } =
|
|
1226
|
+
const { isReady, init, updateField, setDirty } = useEditor();
|
|
2782
1227
|
const containerRef = useAutoResize({ minHeight: 600 });
|
|
2783
1228
|
|
|
2784
1229
|
if (!isReady) return null;
|
|
@@ -2786,7 +1231,7 @@ export function App() {
|
|
|
2786
1231
|
return (
|
|
2787
1232
|
<div ref={containerRef} className="p-6 space-y-4">
|
|
2788
1233
|
<h1 className="text-lg font-semibold">
|
|
2789
|
-
Editing: {init?.
|
|
1234
|
+
Editing: {init?.modelKey}
|
|
2790
1235
|
</h1>
|
|
2791
1236
|
<p className="text-sm text-gray-500">
|
|
2792
1237
|
Record: {init?.recordId}
|
|
@@ -2798,10 +1243,10 @@ export function App() {
|
|
|
2798
1243
|
`;
|
|
2799
1244
|
}
|
|
2800
1245
|
function getWidgetApp() {
|
|
2801
|
-
return `import {
|
|
1246
|
+
return `import { useEditor, useAutoResize } from '@eide/foir-editor-sdk';
|
|
2802
1247
|
|
|
2803
1248
|
export function App() {
|
|
2804
|
-
const { isReady, init, client } =
|
|
1249
|
+
const { isReady, init, client } = useEditor();
|
|
2805
1250
|
const containerRef = useAutoResize({ minHeight: 300 });
|
|
2806
1251
|
|
|
2807
1252
|
if (!isReady) return null;
|
|
@@ -2810,7 +1255,7 @@ export function App() {
|
|
|
2810
1255
|
<div ref={containerRef} className="p-6 space-y-4">
|
|
2811
1256
|
<h2 className="text-lg font-semibold">Dashboard Widget</h2>
|
|
2812
1257
|
<p className="text-sm text-gray-500">
|
|
2813
|
-
Connected to: {init?.
|
|
1258
|
+
Connected to: {init?.modelKey}
|
|
2814
1259
|
</p>
|
|
2815
1260
|
{/* Add your widget content here */}
|
|
2816
1261
|
</div>
|
|
@@ -2819,19 +1264,19 @@ export function App() {
|
|
|
2819
1264
|
`;
|
|
2820
1265
|
}
|
|
2821
1266
|
function getWorkflowApp() {
|
|
2822
|
-
return `import {
|
|
1267
|
+
return `import { useEditor, useAutoResize } from '@eide/foir-editor-sdk';
|
|
2823
1268
|
|
|
2824
1269
|
export function App() {
|
|
2825
|
-
const { isReady, init, client, requestSave } =
|
|
1270
|
+
const { isReady, init, client, requestSave } = useEditor();
|
|
2826
1271
|
const containerRef = useAutoResize({ minHeight: 400 });
|
|
2827
1272
|
|
|
2828
1273
|
if (!isReady) return null;
|
|
2829
1274
|
|
|
2830
1275
|
return (
|
|
2831
1276
|
<div ref={containerRef} className="p-6 space-y-4">
|
|
2832
|
-
<h1 className="text-lg font-semibold">Workflow
|
|
1277
|
+
<h1 className="text-lg font-semibold">Workflow Config</h1>
|
|
2833
1278
|
<p className="text-sm text-gray-500">
|
|
2834
|
-
Processing: {init?.
|
|
1279
|
+
Processing: {init?.modelKey} / {init?.recordId}
|
|
2835
1280
|
</p>
|
|
2836
1281
|
{/* Add your workflow steps here */}
|
|
2837
1282
|
</div>
|
|
@@ -2871,7 +1316,7 @@ function getApiPackageJson(name) {
|
|
|
2871
1316
|
start: "node dist/index.js"
|
|
2872
1317
|
},
|
|
2873
1318
|
dependencies: {
|
|
2874
|
-
"@eide/
|
|
1319
|
+
"@eide/foir-editor-sdk": "^0.1.0",
|
|
2875
1320
|
hono: "^4.0.0",
|
|
2876
1321
|
"@hono/node-server": "^1.0.0"
|
|
2877
1322
|
},
|
|
@@ -2907,8 +1352,8 @@ function getApiEnvExample(apiUrl) {
|
|
|
2907
1352
|
PLATFORM_BASE_URL=${baseUrl}
|
|
2908
1353
|
PLATFORM_API_KEY=sk_your_api_key_here
|
|
2909
1354
|
|
|
2910
|
-
#
|
|
2911
|
-
|
|
1355
|
+
# Config
|
|
1356
|
+
CONFIG_KEY=${"{your-config-key}"}
|
|
2912
1357
|
WEBHOOK_SECRET=your_webhook_secret_here
|
|
2913
1358
|
|
|
2914
1359
|
# Server
|
|
@@ -2929,25 +1374,25 @@ app.route('/', health);
|
|
|
2929
1374
|
|
|
2930
1375
|
const port = parseInt(process.env.PORT || '3002', 10);
|
|
2931
1376
|
|
|
2932
|
-
console.log(\`
|
|
1377
|
+
console.log(\`Config API running on http://localhost:\${port}\`);
|
|
2933
1378
|
|
|
2934
1379
|
serve({ fetch: app.fetch, port });
|
|
2935
1380
|
`;
|
|
2936
1381
|
}
|
|
2937
1382
|
function getApiWebhooks() {
|
|
2938
1383
|
return `import { Hono } from 'hono';
|
|
2939
|
-
import { verifyWebhookSignature } from '@eide/
|
|
1384
|
+
import { verifyWebhookSignature } from '@eide/foir-editor-sdk/server';
|
|
2940
1385
|
|
|
2941
1386
|
const WEBHOOK_SECRET = process.env.WEBHOOK_SECRET || '';
|
|
2942
1387
|
|
|
2943
1388
|
export const webhooks = new Hono();
|
|
2944
1389
|
|
|
2945
1390
|
/**
|
|
2946
|
-
* Receive
|
|
1391
|
+
* Receive record lifecycle events from the platform.
|
|
2947
1392
|
*/
|
|
2948
|
-
webhooks.post('/
|
|
1393
|
+
webhooks.post('/record-changed', async (c) => {
|
|
2949
1394
|
const body = await c.req.text();
|
|
2950
|
-
const signature = c.req.header('x-
|
|
1395
|
+
const signature = c.req.header('x-foir-signature') ?? '';
|
|
2951
1396
|
|
|
2952
1397
|
const valid = await verifyWebhookSignature(body, signature, WEBHOOK_SECRET);
|
|
2953
1398
|
if (!valid) {
|
|
@@ -2955,7 +1400,7 @@ webhooks.post('/entity-changed', async (c) => {
|
|
|
2955
1400
|
}
|
|
2956
1401
|
|
|
2957
1402
|
const payload = JSON.parse(body);
|
|
2958
|
-
console.log('[Webhook]
|
|
1403
|
+
console.log('[Webhook] Record changed:', payload.event, payload.recordId);
|
|
2959
1404
|
|
|
2960
1405
|
// TODO: Handle the event (sync, transform, notify, etc.)
|
|
2961
1406
|
|
|
@@ -2973,36 +1418,14 @@ health.get('/health', (c) => {
|
|
|
2973
1418
|
});
|
|
2974
1419
|
`;
|
|
2975
1420
|
}
|
|
2976
|
-
function getApiPlatform() {
|
|
2977
|
-
return `import { createExtensionClient } from '@eide/extension-sdk/server';
|
|
2978
|
-
|
|
2979
|
-
/**
|
|
2980
|
-
* Pre-configured platform client for this extension.
|
|
2981
|
-
*
|
|
2982
|
-
* Uses env vars for configuration:
|
|
2983
|
-
* - PLATFORM_BASE_URL: Platform API base URL
|
|
2984
|
-
* - PLATFORM_API_KEY: Project-scoped API key (sk_*)
|
|
2985
|
-
* - EXTENSION_KEY: This extension's extension key
|
|
2986
|
-
*/
|
|
2987
|
-
export const platform = createExtensionClient({
|
|
2988
|
-
baseUrl: process.env.PLATFORM_BASE_URL || 'http://localhost:4000',
|
|
2989
|
-
apiKey: process.env.PLATFORM_API_KEY || '',
|
|
2990
|
-
extensionKey: process.env.EXTENSION_KEY || '',
|
|
2991
|
-
});
|
|
2992
|
-
`;
|
|
2993
|
-
}
|
|
2994
1421
|
|
|
2995
|
-
// src/commands/create-
|
|
2996
|
-
var
|
|
2997
|
-
|
|
2998
|
-
|
|
2999
|
-
"widget"
|
|
3000
|
-
];
|
|
3001
|
-
function isValidExtensionType(value) {
|
|
3002
|
-
return EXTENSION_TYPES.includes(value);
|
|
1422
|
+
// src/commands/create-config.ts
|
|
1423
|
+
var CONFIG_TYPES = ["custom-editor", "workflow", "widget"];
|
|
1424
|
+
function isValidConfigType(value) {
|
|
1425
|
+
return CONFIG_TYPES.includes(value);
|
|
3003
1426
|
}
|
|
3004
|
-
function
|
|
3005
|
-
program2.command("create-
|
|
1427
|
+
function registerCreateConfigCommand(program2, globalOpts) {
|
|
1428
|
+
program2.command("create-config [name]").description("Scaffold a new Foir config").option("--type <type>", "Config type: custom-editor, workflow, widget").option(
|
|
3006
1429
|
"--api-url <url>",
|
|
3007
1430
|
"Platform API URL",
|
|
3008
1431
|
"http://localhost:4000/graphql"
|
|
@@ -3011,48 +1434,206 @@ function registerCreateExtensionCommand(program2, globalOpts) {
|
|
|
3011
1434
|
globalOpts,
|
|
3012
1435
|
async (name, cmdOpts) => {
|
|
3013
1436
|
console.log();
|
|
3014
|
-
console.log(
|
|
3015
|
-
console.log(
|
|
1437
|
+
console.log(chalk4.bold(" Create Foir Config"));
|
|
1438
|
+
console.log(chalk4.gray(" ------------------"));
|
|
3016
1439
|
console.log();
|
|
3017
|
-
let
|
|
3018
|
-
if (!
|
|
1440
|
+
let configName = name;
|
|
1441
|
+
if (!configName) {
|
|
3019
1442
|
const { inputName } = await inquirer2.prompt([
|
|
3020
1443
|
{
|
|
3021
1444
|
type: "input",
|
|
3022
1445
|
name: "inputName",
|
|
3023
|
-
message: "
|
|
3024
|
-
default: "my-
|
|
1446
|
+
message: "Config name:",
|
|
1447
|
+
default: "my-config"
|
|
3025
1448
|
}
|
|
3026
1449
|
]);
|
|
3027
|
-
|
|
1450
|
+
configName = inputName;
|
|
3028
1451
|
}
|
|
3029
|
-
let
|
|
3030
|
-
if (cmdOpts?.type &&
|
|
3031
|
-
|
|
1452
|
+
let configType;
|
|
1453
|
+
if (cmdOpts?.type && isValidConfigType(cmdOpts.type)) {
|
|
1454
|
+
configType = cmdOpts.type;
|
|
3032
1455
|
} else {
|
|
3033
1456
|
const { selectedType } = await inquirer2.prompt([
|
|
3034
1457
|
{
|
|
3035
1458
|
type: "list",
|
|
3036
1459
|
name: "selectedType",
|
|
3037
|
-
message: "
|
|
3038
|
-
choices:
|
|
1460
|
+
message: "Config type:",
|
|
1461
|
+
choices: CONFIG_TYPES,
|
|
3039
1462
|
default: "custom-editor"
|
|
3040
1463
|
}
|
|
3041
1464
|
]);
|
|
3042
|
-
|
|
1465
|
+
configType = selectedType;
|
|
3043
1466
|
}
|
|
3044
1467
|
const apiUrl = cmdOpts?.apiUrl ?? "http://localhost:4000/graphql";
|
|
3045
1468
|
console.log();
|
|
3046
1469
|
console.log(
|
|
3047
|
-
` Scaffolding ${
|
|
1470
|
+
` Scaffolding ${chalk4.cyan(`"${configName}"`)} (${configType})...`
|
|
3048
1471
|
);
|
|
3049
1472
|
console.log();
|
|
3050
|
-
await scaffold(
|
|
1473
|
+
await scaffold(configName, configType, apiUrl);
|
|
3051
1474
|
}
|
|
3052
1475
|
)
|
|
3053
1476
|
);
|
|
3054
1477
|
}
|
|
3055
1478
|
|
|
1479
|
+
// src/graphql/generated.ts
|
|
1480
|
+
var GlobalSearchDocument = {
|
|
1481
|
+
kind: "Document",
|
|
1482
|
+
definitions: [
|
|
1483
|
+
{
|
|
1484
|
+
kind: "OperationDefinition",
|
|
1485
|
+
operation: "query",
|
|
1486
|
+
name: { kind: "Name", value: "GlobalSearch" },
|
|
1487
|
+
variableDefinitions: [
|
|
1488
|
+
{
|
|
1489
|
+
kind: "VariableDefinition",
|
|
1490
|
+
variable: {
|
|
1491
|
+
kind: "Variable",
|
|
1492
|
+
name: { kind: "Name", value: "query" }
|
|
1493
|
+
},
|
|
1494
|
+
type: {
|
|
1495
|
+
kind: "NonNullType",
|
|
1496
|
+
type: {
|
|
1497
|
+
kind: "NamedType",
|
|
1498
|
+
name: { kind: "Name", value: "String" }
|
|
1499
|
+
}
|
|
1500
|
+
}
|
|
1501
|
+
},
|
|
1502
|
+
{
|
|
1503
|
+
kind: "VariableDefinition",
|
|
1504
|
+
variable: {
|
|
1505
|
+
kind: "Variable",
|
|
1506
|
+
name: { kind: "Name", value: "limit" }
|
|
1507
|
+
},
|
|
1508
|
+
type: { kind: "NamedType", name: { kind: "Name", value: "Int" } }
|
|
1509
|
+
},
|
|
1510
|
+
{
|
|
1511
|
+
kind: "VariableDefinition",
|
|
1512
|
+
variable: {
|
|
1513
|
+
kind: "Variable",
|
|
1514
|
+
name: { kind: "Name", value: "modelKeys" }
|
|
1515
|
+
},
|
|
1516
|
+
type: {
|
|
1517
|
+
kind: "ListType",
|
|
1518
|
+
type: {
|
|
1519
|
+
kind: "NonNullType",
|
|
1520
|
+
type: {
|
|
1521
|
+
kind: "NamedType",
|
|
1522
|
+
name: { kind: "Name", value: "String" }
|
|
1523
|
+
}
|
|
1524
|
+
}
|
|
1525
|
+
}
|
|
1526
|
+
},
|
|
1527
|
+
{
|
|
1528
|
+
kind: "VariableDefinition",
|
|
1529
|
+
variable: {
|
|
1530
|
+
kind: "Variable",
|
|
1531
|
+
name: { kind: "Name", value: "includeMedia" }
|
|
1532
|
+
},
|
|
1533
|
+
type: { kind: "NamedType", name: { kind: "Name", value: "Boolean" } }
|
|
1534
|
+
}
|
|
1535
|
+
],
|
|
1536
|
+
selectionSet: {
|
|
1537
|
+
kind: "SelectionSet",
|
|
1538
|
+
selections: [
|
|
1539
|
+
{
|
|
1540
|
+
kind: "Field",
|
|
1541
|
+
name: { kind: "Name", value: "globalSearch" },
|
|
1542
|
+
arguments: [
|
|
1543
|
+
{
|
|
1544
|
+
kind: "Argument",
|
|
1545
|
+
name: { kind: "Name", value: "query" },
|
|
1546
|
+
value: {
|
|
1547
|
+
kind: "Variable",
|
|
1548
|
+
name: { kind: "Name", value: "query" }
|
|
1549
|
+
}
|
|
1550
|
+
},
|
|
1551
|
+
{
|
|
1552
|
+
kind: "Argument",
|
|
1553
|
+
name: { kind: "Name", value: "limit" },
|
|
1554
|
+
value: {
|
|
1555
|
+
kind: "Variable",
|
|
1556
|
+
name: { kind: "Name", value: "limit" }
|
|
1557
|
+
}
|
|
1558
|
+
},
|
|
1559
|
+
{
|
|
1560
|
+
kind: "Argument",
|
|
1561
|
+
name: { kind: "Name", value: "modelKeys" },
|
|
1562
|
+
value: {
|
|
1563
|
+
kind: "Variable",
|
|
1564
|
+
name: { kind: "Name", value: "modelKeys" }
|
|
1565
|
+
}
|
|
1566
|
+
},
|
|
1567
|
+
{
|
|
1568
|
+
kind: "Argument",
|
|
1569
|
+
name: { kind: "Name", value: "includeMedia" },
|
|
1570
|
+
value: {
|
|
1571
|
+
kind: "Variable",
|
|
1572
|
+
name: { kind: "Name", value: "includeMedia" }
|
|
1573
|
+
}
|
|
1574
|
+
}
|
|
1575
|
+
],
|
|
1576
|
+
selectionSet: {
|
|
1577
|
+
kind: "SelectionSet",
|
|
1578
|
+
selections: [
|
|
1579
|
+
{
|
|
1580
|
+
kind: "Field",
|
|
1581
|
+
name: { kind: "Name", value: "records" },
|
|
1582
|
+
selectionSet: {
|
|
1583
|
+
kind: "SelectionSet",
|
|
1584
|
+
selections: [
|
|
1585
|
+
{ kind: "Field", name: { kind: "Name", value: "id" } },
|
|
1586
|
+
{
|
|
1587
|
+
kind: "Field",
|
|
1588
|
+
name: { kind: "Name", value: "modelKey" }
|
|
1589
|
+
},
|
|
1590
|
+
{ kind: "Field", name: { kind: "Name", value: "title" } },
|
|
1591
|
+
{
|
|
1592
|
+
kind: "Field",
|
|
1593
|
+
name: { kind: "Name", value: "naturalKey" }
|
|
1594
|
+
},
|
|
1595
|
+
{
|
|
1596
|
+
kind: "Field",
|
|
1597
|
+
name: { kind: "Name", value: "subtitle" }
|
|
1598
|
+
},
|
|
1599
|
+
{
|
|
1600
|
+
kind: "Field",
|
|
1601
|
+
name: { kind: "Name", value: "updatedAt" }
|
|
1602
|
+
}
|
|
1603
|
+
]
|
|
1604
|
+
}
|
|
1605
|
+
},
|
|
1606
|
+
{
|
|
1607
|
+
kind: "Field",
|
|
1608
|
+
name: { kind: "Name", value: "media" },
|
|
1609
|
+
selectionSet: {
|
|
1610
|
+
kind: "SelectionSet",
|
|
1611
|
+
selections: [
|
|
1612
|
+
{ kind: "Field", name: { kind: "Name", value: "id" } },
|
|
1613
|
+
{
|
|
1614
|
+
kind: "Field",
|
|
1615
|
+
name: { kind: "Name", value: "fileName" }
|
|
1616
|
+
},
|
|
1617
|
+
{
|
|
1618
|
+
kind: "Field",
|
|
1619
|
+
name: { kind: "Name", value: "altText" }
|
|
1620
|
+
},
|
|
1621
|
+
{
|
|
1622
|
+
kind: "Field",
|
|
1623
|
+
name: { kind: "Name", value: "fileUrl" }
|
|
1624
|
+
}
|
|
1625
|
+
]
|
|
1626
|
+
}
|
|
1627
|
+
}
|
|
1628
|
+
]
|
|
1629
|
+
}
|
|
1630
|
+
}
|
|
1631
|
+
]
|
|
1632
|
+
}
|
|
1633
|
+
}
|
|
1634
|
+
]
|
|
1635
|
+
};
|
|
1636
|
+
|
|
3056
1637
|
// src/commands/search.ts
|
|
3057
1638
|
function registerSearchCommands(program2, globalOpts) {
|
|
3058
1639
|
program2.command("search <query>").description("Search across all records and media").option(
|
|
@@ -3122,9 +1703,9 @@ Media (${data.globalSearch.media.length}):`);
|
|
|
3122
1703
|
|
|
3123
1704
|
// src/commands/init.ts
|
|
3124
1705
|
import { existsSync as existsSync3, mkdirSync as mkdirSync2 } from "fs";
|
|
3125
|
-
import { writeFile
|
|
3126
|
-
import { resolve as
|
|
3127
|
-
import
|
|
1706
|
+
import { writeFile } from "fs/promises";
|
|
1707
|
+
import { resolve as resolve2, join as join4 } from "path";
|
|
1708
|
+
import chalk5 from "chalk";
|
|
3128
1709
|
import inquirer3 from "inquirer";
|
|
3129
1710
|
var FIELD_DEFAULTS = {
|
|
3130
1711
|
text: "",
|
|
@@ -3214,18 +1795,18 @@ function registerInitCommands(program2, globalOpts) {
|
|
|
3214
1795
|
async (key, opts) => {
|
|
3215
1796
|
const globalFlags = globalOpts();
|
|
3216
1797
|
const template = generateModelTemplate(key);
|
|
3217
|
-
const outDir =
|
|
1798
|
+
const outDir = resolve2(opts.output);
|
|
3218
1799
|
if (!existsSync3(outDir)) {
|
|
3219
1800
|
mkdirSync2(outDir, { recursive: true });
|
|
3220
1801
|
}
|
|
3221
1802
|
const ext = opts.ts ? "ts" : "json";
|
|
3222
1803
|
const filePath = join4(outDir, `${key}.${ext}`);
|
|
3223
1804
|
const content = opts.ts ? formatAsTypeScript(template) : JSON.stringify(template, null, 2) + "\n";
|
|
3224
|
-
await
|
|
1805
|
+
await writeFile(filePath, content, "utf-8");
|
|
3225
1806
|
if (!(globalFlags.json || globalFlags.jsonl || globalFlags.quiet)) {
|
|
3226
1807
|
success(`Created ${filePath}`);
|
|
3227
1808
|
console.log(
|
|
3228
|
-
|
|
1809
|
+
chalk5.gray(
|
|
3229
1810
|
`
|
|
3230
1811
|
Edit the file, then run:
|
|
3231
1812
|
foir models create -f ${filePath}`
|
|
@@ -3251,7 +1832,7 @@ Edit the file, then run:
|
|
|
3251
1832
|
const models = result.models.items;
|
|
3252
1833
|
if (models.length === 0) {
|
|
3253
1834
|
console.log(
|
|
3254
|
-
|
|
1835
|
+
chalk5.yellow(
|
|
3255
1836
|
"No models found. Create models first with `foir models create`."
|
|
3256
1837
|
)
|
|
3257
1838
|
);
|
|
@@ -3264,7 +1845,7 @@ Edit the file, then run:
|
|
|
3264
1845
|
const found = models.find((m) => m.key === key);
|
|
3265
1846
|
if (!found) {
|
|
3266
1847
|
console.error(
|
|
3267
|
-
|
|
1848
|
+
chalk5.red(`Model "${key}" not found.`),
|
|
3268
1849
|
"Available:",
|
|
3269
1850
|
models.map((m) => m.key).join(", ")
|
|
3270
1851
|
);
|
|
@@ -3292,7 +1873,7 @@ Edit the file, then run:
|
|
|
3292
1873
|
console.log("No models selected.");
|
|
3293
1874
|
return;
|
|
3294
1875
|
}
|
|
3295
|
-
const outDir =
|
|
1876
|
+
const outDir = resolve2(opts.output);
|
|
3296
1877
|
if (!existsSync3(outDir)) {
|
|
3297
1878
|
mkdirSync2(outDir, { recursive: true });
|
|
3298
1879
|
}
|
|
@@ -3302,7 +1883,7 @@ Edit the file, then run:
|
|
|
3302
1883
|
const ext = opts.ts ? "ts" : "json";
|
|
3303
1884
|
const filePath = join4(outDir, `${model.key}.${ext}`);
|
|
3304
1885
|
const content = opts.ts ? formatAsTypeScript(seed) : JSON.stringify(seed, null, 2) + "\n";
|
|
3305
|
-
await
|
|
1886
|
+
await writeFile(filePath, content, "utf-8");
|
|
3306
1887
|
createdFiles.push(filePath);
|
|
3307
1888
|
if (!(globalFlags.json || globalFlags.jsonl || globalFlags.quiet)) {
|
|
3308
1889
|
success(`Created ${filePath}`);
|
|
@@ -3310,7 +1891,7 @@ Edit the file, then run:
|
|
|
3310
1891
|
}
|
|
3311
1892
|
if (!(globalFlags.json || globalFlags.jsonl || globalFlags.quiet)) {
|
|
3312
1893
|
console.log(
|
|
3313
|
-
|
|
1894
|
+
chalk5.gray(
|
|
3314
1895
|
`
|
|
3315
1896
|
Edit the files, then run:
|
|
3316
1897
|
foir records create --dir ${outDir} --publish`
|
|
@@ -3324,18 +1905,17 @@ Edit the files, then run:
|
|
|
3324
1905
|
);
|
|
3325
1906
|
}
|
|
3326
1907
|
|
|
3327
|
-
// src/commands/
|
|
3328
|
-
import
|
|
3329
|
-
|
|
3330
|
-
|
|
3331
|
-
import inquirer4 from "inquirer";
|
|
1908
|
+
// src/commands/push.ts
|
|
1909
|
+
import chalk6 from "chalk";
|
|
1910
|
+
import { existsSync as existsSync4 } from "fs";
|
|
1911
|
+
import { resolve as resolve4 } from "path";
|
|
3332
1912
|
|
|
3333
1913
|
// src/lib/config-loader.ts
|
|
3334
1914
|
import { readFile } from "fs/promises";
|
|
3335
1915
|
import { pathToFileURL } from "url";
|
|
3336
|
-
import { resolve as
|
|
1916
|
+
import { resolve as resolve3 } from "path";
|
|
3337
1917
|
async function loadConfig(filePath) {
|
|
3338
|
-
const absPath =
|
|
1918
|
+
const absPath = resolve3(filePath);
|
|
3339
1919
|
if (filePath.endsWith(".ts")) {
|
|
3340
1920
|
const configModule = await import(pathToFileURL(absPath).href);
|
|
3341
1921
|
return configModule.default;
|
|
@@ -3353,7 +1933,194 @@ async function loadConfig(filePath) {
|
|
|
3353
1933
|
);
|
|
3354
1934
|
}
|
|
3355
1935
|
|
|
1936
|
+
// src/commands/push.ts
|
|
1937
|
+
var CONFIG_FILE_NAMES = [
|
|
1938
|
+
"foir.config.ts",
|
|
1939
|
+
"foir.config.js",
|
|
1940
|
+
"foir.config.mjs",
|
|
1941
|
+
"foir.config.json"
|
|
1942
|
+
];
|
|
1943
|
+
var APPLY_CONFIG_MUTATION = (
|
|
1944
|
+
/* GraphQL */
|
|
1945
|
+
`
|
|
1946
|
+
mutation ApplyConfig($input: ApplyConfigInput!) {
|
|
1947
|
+
applyConfig(input: $input) {
|
|
1948
|
+
configId
|
|
1949
|
+
configKey
|
|
1950
|
+
credentials {
|
|
1951
|
+
platformApiKey
|
|
1952
|
+
platformEditorKey
|
|
1953
|
+
webhookSecret
|
|
1954
|
+
}
|
|
1955
|
+
modelsCreated
|
|
1956
|
+
modelsUpdated
|
|
1957
|
+
operationsCreated
|
|
1958
|
+
operationsUpdated
|
|
1959
|
+
segmentsCreated
|
|
1960
|
+
segmentsUpdated
|
|
1961
|
+
schedulesCreated
|
|
1962
|
+
schedulesUpdated
|
|
1963
|
+
hooksCreated
|
|
1964
|
+
hooksUpdated
|
|
1965
|
+
isUpdate
|
|
1966
|
+
}
|
|
1967
|
+
}
|
|
1968
|
+
`
|
|
1969
|
+
);
|
|
1970
|
+
function discoverConfigFile() {
|
|
1971
|
+
for (const name of CONFIG_FILE_NAMES) {
|
|
1972
|
+
const path3 = resolve4(process.cwd(), name);
|
|
1973
|
+
if (existsSync4(path3)) return path3;
|
|
1974
|
+
}
|
|
1975
|
+
return null;
|
|
1976
|
+
}
|
|
1977
|
+
function registerPushCommand(program2, globalOpts) {
|
|
1978
|
+
program2.command("push").description("Push foir.config.ts to the platform").option("--config <path>", "Path to config file (default: auto-discover)").option("--force", "Force reinstall (delete and recreate)", false).action(
|
|
1979
|
+
withErrorHandler(
|
|
1980
|
+
globalOpts,
|
|
1981
|
+
async (opts) => {
|
|
1982
|
+
const configPath = opts.config ? resolve4(opts.config) : discoverConfigFile();
|
|
1983
|
+
if (!configPath) {
|
|
1984
|
+
throw new Error(
|
|
1985
|
+
"No config file found. Create a foir.config.ts or use --config <path>."
|
|
1986
|
+
);
|
|
1987
|
+
}
|
|
1988
|
+
if (!existsSync4(configPath)) {
|
|
1989
|
+
throw new Error(`Config file not found: ${configPath}`);
|
|
1990
|
+
}
|
|
1991
|
+
console.log(chalk6.dim(`Loading ${configPath}...`));
|
|
1992
|
+
const config2 = await loadConfig(configPath);
|
|
1993
|
+
if (!config2?.key || !config2?.name) {
|
|
1994
|
+
throw new Error(
|
|
1995
|
+
'Config must have at least "key" and "name" fields.'
|
|
1996
|
+
);
|
|
1997
|
+
}
|
|
1998
|
+
if (opts.force) {
|
|
1999
|
+
config2.force = true;
|
|
2000
|
+
}
|
|
2001
|
+
const client = await createClient(globalOpts());
|
|
2002
|
+
console.log(
|
|
2003
|
+
chalk6.dim(`Pushing config "${config2.key}" to platform...`)
|
|
2004
|
+
);
|
|
2005
|
+
const data = await client.request(
|
|
2006
|
+
APPLY_CONFIG_MUTATION,
|
|
2007
|
+
{ input: config2 }
|
|
2008
|
+
);
|
|
2009
|
+
const result = data.applyConfig;
|
|
2010
|
+
console.log();
|
|
2011
|
+
if (result.isUpdate) {
|
|
2012
|
+
console.log(chalk6.green("Config updated successfully."));
|
|
2013
|
+
} else {
|
|
2014
|
+
console.log(chalk6.green("Config applied successfully."));
|
|
2015
|
+
}
|
|
2016
|
+
console.log();
|
|
2017
|
+
console.log(` Config ID: ${chalk6.cyan(result.configId)}`);
|
|
2018
|
+
console.log(` Config Key: ${chalk6.cyan(result.configKey)}`);
|
|
2019
|
+
console.log();
|
|
2020
|
+
const stats = [
|
|
2021
|
+
["Models", result.modelsCreated, result.modelsUpdated],
|
|
2022
|
+
["Operations", result.operationsCreated, result.operationsUpdated],
|
|
2023
|
+
["Segments", result.segmentsCreated, result.segmentsUpdated],
|
|
2024
|
+
["Schedules", result.schedulesCreated, result.schedulesUpdated],
|
|
2025
|
+
["Hooks", result.hooksCreated, result.hooksUpdated]
|
|
2026
|
+
].filter(([, c, u]) => c > 0 || u > 0);
|
|
2027
|
+
if (stats.length > 0) {
|
|
2028
|
+
for (const [label, created, updated] of stats) {
|
|
2029
|
+
const parts = [];
|
|
2030
|
+
if (created > 0)
|
|
2031
|
+
parts.push(chalk6.green(`${created} created`));
|
|
2032
|
+
if (updated > 0)
|
|
2033
|
+
parts.push(chalk6.yellow(`${updated} updated`));
|
|
2034
|
+
console.log(` ${label}: ${parts.join(", ")}`);
|
|
2035
|
+
}
|
|
2036
|
+
console.log();
|
|
2037
|
+
}
|
|
2038
|
+
if (result.credentials) {
|
|
2039
|
+
console.log(chalk6.bold.yellow("Credentials (save these now):"));
|
|
2040
|
+
console.log();
|
|
2041
|
+
console.log(
|
|
2042
|
+
` PLATFORM_API_KEY: ${chalk6.cyan(result.credentials.platformApiKey)}`
|
|
2043
|
+
);
|
|
2044
|
+
console.log(
|
|
2045
|
+
` PLATFORM_EDITOR_KEY: ${chalk6.cyan(result.credentials.platformEditorKey)}`
|
|
2046
|
+
);
|
|
2047
|
+
console.log(
|
|
2048
|
+
` WEBHOOK_SECRET: ${chalk6.cyan(result.credentials.webhookSecret)}`
|
|
2049
|
+
);
|
|
2050
|
+
console.log();
|
|
2051
|
+
console.log(
|
|
2052
|
+
chalk6.dim(
|
|
2053
|
+
"These credentials are only shown once. Store them securely."
|
|
2054
|
+
)
|
|
2055
|
+
);
|
|
2056
|
+
}
|
|
2057
|
+
}
|
|
2058
|
+
)
|
|
2059
|
+
);
|
|
2060
|
+
}
|
|
2061
|
+
|
|
2062
|
+
// src/commands/remove.ts
|
|
2063
|
+
import chalk7 from "chalk";
|
|
2064
|
+
import inquirer4 from "inquirer";
|
|
2065
|
+
var GET_CONFIG_QUERY = (
|
|
2066
|
+
/* GraphQL */
|
|
2067
|
+
`
|
|
2068
|
+
query GetConfigByKey($key: String!) {
|
|
2069
|
+
configByKey(key: $key) {
|
|
2070
|
+
id
|
|
2071
|
+
key
|
|
2072
|
+
name
|
|
2073
|
+
configType
|
|
2074
|
+
}
|
|
2075
|
+
}
|
|
2076
|
+
`
|
|
2077
|
+
);
|
|
2078
|
+
var UNREGISTER_MUTATION = (
|
|
2079
|
+
/* GraphQL */
|
|
2080
|
+
`
|
|
2081
|
+
mutation UnregisterConfig($id: ID!) {
|
|
2082
|
+
unregisterConfig(id: $id)
|
|
2083
|
+
}
|
|
2084
|
+
`
|
|
2085
|
+
);
|
|
2086
|
+
function registerRemoveCommand(program2, globalOpts) {
|
|
2087
|
+
program2.command("remove <key>").description("Remove a config and all its provisioned resources").option("--force", "Skip confirmation prompt", false).action(
|
|
2088
|
+
withErrorHandler(
|
|
2089
|
+
globalOpts,
|
|
2090
|
+
async (key, opts) => {
|
|
2091
|
+
const client = await createClient(globalOpts());
|
|
2092
|
+
const { configByKey: config2 } = await client.request(GET_CONFIG_QUERY, { key });
|
|
2093
|
+
if (!config2) {
|
|
2094
|
+
throw new Error(`Config not found: ${key}`);
|
|
2095
|
+
}
|
|
2096
|
+
if (!opts.force) {
|
|
2097
|
+
const { confirmed } = await inquirer4.prompt([
|
|
2098
|
+
{
|
|
2099
|
+
type: "confirm",
|
|
2100
|
+
name: "confirmed",
|
|
2101
|
+
message: `Remove config "${config2.name}" (${config2.key})? This will delete all its models, operations, hooks, and schedules.`,
|
|
2102
|
+
default: false
|
|
2103
|
+
}
|
|
2104
|
+
]);
|
|
2105
|
+
if (!confirmed) {
|
|
2106
|
+
console.log(chalk7.dim("Cancelled."));
|
|
2107
|
+
return;
|
|
2108
|
+
}
|
|
2109
|
+
}
|
|
2110
|
+
await client.request(UNREGISTER_MUTATION, { id: config2.id });
|
|
2111
|
+
console.log(
|
|
2112
|
+
chalk7.green(`Removed config "${config2.name}" (${config2.key}).`)
|
|
2113
|
+
);
|
|
2114
|
+
}
|
|
2115
|
+
)
|
|
2116
|
+
);
|
|
2117
|
+
}
|
|
2118
|
+
|
|
2119
|
+
// src/commands/profiles.ts
|
|
2120
|
+
import chalk8 from "chalk";
|
|
2121
|
+
|
|
3356
2122
|
// src/lib/input.ts
|
|
2123
|
+
import inquirer5 from "inquirer";
|
|
3357
2124
|
async function parseInputData(opts) {
|
|
3358
2125
|
if (opts.data) {
|
|
3359
2126
|
return JSON.parse(opts.data);
|
|
@@ -3382,7 +2149,7 @@ function isUUID(value) {
|
|
|
3382
2149
|
}
|
|
3383
2150
|
async function confirmAction(message, opts) {
|
|
3384
2151
|
if (opts?.confirm) return true;
|
|
3385
|
-
const { confirmed } = await
|
|
2152
|
+
const { confirmed } = await inquirer5.prompt([
|
|
3386
2153
|
{
|
|
3387
2154
|
type: "confirm",
|
|
3388
2155
|
name: "confirmed",
|
|
@@ -3553,7 +2320,7 @@ function registerProfilesCommand(program2, globalOpts) {
|
|
|
3553
2320
|
if (opts.json || opts.jsonl) {
|
|
3554
2321
|
formatOutput({ deleted: name }, opts);
|
|
3555
2322
|
} else {
|
|
3556
|
-
console.log(
|
|
2323
|
+
console.log(chalk8.green(`Deleted profile "${name}".`));
|
|
3557
2324
|
}
|
|
3558
2325
|
}
|
|
3559
2326
|
)
|
|
@@ -3562,9 +2329,9 @@ function registerProfilesCommand(program2, globalOpts) {
|
|
|
3562
2329
|
|
|
3563
2330
|
// src/commands/register-commands.ts
|
|
3564
2331
|
import { readFileSync, readdirSync } from "fs";
|
|
3565
|
-
import { resolve as resolve5, dirname as
|
|
2332
|
+
import { resolve as resolve5, dirname as dirname4 } from "path";
|
|
3566
2333
|
import { fileURLToPath } from "url";
|
|
3567
|
-
import
|
|
2334
|
+
import chalk9 from "chalk";
|
|
3568
2335
|
|
|
3569
2336
|
// ../command-registry/src/command-map.ts
|
|
3570
2337
|
var COMMANDS = [
|
|
@@ -4190,65 +2957,65 @@ var COMMANDS = [
|
|
|
4190
2957
|
// EXTENSIONS
|
|
4191
2958
|
// =========================================================================
|
|
4192
2959
|
{
|
|
4193
|
-
group: "
|
|
2960
|
+
group: "configs",
|
|
4194
2961
|
name: "list",
|
|
4195
|
-
description: "List
|
|
4196
|
-
operation: "
|
|
2962
|
+
description: "List configs",
|
|
2963
|
+
operation: "configs",
|
|
4197
2964
|
operationType: "query",
|
|
4198
2965
|
columns: [
|
|
4199
2966
|
{ key: "id", header: "ID", width: 28 },
|
|
4200
2967
|
{ key: "key", header: "Key", width: 20 },
|
|
4201
2968
|
{ key: "name", header: "Name", width: 20 },
|
|
4202
|
-
{ key: "
|
|
2969
|
+
{ key: "configType", header: "Type", width: 12 },
|
|
4203
2970
|
{ key: "enabled", header: "Enabled", width: 8, format: "boolean" },
|
|
4204
2971
|
{ key: "syncStatus", header: "Sync", width: 10 }
|
|
4205
2972
|
]
|
|
4206
2973
|
},
|
|
4207
2974
|
{
|
|
4208
|
-
group: "
|
|
2975
|
+
group: "configs",
|
|
4209
2976
|
name: "get",
|
|
4210
|
-
description: "Get
|
|
4211
|
-
operation: "
|
|
2977
|
+
description: "Get a config",
|
|
2978
|
+
operation: "config",
|
|
4212
2979
|
operationType: "query",
|
|
4213
2980
|
positionalArgs: [{ name: "id", graphqlArg: "id" }],
|
|
4214
|
-
alternateGet: { operation: "
|
|
2981
|
+
alternateGet: { operation: "configByKey", argName: "key" }
|
|
4215
2982
|
},
|
|
4216
2983
|
{
|
|
4217
|
-
group: "
|
|
2984
|
+
group: "configs",
|
|
4218
2985
|
name: "register",
|
|
4219
|
-
description: "Register
|
|
4220
|
-
operation: "
|
|
2986
|
+
description: "Register a config",
|
|
2987
|
+
operation: "registerConfig",
|
|
4221
2988
|
operationType: "mutation",
|
|
4222
2989
|
acceptsInput: true,
|
|
4223
|
-
successMessage: "Registered
|
|
2990
|
+
successMessage: "Registered config"
|
|
4224
2991
|
},
|
|
4225
2992
|
{
|
|
4226
|
-
group: "
|
|
4227
|
-
name: "
|
|
4228
|
-
description: "
|
|
4229
|
-
operation: "
|
|
2993
|
+
group: "configs",
|
|
2994
|
+
name: "apply",
|
|
2995
|
+
description: "Apply a config",
|
|
2996
|
+
operation: "applyConfig",
|
|
4230
2997
|
operationType: "mutation",
|
|
4231
2998
|
acceptsInput: true,
|
|
4232
|
-
successMessage: "
|
|
2999
|
+
successMessage: "Applied config"
|
|
4233
3000
|
},
|
|
4234
3001
|
{
|
|
4235
|
-
group: "
|
|
3002
|
+
group: "configs",
|
|
4236
3003
|
name: "uninstall",
|
|
4237
|
-
description: "Unregister
|
|
4238
|
-
operation: "
|
|
3004
|
+
description: "Unregister a config",
|
|
3005
|
+
operation: "unregisterConfig",
|
|
4239
3006
|
operationType: "mutation",
|
|
4240
3007
|
positionalArgs: [{ name: "id", graphqlArg: "id" }],
|
|
4241
3008
|
requiresConfirmation: true,
|
|
4242
3009
|
scalarResult: true,
|
|
4243
|
-
successMessage: "Unregistered
|
|
3010
|
+
successMessage: "Unregistered config"
|
|
4244
3011
|
},
|
|
4245
3012
|
{
|
|
4246
|
-
group: "
|
|
3013
|
+
group: "configs",
|
|
4247
3014
|
name: "sync",
|
|
4248
|
-
description: "Trigger
|
|
4249
|
-
operation: "
|
|
3015
|
+
description: "Trigger config sync",
|
|
3016
|
+
operation: "triggerConfigSync",
|
|
4250
3017
|
operationType: "mutation",
|
|
4251
|
-
positionalArgs: [{ name: "
|
|
3018
|
+
positionalArgs: [{ name: "configId", graphqlArg: "configId" }],
|
|
4252
3019
|
successMessage: "Triggered sync"
|
|
4253
3020
|
},
|
|
4254
3021
|
// =========================================================================
|
|
@@ -5163,7 +3930,7 @@ function createSchemaEngine(sdl) {
|
|
|
5163
3930
|
|
|
5164
3931
|
// src/commands/register-commands.ts
|
|
5165
3932
|
var __filename = fileURLToPath(import.meta.url);
|
|
5166
|
-
var __dirname =
|
|
3933
|
+
var __dirname = dirname4(__filename);
|
|
5167
3934
|
function loadSchemaSDL() {
|
|
5168
3935
|
const bundledPath = resolve5(__dirname, "schema.graphql");
|
|
5169
3936
|
try {
|
|
@@ -5329,7 +4096,7 @@ function registerDynamicCommands(program2, globalOpts) {
|
|
|
5329
4096
|
const files = readdirSync(dirPath).filter((f) => /\.(json|ts|js|mjs)$/.test(f)).sort();
|
|
5330
4097
|
if (files.length === 0) {
|
|
5331
4098
|
console.error(
|
|
5332
|
-
|
|
4099
|
+
chalk9.yellow(`\u26A0 No .json/.ts/.js files found in ${dirPath}`)
|
|
5333
4100
|
);
|
|
5334
4101
|
return;
|
|
5335
4102
|
}
|
|
@@ -5374,19 +4141,19 @@ function registerDynamicCommands(program2, globalOpts) {
|
|
|
5374
4141
|
} catch (updateErr) {
|
|
5375
4142
|
failed++;
|
|
5376
4143
|
const msg2 = updateErr instanceof Error ? updateErr.message : String(updateErr);
|
|
5377
|
-
console.error(
|
|
4144
|
+
console.error(chalk9.red(`\u2717 ${label}:`), msg2);
|
|
5378
4145
|
continue;
|
|
5379
4146
|
}
|
|
5380
4147
|
}
|
|
5381
4148
|
failed++;
|
|
5382
4149
|
const msg = err instanceof Error ? err.message : String(err);
|
|
5383
|
-
console.error(
|
|
4150
|
+
console.error(chalk9.red(`\u2717 ${label}:`), msg);
|
|
5384
4151
|
}
|
|
5385
4152
|
}
|
|
5386
4153
|
if (!(opts.json || opts.jsonl || opts.quiet)) {
|
|
5387
4154
|
console.log("");
|
|
5388
4155
|
console.log(
|
|
5389
|
-
|
|
4156
|
+
chalk9.bold(
|
|
5390
4157
|
`Done: ${created} created${updated ? `, ${updated} updated` : ""}${failed ? `, ${failed} failed` : ""}`
|
|
5391
4158
|
)
|
|
5392
4159
|
);
|
|
@@ -5562,7 +4329,7 @@ function registerDynamicCommands(program2, globalOpts) {
|
|
|
5562
4329
|
}
|
|
5563
4330
|
} else if (!(opts.json || opts.jsonl || opts.quiet)) {
|
|
5564
4331
|
console.error(
|
|
5565
|
-
|
|
4332
|
+
chalk9.yellow(
|
|
5566
4333
|
"\u26A0 Could not auto-publish: no version found in response"
|
|
5567
4334
|
)
|
|
5568
4335
|
);
|
|
@@ -5585,7 +4352,7 @@ function autoColumns(items) {
|
|
|
5585
4352
|
|
|
5586
4353
|
// src/cli.ts
|
|
5587
4354
|
var __filename2 = fileURLToPath2(import.meta.url);
|
|
5588
|
-
var __dirname2 =
|
|
4355
|
+
var __dirname2 = dirname5(__filename2);
|
|
5589
4356
|
config({ path: resolve6(__dirname2, "../.env.local") });
|
|
5590
4357
|
var require2 = createRequire(import.meta.url);
|
|
5591
4358
|
var { version } = require2("../package.json");
|
|
@@ -5608,8 +4375,9 @@ registerWhoamiCommand(program, getGlobalOpts);
|
|
|
5608
4375
|
registerProfilesCommand(program, getGlobalOpts);
|
|
5609
4376
|
registerMediaCommands(program, getGlobalOpts);
|
|
5610
4377
|
registerSearchCommands(program, getGlobalOpts);
|
|
5611
|
-
|
|
5612
|
-
|
|
4378
|
+
registerPushCommand(program, getGlobalOpts);
|
|
4379
|
+
registerRemoveCommand(program, getGlobalOpts);
|
|
4380
|
+
registerCreateConfigCommand(program, getGlobalOpts);
|
|
5613
4381
|
registerInitCommands(program, getGlobalOpts);
|
|
5614
4382
|
registerDynamicCommands(program, getGlobalOpts);
|
|
5615
4383
|
program.parse();
|