Compare commits
102 Commits
fbf3f77229
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| df88ce9315 | |||
| 8b54c7abcd | |||
| 5958df8f57 | |||
| b4662a2b10 | |||
| 840fb98988 | |||
| fcd1b1c140 | |||
| 22f633e20b | |||
| e704249d75 | |||
| 0ff82657ab | |||
| fff309f56b | |||
| d3700c8835 | |||
| ea44c9caea | |||
| ab8ea6977f | |||
| d5f91b9b12 | |||
| a8d9b033f3 | |||
| e1fb3055bb | |||
| 6d452394aa | |||
| 62801326cb | |||
| ee2f3e12d2 | |||
| e341e53fe2 | |||
| 03eed8202b | |||
| 624592a63d | |||
| 2191bf2bdf | |||
| d7d68684e4 | |||
| d85cfb303b | |||
| 2e5c04d2d4 | |||
| 2fe6f0c576 | |||
| c176fadafe | |||
| ced688a7a3 | |||
| 263af9fa3a | |||
| 1a55330734 | |||
| 0026f24002 | |||
| 2d6bb69387 | |||
| 70e3a4f2b0 | |||
| 98cd24ee29 | |||
| accbb1a9f6 | |||
| d38feb938d | |||
| edfc641f93 | |||
| 0f9429be45 | |||
| dc3f311037 | |||
| 828c3bbe36 | |||
| 0a5e1635f1 | |||
| 1df5854201 | |||
| 2b1dd4c6a5 | |||
| 52a647b88a | |||
| b1a8611554 | |||
| 0cddf99456 | |||
| 1b45e61daf | |||
| 9c6467176c | |||
| fdc568753c | |||
| a3e9465b88 | |||
| 96c36f9ce1 | |||
| c68dff6123 | |||
| a457b9713c | |||
| 76d01af86c | |||
| b3ab17660a | |||
| 5737973197 | |||
| 9a701b339b | |||
| 7710abcf9e | |||
| edef58568d | |||
| 1c3b3a5151 | |||
| a2a9cc1450 | |||
| effc2c06e0 | |||
| e5c2d38559 | |||
| a1bc980d75 | |||
| ff3b0db02d | |||
| c1ac0a17b2 | |||
| 2deda9cb81 | |||
| 3affadad0d | |||
| 4c6ca7d836 | |||
| 0bc7cf2f66 | |||
| 3a250e8d73 | |||
| a67f55d7c8 | |||
| 932c88ea75 | |||
| 35152ea3fe | |||
| 7b73014ab0 | |||
| fe0409fe4e | |||
| 69cbcf5ff6 | |||
| 2a4d41d4cc | |||
| 72ea9cfbc9 | |||
| a47c191cbb | |||
| 9a6561d4ae | |||
| 21b357a97c | |||
| 69f673b494 | |||
| c97178b68b | |||
| 02e9f9b9a3 | |||
| 983b929d90 | |||
| e48d5ab2d8 | |||
| 320df0b3d4 | |||
| 6fce84c002 | |||
| d695c3e8d4 | |||
| caaacbee44 | |||
| ee4f498db0 | |||
| 68e07a4d53 | |||
| 23b6d236a5 | |||
| 9d4581a2b5 | |||
| 3e13b4f7b0 | |||
| 7138838183 | |||
| f1934ba444 | |||
| 3cf3c9268c | |||
| 61764be51e | |||
| 1e894b7700 |
26
.gitignore
vendored
26
.gitignore
vendored
@@ -1,12 +1,22 @@
|
||||
# ---> Vue
|
||||
# gitignore template for Vue.js projects
|
||||
#
|
||||
# Recommended template: Node.gitignore
|
||||
# Logs
|
||||
npm-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# TODO: where does this rule come from?
|
||||
# Dependencies
|
||||
node_modules/
|
||||
|
||||
# Build output
|
||||
dist/
|
||||
|
||||
# Environment variables
|
||||
.env*
|
||||
*.local
|
||||
|
||||
# IDE config
|
||||
.idea/
|
||||
|
||||
# Docs
|
||||
docs/_book
|
||||
|
||||
# TODO: where does this rule come from?
|
||||
# Tests
|
||||
test/
|
||||
|
||||
.idea/
|
||||
6
Makefile
Normal file
6
Makefile
Normal file
@@ -0,0 +1,6 @@
|
||||
dev:
|
||||
npm run dev
|
||||
|
||||
# 启用谷歌浏览器MCP服务器
|
||||
mcp-chrome:
|
||||
node "C:\nvm4w\nodejs\node_modules\chrome-devtools-mcp\build\src\index.js"
|
||||
6499
docs/swagger.json
6499
docs/swagger.json
File diff suppressed because it is too large
Load Diff
@@ -4,10 +4,8 @@
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>猪场管理系统</title>
|
||||
<link rel="stylesheet" href="./src/assets/styles/main.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="./src/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
54
node_modules/.package-lock.json
generated
vendored
54
node_modules/.package-lock.json
generated
vendored
@@ -3836,6 +3836,17 @@
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/cron-parser": {
|
||||
"version": "5.4.0",
|
||||
"resolved": "https://registry.npmmirror.com/cron-parser/-/cron-parser-5.4.0.tgz",
|
||||
"integrity": "sha512-HxYB8vTvnQFx4dLsZpGRa0uHp6X3qIzS3ZJgJ9v6l/5TJMgeWQbLkR5yiJ5hOxGbc9+jCADDnydIe15ReLZnJA==",
|
||||
"dependencies": {
|
||||
"luxon": "^3.7.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/cross-spawn": {
|
||||
"version": "7.0.6",
|
||||
"resolved": "https://registry.npmmirror.com/cross-spawn/-/cross-spawn-7.0.6.tgz",
|
||||
@@ -6633,6 +6644,14 @@
|
||||
"yallist": "^3.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/luxon": {
|
||||
"version": "3.7.2",
|
||||
"resolved": "https://registry.npmmirror.com/luxon/-/luxon-3.7.2.tgz",
|
||||
"integrity": "sha512-vtEhXh/gNjI9Yg1u4jX/0YVPMvxzHuGgCm6tC5kZyb08yjGWGnqAjGJvcXbqQR2P3MyMEFnRbpcdFS6PBcLqew==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/magic-string": {
|
||||
"version": "0.30.19",
|
||||
"resolved": "https://registry.npmmirror.com/magic-string/-/magic-string-0.30.19.tgz",
|
||||
@@ -9333,6 +9352,22 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/style-loader": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/style-loader/-/style-loader-4.0.0.tgz",
|
||||
"integrity": "sha512-1V4WqhhZZgjVAVJyt7TdDPZoPBPNHbekX4fWnCJL1yQukhCeZhJySUL+gL9y6sNdN95uEOS83Y55SqHcP7MzLA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 18.12.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/webpack"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"webpack": "^5.27.0"
|
||||
}
|
||||
},
|
||||
"node_modules/stylehacks": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmmirror.com/stylehacks/-/stylehacks-5.1.1.tgz",
|
||||
@@ -9641,6 +9676,20 @@
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
"version": "5.9.3",
|
||||
"resolved": "https://registry.npmmirror.com/typescript/-/typescript-5.9.3.tgz",
|
||||
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.17"
|
||||
}
|
||||
},
|
||||
"node_modules/undici-types": {
|
||||
"version": "7.12.0",
|
||||
"resolved": "https://registry.npmmirror.com/undici-types/-/undici-types-7.12.0.tgz",
|
||||
@@ -9903,6 +9952,11 @@
|
||||
"integrity": "sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/vue3-cron-plus-picker": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmmirror.com/vue3-cron-plus-picker/-/vue3-cron-plus-picker-1.0.2.tgz",
|
||||
"integrity": "sha512-SUVmAb2qSPMuAm5GIU0wQZyUawiiL3OKEy1HAZs94eZz+neKF+wEPNP4wICWMU78u4LzeCNni2Njnhf8bsqkcw=="
|
||||
},
|
||||
"node_modules/watchpack": {
|
||||
"version": "2.4.4",
|
||||
"resolved": "https://registry.npmmirror.com/watchpack/-/watchpack-2.4.4.tgz",
|
||||
|
||||
21
node_modules/cron-parser/LICENSE
generated
vendored
Normal file
21
node_modules/cron-parser/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014-2023 Harri Siirak
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
398
node_modules/cron-parser/README.md
generated
vendored
Normal file
398
node_modules/cron-parser/README.md
generated
vendored
Normal file
@@ -0,0 +1,398 @@
|
||||
# cron-parser
|
||||
|
||||
[](https://github.com/harrisiirak/cron-parser/actions/workflows/push.yml)
|
||||
[](http://badge.fury.io/js/cron-parser)
|
||||

|
||||
|
||||
A JavaScript library for parsing and manipulating cron expressions. Features timezone support, DST handling, and iterator capabilities.
|
||||
|
||||
[API documentation](https://harrisiirak.github.io/cron-parser/)
|
||||
|
||||
## Requirements
|
||||
|
||||
- Node.js >= 18
|
||||
- TypeScript >= 5
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
npm install cron-parser
|
||||
```
|
||||
|
||||
## Cron Format
|
||||
|
||||
```
|
||||
* * * * * *
|
||||
┬ ┬ ┬ ┬ ┬ ┬
|
||||
│ │ │ │ │ │
|
||||
│ │ │ │ │ └─ day of week (0-7, 1L-7L) (0 or 7 is Sun)
|
||||
│ │ │ │ └────── month (1-12, JAN-DEC)
|
||||
│ │ │ └─────────── day of month (1-31, L)
|
||||
│ │ └──────────────── hour (0-23)
|
||||
│ └───────────────────── minute (0-59)
|
||||
└────────────────────────── second (0-59, optional)
|
||||
```
|
||||
|
||||
### Special Characters
|
||||
|
||||
| Character | Description | Example |
|
||||
| --------- | ------------------------- | ---------------------------------------------------------------------- |
|
||||
| `*` | Any value | `* * * * *` (every minute) |
|
||||
| `?` | Any value (alias for `*`) | `? * * * *` (every minute) |
|
||||
| `,` | Value list separator | `1,2,3 * * * *` (1st, 2nd, and 3rd minute) |
|
||||
| `-` | Range of values | `1-5 * * * *` (every minute from 1 through 5) |
|
||||
| `/` | Step values | `*/5 * * * *` (every 5th minute) |
|
||||
| `L` | Last day of month/week | `0 0 L * *` (midnight on last day of month) |
|
||||
| `#` | Nth day of month | `0 0 * * 1#1` (first Monday of month) |
|
||||
| `H` | Randomized value | `H * * * *` (every n minute where n is randomly picked within [0, 59]) |
|
||||
|
||||
### Predefined Expressions
|
||||
|
||||
| Expression | Description | Equivalent |
|
||||
| ----------- | ----------------------------------------- | --------------- |
|
||||
| `@yearly` | Once a year at midnight of January 1 | `0 0 0 1 1 *` |
|
||||
| `@monthly` | Once a month at midnight of first day | `0 0 0 1 * *` |
|
||||
| `@weekly` | Once a week at midnight on Sunday | `0 0 0 * * 0` |
|
||||
| `@daily` | Once a day at midnight | `0 0 0 * * *` |
|
||||
| `@hourly` | Once an hour at the beginning of the hour | `0 0 * * * *` |
|
||||
| `@minutely` | Once a minute | `0 * * * * *` |
|
||||
| `@secondly` | Once a second | `* * * * * *` |
|
||||
| `@weekdays` | Every weekday at midnight | `0 0 0 * * 1-5` |
|
||||
| `@weekends` | Every weekend at midnight | `0 0 0 * * 0,6` |
|
||||
|
||||
### Field Values
|
||||
|
||||
| Field | Values | Special Characters | Aliases |
|
||||
| ------------ | ------ | ------------------------------- | ------------------------------ |
|
||||
| second | 0-59 | `*` `?` `,` `-` `/` `H` | |
|
||||
| minute | 0-59 | `*` `?` `,` `-` `/` `H` | |
|
||||
| hour | 0-23 | `*` `?` `,` `-` `/` `H` | |
|
||||
| day of month | 1-31 | `*` `?` `,` `-` `/` `H` `L` | |
|
||||
| month | 1-12 | `*` `?` `,` `-` `/` `H` | `JAN`-`DEC` |
|
||||
| day of week | 0-7 | `*` `?` `,` `-` `/` `H` `L` `#` | `SUN`-`SAT` (0 or 7 is Sunday) |
|
||||
|
||||
## Options
|
||||
|
||||
| Option | Type | Description |
|
||||
| ----------- | ------------------------ | --------------------------------------------------------------------------------------------------------------------------- |
|
||||
| currentDate | Date \| string \| number | Current date. Defaults to current local time in UTC. If not provided but startDate is set, startDate is used as currentDate |
|
||||
| endDate | Date \| string \| number | End date of iteration range. Sets iteration range end point |
|
||||
| startDate | Date \| string \| number | Start date of iteration range. Set iteration range start point |
|
||||
| tz | string | Timezone (e.g., 'Europe/London') |
|
||||
| hashSeed | string | A seed to be used in conjunction with the `H` special character |
|
||||
| strict | boolean | Enable strict mode validation |
|
||||
|
||||
When using string dates, the following formats are supported:
|
||||
|
||||
- ISO8601
|
||||
- HTTP and RFC2822
|
||||
- SQL
|
||||
|
||||
## Basic Usage
|
||||
|
||||
### Expression Parsing
|
||||
|
||||
```typescript
|
||||
import { CronExpressionParser } from 'cron-parser';
|
||||
|
||||
try {
|
||||
const interval = CronExpressionParser.parse('*/2 * * * *');
|
||||
|
||||
// Get next date
|
||||
console.log('Next:', interval.next().toString());
|
||||
// Get next 3 dates
|
||||
console.log(
|
||||
'Next 3:',
|
||||
interval.take(3).map((date) => date.toString()),
|
||||
);
|
||||
|
||||
// Get previous date
|
||||
console.log('Previous:', interval.prev().toString());
|
||||
} catch (err) {
|
||||
console.log('Error:', err.message);
|
||||
}
|
||||
```
|
||||
|
||||
### With Options
|
||||
|
||||
```typescript
|
||||
import { CronExpressionParser } from 'cron-parser';
|
||||
|
||||
const options = {
|
||||
currentDate: '2023-01-01T00:00:00Z',
|
||||
endDate: '2024-01-01T00:00:00Z',
|
||||
tz: 'Europe/London',
|
||||
};
|
||||
|
||||
try {
|
||||
const interval = CronExpressionParser.parse('0 0 * * *', options);
|
||||
console.log('Next:', interval.next().toString());
|
||||
} catch (err) {
|
||||
console.log('Error:', err.message);
|
||||
}
|
||||
```
|
||||
|
||||
### Date Range Handling
|
||||
|
||||
The library provides handling of date ranges with automatic adjustment of the `currentDate`:
|
||||
|
||||
**startDate as fallback**: If `currentDate` is not provided but `startDate` is, the `startDate` will be used as the `currentDate`.
|
||||
|
||||
```typescript
|
||||
const options = {
|
||||
startDate: '2023-01-01T00:00:00Z', // No currentDate provided
|
||||
};
|
||||
// currentDate will be set to 2023-01-01T00:00:00Z automatically
|
||||
const interval = CronExpressionParser.parse('0 0 * * *', options);
|
||||
```
|
||||
|
||||
**Automatic clamping**: If `currentDate` is outside the bounds defined by `startDate` and `endDate`, it will be automatically adjusted:
|
||||
|
||||
```typescript
|
||||
const options = {
|
||||
currentDate: '2022-01-01T00:00:00Z', // Before startDate
|
||||
startDate: '2023-01-01T00:00:00Z',
|
||||
endDate: '2024-01-01T00:00:00Z',
|
||||
};
|
||||
// currentDate will be clamped to startDate (2023-01-01T00:00:00Z)
|
||||
const interval = CronExpressionParser.parse('0 0 * * *', options);
|
||||
```
|
||||
|
||||
**Validation during iteration**: While the initial `currentDate` is automatically adjusted, the library still validates date bounds during iteration:
|
||||
|
||||
```typescript
|
||||
const options = {
|
||||
currentDate: '2023-12-31T00:00:00Z',
|
||||
endDate: '2024-01-01T00:00:00Z', // Very close end date
|
||||
};
|
||||
|
||||
const interval = CronExpressionParser.parse('0 0 * * *', options);
|
||||
console.log('Next:', interval.next().toString()); // Works fine
|
||||
|
||||
// This will throw an error because it would exceed endDate
|
||||
try {
|
||||
console.log('Next:', interval.next().toString());
|
||||
} catch (err) {
|
||||
console.log('Error:', err.message); // "Out of the time span range"
|
||||
}
|
||||
```
|
||||
|
||||
This behavior simplifies working with date ranges by removing the need to manually ensure that `currentDate` is within bounds, reducing confusion and making the API more intuitive.
|
||||
|
||||
### Crontab File Operations
|
||||
|
||||
For working with crontab files, use the CronFileParser:
|
||||
|
||||
```typescript
|
||||
import { CronFileParser } from 'cron-parser';
|
||||
|
||||
// Async file parsing
|
||||
try {
|
||||
const result = await CronFileParser.parseFile('/path/to/crontab');
|
||||
console.log('Variables:', result.variables);
|
||||
console.log('Expressions:', result.expressions);
|
||||
console.log('Errors:', result.errors);
|
||||
} catch (err) {
|
||||
console.log('Error:', err.message);
|
||||
}
|
||||
|
||||
// Sync file parsing
|
||||
try {
|
||||
const result = CronFileParser.parseFileSync('/path/to/crontab');
|
||||
console.log('Variables:', result.variables);
|
||||
console.log('Expressions:', result.expressions);
|
||||
console.log('Errors:', result.errors);
|
||||
} catch (err) {
|
||||
console.log('Error:', err.message);
|
||||
}
|
||||
```
|
||||
|
||||
## Advanced Features
|
||||
|
||||
### Strict Mode
|
||||
|
||||
In several implementations of CRON, it's ambiguous to specify both the Day Of Month and Day Of Week parameters simultaneously, as it's unclear which one should take precedence. Despite this ambiguity, this library allows both parameters to be set by default, although the resultant behavior might not align with your expectations.
|
||||
|
||||
To resolve this ambiguity, you can activate the strict mode of the library. When strict mode is enabled, the library enforces several validation rules:
|
||||
|
||||
1. **Day Of Month and Day Of Week**: Prevents the simultaneous setting of both Day Of Month and Day Of Week fields
|
||||
2. **Complete Expression**: Requires all 6 fields to be present in the expression (second, minute, hour, day of month, month, day of week)
|
||||
3. **Non-empty Expression**: Rejects empty expressions that would otherwise default to '0 \* \* \* \* \*'
|
||||
|
||||
These validations help ensure that your cron expressions are unambiguous and correctly formatted.
|
||||
|
||||
```typescript
|
||||
import { CronExpressionParser } from 'cron-parser';
|
||||
|
||||
// This will throw an error in strict mode because it uses both dayOfMonth and dayOfWeek
|
||||
const options = {
|
||||
currentDate: new Date('Mon, 12 Sep 2022 14:00:00'),
|
||||
strict: true,
|
||||
};
|
||||
|
||||
try {
|
||||
// This will throw an error in strict mode
|
||||
CronExpressionParser.parse('0 0 12 1-31 * 1', options);
|
||||
} catch (err) {
|
||||
console.log('Error:', err.message);
|
||||
// Error: Cannot use both dayOfMonth and dayOfWeek together in strict mode!
|
||||
}
|
||||
|
||||
// This will also throw an error in strict mode because it has fewer than 6 fields
|
||||
try {
|
||||
CronExpressionParser.parse('0 20 15 * *', { strict: true });
|
||||
} catch (err) {
|
||||
console.log('Error:', err.message);
|
||||
// Error: Invalid cron expression, expected 6 fields
|
||||
}
|
||||
```
|
||||
|
||||
### Last Day of Month/Week Support
|
||||
|
||||
The library supports parsing the range `0L - 7L` in the `weekday` position of the cron expression, where the `L` means "last occurrence of this weekday for the month in progress".
|
||||
|
||||
For example, the following expression will run on the last Monday of the month at midnight:
|
||||
|
||||
```typescript
|
||||
import { CronExpressionParser } from 'cron-parser';
|
||||
|
||||
// Last Monday of every month at midnight
|
||||
const lastMonday = CronExpressionParser.parse('0 0 0 * * 1L');
|
||||
|
||||
// You can also combine L expressions with other weekday expressions
|
||||
// This will run every Monday and the last Wednesday of the month
|
||||
const mixedWeekdays = CronExpressionParser.parse('0 0 0 * * 1,3L');
|
||||
|
||||
// Last day of every month
|
||||
const lastDay = CronExpressionParser.parse('0 0 L * *');
|
||||
```
|
||||
|
||||
### Using Iterator
|
||||
|
||||
```typescript
|
||||
import { CronExpressionParser } from 'cron-parser';
|
||||
|
||||
const interval = CronExpressionParser.parse('0 */2 * * *');
|
||||
|
||||
// Using for...of
|
||||
for (const date of interval) {
|
||||
console.log('Iterator value:', date.toString());
|
||||
if (someCondition) break;
|
||||
}
|
||||
|
||||
// Using take() for a specific number of iterations
|
||||
const nextFiveDates = interval.take(5);
|
||||
console.log(
|
||||
'Next 5 dates:',
|
||||
nextFiveDates.map((date) => date.toString()),
|
||||
);
|
||||
```
|
||||
|
||||
### Timezone Support
|
||||
|
||||
The library provides robust timezone support using Luxon, handling DST transitions correctly:
|
||||
|
||||
```typescript
|
||||
import { CronExpressionParser } from 'cron-parser';
|
||||
|
||||
const options = {
|
||||
currentDate: '2023-03-26T01:00:00',
|
||||
tz: 'Europe/London',
|
||||
};
|
||||
|
||||
const interval = CronExpressionParser.parse('0 * * * *', options);
|
||||
|
||||
// Will correctly handle DST transition
|
||||
console.log('Next dates during DST transition:');
|
||||
console.log(interval.next().toString());
|
||||
console.log(interval.next().toString());
|
||||
console.log(interval.next().toString());
|
||||
```
|
||||
|
||||
### Field Manipulation
|
||||
|
||||
You can modify cron fields programmatically using `CronFieldCollection.from` and construct a new expression:
|
||||
|
||||
```typescript
|
||||
import { CronExpressionParser, CronFieldCollection, CronHour, CronMinute } from 'cron-parser';
|
||||
|
||||
// Parse original expression
|
||||
const interval = CronExpressionParser.parse('0 7 * * 1-5');
|
||||
|
||||
// Create new collection with modified fields using raw values
|
||||
const modified = CronFieldCollection.from(interval.fields, {
|
||||
hour: [8],
|
||||
minute: [30],
|
||||
dayOfWeek: [1, 3, 5],
|
||||
});
|
||||
|
||||
console.log(modified.stringify()); // "30 8 * * 1,3,5"
|
||||
|
||||
// You can also use CronField instances
|
||||
const modified2 = CronFieldCollection.from(interval.fields, {
|
||||
hour: new CronHour([15]),
|
||||
minute: new CronMinute([30]),
|
||||
});
|
||||
|
||||
console.log(modified2.stringify()); // "30 15 * * 1-5"
|
||||
```
|
||||
|
||||
The `CronFieldCollection.from` method accepts either CronField instances or raw values that would be valid for creating new CronField instances. This is particularly useful when you need to modify only specific fields while keeping others unchanged.
|
||||
|
||||
### Hash support
|
||||
|
||||
The library supports adding [jitter](https://en.wikipedia.org/wiki/Jitter) to the returned intervals using the `H` special character in a field. When `H` is specified instead of `*`, a random value is used (`H` is replaced by `23`, where 23 is picked randomly, within the valid range of the field).
|
||||
|
||||
This jitter allows to spread the load when it comes to job scheduling. This feature is inspired by Jenkins's cron syntax.
|
||||
|
||||
```typescript
|
||||
import { CronExpressionParser } from 'cron-parser';
|
||||
|
||||
// At 23:<randomized> on every day-of-week from Monday through Friday.
|
||||
const interval = CronExpressionParser.parse('H 23 * * 1-5');
|
||||
|
||||
// At <randomized>:30 everyday.
|
||||
const interval = CronExpressionParser.parse('30 H * * *');
|
||||
|
||||
// At every minutes of <randomized> hour at <randomized> second everyday.
|
||||
const interval = CronExpressionParser.parse('H * H * * *');
|
||||
|
||||
// At every 5th minute starting from a random offset.
|
||||
// For example, if the random offset is 3, it will run at minutes 3, 8, 13, 18, etc.
|
||||
const interval = CronExpressionParser.parse('H/5 * * * *');
|
||||
|
||||
// At a random minute within the range 0-10 everyday.
|
||||
const interval = CronExpressionParser.parse('H(0-10) * * * *');
|
||||
|
||||
// At every 5th minute starting from a random offset within the range 0-4.
|
||||
// For example, if the random offset is 2, it will run at minutes 2, 7, 12, 17, etc.
|
||||
// The random offset is constrained to be less than the step value.
|
||||
const interval = CronExpressionParser.parse('H(0-29)/5 * * * *');
|
||||
|
||||
// At every minute of the third <randomized> day of the month
|
||||
const interval = CronExpressionParser.parse('* * * * H#3');
|
||||
```
|
||||
|
||||
The randomness is seed-able using the `hashSeed` option of `CronExpressionOptions`:
|
||||
|
||||
```typescript
|
||||
import { CronExpressionParser } from 'cron-parser';
|
||||
|
||||
const options = {
|
||||
currentDate: '2023-03-26T01:00:00',
|
||||
hashSeed: 'main-backup', // Generally, hashSeed would be a job name for example
|
||||
};
|
||||
|
||||
const interval = CronExpressionParser.parse('H * * * H', options);
|
||||
|
||||
console.log(interval.stringify()); // "12 * * * 4"
|
||||
|
||||
const otherInterval = CronExpressionParser.parse('H * * * H', options);
|
||||
|
||||
// Using the same seed will always return the same jitter
|
||||
console.log(otherInterval.stringify()); // "12 * * * 4"
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
497
node_modules/cron-parser/dist/CronDate.js
generated
vendored
Normal file
497
node_modules/cron-parser/dist/CronDate.js
generated
vendored
Normal file
@@ -0,0 +1,497 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.CronDate = exports.DAYS_IN_MONTH = exports.DateMathOp = exports.TimeUnit = void 0;
|
||||
const luxon_1 = require("luxon");
|
||||
var TimeUnit;
|
||||
(function (TimeUnit) {
|
||||
TimeUnit["Second"] = "Second";
|
||||
TimeUnit["Minute"] = "Minute";
|
||||
TimeUnit["Hour"] = "Hour";
|
||||
TimeUnit["Day"] = "Day";
|
||||
TimeUnit["Month"] = "Month";
|
||||
TimeUnit["Year"] = "Year";
|
||||
})(TimeUnit || (exports.TimeUnit = TimeUnit = {}));
|
||||
var DateMathOp;
|
||||
(function (DateMathOp) {
|
||||
DateMathOp["Add"] = "Add";
|
||||
DateMathOp["Subtract"] = "Subtract";
|
||||
})(DateMathOp || (exports.DateMathOp = DateMathOp = {}));
|
||||
exports.DAYS_IN_MONTH = Object.freeze([31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]);
|
||||
/**
|
||||
* CronDate class that wraps the Luxon DateTime object to provide
|
||||
* a consistent API for working with dates and times in the context of cron.
|
||||
*/
|
||||
class CronDate {
|
||||
#date;
|
||||
#dstStart = null;
|
||||
#dstEnd = null;
|
||||
/**
|
||||
* Maps the verb to the appropriate method
|
||||
*/
|
||||
#verbMap = {
|
||||
add: {
|
||||
[TimeUnit.Year]: this.addYear.bind(this),
|
||||
[TimeUnit.Month]: this.addMonth.bind(this),
|
||||
[TimeUnit.Day]: this.addDay.bind(this),
|
||||
[TimeUnit.Hour]: this.addHour.bind(this),
|
||||
[TimeUnit.Minute]: this.addMinute.bind(this),
|
||||
[TimeUnit.Second]: this.addSecond.bind(this),
|
||||
},
|
||||
subtract: {
|
||||
[TimeUnit.Year]: this.subtractYear.bind(this),
|
||||
[TimeUnit.Month]: this.subtractMonth.bind(this),
|
||||
[TimeUnit.Day]: this.subtractDay.bind(this),
|
||||
[TimeUnit.Hour]: this.subtractHour.bind(this),
|
||||
[TimeUnit.Minute]: this.subtractMinute.bind(this),
|
||||
[TimeUnit.Second]: this.subtractSecond.bind(this),
|
||||
},
|
||||
};
|
||||
/**
|
||||
* Constructs a new CronDate instance.
|
||||
* @param {CronDate | Date | number | string} [timestamp] - The timestamp to initialize the CronDate with.
|
||||
* @param {string} [tz] - The timezone to use for the CronDate.
|
||||
*/
|
||||
constructor(timestamp, tz) {
|
||||
const dateOpts = { zone: tz };
|
||||
// Initialize the internal DateTime object based on the type of timestamp provided.
|
||||
if (!timestamp) {
|
||||
this.#date = luxon_1.DateTime.local();
|
||||
}
|
||||
else if (timestamp instanceof CronDate) {
|
||||
this.#date = timestamp.#date;
|
||||
this.#dstStart = timestamp.#dstStart;
|
||||
this.#dstEnd = timestamp.#dstEnd;
|
||||
}
|
||||
else if (timestamp instanceof Date) {
|
||||
this.#date = luxon_1.DateTime.fromJSDate(timestamp, dateOpts);
|
||||
}
|
||||
else if (typeof timestamp === 'number') {
|
||||
this.#date = luxon_1.DateTime.fromMillis(timestamp, dateOpts);
|
||||
}
|
||||
else {
|
||||
this.#date = luxon_1.DateTime.fromISO(timestamp, dateOpts);
|
||||
this.#date.isValid || (this.#date = luxon_1.DateTime.fromRFC2822(timestamp, dateOpts));
|
||||
this.#date.isValid || (this.#date = luxon_1.DateTime.fromSQL(timestamp, dateOpts));
|
||||
this.#date.isValid || (this.#date = luxon_1.DateTime.fromFormat(timestamp, 'EEE, d MMM yyyy HH:mm:ss', dateOpts));
|
||||
}
|
||||
// Check for valid DateTime and throw an error if not valid.
|
||||
if (!this.#date.isValid) {
|
||||
throw new Error(`CronDate: unhandled timestamp: ${timestamp}`);
|
||||
}
|
||||
// Set the timezone if it is provided and different from the current zone.
|
||||
if (tz && tz !== this.#date.zoneName) {
|
||||
this.#date = this.#date.setZone(tz);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Determines if the given year is a leap year.
|
||||
* @param {number} year - The year to check
|
||||
* @returns {boolean} - True if the year is a leap year, false otherwise
|
||||
* @private
|
||||
*/
|
||||
static #isLeapYear(year) {
|
||||
return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;
|
||||
}
|
||||
/**
|
||||
* Returns daylight savings start time.
|
||||
* @returns {number | null}
|
||||
*/
|
||||
get dstStart() {
|
||||
return this.#dstStart;
|
||||
}
|
||||
/**
|
||||
* Sets daylight savings start time.
|
||||
* @param {number | null} value
|
||||
*/
|
||||
set dstStart(value) {
|
||||
this.#dstStart = value;
|
||||
}
|
||||
/**
|
||||
* Returns daylight savings end time.
|
||||
* @returns {number | null}
|
||||
*/
|
||||
get dstEnd() {
|
||||
return this.#dstEnd;
|
||||
}
|
||||
/**
|
||||
* Sets daylight savings end time.
|
||||
* @param {number | null} value
|
||||
*/
|
||||
set dstEnd(value) {
|
||||
this.#dstEnd = value;
|
||||
}
|
||||
/**
|
||||
* Adds one year to the current CronDate.
|
||||
*/
|
||||
addYear() {
|
||||
this.#date = this.#date.plus({ years: 1 });
|
||||
}
|
||||
/**
|
||||
* Adds one month to the current CronDate.
|
||||
*/
|
||||
addMonth() {
|
||||
this.#date = this.#date.plus({ months: 1 }).startOf('month');
|
||||
}
|
||||
/**
|
||||
* Adds one day to the current CronDate.
|
||||
*/
|
||||
addDay() {
|
||||
this.#date = this.#date.plus({ days: 1 }).startOf('day');
|
||||
}
|
||||
/**
|
||||
* Adds one hour to the current CronDate.
|
||||
*/
|
||||
addHour() {
|
||||
this.#date = this.#date.plus({ hours: 1 }).startOf('hour');
|
||||
}
|
||||
/**
|
||||
* Adds one minute to the current CronDate.
|
||||
*/
|
||||
addMinute() {
|
||||
this.#date = this.#date.plus({ minutes: 1 }).startOf('minute');
|
||||
}
|
||||
/**
|
||||
* Adds one second to the current CronDate.
|
||||
*/
|
||||
addSecond() {
|
||||
this.#date = this.#date.plus({ seconds: 1 });
|
||||
}
|
||||
/**
|
||||
* Subtracts one year from the current CronDate.
|
||||
*/
|
||||
subtractYear() {
|
||||
this.#date = this.#date.minus({ years: 1 });
|
||||
}
|
||||
/**
|
||||
* Subtracts one month from the current CronDate.
|
||||
* If the month is 1, it will subtract one year instead.
|
||||
*/
|
||||
subtractMonth() {
|
||||
this.#date = this.#date.minus({ months: 1 }).endOf('month').startOf('second');
|
||||
}
|
||||
/**
|
||||
* Subtracts one day from the current CronDate.
|
||||
* If the day is 1, it will subtract one month instead.
|
||||
*/
|
||||
subtractDay() {
|
||||
this.#date = this.#date.minus({ days: 1 }).endOf('day').startOf('second');
|
||||
}
|
||||
/**
|
||||
* Subtracts one hour from the current CronDate.
|
||||
* If the hour is 0, it will subtract one day instead.
|
||||
*/
|
||||
subtractHour() {
|
||||
this.#date = this.#date.minus({ hours: 1 }).endOf('hour').startOf('second');
|
||||
}
|
||||
/**
|
||||
* Subtracts one minute from the current CronDate.
|
||||
* If the minute is 0, it will subtract one hour instead.
|
||||
*/
|
||||
subtractMinute() {
|
||||
this.#date = this.#date.minus({ minutes: 1 }).endOf('minute').startOf('second');
|
||||
}
|
||||
/**
|
||||
* Subtracts one second from the current CronDate.
|
||||
* If the second is 0, it will subtract one minute instead.
|
||||
*/
|
||||
subtractSecond() {
|
||||
this.#date = this.#date.minus({ seconds: 1 });
|
||||
}
|
||||
/**
|
||||
* Adds a unit of time to the current CronDate.
|
||||
* @param {TimeUnit} unit
|
||||
*/
|
||||
addUnit(unit) {
|
||||
this.#verbMap.add[unit]();
|
||||
}
|
||||
/**
|
||||
* Subtracts a unit of time from the current CronDate.
|
||||
* @param {TimeUnit} unit
|
||||
*/
|
||||
subtractUnit(unit) {
|
||||
this.#verbMap.subtract[unit]();
|
||||
}
|
||||
/**
|
||||
* Handles a math operation.
|
||||
* @param {DateMathOp} verb - {'add' | 'subtract'}
|
||||
* @param {TimeUnit} unit - {'year' | 'month' | 'day' | 'hour' | 'minute' | 'second'}
|
||||
*/
|
||||
invokeDateOperation(verb, unit) {
|
||||
if (verb === DateMathOp.Add) {
|
||||
this.addUnit(unit);
|
||||
return;
|
||||
}
|
||||
if (verb === DateMathOp.Subtract) {
|
||||
this.subtractUnit(unit);
|
||||
return;
|
||||
}
|
||||
/* istanbul ignore next - this would only happen if an end user call the handleMathOp with an invalid verb */
|
||||
throw new Error(`Invalid verb: ${verb}`);
|
||||
}
|
||||
/**
|
||||
* Returns the day.
|
||||
* @returns {number}
|
||||
*/
|
||||
getDate() {
|
||||
return this.#date.day;
|
||||
}
|
||||
/**
|
||||
* Returns the year.
|
||||
* @returns {number}
|
||||
*/
|
||||
getFullYear() {
|
||||
return this.#date.year;
|
||||
}
|
||||
/**
|
||||
* Returns the day of the week.
|
||||
* @returns {number}
|
||||
*/
|
||||
getDay() {
|
||||
const weekday = this.#date.weekday;
|
||||
return weekday === 7 ? 0 : weekday;
|
||||
}
|
||||
/**
|
||||
* Returns the month.
|
||||
* @returns {number}
|
||||
*/
|
||||
getMonth() {
|
||||
return this.#date.month - 1;
|
||||
}
|
||||
/**
|
||||
* Returns the hour.
|
||||
* @returns {number}
|
||||
*/
|
||||
getHours() {
|
||||
return this.#date.hour;
|
||||
}
|
||||
/**
|
||||
* Returns the minutes.
|
||||
* @returns {number}
|
||||
*/
|
||||
getMinutes() {
|
||||
return this.#date.minute;
|
||||
}
|
||||
/**
|
||||
* Returns the seconds.
|
||||
* @returns {number}
|
||||
*/
|
||||
getSeconds() {
|
||||
return this.#date.second;
|
||||
}
|
||||
/**
|
||||
* Returns the milliseconds.
|
||||
* @returns {number}
|
||||
*/
|
||||
getMilliseconds() {
|
||||
return this.#date.millisecond;
|
||||
}
|
||||
/**
|
||||
* Returns the time.
|
||||
* @returns {number}
|
||||
*/
|
||||
getTime() {
|
||||
return this.#date.valueOf();
|
||||
}
|
||||
/**
|
||||
* Returns the UTC day.
|
||||
* @returns {number}
|
||||
*/
|
||||
getUTCDate() {
|
||||
return this.#getUTC().day;
|
||||
}
|
||||
/**
|
||||
* Returns the UTC year.
|
||||
* @returns {number}
|
||||
*/
|
||||
getUTCFullYear() {
|
||||
return this.#getUTC().year;
|
||||
}
|
||||
/**
|
||||
* Returns the UTC day of the week.
|
||||
* @returns {number}
|
||||
*/
|
||||
getUTCDay() {
|
||||
const weekday = this.#getUTC().weekday;
|
||||
return weekday === 7 ? 0 : weekday;
|
||||
}
|
||||
/**
|
||||
* Returns the UTC month.
|
||||
* @returns {number}
|
||||
*/
|
||||
getUTCMonth() {
|
||||
return this.#getUTC().month - 1;
|
||||
}
|
||||
/**
|
||||
* Returns the UTC hour.
|
||||
* @returns {number}
|
||||
*/
|
||||
getUTCHours() {
|
||||
return this.#getUTC().hour;
|
||||
}
|
||||
/**
|
||||
* Returns the UTC minutes.
|
||||
* @returns {number}
|
||||
*/
|
||||
getUTCMinutes() {
|
||||
return this.#getUTC().minute;
|
||||
}
|
||||
/**
|
||||
* Returns the UTC seconds.
|
||||
* @returns {number}
|
||||
*/
|
||||
getUTCSeconds() {
|
||||
return this.#getUTC().second;
|
||||
}
|
||||
/**
|
||||
* Returns the UTC milliseconds.
|
||||
* @returns {string | null}
|
||||
*/
|
||||
toISOString() {
|
||||
return this.#date.toUTC().toISO();
|
||||
}
|
||||
/**
|
||||
* Returns the date as a JSON string.
|
||||
* @returns {string | null}
|
||||
*/
|
||||
toJSON() {
|
||||
return this.#date.toJSON();
|
||||
}
|
||||
/**
|
||||
* Sets the day.
|
||||
* @param d
|
||||
*/
|
||||
setDate(d) {
|
||||
this.#date = this.#date.set({ day: d });
|
||||
}
|
||||
/**
|
||||
* Sets the year.
|
||||
* @param y
|
||||
*/
|
||||
setFullYear(y) {
|
||||
this.#date = this.#date.set({ year: y });
|
||||
}
|
||||
/**
|
||||
* Sets the day of the week.
|
||||
* @param d
|
||||
*/
|
||||
setDay(d) {
|
||||
this.#date = this.#date.set({ weekday: d });
|
||||
}
|
||||
/**
|
||||
* Sets the month.
|
||||
* @param m
|
||||
*/
|
||||
setMonth(m) {
|
||||
this.#date = this.#date.set({ month: m + 1 });
|
||||
}
|
||||
/**
|
||||
* Sets the hour.
|
||||
* @param h
|
||||
*/
|
||||
setHours(h) {
|
||||
this.#date = this.#date.set({ hour: h });
|
||||
}
|
||||
/**
|
||||
* Sets the minutes.
|
||||
* @param m
|
||||
*/
|
||||
setMinutes(m) {
|
||||
this.#date = this.#date.set({ minute: m });
|
||||
}
|
||||
/**
|
||||
* Sets the seconds.
|
||||
* @param s
|
||||
*/
|
||||
setSeconds(s) {
|
||||
this.#date = this.#date.set({ second: s });
|
||||
}
|
||||
/**
|
||||
* Sets the milliseconds.
|
||||
* @param s
|
||||
*/
|
||||
setMilliseconds(s) {
|
||||
this.#date = this.#date.set({ millisecond: s });
|
||||
}
|
||||
/**
|
||||
* Returns the date as a string.
|
||||
* @returns {string}
|
||||
*/
|
||||
toString() {
|
||||
return this.toDate().toString();
|
||||
}
|
||||
/**
|
||||
* Returns the date as a Date object.
|
||||
* @returns {Date}
|
||||
*/
|
||||
toDate() {
|
||||
return this.#date.toJSDate();
|
||||
}
|
||||
/**
|
||||
* Returns true if the day is the last day of the month.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
isLastDayOfMonth() {
|
||||
const { day, month } = this.#date;
|
||||
// Special handling for February in leap years
|
||||
if (month === 2) {
|
||||
const isLeap = CronDate.#isLeapYear(this.#date.year);
|
||||
return day === exports.DAYS_IN_MONTH[month - 1] - (isLeap ? 0 : 1);
|
||||
}
|
||||
// For other months, check against the static map
|
||||
return day === exports.DAYS_IN_MONTH[month - 1];
|
||||
}
|
||||
/**
|
||||
* Returns true if the day is the last weekday of the month.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
isLastWeekdayOfMonth() {
|
||||
const { day, month } = this.#date;
|
||||
// Get the last day of the current month
|
||||
let lastDay;
|
||||
if (month === 2) {
|
||||
// Special handling for February
|
||||
lastDay = exports.DAYS_IN_MONTH[month - 1] - (CronDate.#isLeapYear(this.#date.year) ? 0 : 1);
|
||||
}
|
||||
else {
|
||||
lastDay = exports.DAYS_IN_MONTH[month - 1];
|
||||
}
|
||||
// Check if the current day is within 7 days of the end of the month
|
||||
return day > lastDay - 7;
|
||||
}
|
||||
/**
|
||||
* Primarily for internal use.
|
||||
* @param {DateMathOp} op - The operation to perform.
|
||||
* @param {TimeUnit} unit - The unit of time to use.
|
||||
* @param {number} [hoursLength] - The length of the hours. Required when unit is not month or day.
|
||||
*/
|
||||
applyDateOperation(op, unit, hoursLength) {
|
||||
if (unit === TimeUnit.Month || unit === TimeUnit.Day) {
|
||||
this.invokeDateOperation(op, unit);
|
||||
return;
|
||||
}
|
||||
const previousHour = this.getHours();
|
||||
this.invokeDateOperation(op, unit);
|
||||
const currentHour = this.getHours();
|
||||
const diff = currentHour - previousHour;
|
||||
if (diff === 2) {
|
||||
if (hoursLength !== 24) {
|
||||
this.dstStart = currentHour;
|
||||
}
|
||||
}
|
||||
else if (diff === 0 && this.getMinutes() === 0 && this.getSeconds() === 0) {
|
||||
if (hoursLength !== 24) {
|
||||
this.dstEnd = currentHour;
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Returns the UTC date.
|
||||
* @private
|
||||
* @returns {DateTime}
|
||||
*/
|
||||
#getUTC() {
|
||||
return this.#date.toUTC();
|
||||
}
|
||||
}
|
||||
exports.CronDate = CronDate;
|
||||
exports.default = CronDate;
|
||||
407
node_modules/cron-parser/dist/CronExpression.js
generated
vendored
Normal file
407
node_modules/cron-parser/dist/CronExpression.js
generated
vendored
Normal file
@@ -0,0 +1,407 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.CronExpression = exports.LOOPS_LIMIT_EXCEEDED_ERROR_MESSAGE = exports.TIME_SPAN_OUT_OF_BOUNDS_ERROR_MESSAGE = void 0;
|
||||
const CronDate_1 = require("./CronDate");
|
||||
/**
|
||||
* Error message for when the current date is outside the specified time span.
|
||||
*/
|
||||
exports.TIME_SPAN_OUT_OF_BOUNDS_ERROR_MESSAGE = 'Out of the time span range';
|
||||
/**
|
||||
* Error message for when the loop limit is exceeded during iteration.
|
||||
*/
|
||||
exports.LOOPS_LIMIT_EXCEEDED_ERROR_MESSAGE = 'Invalid expression, loop limit exceeded';
|
||||
/**
|
||||
* Cron iteration loop safety limit
|
||||
*/
|
||||
const LOOP_LIMIT = 10000;
|
||||
/**
|
||||
* Class representing a Cron expression.
|
||||
*/
|
||||
class CronExpression {
|
||||
#options;
|
||||
#tz;
|
||||
#currentDate;
|
||||
#startDate;
|
||||
#endDate;
|
||||
#fields;
|
||||
/**
|
||||
* Creates a new CronExpression instance.
|
||||
*
|
||||
* @param {CronFieldCollection} fields - Cron fields.
|
||||
* @param {CronExpressionOptions} options - Parser options.
|
||||
*/
|
||||
constructor(fields, options) {
|
||||
this.#options = options;
|
||||
this.#tz = options.tz;
|
||||
this.#startDate = options.startDate ? new CronDate_1.CronDate(options.startDate, this.#tz) : null;
|
||||
this.#endDate = options.endDate ? new CronDate_1.CronDate(options.endDate, this.#tz) : null;
|
||||
let currentDateValue = options.currentDate ?? options.startDate;
|
||||
if (currentDateValue) {
|
||||
const tempCurrentDate = new CronDate_1.CronDate(currentDateValue, this.#tz);
|
||||
if (this.#startDate && tempCurrentDate.getTime() < this.#startDate.getTime()) {
|
||||
currentDateValue = this.#startDate;
|
||||
}
|
||||
else if (this.#endDate && tempCurrentDate.getTime() > this.#endDate.getTime()) {
|
||||
currentDateValue = this.#endDate;
|
||||
}
|
||||
}
|
||||
this.#currentDate = new CronDate_1.CronDate(currentDateValue, this.#tz);
|
||||
this.#fields = fields;
|
||||
}
|
||||
/**
|
||||
* Getter for the cron fields.
|
||||
*
|
||||
* @returns {CronFieldCollection} Cron fields.
|
||||
*/
|
||||
get fields() {
|
||||
return this.#fields;
|
||||
}
|
||||
/**
|
||||
* Converts cron fields back to a CronExpression instance.
|
||||
*
|
||||
* @public
|
||||
* @param {Record<string, number[]>} fields - The input cron fields object.
|
||||
* @param {CronExpressionOptions} [options] - Optional parsing options.
|
||||
* @returns {CronExpression} - A new CronExpression instance.
|
||||
*/
|
||||
static fieldsToExpression(fields, options) {
|
||||
return new CronExpression(fields, options || {});
|
||||
}
|
||||
/**
|
||||
* Checks if the given value matches any element in the sequence.
|
||||
*
|
||||
* @param {number} value - The value to be matched.
|
||||
* @param {number[]} sequence - The sequence to be checked against.
|
||||
* @returns {boolean} - True if the value matches an element in the sequence; otherwise, false.
|
||||
* @memberof CronExpression
|
||||
* @private
|
||||
*/
|
||||
static #matchSchedule(value, sequence) {
|
||||
return sequence.some((element) => element === value);
|
||||
}
|
||||
/**
|
||||
* Determines if the current date matches the last specified weekday of the month.
|
||||
*
|
||||
* @param {Array<(number|string)>} expressions - An array of expressions containing weekdays and "L" for the last weekday.
|
||||
* @param {CronDate} currentDate - The current date object.
|
||||
* @returns {boolean} - True if the current date matches the last specified weekday of the month; otherwise, false.
|
||||
* @memberof CronExpression
|
||||
* @private
|
||||
*/
|
||||
static #isLastWeekdayOfMonthMatch(expressions, currentDate) {
|
||||
const isLastWeekdayOfMonth = currentDate.isLastWeekdayOfMonth();
|
||||
return expressions.some((expression) => {
|
||||
// The first character represents the weekday
|
||||
const weekday = parseInt(expression.toString().charAt(0), 10) % 7;
|
||||
if (Number.isNaN(weekday)) {
|
||||
throw new Error(`Invalid last weekday of the month expression: ${expression}`);
|
||||
}
|
||||
// Check if the current date matches the last specified weekday of the month
|
||||
return currentDate.getDay() === weekday && isLastWeekdayOfMonth;
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Find the next scheduled date based on the cron expression.
|
||||
* @returns {CronDate} - The next scheduled date or an ES6 compatible iterator object.
|
||||
* @memberof CronExpression
|
||||
* @public
|
||||
*/
|
||||
next() {
|
||||
return this.#findSchedule();
|
||||
}
|
||||
/**
|
||||
* Find the previous scheduled date based on the cron expression.
|
||||
* @returns {CronDate} - The previous scheduled date or an ES6 compatible iterator object.
|
||||
* @memberof CronExpression
|
||||
* @public
|
||||
*/
|
||||
prev() {
|
||||
return this.#findSchedule(true);
|
||||
}
|
||||
/**
|
||||
* Check if there is a next scheduled date based on the current date and cron expression.
|
||||
* @returns {boolean} - Returns true if there is a next scheduled date, false otherwise.
|
||||
* @memberof CronExpression
|
||||
* @public
|
||||
*/
|
||||
hasNext() {
|
||||
const current = this.#currentDate;
|
||||
try {
|
||||
this.#findSchedule();
|
||||
return true;
|
||||
}
|
||||
catch {
|
||||
return false;
|
||||
}
|
||||
finally {
|
||||
this.#currentDate = current;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Check if there is a previous scheduled date based on the current date and cron expression.
|
||||
* @returns {boolean} - Returns true if there is a previous scheduled date, false otherwise.
|
||||
* @memberof CronExpression
|
||||
* @public
|
||||
*/
|
||||
hasPrev() {
|
||||
const current = this.#currentDate;
|
||||
try {
|
||||
this.#findSchedule(true);
|
||||
return true;
|
||||
}
|
||||
catch {
|
||||
return false;
|
||||
}
|
||||
finally {
|
||||
this.#currentDate = current;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Iterate over a specified number of steps and optionally execute a callback function for each step.
|
||||
* @param {number} steps - The number of steps to iterate. Positive value iterates forward, negative value iterates backward.
|
||||
* @returns {CronDate[]} - An array of iterator fields or CronDate objects.
|
||||
* @memberof CronExpression
|
||||
* @public
|
||||
*/
|
||||
take(limit) {
|
||||
const items = [];
|
||||
if (limit >= 0) {
|
||||
for (let i = 0; i < limit; i++) {
|
||||
try {
|
||||
items.push(this.next());
|
||||
}
|
||||
catch {
|
||||
return items;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (let i = 0; i > limit; i--) {
|
||||
try {
|
||||
items.push(this.prev());
|
||||
}
|
||||
catch {
|
||||
return items;
|
||||
}
|
||||
}
|
||||
}
|
||||
return items;
|
||||
}
|
||||
/**
|
||||
* Reset the iterators current date to a new date or the initial date.
|
||||
* @param {Date | CronDate} [newDate] - Optional new date to reset to. If not provided, it will reset to the initial date.
|
||||
* @memberof CronExpression
|
||||
* @public
|
||||
*/
|
||||
reset(newDate) {
|
||||
this.#currentDate = new CronDate_1.CronDate(newDate || this.#options.currentDate);
|
||||
}
|
||||
/**
|
||||
* Generate a string representation of the cron expression.
|
||||
* @param {boolean} [includeSeconds=false] - Whether to include the seconds field in the string representation.
|
||||
* @returns {string} - The string representation of the cron expression.
|
||||
* @memberof CronExpression
|
||||
* @public
|
||||
*/
|
||||
stringify(includeSeconds = false) {
|
||||
return this.#fields.stringify(includeSeconds);
|
||||
}
|
||||
/**
|
||||
* Check if the cron expression includes the given date
|
||||
* @param {Date|CronDate} date
|
||||
* @returns {boolean}
|
||||
*/
|
||||
includesDate(date) {
|
||||
const { second, minute, hour, month } = this.#fields;
|
||||
const dt = new CronDate_1.CronDate(date, this.#tz);
|
||||
// Check basic time fields first
|
||||
if (!second.values.includes(dt.getSeconds()) ||
|
||||
!minute.values.includes(dt.getMinutes()) ||
|
||||
!hour.values.includes(dt.getHours()) ||
|
||||
!month.values.includes((dt.getMonth() + 1))) {
|
||||
return false;
|
||||
}
|
||||
// Check day of month and day of week using the same logic as #findSchedule
|
||||
if (!this.#matchDayOfMonth(dt)) {
|
||||
return false;
|
||||
}
|
||||
// Check nth day of week if specified
|
||||
if (this.#fields.dayOfWeek.nthDay > 0) {
|
||||
const weekInMonth = Math.ceil(dt.getDate() / 7);
|
||||
if (weekInMonth !== this.#fields.dayOfWeek.nthDay) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* Returns the string representation of the cron expression.
|
||||
* @returns {CronDate} - The next schedule date.
|
||||
*/
|
||||
toString() {
|
||||
/* istanbul ignore next - should be impossible under normal use to trigger the or branch */
|
||||
return this.#options.expression || this.stringify(true);
|
||||
}
|
||||
/**
|
||||
* Determines if the given date matches the cron expression's day of month and day of week fields.
|
||||
*
|
||||
* The function checks the following rules:
|
||||
* Rule 1: If both "day of month" and "day of week" are restricted (not wildcard), then one or both must match the current day.
|
||||
* Rule 2: If "day of month" is restricted and "day of week" is not restricted, then "day of month" must match the current day.
|
||||
* Rule 3: If "day of month" is a wildcard, "day of week" is not a wildcard, and "day of week" matches the current day, then the match is accepted.
|
||||
* If none of the rules match, the match is rejected.
|
||||
*
|
||||
* @param {CronDate} currentDate - The current date to be evaluated against the cron expression.
|
||||
* @returns {boolean} Returns true if the current date matches the cron expression's day of month and day of week fields, otherwise false.
|
||||
* @memberof CronExpression
|
||||
* @private
|
||||
*/
|
||||
#matchDayOfMonth(currentDate) {
|
||||
// Check if day of month and day of week fields are wildcards or restricted (not wildcard).
|
||||
const isDayOfMonthWildcardMatch = this.#fields.dayOfMonth.isWildcard;
|
||||
const isRestrictedDayOfMonth = !isDayOfMonthWildcardMatch;
|
||||
const isDayOfWeekWildcardMatch = this.#fields.dayOfWeek.isWildcard;
|
||||
const isRestrictedDayOfWeek = !isDayOfWeekWildcardMatch;
|
||||
// Calculate if the current date matches the day of month and day of week fields.
|
||||
const matchedDOM = CronExpression.#matchSchedule(currentDate.getDate(), this.#fields.dayOfMonth.values) ||
|
||||
(this.#fields.dayOfMonth.hasLastChar && currentDate.isLastDayOfMonth());
|
||||
const matchedDOW = CronExpression.#matchSchedule(currentDate.getDay(), this.#fields.dayOfWeek.values) ||
|
||||
(this.#fields.dayOfWeek.hasLastChar &&
|
||||
CronExpression.#isLastWeekdayOfMonthMatch(this.#fields.dayOfWeek.values, currentDate));
|
||||
// Rule 1: Both "day of month" and "day of week" are restricted; one or both must match the current day.
|
||||
if (isRestrictedDayOfMonth && isRestrictedDayOfWeek && (matchedDOM || matchedDOW)) {
|
||||
return true;
|
||||
}
|
||||
// Rule 2: "day of month" restricted and "day of week" not restricted; "day of month" must match the current day.
|
||||
if (matchedDOM && !isRestrictedDayOfWeek) {
|
||||
return true;
|
||||
}
|
||||
// Rule 3: "day of month" is a wildcard, "day of week" is not a wildcard, and "day of week" matches the current day.
|
||||
if (isDayOfMonthWildcardMatch && !isDayOfWeekWildcardMatch && matchedDOW) {
|
||||
return true;
|
||||
}
|
||||
// If none of the rules match, the match is rejected.
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* Determines if the current hour matches the cron expression.
|
||||
*
|
||||
* @param {CronDate} currentDate - The current date object.
|
||||
* @param {DateMathOp} dateMathVerb - The date math operation enumeration value.
|
||||
* @param {boolean} reverse - A flag indicating whether the matching should be done in reverse order.
|
||||
* @returns {boolean} - True if the current hour matches the cron expression; otherwise, false.
|
||||
*/
|
||||
#matchHour(currentDate, dateMathVerb, reverse) {
|
||||
const currentHour = currentDate.getHours();
|
||||
const isMatch = CronExpression.#matchSchedule(currentHour, this.#fields.hour.values);
|
||||
const isDstStart = currentDate.dstStart === currentHour;
|
||||
const isDstEnd = currentDate.dstEnd === currentHour;
|
||||
if (!isMatch && !isDstStart) {
|
||||
currentDate.dstStart = null;
|
||||
currentDate.applyDateOperation(dateMathVerb, CronDate_1.TimeUnit.Hour, this.#fields.hour.values.length);
|
||||
return false;
|
||||
}
|
||||
if (isDstStart && !CronExpression.#matchSchedule(currentHour - 1, this.#fields.hour.values)) {
|
||||
currentDate.invokeDateOperation(dateMathVerb, CronDate_1.TimeUnit.Hour);
|
||||
return false;
|
||||
}
|
||||
if (isDstEnd && !reverse) {
|
||||
currentDate.dstEnd = null;
|
||||
currentDate.applyDateOperation(CronDate_1.DateMathOp.Add, CronDate_1.TimeUnit.Hour, this.#fields.hour.values.length);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* Validates the current date against the start and end dates of the cron expression.
|
||||
* If the current date is outside the specified time span, an error is thrown.
|
||||
*
|
||||
* @param currentDate {CronDate} - The current date to validate.
|
||||
* @throws {Error} If the current date is outside the specified time span.
|
||||
* @private
|
||||
*/
|
||||
#validateTimeSpan(currentDate) {
|
||||
if (!this.#startDate && !this.#endDate) {
|
||||
return;
|
||||
}
|
||||
const currentTime = currentDate.getTime();
|
||||
if (this.#startDate && currentTime < this.#startDate.getTime()) {
|
||||
throw new Error(exports.TIME_SPAN_OUT_OF_BOUNDS_ERROR_MESSAGE);
|
||||
}
|
||||
if (this.#endDate && currentTime > this.#endDate.getTime()) {
|
||||
throw new Error(exports.TIME_SPAN_OUT_OF_BOUNDS_ERROR_MESSAGE);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Finds the next or previous schedule based on the cron expression.
|
||||
*
|
||||
* @param {boolean} [reverse=false] - If true, finds the previous schedule; otherwise, finds the next schedule.
|
||||
* @returns {CronDate} - The next or previous schedule date.
|
||||
* @private
|
||||
*/
|
||||
#findSchedule(reverse = false) {
|
||||
const dateMathVerb = reverse ? CronDate_1.DateMathOp.Subtract : CronDate_1.DateMathOp.Add;
|
||||
const currentDate = new CronDate_1.CronDate(this.#currentDate);
|
||||
const startTimestamp = currentDate.getTime();
|
||||
let stepCount = 0;
|
||||
while (++stepCount < LOOP_LIMIT) {
|
||||
this.#validateTimeSpan(currentDate);
|
||||
if (!this.#matchDayOfMonth(currentDate)) {
|
||||
currentDate.applyDateOperation(dateMathVerb, CronDate_1.TimeUnit.Day, this.#fields.hour.values.length);
|
||||
continue;
|
||||
}
|
||||
if (!(this.#fields.dayOfWeek.nthDay <= 0 || Math.ceil(currentDate.getDate() / 7) === this.#fields.dayOfWeek.nthDay)) {
|
||||
currentDate.applyDateOperation(dateMathVerb, CronDate_1.TimeUnit.Day, this.#fields.hour.values.length);
|
||||
continue;
|
||||
}
|
||||
if (!CronExpression.#matchSchedule(currentDate.getMonth() + 1, this.#fields.month.values)) {
|
||||
currentDate.applyDateOperation(dateMathVerb, CronDate_1.TimeUnit.Month, this.#fields.hour.values.length);
|
||||
continue;
|
||||
}
|
||||
if (!this.#matchHour(currentDate, dateMathVerb, reverse)) {
|
||||
continue;
|
||||
}
|
||||
if (!CronExpression.#matchSchedule(currentDate.getMinutes(), this.#fields.minute.values)) {
|
||||
currentDate.applyDateOperation(dateMathVerb, CronDate_1.TimeUnit.Minute, this.#fields.hour.values.length);
|
||||
continue;
|
||||
}
|
||||
if (!CronExpression.#matchSchedule(currentDate.getSeconds(), this.#fields.second.values)) {
|
||||
currentDate.applyDateOperation(dateMathVerb, CronDate_1.TimeUnit.Second, this.#fields.hour.values.length);
|
||||
continue;
|
||||
}
|
||||
if (startTimestamp === currentDate.getTime()) {
|
||||
if (dateMathVerb === 'Add' || currentDate.getMilliseconds() === 0) {
|
||||
currentDate.applyDateOperation(dateMathVerb, CronDate_1.TimeUnit.Second, this.#fields.hour.values.length);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
/* istanbul ignore next - should be impossible under normal use to trigger the branch */
|
||||
if (stepCount > LOOP_LIMIT) {
|
||||
throw new Error(exports.LOOPS_LIMIT_EXCEEDED_ERROR_MESSAGE);
|
||||
}
|
||||
if (currentDate.getMilliseconds() !== 0) {
|
||||
currentDate.setMilliseconds(0);
|
||||
}
|
||||
this.#currentDate = currentDate;
|
||||
return currentDate;
|
||||
}
|
||||
/**
|
||||
* Returns an iterator for iterating through future CronDate instances
|
||||
*
|
||||
* @name Symbol.iterator
|
||||
* @memberof CronExpression
|
||||
* @returns {Iterator<CronDate>} An iterator object for CronExpression that returns CronDate values.
|
||||
*/
|
||||
[Symbol.iterator]() {
|
||||
return {
|
||||
next: () => {
|
||||
const schedule = this.#findSchedule();
|
||||
return { value: schedule, done: !this.hasNext() };
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
exports.CronExpression = CronExpression;
|
||||
exports.default = CronExpression;
|
||||
382
node_modules/cron-parser/dist/CronExpressionParser.js
generated
vendored
Normal file
382
node_modules/cron-parser/dist/CronExpressionParser.js
generated
vendored
Normal file
@@ -0,0 +1,382 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.CronExpressionParser = exports.DayOfWeek = exports.Months = exports.CronUnit = exports.PredefinedExpressions = void 0;
|
||||
const CronFieldCollection_1 = require("./CronFieldCollection");
|
||||
const CronExpression_1 = require("./CronExpression");
|
||||
const random_1 = require("./utils/random");
|
||||
const fields_1 = require("./fields");
|
||||
var PredefinedExpressions;
|
||||
(function (PredefinedExpressions) {
|
||||
PredefinedExpressions["@yearly"] = "0 0 0 1 1 *";
|
||||
PredefinedExpressions["@annually"] = "0 0 0 1 1 *";
|
||||
PredefinedExpressions["@monthly"] = "0 0 0 1 * *";
|
||||
PredefinedExpressions["@weekly"] = "0 0 0 * * 0";
|
||||
PredefinedExpressions["@daily"] = "0 0 0 * * *";
|
||||
PredefinedExpressions["@hourly"] = "0 0 * * * *";
|
||||
PredefinedExpressions["@minutely"] = "0 * * * * *";
|
||||
PredefinedExpressions["@secondly"] = "* * * * * *";
|
||||
PredefinedExpressions["@weekdays"] = "0 0 0 * * 1-5";
|
||||
PredefinedExpressions["@weekends"] = "0 0 0 * * 0,6";
|
||||
})(PredefinedExpressions || (exports.PredefinedExpressions = PredefinedExpressions = {}));
|
||||
var CronUnit;
|
||||
(function (CronUnit) {
|
||||
CronUnit["Second"] = "Second";
|
||||
CronUnit["Minute"] = "Minute";
|
||||
CronUnit["Hour"] = "Hour";
|
||||
CronUnit["DayOfMonth"] = "DayOfMonth";
|
||||
CronUnit["Month"] = "Month";
|
||||
CronUnit["DayOfWeek"] = "DayOfWeek";
|
||||
})(CronUnit || (exports.CronUnit = CronUnit = {}));
|
||||
// these need to be lowercase for the parser to work
|
||||
var Months;
|
||||
(function (Months) {
|
||||
Months[Months["jan"] = 1] = "jan";
|
||||
Months[Months["feb"] = 2] = "feb";
|
||||
Months[Months["mar"] = 3] = "mar";
|
||||
Months[Months["apr"] = 4] = "apr";
|
||||
Months[Months["may"] = 5] = "may";
|
||||
Months[Months["jun"] = 6] = "jun";
|
||||
Months[Months["jul"] = 7] = "jul";
|
||||
Months[Months["aug"] = 8] = "aug";
|
||||
Months[Months["sep"] = 9] = "sep";
|
||||
Months[Months["oct"] = 10] = "oct";
|
||||
Months[Months["nov"] = 11] = "nov";
|
||||
Months[Months["dec"] = 12] = "dec";
|
||||
})(Months || (exports.Months = Months = {}));
|
||||
// these need to be lowercase for the parser to work
|
||||
var DayOfWeek;
|
||||
(function (DayOfWeek) {
|
||||
DayOfWeek[DayOfWeek["sun"] = 0] = "sun";
|
||||
DayOfWeek[DayOfWeek["mon"] = 1] = "mon";
|
||||
DayOfWeek[DayOfWeek["tue"] = 2] = "tue";
|
||||
DayOfWeek[DayOfWeek["wed"] = 3] = "wed";
|
||||
DayOfWeek[DayOfWeek["thu"] = 4] = "thu";
|
||||
DayOfWeek[DayOfWeek["fri"] = 5] = "fri";
|
||||
DayOfWeek[DayOfWeek["sat"] = 6] = "sat";
|
||||
})(DayOfWeek || (exports.DayOfWeek = DayOfWeek = {}));
|
||||
/**
|
||||
* Static class that parses a cron expression and returns a CronExpression object.
|
||||
* @static
|
||||
* @class CronExpressionParser
|
||||
*/
|
||||
class CronExpressionParser {
|
||||
/**
|
||||
* Parses a cron expression and returns a CronExpression object.
|
||||
* @param {string} expression - The cron expression to parse.
|
||||
* @param {CronExpressionOptions} [options={}] - The options to use when parsing the expression.
|
||||
* @param {boolean} [options.strict=false] - If true, will throw an error if the expression contains both dayOfMonth and dayOfWeek.
|
||||
* @param {CronDate} [options.currentDate=new CronDate(undefined, 'UTC')] - The date to use when calculating the next/previous occurrence.
|
||||
*
|
||||
* @returns {CronExpression} A CronExpression object.
|
||||
*/
|
||||
static parse(expression, options = {}) {
|
||||
const { strict = false, hashSeed } = options;
|
||||
const rand = (0, random_1.seededRandom)(hashSeed);
|
||||
expression = PredefinedExpressions[expression] || expression;
|
||||
const rawFields = CronExpressionParser.#getRawFields(expression, strict);
|
||||
if (!(rawFields.dayOfMonth === '*' || rawFields.dayOfWeek === '*' || !strict)) {
|
||||
throw new Error('Cannot use both dayOfMonth and dayOfWeek together in strict mode!');
|
||||
}
|
||||
const second = CronExpressionParser.#parseField(CronUnit.Second, rawFields.second, fields_1.CronSecond.constraints, rand);
|
||||
const minute = CronExpressionParser.#parseField(CronUnit.Minute, rawFields.minute, fields_1.CronMinute.constraints, rand);
|
||||
const hour = CronExpressionParser.#parseField(CronUnit.Hour, rawFields.hour, fields_1.CronHour.constraints, rand);
|
||||
const month = CronExpressionParser.#parseField(CronUnit.Month, rawFields.month, fields_1.CronMonth.constraints, rand);
|
||||
const dayOfMonth = CronExpressionParser.#parseField(CronUnit.DayOfMonth, rawFields.dayOfMonth, fields_1.CronDayOfMonth.constraints, rand);
|
||||
const { dayOfWeek: _dayOfWeek, nthDayOfWeek } = CronExpressionParser.#parseNthDay(rawFields.dayOfWeek);
|
||||
const dayOfWeek = CronExpressionParser.#parseField(CronUnit.DayOfWeek, _dayOfWeek, fields_1.CronDayOfWeek.constraints, rand);
|
||||
const fields = new CronFieldCollection_1.CronFieldCollection({
|
||||
second: new fields_1.CronSecond(second, { rawValue: rawFields.second }),
|
||||
minute: new fields_1.CronMinute(minute, { rawValue: rawFields.minute }),
|
||||
hour: new fields_1.CronHour(hour, { rawValue: rawFields.hour }),
|
||||
dayOfMonth: new fields_1.CronDayOfMonth(dayOfMonth, { rawValue: rawFields.dayOfMonth }),
|
||||
month: new fields_1.CronMonth(month, { rawValue: rawFields.month }),
|
||||
dayOfWeek: new fields_1.CronDayOfWeek(dayOfWeek, { rawValue: rawFields.dayOfWeek, nthDayOfWeek }),
|
||||
});
|
||||
return new CronExpression_1.CronExpression(fields, { ...options, expression });
|
||||
}
|
||||
/**
|
||||
* Get the raw fields from a cron expression.
|
||||
* @param {string} expression - The cron expression to parse.
|
||||
* @param {boolean} strict - If true, will throw an error if the expression contains both dayOfMonth and dayOfWeek.
|
||||
* @private
|
||||
* @returns {RawCronFields} The raw fields.
|
||||
*/
|
||||
static #getRawFields(expression, strict) {
|
||||
if (strict && !expression.length) {
|
||||
throw new Error('Invalid cron expression');
|
||||
}
|
||||
expression = expression || '0 * * * * *';
|
||||
const atoms = expression.trim().split(/\s+/);
|
||||
if (strict && atoms.length < 6) {
|
||||
throw new Error('Invalid cron expression, expected 6 fields');
|
||||
}
|
||||
if (atoms.length > 6) {
|
||||
throw new Error('Invalid cron expression, too many fields');
|
||||
}
|
||||
const defaults = ['*', '*', '*', '*', '*', '0'];
|
||||
if (atoms.length < defaults.length) {
|
||||
atoms.unshift(...defaults.slice(atoms.length));
|
||||
}
|
||||
const [second, minute, hour, dayOfMonth, month, dayOfWeek] = atoms;
|
||||
return { second, minute, hour, dayOfMonth, month, dayOfWeek };
|
||||
}
|
||||
/**
|
||||
* Parse a field from a cron expression.
|
||||
* @param {CronUnit} field - The field to parse.
|
||||
* @param {string} value - The value of the field.
|
||||
* @param {CronConstraints} constraints - The constraints for the field.
|
||||
* @private
|
||||
* @returns {(number | string)[]} The parsed field.
|
||||
*/
|
||||
static #parseField(field, value, constraints, rand) {
|
||||
// Replace aliases for month and dayOfWeek
|
||||
if (field === CronUnit.Month || field === CronUnit.DayOfWeek) {
|
||||
value = value.replace(/[a-z]{3}/gi, (match) => {
|
||||
match = match.toLowerCase();
|
||||
const replacer = Months[match] || DayOfWeek[match];
|
||||
if (replacer === undefined) {
|
||||
throw new Error(`Validation error, cannot resolve alias "${match}"`);
|
||||
}
|
||||
return replacer.toString();
|
||||
});
|
||||
}
|
||||
// Check for valid characters
|
||||
if (!constraints.validChars.test(value)) {
|
||||
throw new Error(`Invalid characters, got value: ${value}`);
|
||||
}
|
||||
value = this.#parseWildcard(value, constraints);
|
||||
value = this.#parseHashed(value, constraints, rand);
|
||||
return this.#parseSequence(field, value, constraints);
|
||||
}
|
||||
/**
|
||||
* Parse a wildcard from a cron expression.
|
||||
* @param {string} value - The value to parse.
|
||||
* @param {CronConstraints} constraints - The constraints for the field.
|
||||
* @private
|
||||
*/
|
||||
static #parseWildcard(value, constraints) {
|
||||
return value.replace(/[*?]/g, constraints.min + '-' + constraints.max);
|
||||
}
|
||||
/**
|
||||
* Parse a hashed value from a cron expression.
|
||||
* @param {string} value - The value to parse.
|
||||
* @param {CronConstraints} constraints - The constraints for the field.
|
||||
* @param {PRNG} rand - The random number generator to use.
|
||||
* @private
|
||||
*/
|
||||
static #parseHashed(value, constraints, rand) {
|
||||
const randomValue = rand();
|
||||
return value.replace(/H(?:\((\d+)-(\d+)\))?(?:\/(\d+))?/g, (_, min, max, step) => {
|
||||
// H(range)/step
|
||||
if (min && max && step) {
|
||||
const minNum = parseInt(min, 10);
|
||||
const maxNum = parseInt(max, 10);
|
||||
const stepNum = parseInt(step, 10);
|
||||
if (minNum > maxNum) {
|
||||
throw new Error(`Invalid range: ${minNum}-${maxNum}, min > max`);
|
||||
}
|
||||
if (stepNum <= 0) {
|
||||
throw new Error(`Invalid step: ${stepNum}, must be positive`);
|
||||
}
|
||||
const minStart = Math.max(minNum, constraints.min);
|
||||
const offset = Math.floor(randomValue * stepNum);
|
||||
const values = [];
|
||||
for (let i = Math.floor(minStart / stepNum) * stepNum + offset; i <= maxNum; i += stepNum) {
|
||||
if (i >= minStart) {
|
||||
values.push(i);
|
||||
}
|
||||
}
|
||||
return values.join(',');
|
||||
}
|
||||
// H(range)
|
||||
else if (min && max) {
|
||||
const minNum = parseInt(min, 10);
|
||||
const maxNum = parseInt(max, 10);
|
||||
if (minNum > maxNum) {
|
||||
throw new Error(`Invalid range: ${minNum}-${maxNum}, min > max`);
|
||||
}
|
||||
return String(Math.floor(randomValue * (maxNum - minNum + 1)) + minNum);
|
||||
}
|
||||
// H/step
|
||||
else if (step) {
|
||||
const stepNum = parseInt(step, 10);
|
||||
// Validate step
|
||||
if (stepNum <= 0) {
|
||||
throw new Error(`Invalid step: ${stepNum}, must be positive`);
|
||||
}
|
||||
const offset = Math.floor(randomValue * stepNum);
|
||||
const values = [];
|
||||
for (let i = Math.floor(constraints.min / stepNum) * stepNum + offset; i <= constraints.max; i += stepNum) {
|
||||
if (i >= constraints.min) {
|
||||
values.push(i);
|
||||
}
|
||||
}
|
||||
return values.join(',');
|
||||
}
|
||||
// H
|
||||
else {
|
||||
return String(Math.floor(randomValue * (constraints.max - constraints.min + 1) + constraints.min));
|
||||
}
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Parse a sequence from a cron expression.
|
||||
* @param {CronUnit} field - The field to parse.
|
||||
* @param {string} val - The sequence to parse.
|
||||
* @param {CronConstraints} constraints - The constraints for the field.
|
||||
* @private
|
||||
*/
|
||||
static #parseSequence(field, val, constraints) {
|
||||
const stack = [];
|
||||
function handleResult(result, constraints) {
|
||||
if (Array.isArray(result)) {
|
||||
stack.push(...result);
|
||||
}
|
||||
else {
|
||||
if (CronExpressionParser.#isValidConstraintChar(constraints, result)) {
|
||||
stack.push(result);
|
||||
}
|
||||
else {
|
||||
const v = parseInt(result.toString(), 10);
|
||||
const isValid = v >= constraints.min && v <= constraints.max;
|
||||
if (!isValid) {
|
||||
throw new Error(`Constraint error, got value ${result} expected range ${constraints.min}-${constraints.max}`);
|
||||
}
|
||||
stack.push(field === CronUnit.DayOfWeek ? v % 7 : result);
|
||||
}
|
||||
}
|
||||
}
|
||||
const atoms = val.split(',');
|
||||
atoms.forEach((atom) => {
|
||||
if (!(atom.length > 0)) {
|
||||
throw new Error('Invalid list value format');
|
||||
}
|
||||
handleResult(CronExpressionParser.#parseRepeat(field, atom, constraints), constraints);
|
||||
});
|
||||
return stack;
|
||||
}
|
||||
/**
|
||||
* Parse repeat from a cron expression.
|
||||
* @param {CronUnit} field - The field to parse.
|
||||
* @param {string} val - The repeat to parse.
|
||||
* @param {CronConstraints} constraints - The constraints for the field.
|
||||
* @private
|
||||
* @returns {(number | string)[]} The parsed repeat.
|
||||
*/
|
||||
static #parseRepeat(field, val, constraints) {
|
||||
const atoms = val.split('/');
|
||||
if (atoms.length > 2) {
|
||||
throw new Error(`Invalid repeat: ${val}`);
|
||||
}
|
||||
if (atoms.length === 2) {
|
||||
if (!isNaN(parseInt(atoms[0], 10))) {
|
||||
atoms[0] = `${atoms[0]}-${constraints.max}`;
|
||||
}
|
||||
return CronExpressionParser.#parseRange(field, atoms[0], parseInt(atoms[1], 10), constraints);
|
||||
}
|
||||
return CronExpressionParser.#parseRange(field, val, 1, constraints);
|
||||
}
|
||||
/**
|
||||
* Validate a cron range.
|
||||
* @param {number} min - The minimum value of the range.
|
||||
* @param {number} max - The maximum value of the range.
|
||||
* @param {CronConstraints} constraints - The constraints for the field.
|
||||
* @private
|
||||
* @returns {void}
|
||||
* @throws {Error} Throws an error if the range is invalid.
|
||||
*/
|
||||
static #validateRange(min, max, constraints) {
|
||||
const isValid = !isNaN(min) && !isNaN(max) && min >= constraints.min && max <= constraints.max;
|
||||
if (!isValid) {
|
||||
throw new Error(`Constraint error, got range ${min}-${max} expected range ${constraints.min}-${constraints.max}`);
|
||||
}
|
||||
if (min > max) {
|
||||
throw new Error(`Invalid range: ${min}-${max}, min(${min}) > max(${max})`);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Validate a cron repeat interval.
|
||||
* @param {number} repeatInterval - The repeat interval to validate.
|
||||
* @private
|
||||
* @returns {void}
|
||||
* @throws {Error} Throws an error if the repeat interval is invalid.
|
||||
*/
|
||||
static #validateRepeatInterval(repeatInterval) {
|
||||
if (!(!isNaN(repeatInterval) && repeatInterval > 0)) {
|
||||
throw new Error(`Constraint error, cannot repeat at every ${repeatInterval} time.`);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Create a range from a cron expression.
|
||||
* @param {CronUnit} field - The field to parse.
|
||||
* @param {number} min - The minimum value of the range.
|
||||
* @param {number} max - The maximum value of the range.
|
||||
* @param {number} repeatInterval - The repeat interval of the range.
|
||||
* @private
|
||||
* @returns {number[]} The created range.
|
||||
*/
|
||||
static #createRange(field, min, max, repeatInterval) {
|
||||
const stack = [];
|
||||
if (field === CronUnit.DayOfWeek && max % 7 === 0) {
|
||||
stack.push(0);
|
||||
}
|
||||
for (let index = min; index <= max; index += repeatInterval) {
|
||||
if (stack.indexOf(index) === -1) {
|
||||
stack.push(index);
|
||||
}
|
||||
}
|
||||
return stack;
|
||||
}
|
||||
/**
|
||||
* Parse a range from a cron expression.
|
||||
* @param {CronUnit} field - The field to parse.
|
||||
* @param {string} val - The range to parse.
|
||||
* @param {number} repeatInterval - The repeat interval of the range.
|
||||
* @param {CronConstraints} constraints - The constraints for the field.
|
||||
* @private
|
||||
* @returns {number[] | string[] | number | string} The parsed range.
|
||||
*/
|
||||
static #parseRange(field, val, repeatInterval, constraints) {
|
||||
const atoms = val.split('-');
|
||||
if (atoms.length <= 1) {
|
||||
return isNaN(+val) ? val : +val;
|
||||
}
|
||||
const [min, max] = atoms.map((num) => parseInt(num, 10));
|
||||
this.#validateRange(min, max, constraints);
|
||||
this.#validateRepeatInterval(repeatInterval);
|
||||
// Create range
|
||||
return this.#createRange(field, min, max, repeatInterval);
|
||||
}
|
||||
/**
|
||||
* Parse a cron expression.
|
||||
* @param {string} val - The cron expression to parse.
|
||||
* @private
|
||||
* @returns {string} The parsed cron expression.
|
||||
*/
|
||||
static #parseNthDay(val) {
|
||||
const atoms = val.split('#');
|
||||
if (atoms.length <= 1) {
|
||||
return { dayOfWeek: atoms[0] };
|
||||
}
|
||||
const nthValue = +atoms[atoms.length - 1];
|
||||
const matches = val.match(/([,-/])/);
|
||||
if (matches !== null) {
|
||||
throw new Error(`Constraint error, invalid dayOfWeek \`#\` and \`${matches?.[0]}\` special characters are incompatible`);
|
||||
}
|
||||
if (!(atoms.length <= 2 && !isNaN(nthValue) && nthValue >= 1 && nthValue <= 5)) {
|
||||
throw new Error('Constraint error, invalid dayOfWeek occurrence number (#)');
|
||||
}
|
||||
return { dayOfWeek: atoms[0], nthDayOfWeek: nthValue };
|
||||
}
|
||||
/**
|
||||
* Checks if a character is valid for a field.
|
||||
* @param {CronConstraints} constraints - The constraints for the field.
|
||||
* @param {string | number} value - The value to check.
|
||||
* @private
|
||||
* @returns {boolean} Whether the character is valid for the field.
|
||||
*/
|
||||
static #isValidConstraintChar(constraints, value) {
|
||||
return constraints.chars.some((char) => value.toString().includes(char));
|
||||
}
|
||||
}
|
||||
exports.CronExpressionParser = CronExpressionParser;
|
||||
371
node_modules/cron-parser/dist/CronFieldCollection.js
generated
vendored
Normal file
371
node_modules/cron-parser/dist/CronFieldCollection.js
generated
vendored
Normal file
@@ -0,0 +1,371 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.CronFieldCollection = void 0;
|
||||
const fields_1 = require("./fields");
|
||||
/**
|
||||
* Represents a complete set of cron fields.
|
||||
* @class CronFieldCollection
|
||||
*/
|
||||
class CronFieldCollection {
|
||||
#second;
|
||||
#minute;
|
||||
#hour;
|
||||
#dayOfMonth;
|
||||
#month;
|
||||
#dayOfWeek;
|
||||
/**
|
||||
* Creates a new CronFieldCollection instance by partially overriding fields from an existing one.
|
||||
* @param {CronFieldCollection} base - The base CronFieldCollection to copy fields from
|
||||
* @param {CronFieldOverride} fields - The fields to override, can be CronField instances or raw values
|
||||
* @returns {CronFieldCollection} A new CronFieldCollection instance
|
||||
* @example
|
||||
* const base = new CronFieldCollection({
|
||||
* second: new CronSecond([0]),
|
||||
* minute: new CronMinute([0]),
|
||||
* hour: new CronHour([12]),
|
||||
* dayOfMonth: new CronDayOfMonth([1]),
|
||||
* month: new CronMonth([1]),
|
||||
* dayOfWeek: new CronDayOfWeek([1])
|
||||
* });
|
||||
*
|
||||
* // Using CronField instances
|
||||
* const modified1 = CronFieldCollection.from(base, {
|
||||
* hour: new CronHour([15]),
|
||||
* minute: new CronMinute([30])
|
||||
* });
|
||||
*
|
||||
* // Using raw values
|
||||
* const modified2 = CronFieldCollection.from(base, {
|
||||
* hour: [15], // Will create new CronHour
|
||||
* minute: [30] // Will create new CronMinute
|
||||
* });
|
||||
*/
|
||||
static from(base, fields) {
|
||||
return new CronFieldCollection({
|
||||
second: this.resolveField(fields_1.CronSecond, base.second, fields.second),
|
||||
minute: this.resolveField(fields_1.CronMinute, base.minute, fields.minute),
|
||||
hour: this.resolveField(fields_1.CronHour, base.hour, fields.hour),
|
||||
dayOfMonth: this.resolveField(fields_1.CronDayOfMonth, base.dayOfMonth, fields.dayOfMonth),
|
||||
month: this.resolveField(fields_1.CronMonth, base.month, fields.month),
|
||||
dayOfWeek: this.resolveField(fields_1.CronDayOfWeek, base.dayOfWeek, fields.dayOfWeek),
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Resolves a field value, either using the provided CronField instance or creating a new one from raw values.
|
||||
* @param constructor - The constructor for creating new field instances
|
||||
* @param baseField - The base field to use if no override is provided
|
||||
* @param fieldValue - The override value, either a CronField instance or raw values
|
||||
* @returns The resolved CronField instance
|
||||
* @private
|
||||
*/
|
||||
static resolveField(constructor, baseField, fieldValue) {
|
||||
if (!fieldValue) {
|
||||
return baseField;
|
||||
}
|
||||
if (fieldValue instanceof fields_1.CronField) {
|
||||
return fieldValue;
|
||||
}
|
||||
return new constructor(fieldValue);
|
||||
}
|
||||
/**
|
||||
* CronFieldCollection constructor. Initializes the cron fields with the provided values.
|
||||
* @param {CronFields} param0 - The cron fields values
|
||||
* @throws {Error} if validation fails
|
||||
* @example
|
||||
* const cronFields = new CronFieldCollection({
|
||||
* second: new CronSecond([0]),
|
||||
* minute: new CronMinute([0, 30]),
|
||||
* hour: new CronHour([9]),
|
||||
* dayOfMonth: new CronDayOfMonth([15]),
|
||||
* month: new CronMonth([1]),
|
||||
* dayOfWeek: new CronDayOfTheWeek([1, 2, 3, 4, 5]),
|
||||
* })
|
||||
*
|
||||
* console.log(cronFields.second.values); // [0]
|
||||
* console.log(cronFields.minute.values); // [0, 30]
|
||||
* console.log(cronFields.hour.values); // [9]
|
||||
* console.log(cronFields.dayOfMonth.values); // [15]
|
||||
* console.log(cronFields.month.values); // [1]
|
||||
* console.log(cronFields.dayOfWeek.values); // [1, 2, 3, 4, 5]
|
||||
*/
|
||||
constructor({ second, minute, hour, dayOfMonth, month, dayOfWeek }) {
|
||||
if (!second) {
|
||||
throw new Error('Validation error, Field second is missing');
|
||||
}
|
||||
if (!minute) {
|
||||
throw new Error('Validation error, Field minute is missing');
|
||||
}
|
||||
if (!hour) {
|
||||
throw new Error('Validation error, Field hour is missing');
|
||||
}
|
||||
if (!dayOfMonth) {
|
||||
throw new Error('Validation error, Field dayOfMonth is missing');
|
||||
}
|
||||
if (!month) {
|
||||
throw new Error('Validation error, Field month is missing');
|
||||
}
|
||||
if (!dayOfWeek) {
|
||||
throw new Error('Validation error, Field dayOfWeek is missing');
|
||||
}
|
||||
if (month.values.length === 1 && !dayOfMonth.hasLastChar) {
|
||||
if (!(parseInt(dayOfMonth.values[0], 10) <= fields_1.CronMonth.daysInMonth[month.values[0] - 1])) {
|
||||
throw new Error('Invalid explicit day of month definition');
|
||||
}
|
||||
}
|
||||
this.#second = second;
|
||||
this.#minute = minute;
|
||||
this.#hour = hour;
|
||||
this.#month = month;
|
||||
this.#dayOfWeek = dayOfWeek;
|
||||
this.#dayOfMonth = dayOfMonth;
|
||||
}
|
||||
/**
|
||||
* Returns the second field.
|
||||
* @returns {CronSecond}
|
||||
*/
|
||||
get second() {
|
||||
return this.#second;
|
||||
}
|
||||
/**
|
||||
* Returns the minute field.
|
||||
* @returns {CronMinute}
|
||||
*/
|
||||
get minute() {
|
||||
return this.#minute;
|
||||
}
|
||||
/**
|
||||
* Returns the hour field.
|
||||
* @returns {CronHour}
|
||||
*/
|
||||
get hour() {
|
||||
return this.#hour;
|
||||
}
|
||||
/**
|
||||
* Returns the day of the month field.
|
||||
* @returns {CronDayOfMonth}
|
||||
*/
|
||||
get dayOfMonth() {
|
||||
return this.#dayOfMonth;
|
||||
}
|
||||
/**
|
||||
* Returns the month field.
|
||||
* @returns {CronMonth}
|
||||
*/
|
||||
get month() {
|
||||
return this.#month;
|
||||
}
|
||||
/**
|
||||
* Returns the day of the week field.
|
||||
* @returns {CronDayOfWeek}
|
||||
*/
|
||||
get dayOfWeek() {
|
||||
return this.#dayOfWeek;
|
||||
}
|
||||
/**
|
||||
* Returns a string representation of the cron fields.
|
||||
* @param {(number | CronChars)[]} input - The cron fields values
|
||||
* @static
|
||||
* @returns {FieldRange[]} - The compacted cron fields
|
||||
*/
|
||||
static compactField(input) {
|
||||
if (input.length === 0) {
|
||||
return [];
|
||||
}
|
||||
// Initialize the output array and current IFieldRange
|
||||
const output = [];
|
||||
let current = undefined;
|
||||
input.forEach((item, i, arr) => {
|
||||
// If the current FieldRange is undefined, create a new one with the current item as the start.
|
||||
if (current === undefined) {
|
||||
current = { start: item, count: 1 };
|
||||
return;
|
||||
}
|
||||
// Cache the previous and next items in the array.
|
||||
const prevItem = arr[i - 1] || current.start;
|
||||
const nextItem = arr[i + 1];
|
||||
// If the current item is 'L' or 'W', push the current FieldRange to the output and
|
||||
// create a new FieldRange with the current item as the start.
|
||||
// 'L' and 'W' characters are special cases that need to be handled separately.
|
||||
if (item === 'L' || item === 'W') {
|
||||
output.push(current);
|
||||
output.push({ start: item, count: 1 });
|
||||
current = undefined;
|
||||
return;
|
||||
}
|
||||
// If the current step is undefined and there is a next item, update the current IFieldRange.
|
||||
// This block checks if the current step needs to be updated and does so if needed.
|
||||
if (current.step === undefined && nextItem !== undefined) {
|
||||
const step = item - prevItem;
|
||||
const nextStep = nextItem - item;
|
||||
// If the current step is less or equal to the next step, update the current FieldRange to include the current item.
|
||||
if (step <= nextStep) {
|
||||
current = { ...current, count: 2, end: item, step };
|
||||
return;
|
||||
}
|
||||
current.step = 1;
|
||||
}
|
||||
// If the difference between the current item and the current end is equal to the current step,
|
||||
// update the current IFieldRange's count and end.
|
||||
// This block checks if the current item is part of the current range and updates the range accordingly.
|
||||
if (item - (current.end ?? 0) === current.step) {
|
||||
current.count++;
|
||||
current.end = item;
|
||||
}
|
||||
else {
|
||||
// If the count is 1, push a new FieldRange with the current start.
|
||||
// This handles the case where the current range has only one element.
|
||||
if (current.count === 1) {
|
||||
// If the count is 2, push two separate IFieldRanges, one for each element.
|
||||
output.push({ start: current.start, count: 1 });
|
||||
}
|
||||
else if (current.count === 2) {
|
||||
output.push({ start: current.start, count: 1 });
|
||||
// current.end can never be undefined here but typescript doesn't know that
|
||||
// this is why we ?? it and then ignore the prevItem in the coverage
|
||||
output.push({
|
||||
start: current.end ?? /* istanbul ignore next - see above */ prevItem,
|
||||
count: 1,
|
||||
});
|
||||
}
|
||||
else {
|
||||
// Otherwise, push the current FieldRange to the output.
|
||||
output.push(current);
|
||||
}
|
||||
// Reset the current FieldRange with the current item as the start.
|
||||
current = { start: item, count: 1 };
|
||||
}
|
||||
});
|
||||
// Push the final IFieldRange, if any, to the output array.
|
||||
if (current) {
|
||||
output.push(current);
|
||||
}
|
||||
return output;
|
||||
}
|
||||
/**
|
||||
* Handles a single range.
|
||||
* @param {CronField} field - The cron field to stringify
|
||||
* @param {FieldRange} range {start: number, end: number, step: number, count: number} The range to handle.
|
||||
* @param {number} max The maximum value for the field.
|
||||
* @returns {string | null} The stringified range or null if it cannot be stringified.
|
||||
* @private
|
||||
*/
|
||||
static #handleSingleRange(field, range, max) {
|
||||
const step = range.step;
|
||||
if (!step) {
|
||||
return null;
|
||||
}
|
||||
if (step === 1 && range.start === field.min && range.end && range.end >= max) {
|
||||
return field.hasQuestionMarkChar ? '?' : '*';
|
||||
}
|
||||
if (step !== 1 && range.start === field.min && range.end && range.end >= max - step + 1) {
|
||||
return `*/${step}`;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
/**
|
||||
* Handles multiple ranges.
|
||||
* @param {FieldRange} range {start: number, end: number, step: number, count: number} The range to handle.
|
||||
* @param {number} max The maximum value for the field.
|
||||
* @returns {string} The stringified range.
|
||||
* @private
|
||||
*/
|
||||
static #handleMultipleRanges(range, max) {
|
||||
const step = range.step;
|
||||
if (step === 1) {
|
||||
return `${range.start}-${range.end}`;
|
||||
}
|
||||
const multiplier = range.start === 0 ? range.count - 1 : range.count;
|
||||
/* istanbul ignore if */
|
||||
if (!step) {
|
||||
throw new Error('Unexpected range step');
|
||||
}
|
||||
/* istanbul ignore if */
|
||||
if (!range.end) {
|
||||
throw new Error('Unexpected range end');
|
||||
}
|
||||
if (step * multiplier > range.end) {
|
||||
const mapFn = (_, index) => {
|
||||
/* istanbul ignore if */
|
||||
if (typeof range.start !== 'number') {
|
||||
throw new Error('Unexpected range start');
|
||||
}
|
||||
return index % step === 0 ? range.start + index : null;
|
||||
};
|
||||
/* istanbul ignore if */
|
||||
if (typeof range.start !== 'number') {
|
||||
throw new Error('Unexpected range start');
|
||||
}
|
||||
const seed = { length: range.end - range.start + 1 };
|
||||
return Array.from(seed, mapFn)
|
||||
.filter((value) => value !== null)
|
||||
.join(',');
|
||||
}
|
||||
return range.end === max - step + 1 ? `${range.start}/${step}` : `${range.start}-${range.end}/${step}`;
|
||||
}
|
||||
/**
|
||||
* Returns a string representation of the cron fields.
|
||||
* @param {CronField} field - The cron field to stringify
|
||||
* @static
|
||||
* @returns {string} - The stringified cron field
|
||||
*/
|
||||
stringifyField(field) {
|
||||
let max = field.max;
|
||||
let values = field.values;
|
||||
if (field instanceof fields_1.CronDayOfWeek) {
|
||||
max = 6;
|
||||
const dayOfWeek = this.#dayOfWeek.values;
|
||||
values = dayOfWeek[dayOfWeek.length - 1] === 7 ? dayOfWeek.slice(0, -1) : dayOfWeek;
|
||||
}
|
||||
if (field instanceof fields_1.CronDayOfMonth) {
|
||||
max = this.#month.values.length === 1 ? fields_1.CronMonth.daysInMonth[this.#month.values[0] - 1] : field.max;
|
||||
}
|
||||
const ranges = CronFieldCollection.compactField(values);
|
||||
if (ranges.length === 1) {
|
||||
const singleRangeResult = CronFieldCollection.#handleSingleRange(field, ranges[0], max);
|
||||
if (singleRangeResult) {
|
||||
return singleRangeResult;
|
||||
}
|
||||
}
|
||||
return ranges
|
||||
.map((range) => {
|
||||
const value = range.count === 1 ? range.start.toString() : CronFieldCollection.#handleMultipleRanges(range, max);
|
||||
if (field instanceof fields_1.CronDayOfWeek && field.nthDay > 0) {
|
||||
return `${value}#${field.nthDay}`;
|
||||
}
|
||||
return value;
|
||||
})
|
||||
.join(',');
|
||||
}
|
||||
/**
|
||||
* Returns a string representation of the cron field values.
|
||||
* @param {boolean} includeSeconds - Whether to include seconds in the output
|
||||
* @returns {string} The formatted cron string
|
||||
*/
|
||||
stringify(includeSeconds = false) {
|
||||
const arr = [];
|
||||
if (includeSeconds) {
|
||||
arr.push(this.stringifyField(this.#second)); // second
|
||||
}
|
||||
arr.push(this.stringifyField(this.#minute), // minute
|
||||
this.stringifyField(this.#hour), // hour
|
||||
this.stringifyField(this.#dayOfMonth), // dayOfMonth
|
||||
this.stringifyField(this.#month), // month
|
||||
this.stringifyField(this.#dayOfWeek));
|
||||
return arr.join(' ');
|
||||
}
|
||||
/**
|
||||
* Returns a serialized representation of the cron fields values.
|
||||
* @returns {SerializedCronFields} An object containing the cron field values
|
||||
*/
|
||||
serialize() {
|
||||
return {
|
||||
second: this.#second.serialize(),
|
||||
minute: this.#minute.serialize(),
|
||||
hour: this.#hour.serialize(),
|
||||
dayOfMonth: this.#dayOfMonth.serialize(),
|
||||
month: this.#month.serialize(),
|
||||
dayOfWeek: this.#dayOfWeek.serialize(),
|
||||
};
|
||||
}
|
||||
}
|
||||
exports.CronFieldCollection = CronFieldCollection;
|
||||
109
node_modules/cron-parser/dist/CronFileParser.js
generated
vendored
Normal file
109
node_modules/cron-parser/dist/CronFileParser.js
generated
vendored
Normal file
@@ -0,0 +1,109 @@
|
||||
"use strict";
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||
}) : function(o, v) {
|
||||
o["default"] = v;
|
||||
});
|
||||
var __importStar = (this && this.__importStar) || (function () {
|
||||
var ownKeys = function(o) {
|
||||
ownKeys = Object.getOwnPropertyNames || function (o) {
|
||||
var ar = [];
|
||||
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
||||
return ar;
|
||||
};
|
||||
return ownKeys(o);
|
||||
};
|
||||
return function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
||||
__setModuleDefault(result, mod);
|
||||
return result;
|
||||
};
|
||||
})();
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.CronFileParser = void 0;
|
||||
const CronExpressionParser_1 = require("./CronExpressionParser");
|
||||
/**
|
||||
* Parser for crontab files that handles both synchronous and asynchronous operations.
|
||||
*/
|
||||
class CronFileParser {
|
||||
/**
|
||||
* Parse a crontab file asynchronously
|
||||
* @param filePath Path to crontab file
|
||||
* @returns Promise resolving to parse results
|
||||
* @throws If file cannot be read
|
||||
*/
|
||||
static async parseFile(filePath) {
|
||||
const { readFile } = await Promise.resolve().then(() => __importStar(require('fs/promises')));
|
||||
const data = await readFile(filePath, 'utf8');
|
||||
return CronFileParser.#parseContent(data);
|
||||
}
|
||||
/**
|
||||
* Parse a crontab file synchronously
|
||||
* @param filePath Path to crontab file
|
||||
* @returns Parse results
|
||||
* @throws If file cannot be read
|
||||
*/
|
||||
static parseFileSync(filePath) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
||||
const { readFileSync } = require('fs');
|
||||
const data = readFileSync(filePath, 'utf8');
|
||||
return CronFileParser.#parseContent(data);
|
||||
}
|
||||
/**
|
||||
* Internal method to parse crontab file content
|
||||
* @private
|
||||
*/
|
||||
static #parseContent(data) {
|
||||
const blocks = data.split('\n');
|
||||
const result = {
|
||||
variables: {},
|
||||
expressions: [],
|
||||
errors: {},
|
||||
};
|
||||
for (const block of blocks) {
|
||||
const entry = block.trim();
|
||||
if (entry.length === 0 || entry.startsWith('#')) {
|
||||
continue;
|
||||
}
|
||||
const variableMatch = entry.match(/^(.*)=(.*)$/);
|
||||
if (variableMatch) {
|
||||
const [, key, value] = variableMatch;
|
||||
result.variables[key] = value.replace(/["']/g, ''); // Remove quotes
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
const parsedEntry = CronFileParser.#parseEntry(entry);
|
||||
result.expressions.push(parsedEntry.interval);
|
||||
}
|
||||
catch (err) {
|
||||
result.errors[entry] = err;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
/**
|
||||
* Parse a single crontab entry
|
||||
* @private
|
||||
*/
|
||||
static #parseEntry(entry) {
|
||||
const atoms = entry.split(' ');
|
||||
return {
|
||||
interval: CronExpressionParser_1.CronExpressionParser.parse(atoms.slice(0, 5).join(' ')),
|
||||
command: atoms.slice(5, atoms.length),
|
||||
};
|
||||
}
|
||||
}
|
||||
exports.CronFileParser = CronFileParser;
|
||||
44
node_modules/cron-parser/dist/fields/CronDayOfMonth.js
generated
vendored
Normal file
44
node_modules/cron-parser/dist/fields/CronDayOfMonth.js
generated
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.CronDayOfMonth = void 0;
|
||||
const CronField_1 = require("./CronField");
|
||||
const MIN_DAY = 1;
|
||||
const MAX_DAY = 31;
|
||||
const DAY_CHARS = Object.freeze(['L']);
|
||||
/**
|
||||
* Represents the "day of the month" field within a cron expression.
|
||||
* @class CronDayOfMonth
|
||||
* @extends CronField
|
||||
*/
|
||||
class CronDayOfMonth extends CronField_1.CronField {
|
||||
static get min() {
|
||||
return MIN_DAY;
|
||||
}
|
||||
static get max() {
|
||||
return MAX_DAY;
|
||||
}
|
||||
static get chars() {
|
||||
return DAY_CHARS;
|
||||
}
|
||||
static get validChars() {
|
||||
return /^[?,*\dLH/-]+$|^.*H\(\d+-\d+\)\/\d+.*$|^.*H\(\d+-\d+\).*$|^.*H\/\d+.*$/;
|
||||
}
|
||||
/**
|
||||
* CronDayOfMonth constructor. Initializes the "day of the month" field with the provided values.
|
||||
* @param {DayOfMonthRange[]} values - Values for the "day of the month" field
|
||||
* @param {CronFieldOptions} [options] - Options provided by the parser
|
||||
* @throws {Error} if validation fails
|
||||
*/
|
||||
constructor(values, options) {
|
||||
super(values, options);
|
||||
this.validate();
|
||||
}
|
||||
/**
|
||||
* Returns an array of allowed values for the "day of the month" field.
|
||||
* @returns {DayOfMonthRange[]}
|
||||
*/
|
||||
get values() {
|
||||
return super.values;
|
||||
}
|
||||
}
|
||||
exports.CronDayOfMonth = CronDayOfMonth;
|
||||
51
node_modules/cron-parser/dist/fields/CronDayOfWeek.js
generated
vendored
Normal file
51
node_modules/cron-parser/dist/fields/CronDayOfWeek.js
generated
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.CronDayOfWeek = void 0;
|
||||
const CronField_1 = require("./CronField");
|
||||
const MIN_DAY = 0;
|
||||
const MAX_DAY = 7;
|
||||
const DAY_CHARS = Object.freeze(['L']);
|
||||
/**
|
||||
* Represents the "day of the week" field within a cron expression.
|
||||
* @class CronDayOfTheWeek
|
||||
* @extends CronField
|
||||
*/
|
||||
class CronDayOfWeek extends CronField_1.CronField {
|
||||
static get min() {
|
||||
return MIN_DAY;
|
||||
}
|
||||
static get max() {
|
||||
return MAX_DAY;
|
||||
}
|
||||
static get chars() {
|
||||
return DAY_CHARS;
|
||||
}
|
||||
static get validChars() {
|
||||
return /^[?,*\dLH#/-]+$|^.*H\(\d+-\d+\)\/\d+.*$|^.*H\(\d+-\d+\).*$|^.*H\/\d+.*$/;
|
||||
}
|
||||
/**
|
||||
* CronDayOfTheWeek constructor. Initializes the "day of the week" field with the provided values.
|
||||
* @param {DayOfWeekRange[]} values - Values for the "day of the week" field
|
||||
* @param {CronFieldOptions} [options] - Options provided by the parser
|
||||
*/
|
||||
constructor(values, options) {
|
||||
super(values, options);
|
||||
this.validate();
|
||||
}
|
||||
/**
|
||||
* Returns an array of allowed values for the "day of the week" field.
|
||||
* @returns {DayOfWeekRange[]}
|
||||
*/
|
||||
get values() {
|
||||
return super.values;
|
||||
}
|
||||
/**
|
||||
* Returns the nth day of the week if specified in the cron expression.
|
||||
* This is used for the '#' character in the cron expression.
|
||||
* @returns {number} The nth day of the week (1-5) or 0 if not specified.
|
||||
*/
|
||||
get nthDay() {
|
||||
return this.options.nthDayOfWeek ?? 0;
|
||||
}
|
||||
}
|
||||
exports.CronDayOfWeek = CronDayOfWeek;
|
||||
183
node_modules/cron-parser/dist/fields/CronField.js
generated
vendored
Normal file
183
node_modules/cron-parser/dist/fields/CronField.js
generated
vendored
Normal file
@@ -0,0 +1,183 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.CronField = void 0;
|
||||
/**
|
||||
* Represents a field within a cron expression.
|
||||
* This is a base class and should not be instantiated directly.
|
||||
* @class CronField
|
||||
*/
|
||||
class CronField {
|
||||
#hasLastChar = false;
|
||||
#hasQuestionMarkChar = false;
|
||||
#wildcard = false;
|
||||
#values = [];
|
||||
options = { rawValue: '' };
|
||||
/**
|
||||
* Returns the minimum value allowed for this field.
|
||||
*/
|
||||
/* istanbul ignore next */ static get min() {
|
||||
/* istanbul ignore next */
|
||||
throw new Error('min must be overridden');
|
||||
}
|
||||
/**
|
||||
* Returns the maximum value allowed for this field.
|
||||
*/
|
||||
/* istanbul ignore next */ static get max() {
|
||||
/* istanbul ignore next */
|
||||
throw new Error('max must be overridden');
|
||||
}
|
||||
/**
|
||||
* Returns the allowed characters for this field.
|
||||
*/
|
||||
/* istanbul ignore next */ static get chars() {
|
||||
/* istanbul ignore next - this is overridden */
|
||||
return Object.freeze([]);
|
||||
}
|
||||
/**
|
||||
* Returns the regular expression used to validate this field.
|
||||
*/
|
||||
static get validChars() {
|
||||
return /^[?,*\dH/-]+$|^.*H\(\d+-\d+\)\/\d+.*$|^.*H\(\d+-\d+\).*$|^.*H\/\d+.*$/;
|
||||
}
|
||||
/**
|
||||
* Returns the constraints for this field.
|
||||
*/
|
||||
static get constraints() {
|
||||
return { min: this.min, max: this.max, chars: this.chars, validChars: this.validChars };
|
||||
}
|
||||
/**
|
||||
* CronField constructor. Initializes the field with the provided values.
|
||||
* @param {number[] | string[]} values - Values for this field
|
||||
* @param {CronFieldOptions} [options] - Options provided by the parser
|
||||
* @throws {TypeError} if the constructor is called directly
|
||||
* @throws {Error} if validation fails
|
||||
*/
|
||||
constructor(values, options = { rawValue: '' }) {
|
||||
if (!Array.isArray(values)) {
|
||||
throw new Error(`${this.constructor.name} Validation error, values is not an array`);
|
||||
}
|
||||
if (!(values.length > 0)) {
|
||||
throw new Error(`${this.constructor.name} Validation error, values contains no values`);
|
||||
}
|
||||
/* istanbul ignore next */
|
||||
this.options = {
|
||||
...options,
|
||||
rawValue: options.rawValue ?? '',
|
||||
};
|
||||
this.#values = values.sort(CronField.sorter);
|
||||
this.#wildcard = this.options.wildcard !== undefined ? this.options.wildcard : this.#isWildcardValue();
|
||||
this.#hasLastChar = this.options.rawValue.includes('L') || values.includes('L');
|
||||
this.#hasQuestionMarkChar = this.options.rawValue.includes('?') || values.includes('?');
|
||||
}
|
||||
/**
|
||||
* Returns the minimum value allowed for this field.
|
||||
* @returns {number}
|
||||
*/
|
||||
get min() {
|
||||
// return the static value from the child class
|
||||
return this.constructor.min;
|
||||
}
|
||||
/**
|
||||
* Returns the maximum value allowed for this field.
|
||||
* @returns {number}
|
||||
*/
|
||||
get max() {
|
||||
// return the static value from the child class
|
||||
return this.constructor.max;
|
||||
}
|
||||
/**
|
||||
* Returns an array of allowed special characters for this field.
|
||||
* @returns {string[]}
|
||||
*/
|
||||
get chars() {
|
||||
// return the frozen static value from the child class
|
||||
return this.constructor.chars;
|
||||
}
|
||||
/**
|
||||
* Indicates whether this field has a "last" character.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
get hasLastChar() {
|
||||
return this.#hasLastChar;
|
||||
}
|
||||
/**
|
||||
* Indicates whether this field has a "question mark" character.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
get hasQuestionMarkChar() {
|
||||
return this.#hasQuestionMarkChar;
|
||||
}
|
||||
/**
|
||||
* Indicates whether this field is a wildcard.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
get isWildcard() {
|
||||
return this.#wildcard;
|
||||
}
|
||||
/**
|
||||
* Returns an array of allowed values for this field.
|
||||
* @returns {CronFieldType}
|
||||
*/
|
||||
get values() {
|
||||
return this.#values;
|
||||
}
|
||||
/**
|
||||
* Helper function to sort values in ascending order.
|
||||
* @param {number | string} a - First value to compare
|
||||
* @param {number | string} b - Second value to compare
|
||||
* @returns {number} - A negative, zero, or positive value, depending on the sort order
|
||||
*/
|
||||
static sorter(a, b) {
|
||||
const aIsNumber = typeof a === 'number';
|
||||
const bIsNumber = typeof b === 'number';
|
||||
if (aIsNumber && bIsNumber)
|
||||
return a - b;
|
||||
if (!aIsNumber && !bIsNumber)
|
||||
return a.localeCompare(b);
|
||||
return aIsNumber ? /* istanbul ignore next - A will always be a number until L-2 is supported */ -1 : 1;
|
||||
}
|
||||
/**
|
||||
* Serializes the field to an object.
|
||||
* @returns {SerializedCronField}
|
||||
*/
|
||||
serialize() {
|
||||
return {
|
||||
wildcard: this.#wildcard,
|
||||
values: this.#values,
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Validates the field values against the allowed range and special characters.
|
||||
* @throws {Error} if validation fails
|
||||
*/
|
||||
validate() {
|
||||
let badValue;
|
||||
const charsString = this.chars.length > 0 ? ` or chars ${this.chars.join('')}` : '';
|
||||
const charTest = (value) => (char) => new RegExp(`^\\d{0,2}${char}$`).test(value);
|
||||
const rangeTest = (value) => {
|
||||
badValue = value;
|
||||
return typeof value === 'number' ? value >= this.min && value <= this.max : this.chars.some(charTest(value));
|
||||
};
|
||||
const isValidRange = this.#values.every(rangeTest);
|
||||
if (!isValidRange) {
|
||||
throw new Error(`${this.constructor.name} Validation error, got value ${badValue} expected range ${this.min}-${this.max}${charsString}`);
|
||||
}
|
||||
// check for duplicate value in this.#values array
|
||||
const duplicate = this.#values.find((value, index) => this.#values.indexOf(value) !== index);
|
||||
if (duplicate) {
|
||||
throw new Error(`${this.constructor.name} Validation error, duplicate values found: ${duplicate}`);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Determines if the field is a wildcard based on the values.
|
||||
* When options.rawValue is not empty, it checks if the raw value is a wildcard, otherwise it checks if all values in the range are included.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
#isWildcardValue() {
|
||||
if (this.options.rawValue.length > 0) {
|
||||
return ['*', '?'].includes(this.options.rawValue);
|
||||
}
|
||||
return Array.from({ length: this.max - this.min + 1 }, (_, i) => i + this.min).every((value) => this.#values.includes(value));
|
||||
}
|
||||
}
|
||||
exports.CronField = CronField;
|
||||
40
node_modules/cron-parser/dist/fields/CronHour.js
generated
vendored
Normal file
40
node_modules/cron-parser/dist/fields/CronHour.js
generated
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.CronHour = void 0;
|
||||
const CronField_1 = require("./CronField");
|
||||
const MIN_HOUR = 0;
|
||||
const MAX_HOUR = 23;
|
||||
const HOUR_CHARS = Object.freeze([]);
|
||||
/**
|
||||
* Represents the "hour" field within a cron expression.
|
||||
* @class CronHour
|
||||
* @extends CronField
|
||||
*/
|
||||
class CronHour extends CronField_1.CronField {
|
||||
static get min() {
|
||||
return MIN_HOUR;
|
||||
}
|
||||
static get max() {
|
||||
return MAX_HOUR;
|
||||
}
|
||||
static get chars() {
|
||||
return HOUR_CHARS;
|
||||
}
|
||||
/**
|
||||
* CronHour constructor. Initializes the "hour" field with the provided values.
|
||||
* @param {HourRange[]} values - Values for the "hour" field
|
||||
* @param {CronFieldOptions} [options] - Options provided by the parser
|
||||
*/
|
||||
constructor(values, options) {
|
||||
super(values, options);
|
||||
this.validate();
|
||||
}
|
||||
/**
|
||||
* Returns an array of allowed values for the "hour" field.
|
||||
* @returns {HourRange[]}
|
||||
*/
|
||||
get values() {
|
||||
return super.values;
|
||||
}
|
||||
}
|
||||
exports.CronHour = CronHour;
|
||||
40
node_modules/cron-parser/dist/fields/CronMinute.js
generated
vendored
Normal file
40
node_modules/cron-parser/dist/fields/CronMinute.js
generated
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.CronMinute = void 0;
|
||||
const CronField_1 = require("./CronField");
|
||||
const MIN_MINUTE = 0;
|
||||
const MAX_MINUTE = 59;
|
||||
const MINUTE_CHARS = Object.freeze([]);
|
||||
/**
|
||||
* Represents the "second" field within a cron expression.
|
||||
* @class CronSecond
|
||||
* @extends CronField
|
||||
*/
|
||||
class CronMinute extends CronField_1.CronField {
|
||||
static get min() {
|
||||
return MIN_MINUTE;
|
||||
}
|
||||
static get max() {
|
||||
return MAX_MINUTE;
|
||||
}
|
||||
static get chars() {
|
||||
return MINUTE_CHARS;
|
||||
}
|
||||
/**
|
||||
* CronSecond constructor. Initializes the "second" field with the provided values.
|
||||
* @param {SixtyRange[]} values - Values for the "second" field
|
||||
* @param {CronFieldOptions} [options] - Options provided by the parser
|
||||
*/
|
||||
constructor(values, options) {
|
||||
super(values, options);
|
||||
this.validate();
|
||||
}
|
||||
/**
|
||||
* Returns an array of allowed values for the "second" field.
|
||||
* @returns {SixtyRange[]}
|
||||
*/
|
||||
get values() {
|
||||
return super.values;
|
||||
}
|
||||
}
|
||||
exports.CronMinute = CronMinute;
|
||||
44
node_modules/cron-parser/dist/fields/CronMonth.js
generated
vendored
Normal file
44
node_modules/cron-parser/dist/fields/CronMonth.js
generated
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.CronMonth = void 0;
|
||||
const CronDate_1 = require("../CronDate");
|
||||
const CronField_1 = require("./CronField");
|
||||
const MIN_MONTH = 1;
|
||||
const MAX_MONTH = 12;
|
||||
const MONTH_CHARS = Object.freeze([]);
|
||||
/**
|
||||
* Represents the "day of the month" field within a cron expression.
|
||||
* @class CronDayOfMonth
|
||||
* @extends CronField
|
||||
*/
|
||||
class CronMonth extends CronField_1.CronField {
|
||||
static get min() {
|
||||
return MIN_MONTH;
|
||||
}
|
||||
static get max() {
|
||||
return MAX_MONTH;
|
||||
}
|
||||
static get chars() {
|
||||
return MONTH_CHARS;
|
||||
}
|
||||
static get daysInMonth() {
|
||||
return CronDate_1.DAYS_IN_MONTH;
|
||||
}
|
||||
/**
|
||||
* CronDayOfMonth constructor. Initializes the "day of the month" field with the provided values.
|
||||
* @param {MonthRange[]} values - Values for the "day of the month" field
|
||||
* @param {CronFieldOptions} [options] - Options provided by the parser
|
||||
*/
|
||||
constructor(values, options) {
|
||||
super(values, options);
|
||||
this.validate();
|
||||
}
|
||||
/**
|
||||
* Returns an array of allowed values for the "day of the month" field.
|
||||
* @returns {MonthRange[]}
|
||||
*/
|
||||
get values() {
|
||||
return super.values;
|
||||
}
|
||||
}
|
||||
exports.CronMonth = CronMonth;
|
||||
40
node_modules/cron-parser/dist/fields/CronSecond.js
generated
vendored
Normal file
40
node_modules/cron-parser/dist/fields/CronSecond.js
generated
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.CronSecond = void 0;
|
||||
const CronField_1 = require("./CronField");
|
||||
const MIN_SECOND = 0;
|
||||
const MAX_SECOND = 59;
|
||||
const SECOND_CHARS = Object.freeze([]);
|
||||
/**
|
||||
* Represents the "second" field within a cron expression.
|
||||
* @class CronSecond
|
||||
* @extends CronField
|
||||
*/
|
||||
class CronSecond extends CronField_1.CronField {
|
||||
static get min() {
|
||||
return MIN_SECOND;
|
||||
}
|
||||
static get max() {
|
||||
return MAX_SECOND;
|
||||
}
|
||||
static get chars() {
|
||||
return SECOND_CHARS;
|
||||
}
|
||||
/**
|
||||
* CronSecond constructor. Initializes the "second" field with the provided values.
|
||||
* @param {SixtyRange[]} values - Values for the "second" field
|
||||
* @param {CronFieldOptions} [options] - Options provided by the parser
|
||||
*/
|
||||
constructor(values, options) {
|
||||
super(values, options);
|
||||
this.validate();
|
||||
}
|
||||
/**
|
||||
* Returns an array of allowed values for the "second" field.
|
||||
* @returns {SixtyRange[]}
|
||||
*/
|
||||
get values() {
|
||||
return super.values;
|
||||
}
|
||||
}
|
||||
exports.CronSecond = CronSecond;
|
||||
24
node_modules/cron-parser/dist/fields/index.js
generated
vendored
Normal file
24
node_modules/cron-parser/dist/fields/index.js
generated
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
"use strict";
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
||||
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
__exportStar(require("./types"), exports);
|
||||
__exportStar(require("./CronDayOfMonth"), exports);
|
||||
__exportStar(require("./CronDayOfWeek"), exports);
|
||||
__exportStar(require("./CronField"), exports);
|
||||
__exportStar(require("./CronHour"), exports);
|
||||
__exportStar(require("./CronMinute"), exports);
|
||||
__exportStar(require("./CronMonth"), exports);
|
||||
__exportStar(require("./CronSecond"), exports);
|
||||
2
node_modules/cron-parser/dist/fields/types.js
generated
vendored
Normal file
2
node_modules/cron-parser/dist/fields/types.js
generated
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
31
node_modules/cron-parser/dist/index.js
generated
vendored
Normal file
31
node_modules/cron-parser/dist/index.js
generated
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
"use strict";
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
||||
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.CronFileParser = exports.CronExpressionParser = exports.CronExpression = exports.CronFieldCollection = exports.CronDate = void 0;
|
||||
/* istanbul ignore file */
|
||||
const CronExpressionParser_1 = require("./CronExpressionParser");
|
||||
var CronDate_1 = require("./CronDate");
|
||||
Object.defineProperty(exports, "CronDate", { enumerable: true, get: function () { return CronDate_1.CronDate; } });
|
||||
var CronFieldCollection_1 = require("./CronFieldCollection");
|
||||
Object.defineProperty(exports, "CronFieldCollection", { enumerable: true, get: function () { return CronFieldCollection_1.CronFieldCollection; } });
|
||||
var CronExpression_1 = require("./CronExpression");
|
||||
Object.defineProperty(exports, "CronExpression", { enumerable: true, get: function () { return CronExpression_1.CronExpression; } });
|
||||
var CronExpressionParser_2 = require("./CronExpressionParser");
|
||||
Object.defineProperty(exports, "CronExpressionParser", { enumerable: true, get: function () { return CronExpressionParser_2.CronExpressionParser; } });
|
||||
var CronFileParser_1 = require("./CronFileParser");
|
||||
Object.defineProperty(exports, "CronFileParser", { enumerable: true, get: function () { return CronFileParser_1.CronFileParser; } });
|
||||
__exportStar(require("./fields"), exports);
|
||||
exports.default = CronExpressionParser_1.CronExpressionParser;
|
||||
273
node_modules/cron-parser/dist/types/CronDate.d.ts
generated
vendored
Normal file
273
node_modules/cron-parser/dist/types/CronDate.d.ts
generated
vendored
Normal file
@@ -0,0 +1,273 @@
|
||||
export declare enum TimeUnit {
|
||||
Second = "Second",
|
||||
Minute = "Minute",
|
||||
Hour = "Hour",
|
||||
Day = "Day",
|
||||
Month = "Month",
|
||||
Year = "Year"
|
||||
}
|
||||
export declare enum DateMathOp {
|
||||
Add = "Add",
|
||||
Subtract = "Subtract"
|
||||
}
|
||||
export declare const DAYS_IN_MONTH: readonly number[];
|
||||
/**
|
||||
* CronDate class that wraps the Luxon DateTime object to provide
|
||||
* a consistent API for working with dates and times in the context of cron.
|
||||
*/
|
||||
export declare class CronDate {
|
||||
#private;
|
||||
/**
|
||||
* Constructs a new CronDate instance.
|
||||
* @param {CronDate | Date | number | string} [timestamp] - The timestamp to initialize the CronDate with.
|
||||
* @param {string} [tz] - The timezone to use for the CronDate.
|
||||
*/
|
||||
constructor(timestamp?: CronDate | Date | number | string, tz?: string);
|
||||
/**
|
||||
* Returns daylight savings start time.
|
||||
* @returns {number | null}
|
||||
*/
|
||||
get dstStart(): number | null;
|
||||
/**
|
||||
* Sets daylight savings start time.
|
||||
* @param {number | null} value
|
||||
*/
|
||||
set dstStart(value: number | null);
|
||||
/**
|
||||
* Returns daylight savings end time.
|
||||
* @returns {number | null}
|
||||
*/
|
||||
get dstEnd(): number | null;
|
||||
/**
|
||||
* Sets daylight savings end time.
|
||||
* @param {number | null} value
|
||||
*/
|
||||
set dstEnd(value: number | null);
|
||||
/**
|
||||
* Adds one year to the current CronDate.
|
||||
*/
|
||||
addYear(): void;
|
||||
/**
|
||||
* Adds one month to the current CronDate.
|
||||
*/
|
||||
addMonth(): void;
|
||||
/**
|
||||
* Adds one day to the current CronDate.
|
||||
*/
|
||||
addDay(): void;
|
||||
/**
|
||||
* Adds one hour to the current CronDate.
|
||||
*/
|
||||
addHour(): void;
|
||||
/**
|
||||
* Adds one minute to the current CronDate.
|
||||
*/
|
||||
addMinute(): void;
|
||||
/**
|
||||
* Adds one second to the current CronDate.
|
||||
*/
|
||||
addSecond(): void;
|
||||
/**
|
||||
* Subtracts one year from the current CronDate.
|
||||
*/
|
||||
subtractYear(): void;
|
||||
/**
|
||||
* Subtracts one month from the current CronDate.
|
||||
* If the month is 1, it will subtract one year instead.
|
||||
*/
|
||||
subtractMonth(): void;
|
||||
/**
|
||||
* Subtracts one day from the current CronDate.
|
||||
* If the day is 1, it will subtract one month instead.
|
||||
*/
|
||||
subtractDay(): void;
|
||||
/**
|
||||
* Subtracts one hour from the current CronDate.
|
||||
* If the hour is 0, it will subtract one day instead.
|
||||
*/
|
||||
subtractHour(): void;
|
||||
/**
|
||||
* Subtracts one minute from the current CronDate.
|
||||
* If the minute is 0, it will subtract one hour instead.
|
||||
*/
|
||||
subtractMinute(): void;
|
||||
/**
|
||||
* Subtracts one second from the current CronDate.
|
||||
* If the second is 0, it will subtract one minute instead.
|
||||
*/
|
||||
subtractSecond(): void;
|
||||
/**
|
||||
* Adds a unit of time to the current CronDate.
|
||||
* @param {TimeUnit} unit
|
||||
*/
|
||||
addUnit(unit: TimeUnit): void;
|
||||
/**
|
||||
* Subtracts a unit of time from the current CronDate.
|
||||
* @param {TimeUnit} unit
|
||||
*/
|
||||
subtractUnit(unit: TimeUnit): void;
|
||||
/**
|
||||
* Handles a math operation.
|
||||
* @param {DateMathOp} verb - {'add' | 'subtract'}
|
||||
* @param {TimeUnit} unit - {'year' | 'month' | 'day' | 'hour' | 'minute' | 'second'}
|
||||
*/
|
||||
invokeDateOperation(verb: DateMathOp, unit: TimeUnit): void;
|
||||
/**
|
||||
* Returns the day.
|
||||
* @returns {number}
|
||||
*/
|
||||
getDate(): number;
|
||||
/**
|
||||
* Returns the year.
|
||||
* @returns {number}
|
||||
*/
|
||||
getFullYear(): number;
|
||||
/**
|
||||
* Returns the day of the week.
|
||||
* @returns {number}
|
||||
*/
|
||||
getDay(): number;
|
||||
/**
|
||||
* Returns the month.
|
||||
* @returns {number}
|
||||
*/
|
||||
getMonth(): number;
|
||||
/**
|
||||
* Returns the hour.
|
||||
* @returns {number}
|
||||
*/
|
||||
getHours(): number;
|
||||
/**
|
||||
* Returns the minutes.
|
||||
* @returns {number}
|
||||
*/
|
||||
getMinutes(): number;
|
||||
/**
|
||||
* Returns the seconds.
|
||||
* @returns {number}
|
||||
*/
|
||||
getSeconds(): number;
|
||||
/**
|
||||
* Returns the milliseconds.
|
||||
* @returns {number}
|
||||
*/
|
||||
getMilliseconds(): number;
|
||||
/**
|
||||
* Returns the time.
|
||||
* @returns {number}
|
||||
*/
|
||||
getTime(): number;
|
||||
/**
|
||||
* Returns the UTC day.
|
||||
* @returns {number}
|
||||
*/
|
||||
getUTCDate(): number;
|
||||
/**
|
||||
* Returns the UTC year.
|
||||
* @returns {number}
|
||||
*/
|
||||
getUTCFullYear(): number;
|
||||
/**
|
||||
* Returns the UTC day of the week.
|
||||
* @returns {number}
|
||||
*/
|
||||
getUTCDay(): number;
|
||||
/**
|
||||
* Returns the UTC month.
|
||||
* @returns {number}
|
||||
*/
|
||||
getUTCMonth(): number;
|
||||
/**
|
||||
* Returns the UTC hour.
|
||||
* @returns {number}
|
||||
*/
|
||||
getUTCHours(): number;
|
||||
/**
|
||||
* Returns the UTC minutes.
|
||||
* @returns {number}
|
||||
*/
|
||||
getUTCMinutes(): number;
|
||||
/**
|
||||
* Returns the UTC seconds.
|
||||
* @returns {number}
|
||||
*/
|
||||
getUTCSeconds(): number;
|
||||
/**
|
||||
* Returns the UTC milliseconds.
|
||||
* @returns {string | null}
|
||||
*/
|
||||
toISOString(): string | null;
|
||||
/**
|
||||
* Returns the date as a JSON string.
|
||||
* @returns {string | null}
|
||||
*/
|
||||
toJSON(): string | null;
|
||||
/**
|
||||
* Sets the day.
|
||||
* @param d
|
||||
*/
|
||||
setDate(d: number): void;
|
||||
/**
|
||||
* Sets the year.
|
||||
* @param y
|
||||
*/
|
||||
setFullYear(y: number): void;
|
||||
/**
|
||||
* Sets the day of the week.
|
||||
* @param d
|
||||
*/
|
||||
setDay(d: number): void;
|
||||
/**
|
||||
* Sets the month.
|
||||
* @param m
|
||||
*/
|
||||
setMonth(m: number): void;
|
||||
/**
|
||||
* Sets the hour.
|
||||
* @param h
|
||||
*/
|
||||
setHours(h: number): void;
|
||||
/**
|
||||
* Sets the minutes.
|
||||
* @param m
|
||||
*/
|
||||
setMinutes(m: number): void;
|
||||
/**
|
||||
* Sets the seconds.
|
||||
* @param s
|
||||
*/
|
||||
setSeconds(s: number): void;
|
||||
/**
|
||||
* Sets the milliseconds.
|
||||
* @param s
|
||||
*/
|
||||
setMilliseconds(s: number): void;
|
||||
/**
|
||||
* Returns the date as a string.
|
||||
* @returns {string}
|
||||
*/
|
||||
toString(): string;
|
||||
/**
|
||||
* Returns the date as a Date object.
|
||||
* @returns {Date}
|
||||
*/
|
||||
toDate(): Date;
|
||||
/**
|
||||
* Returns true if the day is the last day of the month.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
isLastDayOfMonth(): boolean;
|
||||
/**
|
||||
* Returns true if the day is the last weekday of the month.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
isLastWeekdayOfMonth(): boolean;
|
||||
/**
|
||||
* Primarily for internal use.
|
||||
* @param {DateMathOp} op - The operation to perform.
|
||||
* @param {TimeUnit} unit - The unit of time to use.
|
||||
* @param {number} [hoursLength] - The length of the hours. Required when unit is not month or day.
|
||||
*/
|
||||
applyDateOperation(op: DateMathOp, unit: TimeUnit, hoursLength?: number): void;
|
||||
}
|
||||
export default CronDate;
|
||||
118
node_modules/cron-parser/dist/types/CronExpression.d.ts
generated
vendored
Normal file
118
node_modules/cron-parser/dist/types/CronExpression.d.ts
generated
vendored
Normal file
@@ -0,0 +1,118 @@
|
||||
import { CronDate } from './CronDate';
|
||||
import { CronFieldCollection } from './CronFieldCollection';
|
||||
export type CronExpressionOptions = {
|
||||
currentDate?: Date | string | number | CronDate;
|
||||
endDate?: Date | string | number | CronDate;
|
||||
startDate?: Date | string | number | CronDate;
|
||||
tz?: string;
|
||||
expression?: string;
|
||||
hashSeed?: string;
|
||||
strict?: boolean;
|
||||
};
|
||||
/**
|
||||
* Error message for when the current date is outside the specified time span.
|
||||
*/
|
||||
export declare const TIME_SPAN_OUT_OF_BOUNDS_ERROR_MESSAGE = "Out of the time span range";
|
||||
/**
|
||||
* Error message for when the loop limit is exceeded during iteration.
|
||||
*/
|
||||
export declare const LOOPS_LIMIT_EXCEEDED_ERROR_MESSAGE = "Invalid expression, loop limit exceeded";
|
||||
/**
|
||||
* Class representing a Cron expression.
|
||||
*/
|
||||
export declare class CronExpression {
|
||||
#private;
|
||||
/**
|
||||
* Creates a new CronExpression instance.
|
||||
*
|
||||
* @param {CronFieldCollection} fields - Cron fields.
|
||||
* @param {CronExpressionOptions} options - Parser options.
|
||||
*/
|
||||
constructor(fields: CronFieldCollection, options: CronExpressionOptions);
|
||||
/**
|
||||
* Getter for the cron fields.
|
||||
*
|
||||
* @returns {CronFieldCollection} Cron fields.
|
||||
*/
|
||||
get fields(): CronFieldCollection;
|
||||
/**
|
||||
* Converts cron fields back to a CronExpression instance.
|
||||
*
|
||||
* @public
|
||||
* @param {Record<string, number[]>} fields - The input cron fields object.
|
||||
* @param {CronExpressionOptions} [options] - Optional parsing options.
|
||||
* @returns {CronExpression} - A new CronExpression instance.
|
||||
*/
|
||||
static fieldsToExpression(fields: CronFieldCollection, options?: CronExpressionOptions): CronExpression;
|
||||
/**
|
||||
* Find the next scheduled date based on the cron expression.
|
||||
* @returns {CronDate} - The next scheduled date or an ES6 compatible iterator object.
|
||||
* @memberof CronExpression
|
||||
* @public
|
||||
*/
|
||||
next(): CronDate;
|
||||
/**
|
||||
* Find the previous scheduled date based on the cron expression.
|
||||
* @returns {CronDate} - The previous scheduled date or an ES6 compatible iterator object.
|
||||
* @memberof CronExpression
|
||||
* @public
|
||||
*/
|
||||
prev(): CronDate;
|
||||
/**
|
||||
* Check if there is a next scheduled date based on the current date and cron expression.
|
||||
* @returns {boolean} - Returns true if there is a next scheduled date, false otherwise.
|
||||
* @memberof CronExpression
|
||||
* @public
|
||||
*/
|
||||
hasNext(): boolean;
|
||||
/**
|
||||
* Check if there is a previous scheduled date based on the current date and cron expression.
|
||||
* @returns {boolean} - Returns true if there is a previous scheduled date, false otherwise.
|
||||
* @memberof CronExpression
|
||||
* @public
|
||||
*/
|
||||
hasPrev(): boolean;
|
||||
/**
|
||||
* Iterate over a specified number of steps and optionally execute a callback function for each step.
|
||||
* @param {number} steps - The number of steps to iterate. Positive value iterates forward, negative value iterates backward.
|
||||
* @returns {CronDate[]} - An array of iterator fields or CronDate objects.
|
||||
* @memberof CronExpression
|
||||
* @public
|
||||
*/
|
||||
take(limit: number): CronDate[];
|
||||
/**
|
||||
* Reset the iterators current date to a new date or the initial date.
|
||||
* @param {Date | CronDate} [newDate] - Optional new date to reset to. If not provided, it will reset to the initial date.
|
||||
* @memberof CronExpression
|
||||
* @public
|
||||
*/
|
||||
reset(newDate?: Date | CronDate): void;
|
||||
/**
|
||||
* Generate a string representation of the cron expression.
|
||||
* @param {boolean} [includeSeconds=false] - Whether to include the seconds field in the string representation.
|
||||
* @returns {string} - The string representation of the cron expression.
|
||||
* @memberof CronExpression
|
||||
* @public
|
||||
*/
|
||||
stringify(includeSeconds?: boolean): string;
|
||||
/**
|
||||
* Check if the cron expression includes the given date
|
||||
* @param {Date|CronDate} date
|
||||
* @returns {boolean}
|
||||
*/
|
||||
includesDate(date: Date | CronDate): boolean;
|
||||
/**
|
||||
* Returns the string representation of the cron expression.
|
||||
* @returns {CronDate} - The next schedule date.
|
||||
*/
|
||||
toString(): string;
|
||||
/**
|
||||
* Returns an iterator for iterating through future CronDate instances
|
||||
*
|
||||
* @name Symbol.iterator
|
||||
* @memberof CronExpression
|
||||
* @returns {Iterator<CronDate>} An iterator object for CronExpression that returns CronDate values.
|
||||
*/
|
||||
[Symbol.iterator](): Iterator<CronDate>;
|
||||
}
|
||||
export default CronExpression;
|
||||
70
node_modules/cron-parser/dist/types/CronExpressionParser.d.ts
generated
vendored
Normal file
70
node_modules/cron-parser/dist/types/CronExpressionParser.d.ts
generated
vendored
Normal file
@@ -0,0 +1,70 @@
|
||||
import { CronExpression, CronExpressionOptions } from './CronExpression';
|
||||
export declare enum PredefinedExpressions {
|
||||
'@yearly' = "0 0 0 1 1 *",
|
||||
'@annually' = "0 0 0 1 1 *",
|
||||
'@monthly' = "0 0 0 1 * *",
|
||||
'@weekly' = "0 0 0 * * 0",
|
||||
'@daily' = "0 0 0 * * *",
|
||||
'@hourly' = "0 0 * * * *",
|
||||
'@minutely' = "0 * * * * *",
|
||||
'@secondly' = "* * * * * *",
|
||||
'@weekdays' = "0 0 0 * * 1-5",
|
||||
'@weekends' = "0 0 0 * * 0,6"
|
||||
}
|
||||
export declare enum CronUnit {
|
||||
Second = "Second",
|
||||
Minute = "Minute",
|
||||
Hour = "Hour",
|
||||
DayOfMonth = "DayOfMonth",
|
||||
Month = "Month",
|
||||
DayOfWeek = "DayOfWeek"
|
||||
}
|
||||
export declare enum Months {
|
||||
jan = 1,
|
||||
feb = 2,
|
||||
mar = 3,
|
||||
apr = 4,
|
||||
may = 5,
|
||||
jun = 6,
|
||||
jul = 7,
|
||||
aug = 8,
|
||||
sep = 9,
|
||||
oct = 10,
|
||||
nov = 11,
|
||||
dec = 12
|
||||
}
|
||||
export declare enum DayOfWeek {
|
||||
sun = 0,
|
||||
mon = 1,
|
||||
tue = 2,
|
||||
wed = 3,
|
||||
thu = 4,
|
||||
fri = 5,
|
||||
sat = 6
|
||||
}
|
||||
export type RawCronFields = {
|
||||
second: string;
|
||||
minute: string;
|
||||
hour: string;
|
||||
dayOfMonth: string;
|
||||
month: string;
|
||||
dayOfWeek: string;
|
||||
};
|
||||
/**
|
||||
* Static class that parses a cron expression and returns a CronExpression object.
|
||||
* @static
|
||||
* @class CronExpressionParser
|
||||
*/
|
||||
export declare class CronExpressionParser {
|
||||
#private;
|
||||
/**
|
||||
* Parses a cron expression and returns a CronExpression object.
|
||||
* @param {string} expression - The cron expression to parse.
|
||||
* @param {CronExpressionOptions} [options={}] - The options to use when parsing the expression.
|
||||
* @param {boolean} [options.strict=false] - If true, will throw an error if the expression contains both dayOfMonth and dayOfWeek.
|
||||
* @param {CronDate} [options.currentDate=new CronDate(undefined, 'UTC')] - The date to use when calculating the next/previous occurrence.
|
||||
*
|
||||
* @returns {CronExpression} A CronExpression object.
|
||||
*/
|
||||
static parse(expression: string, options?: CronExpressionOptions): CronExpression;
|
||||
}
|
||||
153
node_modules/cron-parser/dist/types/CronFieldCollection.d.ts
generated
vendored
Normal file
153
node_modules/cron-parser/dist/types/CronFieldCollection.d.ts
generated
vendored
Normal file
@@ -0,0 +1,153 @@
|
||||
import { CronSecond, CronMinute, CronHour, CronDayOfMonth, CronMonth, CronDayOfWeek, CronField, SerializedCronField, CronChars } from './fields';
|
||||
import { SixtyRange, HourRange, DayOfMonthRange, MonthRange, DayOfWeekRange } from './fields/types';
|
||||
export type FieldRange = {
|
||||
start: number | CronChars;
|
||||
count: number;
|
||||
end?: number;
|
||||
step?: number;
|
||||
};
|
||||
export type CronFields = {
|
||||
second: CronSecond;
|
||||
minute: CronMinute;
|
||||
hour: CronHour;
|
||||
dayOfMonth: CronDayOfMonth;
|
||||
month: CronMonth;
|
||||
dayOfWeek: CronDayOfWeek;
|
||||
};
|
||||
export type CronFieldOverride = {
|
||||
second?: CronSecond | SixtyRange[];
|
||||
minute?: CronMinute | SixtyRange[];
|
||||
hour?: CronHour | HourRange[];
|
||||
dayOfMonth?: CronDayOfMonth | DayOfMonthRange[];
|
||||
month?: CronMonth | MonthRange[];
|
||||
dayOfWeek?: CronDayOfWeek | DayOfWeekRange[];
|
||||
};
|
||||
export type SerializedCronFields = {
|
||||
second: SerializedCronField;
|
||||
minute: SerializedCronField;
|
||||
hour: SerializedCronField;
|
||||
dayOfMonth: SerializedCronField;
|
||||
month: SerializedCronField;
|
||||
dayOfWeek: SerializedCronField;
|
||||
};
|
||||
/**
|
||||
* Represents a complete set of cron fields.
|
||||
* @class CronFieldCollection
|
||||
*/
|
||||
export declare class CronFieldCollection {
|
||||
#private;
|
||||
/**
|
||||
* Creates a new CronFieldCollection instance by partially overriding fields from an existing one.
|
||||
* @param {CronFieldCollection} base - The base CronFieldCollection to copy fields from
|
||||
* @param {CronFieldOverride} fields - The fields to override, can be CronField instances or raw values
|
||||
* @returns {CronFieldCollection} A new CronFieldCollection instance
|
||||
* @example
|
||||
* const base = new CronFieldCollection({
|
||||
* second: new CronSecond([0]),
|
||||
* minute: new CronMinute([0]),
|
||||
* hour: new CronHour([12]),
|
||||
* dayOfMonth: new CronDayOfMonth([1]),
|
||||
* month: new CronMonth([1]),
|
||||
* dayOfWeek: new CronDayOfWeek([1])
|
||||
* });
|
||||
*
|
||||
* // Using CronField instances
|
||||
* const modified1 = CronFieldCollection.from(base, {
|
||||
* hour: new CronHour([15]),
|
||||
* minute: new CronMinute([30])
|
||||
* });
|
||||
*
|
||||
* // Using raw values
|
||||
* const modified2 = CronFieldCollection.from(base, {
|
||||
* hour: [15], // Will create new CronHour
|
||||
* minute: [30] // Will create new CronMinute
|
||||
* });
|
||||
*/
|
||||
static from(base: CronFieldCollection, fields: CronFieldOverride): CronFieldCollection;
|
||||
/**
|
||||
* Resolves a field value, either using the provided CronField instance or creating a new one from raw values.
|
||||
* @param constructor - The constructor for creating new field instances
|
||||
* @param baseField - The base field to use if no override is provided
|
||||
* @param fieldValue - The override value, either a CronField instance or raw values
|
||||
* @returns The resolved CronField instance
|
||||
* @private
|
||||
*/
|
||||
private static resolveField;
|
||||
/**
|
||||
* CronFieldCollection constructor. Initializes the cron fields with the provided values.
|
||||
* @param {CronFields} param0 - The cron fields values
|
||||
* @throws {Error} if validation fails
|
||||
* @example
|
||||
* const cronFields = new CronFieldCollection({
|
||||
* second: new CronSecond([0]),
|
||||
* minute: new CronMinute([0, 30]),
|
||||
* hour: new CronHour([9]),
|
||||
* dayOfMonth: new CronDayOfMonth([15]),
|
||||
* month: new CronMonth([1]),
|
||||
* dayOfWeek: new CronDayOfTheWeek([1, 2, 3, 4, 5]),
|
||||
* })
|
||||
*
|
||||
* console.log(cronFields.second.values); // [0]
|
||||
* console.log(cronFields.minute.values); // [0, 30]
|
||||
* console.log(cronFields.hour.values); // [9]
|
||||
* console.log(cronFields.dayOfMonth.values); // [15]
|
||||
* console.log(cronFields.month.values); // [1]
|
||||
* console.log(cronFields.dayOfWeek.values); // [1, 2, 3, 4, 5]
|
||||
*/
|
||||
constructor({ second, minute, hour, dayOfMonth, month, dayOfWeek }: CronFields);
|
||||
/**
|
||||
* Returns the second field.
|
||||
* @returns {CronSecond}
|
||||
*/
|
||||
get second(): CronSecond;
|
||||
/**
|
||||
* Returns the minute field.
|
||||
* @returns {CronMinute}
|
||||
*/
|
||||
get minute(): CronMinute;
|
||||
/**
|
||||
* Returns the hour field.
|
||||
* @returns {CronHour}
|
||||
*/
|
||||
get hour(): CronHour;
|
||||
/**
|
||||
* Returns the day of the month field.
|
||||
* @returns {CronDayOfMonth}
|
||||
*/
|
||||
get dayOfMonth(): CronDayOfMonth;
|
||||
/**
|
||||
* Returns the month field.
|
||||
* @returns {CronMonth}
|
||||
*/
|
||||
get month(): CronMonth;
|
||||
/**
|
||||
* Returns the day of the week field.
|
||||
* @returns {CronDayOfWeek}
|
||||
*/
|
||||
get dayOfWeek(): CronDayOfWeek;
|
||||
/**
|
||||
* Returns a string representation of the cron fields.
|
||||
* @param {(number | CronChars)[]} input - The cron fields values
|
||||
* @static
|
||||
* @returns {FieldRange[]} - The compacted cron fields
|
||||
*/
|
||||
static compactField(input: (number | CronChars)[]): FieldRange[];
|
||||
/**
|
||||
* Returns a string representation of the cron fields.
|
||||
* @param {CronField} field - The cron field to stringify
|
||||
* @static
|
||||
* @returns {string} - The stringified cron field
|
||||
*/
|
||||
stringifyField(field: CronField): string;
|
||||
/**
|
||||
* Returns a string representation of the cron field values.
|
||||
* @param {boolean} includeSeconds - Whether to include seconds in the output
|
||||
* @returns {string} The formatted cron string
|
||||
*/
|
||||
stringify(includeSeconds?: boolean): string;
|
||||
/**
|
||||
* Returns a serialized representation of the cron fields values.
|
||||
* @returns {SerializedCronFields} An object containing the cron field values
|
||||
*/
|
||||
serialize(): SerializedCronFields;
|
||||
}
|
||||
30
node_modules/cron-parser/dist/types/CronFileParser.d.ts
generated
vendored
Normal file
30
node_modules/cron-parser/dist/types/CronFileParser.d.ts
generated
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
import { CronExpression } from './CronExpression';
|
||||
export type CronFileParserResult = {
|
||||
variables: {
|
||||
[key: string]: string;
|
||||
};
|
||||
expressions: CronExpression[];
|
||||
errors: {
|
||||
[key: string]: unknown;
|
||||
};
|
||||
};
|
||||
/**
|
||||
* Parser for crontab files that handles both synchronous and asynchronous operations.
|
||||
*/
|
||||
export declare class CronFileParser {
|
||||
#private;
|
||||
/**
|
||||
* Parse a crontab file asynchronously
|
||||
* @param filePath Path to crontab file
|
||||
* @returns Promise resolving to parse results
|
||||
* @throws If file cannot be read
|
||||
*/
|
||||
static parseFile(filePath: string): Promise<CronFileParserResult>;
|
||||
/**
|
||||
* Parse a crontab file synchronously
|
||||
* @param filePath Path to crontab file
|
||||
* @returns Parse results
|
||||
* @throws If file cannot be read
|
||||
*/
|
||||
static parseFileSync(filePath: string): CronFileParserResult;
|
||||
}
|
||||
25
node_modules/cron-parser/dist/types/fields/CronDayOfMonth.d.ts
generated
vendored
Normal file
25
node_modules/cron-parser/dist/types/fields/CronDayOfMonth.d.ts
generated
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
import { CronField, CronFieldOptions } from './CronField';
|
||||
import { CronChars, CronMax, CronMin, DayOfMonthRange } from './types';
|
||||
/**
|
||||
* Represents the "day of the month" field within a cron expression.
|
||||
* @class CronDayOfMonth
|
||||
* @extends CronField
|
||||
*/
|
||||
export declare class CronDayOfMonth extends CronField {
|
||||
static get min(): CronMin;
|
||||
static get max(): CronMax;
|
||||
static get chars(): CronChars[];
|
||||
static get validChars(): RegExp;
|
||||
/**
|
||||
* CronDayOfMonth constructor. Initializes the "day of the month" field with the provided values.
|
||||
* @param {DayOfMonthRange[]} values - Values for the "day of the month" field
|
||||
* @param {CronFieldOptions} [options] - Options provided by the parser
|
||||
* @throws {Error} if validation fails
|
||||
*/
|
||||
constructor(values: DayOfMonthRange[], options?: CronFieldOptions);
|
||||
/**
|
||||
* Returns an array of allowed values for the "day of the month" field.
|
||||
* @returns {DayOfMonthRange[]}
|
||||
*/
|
||||
get values(): DayOfMonthRange[];
|
||||
}
|
||||
30
node_modules/cron-parser/dist/types/fields/CronDayOfWeek.d.ts
generated
vendored
Normal file
30
node_modules/cron-parser/dist/types/fields/CronDayOfWeek.d.ts
generated
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
import { CronField, CronFieldOptions } from './CronField';
|
||||
import { CronChars, CronMax, CronMin, DayOfWeekRange } from './types';
|
||||
/**
|
||||
* Represents the "day of the week" field within a cron expression.
|
||||
* @class CronDayOfTheWeek
|
||||
* @extends CronField
|
||||
*/
|
||||
export declare class CronDayOfWeek extends CronField {
|
||||
static get min(): CronMin;
|
||||
static get max(): CronMax;
|
||||
static get chars(): readonly CronChars[];
|
||||
static get validChars(): RegExp;
|
||||
/**
|
||||
* CronDayOfTheWeek constructor. Initializes the "day of the week" field with the provided values.
|
||||
* @param {DayOfWeekRange[]} values - Values for the "day of the week" field
|
||||
* @param {CronFieldOptions} [options] - Options provided by the parser
|
||||
*/
|
||||
constructor(values: DayOfWeekRange[], options?: CronFieldOptions);
|
||||
/**
|
||||
* Returns an array of allowed values for the "day of the week" field.
|
||||
* @returns {DayOfWeekRange[]}
|
||||
*/
|
||||
get values(): DayOfWeekRange[];
|
||||
/**
|
||||
* Returns the nth day of the week if specified in the cron expression.
|
||||
* This is used for the '#' character in the cron expression.
|
||||
* @returns {number} The nth day of the week (1-5) or 0 if not specified.
|
||||
*/
|
||||
get nthDay(): number;
|
||||
}
|
||||
114
node_modules/cron-parser/dist/types/fields/CronField.d.ts
generated
vendored
Normal file
114
node_modules/cron-parser/dist/types/fields/CronField.d.ts
generated
vendored
Normal file
@@ -0,0 +1,114 @@
|
||||
import { CronChars, CronConstraints, CronFieldType, CronMax, CronMin } from './types';
|
||||
/**
|
||||
* Represents the serialized form of a cron field.
|
||||
* @typedef {Object} SerializedCronField
|
||||
* @property {boolean} wildcard - Indicates if the field is a wildcard.
|
||||
* @property {(number|string)[]} values - The values of the field.
|
||||
*/
|
||||
export type SerializedCronField = {
|
||||
wildcard: boolean;
|
||||
values: (number | string)[];
|
||||
};
|
||||
/**
|
||||
* Represents the options for a cron field.
|
||||
* @typedef {Object} CronFieldOptions
|
||||
* @property {string} rawValue - The raw value of the field.
|
||||
* @property {boolean} [wildcard] - Indicates if the field is a wildcard.
|
||||
* @property {number} [nthDayOfWeek] - The nth day of the week.
|
||||
*/
|
||||
export type CronFieldOptions = {
|
||||
rawValue?: string;
|
||||
wildcard?: boolean;
|
||||
nthDayOfWeek?: number;
|
||||
};
|
||||
/**
|
||||
* Represents a field within a cron expression.
|
||||
* This is a base class and should not be instantiated directly.
|
||||
* @class CronField
|
||||
*/
|
||||
export declare abstract class CronField {
|
||||
#private;
|
||||
protected readonly options: CronFieldOptions & {
|
||||
rawValue: string;
|
||||
};
|
||||
/**
|
||||
* Returns the minimum value allowed for this field.
|
||||
*/
|
||||
static get min(): CronMin;
|
||||
/**
|
||||
* Returns the maximum value allowed for this field.
|
||||
*/
|
||||
static get max(): CronMax;
|
||||
/**
|
||||
* Returns the allowed characters for this field.
|
||||
*/
|
||||
static get chars(): readonly CronChars[];
|
||||
/**
|
||||
* Returns the regular expression used to validate this field.
|
||||
*/
|
||||
static get validChars(): RegExp;
|
||||
/**
|
||||
* Returns the constraints for this field.
|
||||
*/
|
||||
static get constraints(): CronConstraints;
|
||||
/**
|
||||
* CronField constructor. Initializes the field with the provided values.
|
||||
* @param {number[] | string[]} values - Values for this field
|
||||
* @param {CronFieldOptions} [options] - Options provided by the parser
|
||||
* @throws {TypeError} if the constructor is called directly
|
||||
* @throws {Error} if validation fails
|
||||
*/
|
||||
protected constructor(values: (number | string)[], options?: CronFieldOptions);
|
||||
/**
|
||||
* Returns the minimum value allowed for this field.
|
||||
* @returns {number}
|
||||
*/
|
||||
get min(): number;
|
||||
/**
|
||||
* Returns the maximum value allowed for this field.
|
||||
* @returns {number}
|
||||
*/
|
||||
get max(): number;
|
||||
/**
|
||||
* Returns an array of allowed special characters for this field.
|
||||
* @returns {string[]}
|
||||
*/
|
||||
get chars(): readonly string[];
|
||||
/**
|
||||
* Indicates whether this field has a "last" character.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
get hasLastChar(): boolean;
|
||||
/**
|
||||
* Indicates whether this field has a "question mark" character.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
get hasQuestionMarkChar(): boolean;
|
||||
/**
|
||||
* Indicates whether this field is a wildcard.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
get isWildcard(): boolean;
|
||||
/**
|
||||
* Returns an array of allowed values for this field.
|
||||
* @returns {CronFieldType}
|
||||
*/
|
||||
get values(): CronFieldType;
|
||||
/**
|
||||
* Helper function to sort values in ascending order.
|
||||
* @param {number | string} a - First value to compare
|
||||
* @param {number | string} b - Second value to compare
|
||||
* @returns {number} - A negative, zero, or positive value, depending on the sort order
|
||||
*/
|
||||
static sorter(a: number | string, b: number | string): number;
|
||||
/**
|
||||
* Serializes the field to an object.
|
||||
* @returns {SerializedCronField}
|
||||
*/
|
||||
serialize(): SerializedCronField;
|
||||
/**
|
||||
* Validates the field values against the allowed range and special characters.
|
||||
* @throws {Error} if validation fails
|
||||
*/
|
||||
validate(): void;
|
||||
}
|
||||
23
node_modules/cron-parser/dist/types/fields/CronHour.d.ts
generated
vendored
Normal file
23
node_modules/cron-parser/dist/types/fields/CronHour.d.ts
generated
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
import { CronField, CronFieldOptions } from './CronField';
|
||||
import { CronChars, CronMax, CronMin, HourRange } from './types';
|
||||
/**
|
||||
* Represents the "hour" field within a cron expression.
|
||||
* @class CronHour
|
||||
* @extends CronField
|
||||
*/
|
||||
export declare class CronHour extends CronField {
|
||||
static get min(): CronMin;
|
||||
static get max(): CronMax;
|
||||
static get chars(): readonly CronChars[];
|
||||
/**
|
||||
* CronHour constructor. Initializes the "hour" field with the provided values.
|
||||
* @param {HourRange[]} values - Values for the "hour" field
|
||||
* @param {CronFieldOptions} [options] - Options provided by the parser
|
||||
*/
|
||||
constructor(values: HourRange[], options?: CronFieldOptions);
|
||||
/**
|
||||
* Returns an array of allowed values for the "hour" field.
|
||||
* @returns {HourRange[]}
|
||||
*/
|
||||
get values(): HourRange[];
|
||||
}
|
||||
23
node_modules/cron-parser/dist/types/fields/CronMinute.d.ts
generated
vendored
Normal file
23
node_modules/cron-parser/dist/types/fields/CronMinute.d.ts
generated
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
import { CronField, CronFieldOptions } from './CronField';
|
||||
import { CronChars, CronMax, CronMin, SixtyRange } from './types';
|
||||
/**
|
||||
* Represents the "second" field within a cron expression.
|
||||
* @class CronSecond
|
||||
* @extends CronField
|
||||
*/
|
||||
export declare class CronMinute extends CronField {
|
||||
static get min(): CronMin;
|
||||
static get max(): CronMax;
|
||||
static get chars(): readonly CronChars[];
|
||||
/**
|
||||
* CronSecond constructor. Initializes the "second" field with the provided values.
|
||||
* @param {SixtyRange[]} values - Values for the "second" field
|
||||
* @param {CronFieldOptions} [options] - Options provided by the parser
|
||||
*/
|
||||
constructor(values: SixtyRange[], options?: CronFieldOptions);
|
||||
/**
|
||||
* Returns an array of allowed values for the "second" field.
|
||||
* @returns {SixtyRange[]}
|
||||
*/
|
||||
get values(): SixtyRange[];
|
||||
}
|
||||
24
node_modules/cron-parser/dist/types/fields/CronMonth.d.ts
generated
vendored
Normal file
24
node_modules/cron-parser/dist/types/fields/CronMonth.d.ts
generated
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
import { CronField, CronFieldOptions } from './CronField';
|
||||
import { CronChars, CronMax, CronMin, MonthRange } from './types';
|
||||
/**
|
||||
* Represents the "day of the month" field within a cron expression.
|
||||
* @class CronDayOfMonth
|
||||
* @extends CronField
|
||||
*/
|
||||
export declare class CronMonth extends CronField {
|
||||
static get min(): CronMin;
|
||||
static get max(): CronMax;
|
||||
static get chars(): readonly CronChars[];
|
||||
static get daysInMonth(): readonly number[];
|
||||
/**
|
||||
* CronDayOfMonth constructor. Initializes the "day of the month" field with the provided values.
|
||||
* @param {MonthRange[]} values - Values for the "day of the month" field
|
||||
* @param {CronFieldOptions} [options] - Options provided by the parser
|
||||
*/
|
||||
constructor(values: MonthRange[], options?: CronFieldOptions);
|
||||
/**
|
||||
* Returns an array of allowed values for the "day of the month" field.
|
||||
* @returns {MonthRange[]}
|
||||
*/
|
||||
get values(): MonthRange[];
|
||||
}
|
||||
23
node_modules/cron-parser/dist/types/fields/CronSecond.d.ts
generated
vendored
Normal file
23
node_modules/cron-parser/dist/types/fields/CronSecond.d.ts
generated
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
import { CronChars, CronMax, CronMin, SixtyRange } from './types';
|
||||
import { CronField, CronFieldOptions } from './CronField';
|
||||
/**
|
||||
* Represents the "second" field within a cron expression.
|
||||
* @class CronSecond
|
||||
* @extends CronField
|
||||
*/
|
||||
export declare class CronSecond extends CronField {
|
||||
static get min(): CronMin;
|
||||
static get max(): CronMax;
|
||||
static get chars(): readonly CronChars[];
|
||||
/**
|
||||
* CronSecond constructor. Initializes the "second" field with the provided values.
|
||||
* @param {SixtyRange[]} values - Values for the "second" field
|
||||
* @param {CronFieldOptions} [options] - Options provided by the parser
|
||||
*/
|
||||
constructor(values: SixtyRange[], options?: CronFieldOptions);
|
||||
/**
|
||||
* Returns an array of allowed values for the "second" field.
|
||||
* @returns {SixtyRange[]}
|
||||
*/
|
||||
get values(): SixtyRange[];
|
||||
}
|
||||
8
node_modules/cron-parser/dist/types/fields/index.d.ts
generated
vendored
Normal file
8
node_modules/cron-parser/dist/types/fields/index.d.ts
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
export * from './types';
|
||||
export * from './CronDayOfMonth';
|
||||
export * from './CronDayOfWeek';
|
||||
export * from './CronField';
|
||||
export * from './CronHour';
|
||||
export * from './CronMinute';
|
||||
export * from './CronMonth';
|
||||
export * from './CronSecond';
|
||||
18
node_modules/cron-parser/dist/types/fields/types.d.ts
generated
vendored
Normal file
18
node_modules/cron-parser/dist/types/fields/types.d.ts
generated
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
export type RangeFrom<LENGTH extends number, ACC extends unknown[] = []> = ACC['length'] extends LENGTH ? ACC : RangeFrom<LENGTH, [...ACC, 1]>;
|
||||
export type IntRange<FROM extends number[], TO extends number, ACC extends number = never> = FROM['length'] extends TO ? ACC | TO : IntRange<[...FROM, 1], TO, ACC | FROM['length']>;
|
||||
export type SixtyRange = IntRange<RangeFrom<0>, 59>;
|
||||
export type HourRange = IntRange<RangeFrom<0>, 23>;
|
||||
export type DayOfMonthRange = IntRange<RangeFrom<1>, 31> | 'L';
|
||||
export type MonthRange = IntRange<RangeFrom<1>, 12>;
|
||||
export type DayOfWeekRange = IntRange<RangeFrom<0>, 7> | 'L';
|
||||
export type CronFieldType = SixtyRange[] | HourRange[] | DayOfMonthRange[] | MonthRange[] | DayOfWeekRange[];
|
||||
export type CronChars = 'L' | 'W';
|
||||
export type CronMin = 0 | 1;
|
||||
export type CronMax = 7 | 12 | 23 | 31 | 59;
|
||||
export type ParseRangeResponse = number[] | string[] | number | string;
|
||||
export type CronConstraints = {
|
||||
min: CronMin;
|
||||
max: CronMax;
|
||||
chars: readonly CronChars[];
|
||||
validChars: RegExp;
|
||||
};
|
||||
8
node_modules/cron-parser/dist/types/index.d.ts
generated
vendored
Normal file
8
node_modules/cron-parser/dist/types/index.d.ts
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
import { CronExpressionParser } from './CronExpressionParser';
|
||||
export { CronDate } from './CronDate';
|
||||
export { CronFieldCollection } from './CronFieldCollection';
|
||||
export { CronExpression, CronExpressionOptions } from './CronExpression';
|
||||
export { CronExpressionParser } from './CronExpressionParser';
|
||||
export { CronFileParser, CronFileParserResult } from './CronFileParser';
|
||||
export * from './fields';
|
||||
export default CronExpressionParser;
|
||||
10
node_modules/cron-parser/dist/types/utils/random.d.ts
generated
vendored
Normal file
10
node_modules/cron-parser/dist/types/utils/random.d.ts
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
/**
|
||||
* A type representing a Pseudorandom Number Generator, similar to Math.random()
|
||||
*/
|
||||
export type PRNG = () => number;
|
||||
/**
|
||||
* Generates a PRNG using a given seed. When not provided, the seed is randomly generated
|
||||
* @param {string} str A string to derive the seed from
|
||||
* @returns {PRNG} A random number generator correctly seeded
|
||||
*/
|
||||
export declare function seededRandom(str?: string): PRNG;
|
||||
38
node_modules/cron-parser/dist/utils/random.js
generated
vendored
Normal file
38
node_modules/cron-parser/dist/utils/random.js
generated
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.seededRandom = seededRandom;
|
||||
/**
|
||||
* Computes a numeric hash from a given string
|
||||
* @param {string} str A value to hash
|
||||
* @returns {number} A numeric hash computed from the given value
|
||||
*/
|
||||
function xfnv1a(str) {
|
||||
let h = 2166136261 >>> 0;
|
||||
for (let i = 0; i < str.length; i++) {
|
||||
h ^= str.charCodeAt(i);
|
||||
h = Math.imul(h, 16777619);
|
||||
}
|
||||
return () => h >>> 0;
|
||||
}
|
||||
/**
|
||||
* Initialize a new PRNG using a given seed
|
||||
* @param {number} seed The seed used to initialize the PRNG
|
||||
* @returns {PRNG} A random number generator
|
||||
*/
|
||||
function mulberry32(seed) {
|
||||
return () => {
|
||||
let t = (seed += 0x6d2b79f5);
|
||||
t = Math.imul(t ^ (t >>> 15), t | 1);
|
||||
t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
|
||||
return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Generates a PRNG using a given seed. When not provided, the seed is randomly generated
|
||||
* @param {string} str A string to derive the seed from
|
||||
* @returns {PRNG} A random number generator correctly seeded
|
||||
*/
|
||||
function seededRandom(str) {
|
||||
const seed = str ? xfnv1a(str)() : Math.floor(Math.random() * 10_000_000_000);
|
||||
return mulberry32(seed);
|
||||
}
|
||||
117
node_modules/cron-parser/package.json
generated
vendored
Normal file
117
node_modules/cron-parser/package.json
generated
vendored
Normal file
@@ -0,0 +1,117 @@
|
||||
{
|
||||
"name": "cron-parser",
|
||||
"version": "5.4.0",
|
||||
"description": "Node.js library for parsing crontab instructions",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/types/index.d.ts",
|
||||
"type": "commonjs",
|
||||
"scripts": {
|
||||
"clean": "rimraf dist",
|
||||
"bench": "cross-env node -r ts-node/register benchmarks/index.ts",
|
||||
"bench:pattern": "cross-env node -r ts-node/register benchmarks/pattern.ts",
|
||||
"bench:clean": "rimraf benchmarks/versions && rimraf benchmarks/results",
|
||||
"build": "npm run clean && tsc -p tsconfig.json",
|
||||
"prepublishOnly": "npm run build",
|
||||
"prepare": "husky && npm run build",
|
||||
"precommit": "lint-staged",
|
||||
"lint": "eslint .",
|
||||
"lint:fix": "eslint --fix .",
|
||||
"lint:debug": "cross-env DEBUG=eslint:cli-engine eslint .",
|
||||
"format": "prettier --write \"**/*.{ts,js,json,md}\"",
|
||||
"format:check": "prettier --check \"**/*.{ts,js,json,md}\"",
|
||||
"test:unit": "cross-env TZ=UTC jest",
|
||||
"test:coverage": "cross-env TZ=UTC jest --coverage",
|
||||
"generate-badges": "jest-coverage-badges",
|
||||
"test:types": "npm run build && tsd",
|
||||
"test": "cross-env TZ=UTC npm run lint && npm run test:types && npm run test:coverage && npm run generate-badges",
|
||||
"docs": "rimraf docs && typedoc --out docs --readme none --name 'CronParser' src"
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
"LICENSE",
|
||||
"README.md"
|
||||
],
|
||||
"dependencies": {
|
||||
"luxon": "^3.7.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tsd/typescript": "^5.8.2",
|
||||
"@types/jest": "^29.5.14",
|
||||
"@types/luxon": "^3.6.2",
|
||||
"@types/node": "^22.14.0",
|
||||
"@typescript-eslint/eslint-plugin": "^8.29.0",
|
||||
"@typescript-eslint/parser": "^8.29.0",
|
||||
"chalk": "^5.4.1",
|
||||
"cli-table3": "^0.6.5",
|
||||
"cross-env": "^7.0.3",
|
||||
"eslint": "^9.23.0",
|
||||
"eslint-config-prettier": "^10.1.1",
|
||||
"eslint-plugin-prettier": "^5.2.6",
|
||||
"husky": "^9.1.7",
|
||||
"jest": "^29.7.0",
|
||||
"jest-coverage-badges": "^1.0.0",
|
||||
"lint-staged": "^15.5.0",
|
||||
"prettier": "^3.5.3",
|
||||
"rimraf": "^6.0.1",
|
||||
"sinon": "^20.0.0",
|
||||
"ts-jest": "^29.3.1",
|
||||
"ts-node": "^10.9.2",
|
||||
"tsd": "^0.31.2",
|
||||
"typedoc": "^0.28.1",
|
||||
"typescript": "^5.8.2"
|
||||
},
|
||||
"husky": {
|
||||
"hooks": {
|
||||
"pre-commit": "lint-staged"
|
||||
}
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.{ts,js,json}": [
|
||||
"prettier --write"
|
||||
]
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"browser": {
|
||||
"fs": false,
|
||||
"fs/promises": false
|
||||
},
|
||||
"tsd": {
|
||||
"directory": "tests"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/harrisiirak/cron-parser.git"
|
||||
},
|
||||
"keywords": [
|
||||
"cron",
|
||||
"crontab",
|
||||
"parser"
|
||||
],
|
||||
"author": "Harri Siirak",
|
||||
"contributors": [
|
||||
"Nicholas Clawson",
|
||||
"Daniel Prentis <daniel@salsitasoft.com>",
|
||||
"Renault John Lecoultre",
|
||||
"Richard Astbury <richard.astbury@gmail.com>",
|
||||
"Meaglin Wasabi <Meaglin.wasabi@gmail.com>",
|
||||
"Mike Kusold <hello@mikekusold.com>",
|
||||
"Alex Kit <alex.kit@atmajs.com>",
|
||||
"Santiago Gimeno <santiago.gimeno@gmail.com>",
|
||||
"Daniel <darc.tec@gmail.com>",
|
||||
"Christian Steininger <christian.steininger.cs@gmail.com>",
|
||||
"Mykola Piskovyi <m.piskovyi@gmail.com>",
|
||||
"Brian Vaughn <brian.david.vaughn@gmail.com>",
|
||||
"Nicholas Clawson <nickclaw@gmail.com>",
|
||||
"Yasuhiroki <yasuhiroki.duck@gmail.com>",
|
||||
"Nicholas Clawson <nickclaw@gmail.com>",
|
||||
"Brendan Warkentin <faazshift@gmail.com>",
|
||||
"Charlie Fish <fishcharlie.code@gmail.com>",
|
||||
"Ian Graves <ian+diskimage@iangrav.es>",
|
||||
"Andy Thompson <me@andytson.com>",
|
||||
"Regev Brody <regevbr@gmail.com>",
|
||||
"Michael Hobbs <michael.lee.hobbs@gmail.com>"
|
||||
],
|
||||
"license": "MIT"
|
||||
}
|
||||
7
node_modules/luxon/LICENSE.md
generated
vendored
Normal file
7
node_modules/luxon/LICENSE.md
generated
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
Copyright 2019 JS Foundation and other contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
55
node_modules/luxon/README.md
generated
vendored
Normal file
55
node_modules/luxon/README.md
generated
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
# Luxon
|
||||
|
||||
[![MIT License][license-image]][license] [![Build Status][github-action-image]][github-action-url] [![NPM version][npm-version-image]][npm-url] [![Coverage Status][test-coverage-image]][test-coverage-url] [![PRs welcome][contributing-image]][contributing-url]
|
||||
|
||||
Luxon is a library for working with dates and times in JavaScript.
|
||||
|
||||
```js
|
||||
DateTime.now().setZone("America/New_York").minus({ weeks: 1 }).endOf("day").toISO();
|
||||
```
|
||||
|
||||
## Upgrading to 3.0
|
||||
|
||||
[Guide](https://moment.github.io/luxon/#upgrading)
|
||||
|
||||
## Features
|
||||
* DateTime, Duration, and Interval types.
|
||||
* Immutable, chainable, unambiguous API.
|
||||
* Parsing and formatting for common and custom formats.
|
||||
* Native time zone and Intl support (no locale or tz files).
|
||||
|
||||
## Download/install
|
||||
|
||||
[Download/install instructions](https://moment.github.io/luxon/#/install)
|
||||
|
||||
## Documentation
|
||||
|
||||
* [General documentation](https://moment.github.io/luxon/#/?id=luxon)
|
||||
* [API docs](https://moment.github.io/luxon/api-docs/index.html)
|
||||
* [Quick tour](https://moment.github.io/luxon/#/tour)
|
||||
* [For Moment users](https://moment.github.io/luxon/#/moment)
|
||||
* [Why does Luxon exist?](https://moment.github.io/luxon/#/why)
|
||||
* [A quick demo](https://moment.github.io/luxon/demo/global.html)
|
||||
|
||||
## Development
|
||||
|
||||
See [contributing](CONTRIBUTING.md).
|
||||
|
||||
![Phasers to stun][phasers-image]
|
||||
|
||||
[license-image]: https://img.shields.io/badge/license-MIT-blue.svg
|
||||
[license]: LICENSE.md
|
||||
|
||||
[github-action-image]: https://github.com/moment/luxon/actions/workflows/test.yml/badge.svg
|
||||
[github-action-url]: https://github.com/moment/luxon/actions/workflows/test.yml
|
||||
|
||||
[npm-url]: https://npmjs.org/package/luxon
|
||||
[npm-version-image]: https://badge.fury.io/js/luxon.svg
|
||||
|
||||
[test-coverage-url]: https://codecov.io/gh/moment/luxon
|
||||
[test-coverage-image]: https://codecov.io/gh/moment/luxon/branch/master/graph/badge.svg
|
||||
|
||||
[contributing-url]: https://github.com/moment/luxon/blob/master/CONTRIBUTING.md
|
||||
[contributing-image]: https://img.shields.io/badge/PRs-welcome-brightgreen.svg
|
||||
|
||||
[phasers-image]: https://img.shields.io/badge/phasers-stun-brightgreen.svg
|
||||
7792
node_modules/luxon/build/node/luxon.js
generated
vendored
Normal file
7792
node_modules/luxon/build/node/luxon.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1
node_modules/luxon/build/node/luxon.js.map
generated
vendored
Normal file
1
node_modules/luxon/build/node/luxon.js.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
87
node_modules/luxon/package.json
generated
vendored
Normal file
87
node_modules/luxon/package.json
generated
vendored
Normal file
@@ -0,0 +1,87 @@
|
||||
{
|
||||
"name": "luxon",
|
||||
"version": "3.7.2",
|
||||
"description": "Immutable date wrapper",
|
||||
"author": "Isaac Cambron",
|
||||
"keywords": [
|
||||
"date",
|
||||
"immutable"
|
||||
],
|
||||
"repository": "https://github.com/moment/luxon",
|
||||
"exports": {
|
||||
".": {
|
||||
"import": "./build/es6/luxon.mjs",
|
||||
"require": "./build/node/luxon.js"
|
||||
},
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "babel-node tasks/buildAll.js",
|
||||
"build-node": "babel-node tasks/buildNode.js",
|
||||
"build-global": "babel-node tasks/buildGlobal.js",
|
||||
"jest": "jest",
|
||||
"test": "jest --coverage",
|
||||
"api-docs": "mkdir -p build && documentation build src/luxon.js -f html -o build/api-docs && sed -i.bak 's/<\\/body>/<script src=\"\\..\\/global\\/luxon.js\"><\\/script><script>console.log(\"You can try Luxon right here using the `luxon` global, like `luxon.DateTime.now()`\");<\\/script><\\/body>/g' build/api-docs/index.html && rm build/api-docs/index.html.bak",
|
||||
"copy-site": "mkdir -p build && rsync -a docs/ build/docs && rsync -a site/ build",
|
||||
"site": "npm run api-docs && npm run copy-site",
|
||||
"format": "prettier --write 'src/**/*.js' 'test/**/*.js' 'benchmarks/*.js'",
|
||||
"format-check": "prettier --check 'src/**/*.js' 'test/**/*.js' 'benchmarks/*.js'",
|
||||
"benchmark": "node benchmarks/index.js",
|
||||
"codecov": "codecov",
|
||||
"prepack": "babel-node tasks/buildAll.js",
|
||||
"prepare": "husky install",
|
||||
"show-site": "http-server build"
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.{js,json}": [
|
||||
"prettier --write"
|
||||
]
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.18.6",
|
||||
"@babel/node": "^7.18.6",
|
||||
"@babel/plugin-external-helpers": "^7.18.6",
|
||||
"@babel/preset-env": "^7.18.6",
|
||||
"@rollup/plugin-babel": "^5.3.0",
|
||||
"@rollup/plugin-commonjs": "^19.0.0",
|
||||
"@rollup/plugin-node-resolve": "^13.0.0",
|
||||
"babel-jest": "^28.1.2",
|
||||
"benchmark": "latest",
|
||||
"codecov": "latest",
|
||||
"documentation": "latest",
|
||||
"fs-extra": "^6.0.1",
|
||||
"http-server": "^14.1.1",
|
||||
"husky": "^7.0.0",
|
||||
"jest": "^29.4.3",
|
||||
"lint-staged": "^13.2.1",
|
||||
"prettier": "latest",
|
||||
"rollup": "^2.52.7",
|
||||
"rollup-plugin-terser": "^7.0.2",
|
||||
"uglify-js": "^3.13.10"
|
||||
},
|
||||
"main": "build/node/luxon.js",
|
||||
"module": "src/luxon.js",
|
||||
"browser": "build/cjs-browser/luxon.js",
|
||||
"jsdelivr": "build/global/luxon.min.js",
|
||||
"unpkg": "build/global/luxon.min.js",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"files": [
|
||||
"build/node/luxon.js",
|
||||
"build/node/luxon.js.map",
|
||||
"build/cjs-browser/luxon.js",
|
||||
"build/cjs-browser/luxon.js.map",
|
||||
"build/amd/luxon.js",
|
||||
"build/amd/luxon.js.map",
|
||||
"build/global/luxon.js",
|
||||
"build/global/luxon.js.map",
|
||||
"build/global/luxon.min.js",
|
||||
"build/global/luxon.min.js.map",
|
||||
"build/es6/luxon.mjs",
|
||||
"build/es6/luxon.mjs.map",
|
||||
"src"
|
||||
],
|
||||
"license": "MIT",
|
||||
"sideEffects": false
|
||||
}
|
||||
2603
node_modules/luxon/src/datetime.js
generated
vendored
Normal file
2603
node_modules/luxon/src/datetime.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1009
node_modules/luxon/src/duration.js
generated
vendored
Normal file
1009
node_modules/luxon/src/duration.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
61
node_modules/luxon/src/errors.js
generated
vendored
Normal file
61
node_modules/luxon/src/errors.js
generated
vendored
Normal file
@@ -0,0 +1,61 @@
|
||||
// these aren't really private, but nor are they really useful to document
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
class LuxonError extends Error {}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
export class InvalidDateTimeError extends LuxonError {
|
||||
constructor(reason) {
|
||||
super(`Invalid DateTime: ${reason.toMessage()}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
export class InvalidIntervalError extends LuxonError {
|
||||
constructor(reason) {
|
||||
super(`Invalid Interval: ${reason.toMessage()}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
export class InvalidDurationError extends LuxonError {
|
||||
constructor(reason) {
|
||||
super(`Invalid Duration: ${reason.toMessage()}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
export class ConflictingSpecificationError extends LuxonError {}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
export class InvalidUnitError extends LuxonError {
|
||||
constructor(unit) {
|
||||
super(`Invalid unit ${unit}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
export class InvalidArgumentError extends LuxonError {}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
export class ZoneIsAbstractError extends LuxonError {
|
||||
constructor() {
|
||||
super("Zone is an abstract class");
|
||||
}
|
||||
}
|
||||
205
node_modules/luxon/src/info.js
generated
vendored
Normal file
205
node_modules/luxon/src/info.js
generated
vendored
Normal file
@@ -0,0 +1,205 @@
|
||||
import DateTime from "./datetime.js";
|
||||
import Settings from "./settings.js";
|
||||
import Locale from "./impl/locale.js";
|
||||
import IANAZone from "./zones/IANAZone.js";
|
||||
import { normalizeZone } from "./impl/zoneUtil.js";
|
||||
|
||||
import { hasLocaleWeekInfo, hasRelative } from "./impl/util.js";
|
||||
|
||||
/**
|
||||
* The Info class contains static methods for retrieving general time and date related data. For example, it has methods for finding out if a time zone has a DST, for listing the months in any supported locale, and for discovering which of Luxon features are available in the current environment.
|
||||
*/
|
||||
export default class Info {
|
||||
/**
|
||||
* Return whether the specified zone contains a DST.
|
||||
* @param {string|Zone} [zone='local'] - Zone to check. Defaults to the environment's local zone.
|
||||
* @return {boolean}
|
||||
*/
|
||||
static hasDST(zone = Settings.defaultZone) {
|
||||
const proto = DateTime.now().setZone(zone).set({ month: 12 });
|
||||
|
||||
return !zone.isUniversal && proto.offset !== proto.set({ month: 6 }).offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether the specified zone is a valid IANA specifier.
|
||||
* @param {string} zone - Zone to check
|
||||
* @return {boolean}
|
||||
*/
|
||||
static isValidIANAZone(zone) {
|
||||
return IANAZone.isValidZone(zone);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the input into a {@link Zone} instance.
|
||||
*
|
||||
* * If `input` is already a Zone instance, it is returned unchanged.
|
||||
* * If `input` is a string containing a valid time zone name, a Zone instance
|
||||
* with that name is returned.
|
||||
* * If `input` is a string that doesn't refer to a known time zone, a Zone
|
||||
* instance with {@link Zone#isValid} == false is returned.
|
||||
* * If `input is a number, a Zone instance with the specified fixed offset
|
||||
* in minutes is returned.
|
||||
* * If `input` is `null` or `undefined`, the default zone is returned.
|
||||
* @param {string|Zone|number} [input] - the value to be converted
|
||||
* @return {Zone}
|
||||
*/
|
||||
static normalizeZone(input) {
|
||||
return normalizeZone(input, Settings.defaultZone);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the weekday on which the week starts according to the given locale.
|
||||
* @param {Object} opts - options
|
||||
* @param {string} [opts.locale] - the locale code
|
||||
* @param {string} [opts.locObj=null] - an existing locale object to use
|
||||
* @returns {number} the start of the week, 1 for Monday through 7 for Sunday
|
||||
*/
|
||||
static getStartOfWeek({ locale = null, locObj = null } = {}) {
|
||||
return (locObj || Locale.create(locale)).getStartOfWeek();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the minimum number of days necessary in a week before it is considered part of the next year according
|
||||
* to the given locale.
|
||||
* @param {Object} opts - options
|
||||
* @param {string} [opts.locale] - the locale code
|
||||
* @param {string} [opts.locObj=null] - an existing locale object to use
|
||||
* @returns {number}
|
||||
*/
|
||||
static getMinimumDaysInFirstWeek({ locale = null, locObj = null } = {}) {
|
||||
return (locObj || Locale.create(locale)).getMinDaysInFirstWeek();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the weekdays, which are considered the weekend according to the given locale
|
||||
* @param {Object} opts - options
|
||||
* @param {string} [opts.locale] - the locale code
|
||||
* @param {string} [opts.locObj=null] - an existing locale object to use
|
||||
* @returns {number[]} an array of weekdays, 1 for Monday through 7 for Sunday
|
||||
*/
|
||||
static getWeekendWeekdays({ locale = null, locObj = null } = {}) {
|
||||
// copy the array, because we cache it internally
|
||||
return (locObj || Locale.create(locale)).getWeekendDays().slice();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an array of standalone month names.
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DateTimeFormat
|
||||
* @param {string} [length='long'] - the length of the month representation, such as "numeric", "2-digit", "narrow", "short", "long"
|
||||
* @param {Object} opts - options
|
||||
* @param {string} [opts.locale] - the locale code
|
||||
* @param {string} [opts.numberingSystem=null] - the numbering system
|
||||
* @param {string} [opts.locObj=null] - an existing locale object to use
|
||||
* @param {string} [opts.outputCalendar='gregory'] - the calendar
|
||||
* @example Info.months()[0] //=> 'January'
|
||||
* @example Info.months('short')[0] //=> 'Jan'
|
||||
* @example Info.months('numeric')[0] //=> '1'
|
||||
* @example Info.months('short', { locale: 'fr-CA' } )[0] //=> 'janv.'
|
||||
* @example Info.months('numeric', { locale: 'ar' })[0] //=> '١'
|
||||
* @example Info.months('long', { outputCalendar: 'islamic' })[0] //=> 'Rabiʻ I'
|
||||
* @return {Array}
|
||||
*/
|
||||
static months(
|
||||
length = "long",
|
||||
{ locale = null, numberingSystem = null, locObj = null, outputCalendar = "gregory" } = {}
|
||||
) {
|
||||
return (locObj || Locale.create(locale, numberingSystem, outputCalendar)).months(length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an array of format month names.
|
||||
* Format months differ from standalone months in that they're meant to appear next to the day of the month. In some languages, that
|
||||
* changes the string.
|
||||
* See {@link Info#months}
|
||||
* @param {string} [length='long'] - the length of the month representation, such as "numeric", "2-digit", "narrow", "short", "long"
|
||||
* @param {Object} opts - options
|
||||
* @param {string} [opts.locale] - the locale code
|
||||
* @param {string} [opts.numberingSystem=null] - the numbering system
|
||||
* @param {string} [opts.locObj=null] - an existing locale object to use
|
||||
* @param {string} [opts.outputCalendar='gregory'] - the calendar
|
||||
* @return {Array}
|
||||
*/
|
||||
static monthsFormat(
|
||||
length = "long",
|
||||
{ locale = null, numberingSystem = null, locObj = null, outputCalendar = "gregory" } = {}
|
||||
) {
|
||||
return (locObj || Locale.create(locale, numberingSystem, outputCalendar)).months(length, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an array of standalone week names.
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DateTimeFormat
|
||||
* @param {string} [length='long'] - the length of the weekday representation, such as "narrow", "short", "long".
|
||||
* @param {Object} opts - options
|
||||
* @param {string} [opts.locale] - the locale code
|
||||
* @param {string} [opts.numberingSystem=null] - the numbering system
|
||||
* @param {string} [opts.locObj=null] - an existing locale object to use
|
||||
* @example Info.weekdays()[0] //=> 'Monday'
|
||||
* @example Info.weekdays('short')[0] //=> 'Mon'
|
||||
* @example Info.weekdays('short', { locale: 'fr-CA' })[0] //=> 'lun.'
|
||||
* @example Info.weekdays('short', { locale: 'ar' })[0] //=> 'الاثنين'
|
||||
* @return {Array}
|
||||
*/
|
||||
static weekdays(length = "long", { locale = null, numberingSystem = null, locObj = null } = {}) {
|
||||
return (locObj || Locale.create(locale, numberingSystem, null)).weekdays(length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an array of format week names.
|
||||
* Format weekdays differ from standalone weekdays in that they're meant to appear next to more date information. In some languages, that
|
||||
* changes the string.
|
||||
* See {@link Info#weekdays}
|
||||
* @param {string} [length='long'] - the length of the month representation, such as "narrow", "short", "long".
|
||||
* @param {Object} opts - options
|
||||
* @param {string} [opts.locale=null] - the locale code
|
||||
* @param {string} [opts.numberingSystem=null] - the numbering system
|
||||
* @param {string} [opts.locObj=null] - an existing locale object to use
|
||||
* @return {Array}
|
||||
*/
|
||||
static weekdaysFormat(
|
||||
length = "long",
|
||||
{ locale = null, numberingSystem = null, locObj = null } = {}
|
||||
) {
|
||||
return (locObj || Locale.create(locale, numberingSystem, null)).weekdays(length, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an array of meridiems.
|
||||
* @param {Object} opts - options
|
||||
* @param {string} [opts.locale] - the locale code
|
||||
* @example Info.meridiems() //=> [ 'AM', 'PM' ]
|
||||
* @example Info.meridiems({ locale: 'my' }) //=> [ 'နံနက်', 'ညနေ' ]
|
||||
* @return {Array}
|
||||
*/
|
||||
static meridiems({ locale = null } = {}) {
|
||||
return Locale.create(locale).meridiems();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an array of eras, such as ['BC', 'AD']. The locale can be specified, but the calendar system is always Gregorian.
|
||||
* @param {string} [length='short'] - the length of the era representation, such as "short" or "long".
|
||||
* @param {Object} opts - options
|
||||
* @param {string} [opts.locale] - the locale code
|
||||
* @example Info.eras() //=> [ 'BC', 'AD' ]
|
||||
* @example Info.eras('long') //=> [ 'Before Christ', 'Anno Domini' ]
|
||||
* @example Info.eras('long', { locale: 'fr' }) //=> [ 'avant Jésus-Christ', 'après Jésus-Christ' ]
|
||||
* @return {Array}
|
||||
*/
|
||||
static eras(length = "short", { locale = null } = {}) {
|
||||
return Locale.create(locale, null, "gregory").eras(length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the set of available features in this environment.
|
||||
* Some features of Luxon are not available in all environments. For example, on older browsers, relative time formatting support is not available. Use this function to figure out if that's the case.
|
||||
* Keys:
|
||||
* * `relative`: whether this environment supports relative time formatting
|
||||
* * `localeWeek`: whether this environment supports different weekdays for the start of the week based on the locale
|
||||
* @example Info.features() //=> { relative: false, localeWeek: true }
|
||||
* @return {Object}
|
||||
*/
|
||||
static features() {
|
||||
return { relative: hasRelative(), localeWeek: hasLocaleWeekInfo() };
|
||||
}
|
||||
}
|
||||
669
node_modules/luxon/src/interval.js
generated
vendored
Normal file
669
node_modules/luxon/src/interval.js
generated
vendored
Normal file
@@ -0,0 +1,669 @@
|
||||
import DateTime, { friendlyDateTime } from "./datetime.js";
|
||||
import Duration from "./duration.js";
|
||||
import Settings from "./settings.js";
|
||||
import { InvalidArgumentError, InvalidIntervalError } from "./errors.js";
|
||||
import Invalid from "./impl/invalid.js";
|
||||
import Formatter from "./impl/formatter.js";
|
||||
import * as Formats from "./impl/formats.js";
|
||||
|
||||
const INVALID = "Invalid Interval";
|
||||
|
||||
// checks if the start is equal to or before the end
|
||||
function validateStartEnd(start, end) {
|
||||
if (!start || !start.isValid) {
|
||||
return Interval.invalid("missing or invalid start");
|
||||
} else if (!end || !end.isValid) {
|
||||
return Interval.invalid("missing or invalid end");
|
||||
} else if (end < start) {
|
||||
return Interval.invalid(
|
||||
"end before start",
|
||||
`The end of an interval must be after its start, but you had start=${start.toISO()} and end=${end.toISO()}`
|
||||
);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An Interval object represents a half-open interval of time, where each endpoint is a {@link DateTime}. Conceptually, it's a container for those two endpoints, accompanied by methods for creating, parsing, interrogating, comparing, transforming, and formatting them.
|
||||
*
|
||||
* Here is a brief overview of the most commonly used methods and getters in Interval:
|
||||
*
|
||||
* * **Creation** To create an Interval, use {@link Interval.fromDateTimes}, {@link Interval.after}, {@link Interval.before}, or {@link Interval.fromISO}.
|
||||
* * **Accessors** Use {@link Interval#start} and {@link Interval#end} to get the start and end.
|
||||
* * **Interrogation** To analyze the Interval, use {@link Interval#count}, {@link Interval#length}, {@link Interval#hasSame}, {@link Interval#contains}, {@link Interval#isAfter}, or {@link Interval#isBefore}.
|
||||
* * **Transformation** To create other Intervals out of this one, use {@link Interval#set}, {@link Interval#splitAt}, {@link Interval#splitBy}, {@link Interval#divideEqually}, {@link Interval.merge}, {@link Interval.xor}, {@link Interval#union}, {@link Interval#intersection}, or {@link Interval#difference}.
|
||||
* * **Comparison** To compare this Interval to another one, use {@link Interval#equals}, {@link Interval#overlaps}, {@link Interval#abutsStart}, {@link Interval#abutsEnd}, {@link Interval#engulfs}
|
||||
* * **Output** To convert the Interval into other representations, see {@link Interval#toString}, {@link Interval#toLocaleString}, {@link Interval#toISO}, {@link Interval#toISODate}, {@link Interval#toISOTime}, {@link Interval#toFormat}, and {@link Interval#toDuration}.
|
||||
*/
|
||||
export default class Interval {
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
constructor(config) {
|
||||
/**
|
||||
* @access private
|
||||
*/
|
||||
this.s = config.start;
|
||||
/**
|
||||
* @access private
|
||||
*/
|
||||
this.e = config.end;
|
||||
/**
|
||||
* @access private
|
||||
*/
|
||||
this.invalid = config.invalid || null;
|
||||
/**
|
||||
* @access private
|
||||
*/
|
||||
this.isLuxonInterval = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an invalid Interval.
|
||||
* @param {string} reason - simple string of why this Interval is invalid. Should not contain parameters or anything else data-dependent
|
||||
* @param {string} [explanation=null] - longer explanation, may include parameters and other useful debugging information
|
||||
* @return {Interval}
|
||||
*/
|
||||
static invalid(reason, explanation = null) {
|
||||
if (!reason) {
|
||||
throw new InvalidArgumentError("need to specify a reason the Interval is invalid");
|
||||
}
|
||||
|
||||
const invalid = reason instanceof Invalid ? reason : new Invalid(reason, explanation);
|
||||
|
||||
if (Settings.throwOnInvalid) {
|
||||
throw new InvalidIntervalError(invalid);
|
||||
} else {
|
||||
return new Interval({ invalid });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an Interval from a start DateTime and an end DateTime. Inclusive of the start but not the end.
|
||||
* @param {DateTime|Date|Object} start
|
||||
* @param {DateTime|Date|Object} end
|
||||
* @return {Interval}
|
||||
*/
|
||||
static fromDateTimes(start, end) {
|
||||
const builtStart = friendlyDateTime(start),
|
||||
builtEnd = friendlyDateTime(end);
|
||||
|
||||
const validateError = validateStartEnd(builtStart, builtEnd);
|
||||
|
||||
if (validateError == null) {
|
||||
return new Interval({
|
||||
start: builtStart,
|
||||
end: builtEnd,
|
||||
});
|
||||
} else {
|
||||
return validateError;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an Interval from a start DateTime and a Duration to extend to.
|
||||
* @param {DateTime|Date|Object} start
|
||||
* @param {Duration|Object|number} duration - the length of the Interval.
|
||||
* @return {Interval}
|
||||
*/
|
||||
static after(start, duration) {
|
||||
const dur = Duration.fromDurationLike(duration),
|
||||
dt = friendlyDateTime(start);
|
||||
return Interval.fromDateTimes(dt, dt.plus(dur));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an Interval from an end DateTime and a Duration to extend backwards to.
|
||||
* @param {DateTime|Date|Object} end
|
||||
* @param {Duration|Object|number} duration - the length of the Interval.
|
||||
* @return {Interval}
|
||||
*/
|
||||
static before(end, duration) {
|
||||
const dur = Duration.fromDurationLike(duration),
|
||||
dt = friendlyDateTime(end);
|
||||
return Interval.fromDateTimes(dt.minus(dur), dt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an Interval from an ISO 8601 string.
|
||||
* Accepts `<start>/<end>`, `<start>/<duration>`, and `<duration>/<end>` formats.
|
||||
* @param {string} text - the ISO string to parse
|
||||
* @param {Object} [opts] - options to pass {@link DateTime#fromISO} and optionally {@link Duration#fromISO}
|
||||
* @see https://en.wikipedia.org/wiki/ISO_8601#Time_intervals
|
||||
* @return {Interval}
|
||||
*/
|
||||
static fromISO(text, opts) {
|
||||
const [s, e] = (text || "").split("/", 2);
|
||||
if (s && e) {
|
||||
let start, startIsValid;
|
||||
try {
|
||||
start = DateTime.fromISO(s, opts);
|
||||
startIsValid = start.isValid;
|
||||
} catch (e) {
|
||||
startIsValid = false;
|
||||
}
|
||||
|
||||
let end, endIsValid;
|
||||
try {
|
||||
end = DateTime.fromISO(e, opts);
|
||||
endIsValid = end.isValid;
|
||||
} catch (e) {
|
||||
endIsValid = false;
|
||||
}
|
||||
|
||||
if (startIsValid && endIsValid) {
|
||||
return Interval.fromDateTimes(start, end);
|
||||
}
|
||||
|
||||
if (startIsValid) {
|
||||
const dur = Duration.fromISO(e, opts);
|
||||
if (dur.isValid) {
|
||||
return Interval.after(start, dur);
|
||||
}
|
||||
} else if (endIsValid) {
|
||||
const dur = Duration.fromISO(s, opts);
|
||||
if (dur.isValid) {
|
||||
return Interval.before(end, dur);
|
||||
}
|
||||
}
|
||||
}
|
||||
return Interval.invalid("unparsable", `the input "${text}" can't be parsed as ISO 8601`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if an object is an Interval. Works across context boundaries
|
||||
* @param {object} o
|
||||
* @return {boolean}
|
||||
*/
|
||||
static isInterval(o) {
|
||||
return (o && o.isLuxonInterval) || false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the start of the Interval
|
||||
* @type {DateTime}
|
||||
*/
|
||||
get start() {
|
||||
return this.isValid ? this.s : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the end of the Interval. This is the first instant which is not part of the interval
|
||||
* (Interval is half-open).
|
||||
* @type {DateTime}
|
||||
*/
|
||||
get end() {
|
||||
return this.isValid ? this.e : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the last DateTime included in the interval (since end is not part of the interval)
|
||||
* @type {DateTime}
|
||||
*/
|
||||
get lastDateTime() {
|
||||
return this.isValid ? (this.e ? this.e.minus(1) : null) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this Interval's end is at least its start, meaning that the Interval isn't 'backwards'.
|
||||
* @type {boolean}
|
||||
*/
|
||||
get isValid() {
|
||||
return this.invalidReason === null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an error code if this Interval is invalid, or null if the Interval is valid
|
||||
* @type {string}
|
||||
*/
|
||||
get invalidReason() {
|
||||
return this.invalid ? this.invalid.reason : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an explanation of why this Interval became invalid, or null if the Interval is valid
|
||||
* @type {string}
|
||||
*/
|
||||
get invalidExplanation() {
|
||||
return this.invalid ? this.invalid.explanation : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the length of the Interval in the specified unit.
|
||||
* @param {string} unit - the unit (such as 'hours' or 'days') to return the length in.
|
||||
* @return {number}
|
||||
*/
|
||||
length(unit = "milliseconds") {
|
||||
return this.isValid ? this.toDuration(...[unit]).get(unit) : NaN;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the count of minutes, hours, days, months, or years included in the Interval, even in part.
|
||||
* Unlike {@link Interval#length} this counts sections of the calendar, not periods of time, e.g. specifying 'day'
|
||||
* asks 'what dates are included in this interval?', not 'how many days long is this interval?'
|
||||
* @param {string} [unit='milliseconds'] - the unit of time to count.
|
||||
* @param {Object} opts - options
|
||||
* @param {boolean} [opts.useLocaleWeeks=false] - If true, use weeks based on the locale, i.e. use the locale-dependent start of the week; this operation will always use the locale of the start DateTime
|
||||
* @return {number}
|
||||
*/
|
||||
count(unit = "milliseconds", opts) {
|
||||
if (!this.isValid) return NaN;
|
||||
const start = this.start.startOf(unit, opts);
|
||||
let end;
|
||||
if (opts?.useLocaleWeeks) {
|
||||
end = this.end.reconfigure({ locale: start.locale });
|
||||
} else {
|
||||
end = this.end;
|
||||
}
|
||||
end = end.startOf(unit, opts);
|
||||
return Math.floor(end.diff(start, unit).get(unit)) + (end.valueOf() !== this.end.valueOf());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this Interval's start and end are both in the same unit of time
|
||||
* @param {string} unit - the unit of time to check sameness on
|
||||
* @return {boolean}
|
||||
*/
|
||||
hasSame(unit) {
|
||||
return this.isValid ? this.isEmpty() || this.e.minus(1).hasSame(this.s, unit) : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether this Interval has the same start and end DateTimes.
|
||||
* @return {boolean}
|
||||
*/
|
||||
isEmpty() {
|
||||
return this.s.valueOf() === this.e.valueOf();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether this Interval's start is after the specified DateTime.
|
||||
* @param {DateTime} dateTime
|
||||
* @return {boolean}
|
||||
*/
|
||||
isAfter(dateTime) {
|
||||
if (!this.isValid) return false;
|
||||
return this.s > dateTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether this Interval's end is before the specified DateTime.
|
||||
* @param {DateTime} dateTime
|
||||
* @return {boolean}
|
||||
*/
|
||||
isBefore(dateTime) {
|
||||
if (!this.isValid) return false;
|
||||
return this.e <= dateTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether this Interval contains the specified DateTime.
|
||||
* @param {DateTime} dateTime
|
||||
* @return {boolean}
|
||||
*/
|
||||
contains(dateTime) {
|
||||
if (!this.isValid) return false;
|
||||
return this.s <= dateTime && this.e > dateTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* "Sets" the start and/or end dates. Returns a newly-constructed Interval.
|
||||
* @param {Object} values - the values to set
|
||||
* @param {DateTime} values.start - the starting DateTime
|
||||
* @param {DateTime} values.end - the ending DateTime
|
||||
* @return {Interval}
|
||||
*/
|
||||
set({ start, end } = {}) {
|
||||
if (!this.isValid) return this;
|
||||
return Interval.fromDateTimes(start || this.s, end || this.e);
|
||||
}
|
||||
|
||||
/**
|
||||
* Split this Interval at each of the specified DateTimes
|
||||
* @param {...DateTime} dateTimes - the unit of time to count.
|
||||
* @return {Array}
|
||||
*/
|
||||
splitAt(...dateTimes) {
|
||||
if (!this.isValid) return [];
|
||||
const sorted = dateTimes
|
||||
.map(friendlyDateTime)
|
||||
.filter((d) => this.contains(d))
|
||||
.sort((a, b) => a.toMillis() - b.toMillis()),
|
||||
results = [];
|
||||
let { s } = this,
|
||||
i = 0;
|
||||
|
||||
while (s < this.e) {
|
||||
const added = sorted[i] || this.e,
|
||||
next = +added > +this.e ? this.e : added;
|
||||
results.push(Interval.fromDateTimes(s, next));
|
||||
s = next;
|
||||
i += 1;
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Split this Interval into smaller Intervals, each of the specified length.
|
||||
* Left over time is grouped into a smaller interval
|
||||
* @param {Duration|Object|number} duration - The length of each resulting interval.
|
||||
* @return {Array}
|
||||
*/
|
||||
splitBy(duration) {
|
||||
const dur = Duration.fromDurationLike(duration);
|
||||
|
||||
if (!this.isValid || !dur.isValid || dur.as("milliseconds") === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
let { s } = this,
|
||||
idx = 1,
|
||||
next;
|
||||
|
||||
const results = [];
|
||||
while (s < this.e) {
|
||||
const added = this.start.plus(dur.mapUnits((x) => x * idx));
|
||||
next = +added > +this.e ? this.e : added;
|
||||
results.push(Interval.fromDateTimes(s, next));
|
||||
s = next;
|
||||
idx += 1;
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Split this Interval into the specified number of smaller intervals.
|
||||
* @param {number} numberOfParts - The number of Intervals to divide the Interval into.
|
||||
* @return {Array}
|
||||
*/
|
||||
divideEqually(numberOfParts) {
|
||||
if (!this.isValid) return [];
|
||||
return this.splitBy(this.length() / numberOfParts).slice(0, numberOfParts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether this Interval overlaps with the specified Interval
|
||||
* @param {Interval} other
|
||||
* @return {boolean}
|
||||
*/
|
||||
overlaps(other) {
|
||||
return this.e > other.s && this.s < other.e;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether this Interval's end is adjacent to the specified Interval's start.
|
||||
* @param {Interval} other
|
||||
* @return {boolean}
|
||||
*/
|
||||
abutsStart(other) {
|
||||
if (!this.isValid) return false;
|
||||
return +this.e === +other.s;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether this Interval's start is adjacent to the specified Interval's end.
|
||||
* @param {Interval} other
|
||||
* @return {boolean}
|
||||
*/
|
||||
abutsEnd(other) {
|
||||
if (!this.isValid) return false;
|
||||
return +other.e === +this.s;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this Interval fully contains the specified Interval, specifically if the intersect (of this Interval and the other Interval) is equal to the other Interval; false otherwise.
|
||||
* @param {Interval} other
|
||||
* @return {boolean}
|
||||
*/
|
||||
engulfs(other) {
|
||||
if (!this.isValid) return false;
|
||||
return this.s <= other.s && this.e >= other.e;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether this Interval has the same start and end as the specified Interval.
|
||||
* @param {Interval} other
|
||||
* @return {boolean}
|
||||
*/
|
||||
equals(other) {
|
||||
if (!this.isValid || !other.isValid) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return this.s.equals(other.s) && this.e.equals(other.e);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an Interval representing the intersection of this Interval and the specified Interval.
|
||||
* Specifically, the resulting Interval has the maximum start time and the minimum end time of the two Intervals.
|
||||
* Returns null if the intersection is empty, meaning, the intervals don't intersect.
|
||||
* @param {Interval} other
|
||||
* @return {Interval}
|
||||
*/
|
||||
intersection(other) {
|
||||
if (!this.isValid) return this;
|
||||
const s = this.s > other.s ? this.s : other.s,
|
||||
e = this.e < other.e ? this.e : other.e;
|
||||
|
||||
if (s >= e) {
|
||||
return null;
|
||||
} else {
|
||||
return Interval.fromDateTimes(s, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an Interval representing the union of this Interval and the specified Interval.
|
||||
* Specifically, the resulting Interval has the minimum start time and the maximum end time of the two Intervals.
|
||||
* @param {Interval} other
|
||||
* @return {Interval}
|
||||
*/
|
||||
union(other) {
|
||||
if (!this.isValid) return this;
|
||||
const s = this.s < other.s ? this.s : other.s,
|
||||
e = this.e > other.e ? this.e : other.e;
|
||||
return Interval.fromDateTimes(s, e);
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge an array of Intervals into an equivalent minimal set of Intervals.
|
||||
* Combines overlapping and adjacent Intervals.
|
||||
* The resulting array will contain the Intervals in ascending order, that is, starting with the earliest Interval
|
||||
* and ending with the latest.
|
||||
*
|
||||
* @param {Array} intervals
|
||||
* @return {Array}
|
||||
*/
|
||||
static merge(intervals) {
|
||||
const [found, final] = intervals
|
||||
.sort((a, b) => a.s - b.s)
|
||||
.reduce(
|
||||
([sofar, current], item) => {
|
||||
if (!current) {
|
||||
return [sofar, item];
|
||||
} else if (current.overlaps(item) || current.abutsStart(item)) {
|
||||
return [sofar, current.union(item)];
|
||||
} else {
|
||||
return [sofar.concat([current]), item];
|
||||
}
|
||||
},
|
||||
[[], null]
|
||||
);
|
||||
if (final) {
|
||||
found.push(final);
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an array of Intervals representing the spans of time that only appear in one of the specified Intervals.
|
||||
* @param {Array} intervals
|
||||
* @return {Array}
|
||||
*/
|
||||
static xor(intervals) {
|
||||
let start = null,
|
||||
currentCount = 0;
|
||||
const results = [],
|
||||
ends = intervals.map((i) => [
|
||||
{ time: i.s, type: "s" },
|
||||
{ time: i.e, type: "e" },
|
||||
]),
|
||||
flattened = Array.prototype.concat(...ends),
|
||||
arr = flattened.sort((a, b) => a.time - b.time);
|
||||
|
||||
for (const i of arr) {
|
||||
currentCount += i.type === "s" ? 1 : -1;
|
||||
|
||||
if (currentCount === 1) {
|
||||
start = i.time;
|
||||
} else {
|
||||
if (start && +start !== +i.time) {
|
||||
results.push(Interval.fromDateTimes(start, i.time));
|
||||
}
|
||||
|
||||
start = null;
|
||||
}
|
||||
}
|
||||
|
||||
return Interval.merge(results);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an Interval representing the span of time in this Interval that doesn't overlap with any of the specified Intervals.
|
||||
* @param {...Interval} intervals
|
||||
* @return {Array}
|
||||
*/
|
||||
difference(...intervals) {
|
||||
return Interval.xor([this].concat(intervals))
|
||||
.map((i) => this.intersection(i))
|
||||
.filter((i) => i && !i.isEmpty());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string representation of this Interval appropriate for debugging.
|
||||
* @return {string}
|
||||
*/
|
||||
toString() {
|
||||
if (!this.isValid) return INVALID;
|
||||
return `[${this.s.toISO()} – ${this.e.toISO()})`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string representation of this Interval appropriate for the REPL.
|
||||
* @return {string}
|
||||
*/
|
||||
[Symbol.for("nodejs.util.inspect.custom")]() {
|
||||
if (this.isValid) {
|
||||
return `Interval { start: ${this.s.toISO()}, end: ${this.e.toISO()} }`;
|
||||
} else {
|
||||
return `Interval { Invalid, reason: ${this.invalidReason} }`;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a localized string representing this Interval. Accepts the same options as the
|
||||
* Intl.DateTimeFormat constructor and any presets defined by Luxon, such as
|
||||
* {@link DateTime.DATE_FULL} or {@link DateTime.TIME_SIMPLE}. The exact behavior of this method
|
||||
* is browser-specific, but in general it will return an appropriate representation of the
|
||||
* Interval in the assigned locale. Defaults to the system's locale if no locale has been
|
||||
* specified.
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DateTimeFormat
|
||||
* @param {Object} [formatOpts=DateTime.DATE_SHORT] - Either a DateTime preset or
|
||||
* Intl.DateTimeFormat constructor options.
|
||||
* @param {Object} opts - Options to override the configuration of the start DateTime.
|
||||
* @example Interval.fromISO('2022-11-07T09:00Z/2022-11-08T09:00Z').toLocaleString(); //=> 11/7/2022 – 11/8/2022
|
||||
* @example Interval.fromISO('2022-11-07T09:00Z/2022-11-08T09:00Z').toLocaleString(DateTime.DATE_FULL); //=> November 7 – 8, 2022
|
||||
* @example Interval.fromISO('2022-11-07T09:00Z/2022-11-08T09:00Z').toLocaleString(DateTime.DATE_FULL, { locale: 'fr-FR' }); //=> 7–8 novembre 2022
|
||||
* @example Interval.fromISO('2022-11-07T17:00Z/2022-11-07T19:00Z').toLocaleString(DateTime.TIME_SIMPLE); //=> 6:00 – 8:00 PM
|
||||
* @example Interval.fromISO('2022-11-07T17:00Z/2022-11-07T19:00Z').toLocaleString({ weekday: 'short', month: 'short', day: '2-digit', hour: '2-digit', minute: '2-digit' }); //=> Mon, Nov 07, 6:00 – 8:00 p
|
||||
* @return {string}
|
||||
*/
|
||||
toLocaleString(formatOpts = Formats.DATE_SHORT, opts = {}) {
|
||||
return this.isValid
|
||||
? Formatter.create(this.s.loc.clone(opts), formatOpts).formatInterval(this)
|
||||
: INVALID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an ISO 8601-compliant string representation of this Interval.
|
||||
* @see https://en.wikipedia.org/wiki/ISO_8601#Time_intervals
|
||||
* @param {Object} opts - The same options as {@link DateTime#toISO}
|
||||
* @return {string}
|
||||
*/
|
||||
toISO(opts) {
|
||||
if (!this.isValid) return INVALID;
|
||||
return `${this.s.toISO(opts)}/${this.e.toISO(opts)}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an ISO 8601-compliant string representation of date of this Interval.
|
||||
* The time components are ignored.
|
||||
* @see https://en.wikipedia.org/wiki/ISO_8601#Time_intervals
|
||||
* @return {string}
|
||||
*/
|
||||
toISODate() {
|
||||
if (!this.isValid) return INVALID;
|
||||
return `${this.s.toISODate()}/${this.e.toISODate()}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an ISO 8601-compliant string representation of time of this Interval.
|
||||
* The date components are ignored.
|
||||
* @see https://en.wikipedia.org/wiki/ISO_8601#Time_intervals
|
||||
* @param {Object} opts - The same options as {@link DateTime#toISO}
|
||||
* @return {string}
|
||||
*/
|
||||
toISOTime(opts) {
|
||||
if (!this.isValid) return INVALID;
|
||||
return `${this.s.toISOTime(opts)}/${this.e.toISOTime(opts)}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string representation of this Interval formatted according to the specified format
|
||||
* string. **You may not want this.** See {@link Interval#toLocaleString} for a more flexible
|
||||
* formatting tool.
|
||||
* @param {string} dateFormat - The format string. This string formats the start and end time.
|
||||
* See {@link DateTime#toFormat} for details.
|
||||
* @param {Object} opts - Options.
|
||||
* @param {string} [opts.separator = ' – '] - A separator to place between the start and end
|
||||
* representations.
|
||||
* @return {string}
|
||||
*/
|
||||
toFormat(dateFormat, { separator = " – " } = {}) {
|
||||
if (!this.isValid) return INVALID;
|
||||
return `${this.s.toFormat(dateFormat)}${separator}${this.e.toFormat(dateFormat)}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a Duration representing the time spanned by this interval.
|
||||
* @param {string|string[]} [unit=['milliseconds']] - the unit or units (such as 'hours' or 'days') to include in the duration.
|
||||
* @param {Object} opts - options that affect the creation of the Duration
|
||||
* @param {string} [opts.conversionAccuracy='casual'] - the conversion system to use
|
||||
* @example Interval.fromDateTimes(dt1, dt2).toDuration().toObject() //=> { milliseconds: 88489257 }
|
||||
* @example Interval.fromDateTimes(dt1, dt2).toDuration('days').toObject() //=> { days: 1.0241812152777778 }
|
||||
* @example Interval.fromDateTimes(dt1, dt2).toDuration(['hours', 'minutes']).toObject() //=> { hours: 24, minutes: 34.82095 }
|
||||
* @example Interval.fromDateTimes(dt1, dt2).toDuration(['hours', 'minutes', 'seconds']).toObject() //=> { hours: 24, minutes: 34, seconds: 49.257 }
|
||||
* @example Interval.fromDateTimes(dt1, dt2).toDuration('seconds').toObject() //=> { seconds: 88489.257 }
|
||||
* @return {Duration}
|
||||
*/
|
||||
toDuration(unit, opts) {
|
||||
if (!this.isValid) {
|
||||
return Duration.invalid(this.invalidReason);
|
||||
}
|
||||
return this.e.diff(this.s, unit, opts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Run mapFn on the interval start and end, returning a new Interval from the resulting DateTimes
|
||||
* @param {function} mapFn
|
||||
* @return {Interval}
|
||||
* @example Interval.fromDateTimes(dt1, dt2).mapEndpoints(endpoint => endpoint.toUTC())
|
||||
* @example Interval.fromDateTimes(dt1, dt2).mapEndpoints(endpoint => endpoint.plus({ hours: 2 }))
|
||||
*/
|
||||
mapEndpoints(mapFn) {
|
||||
return Interval.fromDateTimes(mapFn(this.s), mapFn(this.e));
|
||||
}
|
||||
}
|
||||
26
node_modules/luxon/src/luxon.js
generated
vendored
Normal file
26
node_modules/luxon/src/luxon.js
generated
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
import DateTime from "./datetime.js";
|
||||
import Duration from "./duration.js";
|
||||
import Interval from "./interval.js";
|
||||
import Info from "./info.js";
|
||||
import Zone from "./zone.js";
|
||||
import FixedOffsetZone from "./zones/fixedOffsetZone.js";
|
||||
import IANAZone from "./zones/IANAZone.js";
|
||||
import InvalidZone from "./zones/invalidZone.js";
|
||||
import SystemZone from "./zones/systemZone.js";
|
||||
import Settings from "./settings.js";
|
||||
|
||||
const VERSION = "3.7.2";
|
||||
|
||||
export {
|
||||
VERSION,
|
||||
DateTime,
|
||||
Duration,
|
||||
Interval,
|
||||
Info,
|
||||
Zone,
|
||||
FixedOffsetZone,
|
||||
IANAZone,
|
||||
InvalidZone,
|
||||
SystemZone,
|
||||
Settings,
|
||||
};
|
||||
4
node_modules/luxon/src/package.json
generated
vendored
Normal file
4
node_modules/luxon/src/package.json
generated
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"type": "module",
|
||||
"version": "3.7.2"
|
||||
}
|
||||
180
node_modules/luxon/src/settings.js
generated
vendored
Normal file
180
node_modules/luxon/src/settings.js
generated
vendored
Normal file
@@ -0,0 +1,180 @@
|
||||
import SystemZone from "./zones/systemZone.js";
|
||||
import IANAZone from "./zones/IANAZone.js";
|
||||
import Locale from "./impl/locale.js";
|
||||
import DateTime from "./datetime.js";
|
||||
|
||||
import { normalizeZone } from "./impl/zoneUtil.js";
|
||||
import { validateWeekSettings } from "./impl/util.js";
|
||||
import { resetDigitRegexCache } from "./impl/digits.js";
|
||||
|
||||
let now = () => Date.now(),
|
||||
defaultZone = "system",
|
||||
defaultLocale = null,
|
||||
defaultNumberingSystem = null,
|
||||
defaultOutputCalendar = null,
|
||||
twoDigitCutoffYear = 60,
|
||||
throwOnInvalid,
|
||||
defaultWeekSettings = null;
|
||||
|
||||
/**
|
||||
* Settings contains static getters and setters that control Luxon's overall behavior. Luxon is a simple library with few options, but the ones it does have live here.
|
||||
*/
|
||||
export default class Settings {
|
||||
/**
|
||||
* Get the callback for returning the current timestamp.
|
||||
* @type {function}
|
||||
*/
|
||||
static get now() {
|
||||
return now;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the callback for returning the current timestamp.
|
||||
* The function should return a number, which will be interpreted as an Epoch millisecond count
|
||||
* @type {function}
|
||||
* @example Settings.now = () => Date.now() + 3000 // pretend it is 3 seconds in the future
|
||||
* @example Settings.now = () => 0 // always pretend it's Jan 1, 1970 at midnight in UTC time
|
||||
*/
|
||||
static set now(n) {
|
||||
now = n;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default time zone to create DateTimes in. Does not affect existing instances.
|
||||
* Use the value "system" to reset this value to the system's time zone.
|
||||
* @type {string}
|
||||
*/
|
||||
static set defaultZone(zone) {
|
||||
defaultZone = zone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the default time zone object currently used to create DateTimes. Does not affect existing instances.
|
||||
* The default value is the system's time zone (the one set on the machine that runs this code).
|
||||
* @type {Zone}
|
||||
*/
|
||||
static get defaultZone() {
|
||||
return normalizeZone(defaultZone, SystemZone.instance);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the default locale to create DateTimes with. Does not affect existing instances.
|
||||
* @type {string}
|
||||
*/
|
||||
static get defaultLocale() {
|
||||
return defaultLocale;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default locale to create DateTimes with. Does not affect existing instances.
|
||||
* @type {string}
|
||||
*/
|
||||
static set defaultLocale(locale) {
|
||||
defaultLocale = locale;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the default numbering system to create DateTimes with. Does not affect existing instances.
|
||||
* @type {string}
|
||||
*/
|
||||
static get defaultNumberingSystem() {
|
||||
return defaultNumberingSystem;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default numbering system to create DateTimes with. Does not affect existing instances.
|
||||
* @type {string}
|
||||
*/
|
||||
static set defaultNumberingSystem(numberingSystem) {
|
||||
defaultNumberingSystem = numberingSystem;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the default output calendar to create DateTimes with. Does not affect existing instances.
|
||||
* @type {string}
|
||||
*/
|
||||
static get defaultOutputCalendar() {
|
||||
return defaultOutputCalendar;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default output calendar to create DateTimes with. Does not affect existing instances.
|
||||
* @type {string}
|
||||
*/
|
||||
static set defaultOutputCalendar(outputCalendar) {
|
||||
defaultOutputCalendar = outputCalendar;
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {Object} WeekSettings
|
||||
* @property {number} firstDay
|
||||
* @property {number} minimalDays
|
||||
* @property {number[]} weekend
|
||||
*/
|
||||
|
||||
/**
|
||||
* @return {WeekSettings|null}
|
||||
*/
|
||||
static get defaultWeekSettings() {
|
||||
return defaultWeekSettings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows overriding the default locale week settings, i.e. the start of the week, the weekend and
|
||||
* how many days are required in the first week of a year.
|
||||
* Does not affect existing instances.
|
||||
*
|
||||
* @param {WeekSettings|null} weekSettings
|
||||
*/
|
||||
static set defaultWeekSettings(weekSettings) {
|
||||
defaultWeekSettings = validateWeekSettings(weekSettings);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the cutoff year for whether a 2-digit year string is interpreted in the current or previous century. Numbers higher than the cutoff will be considered to mean 19xx and numbers lower or equal to the cutoff will be considered 20xx.
|
||||
* @type {number}
|
||||
*/
|
||||
static get twoDigitCutoffYear() {
|
||||
return twoDigitCutoffYear;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the cutoff year for whether a 2-digit year string is interpreted in the current or previous century. Numbers higher than the cutoff will be considered to mean 19xx and numbers lower or equal to the cutoff will be considered 20xx.
|
||||
* @type {number}
|
||||
* @example Settings.twoDigitCutoffYear = 0 // all 'yy' are interpreted as 20th century
|
||||
* @example Settings.twoDigitCutoffYear = 99 // all 'yy' are interpreted as 21st century
|
||||
* @example Settings.twoDigitCutoffYear = 50 // '49' -> 2049; '50' -> 1950
|
||||
* @example Settings.twoDigitCutoffYear = 1950 // interpreted as 50
|
||||
* @example Settings.twoDigitCutoffYear = 2050 // ALSO interpreted as 50
|
||||
*/
|
||||
static set twoDigitCutoffYear(cutoffYear) {
|
||||
twoDigitCutoffYear = cutoffYear % 100;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get whether Luxon will throw when it encounters invalid DateTimes, Durations, or Intervals
|
||||
* @type {boolean}
|
||||
*/
|
||||
static get throwOnInvalid() {
|
||||
return throwOnInvalid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether Luxon will throw when it encounters invalid DateTimes, Durations, or Intervals
|
||||
* @type {boolean}
|
||||
*/
|
||||
static set throwOnInvalid(t) {
|
||||
throwOnInvalid = t;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset Luxon's global caches. Should only be necessary in testing scenarios.
|
||||
* @return {void}
|
||||
*/
|
||||
static resetCaches() {
|
||||
Locale.resetCache();
|
||||
IANAZone.resetCache();
|
||||
DateTime.resetCache();
|
||||
resetDigitRegexCache();
|
||||
}
|
||||
}
|
||||
97
node_modules/luxon/src/zone.js
generated
vendored
Normal file
97
node_modules/luxon/src/zone.js
generated
vendored
Normal file
@@ -0,0 +1,97 @@
|
||||
import { ZoneIsAbstractError } from "./errors.js";
|
||||
|
||||
/**
|
||||
* @interface
|
||||
*/
|
||||
export default class Zone {
|
||||
/**
|
||||
* The type of zone
|
||||
* @abstract
|
||||
* @type {string}
|
||||
*/
|
||||
get type() {
|
||||
throw new ZoneIsAbstractError();
|
||||
}
|
||||
|
||||
/**
|
||||
* The name of this zone.
|
||||
* @abstract
|
||||
* @type {string}
|
||||
*/
|
||||
get name() {
|
||||
throw new ZoneIsAbstractError();
|
||||
}
|
||||
|
||||
/**
|
||||
* The IANA name of this zone.
|
||||
* Defaults to `name` if not overwritten by a subclass.
|
||||
* @abstract
|
||||
* @type {string}
|
||||
*/
|
||||
get ianaName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the offset is known to be fixed for the whole year.
|
||||
* @abstract
|
||||
* @type {boolean}
|
||||
*/
|
||||
get isUniversal() {
|
||||
throw new ZoneIsAbstractError();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the offset's common name (such as EST) at the specified timestamp
|
||||
* @abstract
|
||||
* @param {number} ts - Epoch milliseconds for which to get the name
|
||||
* @param {Object} opts - Options to affect the format
|
||||
* @param {string} opts.format - What style of offset to return. Accepts 'long' or 'short'.
|
||||
* @param {string} opts.locale - What locale to return the offset name in.
|
||||
* @return {string}
|
||||
*/
|
||||
offsetName(ts, opts) {
|
||||
throw new ZoneIsAbstractError();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the offset's value as a string
|
||||
* @abstract
|
||||
* @param {number} ts - Epoch milliseconds for which to get the offset
|
||||
* @param {string} format - What style of offset to return.
|
||||
* Accepts 'narrow', 'short', or 'techie'. Returning '+6', '+06:00', or '+0600' respectively
|
||||
* @return {string}
|
||||
*/
|
||||
formatOffset(ts, format) {
|
||||
throw new ZoneIsAbstractError();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the offset in minutes for this zone at the specified timestamp.
|
||||
* @abstract
|
||||
* @param {number} ts - Epoch milliseconds for which to compute the offset
|
||||
* @return {number}
|
||||
*/
|
||||
offset(ts) {
|
||||
throw new ZoneIsAbstractError();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether this Zone is equal to another zone
|
||||
* @abstract
|
||||
* @param {Zone} otherZone - the zone to compare
|
||||
* @return {boolean}
|
||||
*/
|
||||
equals(otherZone) {
|
||||
throw new ZoneIsAbstractError();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether this Zone is valid.
|
||||
* @abstract
|
||||
* @type {boolean}
|
||||
*/
|
||||
get isValid() {
|
||||
throw new ZoneIsAbstractError();
|
||||
}
|
||||
}
|
||||
20
node_modules/style-loader/LICENSE
generated
vendored
Normal file
20
node_modules/style-loader/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
Copyright JS Foundation and other contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
'Software'), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
1270
node_modules/style-loader/README.md
generated
vendored
Normal file
1270
node_modules/style-loader/README.md
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
4
node_modules/style-loader/dist/cjs.js
generated
vendored
Normal file
4
node_modules/style-loader/dist/cjs.js
generated
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
"use strict";
|
||||
|
||||
const loader = require("./index");
|
||||
module.exports = loader.default;
|
||||
151
node_modules/style-loader/dist/index.js
generated
vendored
Normal file
151
node_modules/style-loader/dist/index.js
generated
vendored
Normal file
@@ -0,0 +1,151 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.default = void 0;
|
||||
var _path = _interopRequireDefault(require("path"));
|
||||
var _utils = require("./utils");
|
||||
var _options = _interopRequireDefault(require("./options.json"));
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
// eslint-disable-next-line consistent-return
|
||||
const loader = function loader(content) {
|
||||
if (this._compiler && this._compiler.options && this._compiler.options.experiments && this._compiler.options.experiments.css && this._module && (this._module.type === "css" || this._module.type === "css/global" || this._module.type === "css/module" || this._module.type === "css/auto")) {
|
||||
return content;
|
||||
}
|
||||
};
|
||||
loader.pitch = function pitch(request) {
|
||||
if (this._compiler && this._compiler.options && this._compiler.options.experiments && this._compiler.options.experiments.css && this._module && (this._module.type === "css" || this._module.type === "css/global" || this._module.type === "css/module" || this._module.type === "css/auto")) {
|
||||
this.emitWarning(new Error('You can\'t use `experiments.css` (`experiments.futureDefaults` enable built-in CSS support by default) and `style-loader` together, please set `experiments.css` to `false` or set `{ type: "javascript/auto" }` for rules with `style-loader` in your webpack config (now `style-loader` does nothing).'));
|
||||
return;
|
||||
}
|
||||
const options = this.getOptions(_options.default);
|
||||
const injectType = options.injectType || "styleTag";
|
||||
const esModule = typeof options.esModule !== "undefined" ? options.esModule : true;
|
||||
const runtimeOptions = {};
|
||||
if (options.attributes) {
|
||||
runtimeOptions.attributes = options.attributes;
|
||||
}
|
||||
if (options.base) {
|
||||
runtimeOptions.base = options.base;
|
||||
}
|
||||
const insertType = options.insert && _path.default.isAbsolute(options.insert) ? "module-path" : "selector";
|
||||
switch (injectType) {
|
||||
case "linkTag":
|
||||
{
|
||||
const hmrCode = this.hot ? (0, _utils.getLinkHmrCode)(esModule, this, request) : "";
|
||||
|
||||
// eslint-disable-next-line consistent-return
|
||||
return `
|
||||
${(0, _utils.getImportLinkAPICode)(esModule, this)}
|
||||
${(0, _utils.getImportInsertBySelectorCode)(esModule, this, insertType, options)}
|
||||
${(0, _utils.getImportLinkContentCode)(esModule, this, request)}
|
||||
${esModule ? "" : `content = content.__esModule ? content.default : content;`}
|
||||
|
||||
var options = ${JSON.stringify(runtimeOptions)};
|
||||
|
||||
${(0, _utils.getInsertOptionCode)(insertType, options)}
|
||||
|
||||
var update = API(content, options);
|
||||
|
||||
${hmrCode}
|
||||
|
||||
${esModule ? "export default {}" : ""}`;
|
||||
}
|
||||
case "lazyStyleTag":
|
||||
case "lazyAutoStyleTag":
|
||||
case "lazySingletonStyleTag":
|
||||
{
|
||||
const isSingleton = injectType === "lazySingletonStyleTag";
|
||||
const isAuto = injectType === "lazyAutoStyleTag";
|
||||
const hmrCode = this.hot ? (0, _utils.getStyleHmrCode)(esModule, this, request, true) : "";
|
||||
|
||||
// eslint-disable-next-line consistent-return
|
||||
return `
|
||||
var exported = {};
|
||||
|
||||
${(0, _utils.getImportStyleAPICode)(esModule, this)}
|
||||
${(0, _utils.getImportStyleDomAPICode)(esModule, this, isSingleton, isAuto)}
|
||||
${(0, _utils.getImportInsertBySelectorCode)(esModule, this, insertType, options)}
|
||||
${(0, _utils.getSetAttributesCode)(esModule, this, options)}
|
||||
${(0, _utils.getImportInsertStyleElementCode)(esModule, this)}
|
||||
${(0, _utils.getStyleTagTransformFnCode)(esModule, this, options, isSingleton)}
|
||||
${(0, _utils.getImportStyleContentCode)(esModule, this, request)}
|
||||
${isAuto ? (0, _utils.getImportIsOldIECode)(esModule, this) : ""}
|
||||
${esModule ? `if (content && content.locals) {
|
||||
exported.locals = content.locals;
|
||||
}
|
||||
` : `content = content.__esModule ? content.default : content;
|
||||
|
||||
exported.locals = content.locals || {};`}
|
||||
|
||||
var refs = 0;
|
||||
var update;
|
||||
var options = ${JSON.stringify(runtimeOptions)};
|
||||
|
||||
${(0, _utils.getStyleTagTransformFn)(options, isSingleton)};
|
||||
options.setAttributes = setAttributes;
|
||||
${(0, _utils.getInsertOptionCode)(insertType, options)}
|
||||
options.domAPI = ${(0, _utils.getdomAPI)(isAuto)};
|
||||
options.insertStyleElement = insertStyleElement;
|
||||
|
||||
exported.use = function(insertOptions) {
|
||||
options.options = insertOptions || {};
|
||||
|
||||
if (!(refs++)) {
|
||||
update = API(content, options);
|
||||
}
|
||||
|
||||
return exported;
|
||||
};
|
||||
exported.unuse = function() {
|
||||
if (refs > 0 && !--refs) {
|
||||
update();
|
||||
update = null;
|
||||
}
|
||||
};
|
||||
|
||||
${hmrCode}
|
||||
|
||||
${(0, _utils.getExportLazyStyleCode)(esModule, this, request)}
|
||||
`;
|
||||
}
|
||||
case "styleTag":
|
||||
case "autoStyleTag":
|
||||
case "singletonStyleTag":
|
||||
default:
|
||||
{
|
||||
const isSingleton = injectType === "singletonStyleTag";
|
||||
const isAuto = injectType === "autoStyleTag";
|
||||
const hmrCode = this.hot ? (0, _utils.getStyleHmrCode)(esModule, this, request, false) : "";
|
||||
|
||||
// eslint-disable-next-line consistent-return
|
||||
return `
|
||||
${(0, _utils.getImportStyleAPICode)(esModule, this)}
|
||||
${(0, _utils.getImportStyleDomAPICode)(esModule, this, isSingleton, isAuto)}
|
||||
${(0, _utils.getImportInsertBySelectorCode)(esModule, this, insertType, options)}
|
||||
${(0, _utils.getSetAttributesCode)(esModule, this, options)}
|
||||
${(0, _utils.getImportInsertStyleElementCode)(esModule, this)}
|
||||
${(0, _utils.getStyleTagTransformFnCode)(esModule, this, options, isSingleton)}
|
||||
${(0, _utils.getImportStyleContentCode)(esModule, this, request)}
|
||||
${isAuto ? (0, _utils.getImportIsOldIECode)(esModule, this) : ""}
|
||||
${esModule ? "" : `content = content.__esModule ? content.default : content;`}
|
||||
|
||||
var options = ${JSON.stringify(runtimeOptions)};
|
||||
|
||||
${(0, _utils.getStyleTagTransformFn)(options, isSingleton)};
|
||||
options.setAttributes = setAttributes;
|
||||
${(0, _utils.getInsertOptionCode)(insertType, options)}
|
||||
options.domAPI = ${(0, _utils.getdomAPI)(isAuto)};
|
||||
options.insertStyleElement = insertStyleElement;
|
||||
|
||||
var update = API(content, options);
|
||||
|
||||
${hmrCode}
|
||||
|
||||
${(0, _utils.getExportStyleCode)(esModule, this, request)}
|
||||
`;
|
||||
}
|
||||
}
|
||||
};
|
||||
var _default = exports.default = loader;
|
||||
45
node_modules/style-loader/dist/options.json
generated
vendored
Normal file
45
node_modules/style-loader/dist/options.json
generated
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
{
|
||||
"title": "Style Loader options",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"injectType": {
|
||||
"description": "Allows to setup how styles will be injected into DOM.",
|
||||
"link": "https://github.com/webpack-contrib/style-loader#injecttype",
|
||||
"enum": [
|
||||
"styleTag",
|
||||
"singletonStyleTag",
|
||||
"autoStyleTag",
|
||||
"lazyStyleTag",
|
||||
"lazySingletonStyleTag",
|
||||
"lazyAutoStyleTag",
|
||||
"linkTag"
|
||||
]
|
||||
},
|
||||
"attributes": {
|
||||
"description": "Adds custom attributes to tag.",
|
||||
"link": "https://github.com/webpack-contrib/style-loader#attributes",
|
||||
"type": "object"
|
||||
},
|
||||
"insert": {
|
||||
"description": "Inserts `<style>`/`<link>` at the given position.",
|
||||
"link": "https://github.com/webpack-contrib/style-loader#insert",
|
||||
"type": "string"
|
||||
},
|
||||
"base": {
|
||||
"description": "Sets module ID base for DLLPlugin.",
|
||||
"link": "https://github.com/webpack-contrib/style-loader#base",
|
||||
"type": "number"
|
||||
},
|
||||
"esModule": {
|
||||
"description": "Use the ES modules syntax.",
|
||||
"link": "https://github.com/webpack-contrib/css-loader#esmodule",
|
||||
"type": "boolean"
|
||||
},
|
||||
"styleTagTransform": {
|
||||
"description": "Transform tag and css when insert 'style' tag into the DOM",
|
||||
"link": "https://github.com/webpack-contrib/style-loader#styleTagTransform",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
29
node_modules/style-loader/dist/runtime/injectStylesIntoLinkTag.js
generated
vendored
Normal file
29
node_modules/style-loader/dist/runtime/injectStylesIntoLinkTag.js
generated
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
"use strict";
|
||||
|
||||
module.exports = function (url, options) {
|
||||
if (typeof document === "undefined") {
|
||||
return function () {};
|
||||
}
|
||||
options = options || {};
|
||||
options.attributes = typeof options.attributes === "object" ? options.attributes : {};
|
||||
if (typeof options.attributes.nonce === "undefined") {
|
||||
var nonce = typeof __webpack_nonce__ !== "undefined" ? __webpack_nonce__ : null;
|
||||
if (nonce) {
|
||||
options.attributes.nonce = nonce;
|
||||
}
|
||||
}
|
||||
var linkElement = document.createElement("link");
|
||||
linkElement.rel = "stylesheet";
|
||||
linkElement.href = url;
|
||||
Object.keys(options.attributes).forEach(function (key) {
|
||||
linkElement.setAttribute(key, options.attributes[key]);
|
||||
});
|
||||
options.insert(linkElement);
|
||||
return function (newUrl) {
|
||||
if (typeof newUrl === "string") {
|
||||
linkElement.href = newUrl;
|
||||
} else {
|
||||
linkElement.parentNode.removeChild(linkElement);
|
||||
}
|
||||
};
|
||||
};
|
||||
84
node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js
generated
vendored
Normal file
84
node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js
generated
vendored
Normal file
@@ -0,0 +1,84 @@
|
||||
"use strict";
|
||||
|
||||
var stylesInDOM = [];
|
||||
function getIndexByIdentifier(identifier) {
|
||||
var result = -1;
|
||||
for (var i = 0; i < stylesInDOM.length; i++) {
|
||||
if (stylesInDOM[i].identifier === identifier) {
|
||||
result = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
function modulesToDom(list, options) {
|
||||
var idCountMap = {};
|
||||
var identifiers = [];
|
||||
for (var i = 0; i < list.length; i++) {
|
||||
var item = list[i];
|
||||
var id = options.base ? item[0] + options.base : item[0];
|
||||
var count = idCountMap[id] || 0;
|
||||
var identifier = "".concat(id, " ").concat(count);
|
||||
idCountMap[id] = count + 1;
|
||||
var indexByIdentifier = getIndexByIdentifier(identifier);
|
||||
var obj = {
|
||||
css: item[1],
|
||||
media: item[2],
|
||||
sourceMap: item[3],
|
||||
supports: item[4],
|
||||
layer: item[5]
|
||||
};
|
||||
if (indexByIdentifier !== -1) {
|
||||
stylesInDOM[indexByIdentifier].references++;
|
||||
stylesInDOM[indexByIdentifier].updater(obj);
|
||||
} else {
|
||||
var updater = addElementStyle(obj, options);
|
||||
options.byIndex = i;
|
||||
stylesInDOM.splice(i, 0, {
|
||||
identifier: identifier,
|
||||
updater: updater,
|
||||
references: 1
|
||||
});
|
||||
}
|
||||
identifiers.push(identifier);
|
||||
}
|
||||
return identifiers;
|
||||
}
|
||||
function addElementStyle(obj, options) {
|
||||
var api = options.domAPI(options);
|
||||
api.update(obj);
|
||||
var updater = function updater(newObj) {
|
||||
if (newObj) {
|
||||
if (newObj.css === obj.css && newObj.media === obj.media && newObj.sourceMap === obj.sourceMap && newObj.supports === obj.supports && newObj.layer === obj.layer) {
|
||||
return;
|
||||
}
|
||||
api.update(obj = newObj);
|
||||
} else {
|
||||
api.remove();
|
||||
}
|
||||
};
|
||||
return updater;
|
||||
}
|
||||
module.exports = function (list, options) {
|
||||
options = options || {};
|
||||
list = list || [];
|
||||
var lastIdentifiers = modulesToDom(list, options);
|
||||
return function update(newList) {
|
||||
newList = newList || [];
|
||||
for (var i = 0; i < lastIdentifiers.length; i++) {
|
||||
var identifier = lastIdentifiers[i];
|
||||
var index = getIndexByIdentifier(identifier);
|
||||
stylesInDOM[index].references--;
|
||||
}
|
||||
var newLastIdentifiers = modulesToDom(newList, options);
|
||||
for (var _i = 0; _i < lastIdentifiers.length; _i++) {
|
||||
var _identifier = lastIdentifiers[_i];
|
||||
var _index = getIndexByIdentifier(_identifier);
|
||||
if (stylesInDOM[_index].references === 0) {
|
||||
stylesInDOM[_index].updater();
|
||||
stylesInDOM.splice(_index, 1);
|
||||
}
|
||||
}
|
||||
lastIdentifiers = newLastIdentifiers;
|
||||
};
|
||||
};
|
||||
34
node_modules/style-loader/dist/runtime/insertBySelector.js
generated
vendored
Normal file
34
node_modules/style-loader/dist/runtime/insertBySelector.js
generated
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
"use strict";
|
||||
|
||||
var memo = {};
|
||||
|
||||
/* istanbul ignore next */
|
||||
function getTarget(target) {
|
||||
if (typeof memo[target] === "undefined") {
|
||||
var styleTarget = document.querySelector(target);
|
||||
|
||||
// Special case to return head of iframe instead of iframe itself
|
||||
if (window.HTMLIFrameElement && styleTarget instanceof window.HTMLIFrameElement) {
|
||||
try {
|
||||
// This will throw an exception if access to iframe is blocked
|
||||
// due to cross-origin restrictions
|
||||
styleTarget = styleTarget.contentDocument.head;
|
||||
} catch (e) {
|
||||
// istanbul ignore next
|
||||
styleTarget = null;
|
||||
}
|
||||
}
|
||||
memo[target] = styleTarget;
|
||||
}
|
||||
return memo[target];
|
||||
}
|
||||
|
||||
/* istanbul ignore next */
|
||||
function insertBySelector(insert, style) {
|
||||
var target = getTarget(insert);
|
||||
if (!target) {
|
||||
throw new Error("Couldn't find a style target. This probably means that the value for the 'insert' parameter is invalid.");
|
||||
}
|
||||
target.appendChild(style);
|
||||
}
|
||||
module.exports = insertBySelector;
|
||||
10
node_modules/style-loader/dist/runtime/insertStyleElement.js
generated
vendored
Normal file
10
node_modules/style-loader/dist/runtime/insertStyleElement.js
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
"use strict";
|
||||
|
||||
/* istanbul ignore next */
|
||||
function insertStyleElement(options) {
|
||||
var element = document.createElement("style");
|
||||
options.setAttributes(element, options.attributes);
|
||||
options.insert(element, options.options);
|
||||
return element;
|
||||
}
|
||||
module.exports = insertStyleElement;
|
||||
28
node_modules/style-loader/dist/runtime/isEqualLocals.js
generated
vendored
Normal file
28
node_modules/style-loader/dist/runtime/isEqualLocals.js
generated
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
"use strict";
|
||||
|
||||
function isEqualLocals(a, b, isNamedExport) {
|
||||
if (!a && b || a && !b) {
|
||||
return false;
|
||||
}
|
||||
var p;
|
||||
for (p in a) {
|
||||
if (isNamedExport && p === "default") {
|
||||
// eslint-disable-next-line no-continue
|
||||
continue;
|
||||
}
|
||||
if (a[p] !== b[p]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
for (p in b) {
|
||||
if (isNamedExport && p === "default") {
|
||||
// eslint-disable-next-line no-continue
|
||||
continue;
|
||||
}
|
||||
if (!a[p]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
module.exports = isEqualLocals;
|
||||
17
node_modules/style-loader/dist/runtime/isOldIE.js
generated
vendored
Normal file
17
node_modules/style-loader/dist/runtime/isOldIE.js
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
"use strict";
|
||||
|
||||
var memo;
|
||||
|
||||
/* istanbul ignore next */
|
||||
function isOldIE() {
|
||||
if (typeof memo === "undefined") {
|
||||
// Test for IE <= 9 as proposed by Browserhacks
|
||||
// @see http://browserhacks.com/#hack-e71d8692f65334173fee715c222cb805
|
||||
// Tests for existence of standard globals is to allow style-loader
|
||||
// to operate correctly into non-standard environments
|
||||
// @see https://github.com/webpack-contrib/style-loader/issues/177
|
||||
memo = Boolean(typeof window !== "undefined" && typeof document !== "undefined" && document.all && !window.atob);
|
||||
}
|
||||
return memo;
|
||||
}
|
||||
module.exports = isOldIE;
|
||||
13
node_modules/style-loader/dist/runtime/setAttributesWithAttributes.js
generated
vendored
Normal file
13
node_modules/style-loader/dist/runtime/setAttributesWithAttributes.js
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
"use strict";
|
||||
|
||||
/* istanbul ignore next */
|
||||
function setAttributesWithoutAttributes(styleElement, attributes) {
|
||||
var nonce = typeof __webpack_nonce__ !== "undefined" ? __webpack_nonce__ : null;
|
||||
if (nonce) {
|
||||
attributes.nonce = nonce;
|
||||
}
|
||||
Object.keys(attributes).forEach(function (key) {
|
||||
styleElement.setAttribute(key, attributes[key]);
|
||||
});
|
||||
}
|
||||
module.exports = setAttributesWithoutAttributes;
|
||||
9
node_modules/style-loader/dist/runtime/setAttributesWithAttributesAndNonce.js
generated
vendored
Normal file
9
node_modules/style-loader/dist/runtime/setAttributesWithAttributesAndNonce.js
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
"use strict";
|
||||
|
||||
/* istanbul ignore next */
|
||||
function setAttributesWithoutAttributes(styleElement, attributes) {
|
||||
Object.keys(attributes).forEach(function (key) {
|
||||
styleElement.setAttribute(key, attributes[key]);
|
||||
});
|
||||
}
|
||||
module.exports = setAttributesWithoutAttributes;
|
||||
10
node_modules/style-loader/dist/runtime/setAttributesWithoutAttributes.js
generated
vendored
Normal file
10
node_modules/style-loader/dist/runtime/setAttributesWithoutAttributes.js
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
"use strict";
|
||||
|
||||
/* istanbul ignore next */
|
||||
function setAttributesWithoutAttributes(styleElement) {
|
||||
var nonce = typeof __webpack_nonce__ !== "undefined" ? __webpack_nonce__ : null;
|
||||
if (nonce) {
|
||||
styleElement.setAttribute("nonce", nonce);
|
||||
}
|
||||
}
|
||||
module.exports = setAttributesWithoutAttributes;
|
||||
86
node_modules/style-loader/dist/runtime/singletonStyleDomAPI.js
generated
vendored
Normal file
86
node_modules/style-loader/dist/runtime/singletonStyleDomAPI.js
generated
vendored
Normal file
@@ -0,0 +1,86 @@
|
||||
"use strict";
|
||||
|
||||
/* istanbul ignore next */
|
||||
var replaceText = function replaceText() {
|
||||
var textStore = [];
|
||||
return function replace(index, replacement) {
|
||||
textStore[index] = replacement;
|
||||
return textStore.filter(Boolean).join("\n");
|
||||
};
|
||||
}();
|
||||
|
||||
/* istanbul ignore next */
|
||||
function apply(styleElement, index, remove, obj) {
|
||||
var css;
|
||||
if (remove) {
|
||||
css = "";
|
||||
} else {
|
||||
css = "";
|
||||
if (obj.supports) {
|
||||
css += "@supports (".concat(obj.supports, ") {");
|
||||
}
|
||||
if (obj.media) {
|
||||
css += "@media ".concat(obj.media, " {");
|
||||
}
|
||||
var needLayer = typeof obj.layer !== "undefined";
|
||||
if (needLayer) {
|
||||
css += "@layer".concat(obj.layer.length > 0 ? " ".concat(obj.layer) : "", " {");
|
||||
}
|
||||
css += obj.css;
|
||||
if (needLayer) {
|
||||
css += "}";
|
||||
}
|
||||
if (obj.media) {
|
||||
css += "}";
|
||||
}
|
||||
if (obj.supports) {
|
||||
css += "}";
|
||||
}
|
||||
}
|
||||
|
||||
// For old IE
|
||||
/* istanbul ignore if */
|
||||
if (styleElement.styleSheet) {
|
||||
styleElement.styleSheet.cssText = replaceText(index, css);
|
||||
} else {
|
||||
var cssNode = document.createTextNode(css);
|
||||
var childNodes = styleElement.childNodes;
|
||||
if (childNodes[index]) {
|
||||
styleElement.removeChild(childNodes[index]);
|
||||
}
|
||||
if (childNodes.length) {
|
||||
styleElement.insertBefore(cssNode, childNodes[index]);
|
||||
} else {
|
||||
styleElement.appendChild(cssNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
var singletonData = {
|
||||
singleton: null,
|
||||
singletonCounter: 0
|
||||
};
|
||||
|
||||
/* istanbul ignore next */
|
||||
function domAPI(options) {
|
||||
if (typeof document === "undefined") return {
|
||||
update: function update() {},
|
||||
remove: function remove() {}
|
||||
};
|
||||
|
||||
// eslint-disable-next-line no-undef,no-use-before-define
|
||||
var styleIndex = singletonData.singletonCounter++;
|
||||
var styleElement =
|
||||
// eslint-disable-next-line no-undef,no-use-before-define
|
||||
singletonData.singleton || (
|
||||
// eslint-disable-next-line no-undef,no-use-before-define
|
||||
singletonData.singleton = options.insertStyleElement(options));
|
||||
return {
|
||||
update: function update(obj) {
|
||||
apply(styleElement, styleIndex, false, obj);
|
||||
},
|
||||
remove: function remove(obj) {
|
||||
apply(styleElement, styleIndex, true, obj);
|
||||
}
|
||||
};
|
||||
}
|
||||
module.exports = domAPI;
|
||||
61
node_modules/style-loader/dist/runtime/styleDomAPI.js
generated
vendored
Normal file
61
node_modules/style-loader/dist/runtime/styleDomAPI.js
generated
vendored
Normal file
@@ -0,0 +1,61 @@
|
||||
"use strict";
|
||||
|
||||
/* istanbul ignore next */
|
||||
function apply(styleElement, options, obj) {
|
||||
var css = "";
|
||||
if (obj.supports) {
|
||||
css += "@supports (".concat(obj.supports, ") {");
|
||||
}
|
||||
if (obj.media) {
|
||||
css += "@media ".concat(obj.media, " {");
|
||||
}
|
||||
var needLayer = typeof obj.layer !== "undefined";
|
||||
if (needLayer) {
|
||||
css += "@layer".concat(obj.layer.length > 0 ? " ".concat(obj.layer) : "", " {");
|
||||
}
|
||||
css += obj.css;
|
||||
if (needLayer) {
|
||||
css += "}";
|
||||
}
|
||||
if (obj.media) {
|
||||
css += "}";
|
||||
}
|
||||
if (obj.supports) {
|
||||
css += "}";
|
||||
}
|
||||
var sourceMap = obj.sourceMap;
|
||||
if (sourceMap && typeof btoa !== "undefined") {
|
||||
css += "\n/*# sourceMappingURL=data:application/json;base64,".concat(btoa(unescape(encodeURIComponent(JSON.stringify(sourceMap)))), " */");
|
||||
}
|
||||
|
||||
// For old IE
|
||||
/* istanbul ignore if */
|
||||
options.styleTagTransform(css, styleElement, options.options);
|
||||
}
|
||||
function removeStyleElement(styleElement) {
|
||||
// istanbul ignore if
|
||||
if (styleElement.parentNode === null) {
|
||||
return false;
|
||||
}
|
||||
styleElement.parentNode.removeChild(styleElement);
|
||||
}
|
||||
|
||||
/* istanbul ignore next */
|
||||
function domAPI(options) {
|
||||
if (typeof document === "undefined") {
|
||||
return {
|
||||
update: function update() {},
|
||||
remove: function remove() {}
|
||||
};
|
||||
}
|
||||
var styleElement = options.insertStyleElement(options);
|
||||
return {
|
||||
update: function update(obj) {
|
||||
apply(styleElement, options, obj);
|
||||
},
|
||||
remove: function remove() {
|
||||
removeStyleElement(styleElement);
|
||||
}
|
||||
};
|
||||
}
|
||||
module.exports = domAPI;
|
||||
14
node_modules/style-loader/dist/runtime/styleTagTransform.js
generated
vendored
Normal file
14
node_modules/style-loader/dist/runtime/styleTagTransform.js
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
"use strict";
|
||||
|
||||
/* istanbul ignore next */
|
||||
function styleTagTransform(css, styleElement) {
|
||||
if (styleElement.styleSheet) {
|
||||
styleElement.styleSheet.cssText = css;
|
||||
} else {
|
||||
while (styleElement.firstChild) {
|
||||
styleElement.removeChild(styleElement.firstChild);
|
||||
}
|
||||
styleElement.appendChild(document.createTextNode(css));
|
||||
}
|
||||
}
|
||||
module.exports = styleTagTransform;
|
||||
191
node_modules/style-loader/dist/utils.js
generated
vendored
Normal file
191
node_modules/style-loader/dist/utils.js
generated
vendored
Normal file
@@ -0,0 +1,191 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.getExportLazyStyleCode = getExportLazyStyleCode;
|
||||
exports.getExportStyleCode = getExportStyleCode;
|
||||
exports.getImportInsertBySelectorCode = getImportInsertBySelectorCode;
|
||||
exports.getImportInsertStyleElementCode = getImportInsertStyleElementCode;
|
||||
exports.getImportIsOldIECode = getImportIsOldIECode;
|
||||
exports.getImportLinkAPICode = getImportLinkAPICode;
|
||||
exports.getImportLinkContentCode = getImportLinkContentCode;
|
||||
exports.getImportStyleAPICode = getImportStyleAPICode;
|
||||
exports.getImportStyleContentCode = getImportStyleContentCode;
|
||||
exports.getImportStyleDomAPICode = getImportStyleDomAPICode;
|
||||
exports.getInsertOptionCode = getInsertOptionCode;
|
||||
exports.getLinkHmrCode = getLinkHmrCode;
|
||||
exports.getSetAttributesCode = getSetAttributesCode;
|
||||
exports.getStyleHmrCode = getStyleHmrCode;
|
||||
exports.getStyleTagTransformFn = getStyleTagTransformFn;
|
||||
exports.getStyleTagTransformFnCode = getStyleTagTransformFnCode;
|
||||
exports.getdomAPI = getdomAPI;
|
||||
exports.stringifyRequest = stringifyRequest;
|
||||
var _path = _interopRequireDefault(require("path"));
|
||||
var _isEqualLocals = _interopRequireDefault(require("./runtime/isEqualLocals"));
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
function stringifyRequest(loaderContext, request) {
|
||||
return JSON.stringify(loaderContext.utils.contextify(loaderContext.context, request));
|
||||
}
|
||||
function getImportLinkAPICode(esModule, loaderContext) {
|
||||
const modulePath = stringifyRequest(loaderContext, `!${_path.default.join(__dirname, "runtime/injectStylesIntoLinkTag.js")}`);
|
||||
return esModule ? `import API from ${modulePath};` : `var API = require(${modulePath});`;
|
||||
}
|
||||
function getImportLinkContentCode(esModule, loaderContext, request) {
|
||||
const modulePath = stringifyRequest(loaderContext, `!!${request}`);
|
||||
return esModule ? `import content from ${modulePath};` : `var content = require(${modulePath});`;
|
||||
}
|
||||
function getImportStyleAPICode(esModule, loaderContext) {
|
||||
const modulePath = stringifyRequest(loaderContext, `!${_path.default.join(__dirname, "runtime/injectStylesIntoStyleTag.js")}`);
|
||||
return esModule ? `import API from ${modulePath};` : `var API = require(${modulePath});`;
|
||||
}
|
||||
function getImportStyleDomAPICode(esModule, loaderContext, isSingleton, isAuto) {
|
||||
const styleAPI = stringifyRequest(loaderContext, `!${_path.default.join(__dirname, "runtime/styleDomAPI.js")}`);
|
||||
const singletonAPI = stringifyRequest(loaderContext, `!${_path.default.join(__dirname, "runtime/singletonStyleDomAPI.js")}`);
|
||||
if (isAuto) {
|
||||
return esModule ? `import domAPI from ${styleAPI};
|
||||
import domAPISingleton from ${singletonAPI};` : `var domAPI = require(${styleAPI});
|
||||
var domAPISingleton = require(${singletonAPI});`;
|
||||
}
|
||||
return esModule ? `import domAPI from ${isSingleton ? singletonAPI : styleAPI};` : `var domAPI = require(${isSingleton ? singletonAPI : styleAPI});`;
|
||||
}
|
||||
function getImportStyleContentCode(esModule, loaderContext, request) {
|
||||
const modulePath = stringifyRequest(loaderContext, `!!${request}`);
|
||||
return esModule ? `import content, * as namedExport from ${modulePath};` : `var content = require(${modulePath});`;
|
||||
}
|
||||
function getImportInsertBySelectorCode(esModule, loaderContext, insertType, options) {
|
||||
if (insertType === "module-path") {
|
||||
const modulePath = stringifyRequest(loaderContext, `${options.insert}`);
|
||||
loaderContext.addBuildDependency(options.insert);
|
||||
return esModule ? `import insertFn from ${modulePath};` : `var insertFn = require(${modulePath});`;
|
||||
}
|
||||
const modulePath = stringifyRequest(loaderContext, `!${_path.default.join(__dirname, "runtime/insertBySelector.js")}`);
|
||||
return esModule ? `import insertFn from ${modulePath};` : `var insertFn = require(${modulePath});`;
|
||||
}
|
||||
function getInsertOptionCode(insertType, options) {
|
||||
if (insertType === "module-path") {
|
||||
return `options.insert = insertFn;`;
|
||||
}
|
||||
const insert = options.insert ? JSON.stringify(options.insert) : '"head"';
|
||||
return `options.insert = insertFn.bind(null, ${insert});`;
|
||||
}
|
||||
function getImportInsertStyleElementCode(esModule, loaderContext) {
|
||||
const modulePath = stringifyRequest(loaderContext, `!${_path.default.join(__dirname, "runtime/insertStyleElement.js")}`);
|
||||
return esModule ? `import insertStyleElement from ${modulePath};` : `var insertStyleElement = require(${modulePath});`;
|
||||
}
|
||||
function getStyleHmrCode(esModule, loaderContext, request, lazy) {
|
||||
const modulePath = stringifyRequest(loaderContext, `!!${request}`);
|
||||
return `
|
||||
if (module.hot) {
|
||||
if (!content.locals || module.hot.invalidate) {
|
||||
var isEqualLocals = ${_isEqualLocals.default.toString()};
|
||||
var isNamedExport = ${esModule ? "!content.locals" : false};
|
||||
var oldLocals = isNamedExport ? namedExport : content.locals;
|
||||
|
||||
module.hot.accept(
|
||||
${modulePath},
|
||||
function () {
|
||||
${esModule ? `if (!isEqualLocals(oldLocals, isNamedExport ? namedExport : content.locals, isNamedExport)) {
|
||||
module.hot.invalidate();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
oldLocals = isNamedExport ? namedExport : content.locals;
|
||||
|
||||
${lazy ? `if (update && refs > 0) {
|
||||
update(content);
|
||||
}` : `update(content);`}` : `content = require(${modulePath});
|
||||
|
||||
content = content.__esModule ? content.default : content;
|
||||
|
||||
${lazy ? "" : `if (typeof content === 'string') {
|
||||
content = [[module.id, content, '']];
|
||||
}`}
|
||||
|
||||
if (!isEqualLocals(oldLocals, content.locals)) {
|
||||
module.hot.invalidate();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
oldLocals = content.locals;
|
||||
|
||||
${lazy ? `if (update && refs > 0) {
|
||||
update(content);
|
||||
}` : `update(content);`}`}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
module.hot.dispose(function() {
|
||||
${lazy ? `if (update) {
|
||||
update();
|
||||
}` : `update();`}
|
||||
});
|
||||
}
|
||||
`;
|
||||
}
|
||||
function getLinkHmrCode(esModule, loaderContext, request) {
|
||||
const modulePath = stringifyRequest(loaderContext, `!!${request}`);
|
||||
return `
|
||||
if (module.hot) {
|
||||
module.hot.accept(
|
||||
${modulePath},
|
||||
function() {
|
||||
${esModule ? "update(content);" : `content = require(${modulePath});
|
||||
|
||||
content = content.__esModule ? content.default : content;
|
||||
|
||||
update(content);`}
|
||||
}
|
||||
);
|
||||
|
||||
module.hot.dispose(function() {
|
||||
update();
|
||||
});
|
||||
}`;
|
||||
}
|
||||
function getdomAPI(isAuto) {
|
||||
return isAuto ? "isOldIE() ? domAPISingleton : domAPI" : "domAPI";
|
||||
}
|
||||
function getImportIsOldIECode(esModule, loaderContext) {
|
||||
const modulePath = stringifyRequest(loaderContext, `!${_path.default.join(__dirname, "runtime/isOldIE.js")}`);
|
||||
return esModule ? `import isOldIE from ${modulePath};` : `var isOldIE = require(${modulePath});`;
|
||||
}
|
||||
function getStyleTagTransformFnCode(esModule, loaderContext, options, isSingleton) {
|
||||
if (isSingleton) {
|
||||
return "";
|
||||
}
|
||||
if (typeof options.styleTagTransform !== "undefined") {
|
||||
const modulePath = stringifyRequest(loaderContext, `${options.styleTagTransform}`);
|
||||
loaderContext.addBuildDependency(options.styleTagTransform);
|
||||
return esModule ? `import styleTagTransformFn from ${modulePath};` : `var styleTagTransformFn = require(${modulePath});`;
|
||||
}
|
||||
const modulePath = stringifyRequest(loaderContext, `!${_path.default.join(__dirname, "runtime/styleTagTransform.js")}`);
|
||||
return esModule ? `import styleTagTransformFn from ${modulePath};` : `var styleTagTransformFn = require(${modulePath});`;
|
||||
}
|
||||
function getStyleTagTransformFn(options, isSingleton) {
|
||||
return isSingleton ? "" : `options.styleTagTransform = styleTagTransformFn`;
|
||||
}
|
||||
function getExportStyleCode(esModule, loaderContext, request) {
|
||||
const modulePath = stringifyRequest(loaderContext, `!!${request}`);
|
||||
return esModule ? `export * from ${modulePath};
|
||||
export default content && content.locals ? content.locals : undefined;` : "module.exports = content && content.locals || {};";
|
||||
}
|
||||
function getExportLazyStyleCode(esModule, loaderContext, request) {
|
||||
const modulePath = stringifyRequest(loaderContext, `!!${request}`);
|
||||
return esModule ? `export * from ${modulePath};
|
||||
export default exported;` : "module.exports = exported;";
|
||||
}
|
||||
function getSetAttributesCode(esModule, loaderContext, options) {
|
||||
let modulePath;
|
||||
if (typeof options.attributes !== "undefined") {
|
||||
modulePath = options.attributes.nonce !== "undefined" ? stringifyRequest(loaderContext, `!${_path.default.join(__dirname, "runtime/setAttributesWithAttributesAndNonce.js")}`) : stringifyRequest(loaderContext, `!${_path.default.join(__dirname, "runtime/setAttributesWithAttributes.js")}`);
|
||||
} else {
|
||||
modulePath = stringifyRequest(loaderContext, `!${_path.default.join(__dirname, "runtime/setAttributesWithoutAttributes.js")}`);
|
||||
}
|
||||
return esModule ? `import setAttributes from ${modulePath};` : `var setAttributes = require(${modulePath});`;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line import/prefer-default-export
|
||||
85
node_modules/style-loader/package.json
generated
vendored
Normal file
85
node_modules/style-loader/package.json
generated
vendored
Normal file
@@ -0,0 +1,85 @@
|
||||
{
|
||||
"name": "style-loader",
|
||||
"version": "4.0.0",
|
||||
"description": "style loader module for webpack",
|
||||
"license": "MIT",
|
||||
"repository": "webpack-contrib/style-loader",
|
||||
"author": "Tobias Koppers @sokra",
|
||||
"homepage": "https://github.com/webpack-contrib/style-loader",
|
||||
"bugs": "https://github.com/webpack-contrib/style-loader/issues",
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/webpack"
|
||||
},
|
||||
"main": "dist/cjs.js",
|
||||
"engines": {
|
||||
"node": ">= 18.12.0"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "npm run build -- -w",
|
||||
"clean": "del-cli dist",
|
||||
"validate:runtime": "es-check es3 \"dist/runtime/**/*.js\"",
|
||||
"prebuild": "npm run clean",
|
||||
"build": "cross-env NODE_ENV=production babel src -d dist --copy-files",
|
||||
"postbuild": "npm run validate:runtime",
|
||||
"commitlint": "commitlint --from=master",
|
||||
"security": "npm audit --production",
|
||||
"lint:prettier": "prettier --cache --list-different .",
|
||||
"lint:js": "eslint --cache .",
|
||||
"lint:spelling": "cspell --cache --no-must-find-files --quiet \"**/*.*\"",
|
||||
"lint": "npm-run-all -l -p \"lint:**\"",
|
||||
"fix:js": "npm run lint:js -- --fix",
|
||||
"fix:prettier": "npm run lint:prettier -- --write",
|
||||
"fix": "npm-run-all -l fix:js fix:prettier",
|
||||
"test:only": "cross-env NODE_ENV=test jest",
|
||||
"test:watch": "npm run test:only -- --watch",
|
||||
"test:coverage": "npm run test:only -- --collectCoverageFrom=\"src/**/*.js\" --coverage",
|
||||
"test:manual": "npm run build && webpack serve ./test/manual/src/index.js --open --config test/manual/webpack.config.js",
|
||||
"pretest": "npm run lint",
|
||||
"test": "npm run test:coverage",
|
||||
"prepare": "husky && npm run build",
|
||||
"release": "standard-version"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"peerDependencies": {
|
||||
"webpack": "^5.27.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "^7.24.1",
|
||||
"@babel/core": "^7.24.4",
|
||||
"@babel/preset-env": "^7.24.4",
|
||||
"@commitlint/cli": "^19.2.1",
|
||||
"@commitlint/config-conventional": "^19.1.0",
|
||||
"@webpack-contrib/eslint-config-webpack": "^3.0.0",
|
||||
"babel-jest": "^29.7.0",
|
||||
"cross-env": "^7.0.3",
|
||||
"cspell": "^8.6.1",
|
||||
"css-loader": "^7.0.0",
|
||||
"del-cli": "^5.1.0",
|
||||
"es-check": "^7.1.1",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
"eslint-plugin-import": "^2.29.1",
|
||||
"file-loader": "^6.2.0",
|
||||
"husky": "^9.0.11",
|
||||
"jest": "^29.7.0",
|
||||
"jest-environment-jsdom": "^29.7.0",
|
||||
"jsdom": "^24.0.0",
|
||||
"lint-staged": "^15.2.2",
|
||||
"memfs": "^4.8.1",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"prettier": "^3.2.5",
|
||||
"sass": "^1.74.1",
|
||||
"sass-loader": "^14.1.1",
|
||||
"semver": "^7.6.0",
|
||||
"standard-version": "^9.5.0",
|
||||
"webpack": "^5.91.0",
|
||||
"webpack-cli": "^5.1.4",
|
||||
"webpack-dev-server": "^5.0.4"
|
||||
},
|
||||
"keywords": [
|
||||
"webpack"
|
||||
]
|
||||
}
|
||||
125
node_modules/vue3-cron-plus-picker/README.md
generated
vendored
Normal file
125
node_modules/vue3-cron-plus-picker/README.md
generated
vendored
Normal file
@@ -0,0 +1,125 @@
|
||||
# vue3+elementplus 的cron表达式生成插件
|
||||
## 目的
|
||||
- vue3环境中使用cron表达式插件
|
||||
## 依赖版本
|
||||
- Vue3.0.0+
|
||||
- element-plus
|
||||
## 使用
|
||||
### 1 安装
|
||||
`npm i vue3-cron-plus-picker`
|
||||
或者
|
||||
`pnpm add vue3-cron-plus-picker`
|
||||
|
||||
### 2 引入
|
||||
1. 全局引入
|
||||
在src\main.js中引入下载的包,并引入其样式
|
||||
```js
|
||||
import { createApp } from 'vue'
|
||||
import './style.css'
|
||||
import App from './App.vue'
|
||||
import ElementPlus from 'element-plus'
|
||||
import 'element-plus/dist/index.css'
|
||||
import Vue3CronPlusPicker from 'vue3-cron-plus-picker' // 引入组件
|
||||
import 'vue3-cron-plus-picker/style.css' //引入组件相关样式
|
||||
|
||||
createApp(App).use(ElementPlus).use(Vue3CronPlusPicker).mount('#app')
|
||||
|
||||
```
|
||||
2. 局部引入
|
||||
在使用的组件的vue文件中直接引入
|
||||
```js
|
||||
import 'vue3-cron-plus-picker/style.css'
|
||||
import {Vue3CronPlusPicker} from 'vue3-cron-plus-picker'
|
||||
```
|
||||
### 3 使用
|
||||
```js
|
||||
<template>
|
||||
<div>
|
||||
<el-input class="elInput" v-model="cronValue" placeholder="请输入正确的cron表达式">
|
||||
<template #append>
|
||||
<el-button class="inputButton" @click="openDialog">配置cron</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
<el-dialog v-model="showCron" >
|
||||
<vue3-cron-plus-picker @hide="showCron=false" @fill="cronFill" :expression="expression"/>
|
||||
</el-dialog>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {ref} from 'vue'
|
||||
|
||||
|
||||
const cronValue = ref('')
|
||||
const showCron = ref()
|
||||
const expression = ref('')
|
||||
const openDialog = ()=>{
|
||||
showCron.value = true
|
||||
expression.value = cronValue.value
|
||||
}
|
||||
const cronFill = (contabValue)=>{
|
||||
cronValue.value = contabValue
|
||||
}
|
||||
|
||||
</script>
|
||||
<style>
|
||||
</style>
|
||||
|
||||
```
|
||||
### 4 参数
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>属性名</th>
|
||||
<th>说明</th>
|
||||
<th>类型</th>
|
||||
<th>Default</th>
|
||||
<th>可选值</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>expression</td>
|
||||
<td>cron表达式绑定的值</td>
|
||||
<td><span class="inline-flex items-center"><code class="api-typing mr-1">string</code></span></td>
|
||||
<td>-</td>
|
||||
<td>-</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>hideComponent</td>
|
||||
<td>可以隐藏的组件内容</td>
|
||||
<td><span class="inline-flex items-center"><code class="api-typing mr-1">Array</code></span></td>
|
||||
<td>-</td>
|
||||
<td>second/min/hour/day/mouth/week/year/result</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
### 5 事件
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>名称</th>
|
||||
<th>说明</th>
|
||||
<th>类型</th>
|
||||
<th>方法对应的参数类型</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>fill</td>
|
||||
<td>填充选择的cron表达式</td>
|
||||
<td><span class="inline-flex items-center"><code class="api-typing mr-1">Function</code></span></td>
|
||||
<td>(contabValue: string) => void</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>hide</td>
|
||||
<td>关闭组件</td>
|
||||
<td><span class="inline-flex items-center"><code class="api-typing mr-1">Function</code></span></td>
|
||||
<td>() => void</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
18
node_modules/vue3-cron-plus-picker/package.json
generated
vendored
Normal file
18
node_modules/vue3-cron-plus-picker/package.json
generated
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"name": "vue3-cron-plus-picker",
|
||||
"version": "1.0.2",
|
||||
"description": "Corn expression plugin based on vue3+elementPlus(基于vue3+elementplus+vue3的corn表达式插件)",
|
||||
"main": "vue3-cron-plus-picker.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"keywords": [
|
||||
"vue3-cron-plus-picker",
|
||||
"vue3-cron-plus",
|
||||
"vue3-cron",
|
||||
"vue3",
|
||||
"cron"
|
||||
],
|
||||
"author": "",
|
||||
"license": "ISC"
|
||||
}
|
||||
1
node_modules/vue3-cron-plus-picker/style.css
generated
vendored
Normal file
1
node_modules/vue3-cron-plus-picker/style.css
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
.pop_btn[data-v-8ae49d29]{text-align:center;margin-top:20px}.popup-main[data-v-8ae49d29]{position:relative;margin:10px auto;background:#fff;border-radius:5px;font-size:12px;overflow:hidden}.popup-title[data-v-8ae49d29]{overflow:hidden;line-height:34px;padding-top:6px;background:#f2f2f2}.popup-result[data-v-8ae49d29]{box-sizing:border-box;line-height:24px;margin:25px auto;padding:15px 10px 10px;border:1px solid #ccc;position:relative}.popup-result .title[data-v-8ae49d29]{position:absolute;top:-28px;left:50%;width:140px;font-size:14px;margin-left:-70px;text-align:center;line-height:30px;background:#fff}.popup-result table[data-v-8ae49d29]{text-align:center;width:100%;margin:0 auto}.popup-result table span[data-v-8ae49d29]{display:block;width:100%;font-family:arial;line-height:30px;height:30px;white-space:nowrap;overflow:hidden;border:1px solid #e8e8e8}.popup-result-scroll[data-v-8ae49d29]{font-size:12px;line-height:24px;height:10em;overflow-y:auto}
|
||||
1
node_modules/vue3-cron-plus-picker/vite.svg
generated
vendored
Normal file
1
node_modules/vue3-cron-plus-picker/vite.svg
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
1852
node_modules/vue3-cron-plus-picker/vue3-cron-plus-picker.js
generated
vendored
Normal file
1852
node_modules/vue3-cron-plus-picker/vue3-cron-plus-picker.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1
node_modules/vue3-cron-plus-picker/vue3-cron-plus-picker.umd.cjs
generated
vendored
Normal file
1
node_modules/vue3-cron-plus-picker/vue3-cron-plus-picker.umd.cjs
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
62
package-lock.json
generated
62
package-lock.json
generated
@@ -10,17 +10,21 @@
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"axios": "^1.6.0",
|
||||
"cron-parser": "^5.4.0",
|
||||
"element-plus": "^2.4.0",
|
||||
"vue": "^3.4.0",
|
||||
"vue-router": "^4.2.0"
|
||||
"vue-router": "^4.2.0",
|
||||
"vue3-cron-plus-picker": "^1.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.23.0",
|
||||
"@babel/preset-env": "^7.23.0",
|
||||
"@vue/cli-service": "^5.0.0",
|
||||
"babel-loader": "^9.1.0",
|
||||
"css-loader": "^6.8.0",
|
||||
"css-loader": "^6.11.0",
|
||||
"eslint": "^8.50.0",
|
||||
"html-webpack-plugin": "^5.6.4",
|
||||
"style-loader": "^4.0.0",
|
||||
"vue-loader": "^17.3.0",
|
||||
"vue-template-compiler": "^2.7.0",
|
||||
"webpack": "^5.89.0",
|
||||
@@ -3860,6 +3864,17 @@
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/cron-parser": {
|
||||
"version": "5.4.0",
|
||||
"resolved": "https://registry.npmmirror.com/cron-parser/-/cron-parser-5.4.0.tgz",
|
||||
"integrity": "sha512-HxYB8vTvnQFx4dLsZpGRa0uHp6X3qIzS3ZJgJ9v6l/5TJMgeWQbLkR5yiJ5hOxGbc9+jCADDnydIe15ReLZnJA==",
|
||||
"dependencies": {
|
||||
"luxon": "^3.7.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/cross-spawn": {
|
||||
"version": "7.0.6",
|
||||
"resolved": "https://registry.npmmirror.com/cross-spawn/-/cross-spawn-7.0.6.tgz",
|
||||
@@ -6671,6 +6686,14 @@
|
||||
"yallist": "^3.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/luxon": {
|
||||
"version": "3.7.2",
|
||||
"resolved": "https://registry.npmmirror.com/luxon/-/luxon-3.7.2.tgz",
|
||||
"integrity": "sha512-vtEhXh/gNjI9Yg1u4jX/0YVPMvxzHuGgCm6tC5kZyb08yjGWGnqAjGJvcXbqQR2P3MyMEFnRbpcdFS6PBcLqew==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/magic-string": {
|
||||
"version": "0.30.19",
|
||||
"resolved": "https://registry.npmmirror.com/magic-string/-/magic-string-0.30.19.tgz",
|
||||
@@ -9371,6 +9394,22 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/style-loader": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/style-loader/-/style-loader-4.0.0.tgz",
|
||||
"integrity": "sha512-1V4WqhhZZgjVAVJyt7TdDPZoPBPNHbekX4fWnCJL1yQukhCeZhJySUL+gL9y6sNdN95uEOS83Y55SqHcP7MzLA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 18.12.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/webpack"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"webpack": "^5.27.0"
|
||||
}
|
||||
},
|
||||
"node_modules/stylehacks": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmmirror.com/stylehacks/-/stylehacks-5.1.1.tgz",
|
||||
@@ -9679,6 +9718,20 @@
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
"version": "5.9.3",
|
||||
"resolved": "https://registry.npmmirror.com/typescript/-/typescript-5.9.3.tgz",
|
||||
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.17"
|
||||
}
|
||||
},
|
||||
"node_modules/undici-types": {
|
||||
"version": "7.12.0",
|
||||
"resolved": "https://registry.npmmirror.com/undici-types/-/undici-types-7.12.0.tgz",
|
||||
@@ -9941,6 +9994,11 @@
|
||||
"integrity": "sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/vue3-cron-plus-picker": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmmirror.com/vue3-cron-plus-picker/-/vue3-cron-plus-picker-1.0.2.tgz",
|
||||
"integrity": "sha512-SUVmAb2qSPMuAm5GIU0wQZyUawiiL3OKEy1HAZs94eZz+neKF+wEPNP4wICWMU78u4LzeCNni2Njnhf8bsqkcw=="
|
||||
},
|
||||
"node_modules/watchpack": {
|
||||
"version": "2.4.4",
|
||||
"resolved": "https://registry.npmmirror.com/watchpack/-/watchpack-2.4.4.tgz",
|
||||
|
||||
22
package.json
22
package.json
@@ -17,22 +17,26 @@
|
||||
"author": "",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"axios": "^1.6.0",
|
||||
"cron-parser": "^5.4.0",
|
||||
"element-plus": "^2.4.0",
|
||||
"vue": "^3.4.0",
|
||||
"vue-router": "^4.2.0",
|
||||
"axios": "^1.6.0",
|
||||
"element-plus": "^2.4.0"
|
||||
"vue3-cron-plus-picker": "^1.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.23.0",
|
||||
"@babel/preset-env": "^7.23.0",
|
||||
"@vue/cli-service": "^5.0.0",
|
||||
"babel-loader": "^9.1.0",
|
||||
"css-loader": "^6.11.0",
|
||||
"eslint": "^8.50.0",
|
||||
"html-webpack-plugin": "^5.6.4",
|
||||
"style-loader": "^4.0.0",
|
||||
"vue-loader": "^17.3.0",
|
||||
"vue-template-compiler": "^2.7.0",
|
||||
"webpack": "^5.89.0",
|
||||
"webpack-cli": "^5.1.0",
|
||||
"webpack-dev-server": "^4.15.0",
|
||||
"eslint": "^8.50.0",
|
||||
"babel-loader": "^9.1.0",
|
||||
"css-loader": "^6.8.0",
|
||||
"vue-loader": "^17.3.0",
|
||||
"vue-template-compiler": "^2.7.0"
|
||||
"webpack-dev-server": "^4.15.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
69
src/App.vue
69
src/App.vue
@@ -1,58 +1,37 @@
|
||||
<template>
|
||||
<div id="app">
|
||||
<header class="header">
|
||||
<h1>猪场管理系统</h1>
|
||||
<nav class="navigation">
|
||||
<el-menu mode="horizontal" :router="true">
|
||||
<el-menu-item index="/">首页</el-menu-item>
|
||||
<el-menu-item index="/devices">设备管理</el-menu-item>
|
||||
</el-menu>
|
||||
</nav>
|
||||
</header>
|
||||
<main class="main-content">
|
||||
<template v-if="isLoginPage">
|
||||
<router-view />
|
||||
</main>
|
||||
<footer class="footer">
|
||||
<p>© 2025 猪场管理系统. All rights reserved.</p>
|
||||
</footer>
|
||||
</template>
|
||||
<template v-else>
|
||||
<MainLayout />
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { computed } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import MainLayout from './layouts/MainLayout.vue';
|
||||
|
||||
export default {
|
||||
name: 'App'
|
||||
name: 'App',
|
||||
components: {
|
||||
MainLayout
|
||||
},
|
||||
setup() {
|
||||
const route = useRoute();
|
||||
const isLoginPage = computed(() => route.path === '/login');
|
||||
|
||||
return {
|
||||
isLoginPage
|
||||
};
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.header {
|
||||
background-color: #409EFF;
|
||||
color: white;
|
||||
padding: 1rem;
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,.1);
|
||||
}
|
||||
|
||||
.header h1 {
|
||||
text-align: center;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.navigation {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.main-content {
|
||||
max-width: 1200px;
|
||||
margin: 2rem auto;
|
||||
padding: 0 1rem;
|
||||
min-height: calc(100vh - 100px);
|
||||
}
|
||||
|
||||
.footer {
|
||||
text-align: center;
|
||||
padding: 1rem;
|
||||
background-color: #f5f5f5;
|
||||
border-top: 1px solid #ebeef5;
|
||||
<style>
|
||||
#app {
|
||||
min-height: 100vh;
|
||||
}
|
||||
</style>
|
||||
89
src/api/areaController.js
Normal file
89
src/api/areaController.js
Normal file
@@ -0,0 +1,89 @@
|
||||
import http from '../utils/http';
|
||||
|
||||
/**
|
||||
* @typedef {object} Response
|
||||
* @property {number} code - 业务状态码
|
||||
* @property {object} [data] - 业务数据
|
||||
* @property {string} [message] - 提示信息
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} AreaControllerResponse
|
||||
* @property {number} id
|
||||
* @property {string} name
|
||||
* @property {string} network_id
|
||||
* @property {string} location
|
||||
* @property {string} status
|
||||
* @property {object} properties
|
||||
* @property {string} created_at
|
||||
* @property {string} updated_at
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} CreateAreaControllerRequest
|
||||
* @property {string} name
|
||||
* @property {string} network_id
|
||||
* @property {string} [location]
|
||||
* @property {object} [properties]
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} UpdateAreaControllerRequest
|
||||
* @property {string} name
|
||||
* @property {string} network_id
|
||||
* @property {string} [location]
|
||||
* @property {object} [properties]
|
||||
*/
|
||||
|
||||
/**
|
||||
* 获取系统中所有区域主控的列表
|
||||
* @returns {Promise<Array<AreaControllerResponse>>}
|
||||
*/
|
||||
export const getAreaControllers = () => {
|
||||
return http.get('/api/v1/area-controllers');
|
||||
};
|
||||
|
||||
/**
|
||||
* 根据提供的信息创建一个新区域主控
|
||||
* @param {CreateAreaControllerRequest} areaControllerData - 区域主控信息
|
||||
* @returns {Promise<AreaControllerResponse>}
|
||||
*/
|
||||
export const createAreaController = (areaControllerData) => {
|
||||
return http.post('/api/v1/area-controllers', areaControllerData);
|
||||
};
|
||||
|
||||
/**
|
||||
* 根据ID获取单个区域主控的详细信息
|
||||
* @param {string} id - 区域主控ID
|
||||
* @returns {Promise<AreaControllerResponse>}
|
||||
*/
|
||||
export const getAreaControllerById = (id) => {
|
||||
return http.get(`/api/v1/area-controllers/${id}`);
|
||||
};
|
||||
|
||||
/**
|
||||
* 根据ID更新一个已存在的区域主控信息
|
||||
* @param {string} id - 区域主控ID
|
||||
* @param {UpdateAreaControllerRequest} areaControllerData - 要更新的区域主控信息
|
||||
* @returns {Promise<AreaControllerResponse>}
|
||||
*/
|
||||
export const updateAreaController = (id, areaControllerData) => {
|
||||
return http.put(`/api/v1/area-controllers/${id}`, areaControllerData);
|
||||
};
|
||||
|
||||
/**
|
||||
* 根据ID删除一个区域主控(软删除)
|
||||
* @param {string} id - 区域主控ID
|
||||
* @returns {Promise<Response>}
|
||||
*/
|
||||
export const deleteAreaController = (id) => {
|
||||
return http.delete(`/api/v1/area-controllers/${id}`);
|
||||
};
|
||||
|
||||
export const AreaControllerApi = {
|
||||
list: getAreaControllers,
|
||||
create: createAreaController,
|
||||
getById: getAreaControllerById,
|
||||
update: updateAreaController,
|
||||
delete: deleteAreaController,
|
||||
};
|
||||
203
src/api/device.js
Normal file
203
src/api/device.js
Normal file
@@ -0,0 +1,203 @@
|
||||
import http from '../utils/http';
|
||||
|
||||
// --- Typedefs ---
|
||||
|
||||
/**
|
||||
* @typedef {object} Response
|
||||
* @property {number} code - 业务状态码
|
||||
* @property {object} [data] - 业务数据
|
||||
* @property {string} [message] - 提示信息
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} DeviceResponse
|
||||
* @property {number} id
|
||||
* @property {string} name
|
||||
* @property {string} location
|
||||
* @property {number} area_controller_id
|
||||
* @property {string} area_controller_name
|
||||
* @property {number} device_template_id
|
||||
* @property {string} device_template_name
|
||||
* @property {object} properties
|
||||
* @property {string} created_at
|
||||
* @property {string} updated_at
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} CreateDeviceRequest
|
||||
* @property {string} name
|
||||
* @property {string} [location]
|
||||
* @property {number} area_controller_id
|
||||
* @property {number} device_template_id
|
||||
* @property {object} [properties]
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} UpdateDeviceRequest
|
||||
* @property {string} name
|
||||
* @property {string} [location]
|
||||
* @property {number} area_controller_id
|
||||
* @property {number} device_template_id
|
||||
* @property {object} [properties]
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} ManualControlDeviceRequest
|
||||
* @property {string} [action] - Action 不传表示这是一个传感器, 会触发一次采集
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} AreaControllerResponse
|
||||
* @property {number} id
|
||||
* @property {string} name
|
||||
* @property {string} network_id
|
||||
* @property {string} location
|
||||
* @property {string} status
|
||||
* @property {object} properties
|
||||
* @property {string} created_at
|
||||
* @property {string} updated_at
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} CreateAreaControllerRequest
|
||||
* @property {string} name
|
||||
* @property {string} network_id
|
||||
* @property {string} [location]
|
||||
* @property {object} [properties]
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} UpdateAreaControllerRequest
|
||||
* @property {string} name
|
||||
* @property {string} network_id
|
||||
* @property {string} [location]
|
||||
* @property {object} [properties]
|
||||
*/
|
||||
|
||||
|
||||
// --- Device API Functions ---
|
||||
|
||||
/**
|
||||
* 获取系统中所有设备的列表
|
||||
* @returns {Promise<Array<DeviceResponse>>}
|
||||
*/
|
||||
export const getDevices = () => {
|
||||
return http.get('/api/v1/devices');
|
||||
};
|
||||
|
||||
/**
|
||||
* 根据提供的信息创建一个新设备
|
||||
* @param {CreateDeviceRequest} deviceData - 设备信息
|
||||
* @returns {Promise<DeviceResponse>}
|
||||
*/
|
||||
export const createDevice = (deviceData) => {
|
||||
return http.post('/api/v1/devices', deviceData);
|
||||
};
|
||||
|
||||
/**
|
||||
* 根据设备ID获取单个设备的详细信息
|
||||
* @param {string} id - 设备ID
|
||||
* @returns {Promise<DeviceResponse>}
|
||||
*/
|
||||
export const getDeviceById = (id) => {
|
||||
return http.get(`/api/v1/devices/${id}`);
|
||||
};
|
||||
|
||||
/**
|
||||
* 根据设备ID更新一个已存在的设备信息
|
||||
* @param {string} id - 设备ID
|
||||
* @param {UpdateDeviceRequest} deviceData - 要更新的设备信息
|
||||
* @returns {Promise<DeviceResponse>}
|
||||
*/
|
||||
export const updateDevice = (id, deviceData) => {
|
||||
return http.put(`/api/v1/devices/${id}`, deviceData);
|
||||
};
|
||||
|
||||
/**
|
||||
* 根据设备ID删除一个设备(软删除)
|
||||
* @param {string} id - 设备ID
|
||||
* @returns {Promise<Response>}
|
||||
*/
|
||||
export const deleteDevice = (id) => {
|
||||
return http.delete(`/api/v1/devices/${id}`);
|
||||
};
|
||||
|
||||
/**
|
||||
* 根据设备ID和指定的动作(开启或关闭)来手动控制设备
|
||||
* @param {string} id - 设备ID
|
||||
* @param {ManualControlDeviceRequest} manualControlData - 手动控制指令
|
||||
* @returns {Promise<Response>}
|
||||
*/
|
||||
export const manualControlDevice = (id, manualControlData) => {
|
||||
return http.post(`/api/v1/devices/manual-control/${id}`, manualControlData);
|
||||
};
|
||||
|
||||
|
||||
// --- AreaController API Functions ---
|
||||
|
||||
/**
|
||||
* 获取系统中所有区域主控的列表
|
||||
* @returns {Promise<Array<AreaControllerResponse>>}
|
||||
*/
|
||||
export const getAreaControllers = () => {
|
||||
return http.get('/api/v1/area-controllers');
|
||||
};
|
||||
|
||||
/**
|
||||
* 创建一个新区域主控
|
||||
* @param {CreateAreaControllerRequest} areaControllerData - 区域主控信息
|
||||
* @returns {Promise<AreaControllerResponse>}
|
||||
*/
|
||||
export const createAreaController = (areaControllerData) => {
|
||||
return http.post('/api/v1/area-controllers', areaControllerData);
|
||||
};
|
||||
|
||||
/**
|
||||
* 根据ID获取单个区域主控的详细信息
|
||||
* @param {string} id - 区域主控ID
|
||||
* @returns {Promise<AreaControllerResponse>}
|
||||
*/
|
||||
export const getAreaControllerById = (id) => {
|
||||
return http.get(`/api/v1/area-controllers/${id}`);
|
||||
};
|
||||
|
||||
/**
|
||||
* 根据ID更新一个已存在的区域主控信息
|
||||
* @param {string} id - 区域主控ID
|
||||
* @param {UpdateAreaControllerRequest} areaControllerData - 要更新的区域主控信息
|
||||
* @returns {Promise<AreaControllerResponse>}
|
||||
*/
|
||||
export const updateAreaController = (id, areaControllerData) => {
|
||||
return http.put(`/api/v1/area-controllers/${id}`, areaControllerData);
|
||||
};
|
||||
|
||||
/**
|
||||
* 根据ID删除一个区域主控
|
||||
* @param {string} id - 区域主控ID
|
||||
* @returns {Promise<Response>}
|
||||
*/
|
||||
export const deleteAreaController = (id) => {
|
||||
return http.delete(`/api/v1/area-controllers/${id}`);
|
||||
};
|
||||
|
||||
|
||||
// --- API Wrappers ---
|
||||
|
||||
// AreaControllerApi 封装
|
||||
export const AreaControllerApi = {
|
||||
list: getAreaControllers,
|
||||
create: createAreaController,
|
||||
getById: getAreaControllerById,
|
||||
update: updateAreaController,
|
||||
delete: deleteAreaController
|
||||
};
|
||||
|
||||
// DeviceApi 封装
|
||||
export const DeviceApi = {
|
||||
list: getDevices,
|
||||
create: createDevice,
|
||||
getById: getDeviceById,
|
||||
update: updateDevice,
|
||||
delete: deleteDevice,
|
||||
manualControl: manualControlDevice
|
||||
};
|
||||
110
src/api/deviceTemplate.js
Normal file
110
src/api/deviceTemplate.js
Normal file
@@ -0,0 +1,110 @@
|
||||
import http from '../utils/http';
|
||||
|
||||
/**
|
||||
* @typedef {object} Response
|
||||
* @property {number} code - 业务状态码
|
||||
* @property {object} [data] - 业务数据
|
||||
* @property {string} [message] - 提示信息
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {('执行器'|'传感器')} DeviceCategory
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {('信号强度'|'电池电量'|'温度'|'湿度'|'重量')} SensorType
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} ValueDescriptor
|
||||
* @property {SensorType} type
|
||||
* @property {number} [multiplier] - 乘数,用于原始数据转换
|
||||
* @property {number} [offset] - 偏移量,用于原始数据转换
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} DeviceTemplateResponse
|
||||
* @property {number} id
|
||||
* @property {string} name
|
||||
* @property {string} [description]
|
||||
* @property {string} [manufacturer]
|
||||
* @property {DeviceCategory} category
|
||||
* @property {object} commands
|
||||
* @property {Array<ValueDescriptor>} values
|
||||
* @property {string} created_at
|
||||
* @property {string} updated_at
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} CreateDeviceTemplateRequest
|
||||
* @property {string} name
|
||||
* @property {string} [description]
|
||||
* @property {string} [manufacturer]
|
||||
* @property {DeviceCategory} category
|
||||
* @property {object} commands
|
||||
* @property {Array<ValueDescriptor>} [values]
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} UpdateDeviceTemplateRequest
|
||||
* @property {string} name
|
||||
* @property {string} [description]
|
||||
* @property {string} [manufacturer]
|
||||
* @property {DeviceCategory} category
|
||||
* @property {object} commands
|
||||
* @property {Array<ValueDescriptor>} [values]
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* 获取系统中所有设备模板的列表
|
||||
* @returns {Promise<Array<DeviceTemplateResponse>>}
|
||||
*/
|
||||
const getDeviceTemplates = () => {
|
||||
return http.get('/api/v1/device-templates');
|
||||
};
|
||||
|
||||
/**
|
||||
* 根据提供的信息创建一个新设备模板
|
||||
* @param {CreateDeviceTemplateRequest} deviceTemplateData - 设备模板信息
|
||||
* @returns {Promise<DeviceTemplateResponse>}
|
||||
*/
|
||||
const createDeviceTemplate = (deviceTemplateData) => {
|
||||
return http.post('/api/v1/device-templates', deviceTemplateData);
|
||||
};
|
||||
|
||||
/**
|
||||
* 根据设备模板ID获取单个设备模板的详细信息
|
||||
* @param {number} id - 设备模板ID
|
||||
* @returns {Promise<DeviceTemplateResponse>}
|
||||
*/
|
||||
const getDeviceTemplateById = (id) => {
|
||||
return http.get(`/api/v1/device-templates/${id}`);
|
||||
};
|
||||
|
||||
/**
|
||||
* 根据设备模板ID更新一个已存在的设备模板信息
|
||||
* @param {number} id - 设备模板ID
|
||||
* @param {UpdateDeviceTemplateRequest} deviceTemplateData - 要更新的设备模板信息
|
||||
* @returns {Promise<DeviceTemplateResponse>}
|
||||
*/
|
||||
const updateDeviceTemplate = (id, deviceTemplateData) => {
|
||||
return http.put(`/api/v1/device-templates/${id}`, deviceTemplateData);
|
||||
};
|
||||
|
||||
/**
|
||||
* 根据设备模板ID删除一个设备模板(软删除)
|
||||
* @param {number} id - 设备模板ID
|
||||
* @returns {Promise<Response>}
|
||||
*/
|
||||
const deleteDeviceTemplate = (id) => {
|
||||
return http.delete(`/api/v1/device-templates/${id}`);
|
||||
};
|
||||
|
||||
export const DeviceTemplateApi = {
|
||||
getDeviceTemplates,
|
||||
createDeviceTemplate,
|
||||
getDeviceTemplateById,
|
||||
updateDeviceTemplate,
|
||||
deleteDeviceTemplate,
|
||||
};
|
||||
20
src/api/index.js
Normal file
20
src/api/index.js
Normal file
@@ -0,0 +1,20 @@
|
||||
import { AreaControllerApi, DeviceApi } from './device.js';
|
||||
import { PlanApi } from './plan.js';
|
||||
import { UserApi } from './user.js';
|
||||
import { DeviceTemplateApi } from './deviceTemplate.js'; // 导入设备模板API
|
||||
|
||||
/**
|
||||
* API客户端
|
||||
*/
|
||||
export class ApiClient {
|
||||
constructor() {
|
||||
this.areaControllers = AreaControllerApi;
|
||||
this.devices = DeviceApi;
|
||||
this.plans = PlanApi;
|
||||
this.users = UserApi;
|
||||
this.deviceTemplates = DeviceTemplateApi; // 添加设备模板API
|
||||
}
|
||||
}
|
||||
|
||||
// 导出API客户端实例
|
||||
export default new ApiClient();
|
||||
900
src/api/monitor.js
Normal file
900
src/api/monitor.js
Normal file
@@ -0,0 +1,900 @@
|
||||
import http from '../utils/http';
|
||||
|
||||
// --- Typedefs ---
|
||||
|
||||
/**
|
||||
* @typedef {object} PaginationDTO
|
||||
* @property {number} page
|
||||
* @property {number} page_size
|
||||
* @property {number} total
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} DeviceCommandLogDTO
|
||||
* @property {string} message_id
|
||||
* @property {number} device_id
|
||||
* @property {string} sent_at
|
||||
* @property {string} acknowledged_at
|
||||
* @property {boolean} received_success
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} ListDeviceCommandLogResponse
|
||||
* @property {Array<DeviceCommandLogDTO>} list
|
||||
* @property {PaginationDTO} pagination
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} DeviceCommandLogsParams
|
||||
* @property {number} [page]
|
||||
* @property {number} [page_size]
|
||||
* @property {string} [order_by]
|
||||
* @property {number} [device_id]
|
||||
* @property {string} [start_time]
|
||||
* @property {string} [end_time]
|
||||
* @property {boolean} [received_success]
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} FeedFormulaDTO
|
||||
* @property {number} id
|
||||
* @property {string} name
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} PenDTO
|
||||
* @property {number} id
|
||||
* @property {string} name
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} FeedUsageRecordDTO
|
||||
* @property {number} id
|
||||
* @property {number} pen_id
|
||||
* @property {PenDTO} pen
|
||||
* @property {number} feed_formula_id
|
||||
* @property {FeedFormulaDTO} feed_formula
|
||||
* @property {number} amount
|
||||
* @property {string} recorded_at
|
||||
* @property {string} remarks
|
||||
* @property {number} operator_id
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} ListFeedUsageRecordResponse
|
||||
* @property {Array<FeedUsageRecordDTO>} list
|
||||
* @property {PaginationDTO} pagination
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} FeedUsageRecordsParams
|
||||
* @property {number} [page]
|
||||
* @property {number} [page_size]
|
||||
* @property {string} [order_by]
|
||||
* @property {number} [pen_id]
|
||||
* @property {number} [feed_formula_id]
|
||||
* @property {string} [start_time]
|
||||
* @property {string} [end_time]
|
||||
* @property {number} [operator_id]
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {('预防'|'治疗'|'保健')} MedicationReasonType
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} MedicationDTO
|
||||
* @property {number} id
|
||||
* @property {string} name
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} MedicationLogDTO
|
||||
* @property {number} id
|
||||
* @property {number} pig_batch_id
|
||||
* @property {number} medication_id
|
||||
* @property {MedicationDTO} medication
|
||||
* @property {number} dosage_used
|
||||
* @property {number} target_count
|
||||
* @property {MedicationReasonType} reason
|
||||
* @property {string} description
|
||||
* @property {string} happened_at
|
||||
* @property {number} operator_id
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} ListMedicationLogResponse
|
||||
* @property {Array<MedicationLogDTO>} list
|
||||
* @property {PaginationDTO} pagination
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} MedicationLogsParams
|
||||
* @property {number} [page]
|
||||
* @property {number} [page_size]
|
||||
* @property {string} [order_by]
|
||||
* @property {number} [pig_batch_id]
|
||||
* @property {number} [medication_id]
|
||||
* @property {string} [reason]
|
||||
* @property {string} [start_time]
|
||||
* @property {string} [end_time]
|
||||
* @property {number} [operator_id]
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {('邮件'|'企业微信'|'飞书'|'日志')} NotifierType
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {('发送成功'|'发送失败'|'已跳过')} NotificationStatus
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} NotificationDTO
|
||||
* @property {number} id
|
||||
* @property {number} user_id
|
||||
* @property {NotifierType} notifier_type
|
||||
* @property {string} to_address
|
||||
* @property {string} title
|
||||
* @property {string} message
|
||||
* @property {number} level - 日志级别, 见 ZapcoreLevel 枚举
|
||||
* @property {string} alarm_timestamp
|
||||
* @property {NotificationStatus} status
|
||||
* @property {string} error_message
|
||||
* @property {string} created_at
|
||||
* @property {string} updated_at
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} ListNotificationResponse
|
||||
* @property {Array<NotificationDTO>} list
|
||||
* @property {PaginationDTO} pagination
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} NotificationsParams
|
||||
* @property {number} [page]
|
||||
* @property {number} [page_size]
|
||||
* @property {string} [order_by]
|
||||
* @property {string} [start_time]
|
||||
* @property {string} [end_time]
|
||||
* @property {number} [level] - 日志级别, 见 ZapcoreLevel 枚举
|
||||
* @property {NotifierType} [notifier_type]
|
||||
* @property {NotificationStatus} [status]
|
||||
* @property {number} [user_id]
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {('等待中'|'已完成'|'已超时')} PendingCollectionStatus
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} PendingCollectionDTO
|
||||
* @property {string} correlation_id
|
||||
* @property {number} device_id
|
||||
* @property {Array<number>} command_metadata
|
||||
* @property {PendingCollectionStatus} status
|
||||
* @property {string} created_at
|
||||
* @property {string} fulfilled_at
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} ListPendingCollectionResponse
|
||||
* @property {Array<PendingCollectionDTO>} list
|
||||
* @property {PaginationDTO} pagination
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} PendingCollectionsParams
|
||||
* @property {number} [page]
|
||||
* @property {number} [page_size]
|
||||
* @property {string} [order_by]
|
||||
* @property {number} [device_id]
|
||||
* @property {string} [status]
|
||||
* @property {string} [start_time]
|
||||
* @property {string} [end_time]
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {('死亡'|'淘汰'|'销售'|'购买'|'转入'|'转出'|'盘点校正')} LogChangeType
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} PigBatchLogDTO
|
||||
* @property {number} id
|
||||
* @property {number} pig_batch_id
|
||||
* @property {LogChangeType} change_type
|
||||
* @property {number} before_count
|
||||
* @property {number} after_count
|
||||
* @property {number} change_count
|
||||
* @property {string} reason
|
||||
* @property {number} operator_id
|
||||
* @property {string} happened_at
|
||||
* @property {string} created_at
|
||||
* @property {string} updated_at
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} ListPigBatchLogResponse
|
||||
* @property {Array<PigBatchLogDTO>} list
|
||||
* @property {PaginationDTO} pagination
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} PigBatchLogsParams
|
||||
* @property {number} [page]
|
||||
* @property {number} [page_size]
|
||||
* @property {string} [order_by]
|
||||
* @property {number} [pig_batch_id]
|
||||
* @property {string} [change_type]
|
||||
* @property {string} [start_time]
|
||||
* @property {string} [end_time]
|
||||
* @property {number} [operator_id]
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} PigPurchaseDTO
|
||||
* @property {number} id
|
||||
* @property {number} pig_batch_id
|
||||
* @property {number} quantity
|
||||
* @property {number} unit_price
|
||||
* @property {number} total_price
|
||||
* @property {string} supplier
|
||||
* @property {string} purchase_date
|
||||
* @property {string} remarks
|
||||
* @property {number} operator_id
|
||||
* @property {string} created_at
|
||||
* @property {string} updated_at
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} ListPigPurchaseResponse
|
||||
* @property {Array<PigPurchaseDTO>} list
|
||||
* @property {PaginationDTO} pagination
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} PigPurchasesParams
|
||||
* @property {number} [page]
|
||||
* @property {number} [page_size]
|
||||
* @property {string} [order_by]
|
||||
* @property {number} [pig_batch_id]
|
||||
* @property {string} [supplier]
|
||||
* @property {string} [start_time]
|
||||
* @property {string} [end_time]
|
||||
* @property {number} [operator_id]
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} PigSaleDTO
|
||||
* @property {number} id
|
||||
* @property {number} pig_batch_id
|
||||
* @property {number} quantity
|
||||
* @property {number} unit_price
|
||||
* @property {number} total_price
|
||||
* @property {string} buyer
|
||||
* @property {string} sale_date
|
||||
* @property {string} remarks
|
||||
* @property {number} operator_id
|
||||
* @property {string} created_at
|
||||
* @property {string} updated_at
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} ListPigSaleResponse
|
||||
* @property {Array<PigSaleDTO>} list
|
||||
* @property {PaginationDTO} pagination
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} PigSalesParams
|
||||
* @property {number} [page]
|
||||
* @property {number} [page_size]
|
||||
* @property {string} [order_by]
|
||||
* @property {number} [pig_batch_id]
|
||||
* @property {string} [buyer]
|
||||
* @property {string} [start_time]
|
||||
* @property {string} [end_time]
|
||||
* @property {number} [operator_id]
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {('患病'|'康复'|'死亡'|'淘汰'|'转入'|'转出'|'其他')} PigBatchSickPigReasonType
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {('原地治疗'|'病猪栏治疗')} PigBatchSickPigTreatmentLocation
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} PigSickLogDTO
|
||||
* @property {number} id
|
||||
* @property {number} pig_batch_id
|
||||
* @property {number} pen_id
|
||||
* @property {PigBatchSickPigReasonType} reason
|
||||
* @property {PigBatchSickPigTreatmentLocation} treatment_location
|
||||
* @property {number} before_count
|
||||
* @property {number} after_count
|
||||
* @property {number} change_count
|
||||
* @property {string} remarks
|
||||
* @property {number} operator_id
|
||||
* @property {string} happened_at
|
||||
* @property {string} created_at
|
||||
* @property {string} updated_at
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} ListPigSickLogResponse
|
||||
* @property {Array<PigSickLogDTO>} list
|
||||
* @property {PaginationDTO} pagination
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} PigSickLogsParams
|
||||
* @property {number} [page]
|
||||
* @property {number} [page_size]
|
||||
* @property {string} [order_by]
|
||||
* @property {number} [pig_batch_id]
|
||||
* @property {number} [pen_id]
|
||||
* @property {string} [reason]
|
||||
* @property {string} [treatment_location]
|
||||
* @property {string} [start_time]
|
||||
* @property {string} [end_time]
|
||||
* @property {number} [operator_id]
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {('群内调栏'|'跨群调栏'|'销售'|'死亡'|'淘汰'|'新购入'|'产房转入')} PigTransferType
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} PigTransferLogDTO
|
||||
* @property {number} id
|
||||
* @property {number} pig_batch_id
|
||||
* @property {number} pen_id
|
||||
* @property {PigTransferType} type
|
||||
* @property {number} quantity
|
||||
* @property {string} remarks
|
||||
* @property {string} correlation_id
|
||||
* @property {number} operator_id
|
||||
* @property {string} transfer_time
|
||||
* @property {string} created_at
|
||||
* @property {string} updated_at
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} ListPigTransferLogResponse
|
||||
* @property {Array<PigTransferLogDTO>} list
|
||||
* @property {PaginationDTO} pagination
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} PigTransferLogsParams
|
||||
* @property {number} [page]
|
||||
* @property {number} [page_size]
|
||||
* @property {string} [order_by]
|
||||
* @property {number} [pig_batch_id]
|
||||
* @property {number} [pen_id]
|
||||
* @property {string} [transfer_type]
|
||||
* @property {string} [correlation_id]
|
||||
* @property {string} [start_time]
|
||||
* @property {string} [end_time]
|
||||
* @property {number} [operator_id]
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {('已开始'|'已完成'|'失败'|'已取消'|'等待中')} ExecutionStatus
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} PlanExecutionLogDTO
|
||||
* @property {number} id
|
||||
* @property {number} plan_id
|
||||
* @property {string} plan_name
|
||||
* @property {ExecutionStatus} status
|
||||
* @property {string} started_at
|
||||
* @property {string} ended_at
|
||||
* @property {string} error
|
||||
* @property {string} created_at
|
||||
* @property {string} updated_at
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} ListPlanExecutionLogResponse
|
||||
* @property {Array<PlanExecutionLogDTO>} list
|
||||
* @property {PaginationDTO} pagination
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} PlanExecutionLogsParams
|
||||
* @property {number} [page]
|
||||
* @property {number} [page_size]
|
||||
* @property {string} [order_by]
|
||||
* @property {number} [plan_id]
|
||||
* @property {string} [status]
|
||||
* @property {string} [start_time]
|
||||
* @property {string} [end_time]
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} RawMaterialDTO
|
||||
* @property {number} id
|
||||
* @property {string} name
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} RawMaterialPurchaseDTO
|
||||
* @property {number} id
|
||||
* @property {number} raw_material_id
|
||||
* @property {RawMaterialDTO} raw_material
|
||||
* @property {number} amount
|
||||
* @property {number} unit_price
|
||||
* @property {number} total_price
|
||||
* @property {string} supplier
|
||||
* @property {string} purchase_date
|
||||
* @property {string} created_at
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} ListRawMaterialPurchaseResponse
|
||||
* @property {Array<RawMaterialPurchaseDTO>} list
|
||||
* @property {PaginationDTO} pagination
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} RawMaterialPurchasesParams
|
||||
* @property {number} [page]
|
||||
* @property {number} [page_size]
|
||||
* @property {string} [order_by]
|
||||
* @property {number} [raw_material_id]
|
||||
* @property {string} [supplier]
|
||||
* @property {string} [start_time]
|
||||
* @property {string} [end_time]
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {('采购入库'|'饲喂出库'|'变质出库'|'售卖出库'|'杂用领取'|'手动盘点')} StockLogSourceType
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} RawMaterialStockLogDTO
|
||||
* @property {number} id
|
||||
* @property {number} raw_material_id
|
||||
* @property {number} change_amount
|
||||
* @property {StockLogSourceType} source_type
|
||||
* @property {number} source_id
|
||||
* @property {string} remarks
|
||||
* @property {string} happened_at
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} ListRawMaterialStockLogResponse
|
||||
* @property {Array<RawMaterialStockLogDTO>} list
|
||||
* @property {PaginationDTO} pagination
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} RawMaterialStockLogsParams
|
||||
* @property {number} [page]
|
||||
* @property {number} [page_size]
|
||||
* @property {string} [order_by]
|
||||
* @property {number} [raw_material_id]
|
||||
* @property {string} [source_type]
|
||||
* @property {number} [source_id]
|
||||
* @property {string} [start_time]
|
||||
* @property {string} [end_time]
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {('信号强度'|'电池电量'|'温度'|'湿度'|'重量')} SensorType
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} SensorDataDTO
|
||||
* @property {number} regional_controller_id
|
||||
* @property {number} device_id
|
||||
* @property {SensorType} sensor_type
|
||||
* @property {Array<number>} data
|
||||
* @property {string} time
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} ListSensorDataResponse
|
||||
* @property {Array<SensorDataDTO>} list
|
||||
* @property {PaginationDTO} pagination
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} SensorDataParams
|
||||
* @property {number} [page]
|
||||
* @property {number} [page_size]
|
||||
* @property {string} [order_by]
|
||||
* @property {number} [device_id]
|
||||
* @property {string} [sensor_type]
|
||||
* @property {string} [start_time]
|
||||
* @property {string} [end_time]
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} TaskDTO
|
||||
* @property {number} id
|
||||
* @property {string} name
|
||||
* @property {string} description
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} TaskExecutionLogDTO
|
||||
* @property {number} id
|
||||
* @property {number} plan_execution_log_id
|
||||
* @property {number} task_id
|
||||
* @property {TaskDTO} task
|
||||
* @property {ExecutionStatus} status
|
||||
* @property {string} output
|
||||
* @property {string} started_at
|
||||
* @property {string} ended_at
|
||||
* @property {string} created_at
|
||||
* @property {string} updated_at
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} ListTaskExecutionLogResponse
|
||||
* @property {Array<TaskExecutionLogDTO>} list
|
||||
* @property {PaginationDTO} pagination
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} TaskExecutionLogsParams
|
||||
* @property {number} [page]
|
||||
* @property {number} [page_size]
|
||||
* @property {string} [order_by]
|
||||
* @property {number} [plan_execution_log_id]
|
||||
* @property {number} [task_id]
|
||||
* @property {string} [status]
|
||||
* @property {string} [start_time]
|
||||
* @property {string} [end_time]
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {('成功'|'失败')} AuditStatus
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} UserActionLogDTO
|
||||
* @property {number} id
|
||||
* @property {number} user_id
|
||||
* @property {string} username
|
||||
* @property {string} action_type
|
||||
* @property {string} description
|
||||
* @property {string} http_method
|
||||
* @property {string} http_path
|
||||
* @property {string} source_ip
|
||||
* @property {Array<number>} target_resource
|
||||
* @property {AuditStatus} status
|
||||
* @property {string} result_details
|
||||
* @property {string} time
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} ListUserActionLogResponse
|
||||
* @property {Array<UserActionLogDTO>} list
|
||||
* @property {PaginationDTO} pagination
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} UserActionLogsParams
|
||||
* @property {number} [page]
|
||||
* @property {number} [page_size]
|
||||
* @property {string} [order_by]
|
||||
* @property {number} [user_id]
|
||||
* @property {string} [username]
|
||||
* @property {string} [action_type]
|
||||
* @property {string} [status]
|
||||
* @property {string} [start_time]
|
||||
* @property {string} [end_time]
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} WeighingBatchDTO
|
||||
* @property {number} id
|
||||
* @property {number} pig_batch_id
|
||||
* @property {string} description
|
||||
* @property {string} weighing_time
|
||||
* @property {string} created_at
|
||||
* @property {string} updated_at
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} ListWeighingBatchResponse
|
||||
* @property {Array<WeighingBatchDTO>} list
|
||||
* @property {PaginationDTO} pagination
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} WeighingBatchesParams
|
||||
* @property {number} [page]
|
||||
* @property {number} [page_size]
|
||||
* @property {string} [order_by]
|
||||
* @property {number} [pig_batch_id]
|
||||
* @property {string} [start_time]
|
||||
* @property {string} [end_time]
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} WeighingRecordDTO
|
||||
* @property {number} id
|
||||
* @property {number} weighing_batch_id
|
||||
* @property {number} pen_id
|
||||
* @property {number} weight
|
||||
* @property {string} remark
|
||||
* @property {number} operator_id
|
||||
* @property {string} weighing_time
|
||||
* @property {string} created_at
|
||||
* @property {string} updated_at
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} ListWeighingRecordResponse
|
||||
* @property {Array<WeighingRecordDTO>} list
|
||||
* @property {PaginationDTO} pagination
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} WeighingRecordsParams
|
||||
* @property {number} [page]
|
||||
* @property {number} [page_size]
|
||||
* @property {string} [order_by]
|
||||
* @property {number} [weighing_batch_id]
|
||||
* @property {number} [pen_id]
|
||||
* @property {string} [start_time]
|
||||
* @property {string} [end_time]
|
||||
* @property {number} [operator_id]
|
||||
*/
|
||||
|
||||
// --- Enums ---
|
||||
|
||||
/**
|
||||
* 日志级别, 对应后端的 zapcore.Level
|
||||
* @enum {number}
|
||||
*/
|
||||
export const ZapcoreLevel = {
|
||||
Debug: -1,
|
||||
Info: 0,
|
||||
Warn: 1,
|
||||
Error: 2,
|
||||
DPanic: 3,
|
||||
Panic: 4,
|
||||
Fatal: 5,
|
||||
_minLevel: -1,
|
||||
_maxLevel: 5,
|
||||
Invalid: 6,
|
||||
_numLevels: 7,
|
||||
};
|
||||
|
||||
// --- Functions ---
|
||||
|
||||
const processResponse = (responseData) => {
|
||||
const data = responseData.data;
|
||||
return {
|
||||
list: data.list || [],
|
||||
total: data.pagination ? data.pagination.total : 0,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取设备命令日志列表
|
||||
* @param {DeviceCommandLogsParams} params - 查询参数
|
||||
* @returns {Promise<{list: Array<DeviceCommandLogDTO>, total: number}>}
|
||||
*/
|
||||
export const getDeviceCommandLogs = async (params) => {
|
||||
const newParams = { ...params, page_size: params.page_size };
|
||||
const responseData = await http.get('/api/v1/monitor/device-command-logs', { params: newParams });
|
||||
return processResponse(responseData);
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取饲料使用记录列表
|
||||
* @param {FeedUsageRecordsParams} params - 查询参数
|
||||
* @returns {Promise<{list: Array<FeedUsageRecordDTO>, total: number}>}
|
||||
*/
|
||||
export const getFeedUsageRecords = async (params) => {
|
||||
const newParams = { ...params, page_size: params.page_size };
|
||||
const responseData = await http.get('/api/v1/monitor/feed-usage-records', { params: newParams });
|
||||
return processResponse(responseData);
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取用药记录列表
|
||||
* @param {MedicationLogsParams} params - 查询参数
|
||||
* @returns {Promise<{list: Array<MedicationLogDTO>, total: number}>}
|
||||
*/
|
||||
export const getMedicationLogs = async (params) => {
|
||||
const newParams = { ...params, page_size: params.page_size };
|
||||
const responseData = await http.get('/api/v1/monitor/medication-logs', { params: newParams });
|
||||
return processResponse(responseData);
|
||||
};
|
||||
|
||||
/**
|
||||
* 批量查询通知
|
||||
* @param {NotificationsParams} params - 查询参数
|
||||
* @returns {Promise<{list: Array<NotificationDTO>, total: number}>}
|
||||
*/
|
||||
export const getNotifications = async (params) => {
|
||||
const newParams = { ...params, page_size: params.page_size };
|
||||
const responseData = await http.get('/api/v1/monitor/notifications', { params: newParams });
|
||||
return processResponse(responseData);
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取待采集请求列表
|
||||
* @param {PendingCollectionsParams} params - 查询参数
|
||||
* @returns {Promise<{list: Array<PendingCollectionDTO>, total: number}>}
|
||||
*/
|
||||
export const getPendingCollections = async (params) => {
|
||||
const newParams = { ...params, page_size: params.page_size };
|
||||
const responseData = await http.get('/api/v1/monitor/pending-collections', { params: newParams });
|
||||
return processResponse(responseData);
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取猪批次日志列表
|
||||
* @param {PigBatchLogsParams} params - 查询参数
|
||||
* @returns {Promise<{list: Array<PigBatchLogDTO>, total: number}>}
|
||||
*/
|
||||
export const getPigBatchLogs = async (params) => {
|
||||
const newParams = { ...params, page_size: params.page_size };
|
||||
const responseData = await http.get('/api/v1/monitor/pig-batch-logs', { params: newParams });
|
||||
return processResponse(responseData);
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取猪只采购记录列表
|
||||
* @param {PigPurchasesParams} params - 查询参数
|
||||
* @returns {Promise<{list: Array<PigPurchaseDTO>, total: number}>}
|
||||
*/
|
||||
export const getPigPurchases = async (params) => {
|
||||
const newParams = { ...params, page_size: params.page_size };
|
||||
const responseData = await http.get('/api/v1/monitor/pig-purchases', { params: newParams });
|
||||
return processResponse(responseData);
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取猪只售卖记录列表
|
||||
* @param {PigSalesParams} params - 查询参数
|
||||
* @returns {Promise<{list: Array<PigSaleDTO>, total: number}>}
|
||||
*/
|
||||
export const getPigSales = async (params) => {
|
||||
const newParams = { ...params, page_size: params.page_size };
|
||||
const responseData = await http.get('/api/v1/monitor/pig-sales', { params: newParams });
|
||||
return processResponse(responseData);
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取病猪日志列表
|
||||
* @param {PigSickLogsParams} params - 查询参数
|
||||
* @returns {Promise<{list: Array<PigSickLogDTO>, total: number}>}
|
||||
*/
|
||||
export const getPigSickLogs = async (params) => {
|
||||
const newParams = { ...params, page_size: params.page_size };
|
||||
const responseData = await http.get('/api/v1/monitor/pig-sick-logs', { params: newParams });
|
||||
return processResponse(responseData);
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取猪只迁移日志列表
|
||||
* @param {PigTransferLogsParams} params - 查询参数
|
||||
* @returns {Promise<{list: Array<PigTransferLogDTO>, total: number}>}
|
||||
*/
|
||||
export const getPigTransferLogs = async (params) => {
|
||||
const newParams = { ...params, page_size: params.page_size };
|
||||
const responseData = await http.get('/api/v1/monitor/pig-transfer-logs', { params: newParams });
|
||||
return processResponse(responseData);
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取计划执行日志列表
|
||||
* @param {PlanExecutionLogsParams} params - 查询参数
|
||||
* @returns {Promise<{list: Array<PlanExecutionLogDTO>, total: number}>}
|
||||
*/
|
||||
export const getPlanExecutionLogs = async (params) => {
|
||||
const newParams = { ...params, page_size: params.page_size };
|
||||
const responseData = await http.get('/api/v1/monitor/plan-execution-logs', { params: newParams });
|
||||
return processResponse(responseData);
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取原料采购记录列表
|
||||
* @param {RawMaterialPurchasesParams} params - 查询参数
|
||||
* @returns {Promise<{list: Array<RawMaterialPurchaseDTO>, total: number}>}
|
||||
*/
|
||||
export const getRawMaterialPurchases = async (params) => {
|
||||
const newParams = { ...params, page_size: params.page_size };
|
||||
const responseData = await http.get('/api/v1/monitor/raw-material-purchases', { params: newParams });
|
||||
return processResponse(responseData);
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取原料库存日志列表
|
||||
* @param {RawMaterialStockLogsParams} params - 查询参数
|
||||
* @returns {Promise<{list: Array<RawMaterialStockLogDTO>, total: number}>}
|
||||
*/
|
||||
export const getRawMaterialStockLogs = async (params) => {
|
||||
const newParams = { ...params, page_size: params.page_size };
|
||||
const responseData = await http.get('/api/v1/monitor/raw-material-stock-logs', { params: newParams });
|
||||
return processResponse(responseData);
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取传感器数据列表
|
||||
* @param {SensorDataParams} params - 查询参数
|
||||
* @returns {Promise<{list: Array<SensorDataDTO>, total: number}>}
|
||||
*/
|
||||
export const getSensorData = async (params) => {
|
||||
const newParams = { ...params, page_size: params.page_size };
|
||||
const responseData = await http.get('/api/v1/monitor/sensor-data', { params: newParams });
|
||||
return processResponse(responseData);
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取任务执行日志列表
|
||||
* @param {TaskExecutionLogsParams} params - 查询参数
|
||||
* @returns {Promise<{list: Array<TaskExecutionLogDTO>, total: number}>}
|
||||
*/
|
||||
export const getTaskExecutionLogs = async (params) => {
|
||||
const newParams = { ...params, page_size: params.page_size };
|
||||
const responseData = await http.get('/api/v1/monitor/task-execution-logs', { params: newParams });
|
||||
return processResponse(responseData);
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取用户操作日志列表
|
||||
* @param {UserActionLogsParams} params - 查询参数
|
||||
* @returns {Promise<{list: Array<UserActionLogDTO>, total: number}>}
|
||||
*/
|
||||
export const getUserActionLogs = async (params) => {
|
||||
const newParams = { ...params, page_size: params.page_size };
|
||||
const responseData = await http.get('/api/v1/monitor/user-action-logs', { params: newParams });
|
||||
return processResponse(responseData);
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取批次称重记录列表
|
||||
* @param {WeighingBatchesParams} params - 查询参数
|
||||
* @returns {Promise<{list: Array<WeighingBatchDTO>, total: number}>}
|
||||
*/
|
||||
export const getWeighingBatches = async (params) => {
|
||||
const newParams = { ...params, page_size: params.page_size };
|
||||
const responseData = await http.get('/api/v1/monitor/weighing-batches', { params: newParams });
|
||||
return processResponse(responseData);
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取单次称重记录列表
|
||||
* @param {WeighingRecordsParams} params - 查询参数
|
||||
* @returns {Promise<{list: Array<WeighingRecordDTO>, total: number}>}
|
||||
*/
|
||||
export const getWeighingRecords = async (params) => {
|
||||
const newParams = { ...params, page_size: params.page_size };
|
||||
const responseData = await http.get('/api/v1/monitor/weighing-records', { params: newParams });
|
||||
return processResponse(responseData);
|
||||
};
|
||||
|
||||
export const MonitorApi = {
|
||||
getDeviceCommandLogs,
|
||||
getFeedUsageRecords,
|
||||
getMedicationLogs,
|
||||
getNotifications,
|
||||
getPendingCollections,
|
||||
getPigBatchLogs,
|
||||
getPigPurchases,
|
||||
getPigSales,
|
||||
getPigSickLogs,
|
||||
getPigTransferLogs,
|
||||
getPlanExecutionLogs,
|
||||
getRawMaterialPurchases,
|
||||
getRawMaterialStockLogs,
|
||||
getSensorData,
|
||||
getTaskExecutionLogs,
|
||||
getUserActionLogs,
|
||||
getWeighingBatches,
|
||||
getWeighingRecords,
|
||||
};
|
||||
103
src/api/pen.js
Normal file
103
src/api/pen.js
Normal file
@@ -0,0 +1,103 @@
|
||||
import http from '../utils/http';
|
||||
|
||||
/**
|
||||
* @typedef {object} Response
|
||||
* @property {number} code - 业务状态码
|
||||
* @property {object} [data] - 业务数据
|
||||
* @property {string} [message] - 提示信息
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} PenResponse
|
||||
* @property {number} id
|
||||
* @property {number} house_id
|
||||
* @property {string} pen_number
|
||||
* @property {number} capacity
|
||||
* @property {number} current_pig_count
|
||||
* @property {number} pig_batch_id
|
||||
* @property {('空闲'|'使用中'|'病猪栏'|'康复栏'|'清洗消毒'|'维修中')} status
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} CreatePenRequest
|
||||
* @property {number} house_id
|
||||
* @property {string} pen_number
|
||||
* @property {number} capacity
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} UpdatePenRequest
|
||||
* @property {number} house_id
|
||||
* @property {string} pen_number
|
||||
* @property {number} capacity
|
||||
* @property {('空闲'|'使用中'|'病猪栏'|'康复栏'|'清洗消毒'|'维修中')} status
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} UpdatePenStatusRequest
|
||||
* @property {('空闲'|'使用中'|'病猪栏'|'康复栏'|'清洗消毒'|'维修中')} status
|
||||
*/
|
||||
|
||||
/**
|
||||
* 获取所有猪栏的列表
|
||||
* @returns {Promise<Array<PenResponse>>}
|
||||
*/
|
||||
export const getPens = () => {
|
||||
return http.get('/api/v1/pens');
|
||||
};
|
||||
|
||||
/**
|
||||
* 创建一个新的猪栏
|
||||
* @param {CreatePenRequest} penData - 猪栏信息
|
||||
* @returns {Promise<PenResponse>}
|
||||
*/
|
||||
export const createPen = (penData) => {
|
||||
return http.post('/api/v1/pens', penData);
|
||||
};
|
||||
|
||||
/**
|
||||
* 根据ID获取单个猪栏信息
|
||||
* @param {number} id - 猪栏ID
|
||||
* @returns {Promise<PenResponse>}
|
||||
*/
|
||||
export const getPenById = (id) => {
|
||||
return http.get(`/api/v1/pens/${id}`);
|
||||
};
|
||||
|
||||
/**
|
||||
* 更新一个已存在的猪栏信息
|
||||
* @param {number} id - 猪栏ID
|
||||
* @param {UpdatePenRequest} penData - 猪栏信息
|
||||
* @returns {Promise<PenResponse>}
|
||||
*/
|
||||
export const updatePen = (id, penData) => {
|
||||
return http.put(`/api/v1/pens/${id}`, penData);
|
||||
};
|
||||
|
||||
/**
|
||||
* 根据ID删除一个猪栏
|
||||
* @param {number} id - 猪栏ID
|
||||
* @returns {Promise<Response>}
|
||||
*/
|
||||
export const deletePen = (id) => {
|
||||
return http.delete(`/api/v1/pens/${id}`);
|
||||
};
|
||||
|
||||
/**
|
||||
* 更新指定猪栏的当前状态
|
||||
* @param {number} id - 猪栏ID
|
||||
* @param {UpdatePenStatusRequest} statusData - 新的猪栏状态
|
||||
* @returns {Promise<PenResponse>}
|
||||
*/
|
||||
export const updatePenStatus = (id, statusData) => {
|
||||
return http.put(`/api/v1/pens/${id}/status`, statusData);
|
||||
};
|
||||
|
||||
export const PenApi = {
|
||||
getPens,
|
||||
createPen,
|
||||
getPenById,
|
||||
updatePen,
|
||||
deletePen,
|
||||
updatePenStatus,
|
||||
};
|
||||
424
src/api/pigBatch.js
Normal file
424
src/api/pigBatch.js
Normal file
@@ -0,0 +1,424 @@
|
||||
import http from '../utils/http';
|
||||
|
||||
// --- Typedefs ---
|
||||
|
||||
/**
|
||||
* @typedef {object} Response
|
||||
* @property {number} code - 业务状态码
|
||||
* @property {object} [data] - 业务数据
|
||||
* @property {string} [message] - 提示信息
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {('自繁'|'外购')} PigBatchOriginType
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {('保育'|'生长'|'育肥'|'待售'|'已出售'|'已归档')} PigBatchStatus
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} PigBatchResponseDTO
|
||||
* @property {number} id - 批次ID
|
||||
* @property {string} batch_number - 批次编号
|
||||
* @property {PigBatchOriginType} origin_type - 批次来源
|
||||
* @property {string} [start_date] - 批次开始日期
|
||||
* @property {string} [end_date] - 批次结束日期
|
||||
* @property {number} initial_count - 初始数量
|
||||
* @property {PigBatchStatus} status - 批次状态
|
||||
* @property {boolean} is_active - 是否活跃
|
||||
* @property {string} create_time - 创建时间
|
||||
* @property {string} update_time - 更新时间
|
||||
* @property {number} current_total_quantity - 当前总数
|
||||
* @property {number} current_total_pigs_in_pens - 当前存栏总数
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} PigBatchesParams
|
||||
* @property {boolean} [is_active] - 是否活跃 (true/false)
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} PigBatchCreateDTO
|
||||
* @property {string} batch_number - 批次编号,必填
|
||||
* @property {PigBatchOriginType} origin_type - 批次来源,必填
|
||||
* @property {string} start_date - 批次开始日期,必填
|
||||
* @property {number} initial_count - 初始数量,必填,最小为1
|
||||
* @property {PigBatchStatus} status - 批次状态,必填
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} PigBatchUpdateDTO
|
||||
* @property {string} [batch_number] - 批次编号,可选
|
||||
* @property {PigBatchOriginType} [origin_type] - 批次来源,可选
|
||||
* @property {string} [start_date] - 批次开始日期,可选
|
||||
* @property {string} [end_date] - 批次结束日期,可选
|
||||
* @property {number} [initial_count] - 初始数量,可选
|
||||
* @property {PigBatchStatus} [status] - 批次状态,可选
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} AssignEmptyPensToBatchRequest
|
||||
* @property {Array<number>} pen_ids - 待分配的猪栏ID列表
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} BuyPigsRequest
|
||||
* @property {number} pen_id - 猪栏ID
|
||||
* @property {number} quantity - 买入猪只数量
|
||||
* @property {number} unit_price - 单价
|
||||
* @property {number} total_price - 总价
|
||||
* @property {string} trader_name - 交易方名称
|
||||
* @property {string} trade_date - 交易日期
|
||||
* @property {string} [remarks] - 备注
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} SellPigsRequest
|
||||
* @property {number} pen_id - 猪栏ID
|
||||
* @property {number} quantity - 卖出猪只数量
|
||||
* @property {number} unit_price - 单价
|
||||
* @property {number} total_price - 总价
|
||||
* @property {string} trader_name - 交易方名称
|
||||
* @property {string} trade_date - 交易日期
|
||||
* @property {string} [remarks] - 备注
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} MovePigsIntoPenRequest
|
||||
* @property {number} to_pen_id - 目标猪栏ID
|
||||
* @property {number} quantity - 移入猪只数量
|
||||
* @property {string} [remarks] - 备注
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} TransferPigsWithinBatchRequest
|
||||
* @property {number} from_pen_id - 源猪栏ID
|
||||
* @property {number} to_pen_id - 目标猪栏ID
|
||||
* @property {number} quantity - 调栏猪只数量
|
||||
* @property {string} [remarks] - 备注
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} TransferPigsAcrossBatchesRequest
|
||||
* @property {number} dest_batch_id - 目标猪批次ID
|
||||
* @property {number} from_pen_id - 源猪栏ID
|
||||
* @property {number} to_pen_id - 目标猪栏ID
|
||||
* @property {number} quantity - 调栏猪只数量
|
||||
* @property {string} [remarks] - 备注
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} ReclassifyPenToNewBatchRequest
|
||||
* @property {number} pen_id - 待划拨的猪栏ID
|
||||
* @property {number} to_batch_id - 目标猪批次ID
|
||||
* @property {string} [remarks] - 备注
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {('原地治疗'|'病猪栏治疗')} PigBatchSickPigTreatmentLocation
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} RecordSickPigsRequest
|
||||
* @property {number} pen_id - 猪栏ID
|
||||
* @property {number} quantity - 病猪数量
|
||||
* @property {PigBatchSickPigTreatmentLocation} treatment_location - 治疗地点
|
||||
* @property {string} happened_at - 发生时间
|
||||
* @property {string} [remarks] - 备注
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} RecordSickPigRecoveryRequest
|
||||
* @property {number} pen_id - 猪栏ID
|
||||
* @property {number} quantity - 康复猪数量
|
||||
* @property {PigBatchSickPigTreatmentLocation} treatment_location - 治疗地点
|
||||
* @property {string} happened_at - 发生时间
|
||||
* @property {string} [remarks] - 备注
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} RecordSickPigDeathRequest
|
||||
* @property {number} pen_id - 猪栏ID
|
||||
* @property {number} quantity - 死亡猪数量
|
||||
* @property {PigBatchSickPigTreatmentLocation} treatment_location - 治疗地点
|
||||
* @property {string} happened_at - 发生时间
|
||||
* @property {string} [remarks] - 备注
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} RecordSickPigCullRequest
|
||||
* @property {number} pen_id - 猪栏ID
|
||||
* @property {number} quantity - 淘汰猪数量
|
||||
* @property {PigBatchSickPigTreatmentLocation} treatment_location - 治疗地点
|
||||
* @property {string} happened_at - 发生时间
|
||||
* @property {string} [remarks] - 备注
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} RecordDeathRequest
|
||||
* @property {number} pen_id - 猪栏ID
|
||||
* @property {number} quantity - 死亡猪数量
|
||||
* @property {string} happened_at - 发生时间
|
||||
* @property {string} [remarks] - 备注
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} RecordCullRequest
|
||||
* @property {number} pen_id - 猪栏ID
|
||||
* @property {number} quantity - 淘汰猪数量
|
||||
* @property {string} happened_at - 发生时间
|
||||
* @property {string} [remarks] - 备注
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} PenResponse
|
||||
* @property {number} id
|
||||
* @property {number} house_id
|
||||
* @property {string} pen_number
|
||||
* @property {number} capacity
|
||||
* @property {number} current_pig_count
|
||||
* @property {number} pig_batch_id
|
||||
* @property {('空闲'|'使用中'|'病猪栏'|'康复栏'|'清洗消毒'|'维修中')} status
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} PigHouseResponse
|
||||
* @property {number} id
|
||||
* @property {string} name
|
||||
* @property {string} description
|
||||
*/
|
||||
|
||||
// --- 猪批次基础操作 ---
|
||||
|
||||
/**
|
||||
* 获取所有猪批次的列表
|
||||
* @param {PigBatchesParams} params - 查询参数
|
||||
* @returns {Promise<Array<PigBatchResponseDTO>>}
|
||||
*/
|
||||
export const getPigBatches = (params) => {
|
||||
return http.get('/api/v1/pig-batches', params);
|
||||
};
|
||||
|
||||
/**
|
||||
* 创建一个新的猪批次
|
||||
* @param {PigBatchCreateDTO} batchData - 猪批次信息
|
||||
* @returns {Promise<PigBatchResponseDTO>}
|
||||
*/
|
||||
export const createPigBatch = (batchData) => {
|
||||
return http.post('/api/v1/pig-batches', batchData);
|
||||
};
|
||||
|
||||
/**
|
||||
* 根据ID获取单个猪批次信息
|
||||
* @param {number} id - 猪批次ID
|
||||
* @returns {Promise<PigBatchResponseDTO>}
|
||||
*/
|
||||
export const getPigBatchById = (id) => {
|
||||
return http.get(`/api/v1/pig-batches/${id}`);
|
||||
};
|
||||
|
||||
/**
|
||||
* 更新一个已存在的猪批次信息
|
||||
* @param {number} id - 猪批次ID
|
||||
* @param {PigBatchUpdateDTO} batchData - 猪批次信息
|
||||
* @returns {Promise<PigBatchResponseDTO>}
|
||||
*/
|
||||
export const updatePigBatch = (id, batchData) => {
|
||||
return http.put(`/api/v1/pig-batches/${id}`, batchData);
|
||||
};
|
||||
|
||||
/**
|
||||
* 根据ID删除一个猪批次
|
||||
* @param {number} id - 猪批次ID
|
||||
* @returns {Promise<Response>}
|
||||
*/
|
||||
export const deletePigBatch = (id) => {
|
||||
return http.delete(`/api/v1/pig-batches/${id}`);
|
||||
};
|
||||
|
||||
// --- 猪批次业务操作 ---
|
||||
|
||||
/**
|
||||
* 为猪批次分配空栏
|
||||
* @param {number} id - 猪批次ID
|
||||
* @param {AssignEmptyPensToBatchRequest} pensData - 待分配的猪栏ID列表
|
||||
* @returns {Promise<Response>}
|
||||
*/
|
||||
export const assignPensToBatch = (id, pensData) => {
|
||||
return http.post(`/api/v1/pig-batches/assign-pens/${id}`, pensData);
|
||||
};
|
||||
|
||||
/**
|
||||
* 从猪批次移除空栏
|
||||
* @param {number} penID - 待移除的猪栏ID
|
||||
* @param {number} batchID - 猪批次ID
|
||||
* @returns {Promise<Response>}
|
||||
*/
|
||||
export const removePenFromBatch = (penID, batchID) => {
|
||||
return http.delete(`/api/v1/pig-batches/remove-pen/${penID}/${batchID}`);
|
||||
};
|
||||
|
||||
/**
|
||||
* 处理买猪的业务逻辑
|
||||
* @param {number} id - 猪批次ID
|
||||
* @param {BuyPigsRequest} buyData - 买猪请求信息
|
||||
* @returns {Promise<Response>}
|
||||
*/
|
||||
export const buyPigsForBatch = (id, buyData) => {
|
||||
return http.post(`/api/v1/pig-batches/buy-pigs/${id}`, buyData);
|
||||
};
|
||||
|
||||
/**
|
||||
* 处理卖猪的业务逻辑
|
||||
* @param {number} id - 猪批次ID
|
||||
* @param {SellPigsRequest} sellData - 卖猪请求信息
|
||||
* @returns {Promise<Response>}
|
||||
*/
|
||||
export const sellPigsFromBatch = (id, sellData) => {
|
||||
return http.post(`/api/v1/pig-batches/sell-pigs/${id}`, sellData);
|
||||
};
|
||||
|
||||
/**
|
||||
* 将猪只从“虚拟库存”移入指定猪栏
|
||||
* @param {number} id - 猪批次ID
|
||||
* @param {MovePigsIntoPenRequest} moveData - 移入猪只请求信息
|
||||
* @returns {Promise<Response>}
|
||||
*/
|
||||
export const movePigsIntoPen = (id, moveData) => {
|
||||
return http.post(`/api/v1/pig-batches/move-pigs-into-pen/${id}`, moveData);
|
||||
};
|
||||
|
||||
/**
|
||||
* 群内调栏
|
||||
* @param {number} id - 猪批次ID
|
||||
* @param {TransferPigsWithinBatchRequest} transferData - 群内调栏请求信息
|
||||
* @returns {Promise<Response>}
|
||||
*/
|
||||
export const transferPigsWithinBatch = (id, transferData) => {
|
||||
return http.post(`/api/v1/pig-batches/transfer-within-batch/${id}`, transferData);
|
||||
};
|
||||
|
||||
/**
|
||||
* 跨猪群调栏
|
||||
* @param {number} sourceBatchID - 源猪批次ID
|
||||
* @param {TransferPigsAcrossBatchesRequest} transferData - 跨群调栏请求信息
|
||||
* @returns {Promise<Response>}
|
||||
*/
|
||||
export const transferPigsAcrossBatches = (sourceBatchID, transferData) => {
|
||||
return http.post(`/api/v1/pig-batches/transfer-across-batches/${sourceBatchID}`, transferData);
|
||||
};
|
||||
|
||||
/**
|
||||
* 将猪栏划拨到新批次
|
||||
* @param {number} fromBatchID - 源猪批次ID
|
||||
* @param {ReclassifyPenToNewBatchRequest} reclassifyData - 划拨请求信息
|
||||
* @returns {Promise<Response>}
|
||||
*/
|
||||
export const reclassifyPenToNewBatch = (fromBatchID, reclassifyData) => {
|
||||
return http.post(`/api/v1/pig-batches/reclassify-pen/${fromBatchID}`, reclassifyData);
|
||||
};
|
||||
|
||||
// --- 猪只数量变更记录 ---
|
||||
|
||||
/**
|
||||
* 记录新增病猪事件
|
||||
* @param {number} id - 猪批次ID
|
||||
* @param {RecordSickPigsRequest} sickData - 记录病猪请求信息
|
||||
* @returns {Promise<Response>}
|
||||
*/
|
||||
export const recordSickPigsInBatch = (id, sickData) => {
|
||||
return http.post(`/api/v1/pig-batches/record-sick-pigs/${id}`, sickData);
|
||||
};
|
||||
|
||||
/**
|
||||
* 记录病猪康复事件
|
||||
* @param {number} id - 猪批次ID
|
||||
* @param {RecordSickPigRecoveryRequest} recoveryData - 记录病猪康复请求信息
|
||||
* @returns {Promise<Response>}
|
||||
*/
|
||||
export const recordSickPigRecoveryInBatch = (id, recoveryData) => {
|
||||
return http.post(`/api/v1/pig-batches/record-sick-pig-recovery/${id}`, recoveryData);
|
||||
};
|
||||
|
||||
/**
|
||||
* 记录病猪死亡事件
|
||||
* @param {number} id - 猪批次ID
|
||||
* @param {RecordSickPigDeathRequest} deathData - 记录病猪死亡请求信息
|
||||
* @returns {Promise<Response>}
|
||||
*/
|
||||
export const recordSickPigDeathInBatch = (id, deathData) => {
|
||||
return http.post(`/api/v1/pig-batches/record-sick-pig-death/${id}`, deathData);
|
||||
};
|
||||
|
||||
/**
|
||||
* 记录病猪淘汰事件
|
||||
* @param {number} id - 猪批次ID
|
||||
* @param {RecordSickPigCullRequest} cullData - 记录病猪淘汰请求信息
|
||||
* @returns {Promise<Response>}
|
||||
*/
|
||||
export const recordSickPigCullInBatch = (id, cullData) => {
|
||||
return http.post(`/api/v1/pig-batches/record-sick-pig-cull/${id}`, cullData);
|
||||
};
|
||||
|
||||
/**
|
||||
* 记录正常猪只死亡事件
|
||||
* @param {number} id - 猪批次ID
|
||||
* @param {RecordDeathRequest} deathData - 记录正常猪只死亡请求信息
|
||||
* @returns {Promise<Response>}
|
||||
*/
|
||||
export const recordDeathInBatch = (id, deathData) => {
|
||||
return http.post(`/api/v1/pig-batches/record-death/${id}`, deathData);
|
||||
};
|
||||
|
||||
/**
|
||||
* 记录正常猪只淘汰事件
|
||||
* @param {number} id - 猪批次ID
|
||||
* @param {RecordCullRequest} cullData - 记录正常猪只淘汰请求信息
|
||||
* @returns {Promise<Response>}
|
||||
*/
|
||||
export const recordCullInBatch = (id, cullData) => {
|
||||
return http.post(`/api/v1/pig-batches/record-cull/${id}`, cullData);
|
||||
};
|
||||
|
||||
// --- 新增的猪栏和猪舍API ---
|
||||
|
||||
/**
|
||||
* 获取所有猪栏的列表
|
||||
* @returns {Promise<Array<PenResponse>>}
|
||||
*/
|
||||
export const getAllPens = () => {
|
||||
return http.get('/api/v1/pens');
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取所有猪舍的列表
|
||||
* @returns {Promise<Array<PigHouseResponse>>}
|
||||
*/
|
||||
export const getAllPigHouses = () => {
|
||||
return http.get('/api/v1/pig-houses');
|
||||
};
|
||||
|
||||
export const PigBatchApi = {
|
||||
getPigBatches,
|
||||
createPigBatch,
|
||||
getPigBatchById,
|
||||
updatePigBatch,
|
||||
deletePigBatch,
|
||||
assignPensToBatch,
|
||||
removePenFromBatch,
|
||||
buyPigsForBatch,
|
||||
sellPigsFromBatch,
|
||||
movePigsIntoPen,
|
||||
transferPigsWithinBatch,
|
||||
transferPigsAcrossBatches,
|
||||
reclassifyPenToNewBatch,
|
||||
recordSickPigsInBatch,
|
||||
recordSickPigRecoveryInBatch,
|
||||
recordSickPigDeathInBatch,
|
||||
recordSickPigCullInBatch,
|
||||
recordDeathInBatch,
|
||||
recordCullInBatch,
|
||||
getAllPens,
|
||||
getAllPigHouses,
|
||||
};
|
||||
80
src/api/pigHouse.js
Normal file
80
src/api/pigHouse.js
Normal file
@@ -0,0 +1,80 @@
|
||||
import http from '../utils/http';
|
||||
|
||||
/**
|
||||
* @typedef {object} Response
|
||||
* @property {number} code - 业务状态码
|
||||
* @property {object} [data] - 业务数据
|
||||
* @property {string} [message] - 提示信息
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} PigHouseResponse
|
||||
* @property {number} id
|
||||
* @property {string} name
|
||||
* @property {string} description
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} CreatePigHouseRequest
|
||||
* @property {string} name
|
||||
* @property {string} [description]
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} UpdatePigHouseRequest
|
||||
* @property {string} name
|
||||
* @property {string} [description]
|
||||
*/
|
||||
|
||||
/**
|
||||
* 获取所有猪舍的列表
|
||||
* @returns {Promise<Array<PigHouseResponse>>}
|
||||
*/
|
||||
export const getPigHouses = () => {
|
||||
return http.get('/api/v1/pig-houses');
|
||||
};
|
||||
|
||||
/**
|
||||
* 创建一个新的猪舍
|
||||
* @param {CreatePigHouseRequest} pigHouseData - 猪舍信息
|
||||
* @returns {Promise<PigHouseResponse>}
|
||||
*/
|
||||
export const createPigHouse = (pigHouseData) => {
|
||||
return http.post('/api/v1/pig-houses', pigHouseData);
|
||||
};
|
||||
|
||||
/**
|
||||
* 根据ID获取单个猪舍信息
|
||||
* @param {number} id - 猪舍ID
|
||||
* @returns {Promise<PigHouseResponse>}
|
||||
*/
|
||||
export const getPigHouseById = (id) => {
|
||||
return http.get(`/api/v1/pig-houses/${id}`);
|
||||
};
|
||||
|
||||
/**
|
||||
* 更新一个已存在的猪舍信息
|
||||
* @param {number} id - 猪舍ID
|
||||
* @param {UpdatePigHouseRequest} pigHouseData - 猪舍信息
|
||||
* @returns {Promise<PigHouseResponse>}
|
||||
*/
|
||||
export const updatePigHouse = (id, pigHouseData) => {
|
||||
return http.put(`/api/v1/pig-houses/${id}`, pigHouseData);
|
||||
};
|
||||
|
||||
/**
|
||||
* 根据ID删除一个猪舍
|
||||
* @param {number} id - 猪舍ID
|
||||
* @returns {Promise<Response>}
|
||||
*/
|
||||
export const deletePigHouse = (id) => {
|
||||
return http.delete(`/api/v1/pig-houses/${id}`);
|
||||
};
|
||||
|
||||
export const PigHouseApi = {
|
||||
getPigHouses,
|
||||
createPigHouse,
|
||||
getPigHouseById,
|
||||
updatePigHouse,
|
||||
deletePigHouse,
|
||||
};
|
||||
196
src/api/plan.js
Normal file
196
src/api/plan.js
Normal file
@@ -0,0 +1,196 @@
|
||||
import http from '../utils/http';
|
||||
|
||||
/**
|
||||
* @typedef {('计划分析'|'等待'|'下料'|'全量采集')} TaskType
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} TaskRequest
|
||||
* @property {string} [name]
|
||||
* @property {string} [description]
|
||||
* @property {TaskType} [type]
|
||||
* @property {object} [parameters]
|
||||
* @property {number} [execution_order]
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {('自动'|'手动')} PlanExecutionType
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} CreatePlanRequest
|
||||
* @property {string} name
|
||||
* @property {string} [description]
|
||||
* @property {PlanExecutionType} execution_type
|
||||
* @property {string} [cron_expression]
|
||||
* @property {number} [execute_num]
|
||||
* @property {Array<TaskRequest>} [tasks]
|
||||
* @property {Array<number>} [sub_plan_ids]
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} UpdatePlanRequest
|
||||
* @property {string} [name]
|
||||
* @property {string} [description]
|
||||
* @property {PlanExecutionType} execution_type
|
||||
* @property {string} [cron_expression]
|
||||
* @property {number} [execute_num]
|
||||
* @property {Array<TaskRequest>} [tasks]
|
||||
* @property {Array<number>} [sub_plan_ids]
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} TaskResponse
|
||||
* @property {number} id
|
||||
* @property {number} plan_id
|
||||
* @property {string} name
|
||||
* @property {string} description
|
||||
* @property {TaskType} type
|
||||
* @property {object} parameters
|
||||
* @property {number} execution_order
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} SubPlanResponse
|
||||
* @property {number} id
|
||||
* @property {number} parent_plan_id
|
||||
* @property {number} child_plan_id
|
||||
* @property {number} execution_order
|
||||
* @property {PlanResponse} child_plan
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {('已禁用'|'已启用'|'执行完毕'|'执行失败')} PlanStatus
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {('子计划'|'任务')} PlanContentType
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {('自定义任务'|'系统任务')} PlanType
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} PlanResponse
|
||||
* @property {number} id
|
||||
* @property {string} name
|
||||
* @property {string} description
|
||||
* @property {PlanExecutionType} execution_type
|
||||
* @property {string} cron_expression
|
||||
* @property {number} execute_num
|
||||
* @property {number} execute_count
|
||||
* @property {PlanStatus} status
|
||||
* @property {PlanContentType} content_type
|
||||
* @property {PlanType} plan_type
|
||||
* @property {Array<TaskResponse>} tasks
|
||||
* @property {Array<SubPlanResponse>} sub_plans
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} ListPlansResponse
|
||||
* @property {Array<PlanResponse>} plans
|
||||
* @property {number} total
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} PlanExecutionLogDTO
|
||||
* @property {string} created_at
|
||||
* @property {string} ended_at
|
||||
* @property {string} error
|
||||
* @property {number} id
|
||||
* @property {number} plan_id
|
||||
* @property {string} plan_name
|
||||
* @property {string} started_at
|
||||
* @property {string} status
|
||||
* @property {string} updated_at
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} Response
|
||||
* @property {number} code - 业务状态码
|
||||
* @property {object} [data] - 业务数据
|
||||
* @property {string} [message] - 提示信息
|
||||
*/
|
||||
|
||||
/**
|
||||
* 获取所有计划的列表
|
||||
* @param {object} params - 查询参数
|
||||
* @param {number} [params.page] - 页码
|
||||
* @param {number} [params.page_size] - 每页大小
|
||||
* @param {('所有任务'|'自定义任务'|'系统任务')} [params.plan_type] - 计划类型
|
||||
* @returns {Promise<ListPlansResponse>}
|
||||
*/
|
||||
const getPlans = (params) => {
|
||||
const newParams = {
|
||||
page: params.page,
|
||||
page_size: params.page_size,
|
||||
plan_type: params.plan_type,
|
||||
};
|
||||
return http.get('/api/v1/plans', { params: newParams });
|
||||
};
|
||||
|
||||
/**
|
||||
* 创建一个新的计划
|
||||
* @param {CreatePlanRequest} planData - 计划信息
|
||||
* @returns {Promise<PlanResponse>}
|
||||
*/
|
||||
const createPlan = (planData) => {
|
||||
return http.post('/api/v1/plans', planData);
|
||||
};
|
||||
|
||||
/**
|
||||
* 根据计划ID获取单个计划的详细信息
|
||||
* @param {number} id - 计划ID
|
||||
* @returns {Promise<PlanResponse>}
|
||||
*/
|
||||
const getPlanById = (id) => {
|
||||
return http.get(`/api/v1/plans/${id}`);
|
||||
};
|
||||
|
||||
/**
|
||||
* 根据计划ID更新计划的详细信息。系统计划不允许修改。
|
||||
* @param {number} id - 计划ID
|
||||
* @param {UpdatePlanRequest} planData - 更新后的计划信息
|
||||
* @returns {Promise<PlanResponse>}
|
||||
*/
|
||||
const updatePlan = (id, planData) => {
|
||||
return http.put(`/api/v1/plans/${id}`, planData);
|
||||
};
|
||||
|
||||
/**
|
||||
* 根据计划ID删除计划。(软删除)系统计划不允许删除。
|
||||
* @param {number} id - 计划ID
|
||||
* @returns {Promise<Response>}
|
||||
*/
|
||||
const deletePlan = (id) => {
|
||||
return http.delete(`/api/v1/plans/${id}`);
|
||||
};
|
||||
|
||||
/**
|
||||
* 根据计划ID启动一个计划的执行。系统计划不允许手动启动。
|
||||
* @param {number} id - 计划ID
|
||||
* @returns {Promise<Response>}
|
||||
*/
|
||||
const startPlan = (id) => {
|
||||
return http.post(`/api/v1/plans/${id}/start`);
|
||||
};
|
||||
|
||||
/**
|
||||
* 根据计划ID停止一个正在执行的计划。系统计划不能被停止。
|
||||
* @param {number} id - 计划ID
|
||||
* @returns {Promise<Response>}
|
||||
*/
|
||||
const stopPlan = (id) => {
|
||||
return http.post(`/api/v1/plans/${id}/stop`);
|
||||
};
|
||||
|
||||
export const PlanApi = {
|
||||
getPlans,
|
||||
createPlan,
|
||||
getPlanById,
|
||||
updatePlan,
|
||||
deletePlan,
|
||||
startPlan,
|
||||
stopPlan,
|
||||
};
|
||||
143
src/api/user.js
Normal file
143
src/api/user.js
Normal file
@@ -0,0 +1,143 @@
|
||||
import http from '../utils/http';
|
||||
|
||||
/**
|
||||
* @typedef {object} CreateUserRequest
|
||||
* @property {string} username
|
||||
* @property {string} password
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} CreateUserResponse
|
||||
* @property {number} id
|
||||
* @property {string} username
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} LoginRequest
|
||||
* @property {string} identifier - Identifier 可以是用户名、邮箱、手机号、微信号或飞书账号
|
||||
* @property {string} password
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} LoginResponse
|
||||
* @property {number} id
|
||||
* @property {string} username
|
||||
* @property {string} token
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {('成功'|'失败')} AuditStatus
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} UserActionLogDTO
|
||||
* @property {number} id
|
||||
* @property {number} user_id
|
||||
* @property {string} username
|
||||
* @property {string} action_type
|
||||
* @property {string} description
|
||||
* @property {string} http_method
|
||||
* @property {string} http_path
|
||||
* @property {string} source_ip
|
||||
* @property {Array<number>} target_resource
|
||||
* @property {AuditStatus} status
|
||||
* @property {string} result_details
|
||||
* @property {string} time
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} PaginationDTO
|
||||
* @property {number} page
|
||||
* @property {number} page_size
|
||||
* @property {number} total
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} ListUserActionLogResponse
|
||||
* @property {Array<UserActionLogDTO>} list
|
||||
* @property {PaginationDTO} pagination
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} UserHistoryParams
|
||||
* @property {string} [action_type]
|
||||
* @property {string} [end_time]
|
||||
* @property {string} [order_by]
|
||||
* @property {number} [page]
|
||||
* @property {number} [page_size]
|
||||
* @property {string} [start_time]
|
||||
* @property {string} [status]
|
||||
* @property {number} [user_id]
|
||||
* @property {string} [username]
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {('邮件'|'企业微信'|'飞书'|'日志')} NotifierType
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} SendTestNotificationRequest
|
||||
* @property {NotifierType} type - Type 指定要测试的通知渠道
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} Response
|
||||
* @property {number} code - 业务状态码
|
||||
* @property {object} [data] - 业务数据
|
||||
* @property {string} [message] - 提示信息
|
||||
*/
|
||||
|
||||
/**
|
||||
* 创建一个新用户
|
||||
* @param {CreateUserRequest} userData - 用户信息
|
||||
* @returns {Promise<CreateUserResponse>}
|
||||
*/
|
||||
const createUser = (userData) => {
|
||||
return http.post('/api/v1/users', userData);
|
||||
};
|
||||
|
||||
/**
|
||||
* 用户登录
|
||||
* @param {LoginRequest} credentials - 登录凭证
|
||||
* @returns {Promise<LoginResponse>}
|
||||
*/
|
||||
const login = (credentials) => {
|
||||
return http.post('/api/v1/users/login', credentials);
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取用户操作日志列表
|
||||
* @param {UserHistoryParams} params - 查询参数
|
||||
* @returns {Promise<ListUserActionLogResponse>}
|
||||
*/
|
||||
const getUserActionLogs = (params) => {
|
||||
const newParams = {
|
||||
action_type: params.action_type,
|
||||
end_time: params.end_time,
|
||||
order_by: params.order_by,
|
||||
page: params.page,
|
||||
page_size: params.page_size,
|
||||
start_time: params.start_time,
|
||||
status: params.status,
|
||||
user_id: params.user_id,
|
||||
username: params.username,
|
||||
};
|
||||
return http.get('/api/v1/monitor/user-action-logs', { params: newParams });
|
||||
};
|
||||
|
||||
/**
|
||||
* 发送测试通知
|
||||
* @param {number} id - 用户ID
|
||||
* @param {SendTestNotificationRequest} data - 请求体
|
||||
* @returns {Promise<Response>}
|
||||
*/
|
||||
const sendTestNotification = (id, data) => {
|
||||
return http.post(`/api/v1/users/${id}/notifications/test`, data);
|
||||
};
|
||||
|
||||
export const UserApi = {
|
||||
createUser,
|
||||
login,
|
||||
getUserActionLogs,
|
||||
sendTestNotification,
|
||||
};
|
||||
80
src/components/AllocatePigsDialog.vue
Normal file
80
src/components/AllocatePigsDialog.vue
Normal file
@@ -0,0 +1,80 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
title="分配猪只"
|
||||
:model-value="visible"
|
||||
@update:model-value="$emit('update:visible', $event)"
|
||||
width="30%"
|
||||
@close="resetForm"
|
||||
>
|
||||
<el-form ref="form" :model="form" :rules="rules" label-width="100px">
|
||||
<el-form-item label="未分配数量">
|
||||
<span>{{ unassignedPigCount }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="分配数量" prop="quantity">
|
||||
<el-input-number v-model="form.quantity" :min="1" :max="unassignedPigCount" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="$emit('update:visible', false)">取 消</el-button>
|
||||
<el-button type="primary" @click="handleConfirm">确 定</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'AllocatePigsDialog',
|
||||
props: {
|
||||
visible: {
|
||||
type: Boolean,
|
||||
required: true
|
||||
},
|
||||
unassignedPigCount: {
|
||||
type: Number,
|
||||
required: true
|
||||
},
|
||||
penId: {
|
||||
type: Number,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
emits: ['update:visible', 'confirm'],
|
||||
data() {
|
||||
return {
|
||||
form: {
|
||||
quantity: 1
|
||||
},
|
||||
rules: {
|
||||
quantity: [
|
||||
{ required: true, message: '请输入分配数量', trigger: 'blur' },
|
||||
{ type: 'integer', message: '请输入整数', trigger: 'blur' },
|
||||
{ validator: this.validateQuantity, trigger: 'blur' }
|
||||
]
|
||||
}
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
validateQuantity(rule, value, callback) {
|
||||
if (value > this.unassignedPigCount) {
|
||||
callback(new Error('分配数量不能超过未分配数量'));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
},
|
||||
handleConfirm() {
|
||||
this.$refs.form.validate(valid => {
|
||||
if (valid) {
|
||||
this.$emit('confirm', { penId: this.penId, quantity: this.form.quantity });
|
||||
this.$emit('update:visible', false);
|
||||
}
|
||||
});
|
||||
},
|
||||
resetForm() {
|
||||
this.$refs.form.resetFields();
|
||||
this.form.quantity = 1;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
330
src/components/CronExpressionEditor.vue
Normal file
330
src/components/CronExpressionEditor.vue
Normal file
@@ -0,0 +1,330 @@
|
||||
<template>
|
||||
<div class="cron-expression-editor">
|
||||
<el-input
|
||||
v-model="cronExpression"
|
||||
placeholder="请输入标准Unix 5位cron表达式,如: 0 0 * * *"
|
||||
readonly
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="openCronDialog">可视化配置</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
|
||||
<el-dialog
|
||||
v-model="showCronDialog"
|
||||
title="可视化配置Cron表达式"
|
||||
width="600px"
|
||||
:before-close="handleCronDialogClose"
|
||||
>
|
||||
<div class="cron-dialog-content">
|
||||
<el-form :model="cronConfig" label-width="100px">
|
||||
<!-- 分 -->
|
||||
<el-form-item label="分">
|
||||
<el-select
|
||||
v-model="cronConfig.minute"
|
||||
placeholder="请选择分钟"
|
||||
clearable
|
||||
filterable
|
||||
allow-create
|
||||
default-first-option
|
||||
>
|
||||
<el-option label="*" value="*">
|
||||
<span>*</span>
|
||||
<span class="option-desc">每分钟</span>
|
||||
</el-option>
|
||||
<el-option label="*/5" value="*/5">
|
||||
<span>*/5</span>
|
||||
<span class="option-desc">每隔5分钟</span>
|
||||
</el-option>
|
||||
<el-option label="*/10" value="*/10">
|
||||
<span>*/10</span>
|
||||
<span class="option-desc">每隔10分钟</span>
|
||||
</el-option>
|
||||
<el-option label="*/15" value="*/15">
|
||||
<span>*/15</span>
|
||||
<span class="option-desc">每隔15分钟</span>
|
||||
</el-option>
|
||||
<el-option label="*/30" value="*/30">
|
||||
<span>*/30</span>
|
||||
<span class="option-desc">每隔30分钟</span>
|
||||
</el-option>
|
||||
<el-option
|
||||
v-for="minute in 60"
|
||||
:key="minute-1"
|
||||
:label="minute-1"
|
||||
:value="(minute-1).toString()"
|
||||
>
|
||||
<span>{{ minute-1 }}</span>
|
||||
<span class="option-desc">第{{ minute-1 }}分钟</span>
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<!-- 时 -->
|
||||
<el-form-item label="时">
|
||||
<el-select
|
||||
v-model="cronConfig.hour"
|
||||
placeholder="请选择小时"
|
||||
clearable
|
||||
filterable
|
||||
allow-create
|
||||
default-first-option
|
||||
>
|
||||
<el-option label="*" value="*">
|
||||
<span>*</span>
|
||||
<span class="option-desc">每小时</span>
|
||||
</el-option>
|
||||
<el-option label="*/2" value="*/2">
|
||||
<span>*/2</span>
|
||||
<span class="option-desc">每隔2小时</span>
|
||||
</el-option>
|
||||
<el-option label="*/3" value="*/3">
|
||||
<span>*/3</span>
|
||||
<span class="option-desc">每隔3小时</span>
|
||||
</el-option>
|
||||
<el-option label="*/6" value="*/6">
|
||||
<span>*/6</span>
|
||||
<span class="option-desc">每隔6小时</span>
|
||||
</el-option>
|
||||
<el-option label="*/12" value="*/12">
|
||||
<span>*/12</span>
|
||||
<span class="option-desc">每隔12小时</span>
|
||||
</el-option>
|
||||
<el-option
|
||||
v-for="hour in 24"
|
||||
:key="hour-1"
|
||||
:label="hour-1"
|
||||
:value="(hour-1).toString()"
|
||||
>
|
||||
<span>{{ hour-1 }}</span>
|
||||
<span class="option-desc">{{ hour-1 }}点</span>
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<!-- 日 -->
|
||||
<el-form-item label="日">
|
||||
<el-select
|
||||
v-model="cronConfig.day"
|
||||
placeholder="请选择日期"
|
||||
clearable
|
||||
filterable
|
||||
allow-create
|
||||
default-first-option
|
||||
>
|
||||
<el-option label="*" value="*">
|
||||
<span>*</span>
|
||||
<span class="option-desc">每天</span>
|
||||
</el-option>
|
||||
<el-option label="*/2" value="*/2">
|
||||
<span>*/2</span>
|
||||
<span class="option-desc">每隔2天</span>
|
||||
</el-option>
|
||||
<el-option label="*/7" value="*/7">
|
||||
<span>*/7</span>
|
||||
<span class="option-desc">每隔7天</span>
|
||||
</el-option>
|
||||
<el-option label="*/15" value="*/15">
|
||||
<span>*/15</span>
|
||||
<span class="option-desc">每隔15天</span>
|
||||
</el-option>
|
||||
<el-option
|
||||
v-for="day in 31"
|
||||
:key="day"
|
||||
:label="day"
|
||||
:value="day.toString()"
|
||||
>
|
||||
<span>{{ day }}</span>
|
||||
<span class="option-desc">每月{{ day }}日</span>
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<!-- 月 -->
|
||||
<el-form-item label="月">
|
||||
<el-select
|
||||
v-model="cronConfig.month"
|
||||
placeholder="请选择月份"
|
||||
clearable
|
||||
filterable
|
||||
allow-create
|
||||
default-first-option
|
||||
>
|
||||
<el-option label="*" value="*">
|
||||
<span>*</span>
|
||||
<span class="option-desc">每月</span>
|
||||
</el-option>
|
||||
<el-option label="*/2" value="*/2">
|
||||
<span>*/2</span>
|
||||
<span class="option-desc">每隔2个月</span>
|
||||
</el-option>
|
||||
<el-option label="*/3" value="*/3">
|
||||
<span>*/3</span>
|
||||
<span class="option-desc">每隔3个月</span>
|
||||
</el-option>
|
||||
<el-option label="*/6" value="*/6">
|
||||
<span>*/6</span>
|
||||
<span class="option-desc">每隔6个月</span>
|
||||
</el-option>
|
||||
<el-option
|
||||
v-for="month in 12"
|
||||
:key="month"
|
||||
:label="month"
|
||||
:value="month.toString()"
|
||||
>
|
||||
<span>{{ month }}</span>
|
||||
<span class="option-desc">第{{ month }}月</span>
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<!-- 周 -->
|
||||
<el-form-item label="周">
|
||||
<el-select
|
||||
v-model="cronConfig.week"
|
||||
placeholder="请选择星期"
|
||||
clearable
|
||||
filterable
|
||||
allow-create
|
||||
default-first-option
|
||||
>
|
||||
<el-option label="*" value="*">
|
||||
<span>*</span>
|
||||
<span class="option-desc">每周</span>
|
||||
</el-option>
|
||||
<el-option label="1-5" value="1-5">
|
||||
<span>1-5</span>
|
||||
<span class="option-desc">工作日</span>
|
||||
</el-option>
|
||||
<el-option
|
||||
v-for="(week, index) in weekOptions"
|
||||
:key="index"
|
||||
:label="week.label"
|
||||
:value="index.toString()"
|
||||
>
|
||||
<span>{{ week.label }}</span>
|
||||
<span class="option-desc">{{ week.desc }}</span>
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="handleCronDialogClose">取消</el-button>
|
||||
<el-button type="primary" @click="confirmCronExpression">确认</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ref, computed, watch, reactive } from 'vue'
|
||||
|
||||
export default {
|
||||
name: 'CronExpressionEditor',
|
||||
props: {
|
||||
modelValue: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
emits: ['update:modelValue'],
|
||||
setup(props, { emit }) {
|
||||
// 是否显示Cron表达式配置对话框
|
||||
const showCronDialog = ref(false)
|
||||
|
||||
// 星期选项
|
||||
const weekOptions = [
|
||||
{ label: '日', desc: '星期日' },
|
||||
{ label: '一', desc: '星期一' },
|
||||
{ label: '二', desc: '星期二' },
|
||||
{ label: '三', desc: '星期三' },
|
||||
{ label: '四', desc: '星期四' },
|
||||
{ label: '五', desc: '星期五' },
|
||||
{ label: '六', desc: '星期六' }
|
||||
]
|
||||
|
||||
// cron配置
|
||||
const cronConfig = reactive({
|
||||
minute: '*',
|
||||
hour: '*',
|
||||
day: '*',
|
||||
month: '*',
|
||||
week: '*'
|
||||
})
|
||||
|
||||
// 计算属性:cron表达式
|
||||
const cronExpression = computed({
|
||||
get: () => props.modelValue,
|
||||
set: (value) => emit('update:modelValue', value)
|
||||
})
|
||||
|
||||
// 打开Cron对话框
|
||||
const openCronDialog = () => {
|
||||
// 解析当前的cron表达式
|
||||
if (cronExpression.value) {
|
||||
const parts = cronExpression.value.split(' ')
|
||||
if (parts.length === 5) {
|
||||
cronConfig.minute = parts[0]
|
||||
cronConfig.hour = parts[1]
|
||||
cronConfig.day = parts[2]
|
||||
cronConfig.month = parts[3]
|
||||
cronConfig.week = parts[4]
|
||||
}
|
||||
} else {
|
||||
// 默认值
|
||||
cronConfig.minute = '*'
|
||||
cronConfig.hour = '*'
|
||||
cronConfig.day = '*'
|
||||
cronConfig.month = '*'
|
||||
cronConfig.week = '*'
|
||||
}
|
||||
showCronDialog.value = true
|
||||
}
|
||||
|
||||
// 确认Cron表达式
|
||||
const confirmCronExpression = () => {
|
||||
cronExpression.value = `${cronConfig.minute} ${cronConfig.hour} ${cronConfig.day} ${cronConfig.month} ${cronConfig.week}`
|
||||
showCronDialog.value = false
|
||||
}
|
||||
|
||||
// 处理Cron对话框关闭
|
||||
const handleCronDialogClose = () => {
|
||||
showCronDialog.value = false
|
||||
}
|
||||
|
||||
return {
|
||||
showCronDialog,
|
||||
cronConfig,
|
||||
cronExpression,
|
||||
weekOptions,
|
||||
openCronDialog,
|
||||
confirmCronExpression,
|
||||
handleCronDialogClose
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.cron-expression-editor {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.cron-dialog-content {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.dialog-footer {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.option-desc {
|
||||
float: right;
|
||||
color: #8492a6;
|
||||
font-size: 13px;
|
||||
}
|
||||
</style>
|
||||
360
src/components/DeviceForm.vue
Normal file
360
src/components/DeviceForm.vue
Normal file
@@ -0,0 +1,360 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
:model-value="visible"
|
||||
:title="title"
|
||||
@close="handleClose"
|
||||
:close-on-click-modal="false"
|
||||
width="600px"
|
||||
>
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="formData"
|
||||
:rules="rules"
|
||||
label-width="120px"
|
||||
@submit.prevent
|
||||
>
|
||||
<!-- 基础信息 -->
|
||||
<el-form-item label="名称" prop="name">
|
||||
<el-input v-model="formData.name" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="类型" prop="type">
|
||||
<el-select v-model="formData.type" @change="handleTypeChange" :disabled="isEdit">
|
||||
<el-option label="区域主控" value="area_controller" />
|
||||
<el-option label="普通设备" value="device" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="位置描述" prop="location">
|
||||
<el-input v-model="formData.location" type="textarea" />
|
||||
</el-form-item>
|
||||
|
||||
<!-- 区域主控类型额外字段 -->
|
||||
<div v-if="formData.type === 'area_controller'">
|
||||
<el-form-item label="网络ID" prop="network_id">
|
||||
<el-input v-model="formData.network_id" />
|
||||
</el-form-item>
|
||||
</div>
|
||||
|
||||
<!-- 普通设备类型额外字段 -->
|
||||
<div v-if="formData.type === 'device'">
|
||||
<el-form-item label="所属区域主控" prop="area_controller_id">
|
||||
<el-select v-model="formData.area_controller_id" placeholder="请选择区域主控">
|
||||
<el-option
|
||||
v-for="controller in areaControllers"
|
||||
:key="controller.id"
|
||||
:label="controller.name"
|
||||
:value="controller.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="设备模板" prop="device_template_id">
|
||||
<el-select v-model="formData.device_template_id" placeholder="请选择设备模板">
|
||||
<el-option
|
||||
v-for="template in deviceTemplates"
|
||||
:key="template.id"
|
||||
:label="template.name"
|
||||
:value="template.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="485总线号" prop="properties.bus_number">
|
||||
<el-input-number
|
||||
v-model="formData.properties.bus_number"
|
||||
:min="0"
|
||||
controls-position="right"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="485总线地址" prop="properties.bus_address">
|
||||
<el-input-number
|
||||
v-model="formData.properties.bus_address"
|
||||
:min="0"
|
||||
controls-position="right"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
</div>
|
||||
</el-form>
|
||||
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="handleClose">取消</el-button>
|
||||
<el-button type="primary" @click="handleSubmit" :loading="loading">确定</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ref, reactive, onMounted, watch, computed, nextTick } from 'vue';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import { AreaControllerApi, DeviceApi } from '../api/device.js';
|
||||
import deviceTemplateService from '../services/deviceTemplateService.js'; // 导入设备模板服务
|
||||
|
||||
export default {
|
||||
name: 'DeviceForm',
|
||||
props: {
|
||||
visible: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
deviceData: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
isEdit: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
emits: ['update:visible', 'success', 'cancel'],
|
||||
setup(props, { emit }) {
|
||||
const formRef = ref(null);
|
||||
const loading = ref(false);
|
||||
const areaControllers = ref([]);
|
||||
const deviceTemplates = ref([]); // 新增设备模板列表状态
|
||||
|
||||
const initialFormData = () => ({
|
||||
id: '',
|
||||
name: '',
|
||||
type: 'device', // 默认创建普通设备
|
||||
location: '',
|
||||
network_id: '', // 区域主控字段
|
||||
area_controller_id: '', // 普通设备字段
|
||||
device_template_id: '', // 普通设备字段
|
||||
properties: { // 嵌套的properties对象
|
||||
bus_number: 0,
|
||||
bus_address: 0,
|
||||
}
|
||||
});
|
||||
|
||||
const formData = reactive(initialFormData());
|
||||
|
||||
const rules = computed(() => ({
|
||||
name: [
|
||||
{ required: true, message: '请输入名称', trigger: 'blur' }
|
||||
],
|
||||
type: [
|
||||
{ required: true, message: '请选择类型', trigger: 'change' }
|
||||
],
|
||||
location: [
|
||||
{ required: true, message: '请输入位置描述', trigger: 'blur' }
|
||||
],
|
||||
network_id: [
|
||||
{ required: formData.type === 'area_controller', message: '请输入网络ID', trigger: 'blur' }
|
||||
],
|
||||
area_controller_id: [
|
||||
{ required: formData.type === 'device', message: '请选择所属区域主控', trigger: 'change' }
|
||||
],
|
||||
device_template_id: [
|
||||
{ required: formData.type === 'device', message: '请选择设备模板', trigger: 'change' }
|
||||
],
|
||||
'properties.bus_number': [
|
||||
{ required: formData.type === 'device', message: '请输入485总线号', trigger: 'blur' }
|
||||
],
|
||||
'properties.bus_address': [
|
||||
{ required: formData.type === 'device', message: '请输入485总线地址', trigger: 'blur' }
|
||||
],
|
||||
}));
|
||||
|
||||
const title = computed(() => {
|
||||
return props.isEdit ? '编辑设备' : '添加设备';
|
||||
});
|
||||
|
||||
const handleTypeChange = (value) => {
|
||||
// 清除不同类型特有的字段
|
||||
if (value === 'area_controller') {
|
||||
formData.area_controller_id = '';
|
||||
formData.device_template_id = '';
|
||||
formData.properties = { bus_number: 0, bus_address: 0 };
|
||||
} else {
|
||||
formData.network_id = '';
|
||||
}
|
||||
// 触发验证规则更新
|
||||
nextTick(() => {
|
||||
if (formRef.value) {
|
||||
formRef.value.clearValidate();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const loadAreaControllers = async () => {
|
||||
try {
|
||||
const response = await AreaControllerApi.list();
|
||||
areaControllers.value = response.data || [];
|
||||
} catch (error) {
|
||||
console.error('获取区域主控列表失败:', error);
|
||||
areaControllers.value = [];
|
||||
}
|
||||
};
|
||||
|
||||
// 新增:加载设备模板列表
|
||||
const loadDeviceTemplates = async () => {
|
||||
try {
|
||||
const response = await deviceTemplateService.getDeviceTemplates();
|
||||
deviceTemplates.value = response.data || [];
|
||||
} catch (error) {
|
||||
console.error('获取设备模板列表失败:', error);
|
||||
deviceTemplates.value = [];
|
||||
}
|
||||
};
|
||||
|
||||
const handleClose = () => {
|
||||
emit('update:visible', false);
|
||||
emit('cancel');
|
||||
// 重置表单
|
||||
Object.assign(formData, initialFormData());
|
||||
nextTick(() => {
|
||||
if (formRef.value) {
|
||||
formRef.value.resetFields();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const getSubmitData = () => {
|
||||
const data = {
|
||||
name: formData.name,
|
||||
location: formData.location,
|
||||
properties: formData.properties // properties直接作为对象传递
|
||||
};
|
||||
|
||||
if (formData.type === 'area_controller') {
|
||||
data.network_id = formData.network_id;
|
||||
} else {
|
||||
data.area_controller_id = formData.area_controller_id;
|
||||
data.device_template_id = formData.device_template_id;
|
||||
}
|
||||
return data;
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
if (!formRef.value) return;
|
||||
await formRef.value.validate(async (valid) => {
|
||||
if (valid) {
|
||||
loading.value = true;
|
||||
try {
|
||||
let result;
|
||||
const submitData = getSubmitData();
|
||||
|
||||
if (props.isEdit) {
|
||||
if (formData.type === 'area_controller') {
|
||||
result = await AreaControllerApi.update(formData.id, submitData);
|
||||
} else {
|
||||
result = await DeviceApi.update(formData.id, submitData);
|
||||
}
|
||||
} else {
|
||||
if (formData.type === 'area_controller') {
|
||||
result = await AreaControllerApi.create(submitData);
|
||||
} else {
|
||||
result = await DeviceApi.create(submitData);
|
||||
}
|
||||
}
|
||||
|
||||
// 适配DeviceList的树形结构,添加type和parent_id
|
||||
const processedResult = {
|
||||
...result.data,
|
||||
type: formData.type
|
||||
};
|
||||
if (formData.type === 'device') {
|
||||
processedResult.parent_id = processedResult.area_controller_id;
|
||||
}
|
||||
|
||||
emit('success', processedResult);
|
||||
handleClose();
|
||||
} catch (error) {
|
||||
console.error('保存设备失败:', error);
|
||||
ElMessage.error(props.isEdit ? '编辑设备失败: ' + (error.message || '未知错误') : '创建设备失败: ' + (error.message || '未知错误'));
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
watch(() => props.deviceData, (newVal) => {
|
||||
if (newVal && Object.keys(newVal).length > 0) {
|
||||
// 重置表单以清除旧数据和验证状态
|
||||
Object.assign(formData, initialFormData());
|
||||
nextTick(() => {
|
||||
if (formRef.value) {
|
||||
formRef.value.clearValidate();
|
||||
}
|
||||
});
|
||||
|
||||
formData.id = newVal.id;
|
||||
formData.name = newVal.name;
|
||||
formData.type = newVal.type;
|
||||
formData.location = newVal.location;
|
||||
|
||||
if (newVal.type === 'area_controller') {
|
||||
formData.network_id = newVal.network_id || '';
|
||||
} else if (newVal.type === 'device') {
|
||||
formData.area_controller_id = newVal.area_controller_id || newVal.parent_id || '';
|
||||
formData.device_template_id = newVal.device_template_id || '';
|
||||
}
|
||||
|
||||
// 处理properties对象的数据填充
|
||||
if (newVal.properties) {
|
||||
// 确保properties是一个对象,如果API返回的是字符串则尝试解析
|
||||
const propsData = typeof newVal.properties === 'string' ? JSON.parse(newVal.properties) : newVal.properties;
|
||||
Object.assign(formData.properties, propsData);
|
||||
}
|
||||
} else {
|
||||
// 如果没有传入deviceData,则重置为初始状态
|
||||
Object.assign(formData, initialFormData());
|
||||
nextTick(() => {
|
||||
if (formRef.value) {
|
||||
formRef.value.clearValidate();
|
||||
}
|
||||
});
|
||||
}
|
||||
}, { immediate: true });
|
||||
|
||||
watch(() => props.visible, (newVal) => {
|
||||
if (newVal) {
|
||||
loadAreaControllers();
|
||||
loadDeviceTemplates(); // 新增:对话框打开时加载设备模板
|
||||
// 如果是新增,确保type是默认值,且清空所有字段
|
||||
if (!props.isEdit) {
|
||||
Object.assign(formData, initialFormData());
|
||||
nextTick(() => {
|
||||
if (formRef.value) {
|
||||
formRef.value.clearValidate();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}, { immediate: true });
|
||||
|
||||
onMounted(() => {
|
||||
loadAreaControllers();
|
||||
loadDeviceTemplates(); // 新增:组件挂载时加载设备模板
|
||||
});
|
||||
|
||||
return {
|
||||
formRef,
|
||||
loading,
|
||||
areaControllers,
|
||||
deviceTemplates, // 暴露设备模板列表
|
||||
formData,
|
||||
rules,
|
||||
title,
|
||||
handleTypeChange,
|
||||
handleClose,
|
||||
handleSubmit
|
||||
};
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.dialog-footer {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 10px;
|
||||
}
|
||||
</style>
|
||||
@@ -1,236 +0,0 @@
|
||||
<template>
|
||||
<div class="device-list">
|
||||
<el-card>
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>设备管理</span>
|
||||
<el-button type="primary" @click="addDevice">添加设备</el-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- 加载状态 -->
|
||||
<div v-if="loading" class="loading">
|
||||
<el-skeleton animated />
|
||||
</div>
|
||||
|
||||
<!-- 错误状态 -->
|
||||
<div v-else-if="error" class="error">
|
||||
<el-alert
|
||||
title="获取设备数据失败"
|
||||
:description="error"
|
||||
type="error"
|
||||
show-icon
|
||||
closable
|
||||
@close="error = null"
|
||||
/>
|
||||
<el-button type="primary" @click="loadDevices" class="retry-btn">重新加载</el-button>
|
||||
</div>
|
||||
|
||||
<!-- 设备列表 -->
|
||||
<el-table v-else :data="devices" style="width: 100%">
|
||||
<el-table-column prop="id" label="设备ID" width="180" />
|
||||
<el-table-column prop="name" label="设备名称" width="180" />
|
||||
<el-table-column prop="type" label="设备类型" />
|
||||
<el-table-column prop="status" label="状态">
|
||||
<template #default="scope">
|
||||
<el-tag :type="scope.row.status === 'online' ? 'success' : 'danger'">
|
||||
{{ scope.row.status === 'online' ? '在线' : '离线' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="lastUpdate" label="最后更新" />
|
||||
<el-table-column label="操作">
|
||||
<template #default="scope">
|
||||
<el-button size="small" @click="editDevice(scope.row)">编辑</el-button>
|
||||
<el-button size="small" type="danger" @click="deleteDevice(scope.row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-card>
|
||||
|
||||
<!-- 添加/编辑设备对话框 -->
|
||||
<el-dialog v-model="dialogVisible" :title="dialogTitle">
|
||||
<el-form :model="currentDevice" label-width="120px">
|
||||
<el-form-item label="设备名称">
|
||||
<el-input v-model="currentDevice.name" />
|
||||
</el-form-item>
|
||||
<el-form-item label="设备类型">
|
||||
<el-select v-model="currentDevice.type" placeholder="请选择设备类型">
|
||||
<el-option label="传感器" value="sensor" />
|
||||
<el-option label="控制器" value="controller" />
|
||||
<el-option label="摄像头" value="camera" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="设备状态">
|
||||
<el-select v-model="currentDevice.status" placeholder="请选择设备状态">
|
||||
<el-option label="在线" value="online" />
|
||||
<el-option label="离线" value="offline" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="dialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="saveDevice" :loading="saving">保存</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import deviceService from '../services/deviceService.js';
|
||||
|
||||
export default {
|
||||
name: 'DeviceList',
|
||||
data() {
|
||||
return {
|
||||
devices: [],
|
||||
loading: false,
|
||||
error: null,
|
||||
saving: false,
|
||||
dialogVisible: false,
|
||||
dialogTitle: '',
|
||||
currentDevice: {
|
||||
id: '',
|
||||
name: '',
|
||||
type: '',
|
||||
status: 'online'
|
||||
},
|
||||
isEdit: false
|
||||
};
|
||||
},
|
||||
async mounted() {
|
||||
await this.loadDevices();
|
||||
},
|
||||
methods: {
|
||||
// 加载设备列表
|
||||
async loadDevices() {
|
||||
this.loading = true;
|
||||
this.error = null;
|
||||
|
||||
try {
|
||||
const data = await deviceService.getDevices();
|
||||
this.devices = data.map(device => ({
|
||||
...device,
|
||||
// 格式化数据显示
|
||||
type: this.formatDeviceType(device.type),
|
||||
lastUpdate: device.lastUpdate || '-'
|
||||
}));
|
||||
} catch (err) {
|
||||
this.error = err.message || '未知错误';
|
||||
console.error('加载设备列表失败:', err);
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
|
||||
// 格式化设备类型显示
|
||||
formatDeviceType(type) {
|
||||
const typeMap = {
|
||||
'sensor': '传感器',
|
||||
'controller': '控制器',
|
||||
'camera': '摄像头'
|
||||
};
|
||||
return typeMap[type] || type;
|
||||
},
|
||||
|
||||
addDevice() {
|
||||
this.dialogTitle = '添加设备';
|
||||
this.currentDevice = {
|
||||
id: '',
|
||||
name: '',
|
||||
type: '',
|
||||
status: 'online'
|
||||
};
|
||||
this.isEdit = false;
|
||||
this.dialogVisible = true;
|
||||
},
|
||||
|
||||
editDevice(device) {
|
||||
this.dialogTitle = '编辑设备';
|
||||
// 注意:这里需要将显示值转换回API值
|
||||
const typeMap = {
|
||||
'传感器': 'sensor',
|
||||
'控制器': 'controller',
|
||||
'摄像头': 'camera'
|
||||
};
|
||||
|
||||
this.currentDevice = {
|
||||
...device,
|
||||
type: typeMap[device.type] || device.type
|
||||
};
|
||||
this.isEdit = true;
|
||||
this.dialogVisible = true;
|
||||
},
|
||||
|
||||
async deleteDevice(device) {
|
||||
try {
|
||||
await this.$confirm('确认删除该设备吗?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
});
|
||||
|
||||
await deviceService.deleteDevice(device.id);
|
||||
this.$message.success('删除成功');
|
||||
// 重新加载设备列表
|
||||
await this.loadDevices();
|
||||
} catch (err) {
|
||||
if (err !== 'cancel') {
|
||||
this.$message.error('删除失败: ' + (err.message || '未知错误'));
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
async saveDevice() {
|
||||
this.saving = true;
|
||||
|
||||
try {
|
||||
if (this.isEdit) {
|
||||
// 编辑设备
|
||||
await deviceService.updateDevice(this.currentDevice.id, this.currentDevice);
|
||||
this.$message.success('设备更新成功');
|
||||
} else {
|
||||
// 添加新设备
|
||||
await deviceService.createDevice(this.currentDevice);
|
||||
this.$message.success('设备添加成功');
|
||||
}
|
||||
|
||||
this.dialogVisible = false;
|
||||
// 重新加载设备列表
|
||||
await this.loadDevices();
|
||||
} catch (err) {
|
||||
this.$message.error('保存失败: ' + (err.message || '未知错误'));
|
||||
} finally {
|
||||
this.saving = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.dialog-footer {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.loading {
|
||||
padding: 20px 0;
|
||||
}
|
||||
|
||||
.error {
|
||||
padding: 20px 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.retry-btn {
|
||||
margin-top: 15px;
|
||||
}
|
||||
</style>
|
||||
288
src/components/DeviceTemplateForm.vue
Normal file
288
src/components/DeviceTemplateForm.vue
Normal file
@@ -0,0 +1,288 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
:model-value="visible"
|
||||
:title="title"
|
||||
@close="handleClose"
|
||||
:close-on-click-modal="false"
|
||||
width="600px"
|
||||
>
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="formData"
|
||||
:rules="rules"
|
||||
label-width="120px"
|
||||
@submit.prevent
|
||||
>
|
||||
<el-form-item label="名称" prop="name">
|
||||
<el-input v-model="formData.name" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="制造商" prop="manufacturer">
|
||||
<el-input v-model="formData.manufacturer" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="描述" prop="description">
|
||||
<el-input v-model="formData.description" type="textarea" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="类别" prop="category">
|
||||
<el-select v-model="formData.category" placeholder="请选择类别" @change="handleCategoryChange">
|
||||
<el-option label="执行器" value="执行器" />
|
||||
<el-option label="传感器" value="传感器" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="指令 (JSON)" prop="commands">
|
||||
<el-input
|
||||
v-model="formData.commands"
|
||||
type="textarea"
|
||||
:rows="5"
|
||||
placeholder="请输入JSON格式的指令参数"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item
|
||||
v-if="formData.category === '传感器'"
|
||||
label="值描述 (JSON)"
|
||||
prop="values"
|
||||
>
|
||||
<el-input
|
||||
v-model="formData.values"
|
||||
type="textarea"
|
||||
:rows="5"
|
||||
placeholder="请输入JSON格式的值描述数组"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="handleClose">取消</el-button>
|
||||
<el-button type="primary" @click="handleSubmit" :loading="loading">确定</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ref, reactive, computed, watch, nextTick } from 'vue';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import deviceTemplateService from '../services/deviceTemplateService.js';
|
||||
|
||||
// 默认的JSON模板
|
||||
const DEFAULT_ACTUATOR_COMMANDS = JSON.stringify({
|
||||
modbus_start_address: 0,
|
||||
modbus_quantity: 1
|
||||
}, null, 2);
|
||||
|
||||
const DEFAULT_SENSOR_COMMANDS = JSON.stringify({
|
||||
modbus_function_code: 3,
|
||||
modbus_start_address: 0,
|
||||
modbus_quantity: 1
|
||||
}, null, 2);
|
||||
|
||||
const DEFAULT_SENSOR_VALUES = JSON.stringify([
|
||||
{
|
||||
type: "temperature",
|
||||
multiplier: 0.1,
|
||||
offset: 0
|
||||
}
|
||||
], null, 2);
|
||||
|
||||
export default {
|
||||
name: 'DeviceTemplateForm',
|
||||
props: {
|
||||
visible: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
templateData: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
isEdit: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
emits: ['update:visible', 'success', 'cancel'],
|
||||
setup(props, { emit }) {
|
||||
const formRef = ref(null);
|
||||
const loading = ref(false);
|
||||
|
||||
const initialFormData = () => ({
|
||||
id: '',
|
||||
name: '',
|
||||
manufacturer: '',
|
||||
description: '',
|
||||
category: '执行器', // 默认执行器
|
||||
commands: DEFAULT_ACTUATOR_COMMANDS, // 预填充执行器指令
|
||||
values: '',
|
||||
});
|
||||
|
||||
const formData = reactive(initialFormData());
|
||||
|
||||
// JSON 验证器
|
||||
const validateJson = (rule, value, callback) => {
|
||||
if (!value) {
|
||||
return callback(new Error(rule.message));
|
||||
}
|
||||
try {
|
||||
JSON.parse(value);
|
||||
callback();
|
||||
} catch (e) {
|
||||
callback(new Error('请输入有效的 JSON 格式'));
|
||||
}
|
||||
};
|
||||
|
||||
const rules = computed(() => ({
|
||||
name: [{ required: true, message: '请输入模板名称', trigger: 'blur' }],
|
||||
category: [{ required: true, message: '请选择模板类别', trigger: 'change' }],
|
||||
commands: [
|
||||
{ required: true, message: '请输入指令参数', trigger: 'blur' },
|
||||
{ validator: validateJson, message: '指令参数必须是有效的 JSON 格式', trigger: 'blur' },
|
||||
],
|
||||
values: [
|
||||
{ required: formData.category === '传感器', message: '请输入值描述', trigger: 'blur' },
|
||||
{ validator: validateJson, message: '值描述必须是有效的 JSON 格式', trigger: 'blur' },
|
||||
],
|
||||
}));
|
||||
|
||||
const title = computed(() => {
|
||||
return props.isEdit ? '编辑设备模板' : '新增设备模板';
|
||||
});
|
||||
|
||||
const handleCategoryChange = (newCategory) => {
|
||||
if (newCategory === '执行器') {
|
||||
formData.commands = DEFAULT_ACTUATOR_COMMANDS;
|
||||
formData.values = ''; // 执行器没有values
|
||||
} else if (newCategory === '传感器') {
|
||||
formData.commands = DEFAULT_SENSOR_COMMANDS;
|
||||
formData.values = DEFAULT_SENSOR_VALUES; // 传感器预填充values
|
||||
}
|
||||
nextTick(() => {
|
||||
if (formRef.value) {
|
||||
formRef.value.clearValidate();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const handleClose = () => {
|
||||
emit('update:visible', false);
|
||||
emit('cancel');
|
||||
// 重置表单
|
||||
Object.assign(formData, initialFormData());
|
||||
nextTick(() => {
|
||||
if (formRef.value) {
|
||||
formRef.value.resetFields();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
if (!formRef.value) return;
|
||||
await formRef.value.validate(async (valid) => {
|
||||
if (valid) {
|
||||
loading.value = true;
|
||||
try {
|
||||
const submitData = {
|
||||
name: formData.name,
|
||||
manufacturer: formData.manufacturer,
|
||||
description: formData.description,
|
||||
category: formData.category,
|
||||
commands: JSON.parse(formData.commands),
|
||||
};
|
||||
|
||||
if (formData.category === '传感器' && formData.values) {
|
||||
submitData.values = JSON.parse(formData.values);
|
||||
}
|
||||
|
||||
if (props.isEdit) {
|
||||
await deviceTemplateService.updateDeviceTemplate(formData.id, submitData);
|
||||
} else {
|
||||
await deviceTemplateService.createDeviceTemplate(submitData);
|
||||
}
|
||||
|
||||
emit('success');
|
||||
handleClose();
|
||||
} catch (error) {
|
||||
console.error('保存设备模板失败:', error);
|
||||
ElMessage.error(
|
||||
props.isEdit ? '编辑设备模板失败: ' + (error.message || '未知错误') : '创建设备模板失败: ' + (error.message || '未知错误')
|
||||
);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
watch(
|
||||
() => props.templateData,
|
||||
(newVal) => {
|
||||
if (newVal && Object.keys(newVal).length > 0) {
|
||||
// 填充表单数据
|
||||
formData.id = newVal.id;
|
||||
formData.name = newVal.name;
|
||||
formData.manufacturer = newVal.manufacturer;
|
||||
formData.description = newVal.description;
|
||||
if (newVal.category === 'sensor') {
|
||||
formData.category = '传感器';
|
||||
} else if (newVal.category === 'actuator') {
|
||||
formData.category = '执行器';
|
||||
} else {
|
||||
formData.category = newVal.category;
|
||||
}
|
||||
// 格式化JSON显示
|
||||
formData.commands = newVal.commands ? JSON.stringify(newVal.commands, null, 2) : '';
|
||||
formData.values = newVal.values ? JSON.stringify(newVal.values, null, 2) : '';
|
||||
} else {
|
||||
// 重置表单数据到初始状态 (新增模式)
|
||||
Object.assign(formData, initialFormData());
|
||||
}
|
||||
nextTick(() => {
|
||||
if (formRef.value) {
|
||||
formRef.value.clearValidate();
|
||||
}
|
||||
});
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
watch(
|
||||
() => props.visible,
|
||||
(newVal) => {
|
||||
if (newVal && !props.isEdit) {
|
||||
// 如果是新增模式且对话框打开,重置表单
|
||||
Object.assign(formData, initialFormData());
|
||||
nextTick(() => {
|
||||
if (formRef.value) {
|
||||
formRef.value.clearValidate();
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
return {
|
||||
formRef,
|
||||
loading,
|
||||
formData,
|
||||
rules,
|
||||
title,
|
||||
handleCategoryChange,
|
||||
handleClose,
|
||||
handleSubmit,
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.dialog-footer {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 10px;
|
||||
}
|
||||
</style>
|
||||
268
src/components/GenericMonitorList.vue
Normal file
268
src/components/GenericMonitorList.vue
Normal file
@@ -0,0 +1,268 @@
|
||||
<template>
|
||||
<div class="generic-monitor-list">
|
||||
<el-card shadow="never">
|
||||
<el-form :inline="true" :model="filters" class="filter-form">
|
||||
<el-form-item v-for="col in filterableColumns" :key="col.dataIndex" :label="col.title">
|
||||
<template v-if="col.filterType === 'text'">
|
||||
<el-input
|
||||
v-model="filters[col.dataIndex]"
|
||||
:placeholder="`搜索 ${col.title}`"
|
||||
clearable
|
||||
@change="handleFilterChange(col.dataIndex, filters[col.dataIndex])"
|
||||
></el-input>
|
||||
</template>
|
||||
<template v-else-if="col.filterType === 'number'">
|
||||
<el-input-number
|
||||
v-model="filters[col.dataIndex]"
|
||||
:placeholder="`搜索 ${col.title}`"
|
||||
:controls="false"
|
||||
clearable
|
||||
@change="handleFilterChange(col.dataIndex, filters[col.dataIndex])"
|
||||
></el-input-number>
|
||||
</template>
|
||||
<template v-else-if="col.filterType === 'dateRange'">
|
||||
<el-date-picker
|
||||
v-model="filters[col.dataIndex]"
|
||||
type="datetimerange"
|
||||
range-separator="至"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
@change="handleFilterChange(col.dataIndex, filters[col.dataIndex])"
|
||||
></el-date-picker>
|
||||
</template>
|
||||
<template v-else-if="col.filterType === 'select'">
|
||||
<el-select
|
||||
v-model="filters[col.dataIndex]"
|
||||
:placeholder="`选择 ${col.title}`"
|
||||
clearable
|
||||
@change="handleFilterChange(col.dataIndex, filters[col.dataIndex])"
|
||||
>
|
||||
<el-option
|
||||
v-for="option in col.filterOptions"
|
||||
:key="option.value"
|
||||
:label="option.text"
|
||||
:value="option.value"
|
||||
></el-option>
|
||||
</el-select>
|
||||
</template>
|
||||
<template v-else-if="col.filterType === 'boolean'">
|
||||
<el-select
|
||||
v-model="filters[col.dataIndex]"
|
||||
:placeholder="`选择 ${col.title}`"
|
||||
clearable
|
||||
@change="handleFilterChange(col.dataIndex, filters[col.dataIndex])"
|
||||
>
|
||||
<el-option label="是" :value="true"></el-option>
|
||||
<el-option label="否" :value="false"></el-option>
|
||||
</el-select>
|
||||
</template>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="loadData">查询</el-button>
|
||||
<el-button @click="resetFilters">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-table
|
||||
:data="data"
|
||||
v-loading="loading"
|
||||
border
|
||||
stripe
|
||||
style="width: 100%"
|
||||
table-layout="auto"
|
||||
:fit="true"
|
||||
:scrollbar-always-on="true"
|
||||
@sort-change="handleSortChange"
|
||||
>
|
||||
<el-table-column
|
||||
v-for="col in tableColumns"
|
||||
:key="col.key"
|
||||
:prop="col.prop"
|
||||
:label="col.title"
|
||||
:sortable="col.sorter ? 'custom' : false"
|
||||
:formatter="col.formatter"
|
||||
:min-width="col.minWidth"
|
||||
>
|
||||
<template v-if="col.render" #default="{ row }">
|
||||
<component :is="col.render(row)"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<el-pagination
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange"
|
||||
:current-page="pagination.currentPage"
|
||||
:page-sizes="[10, 20, 50, 100]"
|
||||
:page-size="pagination.pageSize"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
:total="pagination.total"
|
||||
background
|
||||
style="margin-top: 20px; text-align: right;"
|
||||
></el-pagination>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {ref, reactive, onMounted, watch, computed} from 'vue';
|
||||
import {ElMessage} from 'element-plus';
|
||||
|
||||
const props = defineProps({
|
||||
fetchData: {
|
||||
type: Function,
|
||||
required: true,
|
||||
},
|
||||
columnsConfig: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
|
||||
const data = ref([]);
|
||||
const loading = ref(false);
|
||||
const pagination = reactive({
|
||||
currentPage: 1,
|
||||
pageSize: 10,
|
||||
total: 0,
|
||||
});
|
||||
const filters = reactive({});
|
||||
const sortOrder = reactive({
|
||||
prop: undefined,
|
||||
order: undefined,
|
||||
});
|
||||
|
||||
const filterableColumns = computed(() => {
|
||||
return props.columnsConfig.filter(col => col.filterType);
|
||||
});
|
||||
|
||||
const tableColumns = computed(() => {
|
||||
return props.columnsConfig.map(col => {
|
||||
const newCol = {...col};
|
||||
newCol.prop = Array.isArray(col.dataIndex) ? col.dataIndex.join('.') : col.dataIndex;
|
||||
|
||||
// 添加智能默认 formatter
|
||||
if (!newCol.formatter) {
|
||||
newCol.formatter = (row, column, cellValue) => {
|
||||
if (typeof cellValue === 'object' && cellValue !== null) {
|
||||
try {
|
||||
return JSON.stringify(cellValue, null, 2); // 格式化为可读的JSON字符串
|
||||
} catch (e) {
|
||||
console.warn('Failed to stringify object for display:', cellValue, e);
|
||||
return '[Object]'; // 无法序列化时显示简短提示
|
||||
}
|
||||
} else if (Array.isArray(cellValue)) {
|
||||
return cellValue.join(', '); // 数组也默认用逗号连接
|
||||
}
|
||||
return cellValue;
|
||||
};
|
||||
}
|
||||
|
||||
return newCol;
|
||||
});
|
||||
});
|
||||
|
||||
const loadData = async () => {
|
||||
loading.value = true;
|
||||
try {
|
||||
const params = {
|
||||
page: pagination.currentPage,
|
||||
page_size: pagination.pageSize, // Changed from pageSize to page_size
|
||||
...filters,
|
||||
orderBy: sortOrder.prop,
|
||||
order: sortOrder.order === 'ascending' ? 'asc' : (sortOrder.order === 'descending' ? 'desc' : undefined),
|
||||
};
|
||||
|
||||
// Custom function to format Date objects to YYYY-MM-DDTHH:mm:ssZ
|
||||
const formatToRFC3339WithOffset = (date) => {
|
||||
if (!date) return ''; // Handle null or undefined dates
|
||||
const year = date.getUTCFullYear();
|
||||
const month = (date.getUTCMonth() + 1).toString().padStart(2, '0');
|
||||
const day = date.getUTCDate().toString().padStart(2, '0');
|
||||
const hours = date.getUTCHours().toString().padStart(2, '0');
|
||||
const minutes = date.getUTCMinutes().toString().padStart(2, '0');
|
||||
const seconds = date.getUTCSeconds().toString().padStart(2, '0');
|
||||
return `${year}-${month}-${day}T${hours}:${minutes}:${seconds}Z`;
|
||||
};
|
||||
|
||||
// 将日期范围筛选转换为 start_time 和 end_time,并确保是 RFC3339 UTC 格式 (不带毫秒)
|
||||
filterableColumns.value.forEach(col => {
|
||||
if (col.filterType === 'dateRange' && filters[col.dataIndex] && filters[col.dataIndex].length === 2) {
|
||||
// filters[col.dataIndex] will now contain Date objects directly from el-date-picker
|
||||
const startDateObj = filters[col.dataIndex][0];
|
||||
const endDateObj = filters[col.dataIndex][1];
|
||||
|
||||
params[`start_time`] = formatToRFC3339WithOffset(startDateObj);
|
||||
params[`end_time`] = formatToRFC3339WithOffset(endDateObj);
|
||||
delete params[col.dataIndex];
|
||||
}
|
||||
});
|
||||
|
||||
console.log('Sending parameters to fetchData:', params);
|
||||
|
||||
const result = await props.fetchData(params);
|
||||
data.value = result.list;
|
||||
pagination.total = result.total;
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch data:', error);
|
||||
ElMessage.error('获取数据失败,请稍后再试。');
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const handleSizeChange = (val) => {
|
||||
pagination.pageSize = val;
|
||||
pagination.currentPage = 1;
|
||||
loadData();
|
||||
};
|
||||
|
||||
const handleCurrentChange = (val) => {
|
||||
pagination.currentPage = val;
|
||||
loadData();
|
||||
};
|
||||
|
||||
const handleSortChange = ({prop, order}) => {
|
||||
sortOrder.prop = prop;
|
||||
sortOrder.order = order;
|
||||
loadData();
|
||||
};
|
||||
|
||||
const handleFilterChange = (key, value) => {
|
||||
filters[key] = value;
|
||||
pagination.currentPage = 1;
|
||||
};
|
||||
|
||||
const resetFilters = () => {
|
||||
for (const key in filters) {
|
||||
delete filters[key];
|
||||
}
|
||||
sortOrder.prop = undefined;
|
||||
sortOrder.order = undefined;
|
||||
pagination.currentPage = 1;
|
||||
loadData();
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
loadData();
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.generic-monitor-list {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.filter-form {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.filter-form .el-form-item {
|
||||
min-width: 220px; /* 增加最小宽度 */
|
||||
}
|
||||
|
||||
.el-card {
|
||||
border: none;
|
||||
}
|
||||
</style>
|
||||
157
src/components/PenForm.vue
Normal file
157
src/components/PenForm.vue
Normal file
@@ -0,0 +1,157 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
:model-value="visible"
|
||||
:title="title"
|
||||
@close="handleClose"
|
||||
:close-on-click-modal="false"
|
||||
width="500px"
|
||||
>
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="formData"
|
||||
:rules="rules"
|
||||
label-width="100px"
|
||||
@submit.prevent
|
||||
>
|
||||
<el-form-item label="猪栏编号" prop="pen_number">
|
||||
<el-input v-model="formData.pen_number" placeholder="例如:A01-01" />
|
||||
</el-form-item>
|
||||
<el-form-item label="容量" prop="capacity">
|
||||
<el-input-number v-model="formData.capacity" :min="1" controls-position="right" style="width: 100%" />
|
||||
</el-form-item>
|
||||
<el-form-item v-if="isEdit" label="状态" prop="status">
|
||||
<el-select v-model="formData.status" placeholder="请选择状态" style="width: 100%">
|
||||
<el-option v-for="item in penStatusOptions" :key="item" :label="item" :value="item" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="handleClose">取消</el-button>
|
||||
<el-button type="primary" @click="handleSubmit" :loading="loading">确定</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ref, reactive, watch, computed, nextTick } from 'vue';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import { createPen, updatePen } from '@/api/pen.js';
|
||||
|
||||
export default {
|
||||
name: 'PenForm',
|
||||
props: {
|
||||
visible: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
penData: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
isEdit: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
emits: ['update:visible', 'success', 'cancel'],
|
||||
setup(props, { emit }) {
|
||||
const formRef = ref(null);
|
||||
const loading = ref(false);
|
||||
const penStatusOptions = ["空闲", "使用中", "病猪栏", "康复栏", "清洗消毒", "维修中"];
|
||||
|
||||
const initialFormData = () => ({
|
||||
id: null,
|
||||
pen_number: '',
|
||||
capacity: 10,
|
||||
status: '空闲',
|
||||
house_id: null
|
||||
});
|
||||
|
||||
const formData = reactive(initialFormData());
|
||||
|
||||
const rules = {
|
||||
pen_number: [
|
||||
{ required: true, message: '请输入猪栏编号', trigger: 'blur' }
|
||||
],
|
||||
capacity: [
|
||||
{ required: true, message: '请输入容量', trigger: 'blur' },
|
||||
{ type: 'integer', min: 1, message: '容量必须是大于0的整数', trigger: 'blur' }
|
||||
],
|
||||
status: [
|
||||
{ required: true, message: '请选择状态', trigger: 'change' }
|
||||
]
|
||||
};
|
||||
|
||||
const title = computed(() => (props.isEdit ? '编辑猪栏' : '添加猪栏'));
|
||||
|
||||
const handleClose = () => {
|
||||
emit('update:visible', false);
|
||||
emit('cancel');
|
||||
nextTick(() => {
|
||||
formRef.value?.resetFields();
|
||||
});
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
if (!formRef.value) return;
|
||||
await formRef.value.validate(async (valid) => {
|
||||
if (valid) {
|
||||
loading.value = true;
|
||||
try {
|
||||
let response;
|
||||
if (props.isEdit) {
|
||||
const { id, ...updateData } = formData;
|
||||
response = await updatePen(id, updateData);
|
||||
} else {
|
||||
const { id, status, ...createData } = formData;
|
||||
response = await createPen(createData);
|
||||
}
|
||||
emit('success', response.data);
|
||||
handleClose();
|
||||
} catch (error) {
|
||||
ElMessage.error((props.isEdit ? '更新' : '创建') + '猪栏失败: ' + (error.message || '未知错误'));
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
watch(() => props.visible, (newVal) => {
|
||||
if (newVal) {
|
||||
Object.assign(formData, initialFormData());
|
||||
if (props.isEdit && props.penData) {
|
||||
Object.assign(formData, props.penData);
|
||||
} else if (props.penData) {
|
||||
formData.house_id = props.penData.house_id;
|
||||
}
|
||||
nextTick(() => {
|
||||
formRef.value?.clearValidate();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
formRef,
|
||||
loading,
|
||||
formData,
|
||||
rules,
|
||||
title,
|
||||
penStatusOptions,
|
||||
handleClose,
|
||||
handleSubmit
|
||||
};
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.dialog-footer {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 10px;
|
||||
}
|
||||
</style>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user