@secureport/core 0.2.0 → 0.3.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.
- package/README.md +67 -31
- package/dist/coverage.d.ts +21 -0
- package/dist/coverage.d.ts.map +1 -0
- package/dist/coverage.js +65 -0
- package/dist/coverage.js.map +1 -0
- package/dist/finding.d.ts +89 -0
- package/dist/finding.d.ts.map +1 -0
- package/dist/finding.js +2 -0
- package/dist/finding.js.map +1 -0
- package/dist/fingerprint.d.ts +185 -0
- package/dist/fingerprint.d.ts.map +1 -0
- package/dist/fingerprint.js +247 -0
- package/dist/fingerprint.js.map +1 -0
- package/dist/import/nuclei.d.ts +39 -0
- package/dist/import/nuclei.d.ts.map +1 -0
- package/dist/import/nuclei.js +115 -0
- package/dist/import/nuclei.js.map +1 -0
- package/dist/import/zap.d.ts +26 -0
- package/dist/import/zap.d.ts.map +1 -0
- package/dist/import/zap.js +119 -0
- package/dist/import/zap.js.map +1 -0
- package/dist/index.d.ts +31 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +22 -2
- package/dist/index.js.map +1 -1
- package/dist/issue.d.ts +190 -11
- package/dist/issue.d.ts.map +1 -1
- package/dist/reconcile.d.ts +115 -10
- package/dist/reconcile.d.ts.map +1 -1
- package/dist/reconcile.js +306 -12
- package/dist/reconcile.js.map +1 -1
- package/dist/run.d.ts +134 -0
- package/dist/run.d.ts.map +1 -0
- package/dist/run.js +2 -0
- package/dist/run.js.map +1 -0
- package/dist/severity.d.ts +191 -0
- package/dist/severity.d.ts.map +1 -0
- package/dist/severity.js +171 -0
- package/dist/severity.js.map +1 -0
- package/dist/snapshot-builder.d.ts +70 -0
- package/dist/snapshot-builder.d.ts.map +1 -0
- package/dist/snapshot-builder.js +148 -0
- package/dist/snapshot-builder.js.map +1 -0
- package/dist/snapshot.d.ts +125 -0
- package/dist/snapshot.d.ts.map +1 -0
- package/dist/snapshot.js +2 -0
- package/dist/snapshot.js.map +1 -0
- package/package.json +23 -2
- package/src/coverage.ts +65 -0
- package/src/finding.ts +112 -0
- package/src/fingerprint.ts +315 -0
- package/src/import/nuclei.ts +173 -0
- package/src/import/zap.ts +161 -0
- package/src/index.ts +56 -2
- package/src/issue.ts +244 -11
- package/src/reconcile.ts +421 -17
- package/src/run.ts +163 -0
- package/src/severity.ts +250 -0
- package/src/snapshot-builder.ts +199 -0
- package/src/snapshot.ts +146 -0
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAIH,OAAO,EACL,mBAAmB,EACnB,gBAAgB,EAChB,YAAY,EACZ,WAAW,EACX,iBAAiB,EACjB,OAAO,GACR,MAAM,kBAAkB,CAAC;AAU1B,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAE/C,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE5C,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAErE,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAI3C,OAAO,EACL,kBAAkB,EAClB,cAAc,EACd,0BAA0B,EAC1B,gBAAgB,EAChB,gBAAgB,EAChB,YAAY,EACZ,QAAQ,EACR,SAAS,GACV,MAAM,eAAe,CAAC"}
|
package/dist/issue.d.ts
CHANGED
|
@@ -1,16 +1,195 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
/** Lifecycle state of an {@link Issue}. */
|
|
4
|
-
export type IssueStatus = 'open' | 'triaging' | 'accepted' | 'resolved';
|
|
1
|
+
import type { Severity } from './severity.js';
|
|
2
|
+
import type { RunKind } from './run.js';
|
|
5
3
|
/**
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
4
|
+
* Where an issue is in its lifecycle.
|
|
5
|
+
*
|
|
6
|
+
* - `open` — currently detected, or detected recently enough not to be resolved.
|
|
7
|
+
* - `resolved` — no longer detected by runs that covered it.
|
|
8
|
+
* - `regressed` — was resolved, and has come back. Distinct from `open` because
|
|
9
|
+
* a returning issue is a different and more interesting event than a new one.
|
|
10
|
+
* - `ignored` — deliberately suppressed, with a reason and an author.
|
|
11
|
+
*
|
|
12
|
+
* There is no `triaging` state. Triage is a person's activity, not an issue's
|
|
13
|
+
* condition, and a status nobody can define precisely is a status nobody
|
|
14
|
+
* filters on correctly.
|
|
15
|
+
*/
|
|
16
|
+
export type IssueStatus = 'open' | 'resolved' | 'regressed' | 'ignored';
|
|
17
|
+
/**
|
|
18
|
+
* How an issue came into existence.
|
|
19
|
+
*
|
|
20
|
+
* Matters because **manual-origin issues never auto-resolve** (invariant 6): a
|
|
21
|
+
* human closes what a human opened.
|
|
22
|
+
*/
|
|
23
|
+
export type IssueOrigin = RunKind;
|
|
24
|
+
/**
|
|
25
|
+
* Why an issue was suppressed.
|
|
26
|
+
*
|
|
27
|
+
* A fixed set rather than free text because this list is what a
|
|
28
|
+
* suppressed-findings appendix is grouped by, and an auditor reading it needs
|
|
29
|
+
* categories that mean the same thing every time.
|
|
30
|
+
*/
|
|
31
|
+
export type IgnoreReason = 'false_positive' | 'accepted_risk' | 'out_of_scope' | 'duplicate';
|
|
32
|
+
/**
|
|
33
|
+
* How widely an ignore applies.
|
|
34
|
+
*
|
|
35
|
+
* - `target` — this issue on this target only. The default.
|
|
36
|
+
* - `org` — the same weakness anywhere in the organisation, matched on
|
|
37
|
+
* `vulnKey` and normalised location, ignoring the parameter.
|
|
38
|
+
*/
|
|
39
|
+
export type IgnoreScope = 'target' | 'org';
|
|
40
|
+
/**
|
|
41
|
+
* The tracked record that findings reconcile into, and the entity the product
|
|
42
|
+
* is actually about.
|
|
43
|
+
*
|
|
44
|
+
* An issue persists across runs. It carries status, severity, age, ownership
|
|
45
|
+
* and history; findings are the evidence beneath it. The distinction is the
|
|
46
|
+
* whole point of the model: two scanners reporting the same weakness produce
|
|
47
|
+
* one issue with two sources, not two rows, and an issue that comes back after
|
|
48
|
+
* being fixed is the same issue regressing rather than a new discovery.
|
|
49
|
+
*
|
|
50
|
+
* Unique on `(orgId, targetId, fingerprint)`.
|
|
51
|
+
*
|
|
52
|
+
* **Issue state changes only through reconciliation or an issue-service
|
|
53
|
+
* method, and every change emits an {@link IssueEvent}** (invariant 2).
|
|
9
54
|
*/
|
|
10
55
|
export interface Issue {
|
|
11
|
-
id
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
56
|
+
/** Unique id. */
|
|
57
|
+
readonly id: string;
|
|
58
|
+
/** Organisation this issue belongs to. */
|
|
59
|
+
readonly orgId: string;
|
|
60
|
+
/**
|
|
61
|
+
* Target this issue is on.
|
|
62
|
+
*
|
|
63
|
+
* Part of the identity, and deliberately so: because the fingerprint includes
|
|
64
|
+
* the target, **the same weakness in staging and in production is two
|
|
65
|
+
* issues**. They are two systems, fixed separately, and verifying one must
|
|
66
|
+
* never authorise the other.
|
|
67
|
+
*/
|
|
68
|
+
readonly targetId: string;
|
|
69
|
+
/** Content address of the weakness. Stable across runs and engines. */
|
|
70
|
+
readonly fingerprint: string;
|
|
71
|
+
/** Which algorithm produced {@link Issue.fingerprint}. */
|
|
72
|
+
readonly fingerprintVersion: string;
|
|
73
|
+
/** Human-readable name. Seeded from the first finding; editable afterwards. */
|
|
74
|
+
readonly title: string;
|
|
75
|
+
/** Engine-independent key for the weakness class. */
|
|
76
|
+
readonly vulnKey: string;
|
|
77
|
+
/** CWE identifier, where one applies. */
|
|
78
|
+
readonly cwe?: string;
|
|
79
|
+
/** Where the weakness is. */
|
|
80
|
+
readonly location: string;
|
|
81
|
+
/** The specific parameter implicated, where there is one. */
|
|
82
|
+
readonly parameter?: string;
|
|
83
|
+
/** Lifecycle state. */
|
|
84
|
+
readonly status: IssueStatus;
|
|
85
|
+
/**
|
|
86
|
+
* Severity as most recently detected.
|
|
87
|
+
*
|
|
88
|
+
* Follows the evidence. Changing it emits `severity_detected_changed`.
|
|
89
|
+
*/
|
|
90
|
+
readonly detectedSeverity: Severity;
|
|
91
|
+
/**
|
|
92
|
+
* Severity that reports show, and that the SLA deadline derives from.
|
|
93
|
+
*
|
|
94
|
+
* Follows {@link Issue.detectedSeverity} unless a human has overridden it. A
|
|
95
|
+
* null override means the two move together.
|
|
96
|
+
*/
|
|
97
|
+
readonly effectiveSeverity: Severity;
|
|
98
|
+
/** Why the severity was overridden. Present only when it was. */
|
|
99
|
+
readonly severityOverrideReason?: string;
|
|
100
|
+
/** Who overrode it. */
|
|
101
|
+
readonly severityOverriddenBy?: string;
|
|
102
|
+
/** When it was overridden. */
|
|
103
|
+
readonly severityOverriddenAt?: Date;
|
|
104
|
+
/**
|
|
105
|
+
* When this issue was first seen.
|
|
106
|
+
*
|
|
107
|
+
* **Never reset** (invariant 4) — not by a regression, not by a severity
|
|
108
|
+
* change, not by anything. It is what "how long has this been open" means,
|
|
109
|
+
* and resetting it would quietly erase the age of the oldest problems.
|
|
110
|
+
*/
|
|
111
|
+
readonly firstSeen: Date;
|
|
112
|
+
/** When it was most recently detected. Keeps updating even while ignored. */
|
|
113
|
+
readonly lastSeen: Date;
|
|
114
|
+
/** When it was resolved, if it has been. */
|
|
115
|
+
readonly resolvedAt?: Date;
|
|
116
|
+
/** When it most recently regressed, if it has. */
|
|
117
|
+
readonly reopenedAt?: Date;
|
|
118
|
+
/**
|
|
119
|
+
* Consecutive covering runs that did not detect this issue.
|
|
120
|
+
*
|
|
121
|
+
* The auto-resolve counter, and the flapping guard: a single missed
|
|
122
|
+
* detection from a timeout or a rate limit should not close an issue. Reset
|
|
123
|
+
* to zero the moment the issue is seen again.
|
|
124
|
+
*/
|
|
125
|
+
readonly consecutiveMisses: number;
|
|
126
|
+
/** How the issue came into existence. */
|
|
127
|
+
readonly origin: IssueOrigin;
|
|
128
|
+
/** Who it is assigned to, if anyone. */
|
|
129
|
+
readonly assignee?: string;
|
|
130
|
+
/** Key in an external tracker, set by the export. */
|
|
131
|
+
readonly externalRef?: string;
|
|
132
|
+
/** Why it is suppressed. Present only while `status` is `ignored`. */
|
|
133
|
+
readonly ignoreReason?: IgnoreReason;
|
|
134
|
+
/** Free-text justification for the suppression. Required when ignoring. */
|
|
135
|
+
readonly ignoreComment?: string;
|
|
136
|
+
/** How widely the suppression applies. */
|
|
137
|
+
readonly ignoreScope?: IgnoreScope;
|
|
138
|
+
/** When the suppression lapses, after which the issue re-surfaces. */
|
|
139
|
+
readonly ignoreExpiresAt?: Date;
|
|
140
|
+
/** Who suppressed it. */
|
|
141
|
+
readonly ignoredBy?: string;
|
|
142
|
+
/**
|
|
143
|
+
* Detected severity at the moment it was ignored.
|
|
144
|
+
*
|
|
145
|
+
* Recorded so that a *detected increase* re-surfaces the issue
|
|
146
|
+
* automatically: accepting the risk of a medium is not accepting the risk of
|
|
147
|
+
* the critical it later turns out to be.
|
|
148
|
+
*/
|
|
149
|
+
readonly severityAtIgnore?: Severity;
|
|
150
|
+
/**
|
|
151
|
+
* When remediation is due, derived from {@link Issue.effectiveSeverity}.
|
|
152
|
+
*
|
|
153
|
+
* Recomputed whenever the effective severity changes. `null` where the
|
|
154
|
+
* severity carries no deadline.
|
|
155
|
+
*/
|
|
156
|
+
readonly slaDueAt?: Date | null;
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Something that happened to an issue.
|
|
160
|
+
*
|
|
161
|
+
* The audit trail and the source of every analytic. Nothing scans findings at
|
|
162
|
+
* request time: "issues resolved this quarter" and "regressions caught" are
|
|
163
|
+
* both counts over this log.
|
|
164
|
+
*/
|
|
165
|
+
export type IssueEventType = 'created' | 'seen' | 'severity_detected_changed' | 'severity_overridden' | 'resolved' | 'reopened' | 'ignored' | 'unignored' | 'commented' | 'assigned' | 'merged' | 'exported';
|
|
166
|
+
/**
|
|
167
|
+
* An append-only record of a single change to an issue.
|
|
168
|
+
*
|
|
169
|
+
* Append-only is load-bearing rather than stylistic: it is what makes the
|
|
170
|
+
* history trustworthy as evidence, and what lets analytics be a query rather
|
|
171
|
+
* than a recomputation.
|
|
172
|
+
*/
|
|
173
|
+
export interface IssueEvent {
|
|
174
|
+
/** Unique id. */
|
|
175
|
+
readonly id: string;
|
|
176
|
+
/** Organisation this event belongs to. */
|
|
177
|
+
readonly orgId: string;
|
|
178
|
+
/** The issue it happened to. */
|
|
179
|
+
readonly issueId: string;
|
|
180
|
+
/** The run that caused it, where a run did. Absent for human actions. */
|
|
181
|
+
readonly runId?: string;
|
|
182
|
+
/** What happened. */
|
|
183
|
+
readonly type: IssueEventType;
|
|
184
|
+
/**
|
|
185
|
+
* Who or what did it — a user id, an API key id, or the reconciler.
|
|
186
|
+
*
|
|
187
|
+
* Never optional: an audit trail that cannot say who is not one.
|
|
188
|
+
*/
|
|
189
|
+
readonly actor: string;
|
|
190
|
+
/** Type-specific detail, e.g. the old and new severity. */
|
|
191
|
+
readonly payload?: Readonly<Record<string, unknown>>;
|
|
192
|
+
/** When it happened. */
|
|
193
|
+
readonly createdAt: Date;
|
|
15
194
|
}
|
|
16
195
|
//# sourceMappingURL=issue.d.ts.map
|
package/dist/issue.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"issue.d.ts","sourceRoot":"","sources":["../src/issue.ts"],"names":[],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"issue.d.ts","sourceRoot":"","sources":["../src/issue.ts"],"names":[],"mappings":"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"}
|
package/dist/reconcile.d.ts
CHANGED
|
@@ -1,15 +1,120 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
import type { Finding } from './finding.js';
|
|
2
|
+
import type { Issue, IssueEvent } from './issue.js';
|
|
3
|
+
import type { Run, RunSummary } from './run.js';
|
|
4
|
+
import { type SlaPolicy } from './severity.js';
|
|
5
|
+
/**
|
|
6
|
+
* Everything reconciliation needs, and nothing it could get for itself.
|
|
7
|
+
*
|
|
8
|
+
* The clock and the id generator are arguments because the function is pure:
|
|
9
|
+
* no `Date.now()`, no `randomUUID()`, no I/O. The same inputs give the same
|
|
10
|
+
* issues and the same events, on any machine, which is what makes golden-file
|
|
11
|
+
* testing possible and what lets a run be replayed.
|
|
12
|
+
*/
|
|
13
|
+
export interface ReconcileInput {
|
|
14
|
+
/**
|
|
15
|
+
* Every known issue for this run's target.
|
|
16
|
+
*
|
|
17
|
+
* Issues for other targets must not be passed in: the fingerprint already
|
|
18
|
+
* includes the target, so they could never match, and including them would
|
|
19
|
+
* return them as untouched output and invite a caller to write them back.
|
|
20
|
+
*/
|
|
21
|
+
readonly issues: readonly Issue[];
|
|
22
|
+
/** The findings this run produced. */
|
|
23
|
+
readonly findings: readonly Finding[];
|
|
24
|
+
/** The run itself, which supplies the target, the kind and the coverage. */
|
|
25
|
+
readonly run: Run;
|
|
26
|
+
/** The moment the run is being reconciled at. */
|
|
27
|
+
readonly now: Date;
|
|
28
|
+
/**
|
|
29
|
+
* Supplies ids for issues and events.
|
|
30
|
+
*
|
|
31
|
+
* Injected rather than generated so the function stays deterministic. A
|
|
32
|
+
* caller in production passes `randomUUID`; a test passes a counter.
|
|
33
|
+
*/
|
|
34
|
+
readonly newId: () => string;
|
|
35
|
+
/**
|
|
36
|
+
* Who to record as having made these changes.
|
|
37
|
+
*
|
|
38
|
+
* Defaults to `reconciler`. An audit trail that cannot say who is not one.
|
|
39
|
+
*/
|
|
40
|
+
readonly actor?: string;
|
|
41
|
+
/** Remediation windows, for recomputing `slaDueAt`. */
|
|
42
|
+
readonly slaPolicy?: SlaPolicy;
|
|
43
|
+
/**
|
|
44
|
+
* How many consecutive covering runs may miss an issue before it resolves.
|
|
45
|
+
*
|
|
46
|
+
* **The flapping guard.** Defaults to **1 for uploads** — the person
|
|
47
|
+
* uploading is asserting a complete result — and **2 for scans**, where a
|
|
48
|
+
* timeout or a rate limit causes a miss that means nothing. Between the first
|
|
49
|
+
* miss and resolution the issue stays open, and reports can say "not seen in
|
|
50
|
+
* last run".
|
|
51
|
+
*
|
|
52
|
+
* Per organisation in the hosted product; an argument here, because this
|
|
53
|
+
* function has nowhere to read configuration from.
|
|
54
|
+
*/
|
|
55
|
+
readonly autoResolveThreshold?: number;
|
|
56
|
+
/** How long the run took, for the summary. */
|
|
57
|
+
readonly durationMs?: number;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* What reconciliation decided.
|
|
61
|
+
*
|
|
62
|
+
* Returns **every** issue for the target, changed or not, so a caller can write
|
|
63
|
+
* the set back without working out which ones moved. Events are returned rather
|
|
64
|
+
* than applied: persisting them is the caller's transaction, and reconciliation
|
|
65
|
+
* has no I/O.
|
|
66
|
+
*/
|
|
67
|
+
export interface ReconcileResult {
|
|
68
|
+
/** Every issue for the target, after reconciliation. */
|
|
69
|
+
readonly issues: readonly Issue[];
|
|
70
|
+
/** What changed, in the order it was decided. */
|
|
71
|
+
readonly events: readonly IssueEvent[];
|
|
72
|
+
/** What the run did, in the terms a report opens with. */
|
|
73
|
+
readonly summary: RunSummary;
|
|
6
74
|
}
|
|
7
75
|
/**
|
|
8
|
-
*
|
|
9
|
-
*
|
|
76
|
+
* Turns a run's findings into issue state.
|
|
77
|
+
*
|
|
78
|
+
* **The only writer of issue state, and a pure function.** Given the same
|
|
79
|
+
* issues, findings, run and clock it produces the same result every time. It
|
|
80
|
+
* performs no I/O, reads no clock and generates no randomness; the caller
|
|
81
|
+
* supplies `now` and `newId` and persists what comes back.
|
|
82
|
+
*
|
|
83
|
+
* This half handles everything a **present** finding causes:
|
|
84
|
+
*
|
|
85
|
+
* - a fingerprint that matches nothing opens an issue (`created`);
|
|
86
|
+
* - a fingerprint that matches attaches evidence and refreshes `lastSeen`,
|
|
87
|
+
* clearing the miss counter (`seen`);
|
|
88
|
+
* - a `resolved` issue seen again becomes `regressed` (`reopened`) — a returning
|
|
89
|
+
* problem is a different and more interesting event than a new one;
|
|
90
|
+
* - a changed detected severity updates the issue, and updates the *effective*
|
|
91
|
+
* severity and the SLA deadline only where nobody has overridden it
|
|
92
|
+
* (`severity_detected_changed`);
|
|
93
|
+
* - an `ignored` issue whose detected severity has risen above what was
|
|
94
|
+
* accepted comes back (`unignored`) — accepting the risk of a medium is not
|
|
95
|
+
* accepting the risk of the critical it turned out to be.
|
|
96
|
+
*
|
|
97
|
+
* And everything an **absent** finding causes:
|
|
98
|
+
*
|
|
99
|
+
* - an issue the run covered but did not see takes a miss, and resolves once it
|
|
100
|
+
* has missed enough consecutive covering runs (`resolved`);
|
|
101
|
+
* - an issue **outside** the run's coverage is untouched — not resolved, and not
|
|
102
|
+
* even counted as a miss, so a sequence of narrow scans cannot accumulate
|
|
103
|
+
* misses against a path none of them looked at (invariant 5);
|
|
104
|
+
* - a **manual-origin** issue never auto-resolves; a human closes what a human
|
|
105
|
+
* opened (invariant 6);
|
|
106
|
+
* - a suppression whose expiry has passed lapses (`unignored`), regardless of
|
|
107
|
+
* coverage — an ignore expiring is a decision timing out, not an observation.
|
|
108
|
+
*
|
|
109
|
+
* Finally it computes the {@link RunSummary}: counts by severity for new, still
|
|
110
|
+
* open, resolved, regressed and ignored, plus the exposure score. Ignored issues
|
|
111
|
+
* are excluded from every count but their own, and from exposure, and never from
|
|
112
|
+
* the suppressed appendix (invariant 7).
|
|
113
|
+
*
|
|
114
|
+
* `firstSeen` is never reset, by anything here (invariant 4).
|
|
10
115
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
116
|
+
* @param input - Issues, findings, the run, the clock and an id source.
|
|
117
|
+
* @returns Every issue for the target, plus the events that explain the changes.
|
|
13
118
|
*/
|
|
14
|
-
export declare function reconcile(
|
|
119
|
+
export declare function reconcile(input: ReconcileInput): ReconcileResult;
|
|
15
120
|
//# sourceMappingURL=reconcile.d.ts.map
|
package/dist/reconcile.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reconcile.d.ts","sourceRoot":"","sources":["../src/reconcile.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"reconcile.d.ts","sourceRoot":"","sources":["../src/reconcile.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACpD,OAAO,KAAK,EAAE,GAAG,EAAW,UAAU,EAAkB,MAAM,UAAU,CAAC;AACzE,OAAO,EAML,KAAK,SAAS,EACf,MAAM,eAAe,CAAC;AAGvB;;;;;;;GAOG;AACH,MAAM,WAAW,cAAc;IAC7B;;;;;;OAMG;IACH,QAAQ,CAAC,MAAM,EAAE,SAAS,KAAK,EAAE,CAAC;IAElC,sCAAsC;IACtC,QAAQ,CAAC,QAAQ,EAAE,SAAS,OAAO,EAAE,CAAC;IAEtC,4EAA4E;IAC5E,QAAQ,CAAC,GAAG,EAAE,GAAG,CAAC;IAElB,iDAAiD;IACjD,QAAQ,CAAC,GAAG,EAAE,IAAI,CAAC;IAEnB;;;;;OAKG;IACH,QAAQ,CAAC,KAAK,EAAE,MAAM,MAAM,CAAC;IAE7B;;;;OAIG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAExB,uDAAuD;IACvD,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,CAAC;IAE/B;;;;;;;;;;;OAWG;IACH,QAAQ,CAAC,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAEvC,8CAA8C;IAC9C,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,eAAe;IAC9B,wDAAwD;IACxD,QAAQ,CAAC,MAAM,EAAE,SAAS,KAAK,EAAE,CAAC;IAElC,iDAAiD;IACjD,QAAQ,CAAC,MAAM,EAAE,SAAS,UAAU,EAAE,CAAC;IAEvC,0DAA0D;IAC1D,QAAQ,CAAC,OAAO,EAAE,UAAU,CAAC;CAC9B;AAgED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,cAAc,GAAG,eAAe,CA0OhE"}
|