UNPKG

@syngrisi/syngrisi

Version:
2,295 lines 54.3 kB
// src/server/models/Baseline.model.ts
import mongoose15, { Schema as Schema14 } from "mongoose";

// src/server/models/plugins/paginate.plugin.ts
var paginate = (schema) => {
  schema.statics.paginate = async function(filter, options) {
    let sort;
    if (options.sortBy) {
      const sortingCriteria = [];
      let primaryOrder = "desc";
      options.sortBy.split(",").forEach((sortOption, index) => {
        const [key, order] = sortOption.split(":");
        if (index === 0) primaryOrder = order || "asc";
        sortingCriteria.push((order === "desc" ? "-" : "") + key);
      });
      if (!sortingCriteria.some((s) => s === "_id" || s === "-_id")) {
        sortingCriteria.push((primaryOrder === "desc" ? "-" : "") + "_id");
      }
      sort = sortingCriteria.join(" ");
    } else {
      sort = { _id: -1 };
    }
    const limit = options.limit && parseInt(options.limit.toString(), 10) >= 0 ? parseInt(options.limit.toString(), 10) : 10;
    const page = options.page && parseInt(options.page.toString(), 10) > 0 ? parseInt(options.page.toString(), 10) : 1;
    const skip = (page - 1) * limit;
    const countStrategy = options.countStrategy ?? (filter && Object.keys(filter).length === 0 ? "estimated" : "exact");
    const countPromise = countStrategy === "estimated" ? this.estimatedDocumentCount().exec() : this.countDocuments(filter).exec();
    let docsPromise = this.find(filter).sort(sort).skip(skip).limit(limit);
    if (options.populate) {
      options.populate.split(",").forEach((populateOption) => {
        docsPromise = docsPromise.populate(
          populateOption.split(".").reverse().reduce((a, b) => ({ path: b, populate: a }))
        );
      });
    }
    docsPromise = docsPromise.exec();
    return Promise.all([countPromise, docsPromise]).then((values) => {
      const [totalResults, results] = values;
      const totalPages = Math.ceil(totalResults / limit);
      const result = {
        results,
        page,
        limit,
        totalPages,
        totalResults,
        timestamp: Number(Date.now() + String(process.hrtime()[1]).slice(3, 6))
      };
      return Promise.resolve(result);
    });
  };
};
var paginate_plugin_default = paginate;

// src/server/models/plugins/toJSON.plugin.ts
var deleteAtPath = (obj, path2, index) => {
  if (index === path2.length - 1) {
    delete obj[path2[index]];
    return;
  }
  deleteAtPath(obj[path2[index]], path2, index + 1);
};
var toJSON = (schema) => {
  let transform;
  if (schema.options.toJSON && schema.options.toJSON.transform) {
    transform = schema.options.toJSON.transform;
  }
  schema.options.toJSON = Object.assign(schema.options.toJSON || {}, {
    transform(doc, ret, options) {
      Object.keys(schema.paths).forEach((path2) => {
        if (schema.paths[path2].options && schema.paths[path2].options.private) {
          deleteAtPath(ret, path2.split("."), 0);
        }
      });
      ret.id = ret._id.toString();
      delete ret.__v;
      delete ret.createdAt;
      delete ret.updatedAt;
      if (transform) {
        return transform(doc, ret, options);
      }
    }
  });
};
var toJSON_plugin_default = toJSON;

