zinkee 0.1.7 → 0.1.8

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.
@@ -10,7 +10,13 @@ export type SchemaFieldTypeKey =
10
10
  | "reference"
11
11
  | "formula"
12
12
  | "member"
13
- | "autoincremental";
13
+ | "autoincremental"
14
+ | "schedule";
15
+
16
+ export interface SchemaFieldExamplePayload {
17
+ name: string;
18
+ payload: JsonObject;
19
+ }
14
20
 
15
21
  export interface SchemaFieldTypeDefinition {
16
22
  key: SchemaFieldTypeKey;
@@ -18,7 +24,7 @@ export interface SchemaFieldTypeDefinition {
18
24
  aliases: readonly string[];
19
25
  description: string;
20
26
  rawShape: string;
21
- examplePayload: JsonObject;
27
+ examplePayloads: readonly SchemaFieldExamplePayload[];
22
28
  }
23
29
 
24
30
  function isJsonObject(value: unknown): value is JsonObject {
@@ -55,12 +61,37 @@ const fieldTypeDefinitions = [
55
61
  aliases: ["text", "TextField"],
56
62
  description: "Plain text field.",
57
63
  rawShape:
58
- '{"type":"text","slug":"name","label":"Name"}',
59
- examplePayload: {
60
- type: "text",
61
- slug: "name",
62
- label: "Name",
63
- },
64
+ '{"type":"TextField","slug":"name","label":"Name","config":{"textType":"NORMAL"}}',
65
+ examplePayloads: [
66
+ {
67
+ name: "Text field (normal)",
68
+ payload: {
69
+ type: "TextField",
70
+ slug: "customer-name",
71
+ label: "Customer Name",
72
+ required: true,
73
+ config: {
74
+ textType: "NORMAL",
75
+ defaultValue: null,
76
+ conditionalFormatting: [],
77
+ },
78
+ },
79
+ },
80
+ {
81
+ name: "Text field (long)",
82
+ payload: {
83
+ type: "TextField",
84
+ slug: "customer-notes",
85
+ label: "Customer Notes",
86
+ required: false,
87
+ config: {
88
+ textType: "LONG",
89
+ defaultValue: "Add internal notes here...",
90
+ conditionalFormatting: [],
91
+ },
92
+ },
93
+ },
94
+ ],
64
95
  },
65
96
  {
66
97
  key: "number",
@@ -68,23 +99,125 @@ const fieldTypeDefinitions = [
68
99
  aliases: ["number", "NumberField"],
69
100
  description: "Numeric field.",
70
101
  rawShape:
71
- '{"type":"number","slug":"price-eur","label":"Price EUR","config":{"defaultValue":"0","formatOptions":{"format":"number","decimals":2,"thousandsSeparator":true,"symbolEnabled":true,"symbolValue":" €","symbolPosition":"right"}}}',
72
- examplePayload: {
73
- type: "number",
74
- slug: "price-eur",
75
- label: "Price EUR",
76
- config: {
77
- defaultValue: "0",
78
- formatOptions: {
79
- format: "number",
80
- decimals: 2,
81
- thousandsSeparator: true,
82
- symbolEnabled: true,
83
- symbolValue: " €",
84
- symbolPosition: "right",
102
+ '{"type":"NumberField","slug":"quantity","label":"Quantity","config":{"defaultValue":"0","formatOptions":{"format":"number","decimals":0,"thousandsSeparator":false}}}',
103
+ examplePayloads: [
104
+ {
105
+ name: "Number field (plain integer)",
106
+ payload: {
107
+ type: "NumberField",
108
+ slug: "quantity",
109
+ label: "Quantity",
110
+ required: false,
111
+ config: {
112
+ defaultValue: "0",
113
+ formatOptions: {
114
+ format: "number",
115
+ decimals: 0,
116
+ thousandsSeparator: false,
117
+ },
118
+ conditionalFormatting: [],
119
+ },
85
120
  },
86
121
  },
87
- },
122
+ {
123
+ name: "Number field (decimal with thousands)",
124
+ payload: {
125
+ type: "NumberField",
126
+ slug: "weight",
127
+ label: "Weight (kg)",
128
+ required: false,
129
+ config: {
130
+ defaultValue: "0",
131
+ formatOptions: {
132
+ format: "number",
133
+ decimals: 2,
134
+ thousandsSeparator: true,
135
+ negativesInRed: false,
136
+ },
137
+ conditionalFormatting: [],
138
+ },
139
+ },
140
+ },
141
+ {
142
+ name: "Number field (currency € prefix)",
143
+ payload: {
144
+ type: "NumberField",
145
+ slug: "amount-eur",
146
+ label: "Amount (EUR)",
147
+ required: false,
148
+ config: {
149
+ defaultValue: "0",
150
+ formatOptions: {
151
+ format: "currency",
152
+ decimals: 2,
153
+ negativesInRed: true,
154
+ thousandsSeparator: true,
155
+ symbolEnabled: true,
156
+ symbolPosition: "before",
157
+ symbolValue: "€",
158
+ },
159
+ conditionalFormatting: [],
160
+ },
161
+ },
162
+ },
163
+ {
164
+ name: "Number field (currency $ suffix)",
165
+ payload: {
166
+ type: "NumberField",
167
+ slug: "amount-usd",
168
+ label: "Amount (USD)",
169
+ required: false,
170
+ config: {
171
+ defaultValue: "0",
172
+ formatOptions: {
173
+ format: "currency",
174
+ decimals: 2,
175
+ negativesInRed: false,
176
+ thousandsSeparator: true,
177
+ symbolEnabled: true,
178
+ symbolPosition: "right",
179
+ symbolValue: " $",
180
+ },
181
+ conditionalFormatting: [],
182
+ },
183
+ },
184
+ },
185
+ {
186
+ name: "Number field (percentage)",
187
+ payload: {
188
+ type: "NumberField",
189
+ slug: "discount-rate",
190
+ label: "Discount Rate",
191
+ required: false,
192
+ config: {
193
+ defaultValue: "0",
194
+ formatOptions: {
195
+ format: "percentage",
196
+ decimals: 1,
197
+ negativesInRed: true,
198
+ },
199
+ conditionalFormatting: [],
200
+ },
201
+ },
202
+ },
203
+ {
204
+ name: "Number field (duration HH:MM)",
205
+ payload: {
206
+ type: "NumberField",
207
+ slug: "tracked-time",
208
+ label: "Tracked Time",
209
+ required: false,
210
+ config: {
211
+ defaultValue: "0",
212
+ formatOptions: {
213
+ format: "duration",
214
+ durationFormat: "HH:MM",
215
+ },
216
+ conditionalFormatting: [],
217
+ },
218
+ },
219
+ },
220
+ ],
88
221
  },
89
222
  {
90
223
  key: "date",
@@ -92,12 +225,63 @@ const fieldTypeDefinitions = [
92
225
  aliases: ["date", "DateField"],
93
226
  description: "Date or datetime field.",
94
227
  rawShape:
95
- '{"type":"date","slug":"due-date","label":"Due date"}',
96
- examplePayload: {
97
- type: "date",
98
- slug: "due-date",
99
- label: "Due date",
100
- },
228
+ '{"type":"DateField","slug":"due-date","label":"Due Date","config":{"formatOptions":{"format":"date","dateFormat":"YYYY-MM-DD","showTime":false}}}',
229
+ examplePayloads: [
230
+ {
231
+ name: "Date field (date only)",
232
+ payload: {
233
+ type: "DateField",
234
+ slug: "birth-date",
235
+ label: "Birth Date",
236
+ required: false,
237
+ config: {
238
+ defaultValue: null,
239
+ formatOptions: {
240
+ format: "date",
241
+ dateFormat: "YYYY-MM-DD",
242
+ showTime: false,
243
+ },
244
+ conditionalFormatting: [],
245
+ },
246
+ },
247
+ },
248
+ {
249
+ name: "Date field (datetime EU)",
250
+ payload: {
251
+ type: "DateField",
252
+ slug: "due-date",
253
+ label: "Due Date",
254
+ required: false,
255
+ config: {
256
+ defaultValue: null,
257
+ formatOptions: {
258
+ format: "datetime",
259
+ dateFormat: "DD/MM/YYYY",
260
+ showTime: true,
261
+ },
262
+ conditionalFormatting: [],
263
+ },
264
+ },
265
+ },
266
+ {
267
+ name: "Date field (datetime US)",
268
+ payload: {
269
+ type: "DateField",
270
+ slug: "appointment-at",
271
+ label: "Appointment",
272
+ required: false,
273
+ config: {
274
+ defaultValue: null,
275
+ formatOptions: {
276
+ format: "datetime",
277
+ dateFormat: "MM/DD/YYYY",
278
+ showTime: true,
279
+ },
280
+ conditionalFormatting: [],
281
+ },
282
+ },
283
+ },
284
+ ],
101
285
  },
102
286
  {
103
287
  key: "option",
@@ -105,26 +289,85 @@ const fieldTypeDefinitions = [
105
289
  aliases: ["option", "OptionField"],
106
290
  description: "Dropdown/select field with options.",
107
291
  rawShape:
108
- '{"type":"option","slug":"status","label":"Status","config":{"options":[{"id":1,"name":"Open","color":"default"},{"id":2,"name":"Closed","color":"success"}]}}',
109
- examplePayload: {
110
- type: "option",
111
- slug: "status",
112
- label: "Status",
113
- config: {
114
- options: [
115
- {
116
- id: 1,
117
- name: "Open",
118
- color: "default",
292
+ '{"type":"OptionField","slug":"status","label":"Status","config":{"options":[{"id":1,"name":"Open","color":"default"},{"id":2,"name":"Closed","color":"success"}]}}',
293
+ examplePayloads: [
294
+ {
295
+ name: "Option field (select dropdown)",
296
+ payload: {
297
+ type: "OptionField",
298
+ slug: "priority",
299
+ label: "Priority",
300
+ required: false,
301
+ config: {
302
+ options: [
303
+ { id: 1, name: "Low", color: "default" },
304
+ { id: 2, name: "Medium", color: "warning" },
305
+ { id: 3, name: "High", color: "danger" },
306
+ ],
307
+ defaultValue: 1,
308
+ conditionalFormatting: [],
309
+ buttonConfig: null,
119
310
  },
120
- {
121
- id: 2,
122
- name: "Closed",
123
- color: "success",
311
+ },
312
+ },
313
+ {
314
+ name: "Option field (button cycle)",
315
+ payload: {
316
+ type: "OptionField",
317
+ slug: "stage",
318
+ label: "Stage",
319
+ required: false,
320
+ config: {
321
+ options: [
322
+ { id: 1, name: "To Do", color: "default" },
323
+ { id: 2, name: "Doing", color: "warning" },
324
+ { id: 3, name: "Done", color: "success" },
325
+ ],
326
+ defaultValue: 1,
327
+ conditionalFormatting: [],
328
+ buttonConfig: {
329
+ displayMode: "button",
330
+ buttonBehavior: {
331
+ mode: "cycle",
332
+ cycleOrder: [1, 2, 3],
333
+ requireConfirmation: false,
334
+ lockAfterSelection: false,
335
+ allowReselect: true,
336
+ },
337
+ },
124
338
  },
125
- ],
339
+ },
126
340
  },
127
- },
341
+ {
342
+ name: "Option field (button dropdown w/ confirm)",
343
+ payload: {
344
+ type: "OptionField",
345
+ slug: "status",
346
+ label: "Status",
347
+ required: false,
348
+ config: {
349
+ options: [
350
+ { id: 1, name: "Open", color: "default" },
351
+ { id: 2, name: "In Progress", color: "warning" },
352
+ { id: 3, name: "Done", color: "success" },
353
+ ],
354
+ defaultValue: 1,
355
+ conditionalFormatting: [],
356
+ buttonConfig: {
357
+ displayMode: "button",
358
+ buttonBehavior: {
359
+ mode: "dropdown",
360
+ requireConfirmation: true,
361
+ confirmationMessage: "Confirm status change",
362
+ useFixedLabel: true,
363
+ buttonLabel: "Set status",
364
+ allowReselect: true,
365
+ },
366
+ },
367
+ },
368
+ },
369
+ },
370
+ ],
128
371
  },
129
372
  {
130
373
  key: "file",
@@ -132,12 +375,19 @@ const fieldTypeDefinitions = [
132
375
  aliases: ["file", "FileField"],
133
376
  description: "File attachment field.",
134
377
  rawShape:
135
- '{"type":"file","slug":"attachment","label":"Attachment"}',
136
- examplePayload: {
137
- type: "file",
138
- slug: "attachment",
139
- label: "Attachment",
140
- },
378
+ '{"type":"FileField","slug":"attachment","label":"Attachment","config":{}}',
379
+ examplePayloads: [
380
+ {
381
+ name: "File field",
382
+ payload: {
383
+ type: "FileField",
384
+ slug: "attachment",
385
+ label: "Attachment",
386
+ required: false,
387
+ config: {},
388
+ },
389
+ },
390
+ ],
141
391
  },
142
392
  {
143
393
  key: "reference",
@@ -145,17 +395,54 @@ const fieldTypeDefinitions = [
145
395
  aliases: ["reference", "ReferenceField"],
146
396
  description: "Reference to another schema.",
147
397
  rawShape:
148
- '{"type":"reference","slug":"project","label":"Project","config":{"targetSchema":"<schema-uuid>","targetDisplayField":"<field-uuid>","deleteBehavior":"SET_NULL"}}',
149
- examplePayload: {
150
- type: "reference",
151
- slug: "project",
152
- label: "Project",
153
- config: {
154
- targetSchema: "<schema-uuid>",
155
- targetDisplayField: "<field-uuid>",
156
- deleteBehavior: "SET_NULL",
398
+ '{"type":"ReferenceField","slug":"company-ref","label":"Company","config":{"targetSchema":"<schema-uuid>","targetDisplayField":"<field-uuid>"}}',
399
+ examplePayloads: [
400
+ {
401
+ name: "Reference field (minimal)",
402
+ payload: {
403
+ type: "ReferenceField",
404
+ slug: "company-ref",
405
+ label: "Company",
406
+ required: false,
407
+ config: {
408
+ targetSchema: "5223cbc8-8c6c-4694-93bc-dd176364df9b",
409
+ targetDisplayField: "c60b8526-5835-4c9f-aecd-9a16c7704a34",
410
+ },
411
+ },
157
412
  },
158
- },
413
+ {
414
+ name: "Reference field (restrict + filter + description)",
415
+ payload: {
416
+ type: "ReferenceField",
417
+ slug: "customer-ref",
418
+ label: "Customer",
419
+ required: false,
420
+ config: {
421
+ targetSchema: "5223cbc8-8c6c-4694-93bc-dd176364df9b",
422
+ targetDisplayField: "c60b8526-5835-4c9f-aecd-9a16c7704a34",
423
+ deleteBehavior: "RESTRICT",
424
+ defaultValue: "8ad602a1-f6e3-43f6-9ab2-f1c5afc99621::Ref A",
425
+ filterFields: [
426
+ {
427
+ fatherField: "c60b8526-5835-4c9f-aecd-9a16c7704a34",
428
+ currentField: "45c5da96-0dc2-4cc1-ba61-7660a9c6da32",
429
+ comparison: "EQUAL",
430
+ value: null,
431
+ },
432
+ ],
433
+ descriptionFields: [
434
+ {
435
+ id: "c52efe3f-2825-4c60-a7e3-34dd4c1ce043",
436
+ fieldType: "NumberField",
437
+ options: null,
438
+ format: "number",
439
+ },
440
+ ],
441
+ conditionalFormatting: [],
442
+ },
443
+ },
444
+ },
445
+ ],
159
446
  },
160
447
  {
161
448
  key: "formula",
@@ -163,15 +450,79 @@ const fieldTypeDefinitions = [
163
450
  aliases: ["formula", "FormulaField"],
164
451
  description: "Calculated field from a formula.",
165
452
  rawShape:
166
- '{"type":"formula","slug":"total","label":"Total","config":{"formulaText":"{price} * {quantity}"}}',
167
- examplePayload: {
168
- type: "formula",
169
- slug: "total",
170
- label: "Total",
171
- config: {
172
- formulaText: "{price} * {quantity}",
453
+ '{"type":"FormulaField","slug":"formula-summary","label":"Formula Summary","config":{"formulaText":"concat(firstName, \' \', lastName)","uiFormatKind":"none"}}',
454
+ examplePayloads: [
455
+ {
456
+ name: "Formula field (none)",
457
+ payload: {
458
+ type: "FormulaField",
459
+ slug: "formula-summary",
460
+ label: "Formula Summary",
461
+ required: false,
462
+ config: {
463
+ formulaText: "concat(firstName, ' ', lastName)",
464
+ uiFormatKind: "none",
465
+ status: "ACTIVE",
466
+ conditionalFormatting: [],
467
+ },
468
+ },
173
469
  },
174
- },
470
+ {
471
+ name: "Formula field (number)",
472
+ payload: {
473
+ type: "FormulaField",
474
+ slug: "formula-total",
475
+ label: "Formula Total",
476
+ required: false,
477
+ config: {
478
+ formulaText: "amount * 1.21",
479
+ uiFormatKind: "number",
480
+ status: "ACTIVE",
481
+ numberFormatOptions: {
482
+ format: "number",
483
+ decimals: 2,
484
+ thousandsSeparator: true,
485
+ },
486
+ conditionalFormatting: [],
487
+ },
488
+ },
489
+ },
490
+ {
491
+ name: "Formula field (date)",
492
+ payload: {
493
+ type: "FormulaField",
494
+ slug: "formula-deadline",
495
+ label: "Computed Deadline",
496
+ required: false,
497
+ config: {
498
+ formulaText: "addDays(startDate, 7)",
499
+ uiFormatKind: "date",
500
+ status: "ACTIVE",
501
+ dateFormatOptions: {
502
+ format: "date",
503
+ dateFormat: "DD/MM/YYYY",
504
+ showTime: false,
505
+ },
506
+ conditionalFormatting: [],
507
+ },
508
+ },
509
+ },
510
+ {
511
+ name: "Formula field (member)",
512
+ payload: {
513
+ type: "FormulaField",
514
+ slug: "formula-assignee",
515
+ label: "Computed Assignee",
516
+ required: false,
517
+ config: {
518
+ formulaText: "manager",
519
+ uiFormatKind: "member",
520
+ status: "ACTIVE",
521
+ conditionalFormatting: [],
522
+ },
523
+ },
524
+ },
525
+ ],
175
526
  },
176
527
  {
177
528
  key: "member",
@@ -179,15 +530,22 @@ const fieldTypeDefinitions = [
179
530
  aliases: ["member", "MemberField"],
180
531
  description: "Workspace member field.",
181
532
  rawShape:
182
- '{"type":"member","slug":"owner","label":"Owner","config":{"defaultValue":"<member-id>"}}',
183
- examplePayload: {
184
- type: "member",
185
- slug: "owner",
186
- label: "Owner",
187
- config: {
188
- defaultValue: "<member-id>",
533
+ '{"type":"MemberField","slug":"owner","label":"Owner","config":{"defaultValue":null}}',
534
+ examplePayloads: [
535
+ {
536
+ name: "Member field",
537
+ payload: {
538
+ type: "MemberField",
539
+ slug: "owner",
540
+ label: "Owner",
541
+ required: false,
542
+ config: {
543
+ defaultValue: null,
544
+ conditionalFormatting: [],
545
+ },
546
+ },
189
547
  },
190
- },
548
+ ],
191
549
  },
192
550
  {
193
551
  key: "autoincremental",
@@ -200,15 +558,76 @@ const fieldTypeDefinitions = [
200
558
  ],
201
559
  description: "Auto-generated incremental counter.",
202
560
  rawShape:
203
- '{"type":"autoincremental","slug":"counter","label":"Counter","config":{"segmentedByFieldId":null}}',
204
- examplePayload: {
205
- type: "autoincremental",
206
- slug: "counter",
207
- label: "Counter",
208
- config: {
209
- segmentedByFieldId: null,
561
+ '{"type":"AutoIncrementalField","slug":"order-number","label":"Order Number","config":{"segmentedByFieldId":null}}',
562
+ examplePayloads: [
563
+ {
564
+ name: "Autoincremental (global counter)",
565
+ payload: {
566
+ type: "AutoIncrementalField",
567
+ slug: "order-number",
568
+ label: "Order Number",
569
+ required: false,
570
+ config: {
571
+ segmentedByFieldId: null,
572
+ conditionalFormatting: [],
573
+ },
574
+ },
210
575
  },
211
- },
576
+ {
577
+ name: "Autoincremental (segmented)",
578
+ payload: {
579
+ type: "AutoIncrementalField",
580
+ slug: "sequence-code",
581
+ label: "Sequence Code",
582
+ required: false,
583
+ config: {
584
+ segmentedByFieldId: "45c5da96-0dc2-4cc1-ba61-7660a9c6da32",
585
+ conditionalFormatting: [],
586
+ },
587
+ },
588
+ },
589
+ ],
590
+ },
591
+ {
592
+ key: "schedule",
593
+ className: "ScheduleField",
594
+ aliases: ["schedule", "ScheduleField"],
595
+ description:
596
+ "Recurrence/schedule field bound to a number reference and a date period (frequency: monthly or daily).",
597
+ rawShape:
598
+ '{"type":"ScheduleField","slug":"recurrence","label":"Recurrence","config":{"referenceField":"<number-field-uuid>","beginningPeriodField":"<date-field-uuid>","endingPeriodField":"<date-field-uuid>","frequency":"monthly"}}',
599
+ examplePayloads: [
600
+ {
601
+ name: "Schedule field (monthly)",
602
+ payload: {
603
+ type: "ScheduleField",
604
+ slug: "monthly-recurrence",
605
+ label: "Monthly Recurrence",
606
+ required: false,
607
+ config: {
608
+ referenceField: "c52efe3f-2825-4c60-a7e3-34dd4c1ce043",
609
+ beginningPeriodField: "d4e5f6a7-b8c9-0d1e-2f3a-4b5c6d7e8f90",
610
+ endingPeriodField: "d4e5f6a7-b8c9-0d1e-2f3a-4b5c6d7e8f90",
611
+ frequency: "monthly",
612
+ },
613
+ },
614
+ },
615
+ {
616
+ name: "Schedule field (daily)",
617
+ payload: {
618
+ type: "ScheduleField",
619
+ slug: "daily-recurrence",
620
+ label: "Daily Recurrence",
621
+ required: false,
622
+ config: {
623
+ referenceField: "c52efe3f-2825-4c60-a7e3-34dd4c1ce043",
624
+ beginningPeriodField: "d4e5f6a7-b8c9-0d1e-2f3a-4b5c6d7e8f90",
625
+ endingPeriodField: "d4e5f6a7-b8c9-0d1e-2f3a-4b5c6d7e8f90",
626
+ frequency: "daily",
627
+ },
628
+ },
629
+ },
630
+ ],
212
631
  },
213
632
  ] as const satisfies readonly SchemaFieldTypeDefinition[];
214
633
 
@@ -397,6 +816,70 @@ function normalizeReferencePayload(input: JsonObject): JsonObject {
397
816
  return normalized;
398
817
  }
399
818
 
819
+ const scheduleAllowedFrequencies = new Set(["monthly", "daily"]);
820
+
821
+ function normalizeSchedulePayload(input: JsonObject): JsonObject {
822
+ const additionalProperties = toJsonObject(input.additionalProperties) ?? {};
823
+ const config = toJsonObject(input.config) ?? {};
824
+ const referenceField =
825
+ config.referenceField ?? input.referenceField ?? additionalProperties.referenceField;
826
+ const beginningPeriodField =
827
+ config.beginningPeriodField ??
828
+ input.beginningPeriodField ??
829
+ additionalProperties.beginningPeriodField;
830
+ const endingPeriodField =
831
+ config.endingPeriodField ??
832
+ input.endingPeriodField ??
833
+ additionalProperties.endingPeriodField;
834
+ const frequency =
835
+ config.frequency ?? input.frequency ?? additionalProperties.frequency;
836
+
837
+ const {
838
+ referenceField: _referenceField,
839
+ beginningPeriodField: _beginningPeriodField,
840
+ endingPeriodField: _endingPeriodField,
841
+ frequency: _frequency,
842
+ additionalProperties: _additionalProperties,
843
+ ...rest
844
+ } = input;
845
+
846
+ const normalized: JsonObject = {
847
+ ...rest,
848
+ config: {
849
+ ...config,
850
+ ...(referenceField !== undefined ? { referenceField } : {}),
851
+ ...(beginningPeriodField !== undefined ? { beginningPeriodField } : {}),
852
+ ...(endingPeriodField !== undefined ? { endingPeriodField } : {}),
853
+ ...(frequency !== undefined ? { frequency } : {}),
854
+ },
855
+ };
856
+
857
+ const normalizedConfig = toJsonObject(normalized.config) ?? {};
858
+ const definition = resolveSchemaFieldType("schedule")!;
859
+
860
+ if (
861
+ !toStringValue(normalizedConfig.referenceField) ||
862
+ !toStringValue(normalizedConfig.beginningPeriodField) ||
863
+ !toStringValue(normalizedConfig.endingPeriodField) ||
864
+ !toStringValue(normalizedConfig.frequency)
865
+ ) {
866
+ throw new CliError(
867
+ "invalid_cli_usage",
868
+ `Schedule fields require "referenceField", "beginningPeriodField", "endingPeriodField" and "frequency". Expected raw shape: ${definition.rawShape}`,
869
+ );
870
+ }
871
+
872
+ const frequencyValue = toStringValue(normalizedConfig.frequency)!;
873
+ if (!scheduleAllowedFrequencies.has(frequencyValue)) {
874
+ throw new CliError(
875
+ "invalid_cli_usage",
876
+ `Schedule "frequency" must be "monthly" or "daily" (received "${frequencyValue}").`,
877
+ );
878
+ }
879
+
880
+ return normalized;
881
+ }
882
+
400
883
  function normalizeFormulaPayload(input: JsonObject): JsonObject {
401
884
  const { status: _status, ...rest } = input;
402
885
  const rawAdditionalProperties = toJsonObject(input.additionalProperties);
@@ -465,6 +948,8 @@ export function normalizeSchemaFieldCreatePayload(input: JsonObject): JsonObject
465
948
  return normalizeReferencePayload(normalizedWithType);
466
949
  case "formula":
467
950
  return normalizeFormulaPayload(normalizedWithType);
951
+ case "schedule":
952
+ return normalizeSchedulePayload(normalizedWithType);
468
953
  case "autoincremental": {
469
954
  const config = toJsonObject(normalizedWithType.config) ?? {};
470
955
  const segmentedByFieldId =