ghcr-manager 0.0.4 → 0.9.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/CHANGELOG.md +50 -1
- package/README.md +166 -57
- package/dist/cleanup-summary/_cleanup-summary-markdown.d.ts +6 -0
- package/dist/cleanup-summary/_cleanup-summary-markdown.js +113 -0
- package/dist/cleanup-summary/_cleanup-summary.d.ts +40 -0
- package/dist/cleanup-summary/_cleanup-summary.js +40 -0
- package/dist/cleanup-summary/index.d.ts +2 -0
- package/dist/cleanup-summary/index.js +2 -0
- package/dist/cli/_args.d.ts +1 -0
- package/dist/cli/_args.js +3 -0
- package/dist/cli/_cleanup-command.d.ts +1 -0
- package/dist/cli/_cleanup-command.js +63 -0
- package/dist/cli/_db-merge-command.d.ts +1 -0
- package/dist/cli/_db-merge-command.js +41 -0
- package/dist/cli/_github-output.d.ts +10 -0
- package/dist/cli/_github-output.js +13 -0
- package/dist/cli/_logger.d.ts +2 -1
- package/dist/cli/_logger.js +2 -0
- package/dist/cli/_older-than.d.ts +5 -0
- package/dist/cli/_older-than.js +42 -0
- package/dist/cli/_planner-options.d.ts +20 -0
- package/dist/cli/_planner-options.js +101 -0
- package/dist/cli/_scan-command.js +11 -4
- package/dist/cli/_tag-selector-resolver.d.ts +3 -0
- package/dist/cli/_tag-selector-resolver.js +109 -0
- package/dist/cli/_untag-command.d.ts +1 -0
- package/dist/cli/_untag-command.js +57 -0
- package/dist/cli/index.js +19 -1
- package/dist/config/_service-constants.d.ts +3 -0
- package/dist/config/_service-constants.js +3 -0
- package/dist/{tuning → config}/index.d.ts +3 -0
- package/dist/{tuning → config}/index.js +3 -0
- package/dist/core/_github-package-owner.d.ts +11 -0
- package/dist/core/_github-package-owner.js +45 -0
- package/dist/core/_http-error.d.ts +6 -0
- package/dist/core/_http-error.js +33 -0
- package/dist/core/_types.d.ts +3 -2
- package/dist/core/index.d.ts +4 -1
- package/dist/core/index.js +2 -1
- package/dist/db/_cleanup-run-writer.d.ts +10 -0
- package/dist/db/_cleanup-run-writer.js +73 -0
- package/dist/db/_db-merge-cleanup-copy.d.ts +7 -0
- package/dist/db/_db-merge-cleanup-copy.js +122 -0
- package/dist/db/_db-merge-history.d.ts +2 -0
- package/dist/db/_db-merge-history.js +15 -0
- package/dist/db/_db-merge-repository.d.ts +8 -0
- package/dist/db/_db-merge-repository.js +95 -0
- package/dist/db/_db-merge-scan-copy.d.ts +10 -0
- package/dist/db/_db-merge-scan-copy.js +69 -0
- package/dist/db/_db-merge-types.d.ts +44 -0
- package/dist/db/_db-merge-types.js +1 -0
- package/dist/db/_github-actions-run-url.d.ts +1 -0
- package/dist/db/_github-actions-run-url.js +9 -0
- package/dist/db/_scan-writer.d.ts +3 -1
- package/dist/db/_scan-writer.js +28 -13
- package/dist/db/_snapshot-repository.d.ts +9 -9
- package/dist/db/_snapshot-repository.js +37 -49
- package/dist/db/_sql-placeholders.d.ts +2 -0
- package/dist/db/_sql-placeholders.js +16 -0
- package/dist/db/index.d.ts +5 -0
- package/dist/db/index.js +3 -0
- package/dist/db/planner/_planner-delete-tag-root-targets.d.ts +7 -0
- package/dist/db/planner/_planner-delete-tag-root-targets.js +130 -0
- package/dist/db/planner/_planner-direct-target-tags.d.ts +6 -0
- package/dist/db/planner/_planner-direct-target-tags.js +47 -0
- package/dist/db/planner/_planner-keep-tagged-root-targets.d.ts +7 -0
- package/dist/db/planner/_planner-keep-tagged-root-targets.js +74 -0
- package/dist/db/planner/_planner-output.d.ts +5 -0
- package/dist/db/planner/_planner-output.js +101 -0
- package/dist/db/planner/_planner-plan-artifacts.d.ts +7 -0
- package/dist/db/planner/_planner-plan-artifacts.js +211 -0
- package/dist/db/planner/_planner-repository.d.ts +34 -0
- package/dist/db/planner/_planner-repository.js +126 -0
- package/dist/db/planner/_planner-sql.d.ts +12 -0
- package/dist/db/planner/_planner-sql.js +35 -0
- package/dist/db/planner/_planner-tag-selectors.d.ts +8 -0
- package/dist/db/planner/_planner-tag-selectors.js +57 -0
- package/dist/db/planner/_planner-tagged-root-targets.d.ts +15 -0
- package/dist/db/planner/_planner-tagged-root-targets.js +19 -0
- package/dist/db/planner/_planner-tagged-targets.d.ts +8 -0
- package/dist/db/planner/_planner-tagged-targets.js +16 -0
- package/dist/db/planner/_planner-types.d.ts +135 -0
- package/dist/db/planner/_planner-types.js +38 -0
- package/dist/db/planner/_planner-untagged-targets.d.ts +9 -0
- package/dist/db/planner/_planner-untagged-targets.js +91 -0
- package/dist/db/planner/index.d.ts +2 -0
- package/dist/db/planner/index.js +1 -0
- package/dist/execute/_http.d.ts +7 -0
- package/dist/execute/_http.js +48 -0
- package/dist/execute/_manifest-detach.d.ts +4 -0
- package/dist/execute/_manifest-detach.js +31 -0
- package/dist/execute/_package-version-delete-client.d.ts +4 -0
- package/dist/execute/_package-version-delete-client.js +34 -0
- package/dist/execute/_package-version-page-client.d.ts +14 -0
- package/dist/execute/_package-version-page-client.js +64 -0
- package/dist/execute/_package-version-tag-source-client.d.ts +12 -0
- package/dist/execute/_package-version-tag-source-client.js +65 -0
- package/dist/execute/_plan-executor.d.ts +3 -0
- package/dist/execute/_plan-executor.js +47 -0
- package/dist/execute/_registry-manifest-client.d.ts +12 -0
- package/dist/execute/_registry-manifest-client.js +79 -0
- package/dist/execute/_registry-token-client.d.ts +4 -0
- package/dist/execute/_registry-token-client.js +37 -0
- package/dist/execute/_types.d.ts +51 -0
- package/dist/execute/_types.js +1 -0
- package/dist/execute/_untag-client.d.ts +2 -0
- package/dist/execute/_untag-client.js +71 -0
- package/dist/execute/index.d.ts +5 -0
- package/dist/execute/index.js +3 -0
- package/dist/ingest/github/_manifest-client.d.ts +7 -1
- package/dist/ingest/github/_manifest-client.js +8 -0
- package/dist/ingest/github/_manifest-ingest.js +39 -53
- package/dist/ingest/github/_manifest-kind.d.ts +20 -0
- package/dist/ingest/github/_manifest-kind.js +50 -0
- package/dist/ingest/github/_package-metadata-load.d.ts +5 -0
- package/dist/ingest/github/_package-metadata-load.js +45 -0
- package/dist/ingest/github/_package-version-page-load.d.ts +1 -1
- package/dist/ingest/github/_package-version-page-load.js +8 -5
- package/dist/ingest/github/_packages-client.d.ts +1 -1
- package/dist/ingest/github/_packages-client.js +21 -4
- package/dist/ingest/github/_parallel-paginated-ingest.d.ts +1 -0
- package/dist/ingest/github/_parallel-paginated-ingest.js +2 -2
- package/dist/ingest/github/_shared.d.ts +1 -1
- package/dist/ingest/github/_shared.js +2 -34
- package/dist/ingest/github/index.d.ts +4 -0
- package/dist/ingest/github/index.js +8 -5
- package/package.json +7 -5
- package/resources/sql/schema/001_schema.sql +82 -8
- package/resources/sql/views/001_v_latest_scan_per_package.sql +2 -2
- package/resources/sql/views/003_v_scan_root_manifests.sql +43 -0
- package/resources/sql/views/004_v_digest_derived_tag_relations.sql +51 -0
- package/resources/sql/views/005_v_cleanup_root_closure_members.sql +100 -0
- package/resources/sql/views/006_v_cleanup_blocking_overlaps.sql +42 -0
- package/dist/ingest/github/_paginated-ingest.d.ts +0 -11
- package/dist/ingest/github/_paginated-ingest.js +0 -28
- package/resources/sql/views/003_v_missing_digests_related_manifests.sql +0 -78
- package/resources/sql/views/004_v_manifests_related_manifests.sql +0 -142
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import { mapBlockedRootRow, mapClosureManifestRow } from "./_planner-types.js";
|
|
2
|
+
export class PlannerPlanArtifacts {
|
|
3
|
+
#sql;
|
|
4
|
+
constructor(sql) {
|
|
5
|
+
this.#sql = sql;
|
|
6
|
+
}
|
|
7
|
+
build(scanId, directTargetRoots) {
|
|
8
|
+
const deleteRootCandidates = directTargetRoots.filter((root) => root.selectionMode === "delete-root");
|
|
9
|
+
if (deleteRootCandidates.length === 0) {
|
|
10
|
+
return {
|
|
11
|
+
closureManifests: [],
|
|
12
|
+
blockedRoots: [],
|
|
13
|
+
fullyDeletableRoots: []
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
return this.#withDirectTargetRootsTempTable(deleteRootCandidates, () => {
|
|
17
|
+
const closureManifests = this.#listClosureManifests(scanId);
|
|
18
|
+
const blockedRoots = this.#listBlockedRoots(scanId);
|
|
19
|
+
const blockedVersionIds = new Set(blockedRoots.map((root) => root.blockedVersionId));
|
|
20
|
+
const fullyDeletableRoots = deleteRootCandidates.filter((root) => !blockedVersionIds.has(root.versionId));
|
|
21
|
+
return {
|
|
22
|
+
closureManifests,
|
|
23
|
+
blockedRoots,
|
|
24
|
+
fullyDeletableRoots
|
|
25
|
+
};
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
#listClosureManifests(scanId) {
|
|
29
|
+
const sql = `
|
|
30
|
+
WITH direct_target_closure AS (
|
|
31
|
+
SELECT
|
|
32
|
+
dtr.root_version_id AS source_version_id,
|
|
33
|
+
dtr.root_digest AS source_digest,
|
|
34
|
+
dtr.root_version_id AS member_version_id,
|
|
35
|
+
dtr.root_digest AS member_digest,
|
|
36
|
+
dtr.root_manifest_kind AS member_manifest_kind,
|
|
37
|
+
0 AS hops_from_root,
|
|
38
|
+
'root' AS member_role
|
|
39
|
+
FROM temp_direct_target_roots dtr
|
|
40
|
+
|
|
41
|
+
UNION ALL
|
|
42
|
+
|
|
43
|
+
SELECT
|
|
44
|
+
dtr.root_version_id AS source_version_id,
|
|
45
|
+
dtr.root_digest AS source_digest,
|
|
46
|
+
m.version_id AS member_version_id,
|
|
47
|
+
m.digest AS member_digest,
|
|
48
|
+
m.manifest_kind AS member_manifest_kind,
|
|
49
|
+
mr.min_distance AS hops_from_root,
|
|
50
|
+
'descendant' AS member_role
|
|
51
|
+
FROM temp_direct_target_roots dtr
|
|
52
|
+
JOIN manifest_reachability mr
|
|
53
|
+
ON mr.scan_id = ?
|
|
54
|
+
AND mr.ancestor_digest = dtr.root_digest
|
|
55
|
+
AND mr.min_distance > 0
|
|
56
|
+
JOIN manifests m
|
|
57
|
+
ON m.scan_id = ?
|
|
58
|
+
AND m.digest = mr.descendant_digest
|
|
59
|
+
)
|
|
60
|
+
SELECT
|
|
61
|
+
source_version_id,
|
|
62
|
+
source_digest,
|
|
63
|
+
member_version_id,
|
|
64
|
+
member_digest,
|
|
65
|
+
member_manifest_kind,
|
|
66
|
+
hops_from_root,
|
|
67
|
+
member_role
|
|
68
|
+
FROM direct_target_closure
|
|
69
|
+
ORDER BY source_digest, hops_from_root, member_digest
|
|
70
|
+
`;
|
|
71
|
+
return this.#sql.all(sql, [scanId, scanId]).map(mapClosureManifestRow);
|
|
72
|
+
}
|
|
73
|
+
#listBlockedRoots(scanId) {
|
|
74
|
+
const sql = `
|
|
75
|
+
WITH retained_roots AS (
|
|
76
|
+
SELECT
|
|
77
|
+
m.version_id AS root_version_id,
|
|
78
|
+
m.digest AS root_digest
|
|
79
|
+
FROM manifests m
|
|
80
|
+
WHERE m.scan_id = ?
|
|
81
|
+
AND NOT EXISTS (
|
|
82
|
+
SELECT 1
|
|
83
|
+
FROM manifest_reachability mr
|
|
84
|
+
WHERE mr.scan_id = m.scan_id
|
|
85
|
+
AND mr.descendant_digest = m.digest
|
|
86
|
+
AND mr.min_distance > 0
|
|
87
|
+
)
|
|
88
|
+
AND NOT EXISTS (
|
|
89
|
+
SELECT 1
|
|
90
|
+
FROM temp_direct_target_roots dtr
|
|
91
|
+
WHERE dtr.root_digest = m.digest
|
|
92
|
+
)
|
|
93
|
+
),
|
|
94
|
+
direct_target_closure AS (
|
|
95
|
+
SELECT
|
|
96
|
+
dtr.root_version_id AS root_version_id,
|
|
97
|
+
dtr.root_digest AS root_digest,
|
|
98
|
+
dtr.root_manifest_kind AS member_manifest_kind,
|
|
99
|
+
dtr.root_digest AS member_digest,
|
|
100
|
+
0 AS hops_from_root
|
|
101
|
+
FROM temp_direct_target_roots dtr
|
|
102
|
+
|
|
103
|
+
UNION ALL
|
|
104
|
+
|
|
105
|
+
SELECT
|
|
106
|
+
dtr.root_version_id AS root_version_id,
|
|
107
|
+
dtr.root_digest AS root_digest,
|
|
108
|
+
m.manifest_kind AS member_manifest_kind,
|
|
109
|
+
m.digest AS member_digest,
|
|
110
|
+
mr.min_distance AS hops_from_root
|
|
111
|
+
FROM temp_direct_target_roots dtr
|
|
112
|
+
JOIN manifest_reachability mr
|
|
113
|
+
ON mr.scan_id = ?
|
|
114
|
+
AND mr.ancestor_digest = dtr.root_digest
|
|
115
|
+
AND mr.min_distance > 0
|
|
116
|
+
JOIN manifests m
|
|
117
|
+
ON m.scan_id = ?
|
|
118
|
+
AND m.digest = mr.descendant_digest
|
|
119
|
+
),
|
|
120
|
+
ranked_blocks AS (
|
|
121
|
+
SELECT
|
|
122
|
+
dtc.root_version_id AS blocked_version_id,
|
|
123
|
+
dtc.root_digest AS blocked_digest,
|
|
124
|
+
rr.root_version_id AS blocking_version_id,
|
|
125
|
+
rr.root_digest AS blocking_digest,
|
|
126
|
+
dtc.member_digest AS overlap_digest,
|
|
127
|
+
dtc.member_manifest_kind AS overlap_manifest_kind,
|
|
128
|
+
'overlap-with-retained-root' AS block_reason,
|
|
129
|
+
ROW_NUMBER() OVER (
|
|
130
|
+
PARTITION BY dtc.root_digest, rr.root_digest
|
|
131
|
+
ORDER BY
|
|
132
|
+
dtc.hops_from_root,
|
|
133
|
+
retained_overlap.min_distance,
|
|
134
|
+
dtc.member_digest
|
|
135
|
+
) AS rn
|
|
136
|
+
FROM direct_target_closure dtc
|
|
137
|
+
JOIN retained_roots rr
|
|
138
|
+
ON rr.root_digest <> dtc.root_digest
|
|
139
|
+
JOIN manifest_reachability retained_overlap
|
|
140
|
+
ON retained_overlap.scan_id = ?
|
|
141
|
+
AND retained_overlap.ancestor_digest = rr.root_digest
|
|
142
|
+
AND retained_overlap.descendant_digest = dtc.member_digest
|
|
143
|
+
)
|
|
144
|
+
SELECT
|
|
145
|
+
blocked_version_id,
|
|
146
|
+
blocked_digest,
|
|
147
|
+
blocking_version_id,
|
|
148
|
+
blocking_digest,
|
|
149
|
+
overlap_digest,
|
|
150
|
+
overlap_manifest_kind,
|
|
151
|
+
block_reason
|
|
152
|
+
FROM ranked_blocks
|
|
153
|
+
WHERE rn = 1
|
|
154
|
+
ORDER BY blocked_digest, blocking_digest, overlap_digest
|
|
155
|
+
`;
|
|
156
|
+
return this.#sql
|
|
157
|
+
.all(sql, [scanId, scanId, scanId, scanId])
|
|
158
|
+
.map(mapBlockedRootRow);
|
|
159
|
+
}
|
|
160
|
+
#withDirectTargetRootsTempTable(directTargetRoots, callback) {
|
|
161
|
+
this.#sql.exec(`
|
|
162
|
+
CREATE TEMP TABLE IF NOT EXISTS temp_direct_target_roots (
|
|
163
|
+
root_version_id INTEGER NOT NULL,
|
|
164
|
+
root_digest TEXT NOT NULL,
|
|
165
|
+
root_manifest_kind TEXT,
|
|
166
|
+
direct_target_reason TEXT NOT NULL,
|
|
167
|
+
selection_mode TEXT NOT NULL
|
|
168
|
+
)
|
|
169
|
+
`);
|
|
170
|
+
this.#sql.exec(`
|
|
171
|
+
CREATE INDEX IF NOT EXISTS idx_temp_direct_target_roots_digest
|
|
172
|
+
ON temp_direct_target_roots(root_digest)
|
|
173
|
+
`);
|
|
174
|
+
this.#sql.exec(`
|
|
175
|
+
CREATE INDEX IF NOT EXISTS idx_temp_direct_target_roots_version_digest
|
|
176
|
+
ON temp_direct_target_roots(root_version_id, root_digest)
|
|
177
|
+
`);
|
|
178
|
+
this.#sql.exec("DELETE FROM temp_direct_target_roots");
|
|
179
|
+
this.#insertDirectTargetRoots(directTargetRoots);
|
|
180
|
+
try {
|
|
181
|
+
return callback();
|
|
182
|
+
}
|
|
183
|
+
finally {
|
|
184
|
+
this.#sql.exec("DELETE FROM temp_direct_target_roots");
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
#insertDirectTargetRoots(directTargetRoots) {
|
|
188
|
+
const insertSql = `
|
|
189
|
+
INSERT INTO temp_direct_target_roots (
|
|
190
|
+
root_version_id,
|
|
191
|
+
root_digest,
|
|
192
|
+
root_manifest_kind,
|
|
193
|
+
direct_target_reason,
|
|
194
|
+
selection_mode
|
|
195
|
+
) VALUES (?, ?, ?, ?, ?)
|
|
196
|
+
`;
|
|
197
|
+
this.#sql.traceSql(insertSql, ["<chunked rows omitted>"]);
|
|
198
|
+
const insert = this.#sql.database.prepare(insertSql);
|
|
199
|
+
const insertMany = this.#sql.database.transaction((roots) => {
|
|
200
|
+
for (const root of roots) {
|
|
201
|
+
insert.run(root.versionId, root.digest, root.manifestKind ?? null, root.reason, root.selectionMode);
|
|
202
|
+
}
|
|
203
|
+
});
|
|
204
|
+
const chunkSize = 1000;
|
|
205
|
+
for (let index = 0; index < directTargetRoots.length; index += chunkSize) {
|
|
206
|
+
const chunk = directTargetRoots.slice(index, index + chunkSize);
|
|
207
|
+
insertMany(chunk);
|
|
208
|
+
this.#sql.logger.debug(`Inserted ${chunk.length} direct target root row(s) into temp_direct_target_roots`);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type Database from "better-sqlite3";
|
|
2
|
+
import type { DeletePlan, PlannerLogger } from "./_planner-types.js";
|
|
3
|
+
export type { DeletePlan, DeletePlanBlockedRoot, DeletePlanClosureManifest, DeletePlanProtectedRoot, DeletePlanRoot, DeletePlanRootDecision } from "./_planner-types.js";
|
|
4
|
+
export declare class PlannerRepository {
|
|
5
|
+
#private;
|
|
6
|
+
constructor(database: Database.Database, logger?: PlannerLogger);
|
|
7
|
+
getDeleteUntaggedPlan(owner: string, packageName: string): DeletePlan;
|
|
8
|
+
getLatestCompletedScanId(owner: string, packageName: string): number;
|
|
9
|
+
getKeepNUntaggedPlan(owner: string, packageName: string, keepCount: number): DeletePlan;
|
|
10
|
+
getKeepNTaggedPlan(owner: string, packageName: string, keepCount: number): DeletePlan;
|
|
11
|
+
getDeleteUntaggedPlanWithCutoff(owner: string, packageName: string, options?: {
|
|
12
|
+
olderThan?: string;
|
|
13
|
+
cutoffTimestamp?: string;
|
|
14
|
+
}): DeletePlan;
|
|
15
|
+
getKeepNUntaggedPlanWithCutoff(owner: string, packageName: string, keepCount: number, options?: {
|
|
16
|
+
olderThan?: string;
|
|
17
|
+
cutoffTimestamp?: string;
|
|
18
|
+
}): DeletePlan;
|
|
19
|
+
getKeepNTaggedPlanWithCutoff(owner: string, packageName: string, keepCount: number, excludeTags: string[], options?: {
|
|
20
|
+
olderThan?: string;
|
|
21
|
+
cutoffTimestamp?: string;
|
|
22
|
+
}): DeletePlan;
|
|
23
|
+
getDeleteTagsPlan(owner: string, packageName: string, deleteTags: string[], excludeTags: string[]): DeletePlan;
|
|
24
|
+
getDeleteTagsPlanWithCutoff(owner: string, packageName: string, deleteTags: string[], excludeTags: string[], options?: {
|
|
25
|
+
deleteTagsRequested?: boolean;
|
|
26
|
+
deleteGhostImages?: boolean;
|
|
27
|
+
deletePartialImages?: boolean;
|
|
28
|
+
deleteOrphanedImages?: boolean;
|
|
29
|
+
keepNTagged?: number;
|
|
30
|
+
useRegex?: boolean;
|
|
31
|
+
olderThan?: string;
|
|
32
|
+
cutoffTimestamp?: string;
|
|
33
|
+
}): DeletePlan;
|
|
34
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { buildPlanOutputs } from "./_planner-output.js";
|
|
2
|
+
import { PlannerPlanArtifacts } from "./_planner-plan-artifacts.js";
|
|
3
|
+
import { PlannerSql } from "./_planner-sql.js";
|
|
4
|
+
import { PlannerTaggedTargets } from "./_planner-tagged-targets.js";
|
|
5
|
+
import { PlannerUntaggedTargets } from "./_planner-untagged-targets.js";
|
|
6
|
+
export class PlannerRepository {
|
|
7
|
+
#untaggedTargets;
|
|
8
|
+
#taggedTargets;
|
|
9
|
+
#planArtifacts;
|
|
10
|
+
constructor(database, logger) {
|
|
11
|
+
const sql = new PlannerSql(database, logger);
|
|
12
|
+
this.#untaggedTargets = new PlannerUntaggedTargets(sql);
|
|
13
|
+
this.#taggedTargets = new PlannerTaggedTargets(sql);
|
|
14
|
+
this.#planArtifacts = new PlannerPlanArtifacts(sql);
|
|
15
|
+
}
|
|
16
|
+
getDeleteUntaggedPlan(owner, packageName) {
|
|
17
|
+
return this.getDeleteUntaggedPlanWithCutoff(owner, packageName);
|
|
18
|
+
}
|
|
19
|
+
getLatestCompletedScanId(owner, packageName) {
|
|
20
|
+
return this.#untaggedTargets.getLatestCompletedScan(owner, packageName).scan_id;
|
|
21
|
+
}
|
|
22
|
+
getKeepNUntaggedPlan(owner, packageName, keepCount) {
|
|
23
|
+
return this.getKeepNUntaggedPlanWithCutoff(owner, packageName, keepCount);
|
|
24
|
+
}
|
|
25
|
+
getKeepNTaggedPlan(owner, packageName, keepCount) {
|
|
26
|
+
return this.getKeepNTaggedPlanWithCutoff(owner, packageName, keepCount, []);
|
|
27
|
+
}
|
|
28
|
+
getDeleteUntaggedPlanWithCutoff(owner, packageName, options) {
|
|
29
|
+
const scan = this.#untaggedTargets.getLatestCompletedScan(owner, packageName);
|
|
30
|
+
const directTargetRoots = this.#untaggedTargets.listDeleteUntaggedDirectTargetRoots(scan.scan_id, options?.cutoffTimestamp);
|
|
31
|
+
const planArtifacts = this.#planArtifacts.build(scan.scan_id, directTargetRoots);
|
|
32
|
+
return {
|
|
33
|
+
owner: scan.owner,
|
|
34
|
+
packageName: scan.package_name,
|
|
35
|
+
scanCompletedAt: scan.scan_completed_at,
|
|
36
|
+
plannerInputs: _buildPlannerInputs({
|
|
37
|
+
deleteUntagged: true,
|
|
38
|
+
olderThan: options?.olderThan,
|
|
39
|
+
cutoffTimestamp: options?.cutoffTimestamp
|
|
40
|
+
}),
|
|
41
|
+
...buildPlanOutputs([], directTargetRoots, planArtifacts)
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
getKeepNUntaggedPlanWithCutoff(owner, packageName, keepCount, options) {
|
|
45
|
+
const scan = this.#untaggedTargets.getLatestCompletedScan(owner, packageName);
|
|
46
|
+
const directTargetRoots = this.#untaggedTargets.listKeepNUntaggedDirectTargetRoots(scan.scan_id, keepCount, options?.cutoffTimestamp);
|
|
47
|
+
const planArtifacts = this.#planArtifacts.build(scan.scan_id, directTargetRoots);
|
|
48
|
+
return {
|
|
49
|
+
owner: scan.owner,
|
|
50
|
+
packageName: scan.package_name,
|
|
51
|
+
scanCompletedAt: scan.scan_completed_at,
|
|
52
|
+
plannerInputs: _buildPlannerInputs({
|
|
53
|
+
keepNUntagged: keepCount,
|
|
54
|
+
olderThan: options?.olderThan,
|
|
55
|
+
cutoffTimestamp: options?.cutoffTimestamp
|
|
56
|
+
}),
|
|
57
|
+
...buildPlanOutputs([], directTargetRoots, planArtifacts)
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
getKeepNTaggedPlanWithCutoff(owner, packageName, keepCount, excludeTags, options) {
|
|
61
|
+
const scan = this.#untaggedTargets.getLatestCompletedScan(owner, packageName);
|
|
62
|
+
const directTargetRoots = this.#taggedTargets.listTaggedDirectTargetRoots(scan.scan_id, {
|
|
63
|
+
deleteTags: [],
|
|
64
|
+
excludeTags,
|
|
65
|
+
keepCount,
|
|
66
|
+
cutoffTimestamp: options?.cutoffTimestamp
|
|
67
|
+
});
|
|
68
|
+
const planArtifacts = this.#planArtifacts.build(scan.scan_id, directTargetRoots);
|
|
69
|
+
return {
|
|
70
|
+
owner: scan.owner,
|
|
71
|
+
packageName: scan.package_name,
|
|
72
|
+
scanCompletedAt: scan.scan_completed_at,
|
|
73
|
+
plannerInputs: _buildPlannerInputs({
|
|
74
|
+
excludeTags,
|
|
75
|
+
keepNTagged: keepCount,
|
|
76
|
+
olderThan: options?.olderThan,
|
|
77
|
+
cutoffTimestamp: options?.cutoffTimestamp
|
|
78
|
+
}),
|
|
79
|
+
...buildPlanOutputs([], directTargetRoots, planArtifacts)
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
getDeleteTagsPlan(owner, packageName, deleteTags, excludeTags) {
|
|
83
|
+
return this.getDeleteTagsPlanWithCutoff(owner, packageName, deleteTags, excludeTags);
|
|
84
|
+
}
|
|
85
|
+
getDeleteTagsPlanWithCutoff(owner, packageName, deleteTags, excludeTags, options) {
|
|
86
|
+
const scan = this.#untaggedTargets.getLatestCompletedScan(owner, packageName);
|
|
87
|
+
const directTargetTags = this.#taggedTargets.listDeleteTagDirectTargetTags(scan.scan_id, deleteTags, excludeTags, options?.useRegex ?? false, options?.cutoffTimestamp);
|
|
88
|
+
const directTargetRoots = this.#taggedTargets.listTaggedDirectTargetRoots(scan.scan_id, {
|
|
89
|
+
deleteTags,
|
|
90
|
+
deleteTagsRequested: options?.deleteTagsRequested ?? true,
|
|
91
|
+
excludeTags,
|
|
92
|
+
keepCount: options?.keepNTagged,
|
|
93
|
+
useRegex: options?.useRegex ?? false,
|
|
94
|
+
cutoffTimestamp: options?.cutoffTimestamp
|
|
95
|
+
});
|
|
96
|
+
const planArtifacts = this.#planArtifacts.build(scan.scan_id, directTargetRoots);
|
|
97
|
+
return {
|
|
98
|
+
owner: scan.owner,
|
|
99
|
+
packageName: scan.package_name,
|
|
100
|
+
scanCompletedAt: scan.scan_completed_at,
|
|
101
|
+
plannerInputs: _buildPlannerInputs({
|
|
102
|
+
deleteGhostImages: options?.deleteGhostImages || undefined,
|
|
103
|
+
deletePartialImages: options?.deletePartialImages || undefined,
|
|
104
|
+
deleteOrphanedImages: options?.deleteOrphanedImages || undefined,
|
|
105
|
+
deleteTags,
|
|
106
|
+
excludeTags,
|
|
107
|
+
keepNTagged: options?.keepNTagged,
|
|
108
|
+
useRegex: options?.useRegex || undefined,
|
|
109
|
+
olderThan: options?.olderThan,
|
|
110
|
+
cutoffTimestamp: options?.cutoffTimestamp
|
|
111
|
+
}),
|
|
112
|
+
...buildPlanOutputs(directTargetTags, directTargetRoots, planArtifacts)
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
function _buildPlannerInputs(inputs) {
|
|
117
|
+
return Object.fromEntries(Object.entries(inputs).filter(([, value]) => {
|
|
118
|
+
if (value === undefined) {
|
|
119
|
+
return false;
|
|
120
|
+
}
|
|
121
|
+
if (value === false) {
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
124
|
+
return !(Array.isArray(value) && value.length === 0);
|
|
125
|
+
}));
|
|
126
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type Database from "better-sqlite3";
|
|
2
|
+
import { type PlannerLogger } from "./_planner-types.js";
|
|
3
|
+
export declare class PlannerSql {
|
|
4
|
+
#private;
|
|
5
|
+
constructor(database: Database.Database, logger?: PlannerLogger);
|
|
6
|
+
get database(): Database.Database;
|
|
7
|
+
get logger(): PlannerLogger;
|
|
8
|
+
exec(sql: string, params?: Array<number | string | null>): void;
|
|
9
|
+
get<T>(sql: string, params: Array<number | string>): T | undefined;
|
|
10
|
+
all<T>(sql: string, params: Array<number | string>): T[];
|
|
11
|
+
traceSql(sql: string, params: Array<number | string | null>): void;
|
|
12
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { silentPlannerLogger } from "./_planner-types.js";
|
|
2
|
+
export class PlannerSql {
|
|
3
|
+
#database;
|
|
4
|
+
#logger;
|
|
5
|
+
constructor(database, logger = silentPlannerLogger) {
|
|
6
|
+
this.#database = database;
|
|
7
|
+
this.#logger = logger;
|
|
8
|
+
}
|
|
9
|
+
get database() {
|
|
10
|
+
return this.#database;
|
|
11
|
+
}
|
|
12
|
+
get logger() {
|
|
13
|
+
return this.#logger;
|
|
14
|
+
}
|
|
15
|
+
exec(sql, params = []) {
|
|
16
|
+
this.#traceSql(sql, params);
|
|
17
|
+
this.#database.prepare(sql).run(...params);
|
|
18
|
+
}
|
|
19
|
+
get(sql, params) {
|
|
20
|
+
this.#traceSql(sql, params);
|
|
21
|
+
return this.#database.prepare(sql).get(...params);
|
|
22
|
+
}
|
|
23
|
+
all(sql, params) {
|
|
24
|
+
this.#traceSql(sql, params);
|
|
25
|
+
const rows = this.#database.prepare(sql).all(...params);
|
|
26
|
+
this.#logger.debug(`SQL returned ${rows.length} row(s)`);
|
|
27
|
+
return rows;
|
|
28
|
+
}
|
|
29
|
+
traceSql(sql, params) {
|
|
30
|
+
this.#traceSql(sql, params);
|
|
31
|
+
}
|
|
32
|
+
#traceSql(sql, params) {
|
|
33
|
+
this.#logger.trace(`SQL:\n${sql.trim()}\nPARAMS: ${JSON.stringify(params)}`);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type Database from "better-sqlite3";
|
|
2
|
+
export interface PlannerTagSelectorPredicate {
|
|
3
|
+
sql: string;
|
|
4
|
+
params: string[];
|
|
5
|
+
}
|
|
6
|
+
export declare function buildTagSelectorPredicate(database: Database.Database, columnSql: string, selectors: string[], useRegex: boolean): PlannerTagSelectorPredicate;
|
|
7
|
+
export declare function wildcardSelectorToSqlLike(selector: string): string;
|
|
8
|
+
export declare function hasWildcard(selector: string): boolean;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
export function buildTagSelectorPredicate(database, columnSql, selectors, useRegex) {
|
|
2
|
+
if (selectors.length === 0) {
|
|
3
|
+
throw new Error("selectors must not be empty");
|
|
4
|
+
}
|
|
5
|
+
if (useRegex) {
|
|
6
|
+
registerRegexFunction(database);
|
|
7
|
+
}
|
|
8
|
+
return {
|
|
9
|
+
sql: selectors
|
|
10
|
+
.map((selector) => {
|
|
11
|
+
if (useRegex) {
|
|
12
|
+
return `regexp(?, ${columnSql})`;
|
|
13
|
+
}
|
|
14
|
+
return hasWildcard(selector) ? `${columnSql} LIKE ? ESCAPE '\\'` : `${columnSql} = ?`;
|
|
15
|
+
})
|
|
16
|
+
.join(" OR "),
|
|
17
|
+
params: useRegex ? selectors : selectors.map((selector) => wildcardSelectorToSqlLike(selector))
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
export function wildcardSelectorToSqlLike(selector) {
|
|
21
|
+
if (!hasWildcard(selector)) {
|
|
22
|
+
return selector;
|
|
23
|
+
}
|
|
24
|
+
return selector.replaceAll(/[%_\\*?]/g, (character) => {
|
|
25
|
+
switch (character) {
|
|
26
|
+
case "%":
|
|
27
|
+
case "_":
|
|
28
|
+
case "\\":
|
|
29
|
+
return `\\${character}`;
|
|
30
|
+
case "*":
|
|
31
|
+
return "%";
|
|
32
|
+
case "?":
|
|
33
|
+
return "_";
|
|
34
|
+
default:
|
|
35
|
+
return character;
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
export function hasWildcard(selector) {
|
|
40
|
+
return selector.includes("*") || selector.includes("?");
|
|
41
|
+
}
|
|
42
|
+
function registerRegexFunction(database) {
|
|
43
|
+
const markedDatabase = database;
|
|
44
|
+
if (markedDatabase.__ghcrManagerRegexRegistered) {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
markedDatabase.__ghcrManagerRegexCache = new Map();
|
|
48
|
+
database.function("regexp", (pattern, value) => {
|
|
49
|
+
let compiled = markedDatabase.__ghcrManagerRegexCache?.get(pattern);
|
|
50
|
+
if (!compiled) {
|
|
51
|
+
compiled = new RegExp(pattern);
|
|
52
|
+
markedDatabase.__ghcrManagerRegexCache?.set(pattern, compiled);
|
|
53
|
+
}
|
|
54
|
+
return compiled.test(value) ? 1 : 0;
|
|
55
|
+
});
|
|
56
|
+
markedDatabase.__ghcrManagerRegexRegistered = true;
|
|
57
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { PlannerSql } from "./_planner-sql.js";
|
|
2
|
+
import type { DeletePlanRoot } from "./_planner-types.js";
|
|
3
|
+
export interface TaggedRootTargetOptions {
|
|
4
|
+
deleteTags: string[];
|
|
5
|
+
deleteTagsRequested?: boolean;
|
|
6
|
+
excludeTags: string[];
|
|
7
|
+
keepCount?: number;
|
|
8
|
+
useRegex?: boolean;
|
|
9
|
+
cutoffTimestamp?: string;
|
|
10
|
+
}
|
|
11
|
+
export declare class PlannerTaggedRootTargets {
|
|
12
|
+
#private;
|
|
13
|
+
constructor(sql: PlannerSql);
|
|
14
|
+
listTaggedDirectTargetRoots(scanId: number, options: TaggedRootTargetOptions): DeletePlanRoot[];
|
|
15
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { PlannerDeleteTagRootTargets } from "./_planner-delete-tag-root-targets.js";
|
|
2
|
+
import { PlannerKeepTaggedRootTargets } from "./_planner-keep-tagged-root-targets.js";
|
|
3
|
+
export class PlannerTaggedRootTargets {
|
|
4
|
+
#deleteTagTargets;
|
|
5
|
+
#keepTaggedTargets;
|
|
6
|
+
constructor(sql) {
|
|
7
|
+
this.#deleteTagTargets = new PlannerDeleteTagRootTargets(sql);
|
|
8
|
+
this.#keepTaggedTargets = new PlannerKeepTaggedRootTargets(sql);
|
|
9
|
+
}
|
|
10
|
+
listTaggedDirectTargetRoots(scanId, options) {
|
|
11
|
+
if (options.deleteTagsRequested && options.deleteTags.length === 0) {
|
|
12
|
+
return [];
|
|
13
|
+
}
|
|
14
|
+
if (options.deleteTags.length === 0) {
|
|
15
|
+
return this.#keepTaggedTargets.list(scanId, options.excludeTags, options.useRegex ?? false, options.keepCount, options.cutoffTimestamp);
|
|
16
|
+
}
|
|
17
|
+
return this.#deleteTagTargets.list(scanId, options.deleteTags, options.excludeTags, options.useRegex ?? false, options.keepCount, options.cutoffTimestamp);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type TaggedRootTargetOptions } from "./_planner-tagged-root-targets.js";
|
|
2
|
+
import { PlannerSql } from "./_planner-sql.js";
|
|
3
|
+
export declare class PlannerTaggedTargets {
|
|
4
|
+
#private;
|
|
5
|
+
constructor(sql: PlannerSql);
|
|
6
|
+
listDeleteTagDirectTargetTags(scanId: number, deleteTags: string[], excludeTags: string[], useRegex: boolean, cutoffTimestamp?: string): string[];
|
|
7
|
+
listTaggedDirectTargetRoots(scanId: number, options: TaggedRootTargetOptions): import("./_planner-types.js").DeletePlanRoot[];
|
|
8
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { PlannerDirectTargetTags } from "./_planner-direct-target-tags.js";
|
|
2
|
+
import { PlannerTaggedRootTargets } from "./_planner-tagged-root-targets.js";
|
|
3
|
+
export class PlannerTaggedTargets {
|
|
4
|
+
#directTargetTags;
|
|
5
|
+
#rootTargets;
|
|
6
|
+
constructor(sql) {
|
|
7
|
+
this.#directTargetTags = new PlannerDirectTargetTags(sql);
|
|
8
|
+
this.#rootTargets = new PlannerTaggedRootTargets(sql);
|
|
9
|
+
}
|
|
10
|
+
listDeleteTagDirectTargetTags(scanId, deleteTags, excludeTags, useRegex, cutoffTimestamp) {
|
|
11
|
+
return this.#directTargetTags.listDeleteTagDirectTargetTags(scanId, deleteTags, excludeTags, useRegex, cutoffTimestamp);
|
|
12
|
+
}
|
|
13
|
+
listTaggedDirectTargetRoots(scanId, options) {
|
|
14
|
+
return this.#rootTargets.listTaggedDirectTargetRoots(scanId, options);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
interface _PlanRootRow {
|
|
2
|
+
version_id: number;
|
|
3
|
+
root_digest: string;
|
|
4
|
+
root_manifest_kind: string | null;
|
|
5
|
+
direct_target_reason: string;
|
|
6
|
+
selection_mode: string;
|
|
7
|
+
}
|
|
8
|
+
interface _PlanTagRow {
|
|
9
|
+
target_tag: string;
|
|
10
|
+
}
|
|
11
|
+
interface _ClosureManifestRow {
|
|
12
|
+
source_version_id: number;
|
|
13
|
+
source_digest: string;
|
|
14
|
+
member_version_id: number;
|
|
15
|
+
member_digest: string;
|
|
16
|
+
member_manifest_kind: string | null;
|
|
17
|
+
hops_from_root: number;
|
|
18
|
+
member_role: string;
|
|
19
|
+
}
|
|
20
|
+
interface _BlockedRootRow {
|
|
21
|
+
blocked_version_id: number;
|
|
22
|
+
blocked_digest: string;
|
|
23
|
+
blocking_version_id: number;
|
|
24
|
+
blocking_digest: string;
|
|
25
|
+
overlap_digest: string;
|
|
26
|
+
overlap_manifest_kind: string | null;
|
|
27
|
+
block_reason: string;
|
|
28
|
+
}
|
|
29
|
+
export interface PlannerLogger {
|
|
30
|
+
trace(message: string): void;
|
|
31
|
+
debug(message: string): void;
|
|
32
|
+
warn?(message: string): void;
|
|
33
|
+
}
|
|
34
|
+
export interface ScanRow {
|
|
35
|
+
scan_id: number;
|
|
36
|
+
owner: string;
|
|
37
|
+
package_name: string;
|
|
38
|
+
scan_completed_at: string;
|
|
39
|
+
}
|
|
40
|
+
export interface DeletePlanRoot {
|
|
41
|
+
versionId: number;
|
|
42
|
+
digest: string;
|
|
43
|
+
manifestKind?: string;
|
|
44
|
+
reason: string;
|
|
45
|
+
selectionMode: string;
|
|
46
|
+
}
|
|
47
|
+
export interface DeletePlanClosureManifest {
|
|
48
|
+
sourceVersionId: number;
|
|
49
|
+
sourceDigest: string;
|
|
50
|
+
memberVersionId: number;
|
|
51
|
+
memberDigest: string;
|
|
52
|
+
memberManifestKind?: string;
|
|
53
|
+
hopsFromRoot: number;
|
|
54
|
+
memberRole: string;
|
|
55
|
+
}
|
|
56
|
+
export interface DeletePlanBlockedRoot {
|
|
57
|
+
blockedVersionId: number;
|
|
58
|
+
blockedDigest: string;
|
|
59
|
+
blockingVersionId: number;
|
|
60
|
+
blockingDigest: string;
|
|
61
|
+
overlapDigest: string;
|
|
62
|
+
overlapManifestKind?: string;
|
|
63
|
+
reason: string;
|
|
64
|
+
}
|
|
65
|
+
export interface DeletePlanRootDecision {
|
|
66
|
+
versionId: number;
|
|
67
|
+
digest: string;
|
|
68
|
+
manifestKind?: string;
|
|
69
|
+
selectionMode: string;
|
|
70
|
+
selectionReason: string;
|
|
71
|
+
validationStatus: "fully-deletable" | "blocked" | "untag-only";
|
|
72
|
+
validationReasonCode: "untag-only-partial-tag-match" | "fully-deletable-no-retained-overlap" | "blocked-overlap-with-retained-root";
|
|
73
|
+
validationReason: string;
|
|
74
|
+
blockingVersionId?: number;
|
|
75
|
+
blockingDigest?: string;
|
|
76
|
+
overlapDigest?: string;
|
|
77
|
+
overlapManifestKind?: string;
|
|
78
|
+
}
|
|
79
|
+
export interface DeletePlanProtectedRoot {
|
|
80
|
+
versionId: number;
|
|
81
|
+
digest: string;
|
|
82
|
+
blocks: Array<{
|
|
83
|
+
blockedVersionId: number;
|
|
84
|
+
blockedDigest: string;
|
|
85
|
+
blockReasonCode: string;
|
|
86
|
+
overlapDigest: string;
|
|
87
|
+
overlapManifestKind?: string;
|
|
88
|
+
}>;
|
|
89
|
+
}
|
|
90
|
+
export interface PlanArtifacts {
|
|
91
|
+
closureManifests: DeletePlanClosureManifest[];
|
|
92
|
+
blockedRoots: DeletePlanBlockedRoot[];
|
|
93
|
+
fullyDeletableRoots: DeletePlanRoot[];
|
|
94
|
+
}
|
|
95
|
+
export interface DeletePlan {
|
|
96
|
+
owner: string;
|
|
97
|
+
packageName: string;
|
|
98
|
+
scanCompletedAt: string;
|
|
99
|
+
plannerInputs: {
|
|
100
|
+
deleteUntagged?: boolean;
|
|
101
|
+
deleteGhostImages?: boolean;
|
|
102
|
+
deletePartialImages?: boolean;
|
|
103
|
+
deleteOrphanedImages?: boolean;
|
|
104
|
+
deleteTags?: string[];
|
|
105
|
+
excludeTags?: string[];
|
|
106
|
+
keepNTagged?: number;
|
|
107
|
+
keepNUntagged?: number;
|
|
108
|
+
useRegex?: boolean;
|
|
109
|
+
olderThan?: string;
|
|
110
|
+
cutoffTimestamp?: string;
|
|
111
|
+
};
|
|
112
|
+
validationSummary: {
|
|
113
|
+
directTargetTagCount: number;
|
|
114
|
+
directTargetRootCount: number;
|
|
115
|
+
deleteRootCandidateCount: number;
|
|
116
|
+
untagOnlyRootCount: number;
|
|
117
|
+
fullyDeletableRootCount: number;
|
|
118
|
+
blockedDeleteRootCount: number;
|
|
119
|
+
protectedRootCount: number;
|
|
120
|
+
};
|
|
121
|
+
directTargetTags: string[];
|
|
122
|
+
directTargetRoots: DeletePlanRoot[];
|
|
123
|
+
rootDecisions: DeletePlanRootDecision[];
|
|
124
|
+
protectedRoots: DeletePlanProtectedRoot[];
|
|
125
|
+
closureManifests: DeletePlanClosureManifest[];
|
|
126
|
+
blockedRoots: DeletePlanBlockedRoot[];
|
|
127
|
+
fullyDeletableRoots: DeletePlanRoot[];
|
|
128
|
+
collateralTags: string[];
|
|
129
|
+
}
|
|
130
|
+
export declare const silentPlannerLogger: PlannerLogger;
|
|
131
|
+
export declare function mapPlanRootRow(row: _PlanRootRow): DeletePlanRoot;
|
|
132
|
+
export declare function mapPlanTagRows(rows: _PlanTagRow[]): string[];
|
|
133
|
+
export declare function mapClosureManifestRow(row: _ClosureManifestRow): DeletePlanClosureManifest;
|
|
134
|
+
export declare function mapBlockedRootRow(row: _BlockedRootRow): DeletePlanBlockedRoot;
|
|
135
|
+
export {};
|