cronsmith 0.1.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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Fariz Prawira
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,239 @@
1
+ # cronsmith
2
+
3
+ [![npm version](https://img.shields.io/npm/v/cronsmith.svg)](https://www.npmjs.com/package/cronsmith)
4
+ [![npm downloads](https://img.shields.io/npm/dm/cronsmith.svg)](https://www.npmjs.com/package/cronsmith)
5
+ ![zero dependencies](https://img.shields.io/badge/dependencies-none-brightgreen)
6
+ ![types included](https://img.shields.io/badge/types-included-blue)
7
+ [![license MIT](https://img.shields.io/badge/license-MIT-blue.svg)](./LICENSE)
8
+
9
+ Fluent, type-safe, zero-runtime-dependency cron expression builder for TypeScript and JavaScript. Works in Node, Deno, Bun, and the browser.
10
+
11
+ - Fluent chainable API — `cron().every(5, 'minutes').toString()`
12
+ - Type-safe inputs — `'monday'` autocompletes, `'Munday'` won't compile
13
+ - Validates at the call site — `atHour(25)` throws immediately, not at 2 a.m. when your scheduler silently skips
14
+ - Immutable builder — every method returns a new instance, safe to share and reuse
15
+ - Zero runtime dependencies, ships with TypeScript types
16
+ - Emits standard 5-field cron strings — pair it with any scheduler (`node-cron`, `croner`, system crontab)
17
+ - Tiny surface area — one entry point, one error class, no hidden config
18
+
19
+ ```ts
20
+ import { cron } from 'cronsmith';
21
+
22
+ // Poll a flaky upstream every 5 minutes — one of the most common cron shapes.
23
+ cron().every(5, 'minutes').toString();
24
+ // → "*/5 * * * *"
25
+
26
+ // Nightly database backup at 02:30 — single call sets both minute and hour.
27
+ cron().at('02:30').toString();
28
+ // → "30 2 * * *"
29
+
30
+ // Send the Monday-morning digest. Weekdays use lowercase string literals.
31
+ cron().on('monday').toString();
32
+ // → "* * * * 1"
33
+
34
+ // Run on the 1st and 15th — payroll, invoicing, mid-month reports.
35
+ cron().dayOfMonth([1, 15]).toString();
36
+ // → "* * 1,15 * *"
37
+
38
+ // Quarterly cleanup job. Month names are typed literals, not magic numbers.
39
+ cron().inMonth(['january', 'april', 'july', 'october']).toString();
40
+ // → "* * * 1,4,7,10 *"
41
+
42
+ // Business-hours window — pair with a minute selector to control firing rate.
43
+ cron().betweenHours(9, 17).toString();
44
+ // → "* 9-17 * * *" (every minute, 09:00–17:59 — chain .every(15,'minutes') etc.)
45
+
46
+ // Weekend-only batch job — Saturday and Sunday, leaves other fields wild.
47
+ cron().onWeekends().toString();
48
+ // → "* * * * 0,6"
49
+
50
+ // Wrap-around weekend coverage: Friday through Sunday inclusive.
51
+ cron().betweenDays('friday', 'sunday').toString();
52
+ // → "* * * * 5,6,0"
53
+ ```
54
+
55
+ ## Install
56
+
57
+ ```sh
58
+ npm install cronsmith
59
+ # or
60
+ pnpm add cronsmith
61
+ # or
62
+ yarn add cronsmith
63
+ # or
64
+ bun add cronsmith
65
+ ```
66
+
67
+ Both ESM and CommonJS are shipped:
68
+
69
+ ```ts
70
+ import { cron } from 'cronsmith'; // ESM / TypeScript
71
+ const { cron } = require('cronsmith'); // CommonJS
72
+ ```
73
+
74
+ Deno (via npm specifier):
75
+
76
+ ```ts
77
+ import { cron } from 'npm:cronsmith';
78
+ ```
79
+
80
+ Zero runtime dependencies. DevDeps (tsup, vitest, typescript) are not installed for consumers.
81
+
82
+ ## Why
83
+
84
+ Writing `0 9 * * 1-5` by hand is error-prone and impossible to autocomplete. Cronsmith gives you typed inputs, throws on invalid values at the method call (not when your scheduler silently fails), and produces standard 5-field cron strings.
85
+
86
+ ## API
87
+
88
+ `cron()` returns an immutable `CronBuilder`. Every method returns a new builder.
89
+
90
+ ### Steps and pins
91
+
92
+ | Method | Example | Result | Reads as |
93
+ |---|---|---|---|
94
+ | `.everyMinute()` | `.everyMinute()` | `* * * * *` | Every minute, forever |
95
+ | `.every(n, 'minutes')` | `.every(15, 'minutes')` | `*/15 * * * *` | Every 15 minutes |
96
+ | `.every(n, 'hours')` | `.every(2, 'hours')` | `0 */2 * * *` | Once every 2 hours, on the hour |
97
+ | `.every(n, 'days')` | `.every(3, 'days')` | `0 0 */3 * *` | Once a day at midnight, every 3rd day of the month |
98
+ | `.every(n, 'months')` | `.every(2, 'months')` | `0 0 1 */2 *` | Once at midnight on the 1st, every 2nd month of the year |
99
+ | `.atMinute(n)` | `.atMinute(30)` | `30 * * * *` | At minute 30 of every hour |
100
+ | `.atMinute(n[])` | `.atMinute([0, 30])` | `0,30 * * * *` | At minute 0 and minute 30 of every hour |
101
+ | `.atHour(n)` | `.atHour(9)` | `0 9 * * *` | Once a day at 09:00 |
102
+ | `.atHour(n[])` | `.atHour([9, 17])` | `0 9,17 * * *` | Twice a day, at 09:00 and 17:00 |
103
+ | `.at('HH:MM')` | `.at('09:30')` | `30 9 * * *` | Once a day at 09:30 |
104
+
105
+ ### Day / month / day-of-week
106
+
107
+ | Method | Example | Result | Reads as |
108
+ |---|---|---|---|
109
+ | `.on(day)` | `.on('monday')` | `* * * * 1` | On Mondays |
110
+ | `.on(day[])` | `.on(['monday', 'friday'])` | `* * * * 1,5` | On Mondays and Fridays |
111
+ | `.onWeekdays()` | `.onWeekdays()` | `* * * * 1-5` | Monday through Friday |
112
+ | `.onWeekends()` | `.onWeekends()` | `* * * * 0,6` | Saturday and Sunday |
113
+ | `.dayOfMonth(n)` | `.dayOfMonth(1)` | `* * 1 * *` | On the 1st of every month |
114
+ | `.dayOfMonth(n[])` | `.dayOfMonth([1, 15])` | `* * 1,15 * *` | On the 1st and 15th of every month |
115
+ | `.inMonth(m)` | `.inMonth('june')` | `* * * 6 *` | In June |
116
+ | `.inMonth(m[])` | `.inMonth(['january', 'july'])` | `* * * 1,7 *` | In January and July |
117
+
118
+ ### Ranges
119
+
120
+ | Method | Example | Result | Reads as |
121
+ |---|---|---|---|
122
+ | `.betweenHours(from, to)` | `.betweenHours(9, 17)` | `* 9-17 * * *` | Hours 9 through 17 |
123
+ | `.betweenDays(from, to)` | `.betweenDays('monday', 'friday')` | `* * * * 1-5` | Monday through Friday |
124
+ | `.betweenMonths(from, to)` | `.betweenMonths('march', 'september')` | `* * * 3-9 *` | March through September |
125
+
126
+ All three `between*` methods support wrap-around when the start is greater than the end:
127
+
128
+ - `betweenHours(22, 2)` → `* 22,23,0,1,2 * * *` (overnight window)
129
+ - `betweenDays('friday', 'sunday')` → `* * * * 5,6,0` (weekend straddling the week boundary)
130
+ - `betweenMonths('november', 'february')` → `* * * 11,12,1,2 *` (winter straddling year-end)
131
+
132
+ ### Output
133
+
134
+ | Method | Result | Reads as |
135
+ |---|---|---|
136
+ | `.toString()` | 5-field cron string | Render the builder as a cron expression you can paste into a scheduler |
137
+ | `.toObject()` | `{ minute, hour, dayOfMonth, month, dayOfWeek }` | Get each field separately, useful for storing in a database or rendering in a UI |
138
+
139
+ ## Rules
140
+
141
+ **Each field can only be set once.** Re-setting the same field throws `CronsmithError` with code `CONFLICTING_CALL`. To combine values, pass an array:
142
+
143
+ ```ts
144
+ cron().on(['monday', 'friday']).toString(); // ✓ → "* * * * 1,5"
145
+ cron().on('monday').on('friday').toString(); // ✗ throws
146
+ ```
147
+
148
+ **Validation throws at the method call**, not at runtime. `cron().atHour(25)` throws immediately.
149
+
150
+ **Names are case-insensitive.** TypeScript callers can pass lowercase (`'monday'`), Capitalize (`'Monday'`), or Uppercase (`'MONDAY'`) — all three are typed and autocomplete. At runtime, any case is normalized (`'mOnDaY'` works too, though you'd lose autocomplete on that one).
151
+
152
+ **Sunday is 0** in output. Wrap-around ranges that cross Saturday→Sunday emit a comma list rather than `5-7`.
153
+
154
+ **DOM and DOW combine with OR semantics.** This is standard Vixie cron behavior, not a Cronsmith choice:
155
+
156
+ ```ts
157
+ cron().dayOfMonth(1).on('monday').toString();
158
+ // → "* * 1 * 1"
159
+ // Runs on every 1st AND every Monday — not "the 1st only if Monday."
160
+ ```
161
+
162
+ **Pinning methods soft-default smaller fields.** Without this, `*` in the smaller fields would multiply firings by 60 / 1,440 / etc., which is almost never what "at 9 AM" or "every 3 days" means. Defaults are overridable in either call order.
163
+
164
+ | Method | Pins | Smaller fields soft-defaulted to |
165
+ |---|---|---|
166
+ | `.atHour(...)` | hour | minute = 0 |
167
+ | `.every(n, 'hours')` | hour | minute = 0 |
168
+ | `.every(n, 'days')` | dayOfMonth | minute = 0, hour = 0 |
169
+ | `.every(n, 'months')` | month | minute = 0, hour = 0, dayOfMonth = 1 |
170
+
171
+ ```ts
172
+ cron().atHour(9).toString(); // → "0 9 * * *" (once a day at 09:00)
173
+ cron().atMinute(30).atHour(9).toString(); // → "30 9 * * *" (override, order-independent)
174
+
175
+ cron().every(2, 'hours').toString(); // → "0 */2 * * *" (12 firings a day)
176
+ cron().every(3, 'days').toString(); // → "0 0 */3 * *" (~10 firings a month, at midnight)
177
+ cron().every(3, 'days').at('09:30').toString(); // → "30 9 */3 * *" (override time, keep day step)
178
+ cron().every(2, 'months').toString(); // → "0 0 1 */2 *" (6 firings a year, on the 1st at midnight)
179
+ ```
180
+
181
+ **Range and selector methods do not soft-default time.** `.betweenHours()`, `.betweenDays()`, `.betweenMonths()`, `.on()`, `.dayOfMonth()`, and `.inMonth()` only set their own field. Minute and hour stay `*` so they compose cleanly with `.every(N, 'minutes')` or `.atMinute(...)`:
182
+
183
+ ```ts
184
+ cron().betweenHours(9, 17).toString(); // → "* 9-17 * * *" (every minute, 9–17)
185
+ cron().every(15, 'minutes').betweenHours(9, 17).toString();
186
+ // → "*/15 9-17 * * *" (the usual business-hours pattern)
187
+ ```
188
+
189
+ **`.every(n, ...)` resets at the next-larger field boundary.** `*/n` in cron is calendar-aligned, not period-true. The step restarts when the next-larger field rolls over, so the gap between firings can be uneven:
190
+
191
+ | Step | Aligns evenly when | Otherwise the gap shows up at |
192
+ |---|---|---|
193
+ | `every(n, 'minutes')` | `60 % n === 0` | the top of the next hour |
194
+ | `every(n, 'hours')` | `24 % n === 0` | midnight (e.g. `every(5,'hours')` → 4-hour gap) |
195
+ | `every(n, 'days')` | never (month lengths vary 28–31) | the 1st of each month |
196
+ | `every(n, 'months')` | `12 % n === 0` | January (e.g. `every(5,'months')` → 2-month gap) |
197
+
198
+ If you need a true fixed period, cron is the wrong tool — use a scheduler that tracks "last fired" timestamps.
199
+
200
+ ## What's not included
201
+
202
+ - ❌ Natural language parsing
203
+ - ❌ Job scheduling / execution (use `node-cron`, `croner`)
204
+ - ❌ `cron.parse()`
205
+ - ❌ Seconds, year, or Quartz `L`/`W`/`#`
206
+ - ❌ Timezone handling (that's the scheduler's job)
207
+
208
+ ## Errors
209
+
210
+ All validation errors are `CronsmithError` with a `code` field:
211
+
212
+ ```ts
213
+ import { CronsmithError } from 'cronsmith';
214
+
215
+ try {
216
+ cron().atHour(25);
217
+ } catch (e) {
218
+ if (e instanceof CronsmithError && e.code === 'INVALID_HOUR') {
219
+ // ...
220
+ }
221
+ }
222
+ ```
223
+
224
+ Codes:
225
+
226
+ - `INVALID_MINUTE` — minute value outside 0–59 (e.g. `.atMinute(60)`)
227
+ - `INVALID_HOUR` — hour value outside 0–23 (e.g. `.atHour(25)`)
228
+ - `INVALID_TIME` — malformed `HH:MM` string passed to `.at()` (e.g. `.at('9:00')` or `.at('25:00')`)
229
+ - `INVALID_DAY_OF_MONTH` — day value outside 1–31 (e.g. `.dayOfMonth(32)`)
230
+ - `INVALID_WEEKDAY` — unrecognized weekday name (e.g. `.on('funday')`)
231
+ - `INVALID_MONTH` — unrecognized month name (e.g. `.inMonth('smarch')`)
232
+ - `INVALID_STEP` — step value less than 1 or beyond the unit's max (e.g. `.every(0, 'hours')`, `.every(60, 'hours')`)
233
+ - `INVALID_RANGE` — out-of-bounds value passed to a `between*()` method (e.g. `.betweenHours(9, 24)`)
234
+ - `INVALID_UNIT` — unrecognized argument to `.every()` (e.g. `.every(5, 'seconds')`)
235
+ - `CONFLICTING_CALL` — the same field was set twice (e.g. `.atMinute(5).atMinute(10)`); see Rules for how soft defaults interact with this
236
+
237
+ ## License
238
+
239
+ MIT
package/dist/index.cjs ADDED
@@ -0,0 +1,314 @@
1
+ 'use strict';
2
+
3
+ // src/errors.ts
4
+ var CronsmithError = class extends Error {
5
+ code;
6
+ constructor(code, message) {
7
+ super(message);
8
+ this.name = "CronsmithError";
9
+ this.code = code;
10
+ }
11
+ };
12
+
13
+ // src/serializer.ts
14
+ var serialize = (f) => `${f.minute} ${f.hour} ${f.dayOfMonth} ${f.month} ${f.dayOfWeek}`;
15
+ var formatList = (values) => {
16
+ const sorted = [...new Set(values)].sort((a, b) => a - b);
17
+ return sorted.join(",");
18
+ };
19
+ var formatStep = (n) => n === 1 ? "*" : `*/${n}`;
20
+ var formatRange = (start, end) => start === end ? String(start) : `${start}-${end}`;
21
+
22
+ // src/constants.ts
23
+ var WEEKDAYS = [
24
+ "sunday",
25
+ "monday",
26
+ "tuesday",
27
+ "wednesday",
28
+ "thursday",
29
+ "friday",
30
+ "saturday"
31
+ ];
32
+ var WEEKDAY_TO_NUM = {
33
+ sunday: 0,
34
+ monday: 1,
35
+ tuesday: 2,
36
+ wednesday: 3,
37
+ thursday: 4,
38
+ friday: 5,
39
+ saturday: 6
40
+ };
41
+ var MONTHS = [
42
+ "january",
43
+ "february",
44
+ "march",
45
+ "april",
46
+ "may",
47
+ "june",
48
+ "july",
49
+ "august",
50
+ "september",
51
+ "october",
52
+ "november",
53
+ "december"
54
+ ];
55
+ var MONTH_TO_NUM = {
56
+ january: 1,
57
+ february: 2,
58
+ march: 3,
59
+ april: 4,
60
+ may: 5,
61
+ june: 6,
62
+ july: 7,
63
+ august: 8,
64
+ september: 9,
65
+ october: 10,
66
+ november: 11,
67
+ december: 12
68
+ };
69
+ var FIELD_MAX = {
70
+ minutes: 59,
71
+ hours: 23,
72
+ days: 31,
73
+ months: 12
74
+ };
75
+ var TIME_UNITS = ["minutes", "hours", "days", "months"];
76
+
77
+ // src/validators.ts
78
+ var isInt = (n) => typeof n === "number" && Number.isInteger(n) && Number.isFinite(n);
79
+ var assertMinute = (m) => {
80
+ if (!isInt(m) || m < 0 || m > 59) {
81
+ throw new CronsmithError("INVALID_MINUTE", `Invalid minute: ${m} (expected 0-59)`);
82
+ }
83
+ };
84
+ var assertHour = (h) => {
85
+ if (!isInt(h) || h < 0 || h > 23) {
86
+ throw new CronsmithError("INVALID_HOUR", `Invalid hour: ${h} (expected 0-23)`);
87
+ }
88
+ };
89
+ var assertDayOfMonth = (d) => {
90
+ if (!isInt(d) || d < 1 || d > 31) {
91
+ throw new CronsmithError("INVALID_DAY_OF_MONTH", `Invalid day of month: ${d} (expected 1-31)`);
92
+ }
93
+ };
94
+ var TIME_RE = /^([0-9]{2}):([0-9]{2})$/;
95
+ var parseTime = (s) => {
96
+ const m = TIME_RE.exec(s);
97
+ if (!m) {
98
+ throw new CronsmithError(
99
+ "INVALID_TIME",
100
+ `Invalid time: '${s}' (expected HH:MM, 00:00-23:59)`
101
+ );
102
+ }
103
+ const hour = Number(m[1]);
104
+ const minute = Number(m[2]);
105
+ if (hour < 0 || hour > 23 || minute < 0 || minute > 59) {
106
+ throw new CronsmithError(
107
+ "INVALID_TIME",
108
+ `Invalid time: '${s}' (expected HH:MM, 00:00-23:59)`
109
+ );
110
+ }
111
+ return { hour, minute };
112
+ };
113
+ var normalizeWeekday = (raw) => {
114
+ const lc = raw.toLowerCase();
115
+ if (!WEEKDAYS.includes(lc)) {
116
+ throw new CronsmithError(
117
+ "INVALID_WEEKDAY",
118
+ `Invalid weekday: '${raw}' (expected one of: ${WEEKDAYS.join(", ")})`
119
+ );
120
+ }
121
+ return lc;
122
+ };
123
+ var normalizeMonth = (raw) => {
124
+ const lc = raw.toLowerCase();
125
+ if (!MONTHS.includes(lc)) {
126
+ throw new CronsmithError(
127
+ "INVALID_MONTH",
128
+ `Invalid month: '${raw}' (expected one of: ${MONTHS.join(", ")})`
129
+ );
130
+ }
131
+ return lc;
132
+ };
133
+ var assertTimeUnit = (u) => {
134
+ if (!TIME_UNITS.includes(u)) {
135
+ throw new CronsmithError(
136
+ "INVALID_UNIT",
137
+ `Invalid time unit: '${u}' (expected one of: ${TIME_UNITS.join(", ")})`
138
+ );
139
+ }
140
+ return u;
141
+ };
142
+ var assertStep = (n, unit) => {
143
+ if (!isInt(n) || n < 1) {
144
+ throw new CronsmithError("INVALID_STEP", `Step must be >= 1, got ${n}`);
145
+ }
146
+ const max = FIELD_MAX[unit];
147
+ if (n > max) {
148
+ throw new CronsmithError(
149
+ "INVALID_STEP",
150
+ `Step ${n} exceeds max for ${unit} (${max})`
151
+ );
152
+ }
153
+ };
154
+ var assertHourRange = (start, end) => {
155
+ assertHour(start);
156
+ assertHour(end);
157
+ };
158
+ var weekdayNum = (w) => WEEKDAY_TO_NUM[w];
159
+ var monthNum = (m) => MONTH_TO_NUM[m];
160
+
161
+ // src/builder.ts
162
+ var FRESH_FIELDS = {
163
+ minute: "*",
164
+ hour: "*",
165
+ dayOfMonth: "*",
166
+ month: "*",
167
+ dayOfWeek: "*"
168
+ };
169
+ var FRESH_TOUCHED = {
170
+ minute: "untouched",
171
+ hour: "untouched",
172
+ dayOfMonth: "untouched",
173
+ month: "untouched",
174
+ dayOfWeek: "untouched"
175
+ };
176
+ var FIELD_LABEL = {
177
+ minute: "minute",
178
+ hour: "hour",
179
+ dayOfMonth: "day-of-month",
180
+ month: "month",
181
+ dayOfWeek: "day-of-week"
182
+ };
183
+ var toList = (x) => Array.isArray(x) ? x : [x];
184
+ var wrapList = (start, end, min, max) => {
185
+ const out = [];
186
+ for (let v = start; v <= max; v++) out.push(v);
187
+ for (let v = min; v <= end; v++) out.push(v);
188
+ return out;
189
+ };
190
+ var CronBuilder = class _CronBuilder {
191
+ fields;
192
+ touched;
193
+ constructor(fields = FRESH_FIELDS, touched = FRESH_TOUCHED) {
194
+ this.fields = fields;
195
+ this.touched = touched;
196
+ }
197
+ // hardKeys throw on conflict and become 'hard'; softPatch applies only to
198
+ // 'untouched' fields and becomes 'soft' (a later hard call overrides it
199
+ // silently). This is what makes pinning order-independent.
200
+ clone(patch, hardKeys, softPatch) {
201
+ for (const k of hardKeys) {
202
+ if (this.touched[k] === "hard") {
203
+ throw new CronsmithError(
204
+ "CONFLICTING_CALL",
205
+ `${FIELD_LABEL[k]} field has already been set. To combine values, pass an array (e.g. atMinute([5, 35])) instead of chaining.`
206
+ );
207
+ }
208
+ }
209
+ const nextFields = { ...this.fields, ...patch };
210
+ const nextTouched = { ...this.touched };
211
+ for (const k of hardKeys) nextTouched[k] = "hard";
212
+ if (softPatch) {
213
+ for (const k of Object.keys(softPatch)) {
214
+ if (this.touched[k] === "untouched") {
215
+ nextFields[k] = softPatch[k];
216
+ nextTouched[k] = "soft";
217
+ }
218
+ }
219
+ }
220
+ return new _CronBuilder(nextFields, nextTouched);
221
+ }
222
+ everyMinute() {
223
+ return this.clone({ minute: "*" }, ["minute"]);
224
+ }
225
+ // Soft-defaults below pin smaller fields so every(n, X) fires once per
226
+ // qualifying X instead of inheriting * from the unset fields.
227
+ every(n, unit) {
228
+ const u = assertTimeUnit(unit);
229
+ assertStep(n, u);
230
+ if (u === "minutes") return this.clone({ minute: formatStep(n) }, ["minute"]);
231
+ if (u === "hours") {
232
+ return this.clone({ hour: formatStep(n) }, ["hour"], { minute: "0" });
233
+ }
234
+ if (u === "days") {
235
+ return this.clone({ dayOfMonth: formatStep(n) }, ["dayOfMonth"], {
236
+ minute: "0",
237
+ hour: "0"
238
+ });
239
+ }
240
+ return this.clone({ month: formatStep(n) }, ["month"], {
241
+ minute: "0",
242
+ hour: "0",
243
+ dayOfMonth: "1"
244
+ });
245
+ }
246
+ atMinute(m) {
247
+ const list = toList(m);
248
+ for (const v of list) assertMinute(v);
249
+ return this.clone({ minute: formatList(list) }, ["minute"]);
250
+ }
251
+ atHour(h) {
252
+ const list = toList(h);
253
+ for (const v of list) assertHour(v);
254
+ return this.clone({ hour: formatList(list) }, ["hour"], { minute: "0" });
255
+ }
256
+ at(time) {
257
+ const { hour, minute } = parseTime(time);
258
+ return this.clone({ minute: String(minute), hour: String(hour) }, ["minute", "hour"]);
259
+ }
260
+ on(day) {
261
+ const nums = toList(day).map((d) => weekdayNum(normalizeWeekday(d)));
262
+ return this.clone({ dayOfWeek: formatList(nums) }, ["dayOfWeek"]);
263
+ }
264
+ onWeekdays() {
265
+ return this.clone({ dayOfWeek: "1-5" }, ["dayOfWeek"]);
266
+ }
267
+ onWeekends() {
268
+ return this.clone({ dayOfWeek: "0,6" }, ["dayOfWeek"]);
269
+ }
270
+ dayOfMonth(d) {
271
+ const list = toList(d);
272
+ for (const v of list) assertDayOfMonth(v);
273
+ return this.clone({ dayOfMonth: formatList(list) }, ["dayOfMonth"]);
274
+ }
275
+ inMonth(m) {
276
+ const nums = toList(m).map((x) => monthNum(normalizeMonth(x)));
277
+ return this.clone({ month: formatList(nums) }, ["month"]);
278
+ }
279
+ betweenHours(start, end) {
280
+ assertHourRange(start, end);
281
+ if (start <= end) return this.clone({ hour: formatRange(start, end) }, ["hour"]);
282
+ const hours = wrapList(start, end, 0, 23);
283
+ return this.clone({ hour: hours.join(",") }, ["hour"]);
284
+ }
285
+ betweenDays(start, end) {
286
+ const s = weekdayNum(normalizeWeekday(start));
287
+ const e = weekdayNum(normalizeWeekday(end));
288
+ if (s <= e) return this.clone({ dayOfWeek: formatRange(s, e) }, ["dayOfWeek"]);
289
+ const days = wrapList(s, e, 0, 6);
290
+ return this.clone({ dayOfWeek: days.join(",") }, ["dayOfWeek"]);
291
+ }
292
+ betweenMonths(start, end) {
293
+ const s = monthNum(normalizeMonth(start));
294
+ const e = monthNum(normalizeMonth(end));
295
+ if (s <= e) return this.clone({ month: formatRange(s, e) }, ["month"]);
296
+ const months = wrapList(s, e, 1, 12);
297
+ return this.clone({ month: months.join(",") }, ["month"]);
298
+ }
299
+ toString() {
300
+ return serialize(this.fields);
301
+ }
302
+ toObject() {
303
+ return { ...this.fields };
304
+ }
305
+ };
306
+
307
+ // src/index.ts
308
+ var cron = () => new CronBuilder();
309
+
310
+ exports.CronBuilder = CronBuilder;
311
+ exports.CronsmithError = CronsmithError;
312
+ exports.cron = cron;
313
+ //# sourceMappingURL=index.cjs.map
314
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/errors.ts","../src/serializer.ts","../src/constants.ts","../src/validators.ts","../src/builder.ts","../src/index.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["import type { CronErrorCode } from './types.js';\n\nexport class CronsmithError extends Error {\n readonly code: CronErrorCode;\n\n constructor(code: CronErrorCode, message: string) {\n super(message);\n this.name = 'CronsmithError';\n this.code = code;\n }\n}\n","import type { CronFields } from './types.js';\n\nexport const serialize = (f: CronFields): string =>\n `${f.minute} ${f.hour} ${f.dayOfMonth} ${f.month} ${f.dayOfWeek}`;\n\nexport const formatList = (values: readonly number[]): string => {\n const sorted = [...new Set(values)].sort((a, b) => a - b);\n return sorted.join(',');\n};\n\nexport const formatStep = (n: number): string => (n === 1 ? '*' : `*/${n}`);\n\nexport const formatRange = (start: number, end: number): string =>\n start === end ? String(start) : `${start}-${end}`;\n","import type { Month, TimeUnit, Weekday } from './types.js';\n\nexport const WEEKDAYS: readonly Weekday[] = [\n 'sunday',\n 'monday',\n 'tuesday',\n 'wednesday',\n 'thursday',\n 'friday',\n 'saturday',\n] as const;\n\nexport const WEEKDAY_TO_NUM: Record<Weekday, number> = {\n sunday: 0,\n monday: 1,\n tuesday: 2,\n wednesday: 3,\n thursday: 4,\n friday: 5,\n saturday: 6,\n};\n\nexport const MONTHS: readonly Month[] = [\n 'january',\n 'february',\n 'march',\n 'april',\n 'may',\n 'june',\n 'july',\n 'august',\n 'september',\n 'october',\n 'november',\n 'december',\n] as const;\n\nexport const MONTH_TO_NUM: Record<Month, number> = {\n january: 1,\n february: 2,\n march: 3,\n april: 4,\n may: 5,\n june: 6,\n july: 7,\n august: 8,\n september: 9,\n october: 10,\n november: 11,\n december: 12,\n};\n\nexport const FIELD_MAX: Record<TimeUnit, number> = {\n minutes: 59,\n hours: 23,\n days: 31,\n months: 12,\n};\n\nexport const FIELD_MIN: Record<TimeUnit, number> = {\n minutes: 0,\n hours: 0,\n days: 1,\n months: 1,\n};\n\nexport const TIME_UNITS: readonly TimeUnit[] = ['minutes', 'hours', 'days', 'months'] as const;\n","import { FIELD_MAX, MONTH_TO_NUM, MONTHS, TIME_UNITS, WEEKDAY_TO_NUM, WEEKDAYS } from './constants.js';\nimport { CronsmithError } from './errors.js';\nimport type { Month, TimeUnit, Weekday } from './types.js';\n\nconst isInt = (n: unknown): n is number =>\n typeof n === 'number' && Number.isInteger(n) && Number.isFinite(n);\n\nexport const assertMinute = (m: number): void => {\n if (!isInt(m) || m < 0 || m > 59) {\n throw new CronsmithError('INVALID_MINUTE', `Invalid minute: ${m} (expected 0-59)`);\n }\n};\n\nexport const assertHour = (h: number): void => {\n if (!isInt(h) || h < 0 || h > 23) {\n throw new CronsmithError('INVALID_HOUR', `Invalid hour: ${h} (expected 0-23)`);\n }\n};\n\nexport const assertDayOfMonth = (d: number): void => {\n if (!isInt(d) || d < 1 || d > 31) {\n throw new CronsmithError('INVALID_DAY_OF_MONTH', `Invalid day of month: ${d} (expected 1-31)`);\n }\n};\n\nconst TIME_RE = /^([0-9]{2}):([0-9]{2})$/;\n\nexport const parseTime = (s: string): { hour: number; minute: number } => {\n const m = TIME_RE.exec(s);\n if (!m) {\n throw new CronsmithError(\n 'INVALID_TIME',\n `Invalid time: '${s}' (expected HH:MM, 00:00-23:59)`,\n );\n }\n const hour = Number(m[1]);\n const minute = Number(m[2]);\n if (hour < 0 || hour > 23 || minute < 0 || minute > 59) {\n throw new CronsmithError(\n 'INVALID_TIME',\n `Invalid time: '${s}' (expected HH:MM, 00:00-23:59)`,\n );\n }\n return { hour, minute };\n};\n\nexport const normalizeWeekday = (raw: string): Weekday => {\n const lc = raw.toLowerCase();\n if (!(WEEKDAYS as readonly string[]).includes(lc)) {\n throw new CronsmithError(\n 'INVALID_WEEKDAY',\n `Invalid weekday: '${raw}' (expected one of: ${WEEKDAYS.join(', ')})`,\n );\n }\n return lc as Weekday;\n};\n\nexport const normalizeMonth = (raw: string): Month => {\n const lc = raw.toLowerCase();\n if (!(MONTHS as readonly string[]).includes(lc)) {\n throw new CronsmithError(\n 'INVALID_MONTH',\n `Invalid month: '${raw}' (expected one of: ${MONTHS.join(', ')})`,\n );\n }\n return lc as Month;\n};\n\nexport const assertTimeUnit = (u: string): TimeUnit => {\n if (!(TIME_UNITS as readonly string[]).includes(u)) {\n throw new CronsmithError(\n 'INVALID_UNIT',\n `Invalid time unit: '${u}' (expected one of: ${TIME_UNITS.join(', ')})`,\n );\n }\n return u as TimeUnit;\n};\n\nexport const assertStep = (n: number, unit: TimeUnit): void => {\n if (!isInt(n) || n < 1) {\n throw new CronsmithError('INVALID_STEP', `Step must be >= 1, got ${n}`);\n }\n const max = FIELD_MAX[unit];\n if (n > max) {\n throw new CronsmithError(\n 'INVALID_STEP',\n `Step ${n} exceeds max for ${unit} (${max})`,\n );\n }\n};\n\nexport const assertHourRange = (start: number, end: number): void => {\n assertHour(start);\n assertHour(end);\n};\n\nexport const weekdayNum = (w: Weekday): number => WEEKDAY_TO_NUM[w];\nexport const monthNum = (m: Month): number => MONTH_TO_NUM[m];\n","import { CronsmithError } from './errors.js';\nimport { formatList, formatRange, formatStep, serialize } from './serializer.js';\nimport type { CronFields, MonthInput, TimeUnit, WeekdayInput } from './types.js';\nimport {\n assertDayOfMonth,\n assertHour,\n assertHourRange,\n assertMinute,\n assertStep,\n assertTimeUnit,\n monthNum,\n normalizeMonth,\n normalizeWeekday,\n parseTime,\n weekdayNum,\n} from './validators.js';\n\ntype TouchState = 'hard' | 'soft' | 'untouched';\n\ntype Touched = {\n minute: TouchState;\n hour: TouchState;\n dayOfMonth: TouchState;\n month: TouchState;\n dayOfWeek: TouchState;\n};\n\nconst FRESH_FIELDS: CronFields = {\n minute: '*',\n hour: '*',\n dayOfMonth: '*',\n month: '*',\n dayOfWeek: '*',\n};\n\nconst FRESH_TOUCHED: Touched = {\n minute: 'untouched',\n hour: 'untouched',\n dayOfMonth: 'untouched',\n month: 'untouched',\n dayOfWeek: 'untouched',\n};\n\nconst FIELD_LABEL: Record<keyof Touched, string> = {\n minute: 'minute',\n hour: 'hour',\n dayOfMonth: 'day-of-month',\n month: 'month',\n dayOfWeek: 'day-of-week',\n};\n\nconst toList = <T>(x: T | readonly T[]): readonly T[] =>\n Array.isArray(x) ? (x as readonly T[]) : [x as T];\n\nconst wrapList = (start: number, end: number, min: number, max: number): number[] => {\n // Enumerate values from start..max then min..end. Used when a range wraps\n // around the field boundary (e.g. Fri→Sun on weekdays, Nov→Feb on months).\n const out: number[] = [];\n for (let v = start; v <= max; v++) out.push(v);\n for (let v = min; v <= end; v++) out.push(v);\n return out;\n};\n\nexport class CronBuilder {\n private readonly fields: CronFields;\n private readonly touched: Touched;\n\n constructor(fields: CronFields = FRESH_FIELDS, touched: Touched = FRESH_TOUCHED) {\n this.fields = fields;\n this.touched = touched;\n }\n\n // hardKeys throw on conflict and become 'hard'; softPatch applies only to\n // 'untouched' fields and becomes 'soft' (a later hard call overrides it\n // silently). This is what makes pinning order-independent.\n private clone(\n patch: Partial<CronFields>,\n hardKeys: readonly (keyof Touched)[],\n softPatch?: Partial<CronFields>,\n ): CronBuilder {\n for (const k of hardKeys) {\n if (this.touched[k] === 'hard') {\n throw new CronsmithError(\n 'CONFLICTING_CALL',\n `${FIELD_LABEL[k]} field has already been set. To combine values, pass an array (e.g. atMinute([5, 35])) instead of chaining.`,\n );\n }\n }\n const nextFields: CronFields = { ...this.fields, ...patch };\n const nextTouched: Touched = { ...this.touched };\n for (const k of hardKeys) nextTouched[k] = 'hard';\n if (softPatch) {\n for (const k of Object.keys(softPatch) as (keyof CronFields)[]) {\n if (this.touched[k] === 'untouched') {\n nextFields[k] = softPatch[k]!;\n nextTouched[k] = 'soft';\n }\n }\n }\n return new CronBuilder(nextFields, nextTouched);\n }\n\n everyMinute(): CronBuilder {\n return this.clone({ minute: '*' }, ['minute']);\n }\n\n // Soft-defaults below pin smaller fields so every(n, X) fires once per\n // qualifying X instead of inheriting * from the unset fields.\n every(n: number, unit: TimeUnit): CronBuilder {\n const u = assertTimeUnit(unit);\n assertStep(n, u);\n if (u === 'minutes') return this.clone({ minute: formatStep(n) }, ['minute']);\n if (u === 'hours') {\n return this.clone({ hour: formatStep(n) }, ['hour'], { minute: '0' });\n }\n if (u === 'days') {\n return this.clone({ dayOfMonth: formatStep(n) }, ['dayOfMonth'], {\n minute: '0',\n hour: '0',\n });\n }\n return this.clone({ month: formatStep(n) }, ['month'], {\n minute: '0',\n hour: '0',\n dayOfMonth: '1',\n });\n }\n\n atMinute(m: number | readonly number[]): CronBuilder {\n const list = toList(m);\n for (const v of list) assertMinute(v);\n return this.clone({ minute: formatList(list) }, ['minute']);\n }\n\n atHour(h: number | readonly number[]): CronBuilder {\n const list = toList(h);\n for (const v of list) assertHour(v);\n return this.clone({ hour: formatList(list) }, ['hour'], { minute: '0' });\n }\n\n at(time: string): CronBuilder {\n const { hour, minute } = parseTime(time);\n return this.clone({ minute: String(minute), hour: String(hour) }, ['minute', 'hour']);\n }\n\n on(day: WeekdayInput | readonly WeekdayInput[]): CronBuilder {\n const nums = toList(day).map((d) => weekdayNum(normalizeWeekday(d)));\n return this.clone({ dayOfWeek: formatList(nums) }, ['dayOfWeek']);\n }\n\n onWeekdays(): CronBuilder {\n return this.clone({ dayOfWeek: '1-5' }, ['dayOfWeek']);\n }\n\n onWeekends(): CronBuilder {\n return this.clone({ dayOfWeek: '0,6' }, ['dayOfWeek']);\n }\n\n dayOfMonth(d: number | readonly number[]): CronBuilder {\n const list = toList(d);\n for (const v of list) assertDayOfMonth(v);\n return this.clone({ dayOfMonth: formatList(list) }, ['dayOfMonth']);\n }\n\n inMonth(m: MonthInput | readonly MonthInput[]): CronBuilder {\n const nums = toList(m).map((x) => monthNum(normalizeMonth(x)));\n return this.clone({ month: formatList(nums) }, ['month']);\n }\n\n betweenHours(start: number, end: number): CronBuilder {\n assertHourRange(start, end);\n if (start <= end) return this.clone({ hour: formatRange(start, end) }, ['hour']);\n const hours = wrapList(start, end, 0, 23);\n return this.clone({ hour: hours.join(',') }, ['hour']);\n }\n\n betweenDays(start: WeekdayInput, end: WeekdayInput): CronBuilder {\n const s = weekdayNum(normalizeWeekday(start));\n const e = weekdayNum(normalizeWeekday(end));\n if (s <= e) return this.clone({ dayOfWeek: formatRange(s, e) }, ['dayOfWeek']);\n const days = wrapList(s, e, 0, 6);\n return this.clone({ dayOfWeek: days.join(',') }, ['dayOfWeek']);\n }\n\n betweenMonths(start: MonthInput, end: MonthInput): CronBuilder {\n const s = monthNum(normalizeMonth(start));\n const e = monthNum(normalizeMonth(end));\n if (s <= e) return this.clone({ month: formatRange(s, e) }, ['month']);\n const months = wrapList(s, e, 1, 12);\n return this.clone({ month: months.join(',') }, ['month']);\n }\n\n toString(): string {\n return serialize(this.fields);\n }\n\n toObject(): CronFields {\n return { ...this.fields };\n }\n}\n","import { CronBuilder } from './builder.js';\n\nexport const cron = (): CronBuilder => new CronBuilder();\n\nexport { CronBuilder } from './builder.js';\nexport { CronsmithError } from './errors.js';\nexport type {\n CronErrorCode,\n CronFields,\n Month,\n MonthInput,\n TimeUnit,\n Weekday,\n WeekdayInput,\n} from './types.js';\n"]}
@@ -0,0 +1,52 @@
1
+ type Weekday = 'sunday' | 'monday' | 'tuesday' | 'wednesday' | 'thursday' | 'friday' | 'saturday';
2
+ type WeekdayInput = Weekday | Capitalize<Weekday> | Uppercase<Weekday>;
3
+ type Month = 'january' | 'february' | 'march' | 'april' | 'may' | 'june' | 'july' | 'august' | 'september' | 'october' | 'november' | 'december';
4
+ type MonthInput = Month | Capitalize<Month> | Uppercase<Month>;
5
+ type TimeUnit = 'minutes' | 'hours' | 'days' | 'months';
6
+ type CronFields = {
7
+ minute: string;
8
+ hour: string;
9
+ dayOfMonth: string;
10
+ month: string;
11
+ dayOfWeek: string;
12
+ };
13
+ type CronErrorCode = 'INVALID_MINUTE' | 'INVALID_HOUR' | 'INVALID_TIME' | 'INVALID_DAY_OF_MONTH' | 'INVALID_WEEKDAY' | 'INVALID_MONTH' | 'INVALID_STEP' | 'INVALID_RANGE' | 'INVALID_UNIT' | 'CONFLICTING_CALL';
14
+
15
+ type TouchState = 'hard' | 'soft' | 'untouched';
16
+ type Touched = {
17
+ minute: TouchState;
18
+ hour: TouchState;
19
+ dayOfMonth: TouchState;
20
+ month: TouchState;
21
+ dayOfWeek: TouchState;
22
+ };
23
+ declare class CronBuilder {
24
+ private readonly fields;
25
+ private readonly touched;
26
+ constructor(fields?: CronFields, touched?: Touched);
27
+ private clone;
28
+ everyMinute(): CronBuilder;
29
+ every(n: number, unit: TimeUnit): CronBuilder;
30
+ atMinute(m: number | readonly number[]): CronBuilder;
31
+ atHour(h: number | readonly number[]): CronBuilder;
32
+ at(time: string): CronBuilder;
33
+ on(day: WeekdayInput | readonly WeekdayInput[]): CronBuilder;
34
+ onWeekdays(): CronBuilder;
35
+ onWeekends(): CronBuilder;
36
+ dayOfMonth(d: number | readonly number[]): CronBuilder;
37
+ inMonth(m: MonthInput | readonly MonthInput[]): CronBuilder;
38
+ betweenHours(start: number, end: number): CronBuilder;
39
+ betweenDays(start: WeekdayInput, end: WeekdayInput): CronBuilder;
40
+ betweenMonths(start: MonthInput, end: MonthInput): CronBuilder;
41
+ toString(): string;
42
+ toObject(): CronFields;
43
+ }
44
+
45
+ declare class CronsmithError extends Error {
46
+ readonly code: CronErrorCode;
47
+ constructor(code: CronErrorCode, message: string);
48
+ }
49
+
50
+ declare const cron: () => CronBuilder;
51
+
52
+ export { CronBuilder, type CronErrorCode, type CronFields, CronsmithError, type Month, type MonthInput, type TimeUnit, type Weekday, type WeekdayInput, cron };
@@ -0,0 +1,52 @@
1
+ type Weekday = 'sunday' | 'monday' | 'tuesday' | 'wednesday' | 'thursday' | 'friday' | 'saturday';
2
+ type WeekdayInput = Weekday | Capitalize<Weekday> | Uppercase<Weekday>;
3
+ type Month = 'january' | 'february' | 'march' | 'april' | 'may' | 'june' | 'july' | 'august' | 'september' | 'october' | 'november' | 'december';
4
+ type MonthInput = Month | Capitalize<Month> | Uppercase<Month>;
5
+ type TimeUnit = 'minutes' | 'hours' | 'days' | 'months';
6
+ type CronFields = {
7
+ minute: string;
8
+ hour: string;
9
+ dayOfMonth: string;
10
+ month: string;
11
+ dayOfWeek: string;
12
+ };
13
+ type CronErrorCode = 'INVALID_MINUTE' | 'INVALID_HOUR' | 'INVALID_TIME' | 'INVALID_DAY_OF_MONTH' | 'INVALID_WEEKDAY' | 'INVALID_MONTH' | 'INVALID_STEP' | 'INVALID_RANGE' | 'INVALID_UNIT' | 'CONFLICTING_CALL';
14
+
15
+ type TouchState = 'hard' | 'soft' | 'untouched';
16
+ type Touched = {
17
+ minute: TouchState;
18
+ hour: TouchState;
19
+ dayOfMonth: TouchState;
20
+ month: TouchState;
21
+ dayOfWeek: TouchState;
22
+ };
23
+ declare class CronBuilder {
24
+ private readonly fields;
25
+ private readonly touched;
26
+ constructor(fields?: CronFields, touched?: Touched);
27
+ private clone;
28
+ everyMinute(): CronBuilder;
29
+ every(n: number, unit: TimeUnit): CronBuilder;
30
+ atMinute(m: number | readonly number[]): CronBuilder;
31
+ atHour(h: number | readonly number[]): CronBuilder;
32
+ at(time: string): CronBuilder;
33
+ on(day: WeekdayInput | readonly WeekdayInput[]): CronBuilder;
34
+ onWeekdays(): CronBuilder;
35
+ onWeekends(): CronBuilder;
36
+ dayOfMonth(d: number | readonly number[]): CronBuilder;
37
+ inMonth(m: MonthInput | readonly MonthInput[]): CronBuilder;
38
+ betweenHours(start: number, end: number): CronBuilder;
39
+ betweenDays(start: WeekdayInput, end: WeekdayInput): CronBuilder;
40
+ betweenMonths(start: MonthInput, end: MonthInput): CronBuilder;
41
+ toString(): string;
42
+ toObject(): CronFields;
43
+ }
44
+
45
+ declare class CronsmithError extends Error {
46
+ readonly code: CronErrorCode;
47
+ constructor(code: CronErrorCode, message: string);
48
+ }
49
+
50
+ declare const cron: () => CronBuilder;
51
+
52
+ export { CronBuilder, type CronErrorCode, type CronFields, CronsmithError, type Month, type MonthInput, type TimeUnit, type Weekday, type WeekdayInput, cron };
package/dist/index.js ADDED
@@ -0,0 +1,310 @@
1
+ // src/errors.ts
2
+ var CronsmithError = class extends Error {
3
+ code;
4
+ constructor(code, message) {
5
+ super(message);
6
+ this.name = "CronsmithError";
7
+ this.code = code;
8
+ }
9
+ };
10
+
11
+ // src/serializer.ts
12
+ var serialize = (f) => `${f.minute} ${f.hour} ${f.dayOfMonth} ${f.month} ${f.dayOfWeek}`;
13
+ var formatList = (values) => {
14
+ const sorted = [...new Set(values)].sort((a, b) => a - b);
15
+ return sorted.join(",");
16
+ };
17
+ var formatStep = (n) => n === 1 ? "*" : `*/${n}`;
18
+ var formatRange = (start, end) => start === end ? String(start) : `${start}-${end}`;
19
+
20
+ // src/constants.ts
21
+ var WEEKDAYS = [
22
+ "sunday",
23
+ "monday",
24
+ "tuesday",
25
+ "wednesday",
26
+ "thursday",
27
+ "friday",
28
+ "saturday"
29
+ ];
30
+ var WEEKDAY_TO_NUM = {
31
+ sunday: 0,
32
+ monday: 1,
33
+ tuesday: 2,
34
+ wednesday: 3,
35
+ thursday: 4,
36
+ friday: 5,
37
+ saturday: 6
38
+ };
39
+ var MONTHS = [
40
+ "january",
41
+ "february",
42
+ "march",
43
+ "april",
44
+ "may",
45
+ "june",
46
+ "july",
47
+ "august",
48
+ "september",
49
+ "october",
50
+ "november",
51
+ "december"
52
+ ];
53
+ var MONTH_TO_NUM = {
54
+ january: 1,
55
+ february: 2,
56
+ march: 3,
57
+ april: 4,
58
+ may: 5,
59
+ june: 6,
60
+ july: 7,
61
+ august: 8,
62
+ september: 9,
63
+ october: 10,
64
+ november: 11,
65
+ december: 12
66
+ };
67
+ var FIELD_MAX = {
68
+ minutes: 59,
69
+ hours: 23,
70
+ days: 31,
71
+ months: 12
72
+ };
73
+ var TIME_UNITS = ["minutes", "hours", "days", "months"];
74
+
75
+ // src/validators.ts
76
+ var isInt = (n) => typeof n === "number" && Number.isInteger(n) && Number.isFinite(n);
77
+ var assertMinute = (m) => {
78
+ if (!isInt(m) || m < 0 || m > 59) {
79
+ throw new CronsmithError("INVALID_MINUTE", `Invalid minute: ${m} (expected 0-59)`);
80
+ }
81
+ };
82
+ var assertHour = (h) => {
83
+ if (!isInt(h) || h < 0 || h > 23) {
84
+ throw new CronsmithError("INVALID_HOUR", `Invalid hour: ${h} (expected 0-23)`);
85
+ }
86
+ };
87
+ var assertDayOfMonth = (d) => {
88
+ if (!isInt(d) || d < 1 || d > 31) {
89
+ throw new CronsmithError("INVALID_DAY_OF_MONTH", `Invalid day of month: ${d} (expected 1-31)`);
90
+ }
91
+ };
92
+ var TIME_RE = /^([0-9]{2}):([0-9]{2})$/;
93
+ var parseTime = (s) => {
94
+ const m = TIME_RE.exec(s);
95
+ if (!m) {
96
+ throw new CronsmithError(
97
+ "INVALID_TIME",
98
+ `Invalid time: '${s}' (expected HH:MM, 00:00-23:59)`
99
+ );
100
+ }
101
+ const hour = Number(m[1]);
102
+ const minute = Number(m[2]);
103
+ if (hour < 0 || hour > 23 || minute < 0 || minute > 59) {
104
+ throw new CronsmithError(
105
+ "INVALID_TIME",
106
+ `Invalid time: '${s}' (expected HH:MM, 00:00-23:59)`
107
+ );
108
+ }
109
+ return { hour, minute };
110
+ };
111
+ var normalizeWeekday = (raw) => {
112
+ const lc = raw.toLowerCase();
113
+ if (!WEEKDAYS.includes(lc)) {
114
+ throw new CronsmithError(
115
+ "INVALID_WEEKDAY",
116
+ `Invalid weekday: '${raw}' (expected one of: ${WEEKDAYS.join(", ")})`
117
+ );
118
+ }
119
+ return lc;
120
+ };
121
+ var normalizeMonth = (raw) => {
122
+ const lc = raw.toLowerCase();
123
+ if (!MONTHS.includes(lc)) {
124
+ throw new CronsmithError(
125
+ "INVALID_MONTH",
126
+ `Invalid month: '${raw}' (expected one of: ${MONTHS.join(", ")})`
127
+ );
128
+ }
129
+ return lc;
130
+ };
131
+ var assertTimeUnit = (u) => {
132
+ if (!TIME_UNITS.includes(u)) {
133
+ throw new CronsmithError(
134
+ "INVALID_UNIT",
135
+ `Invalid time unit: '${u}' (expected one of: ${TIME_UNITS.join(", ")})`
136
+ );
137
+ }
138
+ return u;
139
+ };
140
+ var assertStep = (n, unit) => {
141
+ if (!isInt(n) || n < 1) {
142
+ throw new CronsmithError("INVALID_STEP", `Step must be >= 1, got ${n}`);
143
+ }
144
+ const max = FIELD_MAX[unit];
145
+ if (n > max) {
146
+ throw new CronsmithError(
147
+ "INVALID_STEP",
148
+ `Step ${n} exceeds max for ${unit} (${max})`
149
+ );
150
+ }
151
+ };
152
+ var assertHourRange = (start, end) => {
153
+ assertHour(start);
154
+ assertHour(end);
155
+ };
156
+ var weekdayNum = (w) => WEEKDAY_TO_NUM[w];
157
+ var monthNum = (m) => MONTH_TO_NUM[m];
158
+
159
+ // src/builder.ts
160
+ var FRESH_FIELDS = {
161
+ minute: "*",
162
+ hour: "*",
163
+ dayOfMonth: "*",
164
+ month: "*",
165
+ dayOfWeek: "*"
166
+ };
167
+ var FRESH_TOUCHED = {
168
+ minute: "untouched",
169
+ hour: "untouched",
170
+ dayOfMonth: "untouched",
171
+ month: "untouched",
172
+ dayOfWeek: "untouched"
173
+ };
174
+ var FIELD_LABEL = {
175
+ minute: "minute",
176
+ hour: "hour",
177
+ dayOfMonth: "day-of-month",
178
+ month: "month",
179
+ dayOfWeek: "day-of-week"
180
+ };
181
+ var toList = (x) => Array.isArray(x) ? x : [x];
182
+ var wrapList = (start, end, min, max) => {
183
+ const out = [];
184
+ for (let v = start; v <= max; v++) out.push(v);
185
+ for (let v = min; v <= end; v++) out.push(v);
186
+ return out;
187
+ };
188
+ var CronBuilder = class _CronBuilder {
189
+ fields;
190
+ touched;
191
+ constructor(fields = FRESH_FIELDS, touched = FRESH_TOUCHED) {
192
+ this.fields = fields;
193
+ this.touched = touched;
194
+ }
195
+ // hardKeys throw on conflict and become 'hard'; softPatch applies only to
196
+ // 'untouched' fields and becomes 'soft' (a later hard call overrides it
197
+ // silently). This is what makes pinning order-independent.
198
+ clone(patch, hardKeys, softPatch) {
199
+ for (const k of hardKeys) {
200
+ if (this.touched[k] === "hard") {
201
+ throw new CronsmithError(
202
+ "CONFLICTING_CALL",
203
+ `${FIELD_LABEL[k]} field has already been set. To combine values, pass an array (e.g. atMinute([5, 35])) instead of chaining.`
204
+ );
205
+ }
206
+ }
207
+ const nextFields = { ...this.fields, ...patch };
208
+ const nextTouched = { ...this.touched };
209
+ for (const k of hardKeys) nextTouched[k] = "hard";
210
+ if (softPatch) {
211
+ for (const k of Object.keys(softPatch)) {
212
+ if (this.touched[k] === "untouched") {
213
+ nextFields[k] = softPatch[k];
214
+ nextTouched[k] = "soft";
215
+ }
216
+ }
217
+ }
218
+ return new _CronBuilder(nextFields, nextTouched);
219
+ }
220
+ everyMinute() {
221
+ return this.clone({ minute: "*" }, ["minute"]);
222
+ }
223
+ // Soft-defaults below pin smaller fields so every(n, X) fires once per
224
+ // qualifying X instead of inheriting * from the unset fields.
225
+ every(n, unit) {
226
+ const u = assertTimeUnit(unit);
227
+ assertStep(n, u);
228
+ if (u === "minutes") return this.clone({ minute: formatStep(n) }, ["minute"]);
229
+ if (u === "hours") {
230
+ return this.clone({ hour: formatStep(n) }, ["hour"], { minute: "0" });
231
+ }
232
+ if (u === "days") {
233
+ return this.clone({ dayOfMonth: formatStep(n) }, ["dayOfMonth"], {
234
+ minute: "0",
235
+ hour: "0"
236
+ });
237
+ }
238
+ return this.clone({ month: formatStep(n) }, ["month"], {
239
+ minute: "0",
240
+ hour: "0",
241
+ dayOfMonth: "1"
242
+ });
243
+ }
244
+ atMinute(m) {
245
+ const list = toList(m);
246
+ for (const v of list) assertMinute(v);
247
+ return this.clone({ minute: formatList(list) }, ["minute"]);
248
+ }
249
+ atHour(h) {
250
+ const list = toList(h);
251
+ for (const v of list) assertHour(v);
252
+ return this.clone({ hour: formatList(list) }, ["hour"], { minute: "0" });
253
+ }
254
+ at(time) {
255
+ const { hour, minute } = parseTime(time);
256
+ return this.clone({ minute: String(minute), hour: String(hour) }, ["minute", "hour"]);
257
+ }
258
+ on(day) {
259
+ const nums = toList(day).map((d) => weekdayNum(normalizeWeekday(d)));
260
+ return this.clone({ dayOfWeek: formatList(nums) }, ["dayOfWeek"]);
261
+ }
262
+ onWeekdays() {
263
+ return this.clone({ dayOfWeek: "1-5" }, ["dayOfWeek"]);
264
+ }
265
+ onWeekends() {
266
+ return this.clone({ dayOfWeek: "0,6" }, ["dayOfWeek"]);
267
+ }
268
+ dayOfMonth(d) {
269
+ const list = toList(d);
270
+ for (const v of list) assertDayOfMonth(v);
271
+ return this.clone({ dayOfMonth: formatList(list) }, ["dayOfMonth"]);
272
+ }
273
+ inMonth(m) {
274
+ const nums = toList(m).map((x) => monthNum(normalizeMonth(x)));
275
+ return this.clone({ month: formatList(nums) }, ["month"]);
276
+ }
277
+ betweenHours(start, end) {
278
+ assertHourRange(start, end);
279
+ if (start <= end) return this.clone({ hour: formatRange(start, end) }, ["hour"]);
280
+ const hours = wrapList(start, end, 0, 23);
281
+ return this.clone({ hour: hours.join(",") }, ["hour"]);
282
+ }
283
+ betweenDays(start, end) {
284
+ const s = weekdayNum(normalizeWeekday(start));
285
+ const e = weekdayNum(normalizeWeekday(end));
286
+ if (s <= e) return this.clone({ dayOfWeek: formatRange(s, e) }, ["dayOfWeek"]);
287
+ const days = wrapList(s, e, 0, 6);
288
+ return this.clone({ dayOfWeek: days.join(",") }, ["dayOfWeek"]);
289
+ }
290
+ betweenMonths(start, end) {
291
+ const s = monthNum(normalizeMonth(start));
292
+ const e = monthNum(normalizeMonth(end));
293
+ if (s <= e) return this.clone({ month: formatRange(s, e) }, ["month"]);
294
+ const months = wrapList(s, e, 1, 12);
295
+ return this.clone({ month: months.join(",") }, ["month"]);
296
+ }
297
+ toString() {
298
+ return serialize(this.fields);
299
+ }
300
+ toObject() {
301
+ return { ...this.fields };
302
+ }
303
+ };
304
+
305
+ // src/index.ts
306
+ var cron = () => new CronBuilder();
307
+
308
+ export { CronBuilder, CronsmithError, cron };
309
+ //# sourceMappingURL=index.js.map
310
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/errors.ts","../src/serializer.ts","../src/constants.ts","../src/validators.ts","../src/builder.ts","../src/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import type { CronErrorCode } from './types.js';\n\nexport class CronsmithError extends Error {\n readonly code: CronErrorCode;\n\n constructor(code: CronErrorCode, message: string) {\n super(message);\n this.name = 'CronsmithError';\n this.code = code;\n }\n}\n","import type { CronFields } from './types.js';\n\nexport const serialize = (f: CronFields): string =>\n `${f.minute} ${f.hour} ${f.dayOfMonth} ${f.month} ${f.dayOfWeek}`;\n\nexport const formatList = (values: readonly number[]): string => {\n const sorted = [...new Set(values)].sort((a, b) => a - b);\n return sorted.join(',');\n};\n\nexport const formatStep = (n: number): string => (n === 1 ? '*' : `*/${n}`);\n\nexport const formatRange = (start: number, end: number): string =>\n start === end ? String(start) : `${start}-${end}`;\n","import type { Month, TimeUnit, Weekday } from './types.js';\n\nexport const WEEKDAYS: readonly Weekday[] = [\n 'sunday',\n 'monday',\n 'tuesday',\n 'wednesday',\n 'thursday',\n 'friday',\n 'saturday',\n] as const;\n\nexport const WEEKDAY_TO_NUM: Record<Weekday, number> = {\n sunday: 0,\n monday: 1,\n tuesday: 2,\n wednesday: 3,\n thursday: 4,\n friday: 5,\n saturday: 6,\n};\n\nexport const MONTHS: readonly Month[] = [\n 'january',\n 'february',\n 'march',\n 'april',\n 'may',\n 'june',\n 'july',\n 'august',\n 'september',\n 'october',\n 'november',\n 'december',\n] as const;\n\nexport const MONTH_TO_NUM: Record<Month, number> = {\n january: 1,\n february: 2,\n march: 3,\n april: 4,\n may: 5,\n june: 6,\n july: 7,\n august: 8,\n september: 9,\n october: 10,\n november: 11,\n december: 12,\n};\n\nexport const FIELD_MAX: Record<TimeUnit, number> = {\n minutes: 59,\n hours: 23,\n days: 31,\n months: 12,\n};\n\nexport const FIELD_MIN: Record<TimeUnit, number> = {\n minutes: 0,\n hours: 0,\n days: 1,\n months: 1,\n};\n\nexport const TIME_UNITS: readonly TimeUnit[] = ['minutes', 'hours', 'days', 'months'] as const;\n","import { FIELD_MAX, MONTH_TO_NUM, MONTHS, TIME_UNITS, WEEKDAY_TO_NUM, WEEKDAYS } from './constants.js';\nimport { CronsmithError } from './errors.js';\nimport type { Month, TimeUnit, Weekday } from './types.js';\n\nconst isInt = (n: unknown): n is number =>\n typeof n === 'number' && Number.isInteger(n) && Number.isFinite(n);\n\nexport const assertMinute = (m: number): void => {\n if (!isInt(m) || m < 0 || m > 59) {\n throw new CronsmithError('INVALID_MINUTE', `Invalid minute: ${m} (expected 0-59)`);\n }\n};\n\nexport const assertHour = (h: number): void => {\n if (!isInt(h) || h < 0 || h > 23) {\n throw new CronsmithError('INVALID_HOUR', `Invalid hour: ${h} (expected 0-23)`);\n }\n};\n\nexport const assertDayOfMonth = (d: number): void => {\n if (!isInt(d) || d < 1 || d > 31) {\n throw new CronsmithError('INVALID_DAY_OF_MONTH', `Invalid day of month: ${d} (expected 1-31)`);\n }\n};\n\nconst TIME_RE = /^([0-9]{2}):([0-9]{2})$/;\n\nexport const parseTime = (s: string): { hour: number; minute: number } => {\n const m = TIME_RE.exec(s);\n if (!m) {\n throw new CronsmithError(\n 'INVALID_TIME',\n `Invalid time: '${s}' (expected HH:MM, 00:00-23:59)`,\n );\n }\n const hour = Number(m[1]);\n const minute = Number(m[2]);\n if (hour < 0 || hour > 23 || minute < 0 || minute > 59) {\n throw new CronsmithError(\n 'INVALID_TIME',\n `Invalid time: '${s}' (expected HH:MM, 00:00-23:59)`,\n );\n }\n return { hour, minute };\n};\n\nexport const normalizeWeekday = (raw: string): Weekday => {\n const lc = raw.toLowerCase();\n if (!(WEEKDAYS as readonly string[]).includes(lc)) {\n throw new CronsmithError(\n 'INVALID_WEEKDAY',\n `Invalid weekday: '${raw}' (expected one of: ${WEEKDAYS.join(', ')})`,\n );\n }\n return lc as Weekday;\n};\n\nexport const normalizeMonth = (raw: string): Month => {\n const lc = raw.toLowerCase();\n if (!(MONTHS as readonly string[]).includes(lc)) {\n throw new CronsmithError(\n 'INVALID_MONTH',\n `Invalid month: '${raw}' (expected one of: ${MONTHS.join(', ')})`,\n );\n }\n return lc as Month;\n};\n\nexport const assertTimeUnit = (u: string): TimeUnit => {\n if (!(TIME_UNITS as readonly string[]).includes(u)) {\n throw new CronsmithError(\n 'INVALID_UNIT',\n `Invalid time unit: '${u}' (expected one of: ${TIME_UNITS.join(', ')})`,\n );\n }\n return u as TimeUnit;\n};\n\nexport const assertStep = (n: number, unit: TimeUnit): void => {\n if (!isInt(n) || n < 1) {\n throw new CronsmithError('INVALID_STEP', `Step must be >= 1, got ${n}`);\n }\n const max = FIELD_MAX[unit];\n if (n > max) {\n throw new CronsmithError(\n 'INVALID_STEP',\n `Step ${n} exceeds max for ${unit} (${max})`,\n );\n }\n};\n\nexport const assertHourRange = (start: number, end: number): void => {\n assertHour(start);\n assertHour(end);\n};\n\nexport const weekdayNum = (w: Weekday): number => WEEKDAY_TO_NUM[w];\nexport const monthNum = (m: Month): number => MONTH_TO_NUM[m];\n","import { CronsmithError } from './errors.js';\nimport { formatList, formatRange, formatStep, serialize } from './serializer.js';\nimport type { CronFields, MonthInput, TimeUnit, WeekdayInput } from './types.js';\nimport {\n assertDayOfMonth,\n assertHour,\n assertHourRange,\n assertMinute,\n assertStep,\n assertTimeUnit,\n monthNum,\n normalizeMonth,\n normalizeWeekday,\n parseTime,\n weekdayNum,\n} from './validators.js';\n\ntype TouchState = 'hard' | 'soft' | 'untouched';\n\ntype Touched = {\n minute: TouchState;\n hour: TouchState;\n dayOfMonth: TouchState;\n month: TouchState;\n dayOfWeek: TouchState;\n};\n\nconst FRESH_FIELDS: CronFields = {\n minute: '*',\n hour: '*',\n dayOfMonth: '*',\n month: '*',\n dayOfWeek: '*',\n};\n\nconst FRESH_TOUCHED: Touched = {\n minute: 'untouched',\n hour: 'untouched',\n dayOfMonth: 'untouched',\n month: 'untouched',\n dayOfWeek: 'untouched',\n};\n\nconst FIELD_LABEL: Record<keyof Touched, string> = {\n minute: 'minute',\n hour: 'hour',\n dayOfMonth: 'day-of-month',\n month: 'month',\n dayOfWeek: 'day-of-week',\n};\n\nconst toList = <T>(x: T | readonly T[]): readonly T[] =>\n Array.isArray(x) ? (x as readonly T[]) : [x as T];\n\nconst wrapList = (start: number, end: number, min: number, max: number): number[] => {\n // Enumerate values from start..max then min..end. Used when a range wraps\n // around the field boundary (e.g. Fri→Sun on weekdays, Nov→Feb on months).\n const out: number[] = [];\n for (let v = start; v <= max; v++) out.push(v);\n for (let v = min; v <= end; v++) out.push(v);\n return out;\n};\n\nexport class CronBuilder {\n private readonly fields: CronFields;\n private readonly touched: Touched;\n\n constructor(fields: CronFields = FRESH_FIELDS, touched: Touched = FRESH_TOUCHED) {\n this.fields = fields;\n this.touched = touched;\n }\n\n // hardKeys throw on conflict and become 'hard'; softPatch applies only to\n // 'untouched' fields and becomes 'soft' (a later hard call overrides it\n // silently). This is what makes pinning order-independent.\n private clone(\n patch: Partial<CronFields>,\n hardKeys: readonly (keyof Touched)[],\n softPatch?: Partial<CronFields>,\n ): CronBuilder {\n for (const k of hardKeys) {\n if (this.touched[k] === 'hard') {\n throw new CronsmithError(\n 'CONFLICTING_CALL',\n `${FIELD_LABEL[k]} field has already been set. To combine values, pass an array (e.g. atMinute([5, 35])) instead of chaining.`,\n );\n }\n }\n const nextFields: CronFields = { ...this.fields, ...patch };\n const nextTouched: Touched = { ...this.touched };\n for (const k of hardKeys) nextTouched[k] = 'hard';\n if (softPatch) {\n for (const k of Object.keys(softPatch) as (keyof CronFields)[]) {\n if (this.touched[k] === 'untouched') {\n nextFields[k] = softPatch[k]!;\n nextTouched[k] = 'soft';\n }\n }\n }\n return new CronBuilder(nextFields, nextTouched);\n }\n\n everyMinute(): CronBuilder {\n return this.clone({ minute: '*' }, ['minute']);\n }\n\n // Soft-defaults below pin smaller fields so every(n, X) fires once per\n // qualifying X instead of inheriting * from the unset fields.\n every(n: number, unit: TimeUnit): CronBuilder {\n const u = assertTimeUnit(unit);\n assertStep(n, u);\n if (u === 'minutes') return this.clone({ minute: formatStep(n) }, ['minute']);\n if (u === 'hours') {\n return this.clone({ hour: formatStep(n) }, ['hour'], { minute: '0' });\n }\n if (u === 'days') {\n return this.clone({ dayOfMonth: formatStep(n) }, ['dayOfMonth'], {\n minute: '0',\n hour: '0',\n });\n }\n return this.clone({ month: formatStep(n) }, ['month'], {\n minute: '0',\n hour: '0',\n dayOfMonth: '1',\n });\n }\n\n atMinute(m: number | readonly number[]): CronBuilder {\n const list = toList(m);\n for (const v of list) assertMinute(v);\n return this.clone({ minute: formatList(list) }, ['minute']);\n }\n\n atHour(h: number | readonly number[]): CronBuilder {\n const list = toList(h);\n for (const v of list) assertHour(v);\n return this.clone({ hour: formatList(list) }, ['hour'], { minute: '0' });\n }\n\n at(time: string): CronBuilder {\n const { hour, minute } = parseTime(time);\n return this.clone({ minute: String(minute), hour: String(hour) }, ['minute', 'hour']);\n }\n\n on(day: WeekdayInput | readonly WeekdayInput[]): CronBuilder {\n const nums = toList(day).map((d) => weekdayNum(normalizeWeekday(d)));\n return this.clone({ dayOfWeek: formatList(nums) }, ['dayOfWeek']);\n }\n\n onWeekdays(): CronBuilder {\n return this.clone({ dayOfWeek: '1-5' }, ['dayOfWeek']);\n }\n\n onWeekends(): CronBuilder {\n return this.clone({ dayOfWeek: '0,6' }, ['dayOfWeek']);\n }\n\n dayOfMonth(d: number | readonly number[]): CronBuilder {\n const list = toList(d);\n for (const v of list) assertDayOfMonth(v);\n return this.clone({ dayOfMonth: formatList(list) }, ['dayOfMonth']);\n }\n\n inMonth(m: MonthInput | readonly MonthInput[]): CronBuilder {\n const nums = toList(m).map((x) => monthNum(normalizeMonth(x)));\n return this.clone({ month: formatList(nums) }, ['month']);\n }\n\n betweenHours(start: number, end: number): CronBuilder {\n assertHourRange(start, end);\n if (start <= end) return this.clone({ hour: formatRange(start, end) }, ['hour']);\n const hours = wrapList(start, end, 0, 23);\n return this.clone({ hour: hours.join(',') }, ['hour']);\n }\n\n betweenDays(start: WeekdayInput, end: WeekdayInput): CronBuilder {\n const s = weekdayNum(normalizeWeekday(start));\n const e = weekdayNum(normalizeWeekday(end));\n if (s <= e) return this.clone({ dayOfWeek: formatRange(s, e) }, ['dayOfWeek']);\n const days = wrapList(s, e, 0, 6);\n return this.clone({ dayOfWeek: days.join(',') }, ['dayOfWeek']);\n }\n\n betweenMonths(start: MonthInput, end: MonthInput): CronBuilder {\n const s = monthNum(normalizeMonth(start));\n const e = monthNum(normalizeMonth(end));\n if (s <= e) return this.clone({ month: formatRange(s, e) }, ['month']);\n const months = wrapList(s, e, 1, 12);\n return this.clone({ month: months.join(',') }, ['month']);\n }\n\n toString(): string {\n return serialize(this.fields);\n }\n\n toObject(): CronFields {\n return { ...this.fields };\n }\n}\n","import { CronBuilder } from './builder.js';\n\nexport const cron = (): CronBuilder => new CronBuilder();\n\nexport { CronBuilder } from './builder.js';\nexport { CronsmithError } from './errors.js';\nexport type {\n CronErrorCode,\n CronFields,\n Month,\n MonthInput,\n TimeUnit,\n Weekday,\n WeekdayInput,\n} from './types.js';\n"]}
package/package.json ADDED
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "cronsmith",
3
+ "version": "0.1.0",
4
+ "description": "Fluent, type-safe, zero-runtime-dependency cron expression builder for TypeScript and JavaScript.",
5
+ "type": "module",
6
+ "main": "./dist/index.cjs",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js",
13
+ "require": "./dist/index.cjs"
14
+ }
15
+ },
16
+ "files": ["dist"],
17
+ "sideEffects": false,
18
+ "scripts": {
19
+ "build": "tsup",
20
+ "test": "vitest run",
21
+ "test:watch": "vitest",
22
+ "typecheck": "tsc -p tsconfig.test.json",
23
+ "verify": "npm run typecheck && npm test && npm run build",
24
+ "preversion": "npm run verify",
25
+ "postversion": "git push --follow-tags",
26
+ "prepublishOnly": "npm run verify"
27
+ },
28
+ "keywords": ["cron", "crontab", "scheduler", "builder", "fluent", "typescript"],
29
+ "author": "Fariz Prawira <farizprawira94@gmail.com>",
30
+ "license": "MIT",
31
+ "repository": {
32
+ "type": "git",
33
+ "url": "git+https://github.com/FarizPrawira/cronsmith.git"
34
+ },
35
+ "bugs": {
36
+ "url": "https://github.com/FarizPrawira/cronsmith/issues"
37
+ },
38
+ "homepage": "https://github.com/FarizPrawira/cronsmith#readme",
39
+ "engines": { "node": ">=22" },
40
+ "devDependencies": {
41
+ "@types/node": "^22.0.0",
42
+ "@vitest/coverage-v8": "^4.1.6",
43
+ "tsup": "^8.3.0",
44
+ "typescript": "^5.6.0",
45
+ "vitest": "^4.0.0"
46
+ }
47
+ }