@wat-toolbox/wat 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +62 -0
- package/dist/cli/commands/availability.d.ts +10 -0
- package/dist/cli/commands/availability.d.ts.map +1 -0
- package/dist/cli/commands/availability.js +83 -0
- package/dist/cli/commands/availability.js.map +1 -0
- package/dist/cli/commands/bookings.d.ts +12 -0
- package/dist/cli/commands/bookings.d.ts.map +1 -0
- package/dist/cli/commands/bookings.js +195 -0
- package/dist/cli/commands/bookings.js.map +1 -0
- package/dist/cli/commands/config.d.ts +7 -0
- package/dist/cli/commands/config.d.ts.map +1 -0
- package/dist/cli/commands/config.js +59 -0
- package/dist/cli/commands/config.js.map +1 -0
- package/dist/cli/commands/login.d.ts +11 -0
- package/dist/cli/commands/login.d.ts.map +1 -0
- package/dist/cli/commands/login.js +127 -0
- package/dist/cli/commands/login.js.map +1 -0
- package/dist/cli/commands/logout.d.ts +8 -0
- package/dist/cli/commands/logout.d.ts.map +1 -0
- package/dist/cli/commands/logout.js +38 -0
- package/dist/cli/commands/logout.js.map +1 -0
- package/dist/cli/commands/rooms.d.ts +22 -0
- package/dist/cli/commands/rooms.d.ts.map +1 -0
- package/dist/cli/commands/rooms.js +76 -0
- package/dist/cli/commands/rooms.js.map +1 -0
- package/dist/cli/commands/whoami.d.ts +6 -0
- package/dist/cli/commands/whoami.d.ts.map +1 -0
- package/dist/cli/commands/whoami.js +37 -0
- package/dist/cli/commands/whoami.js.map +1 -0
- package/dist/cli/index.d.ts +22 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +61 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/utils/output.d.ts +28 -0
- package/dist/cli/utils/output.d.ts.map +1 -0
- package/dist/cli/utils/output.js +40 -0
- package/dist/cli/utils/output.js.map +1 -0
- package/dist/cli/utils/require-key.d.ts +6 -0
- package/dist/cli/utils/require-key.d.ts.map +1 -0
- package/dist/cli/utils/require-key.js +18 -0
- package/dist/cli/utils/require-key.js.map +1 -0
- package/dist/utils/api-client.d.ts +35 -0
- package/dist/utils/api-client.d.ts.map +1 -0
- package/dist/utils/api-client.js +98 -0
- package/dist/utils/api-client.js.map +1 -0
- package/dist/utils/auth-flow-client.d.ts +60 -0
- package/dist/utils/auth-flow-client.d.ts.map +1 -0
- package/dist/utils/auth-flow-client.js +28 -0
- package/dist/utils/auth-flow-client.js.map +1 -0
- package/dist/utils/config.d.ts +95 -0
- package/dist/utils/config.d.ts.map +1 -0
- package/dist/utils/config.js +246 -0
- package/dist/utils/config.js.map +1 -0
- package/dist/utils/rooms-client.d.ts +70 -0
- package/dist/utils/rooms-client.d.ts.map +1 -0
- package/dist/utils/rooms-client.js +59 -0
- package/dist/utils/rooms-client.js.map +1 -0
- package/dist/utils/time.d.ts +37 -0
- package/dist/utils/time.d.ts.map +1 -0
- package/dist/utils/time.js +130 -0
- package/dist/utils/time.js.map +1 -0
- package/package.json +54 -0
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Time interpretation for the CLI (plan §8).
|
|
3
|
+
*
|
|
4
|
+
* A bare `--start "2026-06-09T10:00"` is read as **Europe/Brussels** wall-clock
|
|
5
|
+
* by default (matching the UI), converted to a UTC instant for the API. Rules:
|
|
6
|
+
*
|
|
7
|
+
* - A value that already carries a UTC `Z` or a numeric offset (`+02:00`,
|
|
8
|
+
* `-0500`) is an absolute instant — honored as-is.
|
|
9
|
+
* - A bare local value (`YYYY-MM-DDTHH:mm[:ss]`, or with a space) is interpreted
|
|
10
|
+
* in the given IANA zone (default Europe/Brussels, override with `--tz`),
|
|
11
|
+
* DST-safe, and converted to UTC.
|
|
12
|
+
*
|
|
13
|
+
* The CLI prints times back in the SAME zone it accepted, so what a member types
|
|
14
|
+
* and what they see never disagree. No external dependency: the zone offset is
|
|
15
|
+
* derived from `Intl.DateTimeFormat`, which is DST-correct.
|
|
16
|
+
*/
|
|
17
|
+
export const DEFAULT_TZ = 'Europe/Brussels';
|
|
18
|
+
/** True when a datetime string carries an explicit offset or `Z` (absolute). */
|
|
19
|
+
export function hasExplicitOffset(value) {
|
|
20
|
+
const trimmed = value.trim();
|
|
21
|
+
// Only the TIME portion (after the `T`/space) may carry an offset. A bare
|
|
22
|
+
// date like `2026-06-09` must NOT be mistaken for one (its `-09` is a month/day
|
|
23
|
+
// separator, not a `-09:00` offset).
|
|
24
|
+
const timePart = /[T ](\d{2}:\d{2}(?::\d{2})?)(.*)$/.exec(trimmed);
|
|
25
|
+
if (!timePart)
|
|
26
|
+
return false;
|
|
27
|
+
const suffix = timePart[2];
|
|
28
|
+
// `Z`, or `+HH`, `+HH:MM`, `+HHMM` (likewise `-`) immediately after the time.
|
|
29
|
+
return /^([zZ]|[+-]\d{2}(:?\d{2})?)$/.test(suffix);
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Compute the offset (in minutes, east-of-UTC positive) of an IANA time zone at
|
|
33
|
+
* a given UTC instant. Derived from the difference between the same instant
|
|
34
|
+
* formatted as the zone's local wall-clock and the UTC wall-clock — DST-safe.
|
|
35
|
+
*/
|
|
36
|
+
function zoneOffsetMinutes(instant, timeZone) {
|
|
37
|
+
const dtf = new Intl.DateTimeFormat('en-US', {
|
|
38
|
+
timeZone,
|
|
39
|
+
hour12: false,
|
|
40
|
+
year: 'numeric',
|
|
41
|
+
month: '2-digit',
|
|
42
|
+
day: '2-digit',
|
|
43
|
+
hour: '2-digit',
|
|
44
|
+
minute: '2-digit',
|
|
45
|
+
second: '2-digit'
|
|
46
|
+
});
|
|
47
|
+
const parts = dtf.formatToParts(instant);
|
|
48
|
+
const get = (type) => Number(parts.find((p) => p.type === type)?.value);
|
|
49
|
+
let hour = get('hour');
|
|
50
|
+
if (hour === 24)
|
|
51
|
+
hour = 0; // some engines emit 24 for midnight
|
|
52
|
+
// The wall-clock the zone shows for this instant, expressed as a UTC timestamp.
|
|
53
|
+
const asUTC = Date.UTC(get('year'), get('month') - 1, get('day'), hour, get('minute'), get('second'));
|
|
54
|
+
return Math.round((asUTC - instant.getTime()) / 60000);
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Parse the wall-clock fields out of a bare local datetime string.
|
|
58
|
+
* Accepts `YYYY-MM-DDTHH:mm`, `YYYY-MM-DDTHH:mm:ss`, or a space separator.
|
|
59
|
+
* Returns null when the shape is not a bare local datetime.
|
|
60
|
+
*/
|
|
61
|
+
function parseLocalParts(value) {
|
|
62
|
+
const m = /^(\d{4})-(\d{2})-(\d{2})[T ](\d{2}):(\d{2})(?::(\d{2}))?$/.exec(value.trim());
|
|
63
|
+
if (!m)
|
|
64
|
+
return null;
|
|
65
|
+
return {
|
|
66
|
+
y: Number(m[1]),
|
|
67
|
+
mo: Number(m[2]),
|
|
68
|
+
d: Number(m[3]),
|
|
69
|
+
h: Number(m[4]),
|
|
70
|
+
mi: Number(m[5]),
|
|
71
|
+
s: m[6] ? Number(m[6]) : 0
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Resolve a datetime argument to an absolute instant.
|
|
76
|
+
*
|
|
77
|
+
* @param value the raw `--start` / `--end` / `--from` / `--to` argument
|
|
78
|
+
* @param tz the IANA zone for bare local values (default Europe/Brussels)
|
|
79
|
+
*/
|
|
80
|
+
export function parseDateTimeArg(value, tz = DEFAULT_TZ) {
|
|
81
|
+
const trimmed = value.trim();
|
|
82
|
+
// 1) Explicit offset / Z → an absolute instant. Honor it as-is.
|
|
83
|
+
if (hasExplicitOffset(trimmed)) {
|
|
84
|
+
const date = new Date(trimmed);
|
|
85
|
+
if (Number.isNaN(date.getTime())) {
|
|
86
|
+
throw new Error(`Invalid datetime: "${value}"`);
|
|
87
|
+
}
|
|
88
|
+
return { date, zone: tz, interpretedLocal: false };
|
|
89
|
+
}
|
|
90
|
+
// 2) Bare local datetime → interpret in `tz`.
|
|
91
|
+
const parts = parseLocalParts(trimmed);
|
|
92
|
+
if (parts) {
|
|
93
|
+
// First guess: treat the wall-clock as if it were UTC, then correct by the
|
|
94
|
+
// zone's offset at that approximate instant (DST-safe to a one-pass refine).
|
|
95
|
+
const guessUTC = Date.UTC(parts.y, parts.mo - 1, parts.d, parts.h, parts.mi, parts.s);
|
|
96
|
+
const off1 = zoneOffsetMinutes(new Date(guessUTC), tz);
|
|
97
|
+
let utcMs = guessUTC - off1 * 60000;
|
|
98
|
+
// Refine once in case the first guess landed on the wrong side of a DST jump.
|
|
99
|
+
const off2 = zoneOffsetMinutes(new Date(utcMs), tz);
|
|
100
|
+
if (off2 !== off1)
|
|
101
|
+
utcMs = guessUTC - off2 * 60000;
|
|
102
|
+
return { date: new Date(utcMs), zone: tz, interpretedLocal: true };
|
|
103
|
+
}
|
|
104
|
+
// 3) Date-only (`YYYY-MM-DD`) → midnight in `tz`.
|
|
105
|
+
if (/^\d{4}-\d{2}-\d{2}$/.test(trimmed)) {
|
|
106
|
+
return parseDateTimeArg(`${trimmed}T00:00:00`, tz);
|
|
107
|
+
}
|
|
108
|
+
// 4) Last resort: let the JS engine try (e.g. RFC-ish strings).
|
|
109
|
+
const date = new Date(trimmed);
|
|
110
|
+
if (Number.isNaN(date.getTime())) {
|
|
111
|
+
throw new Error(`Invalid datetime: "${value}"`);
|
|
112
|
+
}
|
|
113
|
+
return { date, zone: tz, interpretedLocal: false };
|
|
114
|
+
}
|
|
115
|
+
/** Format an instant as a wall-clock string in the given zone (round-trip display). */
|
|
116
|
+
export function formatInZone(date, tz = DEFAULT_TZ) {
|
|
117
|
+
const dtf = new Intl.DateTimeFormat('en-GB', {
|
|
118
|
+
timeZone: tz,
|
|
119
|
+
hour12: false,
|
|
120
|
+
year: 'numeric',
|
|
121
|
+
month: '2-digit',
|
|
122
|
+
day: '2-digit',
|
|
123
|
+
hour: '2-digit',
|
|
124
|
+
minute: '2-digit'
|
|
125
|
+
});
|
|
126
|
+
const parts = dtf.formatToParts(date);
|
|
127
|
+
const get = (type) => parts.find((p) => p.type === type)?.value ?? '';
|
|
128
|
+
return `${get('year')}-${get('month')}-${get('day')} ${get('hour')}:${get('minute')} (${tz})`;
|
|
129
|
+
}
|
|
130
|
+
//# sourceMappingURL=time.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"time.js","sourceRoot":"","sources":["../../src/utils/time.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@wat-toolbox/wat",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "wat — book WAT meeting rooms from the terminal. Email-OTP login, API-key auth, dev/prod targeting.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"wat": "./dist/cli/index.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist"
|
|
11
|
+
],
|
|
12
|
+
"scripts": {
|
|
13
|
+
"build": "tsc",
|
|
14
|
+
"test": "vitest run",
|
|
15
|
+
"test:watch": "vitest",
|
|
16
|
+
"prepublishOnly": "npm run build && npm test"
|
|
17
|
+
},
|
|
18
|
+
"keywords": [
|
|
19
|
+
"cli",
|
|
20
|
+
"wat",
|
|
21
|
+
"rooms",
|
|
22
|
+
"booking",
|
|
23
|
+
"codika",
|
|
24
|
+
"otp"
|
|
25
|
+
],
|
|
26
|
+
"author": {
|
|
27
|
+
"name": "Codika",
|
|
28
|
+
"email": "team@codika.io",
|
|
29
|
+
"url": "https://codika.io"
|
|
30
|
+
},
|
|
31
|
+
"license": "MIT",
|
|
32
|
+
"repository": {
|
|
33
|
+
"type": "git",
|
|
34
|
+
"url": "git+https://github.com/WATbeta/wat-cli.git"
|
|
35
|
+
},
|
|
36
|
+
"bugs": {
|
|
37
|
+
"url": "https://github.com/WATbeta/wat-cli/issues"
|
|
38
|
+
},
|
|
39
|
+
"homepage": "https://github.com/WATbeta/wat-cli#readme",
|
|
40
|
+
"publishConfig": {
|
|
41
|
+
"access": "public"
|
|
42
|
+
},
|
|
43
|
+
"dependencies": {
|
|
44
|
+
"commander": "^12.0.0"
|
|
45
|
+
},
|
|
46
|
+
"devDependencies": {
|
|
47
|
+
"@types/node": "^22.0.0",
|
|
48
|
+
"typescript": "^5.4.0",
|
|
49
|
+
"vitest": "^3.0.0"
|
|
50
|
+
},
|
|
51
|
+
"engines": {
|
|
52
|
+
"node": ">=20.0.0"
|
|
53
|
+
}
|
|
54
|
+
}
|