@scolladon/tsgit 2.0.1 → 3.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 (154) hide show
  1. package/README.md +14 -2
  2. package/dist/cjs/adapters/browser/index.cjs +1 -2
  3. package/dist/cjs/adapters/memory/index.cjs +1 -2
  4. package/dist/cjs/adapters/node/index.cjs +1 -2
  5. package/dist/cjs/chunks/adapter-detect-DW9fsw9d.cjs +1 -0
  6. package/dist/cjs/chunks/browser-http-transport-DGwW5YL7.cjs +1 -0
  7. package/dist/cjs/chunks/context-BcoAzPuU.cjs +0 -1
  8. package/dist/cjs/chunks/error-CfLWDui_.cjs +1 -0
  9. package/dist/cjs/chunks/index-Cy44GVR6.cjs +1 -0
  10. package/dist/cjs/chunks/inflate-CmCtM4-w.cjs +1 -0
  11. package/dist/cjs/chunks/lru-cache-1OU7JhD8.cjs +0 -1
  12. package/dist/cjs/chunks/memory-http-transport-CGriODcQ.cjs +1 -0
  13. package/dist/cjs/chunks/node-ssh-transport-PFu3ijfp.cjs +1 -0
  14. package/dist/cjs/chunks/{progress-CxU62NC1.cjs → progress-D8Dqbftk.cjs} +1 -2
  15. package/dist/cjs/chunks/readable-stream-MTA6D0Zh.cjs +0 -1
  16. package/dist/cjs/chunks/repository-Ci-BBxk0.cjs +1 -0
  17. package/dist/cjs/chunks/write-sparse-checkout-C1dsHCAD.cjs +1 -0
  18. package/dist/cjs/commands/index.cjs +1 -2
  19. package/dist/cjs/index.browser.cjs +1 -2
  20. package/dist/cjs/index.cjs +1 -2
  21. package/dist/cjs/index.default.cjs +1 -2
  22. package/dist/cjs/index.node.cjs +1 -2
  23. package/dist/cjs/operators/index.cjs +0 -1
  24. package/dist/cjs/primitives/index.cjs +1 -2
  25. package/dist/cjs/transport/index.cjs +0 -1
  26. package/dist/esm/adapters/browser/index.js +1 -2
  27. package/dist/esm/adapters/memory/index.js +1 -2
  28. package/dist/esm/adapters/node/index.js +1 -2
  29. package/dist/esm/chunks/adapter-detect-BkCMnjrI.js +1 -0
  30. package/dist/esm/chunks/browser-http-transport-CSxAte5h.js +1 -0
  31. package/dist/esm/chunks/context-CumKOV7K.js +0 -1
  32. package/dist/esm/chunks/error-BotOd-nD.js +1 -0
  33. package/dist/esm/chunks/index-DsvINN8n.js +1 -0
  34. package/dist/esm/chunks/inflate-BCIXqeeE.js +1 -0
  35. package/dist/esm/chunks/lru-cache-Cy7C7Lx2.js +0 -1
  36. package/dist/esm/chunks/memory-http-transport-caqmMGR1.js +1 -0
  37. package/dist/esm/chunks/node-ssh-transport-BgSv2IhT.js +1 -0
  38. package/dist/esm/chunks/{progress-BZobfl7p.js → progress-B_AJnOHx.js} +1 -2
  39. package/dist/esm/chunks/readable-stream-CGuf8k1J.js +0 -1
  40. package/dist/esm/chunks/repository-DyxknzvE.js +1 -0
  41. package/dist/esm/chunks/write-sparse-checkout-BwHbNKNS.js +1 -0
  42. package/dist/esm/commands/index.js +1 -2
  43. package/dist/esm/index.browser.js +1 -2
  44. package/dist/esm/index.default.js +1 -2
  45. package/dist/esm/index.js +1 -2
  46. package/dist/esm/index.node.js +1 -2
  47. package/dist/esm/operators/index.js +0 -1
  48. package/dist/esm/primitives/index.js +1 -2
  49. package/dist/esm/transport/index.js +0 -1
  50. package/dist/types/adapters/browser/index.d.cts +5 -3
  51. package/dist/types/adapters/browser/index.d.ts +5 -3
  52. package/dist/types/adapters/memory/index.d.cts +31 -4
  53. package/dist/types/adapters/memory/index.d.ts +31 -4
  54. package/dist/types/adapters/node/index.d.cts +146 -40
  55. package/dist/types/adapters/node/index.d.ts +146 -40
  56. package/dist/types/chunks/{context-YDw6qicq.d.cts → context-CF1YF2S8.d.cts} +165 -6
  57. package/dist/types/chunks/{context-CCL0_F8R.d.ts → context-sIhhtk2u.d.ts} +165 -6
  58. package/dist/types/chunks/{http-transport--_NdQOiw.d.cts → http-transport-DirKfK2S.d.cts} +1 -1
  59. package/dist/types/chunks/{http-transport--_NdQOiw.d.ts → http-transport-DirKfK2S.d.ts} +1 -1
  60. package/dist/types/chunks/index-B5Dps3fg.d.ts +2468 -0
  61. package/dist/types/chunks/index-B_A8VLaf.d.ts +1221 -0
  62. package/dist/types/chunks/index-Dfh8cvEs.d.cts +2468 -0
  63. package/dist/types/chunks/index-Dqxddhav.d.cts +1221 -0
  64. package/dist/types/chunks/progress-BjAKfkam.d.ts +1217 -0
  65. package/dist/types/chunks/progress-Ldnfb8st.d.cts +1217 -0
  66. package/dist/types/chunks/reflog-entry-Cqh3QrPH.d.ts +697 -0
  67. package/dist/types/chunks/reflog-entry-Dy-O27Dd.d.cts +697 -0
  68. package/dist/types/chunks/{repository-FoS_Laoy.d.ts → repository-D8QBTkEz.d.ts} +58 -20
  69. package/dist/types/chunks/{repository-BeifCqp0.d.cts → repository-D9YrVhcH.d.cts} +58 -20
  70. package/dist/types/commands/index.d.cts +4 -1259
  71. package/dist/types/commands/index.d.ts +4 -1259
  72. package/dist/types/index.browser.d.cts +7 -8
  73. package/dist/types/index.browser.d.ts +7 -8
  74. package/dist/types/index.d.cts +61 -124
  75. package/dist/types/index.d.ts +61 -124
  76. package/dist/types/index.default.d.cts +7 -8
  77. package/dist/types/index.default.d.ts +7 -8
  78. package/dist/types/index.node.d.cts +7 -8
  79. package/dist/types/index.node.d.ts +7 -8
  80. package/dist/types/primitives/index.d.cts +4 -692
  81. package/dist/types/primitives/index.d.ts +4 -692
  82. package/dist/types/transport/index.d.cts +1 -1
  83. package/dist/types/transport/index.d.ts +1 -1
  84. package/package.json +94 -28
  85. package/dist/cjs/adapters/browser/index.cjs.map +0 -1
  86. package/dist/cjs/adapters/memory/index.cjs.map +0 -1
  87. package/dist/cjs/adapters/node/index.cjs.map +0 -1
  88. package/dist/cjs/chunks/adapter-detect-CD_LcgaH.cjs +0 -2
  89. package/dist/cjs/chunks/adapter-detect-CD_LcgaH.cjs.map +0 -1
  90. package/dist/cjs/chunks/browser-http-transport-CiMRqtDS.cjs +0 -2
  91. package/dist/cjs/chunks/browser-http-transport-CiMRqtDS.cjs.map +0 -1
  92. package/dist/cjs/chunks/context-BcoAzPuU.cjs.map +0 -1
  93. package/dist/cjs/chunks/error-CfjFO3x_.cjs +0 -2
  94. package/dist/cjs/chunks/error-CfjFO3x_.cjs.map +0 -1
  95. package/dist/cjs/chunks/index-BWCq_5BJ.cjs +0 -2
  96. package/dist/cjs/chunks/index-BWCq_5BJ.cjs.map +0 -1
  97. package/dist/cjs/chunks/lru-cache-1OU7JhD8.cjs.map +0 -1
  98. package/dist/cjs/chunks/memory-http-transport-BVL6VgB-.cjs +0 -2
  99. package/dist/cjs/chunks/memory-http-transport-BVL6VgB-.cjs.map +0 -1
  100. package/dist/cjs/chunks/node-http-transport-BvfVcf02.cjs +0 -2
  101. package/dist/cjs/chunks/node-http-transport-BvfVcf02.cjs.map +0 -1
  102. package/dist/cjs/chunks/progress-CxU62NC1.cjs.map +0 -1
  103. package/dist/cjs/chunks/readable-stream-MTA6D0Zh.cjs.map +0 -1
  104. package/dist/cjs/chunks/repository-BEg2zNtx.cjs +0 -2
  105. package/dist/cjs/chunks/repository-BEg2zNtx.cjs.map +0 -1
  106. package/dist/cjs/chunks/walk-submodules-D2Sp7JEB.cjs +0 -2
  107. package/dist/cjs/chunks/walk-submodules-D2Sp7JEB.cjs.map +0 -1
  108. package/dist/cjs/commands/index.cjs.map +0 -1
  109. package/dist/cjs/index.browser.cjs.map +0 -1
  110. package/dist/cjs/index.cjs.map +0 -1
  111. package/dist/cjs/index.default.cjs.map +0 -1
  112. package/dist/cjs/index.node.cjs.map +0 -1
  113. package/dist/cjs/operators/index.cjs.map +0 -1
  114. package/dist/cjs/primitives/index.cjs.map +0 -1
  115. package/dist/cjs/transport/index.cjs.map +0 -1
  116. package/dist/esm/adapters/browser/index.js.map +0 -1
  117. package/dist/esm/adapters/memory/index.js.map +0 -1
  118. package/dist/esm/adapters/node/index.js.map +0 -1
  119. package/dist/esm/chunks/adapter-detect-DkZhhUhd.js +0 -2
  120. package/dist/esm/chunks/adapter-detect-DkZhhUhd.js.map +0 -1
  121. package/dist/esm/chunks/browser-http-transport-4rJkHcQu.js +0 -2
  122. package/dist/esm/chunks/browser-http-transport-4rJkHcQu.js.map +0 -1
  123. package/dist/esm/chunks/context-CumKOV7K.js.map +0 -1
  124. package/dist/esm/chunks/error-Dc-SRSJK.js +0 -2
  125. package/dist/esm/chunks/error-Dc-SRSJK.js.map +0 -1
  126. package/dist/esm/chunks/index-jH0UMhh-.js +0 -2
  127. package/dist/esm/chunks/index-jH0UMhh-.js.map +0 -1
  128. package/dist/esm/chunks/lru-cache-Cy7C7Lx2.js.map +0 -1
  129. package/dist/esm/chunks/memory-http-transport-CA1WxWBp.js +0 -2
  130. package/dist/esm/chunks/memory-http-transport-CA1WxWBp.js.map +0 -1
  131. package/dist/esm/chunks/node-http-transport-DPa2lK3T.js +0 -2
  132. package/dist/esm/chunks/node-http-transport-DPa2lK3T.js.map +0 -1
  133. package/dist/esm/chunks/progress-BZobfl7p.js.map +0 -1
  134. package/dist/esm/chunks/readable-stream-CGuf8k1J.js.map +0 -1
  135. package/dist/esm/chunks/repository-DF6ePd1x.js +0 -2
  136. package/dist/esm/chunks/repository-DF6ePd1x.js.map +0 -1
  137. package/dist/esm/chunks/walk-submodules-Dq9oGwP8.js +0 -2
  138. package/dist/esm/chunks/walk-submodules-Dq9oGwP8.js.map +0 -1
  139. package/dist/esm/commands/index.js.map +0 -1
  140. package/dist/esm/index.browser.js.map +0 -1
  141. package/dist/esm/index.default.js.map +0 -1
  142. package/dist/esm/index.js.map +0 -1
  143. package/dist/esm/index.node.js.map +0 -1
  144. package/dist/esm/operators/index.js.map +0 -1
  145. package/dist/esm/primitives/index.js.map +0 -1
  146. package/dist/esm/transport/index.js.map +0 -1
  147. package/dist/types/chunks/index-entry-DtEbVpOG.d.cts +0 -47
  148. package/dist/types/chunks/index-entry-UD4dvtKh.d.ts +0 -47
  149. package/dist/types/chunks/progress-7KKiXXfX.d.ts +0 -405
  150. package/dist/types/chunks/progress-By8yFaUE.d.cts +0 -405
  151. package/dist/types/chunks/reflog-entry-BbNnXCTC.d.cts +0 -368
  152. package/dist/types/chunks/reflog-entry-CSzjnZd4.d.ts +0 -368
  153. package/dist/types/chunks/write-tree-C3oNxOlX.d.cts +0 -196
  154. package/dist/types/chunks/write-tree-DRI-LzSr.d.ts +0 -196
