@company-semantics/contracts 99.0.0 → 99.2.0

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.
@@ -0,0 +1,99 @@
1
+ /**
2
+ * Scheduled-execution identity.
3
+ *
4
+ * An execution is named by the slot it was INTENDED for, never by when a run
5
+ * happened to start: `execution_id = <schedule_id>/<slot ISO>`, with the slot a
6
+ * whole UTC minute written `YYYY-MM-DDTHH:MM:00Z`. A dispatch attempt is named
7
+ * `<execution_id>#<attempt>`; that string is the workflow's `run-name`, so the
8
+ * GitHub run's `display_title` is how a dispatched run is found again after an
9
+ * ambiguous dispatch.
10
+ *
11
+ * A run that carries no execution_id (a manual run, an unrelated dispatch, or a
12
+ * legacy `on.schedule` run) never satisfies a slot. Legacy runs are admitted
13
+ * only while the schedule's mode is not `live`, and still never get an id
14
+ * (migration admission; contracts ADR slug schedule-execution-wire-types).
15
+ */
16
+
17
+ /** Kebab-case, no `/` or `#`, so an execution_id and a display title parse unambiguously. */
18
+ export const SCHEDULE_ID_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
19
+ export const SCHEDULE_ID_MAX_LENGTH = 64;
20
+ export const SCHEDULE_SLOT_ISO_PATTERN = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:00Z$/;
21
+
22
+ /** The workflow_dispatch inputs every registered workflow declares once it leaves mode `off`. */
23
+ export const SCHEDULE_DISPATCH_INPUTS = [
24
+ "schedule_id",
25
+ "scheduled_at",
26
+ "execution_id",
27
+ "attempt",
28
+ ] as const;
29
+ export type ScheduleDispatchInput = (typeof SCHEDULE_DISPATCH_INPUTS)[number];
30
+
31
+ /** The exact `run-name:` a registered workflow declares, so display_title = execution_id#attempt. */
32
+ export const SCHEDULE_RUN_NAME =
33
+ "${{ inputs.execution_id && format('{0}#{1}', inputs.execution_id, inputs.attempt) || github.workflow }}";
34
+
35
+ export function isScheduleId(value: string): boolean {
36
+ return (
37
+ value.length <= SCHEDULE_ID_MAX_LENGTH && SCHEDULE_ID_PATTERN.test(value)
38
+ );
39
+ }
40
+
41
+ /** `YYYY-MM-DDTHH:MM:00Z`. Throws RangeError when `slot` is not a whole UTC minute. */
42
+ export function slotIso(slot: Date): string {
43
+ if (Number.isNaN(slot.getTime()) || slot.getTime() % 60_000 !== 0) {
44
+ throw new RangeError("a schedule slot must be a whole UTC minute");
45
+ }
46
+ return slot.toISOString().replace(".000Z", "Z");
47
+ }
48
+
49
+ export function executionIdFor(scheduleId: string, slot: Date): string {
50
+ if (!isScheduleId(scheduleId))
51
+ throw new RangeError(`invalid schedule id "${scheduleId}"`);
52
+ return `${scheduleId}/${slotIso(slot)}`;
53
+ }
54
+
55
+ export interface ParsedExecutionId {
56
+ readonly scheduleId: string;
57
+ readonly slot: Date;
58
+ }
59
+
60
+ export function parseExecutionId(
61
+ executionId: string,
62
+ ): ParsedExecutionId | null {
63
+ const slash = executionId.indexOf("/");
64
+ if (slash < 0) return null;
65
+ const scheduleId = executionId.slice(0, slash);
66
+ const iso = executionId.slice(slash + 1);
67
+ if (!isScheduleId(scheduleId) || !SCHEDULE_SLOT_ISO_PATTERN.test(iso))
68
+ return null;
69
+ const slot = new Date(iso);
70
+ if (Number.isNaN(slot.getTime()) || slotIso(slot) !== iso) return null;
71
+ return { scheduleId, slot };
72
+ }
73
+
74
+ export function displayTitleFor(executionId: string, attempt: number): string {
75
+ if (parseExecutionId(executionId) === null) {
76
+ throw new RangeError(`invalid execution id "${executionId}"`);
77
+ }
78
+ if (!Number.isInteger(attempt) || attempt < 1) {
79
+ throw new RangeError("a dispatch attempt is a positive integer");
80
+ }
81
+ return `${executionId}#${attempt}`;
82
+ }
83
+
84
+ export interface ParsedDisplayTitle extends ParsedExecutionId {
85
+ readonly executionId: string;
86
+ readonly attempt: number;
87
+ }
88
+
89
+ /** Inverse of displayTitleFor; null for any run title that is not a scheduled dispatch. */
90
+ export function parseDisplayTitle(title: string): ParsedDisplayTitle | null {
91
+ const hash = title.lastIndexOf("#");
92
+ if (hash < 0) return null;
93
+ const executionId = title.slice(0, hash);
94
+ const attemptText = title.slice(hash + 1);
95
+ if (!/^[1-9]\d*$/.test(attemptText)) return null;
96
+ const parsed = parseExecutionId(executionId);
97
+ if (parsed === null) return null;
98
+ return { ...parsed, executionId, attempt: Number(attemptText) };
99
+ }
@@ -0,0 +1,124 @@
1
+ /**
2
+ * Schedules Domain Barrel
3
+ *
4
+ * Re-exports the scheduled-execution vocabulary: schedule definitions and the
5
+ * registry snapshot, execution identity, the execution lifecycle with its
6
+ * recovery and incident vocabulary, the pure UTC cron evaluator, and the
7
+ * responses the backend's schedule routes publish.
8
+ * Import from the package root, '@company-semantics/contracts'. This barrel is
9
+ * reached only through that root re-export: there is no './schedules' subpath
10
+ * in package.json, by design (same as product-intake).
11
+ *
12
+ * Participating repos: control authors the registry and validates it with
13
+ * parseCron; the backend stores the ledger and runs the reconciler on the same
14
+ * evaluator; edge reads SCHEDULE_TICK_PATH and the ScheduleTickRequest type. The
15
+ * ci action speaks HTTP only and imports nothing here.
16
+ *
17
+ * @see ./README.md
18
+ */
19
+
20
+ export {
21
+ parseCron,
22
+ cronMatches,
23
+ previousSlot,
24
+ nextSlot,
25
+ slotsBetween,
26
+ maxIntervalMinutes,
27
+ } from "./cron";
28
+ export type {
29
+ CronField,
30
+ ParsedCron,
31
+ CronParseResult,
32
+ SlotWindow,
33
+ } from "./cron";
34
+
35
+ export {
36
+ SCHEDULE_ID_PATTERN,
37
+ SCHEDULE_ID_MAX_LENGTH,
38
+ SCHEDULE_SLOT_ISO_PATTERN,
39
+ SCHEDULE_DISPATCH_INPUTS,
40
+ SCHEDULE_RUN_NAME,
41
+ isScheduleId,
42
+ slotIso,
43
+ executionIdFor,
44
+ parseExecutionId,
45
+ displayTitleFor,
46
+ parseDisplayTitle,
47
+ } from "./identity";
48
+ export type {
49
+ ScheduleDispatchInput,
50
+ ParsedExecutionId,
51
+ ParsedDisplayTitle,
52
+ } from "./identity";
53
+
54
+ export {
55
+ SCHEDULE_EXECUTION_STATES,
56
+ SCHEDULE_RUN_CONCLUSIONS,
57
+ SCHEDULE_DISPATCH_OUTCOMES,
58
+ SCHEDULE_EXECUTION_TRANSITIONS,
59
+ canTransitionScheduleExecution,
60
+ isScheduleExecutionSettled,
61
+ SCHEDULE_EFFECTS,
62
+ mayReexecuteAutomatically,
63
+ SCHEDULE_INCIDENT_CLASSES,
64
+ scheduleIncidentKey,
65
+ SCHEDULE_INFRA_LABEL,
66
+ SCHEDULE_LOSS_ACK_LABEL,
67
+ SCHEDULE_CLOCK_SOURCES,
68
+ } from "./execution";
69
+ export type {
70
+ ScheduleExecutionState,
71
+ ScheduleRunConclusion,
72
+ ScheduleDispatchOutcome,
73
+ ScheduleExecutionSettlementInput,
74
+ ScheduleEffects,
75
+ ScheduleIncidentClass,
76
+ ScheduleClockSource,
77
+ } from "./execution";
78
+
79
+ export {
80
+ SCHEDULE_CATCH_UP_POLICIES,
81
+ SCHEDULE_IDEMPOTENCY_LEVELS,
82
+ SCHEDULE_OUTPUT_VERIFIERS,
83
+ SCHEDULE_MODES,
84
+ SCHEDULE_SEVERITIES,
85
+ SCHEDULE_TIMEZONE,
86
+ SCHEDULE_MAX_CATCH_UP_SLOTS,
87
+ SCHEDULE_MAX_TIMEOUT_MINUTES,
88
+ ScheduleDefinitionSchema,
89
+ SCHEDULE_DEFINITION_KEYS,
90
+ canonicalScheduleRegistryJson,
91
+ SCHEDULE_REGISTRY_SCHEMA_VERSION,
92
+ ScheduleRegistrySnapshotSchema,
93
+ } from "./definition";
94
+ export type {
95
+ ScheduleCatchUpPolicy,
96
+ ScheduleIdempotency,
97
+ ScheduleOutputVerifier,
98
+ ScheduleMode,
99
+ ScheduleSeverity,
100
+ ScheduleDefinition,
101
+ ScheduleRegistrySnapshot,
102
+ } from "./definition";
103
+
104
+ export {
105
+ SCHEDULE_CLAIM_REFUSALS,
106
+ ScheduleClaimResponseSchema,
107
+ SCHEDULE_FINISH_REJECTIONS,
108
+ ScheduleFinishResponseSchema,
109
+ ScheduleTickResponseSchema,
110
+ SCHEDULE_TICK_PATH,
111
+ SCHEDULE_METRICS_NAMESPACE,
112
+ SCHEDULE_METRIC_TICK,
113
+ SCHEDULE_METRIC_TICK_SOURCE_DIMENSION,
114
+ SCHEDULE_METRIC_RECONCILE_SUCCESS,
115
+ SCHEDULE_METRIC_ENVIRONMENT_DIMENSION,
116
+ } from "./api";
117
+ export type {
118
+ ScheduleClaimRefusal,
119
+ ScheduleClaimResponse,
120
+ ScheduleFinishRejection,
121
+ ScheduleFinishResponse,
122
+ ScheduleTickResponse,
123
+ ScheduleTickRequest,
124
+ } from "./api";