@justair/justair-library 7.6.0 → 7.7.0-alpha.57a9fb8

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/SECURITY.md ADDED
@@ -0,0 +1,21 @@
1
+ # Security Policy
2
+
3
+ ## Supported versions
4
+
5
+ Security fixes are prioritized for the current default branch and actively supported releases of `JustAir-Solutions/justair-library`. Older or retired versions may not receive fixes.
6
+
7
+ ## Reporting a vulnerability
8
+
9
+ Do not open a public GitHub issue for a suspected vulnerability.
10
+
11
+ Send the initial report to `info@justair.co` with the subject `Security vulnerability: justair-library`. Include the affected repository or component, the potential impact, minimal reproduction steps, and a way to contact you.
12
+
13
+ Do not include passwords, tokens, private keys, personal data, regulated data, or other sensitive payloads in the initial message. Ask for an approved secure transfer method when sensitive evidence is necessary.
14
+
15
+ ## Testing boundaries
16
+
17
+ This policy does not grant authorization to access data that is not yours, disrupt service, perform denial-of-service testing, use social engineering, destroy data, or establish persistence. Stop testing and report the issue if sensitive data or credentials are encountered.
18
+
19
+ ## Coordinated disclosure
20
+
21
+ Please allow JustAir reasonable time to investigate and remediate before public disclosure. Receipt of a report does not create a promise of a specific response or remediation timeline.
@@ -4,4 +4,5 @@ export * from "./userNotifications.js";
4
4
  export * from "./deviceTypes.js";
5
5
  export * from "./timezones.js";
6
6
  export * from "./dataDownloads.js";
7
+ export * from "./terms.js";
7
8
  //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,2 @@
1
+ export const TERMS_VERSION_MAX_LENGTH: 32;
2
+ //# sourceMappingURL=terms.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"terms.d.ts","sourceRoot":"","sources":["../../src/constants/terms.js"],"names":[],"mappings":"AAaA,0CAA2C"}
package/dist/index.d.ts CHANGED
@@ -99,6 +99,7 @@ import { validateDownloadEmail as validateDataDownloadEmail } from "./utils/data
99
99
  import { normalizeDownloadEmail as normalizeDataDownloadEmail } from "./utils/dataDownloadValidation.js";
100
100
  import { validateDownloadTimezone as validateDataDownloadTimezone } from "./utils/dataDownloadValidation.js";
101
101
  import { normalizeDownloadTimezone as normalizeDataDownloadTimezone } from "./utils/dataDownloadValidation.js";
102
+ import { TERMS_VERSION_MAX_LENGTH } from "./constants/terms.js";
102
103
  import { PARAMETERS } from "./constants/pollutants.js";
103
104
  import { HEAVY_METALS } from "./constants/pollutants.js";
104
105
  import { ALERT_LEVEL_WEIGHTS } from "./constants/alertLevels.js";
@@ -121,5 +122,5 @@ import { validateQuietHours } from "./utils/quietHours.js";
121
122
  import { getZoneMinutes } from "./utils/quietHours.js";
122
123
  import { isInQuietWindow } from "./utils/quietHours.js";
123
124
  import { resolveQuietHours } from "./utils/quietHours.js";
