@eide/foir-cli 0.1.1 → 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 +8 -2
- 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
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generates the static field-types.ts file with value types, filter types, and field defs.
|
|
3
|
+
*/
|
|
4
|
+
export function generateFieldTypesFile() {
|
|
5
|
+
return `/**
|
|
6
|
+
* Field Types and Definitions
|
|
7
|
+
*
|
|
8
|
+
* Value types, filter types, and field definition types.
|
|
9
|
+
*
|
|
10
|
+
* @generated by foir — DO NOT EDIT MANUALLY
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
// =============================================================================
|
|
14
|
+
// VALUE TYPES
|
|
15
|
+
// =============================================================================
|
|
16
|
+
|
|
17
|
+
/** Rich text content (Lexical JSON format) */
|
|
18
|
+
export type RichtextValue = unknown;
|
|
19
|
+
|
|
20
|
+
/** Currency value with amount and ISO 4217 code */
|
|
21
|
+
export interface CurrencyValue {
|
|
22
|
+
amount: number;
|
|
23
|
+
currency: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** Image reference with metadata */
|
|
27
|
+
export interface ImageValue {
|
|
28
|
+
id: string;
|
|
29
|
+
url: string;
|
|
30
|
+
alt?: string;
|
|
31
|
+
width?: number;
|
|
32
|
+
height?: number;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** Video reference with metadata */
|
|
36
|
+
export interface VideoValue {
|
|
37
|
+
id: string;
|
|
38
|
+
url: string;
|
|
39
|
+
thumbnail?: string;
|
|
40
|
+
duration?: number;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** File reference with metadata */
|
|
44
|
+
export interface FileValue {
|
|
45
|
+
id: string;
|
|
46
|
+
url: string;
|
|
47
|
+
name: string;
|
|
48
|
+
size: number;
|
|
49
|
+
mimeType: string;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** Link value (internal reference or external URL) */
|
|
53
|
+
export interface LinkValue {
|
|
54
|
+
type: 'entity' | 'url';
|
|
55
|
+
entity?: LinkEntityReference;
|
|
56
|
+
url?: string;
|
|
57
|
+
target?: '_self' | '_blank';
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** Entity reference for internal links */
|
|
61
|
+
export interface LinkEntityReference {
|
|
62
|
+
modelKey: string;
|
|
63
|
+
naturalKey: string;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** Entity reference value */
|
|
67
|
+
export interface ReferenceValue {
|
|
68
|
+
_type: 'reference';
|
|
69
|
+
_schema: string;
|
|
70
|
+
naturalKey: string;
|
|
71
|
+
_preview?: Record<string, unknown>;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** Composite/inline value */
|
|
75
|
+
export interface CompositeValue {
|
|
76
|
+
_type: 'composite';
|
|
77
|
+
_schema: string;
|
|
78
|
+
fields: Record<string, unknown>;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** A single item in a flexible field array */
|
|
82
|
+
export interface FlexibleFieldItem {
|
|
83
|
+
_id: string;
|
|
84
|
+
_key: string;
|
|
85
|
+
_type: string;
|
|
86
|
+
_label: string;
|
|
87
|
+
_required?: boolean;
|
|
88
|
+
_helpText?: string;
|
|
89
|
+
_config?: Record<string, unknown>;
|
|
90
|
+
value: unknown;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// =============================================================================
|
|
94
|
+
// FILTER TYPES
|
|
95
|
+
// =============================================================================
|
|
96
|
+
|
|
97
|
+
export interface TextFilter {
|
|
98
|
+
eq?: string;
|
|
99
|
+
ne?: string;
|
|
100
|
+
contains?: string;
|
|
101
|
+
startsWith?: string;
|
|
102
|
+
endsWith?: string;
|
|
103
|
+
in?: string[];
|
|
104
|
+
notIn?: string[];
|
|
105
|
+
isNull?: boolean;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export interface NumberFilter {
|
|
109
|
+
eq?: number;
|
|
110
|
+
ne?: number;
|
|
111
|
+
gt?: number;
|
|
112
|
+
gte?: number;
|
|
113
|
+
lt?: number;
|
|
114
|
+
lte?: number;
|
|
115
|
+
in?: number[];
|
|
116
|
+
notIn?: number[];
|
|
117
|
+
isNull?: boolean;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export interface BooleanFilter {
|
|
121
|
+
eq?: boolean;
|
|
122
|
+
ne?: boolean;
|
|
123
|
+
isNull?: boolean;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export interface DateFilter {
|
|
127
|
+
eq?: string;
|
|
128
|
+
ne?: string;
|
|
129
|
+
gt?: string;
|
|
130
|
+
gte?: string;
|
|
131
|
+
lt?: string;
|
|
132
|
+
lte?: string;
|
|
133
|
+
isNull?: boolean;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export interface SelectFilter<T extends string = string> {
|
|
137
|
+
eq?: T;
|
|
138
|
+
ne?: T;
|
|
139
|
+
in?: T[];
|
|
140
|
+
notIn?: T[];
|
|
141
|
+
isNull?: boolean;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export interface MultiselectFilter<T extends string = string> {
|
|
145
|
+
contains?: T;
|
|
146
|
+
containsAny?: T[];
|
|
147
|
+
containsAll?: T[];
|
|
148
|
+
isNull?: boolean;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export interface ReferenceFilter {
|
|
152
|
+
eq?: string;
|
|
153
|
+
ne?: string;
|
|
154
|
+
in?: string[];
|
|
155
|
+
notIn?: string[];
|
|
156
|
+
isNull?: boolean;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export interface FilterInput {
|
|
160
|
+
field: string;
|
|
161
|
+
operator: string;
|
|
162
|
+
value: unknown;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
export interface SortInput {
|
|
166
|
+
field: string;
|
|
167
|
+
direction: 'ASC' | 'DESC';
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// =============================================================================
|
|
171
|
+
// RESOLVE TYPES
|
|
172
|
+
// =============================================================================
|
|
173
|
+
|
|
174
|
+
/** Variant context for record resolution */
|
|
175
|
+
export interface VariantContext {
|
|
176
|
+
locale?: string;
|
|
177
|
+
device?: string;
|
|
178
|
+
region?: string;
|
|
179
|
+
contexts?: Record<string, unknown>;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/** Reference resolution options */
|
|
183
|
+
export interface ReferenceResolutionOptions {
|
|
184
|
+
maxDepth?: number;
|
|
185
|
+
resolveMedia?: boolean;
|
|
186
|
+
resolveReferences?: boolean;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/** Resolved record metadata */
|
|
190
|
+
export interface ResolvedRecord {
|
|
191
|
+
id: string;
|
|
192
|
+
modelKey: string;
|
|
193
|
+
naturalKey: string | null;
|
|
194
|
+
metadata?: Record<string, unknown>;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/** Resolved variant info */
|
|
198
|
+
export interface ResolvedVariant {
|
|
199
|
+
id: string;
|
|
200
|
+
variantKey: string;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/** Resolved field with value */
|
|
204
|
+
export interface ResolvedField {
|
|
205
|
+
key: string;
|
|
206
|
+
type: string;
|
|
207
|
+
label?: string;
|
|
208
|
+
required?: boolean;
|
|
209
|
+
value: unknown;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/** Resolved content */
|
|
213
|
+
export interface ResolvedContent {
|
|
214
|
+
fields: ResolvedField[];
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/** Resolution context output */
|
|
218
|
+
export interface ResolutionContext {
|
|
219
|
+
locale: string;
|
|
220
|
+
contexts: Record<string, unknown>;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/** Base resolved record content */
|
|
224
|
+
export interface ResolvedRecordContentBase {
|
|
225
|
+
record: ResolvedRecord;
|
|
226
|
+
variant: ResolvedVariant;
|
|
227
|
+
content: ResolvedContent;
|
|
228
|
+
resolvedWith: ResolutionContext;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// =============================================================================
|
|
232
|
+
// FIELD DEFINITION TYPES
|
|
233
|
+
// =============================================================================
|
|
234
|
+
|
|
235
|
+
export interface BaseFieldDef {
|
|
236
|
+
key: string;
|
|
237
|
+
label: string;
|
|
238
|
+
required?: boolean;
|
|
239
|
+
helpText?: string;
|
|
240
|
+
defaultValue?: unknown;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
export interface TextFieldDef extends BaseFieldDef {
|
|
244
|
+
type: 'text';
|
|
245
|
+
maxLength?: number;
|
|
246
|
+
minLength?: number;
|
|
247
|
+
pattern?: string;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
export interface NumberFieldDef extends BaseFieldDef {
|
|
251
|
+
type: 'number';
|
|
252
|
+
min?: number;
|
|
253
|
+
max?: number;
|
|
254
|
+
step?: number;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
export interface BooleanFieldDef extends BaseFieldDef {
|
|
258
|
+
type: 'boolean';
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
export interface DateFieldDef extends BaseFieldDef {
|
|
262
|
+
type: 'date';
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
export interface RichtextFieldDef extends BaseFieldDef {
|
|
266
|
+
type: 'richtext';
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
export interface ImageFieldDef extends BaseFieldDef {
|
|
270
|
+
type: 'image';
|
|
271
|
+
allowedTypes?: string[];
|
|
272
|
+
maxSize?: number;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
export interface VideoFieldDef extends BaseFieldDef {
|
|
276
|
+
type: 'video';
|
|
277
|
+
allowedTypes?: string[];
|
|
278
|
+
maxSize?: number;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
export interface FileFieldDef extends BaseFieldDef {
|
|
282
|
+
type: 'file';
|
|
283
|
+
allowedTypes?: string[];
|
|
284
|
+
maxSize?: number;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
export interface SelectFieldDef extends BaseFieldDef {
|
|
288
|
+
type: 'select';
|
|
289
|
+
options: Array<{ label: string; value: string }>;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
export interface MultiselectFieldDef extends BaseFieldDef {
|
|
293
|
+
type: 'multiselect';
|
|
294
|
+
options: Array<{ label: string; value: string }>;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
export interface LinkFieldDef extends BaseFieldDef {
|
|
298
|
+
type: 'link';
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
export interface ReferenceFieldDef extends BaseFieldDef {
|
|
302
|
+
type: 'entity-reference' | 'reference';
|
|
303
|
+
referenceTypes?: string[];
|
|
304
|
+
multiple?: boolean;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
export interface ListFieldDef extends BaseFieldDef {
|
|
308
|
+
type: 'list';
|
|
309
|
+
itemType?: string;
|
|
310
|
+
minItems?: number;
|
|
311
|
+
maxItems?: number;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
export interface JsonFieldDef extends BaseFieldDef {
|
|
315
|
+
type: 'json';
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
export interface FlexibleFieldDef extends BaseFieldDef {
|
|
319
|
+
type: 'flexible';
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
export type FieldDef =
|
|
323
|
+
| TextFieldDef
|
|
324
|
+
| NumberFieldDef
|
|
325
|
+
| BooleanFieldDef
|
|
326
|
+
| DateFieldDef
|
|
327
|
+
| RichtextFieldDef
|
|
328
|
+
| ImageFieldDef
|
|
329
|
+
| VideoFieldDef
|
|
330
|
+
| FileFieldDef
|
|
331
|
+
| SelectFieldDef
|
|
332
|
+
| MultiselectFieldDef
|
|
333
|
+
| LinkFieldDef
|
|
334
|
+
| ReferenceFieldDef
|
|
335
|
+
| ListFieldDef
|
|
336
|
+
| JsonFieldDef
|
|
337
|
+
| FlexibleFieldDef;
|
|
338
|
+
`;
|
|
339
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model-index.d.ts","sourceRoot":"","sources":["../../../src/codegen/generators/model-index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAOvD,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,YAAY,EAAE,GAAG,MAAM,CAoBjE"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generates the models/index.ts re-export file.
|
|
3
|
+
*/
|
|
4
|
+
import { toPascalCase, toCamelCase } from '../field-mapping.js';
|
|
5
|
+
function isInlineOnlyModel(model) {
|
|
6
|
+
return model.config.inline && !model.config.records;
|
|
7
|
+
}
|
|
8
|
+
export function generateModelIndex(models) {
|
|
9
|
+
let code = `/**
|
|
10
|
+
* Model Types and Configs — Generated re-exports
|
|
11
|
+
*
|
|
12
|
+
* @generated by foir — DO NOT EDIT MANUALLY
|
|
13
|
+
*/\n\n`;
|
|
14
|
+
for (const model of models) {
|
|
15
|
+
const typeName = toPascalCase(model.key);
|
|
16
|
+
const configName = toCamelCase(model.key) + 'Config';
|
|
17
|
+
if (isInlineOnlyModel(model)) {
|
|
18
|
+
code += `export type { ${typeName} } from './${model.key}.js';\n`;
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
code += `export { ${configName} } from './${model.key}.js';\n`;
|
|
22
|
+
code += `export type { ${typeName}Data } from './${model.key}.js';\n`;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return code;
|
|
26
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generates per-model TypeScript files with config object + data interface.
|
|
3
|
+
*/
|
|
4
|
+
import type { CodegenModel } from '../fetch-models.js';
|
|
5
|
+
/**
|
|
6
|
+
* Generate TypeScript code for a single model.
|
|
7
|
+
*
|
|
8
|
+
* - Inline-only models: data interface only (no config)
|
|
9
|
+
* - Models with records: config object + data interface
|
|
10
|
+
*/
|
|
11
|
+
export declare function generateModelTypes(model: CodegenModel, allModels: CodegenModel[]): string;
|
|
12
|
+
//# sourceMappingURL=model-types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model-types.d.ts","sourceRoot":"","sources":["../../../src/codegen/generators/model-types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAgBvD;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,YAAY,EACnB,SAAS,EAAE,YAAY,EAAE,GACxB,MAAM,CAoBR"}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generates per-model TypeScript files with config object + data interface.
|
|
3
|
+
*/
|
|
4
|
+
import { getFieldType, getInlineSchemaReferences, generateFieldDef, sanitizeFieldName, toPascalCase, toCamelCase, FIELD_TYPE_MAPPING, } from '../field-mapping.js';
|
|
5
|
+
function isInlineOnlyModel(model) {
|
|
6
|
+
return model.config.inline && !model.config.records;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Generate TypeScript code for a single model.
|
|
10
|
+
*
|
|
11
|
+
* - Inline-only models: data interface only (no config)
|
|
12
|
+
* - Models with records: config object + data interface
|
|
13
|
+
*/
|
|
14
|
+
export function generateModelTypes(model, allModels) {
|
|
15
|
+
const typeName = toPascalCase(model.key);
|
|
16
|
+
const configName = toCamelCase(model.key) + 'Config';
|
|
17
|
+
const fields = model.fields ?? [];
|
|
18
|
+
const fieldTypeImports = getFieldTypeImportsForFields(fields);
|
|
19
|
+
const inlineSchemaRefs = getInlineSchemaReferences(fields);
|
|
20
|
+
let code = buildImportStatements(model, fieldTypeImports, inlineSchemaRefs, allModels);
|
|
21
|
+
if (isInlineOnlyModel(model)) {
|
|
22
|
+
code += generateDataInterface(model, fields, typeName, allModels);
|
|
23
|
+
return code;
|
|
24
|
+
}
|
|
25
|
+
code += generateConfigObject(model, fields, configName);
|
|
26
|
+
code += '\n';
|
|
27
|
+
code += generateDataInterface(model, fields, typeName + 'Data', allModels);
|
|
28
|
+
return code;
|
|
29
|
+
}
|
|
30
|
+
function buildImportStatements(model, fieldTypeImports, inlineSchemaRefs, allModels) {
|
|
31
|
+
const imports = [];
|
|
32
|
+
if (!isInlineOnlyModel(model)) {
|
|
33
|
+
imports.push("import type { ModelConfig } from '../config.js';");
|
|
34
|
+
}
|
|
35
|
+
if (fieldTypeImports.size > 0) {
|
|
36
|
+
const types = Array.from(fieldTypeImports).sort().join(', ');
|
|
37
|
+
imports.push(`import type { ${types} } from '../field-types.js';`);
|
|
38
|
+
}
|
|
39
|
+
for (const refKey of inlineSchemaRefs) {
|
|
40
|
+
const refModel = allModels.find((m) => m.key === refKey);
|
|
41
|
+
if (refModel) {
|
|
42
|
+
const refTypeName = isInlineOnlyModel(refModel)
|
|
43
|
+
? toPascalCase(refKey)
|
|
44
|
+
: toPascalCase(refKey) + 'Data';
|
|
45
|
+
imports.push(`import type { ${refTypeName} } from './${refKey}.js';`);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return imports.length > 0 ? imports.join('\n') + '\n\n' : '';
|
|
49
|
+
}
|
|
50
|
+
function generateConfigObject(model, fields, configName) {
|
|
51
|
+
const lines = [];
|
|
52
|
+
lines.push('/**');
|
|
53
|
+
lines.push(` * ${model.name} Configuration`);
|
|
54
|
+
if (model.description)
|
|
55
|
+
lines.push(` * ${model.description}`);
|
|
56
|
+
lines.push(` *`);
|
|
57
|
+
lines.push(` * @generated from model '${model.key}'`);
|
|
58
|
+
lines.push(' */');
|
|
59
|
+
const escapedName = (model.name ?? model.key).replace(/'/g, "\\'");
|
|
60
|
+
lines.push(`export const ${configName} = {`);
|
|
61
|
+
lines.push(` key: '${model.key}',`);
|
|
62
|
+
lines.push(` name: '${escapedName}',`);
|
|
63
|
+
if (model.description) {
|
|
64
|
+
lines.push(` description: '${model.description.replace(/'/g, "\\'")}',`);
|
|
65
|
+
}
|
|
66
|
+
lines.push('');
|
|
67
|
+
lines.push(' // Capability flags');
|
|
68
|
+
lines.push(` records: ${model.config.records},`);
|
|
69
|
+
lines.push(` inline: ${model.config.inline},`);
|
|
70
|
+
lines.push(` publicApi: ${model.config.publicApi},`);
|
|
71
|
+
lines.push(` versioning: ${model.config.versioning},`);
|
|
72
|
+
lines.push(` publishing: ${model.config.publishing},`);
|
|
73
|
+
lines.push(` variants: ${model.config.variants},`);
|
|
74
|
+
lines.push(` customerScoped: ${model.config.customerScoped},`);
|
|
75
|
+
if (model.config.embeddings?.enabled) {
|
|
76
|
+
lines.push('');
|
|
77
|
+
lines.push(' // Embeddings');
|
|
78
|
+
lines.push(` embeddings: ${JSON.stringify(model.config.embeddings, null, 4).replace(/\n/g, '\n ')},`);
|
|
79
|
+
}
|
|
80
|
+
if (model.hooks && Object.keys(model.hooks).length > 0) {
|
|
81
|
+
lines.push('');
|
|
82
|
+
lines.push(' // Lifecycle hooks');
|
|
83
|
+
lines.push(` hooks: ${JSON.stringify(model.hooks, null, 4).replace(/\n/g, '\n ')},`);
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
lines.push('');
|
|
87
|
+
lines.push(' hooks: {},');
|
|
88
|
+
}
|
|
89
|
+
lines.push('');
|
|
90
|
+
lines.push(' // Field definitions');
|
|
91
|
+
if (fields.length === 0) {
|
|
92
|
+
lines.push(' fieldDefs: [],');
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
lines.push(' fieldDefs: [');
|
|
96
|
+
for (const field of fields) {
|
|
97
|
+
lines.push(` ${generateFieldDef(field)},`);
|
|
98
|
+
}
|
|
99
|
+
lines.push(' ],');
|
|
100
|
+
}
|
|
101
|
+
lines.push('} as const satisfies ModelConfig;');
|
|
102
|
+
return lines.join('\n') + '\n';
|
|
103
|
+
}
|
|
104
|
+
function generateDataInterface(model, fields, interfaceName, allModels) {
|
|
105
|
+
const lines = [];
|
|
106
|
+
lines.push('/**');
|
|
107
|
+
lines.push(` * ${model.name} Data`);
|
|
108
|
+
lines.push(` * Field values only — no system fields`);
|
|
109
|
+
lines.push(` *`);
|
|
110
|
+
lines.push(` * @generated from model '${model.key}'`);
|
|
111
|
+
lines.push(' */');
|
|
112
|
+
lines.push(`export interface ${interfaceName} {`);
|
|
113
|
+
for (const field of fields) {
|
|
114
|
+
const fieldName = sanitizeFieldName(field.key);
|
|
115
|
+
let fieldType = getFieldType(field, 'output');
|
|
116
|
+
// Adjust type name for inline schema refs to models with records mode
|
|
117
|
+
const refModel = allModels.find((m) => m.key === field.type);
|
|
118
|
+
if (refModel && !isInlineOnlyModel(refModel)) {
|
|
119
|
+
fieldType = toPascalCase(field.type) + 'Data';
|
|
120
|
+
}
|
|
121
|
+
// Handle list/tree itemType refs
|
|
122
|
+
if ((field.type === 'list' || field.type === 'tree') && field.options?.itemType) {
|
|
123
|
+
const itemRefModel = allModels.find((m) => m.key === field.options.itemType);
|
|
124
|
+
if (itemRefModel && !isInlineOnlyModel(itemRefModel)) {
|
|
125
|
+
fieldType = toPascalCase(field.options.itemType) + 'Data[]';
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
const optional = field.required ? '' : '?';
|
|
129
|
+
const comment = field.helpText ? ` /** ${field.helpText} */\n` : '';
|
|
130
|
+
lines.push(comment + ` ${fieldName}${optional}: ${fieldType};`);
|
|
131
|
+
}
|
|
132
|
+
lines.push('}');
|
|
133
|
+
return lines.join('\n') + '\n';
|
|
134
|
+
}
|
|
135
|
+
function getFieldTypeImportsForFields(fields) {
|
|
136
|
+
const imports = new Set();
|
|
137
|
+
for (const field of fields) {
|
|
138
|
+
const mapping = FIELD_TYPE_MAPPING[field.type];
|
|
139
|
+
if (mapping?.needsImport === 'field-types') {
|
|
140
|
+
imports.add(mapping.outputType);
|
|
141
|
+
}
|
|
142
|
+
if ((field.type === 'list' || field.type === 'tree') && field.options?.itemType) {
|
|
143
|
+
const itemMapping = FIELD_TYPE_MAPPING[field.options.itemType];
|
|
144
|
+
if (itemMapping?.needsImport === 'field-types') {
|
|
145
|
+
imports.add(itemMapping.outputType);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
return imports;
|
|
150
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File writer with optional Prettier formatting.
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Write a generated file to disk with optional Prettier formatting.
|
|
6
|
+
*/
|
|
7
|
+
export declare function writeGeneratedFile(filePath: string, content: string, usePrettier?: boolean): Promise<void>;
|
|
8
|
+
/**
|
|
9
|
+
* Write multiple files in batch.
|
|
10
|
+
*/
|
|
11
|
+
export declare function writeFiles(files: Array<{
|
|
12
|
+
path: string;
|
|
13
|
+
content: string;
|
|
14
|
+
}>, usePrettier?: boolean): Promise<void>;
|
|
15
|
+
//# sourceMappingURL=write-files.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"write-files.d.ts","sourceRoot":"","sources":["../../src/codegen/write-files.ts"],"names":[],"mappings":"AAAA;;GAEG;AAKH;;GAEG;AACH,wBAAsB,kBAAkB,CACtC,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,WAAW,GAAE,OAAc,GAC1B,OAAO,CAAC,IAAI,CAAC,CAqBf;AAED;;GAEG;AACH,wBAAsB,UAAU,CAC9B,KAAK,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,EAC/C,WAAW,GAAE,OAAc,GAC1B,OAAO,CAAC,IAAI,CAAC,CAIf"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File writer with optional Prettier formatting.
|
|
3
|
+
*/
|
|
4
|
+
import { mkdir, writeFile } from 'fs/promises';
|
|
5
|
+
import { dirname } from 'path';
|
|
6
|
+
/**
|
|
7
|
+
* Write a generated file to disk with optional Prettier formatting.
|
|
8
|
+
*/
|
|
9
|
+
export async function writeGeneratedFile(filePath, content, usePrettier = true) {
|
|
10
|
+
await mkdir(dirname(filePath), { recursive: true });
|
|
11
|
+
let formattedContent = content;
|
|
12
|
+
if (usePrettier) {
|
|
13
|
+
try {
|
|
14
|
+
const prettier = await import('prettier');
|
|
15
|
+
const parser = filePath.endsWith('.graphql') ? 'graphql' : 'typescript';
|
|
16
|
+
formattedContent = await prettier.format(content, {
|
|
17
|
+
parser,
|
|
18
|
+
semi: true,
|
|
19
|
+
singleQuote: true,
|
|
20
|
+
trailingComma: 'es5',
|
|
21
|
+
printWidth: 100,
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
// Prettier not available or formatting failed — write unformatted
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
await writeFile(filePath, formattedContent, 'utf-8');
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Write multiple files in batch.
|
|
32
|
+
*/
|
|
33
|
+
export async function writeFiles(files, usePrettier = true) {
|
|
34
|
+
await Promise.all(files.map((file) => writeGeneratedFile(file.path, file.content, usePrettier)));
|
|
35
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"models.d.ts","sourceRoot":"","sources":["../../src/commands/models.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AActD,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,OAAO,EAChB,UAAU,EAAE,MAAM,aAAa,GAC9B,IAAI,
|
|
1
|
+
{"version":3,"file":"models.d.ts","sourceRoot":"","sources":["../../src/commands/models.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AActD,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,OAAO,EAChB,UAAU,EAAE,MAAM,aAAa,GAC9B,IAAI,CAiMN"}
|
package/dist/commands/models.js
CHANGED
|
@@ -27,12 +27,6 @@ export function registerModelsCommands(program, globalOpts) {
|
|
|
27
27
|
{ key: 'key', header: 'Key', width: 24 },
|
|
28
28
|
{ key: 'name', header: 'Name', width: 24 },
|
|
29
29
|
{ key: 'category', header: 'Category', width: 14 },
|
|
30
|
-
{
|
|
31
|
-
key: 'systemEntity',
|
|
32
|
-
header: 'System',
|
|
33
|
-
width: 8,
|
|
34
|
-
format: (v) => (v ? 'yes' : 'no'),
|
|
35
|
-
},
|
|
36
30
|
{
|
|
37
31
|
key: 'updatedAt',
|
|
38
32
|
header: 'Updated',
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pull.d.ts","sourceRoot":"","sources":["../../src/commands/pull.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEzC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAYtD,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,OAAO,EAChB,UAAU,EAAE,MAAM,aAAa,GAC9B,IAAI,CAmHN"}
|