@eide/foir-cli 0.1.32 → 0.1.34

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.
@@ -20,6 +20,11 @@ export interface FieldSchemaForGen {
20
20
  }
21
21
  export declare function getFieldType(field: FieldSchemaForGen, mode?: 'output' | 'input'): string;
22
22
  export declare function getRequiredImports(fields: FieldSchemaForGen[]): Set<string>;
23
+ /**
24
+ * Extract model keys referenced by reference-type fields via `referenceTypes`.
25
+ * Used to generate parameterized `ReferenceValue<Partial<ModelData>>` types.
26
+ */
27
+ export declare function getReferenceTypeModelRefs(fields: FieldSchemaForGen[]): Set<string>;
23
28
  export declare function getInlineSchemaReferences(fields: FieldSchemaForGen[]): Set<string>;
24
29
  export declare function toCamelCase(str: string): string;
25
30
  export declare function toPascalCase(str: string): string;
@@ -1 +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,aAoBhC,CAAC;AAEH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAE1D;AAED,eAAO,MAAM,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAoD1D,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,CACvC,MAAM,EAAE,iBAAiB,EAAE,GAC1B,GAAG,CAAC,MAAM,CAAC,CAcb;AAED,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAK/C;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAIhD;AAED,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAMpD;AAED,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAKrD;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,iBAAiB,GAAG,MAAM,CA2EjE"}
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,aAqBhC,CAAC;AAEH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAE1D;AAED,eAAO,MAAM,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAqD1D,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;;;GAGG;AACH,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,iBAAiB,EAAE,GAC1B,GAAG,CAAC,MAAM,CAAC,CAUb;AAED,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,iBAAiB,EAAE,GAC1B,GAAG,CAAC,MAAM,CAAC,CAcb;AAED,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAK/C;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAIhD;AAED,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAMpD;AAED,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAKrD;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,iBAAiB,GAAG,MAAM,CA2EjE"}
@@ -22,6 +22,7 @@ export const PRIMITIVE_FIELD_TYPES = new Set([
22
22
  'reference',
23
23
  'link',
24
24
  'flexible',
25
+ 'model',
25
26
  ]);
26
27
  export function isPrimitiveFieldType(type) {
27
28
  return PRIMITIVE_FIELD_TYPES.has(type);
@@ -78,6 +79,7 @@ export const FIELD_TYPE_MAPPING = {
78
79
  inputType: 'LinkValue',
79
80
  needsImport: 'field-types',
80
81
  },
82
+ model: { outputType: 'string', inputType: 'string' },
81
83
  };
82
84
  export function getFieldType(field, mode = 'output') {
83
85
  if (!field?.type)
@@ -127,6 +129,21 @@ export function getRequiredImports(fields) {
127
129
  }
128
130
  return imports;
129
131
  }
