@justair/justair-library 6.14.0-alpha.e751b16 → 6.14.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.
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"users.d.ts","sourceRoot":"","sources":["../../src/models/users.js"],"names":[],"mappings":"AA2CA,8BAiFE;
|
|
1
|
+
{"version":3,"file":"users.d.ts","sourceRoot":"","sources":["../../src/models/users.js"],"names":[],"mappings":"AA2CA,8BAiFE;AA+BF,wBAAmD;AAnJnD,iDAA4D;AAC5D,6CAAmD;AAUnD,4DAsBC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"quietHours.d.ts","sourceRoot":"","sources":["../../src/utils/quietHours.js"],"names":[],"mappings":"AAkBA;;;GAGG;AAEH,kDAAmD;AAKnD;;;;GAIG;AAuBI,uDACgD;AAEhD,4DAUN;AAOM;;;;;;;;;KAsCN;AASM,iEASN;AAKM,uEAaN;
|
|
1
|
+
{"version":3,"file":"quietHours.d.ts","sourceRoot":"","sources":["../../src/utils/quietHours.js"],"names":[],"mappings":"AAkBA;;;GAGG;AAEH,kDAAmD;AAKnD;;;;GAIG;AAuBI,uDACgD;AAEhD,4DAUN;AAOM;;;;;;;;;KAsCN;AASM,iEASN;AAKM,uEAaN;AAeM;;;;;;;EA6CN"}
|
package/package.json
CHANGED
package/src/models/users.js
CHANGED
|
@@ -141,7 +141,11 @@ usersSchema.pre("validate", function (next) {
|
|
|
141
141
|
});
|
|
142
142
|
for (const error of errors) {
|
|
143
143
|
if (CROSS_FIELD_QUIET_HOURS_CODES.has(error.code)) {
|
|
144
|
+
// The pair is the unit of a cross-field rule: attach to BOTH paths so
|
|
145
|
+
// consumers surfacing per-field ValidationError entries highlight both
|
|
146
|
+
// inputs, whichever side was set/unset.
|
|
144
147
|
this.invalidate("quietHoursStart", error.message);
|
|
148
|
+
this.invalidate("quietHoursEnd", error.message);
|
|
145
149
|
}
|
|
146
150
|
}
|
|
147
151
|
next();
|
package/src/utils/quietHours.js
CHANGED
|
@@ -151,11 +151,15 @@ export const isInQuietWindow = (minutesNow, window) => {
|
|
|
151
151
|
|
|
152
152
|
// Resolves a user's stored fields into an evaluable { window, timezone },
|
|
153
153
|
// never throwing on corrupt data (JA-3020 D16). Fallback semantics:
|
|
154
|
-
// - fields null/undefined
|
|
155
|
-
// - invalid timezone
|
|
156
|
-
// - half-set or malformed pair
|
|
157
|
-
//
|
|
158
|
-
//
|
|
154
|
+
// - fields null/undefined → defaults, no warning (the normal case)
|
|
155
|
+
// - invalid timezone → DEFAULT_TIMEZONE + warning
|
|
156
|
+
// - half-set or malformed pair → DEFAULT_QUIET_HOURS + warning (corrupt
|
|
157
|
+
// data keeps the protective default; a malformed-AND-identical pair like
|
|
158
|
+
// "25:00"/"25:00" is classified FORMAT by the validator and lands here)
|
|
159
|
+
// - well-formed equal pair → window: null (no suppression) + warning
|
|
160
|
+
// (JA-3020 D11 — ambiguous intent must never become "always quiet")
|
|
161
|
+
// Branching consumes the validator's stable error codes — never re-derives
|
|
162
|
+
// the classification from the raw values.
|
|
159
163
|
// Callers log `warnings` (once per run, not per user) and evaluate with
|
|
160
164
|
// isInQuietWindow(getZoneMinutes(now, timezone), window).
|
|
161
165
|
export const resolveQuietHours = (user) => {
|
|
@@ -184,7 +188,9 @@ export const resolveQuietHours = (user) => {
|
|
|
184
188
|
});
|
|
185
189
|
if (errors.length === 0) {
|
|
186
190
|
window = { start: quietHoursStart, end: quietHoursEnd };
|
|
187
|
-
} else if (
|
|
191
|
+
} else if (
|
|
192
|
+
errors.some((e) => e.code === QUIET_HOURS_ERRORS.EQUAL_PAIR)
|
|
193
|
+
) {
|
|
188
194
|
window = null;
|
|
189
195
|
warnings.push(
|
|
190
196
|
`stored quiet hours are an equal pair ("${quietHoursStart}") — treating as no suppression`
|
|
@@ -327,6 +327,19 @@ describe('resolveQuietHours', () => {
|
|
|
327
327
|
expect(isInQuietWindow(0, resolved.window)).toBe(false);
|
|
328
328
|
});
|
|
329
329
|
|
|
330
|
+
test('malformed-AND-identical pair is corrupt data, not an equal pair — default window + invalid warning', () => {
|
|
331
|
+
const resolved = resolveQuietHours({
|
|
332
|
+
quietHoursStart: '25:00',
|
|
333
|
+
quietHoursEnd: '25:00',
|
|
334
|
+
});
|
|
335
|
+
// FORMAT classification wins: the user keeps the protective default
|
|
336
|
+
// window instead of losing suppression entirely.
|
|
337
|
+
expect(resolved.window).toEqual(DEFAULT_QUIET_HOURS);
|
|
338
|
+
expect(resolved.warnings).toHaveLength(1);
|
|
339
|
+
expect(resolved.warnings[0]).toMatch(/invalid/);
|
|
340
|
+
expect(resolved.warnings[0]).not.toMatch(/equal pair/);
|
|
341
|
+
});
|
|
342
|
+
|
|
330
343
|
test('corrupt window and corrupt timezone warn independently', () => {
|
|
331
344
|
const resolved = resolveQuietHours({
|
|
332
345
|
quietHoursStart: 'bad',
|