@@ -1,5 +1,5 @@
1
- import { b as Context, C as Compressor, I as InflateStreamResult, F as FileSystem, d as FileStat, D as DirEntry, e as FileHandle, H as HashService, f as Hasher, a as HookRunner, i as HookRequest, h as HookResult } from '../../chunks/context-CCL0_F8R.js';
2
- import { H as HttpTransport, a as HttpRequest, b as HttpResponse } from '../../chunks/http-transport--_NdQOiw.js';
1
+ import { c as Context, C as CommandRunner, z as CommandRequest, B as CommandResult, a as Compressor, I as InflateStreamResult, g as FileSystem, f as FileStat, D as DirEntry, y as FileHandle, h as HashService, i as Hasher, m as HookRunner, k as HookRequest, l as HookResult } from '../../chunks/context-sIhhtk2u.js';
2
+ import { b as HttpTransport, H as HttpRequest, a as HttpResponse } from '../../chunks/http-transport-DirKfK2S.js';
3
3
 
4
4
  interface NodeAdapterOptions {
5
5
  readonly workDir: string;
@@ -15,9 +15,62 @@ interface NodeAdapterOptions {
15
15
  * `process.env` — disable it for repositories you do not trust.
16
16
  */
17
17
  readonly hooks?: boolean;
18
+ /**
19
+ * Wire the merge-driver command runner (default true). Pass `false` to
20
+ * disable external merge drivers. A wired runner executes the configured
21
+ * `[merge "<driver>"].driver` shell command with the full `process.env`.
22
+ */
23
+ readonly command?: boolean;
24
+ /**
25
+ * Wire the ssh transport (default true). Pass `false` to refuse ssh/scp
26
+ * remotes. A wired transport spawns the resolved ssh command
27
+ * (`GIT_SSH_COMMAND`/`GIT_SSH`/`core.sshCommand`, else `ssh`) inheriting
28
+ * the full `process.env`.
29
+ */
30
+ readonly ssh?: boolean;
18
31
  }
19
32
  declare function createNodeContext(options: NodeAdapterOptions): Context;
20
33
 
34
+ /** Minimal child-process surface `NodeCommandRunner` consumes. */
35
+ interface CommandChild {
36
+ on(event: 'error', listener: (err: Error) => void): void;
37
+ on(event: 'close', listener: (code: number | null) => void): void;
38
+ kill(): void;
39
+ readonly stdin: {
40
+ write(chunk: Uint8Array): void;
41
+ end(): void;
42
+ };
43
+ readonly stdout: {
44
+ on(event: 'data', listener: (chunk: Uint8Array) => void): void;
45
+ };
46
+ }
47
+ /**
48
+ * Injectable process surface. Production uses `node:child_process` spawn; unit
49
+ * tests inject a fake so every branch runs deterministically without a real
50
+ * process (mirrors `HookRunnerOps`).
51
+ */
52
+ interface CommandRunnerOps {
53
+ readonly spawn: (command: string, args: ReadonlyArray<string>, options: {
54
+ readonly cwd: string;
55
+ readonly env: NodeJS.ProcessEnv;
56
+ readonly stdio: ['pipe', 'pipe', 'inherit'];
57
+ }) => CommandChild;
58
+ }
59
+ declare const realCommandRunnerOps: CommandRunnerOps;
60
+ /**
61
+ * Node `CommandRunner`: runs a command line through the platform shell
62
+ * (`sh -c` on POSIX, `cmd /c` on Windows) with `cwd` and an environment that
63
+ * merges `request.env` over `process.env`. Stdin is piped from `request.stdin`
64
+ * when present; stdout is captured into `result.stdout`. Never rejects for a
65
+ * non-zero exit.
66
+ */
67
+ declare class NodeCommandRunner implements CommandRunner {
68
+ private readonly isWindows;
69
+ private readonly ops;
70
+ constructor(platform?: NodeJS.Platform, ops?: CommandRunnerOps);
71
+ run(request: CommandRequest): Promise<CommandResult>;
72
+ }
73
+
21
74
  interface NodeCompressorOptions {
22
75
  /** Override the inflated-output cap. Tests use a small value to exercise the overflow branch. */
23
76
  readonly maxInflatedBytes?: number;
@@ -25,7 +78,8 @@ interface NodeCompressorOptions {
25
78
  declare class NodeCompressor implements Compressor {
26
79
  private readonly maxInflatedBytes;
27
80
  constructor(options?: NodeCompressorOptions);
28
- deflate: (data: Uint8Array) => Promise<Uint8Array>;
81
+ deflate: (data: Uint8Array, level?: number) => Promise<Uint8Array>;
82
+ deflateRaw: (data: Uint8Array, level?: number) => Promise<Uint8Array>;
29
83
  inflate: (data: Uint8Array) => Promise<Uint8Array>;
30
84
  streamInflate: (bytes: Uint8Array, offset: number) => Promise<InflateStreamResult>;
31
85
  createInflateStream: () => TransformStream<Uint8Array, Uint8Array>;
@@ -95,61 +149,86 @@ declare class NodeFileSystem implements FileSystem {
95
149
  private readonly fsOps;
96
150
  /**
97
151
  * Memoised realpath of an *existing* parent directory, keyed by the raw
98
- * (pre-realpath) parent path. Populated on every cache-miss inside
99
- * `resolveForCreation` so a clone/checkout that writes N files into the
100
- * same tree pays the realpath walk-up once per parent rather than once
101
- * per file.
152
+ * (pre-realpath) parent path, paired with that parent's containment
153
+ * verdict (`isContainedInEitherRoot(realParent, …)`, computed once right
154
+ * after the realpath resolves). The cached value is mode-independent, so
155
+ * this single cache serves BOTH `resolveForCreation` (creation mode) and
156
+ * `resolveForMode`'s lstat arm (lstat mode): a clone/checkout writing N
157
+ * files into the same tree, or a status walk lstat-ing N entries under
158
+ * the same directory, pays the realpath walk-up — and, for the lstat
159
+ * arm, the containment POST-check — once per parent rather than once per
160
+ * file/entry. The verdict is a per-clean-leaf equivalence
161
+ * (`isContainedInEitherRoot(join(realParent, basename))` ≡
162
+ * `isContainedInEitherRoot(realParent)`, see the design doc's proof) and
163
+ * is consulted ONLY by the lstat arm; `read`/`creation` realpath the full
164
+ * leaf and keep their own per-entry post-check.
102
165
  *
103
166
  * Invariants:
104
167
  * - Only EXISTING parents are cached. ENOENT walks fall back to
105
- * `realpathNearestExisting` and are never recorded.
106
- * - `rmRecursive` and `rename` clear the cache, which is correct (the
107
- * parent realpath may have changed) and cheap (the cache holds at
108
- * most a handful of small strings).
168
+ * `realpathNearestExisting` (creation) or propagate (lstat) and are
169
+ * never recorded.
170
+ * - The realpath and the verdict are set together never divergent.
171
+ * - `rmRecursive` and `rename` clear the cache (invalidating BOTH the
172
+ * realpath and the verdict), which is correct (the parent realpath may
173
+ * have changed) and cheap relative to a re-walk. `rm` clears neither —
174
+ * a leaf removal does not change the parent's realpath or containment.
175
+ * - Sized to exceed the 256 loose-object fanout directories so a
176
+ * full-history walk does not thrash the cache.
109
177
  */
110
- private readonly creationParentCache;
178
+ private readonly parentRealpathCache;
111
179
  /**
112
- * Lazy long-name canonicalisation of `rootDir` for containment checks.
113
- * Promise so concurrent first calls share one `realpath`; cleared on
114
- * rejection so a transient ENOENT can be retried.
180
+ * Lazy long-name canonicalisation of `rootDir` for containment checks,
181
+ * resolving to the canonical root's `RootPrefix`. Promise so concurrent
182
+ * first calls share one `realpath`; cleared on rejection so a transient
183
+ * ENOENT can be retried.
115
184
  */
116
185
  private canonicalRootPromise;
117
186
  /**
118
- * Memoised result of `pathPolicy.normalizeForCompare(rootDir)`. The
119
- * rootDir is `readonly` for the adapter's lifetime, so a single
120
- * normalisation is amortised across every containment check.
187
+ * Synchronous cache of the resolved canonical-root `RootPrefix`, set on
188
+ * `getCanonicalRoot()`'s promise-resolution arm and cleared on its
189
+ * rejection arm always in lockstep with `canonicalRootPromise`. Lets
190
+ * hot-path callers (`checkContainment`, `exists`, `symlink`) read the
191
+ * settled value directly, without an `await` (and its microtask), once
192
+ * the root has resolved at least once.
121
193
  */
122
- private normalizedRootDir;
194
+ private resolvedCanonicalRootPrefix;
123
195
  /**
124
- * Memoised result of `pathPolicy.normalizeForCompare(canonicalRoot)`.
125
- * Tracked alongside the canonical-root promise: when the promise
126
- * resolves we cache the normalised form once. The promise's
127
- * rejection-clears-cache rule means a transient ENOENT also clears
128
- * this field (so the next call re-normalises against the retried
129
- * canonical root).
196
+ * Memoised `{ normalized, withSep }` pair for `pathPolicy.normalizeForCompare(rootDir)`.
197
+ * The rootDir is `readonly` for the adapter's lifetime, so a single
198
+ * normalisation (and its `+sep` prefix) is amortised across every
199
+ * containment check. Bundled in one field never two — so the prefix
200
+ * can never diverge from the root it was derived from.
130
201
  */
131
- private normalizedCanonicalRoot;
202
+ private normalizedRootDirPrefix;
132
203
  constructor(rootDir: string, pathPolicy?: PathPolicy, fsOps?: FsOperations);
204
+ /**
205
+ * Lazily normalises `rootDir` and its `+sep` prefix as one bundled
206
+ * `RootPrefix`, memoised for the adapter's lifetime.
207
+ */
208
+ private getRootDirPrefix;
133
209
  private getNormalizedRootDir;
134
210
  /**
135
- * Returns the cached normalised canonical root. Caller must have
136
- * `await this.getCanonicalRoot()` immediately prior the cache is
137
- * populated by the promise's success arm and cleared on rejection, so
138
- * a successful `await` guarantees the field is set.
139
- *
140
- * Kept synchronous (vs `async` + `await this.getCanonicalRoot()`
141
- * inside) so the hot path doesn't pay an extra microtask suspension
142
- * per containment check on a settled promise. The `!` is the only
143
- * machine-readable form of "trust the post-await invariant"; the
144
- * private `await getCanonicalRoot()` discipline at every call site is
145
- * what makes it safe.
211
+ * Resolves and memoises the canonical-root `RootPrefix`. Returns the
212
+ * bundled `{ normalized, withSep }` value directly from the promise
213
+ * chain callers thread the returned value onward, so there is no
214
+ * synchronous "trust it's been set" field read: the type system proves
215
+ * the value is defined via the `await`'s return, not a nullable field.
146
216
  */
147
- private getResolvedNormalizedCanonicalRoot;
148
217
  private getCanonicalRoot;
218
+ /**
219
+ * Synchronous-first-path accessor for the canonical root: returns the
220
+ * already-settled `resolvedCanonicalRootPrefix` field when populated (no
221
+ * `await`, no microtask), falling back to `getCanonicalRoot()` only on
222
+ * the first call or after a rejection cleared the field. Used by every
223
+ * hot-path call site (`checkContainment`, `exists`, `symlink`) instead of
224
+ * an unconditional `await this.getCanonicalRoot()`.
225
+ */
226
+ private resolveCanonicalRoot;
149
227
  read: (path: string) => Promise<Uint8Array>;
150
228
  readSlice: (path: string, offset: number, length: number) => Promise<Uint8Array>;
151
229
  readUtf8: (path: string) => Promise<string>;
152
230
  write: (path: string, data: Uint8Array) => Promise<void>;
231
+ writeStream: (path: string, source: AsyncIterable<Uint8Array>) => Promise<void>;
153
232
  writeExclusive: (path: string, data: Uint8Array) => Promise<void>;
154
233
  writeUtf8: (path: string, content: string) => Promise<void>;
155
234
  appendUtf8: (path: string, content: string) => Promise<void>;
@@ -168,8 +247,35 @@ declare class NodeFileSystem implements FileSystem {
168
247
  private isSymlinkLeaf;
169
248
  private removeTree;
170
249
  private resolveForCreation;
250
+ private cachedParentRealpath;
171
251
  private realpathForCreation;
252
+ /**
253
+ * Resolves `resolved` to its real, containment-checkable form. `contained`
254
+ * is `undefined` for `read`/`creation` — those arms realpath a full leaf,
255
+ * so `checkContainment` still runs its own per-entry POST-check on the
256
+ * returned `real`. For `lstat`, `contained` carries the per-parent
257
+ * verdict `cachedParentRealpath` already computed (once per parent,
258
+ * proven verdict-identical to a per-entry check for a single clean leaf)
259
+ * — `checkContainment` consults it directly instead of re-normalising.
260
+ */
172
261
  private resolveForMode;
262
+ /**
263
+ * `abs` is normalised ONCE (not once per root) and compared against
264
+ * both roots' precomputed `+sep` prefixes. Both the `=== root` equality
265
+ * arm and the `startsWith(root + sep)` prefix arm are retained per
266
+ * root — dropping either changes the verdict (a bare `startsWith(root)`
267
+ * would admit the prefix-only sibling `root-evil`). Both roots are
268
+ * passed in as already-resolved `RootPrefix` values — no field read-back.
269
+ */
270
+ private isContainedInEitherRoot;
271
+ /**
272
+ * Single source of truth for the two-root, two-arm containment test —
273
+ * `isContainedInEitherRoot` and the lstat arm's PRE-check both delegate
274
+ * here so the "is `abs` exactly one of the roots" fact (needed by the
275
+ * lstat arm's exact-root shortcut) is derived from the SAME normalise
276
+ * call as the containment verdict, not a second one.
277
+ */
278
+ private containmentVerdict;
173
279
  private checkContainment;
174
280
  homedir(): string;
175
281
  xdgConfigHome(): string;
@@ -249,5 +355,5 @@ declare class NodeHttpTransport implements HttpTransport {
249
355
  request: (req: HttpRequest) => Promise<HttpResponse>;
250
356
  }
251
357
 
252
- export { NodeCompressor, NodeFileSystem, NodeHashService, NodeHookRunner, NodeHttpTransport, createNodeContext };
253
- export type { NodeAdapterOptions, NodeHttpTransportOptions };
358
+ export { NodeCommandRunner, NodeCompressor, NodeFileSystem, NodeHashService, NodeHookRunner, NodeHttpTransport, createNodeContext, realCommandRunnerOps };
359
+ export type { CommandRunnerOps, NodeAdapterOptions, NodeHttpTransportOptions };
@@ -1,9 +1,11 @@
1
- import { H as HttpTransport } from './http-transport--_NdQOiw.cjs';
1
+ import { b as HttpTransport } from './http-transport-DirKfK2S.cjs';
2
2
 
3
3
  interface HashConfig {
4
4
  readonly digestLength: 20 | 32;
5
5
  readonly hexLength: 40 | 64;
6
6
  }
7
+ declare const SHA1_CONFIG: HashConfig;
8
+ declare const SHA256_CONFIG: HashConfig;
7
9
 
8
10
  type ObjectId = string & {
9
11
  readonly __brand: unique symbol;
@@ -12,6 +14,13 @@ declare const ObjectId: {
12
14
  readonly from: (hex: string) => ObjectId;
13
15
  readonly fromRaw: (bytes: Uint8Array) => ObjectId;
14
16
  };
17
+ declare const ZERO_OID: ObjectId;
18
+ /**
19
+ * SHA-1 of the canonical empty tree object. Used by `merge` for unrelated-history
20
+ * three-way merges where the merge base is undefined — substituted as the
21
+ * "empty base" so mergeTrees produces the union of both sides.
22
+ */
23
+ declare const EMPTY_TREE_OID: ObjectId;
15
24
  type RefName = string & {
16
25
  readonly __brand: unique symbol;
17
26
  };
@@ -36,6 +45,55 @@ interface LruCache<V> {
36
45
  readonly entryCount: number;
37
46
  }
38
47
 
48
+ /**
49
+ * A single shell-command invocation. The port is stateless — every fact the
50
+ * adapter needs to run the command travels in this request.
51
+ */
52
+ interface CommandRequest {
53
+ /** Shell command line, executed through the platform shell (`sh -c` / `cmd /c`). */
54
+ readonly command: string;
55
+ /** Working directory for the spawned process. */
56
+ readonly cwd: string;
57
+ /** Environment additions merged onto the parent environment by the adapter. */
58
+ readonly env: Readonly<Record<string, string>>;
59
+ /** Cancels a running command — the adapter kills the child when it aborts. */
60
+ readonly signal?: AbortSignal;
61
+ /** Bytes fed to the child's stdin (clean/smudge input). When absent, stdin is not written. */
62
+ readonly stdin?: Uint8Array;
63
+ }
64
+ /**
65
+ * Outcome of a command. Two output conventions are carried by one port:
66
+ * - A merge driver communicates its result by overwriting its output file `%A`
67
+ * — only the exit code matters (`0` ⇒ success); `stdout` is ignored.
68
+ * - A textconv or clean/smudge filter driver communicates its result via stdout
69
+ * — the caller reads `result.stdout` for the transformed bytes.
70
+ */
71
+ interface CommandResult {
72
+ readonly exitCode: number;
73
+ /** Bytes captured from the child's stdout (textconv/filter output). Absent when not captured. */
74
+ readonly stdout?: Uint8Array;
75
+ }
76
+ /**
77
+ * Runs an arbitrary shell command line — the custom merge-driver / textconv /
78
+ * clean-smudge filter command. Optional on `Context`: when absent (browser /
79
+ * memory adapters cannot spawn a process), a configured external driver falls
80
+ * back to the built-in behaviour.
81
+ *
82
+ * NEVER rejects for a non-zero exit — interpreting the exit code is the
83
+ * caller's policy.
84
+ *
85
+ * Two output conventions coexist on this single port:
86
+ * - **Merge driver**: communicates via its `%A` output file; the caller
87
+ * ignores `result.stdout`. Pass no `stdin`; `stdout` will be absent.
88
+ * - **Textconv / filter driver**: communicates via stdout; the caller reads
89
+ * `result.stdout`. Pass `stdin` bytes when the driver reads from stdin
90
+ * (clean/smudge); omit `stdin` when the driver reads from a file argument
91
+ * (textconv).
92
+ */
93
+ interface CommandRunner {
94
+ readonly run: (request: CommandRequest) => Promise<CommandResult>;
95
+ }
96
+
39
97
  interface InflateStreamResult {
40
98
  /** The fully-inflated output bytes. */
41
99
  readonly output: Uint8Array;
@@ -43,8 +101,18 @@ interface InflateStreamResult {
43
101
  readonly bytesConsumed: number;
44
102
  }
45
103
  interface Compressor {
46
- /** Deflate (compress) data using zlib deflate format (RFC 1950). */
47
- readonly deflate: (data: Uint8Array) => Promise<Uint8Array>;
104
+ /**
105
+ * Deflate (compress) data using zlib deflate format (RFC 1950).
106
+ * `level` (when given and in zlib's -1..9 domain) tunes the compression
107
+ * level; adapters that cannot set a level accept and ignore it.
108
+ */
109
+ readonly deflate: (data: Uint8Array, level?: number) => Promise<Uint8Array>;
110
+ /**
111
+ * Raw DEFLATE (compress) using bare RFC 1951 bitstream — no zlib (RFC 1950)
112
+ * 2-byte header and no adler32 trailer. `level` semantics match `deflate`.
113
+ * Used by zip archive serializer (method 8). Additive — `deflate` unchanged.
114
+ */
115
+ readonly deflateRaw: (data: Uint8Array, level?: number) => Promise<Uint8Array>;
48
116
  /** Inflate (decompress) zlib-compressed data. */
49
117
  readonly inflate: (data: Uint8Array) => Promise<Uint8Array>;
50
118
  /**
@@ -66,6 +134,11 @@ interface Compressor {
66
134
  readonly createInflateStream: () => TransformStream<Uint8Array, Uint8Array>;
67
135
  }
68
136
 
137
+ interface EnvReader {
138
+ /** A single named environment variable's value, or `undefined` when unset. */
139
+ readonly get: (name: string) => string | undefined;
140
+ }
141
+
69
142
  /** Metadata returned by stat operations. */
70
143
  interface FileStat {
71
144
  readonly ctimeMs: number;
@@ -116,6 +189,8 @@ interface FileSystem {
116
189
  readonly readUtf8: (path: string) => Promise<string>;
117
190
  /** Write bytes to file, creating parent directories as needed. Overwrites if exists. */
118
191
  readonly write: (path: string, data: Uint8Array) => Promise<void>;
192
+ /** Stream bytes to file from an async source, creating parent directories as needed. Overwrites if exists. Writes bytes verbatim. */
193
+ readonly writeStream: (path: string, source: AsyncIterable<Uint8Array>) => Promise<void>;
119
194
  /**
120
195
  * Write bytes to file. Fails with FILE_EXISTS if the file already exists (exclusive create).
121
196
  *
@@ -244,7 +319,7 @@ interface HashService {
244
319
  * `HOOK_FAILED` command error reference it — a port may import domain, but
245
320
  * domain may never import a port.
246
321
  */
247
- type HookName = 'pre-commit' | 'commit-msg' | 'pre-push';
322
+ type HookName = 'pre-commit' | 'prepare-commit-msg' | 'commit-msg' | 'post-commit' | 'post-merge' | 'post-checkout' | 'pre-push' | 'pre-rebase' | 'post-rewrite';
248
323
 
249
324
  /**
250
325
  * A single hook invocation. The port is stateless — every fact the adapter
@@ -373,6 +448,43 @@ interface PromisorRemote {
373
448
  fetch(oids: ReadonlyArray<ObjectId>): Promise<PromisorFetchOutcome>;
374
449
  }
375
450
 
451
+ /**
452
+ * Request to open one SSH duplex channel. `command`/`args` are the fully
453
+ * resolved ssh program and argv (host token, `-p` flag, sq-quoted remote
454
+ * command) — the adapter performs no faithfulness logic of its own.
455
+ */
456
+ interface SshSpawnRequest {
457
+ /** Resolved ssh program (e.g. `ssh`, or a `GIT_SSH`/`core.sshCommand` override). */
458
+ readonly command: string;
459
+ /** Full argv, incl. host token and remote-command token. */
460
+ readonly args: ReadonlyArray<string>;
461
+ /** Additions merged OVER the parent process environment by the adapter. */
462
+ readonly env: Readonly<Record<string, string>>;
463
+ readonly signal?: AbortSignal;
464
+ }
465
+ /**
466
+ * One live SSH duplex channel — a thin process bridge. Stream shapes mirror
467
+ * `HttpTransport`: web streams in, web streams out.
468
+ */
469
+ interface SshChannel {
470
+ /** Request bytes written to the server. */
471
+ readonly stdin: WritableStream<Uint8Array>;
472
+ /** Advertisement + response bytes read from the server. */
473
+ readonly stdout: ReadableStream<Uint8Array>;
474
+ /** Resolves with the ssh process's exit code. */
475
+ readonly exit: Promise<number>;
476
+ /** Idempotent teardown — kills the child process. */
477
+ readonly close: () => Promise<void>;
478
+ }
479
+ /**
480
+ * Thin duplex process spawner. Knows nothing about git — argv building,
481
+ * quoting, and command resolution are pure application-tier concerns tested
482
+ * independently of this port.
483
+ */
484
+ interface SshTransport {
485
+ readonly open: (req: SshSpawnRequest) => Promise<SshChannel>;
486
+ }
487
+
376
488
  /**
377
489
  * Repository physical layout — where the working tree and.git directory live.
378
490
  * Renamed in from the previous `RepositoryConfig` (port-tier) to free that
@@ -383,6 +495,14 @@ interface RepositoryLayout {
383
495
  readonly workDir: string;
384
496
  /** Absolute path to the.git directory (usually `${workDir}/.git`, but may differ for bare repos or worktrees). */
385
497
  readonly gitDir: string;
498
+ /**
499
+ * Absolute path to the shared **common** git dir — objects, `packed-refs`,
500
+ * `config`, and shared refs/reflogs. Absent for a normal repo or the main
501
+ * worktree (it equals `gitDir`); set only for a linked worktree, whose
502
+ * `gitDir` is its own admin dir while shared state lives here. Resolve via
503
+ * `commonGitDir(ctx)` rather than reading this field directly.
504
+ */
505
+ readonly commonDir?: string;
386
506
  /** Whether this is a bare repository. */
387
507
  readonly bare: boolean;
388
508
  /**
@@ -438,6 +558,14 @@ interface RepositoryConfig {
438
558
  */
439
559
  readonly maxObjectsPerPack?: number;
440
560
  readonly detectRenames?: boolean;
561
+ /**
562
+ * Programmatic facade defaults for whitespace-diff modes. These are NOT git's
563
+ * `.git/config` settings and NOT `core.whitespace` — they are library-tier
564
+ * defaults applied by the `diff` command when no per-call option overrides them.
565
+ */
566
+ readonly ignoreWhitespace?: 'all' | 'change' | 'at-eol';
567
+ readonly ignoreCrAtEol?: boolean;
568
+ readonly ignoreBlankLines?: boolean;
441
569
  readonly breakStaleLockMs?: number;
442
570
  readonly dnsResolver?: (host: string) => Promise<ReadonlyArray<string>>;
443
571
  /** Hard cap on `dnsResolver` return-array length to bound resolver-amplification DoS. Default 64. */
@@ -453,6 +581,8 @@ interface Context {
453
581
  readonly layout: RepositoryLayout;
454
582
  /** User-supplied working directory (may be a sub-path of layout.workDir). Defaults to layout.workDir when not set by the facade. */
455
583
  readonly cwd: string;
584
+ /** The runtime this context was built for — names the adapter set in refusal messages. */
585
+ readonly runtime: 'node' | 'browser' | 'memory';
456
586
  /** Object serialization parameters (sha1 vs sha256 digest+hex sizes). */
457
587
  readonly hashConfig: HashConfig;
458
588
  /** Shared delta-base LRU cache; consumed by primitives' iterative delta walker. */
@@ -465,11 +595,36 @@ interface Context {
465
595
  readonly signal?: AbortSignal;
466
596
  /** Optional hook runner. Absent ⇒ hooks are inert (browser, or opted out). */
467
597
  readonly hooks?: HookRunner;
598
+ /**
599
+ * Optional shell-command runner. Absent ⇒ a configured external merge driver
600
+ * falls back to the built-in merge (browser / memory adapters cannot spawn a
601
+ * process).
602
+ */
603
+ readonly command?: CommandRunner;
604
+ /**
605
+ * Optional environment-variable reader. Absent ⇒ every variable is unset (browser /
606
+ * memory, where there is no process env). Notes-ref selection reads GIT_NOTES_REF through it.
607
+ */
608
+ readonly env?: EnvReader;
609
+ /**
610
+ * Optional SSH transport. Absent ⇒ ssh/scp remotes refuse — browser/memory
611
+ * cannot spawn a process.
612
+ */
613
+ readonly ssh?: SshTransport;
468
614
  /**
469
615
  * Optional promisor-remote capability. Populated by `openRepository`;
470
616
  * `readObject` consults it to lazy-fetch an object a partial clone omitted.
471
617
  */
472
618
  readonly promisor?: PromisorRemote;
619
+ /**
620
+ * Optional facade capability returning a `FileSystem` confined to one or more
621
+ * linked worktree paths AND the common dir (ADR-298). The worktree commands
622
+ * route worktree-directory I/O through it so worktrees outside `workDir` stay
623
+ * faithful without dropping containment (`move` passes both endpoints). Absent
624
+ * on sandboxed adapters (memory/browser), where worktrees are confined under
625
+ * the adapter root.
626
+ */
627
+ readonly worktreeFs?: (worktreePath: string | ReadonlyArray<string>) => FileSystem;
473
628
  }
474
629
  interface CreateContextParts {
475
630
  readonly fs: FileSystem;
@@ -479,15 +634,19 @@ interface CreateContextParts {
479
634
  readonly progress: ProgressReporter;
480
635
  readonly layout: RepositoryLayout;
481
636
  readonly cwd?: string;
637
+ readonly runtime: 'node' | 'browser' | 'memory';
482
638
  readonly hashConfig: HashConfig;
483
639
  readonly deltaCache: LruCache<Uint8Array>;
484
640
  readonly config?: RepositoryConfig;
485
641
  readonly logger?: Logger;
486
642
  readonly signal?: AbortSignal;
487
643
  readonly hooks?: HookRunner;
644
+ readonly command?: CommandRunner;
645
+ readonly env?: EnvReader;
646
+ readonly ssh?: SshTransport;
488
647
  }
489
648
  /** Assemble a frozen Context from its constituent ports + layout. */
490
649
  declare function createContext(parts: CreateContextParts): Context;
491
650
 
492
- export { ObjectId as O, FilePath as c, RefName as j, createContext as p, noopLogger as q, wrapLoggerSanitizer as w };
493
- export type { AuthStrategy as A, Compressor as C, DirEntry as D, FileSystem as F, HashService as H, InflateStreamResult as I, Logger as L, ProgressReporter as P, RepositoryConfig as R, HookRunner as a, Context as b, FileStat as d, FileHandle as e, Hasher as f, HookName as g, HookResult as h, HookRequest as i, AuthorIdentity as k, CreateContextParts as l, PromisorFetchOutcome as m, PromisorRemote as n, RepositoryLayout as o };
651
+ export { EMPTY_TREE_OID as E, FilePath as F, ObjectId as O, RefName as R, SHA1_CONFIG as S, ZERO_OID as Z, SHA256_CONFIG as r, createContext as v, noopLogger as w, wrapLoggerSanitizer as x };
652
+ export type { AuthStrategy as A, CommandResult as B, CommandRunner as C, DirEntry as D, HashConfig as H, InflateStreamResult as I, Logger as L, ProgressReporter as P, Compressor as a, AuthorIdentity as b, Context as c, CreateContextParts as d, EnvReader as e, FileStat as f, FileSystem as g, HashService as h, Hasher as i, HookName as j, HookRequest as k, HookResult as l, HookRunner as m, PromisorFetchOutcome as n, PromisorRemote as o, RepositoryConfig as p, RepositoryLayout as q, SshChannel as s, SshSpawnRequest as t, SshTransport as u, FileHandle as y, CommandRequest as z };