@praxisui/cron-builder 8.0.0-beta.6 → 8.0.0-beta.61

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -132,6 +132,28 @@ The AI adapter exposes both the legacy `value` and the structured scheduler stat
132
132
 
133
133
  AI patches should prefer `schedule.kind` plus `schedule.recurrence` for business scheduling intent, and reserve `schedule.expression.cron` for explicit custom CRON requests.
134
134
 
135
+ ## Agentic Authoring
136
+
137
+ The executable authoring contract is exported as `PRAXIS_CRON_BUILDER_AUTHORING_MANIFEST`.
138
+ It models typed operations over the same schedule runtime used by the component:
139
+
140
+ - `cron.expression.set`
141
+ - `cron.frequency.set`
142
+ - `cron.timezone.set`
143
+ - `cron.preset.apply`
144
+ - `cron.validate`
145
+ - `cron.preview.generate`
146
+
147
+ Expression, frequency, timezone and preset operations may patch canonical
148
+ `schedule` or compatibility `value` paths only after validation succeeds.
149
+ `cron.validate` and `cron.preview.generate` are read-only operations: invalid
150
+ schedules return structured `diagnostics` and do not mutate the current schedule.
151
+
152
+ Each operation declares `target.resolver`, `preconditions`, `validators`,
153
+ `affectedPaths`, `effects` and typed `submissionImpact`. Expression, frequency,
154
+ timezone and preset changes affect the submitted schedule value; validation and
155
+ preview generation are diagnostics-only operations with no submission mutation.
156
+
135
157
  ## Build Notes
136
158
 
137
159
  The builder resolves validation, humanized descriptions and occurrence preview inside the library runtime.
@@ -1181,16 +1181,16 @@ class PdxCronBuilderComponent {
1181
1181
  this.destroy$.next();
1182
1182
  this.destroy$.complete();
1183
1183
  }