132
+ /**
133
+ * Extract model keys referenced by reference-type fields via `referenceTypes`.
134
+ * Used to generate parameterized `ReferenceValue<Partial<ModelData>>` types.
135
+ */
136
+ export function getReferenceTypeModelRefs(fields) {
137
+ const refs = new Set();
138
+ for (const field of fields) {
139
+ if (field.type === 'reference' && field.options?.referenceTypes) {
140
+ for (const rt of field.options.referenceTypes) {
141
+ refs.add(rt);
142
+ }
143
+ }
144
+ }
145
+ return refs;
146
+ }
130
147
  export function getInlineSchemaReferences(fields) {
131
148
  const refs = new Set();
132
149
  for (const field of fields) {
@@ -10,7 +10,7 @@ export function generateConfigFile() {
10
10
  * @generated by foir — DO NOT EDIT MANUALLY
11
11
  */
12
12
 
13
- import type { FieldDef } from './field-types.js';
13
+ import type { FieldDef, JsonValue } from './field-types.js';
14
14
 
15
15
  /**
16
16
  * Model configuration
@@ -66,7 +66,7 @@ export interface ModelConfig {
66
66
  };
67
67
 
68
68
  /** Lifecycle hooks configuration */
69
- hooks?: Record<string, unknown>;
69
+ hooks?: Record<string, JsonValue>;
70
70
 
71
71
  /** Field definitions */
72
72
  fieldDefs: readonly FieldDef[];
@@ -1 +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,CAiGlE;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,IAAI,MAAM,CAoBhD"}
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,CAyGlE;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,IAAI,MAAM,CAoBhD"}
@@ -25,10 +25,10 @@ fragment ${typeName}Fields on Record {
25
25
  updatedAt
26
26
  }
27
27
 
28
- query Get${typeName}($id: ID!, $locale: String, $preview: Boolean) {
28
+ query Get${typeName}($id: ID!, $locale: String, $preview: Boolean, $fields: FieldSelectionInput) {
29
29
  record(id: $id) {
30
30
  ...${typeName}Fields
31
- resolved(locale: $locale, preview: $preview) {
31
+ resolved(locale: $locale, preview: $preview, fields: $fields) {
32
32
  content
33
33
  record { id modelKey naturalKey }
34
34
  version { id versionNumber }
@@ -36,10 +36,10 @@ query Get${typeName}($id: ID!, $locale: String, $preview: Boolean) {
36
36
  }
37
37
  }
38
38
 
39
- query Get${typeName}ByKey($naturalKey: String!, $locale: String, $preview: Boolean) {
39
+ query Get${typeName}ByKey($naturalKey: String!, $locale: String, $preview: Boolean, $fields: FieldSelectionInput) {
40
40
  recordByKey(modelKey: "${model.key}", naturalKey: $naturalKey) {
41
41
  ...${typeName}Fields
42
- resolved(locale: $locale, preview: $preview) {
42
+ resolved(locale: $locale, preview: $preview, fields: $fields) {
43
43
  content
44
44
  record { id modelKey naturalKey }
45
45
  version { id versionNumber }
@@ -52,6 +52,9 @@ query List${pluralName}(
52
52
  $offset: Int
53
53
  $filters: [FilterInput!]
54
54
  $sort: SortInput
55
+ $locale: String
56
+ $preview: Boolean
57
+ $fields: FieldSelectionInput
55
58
  ) {
56
59
  records(
57
60
  modelKey: "${model.key}"
@@ -62,6 +65,11 @@ query List${pluralName}(
62
65
  ) {
63
66
  items {
64
67
  ...${typeName}Fields
68
+ resolved(locale: $locale, preview: $preview, fields: $fields) {
69
+ content
70
+ record { id modelKey naturalKey }
71
+ version { id versionNumber }
72
+ }
65
73
  }
66
74
  total
67
75
  }
@@ -1 +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"}
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,CAsV/C"}
@@ -10,12 +10,19 @@ export function generateFieldTypesFile() {
10
10
  * @generated by foir — DO NOT EDIT MANUALLY
11
11
  */
12
12
 
13
+ // =============================================================================
14
+ // JSON-SAFE BASE TYPE
15
+ // =============================================================================
16
+
17
+ /** Recursive JSON-serializable value type. Safe for TanStack Router, Remix, RSC, and other serialization boundaries. */
18
+ export type JsonValue = string | number | boolean | null | JsonValue[] | { [key: string]: JsonValue };
19
+
13
20
  // =============================================================================
14
21
  // VALUE TYPES
15
22
  // =============================================================================
16
23
 
17
24
  /** Rich text content (Lexical JSON format) */
18
- export type RichtextValue = unknown;
25
+ export type RichtextValue = JsonValue;
19
26
 
20
27
  /** Currency value with amount and ISO 4217 code */
21
28
  export interface CurrencyValue {
@@ -63,19 +70,19 @@ export interface LinkRecordReference {
63
70
  naturalKey: string;
64
71
  }
65
72
 
66
- /** Record reference value */
67
- export interface ReferenceValue {
73
+ /** Record reference value (generic TPreview for typed preview data when reference target is known) */
74
+ export interface ReferenceValue<TPreview = Record<string, JsonValue>> {
68
75
  _type: 'reference';
69
76
  _schema: string;
70
77
  naturalKey: string;
71
- _preview?: Record<string, unknown>;
78
+ _preview?: TPreview;
72
79
  }
73
80
 
74
81
  /** Composite/inline value */
75
82
  export interface CompositeValue {
76
83
  _type: 'composite';
77
84
  _schema: string;
78
- fields: Record<string, unknown>;
85
+ fields: Record<string, JsonValue>;
79
86
  }
80
87
 
81
88
  /** A single item in a flexible field array */
@@ -86,8 +93,8 @@ export interface FlexibleFieldItem {
86
93
  _label: string;
87
94
  _required?: boolean;
88
95
  _helpText?: string;
89
- _config?: Record<string, unknown>;
90
- value: unknown;
96
+ _config?: Record<string, JsonValue>;
97
+ value: JsonValue;
91
98
  }
92
99
 
93
100
  // =============================================================================
@@ -159,7 +166,7 @@ export interface ReferenceFilter {
159
166
  export interface FilterInput {
160
167
  field: string;
161
168
  operator: string;
162
- value: unknown;
169
+ value: JsonValue;
163
170
  }
164
171
 
165
172
  export interface SortInput {
@@ -176,7 +183,7 @@ export interface VariantContext {
176
183
  locale?: string;
177
184
  device?: string;
178
185
  region?: string;
179
- contexts?: Record<string, unknown>;
186
+ contexts?: Record<string, JsonValue>;
180
187
  }
181
188
 
182
189
  /** Reference resolution options */
@@ -191,7 +198,7 @@ export interface ResolvedRecord {
191
198
  id: string;
192
199
  modelKey: string;
193
200
  naturalKey: string | null;
194
- metadata?: Record<string, unknown>;
201
+ metadata?: Record<string, JsonValue>;
195
202
  }
196
203
 
197
204
  /** Resolved variant info */
@@ -206,7 +213,7 @@ export interface ResolvedField {
206
213
  type: string;
207
214
  label?: string;
208
215
  required?: boolean;
209
- value: unknown;
216
+ value: JsonValue;
210
217
  }
211
218
 
212
219
  /** Resolved content */
@@ -217,7 +224,7 @@ export interface ResolvedContent {
217
224
  /** Resolution context output */
218
225
  export interface ResolutionContext {
219
226
  locale: string;
220
- contexts: Record<string, unknown>;
227
+ contexts: Record<string, JsonValue>;
221
228
  }
222
229
 
223
230
  /** Base resolved record content */
@@ -237,7 +244,7 @@ export interface BaseFieldDef {
237
244
  label: string;
238
245
  required?: boolean;
239
246
  helpText?: string;
240
- defaultValue?: unknown;
247
+ defaultValue?: JsonValue;
241
248
  }
242
249
 
243
250
  export interface TextFieldDef extends BaseFieldDef {
@@ -1 +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,CAyBR"}
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;AAiBvD;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,YAAY,EACnB,SAAS,EAAE,YAAY,EAAE,GACxB,MAAM,CA2BR"}
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Generates per-model TypeScript files with config object + data interface.
3
3
  */
4
- import { getFieldType, getInlineSchemaReferences, generateFieldDef, sanitizeFieldName, toPascalCase, toCamelCase, FIELD_TYPE_MAPPING, } from '../field-mapping.js';
4
+ import { getFieldType, getInlineSchemaReferences, getReferenceTypeModelRefs, generateFieldDef, sanitizeFieldName, toPascalCase, toCamelCase, FIELD_TYPE_MAPPING, } from '../field-mapping.js';
5
5
  function isInlineOnlyModel(model) {
6
6
  return model.config.inline && !model.config.records;
7
7
  }
@@ -17,7 +17,8 @@ export function generateModelTypes(model, allModels) {
17
17
  const fields = model.fields ?? [];
18
18
  const fieldTypeImports = getFieldTypeImportsForFields(fields);
19
19
  const inlineSchemaRefs = getInlineSchemaReferences(fields);
20
- let code = buildImportStatements(model, fieldTypeImports, inlineSchemaRefs, allModels);
20
+ const referenceModelRefs = getReferenceTypeModelRefs(fields);
21
+ let code = buildImportStatements(model, fieldTypeImports, inlineSchemaRefs, referenceModelRefs, allModels);
21
22
  if (isInlineOnlyModel(model)) {
22
23
  code += generateDataInterface(model, fields, typeName, allModels);
23
24
  return code;
@@ -27,7 +28,7 @@ export function generateModelTypes(model, allModels) {
27
28
  code += generateDataInterface(model, fields, typeName + 'Data', allModels);
28
29
  return code;
29
30
  }
30
- function buildImportStatements(model, fieldTypeImports, inlineSchemaRefs, allModels) {
31
+ function buildImportStatements(model, fieldTypeImports, inlineSchemaRefs, referenceModelRefs, allModels) {
31
32
  const imports = [];
32
33
  if (!isInlineOnlyModel(model)) {
33
34
  imports.push("import type { ModelConfig } from '../config.js';");
@@ -36,7 +37,12 @@ function buildImportStatements(model, fieldTypeImports, inlineSchemaRefs, allMod
36
37
  const types = Array.from(fieldTypeImports).sort().join(', ');
37
38
  imports.push(`import type { ${types} } from '../field-types.js';`);
38
39
  }
39
- for (const refKey of inlineSchemaRefs) {
40
+ // Collect all model refs (inline schemas + reference type targets) to avoid duplicate imports
41
+ const allModelRefKeys = new Set([
42
+ ...inlineSchemaRefs,
43
+ ...referenceModelRefs,
44
+ ]);
45
+ for (const refKey of allModelRefKeys) {
40
46
  if (refKey === model.key)
41
47
  continue; // skip self-reference
42
48
  const refModel = allModels.find((m) => m.key === refKey);
@@ -127,6 +133,24 @@ function generateDataInterface(model, fields, interfaceName, allModels) {
127
133
  fieldType = toPascalCase(field.options.itemType) + 'Data[]';
128
134
  }
129
135
  }
136
+ // Parameterize ReferenceValue with known reference target types for typed _preview
137
+ if (field.type === 'reference' && field.options?.referenceTypes) {
138
+ const refTypes = field.options.referenceTypes;
139
+ const resolvedPreviewTypes = [];
140
+ for (const refKey of refTypes) {
141
+ const targetModel = allModels.find((m) => m.key === refKey);
142
+ if (targetModel) {
143
+ const targetTypeName = isInlineOnlyModel(targetModel)
144
+ ? toPascalCase(refKey)
145
+ : toPascalCase(refKey) + 'Data';
146
+ resolvedPreviewTypes.push(`Partial<${targetTypeName}>`);
147
+ }
148
+ }
149
+ // Only parameterize if all targets resolved — otherwise keep default generic
150
+ if (resolvedPreviewTypes.length === refTypes.length && resolvedPreviewTypes.length > 0) {
151
+ fieldType = `ReferenceValue<${resolvedPreviewTypes.join(' | ')}>`;
152
+ }
153
+ }
130
154
  const optional = field.required ? '' : '?';
131
155
  const comment = field.helpText ? ` /** ${field.helpText} */\n` : '';
132
156
  lines.push(comment + ` ${fieldName}${optional}: ${fieldType};`);
@@ -2,5 +2,5 @@
2
2
  * Generates the shared _common.ts file for typed operations.
3
3
  * Contains generic wrapper types used by all per-model operation modules.
4
4
  */
5
- export declare function generateTypedOperationsCommon(): string;
5
+ export declare function generateTypedOperationsCommon(typesRelPath: string): string;
6
6
  //# sourceMappingURL=typed-operations-common.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"typed-operations-common.d.ts","sourceRoot":"","sources":["../../../src/codegen/generators/typed-operations-common.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,wBAAgB,6BAA6B,IAAI,MAAM,CAqEtD"}
1
+ {"version":3,"file":"typed-operations-common.d.ts","sourceRoot":"","sources":["../../../src/codegen/generators/typed-operations-common.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,wBAAgB,6BAA6B,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CA+E1E"}
@@ -2,20 +2,22 @@
2
2
  * Generates the shared _common.ts file for typed operations.
3
3
  * Contains generic wrapper types used by all per-model operation modules.
4
4
  */
5
- export function generateTypedOperationsCommon() {
5
+ export function generateTypedOperationsCommon(typesRelPath) {
6
6
  return `/**
7
7
  * Shared types for typed GraphQL operations.
8
8
  *
9
9
  * @generated by foir — DO NOT EDIT MANUALLY
10
10
  */
11
11
 
12
+ import type { JsonValue } from '${typesRelPath}/field-types.js';
13
+
12
14
  /** A record with strongly-typed data. */
13
15
  export interface BaseRecord<T> {
14
16
  id: string;
15
17
  modelKey: string;
16
18
  naturalKey: string | null;
17
19
  data: T;
18
- metadata: Record<string, unknown> | null;
20
+ metadata: Record<string, JsonValue> | null;
19
21
  publishedVersionNumber: number | null;
20
22
  publishedAt: string | null;
21
23
  versionNumber: number | null;
@@ -33,7 +35,7 @@ export interface ResolvedContent<T> {
33
35
 
34
36
  /** Paginated list result. */
35
37
  export interface PaginatedResult<T> {
36
- items: BaseRecord<T>[];
38
+ items: (BaseRecord<T> & { resolved: ResolvedContent<T> | null })[];
37
39
  total: number;
38
40
  }
39
41
 
@@ -70,5 +72,13 @@ export interface ShareResult {
70
72
  export interface ShareWithRecord<T> extends ShareResult {
71
73
  record: BaseRecord<T>;
72
74
  }
75
+
76
+ /** Field selection for resolved content — pick or omit specific fields. */
77
+ export interface FieldSelection<T = Record<string, unknown>> {
78
+ /** Include only these field keys (mutually exclusive with omit) */
79
+ pick?: (keyof T & string)[];
80
+ /** Exclude these field keys (mutually exclusive with omit) */
81
+ omit?: (keyof T & string)[];
82
+ }
73
83
  `;
74
84
  }
@@ -1 +1 @@
1
- {"version":3,"file":"typed-operations.d.ts","sourceRoot":"","sources":["../../../src/codegen/generators/typed-operations.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAGvD,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,YAAY,EACnB,YAAY,EAAE,MAAM,GACnB,MAAM,CA4QR;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAG5E"}
1
+ {"version":3,"file":"typed-operations.d.ts","sourceRoot":"","sources":["../../../src/codegen/generators/typed-operations.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAGvD,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,YAAY,EACnB,YAAY,EAAE,MAAM,GACnB,MAAM,CAwRR;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAG5E"}
@@ -21,6 +21,7 @@ export function generateTypedOperations(model, typesRelPath) {
21
21
  * @generated by foir — DO NOT EDIT MANUALLY
22
22
  */
23
23
 
24
+ import type { JsonValue } from '${typesRelPath}/field-types.js';
24
25
  import type { ${dataType} } from '${typesRelPath}/models/${model.key}.js';
25
26
  import type {
26
27
  BaseRecord,
@@ -28,7 +29,8 @@ import type {
28
29
  PaginatedResult,
29
30
  CreateRecordResult,
30
31
  UpdateRecordResult,
31
- DeleteRecordResult,${model.config.sharing?.enabled ? '\n ShareResult,\n ShareWithRecord,' : ''}
32
+ DeleteRecordResult,
33
+ FieldSelection,${model.config.sharing?.enabled ? '\n ShareResult,\n ShareWithRecord,' : ''}
32
34
  } from './_common.js';
33
35
  `);
34
36
  // Type alias
@@ -36,12 +38,12 @@ import type {
36
38
  lines.push('');
37
39
  // GET query
38
40
  lines.push(`export const GET_${upperSnake} = \`
39
- query Get${typeName}($id: ID!, $locale: String, $preview: Boolean) {
41
+ query Get${typeName}($id: ID!, $locale: String, $preview: Boolean, $fields: FieldSelectionInput) {
40
42
  record(id: $id) {
41
43
  id modelKey naturalKey data metadata
42
44
  publishedVersionNumber publishedAt versionNumber changeDescription
43
45
  createdAt updatedAt
44
- resolved(locale: $locale, preview: $preview) {
46
+ resolved(locale: $locale, preview: $preview, fields: $fields) {
45
47
  content
46
48
  record { id modelKey naturalKey }
47
49
  version { id versionNumber }
@@ -54,6 +56,7 @@ import type {
54
56
  id: string;
55
57
  locale?: string;
56
58
  preview?: boolean;
59
+ fields?: FieldSelection<${dataType}>;
57
60
  }`);
58
61
  lines.push('');
59
62
  lines.push(`export interface Get${typeName}Result {
@@ -64,12 +67,12 @@ import type {
64
67
  lines.push('');
65
68
  // GET BY KEY query
66
69
  lines.push(`export const GET_${upperSnake}_BY_KEY = \`
67
- query Get${typeName}ByKey($naturalKey: String!, $locale: String, $preview: Boolean) {
70
+ query Get${typeName}ByKey($naturalKey: String!, $locale: String, $preview: Boolean, $fields: FieldSelectionInput) {
68
71
  recordByKey(modelKey: "${model.key}", naturalKey: $naturalKey) {
69
72
  id modelKey naturalKey data metadata
70
73
  publishedVersionNumber publishedAt versionNumber changeDescription
71
74
  createdAt updatedAt
72
- resolved(locale: $locale, preview: $preview) {
75
+ resolved(locale: $locale, preview: $preview, fields: $fields) {
73
76
  content
74
77
  record { id modelKey naturalKey }
75
78
  version { id versionNumber }
@@ -82,6 +85,7 @@ import type {
82
85
  naturalKey: string;
83
86
  locale?: string;
84
87
  preview?: boolean;
88
+ fields?: FieldSelection<${dataType}>;
85
89
  }`);
86
90
  lines.push('');
87
91
  lines.push(`export interface Get${typeName}ByKeyResult {
@@ -92,12 +96,17 @@ import type {
92
96
  lines.push('');
93
97
  // LIST query
94
98
  lines.push(`export const LIST_${pluralUpperSnake} = \`
95
- query List${pluralName}($limit: Int, $offset: Int, $filters: [FilterInput!], $sort: SortInput) {
99
+ query List${pluralName}($limit: Int, $offset: Int, $filters: [FilterInput!], $sort: SortInput, $locale: String, $preview: Boolean, $fields: FieldSelectionInput) {
96
100
  records(modelKey: "${model.key}", limit: $limit, offset: $offset, filters: $filters, sort: $sort) {
97
101
  items {
98
102
  id modelKey naturalKey data metadata
99
103
  publishedVersionNumber publishedAt versionNumber changeDescription
100
104
  createdAt updatedAt
105
+ resolved(locale: $locale, preview: $preview, fields: $fields) {
106
+ content
107
+ record { id modelKey naturalKey }
108
+ version { id versionNumber }
109
+ }
101
110
  }
102
111
  total
103
112
  }
@@ -107,8 +116,11 @@ import type {
107
116
  lines.push(`export interface List${pluralName}Variables {
108
117
  limit?: number;
109
118
  offset?: number;
110
- filters?: Array<{ field: string; operator: string; value: unknown }>;
119
+ filters?: Array<{ field: string; operator: string; value: JsonValue }>;
111
120
  sort?: { field: string; direction: 'ASC' | 'DESC' };
121
+ locale?: string;
122
+ preview?: boolean;
123
+ fields?: FieldSelection<${dataType}>;
112
124
  }`);
113
125
  lines.push('');
114
126
  lines.push(`export interface List${pluralName}Result {
@@ -131,7 +143,7 @@ import type {
131
143
  modelKey: string;
132
144
  naturalKey?: string;
133
145
  data: Partial<${dataType}>;
134
- metadata?: Record<string, unknown>;
146
+ metadata?: Record<string, JsonValue>;
135
147
  };
136
148
  }`);
137
149
  lines.push('');
@@ -155,7 +167,7 @@ import type {
155
167
  input: {
156
168
  id: string;
157
169
  data?: Partial<${dataType}>;
158
- metadata?: Record<string, unknown>;
170
+ metadata?: Record<string, JsonValue>;
159
171
  changeDescription?: string;
160
172
  };
161
173
  }`);
@@ -1 +1 @@
1
- {"version":3,"file":"swift-field-mapping.d.ts","sourceRoot":"","sources":["../../src/codegen/swift-field-mapping.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAE5D,MAAM,WAAW,gBAAgB;IAC/B,sBAAsB;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,yEAAyE;IACzE,cAAc,EAAE,OAAO,CAAC;IACxB,gFAAgF;IAChF,YAAY,EAAE,MAAM,CAAC;IACrB,iDAAiD;IACjD,cAAc,EAAE,MAAM,CAAC;IACvB,kEAAkE;IAClE,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED,eAAO,MAAM,wBAAwB,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAwHrE,CAAC;AAEF;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,iBAAiB,GAAG;IAC3D,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,OAAO,CAAC;IACpB,OAAO,EAAE,gBAAgB,GAAG,SAAS,CAAC;CACvC,CAeA"}
1
+ {"version":3,"file":"swift-field-mapping.d.ts","sourceRoot":"","sources":["../../src/codegen/swift-field-mapping.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAE5D,MAAM,WAAW,gBAAgB;IAC/B,sBAAsB;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,yEAAyE;IACzE,cAAc,EAAE,OAAO,CAAC;IACxB,gFAAgF;IAChF,YAAY,EAAE,MAAM,CAAC;IACrB,iDAAiD;IACjD,cAAc,EAAE,MAAM,CAAC;IACvB,kEAAkE;IAClE,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED,eAAO,MAAM,wBAAwB,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CA8HrE,CAAC;AAEF;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,iBAAiB,GAAG;IAC3D,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,OAAO,CAAC;IACpB,OAAO,EAAE,gBAAgB,GAAG,SAAS,CAAC;CACvC,CAeA"}
@@ -124,6 +124,12 @@ export const SWIFT_FIELD_TYPE_MAPPING = {
124
124
  castExpression: 'as? [String: Any]',
125
125
  needsSharedType: true,
126
126
  },
127
+ model: {
128
+ type: 'String',
129
+ alwaysOptional: true,
130
+ defaultValue: '""',
131
+ castExpression: 'as? String',
132
+ },
127
133
  };
128
134
  /**
129
135
  * Get the Swift type for a field.
@@ -1 +1 @@
1
- {"version":3,"file":"operations.d.ts","sourceRoot":"","sources":["../../src/commands/operations.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAkBtD,wBAAgB,0BAA0B,CACxC,OAAO,EAAE,OAAO,EAChB,UAAU,EAAE,MAAM,aAAa,GAC9B,IAAI,CAsVN"}
1
+ {"version":3,"file":"operations.d.ts","sourceRoot":"","sources":["../../src/commands/operations.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAkBtD,wBAAgB,0BAA0B,CACxC,OAAO,EAAE,OAAO,EAChB,UAAU,EAAE,MAAM,aAAa,GAC9B,IAAI,CA8ZN"}
@@ -154,6 +154,61 @@ export function registerOperationsCommands(program, globalOpts) {
154
154
  }
155
155
  }
156
156
  }));
157
+ // quota
158
+ operations
159
+ .command('quota <key>')
160
+ .description('Show quota usage for an operation')
161
+ .option('--json', 'JSON output')
162
+ .action(withErrorHandler(globalOpts, async (key) => {
163
+ const opts = globalOpts();
164
+ const client = await createClient(opts);
165
+ // First get the operation to see its quota rules
166
+ const opData = await client.request(GetOperationDocument, { key });
167
+ if (!opData.operation)
168
+ throw new Error(`Operation "${key}" not found.`);
169
+ const quotas = opData.operation.quotas;
170
+ if (!quotas || quotas.length === 0) {
171
+ if (opts.json || opts.jsonl)
172
+ formatOutput({
173
+ operationKey: key,
174
+ quotas: [],
175
+ message: 'No quotas configured',
176
+ }, opts);
177
+ else
178
+ console.log('No quotas configured for this operation.');
179
+ return;
180
+ }
181
+ if (opts.json || opts.jsonl) {
182
+ formatOutput({ operationKey: key, quotas }, opts);
183
+ }
184
+ else {
185
+ console.log(`\nQuota rules for "${key}":\n`);
186
+ const header = [
187
+ 'Segment'.padEnd(20),
188
+ 'Scope'.padEnd(10),
189
+ 'Limit'.padEnd(8),
190
+ 'Window'.padEnd(10),
191
+ ].join(' | ');
192
+ console.log(header);
193
+ console.log('-'.repeat(header.length));
194
+ for (const q of quotas) {
195
+ const segment = q.segmentKey ?? '(default)';
196
+ const limit = q.points === 0 ? '\u221e' : String(q.points);
197
+ const window = q.duration >= 86400
198
+ ? `${q.duration / 86400}d`
199
+ : q.duration >= 3600
200
+ ? `${q.duration / 3600}h`
201
+ : `${q.duration}s`;
202
+ console.log([
203
+ segment.padEnd(20),
204
+ q.scope.padEnd(10),
205
+ limit.padEnd(8),
206
+ window.padEnd(10),
207
+ ].join(' | '));
208
+ }
209
+ console.log('');
210
+ }
211
+ }));
157
212
  // stats
158
213
  operations
159
214
  .command('stats <key>')
@@ -157,7 +157,7 @@ export function registerPullCommand(program, globalOpts) {
157
157
  const typesRelPath = computeTypesRelPath(opsDir, typesDir);
158
158
  files.push({
159
159
  path: resolve(opsDir, '_common.ts'),
160
- content: generateTypedOperationsCommon(),
160
+ content: generateTypedOperationsCommon(typesRelPath),
161
161
  });
162
162
  // 7a. Per-model typed operations
163
163
  for (const model of publicModels) {