@teambit/isolator 1.0.1001 → 1.0.1003
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/dist/capsule-cache.d.ts +192 -0
- package/dist/capsule-cache.js +733 -0
- package/dist/capsule-cache.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +19 -0
- package/dist/index.js.map +1 -1
- package/dist/isolator.composition.d.ts +2 -1
- package/dist/isolator.main.runtime.d.ts +16 -0
- package/dist/isolator.main.runtime.js +57 -6
- package/dist/isolator.main.runtime.js.map +1 -1
- package/dist/{preview-1779999088763.js → preview-1780507494169.js} +2 -2
- package/package.json +24 -24
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import type { Logger } from '@teambit/logger';
|
|
2
|
+
import type { CLIMain } from '@teambit/cli';
|
|
3
|
+
import type { ConfigStoreMain } from '@teambit/config-store';
|
|
4
|
+
import type CapsuleList from './capsule-list';
|
|
5
|
+
/**
|
|
6
|
+
* Marker file written into every capsule dir we manage. Its presence tells the prune logic
|
|
7
|
+
* what kind of dir this is, where it came from, and (via its mtime) when it was last used.
|
|
8
|
+
*/
|
|
9
|
+
export declare const CAPSULE_ORIGIN_FILE = ".bit-capsule-origin.json";
|
|
10
|
+
export declare const CAPSULE_TRASH_DIR = ".trash";
|
|
11
|
+
export type CapsuleKind = 'workspace' | 'scope-aspects-root' | 'scope-aspect' | 'scope';
|
|
12
|
+
export type CapsuleOriginMarker = {
|
|
13
|
+
originPath: string;
|
|
14
|
+
createdAt: string;
|
|
15
|
+
kind: CapsuleKind;
|
|
16
|
+
};
|
|
17
|
+
export type PruneCapsulesOptions = {
|
|
18
|
+
olderThanDays?: number;
|
|
19
|
+
includeOrphans?: boolean;
|
|
20
|
+
keepWorkspaceCaps?: boolean;
|
|
21
|
+
sizeTargetGb?: number;
|
|
22
|
+
dryRun?: boolean;
|
|
23
|
+
/**
|
|
24
|
+
* Compute byte sizes for every entry being considered. When false, all `sizeBytes`
|
|
25
|
+
* in the report are 0 and the cache walk skips the expensive recursive `lstat` pass —
|
|
26
|
+
* deletion (rename-to-trash) is O(1) and runs in milliseconds even on multi-GB caches.
|
|
27
|
+
* Forced on when `sizeTargetGb` is set because that path needs sizes to enforce.
|
|
28
|
+
*/
|
|
29
|
+
withSizes?: boolean;
|
|
30
|
+
};
|
|
31
|
+
export type PruneCapsulesReport = {
|
|
32
|
+
/** `originPath` is the workspace/scope a capsule was created for (from its marker), when known. */
|
|
33
|
+
removed: {
|
|
34
|
+
path: string;
|
|
35
|
+
kind: CapsuleKind | 'unmarked';
|
|
36
|
+
reason: string;
|
|
37
|
+
sizeBytes: number;
|
|
38
|
+
originPath?: string;
|
|
39
|
+
}[];
|
|
40
|
+
totalRemovedBytes: number;
|
|
41
|
+
totalSizeBeforeBytes: number;
|
|
42
|
+
totalSizeAfterBytes: number;
|
|
43
|
+
dryRun: boolean;
|
|
44
|
+
};
|
|
45
|
+
export type CapsuleRootEntry = {
|
|
46
|
+
path: string;
|
|
47
|
+
kind: CapsuleKind | 'unmarked';
|
|
48
|
+
originPath?: string;
|
|
49
|
+
lastUsedMs: number;
|
|
50
|
+
sizeBytes: number;
|
|
51
|
+
};
|
|
52
|
+
export declare class CapsuleCache {
|
|
53
|
+
private logger;
|
|
54
|
+
private cli;
|
|
55
|
+
private configStore;
|
|
56
|
+
/** Thunk so the cache stays independent of `GlobalConfigMain` — IsolatorMain forwards it. */
|
|
57
|
+
private getRootDir;
|
|
58
|
+
constructor(logger: Logger, cli: CLIMain, configStore: ConfigStoreMain,
|
|
59
|
+
/** Thunk so the cache stays independent of `GlobalConfigMain` — IsolatorMain forwards it. */
|
|
60
|
+
getRootDir: () => string);
|
|
61
|
+
deleteCapsules(rootDir?: string): Promise<string>;
|
|
62
|
+
/**
|
|
63
|
+
* Move a capsule dir into a sibling `.trash/<uuid>/` so it disappears from the cache
|
|
64
|
+
* immediately (same-filesystem rename is O(1)), then kick off a detached `rm -rf` so the
|
|
65
|
+
* actual byte-by-byte cleanup happens in the background. This avoids the multi-second
|
|
66
|
+
* stalls users see when deleting capsules with thousands of files.
|
|
67
|
+
*/
|
|
68
|
+
scheduleFastDelete(dir: string): Promise<void>;
|
|
69
|
+
/**
|
|
70
|
+
* Sweep the `.trash` dir in a detached background process. Gated by a PID-stamped
|
|
71
|
+
* lock so we never have more than one sweep running at a time across all concurrent
|
|
72
|
+
* bit processes — previously we spawned one per `bit` invocation and they piled up
|
|
73
|
+
* into the thousands, saturating disk I/O.
|
|
74
|
+
*/
|
|
75
|
+
sweepTrashAsync(): void;
|
|
76
|
+
/**
|
|
77
|
+
* A sweep lock is "active" if the PID it names is still running. If the PID file
|
|
78
|
+
* exists but the process is gone (e.g. crashed mid-sweep), we treat it as stale and
|
|
79
|
+
* allow a new sweep to claim it.
|
|
80
|
+
*/
|
|
81
|
+
private isSweepLockActive;
|
|
82
|
+
/**
|
|
83
|
+
* Register a process-exit hook that, at most once per ~24h, spawns a detached
|
|
84
|
+
* `bit capsule prune` child so the actual work runs out-of-process and never delays
|
|
85
|
+
* the parent's exit. Gated by the mtime of a stamp file under the capsules root so
|
|
86
|
+
* concurrent Bit invocations can't all trigger it at once, and behind the
|
|
87
|
+
* `capsule-auto-prune` feature flag while the behavior is being validated.
|
|
88
|
+
*/
|
|
89
|
+
registerAutoPruneHook(): void;
|
|
90
|
+
private maybeAutoPrune;
|
|
91
|
+
/**
|
|
92
|
+
* Fire-and-forget: spawn a detached child running `bit capsule prune`. Using the same
|
|
93
|
+
* bit binary that's currently running (via process.argv[0] + argv[1]) so we don't depend
|
|
94
|
+
* on PATH. stdio is ignored so nothing leaks to the user's terminal.
|
|
95
|
+
*
|
|
96
|
+
* Recursion guard: the child also runs onBeforeExit → maybeAutoPrune, but it reads the
|
|
97
|
+
* stamp file that we just wrote and bails out before re-spawning.
|
|
98
|
+
*/
|
|
99
|
+
private spawnDetachedAutoPrune;
|
|
100
|
+
/**
|
|
101
|
+
* Spawn one detached Node process that recursively removes `trashRoot`. Using
|
|
102
|
+
* `process.execPath` with an inline `fs.rmSync` keeps this portable across macOS,
|
|
103
|
+
* Linux, and Windows (where there's no `rm` binary). When `lockPath` is given, the
|
|
104
|
+
* child clears the lock on exit so the next bit invocation can claim a fresh sweep slot.
|
|
105
|
+
*/
|
|
106
|
+
private spawnDetachedSweep;
|
|
107
|
+
/**
|
|
108
|
+
* Write the origin marker if missing; otherwise just bump its mtime so it reflects
|
|
109
|
+
* "last used at". Failures are non-fatal — markers are best-effort metadata.
|
|
110
|
+
*/
|
|
111
|
+
ensureOriginMarker(dir: string, kind: CapsuleKind, originPath: string): Promise<void>;
|
|
112
|
+
/**
|
|
113
|
+
* Mark all per-component capsule subdirs as scope-aspect kind, originated from the
|
|
114
|
+
* scope-aspects root. Used right after a scope-aspects isolation.
|
|
115
|
+
*/
|
|
116
|
+
ensureAspectCapsuleMarkers(capsuleList: CapsuleList, rootOriginPath: string): Promise<void>;
|
|
117
|
+
/**
|
|
118
|
+
* Single source of truth for the dated-capsules date-dir name (`YYYY-M-D`, no zero-pad).
|
|
119
|
+
* Used by `getCapsulesRootDir` when writing and `pruneDatedCapsulesChildren` when reading,
|
|
120
|
+
* so the two can never drift.
|
|
121
|
+
*/
|
|
122
|
+
getDatedCapsuleDirName(date?: Date): string;
|
|
123
|
+
/**
|
|
124
|
+
* Standard filter for "real" capsule subdirs we may walk or prune. Skips files, the trash
|
|
125
|
+
* dir (dot-prefixed), `node_modules`, and any other hidden/internal dir.
|
|
126
|
+
*/
|
|
127
|
+
private isPrunableSubdir;
|
|
128
|
+
/**
|
|
129
|
+
* Combined marker read + last-used resolution for a capsule dir: one `readJson`, one
|
|
130
|
+
* fallback `stat`. Replaces the three-syscall (`readMarker` + `getOriginMarkerMtime` +
|
|
131
|
+
* dir `stat`) idiom that was copy-pasted across the prune walks.
|
|
132
|
+
*/
|
|
133
|
+
private readMarkerInfo;
|
|
134
|
+
private readOriginMarker;
|
|
135
|
+
private getOriginMarkerMtime;
|
|
136
|
+
/**
|
|
137
|
+
* Walk the global capsules root and return entries with their classification, size, and
|
|
138
|
+
* last-used time. Used by prune and by `bit capsule list`.
|
|
139
|
+
*/
|
|
140
|
+
listAllCapsuleRoots(opts?: {
|
|
141
|
+
withSizes?: boolean;
|
|
142
|
+
}): Promise<CapsuleRootEntry[]>;
|
|
143
|
+
/**
|
|
144
|
+
* Sum sizes of all entries under `dir`. Tolerant of symlinks and permission errors —
|
|
145
|
+
* any failure returns the partial sum so we never throw from the prune path.
|
|
146
|
+
* Uses bounded concurrency to avoid EMFILE on deep trees.
|
|
147
|
+
*/
|
|
148
|
+
private computeDirSize;
|
|
149
|
+
/**
|
|
150
|
+
* Apply the prune rules from the plan:
|
|
151
|
+
* - workspace caps: deleted unconditionally (unless keepWorkspaceCaps)
|
|
152
|
+
* - scope-aspects-root: never deleted as a whole; per-aspect-version children pruned by age
|
|
153
|
+
* - scope caps and unmarked dirs older than threshold: deleted
|
|
154
|
+
* - orphans (marker says originPath gone): deleted
|
|
155
|
+
* - after the above, if sizeTargetGb given and size still exceeds it, evict oldest-first
|
|
156
|
+
*/
|
|
157
|
+
pruneCapsules(opts?: PruneCapsulesOptions): Promise<PruneCapsulesReport>;
|
|
158
|
+
/**
|
|
159
|
+
* Record a removal in the prune report and, unless this is a dry run, actually delete it
|
|
160
|
+
* (fast rename-to-trash). Keeps the "report and delete are gated by the same dryRun flag"
|
|
161
|
+
* invariant in one place so the per-kind prune helpers can't drift apart.
|
|
162
|
+
*/
|
|
163
|
+
private recordRemoval;
|
|
164
|
+
/**
|
|
165
|
+
* The `dated-capsules` dir holds per-date subdirs (`YYYY-M-D`) of in-flight isolation
|
|
166
|
+
* runs. These are recreated on every isolation, so anything that isn't *today*'s
|
|
167
|
+
* subdir is leftover from a previous run and safe to delete. Today's subdir is
|
|
168
|
+
* preserved to avoid racing a concurrent bit process that may still be writing to it.
|
|
169
|
+
*/
|
|
170
|
+
private pruneDatedCapsulesChildren;
|
|
171
|
+
/**
|
|
172
|
+
* Legacy unmarked dirs may still be a scope-aspects root. Heuristic: a child subdir whose
|
|
173
|
+
* name contains `@` (aspect-version pattern like `teambit.node_node@1.3.4`).
|
|
174
|
+
*/
|
|
175
|
+
private looksLikeAspectsRoot;
|
|
176
|
+
/**
|
|
177
|
+
* Prune per-aspect-version children of a scope-aspects root purely by age (marker mtime,
|
|
178
|
+
* which is touched on every aspect load).
|
|
179
|
+
*
|
|
180
|
+
* Note there's deliberately no orphan check here: a scope-aspect child's `originPath` is
|
|
181
|
+
* the *logical* scope-aspects path (e.g. `<scope.path>-aspects`) used only to hash the
|
|
182
|
+
* capsule root dir name — it need not exist as a real directory, so treating a missing
|
|
183
|
+
* `originPath` as "orphan" would wrongly delete capsules of currently-used aspects.
|
|
184
|
+
* Orphan pruning is still honored elsewhere for `workspace`/`scope` kinds.
|
|
185
|
+
*/
|
|
186
|
+
private pruneAspectsRootChildren;
|
|
187
|
+
/**
|
|
188
|
+
* After the standard prune, if total still exceeds the target, keep evicting the
|
|
189
|
+
* oldest remaining aspect-version subdirs until under the limit.
|
|
190
|
+
*/
|
|
191
|
+
private applySizeTarget;
|
|
192
|
+
}
|