38 lines
1.6 KiB
JavaScript
38 lines
1.6 KiB
JavaScript
export function assertBatchTimeBounds(batch, { now = new Date(), maxFutureSkewSeconds = 300 } = {}) {
|
|
const acceptedAt = validDate(now, "intake_policy_clock_invalid");
|
|
const maxFutureAt = new Date(acceptedAt.getTime() + Number(maxFutureSkewSeconds) * 1000);
|
|
if (!Number.isInteger(maxFutureSkewSeconds) || maxFutureSkewSeconds < 0) {
|
|
throw intakePolicyError("intake_policy_future_skew_invalid");
|
|
}
|
|
|
|
const receivedAt = validDate(batch?.batch?.receivedAt, "batch_received_at_invalid");
|
|
if (receivedAt > maxFutureAt) throw intakePolicyError("batch_received_at_too_far_in_future");
|
|
|
|
for (const fact of batch?.facts || []) {
|
|
const observedAt = validDate(fact?.observedAt, "fact_observed_at_invalid");
|
|
if (observedAt > maxFutureAt) throw intakePolicyError("fact_observed_at_too_far_in_future");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Retention is based on the server's acceptance time, never on provider or L2
|
|
* timestamps supplied in a batch.
|
|
*/
|
|
export function rawRetentionExpiry({ now = new Date(), rawRetentionDays }) {
|
|
const acceptedAt = validDate(now, "intake_policy_clock_invalid");
|
|
if (!Number.isInteger(rawRetentionDays) || rawRetentionDays < 1) {
|
|
throw intakePolicyError("raw_retention_days_invalid");
|
|
}
|
|
return new Date(acceptedAt.getTime() + rawRetentionDays * 24 * 60 * 60 * 1000);
|
|
}
|
|
|
|
export function intakePolicyError(code) {
|
|
return Object.assign(new Error(code), { status: 422, code });
|
|
}
|
|
|
|
function validDate(value, code) {
|
|
const parsed = value instanceof Date ? new Date(value.getTime()) : new Date(String(value ?? ""));
|
|
if (Number.isNaN(parsed.getTime())) throw intakePolicyError(code);
|
|
return parsed;
|
|
}
|