1184
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PdxCronBuilderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1185
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.17", type: PdxCronBuilderComponent, isStandalone: true, selector: "pdx-cron-builder", inputs: { metadata: "metadata" }, providers: [
1184
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PdxCronBuilderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1185
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: PdxCronBuilderComponent, isStandalone: true, selector: "pdx-cron-builder", inputs: { metadata: "metadata" }, providers: [
1186
1186
  {
1187
1187
  provide: NG_VALUE_ACCESSOR,
1188
1188
  useExisting: forwardRef(() => PdxCronBuilderComponent),
1189
1189
  multi: true,
1190
1190
  },
1191
- ], ngImport: i0, template: "<div class=\"cron-builder-container\" (focusout)=\"onTouched()\">\n @if (metadata.mode === 'both') {\n <mat-tab-group\n [selectedIndex]=\"selectedTabIndex\"\n (selectedTabChange)=\"onTabChange($event)\"\n >\n <mat-tab label=\"Simple\"></mat-tab>\n <mat-tab label=\"Advanced\"></mat-tab>\n </mat-tab-group>\n }\n\n @if (value) {\n <div class=\"cron-expression\">\n <mat-form-field appearance=\"outline\" class=\"cron-expression-field\">\n <mat-label>CRON Expression</mat-label>\n <input matInput [value]=\"value\" readonly />\n <button\n mat-icon-button\n matSuffix\n (click)=\"copyCron()\"\n [matTooltip]=\"'Copy to clipboard'\"\n aria-label=\"Copy CRON expression\"\n >\n <mat-icon [praxisIcon]=\"'content_copy'\"></mat-icon>\n </button>\n </mat-form-field>\n <button mat-button (click)=\"importCron()\">Import CRON</button>\n </div>\n }\n\n @switch (activeTab) {\n @case ('simple') {\n <div [formGroup]=\"simpleForm\" class=\"simple-mode\">\n <mat-form-field appearance=\"outline\" class=\"preset-select\">\n <mat-label>Preset</mat-label>\n <mat-select formControlName=\"type\">\n <mat-option value=\"everyNMinutes\">A cada X min</mat-option>\n <mat-option value=\"dailyAt\">Diariamente \u00E0s</mat-option>\n <mat-option value=\"weekly\">Semanal (dias marcados) \u00E0s</mat-option>\n <mat-option value=\"monthlyDay\">Mensal (dia N) \u00E0s</mat-option>\n <mat-option value=\"monthlyNthWeekday\">\n Mensal (N-\u00E9sima 2\u00AA-feira) \u00E0s\n </mat-option>\n </mat-select>\n </mat-form-field>\n\n @switch (simpleControls.type.value) {\n @case ('everyNMinutes') {\n <div class=\"preset-body\">\n <mat-slider\n min=\"1\"\n max=\"60\"\n step=\"1\"\n thumbLabel\n >\n <input matSliderThumb formControlName=\"everyN\" />\n </mat-slider>\n <div class=\"cron-hint\">\n A cada {{ simpleControls.everyN.value }} minutos\n </div>\n </div>\n }\n\n @case ('dailyAt') {\n <div class=\"preset-body\">\n <mat-form-field appearance=\"outline\">\n <mat-label>Hora</mat-label>\n <input matInput type=\"time\" formControlName=\"dailyTime\" />\n </mat-form-field>\n <div class=\"cron-hint\">\n Diariamente \u00E0s {{ simpleControls.dailyTime.value }}\n </div>\n </div>\n }\n\n @case ('weekly') {\n <div class=\"preset-body\">\n <mat-chip-listbox formControlName=\"weeklyDays\" multiple>\n @for (day of weeklyDayOptions; track day) {\n <mat-chip-option [value]=\"day\">\n {{ weekdayLabels[day] }}\n </mat-chip-option>\n }\n </mat-chip-listbox>\n <mat-form-field appearance=\"outline\">\n <mat-label>Hora</mat-label>\n <input matInput type=\"time\" formControlName=\"weeklyTime\" />\n </mat-form-field>\n <div class=\"cron-hint\">\n Semanalmente \u00E0s {{ simpleControls.weeklyTime.value }}\n </div>\n </div>\n }\n\n @case ('monthlyDay') {\n <div class=\"preset-body\">\n <mat-form-field appearance=\"outline\">\n <mat-label>Dia</mat-label>\n <input\n matInput\n type=\"number\"\n formControlName=\"monthlyDay\"\n min=\"1\"\n max=\"31\"\n />\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>Hora</mat-label>\n <input matInput type=\"time\" formControlName=\"monthlyTime\" />\n </mat-form-field>\n <div class=\"cron-hint\">\n Dia {{ simpleControls.monthlyDay.value }} \u00E0s\n {{ simpleControls.monthlyTime.value }}\n </div>\n </div>\n }\n\n @case ('monthlyNthWeekday') {\n <div class=\"preset-body\">\n <mat-form-field appearance=\"outline\">\n <mat-label>N-\u00E9sima</mat-label>\n <mat-select formControlName=\"nth\">\n @for (nth of nthOrderOptions; track nth) {\n <mat-option [value]=\"nth\">{{ nth }}\u00AA</mat-option>\n }\n </mat-select>\n </mat-form-field>\n <mat-chip-listbox formControlName=\"nthDay\">\n @for (day of nthDayOptions; track day) {\n <mat-chip-option [value]=\"day\">\n {{ weekdayLabels[day] }}\n </mat-chip-option>\n }\n </mat-chip-listbox>\n <mat-form-field appearance=\"outline\">\n <mat-label>Hora</mat-label>\n <input matInput type=\"time\" formControlName=\"nthTime\" />\n </mat-form-field>\n <div class=\"cron-hint\">\n {{ simpleControls.nth.value }}\u00AA\n {{ weekdayLabels[simpleControls.nthDay.value] }}\n \u00E0s\n {{ simpleControls.nthTime.value }}\n </div>\n </div>\n }\n }\n </div>\n }\n\n @case ('advanced') {\n <div [formGroup]=\"form\">\n <div class=\"cron-fields\">\n @if (metadata.fields?.minutes) {\n <mat-form-field>\n <mat-label>Minutes</mat-label>\n <input matInput formControlName=\"minutes\" />\n </mat-form-field>\n }\n @if (metadata.fields?.hours) {\n <mat-form-field>\n <mat-label>Hours</mat-label>\n <input matInput formControlName=\"hours\" />\n </mat-form-field>\n }\n @if (metadata.fields?.dom) {\n <mat-form-field>\n <mat-label>Day of Month</mat-label>\n <input matInput formControlName=\"dayOfMonth\" />\n </mat-form-field>\n }\n @if (metadata.fields?.month) {\n <mat-form-field>\n <mat-label>Month</mat-label>\n <input matInput formControlName=\"month\" />\n </mat-form-field>\n }\n @if (metadata.fields?.dow) {\n <mat-form-field>\n <mat-label>Day of Week</mat-label>\n <input matInput formControlName=\"dayOfWeek\" />\n </mat-form-field>\n }\n @if (metadata.fields?.seconds) {\n <mat-form-field>\n <mat-label>Seconds</mat-label>\n <input matInput formControlName=\"seconds\" />\n </mat-form-field>\n }\n </div>\n </div>\n }\n }\n\n <div class=\"cron-feedback\">\n <mat-form-field appearance=\"outline\" class=\"timezone-field\">\n <mat-label>Timezone</mat-label>\n <mat-select [formControl]=\"timezoneControl\">\n @for (tz of timezoneOptions; track tz) {\n <mat-option [value]=\"tz\">\n {{ tz }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n\n @if (humanized) {\n <div class=\"humanized-description\" aria-live=\"polite\">\n {{ humanized }}\n <button\n mat-icon-button\n class=\"copy-humanized\"\n (click)=\"copyHumanized()\"\n [matTooltip]=\"'Copy description'\"\n aria-label=\"Copy description\"\n >\n <mat-icon [praxisIcon]=\"'content_copy'\"></mat-icon>\n </button>\n </div>\n }\n\n @if (error) {\n <div class=\"cron-error\">{{ error }}</div>\n }\n\n @if (preview.length > 0) {\n <div class=\"preview-section\">\n <h4>Next Occurrences:</h4>\n <mat-list>\n @for (date of preview; track $index) {\n <mat-list-item>\n {{\n date\n | date\n : \"full\"\n : timezoneControl.value\n : metadata.locale || \"pt-BR\"\n }}\n </mat-list-item>\n }\n </mat-list>\n </div>\n }\n </div>\n\n @if (metadata.hint) {\n <div class=\"cron-hint\">{{ metadata.hint }}</div>\n }\n</div>\n", styles: [":host{display:block}.cron-expression-field{width:100%}.cron-expression{margin-bottom:1rem}.simple-mode{display:flex;flex-direction:column;gap:1rem}.preset-body{display:flex;flex-direction:column;gap:.5rem}.cron-feedback{margin-top:1rem;display:flex;flex-direction:column;gap:.5rem}.humanized-description{display:flex;align-items:center;gap:.5rem}.timezone-field{width:250px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.MinValidator, selector: "input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]", inputs: ["min"] }, { kind: "directive", type: i1.MaxValidator, selector: "input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]", inputs: ["max"] }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: MatTabsModule }, { kind: "component", type: i2.MatTab, selector: "mat-tab", inputs: ["disabled", "label", "aria-label", "aria-labelledby", "labelClass", "bodyClass", "id"], exportAs: ["matTab"] }, { kind: "component", type: i2.MatTabGroup, selector: "mat-tab-group", inputs: ["color", "fitInkBarToContent", "mat-stretch-tabs", "mat-align-tabs", "dynamicHeight", "selectedIndex", "headerPosition", "animationDuration", "contentTabIndex", "disablePagination", "disableRipple", "preserveContent", "backgroundColor", "aria-label", "aria-labelledby"], outputs: ["selectedIndexChange", "focusChange", "animationDone", "selectedTabChange"], exportAs: ["matTabGroup"] }, { kind: "ngmodule", type: MatRadioModule }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i3.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3.MatLabel, selector: "mat-label" }, { kind: "directive", type: i3.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "component", type: i3.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i3.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i4.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "ngmodule", type: MatListModule }, { kind: "component", type: i5.MatList, selector: "mat-list", exportAs: ["matList"] }, { kind: "component", type: i5.MatListItem, selector: "mat-list-item, a[mat-list-item], button[mat-list-item]", inputs: ["activated"], exportAs: ["matListItem"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i6.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i6.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i7.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i8.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "ngmodule", type: MatSliderModule }, { kind: "component", type: i9.MatSlider, selector: "mat-slider", inputs: ["disabled", "discrete", "showTickMarks", "min", "color", "disableRipple", "max", "step", "displayWith"], exportAs: ["matSlider"] }, { kind: "directive", type: i9.MatSliderThumb, selector: "input[matSliderThumb]", inputs: ["value"], outputs: ["valueChange", "dragStart", "dragEnd"], exportAs: ["matSliderThumb"] }, { kind: "ngmodule", type: MatChipsModule }, { kind: "component", type: i10.MatChipListbox, selector: "mat-chip-listbox", inputs: ["multiple", "aria-orientation", "selectable", "compareWith", "required", "hideSingleSelectionIndicator", "value"], outputs: ["change"] }, { kind: "component", type: i10.MatChipOption, selector: "mat-basic-chip-option, [mat-basic-chip-option], mat-chip-option, [mat-chip-option]", inputs: ["selectable", "selected"], outputs: ["selectionChange"] }, { kind: "ngmodule", type: MatSnackBarModule }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }, { kind: "pipe", type: i11.DatePipe, name: "date" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1191
+ ], ngImport: i0, template: "<div class=\"cron-builder-container\" (focusout)=\"onTouched()\">\n @if (metadata.mode === 'both') {\n <mat-tab-group\n [selectedIndex]=\"selectedTabIndex\"\n (selectedTabChange)=\"onTabChange($event)\"\n >\n <mat-tab label=\"Simple\"></mat-tab>\n <mat-tab label=\"Advanced\"></mat-tab>\n </mat-tab-group>\n }\n\n @if (value) {\n <div class=\"cron-expression\">\n <mat-form-field appearance=\"outline\" class=\"cron-expression-field\">\n <mat-label>CRON Expression</mat-label>\n <input matInput [value]=\"value\" readonly />\n <button\n mat-icon-button\n matSuffix\n (click)=\"copyCron()\"\n [matTooltip]=\"'Copy to clipboard'\"\n aria-label=\"Copy CRON expression\"\n >\n <mat-icon [praxisIcon]=\"'content_copy'\"></mat-icon>\n </button>\n </mat-form-field>\n <button mat-button (click)=\"importCron()\">Import CRON</button>\n </div>\n }\n\n @switch (activeTab) {\n @case ('simple') {\n <div [formGroup]=\"simpleForm\" class=\"simple-mode\">\n <mat-form-field appearance=\"outline\" class=\"preset-select\">\n <mat-label>Preset</mat-label>\n <mat-select formControlName=\"type\">\n <mat-option value=\"everyNMinutes\">A cada X min</mat-option>\n <mat-option value=\"dailyAt\">Diariamente \u00E0s</mat-option>\n <mat-option value=\"weekly\">Semanal (dias marcados) \u00E0s</mat-option>\n <mat-option value=\"monthlyDay\">Mensal (dia N) \u00E0s</mat-option>\n <mat-option value=\"monthlyNthWeekday\">\n Mensal (N-\u00E9sima 2\u00AA-feira) \u00E0s\n </mat-option>\n </mat-select>\n </mat-form-field>\n\n @switch (simpleControls.type.value) {\n @case ('everyNMinutes') {\n <div class=\"preset-body\">\n <mat-slider\n min=\"1\"\n max=\"60\"\n step=\"1\"\n thumbLabel\n >\n <input matSliderThumb formControlName=\"everyN\" />\n </mat-slider>\n <div class=\"cron-hint\">\n A cada {{ simpleControls.everyN.value }} minutos\n </div>\n </div>\n }\n\n @case ('dailyAt') {\n <div class=\"preset-body\">\n <mat-form-field appearance=\"outline\">\n <mat-label>Hora</mat-label>\n <input matInput type=\"time\" formControlName=\"dailyTime\" />\n </mat-form-field>\n <div class=\"cron-hint\">\n Diariamente \u00E0s {{ simpleControls.dailyTime.value }}\n </div>\n </div>\n }\n\n @case ('weekly') {\n <div class=\"preset-body\">\n <mat-chip-listbox formControlName=\"weeklyDays\" multiple>\n @for (day of weeklyDayOptions; track day) {\n <mat-chip-option [value]=\"day\">\n {{ weekdayLabels[day] }}\n </mat-chip-option>\n }\n </mat-chip-listbox>\n <mat-form-field appearance=\"outline\">\n <mat-label>Hora</mat-label>\n <input matInput type=\"time\" formControlName=\"weeklyTime\" />\n </mat-form-field>\n <div class=\"cron-hint\">\n Semanalmente \u00E0s {{ simpleControls.weeklyTime.value }}\n </div>\n </div>\n }\n\n @case ('monthlyDay') {\n <div class=\"preset-body\">\n <mat-form-field appearance=\"outline\">\n <mat-label>Dia</mat-label>\n <input\n matInput\n type=\"number\"\n formControlName=\"monthlyDay\"\n min=\"1\"\n max=\"31\"\n />\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>Hora</mat-label>\n <input matInput type=\"time\" formControlName=\"monthlyTime\" />\n </mat-form-field>\n <div class=\"cron-hint\">\n Dia {{ simpleControls.monthlyDay.value }} \u00E0s\n {{ simpleControls.monthlyTime.value }}\n </div>\n </div>\n }\n\n @case ('monthlyNthWeekday') {\n <div class=\"preset-body\">\n <mat-form-field appearance=\"outline\">\n <mat-label>N-\u00E9sima</mat-label>\n <mat-select formControlName=\"nth\">\n @for (nth of nthOrderOptions; track nth) {\n <mat-option [value]=\"nth\">{{ nth }}\u00AA</mat-option>\n }\n </mat-select>\n </mat-form-field>\n <mat-chip-listbox formControlName=\"nthDay\">\n @for (day of nthDayOptions; track day) {\n <mat-chip-option [value]=\"day\">\n {{ weekdayLabels[day] }}\n </mat-chip-option>\n }\n </mat-chip-listbox>\n <mat-form-field appearance=\"outline\">\n <mat-label>Hora</mat-label>\n <input matInput type=\"time\" formControlName=\"nthTime\" />\n </mat-form-field>\n <div class=\"cron-hint\">\n {{ simpleControls.nth.value }}\u00AA\n {{ weekdayLabels[simpleControls.nthDay.value] }}\n \u00E0s\n {{ simpleControls.nthTime.value }}\n </div>\n </div>\n }\n }\n </div>\n }\n\n @case ('advanced') {\n <div [formGroup]=\"form\">\n <div class=\"cron-fields\">\n @if (metadata.fields?.minutes) {\n <mat-form-field>\n <mat-label>Minutes</mat-label>\n <input matInput formControlName=\"minutes\" />\n </mat-form-field>\n }\n @if (metadata.fields?.hours) {\n <mat-form-field>\n <mat-label>Hours</mat-label>\n <input matInput formControlName=\"hours\" />\n </mat-form-field>\n }\n @if (metadata.fields?.dom) {\n <mat-form-field>\n <mat-label>Day of Month</mat-label>\n <input matInput formControlName=\"dayOfMonth\" />\n </mat-form-field>\n }\n @if (metadata.fields?.month) {\n <mat-form-field>\n <mat-label>Month</mat-label>\n <input matInput formControlName=\"month\" />\n </mat-form-field>\n }\n @if (metadata.fields?.dow) {\n <mat-form-field>\n <mat-label>Day of Week</mat-label>\n <input matInput formControlName=\"dayOfWeek\" />\n </mat-form-field>\n }\n @if (metadata.fields?.seconds) {\n <mat-form-field>\n <mat-label>Seconds</mat-label>\n <input matInput formControlName=\"seconds\" />\n </mat-form-field>\n }\n </div>\n </div>\n }\n }\n\n <div class=\"cron-feedback\">\n <mat-form-field appearance=\"outline\" class=\"timezone-field\">\n <mat-label>Timezone</mat-label>\n <mat-select [formControl]=\"timezoneControl\">\n @for (tz of timezoneOptions; track tz) {\n <mat-option [value]=\"tz\">\n {{ tz }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n\n @if (humanized) {\n <div class=\"humanized-description\" aria-live=\"polite\">\n {{ humanized }}\n <button\n mat-icon-button\n class=\"copy-humanized\"\n (click)=\"copyHumanized()\"\n [matTooltip]=\"'Copy description'\"\n aria-label=\"Copy description\"\n >\n <mat-icon [praxisIcon]=\"'content_copy'\"></mat-icon>\n </button>\n </div>\n }\n\n @if (error) {\n <div class=\"cron-error\">{{ error }}</div>\n }\n\n @if (preview.length > 0) {\n <div class=\"preview-section\">\n <h4>Next Occurrences:</h4>\n <mat-list>\n @for (date of preview; track $index) {\n <mat-list-item>\n {{\n date\n | date\n : \"full\"\n : timezoneControl.value\n : metadata.locale || \"pt-BR\"\n }}\n </mat-list-item>\n }\n </mat-list>\n </div>\n }\n </div>\n\n @if (metadata.hint) {\n <div class=\"cron-hint\">{{ metadata.hint }}</div>\n }\n</div>\n", styles: [":host{display:block}.cron-expression-field{width:100%}.cron-expression{margin-bottom:1rem}.simple-mode{display:flex;flex-direction:column;gap:1rem}.preset-body{display:flex;flex-direction:column;gap:.5rem}.cron-feedback{margin-top:1rem;display:flex;flex-direction:column;gap:.5rem}.humanized-description{display:flex;align-items:center;gap:.5rem}.timezone-field{width:250px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.MinValidator, selector: "input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]", inputs: ["min"] }, { kind: "directive", type: i1.MaxValidator, selector: "input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]", inputs: ["max"] }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: MatTabsModule }, { kind: "component", type: i2.MatTab, selector: "mat-tab", inputs: ["disabled", "label", "aria-label", "aria-labelledby", "labelClass", "bodyClass", "id"], exportAs: ["matTab"] }, { kind: "component", type: i2.MatTabGroup, selector: "mat-tab-group", inputs: ["color", "fitInkBarToContent", "mat-stretch-tabs", "mat-align-tabs", "dynamicHeight", "selectedIndex", "headerPosition", "animationDuration", "contentTabIndex", "disablePagination", "disableRipple", "preserveContent", "backgroundColor", "aria-label", "aria-labelledby"], outputs: ["selectedIndexChange", "focusChange", "animationDone", "selectedTabChange"], exportAs: ["matTabGroup"] }, { kind: "ngmodule", type: MatRadioModule }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i3.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3.MatLabel, selector: "mat-label" }, { kind: "directive", type: i3.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "component", type: i3.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i3.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i4.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "ngmodule", type: MatListModule }, { kind: "component", type: i5.MatList, selector: "mat-list", exportAs: ["matList"] }, { kind: "component", type: i5.MatListItem, selector: "mat-list-item, a[mat-list-item], button[mat-list-item]", inputs: ["activated"], exportAs: ["matListItem"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i6.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i6.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i7.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i8.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "ngmodule", type: MatSliderModule }, { kind: "component", type: i9.MatSlider, selector: "mat-slider", inputs: ["disabled", "discrete", "showTickMarks", "min", "color", "disableRipple", "max", "step", "displayWith"], exportAs: ["matSlider"] }, { kind: "directive", type: i9.MatSliderThumb, selector: "input[matSliderThumb]", inputs: ["value"], outputs: ["valueChange", "dragStart", "dragEnd"], exportAs: ["matSliderThumb"] }, { kind: "ngmodule", type: MatChipsModule }, { kind: "component", type: i10.MatChipListbox, selector: "mat-chip-listbox", inputs: ["multiple", "aria-orientation", "selectable", "compareWith", "required", "hideSingleSelectionIndicator", "value"], outputs: ["change"] }, { kind: "component", type: i10.MatChipOption, selector: "mat-basic-chip-option, [mat-basic-chip-option], mat-chip-option, [mat-chip-option]", inputs: ["selectable", "selected"], outputs: ["selectionChange"] }, { kind: "ngmodule", type: MatSnackBarModule }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }, { kind: "pipe", type: i11.DatePipe, name: "date" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1192
1192
  }
