@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.
Files changed (40) hide show
  1. package/dist/cli.js +8 -2
  2. package/dist/codegen/fetch-models.d.ts +40 -0
  3. package/dist/codegen/fetch-models.d.ts.map +1 -0
  4. package/dist/codegen/fetch-models.js +45 -0
  5. package/dist/codegen/field-mapping.d.ts +28 -0
  6. package/dist/codegen/field-mapping.d.ts.map +1 -0
  7. package/dist/codegen/field-mapping.js +194 -0
  8. package/dist/codegen/generators/config.d.ts +5 -0
  9. package/dist/codegen/generators/config.d.ts.map +1 -0
  10. package/dist/codegen/generators/config.js +82 -0
  11. package/dist/codegen/generators/documents.d.ts +6 -0
  12. package/dist/codegen/generators/documents.d.ts.map +1 -0
  13. package/dist/codegen/generators/documents.js +91 -0
  14. package/dist/codegen/generators/field-types.d.ts +5 -0
  15. package/dist/codegen/generators/field-types.d.ts.map +1 -0
  16. package/dist/codegen/generators/field-types.js +339 -0
  17. package/dist/codegen/generators/model-index.d.ts +6 -0
  18. package/dist/codegen/generators/model-index.d.ts.map +1 -0
  19. package/dist/codegen/generators/model-index.js +26 -0
  20. package/dist/codegen/generators/model-types.d.ts +12 -0
  21. package/dist/codegen/generators/model-types.d.ts.map +1 -0
  22. package/dist/codegen/generators/model-types.js +150 -0
  23. package/dist/codegen/write-files.d.ts +15 -0
  24. package/dist/codegen/write-files.d.ts.map +1 -0
  25. package/dist/codegen/write-files.js +35 -0
  26. package/dist/commands/models.d.ts.map +1 -1
  27. package/dist/commands/models.js +0 -6
  28. package/dist/commands/pull.d.ts +4 -0
  29. package/dist/commands/pull.d.ts.map +1 -0
  30. package/dist/commands/pull.js +118 -0
  31. package/dist/config/pull-config.d.ts +25 -0
  32. package/dist/config/pull-config.d.ts.map +1 -0
  33. package/dist/config/pull-config.js +65 -0
  34. package/dist/config/types.d.ts +31 -0
  35. package/dist/config/types.d.ts.map +1 -0
  36. package/dist/config/types.js +15 -0
  37. package/dist/graphql/queries.d.ts +1 -0
  38. package/dist/graphql/queries.d.ts.map +1 -1
  39. package/dist/graphql/queries.js +8 -0
  40. package/package.json +19 -13
package/dist/cli.js CHANGED
@@ -5,7 +5,10 @@ import { fileURLToPath } from 'url';
5
5
  const __filename = fileURLToPath(import.meta.url);
6
6
  const __dirname = dirname(__filename);
7
7
  config({ path: resolve(__dirname, '../.env.local') });
8
+ import { createRequire } from 'module';
8
9
  import { Command } from 'commander';
10
+ const require = createRequire(import.meta.url);
11
+ const { version } = require('../package.json');
9
12
  // Auth commands
10
13
  import { registerLoginCommand } from './commands/login.js';
11
14
  import { registerLogoutCommand } from './commands/logout.js';
@@ -30,11 +33,12 @@ import { registerLocalesCommands } from './commands/locales.js';
30
33
  import { registerFilesCommands } from './commands/files.js';
31
34
  import { registerNotesCommands } from './commands/notes.js';
32
35
  import { registerVariantCatalogCommands } from './commands/variant-catalog.js';
36
+ import { registerPullCommand } from './commands/pull.js';
33
37
  const program = new Command();
34
38
  program
35
39
  .name('foir')
36
- .description('Universal platform CLI for EIDE')
37
- .version('0.1.0')
40
+ .description('CLI for Foir platform')
41
+ .version(version)
38
42
  .option('--api-url <url>', 'API endpoint URL')
39
43
  .option('--json', 'Output as JSON')
40
44
  .option('--jsonl', 'Output as JSON Lines')
@@ -71,4 +75,6 @@ registerLocalesCommands(program, getGlobalOpts);
71
75
  registerFilesCommands(program, getGlobalOpts);
72
76
  registerNotesCommands(program, getGlobalOpts);
73
77
  registerVariantCatalogCommands(program, getGlobalOpts);
78
+ // Codegen
79
+ registerPullCommand(program, getGlobalOpts);
74
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,5 @@
1
+ /**
2
+ * Generates the static config.ts file with ModelConfig type and defineModel() helper.
3
+ */
4
+ export declare function generateConfigFile(): string;
5
+ //# sourceMappingURL=config.d.ts.map
@@ -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,6 @@
1
+ /**
2
+ * Generates per-model .graphql files with CRUD operations for the public API.
3
+ */
4
+ import type { CodegenModel } from '../fetch-models.js';
5
+ export declare function generateModelDocuments(model: CodegenModel): string;
6
+ //# sourceMappingURL=documents.d.ts.map
@@ -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,5 @@
1
+ /**
2
+ * Generates the static field-types.ts file with value types, filter types, and field defs.
3
+ */
4
+ export declare function generateFieldTypesFile(): string;
5
+ //# sourceMappingURL=field-types.d.ts.map
@@ -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"}