swarm-deploy 0.0.0 → 0.1.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.
Files changed (115) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/LICENSE.md +201 -0
  3. package/NOTICE.md +13 -0
  4. package/README.md +652 -0
  5. package/RELEASING.md +71 -0
  6. package/SECURITY.md +41 -0
  7. package/dist/abort.d.ts +16 -0
  8. package/dist/abort.js +52 -0
  9. package/dist/abort.js.map +1 -0
  10. package/dist/allowlist.d.ts +2 -0
  11. package/dist/allowlist.js +21 -0
  12. package/dist/allowlist.js.map +1 -0
  13. package/dist/bin/swarm-deploy.d.ts +2 -0
  14. package/dist/bin/swarm-deploy.js +41 -0
  15. package/dist/bin/swarm-deploy.js.map +1 -0
  16. package/dist/cli.d.ts +25 -0
  17. package/dist/cli.js +694 -0
  18. package/dist/cli.js.map +1 -0
  19. package/dist/client.d.ts +121 -0
  20. package/dist/client.js +285 -0
  21. package/dist/client.js.map +1 -0
  22. package/dist/direct-dht.d.ts +81 -0
  23. package/dist/direct-dht.js +254 -0
  24. package/dist/direct-dht.js.map +1 -0
  25. package/dist/error-code.d.ts +15 -0
  26. package/dist/error-code.js +30 -0
  27. package/dist/error-code.js.map +1 -0
  28. package/dist/errors.d.ts +27 -0
  29. package/dist/errors.js +33 -0
  30. package/dist/errors.js.map +1 -0
  31. package/dist/events-node.d.ts +2 -0
  32. package/dist/events-node.js +7 -0
  33. package/dist/events-node.js.map +1 -0
  34. package/dist/files.d.ts +47 -0
  35. package/dist/files.js +150 -0
  36. package/dist/files.js.map +1 -0
  37. package/dist/fs-node.d.ts +2 -0
  38. package/dist/fs-node.js +7 -0
  39. package/dist/fs-node.js.map +1 -0
  40. package/dist/identity.d.ts +7 -0
  41. package/dist/identity.js +50 -0
  42. package/dist/identity.js.map +1 -0
  43. package/dist/index.d.ts +8 -0
  44. package/dist/index.js +19 -0
  45. package/dist/index.js.map +1 -0
  46. package/dist/os-node.d.ts +2 -0
  47. package/dist/os-node.js +7 -0
  48. package/dist/os-node.js.map +1 -0
  49. package/dist/path-node.d.ts +2 -0
  50. package/dist/path-node.js +7 -0
  51. package/dist/path-node.js.map +1 -0
  52. package/dist/process-node.d.ts +2 -0
  53. package/dist/process-node.js +7 -0
  54. package/dist/process-node.js.map +1 -0
  55. package/dist/server.d.ts +153 -0
  56. package/dist/server.js +576 -0
  57. package/dist/server.js.map +1 -0
  58. package/dist/storage/atomic-file.d.ts +15 -0
  59. package/dist/storage/atomic-file.js +172 -0
  60. package/dist/storage/atomic-file.js.map +1 -0
  61. package/dist/storage/commit-journal.d.ts +83 -0
  62. package/dist/storage/commit-journal.js +279 -0
  63. package/dist/storage/commit-journal.js.map +1 -0
  64. package/dist/storage/commit-store.d.ts +246 -0
  65. package/dist/storage/commit-store.js +1339 -0
  66. package/dist/storage/commit-store.js.map +1 -0
  67. package/dist/storage/layout.d.ts +16 -0
  68. package/dist/storage/layout.js +403 -0
  69. package/dist/storage/layout.js.map +1 -0
  70. package/dist/storage/recovery.d.ts +99 -0
  71. package/dist/storage/recovery.js +253 -0
  72. package/dist/storage/recovery.js.map +1 -0
  73. package/dist/storage/retention.d.ts +163 -0
  74. package/dist/storage/retention.js +465 -0
  75. package/dist/storage/retention.js.map +1 -0
  76. package/dist/storage/root-coordinator.d.ts +7 -0
  77. package/dist/storage/root-coordinator.js +27 -0
  78. package/dist/storage/root-coordinator.js.map +1 -0
  79. package/dist/storage/session-store.d.ts +2 -0
  80. package/dist/storage/session-store.js +7 -0
  81. package/dist/storage/session-store.js.map +1 -0
  82. package/dist/storage/tar-session-store.d.ts +116 -0
  83. package/dist/storage/tar-session-store.js +816 -0
  84. package/dist/storage/tar-session-store.js.map +1 -0
  85. package/dist/storage/types.d.ts +62 -0
  86. package/dist/storage/types.js +3 -0
  87. package/dist/storage/types.js.map +1 -0
  88. package/dist/tar-protocol/controls.d.ts +48 -0
  89. package/dist/tar-protocol/controls.js +185 -0
  90. package/dist/tar-protocol/controls.js.map +1 -0
  91. package/dist/tar-protocol/direct-wire.d.ts +43 -0
  92. package/dist/tar-protocol/direct-wire.js +194 -0
  93. package/dist/tar-protocol/direct-wire.js.map +1 -0
  94. package/dist/tar-protocol/extract.d.ts +18 -0
  95. package/dist/tar-protocol/extract.js +124 -0
  96. package/dist/tar-protocol/extract.js.map +1 -0
  97. package/dist/tar-protocol/hash.d.ts +10 -0
  98. package/dist/tar-protocol/hash.js +44 -0
  99. package/dist/tar-protocol/hash.js.map +1 -0
  100. package/dist/tar-protocol/lifecycle.d.ts +13 -0
  101. package/dist/tar-protocol/lifecycle.js +90 -0
  102. package/dist/tar-protocol/lifecycle.js.map +1 -0
  103. package/dist/tar-protocol/manifest.d.ts +46 -0
  104. package/dist/tar-protocol/manifest.js +287 -0
  105. package/dist/tar-protocol/manifest.js.map +1 -0
  106. package/dist/tar-protocol/ustar.d.ts +22 -0
  107. package/dist/tar-protocol/ustar.js +87 -0
  108. package/dist/tar-protocol/ustar.js.map +1 -0
  109. package/dist/types.d.ts +58 -0
  110. package/dist/types.js +3 -0
  111. package/dist/types.js.map +1 -0
  112. package/dist/validation.d.ts +4 -0
  113. package/dist/validation.js +26 -0
  114. package/dist/validation.js.map +1 -0
  115. package/package.json +101 -1
