@eide/foir-cli 0.1.2 → 0.1.4

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 (52) hide show
  1. package/dist/cli.js +3 -0
  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/generators/swift-field-types.d.ts +5 -0
  24. package/dist/codegen/generators/swift-field-types.d.ts.map +1 -0
  25. package/dist/codegen/generators/swift-field-types.js +145 -0
  26. package/dist/codegen/generators/swift-model-keys.d.ts +6 -0
  27. package/dist/codegen/generators/swift-model-keys.d.ts.map +1 -0
  28. package/dist/codegen/generators/swift-model-keys.js +25 -0
  29. package/dist/codegen/generators/swift-types.d.ts +13 -0
  30. package/dist/codegen/generators/swift-types.d.ts.map +1 -0
  31. package/dist/codegen/generators/swift-types.js +184 -0
  32. package/dist/codegen/swift-field-mapping.d.ts +30 -0
  33. package/dist/codegen/swift-field-mapping.d.ts.map +1 -0
  34. package/dist/codegen/swift-field-mapping.js +156 -0
  35. package/dist/codegen/write-files.d.ts +15 -0
  36. package/dist/codegen/write-files.d.ts.map +1 -0
  37. package/dist/codegen/write-files.js +36 -0
  38. package/dist/commands/models.d.ts.map +1 -1
  39. package/dist/commands/models.js +0 -6
  40. package/dist/commands/pull.d.ts +4 -0
  41. package/dist/commands/pull.d.ts.map +1 -0
  42. package/dist/commands/pull.js +150 -0
  43. package/dist/config/pull-config.d.ts +27 -0
  44. package/dist/config/pull-config.d.ts.map +1 -0
  45. package/dist/config/pull-config.js +66 -0
  46. package/dist/config/types.d.ts +33 -0
  47. package/dist/config/types.d.ts.map +1 -0
  48. package/dist/config/types.js +15 -0
  49. package/dist/graphql/queries.d.ts +1 -0
  50. package/dist/graphql/queries.d.ts.map +1 -1
  51. package/dist/graphql/queries.js +8 -0
  52. package/package.json +9 -3