124
- export { Database, adminSchema, Admin, configurationsSchema, Configurations, measurementsSchema, Measurements, monitorRequestsSchema, MonitorRequests, monitorsSchema, Monitors, organizationsSchema, Organizations, usersSchema, Users, COOKIE_CONSENT_CATEGORIES, COOKIE_CONSENT_VALUES, validateCookieConsent, eventsSchema, Events, lightMonitorSchema, LightMonitors, monitorSuppliersSchema, MonitorSuppliers, contextsSchema, Contexts, parametersSchema, Parameters, announcementSchema, Announcements, jobsSchema, Jobs, apiKeySchema, ApiKey, UsageMetrics, usageMetricsSchema, Audit, auditSchema, EventsAudit, eventsAuditSchema, MonitorAudit, monitorAuditSchema, parametersEnum, deploymentTypesEnum, AlertsAudit, Alerts, alertsSchema, alertsAuditSchema, Features, featuresSchema, dataCompletenessSchema, DataCompleteness, networkMetricsSchema, NetworkMetrics, rateOfChangeSchema, RateOfChange, changeStreamCheckpointsSchema, ChangeStreamCheckpoints, sitesSchema, Sites, sampleSitesSchema, SampleSites, sampleSiteAuditSchema, SampleSiteAudit, sampleParametersEnum, samplesSchema, Samples, samplesAuditSchema, SamplesAudit, sampleParameterReferenceConcentrations, passwordResetTokenSchema, PasswordResetToken, monitorEpisodesSchema, MonitorEpisode, userNotificationsSchema, UserNotification, AqiAlertNotification, DeviceOfflineNotification, WeeklyDigestNotification, AccountNotification, NOTIFICATION_KINDS, NOTIFICATION_CHANNELS, DELIVERY_STATUSES, DATA_DOWNLOAD_TYPES, dataDownloadSchema, DataDownloads, AqiDataDownload, SampleDataDownload, DATA_DOWNLOAD_MAX_RANGE_DAYS, DATA_DOWNLOAD_ERRORS, validateDataDownloadDateOrder, validateDataDownloadMaxDateRange, validateDataDownloadEmail, normalizeDataDownloadEmail, validateDataDownloadTimezone, normalizeDataDownloadTimezone, PARAMETERS, HEAVY_METALS, ALERT_LEVEL_WEIGHTS, getAlertWeight, DEVICE_TYPES, DEVICE_TYPE_VALUES, isAqiCapableDeviceType, AQI_CAPABLE_DEVICE_TYPES, projectSubscribedMonitors, resolveSubscribedMonitorIdsByUser, VISIBLE_SITE_MONITOR_FILTER, SUBSCRIBED_DEVICES_ERRORS, splitName, DEFAULT_QUIET_HOURS, DEFAULT_TIMEZONE, QUIET_HOURS_ERRORS, isValidTimeString, isValidIANATimezone, validateQuietHours, getZoneMinutes, isInQuietWindow, resolveQuietHours };
125
+ export { Database, adminSchema, Admin, configurationsSchema, Configurations, measurementsSchema, Measurements, monitorRequestsSchema, MonitorRequests, monitorsSchema, Monitors, organizationsSchema, Organizations, usersSchema, Users, COOKIE_CONSENT_CATEGORIES, COOKIE_CONSENT_VALUES, validateCookieConsent, eventsSchema, Events, lightMonitorSchema, LightMonitors, monitorSuppliersSchema, MonitorSuppliers, contextsSchema, Contexts, parametersSchema, Parameters, announcementSchema, Announcements, jobsSchema, Jobs, apiKeySchema, ApiKey, UsageMetrics, usageMetricsSchema, Audit, auditSchema, EventsAudit, eventsAuditSchema, MonitorAudit, monitorAuditSchema, parametersEnum, deploymentTypesEnum, AlertsAudit, Alerts, alertsSchema, alertsAuditSchema, Features, featuresSchema, dataCompletenessSchema, DataCompleteness, networkMetricsSchema, NetworkMetrics, rateOfChangeSchema, RateOfChange, changeStreamCheckpointsSchema, ChangeStreamCheckpoints, sitesSchema, Sites, sampleSitesSchema, SampleSites, sampleSiteAuditSchema, SampleSiteAudit, sampleParametersEnum, samplesSchema, Samples, samplesAuditSchema, SamplesAudit, sampleParameterReferenceConcentrations, passwordResetTokenSchema, PasswordResetToken, monitorEpisodesSchema, MonitorEpisode, userNotificationsSchema, UserNotification, AqiAlertNotification, DeviceOfflineNotification, WeeklyDigestNotification, AccountNotification, NOTIFICATION_KINDS, NOTIFICATION_CHANNELS, DELIVERY_STATUSES, DATA_DOWNLOAD_TYPES, dataDownloadSchema, DataDownloads, AqiDataDownload, SampleDataDownload, DATA_DOWNLOAD_MAX_RANGE_DAYS, DATA_DOWNLOAD_ERRORS, validateDataDownloadDateOrder, validateDataDownloadMaxDateRange, validateDataDownloadEmail, normalizeDataDownloadEmail, validateDataDownloadTimezone, normalizeDataDownloadTimezone, TERMS_VERSION_MAX_LENGTH, PARAMETERS, HEAVY_METALS, ALERT_LEVEL_WEIGHTS, getAlertWeight, DEVICE_TYPES, DEVICE_TYPE_VALUES, isAqiCapableDeviceType, AQI_CAPABLE_DEVICE_TYPES, projectSubscribedMonitors, resolveSubscribedMonitorIdsByUser, VISIBLE_SITE_MONITOR_FILTER, SUBSCRIBED_DEVICES_ERRORS, splitName, DEFAULT_QUIET_HOURS, DEFAULT_TIMEZONE, QUIET_HOURS_ERRORS, isValidTimeString, isValidIANATimezone, validateQuietHours, getZoneMinutes, isInQuietWindow, resolveQuietHours };
125
126
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.js"],"names":[],"mappings":"AAyKA;;;iBAEC;yBAtGwB,oBAAoB;qBADxB,gBAAgB;4BApEF,mBAAmB;sBAAnB,mBAAmB;qCAI/C,4BAA4B;+BAA5B,4BAA4B;mCAM5B,0BAA0B;6BAA1B,0BAA0B;sCAI1B,6BAA6B;gCAA7B,6BAA6B;+BAQ7B,sBAAsB;yBAAtB,sBAAsB;oCACsB,2BAA2B;8BAA3B,2BAA2B;4BAcvE,mBAAmB;sBAAnB,mBAAmB;0CAAnB,mBAAmB;sCAAnB,mBAAmB;sCAAnB,mBAAmB;6BAMnB,oBAAoB;uBAApB,oBAAoB;mCAnBuB,2BAA2B;8BAA3B,2BAA2B;uCAItE,8BAA8B;iCAA9B,8BAA8B;+BACI,sBAAsB;yBAAtB,sBAAsB;iCAClB,wBAAwB;2BAAxB,wBAAwB;mCAcnB,2BAA2B;8BAA3B,2BAA2B;2BAC5C,kBAAkB;qBAAlB,kBAAkB;6BACd,oBAAoB;uBAApB,oBAAoB;6BACR,0BAA0B;mCAA1B,0BAA0B;sBArCpE,0BAA0B;4BAA1B,0BAA0B;4BAiC1B,oBAAoB;kCAApB,oBAAoB;6BArBpB,sBAAsB;mCAAtB,sBAAsB;+BAAtB,sBAAsB;oCAAtB,sBAAsB;4BA+BtB,oBAAoB;uBAApB,oBAAoB;6BAApB,oBAAoB;kCAApB,oBAAoB;yBACc,sBAAsB;+BAAtB,sBAAsB;uCAIxD,8BAA8B;iCAA9B,8BAA8B;qCAI9B,4BAA4B;+BAA5B,4BAA4B;mCACc,0BAA0B;6BAA1B,0BAA0B;8CAIpE,qCAAqC;wCAArC,qCAAqC;4BAkDT,mBAAmB;sBAAnB,mBAAmB;kCAS/C,yBAAyB;4BAAzB,yBAAyB;sCAAzB,yBAAyB;gCAAzB,yBAAyB;qCAAzB,yBAAyB;8BASzB,qBAAqB;wBAArB,qBAAqB;mCAArB,qBAAqB;6BAArB,qBAAqB;uDAArB,qBAAqB;yCAIrB,gCAAgC;mCAAhC,gCAAgC;sCArDhC,6BAA6B;+BAA7B,6BAA6B;wCAW7B,+BAA+B;iCAA/B,+BAA+B;qCAA/B,+BAA+B;0CAA/B,+BAA+B;yCAA/B,+BAA+B;oCAA/B,+BAA+B;mCAA/B,+BAA+B;sCAA/B,+BAA+B;kCAA/B,+BAA+B;oCArBF,8BAA8B;mCAqE3D,2BAA2B;8BAA3B,2BAA2B;gCAA3B,2BAA2B;mCAA3B,2BAA2B;+DAsB3B,mCAAmC;qCAAnC,mCAAmC;mEAAnC,mCAAmC;yEAAnC,mCAAmC;mEAAnC,mCAAmC;qEAAnC,mCAAmC;yEAAnC,mCAAmC;2EAAnC,mCAAmC;2BAjGD,2BAA2B;6BAA3B,2BAA2B;oCAI7D,4BAA4B;+BAA5B,4BAA4B;6BAQ5B,4BAA4B;mCAA5B,4BAA4B;uCAA5B,4BAA4B;yCAA5B,4BAA4B;0CAgC5B,8BAA8B;kDAA9B,8BAA8B;4CAA9B,8BAA8B;0CAA9B,8BAA8B;0BAhBX,sBAAsB;oCASzC,uBAAuB;iCAhCG,0BAA0B;mCAgCpD,uBAAuB;kCAAvB,uBAAuB;oCACM,qBAAqB;mCADlD,uBAAuB;+BAAvB,uBAAuB;gCAAvB,uBAAuB;kCAAvB,uBAAuB"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.js"],"names":[],"mappings":"AA0KA;;;iBAEC;yBAvGwB,oBAAoB;qBADxB,gBAAgB;4BApEF,mBAAmB;sBAAnB,mBAAmB;qCAI/C,4BAA4B;+BAA5B,4BAA4B;mCAM5B,0BAA0B;6BAA1B,0BAA0B;sCAI1B,6BAA6B;gCAA7B,6BAA6B;+BAQ7B,sBAAsB;yBAAtB,sBAAsB;oCACsB,2BAA2B;8BAA3B,2BAA2B;4BAcvE,mBAAmB;sBAAnB,mBAAmB;0CAAnB,mBAAmB;sCAAnB,mBAAmB;sCAAnB,mBAAmB;6BAMnB,oBAAoB;uBAApB,oBAAoB;mCAnBuB,2BAA2B;8BAA3B,2BAA2B;uCAItE,8BAA8B;iCAA9B,8BAA8B;+BACI,sBAAsB;yBAAtB,sBAAsB;iCAClB,wBAAwB;2BAAxB,wBAAwB;mCAcnB,2BAA2B;8BAA3B,2BAA2B;2BAC5C,kBAAkB;qBAAlB,kBAAkB;6BACd,oBAAoB;uBAApB,oBAAoB;6BACR,0BAA0B;mCAA1B,0BAA0B;sBArCpE,0BAA0B;4BAA1B,0BAA0B;4BAiC1B,oBAAoB;kCAApB,oBAAoB;6BArBpB,sBAAsB;mCAAtB,sBAAsB;+BAAtB,sBAAsB;oCAAtB,sBAAsB;4BA+BtB,oBAAoB;uBAApB,oBAAoB;6BAApB,oBAAoB;kCAApB,oBAAoB;yBACc,sBAAsB;+BAAtB,sBAAsB;uCAIxD,8BAA8B;iCAA9B,8BAA8B;qCAI9B,4BAA4B;+BAA5B,4BAA4B;mCACc,0BAA0B;6BAA1B,0BAA0B;8CAIpE,qCAAqC;wCAArC,qCAAqC;4BAmDT,mBAAmB;sBAAnB,mBAAmB;kCAS/C,yBAAyB;4BAAzB,yBAAyB;sCAAzB,yBAAyB;gCAAzB,yBAAyB;qCAAzB,yBAAyB;8BASzB,qBAAqB;wBAArB,qBAAqB;mCAArB,qBAAqB;6BAArB,qBAAqB;uDAArB,qBAAqB;yCAIrB,gCAAgC;mCAAhC,gCAAgC;sCArDhC,6BAA6B;+BAA7B,6BAA6B;wCAW7B,+BAA+B;iCAA/B,+BAA+B;qCAA/B,+BAA+B;0CAA/B,+BAA+B;yCAA/B,+BAA+B;oCAA/B,+BAA+B;mCAA/B,+BAA+B;sCAA/B,+BAA+B;kCAA/B,+BAA+B;oCAtBF,8BAA8B;mCAsE3D,2BAA2B;8BAA3B,2BAA2B;gCAA3B,2BAA2B;mCAA3B,2BAA2B;+DAsB3B,mCAAmC;qCAAnC,mCAAmC;mEAAnC,mCAAmC;yEAAnC,mCAAmC;mEAAnC,mCAAmC;qEAAnC,mCAAmC;yEAAnC,mCAAmC;2EAAnC,mCAAmC;yCA3FD,sBAAsB;2BAPtB,2BAA2B;6BAA3B,2BAA2B;oCAI7D,4BAA4B;+BAA5B,4BAA4B;6BAS5B,4BAA4B;mCAA5B,4BAA4B;uCAA5B,4BAA4B;yCAA5B,4BAA4B;0CAgC5B,8BAA8B;kDAA9B,8BAA8B;4CAA9B,8BAA8B;0CAA9B,8BAA8B;0BAhBX,sBAAsB;oCASzC,uBAAuB;iCAjCG,0BAA0B;mCAiCpD,uBAAuB;kCAAvB,uBAAuB;oCACM,qBAAqB;mCADlD,uBAAuB;+BAAvB,uBAAuB;gCAAvB,uBAAuB;kCAAvB,uBAAuB"}
@@ -1 +1 @@
1
- {"version":3,"file":"users.d.ts","sourceRoot":"","sources":["../../src/models/users.js"],"names":[],"mappings":"AA6FA,8BA0GE;AAiCF,wBAAmD;AA/NnD,iDAA4D;AAC5D,6CAAmD;AAUnD,4DA0BC"}
1
+ {"version":3,"file":"users.d.ts","sourceRoot":"","sources":["../../src/models/users.js"],"names":[],"mappings":"AA8FA,8BA2HE;AAiCF,wBAAmD;AAhPnD,iDAA4D;AAC5D,6CAAmD;AAUnD,4DA0BC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@justair/justair-library",
3
- "version": "7.6.0",
3
+ "version": "7.7.0-alpha.57a9fb8",
4
4
  "description": "JustAir Internal Library",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
@@ -13,3 +13,4 @@ export * from "./userNotifications.js";
13
13
  export * from "./deviceTypes.js";
14
14
  export * from "./timezones.js";
15
15
  export * from "./dataDownloads.js";
16
+ export * from "./terms.js";
@@ -0,0 +1,14 @@
1
+ // Terms-of-service agreement vocabulary.
2
+ //
3
+ // Users.termsVersion stores the policy-document version a public user agreed
4
+ // to at registration (e.g. "2026.1" — the published doc's tab name). The
5
+ // VALUE is owned by the frontend signup flow; nothing in the
6
+ // backend hardcodes a current version, so there is deliberately no
7
+ // TERMS_CURRENT_VERSION constant here.
8
+ //
9
+ // The cap below is the one number shared by the two enforcement points in
10
+ // different repos: the Users schema (`maxlength`) and JustAirUserService's
11
+ // registration route validator. Import it from either barrel rather than
12
+ // inlining the literal — if the two drift, the route accepts what the schema
13
+ // rejects and a signup 500s instead of 400ing.
14
+ export const TERMS_VERSION_MAX_LENGTH = 32;
@@ -8,6 +8,7 @@ import { PARAMETERS, HEAVY_METALS } from '../pollutants.js';
8
8
  import { ALERT_LEVEL_WEIGHTS, getAlertWeight } from '../alertLevels.js';
9
9
  import { DEFAULT_TIMEZONE } from '../timezones.js';
10
10
  import { DATA_DOWNLOAD_TYPES } from '../dataDownloads.js';
11
+ import { TERMS_VERSION_MAX_LENGTH } from '../terms.js';
11
12
 
12
13
  test('barrel re-exports the pollutants constants unchanged (identity, not copies)', () => {
13
14
  expect(barrel.PARAMETERS).toBe(PARAMETERS);
@@ -56,6 +57,11 @@ test('barrel exports DATA_DOWNLOAD_TYPES (browser-safe, no Mongoose)', () => {
56
57
  expect(barrel.DATA_DOWNLOAD_TYPES).toBe(DATA_DOWNLOAD_TYPES);
57
58
  });
58
59
 
60
+ test('barrel exports TERMS_VERSION_MAX_LENGTH (browser-safe, no Mongoose)', () => {
61
+ expect(barrel.TERMS_VERSION_MAX_LENGTH).toBe(TERMS_VERSION_MAX_LENGTH);
62
+ expect(TERMS_VERSION_MAX_LENGTH).toBe(32);
63
+ });
64
+
59
65
  // models/dataDownloads.js destructures this list positionally to build the two
60
66
  // discriminators, so a reorder would silently swap the persisted dataType
61
67
  // values on every future download. Pin contents AND order.
package/src/index.js CHANGED
@@ -75,6 +75,7 @@ import {
75
75
  } from "./constants/alertLevels.js";
76
76
  import { DEFAULT_TIMEZONE } from "./constants/timezones.js";
77
77
  import { DATA_DOWNLOAD_TYPES } from "./constants/dataDownloads.js";
78
+ import { TERMS_VERSION_MAX_LENGTH } from "./constants/terms.js";
78
79
  import {
79
80
  DEVICE_TYPES,
80
81
  DEVICE_TYPE_VALUES,
@@ -274,6 +275,9 @@ export {
274
275
  normalizeDataDownloadEmail,
275
276
  validateDataDownloadTimezone,
276
277
  normalizeDataDownloadTimezone,
278
+ // Terms agreement — cap shared by the Users schema and JustAirUserService's
279
+ // registration route validator
280
+ TERMS_VERSION_MAX_LENGTH,
277
281
  // Pollutant constants
278
282
  PARAMETERS,
279
283
  HEAVY_METALS,
@@ -6,6 +6,7 @@ import {
6
6
  QUIET_HOURS_ERRORS,
7
7
  } from "../utils/quietHours.js";
8
8
  import { DEVICE_TYPE_VALUES } from "../constants/deviceTypes.js";
9
+ import { TERMS_VERSION_MAX_LENGTH } from "../constants/terms.js";
9
10
 
10
11
  const COOKIE_CONSENT_CATEGORIES = ["analytics_performance"];
11
12
  const COOKIE_CONSENT_VALUES = ["accept", "reject"];
@@ -193,6 +194,23 @@ const usersSchema = mongoose.Schema(
193
194
  message: "timezone must be a valid IANA timezone identifier",
194
195
  },
195
196
  },
197
+ // Terms agreement: the policy-document version the user agreed to at
198
+ // registration (e.g. "2026.1"). The client supplies the value (the signup
199
+ // FE owns it); the backend never hardcodes a
200
+ // current version. Written once at account creation — `immutable` means
201
+ // save() mutations are ignored and strict-mode query updates are
202
+ // stripped, silently by design; treat any need to change it as a product
203
+ // decision, not a bug. Absent = registered before terms tracking existed
204
+ // (or a legacy client that sent only { phone }); absence is legal
205
+ // forever. Deliberately NO acceptance boolean (an account cannot be
206
+ // created without pressing "I Agree", so it would be true on every
207
+ // record) and NO separate acceptance timestamp — createdAt IS the
208
+ // agreement date (grooming decision, 2026-09-03).
209
+ termsVersion: {
210
+ type: String,
211
+ immutable: true,
212
+ maxlength: TERMS_VERSION_MAX_LENGTH,
213
+ },
196
214
  },
197
215
  {
198
216
  timestamps: true,