1193
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PdxCronBuilderComponent, decorators: [{
1193
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PdxCronBuilderComponent, decorators: [{
1194
1194
  type: Component,
1195
1195
  args: [{ selector: 'pdx-cron-builder', standalone: true, imports: [
1196
1196
  CommonModule,
@@ -1336,6 +1336,294 @@ const CRON_BUILDER_AI_CAPABILITIES = {
1336
1336
  ],
1337
1337
  };
1338
1338
 
1339
+ const PRAXIS_CRON_BUILDER_AUTHORING_MANIFEST = {
1340
+ schemaVersion: '1.0.0',
1341
+ componentId: 'pdx-cron-builder',
1342
+ ownerPackage: '@praxisui/cron-builder',
1343
+ configSchemaId: 'CronBuilderMetadata',
1344
+ manifestVersion: '1.0.0',
1345
+ runtimeInputs: [
1346
+ { name: 'metadata', type: 'CronBuilderMetadata', description: 'Builder UI, timezone, locale, presets, validation and preview configuration.' },
1347
+ { name: 'value', type: 'string | ScheduleAuthoringConfig', description: 'Current CRON string compatibility value or canonical schedule authoring config.' },
1348
+ { name: 'disabled', type: 'boolean', description: 'ControlValueAccessor disabled state.' },
1349
+ ],
1350
+ editableTargets: [
1351
+ { kind: 'expression', resolver: 'schedule-expression', description: 'Canonical CRON expression under schedule.expression or legacy value.' },
1352
+ { kind: 'frequency', resolver: 'schedule-kind-and-recurrence', description: 'Business recurrence intent compiled into a canonical expression.' },
1353
+ { kind: 'timezone', resolver: 'schedule-timezone', description: 'IANA timezone used for preview and schedule metadata.' },
1354
+ { kind: 'preview', resolver: 'schedule-preview-config', description: 'Read-only preview generation request for next occurrences.' },
1355
+ { kind: 'validation', resolver: 'schedule-validation-request', description: 'Read-only validation diagnostics request for expression or schedule config.' },
1356
+ { kind: 'preset', resolver: 'cron-preset-by-label-or-expression', description: 'Preset entry in metadata.presets[] or top-level presets[].' },
1357
+ ],
1358
+ operations: [
1359
+ {
1360
+ operationId: 'cron.expression.set',
1361
+ title: 'Set CRON expression',
1362
+ scope: 'global',
1363
+ targetKind: 'expression',
1364
+ target: { kind: 'expression', resolver: 'schedule-expression', ambiguityPolicy: 'fail', required: false },
1365
+ inputSchema: {
1366
+ type: 'object',
1367
+ required: ['cron'],
1368
+ properties: {
1369
+ cron: { type: 'string' },
1370
+ dialect: { enum: ['unix', 'quartz', 'aws-eventbridge', 'kubernetes', 'github-actions', 'gcp-scheduler'] },
1371
+ seconds: { type: 'boolean' },
1372
+ },
1373
+ },
1374
+ effects: [{
1375
+ kind: 'compile-domain-patch',
1376
+ handler: 'cron-expression-set',
1377
+ handlerContract: {
1378
+ reads: ['schedule.expression', 'schedule.timezone', 'metadata.timezone', 'value'],
1379
+ writes: ['schedule.kind', 'schedule.expression', 'value', 'diagnostics'],
1380
+ identityKeys: ['schedule.expression.cron', 'schedule.expression.dialect'],
1381
+ inputSchema: {
1382
+ type: 'object',
1383
+ required: ['cron'],
1384
+ properties: {
1385
+ cron: { type: 'string' },
1386
+ dialect: { enum: ['unix', 'quartz', 'aws-eventbridge', 'kubernetes', 'github-actions', 'gcp-scheduler'] },
1387
+ seconds: { type: 'boolean' },
1388
+ },
1389
+ },
1390
+ failureModes: ['invalid-expression', 'invalid-dialect', 'seconds-field-mismatch'],
1391
+ description: 'Validates and normalizes an explicit CRON expression before writing schedule.expression and the compatibility value.',
1392
+ },
1393
+ }],
1394
+ destructive: false,
1395
+ requiresConfirmation: false,
1396
+ validators: ['cron-expression-valid', 'cron-dialect-compatible', 'editor-runtime-round-trip'],
1397
+ affectedPaths: ['schedule.kind', 'schedule.expression', 'schedule.expression.cron', 'schedule.expression.dialect', 'schedule.expression.seconds', 'value', 'diagnostics'],
1398
+ submissionImpact: 'affects-submission',
1399
+ preconditions: ['config-initialized'],
1400
+ },
1401
+ {
1402
+ operationId: 'cron.frequency.set',
1403
+ title: 'Set schedule frequency',
1404
+ scope: 'global',
1405
+ targetKind: 'frequency',
1406
+ target: { kind: 'frequency', resolver: 'schedule-kind-and-recurrence', ambiguityPolicy: 'fail', required: false },
1407
+ inputSchema: {
1408
+ type: 'object',
1409
+ required: ['kind', 'recurrence'],
1410
+ properties: {
1411
+ kind: { enum: ['once', 'interval', 'daily', 'weekly', 'monthly', 'customCron'] },
1412
+ recurrence: { type: 'object' },
1413
+ dialect: { enum: ['unix', 'quartz', 'aws-eventbridge', 'kubernetes', 'github-actions', 'gcp-scheduler'] },
1414
+ },
1415
+ },
1416
+ effects: [{
1417
+ kind: 'compile-domain-patch',
1418
+ handler: 'cron-frequency-to-expression',
1419
+ handlerContract: {
1420
+ reads: ['schedule.kind', 'schedule.recurrence', 'schedule.timezone', 'schedule.expression.dialect'],
1421
+ writes: ['schedule.kind', 'schedule.recurrence', 'schedule.expression', 'value'],
1422
+ identityKeys: ['schedule.kind'],
1423
+ inputSchema: {
1424
+ type: 'object',
1425
+ required: ['kind', 'recurrence'],
1426
+ properties: {
1427
+ kind: { enum: ['once', 'interval', 'daily', 'weekly', 'monthly', 'customCron'] },
1428
+ recurrence: { type: 'object' },
1429
+ dialect: { enum: ['unix', 'quartz', 'aws-eventbridge', 'kubernetes', 'github-actions', 'gcp-scheduler'] },
1430
+ },
1431
+ },
1432
+ failureModes: ['unsupported-kind', 'non-portable-frequency', 'invalid-recurrence', 'cron-compile-failed'],
1433
+ description: 'Compiles structured recurrence intent through compileScheduleExpression and updates the canonical schedule expression only when diagnostics have no errors.',
1434
+ },
1435
+ }],
1436
+ destructive: false,
1437
+ requiresConfirmation: false,
1438
+ validators: ['frequency-maps-to-canonical-expression', 'cron-expression-valid', 'diagnostics-before-patch'],
1439
+ affectedPaths: ['schedule.kind', 'schedule.recurrence', 'schedule.expression', 'value'],
1440
+ submissionImpact: 'affects-submission',
1441
+ preconditions: ['config-initialized'],
1442
+ },
1443
+ {
1444
+ operationId: 'cron.timezone.set',
1445
+ title: 'Set schedule timezone',
1446
+ scope: 'global',
1447
+ targetKind: 'timezone',
1448
+ target: { kind: 'timezone', resolver: 'schedule-timezone', ambiguityPolicy: 'fail', required: false },
1449
+ inputSchema: { type: 'object', required: ['timezone'], properties: { timezone: { type: 'string' } } },
1450
+ effects: [{
1451
+ kind: 'compile-domain-patch',
1452
+ handler: 'cron-timezone-set',
1453
+ handlerContract: {
1454
+ reads: ['value', 'schedule', 'metadata.timezone', 'metadata.previewOccurrences', 'metadata.previewFrom', 'metadata.locale'],
1455
+ writes: ['schedule.timezone', 'metadata.timezone', 'preview', 'diagnostics'],
1456
+ identityKeys: ['schedule.expression.cron', 'schedule.timezone'],
1457
+ inputSchema: { type: 'object', required: ['timezone'], properties: { timezone: { type: 'string' } } },
1458
+ failureModes: ['invalid-timezone', 'invalid-expression', 'preview-generation-failed'],
1459
+ description: 'Sets the IANA timezone used by schedule and metadata, then regenerates preview/diagnostics from the same expression.',
1460
+ },
1461
+ }],
1462
+ destructive: false,
1463
+ requiresConfirmation: false,
1464
+ validators: ['timezone-valid', 'preview-matches-expression', 'editor-runtime-round-trip'],
1465
+ affectedPaths: ['schedule.timezone', 'metadata.timezone', 'preview', 'diagnostics'],
1466
+ submissionImpact: 'affects-submission',
1467
+ preconditions: ['config-initialized'],
1468
+ },
1469
+ {
1470
+ operationId: 'cron.preset.apply',
1471
+ title: 'Apply CRON preset',
1472
+ scope: 'meta',
1473
+ targetKind: 'preset',
1474
+ target: { kind: 'preset', resolver: 'cron-preset-by-label-or-expression', ambiguityPolicy: 'fail', required: true },
1475
+ inputSchema: {
1476
+ type: 'object',
1477
+ required: ['labelOrCron'],
1478
+ properties: {
1479
+ labelOrCron: { type: 'string' },
1480
+ timezone: { type: 'string' },
1481
+ },
1482
+ },
1483
+ effects: [{
1484
+ kind: 'compile-domain-patch',
1485
+ handler: 'cron-preset-apply',
1486
+ handlerContract: {
1487
+ reads: ['metadata.presets[]', 'presets[]', 'schedule.timezone', 'metadata.timezone'],
1488
+ writes: ['schedule.kind', 'schedule.expression', 'schedule.timezone', 'value'],
1489
+ identityKeys: ['metadata.presets[].label', 'metadata.presets[].cron', 'presets[].label', 'presets[].cron'],
1490
+ inputSchema: {
1491
+ type: 'object',
1492
+ required: ['labelOrCron'],
1493
+ properties: {
1494
+ labelOrCron: { type: 'string' },
1495
+ timezone: { type: 'string' },
1496
+ },
1497
+ },
1498
+ failureModes: ['preset-not-found', 'preset-cron-invalid', 'timezone-invalid'],
1499
+ description: 'Resolves a preset by label or cron, validates it through normalizeScheduleValue/createSchedulePreview, then applies the canonical expression.',
1500
+ },
1501
+ }],
1502
+ destructive: false,
1503
+ requiresConfirmation: false,
1504
+ validators: ['preset-exists', 'preset-maps-to-canonical-expression', 'cron-expression-valid'],
1505
+ affectedPaths: ['schedule.kind', 'schedule.expression', 'schedule.timezone', 'value'],
1506
+ submissionImpact: 'affects-submission',
1507
+ preconditions: ['config-initialized', 'target-preset-exists'],
1508
+ },
1509
+ {
1510
+ operationId: 'cron.validate',
1511
+ title: 'Validate schedule',
1512
+ scope: 'global',
1513
+ targetKind: 'validation',
1514
+ target: { kind: 'validation', resolver: 'schedule-validation-request', ambiguityPolicy: 'fail', required: false },
1515
+ inputSchema: {
1516
+ type: 'object',
1517
+ properties: {
1518
+ cron: { type: 'string' },
1519
+ schedule: { type: 'object' },
1520
+ timezone: { type: 'string' },
1521
+ dialect: { enum: ['unix', 'quartz', 'aws-eventbridge', 'kubernetes', 'github-actions', 'gcp-scheduler'] },
1522
+ },
1523
+ },
1524
+ effects: [{
1525
+ kind: 'compile-domain-patch',
1526
+ handler: 'cron-validation-diagnostics',
1527
+ handlerContract: {
1528
+ reads: ['value', 'schedule', 'metadata.timezone'],
1529
+ writes: ['diagnostics'],
1530
+ identityKeys: ['schedule.expression.cron', 'schedule.timezone'],
1531
+ inputSchema: {
1532
+ type: 'object',
1533
+ properties: {
1534
+ cron: { type: 'string' },
1535
+ schedule: { type: 'object' },
1536
+ timezone: { type: 'string' },
1537
+ dialect: { enum: ['unix', 'quartz', 'aws-eventbridge', 'kubernetes', 'github-actions', 'gcp-scheduler'] },
1538
+ },
1539
+ },
1540
+ failureModes: ['invalid-expression', 'invalid-timezone', 'invalid-recurrence'],
1541
+ description: 'Runs normalizeScheduleValue and validateScheduleAuthoringConfig and returns diagnostics without mutating schedule/value paths.',
1542
+ },
1543
+ }],
1544
+ destructive: false,
1545
+ requiresConfirmation: false,
1546
+ validators: ['invalid-schedules-return-diagnostics', 'cron-expression-valid', 'timezone-valid'],
1547
+ affectedPaths: ['diagnostics'],
1548
+ submissionImpact: 'none',
1549
+ preconditions: ['config-initialized'],
1550
+ },
1551
+ {
1552
+ operationId: 'cron.preview.generate',
1553
+ title: 'Generate schedule preview',
1554
+ scope: 'global',
1555
+ targetKind: 'preview',
1556
+ target: { kind: 'preview', resolver: 'schedule-preview-config', ambiguityPolicy: 'fail', required: false },
1557
+ inputSchema: {
1558
+ type: 'object',
1559
+ properties: {
1560
+ occurrences: { type: 'number' },
1561
+ from: { type: 'string' },
1562
+ timezone: { type: 'string' },
1563
+ locale: { type: 'string' },
1564
+ },
1565
+ },
1566
+ effects: [{
1567
+ kind: 'compile-domain-patch',
1568
+ handler: 'cron-preview-generate',
1569
+ handlerContract: {
1570
+ reads: ['value', 'schedule', 'metadata.previewOccurrences', 'metadata.previewFrom', 'metadata.timezone', 'metadata.locale'],
1571
+ writes: ['preview', 'diagnostics'],
1572
+ identityKeys: ['schedule.expression.cron', 'schedule.timezone', 'metadata.previewFrom'],
1573
+ inputSchema: {
1574
+ type: 'object',
1575
+ properties: {
1576
+ occurrences: { type: 'number' },
1577
+ from: { type: 'string' },
1578
+ timezone: { type: 'string' },
1579
+ locale: { type: 'string' },
1580
+ },
1581
+ },
1582
+ failureModes: ['invalid-expression', 'invalid-timezone', 'preview-empty', 'diagnostics-contain-errors'],
1583
+ description: 'Runs createSchedulePreview and returns read-only preview occurrences plus diagnostics; it does not patch the schedule when validation fails.',
1584
+ },
1585
+ }],
1586
+ destructive: false,
1587
+ requiresConfirmation: false,
1588
+ validators: ['preview-matches-expression', 'timezone-valid', 'invalid-schedules-return-diagnostics'],
1589
+ affectedPaths: ['preview', 'diagnostics'],
1590
+ submissionImpact: 'none',
1591
+ preconditions: ['config-initialized'],
1592
+ },
1593
+ ],
1594
+ validators: [
1595
+ { validatorId: 'cron-expression-valid', level: 'error', code: 'PCRON001', description: 'CRON expressions must pass the canonical cron-runtime parser for the selected dialect before schedule/value paths are patched.' },
1596
+ { validatorId: 'cron-dialect-compatible', level: 'error', code: 'PCRON002', description: 'Field count, seconds support and dialect-specific rules must match CRON_DIALECTS.' },
1597
+ { validatorId: 'timezone-valid', level: 'error', code: 'PCRON003', description: 'Timezone must be a valid IANA timezone accepted by Intl.DateTimeFormat and schedule preview.' },
1598
+ { validatorId: 'frequency-maps-to-canonical-expression', level: 'error', code: 'PCRON004', description: 'Structured frequency intent must compile through compileScheduleExpression into the canonical expression when portable.' },
1599
+ { validatorId: 'preset-exists', level: 'error', code: 'PCRON005', description: 'Preset operations must resolve an existing preset by label or cron expression.' },
1600
+ { validatorId: 'preset-maps-to-canonical-expression', level: 'error', code: 'PCRON006', description: 'Preset cron values must normalize into ScheduleAuthoringConfig and a valid canonical expression.' },
1601
+ { validatorId: 'preview-matches-expression', level: 'error', code: 'PCRON007', description: 'Preview occurrences must be generated from the same expression, timezone and from-date requested by authoring.' },
1602
+ { validatorId: 'invalid-schedules-return-diagnostics', level: 'error', code: 'PCRON008', description: 'Validation and preview requests for invalid schedules must return diagnostics and must not patch schedule/value fields.' },
1603
+ { validatorId: 'diagnostics-before-patch', level: 'error', code: 'PCRON009', description: 'Operations that compile recurrence or presets must evaluate diagnostics before mutating canonical schedule/value paths.' },
1604
+ { validatorId: 'editor-runtime-round-trip', level: 'error', code: 'PCRON010', description: 'ControlValueAccessor, runtime preview and AI adapter snapshots must preserve expression and timezone.' },
1605
+ ],
1606
+ roundTripRequirements: [
1607
+ 'Expression and timezone edits must round-trip through ControlValueAccessor, normalizeScheduleValue and createSchedulePreview without losing the selected timezone.',
1608
+ 'Structured frequency authoring must use ScheduleAuthoringConfig and compileScheduleExpression as the canonical source of recurrence semantics.',
1609
+ 'Validation and preview operations are read-only: invalid schedules return diagnostics and must not mutate schedule/value paths.',
1610
+ 'Preset operations must resolve stable label/cron identities and validate the resulting expression before applying it.',
1611
+ 'Preview generation must use the same expression, timezone, locale, occurrences and from-date that are visible in the schedule config or metadata input.',
1612
+ ],
1613
+ examples: [
1614
+ { id: 'weekday-0800', request: 'Run every weekday at 08:00.', operationId: 'cron.frequency.set', params: { kind: 'daily', recurrence: { daily: { times: ['08:00'], onlyWeekdays: true } }, dialect: 'unix' }, isPositive: true },
1615
+ { id: 'every-15-minutes', request: 'Run every 15 minutes.', operationId: 'cron.frequency.set', params: { kind: 'interval', recurrence: { interval: { every: 15, unit: 'minutes' } }, dialect: 'unix' }, isPositive: true },
1616
+ { id: 'monthly-first-day', request: 'Run monthly on the first day at 09:00.', operationId: 'cron.frequency.set', params: { kind: 'monthly', recurrence: { monthly: { mode: 'dayOfMonth', dayOfMonth: 1, times: ['09:00'] } }, dialect: 'unix' }, isPositive: true },
1617
+ { id: 'set-explicit-expression', request: 'Use cron expression 0 8 * * 1-5.', operationId: 'cron.expression.set', params: { cron: '0 8 * * 1-5', dialect: 'unix', seconds: false }, isPositive: true },
1618
+ { id: 'change-timezone-preview', request: 'Change timezone to America/Sao_Paulo and preview the next runs.', operationId: 'cron.timezone.set', params: { timezone: 'America/Sao_Paulo' }, isPositive: true },
1619
+ { id: 'preview-next-five', request: 'Preview the next five runs from 2026-04-20T00:00:00Z.', operationId: 'cron.preview.generate', params: { occurrences: 5, from: '2026-04-20T00:00:00.000Z', timezone: 'UTC' }, isPositive: true },
1620
+ { id: 'apply-business-hours-preset', request: 'Apply the business hours preset.', operationId: 'cron.preset.apply', target: 'business hours', params: { labelOrCron: 'business hours' }, isPositive: true },
1621
+ { id: 'reject-impossible-expression', request: 'Use cron expression 99 99 * * *.', operationId: 'cron.expression.set', params: { cron: '99 99 * * *', dialect: 'unix' }, isPositive: false },
1622
+ { id: 'diagnose-invalid-expression', request: 'Validate 99 99 * * * and explain why it is invalid.', operationId: 'cron.validate', params: { cron: '99 99 * * *', dialect: 'unix' }, isPositive: true },
1623
+ { id: 'reject-invalid-timezone', request: 'Change timezone to Mars/Olympus.', operationId: 'cron.timezone.set', params: { timezone: 'Mars/Olympus' }, isPositive: false },
1624
+ ],
1625
+ };
1626
+
1339
1627
  /*
1340
1628
  * Public API Surface of praxis-cron-builder
1341
1629
  */
@@ -1344,4 +1632,4 @@ const CRON_BUILDER_AI_CAPABILITIES = {
1344
1632
  * Generated bundle index. Do not edit.
1345
1633
  */
1346
1634
 
1347
- export { CRON_BUILDER_AI_CAPABILITIES, CRON_DIALECTS, PDX_CRON_BUILDER_DOC_META_LEGACY, PdxCronBuilderComponent, SCHEDULE_AUTHORING_CONFIG_VERSION, compileScheduleExpression, createSchedulePreview, getCronDialectDefinition, inferCronDialect, normalizeScheduleValue, validateScheduleAuthoringConfig };
1635
+ export { CRON_BUILDER_AI_CAPABILITIES, CRON_DIALECTS, PDX_CRON_BUILDER_DOC_META_LEGACY, PRAXIS_CRON_BUILDER_AUTHORING_MANIFEST, PdxCronBuilderComponent, SCHEDULE_AUTHORING_CONFIG_VERSION, compileScheduleExpression, createSchedulePreview, getCronDialectDefinition, inferCronDialect, normalizeScheduleValue, validateScheduleAuthoringConfig };
package/package.json CHANGED
@@ -1,14 +1,16 @@
1
1
  {
2
2
  "name": "@praxisui/cron-builder",
3
- "version": "8.0.0-beta.6",
3
+ "version": "8.0.0-beta.61",
4
4
  "description": "Cron expression builder utilities and components for Praxis UI.",
5
5
  "peerDependencies": {
6
- "@angular/common": "^20.1.0",
7
- "@angular/core": "^20.1.0",
8
- "@angular/forms": "^20.1.0",
9
- "@angular/cdk": "^20.1.0",
10
- "@angular/material": "^20.1.0",
11
- "@praxisui/core": "^8.0.0-beta.6"
6
+ "@angular/common": "^21.0.0",
7
+ "@angular/core": "^21.0.0",
8
+ "@angular/forms": "^21.0.0",
9
+ "@angular/cdk": "^21.0.0",
10
+ "@angular/material": "^21.0.0",
11
+ "@praxisui/core": "^8.0.0-beta.61",
12
+ "@praxisui/ai": "^8.0.0-beta.61",
13
+ "rxjs": "~7.8.0"
12
14
  },
13
15
  "dependencies": {
14
16
  "tslib": "^2.3.0",
@@ -39,14 +41,15 @@
39
41
  ],
40
42
  "sideEffects": false,
41
43
  "module": "fesm2022/praxisui-cron-builder.mjs",
42
- "typings": "index.d.ts",
44
+ "typings": "types/praxisui-cron-builder.d.ts",
43
45
  "exports": {
44
46
  "./package.json": {
45
47
  "default": "./package.json"
46
48
  },
47
49
  ".": {
48
- "types": "./index.d.ts",
50
+ "types": "./types/praxisui-cron-builder.d.ts",
49
51
  "default": "./fesm2022/praxisui-cron-builder.mjs"
50
52
  }
51
- }
53
+ },
54
+ "type": "module"
52
55
  }
@@ -2,7 +2,7 @@ import * as i0 from '@angular/core';
2
2
  import { OnInit, OnDestroy } from '@angular/core';
3
3
  import { ControlValueAccessor, FormGroup, FormControl } from '@angular/forms';
4
4
  import { MatTabChangeEvent } from '@angular/material/tabs';
5
- import { AiCapabilityCategory, AiValueKind, AiCapability, AiCapabilityCatalog } from '@praxisui/core';
5
+ import { AiCapabilityCatalog, AiCapability, AiCapabilityCategory, AiValueKind, ComponentAuthoringManifest } from '@praxisui/core';
6
6
 
7
7
  declare const SCHEDULE_AUTHORING_CONFIG_VERSION: "v1";
8
8
  type ScheduleAuthoringConfigVersion = typeof SCHEDULE_AUTHORING_CONFIG_VERSION;
@@ -282,5 +282,7 @@ interface CapabilityCatalog extends AiCapabilityCatalog {
282
282
  }
283
283
  declare const CRON_BUILDER_AI_CAPABILITIES: CapabilityCatalog;
284
284
 
285
- export { CRON_BUILDER_AI_CAPABILITIES, CRON_DIALECTS, PDX_CRON_BUILDER_DOC_META_LEGACY, PdxCronBuilderComponent, SCHEDULE_AUTHORING_CONFIG_VERSION, compileScheduleExpression, createSchedulePreview, getCronDialectDefinition, inferCronDialect, normalizeScheduleValue, validateScheduleAuthoringConfig };
285
+ declare const PRAXIS_CRON_BUILDER_AUTHORING_MANIFEST: ComponentAuthoringManifest;
286
+
287
+ export { CRON_BUILDER_AI_CAPABILITIES, CRON_DIALECTS, PDX_CRON_BUILDER_DOC_META_LEGACY, PRAXIS_CRON_BUILDER_AUTHORING_MANIFEST, PdxCronBuilderComponent, SCHEDULE_AUTHORING_CONFIG_VERSION, compileScheduleExpression, createSchedulePreview, getCronDialectDefinition, inferCronDialect, normalizeScheduleValue, validateScheduleAuthoringConfig };
286
288
  export type { AdvancedCronFormValue, Capability, CapabilityCatalog, CapabilityCategory, CompiledScheduleExpression, CreateSchedulePreviewOptions, CronBuilderMetadata, CronDialect, CronDialectDefinition, CronPresetType, DailySchedule, IntervalSchedule, MonthlySchedule, OnceSchedule, Recurrence, ScheduleAuthoringConfig, ScheduleAuthoringConfigVersion, ScheduleCronExpression, ScheduleDiagnostic, ScheduleExecutionPolicy, ScheduleGovernance, ScheduleInput, ScheduleKind, ScheduleNormalizeOptions, ScheduleNormalizeResult, SchedulePreviewConfig, SchedulePreviewOccurrence, SchedulePreviewResult, ScheduleRecurrence, ScheduleSeverity, ScheduleTimeUnit, ScheduleWeekday, ScheduleWindow, SimpleCronFormValue, ValueKind, WeeklySchedule };