@eide/foir-cli 0.1.37 → 0.1.39
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 +514 -33
- package/dist/schema.graphql +70 -3
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
// src/cli.ts
|
|
4
4
|
import { config } from "dotenv";
|
|
5
|
-
import { resolve as
|
|
5
|
+
import { resolve as resolve7, dirname as dirname6 } from "path";
|
|
6
6
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
7
7
|
import { createRequire } from "module";
|
|
8
8
|
import { Command } from "commander";
|
|
@@ -127,6 +127,22 @@ function getGraphQLEndpoint(apiUrl) {
|
|
|
127
127
|
|
|
128
128
|
// src/lib/errors.ts
|
|
129
129
|
import chalk from "chalk";
|
|
130
|
+
function extractErrorMessage(error) {
|
|
131
|
+
if (!error || typeof error !== "object")
|
|
132
|
+
return String(error ?? "Unknown error");
|
|
133
|
+
const err = error;
|
|
134
|
+
const gqlErrors = err.response?.errors;
|
|
135
|
+
if (gqlErrors && gqlErrors.length > 0) {
|
|
136
|
+
return gqlErrors[0].message;
|
|
137
|
+
}
|
|
138
|
+
if (err.message && err.message !== "undefined") {
|
|
139
|
+
return err.message;
|
|
140
|
+
}
|
|
141
|
+
if (error instanceof Error && error.message) {
|
|
142
|
+
return error.message;
|
|
143
|
+
}
|
|
144
|
+
return "An unknown error occurred. Use --json for details.";
|
|
145
|
+
}
|
|
130
146
|
function withErrorHandler(optsFn, fn) {
|
|
131
147
|
return async (...args) => {
|
|
132
148
|
try {
|
|
@@ -145,12 +161,18 @@ function withErrorHandler(optsFn, fn) {
|
|
|
145
161
|
error: {
|
|
146
162
|
message: first.message,
|
|
147
163
|
code: code ?? "GRAPHQL_ERROR",
|
|
148
|
-
...validationErrors ? { validationErrors } : {}
|
|
164
|
+
...validationErrors ? { validationErrors } : {},
|
|
165
|
+
...gqlErrors.length > 1 ? {
|
|
166
|
+
additionalErrors: gqlErrors.slice(1).map((e) => e.message)
|
|
167
|
+
} : {}
|
|
149
168
|
}
|
|
150
169
|
})
|
|
151
170
|
);
|
|
152
171
|
} else {
|
|
153
172
|
console.error(chalk.red("Error:"), first.message);
|
|
173
|
+
for (const extra of gqlErrors.slice(1)) {
|
|
174
|
+
console.error(chalk.red(" \u2022"), extra.message);
|
|
175
|
+
}
|
|
154
176
|
if (validationErrors && Object.keys(validationErrors).length > 0) {
|
|
155
177
|
console.error("");
|
|
156
178
|
console.error(chalk.yellow("Field errors:"));
|
|
@@ -162,12 +184,12 @@ function withErrorHandler(optsFn, fn) {
|
|
|
162
184
|
}
|
|
163
185
|
if (code === "UNAUTHENTICATED") {
|
|
164
186
|
console.error(
|
|
165
|
-
chalk.gray("
|
|
187
|
+
chalk.gray("\nHint: Run `foir login` to authenticate.")
|
|
166
188
|
);
|
|
167
189
|
} else if (code === "FORBIDDEN") {
|
|
168
190
|
console.error(
|
|
169
191
|
chalk.gray(
|
|
170
|
-
"
|
|
192
|
+
"\nHint: You may not have permission. Check your API key scopes."
|
|
171
193
|
)
|
|
172
194
|
);
|
|
173
195
|
}
|
|
@@ -190,7 +212,7 @@ function withErrorHandler(optsFn, fn) {
|
|
|
190
212
|
}
|
|
191
213
|
process.exit(1);
|
|
192
214
|
}
|
|
193
|
-
const message =
|
|
215
|
+
const message = extractErrorMessage(error);
|
|
194
216
|
if (opts?.json || opts?.jsonl) {
|
|
195
217
|
console.error(JSON.stringify({ error: { message } }));
|
|
196
218
|
} else {
|
|
@@ -204,13 +226,13 @@ function withErrorHandler(optsFn, fn) {
|
|
|
204
226
|
// src/commands/login.ts
|
|
205
227
|
async function findAvailablePort(start, end) {
|
|
206
228
|
for (let port = start; port <= end; port++) {
|
|
207
|
-
const available = await new Promise((
|
|
229
|
+
const available = await new Promise((resolve8) => {
|
|
208
230
|
const server = http.createServer();
|
|
209
231
|
server.listen(port, () => {
|
|
210
232
|
server.close();
|
|
211
|
-
|
|
233
|
+
resolve8(true);
|
|
212
234
|
});
|
|
213
|
-
server.on("error", () =>
|
|
235
|
+
server.on("error", () => resolve8(false));
|
|
214
236
|
});
|
|
215
237
|
if (available) return port;
|
|
216
238
|
}
|
|
@@ -248,7 +270,7 @@ async function loginAction(globalOpts) {
|
|
|
248
270
|
const state = crypto.randomBytes(16).toString("hex");
|
|
249
271
|
const port = await findAvailablePort(9876, 9900);
|
|
250
272
|
const redirectUri = `http://localhost:${port}/callback`;
|
|
251
|
-
const authCode = await new Promise((
|
|
273
|
+
const authCode = await new Promise((resolve8, reject) => {
|
|
252
274
|
const server = http.createServer((req, res) => {
|
|
253
275
|
const url = new URL(req.url, `http://localhost:${port}`);
|
|
254
276
|
if (url.pathname === "/callback") {
|
|
@@ -281,7 +303,7 @@ async function loginAction(globalOpts) {
|
|
|
281
303
|
`<html><head><meta http-equiv="refresh" content="2;url=${mainUrl}"></head><body style="font-family:system-ui;text-align:center;padding:50px"><h1>Authentication successful!</h1><p>You can close this window.</p></body></html>`
|
|
282
304
|
);
|
|
283
305
|
server.close();
|
|
284
|
-
|
|
306
|
+
resolve8(code);
|
|
285
307
|
}
|
|
286
308
|
});
|
|
287
309
|
server.listen(port);
|
|
@@ -604,8 +626,15 @@ function timeAgo(dateStr) {
|
|
|
604
626
|
if (days < 30) return `${days}d ago`;
|
|
605
627
|
return date.toLocaleDateString();
|
|
606
628
|
}
|
|
607
|
-
function success(message) {
|
|
608
|
-
|
|
629
|
+
function success(message, data) {
|
|
630
|
+
let interpolated = message;
|
|
631
|
+
if (data) {
|
|
632
|
+
interpolated = message.replace(
|
|
633
|
+
/\{(\w+)\}/g,
|
|
634
|
+
(_, field) => String(data[field] ?? `{${field}}`)
|
|
635
|
+
);
|
|
636
|
+
}
|
|
637
|
+
console.log(chalk2.green(`\u2713 ${interpolated}`));
|
|
609
638
|
}
|
|
610
639
|
|
|
611
640
|
// src/commands/whoami.ts
|
|
@@ -5617,10 +5646,215 @@ Media (${data.globalSearch.media.length}):`);
|
|
|
5617
5646
|
);
|
|
5618
5647
|
}
|
|
5619
5648
|
|
|
5649
|
+
// src/commands/init.ts
|
|
5650
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync2 } from "fs";
|
|
5651
|
+
import { writeFile as writeFile2 } from "fs/promises";
|
|
5652
|
+
import { resolve as resolve4, join as join4 } from "path";
|
|
5653
|
+
import chalk6 from "chalk";
|
|
5654
|
+
import inquirer3 from "inquirer";
|
|
5655
|
+
var FIELD_DEFAULTS = {
|
|
5656
|
+
text: "",
|
|
5657
|
+
richtext: "",
|
|
5658
|
+
number: 0,
|
|
5659
|
+
boolean: false,
|
|
5660
|
+
date: (/* @__PURE__ */ new Date()).toISOString().split("T")[0],
|
|
5661
|
+
select: "",
|
|
5662
|
+
image: null,
|
|
5663
|
+
video: null,
|
|
5664
|
+
file: null,
|
|
5665
|
+
json: {},
|
|
5666
|
+
link: { url: "", label: "" },
|
|
5667
|
+
relationship: null,
|
|
5668
|
+
list: []
|
|
5669
|
+
};
|
|
5670
|
+
function defaultValueForField(field) {
|
|
5671
|
+
return FIELD_DEFAULTS[field.type] ?? null;
|
|
5672
|
+
}
|
|
5673
|
+
function generateModelTemplate(key) {
|
|
5674
|
+
return {
|
|
5675
|
+
key,
|
|
5676
|
+
name: key.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" "),
|
|
5677
|
+
fields: [
|
|
5678
|
+
{
|
|
5679
|
+
key: "title",
|
|
5680
|
+
type: "text",
|
|
5681
|
+
label: "Title",
|
|
5682
|
+
required: true
|
|
5683
|
+
},
|
|
5684
|
+
{
|
|
5685
|
+
key: "slug",
|
|
5686
|
+
type: "text",
|
|
5687
|
+
label: "Slug",
|
|
5688
|
+
required: true
|
|
5689
|
+
},
|
|
5690
|
+
{
|
|
5691
|
+
key: "description",
|
|
5692
|
+
type: "richtext",
|
|
5693
|
+
label: "Description"
|
|
5694
|
+
},
|
|
5695
|
+
{
|
|
5696
|
+
key: "image",
|
|
5697
|
+
type: "image",
|
|
5698
|
+
label: "Featured Image"
|
|
5699
|
+
}
|
|
5700
|
+
],
|
|
5701
|
+
config: {
|
|
5702
|
+
versioning: true,
|
|
5703
|
+
publishing: true,
|
|
5704
|
+
variants: false
|
|
5705
|
+
}
|
|
5706
|
+
};
|
|
5707
|
+
}
|
|
5708
|
+
function generateRecordSeed(model) {
|
|
5709
|
+
const data = {};
|
|
5710
|
+
for (const field of model.fields) {
|
|
5711
|
+
if (["id", "createdAt", "updatedAt", "createdBy", "updatedBy"].includes(
|
|
5712
|
+
field.key
|
|
5713
|
+
)) {
|
|
5714
|
+
continue;
|
|
5715
|
+
}
|
|
5716
|
+
if (field.type === "list" && field.items) {
|
|
5717
|
+
data[field.key] = [
|
|
5718
|
+
defaultValueForField({ key: field.key, type: field.items.type })
|
|
5719
|
+
];
|
|
5720
|
+
} else {
|
|
5721
|
+
data[field.key] = defaultValueForField(field);
|
|
5722
|
+
}
|
|
5723
|
+
}
|
|
5724
|
+
return {
|
|
5725
|
+
modelKey: model.key,
|
|
5726
|
+
naturalKey: `example-${model.key}`,
|
|
5727
|
+
data
|
|
5728
|
+
};
|
|
5729
|
+
}
|
|
5730
|
+
function formatAsTypeScript(obj) {
|
|
5731
|
+
const json = JSON.stringify(obj, null, 2);
|
|
5732
|
+
return `export default ${json} as const;
|
|
5733
|
+
`;
|
|
5734
|
+
}
|
|
5735
|
+
function registerInitCommands(program2, globalOpts) {
|
|
5736
|
+
const initGroup = program2.command("init").description("Generate starter config and seed files");
|
|
5737
|
+
initGroup.command("model").description("Generate a starter model definition file").argument("<key>", "Model key (e.g. blog-post)").option("-o, --output <dir>", "Output directory", "models").option("--ts", "Generate TypeScript (.ts) instead of JSON").action(
|
|
5738
|
+
withErrorHandler(
|
|
5739
|
+
globalOpts,
|
|
5740
|
+
async (key, opts) => {
|
|
5741
|
+
const globalFlags = globalOpts();
|
|
5742
|
+
const template = generateModelTemplate(key);
|
|
5743
|
+
const outDir = resolve4(opts.output);
|
|
5744
|
+
if (!existsSync4(outDir)) {
|
|
5745
|
+
mkdirSync2(outDir, { recursive: true });
|
|
5746
|
+
}
|
|
5747
|
+
const ext = opts.ts ? "ts" : "json";
|
|
5748
|
+
const filePath = join4(outDir, `${key}.${ext}`);
|
|
5749
|
+
const content = opts.ts ? formatAsTypeScript(template) : JSON.stringify(template, null, 2) + "\n";
|
|
5750
|
+
await writeFile2(filePath, content, "utf-8");
|
|
5751
|
+
if (!(globalFlags.json || globalFlags.jsonl || globalFlags.quiet)) {
|
|
5752
|
+
success(`Created ${filePath}`);
|
|
5753
|
+
console.log(
|
|
5754
|
+
chalk6.gray(
|
|
5755
|
+
`
|
|
5756
|
+
Edit the file, then run:
|
|
5757
|
+
foir models create -f ${filePath}`
|
|
5758
|
+
)
|
|
5759
|
+
);
|
|
5760
|
+
} else {
|
|
5761
|
+
console.log(JSON.stringify({ path: filePath }));
|
|
5762
|
+
}
|
|
5763
|
+
}
|
|
5764
|
+
)
|
|
5765
|
+
);
|
|
5766
|
+
initGroup.command("records").description("Generate seed files for records (interactive model selector)").option("-o, --output <dir>", "Output directory", "seed").option("--ts", "Generate TypeScript (.ts) instead of JSON").option(
|
|
5767
|
+
"--model <keys...>",
|
|
5768
|
+
"Model keys to generate for (skip interactive selector)"
|
|
5769
|
+
).action(
|
|
5770
|
+
withErrorHandler(
|
|
5771
|
+
globalOpts,
|
|
5772
|
+
async (opts) => {
|
|
5773
|
+
const globalFlags = globalOpts();
|
|
5774
|
+
const client = await createClient(globalFlags);
|
|
5775
|
+
const query = `query { models(limit: 100) { items { key name fields } total } }`;
|
|
5776
|
+
const result = await client.request(query);
|
|
5777
|
+
const models = result.models.items;
|
|
5778
|
+
if (models.length === 0) {
|
|
5779
|
+
console.log(
|
|
5780
|
+
chalk6.yellow(
|
|
5781
|
+
"No models found. Create models first with `foir models create`."
|
|
5782
|
+
)
|
|
5783
|
+
);
|
|
5784
|
+
return;
|
|
5785
|
+
}
|
|
5786
|
+
let selectedModels;
|
|
5787
|
+
if (opts.model && opts.model.length > 0) {
|
|
5788
|
+
selectedModels = [];
|
|
5789
|
+
for (const key of opts.model) {
|
|
5790
|
+
const found = models.find((m) => m.key === key);
|
|
5791
|
+
if (!found) {
|
|
5792
|
+
console.error(
|
|
5793
|
+
chalk6.red(`Model "${key}" not found.`),
|
|
5794
|
+
"Available:",
|
|
5795
|
+
models.map((m) => m.key).join(", ")
|
|
5796
|
+
);
|
|
5797
|
+
return;
|
|
5798
|
+
}
|
|
5799
|
+
selectedModels.push(found);
|
|
5800
|
+
}
|
|
5801
|
+
} else {
|
|
5802
|
+
const { selected } = await inquirer3.prompt([
|
|
5803
|
+
{
|
|
5804
|
+
type: "checkbox",
|
|
5805
|
+
name: "selected",
|
|
5806
|
+
message: "Select models to generate seed files for:",
|
|
5807
|
+
choices: models.map((m) => ({
|
|
5808
|
+
name: `${m.name} (${m.key})`,
|
|
5809
|
+
value: m.key,
|
|
5810
|
+
short: m.key
|
|
5811
|
+
})),
|
|
5812
|
+
validate: (answer) => answer.length > 0 ? true : "Select at least one model."
|
|
5813
|
+
}
|
|
5814
|
+
]);
|
|
5815
|
+
selectedModels = selected.map((key) => models.find((m) => m.key === key)).filter(Boolean);
|
|
5816
|
+
}
|
|
5817
|
+
if (selectedModels.length === 0) {
|
|
5818
|
+
console.log("No models selected.");
|
|
5819
|
+
return;
|
|
5820
|
+
}
|
|
5821
|
+
const outDir = resolve4(opts.output);
|
|
5822
|
+
if (!existsSync4(outDir)) {
|
|
5823
|
+
mkdirSync2(outDir, { recursive: true });
|
|
5824
|
+
}
|
|
5825
|
+
const createdFiles = [];
|
|
5826
|
+
for (const model of selectedModels) {
|
|
5827
|
+
const seed = generateRecordSeed(model);
|
|
5828
|
+
const ext = opts.ts ? "ts" : "json";
|
|
5829
|
+
const filePath = join4(outDir, `${model.key}.${ext}`);
|
|
5830
|
+
const content = opts.ts ? formatAsTypeScript(seed) : JSON.stringify(seed, null, 2) + "\n";
|
|
5831
|
+
await writeFile2(filePath, content, "utf-8");
|
|
5832
|
+
createdFiles.push(filePath);
|
|
5833
|
+
if (!(globalFlags.json || globalFlags.jsonl || globalFlags.quiet)) {
|
|
5834
|
+
success(`Created ${filePath}`);
|
|
5835
|
+
}
|
|
5836
|
+
}
|
|
5837
|
+
if (!(globalFlags.json || globalFlags.jsonl || globalFlags.quiet)) {
|
|
5838
|
+
console.log(
|
|
5839
|
+
chalk6.gray(
|
|
5840
|
+
`
|
|
5841
|
+
Edit the files, then run:
|
|
5842
|
+
foir records create --dir ${outDir} --publish`
|
|
5843
|
+
)
|
|
5844
|
+
);
|
|
5845
|
+
} else {
|
|
5846
|
+
console.log(JSON.stringify({ files: createdFiles }));
|
|
5847
|
+
}
|
|
5848
|
+
}
|
|
5849
|
+
)
|
|
5850
|
+
);
|
|
5851
|
+
}
|
|
5852
|
+
|
|
5620
5853
|
// src/commands/register-commands.ts
|
|
5621
|
-
import { readFileSync } from "fs";
|
|
5622
|
-
import { resolve as
|
|
5854
|
+
import { readFileSync, readdirSync } from "fs";
|
|
5855
|
+
import { resolve as resolve6, dirname as dirname5 } from "path";
|
|
5623
5856
|
import { fileURLToPath } from "url";
|
|
5857
|
+
import chalk7 from "chalk";
|
|
5624
5858
|
|
|
5625
5859
|
// ../command-registry/src/command-map.ts
|
|
5626
5860
|
var COMMANDS = [
|
|
@@ -5655,7 +5889,17 @@ var COMMANDS = [
|
|
|
5655
5889
|
operation: "createModel",
|
|
5656
5890
|
operationType: "mutation",
|
|
5657
5891
|
acceptsInput: true,
|
|
5658
|
-
successMessage: "Created model {key}"
|
|
5892
|
+
successMessage: "Created model {key}",
|
|
5893
|
+
customFlags: [
|
|
5894
|
+
{
|
|
5895
|
+
flag: "--dir <path>",
|
|
5896
|
+
description: "Create models from all files in directory"
|
|
5897
|
+
},
|
|
5898
|
+
{
|
|
5899
|
+
flag: "--upsert",
|
|
5900
|
+
description: "Update if model key already exists"
|
|
5901
|
+
}
|
|
5902
|
+
]
|
|
5659
5903
|
},
|
|
5660
5904
|
{
|
|
5661
5905
|
group: "models",
|
|
@@ -5725,7 +5969,10 @@ var COMMANDS = [
|
|
|
5725
5969
|
operation: "createRecord",
|
|
5726
5970
|
operationType: "mutation",
|
|
5727
5971
|
acceptsInput: true,
|
|
5728
|
-
successMessage: "Created record"
|
|
5972
|
+
successMessage: "Created record",
|
|
5973
|
+
customFlags: [
|
|
5974
|
+
{ flag: "--publish", description: "Publish the record after creation" }
|
|
5975
|
+
]
|
|
5729
5976
|
},
|
|
5730
5977
|
{
|
|
5731
5978
|
group: "records",
|
|
@@ -5752,9 +5999,21 @@ var COMMANDS = [
|
|
|
5752
5999
|
description: "Publish a record version",
|
|
5753
6000
|
operation: "publishVersion",
|
|
5754
6001
|
operationType: "mutation",
|
|
5755
|
-
positionalArgs: [
|
|
6002
|
+
positionalArgs: [
|
|
6003
|
+
{
|
|
6004
|
+
name: "versionId",
|
|
6005
|
+
graphqlArg: "versionId",
|
|
6006
|
+
description: "Version ID, record ID, or natural key (with --model)"
|
|
6007
|
+
}
|
|
6008
|
+
],
|
|
5756
6009
|
scalarResult: true,
|
|
5757
|
-
successMessage: "Published version"
|
|
6010
|
+
successMessage: "Published version",
|
|
6011
|
+
customFlags: [
|
|
6012
|
+
{
|
|
6013
|
+
flag: "--model <key>",
|
|
6014
|
+
description: "Model key (use with natural key instead of version ID)"
|
|
6015
|
+
}
|
|
6016
|
+
]
|
|
5758
6017
|
},
|
|
5759
6018
|
{
|
|
5760
6019
|
group: "records",
|
|
@@ -6999,6 +7258,7 @@ var COMMANDS = [
|
|
|
6999
7258
|
import {
|
|
7000
7259
|
buildSchema,
|
|
7001
7260
|
isObjectType,
|
|
7261
|
+
isInputObjectType,
|
|
7002
7262
|
isListType,
|
|
7003
7263
|
isNonNullType,
|
|
7004
7264
|
isScalarType,
|
|
@@ -7156,6 +7416,21 @@ function createSchemaEngine(sdl) {
|
|
|
7156
7416
|
const argPart = fieldArgs ? `(${fieldArgs})` : "";
|
|
7157
7417
|
return `${opType} ${opName}${varPart} { ${entry.operation}${argPart} ${selectionSet} }`;
|
|
7158
7418
|
}
|
|
7419
|
+
function getInputFields(operationName, operationType, inputArgName) {
|
|
7420
|
+
const field = getField(operationName, operationType);
|
|
7421
|
+
if (!field) return [];
|
|
7422
|
+
const argName = inputArgName ?? "input";
|
|
7423
|
+
const arg = field.args.find((a) => a.name === argName);
|
|
7424
|
+
if (!arg) return [];
|
|
7425
|
+
const namedType = unwrapType(arg.type);
|
|
7426
|
+
if (!isInputObjectType(namedType)) return [];
|
|
7427
|
+
const fields = namedType.getFields();
|
|
7428
|
+
return Object.entries(fields).map(([name, f]) => ({
|
|
7429
|
+
name,
|
|
7430
|
+
type: typeToString(f.type),
|
|
7431
|
+
required: isNonNullType(f.type)
|
|
7432
|
+
}));
|
|
7433
|
+
}
|
|
7159
7434
|
function getCompletions(partial, commandNames) {
|
|
7160
7435
|
return commandNames.filter(
|
|
7161
7436
|
(name) => name.toLowerCase().startsWith(partial.toLowerCase())
|
|
@@ -7165,19 +7440,20 @@ function createSchemaEngine(sdl) {
|
|
|
7165
7440
|
buildQuery,
|
|
7166
7441
|
getOperationArgs,
|
|
7167
7442
|
coerceArgs,
|
|
7443
|
+
getInputFields,
|
|
7168
7444
|
getCompletions
|
|
7169
7445
|
};
|
|
7170
7446
|
}
|
|
7171
7447
|
|
|
7172
7448
|
// src/lib/input.ts
|
|
7173
|
-
import
|
|
7449
|
+
import inquirer4 from "inquirer";
|
|
7174
7450
|
|
|
7175
7451
|
// src/lib/config-loader.ts
|
|
7176
7452
|
import { readFile } from "fs/promises";
|
|
7177
7453
|
import { pathToFileURL as pathToFileURL2 } from "url";
|
|
7178
|
-
import { resolve as
|
|
7454
|
+
import { resolve as resolve5 } from "path";
|
|
7179
7455
|
async function loadConfig(filePath) {
|
|
7180
|
-
const absPath =
|
|
7456
|
+
const absPath = resolve5(filePath);
|
|
7181
7457
|
if (filePath.endsWith(".ts")) {
|
|
7182
7458
|
const configModule = await import(pathToFileURL2(absPath).href);
|
|
7183
7459
|
return configModule.default;
|
|
@@ -7224,7 +7500,7 @@ function isUUID(value) {
|
|
|
7224
7500
|
}
|
|
7225
7501
|
async function confirmAction(message, opts) {
|
|
7226
7502
|
if (opts?.confirm) return true;
|
|
7227
|
-
const { confirmed } = await
|
|
7503
|
+
const { confirmed } = await inquirer4.prompt([
|
|
7228
7504
|
{
|
|
7229
7505
|
type: "confirm",
|
|
7230
7506
|
name: "confirmed",
|
|
@@ -7239,11 +7515,11 @@ async function confirmAction(message, opts) {
|
|
|
7239
7515
|
var __filename = fileURLToPath(import.meta.url);
|
|
7240
7516
|
var __dirname = dirname5(__filename);
|
|
7241
7517
|
function loadSchemaSDL() {
|
|
7242
|
-
const bundledPath =
|
|
7518
|
+
const bundledPath = resolve6(__dirname, "schema.graphql");
|
|
7243
7519
|
try {
|
|
7244
7520
|
return readFileSync(bundledPath, "utf-8");
|
|
7245
7521
|
} catch {
|
|
7246
|
-
const monorepoPath =
|
|
7522
|
+
const monorepoPath = resolve6(
|
|
7247
7523
|
__dirname,
|
|
7248
7524
|
"../../../graphql-core/schema.graphql"
|
|
7249
7525
|
);
|
|
@@ -7343,6 +7619,26 @@ function registerDynamicCommands(program2, globalOpts) {
|
|
|
7343
7619
|
if (entry.acceptsInput) {
|
|
7344
7620
|
cmd = cmd.option("-d, --data <json>", "Data as JSON");
|
|
7345
7621
|
cmd = cmd.option("-f, --file <path>", "Read data from file");
|
|
7622
|
+
const inputFields = engine.getInputFields(
|
|
7623
|
+
entry.operation,
|
|
7624
|
+
entry.operationType,
|
|
7625
|
+
entry.inputArgName
|
|
7626
|
+
);
|
|
7627
|
+
if (inputFields.length > 0) {
|
|
7628
|
+
const required = inputFields.filter((f) => f.required);
|
|
7629
|
+
const optional = inputFields.filter((f) => !f.required);
|
|
7630
|
+
let fieldHelp = "\nInput fields:";
|
|
7631
|
+
if (required.length > 0) {
|
|
7632
|
+
fieldHelp += "\n Required: " + required.map((f) => `${f.name} (${f.type})`).join(", ");
|
|
7633
|
+
}
|
|
7634
|
+
if (optional.length > 0) {
|
|
7635
|
+
fieldHelp += "\n Optional: " + optional.map((f) => `${f.name} (${f.type})`).join(", ");
|
|
7636
|
+
}
|
|
7637
|
+
cmd = cmd.addHelpText("after", fieldHelp);
|
|
7638
|
+
}
|
|
7639
|
+
}
|
|
7640
|
+
for (const cf of entry.customFlags ?? []) {
|
|
7641
|
+
cmd = cmd.option(cf.flag, cf.description);
|
|
7346
7642
|
}
|
|
7347
7643
|
if (entry.requiresConfirmation) {
|
|
7348
7644
|
cmd = cmd.option("--confirm", "Skip confirmation prompt");
|
|
@@ -7361,9 +7657,15 @@ function registerDynamicCommands(program2, globalOpts) {
|
|
|
7361
7657
|
}
|
|
7362
7658
|
}
|
|
7363
7659
|
const flags = actionArgs[positionals.length] ?? {};
|
|
7660
|
+
const customFlagNames = new Set(
|
|
7661
|
+
(entry.customFlags ?? []).map(
|
|
7662
|
+
(cf) => cf.flag.replace(/ <.*>$/, "").replace(/^--/, "").replace(/-([a-z])/g, (_, c) => c.toUpperCase())
|
|
7663
|
+
)
|
|
7664
|
+
);
|
|
7364
7665
|
const rawFlags = {};
|
|
7365
7666
|
for (const [key, val] of Object.entries(flags)) {
|
|
7366
7667
|
if (key === "data" || key === "file" || key === "confirm") continue;
|
|
7668
|
+
if (customFlagNames.has(key)) continue;
|
|
7367
7669
|
rawFlags[key] = String(val);
|
|
7368
7670
|
}
|
|
7369
7671
|
const coerced = engine.coerceArgs(
|
|
@@ -7372,11 +7674,97 @@ function registerDynamicCommands(program2, globalOpts) {
|
|
|
7372
7674
|
rawFlags
|
|
7373
7675
|
);
|
|
7374
7676
|
Object.assign(variables, coerced);
|
|
7677
|
+
if (flags.dir && entry.acceptsInput) {
|
|
7678
|
+
const dirPath = resolve6(String(flags.dir));
|
|
7679
|
+
const files = readdirSync(dirPath).filter((f) => /\.(json|ts|js|mjs)$/.test(f)).sort();
|
|
7680
|
+
if (files.length === 0) {
|
|
7681
|
+
console.error(
|
|
7682
|
+
chalk7.yellow(`\u26A0 No .json/.ts/.js files found in ${dirPath}`)
|
|
7683
|
+
);
|
|
7684
|
+
return;
|
|
7685
|
+
}
|
|
7686
|
+
let created = 0;
|
|
7687
|
+
let updated = 0;
|
|
7688
|
+
let failed = 0;
|
|
7689
|
+
for (const file of files) {
|
|
7690
|
+
const filePath = resolve6(dirPath, file);
|
|
7691
|
+
const fileData = await parseInputData({ file: filePath });
|
|
7692
|
+
const argName = entry.inputArgName ?? "input";
|
|
7693
|
+
const fileVars = { ...variables, [argName]: fileData };
|
|
7694
|
+
const label = fileData.key ?? fileData.name ?? file;
|
|
7695
|
+
try {
|
|
7696
|
+
const q = engine.buildQuery(entry, fileVars);
|
|
7697
|
+
await client.request(q, fileVars);
|
|
7698
|
+
created++;
|
|
7699
|
+
if (!(opts.json || opts.jsonl || opts.quiet)) {
|
|
7700
|
+
success(`Created ${label}`);
|
|
7701
|
+
}
|
|
7702
|
+
} catch (err) {
|
|
7703
|
+
if (flags.upsert && fileData.key) {
|
|
7704
|
+
try {
|
|
7705
|
+
const updateEntry = COMMANDS.find(
|
|
7706
|
+
(c) => c.group === entry.group && c.name === "update"
|
|
7707
|
+
);
|
|
7708
|
+
if (updateEntry) {
|
|
7709
|
+
const updateVars = {
|
|
7710
|
+
...variables,
|
|
7711
|
+
[updateEntry.inputArgName ?? "input"]: fileData,
|
|
7712
|
+
...updateEntry.positionalArgs?.[0] ? {
|
|
7713
|
+
[updateEntry.positionalArgs[0].graphqlArg]: fileData.key
|
|
7714
|
+
} : {}
|
|
7715
|
+
};
|
|
7716
|
+
const uq = engine.buildQuery(updateEntry, updateVars);
|
|
7717
|
+
await client.request(uq, updateVars);
|
|
7718
|
+
updated++;
|
|
7719
|
+
if (!(opts.json || opts.jsonl || opts.quiet)) {
|
|
7720
|
+
success(`Updated ${label}`);
|
|
7721
|
+
}
|
|
7722
|
+
continue;
|
|
7723
|
+
}
|
|
7724
|
+
} catch (updateErr) {
|
|
7725
|
+
failed++;
|
|
7726
|
+
const msg2 = updateErr instanceof Error ? updateErr.message : String(updateErr);
|
|
7727
|
+
console.error(chalk7.red(`\u2717 ${label}:`), msg2);
|
|
7728
|
+
continue;
|
|
7729
|
+
}
|
|
7730
|
+
}
|
|
7731
|
+
failed++;
|
|
7732
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
7733
|
+
console.error(chalk7.red(`\u2717 ${label}:`), msg);
|
|
7734
|
+
}
|
|
7735
|
+
}
|
|
7736
|
+
if (!(opts.json || opts.jsonl || opts.quiet)) {
|
|
7737
|
+
console.log("");
|
|
7738
|
+
console.log(
|
|
7739
|
+
chalk7.bold(
|
|
7740
|
+
`Done: ${created} created${updated ? `, ${updated} updated` : ""}${failed ? `, ${failed} failed` : ""}`
|
|
7741
|
+
)
|
|
7742
|
+
);
|
|
7743
|
+
}
|
|
7744
|
+
return;
|
|
7745
|
+
}
|
|
7375
7746
|
if (entry.acceptsInput && (flags.data || flags.file)) {
|
|
7376
7747
|
const inputData = await parseInputData({
|
|
7377
7748
|
data: flags.data,
|
|
7378
7749
|
file: flags.file
|
|
7379
7750
|
});
|
|
7751
|
+
const inputFields = engine.getInputFields(
|
|
7752
|
+
entry.operation,
|
|
7753
|
+
entry.operationType,
|
|
7754
|
+
entry.inputArgName
|
|
7755
|
+
);
|
|
7756
|
+
const fieldNames = new Set(inputFields.map((f) => f.name));
|
|
7757
|
+
if (fieldNames.has("projectId") && !inputData.projectId || fieldNames.has("tenantId") && !inputData.tenantId) {
|
|
7758
|
+
const project = await getProjectContext();
|
|
7759
|
+
if (project) {
|
|
7760
|
+
if (fieldNames.has("projectId") && !inputData.projectId) {
|
|
7761
|
+
inputData.projectId = project.id;
|
|
7762
|
+
}
|
|
7763
|
+
if (fieldNames.has("tenantId") && !inputData.tenantId) {
|
|
7764
|
+
inputData.tenantId = project.tenantId;
|
|
7765
|
+
}
|
|
7766
|
+
}
|
|
7767
|
+
}
|
|
7380
7768
|
const argName = entry.inputArgName ?? "input";
|
|
7381
7769
|
variables[argName] = inputData;
|
|
7382
7770
|
}
|
|
@@ -7414,27 +7802,119 @@ function registerDynamicCommands(program2, globalOpts) {
|
|
|
7414
7802
|
return;
|
|
7415
7803
|
}
|
|
7416
7804
|
}
|
|
7805
|
+
if (entry.group === "records" && entry.name === "publish" && variables.versionId) {
|
|
7806
|
+
const versionIdValue = String(variables.versionId);
|
|
7807
|
+
if (flags.model) {
|
|
7808
|
+
const lookupQuery = `query RecordByKey($naturalKey: String!) { recordByKey(naturalKey: $naturalKey) { id currentVersion { id } } }`;
|
|
7809
|
+
const lookupResult = await client.request(lookupQuery, {
|
|
7810
|
+
naturalKey: versionIdValue
|
|
7811
|
+
});
|
|
7812
|
+
const record = lookupResult.recordByKey;
|
|
7813
|
+
const currentVersion = record?.currentVersion;
|
|
7814
|
+
if (!currentVersion?.id) {
|
|
7815
|
+
throw new Error(
|
|
7816
|
+
`No current version found for record "${versionIdValue}"`
|
|
7817
|
+
);
|
|
7818
|
+
}
|
|
7819
|
+
variables.versionId = currentVersion.id;
|
|
7820
|
+
} else if (!isUUID(versionIdValue)) {
|
|
7821
|
+
const lookupQuery = `query RecordByKey($naturalKey: String!) { recordByKey(naturalKey: $naturalKey) { id currentVersion { id } } }`;
|
|
7822
|
+
const lookupResult = await client.request(lookupQuery, {
|
|
7823
|
+
naturalKey: versionIdValue
|
|
7824
|
+
});
|
|
7825
|
+
const record = lookupResult.recordByKey;
|
|
7826
|
+
const currentVersion = record?.currentVersion;
|
|
7827
|
+
if (currentVersion?.id) {
|
|
7828
|
+
variables.versionId = currentVersion.id;
|
|
7829
|
+
}
|
|
7830
|
+
} else {
|
|
7831
|
+
try {
|
|
7832
|
+
const lookupQuery = `query Record($id: ID!) { record(id: $id) { id recordType currentVersion { id } } }`;
|
|
7833
|
+
const lookupResult = await client.request(lookupQuery, {
|
|
7834
|
+
id: versionIdValue
|
|
7835
|
+
});
|
|
7836
|
+
const record = lookupResult.record;
|
|
7837
|
+
if (record?.recordType === "record" && record?.currentVersion) {
|
|
7838
|
+
const cv = record.currentVersion;
|
|
7839
|
+
if (cv.id) {
|
|
7840
|
+
variables.versionId = cv.id;
|
|
7841
|
+
}
|
|
7842
|
+
}
|
|
7843
|
+
} catch {
|
|
7844
|
+
}
|
|
7845
|
+
}
|
|
7846
|
+
}
|
|
7417
7847
|
const queryStr = engine.buildQuery(entry, variables);
|
|
7418
|
-
|
|
7419
|
-
|
|
7420
|
-
|
|
7848
|
+
let result;
|
|
7849
|
+
let usedUpdate = false;
|
|
7850
|
+
try {
|
|
7851
|
+
result = await client.request(queryStr, variables);
|
|
7852
|
+
} catch (createErr) {
|
|
7853
|
+
if (flags.upsert && entry.name === "create") {
|
|
7854
|
+
const updateEntry = COMMANDS.find(
|
|
7855
|
+
(c) => c.group === entry.group && c.name === "update"
|
|
7856
|
+
);
|
|
7857
|
+
const inputArgName = entry.inputArgName ?? "input";
|
|
7858
|
+
const inputData = variables[inputArgName];
|
|
7859
|
+
if (updateEntry && inputData?.key) {
|
|
7860
|
+
const updateVars = {
|
|
7861
|
+
...variables,
|
|
7862
|
+
[updateEntry.inputArgName ?? "input"]: inputData,
|
|
7863
|
+
...updateEntry.positionalArgs?.[0] ? {
|
|
7864
|
+
[updateEntry.positionalArgs[0].graphqlArg]: inputData.key
|
|
7865
|
+
} : {}
|
|
7866
|
+
};
|
|
7867
|
+
const uq = engine.buildQuery(updateEntry, updateVars);
|
|
7868
|
+
result = await client.request(uq, updateVars);
|
|
7869
|
+
usedUpdate = true;
|
|
7870
|
+
} else {
|
|
7871
|
+
throw createErr;
|
|
7872
|
+
}
|
|
7873
|
+
} else {
|
|
7874
|
+
throw createErr;
|
|
7875
|
+
}
|
|
7876
|
+
}
|
|
7877
|
+
const activeEntry = usedUpdate ? COMMANDS.find(
|
|
7878
|
+
(c) => c.group === entry.group && c.name === "update"
|
|
7879
|
+
) ?? entry : entry;
|
|
7880
|
+
const { data, total } = extractResult(result, activeEntry.operation);
|
|
7881
|
+
const responseData = data && typeof data === "object" && !Array.isArray(data) ? data : void 0;
|
|
7882
|
+
const displayEntry = usedUpdate ? activeEntry : entry;
|
|
7883
|
+
if (displayEntry.scalarResult) {
|
|
7421
7884
|
if (!(opts.json || opts.jsonl || opts.quiet)) {
|
|
7422
|
-
if (
|
|
7423
|
-
success(
|
|
7885
|
+
if (displayEntry.successMessage) {
|
|
7886
|
+
success(displayEntry.successMessage, responseData);
|
|
7424
7887
|
}
|
|
7425
7888
|
} else {
|
|
7426
7889
|
formatOutput(data, opts);
|
|
7427
7890
|
}
|
|
7428
7891
|
} else if (Array.isArray(data)) {
|
|
7429
|
-
const cliColumns = toCliColumns(
|
|
7892
|
+
const cliColumns = toCliColumns(displayEntry.columns);
|
|
7430
7893
|
formatList(data, opts, {
|
|
7431
7894
|
columns: cliColumns ?? autoColumns(data),
|
|
7432
7895
|
total
|
|
7433
7896
|
});
|
|
7434
7897
|
} else {
|
|
7435
7898
|
formatOutput(data, opts);
|
|
7436
|
-
if (
|
|
7437
|
-
success(
|
|
7899
|
+
if (displayEntry.successMessage && !(opts.json || opts.jsonl || opts.quiet)) {
|
|
7900
|
+
success(displayEntry.successMessage, responseData);
|
|
7901
|
+
}
|
|
7902
|
+
}
|
|
7903
|
+
if (flags.publish && entry.group === "records" && entry.name === "create" && responseData) {
|
|
7904
|
+
const version2 = responseData.version;
|
|
7905
|
+
const versionId = version2?.id;
|
|
7906
|
+
if (versionId) {
|
|
7907
|
+
const publishQuery = `mutation PublishVersion($versionId: ID!) { publishVersion(versionId: $versionId) }`;
|
|
7908
|
+
await client.request(publishQuery, { versionId });
|
|
7909
|
+
if (!(opts.json || opts.jsonl || opts.quiet)) {
|
|
7910
|
+
success("Published version {id}", { id: versionId });
|
|
7911
|
+
}
|
|
7912
|
+
} else if (!(opts.json || opts.jsonl || opts.quiet)) {
|
|
7913
|
+
console.error(
|
|
7914
|
+
chalk7.yellow(
|
|
7915
|
+
"\u26A0 Could not auto-publish: no version found in response"
|
|
7916
|
+
)
|
|
7917
|
+
);
|
|
7438
7918
|
}
|
|
7439
7919
|
}
|
|
7440
7920
|
})
|
|
@@ -7455,7 +7935,7 @@ function autoColumns(items) {
|
|
|
7455
7935
|
// src/cli.ts
|
|
7456
7936
|
var __filename2 = fileURLToPath2(import.meta.url);
|
|
7457
7937
|
var __dirname2 = dirname6(__filename2);
|
|
7458
|
-
config({ path:
|
|
7938
|
+
config({ path: resolve7(__dirname2, "../.env.local") });
|
|
7459
7939
|
var require2 = createRequire(import.meta.url);
|
|
7460
7940
|
var { version } = require2("../package.json");
|
|
7461
7941
|
var program = new Command();
|
|
@@ -7477,5 +7957,6 @@ registerMediaCommands(program, getGlobalOpts);
|
|
|
7477
7957
|
registerSearchCommands(program, getGlobalOpts);
|
|
7478
7958
|
registerPullCommand(program, getGlobalOpts);
|
|
7479
7959
|
registerCreateExtensionCommand(program, getGlobalOpts);
|
|
7960
|
+
registerInitCommands(program, getGlobalOpts);
|
|
7480
7961
|
registerDynamicCommands(program, getGlobalOpts);
|
|
7481
7962
|
program.parse();
|
package/dist/schema.graphql
CHANGED
|
@@ -202,6 +202,7 @@ type Query {
|
|
|
202
202
|
billingCustomPackage(id: ID!): BillingCustomPackage
|
|
203
203
|
billingSubscription: BillingSubscription
|
|
204
204
|
billingUsageSummary(projectId: ID): BillingUsageSummary!
|
|
205
|
+
billingUsageAlerts: [UsageAlert!]!
|
|
205
206
|
tenantBillingStatus(status: String, limit: Int, offset: Int): TenantBillingStatusResult!
|
|
206
207
|
segments(isActive: Boolean, limit: Int, offset: Int): [Segment!]!
|
|
207
208
|
segment(id: ID!): Segment
|
|
@@ -852,8 +853,13 @@ type Mutation {
|
|
|
852
853
|
assignBillingCustomPackage(tenantId: ID!, customPackageId: ID!): BillingSubscription!
|
|
853
854
|
cancelBillingSubscription(immediate: Boolean): BillingSubscription!
|
|
854
855
|
reactivateBillingSubscription: BillingSubscription!
|
|
855
|
-
|
|
856
|
+
|
|
857
|
+
"""
|
|
858
|
+
Create a Stripe Checkout Session for paid plan subscription. Returns URL to redirect to.
|
|
859
|
+
"""
|
|
860
|
+
createCheckoutSession(planSlug: String!, successUrl: String!, cancelUrl: String!): CheckoutSessionResult!
|
|
856
861
|
createBillingPortalSession(returnUrl: String): BillingPortalSession!
|
|
862
|
+
dismissBillingUsageAlert(id: ID!): Boolean!
|
|
857
863
|
selfServiceSignup(input: SelfServiceSignupInput!): SelfServiceSignupResult!
|
|
858
864
|
createSignupSession(input: CreateSignupSessionInput!): CreateSignupSessionResult!
|
|
859
865
|
createSegment(input: CreateSegmentInput!): Segment!
|
|
@@ -1829,6 +1835,11 @@ type SessionContext {
|
|
|
1829
1835
|
|
|
1830
1836
|
"""Current user info"""
|
|
1831
1837
|
user: AdminUser!
|
|
1838
|
+
|
|
1839
|
+
"""
|
|
1840
|
+
Effective role for the current tenant+project context (e.g. PLATFORM_ADMIN, TENANT_OWNER, PROJECT_ADMIN, PROJECT_EDITOR, PROJECT_VIEWER)
|
|
1841
|
+
"""
|
|
1842
|
+
effectiveRole: String
|
|
1832
1843
|
}
|
|
1833
1844
|
|
|
1834
1845
|
type Project {
|
|
@@ -2782,6 +2793,9 @@ type BillingPlanLimit {
|
|
|
2782
2793
|
maxQuantity: BigInt
|
|
2783
2794
|
overagePriceCents: Int
|
|
2784
2795
|
overageUnitSize: Int!
|
|
2796
|
+
|
|
2797
|
+
"""Whether this limit is unlimited (no max quantity)"""
|
|
2798
|
+
isUnlimited: Boolean!
|
|
2785
2799
|
}
|
|
2786
2800
|
|
|
2787
2801
|
type BillingCustomPackage {
|
|
@@ -2812,6 +2826,9 @@ type BillingPackageLimit {
|
|
|
2812
2826
|
includedQuantity: BigInt
|
|
2813
2827
|
maxQuantity: BigInt
|
|
2814
2828
|
overagePriceCents: Int
|
|
2829
|
+
|
|
2830
|
+
"""Whether this limit is unlimited (no max quantity)"""
|
|
2831
|
+
isUnlimited: Boolean!
|
|
2815
2832
|
}
|
|
2816
2833
|
|
|
2817
2834
|
type BillingSubscription {
|
|
@@ -2856,8 +2873,8 @@ type UsageLimitStatus {
|
|
|
2856
2873
|
percentUsed: Float!
|
|
2857
2874
|
}
|
|
2858
2875
|
|
|
2859
|
-
type
|
|
2860
|
-
|
|
2876
|
+
type CheckoutSessionResult {
|
|
2877
|
+
url: String!
|
|
2861
2878
|
}
|
|
2862
2879
|
|
|
2863
2880
|
type BillingPortalSession {
|
|
@@ -2936,6 +2953,16 @@ type TenantBillingStatusResult {
|
|
|
2936
2953
|
total: Int!
|
|
2937
2954
|
}
|
|
2938
2955
|
|
|
2956
|
+
type UsageAlert {
|
|
2957
|
+
id: ID!
|
|
2958
|
+
metric: BillingMetric!
|
|
2959
|
+
threshold: Int!
|
|
2960
|
+
currentUsage: Float!
|
|
2961
|
+
limitValue: Float!
|
|
2962
|
+
percentUsed: Int!
|
|
2963
|
+
createdAt: DateTime!
|
|
2964
|
+
}
|
|
2965
|
+
|
|
2939
2966
|
input CreateCustomPackageInput {
|
|
2940
2967
|
tenantId: ID!
|
|
2941
2968
|
projectId: ID
|
|
@@ -3204,6 +3231,15 @@ type ExperimentFunnel {
|
|
|
3204
3231
|
steps: [FunnelStep!]!
|
|
3205
3232
|
}
|
|
3206
3233
|
|
|
3234
|
+
"""Experiment lifecycle action"""
|
|
3235
|
+
enum ExperimentAction {
|
|
3236
|
+
start
|
|
3237
|
+
pause
|
|
3238
|
+
resume
|
|
3239
|
+
end
|
|
3240
|
+
declareWinner
|
|
3241
|
+
}
|
|
3242
|
+
|
|
3207
3243
|
"""Experiment stored in the dedicated experiments table"""
|
|
3208
3244
|
type ExperimentRecord {
|
|
3209
3245
|
id: ID!
|
|
@@ -3224,6 +3260,9 @@ type ExperimentRecord {
|
|
|
3224
3260
|
funnel: ExperimentFunnel
|
|
3225
3261
|
targetVariantCatalogId: ID
|
|
3226
3262
|
isActive: Boolean!
|
|
3263
|
+
|
|
3264
|
+
"""Actions available for the current experiment status"""
|
|
3265
|
+
allowedActions: [ExperimentAction!]!
|
|
3227
3266
|
createdAt: DateTime!
|
|
3228
3267
|
updatedAt: DateTime!
|
|
3229
3268
|
}
|
|
@@ -4996,6 +5035,18 @@ input UpdateScheduleInput {
|
|
|
4996
5035
|
isActive: Boolean
|
|
4997
5036
|
}
|
|
4998
5037
|
|
|
5038
|
+
"""Computed capabilities for a model based on its config"""
|
|
5039
|
+
type ModelCapabilities {
|
|
5040
|
+
isVersioned: Boolean!
|
|
5041
|
+
isPublishable: Boolean!
|
|
5042
|
+
hasVariants: Boolean!
|
|
5043
|
+
isInlineOnly: Boolean!
|
|
5044
|
+
recordCapable: Boolean!
|
|
5045
|
+
publicApi: Boolean!
|
|
5046
|
+
customerScoped: Boolean!
|
|
5047
|
+
sharingEnabled: Boolean!
|
|
5048
|
+
}
|
|
5049
|
+
|
|
4999
5050
|
type Model {
|
|
5000
5051
|
id: ID!
|
|
5001
5052
|
key: String!
|
|
@@ -5034,6 +5085,9 @@ type Model {
|
|
|
5034
5085
|
"""Whether this model's fields can be edited"""
|
|
5035
5086
|
editable: Boolean!
|
|
5036
5087
|
|
|
5088
|
+
"""Computed capabilities based on config flags"""
|
|
5089
|
+
capabilities: ModelCapabilities!
|
|
5090
|
+
|
|
5037
5091
|
"""Current schema version"""
|
|
5038
5092
|
currentVersionId: String
|
|
5039
5093
|
publishedVersionNumber: Int
|
|
@@ -5112,6 +5166,19 @@ type Record {
|
|
|
5112
5166
|
scheduledPublishAt: DateTime
|
|
5113
5167
|
scheduledUnpublishAt: DateTime
|
|
5114
5168
|
customerId: String
|
|
5169
|
+
|
|
5170
|
+
"""
|
|
5171
|
+
Whether this record can be published (has unpublished changes on a publishable model)
|
|
5172
|
+
"""
|
|
5173
|
+
canPublish: Boolean!
|
|
5174
|
+
|
|
5175
|
+
"""
|
|
5176
|
+
Whether this record can be unpublished (has a published version on a publishable model)
|
|
5177
|
+
"""
|
|
5178
|
+
canUnpublish: Boolean!
|
|
5179
|
+
|
|
5180
|
+
"""Whether the current version differs from the published version"""
|
|
5181
|
+
hasUnpublishedChanges: Boolean!
|
|
5115
5182
|
createdAt: DateTime!
|
|
5116
5183
|
updatedAt: DateTime!
|
|
5117
5184
|
createdBy: String
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@eide/foir-cli",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "Universal platform CLI for
|
|
3
|
+
"version": "0.1.39",
|
|
4
|
+
"description": "Universal platform CLI for Foir platform",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"publishConfig": {
|
|
7
7
|
"access": "public"
|