@@ -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,6 @@
1
+ /**
2
+ * Generates the models/index.ts re-export file.
3
+ */
4
+ import type { CodegenModel } from '../fetch-models.js';
5
+ export declare function generateModelIndex(models: CodegenModel[]): string;
6
+ //# sourceMappingURL=model-index.d.ts.map
@@ -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,5 @@
1
+ /**
2
+ * Generates the shared FieldTypes.swift file with platform value type structs.
3
+ */
4
+ export declare function generateSwiftFieldTypesFile(): string;
5
+ //# sourceMappingURL=swift-field-types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"swift-field-types.d.ts","sourceRoot":"","sources":["../../../src/codegen/generators/swift-field-types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,wBAAgB,2BAA2B,IAAI,MAAM,CA6IpD"}
@@ -0,0 +1,145 @@
1
+ /**
2
+ * Generates the shared FieldTypes.swift file with platform value type structs.
3
+ */
4
+ export function generateSwiftFieldTypesFile() {
5
+ return `//
6
+ // FieldTypes.swift
7
+ //
8
+ // Shared value types for platform sync data.
9
+ //
10
+ // @generated by foir \u2014 DO NOT EDIT MANUALLY
11
+ //
12
+
13
+ import Foundation
14
+
15
+ // MARK: - Image
16
+
17
+ struct ImageValue {
18
+ let id: String
19
+ let url: String
20
+ var alt: String?
21
+ var width: Int?
22
+ var height: Int?
23
+
24
+ func toSyncData() -> [String: Any] {
25
+ var data: [String: Any] = ["id": id, "url": url]
26
+ if let alt { data["alt"] = alt }
27
+ if let width { data["width"] = width }
28
+ if let height { data["height"] = height }
29
+ return data
30
+ }
31
+
32
+ static func fromSyncData(_ data: [String: Any]) -> ImageValue {
33
+ ImageValue(
34
+ id: data["id"] as? String ?? "",
35
+ url: data["url"] as? String ?? "",
36
+ alt: data["alt"] as? String,
37
+ width: data["width"] as? Int,
38
+ height: data["height"] as? Int
39
+ )
40
+ }
41
+ }
42
+
43
+ // MARK: - Video
44
+
45
+ struct VideoValue {
46
+ let id: String
47
+ let url: String
48
+ var thumbnail: String?
49
+ var duration: Double?
50
+
51
+ func toSyncData() -> [String: Any] {
52
+ var data: [String: Any] = ["id": id, "url": url]
53
+ if let thumbnail { data["thumbnail"] = thumbnail }
54
+ if let duration { data["duration"] = duration }
55
+ return data
56
+ }
57
+
58
+ static func fromSyncData(_ data: [String: Any]) -> VideoValue {
59
+ VideoValue(
60
+ id: data["id"] as? String ?? "",
61
+ url: data["url"] as? String ?? "",
62
+ thumbnail: data["thumbnail"] as? String,
63
+ duration: data["duration"] as? Double
64
+ )
65
+ }
66
+ }
67
+
68
+ // MARK: - File
69
+
70
+ struct FileValue {
71
+ let id: String
72
+ let url: String
73
+ let name: String
74
+ let size: Int
75
+ let mimeType: String
76
+
77
+ func toSyncData() -> [String: Any] {
78
+ [
79
+ "id": id,
80
+ "url": url,
81
+ "name": name,
82
+ "size": size,
83
+ "mimeType": mimeType,
84
+ ]
85
+ }
86
+
87
+ static func fromSyncData(_ data: [String: Any]) -> FileValue {
88
+ FileValue(
89
+ id: data["id"] as? String ?? "",
90
+ url: data["url"] as? String ?? "",
91
+ name: data["name"] as? String ?? "",
92
+ size: data["size"] as? Int ?? 0,
93
+ mimeType: data["mimeType"] as? String ?? ""
94
+ )
95
+ }
96
+ }
97
+
98
+ // MARK: - Currency
99
+
100
+ struct CurrencyValue {
101
+ let amount: Double
102
+ let currency: String
103
+
104
+ func toSyncData() -> [String: Any] {
105
+ ["amount": amount, "currency": currency]
106
+ }
107
+
108
+ static func fromSyncData(_ data: [String: Any]) -> CurrencyValue {
109
+ CurrencyValue(
110
+ amount: data["amount"] as? Double ?? 0,
111
+ currency: data["currency"] as? String ?? ""
112
+ )
113
+ }
114
+ }
115
+
116
+ // MARK: - Link
117
+
118
+ struct LinkValue {
119
+ let type: String
120
+ var entityModelKey: String?
121
+ var entityNaturalKey: String?
122
+ var url: String?
123
+ var target: String?
124
+
125
+ func toSyncData() -> [String: Any] {
126
+ var data: [String: Any] = ["type": type]
127
+ if let entityModelKey { data["entityModelKey"] = entityModelKey }
128
+ if let entityNaturalKey { data["entityNaturalKey"] = entityNaturalKey }
129
+ if let url { data["url"] = url }
130
+ if let target { data["target"] = target }
131
+ return data
132
+ }
133
+
134
+ static func fromSyncData(_ data: [String: Any]) -> LinkValue {
135
+ LinkValue(
136
+ type: data["type"] as? String ?? "",
137
+ entityModelKey: data["entityModelKey"] as? String,
138
+ entityNaturalKey: data["entityNaturalKey"] as? String,
139
+ url: data["url"] as? String,
140
+ target: data["target"] as? String
141
+ )
142
+ }
143
+ }
144
+ `;
145
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Generates ModelKeys.swift with all model key constants in one enum.
3
+ */
4
+ import type { CodegenModel } from '../fetch-models.js';
5
+ export declare function generateSwiftModelKeys(models: CodegenModel[]): string;
6
+ //# sourceMappingURL=swift-model-keys.d.ts.map