@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;AA2BF,wBAAmD;AA/InD,iDAA4D;AAC5D,6CAAmD;AAUnD,4DAsBC"}
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;AAWM;;;;;;;EA2CN"}
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@justair/justair-library",
3
- "version": "6.14.0-alpha.e751b16",
3
+ "version": "6.14.0",
4
4
  "description": "JustAir Internal Library",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
@@ -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();
@@ -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 → defaults, no warning (the normal case)
155
- // - invalid timezone → DEFAULT_TIMEZONE + warning
156
- // - half-set or malformed pair → DEFAULT_QUIET_HOURS + warning
157
- // - stored equal pair → window: null (no suppression) + warning
158
- // (JA-3020 D11 — "always quiet" must never arise from corrupt data)
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 (quietHoursStart === quietHoursEnd) {
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',