@@ -0,0 +1,99 @@
1
+ import type { CommitRecord } from './commit-journal.js';
2
+ import type { StorageAdapter, StorageLayout } from './types.js';
3
+ interface Logger {
4
+ info?: (message: string, details: Record<string, unknown>) => void;
5
+ warn?: (message: string, details: Record<string, unknown>) => void;
6
+ }
7
+ interface SessionStore {
8
+ initialized?: boolean;
9
+ closed?: boolean;
10
+ readVerified(transferId: Uint8Array): Promise<{
11
+ id: string;
12
+ transferId: Uint8Array;
13
+ ownerKey: Uint8Array;
14
+ name: string;
15
+ size: number;
16
+ digest: Uint8Array;
17
+ tarSize: number;
18
+ tarDigest: Uint8Array;
19
+ state: string;
20
+ }>;
21
+ expire(ttl: number, shouldExpire: (session: {
22
+ state: string;
23
+ }) => boolean): Promise<number>;
24
+ delete?(transferId: Uint8Array): Promise<boolean>;
25
+ }
26
+ interface RecoveryResult {
27
+ status: 'MISSING' | 'FILE_EXISTS' | 'COMMITTED' | 'ABORTED' | 'RESUMABLE' | 'CORRUPT';
28
+ record?: CommitRecord;
29
+ reason?: string;
30
+ }
31
+ interface CommitStore {
32
+ storage?: StorageAdapter;
33
+ _readJournal(id: string): Promise<unknown>;
34
+ retireCorruptAttempt(id: string): Promise<{
35
+ retired: boolean;
36
+ removedStaging: boolean;
37
+ }>;
38
+ recoverJournal(id: string, sessionStore: SessionStore, options: {
39
+ isAuthorized: ((ownerKey: Uint8Array) => boolean) | null;
40
+ }): Promise<RecoveryResult>;
41
+ list(): Promise<CommitRecord[]>;
42
+ scrubRecords(): Promise<{
43
+ records: CommitRecord[];
44
+ deleted: number;
45
+ }>;
46
+ delete(record: CommitRecord): Promise<boolean>;
47
+ purge(record: CommitRecord, options?: {
48
+ preservePath?: boolean;
49
+ }): Promise<false | {
50
+ purged: true;
51
+ preservedPath: boolean;
52
+ }>;
53
+ }
54
+ type RecoveryEvent = {
55
+ type: 'recovery';
56
+ status: 'CORRUPT';
57
+ phase: 'classification';
58
+ transfer: string;
59
+ } | {
60
+ type: 'recovery';
61
+ status: 'failed';
62
+ phase: 'classification' | 'journal';
63
+ transfer?: string;
64
+ reason: string;
65
+ } | {
66
+ type: 'recovery';
67
+ status: 'MISSING' | 'FILE_EXISTS' | 'COMMITTED' | 'ABORTED' | 'RESUMABLE' | 'CORRUPT';
68
+ transfer: string;
69
+ } | {
70
+ type: 'cleanup';
71
+ transfer: string;
72
+ name: null;
73
+ reason: 'corrupt-journal';
74
+ } | {
75
+ type: 'scrub';
76
+ status: 'started';
77
+ } | {
78
+ type: 'scrub';
79
+ status: 'completed';
80
+ deleted: number;
81
+ unknownCount: number;
82
+ } | {
83
+ type: 'scrub';
84
+ status: 'failed';
85
+ reason: string;
86
+ };
87
+ interface PrepareStorageRecoveryOptions {
88
+ layout: StorageLayout;
89
+ commitStore: CommitStore;
90
+ logger?: Logger | null;
91
+ onEvent?: ((event: RecoveryEvent) => void) | null;
92
+ }
93
+ interface RecoverStorageOptions extends PrepareStorageRecoveryOptions {
94
+ sessionStore: SessionStore;
95
+ isAuthorized?: ((ownerKey: Uint8Array) => boolean) | null;
96
+ }
97
+ declare function prepareStorageRecovery({ layout, commitStore, logger, onEvent }: PrepareStorageRecoveryOptions): Promise<number>;
98
+ declare function recoverStorage({ layout, sessionStore, commitStore, logger, isAuthorized, onEvent }: RecoverStorageOptions): Promise<RecoveryResult[]>;
99
+ export { prepareStorageRecovery, recoverStorage };
@@ -0,0 +1,253 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.prepareStorageRecovery = prepareStorageRecovery;
7
+ exports.recoverStorage = recoverStorage;
8
+ const b4a_1 = __importDefault(require("b4a"));
9
+ const _fs_1 = __importDefault(require("#fs"));
10
+ const errors_js_1 = require("../errors.js");
11
+ const hash_js_1 = require("../tar-protocol/hash.js");
12
+ const layout_js_1 = require("./layout.js");
13
+ const commit_store_js_1 = require("./commit-store.js");
14
+ const retention_js_1 = require("./retention.js");
15
+ function storageError(message, cause = null) {
16
+ return new errors_js_1.SwarmDeployError(errors_js_1.ERRORS.PROTOCOL_INVALID, message, cause);
17
+ }
18
+ function isJournalName(name) {
19
+ return typeof name === 'string' && /^[0-9a-f]{64}\.json$/.test(name);
20
+ }
21
+ function transferFingerprint(id) {
22
+ return b4a_1.default.toString((0, hash_js_1.sodiumSha256)(b4a_1.default.from(id, 'hex')), 'hex').slice(0, 12);
23
+ }
24
+ function pathFingerprint(name) {
25
+ return b4a_1.default.toString((0, hash_js_1.sodiumSha256)(b4a_1.default.from(name)), 'hex').slice(0, 12);
26
+ }
27
+ function report(logger, level, message, details) {
28
+ if (!logger || typeof logger[level] !== 'function')
29
+ return;
30
+ try {
31
+ logger[level]?.(message, details);
32
+ }
33
+ catch { }
34
+ }
35
+ function emit(onEvent, payload) {
36
+ if (!onEvent)
37
+ return;
38
+ try {
39
+ onEvent(payload);
40
+ }
41
+ catch { }
42
+ }
43
+ function failureReason(error) {
44
+ if (typeof error !== 'object' || error === null || !('code' in error)) {
45
+ return errors_js_1.ERRORS.PROTOCOL_INVALID;
46
+ }
47
+ return typeof error.code === 'string' && error.code.length > 0
48
+ ? error.code
49
+ : errors_js_1.ERRORS.PROTOCOL_INVALID;
50
+ }
51
+ async function assertRecoveryLayout(layout, storage) {
52
+ for (const directory of [
53
+ layout.root,
54
+ layout.internal,
55
+ layout.staging,
56
+ layout.sessions,
57
+ layout.commits,
58
+ layout.journals
59
+ ]) {
60
+ await (0, layout_js_1.assertSafeDirectory)(directory, storage);
61
+ }
62
+ }
63
+ async function prepareStorageRecovery({ layout, commitStore, logger = null, onEvent = null }) {
64
+ if (!layout || typeof layout !== 'object')
65
+ throw storageError('Invalid storage layout');
66
+ if (!commitStore || typeof commitStore._readJournal !== 'function') {
67
+ throw storageError('Invalid commit store');
68
+ }
69
+ if (typeof commitStore.retireCorruptAttempt !== 'function') {
70
+ throw storageError('Commit store cannot retire corrupt attempts');
71
+ }
72
+ if (onEvent !== null && typeof onEvent !== 'function') {
73
+ throw storageError('Invalid recovery event callback');
74
+ }
75
+ const storage = commitStore.storage || _fs_1.default.promises;
76
+ let retired = 0;
77
+ let activeTransfer = null;
78
+ try {
79
+ await assertRecoveryLayout(layout, storage);
80
+ const names = await (0, layout_js_1.withSafeDirectoryIdentity)(layout.journals, storage, () => storage.readdir(layout.journals));
81
+ for (const name of names.sort()) {
82
+ if (!isJournalName(name))
83
+ continue;
84
+ const id = name.slice(0, -'.json'.length);
85
+ activeTransfer = transferFingerprint(id);
86
+ try {
87
+ await commitStore._readJournal(id);
88
+ }
89
+ catch (err) {
90
+ if (!(err instanceof commit_store_js_1.CorruptJournalError))
91
+ throw err;
92
+ const result = await commitStore.retireCorruptAttempt(id);
93
+ retired++;
94
+ report(logger, 'warn', 'Retired corrupt commit attempt before session recovery', {
95
+ transfer: activeTransfer
96
+ });
97
+ emit(onEvent, {
98
+ type: 'recovery',
99
+ status: 'CORRUPT',
100
+ phase: 'classification',
101
+ transfer: activeTransfer
102
+ });
103
+ if (result.removedStaging) {
104
+ emit(onEvent, {
105
+ type: 'cleanup',
106
+ transfer: activeTransfer,
107
+ name: null,
108
+ reason: 'corrupt-journal'
109
+ });
110
+ }
111
+ }
112
+ activeTransfer = null;
113
+ }
114
+ }
115
+ catch (err) {
116
+ emit(onEvent, {
117
+ type: 'recovery',
118
+ status: 'failed',
119
+ phase: 'classification',
120
+ ...(activeTransfer ? { transfer: activeTransfer } : {}),
121
+ reason: failureReason(err)
122
+ });
123
+ throw err;
124
+ }
125
+ return retired;
126
+ }
127
+ async function recoverStorage({ layout, sessionStore, commitStore, logger = null, isAuthorized = null, onEvent = null }) {
128
+ if (!layout || typeof layout !== 'object')
129
+ throw storageError('Invalid storage layout');
130
+ if (!sessionStore || typeof sessionStore !== 'object')
131
+ throw storageError('Invalid session store');
132
+ if (!commitStore || typeof commitStore.recoverJournal !== 'function') {
133
+ throw storageError('Invalid commit store');
134
+ }
135
+ if (onEvent !== null && typeof onEvent !== 'function') {
136
+ throw storageError('Invalid recovery event callback');
137
+ }
138
+ const storage = commitStore.storage || _fs_1.default.promises;
139
+ let names;
140
+ try {
141
+ await assertRecoveryLayout(layout, storage);
142
+ names = await (0, layout_js_1.withSafeDirectoryIdentity)(layout.journals, storage, () => storage.readdir(layout.journals));
143
+ }
144
+ catch (err) {
145
+ emit(onEvent, {
146
+ type: 'recovery',
147
+ status: 'failed',
148
+ phase: 'journal',
149
+ reason: failureReason(err)
150
+ });
151
+ throw err;
152
+ }
153
+ const results = [];
154
+ for (const name of names.sort()) {
155
+ if (!isJournalName(name)) {
156
+ report(logger, 'warn', 'Ignoring unknown journal path', {
157
+ fingerprint: pathFingerprint(name)
158
+ });
159
+ continue;
160
+ }
161
+ const id = name.slice(0, -'.json'.length);
162
+ let result;
163
+ try {
164
+ result = await commitStore.recoverJournal(id, sessionStore, { isAuthorized });
165
+ }
166
+ catch (err) {
167
+ if (!(err instanceof commit_store_js_1.CorruptJournalError)) {
168
+ emit(onEvent, {
169
+ type: 'recovery',
170
+ status: 'failed',
171
+ phase: 'journal',
172
+ transfer: transferFingerprint(id),
173
+ reason: failureReason(err)
174
+ });
175
+ throw err;
176
+ }
177
+ if (typeof commitStore.retireCorruptAttempt !== 'function') {
178
+ throw storageError('Commit store cannot retire corrupt attempts');
179
+ }
180
+ let retired;
181
+ try {
182
+ retired = await commitStore.retireCorruptAttempt(id);
183
+ }
184
+ catch (retireError) {
185
+ emit(onEvent, {
186
+ type: 'recovery',
187
+ status: 'failed',
188
+ phase: 'journal',
189
+ transfer: transferFingerprint(id),
190
+ reason: failureReason(retireError)
191
+ });
192
+ throw retireError;
193
+ }
194
+ result = { status: 'CORRUPT', reason: err.message };
195
+ report(logger, 'warn', 'Skipping corrupt commit journal', {
196
+ transfer: transferFingerprint(id),
197
+ reason: err.message
198
+ });
199
+ if (retired.removedStaging) {
200
+ emit(onEvent, {
201
+ type: 'cleanup',
202
+ transfer: transferFingerprint(id),
203
+ name: null,
204
+ reason: 'corrupt-journal'
205
+ });
206
+ }
207
+ }
208
+ results.push(result);
209
+ report(logger, 'info', 'Recovered commit journal', {
210
+ transfer: transferFingerprint(id),
211
+ status: result.status
212
+ });
213
+ emit(onEvent, {
214
+ type: 'recovery',
215
+ status: result.status,
216
+ transfer: transferFingerprint(id)
217
+ });
218
+ if ((result.status === 'COMMITTED' || result.status === 'ABORTED') &&
219
+ sessionStore.initialized &&
220
+ !sessionStore.closed &&
221
+ typeof sessionStore.delete === 'function') {
222
+ await sessionStore.delete(b4a_1.default.from(id, 'hex'));
223
+ }
224
+ }
225
+ const retention = new retention_js_1.RetentionManager({
226
+ layout,
227
+ sessionStore,
228
+ commitStore,
229
+ storage,
230
+ isSessionActive: () => false,
231
+ logger
232
+ });
233
+ emit(onEvent, { type: 'scrub', status: 'started' });
234
+ try {
235
+ const scrub = await retention.scrubCommitted();
236
+ emit(onEvent, {
237
+ type: 'scrub',
238
+ status: 'completed',
239
+ deleted: scrub.deleted,
240
+ unknownCount: scrub.unknown.length
241
+ });
242
+ }
243
+ catch (err) {
244
+ emit(onEvent, {
245
+ type: 'scrub',
246
+ status: 'failed',
247
+ reason: failureReason(err)
248
+ });
249
+ throw err;
250
+ }
251
+ return results;
252
+ }
253
+ //# sourceMappingURL=recovery.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recovery.js","sourceRoot":"","sources":["../../src/storage/recovery.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,163 @@
1
+ import type { CommitRecord } from './commit-journal.js';
2
+ import type { StorageAdapter, StorageLayout } from './types.js';
3
+ declare const DEFAULT_RESUME_TTL: number;
4
+ declare const DEFAULT_CLEANUP_INTERVAL: number;
5
+ declare const MAX_CLEANUP_INTERVAL = 2147483647;
6
+ interface Session {
7
+ state: string;
8
+ }
9
+ interface SessionStore {
10
+ sessions?: Map<string, Session>;
11
+ expire(ttl: number, shouldExpire: (session: Session) => boolean): Promise<number>;
12
+ }
13
+ interface CommitStore {
14
+ storage?: StorageAdapter;
15
+ list(): Promise<CommitRecord[]>;
16
+ scrubRecords(): Promise<{
17
+ records: CommitRecord[];
18
+ deleted: number;
19
+ }>;
20
+ delete(record: CommitRecord): Promise<boolean>;
21
+ purge(record: CommitRecord, options?: {
22
+ preservePath?: boolean;
23
+ }): Promise<false | {
24
+ purged: true;
25
+ preservedPath: boolean;
26
+ }>;
27
+ }
28
+ interface Logger {
29
+ info?: (message: string, details: Record<string, unknown>) => void;
30
+ warn?: (message: string, details: Record<string, unknown>) => void;
31
+ error?: (message: string, details: Record<string, unknown>) => void;
32
+ }
33
+ interface Clock {
34
+ now(): number;
35
+ }
36
+ interface Scheduler {
37
+ setInterval(callback: () => void, interval: number): unknown;
38
+ clearInterval(timer: unknown): void;
39
+ }
40
+ type RetentionEvent = {
41
+ type: 'retention';
42
+ trigger: string;
43
+ status: 'completed';
44
+ expiredSessions: number;
45
+ scrubbed: number;
46
+ ageDeleted: number;
47
+ storageDeleted: number;
48
+ } | {
49
+ type: 'retention';
50
+ trigger: string;
51
+ status: 'failed';
52
+ reason: string;
53
+ } | {
54
+ type: 'retention';
55
+ trigger: 'scheduled';
56
+ status: 'deferred';
57
+ reason: 'ACTIVE_RECEIVE';
58
+ };
59
+ type RetentionEventPayload = {
60
+ trigger: string;
61
+ status: 'completed';
62
+ expiredSessions: number;
63
+ scrubbed: number;
64
+ ageDeleted: number;
65
+ storageDeleted: number;
66
+ } | {
67
+ trigger: string;
68
+ status: 'failed';
69
+ reason: string;
70
+ } | {
71
+ trigger: 'scheduled';
72
+ status: 'deferred';
73
+ reason: 'ACTIVE_RECEIVE';
74
+ };
75
+ interface RetentionRunOptions {
76
+ incomingBytes?: number;
77
+ trigger?: string;
78
+ }
79
+ interface RetentionManagerOptions {
80
+ layout: StorageLayout;
81
+ sessionStore: SessionStore;
82
+ commitStore: CommitStore;
83
+ maxAge?: number;
84
+ maxStorageBytes?: number;
85
+ resumeTtl?: number;
86
+ cleanupInterval?: number;
87
+ clock?: Clock;
88
+ storage?: StorageAdapter;
89
+ isSessionActive: (session: Session) => boolean;
90
+ hasActiveUploads?: () => boolean;
91
+ isPinned?: (record: CommitRecord) => boolean;
92
+ logger?: Logger | null;
93
+ scheduler?: Scheduler;
94
+ onEvent?: ((event: RetentionEvent) => void) | null;
95
+ }
96
+ declare class RetentionManager {
97
+ layout: StorageLayout;
98
+ sessionStore: SessionStore;
99
+ commitStore: CommitStore;
100
+ maxAge: number | undefined;
101
+ maxStorageBytes: number | undefined;
102
+ resumeTtl: number;
103
+ cleanupInterval: number;
104
+ clock: Clock;
105
+ storage: StorageAdapter;
106
+ isSessionActive: (session: Session) => boolean;
107
+ hasActiveUploads: () => boolean;
108
+ isPinned: (record: CommitRecord) => boolean;
109
+ logger: Logger | null;
110
+ timer: unknown | null;
111
+ cleanupFailure: unknown | null;
112
+ scheduler: Scheduler;
113
+ onEvent: ((event: RetentionEvent) => void) | null;
114
+ startPromise: Promise<void> | null;
115
+ tickPromise: Promise<void> | null;
116
+ tickQueued: boolean;
117
+ lifecycle: number;
118
+ constructor({ layout, sessionStore, commitStore, maxAge, maxStorageBytes, resumeTtl, cleanupInterval, clock, storage, isSessionActive, hasActiveUploads, isPinned, logger, scheduler, onEvent }: RetentionManagerOptions);
119
+ _emit(payload: RetentionEventPayload): void;
120
+ _hasActiveReceivingUpload(): boolean;
121
+ expireSessions(): Promise<number>;
122
+ scrubCommitted({ hash }?: {
123
+ hash?: boolean;
124
+ }): Promise<{
125
+ records: CommitRecord[];
126
+ deleted: number;
127
+ unknown: string[];
128
+ }>;
129
+ _deleteRecord(record: CommitRecord, reason: 'MAX_AGE' | 'MAX_STORAGE'): Promise<void>;
130
+ /**
131
+ * Configured mutable current records survive age and quota eviction. Their
132
+ * size still counts toward the logical total so a replacement that cannot be
133
+ * afforded fails instead of silently exceeding the limit.
134
+ */
135
+ _isPinned(record: CommitRecord): boolean;
136
+ _totalSize(records: CommitRecord[]): number;
137
+ run(options?: RetentionRunOptions): Promise<{
138
+ expiredSessions: number;
139
+ scrubbed: number;
140
+ ageDeleted: number;
141
+ storageDeleted: number;
142
+ }>;
143
+ _runUnlocked(options?: RetentionRunOptions): Promise<{
144
+ expiredSessions: number;
145
+ scrubbed: number;
146
+ ageDeleted: number;
147
+ storageDeleted: number;
148
+ }>;
149
+ _run({ incomingBytes }?: RetentionRunOptions): Promise<{
150
+ expiredSessions: number;
151
+ scrubbed: number;
152
+ ageDeleted: number;
153
+ storageDeleted: number;
154
+ }>;
155
+ afterCommit(): Promise<boolean>;
156
+ _afterCommitUnlocked(): Promise<boolean>;
157
+ start(): Promise<void>;
158
+ _scheduleTick(lifecycle: number): void;
159
+ _drainTicks(lifecycle: number): Promise<void>;
160
+ stop(): Promise<void>;
161
+ close(): Promise<void>;
162
+ }
163
+ export { RetentionManager, DEFAULT_RESUME_TTL, DEFAULT_CLEANUP_INTERVAL, MAX_CLEANUP_INTERVAL };