@eide/foir-cli 0.1.2 → 0.1.3
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 +3 -0
- package/dist/codegen/fetch-models.d.ts +40 -0
- package/dist/codegen/fetch-models.d.ts.map +1 -0
- package/dist/codegen/fetch-models.js +45 -0
- package/dist/codegen/field-mapping.d.ts +28 -0
- package/dist/codegen/field-mapping.d.ts.map +1 -0
- package/dist/codegen/field-mapping.js +194 -0
- package/dist/codegen/generators/config.d.ts +5 -0
- package/dist/codegen/generators/config.d.ts.map +1 -0
- package/dist/codegen/generators/config.js +82 -0
- package/dist/codegen/generators/documents.d.ts +6 -0
- package/dist/codegen/generators/documents.d.ts.map +1 -0
- package/dist/codegen/generators/documents.js +91 -0
- package/dist/codegen/generators/field-types.d.ts +5 -0
- package/dist/codegen/generators/field-types.d.ts.map +1 -0
- package/dist/codegen/generators/field-types.js +339 -0
- package/dist/codegen/generators/model-index.d.ts +6 -0
- package/dist/codegen/generators/model-index.d.ts.map +1 -0
- package/dist/codegen/generators/model-index.js +26 -0
- package/dist/codegen/generators/model-types.d.ts +12 -0
- package/dist/codegen/generators/model-types.d.ts.map +1 -0
- package/dist/codegen/generators/model-types.js +150 -0
- package/dist/codegen/write-files.d.ts +15 -0
- package/dist/codegen/write-files.d.ts.map +1 -0
- package/dist/codegen/write-files.js +35 -0
- package/dist/commands/models.d.ts.map +1 -1
- package/dist/commands/models.js +0 -6
- package/dist/commands/pull.d.ts +4 -0
- package/dist/commands/pull.d.ts.map +1 -0
- package/dist/commands/pull.js +118 -0
- package/dist/config/pull-config.d.ts +25 -0
- package/dist/config/pull-config.d.ts.map +1 -0
- package/dist/config/pull-config.js +65 -0
- package/dist/config/types.d.ts +31 -0
- package/dist/config/types.d.ts.map +1 -0
- package/dist/config/types.js +15 -0
- package/dist/graphql/queries.d.ts +1 -0
- package/dist/graphql/queries.d.ts.map +1 -1
- package/dist/graphql/queries.js +8 -0
- package/package.json +19 -13
package/dist/cli.js
CHANGED
|
@@ -33,6 +33,7 @@ import { registerLocalesCommands } from './commands/locales.js';
|
|
|
33
33
|
import { registerFilesCommands } from './commands/files.js';
|
|
34
34
|
import { registerNotesCommands } from './commands/notes.js';
|
|
35
35
|
import { registerVariantCatalogCommands } from './commands/variant-catalog.js';
|
|
36
|
+
import { registerPullCommand } from './commands/pull.js';
|
|
36
37
|
const program = new Command();
|
|
37
38
|
program
|
|
38
39
|
.name('foir')
|
|
@@ -74,4 +75,6 @@ registerLocalesCommands(program, getGlobalOpts);
|
|
|
74
75
|
registerFilesCommands(program, getGlobalOpts);
|
|
75
76
|
registerNotesCommands(program, getGlobalOpts);
|
|
76
77
|
registerVariantCatalogCommands(program, getGlobalOpts);
|
|
78
|
+
// Codegen
|
|
79
|
+
registerPullCommand(program, getGlobalOpts);
|
|
77
80
|
program.parse();
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fetches models from the platform GraphQL API and transforms them for codegen.
|
|
3
|
+
*/
|
|
4
|
+
import type { GraphQLClient } from 'graphql-request';
|
|
5
|
+
import type { FieldSchemaForGen } from './field-mapping.js';
|
|
6
|
+
export interface CodegenModelConfig {
|
|
7
|
+
records: boolean;
|
|
8
|
+
inline: boolean;
|
|
9
|
+
publicApi: boolean;
|
|
10
|
+
versioning: boolean;
|
|
11
|
+
publishing: boolean;
|
|
12
|
+
variants: boolean;
|
|
13
|
+
customerScoped: boolean;
|
|
14
|
+
embeddings?: {
|
|
15
|
+
enabled: boolean;
|
|
16
|
+
fields: Array<{
|
|
17
|
+
fieldPath: string;
|
|
18
|
+
weight?: number;
|
|
19
|
+
}>;
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
export interface CodegenModel {
|
|
23
|
+
key: string;
|
|
24
|
+
name: string;
|
|
25
|
+
pluralName?: string;
|
|
26
|
+
description?: string;
|
|
27
|
+
category?: string;
|
|
28
|
+
fields: FieldSchemaForGen[];
|
|
29
|
+
config: CodegenModelConfig;
|
|
30
|
+
hooks?: Record<string, unknown>;
|
|
31
|
+
}
|
|
32
|
+
export declare function fetchModelsForCodegen(client: GraphQLClient): Promise<CodegenModel[]>;
|
|
33
|
+
/**
|
|
34
|
+
* Filter models based on pull config options.
|
|
35
|
+
*/
|
|
36
|
+
export declare function filterModels(models: CodegenModel[], options: {
|
|
37
|
+
only?: string[];
|
|
38
|
+
includeInline?: boolean;
|
|
39
|
+
}): CodegenModel[];
|
|
40
|
+
//# sourceMappingURL=fetch-models.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fetch-models.d.ts","sourceRoot":"","sources":["../../src/codegen/fetch-models.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAErD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAE5D,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,OAAO,CAAC;IAChB,SAAS,EAAE,OAAO,CAAC;IACnB,UAAU,EAAE,OAAO,CAAC;IACpB,UAAU,EAAE,OAAO,CAAC;IACpB,QAAQ,EAAE,OAAO,CAAC;IAClB,cAAc,EAAE,OAAO,CAAC;IACxB,UAAU,CAAC,EAAE;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,MAAM,EAAE,KAAK,CAAC;YAAE,SAAS,EAAE,MAAM,CAAC;YAAC,MAAM,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC,CAAA;KAAE,CAAC;CAC1F;AAED,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,iBAAiB,EAAE,CAAC;IAC5B,MAAM,EAAE,kBAAkB,CAAC;IAC3B,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACjC;AAgCD,wBAAsB,qBAAqB,CACzC,MAAM,EAAE,aAAa,GACpB,OAAO,CAAC,YAAY,EAAE,CAAC,CAezB;AAED;;GAEG;AACH,wBAAgB,YAAY,CAC1B,MAAM,EAAE,YAAY,EAAE,EACtB,OAAO,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAAC,aAAa,CAAC,EAAE,OAAO,CAAA;CAAE,GACpD,YAAY,EAAE,CAahB"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fetches models from the platform GraphQL API and transforms them for codegen.
|
|
3
|
+
*/
|
|
4
|
+
import { MODELS_FOR_CODEGEN } from '../graphql/queries.js';
|
|
5
|
+
function normalizeConfig(raw) {
|
|
6
|
+
return {
|
|
7
|
+
records: Boolean(raw.records ?? true),
|
|
8
|
+
inline: Boolean(raw.inline ?? false),
|
|
9
|
+
publicApi: Boolean(raw.publicApi ?? false),
|
|
10
|
+
versioning: Boolean(raw.versioning ?? false),
|
|
11
|
+
publishing: Boolean(raw.publishing ?? false),
|
|
12
|
+
variants: Boolean(raw.variants ?? false),
|
|
13
|
+
customerScoped: Boolean(raw.customerScoped ?? false),
|
|
14
|
+
embeddings: raw.embeddings,
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
export async function fetchModelsForCodegen(client) {
|
|
18
|
+
const data = await client.request(MODELS_FOR_CODEGEN, {
|
|
19
|
+
limit: 500,
|
|
20
|
+
});
|
|
21
|
+
return data.models.items.map((item) => ({
|
|
22
|
+
key: item.key,
|
|
23
|
+
name: item.name,
|
|
24
|
+
pluralName: item.pluralName,
|
|
25
|
+
description: item.description,
|
|
26
|
+
category: item.category,
|
|
27
|
+
fields: item.fields ?? [],
|
|
28
|
+
config: normalizeConfig(item.config ?? {}),
|
|
29
|
+
hooks: item.hooks,
|
|
30
|
+
}));
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Filter models based on pull config options.
|
|
34
|
+
*/
|
|
35
|
+
export function filterModels(models, options) {
|
|
36
|
+
let filtered = models;
|
|
37
|
+
if (options.only && options.only.length > 0) {
|
|
38
|
+
const onlySet = new Set(options.only);
|
|
39
|
+
filtered = filtered.filter((m) => onlySet.has(m.key));
|
|
40
|
+
}
|
|
41
|
+
if (!options.includeInline) {
|
|
42
|
+
filtered = filtered.filter((m) => m.config.records);
|
|
43
|
+
}
|
|
44
|
+
return filtered;
|
|
45
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Maps EIDE field types to TypeScript types.
|
|
3
|
+
* Ported from uniformgen with updates for the unified data layer.
|
|
4
|
+
*/
|
|
5
|
+
export interface TypeMapping {
|
|
6
|
+
outputType: string;
|
|
7
|
+
inputType: string;
|
|
8
|
+
needsImport?: string;
|
|
9
|
+
}
|
|
10
|
+
export declare const PRIMITIVE_FIELD_TYPES: Set<string>;
|
|
11
|
+
export declare function isPrimitiveFieldType(type: string): boolean;
|
|
12
|
+
export declare const FIELD_TYPE_MAPPING: Record<string, TypeMapping>;
|
|
13
|
+
export interface FieldSchemaForGen {
|
|
14
|
+
key: string;
|
|
15
|
+
type: string;
|
|
16
|
+
label?: string;
|
|
17
|
+
required?: boolean;
|
|
18
|
+
helpText?: string;
|
|
19
|
+
options?: Record<string, unknown>;
|
|
20
|
+
}
|
|
21
|
+
export declare function getFieldType(field: FieldSchemaForGen, mode?: 'output' | 'input'): string;
|
|
22
|
+
export declare function getRequiredImports(fields: FieldSchemaForGen[]): Set<string>;
|
|
23
|
+
export declare function getInlineSchemaReferences(fields: FieldSchemaForGen[]): Set<string>;
|
|
24
|
+
export declare function toCamelCase(str: string): string;
|
|
25
|
+
export declare function toPascalCase(str: string): string;
|
|
26
|
+
export declare function sanitizeFieldName(key: string): string;
|
|
27
|
+
export declare function generateFieldDef(field: FieldSchemaForGen): string;
|
|
28
|
+
//# sourceMappingURL=field-mapping.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"field-mapping.d.ts","sourceRoot":"","sources":["../../src/codegen/field-mapping.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,WAAW,WAAW;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,eAAO,MAAM,qBAAqB,aAsBhC,CAAC;AAEH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAE1D;AAED,eAAO,MAAM,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAsB1D,CAAC;AAEF,MAAM,WAAW,iBAAiB;IAChC,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC;AAED,wBAAgB,YAAY,CAC1B,KAAK,EAAE,iBAAiB,EACxB,IAAI,GAAE,QAAQ,GAAG,OAAkB,GAClC,MAAM,CAqCR;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,iBAAiB,EAAE,GAAG,GAAG,CAAC,MAAM,CAAC,CAW3E;AAED,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,iBAAiB,EAAE,GAAG,GAAG,CAAC,MAAM,CAAC,CAclF;AAED,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAG/C;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAIhD;AAED,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAKrD;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,iBAAiB,GAAG,MAAM,CAyDjE"}
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Maps EIDE field types to TypeScript types.
|
|
3
|
+
* Ported from uniformgen with updates for the unified data layer.
|
|
4
|
+
*/
|
|
5
|
+
export const PRIMITIVE_FIELD_TYPES = new Set([
|
|
6
|
+
'text',
|
|
7
|
+
'richtext',
|
|
8
|
+
'number',
|
|
9
|
+
'boolean',
|
|
10
|
+
'email',
|
|
11
|
+
'phone',
|
|
12
|
+
'url',
|
|
13
|
+
'date',
|
|
14
|
+
'image',
|
|
15
|
+
'video',
|
|
16
|
+
'file',
|
|
17
|
+
'currency',
|
|
18
|
+
'select',
|
|
19
|
+
'multiselect',
|
|
20
|
+
'json',
|
|
21
|
+
'list',
|
|
22
|
+
'tree',
|
|
23
|
+
'entity-reference',
|
|
24
|
+
'reference',
|
|
25
|
+
'link',
|
|
26
|
+
'flexible',
|
|
27
|
+
]);
|
|
28
|
+
export function isPrimitiveFieldType(type) {
|
|
29
|
+
return PRIMITIVE_FIELD_TYPES.has(type);
|
|
30
|
+
}
|
|
31
|
+
export const FIELD_TYPE_MAPPING = {
|
|
32
|
+
text: { outputType: 'string', inputType: 'string' },
|
|
33
|
+
richtext: { outputType: 'RichtextValue', inputType: 'RichtextValue', needsImport: 'field-types' },
|
|
34
|
+
number: { outputType: 'number', inputType: 'number' },
|
|
35
|
+
boolean: { outputType: 'boolean', inputType: 'boolean' },
|
|
36
|
+
email: { outputType: 'string', inputType: 'string' },
|
|
37
|
+
phone: { outputType: 'string', inputType: 'string' },
|
|
38
|
+
url: { outputType: 'string', inputType: 'string' },
|
|
39
|
+
date: { outputType: 'Date', inputType: 'string' },
|
|
40
|
+
image: { outputType: 'ImageValue', inputType: 'string', needsImport: 'field-types' },
|
|
41
|
+
video: { outputType: 'VideoValue', inputType: 'string', needsImport: 'field-types' },
|
|
42
|
+
file: { outputType: 'FileValue', inputType: 'string', needsImport: 'field-types' },
|
|
43
|
+
currency: { outputType: 'CurrencyValue', inputType: 'CurrencyValue', needsImport: 'field-types' },
|
|
44
|
+
select: { outputType: 'string', inputType: 'string' },
|
|
45
|
+
multiselect: { outputType: 'string[]', inputType: 'string[]' },
|
|
46
|
+
json: { outputType: 'unknown', inputType: 'unknown' },
|
|
47
|
+
list: { outputType: 'unknown[]', inputType: 'unknown[]' },
|
|
48
|
+
tree: { outputType: 'unknown[]', inputType: 'unknown[]' },
|
|
49
|
+
flexible: { outputType: 'FlexibleFieldItem[]', inputType: 'FlexibleFieldItem[]', needsImport: 'field-types' },
|
|
50
|
+
'entity-reference': { outputType: 'ReferenceValue', inputType: 'ReferenceValue', needsImport: 'field-types' },
|
|
51
|
+
reference: { outputType: 'ReferenceValue', inputType: 'ReferenceValue', needsImport: 'field-types' },
|
|
52
|
+
link: { outputType: 'LinkValue', inputType: 'LinkValue', needsImport: 'field-types' },
|
|
53
|
+
};
|
|
54
|
+
export function getFieldType(field, mode = 'output') {
|
|
55
|
+
if (!field?.type)
|
|
56
|
+
return 'unknown';
|
|
57
|
+
const mapping = FIELD_TYPE_MAPPING[field.type];
|
|
58
|
+
if (!mapping) {
|
|
59
|
+
if (isPrimitiveFieldType(field.type))
|
|
60
|
+
return 'unknown';
|
|
61
|
+
return toPascalCase(field.type);
|
|
62
|
+
}
|
|
63
|
+
let tsType = mode === 'output' ? mapping.outputType : mapping.inputType;
|
|
64
|
+
if (field.type === 'select' && field.options?.options) {
|
|
65
|
+
const options = field.options.options;
|
|
66
|
+
if (options.length > 0) {
|
|
67
|
+
tsType = options.map((o) => `'${o.value}'`).join(' | ');
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
if (field.type === 'multiselect' && field.options?.options) {
|
|
71
|
+
const options = field.options.options;
|
|
72
|
+
if (options.length > 0) {
|
|
73
|
+
tsType = `(${options.map((o) => `'${o.value}'`).join(' | ')})[]`;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
if ((field.type === 'list' || field.type === 'tree') && field.options?.itemType) {
|
|
77
|
+
const itemType = field.options.itemType;
|
|
78
|
+
const itemMapping = FIELD_TYPE_MAPPING[itemType];
|
|
79
|
+
if (itemMapping) {
|
|
80
|
+
tsType = `${mode === 'output' ? itemMapping.outputType : itemMapping.inputType}[]`;
|
|
81
|
+
}
|
|
82
|
+
else if (!isPrimitiveFieldType(itemType)) {
|
|
83
|
+
tsType = `${toPascalCase(itemType)}[]`;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return tsType;
|
|
87
|
+
}
|
|
88
|
+
export function getRequiredImports(fields) {
|
|
89
|
+
const imports = new Set();
|
|
90
|
+
for (const field of fields) {
|
|
91
|
+
const mapping = FIELD_TYPE_MAPPING[field.type];
|
|
92
|
+
if (mapping?.needsImport)
|
|
93
|
+
imports.add(mapping.needsImport);
|
|
94
|
+
if ((field.type === 'list' || field.type === 'tree') && field.options?.itemType) {
|
|
95
|
+
const itemMapping = FIELD_TYPE_MAPPING[field.options.itemType];
|
|
96
|
+
if (itemMapping?.needsImport)
|
|
97
|
+
imports.add(itemMapping.needsImport);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return imports;
|
|
101
|
+
}
|
|
102
|
+
export function getInlineSchemaReferences(fields) {
|
|
103
|
+
const refs = new Set();
|
|
104
|
+
for (const field of fields) {
|
|
105
|
+
if (!isPrimitiveFieldType(field.type) && !FIELD_TYPE_MAPPING[field.type]) {
|
|
106
|
+
refs.add(field.type);
|
|
107
|
+
}
|
|
108
|
+
if ((field.type === 'list' || field.type === 'tree') && field.options?.itemType) {
|
|
109
|
+
const itemType = field.options.itemType;
|
|
110
|
+
if (!isPrimitiveFieldType(itemType) && !FIELD_TYPE_MAPPING[itemType]) {
|
|
111
|
+
refs.add(itemType);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return refs;
|
|
116
|
+
}
|
|
117
|
+
export function toCamelCase(str) {
|
|
118
|
+
if (!str)
|
|
119
|
+
return 'unknown';
|
|
120
|
+
return str.replace(/[-_]([a-z])/g, (_, letter) => letter.toUpperCase());
|
|
121
|
+
}
|
|
122
|
+
export function toPascalCase(str) {
|
|
123
|
+
if (!str)
|
|
124
|
+
return 'Unknown';
|
|
125
|
+
const camel = toCamelCase(str);
|
|
126
|
+
return camel.charAt(0).toUpperCase() + camel.slice(1);
|
|
127
|
+
}
|
|
128
|
+
export function sanitizeFieldName(key) {
|
|
129
|
+
if (!key)
|
|
130
|
+
return 'unknown';
|
|
131
|
+
const camel = toCamelCase(key);
|
|
132
|
+
if (/^[0-9]/.test(camel))
|
|
133
|
+
return `_${camel}`;
|
|
134
|
+
return camel;
|
|
135
|
+
}
|
|
136
|
+
export function generateFieldDef(field) {
|
|
137
|
+
const parts = [];
|
|
138
|
+
parts.push(`key: '${field.key ?? 'unknown'}'`);
|
|
139
|
+
parts.push(`type: '${field.type ?? 'text'}'`);
|
|
140
|
+
parts.push(`label: '${(field.label ?? field.key ?? 'Unknown').replace(/'/g, "\\'")}'`);
|
|
141
|
+
if (field.required)
|
|
142
|
+
parts.push('required: true');
|
|
143
|
+
if (field.helpText)
|
|
144
|
+
parts.push(`helpText: '${field.helpText.replace(/'/g, "\\'")}'`);
|
|
145
|
+
if (field.type === 'text' && field.options) {
|
|
146
|
+
if (field.options.maxLength)
|
|
147
|
+
parts.push(`maxLength: ${field.options.maxLength}`);
|
|
148
|
+
if (field.options.minLength)
|
|
149
|
+
parts.push(`minLength: ${field.options.minLength}`);
|
|
150
|
+
}
|
|
151
|
+
if (field.type === 'number' && field.options) {
|
|
152
|
+
if (field.options.min !== undefined)
|
|
153
|
+
parts.push(`min: ${field.options.min}`);
|
|
154
|
+
if (field.options.max !== undefined)
|
|
155
|
+
parts.push(`max: ${field.options.max}`);
|
|
156
|
+
if (field.options.step !== undefined)
|
|
157
|
+
parts.push(`step: ${field.options.step}`);
|
|
158
|
+
}
|
|
159
|
+
if ((field.type === 'select' || field.type === 'multiselect') && field.options?.options) {
|
|
160
|
+
const options = field.options.options;
|
|
161
|
+
const optionsStr = options
|
|
162
|
+
.filter((o) => o.value !== undefined)
|
|
163
|
+
.map((o) => {
|
|
164
|
+
const label = (o.label ?? o.value ?? '').replace(/'/g, "\\'");
|
|
165
|
+
const value = (o.value ?? '').replace(/'/g, "\\'");
|
|
166
|
+
return `{ label: '${label}', value: '${value}' }`;
|
|
167
|
+
})
|
|
168
|
+
.join(', ');
|
|
169
|
+
parts.push(`options: [${optionsStr}]`);
|
|
170
|
+
}
|
|
171
|
+
if ((field.type === 'entity-reference' || field.type === 'reference') && field.options?.referenceTypes) {
|
|
172
|
+
const refTypes = field.options.referenceTypes;
|
|
173
|
+
parts.push(`referenceTypes: [${refTypes.map((t) => `'${t}'`).join(', ')}]`);
|
|
174
|
+
if (field.options.multiple)
|
|
175
|
+
parts.push('multiple: true');
|
|
176
|
+
}
|
|
177
|
+
if ((field.type === 'list' || field.type === 'tree') && field.options?.itemType) {
|
|
178
|
+
parts.push(`itemType: '${field.options.itemType}'`);
|
|
179
|
+
if (field.options.minItems !== undefined)
|
|
180
|
+
parts.push(`minItems: ${field.options.minItems}`);
|
|
181
|
+
if (field.options.maxItems !== undefined)
|
|
182
|
+
parts.push(`maxItems: ${field.options.maxItems}`);
|
|
183
|
+
}
|
|
184
|
+
if ((field.type === 'image' || field.type === 'video' || field.type === 'file') &&
|
|
185
|
+
field.options) {
|
|
186
|
+
if (field.options.allowedTypes) {
|
|
187
|
+
const types = field.options.allowedTypes;
|
|
188
|
+
parts.push(`allowedTypes: [${types.map((t) => `'${t}'`).join(', ')}]`);
|
|
189
|
+
}
|
|
190
|
+
if (field.options.maxSize)
|
|
191
|
+
parts.push(`maxSize: ${field.options.maxSize}`);
|
|
192
|
+
}
|
|
193
|
+
return `{ ${parts.join(', ')} }`;
|
|
194
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../../src/codegen/generators/config.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,wBAAgB,kBAAkB,IAAI,MAAM,CA8E3C"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generates the static config.ts file with ModelConfig type and defineModel() helper.
|
|
3
|
+
*/
|
|
4
|
+
export function generateConfigFile() {
|
|
5
|
+
return `/**
|
|
6
|
+
* Model Configuration Type
|
|
7
|
+
*
|
|
8
|
+
* Strongly-typed model definitions for the unified data layer.
|
|
9
|
+
*
|
|
10
|
+
* @generated by foir — DO NOT EDIT MANUALLY
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import type { FieldDef } from './field-types.js';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Model configuration
|
|
17
|
+
*
|
|
18
|
+
* Defines the complete configuration for a model including
|
|
19
|
+
* its schema, capabilities, and lifecycle hooks.
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* export const blogPostConfig = {
|
|
23
|
+
* key: 'blog-post',
|
|
24
|
+
* name: 'Blog Post',
|
|
25
|
+
* records: true,
|
|
26
|
+
* inline: false,
|
|
27
|
+
* publicApi: true,
|
|
28
|
+
* versioning: true,
|
|
29
|
+
* publishing: true,
|
|
30
|
+
* variants: false,
|
|
31
|
+
* customerScoped: false,
|
|
32
|
+
* fieldDefs: [
|
|
33
|
+
* { key: 'title', type: 'text', label: 'Title', required: true },
|
|
34
|
+
* { key: 'content', type: 'richtext', label: 'Content' },
|
|
35
|
+
* ],
|
|
36
|
+
* } as const satisfies ModelConfig;
|
|
37
|
+
*/
|
|
38
|
+
export interface ModelConfig {
|
|
39
|
+
/** Unique identifier (kebab-case) */
|
|
40
|
+
key: string;
|
|
41
|
+
/** Display name */
|
|
42
|
+
name: string;
|
|
43
|
+
/** Description */
|
|
44
|
+
description?: string;
|
|
45
|
+
|
|
46
|
+
// Capability flags (from model config)
|
|
47
|
+
/** Can create standalone records */
|
|
48
|
+
records: boolean;
|
|
49
|
+
/** Available as inline field type in other models */
|
|
50
|
+
inline: boolean;
|
|
51
|
+
/** Exposed via public GraphQL API */
|
|
52
|
+
publicApi: boolean;
|
|
53
|
+
/** Version history enabled */
|
|
54
|
+
versioning: boolean;
|
|
55
|
+
/** Publishing workflow enabled */
|
|
56
|
+
publishing: boolean;
|
|
57
|
+
/** Market/device/locale variants enabled */
|
|
58
|
+
variants: boolean;
|
|
59
|
+
/** Customer-level record isolation */
|
|
60
|
+
customerScoped: boolean;
|
|
61
|
+
|
|
62
|
+
/** Embedding configuration */
|
|
63
|
+
embeddings?: {
|
|
64
|
+
enabled: boolean;
|
|
65
|
+
fields: Array<{ fieldPath: string; weight?: number }>;
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
/** Lifecycle hooks configuration */
|
|
69
|
+
hooks?: Record<string, unknown>;
|
|
70
|
+
|
|
71
|
+
/** Field definitions */
|
|
72
|
+
fieldDefs: readonly FieldDef[];
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Helper to create a type-safe model config
|
|
77
|
+
*/
|
|
78
|
+
export function defineModel<T extends ModelConfig>(config: T): T {
|
|
79
|
+
return config;
|
|
80
|
+
}
|
|
81
|
+
`;
|
|
82
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"documents.d.ts","sourceRoot":"","sources":["../../../src/codegen/generators/documents.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAGvD,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,YAAY,GAAG,MAAM,CAuFlE"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generates per-model .graphql files with CRUD operations for the public API.
|
|
3
|
+
*/
|
|
4
|
+
import { toPascalCase } from '../field-mapping.js';
|
|
5
|
+
export function generateModelDocuments(model) {
|
|
6
|
+
const typeName = toPascalCase(model.key);
|
|
7
|
+
const pluralName = model.pluralName
|
|
8
|
+
? toPascalCase(model.pluralName.replace(/\s+/g, ''))
|
|
9
|
+
: `${typeName}s`;
|
|
10
|
+
const displayName = model.name ?? model.key;
|
|
11
|
+
return `# Generated GraphQL operations for ${displayName}
|
|
12
|
+
# @generated by foir — DO NOT EDIT MANUALLY
|
|
13
|
+
|
|
14
|
+
fragment ${typeName}Fields on Record {
|
|
15
|
+
id
|
|
16
|
+
modelKey
|
|
17
|
+
naturalKey
|
|
18
|
+
data
|
|
19
|
+
metadata
|
|
20
|
+
versionNumber
|
|
21
|
+
publishedVersionNumber
|
|
22
|
+
publishStatus
|
|
23
|
+
createdAt
|
|
24
|
+
updatedAt
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
query Get${typeName}($id: ID, $naturalKey: String, $preview: Boolean) {
|
|
28
|
+
record(modelKey: "${model.key}", id: $id, naturalKey: $naturalKey, preview: $preview) {
|
|
29
|
+
...${typeName}Fields
|
|
30
|
+
resolved {
|
|
31
|
+
record { id modelKey naturalKey }
|
|
32
|
+
content { fields { key type label value } }
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
query List${pluralName}(
|
|
38
|
+
$limit: Int
|
|
39
|
+
$offset: Int
|
|
40
|
+
$filters: [FilterInput!]
|
|
41
|
+
$sort: SortInput
|
|
42
|
+
$preview: Boolean
|
|
43
|
+
) {
|
|
44
|
+
records(
|
|
45
|
+
modelKey: "${model.key}"
|
|
46
|
+
limit: $limit
|
|
47
|
+
offset: $offset
|
|
48
|
+
filters: $filters
|
|
49
|
+
sort: $sort
|
|
50
|
+
preview: $preview
|
|
51
|
+
) {
|
|
52
|
+
items {
|
|
53
|
+
...${typeName}Fields
|
|
54
|
+
}
|
|
55
|
+
total
|
|
56
|
+
hasMore
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
mutation Create${typeName}($data: JSON!) {
|
|
61
|
+
createRecord(modelKey: "${model.key}", data: $data) {
|
|
62
|
+
...${typeName}Fields
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
mutation Update${typeName}($id: ID!, $data: JSON!) {
|
|
67
|
+
updateRecord(modelKey: "${model.key}", id: $id, data: $data) {
|
|
68
|
+
...${typeName}Fields
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
mutation Delete${typeName}($id: ID!) {
|
|
73
|
+
deleteRecord(modelKey: "${model.key}", id: $id) {
|
|
74
|
+
success
|
|
75
|
+
message
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
mutation Publish${typeName}($id: ID!, $versionId: ID) {
|
|
80
|
+
publishVersion(modelKey: "${model.key}", id: $id, versionId: $versionId) {
|
|
81
|
+
...${typeName}Fields
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
mutation Unpublish${typeName}($id: ID!) {
|
|
86
|
+
unpublishRecord(modelKey: "${model.key}", id: $id) {
|
|
87
|
+
...${typeName}Fields
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
`;
|
|
91
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"field-types.d.ts","sourceRoot":"","sources":["../../../src/codegen/generators/field-types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,wBAAgB,sBAAsB,IAAI,MAAM,CA+U/C"}
|