@praxisui/cron-builder 7.0.0-beta.0 → 8.0.0-beta.1
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 +39 -0
- package/fesm2022/praxisui-cron-builder.mjs +512 -27
- package/index.d.ts +154 -5
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -79,6 +79,7 @@ Template-only example:
|
|
|
79
79
|
Exports:
|
|
80
80
|
- `PdxCronBuilderComponent`
|
|
81
81
|
- Types: `CronBuilderMetadata`, `SimpleCronFormValue`, `AdvancedCronFormValue`, `CronPresetType`
|
|
82
|
+
- Scheduler authoring foundation: `ScheduleAuthoringConfig`, `CronDialect`, `CRON_DIALECTS`, `normalizeScheduleValue`, `compileScheduleExpression`, `validateScheduleAuthoringConfig`, `createSchedulePreview`
|
|
82
83
|
|
|
83
84
|
Inputs/Outputs:
|
|
84
85
|
- `metadata: CronBuilderMetadata` – configure fields, timezone, locale, presets, preview.
|
|
@@ -93,6 +94,44 @@ Inputs/Outputs:
|
|
|
93
94
|
- `previewOccurrences?: number` – number of preview dates to show.
|
|
94
95
|
- `validators?: { invalidCronMessage?: string }` – customize error messages.
|
|
95
96
|
|
|
97
|
+
## Scheduler Authoring Foundation
|
|
98
|
+
|
|
99
|
+
`@praxisui/cron-builder` also exposes the canonical foundation for evolving CRON editing into schedule authoring.
|
|
100
|
+
This keeps enterprise scheduling semantics in the library instead of pushing cron parsing, dialect checks or preview policy into host applications.
|
|
101
|
+
|
|
102
|
+
The initial contract is `ScheduleAuthoringConfig`. It models:
|
|
103
|
+
- schedule kind: `once`, `interval`, `daily`, `weekly`, `monthly` or `customCron`;
|
|
104
|
+
- timezone and locale;
|
|
105
|
+
- CRON expression plus explicit dialect;
|
|
106
|
+
- recurrence intent;
|
|
107
|
+
- start/end window;
|
|
108
|
+
- execution policy such as enabled state, missed-run policy, concurrency and jitter;
|
|
109
|
+
- preview configuration;
|
|
110
|
+
- governance metadata such as name, owner and tags.
|
|
111
|
+
|
|
112
|
+
The exported `CRON_DIALECTS` matrix describes the supported dialect families:
|
|
113
|
+
- Unix cron
|
|
114
|
+
- Quartz cron
|
|
115
|
+
- AWS EventBridge cron
|
|
116
|
+
- Kubernetes CronJob
|
|
117
|
+
- GitHub Actions schedule
|
|
118
|
+
- Google Cloud Scheduler
|
|
119
|
+
|
|
120
|
+
Use `normalizeScheduleValue(value)` to convert legacy string values into `ScheduleAuthoringConfig` while preserving current `ControlValueAccessor` compatibility.
|
|
121
|
+
The current component still emits the existing CRON string; the schedule authoring contract is the platform foundation for the next visual authoring iteration.
|
|
122
|
+
|
|
123
|
+
Runtime helpers:
|
|
124
|
+
- `compileScheduleExpression(config)` compiles supported schedule intents (`interval`, `daily`, `weekly`, `monthly`, `customCron`) into CRON when the intent has a portable representation.
|
|
125
|
+
- `validateScheduleAuthoringConfig(config)` returns structured diagnostics for invalid or non-portable schedules.
|
|
126
|
+
- `createSchedulePreview(value, preview)` returns the compiled expression, humanized text, structured occurrences and diagnostics.
|
|
127
|
+
|
|
128
|
+
The AI adapter exposes both the legacy `value` and the structured scheduler state:
|
|
129
|
+
- `schedule` is the canonical authoring config snapshot.
|
|
130
|
+
- `diagnostics` contains read-only validation results.
|
|
131
|
+
- `preview` contains read-only structured next occurrences.
|
|
132
|
+
|
|
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
|
+
|
|
96
135
|
## Build Notes
|
|
97
136
|
|
|
98
137
|
The builder resolves validation, humanized descriptions and occurrence preview inside the library runtime.
|
|
@@ -429,6 +429,451 @@ function getNextCronOccurrences(expression, count, options) {
|
|
|
429
429
|
return results;
|
|
430
430
|
}
|
|
431
431
|
|
|
432
|
+
const SCHEDULE_AUTHORING_CONFIG_VERSION = 'v1';
|
|
433
|
+
const CRON_DIALECTS = {
|
|
434
|
+
unix: {
|
|
435
|
+
dialect: 'unix',
|
|
436
|
+
label: 'Unix cron',
|
|
437
|
+
fieldCount: { min: 5, max: 5 },
|
|
438
|
+
supportsSeconds: false,
|
|
439
|
+
supportsYear: false,
|
|
440
|
+
supportsNamedMonths: true,
|
|
441
|
+
supportsNamedWeekdays: true,
|
|
442
|
+
supportsQuestionMark: false,
|
|
443
|
+
supportsLast: false,
|
|
444
|
+
supportsNearestWeekday: false,
|
|
445
|
+
supportsNthWeekday: false,
|
|
446
|
+
timezoneMode: 'host-local',
|
|
447
|
+
dayOfMonthDayOfWeekRule: 'unix-or',
|
|
448
|
+
examples: ['*/5 * * * *', '0 9 * * 1-5'],
|
|
449
|
+
},
|
|
450
|
+
quartz: {
|
|
451
|
+
dialect: 'quartz',
|
|
452
|
+
label: 'Quartz cron',
|
|
453
|
+
fieldCount: { min: 6, max: 7 },
|
|
454
|
+
supportsSeconds: true,
|
|
455
|
+
supportsYear: true,
|
|
456
|
+
supportsNamedMonths: true,
|
|
457
|
+
supportsNamedWeekdays: true,
|
|
458
|
+
supportsQuestionMark: true,
|
|
459
|
+
supportsLast: true,
|
|
460
|
+
supportsNearestWeekday: true,
|
|
461
|
+
supportsNthWeekday: true,
|
|
462
|
+
timezoneMode: 'external-field',
|
|
463
|
+
dayOfMonthDayOfWeekRule: 'either-or-question-mark',
|
|
464
|
+
examples: ['0 0 9 ? * MON-FRI', '0 0 9 ? * 2#1'],
|
|
465
|
+
},
|
|
466
|
+
'aws-eventbridge': {
|
|
467
|
+
dialect: 'aws-eventbridge',
|
|
468
|
+
label: 'AWS EventBridge cron',
|
|
469
|
+
fieldCount: { min: 6, max: 6 },
|
|
470
|
+
supportsSeconds: false,
|
|
471
|
+
supportsYear: true,
|
|
472
|
+
supportsNamedMonths: true,
|
|
473
|
+
supportsNamedWeekdays: true,
|
|
474
|
+
supportsQuestionMark: true,
|
|
475
|
+
supportsLast: true,
|
|
476
|
+
supportsNearestWeekday: false,
|
|
477
|
+
supportsNthWeekday: true,
|
|
478
|
+
timezoneMode: 'external-field',
|
|
479
|
+
dayOfMonthDayOfWeekRule: 'either-or-question-mark',
|
|
480
|
+
examples: ['0 9 ? * MON-FRI *', '0 9 ? * 2#1 *'],
|
|
481
|
+
},
|
|
482
|
+
kubernetes: {
|
|
483
|
+
dialect: 'kubernetes',
|
|
484
|
+
label: 'Kubernetes CronJob',
|
|
485
|
+
fieldCount: { min: 5, max: 5 },
|
|
486
|
+
supportsSeconds: false,
|
|
487
|
+
supportsYear: false,
|
|
488
|
+
supportsNamedMonths: true,
|
|
489
|
+
supportsNamedWeekdays: true,
|
|
490
|
+
supportsQuestionMark: true,
|
|
491
|
+
supportsLast: false,
|
|
492
|
+
supportsNearestWeekday: false,
|
|
493
|
+
supportsNthWeekday: false,
|
|
494
|
+
timezoneMode: 'external-field',
|
|
495
|
+
dayOfMonthDayOfWeekRule: 'unix-or',
|
|
496
|
+
examples: ['*/5 * * * *', '0 9 * * 1-5'],
|
|
497
|
+
},
|
|
498
|
+
'github-actions': {
|
|
499
|
+
dialect: 'github-actions',
|
|
500
|
+
label: 'GitHub Actions schedule',
|
|
501
|
+
fieldCount: { min: 5, max: 5 },
|
|
502
|
+
supportsSeconds: false,
|
|
503
|
+
supportsYear: false,
|
|
504
|
+
supportsNamedMonths: true,
|
|
505
|
+
supportsNamedWeekdays: true,
|
|
506
|
+
supportsQuestionMark: false,
|
|
507
|
+
supportsLast: false,
|
|
508
|
+
supportsNearestWeekday: false,
|
|
509
|
+
supportsNthWeekday: false,
|
|
510
|
+
timezoneMode: 'fixed-utc',
|
|
511
|
+
dayOfMonthDayOfWeekRule: 'unix-or',
|
|
512
|
+
examples: ['*/15 * * * *', '0 9 * * 1-5'],
|
|
513
|
+
},
|
|
514
|
+
'gcp-scheduler': {
|
|
515
|
+
dialect: 'gcp-scheduler',
|
|
516
|
+
label: 'Google Cloud Scheduler',
|
|
517
|
+
fieldCount: { min: 5, max: 5 },
|
|
518
|
+
supportsSeconds: false,
|
|
519
|
+
supportsYear: false,
|
|
520
|
+
supportsNamedMonths: true,
|
|
521
|
+
supportsNamedWeekdays: true,
|
|
522
|
+
supportsQuestionMark: false,
|
|
523
|
+
supportsLast: false,
|
|
524
|
+
supportsNearestWeekday: false,
|
|
525
|
+
supportsNthWeekday: false,
|
|
526
|
+
timezoneMode: 'external-field',
|
|
527
|
+
dayOfMonthDayOfWeekRule: 'unix-or',
|
|
528
|
+
examples: ['*/5 * * * *', '0 9 * * MON-FRI'],
|
|
529
|
+
},
|
|
530
|
+
};
|
|
531
|
+
function getCronDialectDefinition(dialect) {
|
|
532
|
+
return CRON_DIALECTS[dialect];
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
function normalizeScheduleValue(value, options = {}) {
|
|
536
|
+
if (value == null || value === '') {
|
|
537
|
+
return {
|
|
538
|
+
config: null,
|
|
539
|
+
diagnostics: [],
|
|
540
|
+
};
|
|
541
|
+
}
|
|
542
|
+
if (typeof value === 'string') {
|
|
543
|
+
return normalizeCronString(value, options);
|
|
544
|
+
}
|
|
545
|
+
return normalizeScheduleConfig(value, options);
|
|
546
|
+
}
|
|
547
|
+
function inferCronDialect(expression) {
|
|
548
|
+
const fieldCount = expression.trim().split(/\s+/).filter(Boolean).length;
|
|
549
|
+
return fieldCount === 6 ? 'quartz' : 'unix';
|
|
550
|
+
}
|
|
551
|
+
function normalizeCronString(cron, options) {
|
|
552
|
+
const trimmed = cron.trim();
|
|
553
|
+
const dialect = options.dialect ?? inferCronDialect(trimmed);
|
|
554
|
+
const diagnostics = createCronDiagnostics(trimmed, dialect);
|
|
555
|
+
return {
|
|
556
|
+
config: {
|
|
557
|
+
version: SCHEDULE_AUTHORING_CONFIG_VERSION,
|
|
558
|
+
kind: 'customCron',
|
|
559
|
+
timezone: options.timezone ?? 'UTC',
|
|
560
|
+
locale: options.locale,
|
|
561
|
+
expression: {
|
|
562
|
+
cron: trimmed,
|
|
563
|
+
dialect,
|
|
564
|
+
seconds: trimmed.split(/\s+/).length === 6 && dialect !== 'aws-eventbridge',
|
|
565
|
+
},
|
|
566
|
+
preview: {
|
|
567
|
+
occurrences: options.previewOccurrences ?? 5,
|
|
568
|
+
},
|
|
569
|
+
},
|
|
570
|
+
diagnostics,
|
|
571
|
+
};
|
|
572
|
+
}
|
|
573
|
+
function normalizeScheduleConfig(config, options) {
|
|
574
|
+
const normalized = {
|
|
575
|
+
...config,
|
|
576
|
+
version: SCHEDULE_AUTHORING_CONFIG_VERSION,
|
|
577
|
+
timezone: config.timezone || options.timezone || 'UTC',
|
|
578
|
+
locale: config.locale ?? options.locale,
|
|
579
|
+
preview: {
|
|
580
|
+
occurrences: options.previewOccurrences ?? config.preview?.occurrences ?? 5,
|
|
581
|
+
from: config.preview?.from,
|
|
582
|
+
},
|
|
583
|
+
};
|
|
584
|
+
return {
|
|
585
|
+
config: normalized,
|
|
586
|
+
diagnostics: normalized.expression?.cron
|
|
587
|
+
? createCronDiagnostics(normalized.expression.cron, normalized.expression.dialect)
|
|
588
|
+
: [],
|
|
589
|
+
};
|
|
590
|
+
}
|
|
591
|
+
function createCronDiagnostics(cron, dialect) {
|
|
592
|
+
const diagnostics = [];
|
|
593
|
+
const fieldCount = cron.trim().split(/\s+/).filter(Boolean).length;
|
|
594
|
+
if (dialect === 'aws-eventbridge' && fieldCount !== 6) {
|
|
595
|
+
diagnostics.push({
|
|
596
|
+
severity: 'error',
|
|
597
|
+
code: 'cron.aws.fieldCount',
|
|
598
|
+
field: 'expression.cron',
|
|
599
|
+
message: 'AWS EventBridge schedules require six fields.',
|
|
600
|
+
suggestion: 'Use the AWS order: minutes hours day-of-month month day-of-week year.',
|
|
601
|
+
});
|
|
602
|
+
}
|
|
603
|
+
else if (dialect !== 'aws-eventbridge' && !isValidCronExpression(cron)) {
|
|
604
|
+
diagnostics.push({
|
|
605
|
+
severity: 'error',
|
|
606
|
+
code: 'cron.invalid',
|
|
607
|
+
field: 'expression.cron',
|
|
608
|
+
message: 'Invalid CRON expression for the selected dialect.',
|
|
609
|
+
suggestion: 'Check field count, ranges, steps and unsupported special characters.',
|
|
610
|
+
});
|
|
611
|
+
}
|
|
612
|
+
if (dialect === 'unix' && fieldCount !== 5) {
|
|
613
|
+
diagnostics.push({
|
|
614
|
+
severity: 'error',
|
|
615
|
+
code: 'cron.unix.fieldCount',
|
|
616
|
+
field: 'expression.cron',
|
|
617
|
+
message: 'Unix cron schedules require five fields.',
|
|
618
|
+
suggestion: 'Remove seconds or year fields, or select a dialect that supports them.',
|
|
619
|
+
});
|
|
620
|
+
}
|
|
621
|
+
if (dialect === 'quartz' && fieldCount < 6) {
|
|
622
|
+
diagnostics.push({
|
|
623
|
+
severity: 'warning',
|
|
624
|
+
code: 'cron.quartz.fieldCount',
|
|
625
|
+
field: 'expression.cron',
|
|
626
|
+
message: 'Quartz cron commonly uses seconds as the first field.',
|
|
627
|
+
suggestion: 'Add a seconds field or select the Unix dialect.',
|
|
628
|
+
});
|
|
629
|
+
}
|
|
630
|
+
return diagnostics;
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
function compileScheduleExpression(config) {
|
|
634
|
+
const diagnostics = [];
|
|
635
|
+
const dialect = config.expression?.dialect ?? 'unix';
|
|
636
|
+
if (config.kind === 'customCron') {
|
|
637
|
+
if (!config.expression?.cron) {
|
|
638
|
+
diagnostics.push(createDiagnostic('error', 'schedule.cron.required', 'expression.cron', 'A CRON expression is required for custom schedules.'));
|
|
639
|
+
return { expression: null, diagnostics };
|
|
640
|
+
}
|
|
641
|
+
const seconds = config.expression.seconds ?? config.expression.cron.trim().split(/\s+/).length === 6;
|
|
642
|
+
return {
|
|
643
|
+
expression: {
|
|
644
|
+
cron: config.expression.cron.trim(),
|
|
645
|
+
dialect,
|
|
646
|
+
seconds,
|
|
647
|
+
},
|
|
648
|
+
diagnostics,
|
|
649
|
+
};
|
|
650
|
+
}
|
|
651
|
+
if (config.kind === 'once') {
|
|
652
|
+
diagnostics.push(createDiagnostic('warning', 'schedule.once.noCron', 'kind', 'One-time schedules do not have a portable CRON representation.'));
|
|
653
|
+
return { expression: null, diagnostics };
|
|
654
|
+
}
|
|
655
|
+
if (config.kind === 'interval') {
|
|
656
|
+
return compileIntervalSchedule(config, dialect);
|
|
657
|
+
}
|
|
658
|
+
if (config.kind === 'daily') {
|
|
659
|
+
return compileDailySchedule(config, dialect);
|
|
660
|
+
}
|
|
661
|
+
if (config.kind === 'weekly') {
|
|
662
|
+
return compileWeeklySchedule(config, dialect);
|
|
663
|
+
}
|
|
664
|
+
if (config.kind === 'monthly') {
|
|
665
|
+
return compileMonthlySchedule(config, dialect);
|
|
666
|
+
}
|
|
667
|
+
diagnostics.push(createDiagnostic('error', 'schedule.kind.unsupported', 'kind', 'Unsupported schedule kind.'));
|
|
668
|
+
return { expression: null, diagnostics };
|
|
669
|
+
}
|
|
670
|
+
function validateScheduleAuthoringConfig(config) {
|
|
671
|
+
const compiled = compileScheduleExpression(config);
|
|
672
|
+
const diagnostics = [...compiled.diagnostics];
|
|
673
|
+
if (compiled.expression && compiled.expression.dialect !== 'aws-eventbridge' && !isValidCronExpression(compiled.expression.cron)) {
|
|
674
|
+
diagnostics.push(createDiagnostic('error', 'schedule.cron.invalid', 'expression.cron', 'The compiled CRON expression is invalid.'));
|
|
675
|
+
}
|
|
676
|
+
if (!config.timezone) {
|
|
677
|
+
diagnostics.push(createDiagnostic('warning', 'schedule.timezone.defaulted', 'timezone', 'Timezone is missing and will default to UTC.'));
|
|
678
|
+
}
|
|
679
|
+
if (config.window?.startAt && config.window?.endAt && Date.parse(config.window.startAt) > Date.parse(config.window.endAt)) {
|
|
680
|
+
diagnostics.push(createDiagnostic('error', 'schedule.window.invalid', 'window', 'The schedule start date must be before the end date.'));
|
|
681
|
+
}
|
|
682
|
+
return diagnostics;
|
|
683
|
+
}
|
|
684
|
+
function createSchedulePreview(value, preview = {}) {
|
|
685
|
+
const normalized = normalizeScheduleValue(value, {
|
|
686
|
+
timezone: preview.timezone,
|
|
687
|
+
locale: preview.locale,
|
|
688
|
+
dialect: preview.dialect,
|
|
689
|
+
previewOccurrences: preview.occurrences,
|
|
690
|
+
});
|
|
691
|
+
const config = normalized.config;
|
|
692
|
+
if (!config) {
|
|
693
|
+
return {
|
|
694
|
+
config: null,
|
|
695
|
+
expression: null,
|
|
696
|
+
humanized: '',
|
|
697
|
+
occurrences: [],
|
|
698
|
+
diagnostics: normalized.diagnostics,
|
|
699
|
+
};
|
|
700
|
+
}
|
|
701
|
+
const compiled = compileScheduleExpression(config);
|
|
702
|
+
const diagnostics = mergeDiagnostics([
|
|
703
|
+
...normalized.diagnostics,
|
|
704
|
+
...validateScheduleAuthoringConfig(config),
|
|
705
|
+
]);
|
|
706
|
+
if (!compiled.expression || diagnostics.some((diagnostic) => diagnostic.severity === 'error')) {
|
|
707
|
+
return {
|
|
708
|
+
config,
|
|
709
|
+
expression: compiled.expression,
|
|
710
|
+
humanized: '',
|
|
711
|
+
occurrences: [],
|
|
712
|
+
diagnostics,
|
|
713
|
+
};
|
|
714
|
+
}
|
|
715
|
+
const from = preview.from ?? config.preview?.from ?? config.window?.startAt;
|
|
716
|
+
const count = preview.occurrences ?? config.preview?.occurrences ?? 5;
|
|
717
|
+
const currentDate = from ? new Date(from) : new Date();
|
|
718
|
+
const timezone = config.timezone || 'UTC';
|
|
719
|
+
const occurrences = getNextCronOccurrences(compiled.expression.cron, count, {
|
|
720
|
+
currentDate,
|
|
721
|
+
timeZone: timezone,
|
|
722
|
+
}).map((date) => ({
|
|
723
|
+
instant: date.toISOString(),
|
|
724
|
+
localDateTime: formatLocalDateTime(date, timezone, config.locale ?? 'en-US'),
|
|
725
|
+
timezone,
|
|
726
|
+
}));
|
|
727
|
+
return {
|
|
728
|
+
config,
|
|
729
|
+
expression: compiled.expression,
|
|
730
|
+
humanized: humanizeCronExpression(compiled.expression.cron, config.locale ?? 'en-US'),
|
|
731
|
+
occurrences,
|
|
732
|
+
diagnostics,
|
|
733
|
+
};
|
|
734
|
+
}
|
|
735
|
+
function compileIntervalSchedule(config, dialect) {
|
|
736
|
+
const interval = config.recurrence?.interval;
|
|
737
|
+
const diagnostics = [];
|
|
738
|
+
if (!interval || interval.every < 1) {
|
|
739
|
+
diagnostics.push(createDiagnostic('error', 'schedule.interval.invalid', 'recurrence.interval', 'Interval schedules require a positive interval.'));
|
|
740
|
+
return { expression: null, diagnostics };
|
|
741
|
+
}
|
|
742
|
+
if (interval.unit === 'minutes') {
|
|
743
|
+
if (interval.every > 59) {
|
|
744
|
+
diagnostics.push(createDiagnostic('error', 'schedule.interval.minutes.range', 'recurrence.interval.every', 'Minute intervals must be between 1 and 59.'));
|
|
745
|
+
return { expression: null, diagnostics };
|
|
746
|
+
}
|
|
747
|
+
return makeExpression(`*/${interval.every} * * * *`, dialect, false, diagnostics);
|
|
748
|
+
}
|
|
749
|
+
if (interval.unit === 'hours') {
|
|
750
|
+
if (interval.every > 23) {
|
|
751
|
+
diagnostics.push(createDiagnostic('error', 'schedule.interval.hours.range', 'recurrence.interval.every', 'Hourly intervals must be between 1 and 23.'));
|
|
752
|
+
return { expression: null, diagnostics };
|
|
753
|
+
}
|
|
754
|
+
return makeExpression(`0 */${interval.every} * * *`, dialect, false, diagnostics);
|
|
755
|
+
}
|
|
756
|
+
diagnostics.push(createDiagnostic('warning', 'schedule.interval.notPortable', 'recurrence.interval.unit', 'Day and week intervals are not portable CRON schedules without an anchor date.'));
|
|
757
|
+
return { expression: null, diagnostics };
|
|
758
|
+
}
|
|
759
|
+
function compileDailySchedule(config, dialect) {
|
|
760
|
+
const daily = config.recurrence?.daily;
|
|
761
|
+
const diagnostics = validateTimes(daily?.times, 'recurrence.daily.times');
|
|
762
|
+
if (!daily || diagnostics.length > 0) {
|
|
763
|
+
return { expression: null, diagnostics };
|
|
764
|
+
}
|
|
765
|
+
const time = parseTime(daily.times[0]);
|
|
766
|
+
const dayOfWeek = daily.onlyWeekdays ? '1-5' : '*';
|
|
767
|
+
return makeExpression(`${time.minute} ${time.hour} * * ${dayOfWeek}`, dialect, false, diagnostics);
|
|
768
|
+
}
|
|
769
|
+
function compileWeeklySchedule(config, dialect) {
|
|
770
|
+
const weekly = config.recurrence?.weekly;
|
|
771
|
+
const diagnostics = validateTimes(weekly?.times, 'recurrence.weekly.times');
|
|
772
|
+
if (!weekly || diagnostics.length > 0) {
|
|
773
|
+
return { expression: null, diagnostics };
|
|
774
|
+
}
|
|
775
|
+
if (!weekly.daysOfWeek?.length) {
|
|
776
|
+
diagnostics.push(createDiagnostic('error', 'schedule.weekly.days.required', 'recurrence.weekly.daysOfWeek', 'Weekly schedules require at least one weekday.'));
|
|
777
|
+
return { expression: null, diagnostics };
|
|
778
|
+
}
|
|
779
|
+
const time = parseTime(weekly.times[0]);
|
|
780
|
+
const days = normalizeWeekdays(weekly.daysOfWeek).join(',');
|
|
781
|
+
return makeExpression(`${time.minute} ${time.hour} * * ${days}`, dialect, false, diagnostics);
|
|
782
|
+
}
|
|
783
|
+
function compileMonthlySchedule(config, dialect) {
|
|
784
|
+
const monthly = config.recurrence?.monthly;
|
|
785
|
+
const diagnostics = validateTimes(monthly?.times, 'recurrence.monthly.times');
|
|
786
|
+
if (!monthly || diagnostics.length > 0) {
|
|
787
|
+
return { expression: null, diagnostics };
|
|
788
|
+
}
|
|
789
|
+
const time = parseTime(monthly.times[0]);
|
|
790
|
+
if (monthly.mode === 'dayOfMonth') {
|
|
791
|
+
if (!monthly.dayOfMonth || monthly.dayOfMonth < 1 || monthly.dayOfMonth > 31) {
|
|
792
|
+
diagnostics.push(createDiagnostic('error', 'schedule.monthly.day.range', 'recurrence.monthly.dayOfMonth', 'Monthly day must be between 1 and 31.'));
|
|
793
|
+
return { expression: null, diagnostics };
|
|
794
|
+
}
|
|
795
|
+
return makeExpression(`${time.minute} ${time.hour} ${monthly.dayOfMonth} * *`, dialect, false, diagnostics);
|
|
796
|
+
}
|
|
797
|
+
if (monthly.mode === 'nthWeekday') {
|
|
798
|
+
if (!monthly.nth || monthly.weekday == null) {
|
|
799
|
+
diagnostics.push(createDiagnostic('error', 'schedule.monthly.nth.required', 'recurrence.monthly', 'Nth weekday schedules require nth and weekday.'));
|
|
800
|
+
return { expression: null, diagnostics };
|
|
801
|
+
}
|
|
802
|
+
return makeExpression(`${time.minute} ${time.hour} ? * ${monthly.weekday}#${monthly.nth}`, dialect, false, diagnostics);
|
|
803
|
+
}
|
|
804
|
+
diagnostics.push(createDiagnostic('warning', 'schedule.monthly.mode.notPortable', 'recurrence.monthly.mode', 'This monthly mode needs a dialect-specific compiler before it can produce CRON.'));
|
|
805
|
+
return { expression: null, diagnostics };
|
|
806
|
+
}
|
|
807
|
+
function makeExpression(cron, dialect, seconds, diagnostics) {
|
|
808
|
+
if (dialect === 'quartz') {
|
|
809
|
+
return {
|
|
810
|
+
expression: {
|
|
811
|
+
cron: seconds ? cron : `0 ${cron}`,
|
|
812
|
+
dialect,
|
|
813
|
+
seconds: true,
|
|
814
|
+
},
|
|
815
|
+
diagnostics,
|
|
816
|
+
};
|
|
817
|
+
}
|
|
818
|
+
return {
|
|
819
|
+
expression: {
|
|
820
|
+
cron,
|
|
821
|
+
dialect,
|
|
822
|
+
seconds,
|
|
823
|
+
},
|
|
824
|
+
diagnostics,
|
|
825
|
+
};
|
|
826
|
+
}
|
|
827
|
+
function validateTimes(times, field) {
|
|
828
|
+
if (!times?.length) {
|
|
829
|
+
return [createDiagnostic('error', 'schedule.time.required', field, 'At least one time is required.')];
|
|
830
|
+
}
|
|
831
|
+
if (times.length > 1) {
|
|
832
|
+
return [createDiagnostic('warning', 'schedule.time.multiple.unsupported', field, 'Only the first time is compiled in this initial scheduler runtime.')];
|
|
833
|
+
}
|
|
834
|
+
const time = parseTime(times[0]);
|
|
835
|
+
if (time.hour < 0 || time.hour > 23 || time.minute < 0 || time.minute > 59) {
|
|
836
|
+
return [createDiagnostic('error', 'schedule.time.invalid', field, 'Time must use HH:mm with a 24-hour clock.')];
|
|
837
|
+
}
|
|
838
|
+
return [];
|
|
839
|
+
}
|
|
840
|
+
function parseTime(value) {
|
|
841
|
+
if (!/^\d{2}:\d{2}$/.test(value)) {
|
|
842
|
+
return { hour: Number.NaN, minute: Number.NaN };
|
|
843
|
+
}
|
|
844
|
+
const [hour, minute] = value.split(':').map((part) => Number(part));
|
|
845
|
+
return { hour, minute };
|
|
846
|
+
}
|
|
847
|
+
function normalizeWeekdays(days) {
|
|
848
|
+
return Array.from(new Set(days)).sort((a, b) => a - b);
|
|
849
|
+
}
|
|
850
|
+
function formatLocalDateTime(date, timezone, locale) {
|
|
851
|
+
return new Intl.DateTimeFormat(locale, {
|
|
852
|
+
timeZone: timezone,
|
|
853
|
+
dateStyle: 'full',
|
|
854
|
+
timeStyle: 'long',
|
|
855
|
+
}).format(date);
|
|
856
|
+
}
|
|
857
|
+
function mergeDiagnostics(diagnostics) {
|
|
858
|
+
const seen = new Set();
|
|
859
|
+
return diagnostics.filter((diagnostic) => {
|
|
860
|
+
const key = `${diagnostic.severity}:${diagnostic.code}:${diagnostic.field ?? ''}:${diagnostic.message}`;
|
|
861
|
+
if (seen.has(key)) {
|
|
862
|
+
return false;
|
|
863
|
+
}
|
|
864
|
+
seen.add(key);
|
|
865
|
+
return true;
|
|
866
|
+
});
|
|
867
|
+
}
|
|
868
|
+
function createDiagnostic(severity, code, field, message) {
|
|
869
|
+
return {
|
|
870
|
+
severity,
|
|
871
|
+
code,
|
|
872
|
+
field,
|
|
873
|
+
message,
|
|
874
|
+
};
|
|
875
|
+
}
|
|
876
|
+
|
|
432
877
|
class PdxCronBuilderComponent {
|
|
433
878
|
// Tipos auxiliares para Typed Forms
|
|
434
879
|
fb = inject(NonNullableFormBuilder);
|
|
@@ -462,6 +907,9 @@ class PdxCronBuilderComponent {
|
|
|
462
907
|
destroy$ = new Subject();
|
|
463
908
|
humanized = '';
|
|
464
909
|
preview = [];
|
|
910
|
+
structuredPreview = [];
|
|
911
|
+
scheduleConfig = null;
|
|
912
|
+
scheduleDiagnostics = [];
|
|
465
913
|
error = null;
|
|
466
914
|
// Opções expostas ao template para controle de fluxo moderno
|
|
467
915
|
weekdayLabels = ['Dom', 'Seg', 'Ter', 'Qua', 'Qui', 'Sex', 'Sáb'];
|
|
@@ -522,7 +970,7 @@ class PdxCronBuilderComponent {
|
|
|
522
970
|
.subscribe((tz) => {
|
|
523
971
|
this.metadata.timezone = tz || 'UTC';
|
|
524
972
|
if (typeof this.value === 'string') {
|
|
525
|
-
this.
|
|
973
|
+
this.refreshScheduleState(this.value);
|
|
526
974
|
}
|
|
527
975
|
});
|
|
528
976
|
}
|
|
@@ -569,9 +1017,7 @@ class PdxCronBuilderComponent {
|
|
|
569
1017
|
if (emitEvent) {
|
|
570
1018
|
this.onChange(cron);
|
|
571
1019
|
}
|
|
572
|
-
this.
|
|
573
|
-
this.humanize(cron);
|
|
574
|
-
this.generatePreview(cron);
|
|
1020
|
+
this.refreshScheduleState(cron);
|
|
575
1021
|
}
|
|
576
1022
|
copyCron() {
|
|
577
1023
|
if (typeof this.value === 'string') {
|
|
@@ -595,27 +1041,22 @@ class PdxCronBuilderComponent {
|
|
|
595
1041
|
this.syncSimpleForm(cron);
|
|
596
1042
|
}
|
|
597
1043
|
}
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
: this.metadata.
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
if (this.error) {
|
|
605
|
-
this.humanized = '';
|
|
606
|
-
return;
|
|
607
|
-
}
|
|
608
|
-
this.humanized = humanizeCronExpression(cron, this.metadata.locale || 'en-US');
|
|
609
|
-
}
|
|
610
|
-
generatePreview(cron) {
|
|
611
|
-
if (this.error || !this.metadata.previewOccurrences) {
|
|
612
|
-
this.preview = [];
|
|
613
|
-
return;
|
|
614
|
-
}
|
|
615
|
-
this.preview = getNextCronOccurrences(cron, this.metadata.previewOccurrences ?? 5, {
|
|
616
|
-
currentDate: this.metadata.previewFrom ?? new Date(),
|
|
617
|
-
timeZone: this.metadata.timezone,
|
|
1044
|
+
refreshScheduleState(cron) {
|
|
1045
|
+
const preview = createSchedulePreview(cron, {
|
|
1046
|
+
occurrences: this.metadata.previewOccurrences ?? 5,
|
|
1047
|
+
from: this.metadata.previewFrom?.toISOString(),
|
|
1048
|
+
timezone: this.metadata.timezone || this.timezoneControl.value || 'UTC',
|
|
1049
|
+
locale: this.metadata.locale || 'en-US',
|
|
618
1050
|
});
|
|
1051
|
+
this.scheduleConfig = preview.config;
|
|
1052
|
+
this.scheduleDiagnostics = preview.diagnostics;
|
|
1053
|
+
this.structuredPreview = preview.occurrences;
|
|
1054
|
+
this.humanized = preview.humanized;
|
|
1055
|
+
this.preview = preview.occurrences.map((occurrence) => new Date(occurrence.instant));
|
|
1056
|
+
const error = preview.diagnostics.find((diagnostic) => diagnostic.severity === 'error');
|
|
1057
|
+
this.error = error
|
|
1058
|
+
? this.metadata.validators?.invalidCronMessage || error.message || 'Invalid CRON expression'
|
|
1059
|
+
: null;
|
|
619
1060
|
}
|
|
620
1061
|
parseCronString(cron) {
|
|
621
1062
|
const parts = cron.trim().split(/\s+/);
|
|
@@ -747,7 +1188,7 @@ class PdxCronBuilderComponent {
|
|
|
747
1188
|
useExisting: forwardRef(() => PdxCronBuilderComponent),
|
|
748
1189
|
multi: true,
|
|
749
1190
|
},
|
|
750
|
-
], 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 formControlName=\"everyN\"\n min=\"1\"\n max=\"60\"\n step=\"1\"\n thumbLabel\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: "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],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 });
|
|
751
1192
|
}
|
|
752
1193
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PdxCronBuilderComponent, decorators: [{
|
|
753
1194
|
type: Component,
|
|
@@ -773,7 +1214,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
773
1214
|
useExisting: forwardRef(() => PdxCronBuilderComponent),
|
|
774
1215
|
multi: true,
|
|
775
1216
|
},
|
|
776
|
-
], 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
|
|
1217
|
+
], 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"] }]
|
|
777
1218
|
}], ctorParameters: () => [], propDecorators: { metadata: [{
|
|
778
1219
|
type: Input
|
|
779
1220
|
}] } });
|
|
@@ -806,6 +1247,12 @@ const PDX_CRON_BUILDER_DOC_META_LEGACY = {
|
|
|
806
1247
|
*/
|
|
807
1248
|
const ENUMS = {
|
|
808
1249
|
mode: ['simple', 'advanced', 'both'],
|
|
1250
|
+
scheduleKind: ['once', 'interval', 'daily', 'weekly', 'monthly', 'customCron'],
|
|
1251
|
+
cronDialect: ['unix', 'quartz', 'aws-eventbridge', 'kubernetes', 'github-actions', 'gcp-scheduler'],
|
|
1252
|
+
intervalUnit: ['minutes', 'hours', 'days', 'weeks'],
|
|
1253
|
+
monthlyMode: ['dayOfMonth', 'nthWeekday', 'lastDay', 'lastWeekday'],
|
|
1254
|
+
misfirePolicy: ['skip', 'run-late', 'run-once'],
|
|
1255
|
+
concurrencyPolicy: ['allow', 'forbid', 'replace'],
|
|
809
1256
|
validationTrigger: ['change', 'blur'],
|
|
810
1257
|
errorPosition: ['tooltip', 'inline'],
|
|
811
1258
|
};
|
|
@@ -816,6 +1263,8 @@ const CRON_BUILDER_AI_CAPABILITIES = {
|
|
|
816
1263
|
notes: [
|
|
817
1264
|
'presets[] should be merged by cron or label to avoid replacing all entries.',
|
|
818
1265
|
'metadata.previewFrom expects an ISO date string when provided.',
|
|
1266
|
+
'schedule is the canonical scheduler authoring state; value remains the current CRON string compatibility surface.',
|
|
1267
|
+
'Prefer schedule.kind and schedule.recurrence for business intent; use schedule.expression.cron only for custom CRON edits.',
|
|
819
1268
|
],
|
|
820
1269
|
capabilities: [
|
|
821
1270
|
{ path: 'label', category: 'meta', valueKind: 'string', description: 'Field label (if exposed by host).' },
|
|
@@ -848,6 +1297,42 @@ const CRON_BUILDER_AI_CAPABILITIES = {
|
|
|
848
1297
|
{ path: 'metadata.errorPosition', category: 'ui', valueKind: 'enum', allowedValues: ENUMS.errorPosition, description: 'Error display position.' },
|
|
849
1298
|
{ path: 'metadata.showInlineErrors', category: 'ui', valueKind: 'boolean', description: 'Show inline errors.' },
|
|
850
1299
|
{ path: 'metadata.hint', category: 'ui', valueKind: 'string', description: 'Hint text.' },
|
|
1300
|
+
{ path: 'schedule', category: 'schedule', valueKind: 'object', description: 'Canonical scheduler authoring config.' },
|
|
1301
|
+
{ path: 'schedule.kind', category: 'schedule', valueKind: 'enum', allowedValues: ENUMS.scheduleKind, description: 'Business schedule intent.' },
|
|
1302
|
+
{ path: 'schedule.timezone', category: 'i18n', valueKind: 'string', description: 'IANA timezone used by the schedule.' },
|
|
1303
|
+
{ path: 'schedule.locale', category: 'i18n', valueKind: 'string', description: 'Locale used by humanized text and preview.' },
|
|
1304
|
+
{ path: 'schedule.expression', category: 'dialects', valueKind: 'object', description: 'Technical CRON representation.' },
|
|
1305
|
+
{ path: 'schedule.expression.cron', category: 'dialects', valueKind: 'string', description: 'CRON expression for custom or compiled schedules.' },
|
|
1306
|
+
{ path: 'schedule.expression.dialect', category: 'dialects', valueKind: 'enum', allowedValues: ENUMS.cronDialect, description: 'CRON dialect family.' },
|
|
1307
|
+
{ path: 'schedule.expression.seconds', category: 'dialects', valueKind: 'boolean', description: 'Whether the CRON representation includes seconds.' },
|
|
1308
|
+
{ path: 'schedule.recurrence', category: 'recurrence', valueKind: 'object', description: 'Structured recurrence intent.' },
|
|
1309
|
+
{ path: 'schedule.recurrence.once.runAt', category: 'recurrence', valueKind: 'string', description: 'ISO date-time for one-time schedules.' },
|
|
1310
|
+
{ path: 'schedule.recurrence.interval.every', category: 'recurrence', valueKind: 'number', description: 'Interval amount.' },
|
|
1311
|
+
{ path: 'schedule.recurrence.interval.unit', category: 'recurrence', valueKind: 'enum', allowedValues: ENUMS.intervalUnit, description: 'Interval unit.' },
|
|
1312
|
+
{ path: 'schedule.recurrence.daily.times', category: 'recurrence', valueKind: 'array', description: 'Daily run times in HH:mm format.' },
|
|
1313
|
+
{ path: 'schedule.recurrence.daily.onlyWeekdays', category: 'recurrence', valueKind: 'boolean', description: 'Limit daily schedule to Monday through Friday.' },
|
|
1314
|
+
{ path: 'schedule.recurrence.weekly.daysOfWeek', category: 'recurrence', valueKind: 'array', description: 'Weekdays as numbers where 0 is Sunday.' },
|
|
1315
|
+
{ path: 'schedule.recurrence.weekly.times', category: 'recurrence', valueKind: 'array', description: 'Weekly run times in HH:mm format.' },
|
|
1316
|
+
{ path: 'schedule.recurrence.monthly.mode', category: 'recurrence', valueKind: 'enum', allowedValues: ENUMS.monthlyMode, description: 'Monthly recurrence strategy.' },
|
|
1317
|
+
{ path: 'schedule.recurrence.monthly.dayOfMonth', category: 'recurrence', valueKind: 'number', description: 'Day of month for monthly schedules.' },
|
|
1318
|
+
{ path: 'schedule.recurrence.monthly.nth', category: 'recurrence', valueKind: 'number', description: 'Nth weekday occurrence in the month.' },
|
|
1319
|
+
{ path: 'schedule.recurrence.monthly.weekday', category: 'recurrence', valueKind: 'number', description: 'Weekday for nth weekday monthly schedules.' },
|
|
1320
|
+
{ path: 'schedule.recurrence.monthly.times', category: 'recurrence', valueKind: 'array', description: 'Monthly run times in HH:mm format.' },
|
|
1321
|
+
{ path: 'schedule.window.startAt', category: 'policy', valueKind: 'string', description: 'ISO start date-time for schedule validity.' },
|
|
1322
|
+
{ path: 'schedule.window.endAt', category: 'policy', valueKind: 'string', description: 'ISO end date-time for schedule validity.' },
|
|
1323
|
+
{ path: 'schedule.executionPolicy.enabled', category: 'policy', valueKind: 'boolean', description: 'Whether the schedule is active.' },
|
|
1324
|
+
{ path: 'schedule.executionPolicy.misfirePolicy', category: 'policy', valueKind: 'enum', allowedValues: ENUMS.misfirePolicy, description: 'Policy for missed scheduled runs.' },
|
|
1325
|
+
{ path: 'schedule.executionPolicy.concurrencyPolicy', category: 'policy', valueKind: 'enum', allowedValues: ENUMS.concurrencyPolicy, description: 'Policy for overlapping runs.' },
|
|
1326
|
+
{ path: 'schedule.executionPolicy.flexibleWindowMinutes', category: 'policy', valueKind: 'number', description: 'Flexible delivery window in minutes.' },
|
|
1327
|
+
{ path: 'schedule.executionPolicy.jitterMinutes', category: 'policy', valueKind: 'number', description: 'Randomized delay window in minutes.' },
|
|
1328
|
+
{ path: 'schedule.preview.occurrences', category: 'preview', valueKind: 'number', description: 'Number of occurrences to preview.' },
|
|
1329
|
+
{ path: 'schedule.preview.from', category: 'preview', valueKind: 'string', description: 'ISO date-time used as preview start.' },
|
|
1330
|
+
{ path: 'schedule.governance.name', category: 'governance', valueKind: 'string', description: 'Schedule name.' },
|
|
1331
|
+
{ path: 'schedule.governance.description', category: 'governance', valueKind: 'string', description: 'Schedule description.' },
|
|
1332
|
+
{ path: 'schedule.governance.owner', category: 'governance', valueKind: 'string', description: 'Schedule owner.' },
|
|
1333
|
+
{ path: 'schedule.governance.tags', category: 'governance', valueKind: 'array', description: 'Governance tags.' },
|
|
1334
|
+
{ path: 'diagnostics', category: 'validation', valueKind: 'array', description: 'Read-only structured schedule diagnostics.' },
|
|
1335
|
+
{ path: 'preview', category: 'preview', valueKind: 'array', description: 'Read-only structured preview occurrences.' },
|
|
851
1336
|
],
|
|
852
1337
|
};
|
|
853
1338
|
|
|
@@ -859,4 +1344,4 @@ const CRON_BUILDER_AI_CAPABILITIES = {
|
|
|
859
1344
|
* Generated bundle index. Do not edit.
|
|
860
1345
|
*/
|
|
861
1346
|
|
|
862
|
-
export { CRON_BUILDER_AI_CAPABILITIES, PDX_CRON_BUILDER_DOC_META_LEGACY, PdxCronBuilderComponent };
|
|
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 };
|
package/index.d.ts
CHANGED
|
@@ -4,6 +4,149 @@ import { ControlValueAccessor, FormGroup, FormControl } from '@angular/forms';
|
|
|
4
4
|
import { MatTabChangeEvent } from '@angular/material/tabs';
|
|
5
5
|
import { AiCapabilityCategory, AiValueKind, AiCapability, AiCapabilityCatalog } from '@praxisui/core';
|
|
6
6
|
|
|
7
|
+
declare const SCHEDULE_AUTHORING_CONFIG_VERSION: "v1";
|
|
8
|
+
type ScheduleAuthoringConfigVersion = typeof SCHEDULE_AUTHORING_CONFIG_VERSION;
|
|
9
|
+
type CronDialect = 'unix' | 'quartz' | 'aws-eventbridge' | 'kubernetes' | 'github-actions' | 'gcp-scheduler';
|
|
10
|
+
type ScheduleKind = 'once' | 'interval' | 'daily' | 'weekly' | 'monthly' | 'customCron';
|
|
11
|
+
type ScheduleTimeUnit = 'minutes' | 'hours' | 'days' | 'weeks';
|
|
12
|
+
type ScheduleWeekday = 0 | 1 | 2 | 3 | 4 | 5 | 6;
|
|
13
|
+
type ScheduleSeverity = 'error' | 'warning' | 'info';
|
|
14
|
+
interface CronDialectDefinition {
|
|
15
|
+
dialect: CronDialect;
|
|
16
|
+
label: string;
|
|
17
|
+
fieldCount: {
|
|
18
|
+
min: number;
|
|
19
|
+
max: number;
|
|
20
|
+
};
|
|
21
|
+
supportsSeconds: boolean;
|
|
22
|
+
supportsYear: boolean;
|
|
23
|
+
supportsNamedMonths: boolean;
|
|
24
|
+
supportsNamedWeekdays: boolean;
|
|
25
|
+
supportsQuestionMark: boolean;
|
|
26
|
+
supportsLast: boolean;
|
|
27
|
+
supportsNearestWeekday: boolean;
|
|
28
|
+
supportsNthWeekday: boolean;
|
|
29
|
+
timezoneMode: 'external-field' | 'host-local' | 'fixed-utc' | 'expression-prefix-unsupported';
|
|
30
|
+
dayOfMonthDayOfWeekRule: 'either-or-question-mark' | 'unix-or' | 'host-specific';
|
|
31
|
+
examples: readonly string[];
|
|
32
|
+
}
|
|
33
|
+
interface ScheduleDiagnostic {
|
|
34
|
+
severity: ScheduleSeverity;
|
|
35
|
+
code: string;
|
|
36
|
+
field?: string;
|
|
37
|
+
message: string;
|
|
38
|
+
suggestion?: string;
|
|
39
|
+
}
|
|
40
|
+
interface ScheduleCronExpression {
|
|
41
|
+
cron: string;
|
|
42
|
+
dialect: CronDialect;
|
|
43
|
+
seconds?: boolean;
|
|
44
|
+
}
|
|
45
|
+
interface OnceSchedule {
|
|
46
|
+
runAt: string;
|
|
47
|
+
}
|
|
48
|
+
interface IntervalSchedule {
|
|
49
|
+
every: number;
|
|
50
|
+
unit: ScheduleTimeUnit;
|
|
51
|
+
}
|
|
52
|
+
interface DailySchedule {
|
|
53
|
+
times: string[];
|
|
54
|
+
onlyWeekdays?: boolean;
|
|
55
|
+
}
|
|
56
|
+
interface WeeklySchedule {
|
|
57
|
+
daysOfWeek: ScheduleWeekday[];
|
|
58
|
+
times: string[];
|
|
59
|
+
}
|
|
60
|
+
interface MonthlySchedule {
|
|
61
|
+
mode: 'dayOfMonth' | 'nthWeekday' | 'lastDay' | 'lastWeekday';
|
|
62
|
+
dayOfMonth?: number;
|
|
63
|
+
nth?: 1 | 2 | 3 | 4 | 5;
|
|
64
|
+
weekday?: ScheduleWeekday;
|
|
65
|
+
times: string[];
|
|
66
|
+
}
|
|
67
|
+
interface ScheduleRecurrence {
|
|
68
|
+
once?: OnceSchedule;
|
|
69
|
+
interval?: IntervalSchedule;
|
|
70
|
+
daily?: DailySchedule;
|
|
71
|
+
weekly?: WeeklySchedule;
|
|
72
|
+
monthly?: MonthlySchedule;
|
|
73
|
+
}
|
|
74
|
+
interface ScheduleWindow {
|
|
75
|
+
startAt?: string;
|
|
76
|
+
endAt?: string;
|
|
77
|
+
}
|
|
78
|
+
interface ScheduleExecutionPolicy {
|
|
79
|
+
enabled?: boolean;
|
|
80
|
+
misfirePolicy?: 'skip' | 'run-late' | 'run-once';
|
|
81
|
+
concurrencyPolicy?: 'allow' | 'forbid' | 'replace';
|
|
82
|
+
flexibleWindowMinutes?: number;
|
|
83
|
+
jitterMinutes?: number;
|
|
84
|
+
}
|
|
85
|
+
interface SchedulePreviewConfig {
|
|
86
|
+
occurrences?: number;
|
|
87
|
+
from?: string;
|
|
88
|
+
}
|
|
89
|
+
interface ScheduleGovernance {
|
|
90
|
+
name?: string;
|
|
91
|
+
description?: string;
|
|
92
|
+
owner?: string;
|
|
93
|
+
tags?: string[];
|
|
94
|
+
}
|
|
95
|
+
interface ScheduleAuthoringConfig {
|
|
96
|
+
version: ScheduleAuthoringConfigVersion;
|
|
97
|
+
kind: ScheduleKind;
|
|
98
|
+
timezone: string;
|
|
99
|
+
locale?: string;
|
|
100
|
+
expression?: ScheduleCronExpression;
|
|
101
|
+
recurrence?: ScheduleRecurrence;
|
|
102
|
+
window?: ScheduleWindow;
|
|
103
|
+
executionPolicy?: ScheduleExecutionPolicy;
|
|
104
|
+
preview?: SchedulePreviewConfig;
|
|
105
|
+
governance?: ScheduleGovernance;
|
|
106
|
+
}
|
|
107
|
+
declare const CRON_DIALECTS: Record<CronDialect, CronDialectDefinition>;
|
|
108
|
+
declare function getCronDialectDefinition(dialect: CronDialect): CronDialectDefinition;
|
|
109
|
+
|
|
110
|
+
interface ScheduleNormalizeOptions {
|
|
111
|
+
timezone?: string;
|
|
112
|
+
locale?: string;
|
|
113
|
+
dialect?: CronDialect;
|
|
114
|
+
previewOccurrences?: number;
|
|
115
|
+
}
|
|
116
|
+
interface ScheduleNormalizeResult {
|
|
117
|
+
config: ScheduleAuthoringConfig | null;
|
|
118
|
+
diagnostics: ScheduleDiagnostic[];
|
|
119
|
+
}
|
|
120
|
+
type ScheduleInput = string | ScheduleAuthoringConfig | null | undefined;
|
|
121
|
+
declare function normalizeScheduleValue(value: ScheduleInput, options?: ScheduleNormalizeOptions): ScheduleNormalizeResult;
|
|
122
|
+
declare function inferCronDialect(expression: string): CronDialect;
|
|
123
|
+
|
|
124
|
+
interface CompiledScheduleExpression {
|
|
125
|
+
cron: string;
|
|
126
|
+
dialect: CronDialect;
|
|
127
|
+
seconds: boolean;
|
|
128
|
+
}
|
|
129
|
+
interface SchedulePreviewOccurrence {
|
|
130
|
+
instant: string;
|
|
131
|
+
localDateTime: string;
|
|
132
|
+
timezone: string;
|
|
133
|
+
}
|
|
134
|
+
interface SchedulePreviewResult {
|
|
135
|
+
config: ScheduleAuthoringConfig | null;
|
|
136
|
+
expression: CompiledScheduleExpression | null;
|
|
137
|
+
humanized: string;
|
|
138
|
+
occurrences: SchedulePreviewOccurrence[];
|
|
139
|
+
diagnostics: ScheduleDiagnostic[];
|
|
140
|
+
}
|
|
141
|
+
interface CreateSchedulePreviewOptions extends SchedulePreviewConfig, Pick<ScheduleNormalizeOptions, 'timezone' | 'locale' | 'dialect'> {
|
|
142
|
+
}
|
|
143
|
+
declare function compileScheduleExpression(config: ScheduleAuthoringConfig): {
|
|
144
|
+
expression: CompiledScheduleExpression | null;
|
|
145
|
+
diagnostics: ScheduleDiagnostic[];
|
|
146
|
+
};
|
|
147
|
+
declare function validateScheduleAuthoringConfig(config: ScheduleAuthoringConfig): ScheduleDiagnostic[];
|
|
148
|
+
declare function createSchedulePreview(value: string | ScheduleAuthoringConfig | null | undefined, preview?: CreateSchedulePreviewOptions): SchedulePreviewResult;
|
|
149
|
+
|
|
7
150
|
interface Recurrence {
|
|
8
151
|
}
|
|
9
152
|
interface CronBuilderMetadata {
|
|
@@ -70,6 +213,9 @@ declare class PdxCronBuilderComponent implements ControlValueAccessor, OnInit, O
|
|
|
70
213
|
private readonly destroy$;
|
|
71
214
|
humanized: string;
|
|
72
215
|
preview: Date[];
|
|
216
|
+
structuredPreview: SchedulePreviewOccurrence[];
|
|
217
|
+
scheduleConfig: ScheduleAuthoringConfig | null;
|
|
218
|
+
scheduleDiagnostics: ScheduleDiagnostic[];
|
|
73
219
|
error: string | null;
|
|
74
220
|
readonly weekdayLabels: readonly ["Dom", "Seg", "Ter", "Qua", "Qui", "Sex", "Sáb"];
|
|
75
221
|
readonly weeklyDayOptions: readonly [0, 1, 2, 3, 4, 5, 6];
|
|
@@ -91,9 +237,7 @@ declare class PdxCronBuilderComponent implements ControlValueAccessor, OnInit, O
|
|
|
91
237
|
copyCron(): void;
|
|
92
238
|
copyHumanized(): void;
|
|
93
239
|
importCron(): void;
|
|
94
|
-
private
|
|
95
|
-
private humanize;
|
|
96
|
-
private generatePreview;
|
|
240
|
+
private refreshScheduleState;
|
|
97
241
|
private parseCronString;
|
|
98
242
|
private syncSimpleForm;
|
|
99
243
|
private buildCronFromSimple;
|
|
@@ -116,9 +260,14 @@ declare const PDX_CRON_BUILDER_DOC_META_LEGACY: any;
|
|
|
116
260
|
declare module '@praxisui/core' {
|
|
117
261
|
interface AiCapabilityCategoryMap {
|
|
118
262
|
meta: true;
|
|
263
|
+
schedule: true;
|
|
264
|
+
recurrence: true;
|
|
265
|
+
dialects: true;
|
|
119
266
|
fields: true;
|
|
120
267
|
presets: true;
|
|
268
|
+
policy: true;
|
|
121
269
|
preview: true;
|
|
270
|
+
governance: true;
|
|
122
271
|
i18n: true;
|
|
123
272
|
ui: true;
|
|
124
273
|
}
|
|
@@ -133,5 +282,5 @@ interface CapabilityCatalog extends AiCapabilityCatalog {
|
|
|
133
282
|
}
|
|
134
283
|
declare const CRON_BUILDER_AI_CAPABILITIES: CapabilityCatalog;
|
|
135
284
|
|
|
136
|
-
export { CRON_BUILDER_AI_CAPABILITIES, PDX_CRON_BUILDER_DOC_META_LEGACY, PdxCronBuilderComponent };
|
|
137
|
-
export type { AdvancedCronFormValue, Capability, CapabilityCatalog, CapabilityCategory, CronBuilderMetadata, CronPresetType, Recurrence, SimpleCronFormValue, ValueKind };
|
|
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 };
|
|
286
|
+
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 };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@praxisui/cron-builder",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "8.0.0-beta.1",
|
|
4
4
|
"description": "Cron expression builder utilities and components for Praxis UI.",
|
|
5
5
|
"peerDependencies": {
|
|
6
6
|
"@angular/common": "^20.1.0",
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
"@angular/forms": "^20.1.0",
|
|
9
9
|
"@angular/cdk": "^20.1.0",
|
|
10
10
|
"@angular/material": "^20.1.0",
|
|
11
|
-
"@praxisui/core": "^
|
|
11
|
+
"@praxisui/core": "^8.0.0-beta.1"
|
|
12
12
|
},
|
|
13
13
|
"dependencies": {
|
|
14
14
|
"tslib": "^2.3.0",
|