@praxisui/cron-builder 8.0.0-beta.7 → 8.0.0-beta.71
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: "
|
|
1185
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "
|
|
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: "
|
|
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.
|
|
3
|
+
"version": "8.0.0-beta.71",
|
|
4
4
|
"description": "Cron expression builder utilities and components for Praxis UI.",
|
|
5
5
|
"peerDependencies": {
|
|
6
|
-
"@angular/common": "^
|
|
7
|
-
"@angular/core": "^
|
|
8
|
-
"@angular/forms": "^
|
|
9
|
-
"@angular/cdk": "^
|
|
10
|
-
"@angular/material": "^
|
|
11
|
-
"@praxisui/core": "^8.0.0-beta.
|
|
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.71",
|
|
12
|
+
"@praxisui/ai": "^8.0.0-beta.71",
|
|
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": "
|
|
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": "./
|
|
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,
|
|
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
|
-
|
|
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 };
|