@cairn-tool/cairn 2.1.0 → 3.0.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.
@@ -52,11 +52,14 @@ export interface InstallRegistration {
52
52
  * A manifest written before collections carries only `pluginKey`.
53
53
  */
54
54
  export declare function registeredPluginKeys(registration: InstallRegistration): string[];
55
- export interface InstallManifest {
56
- generator: {
57
- name: string;
58
- version: string;
59
- };
55
+ /**
56
+ * One install recorded in a destination's manifest.
57
+ *
58
+ * A destination may hold several: every target declares the same project-scope
59
+ * merge root, so a repository installing for two hosts, or two bundles, records
60
+ * them side by side. {@link installKey} is what tells them apart.
61
+ */
62
+ export interface InstallRecord {
60
63
  /**
61
64
  * What was installed here: a single bundle, or a collection of them. Absent
62
65
  * means `"bundle"`, so every manifest written before collections still parses.
@@ -89,6 +92,45 @@ export interface InstallManifest {
89
92
  materialized?: string;
90
93
  registration?: InstallRegistration;
91
94
  }
95
+ /**
96
+ * The `.cairn-install.json` document: which build wrote it, and every install
97
+ * it accounts for.
98
+ *
99
+ * Serialized in one of two shapes — see {@link serializeDocument}. Both parse,
100
+ * and a document that fails to parse in full is `malformed` rather than
101
+ * partially trusted: its job is to be an *exhaustive* statement of what cairn
102
+ * owns here, and a dropped entry would make that entry's files look unowned to
103
+ * the occupancy check and to `retirePrior`, which is the destruction this shape
104
+ * exists to prevent.
105
+ */
106
+ export interface InstallDocument {
107
+ generator: {
108
+ name: string;
109
+ version: string;
110
+ };
111
+ installs: InstallRecord[];
112
+ }
113
+ /**
114
+ * An install's identity within one destination.
115
+ *
116
+ * Keyed on the bundle **and** the target, which is the whole fix: keyed on the
117
+ * bundle name alone, a second target's install read the first's inventory as
118
+ * its own stale files and deleted them.
119
+ *
120
+ * NUL-separated for the reason `artifactKey` and `sessionKey` are — no half can
121
+ * contain one. `profile` is redundant while `locationFor(target, scope)` fixes
122
+ * it, and is included so a future target declaring two locations for one scope
123
+ * does not silently collide. `destination` is deliberately absent: the key is
124
+ * only ever compared within one manifest file.
125
+ */
126
+ export declare function installKey(record: {
127
+ bundle: {
128
+ name: string;
129
+ };
130
+ target: AgentTarget;
131
+ profile: AgentProfile;
132
+ scope: InstallScope;
133
+ }): string;
92
134
  export interface InstallContext {
93
135
  home?: string;
94
136
  cwd?: string;
@@ -111,7 +153,24 @@ export interface InstallPlan {
111
153
  mode: InstallMode;
112
154
  destination: string;
113
155
  artifacts: Artifact[];
114
- manifest: InstallManifest;
156
+ /** This install's own record. */
157
+ record: InstallRecord;
158
+ /**
159
+ * The whole document this plan writes, siblings included. Byte-identical for
160
+ * every plan sharing a destination in one batch.
161
+ */
162
+ document: InstallDocument;
163
+ /**
164
+ * The document as it stood when the plan was made.
165
+ *
166
+ * `retirePrior` prunes against this snapshot and never against a re-read at
167
+ * commit time: every plan in a destination group writes the same merged
168
+ * document, so once the first has committed, the file already lists the
169
+ * second's record — a re-read would make the second find itself, compute an
170
+ * empty stale set, and silently never prune. It also closes the window
171
+ * between planning and committing.
172
+ */
173
+ prior: InstallDocument | "missing" | "malformed";
115
174
  diagnostics: AgentDiagnostic[];
116
175
  register: boolean;
117
176
  settings?: InstallRegistration;
@@ -128,23 +187,45 @@ export declare function locationFor(target: AgentTarget, scope: InstallScope): I
128
187
  * absolute path is enough, matching the overlay boundary.
129
188
  */
130
189
  export declare function pathEscapesRoot(root: string, relative: string): boolean;
131
- export declare function readInstallManifest(destination: string): InstallManifest | "missing" | "malformed";
190
+ export declare function readInstallDocument(destination: string): InstallDocument | "missing" | "malformed";
191
+ /** The record matching `key` at `destination`, if the document records one. */
192
+ export declare function readInstallRecord(destination: string, key: string): InstallRecord | "missing" | "malformed";
132
193
  export declare function resolveInstallDestination(target: AgentTarget, scope: InstallScope, name: string, options?: {
133
194
  into?: string;
134
195
  } & InstallContext): ResolvedInstall | AgentDiagnostic;
135
196
  export declare function installIsCurrent(plan: InstallPlan): boolean;
136
- /**
137
- * Plans an install: renders and packages in memory, resolves the destination
138
- * from profile data, and records AB8xx findings. Nothing is written.
139
- */
140
- export declare function planInstall(bundle: AgentBundle, target: AgentTarget, options: {
197
+ /** Options shared by every install plan in one run. */
198
+ export interface PlanInstallOptions extends InstallContext {
141
199
  scope?: string;
142
200
  into?: string;
143
201
  profile?: string;
144
202
  link?: boolean;
145
203
  register?: boolean;
146
204
  force?: boolean;
147
- } & InstallContext): InstallPlan;
205
+ }
206
+ /** One bundle to install for one target. */
207
+ export interface InstallRequest {
208
+ bundle: AgentBundle;
209
+ target: AgentTarget;
210
+ }
211
+ export interface InstallBatch {
212
+ plans: InstallPlan[];
213
+ /** Batch-level findings — the in-run AB808. Per-plan findings stay on the plan. */
214
+ diagnostics: AgentDiagnostic[];
215
+ }
216
+ /**
217
+ * Plans every requested install, grouped by destination.
218
+ *
219
+ * Nothing is written here, and the caller must treat the batch as all-or-nothing:
220
+ * committing a subset of a run whose remainder is blocked is how a destination
221
+ * ends up half-populated with no record of it.
222
+ */
223
+ export declare function planInstalls(requests: InstallRequest[], options: PlanInstallOptions): InstallBatch;
224
+ /**
225
+ * Plans a single install: renders and packages in memory, resolves the
226
+ * destination from profile data, and records AB8xx findings. Nothing is written.
227
+ */
228
+ export declare function planInstall(bundle: AgentBundle, target: AgentTarget, options: PlanInstallOptions): InstallPlan;
148
229
  /** What a collection places at one destination. */
149
230
  export interface CollectionInstall {
150
231
  /** The collection's name; also the marketplace key hosts index by. */
@@ -190,7 +271,7 @@ export interface UninstallPlan {
190
271
  name: string;
191
272
  target: AgentTarget;
192
273
  destination: string;
193
- manifest: InstallManifest | null;
274
+ manifest: InstallRecord | null;
194
275
  diagnostics: AgentDiagnostic[];
195
276
  missing: boolean;
196
277
  }