gdpr audit implemented, email log, vollmachten, pdf delete cancel data privacy and vollmachten, removed message no id card in engergy car, and other contracts that are not telecom contracts, added insert counter for engery

This commit is contained in:
2026-03-21 11:59:53 +01:00
parent 89cf92eaf5
commit f2876f877e
1491 changed files with 265550 additions and 1292 deletions
+189
View File
@@ -0,0 +1,189 @@
/// Stores metadata for a part of a change.
export class Span<Data = any> {
/// @internal
constructor(
/// The length of this span.
readonly length: number,
/// The data associated with this span.
readonly data: Data
) {}
/// @internal
cut(length: number) {
return length == this.length ? this : new Span(length, this.data)
}
/// @internal
static slice(spans: readonly Span[], from: number, to: number) {
if (from == to) return Span.none
if (from == 0 && to == Span.len(spans)) return spans
let result = []
for (let i = 0, off = 0; off < to; i++) {
let span = spans[i], end = off + span.length
let overlap = Math.min(to, end) - Math.max(from, off)
if (overlap > 0) result.push(span.cut(overlap))
off = end
}
return result
}
/// @internal
static join<Data>(a: readonly Span<Data>[], b: readonly Span<Data>[], combine: (dataA: Data, dataB: Data) => Data) {
if (a.length == 0) return b
if (b.length == 0) return a
let combined = combine(a[a.length - 1].data, b[0].data)
if (combined == null) return a.concat(b)
let result = a.slice(0, a.length - 1)
result.push(new Span(a[a.length - 1].length + b[0].length, combined))
for (let i = 1; i < b.length; i++) result.push(b[i])
return result
}
/// @internal
static len(spans: readonly Span[]) {
let len = 0
for (let i = 0; i < spans.length; i++) len += spans[i].length
return len
}
/// @internal
static none: readonly Span[] = []
}
/// A replaced range with metadata associated with it.
export class Change<Data = any> {
/// @internal
constructor(
/// The start of the range deleted/replaced in the old document.
readonly fromA: number,
/// The end of the range in the old document.
readonly toA: number,
/// The start of the range inserted in the new document.
readonly fromB: number,
/// The end of the range in the new document.
readonly toB: number,
/// Data associated with the deleted content. The length of these
/// spans adds up to `this.toA - this.fromA`.
readonly deleted: readonly Span<Data>[],
/// Data associated with the inserted content. Length adds up to
/// `this.toB - this.fromB`.
readonly inserted: readonly Span<Data>[]
) {}
/// @internal
get lenA() { return this.toA - this.fromA }
/// @internal
get lenB() { return this.toB - this.fromB }
/// @internal
slice(startA: number, endA: number, startB: number, endB: number): Change<Data> {
if (startA == 0 && startB == 0 && endA == this.toA - this.fromA &&
endB == this.toB - this.fromB) return this
return new Change(this.fromA + startA, this.fromA + endA,
this.fromB + startB, this.fromB + endB,
Span.slice(this.deleted, startA, endA),
Span.slice(this.inserted, startB, endB))
}
/// This merges two changesets (the end document of x should be the
/// start document of y) into a single one spanning the start of x to
/// the end of y.
static merge<Data>(x: readonly Change<Data>[],
y: readonly Change<Data>[],
combine: (dataA: Data, dataB: Data) => Data): readonly Change<Data>[] {
if (x.length == 0) return y
if (y.length == 0) return x
let result = []
// Iterate over both sets in parallel, using the middle coordinate
// system (B in x, A in y) to synchronize.
for (let iX = 0, iY = 0, curX: Change<Data> | null = x[0], curY: Change<Data> | null = y[0];;) {
if (!curX && !curY) {
return result
} else if (curX && (!curY || curX.toB < curY.fromA)) { // curX entirely in front of curY
let off = iY ? y[iY - 1].toB - y[iY - 1].toA : 0
result.push(off == 0 ? curX :
new Change(curX.fromA, curX.toA, curX.fromB + off, curX.toB + off,
curX.deleted, curX.inserted))
curX = iX++ == x.length ? null : x[iX]
} else if (curY && (!curX || curY.toA < curX.fromB)) { // curY entirely in front of curX
let off = iX ? x[iX - 1].toB - x[iX - 1].toA : 0
result.push(off == 0 ? curY :
new Change(curY.fromA - off, curY.toA - off, curY.fromB, curY.toB,
curY.deleted, curY.inserted))
curY = iY++ == y.length ? null : y[iY]
} else { // Touch, need to merge
// The rules for merging ranges are that deletions from the
// old set and insertions from the new are kept. Areas of the
// middle document covered by a but not by b are insertions
// from a that need to be added, and areas covered by b but
// not a are deletions from b that need to be added.
let pos = Math.min(curX!.fromB, curY!.fromA)
let fromA = Math.min(curX!.fromA, curY!.fromA - (iX ? x[iX - 1].toB - x[iX - 1].toA : 0)), toA = fromA
let fromB = Math.min(curY!.fromB, curX!.fromB + (iY ? y[iY - 1].toB - y[iY - 1].toA : 0)), toB = fromB
let deleted = Span.none, inserted = Span.none
// Used to prevent appending ins/del range for the same Change twice
let enteredX = false, enteredY = false
// Need to have an inner loop since any number of further
// ranges might be touching this group
for (;;) {
let nextX = !curX ? 2e8 : pos >= curX.fromB ? curX.toB : curX.fromB
let nextY = !curY ? 2e8 : pos >= curY.fromA ? curY.toA : curY.fromA
let next = Math.min(nextX, nextY)
let inX = curX && pos >= curX.fromB, inY = curY && pos >= curY.fromA
if (!inX && !inY) break
if (inX && pos == curX!.fromB && !enteredX) {
deleted = Span.join(deleted, curX!.deleted, combine)
toA += curX!.lenA
enteredX = true
}
if (inX && !inY) {
inserted = Span.join(inserted, Span.slice(curX!.inserted, pos - curX!.fromB, next - curX!.fromB), combine)
toB += next - pos
}
if (inY && pos == curY!.fromA && !enteredY) {
inserted = Span.join(inserted, curY!.inserted, combine)
toB += curY!.lenB
enteredY = true
}
if (inY && !inX) {
deleted = Span.join(deleted, Span.slice(curY!.deleted, pos - curY!.fromA, next - curY!.fromA), combine)
toA += next - pos
}
if (inX && next == curX!.toB) {
curX = iX++ == x.length ? null : x[iX]
enteredX = false
}
if (inY && next == curY!.toA) {
curY = iY++ == y.length ? null : y[iY]
enteredY = false
}
pos = next
}
if (fromA < toA || fromB < toB)
result.push(new Change(fromA, toA, fromB, toB, deleted, inserted))
}
}
}
/// Deserialize a change from JSON format.
static fromJSON<Data>(json: ChangeJSON<Data>) {
return new Change(json.fromA, json.toA, json.fromB, json.toB,
json.deleted.map(d => new Span(d.length, d.data)),
json.inserted.map(d => new Span(d.length, d.data)))
}
/// Returns a JSON-serializeable object to represent this change.
toJSON(): ChangeJSON<Data> { return this }
}
/// JSON-serialized form of a change.
export type ChangeJSON<Data> = {
fromA: number, toA: number,
fromB: number, toB: number,
deleted: readonly {length: number, data: Data}[],
inserted: readonly {length: number, data: Data}[]
}