// src/server/utils/isJSON.ts
var isJSON = (text) => {
  if (!text) return false;
  const isValid = /^[\],:{}\s]*$/.test(
    text.replace(/\\["\\\/bfnrtu]/g, "@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, "]").replace(/(?:^|:|,)(?:\s*\[)+/g, "")
  );
  return isValid;
};
var isJSON_default = isJSON;

// src/server/utils/ApiError.ts
var ApiError = class extends Error {
  statusCode;
  isOperational;
  constructor(statusCode, message, isOperational = true, stack = "") {
    super(message);
    this.statusCode = statusCode;
    this.isOperational = isOperational;
    if (stack) {
      this.stack = stack;
    } else {
      Error.captureStackTrace(this, this.constructor);
    }
  }
};
var ApiError_default = ApiError;

// src/server/utils/deserializeIfJSON.ts
import mongoose from "mongoose";
var { EJSON } = mongoose.mongo.BSON;
var EXTENDED_JSON_KEYS = [
  "$oid",
  "$date",
  "$numberInt",
  "$numberLong",
  "$numberDouble",
  "$numberDecimal",
  "$regularExpression",
  "$binary",
  "$timestamp"
];
var containsExtendedJsonMarkers = (text) => EXTENDED_JSON_KEYS.some((marker) => text.includes(`"${marker}"`));
var deserializeIfJSON = (text) => {
  if (isJSON_default(text)) {
    if (containsExtendedJsonMarkers(text)) {
      return EJSON.parse(text) || void 0;
    }
    return JSON.parse(text) || void 0;
  }
  return text;
};
var deserializeIfJSON_default = deserializeIfJSON;

// src/server/models/Check.model.ts
import mongoose2, { Schema } from "mongoose";
var CheckSchema = new Schema({
  name: {
    type: String,
    required: [true, 'CheckSchema: The "name" field must be required']
  },
  test: {
    type: Schema.Types.ObjectId,
    ref: "VRSTest",
    required: [true, 'CheckSchema: The "test" field must be required']
  },
  suite: {
    type: Schema.Types.ObjectId,
    ref: "VRSSuite",
    required: [true, 'CheckSchema: The "suite" field must be required']
  },
  app: {
    type: Schema.Types.ObjectId,
    ref: "VRSApp",
    required: [true, 'CheckSchema: The "app" field must be required']
  },
  branch: {
    type: String
  },
  realBaselineId: {
    type: Schema.Types.ObjectId,
    ref: "VRSBaseline"
  },
  baselineId: {
    type: Schema.Types.ObjectId,
    ref: "VRSSnapshot"
  },
  actualSnapshotId: {
    type: Schema.Types.ObjectId,
    ref: "VRSSnapshot"
  },
  diffId: {
    type: Schema.Types.ObjectId,
    ref: "VRSSnapshot"
  },
  createdDate: {
    type: Date,
    required: true,
    default: Date.now
  },
  updatedDate: {
    type: Date
  },
  status: {
    type: [{
      type: String,
      enum: {
        values: ["new", "pending", "approved", "running", "passed", "failed", "aborted"],
        message: "status is required"
      }
    }],
    default: ["new"]
  },
  browserName: {
    type: String
  },
  browserVersion: {
    type: String
  },
  browserFullVersion: {
    type: String
  },
  viewport: {
    type: String
  },
  os: {
    type: String
  },
  domDump: {
    type: String
  },
  result: {
    type: String,
    default: "{}"
  },
  run: {
    type: Schema.Types.ObjectId
  },
  markedAs: {
    type: String,
    enum: ["bug", "accepted"]
  },
  markedDate: {
    type: Date
  },
  markedById: {
    type: Schema.Types.ObjectId,
    ref: "VRSUser"
  },
  markedByUsername: {
    type: String
  },
  markedBugComment: {
    type: String
  },
  creatorId: {
    type: Schema.Types.ObjectId,
    ref: "VRSUser"
  },
  creatorUsername: {
    type: String
  },
  failReasons: {
    type: [String]
  },
  vOffset: {
    type: String
  },
  topStablePixels: {
    type: String
  },
  toleranceThreshold: {
    type: Number,
    min: 0,
    max: 100
  },
  meta: {
    type: Object
  }
});
CheckSchema.plugin(toJSON_plugin_default);
CheckSchema.plugin(paginate_plugin_default);
var Check = mongoose2.model("VRSCheck", CheckSchema);

// src/server/models/Log.model.ts
import mongoose3, { Schema as Schema2 } from "mongoose";
var LogSchema = new Schema2({
  timestamp: {
    type: Date
  },
  level: {
    type: String
  },
  message: {
    type: String
  },
  meta: {
    type: Object
  },
  hostname: {
    type: Object
  }
});
LogSchema.plugin(toJSON_plugin_default);
LogSchema.plugin(paginate_plugin_default);
var Log = mongoose3.model("VRSLog", LogSchema);

// src/server/models/App.model.ts
import mongoose4, { Schema as Schema3 } from "mongoose";
var AppSchema = new Schema3({
  name: {
    type: String,
    default: "Others",
    unique: true,
    required: [true, 'AppSchema: The "name" field must be required']
  },
  description: {
    type: String
  },
  version: {
    type: String
  },
  updatedDate: {
    type: Date
  },
  createdDate: {
    type: Date
  },
  meta: {
    type: Object
  }
});
AppSchema.plugin(paginate_plugin_default);
AppSchema.plugin(toJSON_plugin_default);
var App = mongoose4.model("VRSApp", AppSchema);

// src/server/models/Snapshot.model.ts
import mongoose5, { Schema as Schema4 } from "mongoose";
var SnapshotSchema = new Schema4({
  name: {
    type: String,
    required: [true, 'SnapshotSchema: The "name" field must be required']
  },
  path: {
    type: String
  },
  filename: {
    type: String
  },
  imghash: {
    type: String,
    required: [true, 'SnapshotSchema: The "imghash" field must be required']
  },
  createdDate: {
    type: Date,
    default: Date.now
  },
  vOffset: {
    type: Number
  },
  hOffset: {
    type: Number
  }
});
SnapshotSchema.plugin(toJSON_plugin_default);
SnapshotSchema.plugin(paginate_plugin_default);
var Snapshot = mongoose5.model("VRSSnapshot", SnapshotSchema);

// src/server/models/AppSettings.model.ts
import mongoose6, { Schema as Schema5 } from "mongoose";
var AppSettingsSchema = new Schema5({
  name: {
    type: String,
    unique: true,
    required: [true, 'AppSettingsSchema: The "name" field must be required']
  },
  label: {
    type: String,
    required: [true, 'AppSettingsSchema: The "label" field must be required']
  },
  description: {
    type: String
  },
  type: {
    type: String,
    required: [true, 'AppSettingsSchema: The "type" field must be required']
  },
  value: {
    type: Schema5.Types.Mixed,
    required: [true, 'AppSettingsSchema: The "value" field must be required']
  },
  env_variable: {
    type: String
  },
  enabled: {
    type: Boolean
  }
});
AppSettingsSchema.plugin(toJSON_plugin_default);
var AppSettings = mongoose6.model("VRSAppSettings", AppSettingsSchema);

// src/server/models/Suite.model.ts
import mongoose7, { Schema as Schema6 } from "mongoose";
var SuiteSchema = new Schema6({
  name: {
    type: String,
    default: "Others",
    unique: true,
    required: [true, 'SuiteSchema: The "name" field must be required']
  },
  tags: {
    type: [String]
  },
  app: {
    type: Schema6.Types.ObjectId,
    ref: "VRSApp",
    required: [true, 'SuiteSchema: The "app" field must be required']
  },
  description: {
    type: String
  },
  updatedDate: {
    type: Date,
    default: Date.now
  },
  createdDate: {
    type: Date
  },
  meta: {
    type: Object
  }
});
SuiteSchema.plugin(paginate_plugin_default);
SuiteSchema.plugin(toJSON_plugin_default);
var Suite = mongoose7.model("VRSSuite", SuiteSchema);

// src/server/models/Run.model.ts
import mongoose8, { Schema as Schema7 } from "mongoose";
var RunSchema = new Schema7({
  name: {
    type: String,
    required: [true, 'RunSchema: The "name" field must be required']
  },
  app: {
    type: Schema7.Types.ObjectId,
    ref: "VRSApp",
    required: [true, 'RunSchema: The "app" field must be required']
  },
  ident: {
    type: String,
    unique: true,
    required: [true, 'RunSchema: The "ident" field must be required']
  },
  description: {
    type: String
  },
  updatedDate: {
    type: Date,
    default: Date.now
  },
  createdDate: {
    type: Date
  },
  parameters: {
    type: [String]
  },
  meta: {
    type: Object
  }
});
RunSchema.plugin(paginate_plugin_default);
RunSchema.plugin(toJSON_plugin_default);
var Run = mongoose8.model("VRSRun", RunSchema);

// src/server/models/User.model.ts
import mongoose9, { Schema as Schema8 } from "mongoose";
import plm from "passport-local-mongoose";
var passportLocalMongoose = plm.default || plm;
var UserSchema = new Schema8({
  username: {
    type: String,
    unique: true,
    required: [true, 'UserSchema: The "username" field must be required']
  },
  firstName: {
    type: String,
    required: [true, 'UserSchema: The "firstName" field must be required']
  },
  lastName: {
    type: String,
    required: [true, 'UserSchema: The "lastName" field must be required']
  },
  role: {
    type: String,
    enum: ["admin", "reviewer", "user"],
    required: [true, 'UserSchema: The "role" field must be required']
  },
  provider: {
    type: String,
    default: "local"
  },
  providerId: {
    type: String
  },
  password: {
    type: String
  },
  token: {
    type: String
  },
  apiKey: {
    type: String
  },
  authSource: {
    type: String,
    enum: ["local", "jwt", "ldap", "api"],
    default: "local"
  },
  createdDate: {
    type: Date
  },
  updatedDate: {
    type: Date
  },
  expiration: {
    type: Date
  },
  meta: {
    type: Object
  }
});
UserSchema.statics.isEmailTaken = async function(username, excludeUserId) {
  const user = await this.findOne({ username, _id: { $ne: excludeUserId } });
  return !!user;
};
UserSchema.plugin(toJSON_plugin_default);
UserSchema.plugin(paginate_plugin_default);
UserSchema.plugin(passportLocalMongoose, { hashField: "password" });
var User = mongoose9.model("VRSUser", UserSchema);

// src/server/models/Test.model.ts
import mongoose10, { Schema as Schema9 } from "mongoose";
var TestSchema = new Schema9(
  {
    name: {
      type: String,
      required: "TestSchema: the test name is empty"
    },
    description: {
      type: String
    },
    status: {
      type: String
    },
    browserName: {
      type: String
    },
    browserVersion: {
      type: String
    },
    branch: {
      type: String
    },
    tags: {
      type: [String]
    },
    viewport: {
      type: String
    },
    calculatedViewport: {
      type: String
    },
    os: {
      type: String
    },
    app: {
      type: Schema9.Types.ObjectId,
      ref: "VRSApp",
      required: [true, 'TestSchema: The "app" field must be required']
    },
    blinking: {
      type: Number,
      default: 0
    },
    updatedDate: {
      type: Date
    },
    startDate: {
      type: Date
    },
    checks: [
      {
        type: mongoose10.Schema.Types.ObjectId,
        ref: "VRSCheck"
      }
    ],
    suite: {
      type: Schema9.Types.ObjectId,
      ref: "VRSSuite"
    },
    run: {
      type: Schema9.Types.ObjectId,
      ref: "VRSRun"
    },
    markedAs: {
      type: String,
      enum: ["Bug", "Accepted", "Unaccepted", "Partially"]
    },
    creatorId: {
      type: Schema9.Types.ObjectId,
      ref: "VRSUser"
    },
    creatorUsername: {
      type: String
    },
    meta: {
      type: Object
    }
  },
  { strictQuery: true }
);
TestSchema.plugin(toJSON_plugin_default);
TestSchema.plugin(paginate_plugin_default);
TestSchema.statics.paginateDistinct = async function(filter, options) {
  let sort = { _id: -1 };
  if (options.sortBy) {
    sort = {};
    options.sortBy.split(",").forEach((sortOption) => {
      const [key, order] = sortOption.split(":");
      sort[key] = order === "desc" ? -1 : 1;
    });
  }
  let limit = options.limit && parseInt(options.limit.toString(), 10) >= 0 ? parseInt(options.limit.toString(), 10) : 10;
  limit = limit === 0 ? Number.MAX_SAFE_INTEGER : limit;
  const page = options.page && parseInt(options.page.toString(), 10) > 0 ? parseInt(options.page.toString(), 10) : 1;
  const skip = (page - 1) * limit;
  const groupAggregateObj = { $group: { _id: `$${options.field}` } };
  const parsedFilter = typeof filter?.filter === "string" ? deserializeIfJSON_default(filter.filter) || {} : {};
  const documentsCount = (await this.aggregate([{ $match: parsedFilter }, groupAggregateObj]).exec()).length;
  const aggregatedDocs = (await this.aggregate([
    { $match: parsedFilter },
    groupAggregateObj,
    { $sort: sort },
    { $skip: skip },
    { $limit: limit }
  ])).filter((x) => x._id).map((x) => {
    const fieldValue = options.field ? x[options.field] : void 0;
    if (Array.isArray(fieldValue) && fieldValue.length > 0) {
      return fieldValue[0];
    }
    return { name: x._id };
  });
  const totalPages = Math.ceil(documentsCount / limit);
  return {
    results: aggregatedDocs,
    page,
    limit,
    totalPages,
    totalResults: documentsCount,
    timestamp: Date.now()
  };
};
var Test = mongoose10.model("VRSTest", TestSchema);

// src/server/models/Webhook.model.ts
import mongoose11, { Schema as Schema10 } from "mongoose";
var WebhookSchema = new Schema10({
  url: {
    type: String,
    required: [true, 'WebhookSchema: The "url" field must be required']
  },
  events: {
    type: [String],
    default: ["check.updated", "check.created"]
  },
  secret: {
    type: String
  },
  createdDate: {
    type: Date,
    default: Date.now
  },
  meta: {
    type: Object
  }
});
WebhookSchema.plugin(paginate_plugin_default);
WebhookSchema.plugin(toJSON_plugin_default);
var Webhook = mongoose11.model("VRSWebhook", WebhookSchema);

// src/server/models/ShareToken.model.ts
import mongoose12, { Schema as Schema11 } from "mongoose";
var ShareTokenSchema = new Schema11({
  checkId: {
    type: Schema11.Types.ObjectId,
    ref: "VRSCheck",
    required: [true, 'ShareTokenSchema: The "checkId" field is required'],
    index: true
  },
  token: {
    type: String,
    required: [true, 'ShareTokenSchema: The "token" field is required'],
    unique: true,
    index: true
  },
  createdById: {
    type: Schema11.Types.ObjectId,
    ref: "VRSUser",
    required: [true, 'ShareTokenSchema: The "createdById" field is required']
  },
  createdByUsername: {
    type: String,
    required: [true, 'ShareTokenSchema: The "createdByUsername" field is required']
  },
  createdDate: {
    type: Date,
    required: true,
    default: Date.now
  },
  isRevoked: {
    type: Boolean,
    default: false
  },
  revokedDate: {
    type: Date
  },
  revokedById: {
    type: Schema11.Types.ObjectId,
    ref: "VRSUser"
  },
  revokedByUsername: {
    type: String
  }
});
ShareTokenSchema.plugin(toJSON_plugin_default);
ShareTokenSchema.plugin(paginate_plugin_default);
var ShareToken = mongoose12.model("VRSShareToken", ShareTokenSchema);

// src/server/models/DomSnapshot.model.ts
import mongoose13, { Schema as Schema12 } from "mongoose";
var DomSnapshotSchema = new Schema12({
  checkId: {
    type: Schema12.Types.ObjectId,
    ref: "VRSCheck",
    required: [true, 'DomSnapshotSchema: The "checkId" field is required'],
    index: true
  },
  baselineId: {
    type: Schema12.Types.ObjectId,
    ref: "VRSBaseline",
    index: true
  },
  type: {
    type: String,
    enum: ["actual", "baseline"],
    required: [true, 'DomSnapshotSchema: The "type" field is required']
  },
  filename: {
    type: String,
    required: [true, 'DomSnapshotSchema: The "filename" field is required']
  },
  hash: {
    type: String,
    required: [true, 'DomSnapshotSchema: The "hash" field is required'],
    index: true
  },
  compressed: {
    type: Boolean,
    default: false
  },
  originalSize: {
    type: Number,
    required: [true, 'DomSnapshotSchema: The "originalSize" field is required']
  },
  compressedSize: {
    type: Number
  },
  createdDate: {
    type: Date,
    default: Date.now
  }
});
DomSnapshotSchema.index({ checkId: 1, type: 1 });
DomSnapshotSchema.index({ baselineId: 1, type: 1 });
DomSnapshotSchema.plugin(toJSON_plugin_default);
DomSnapshotSchema.plugin(paginate_plugin_default);
var DomSnapshot = mongoose13.model("VRSDomSnapshot", DomSnapshotSchema);

// src/server/models/PluginSettings.model.ts
import mongoose14, { Schema as Schema13 } from "mongoose";
var PluginSettingSchemaDefinition = new Schema13({
  key: { type: String, required: true },
  label: { type: String, required: true },
  description: { type: String },
  type: {
    type: String,
    enum: ["string", "number", "boolean", "select", "password"],
    required: true
  },
  defaultValue: { type: Schema13.Types.Mixed },
  envVariable: { type: String },
  options: [{ value: String, label: String }],
  required: { type: Boolean, default: false }
}, { _id: false });
var PluginSettingsSchema = new Schema13({
  pluginName: {
    type: String,
    unique: true,
    required: [true, "PluginSettings: pluginName is required"],
    index: true
  },
  displayName: {
    type: String,
    required: [true, "PluginSettings: displayName is required"]
  },
  description: {
    type: String
  },
  enabled: {
    type: Boolean,
    default: false
  },
  settings: {
    type: Schema13.Types.Mixed,
    default: {}
  },
  settingsSchema: {
    type: [PluginSettingSchemaDefinition],
    default: []
  }
}, {
  timestamps: true
});
PluginSettingsSchema.plugin(toJSON_plugin_default);
PluginSettingsSchema.statics.getEffectiveConfig = async function(pluginName, envPrefix = "SYNGRISI_PLUGIN_") {
  const doc = await this.findOne({ pluginName });
  const envPluginKey = pluginName.toUpperCase().replace(/-/g, "_");
  const envEnabledKey = `${envPrefix}${envPluginKey}_ENABLED`;
  const envEnabled = process.env[envEnabledKey]?.toLowerCase() === "true";
  const dbEnabled = doc?.enabled;
  const enabled = doc ? dbEnabled : envEnabled;
  const config2 = {};
  const schema = doc?.settingsSchema || [];
  if (schema.length === 0 && doc?.settings && Object.keys(doc.settings).length > 0) {
    for (const [key, value] of Object.entries(doc.settings)) {
      config2[key] = { value, source: "db" };
    }
    return { config: config2, enabled };
  }
  for (const field of schema) {
    const envKey = field.envVariable || `${envPrefix}${envPluginKey}_${field.key.toUpperCase()}`;
    const envValue = process.env[envKey];
    const dbValue = doc?.settings?.[field.key];
    if (dbValue !== void 0) {
      config2[field.key] = { value: dbValue, source: "db" };
    } else if (envValue !== void 0) {
      let parsedValue = envValue;
      if (field.type === "boolean") {
        parsedValue = envValue.toLowerCase() === "true";
      } else if (field.type === "number") {
        parsedValue = parseFloat(envValue);
      }
      config2[field.key] = { value: parsedValue, source: "env" };
    } else if (field.defaultValue !== void 0) {
      config2[field.key] = { value: field.defaultValue, source: "default" };
    }
  }
  return { config: config2, enabled };
};
PluginSettingsSchema.statics.upsertSettings = async function(pluginName, updates) {
  return this.findOneAndUpdate(
    { pluginName },
    { $set: updates },
    { upsert: true, new: true, runValidators: true }
  );
};
var PluginSettings = mongoose14.model(
  "VRSPluginSettings",
  PluginSettingsSchema
);

// src/server/utils/hash.ts
import { createHash, randomUUID } from "crypto";

// src/server/utils/colors.ts
var RESET = "\x1B[0m";
var colors = {
  // Standard colors
  blue: (s) => `\x1B[34m${s}${RESET}`,
  green: (s) => `\x1B[32m${s}${RESET}`,
  red: (s) => `\x1B[31m${s}${RESET}`,
  yellow: (s) => `\x1B[33m${s}${RESET}`,
  magenta: (s) => `\x1B[35m${s}${RESET}`,
  cyan: (s) => `\x1B[36m${s}${RESET}`,
  // Bright colors
  gray: (s) => `\x1B[90m${s}${RESET}`,
  whiteBright: (s) => `\x1B[97m${s}${RESET}`,
  // Reset code for manual use
  reset: RESET
};

// src/server/models/Baseline.model.ts
var BaselineSchema = new Schema14({
  snapshootId: {
    type: Schema14.Types.ObjectId,
    ref: "VRSSnapshot"
  },
  name: {
    type: String,
    required: [true, 'VRSBaselineSchema: The "name" field must be required']
  },
  app: {
    type: Schema14.Types.ObjectId,
    ref: "VRSApp",
    required: [true, 'VRSBaselineSchema: The "app" field must be required']
  },
  branch: {
    type: String
  },
  browserName: {
    type: String
  },
  browserVersion: {
    type: String
  },
  browserFullVersion: {
    type: String
  },
  viewport: {
    type: String
  },
  os: {
    type: String
  },
  markedAs: {
    type: String,
    enum: ["bug", "accepted"]
  },
  lastMarkedDate: {
    type: Date
  },
  createdDate: {
    type: Date
  },
  updatedDate: {
    type: Date
  },
  markedById: {
    type: Schema14.Types.ObjectId,
    ref: "VRSUser"
  },
  markedByUsername: {
    type: String
  },
  ignoreRegions: {
    type: String
  },
  boundRegions: {
    type: String
  },
  matchType: {
    type: String,
    enum: ["antialiasing", "nothing", "colors"]
  },
  toleranceThreshold: {
    type: Number,
    default: 0,
    min: 0,
    max: 100
  },
  meta: {
    type: Object
  }
});
BaselineSchema.set("autoIndex", false);
BaselineSchema.plugin(toJSON_plugin_default);
BaselineSchema.plugin(paginate_plugin_default);
BaselineSchema.index({
  name: 1,
  app: 1,
  branch: 1,
  browserName: 1,
  viewport: 1,
  os: 1,
  snapshootId: 1
}, { unique: true, name: "baseline_ident_snapshot_idx" });
var Baseline = mongoose15.model("VRSBaseline", BaselineSchema);
var Baseline_model_default = Baseline;

// src/server/lib/logger.ts
import winston from "winston";
import "winston-mongodb";

// src/server/utils/formatISOToDateTime.ts
function formatISOToDateTime(isoDateString) {
  const date = new Date(isoDateString);
  return `${date.toISOString().slice(0, 10)} ${date.toTimeString().slice(0, 8)}`;
}
var formatISOToDateTime_default = formatISOToDateTime;

// src/server/config.ts
import fs from "fs";
import dotenv2 from "dotenv";

// package.json
var version = "3.5.0";
var gitHead = "12bfda406cbe5aaccf3f17fdab02a9bd1a9d6343";

// src/server/config.ts
import crypto2 from "crypto";
import { execSync } from "child_process";

// src/server/envConfig.ts
import { cleanEnv, host, num, port, str, bool } from "envalid";
import crypto from "crypto";
import path from "path";
import dotenv from "dotenv";
dotenv.config({ quiet: true });
if (!process.env.NODE_ENV) {
  process.env.NODE_ENV = "production";
}
var env = cleanEnv(process.env, {
  NODE_ENV: str({ choices: ["development", "production", "test"] }),
  SYNGRISI_DB_URI: str({ default: "mongodb://127.0.0.1:27017/SyngrisiDb" }),
  SYNGRISI_APP_PORT: port({ default: 3e3 }),
  SYNGRISI_IMAGES_PATH: str({ default: path.join(process.cwd(), "./.snapshots-images") }),
  SYNGRISI_DOM_SNAPSHOTS_PATH: str({ default: "" }),
  // If empty, uses SYNGRISI_IMAGES_PATH
  SYNGRISI_TMP_DIR: str({ default: path.join(process.cwd(), ".tmp") }),
  SYNGRISI_ADMIN_DATA_JOBS_PATH: str({ default: path.join(process.cwd(), ".tmp", "admin-data-jobs") }),
  SYNGRISI_ADMIN_DATA_JOBS_TTL_MS: num({ default: 24 * 60 * 60 * 1e3 }),
  SYNGRISI_ADMIN_DATA_MAX_CONCURRENT_JOBS: num({ default: 1 }),
  SYNGRISI_ADMIN_DATA_UPLOAD_MAX_SIZE_MB: num({ default: 10240 }),
  SYNGRISI_HTTP_LOG: bool({ default: false }),
  SYNGRISI_COVERAGE: bool({ default: false }),
  SYNGRISI_HOSTNAME: host({ default: "localhost" }),
  SYNGRISI_AUTH: bool({ default: true }),
  SYNGRISI_TEST_MODE: bool({ default: false }),
  SYNGRISI_DISABLE_FIRST_RUN: bool({ default: false }),
  MONGODB_ROOT_USERNAME: str({ default: "" }),
  MONGODB_ROOT_PASSWORD: str({ default: "" }),
  LOGLEVEL: str({ choices: ["error", "warn", "info", "verbose", "debug", "silly"], default: "debug" }),
  // Legacy tests expect 20 rows per page; keep default aligned for e2e
  SYNGRISI_PAGINATION_SIZE: num({ default: 20 }),
  SYNGRISI_DISABLE_DEV_CORS: bool({ default: true, devDefault: true }),
  SYNGRISI_SESSION_STORE_KEY: str({ default: crypto.randomBytes(64).toString("hex") }),
  SYNGRISI_LOG_LEVEL: str({ default: "debug" }),
  SYNGRISI_DISABLE_LOGS: bool({ default: false }),
  SYNGRISI_AUTO_REMOVE_CHECKS_POLL_INTERVAL_MS: num({ default: 10 * 60 * 1e3 }),
  // 10 minutes
  SYNGRISI_AUTO_REMOVE_CHECKS_MIN_INTERVAL_MS: num({ default: 24 * 60 * 60 * 1e3 }),
  SYNGRISI_ENABLE_SCHEDULERS_IN_TEST_MODE: bool({ default: false }),
  // RCA
  SYNGRISI_RCA: bool({ default: false }),
  // trunk features
  SYNGRISI_TRUNK_FEATURE_AI_SEVERITY: bool({ default: false }),
  SYNGRISI_AI_KEY: str({ default: "" }),
  OPENAI_API_BASE_URL: str({ default: "https://api.openai.com/v1" }),
  OPENAI_API_KEY: str({ default: "" }),
  SYNGRISI_V8_COVERAGE_ON_EXIT: bool({ default: false }),
  // Rate Limiting
  SYNGRISI_RATE_LIMIT_WINDOW_MS: num({ default: 15 * 60 * 1e3 }),
  // 15 minutes
  SYNGRISI_RATE_LIMIT_MAX: num({ default: 5e4 }),
  SYNGRISI_AUTH_RATE_LIMIT_WINDOW_MS: num({ default: 15 * 60 * 1e3 }),
  // 15 minutes
  SYNGRISI_AUTH_RATE_LIMIT_MAX: num({ default: 200 }),
  // Mongo tuneables for tests/CI flake reduction
  SYNGRISI_MONGO_SOCKET_TIMEOUT_MS: num({ default: 6e4 }),
  SYNGRISI_MONGO_MAX_POOL_SIZE: num({ default: 20 }),
  SYNGRISI_MONGO_MIN_POOL_SIZE: num({ default: 2 }),
  SYNGRISI_MONGO_MAX_IDLE_TIME_MS: num({ default: 3e4 }),
  SYNGRISI_MONGO_WAIT_QUEUE_TIMEOUT_MS: num({ default: 3e4 }),
  SYNGRISI_MONGO_SERVER_SELECTION_TIMEOUT_MS: num({ default: 1e4 }),
  SYNGRISI_MONGO_CONNECT_TIMEOUT_MS: num({ default: 3e4 }),
  // SSO Configuration
  SSO_ENABLED: bool({ default: false }),
  SSO_PROTOCOL: str({ choices: ["", "oauth2", "saml"], default: "" }),
  SSO_CLIENT_ID: str({ default: "" }),
  SSO_CLIENT_SECRET: str({ default: "" }),
  SSO_AUTHORIZATION_URL: str({ default: "" }),
  SSO_TOKEN_URL: str({ default: "" }),
  SSO_USERINFO_URL: str({ default: "" }),
  SSO_CALLBACK_URL: str({ default: "/v1/auth/sso/oauth/callback" }),
  // SAML specific
  SSO_ENTRY_POINT: str({ default: "" }),
  SSO_ISSUER: str({ default: "" }),
  SSO_CERT: str({ default: "" }),
  SSO_IDP_ISSUER: str({ default: "" }),
  SSO_IDP_METADATA_URL: str({ default: "" }),
  // URL to fetch IdP metadata XML (alternative to manual SSO_ENTRY_POINT/SSO_CERT)
  // SSO user settings
  SSO_DEFAULT_ROLE: str({ choices: ["", "user", "admin", "reviewer"], default: "reviewer" }),
  SSO_AUTO_CREATE_USERS: bool({ default: true }),
  SSO_ALLOW_ACCOUNT_LINKING: bool({ default: true }),
  // Plugin System
  SYNGRISI_PLUGINS_ENABLED: str({ default: "" }),
  // Comma-separated list of enabled plugins
  SYNGRISI_PLUGINS_DIR: str({ default: "" }),
  // Directory for external plugins
  // Okta Auth Plugin
  // Deprecated: Use SYNGRISI_PLUGIN_JWT_AUTH_* variables instead
  OKTA_JWKS_URL: str({ default: "" }),
  OKTA_ISSUER: str({ default: "" }),
  OKTA_SERVICE_USER_ROLE: str({ default: "" }),
  OKTA_AUTH_HEADER: str({ default: "" }),
  // Custom Check Validator Plugin
  CHECK_MISMATCH_THRESHOLD: str({ default: "0" }),
  // Mismatch % below which checks pass
  CHECK_VALIDATOR_SCRIPT: str({ default: "" })
  // Path to custom validation script
});

// src/server/data/devices.json
var devices_default = [
  {
    os: "ios",
    os_version: "16",
    device: "iPhone 14 Pro Max",
    realMobile: true
  },
  {
    os: "ios",
    os_version: "16",
    device: "iPhone 14 Pro",
    realMobile: true
  },
  {
    os: "ios",
    os_version: "16",
    device: "iPhone 14 Plus",
    realMobile: true
  },
  {
    os: "ios",
    os_version: "16",
    device: "iPhone 14",
    realMobile: true
  },
  {
    os: "ios",
    os_version: "16",
    device: "iPhone 12 Pro Max",
    realMobile: true
  },
  {
    os: "ios",
    os_version: "16",
    device: "iPhone 12 Pro",
    realMobile: true
  },
  {
    os: "ios",
    os_version: "16",
    device: "iPhone 12 Mini",
    realMobile: true
  },
  {
    os: "ios",
    os_version: "16",
    device: "iPhone 11 Pro Max",
    realMobile: true
  },
  {
    os: "ios",
    os_version: "15",
    device: "iPhone XS",
    realMobile: true
  },
  {
    os: "ios",
    os_version: "15",
    device: "iPhone 13 Pro Max",
    realMobile: true
  },
  {
    os: "ios",
    os_version: "15",
    device: "iPhone 13 Pro",
    realMobile: true
  },
  {
    os: "ios",
    os_version: "15",
    device: "iPhone 13 Mini",
    realMobile: true
  },
  {
    os: "ios",
    os_version: "15",
    device: "iPhone 13",
    realMobile: true
  },
  {
    os: "ios",
    os_version: "15",
    device: "iPhone 11 Pro",
    realMobile: true
  },
  {
    os: "ios",
    os_version: "15",
    device: "iPhone 11",
    realMobile: true
  },
  {
    os: "ios",
    os_version: "14",
    device: "iPhone XS",
    realMobile: true
  },
  {
    os: "ios",
    os_version: "14",
    device: "iPhone 12 Pro Max",
    realMobile: true
  },
  {
    os: "ios",
    os_version: "14",
    device: "iPhone 12 Pro",
    realMobile: true
  },
  {
    os: "ios",
    os_version: "14",
    device: "iPhone 12 Mini",
    realMobile: true
  },
  {
    os: "ios",
    os_version: "14",
    device: "iPhone 12",
    realMobile: true
  },
  {
    os: "ios",
    os_version: "14",
    device: "iPhone 11 Pro Max",
    realMobile: true
  },
  {
    os: "ios",
    os_version: "14",
    device: "iPhone 11",
    realMobile: true
  },
  {
    os: "ios",
    os_version: "13",
    device: "iPhone XS",
    realMobile: true
  },
  {
    os: "ios",
    os_version: "13",
    device: "iPhone 11 Pro Max",
    realMobile: true
  },
  {
    os: "ios",
    os_version: "13",
    device: "iPhone 11 Pro",
    realMobile: true
  },
  {
    os: "ios",
    os_version: "13",
    device: "iPhone 11",
    realMobile: true
  },
  {
    os: "ios",
    os_version: "12",
    device: "iPhone XS",
    realMobile: true
  },
  {
    os: "ios",
    os_version: "12",
    device: "iPhone XS Max",
    realMobile: true
  },
  {
    os: "ios",
    os_version: "15",
    device: "iPhone XR",
    realMobile: true
  },
  {
    os: "ios",
    os_version: "12",
    device: "iPhone XR",
    realMobile: true
  },
  {
    os: "ios",
    os_version: "11",
    device: "iPhone X",
    realMobile: true
  },
  {
    os: "ios",
    os_version: "15",
    device: "iPhone 8",
    realMobile: true
  },
  {
    os: "ios",
    os_version: "13",
    device: "iPhone 8",
    realMobile: true
  },
  {
    os: "ios",
    os_version: "12",
    device: "iPhone 8",
    realMobile: true
  },
  {
    os: "ios",
    os_version: "11",
    device: "iPhone 8",
    realMobile: true
  },
  {
    os: "ios",
    os_version: "12",
    device: "iPhone 8 Plus",
    realMobile: true
  },
  {
    os: "ios",
    os_version: "11",
    device: "iPhone 8 Plus",
    realMobile: true
  },
  {
    os: "ios",
    os_version: "12",
    device: "iPhone 7",
    realMobile: true
  },
  {
    os: "ios",
    os_version: "10",
    device: "iPhone 7",
    realMobile: true
  },
  {
    os: "ios",
    os_version: "12",
    device: "iPhone 6S",
    realMobile: true
  },
  {
    os: "ios",
    os_version: "11",
    device: "iPhone 6S",
    realMobile: true
  },
  {
    os: "ios",
    os_version: "11",
    device: "iPhone 6S Plus",
    realMobile: true
  },
  {
    os: "ios",
    os_version: "11",
    device: "iPhone 6",
    realMobile: true
  },
  {
    os: "ios",
    os_version: "15",
    device: "iPhone SE 2022",
    realMobile: true
  },
  {
    os: "ios",
    os_version: "13",
    device: "iPhone SE 2020",
    realMobile: true
  },
  {
    os: "ios",
    os_version: "11",
    device: "iPhone SE",
    realMobile: true
  },
  {
    os: "ios",
    os_version: "14",
    device: "iPad Air 4",
    realMobile: true
  },
  {
    os: "ios",
    os_version: "15",
    device: "iPad 9th",
    realMobile: true
  },
  {
    os: "ios",
    os_version: "16",
    device: "iPad Pro 12.9 2022",
    realMobile: true
  },
  {
    os: "ios",
    os_version: "16",
    device: "iPad Pro 12.9 2020",
    realMobile: true
  },
  {
    os: "ios",
    os_version: "16",
    device: "iPad Pro 11 2022",
    realMobile: true
  },
  {
    os: "ios",
    os_version: "16",
    device: "iPad 10th",
    realMobile: true
  },
  {
    os: "ios",
    os_version: "15",
    device: "iPad Air 5",
    realMobile: true
  },
  {
    os: "ios",
    os_version: "14",
    device: "iPad Pro 12.9 2021",
    realMobile: true
  },
  {
    os: "ios",
    os_version: "14",
    device: "iPad Pro 12.9 2020",
    realMobile: true
  },
  {
    os: "ios",
    os_version: "14",
    device: "iPad Pro 11 2021",
    realMobile: true
  },
  {
    os: "ios",
    os_version: "13",
    device: "iPad Pro 12.9 2020",
    realMobile: true
  },
  {
    os: "ios",
    os_version: "16",
    device: "iPad 8th",
    realMobile: true
  },
  {
    os: "ios",
    os_version: "15",
    device: "iPad Pro 12.9 2018",
    realMobile: true
  },
  {
    os: "ios",
    os_version: "15",
    device: "iPad Mini 2021",
    realMobile: true
  },
  {
    os: "ios",
    os_version: "14",
    device: "iPad 8th",
    realMobile: true
  },
  {
    os: "ios",
    os_version: "13",
    device: "iPad Pro 12.9 2018",
    realMobile: true
  },
  {
    os: "ios",
    os_version: "13",
    device: "iPad Pro 11 2020",
    realMobile: true
  },
  {
    os: "ios",
    os_version: "13",
    device: "iPad Mini 2019",
    realMobile: true
  },
  {
    os: "ios",
    os_version: "13",
    device: "iPad Air 2019",
    realMobile: true
  },
  {
    os: "ios",
    os_version: "13",
    device: "iPad 7th",
    realMobile: true
  },
  {
    os: "ios",
    os_version: "12",
    device: "iPad Pro 12.9 2018",
    realMobile: true
  },
  {
    os: "ios",
    os_version: "12",
    device: "iPad Pro 11 2018",
    realMobile: true
  },
  {
    os: "ios",
    os_version: "12",
    device: "iPad Mini 2019",
    realMobile: true
  },
  {
    os: "ios",
    os_version: "12",
    device: "iPad Air 2019",
    realMobile: true
  },
  {
    os: "ios",
    os_version: "11",
    device: "iPad Pro 9.7 2016",
    realMobile: true
  },
  {
    os: "ios",
    os_version: "11",
    device: "iPad Pro 12.9 2017",
    realMobile: true
  },
  {
    os: "ios",
    os_version: "11",
    device: "iPad Mini 4",
    realMobile: true
  },
  {
    os: "ios",
    os_version: "11",
    device: "iPad 6th",
    realMobile: true
  },
  {
    os: "ios",
    os_version: "11",
    device: "iPad 5th",
    realMobile: true
  },
  {
    os: "android",
    os_version: "12.0",
    device: "Samsung Galaxy S22 Ultra",
    realMobile: true
  },
  {
    os: "android",
    os_version: "12.0",
    device: "Samsung Galaxy S22 Plus",
    realMobile: true
  },
  {
    os: "android",
    os_version: "12.0",
    device: "Samsung Galaxy S22",
    realMobile: true
  },
  {
    os: "android",
    os_version: "12.0",
    device: "Samsung Galaxy S21",
    realMobile: true
  },
  {
    os: "android",
    os_version: "11.0",
    device: "Samsung Galaxy S21 Ultra",
    realMobile: true
  },
  {
    os: "android",
    os_version: "11.0",
    device: "Samsung Galaxy S21",
    realMobile: true
  },
  {
    os: "android",
    os_version: "11.0",
    device: "Samsung Galaxy S21 Plus",
    realMobile: true
  },
  {
    os: "android",
    os_version: "10.0",
    device: "Samsung Galaxy S20",
    realMobile: true
  },
  {
    os: "android",
    os_version: "10.0",
    device: "Samsung Galaxy S20 Plus",
    realMobile: true
  },
  {
    os: "android",
    os_version: "10.0",
    device: "Samsung Galaxy S20 Ultra",
    realMobile: true
  },
  {
    os: "android",
    os_version: "11.0",
    device: "Samsung Galaxy M52",
    realMobile: true
  },
  {
    os: "android",
    os_version: "11.0",
    device: "Samsung Galaxy M32",
    realMobile: true
  },
  {
    os: "android",
    os_version: "11.0",
    device: "Samsung Galaxy A52",
    realMobile: true
  },
  {
    os: "android",
    os_version: "10.0",
    device: "Samsung Galaxy Note 20 Ultra",
    realMobile: true
  },
  {
    os: "android",
    os_version: "10.0",
    device: "Samsung Galaxy Note 20",
    realMobile: true
  },
  {
    os: "android",
    os_version: "10.0",
    device: "Samsung Galaxy A51",
    realMobile: true
  },
  {
    os: "android",
    os_version: "10.0",
    device: "Samsung Galaxy A11",
    realMobile: true
  },
  {
    os: "android",
    os_version: "9.0",
    device: "Samsung Galaxy S9 Plus",
    realMobile: true
  },
  {
    os: "android",
    os_version: "9.0",
    device: "Samsung Galaxy S10e",
    realMobile: true
  },
  {
    os: "android",
    os_version: "9.0",
    device: "Samsung Galaxy S10 Plus",
    realMobile: true
  },
  {
    os: "android",
    os_version: "9.0",
    device: "Samsung Galaxy S10",
    realMobile: true
  },
  {
    os: "android",
    os_version: "9.0",
    device: "Samsung Galaxy Note 10 Plus",
    realMobile: true
  },
  {
    os: "android",
    os_version: "9.0",
    device: "Samsung Galaxy Note 10",
    realMobile: true
  },
  {
    os: "android",
    os_version: "9.0",
    device: "Samsung Galaxy A10",
    realMobile: true
  },
  {
    os: "android",
    os_version: "8.1",
    device: "Samsung Galaxy Note 9",
    realMobile: true
  },
  {
    os: "android",
    os_version: "8.1",
    device: "Samsung Galaxy J7 Prime",
    realMobile: true
  },
  {
    os: "android",
    os_version: "8.0",
    device: "Samsung Galaxy S9 Plus",
    realMobile: true
  },
  {
    os: "android",
    os_version: "8.0",
    device: "Samsung Galaxy S9",
    realMobile: true
  },
  {
    os: "android",
    os_version: "7.1",
    device: "Samsung Galaxy Note 8",
    realMobile: true
  },
  {
    os: "android",
    os_version: "7.1",
    device: "Samsung Galaxy A8",
    realMobile: true
  },
  {
    os: "android",
    os_version: "7.0",
    device: "Samsung Galaxy S8 Plus",
    realMobile: true
  },
  {
    os: "android",
    os_version: "7.0",
    device: "Samsung Galaxy S8",
    realMobile: true
  },
  {
    os: "android",
    os_version: "6.0",
    device: "Samsung Galaxy S7",
    realMobile: true
  },
  {
    os: "android",
    os_version: "5.0",
    device: "Samsung Galaxy S6",
    realMobile: true
  },
  {
    os: "android",
    os_version: "13.0",
    device: "Google Pixel 7 Pro",
    realMobile: true
  },
  {
    os: "android",
    os_version: "13.0",
    device: "Google Pixel 7",
    realMobile: true
  },
  {
    os: "android",
    os_version: "13.0",
    device: "Google Pixel 6 Pro",
    realMobile: true
  },
  {
    os: "android",
    os_version: "12.0",
    device: "Google Pixel 6 Pro",
    realMobile: true
  },
  {
    os: "android",
    os_version: "12.0",
    device: "Google Pixel 6",
    realMobile: true
  },
  {
    os: "android",
    os_version: "12.0",
    device: "Google Pixel 5",
    realMobile: true
  },
  {
    os: "android",
    os_version: "11.0",
    device: "Google Pixel 5",
    realMobile: true
  },
  {
    os: "android",
    os_version: "11.0",
    device: "Google Pixel 4",
    realMobile: true
  },
  {
    os: "android",
    os_version: "10.0",
    device: "Google Pixel 4 XL",
    realMobile: true
  },
  {
    os: "android",
    os_version: "10.0",
    device: "Google Pixel 4",
    realMobile: true
  },
  {
    os: "android",
    os_version: "10.0",
    device: "Google Pixel 3",
    realMobile: true
  },
  {
    os: "android",
    os_version: "9.0",
    device: "Google Pixel 3a XL",
    realMobile: true
  },
  {
    os: "android",
    os_version: "9.0",
    device: "Google Pixel 3a",
    realMobile: true
  },
  {
    os: "android",
    os_version: "9.0",
    device: "Google Pixel 3 XL",
    realMobile: true
  },
  {
    os: "android",
    os_version: "9.0",
    device: "Google Pixel 3",
    realMobile: true
  },
  {
    os: "android",
    os_version: "9.0",
    device: "Google Pixel 2",
    realMobile: true
  },
  {
    os: "android",
    os_version: "8.0",
    device: "Google Pixel 2",
    realMobile: true
  },
  {
    os: "android",
    os_version: "7.1",
    device: "Google Pixel",
    realMobile: true
  },
  {
    os: "android",
    os_version: "6.0",
    device: "Google Nexus 6",
    realMobile: true
  },
  {
    os: "android",
    os_version: "4.4",
    device: "Google Nexus 5",
    realMobile: true
  },
  {
    os: "android",
    os_version: "11.0",
    device: "OnePlus 9",
    realMobile: true
  },
  {
    os: "android",
    os_version: "10.0",
    device: "OnePlus 8",
    realMobile: true
  },
  {
    os: "android",
    os_version: "10.0",
    device: "OnePlus 7T",
    realMobile: true
  },
  {
    os: "android",
    os_version: "9.0",
    device: "OnePlus 7",
    realMobile: true
  },
  {
    os: "android",
    os_version: "9.0",
    device: "OnePlus 6T",
    realMobile: true
  },
  {
    os: "android",
    os_version: "11.0",
    device: "Xiaomi Redmi Note 11",
    realMobile: true
  },
  {
    os: "android",
    os_version: "10.0",
    device: "Xiaomi Redmi Note 9",
    realMobile: true
  },
  {
    os: "android",
    os_version: "9.0",
    device: "Xiaomi Redmi Note 8",
    realMobile: true
  },
  {
    os: "android",
    os_version: "9.0",
    device: "Xiaomi Redmi Note 7",
    realMobile: true
  },
  {
    os: "android",
    os_version: "11.0",
    device: "Vivo Y21",
    realMobile: true
  },
  {
    os: "android",
    os_version: "11.0",
    device: "Vivo V21",
    realMobile: true
  },
  {
    os: "android",
    os_version: "10.0",
    device: "Vivo Y50",
    realMobile: true
  },
  {
    os: "android",
    os_version: "11.0",
    device: "Oppo Reno 6",
    realMobile: true
  },
  {
    os: "android",
    os_version: "11.0",
    device: "Oppo A96",
    realMobile: true
  },
  {
    os: "android",
    os_version: "10.0",
    device: "Oppo Reno 3 Pro",
    realMobile: true
  },
  {
    os: "android",
    os_version: "11.0",
    device: "Motorola Moto G71 5G",
    realMobile: true
  },
  {
    os: "android",
    os_version: "10.0",
    device: "Motorola Moto G9 Play",
    realMobile: true
  },
  {
    os: "android",
    os_version: "9.0",
    device: "Motorola Moto G7 Play",
    realMobile: true
  },
  {
    os: "android",
    os_version: "9.0",
    device: "Huawei P30",
    realMobile: true
  },
  {
    os: "android",
    os_version: "12.0",
    device: "Samsung Galaxy Tab S8",
    realMobile: true
  },
  {
    os: "android",
    os_version: "11.0",
    device: "Samsung Galaxy Tab S7",
    realMobile: true
  },
  {
    os: "android",
    os_version: "10.0",
    device: "Samsung Galaxy Tab S7",
    realMobile: true
  },
  {
    os: "android",
    os_version: "9.0",
    device: "Samsung Galaxy Tab S6",
    realMobile: true
  },
  {
    os: "android",
    os_version: "9.0",
    device: "Samsung Galaxy Tab S5e",
    realMobile: true
  },
  {
    os: "android",
    os_version: "8.1",
    device: "Samsung Galaxy Tab S4",
    realMobile: true
  }
];

// src/server/config.ts
var getCommitHash = () => {
  if (gitHead) {
    return gitHead.substring(0, 7);
  }
  try {
    return execSync("git rev-parse --short HEAD", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
  } catch {
    return "";
  }
};
var customDevicesPath = "./server/data/custom_devices.json";
var logsFolder = "./logs";
dotenv2.config();
var CURRENT_VERSION = version;
var [major, minor] = CURRENT_VERSION.split(".").map(Number);
var minSupportedMinor = Math.max(0, minor - 2);
var MIN_SUPPORTED_SDK_VERSION = `${major}.${minSupportedMinor}.0`;
var config = {
  version,
  commitHash: getCommitHash(),
  minSupportedSdkVersion: MIN_SUPPORTED_SDK_VERSION,
  apiVersion: "1",
  // this isn't used
  getDevices: async () => {
    if (fs.existsSync(customDevicesPath)) {
      return [...devices_default, ...(await import(customDevicesPath)).default];
    }
    return devices_default;
  },
  defaultImagesPath: env.SYNGRISI_IMAGES_PATH,
  domSnapshotsPath: env.SYNGRISI_DOM_SNAPSHOTS_PATH || env.SYNGRISI_IMAGES_PATH,
  connectionString: env.SYNGRISI_DB_URI || "mongodb://127.0.0.1:27017/SyngrisiDb",
  host: env.SYNGRISI_HOSTNAME,
  port: env.SYNGRISI_APP_PORT || 3e3,
  backupsFolder: "./backups",
  enableHttpLogger: env.SYNGRISI_HTTP_LOG,
  httpLoggerFilePath: `${logsFolder}/http.log`,
  storeSessionKey: env.SYNGRISI_SESSION_STORE_KEY || crypto2.randomBytes(64).toString("hex"),
  codeCoverage: env.SYNGRISI_COVERAGE,
  disableCors: env.SYNGRISI_DISABLE_DEV_CORS,
  fileUploadMaxSize: 50 * 1024 * 1024,
  adminDataJobsPath: env.SYNGRISI_ADMIN_DATA_JOBS_PATH,
  adminDataJobsTtlMs: env.SYNGRISI_ADMIN_DATA_JOBS_TTL_MS,
  adminDataMaxConcurrentJobs: env.SYNGRISI_ADMIN_DATA_MAX_CONCURRENT_JOBS,
  adminDataUploadMaxSize: env.SYNGRISI_ADMIN_DATA_UPLOAD_MAX_SIZE_MB * 1024 * 1024,
  testMode: env.SYNGRISI_TEST_MODE,
  jsonLimit: "50mb",
  tmpDir: env.SYNGRISI_TMP_DIR,
  helmet: {
    crossOriginEmbedderPolicy: false,
    crossOriginResourcePolicy: false,
    crossOriginOpenerPolicy: false,
    contentSecurityPolicy: {
      useDefaults: false,
      directives: {
        defaultSrc: ["'self'", "*", "'unsafe-inline'", "'unsafe-eval'", "data:", "blob:"],
        frameAncestors: ["'self'", "*"],
        frameSrc: ["'self'", "*"],
        scriptSrc: ["'self'", "*", "'unsafe-inline'", "'unsafe-eval'"],
        styleSrc: ["'self'", "*", "'unsafe-inline'"],
        imgSrc: ["'self'", "*", "data:", "blob:"],
        fontSrc: ["'self'", "*", "data:"],
        connectSrc: ["'self'", "*"],
        baseUri: ["'self'"],
        formAction: ["'self'"],
        objectSrc: ["'none'"],
        scriptSrcAttr: ["'none'"]
      }
    },
    hsts: false
  },
  rateLimit: {
    windowMs: env.SYNGRISI_RATE_LIMIT_WINDOW_MS,
    max: env.SYNGRISI_RATE_LIMIT_MAX,
    standardHeaders: true,
    legacyHeaders: false
  },
  authRateLimit: {
    windowMs: env.SYNGRISI_AUTH_RATE_LIMIT_WINDOW_MS,
    max: env.SYNGRISI_AUTH_RATE_LIMIT_MAX,
    standardHeaders: true,
    legacyHeaders: false
  }
};
if (!fs.existsSync(config.defaultImagesPath)) {
  fs.mkdirSync(config.defaultImagesPath, { recursive: true });
}
if (config.domSnapshotsPath !== config.defaultImagesPath && !fs.existsSync(config.domSnapshotsPath)) {
  fs.mkdirSync(config.domSnapshotsPath, { recursive: true });
}
if (!fs.existsSync(config.adminDataJobsPath)) {
  fs.mkdirSync(config.adminDataJobsPath, { recursive: true });
}
if (!fs.existsSync(logsFolder)) {
  fs.mkdirSync(logsFolder, { recursive: true });
}

// src/server/lib/logger.ts
var logLevel = env.SYNGRISI_LOG_LEVEL;
function createWinstonLogger(opts) {
  if (env.SYNGRISI_DISABLE_LOGS) {
    return winston.createLogger({ transports: [], silent: true });
  }
  const transports = [
    new winston.transports.Console({
      level: logLevel || "silly",
      format: winston.format.combine(
        winston.format.colorize(),
        winston.format.timestamp(),
        winston.format.ms(),
        winston.format.metadata(),
        winston.format.printf((info) => {
          const user = info.metadata.user ? colors.blue(` <${info.metadata.user}>`) : "";
          const ref = info.metadata.ref ? colors.gray(` ${info.metadata.ref}`) : "";
          const msgType = info.metadata.msgType ? ` ${info.metadata.msgType}` : "";
          const itemType = info.metadata.itemType ? colors.magenta(` ${info.metadata.itemType}`) : "";
          const scope = info.metadata.scope ? colors.magenta(` [${info.metadata.scope}]`) : "";
          const msg = typeof info.message === "object" ? `
${JSON.stringify(info.message, null, 2)}` : info.message;
          return `${info.level} ${scope}${formatISOToDateTime_default(info.metadata.timestamp)} ${info.metadata.ms}${user}${ref}${msgType}${itemType} '${msg}'`;
        }),
        winston.format.padLevels()
      )
    })
  ];
  if (!env.SYNGRISI_TEST_MODE) {
    transports.push(
      new winston.transports.MongoDB({
        level: logLevel || "debug",
        format: winston.format.combine(
          winston.format.timestamp(),
          winston.format.json(),
          winston.format.metadata()
        ),
        options: {},
        db: opts.dbConnectionString,
        collection: "vrslogs"
      })
    );
  }
  return winston.createLogger({ transports });
}
var Logger = class _Logger {
  winstonLogger;
  constructor(opts = { dbConnectionString: config.connectionString }) {
    this.winstonLogger = createWinstonLogger(opts);
  }
  static mergeMeta(objects) {
    return objects.reduce((acc, obj) => {
      return { ...acc, ...obj };
    }, {});
  }
  static sanitizeForLog(obj) {
    try {
      return JSON.parse(JSON.stringify(obj));
    } catch (e) {
      return String(obj);
    }
  }
  log(severity, msg, meta) {
    const mergedMeta = _Logger.sanitizeForLog(_Logger.mergeMeta(meta));
    const formattedMsg = typeof msg === "object" ? JSON.stringify(msg, null, 2) : msg;
    this.winstonLogger.log(severity, formattedMsg, mergedMeta);
  }
  error(msg, ...meta) {
    let message = String(msg);
    let code = 0;
    if (msg instanceof Object) {
      try {
        message = JSON.stringify(_Logger.sanitizeForLog(msg));
      } catch (e) {
        message = String(msg);
      }
    }
    if (msg instanceof Error) {
      message = msg.stack || msg.message;
    }
    if (msg instanceof ApiError_default) {
      code = msg.statusCode;
    }
    this.log("error", `${code !== 0 ? "[" + code + "]" : ""}${message}
 stacktrace: ${new Error().stack}`, meta);
  }
  warn(msg, ...meta) {
    this.log("warn", `${msg}
 stacktrace: ${new Error().stack}`, meta);
  }
  info(msg, ...meta) {
    this.log("info", msg, meta);
  }
  verbose(msg, ...meta) {
    this.log("verbose", msg, meta);
  }
  debug(msg, ...meta) {
    this.log("debug", msg, meta);
  }
  silly(msg, ...meta) {
    this.log("silly", msg, meta);
  }
};
var logger_default = new Logger();

// src/server/migrations/2025-11-ensure-baseline-uniqueness.ts
var identFields = ["name", "app", "branch", "browserName", "viewport", "os", "snapshootId"];
var pickIdent = (doc) => {
  const picked = {};
  identFields.forEach((field) => {
    picked[field] = doc[field];
  });
  return picked;
};
async function deduplicateBaselines() {
  await Baseline_model_default.init();
  const duplicates = await Baseline_model_default.aggregate([
    {
      $group: {
        _id: pickIdent({
          name: "$name",
          app: "$app",
          branch: "$branch",
          browserName: "$browserName",
          viewport: "$viewport",
          os: "$os",
          snapshootId: "$snapshootId"
        }),
        ids: { $push: "$_id" },
        lastMarkedDates: { $push: "$lastMarkedDate" },
        createdDates: { $push: "$createdDate" },
        count: { $sum: 1 }
      }
    },
    { $match: { count: { $gt: 1 } } }
  ]);
  for (const group of duplicates) {
    const ids = group.ids.map((id) => id.toString());
    const keep = await Baseline_model_default.findOne({
      _id: { $in: ids }
    }).sort({ lastMarkedDate: -1, createdDate: -1, _id: -1 }).select("_id").lean();
    if (!keep) continue;
    const keepId = keep._id?.toString();
    const toRemove = ids.filter((id) => id !== keepId);
    if (toRemove.length > 0) {
      logger_default.warn(`deduplicating baselines ${JSON.stringify(group._id)}, removing ${toRemove.length} duplicates`, { scope: "migration" });
      await Baseline_model_default.deleteMany({ _id: { $in: toRemove } });
    }
  }
}
var ensureBaselineUniqueness = {
  name: "2025-11-ensure-baseline-uniqueness",
  up: async () => {
    await deduplicateBaselines();
    await Baseline_model_default.collection.createIndex(
      {
        name: 1,
        app: 1,
        branch: 1,
        browserName: 1,
        viewport: 1,
        os: 1,
        snapshootId: 1
      },
      { unique: true, name: "baseline_ident_snapshot_idx" }
    );
  }
};
export {
  ensureBaselineUniqueness
};
//# sourceMappingURL=2025-11-ensure-baseline-uniqueness.js.map