@fluado/enmetu 0.0.0-stage → 0.2.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 (209) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +209 -2
  3. package/dist/adapters/clock/systemClockAdapter.d.ts +6 -0
  4. package/dist/adapters/clock/systemClockAdapter.js +12 -0
  5. package/dist/adapters/fs/nodeFileSystemAdapter.d.ts +18 -0
  6. package/dist/adapters/fs/nodeFileSystemAdapter.js +230 -0
  7. package/dist/adapters/git/gitFacade.d.ts +148 -0
  8. package/dist/adapters/git/gitFacade.js +822 -0
  9. package/dist/adapters/git/gitLocatorFacade.d.ts +8 -0
  10. package/dist/adapters/git/gitLocatorFacade.js +75 -0
  11. package/dist/adapters/git/gitProcessAdapter.d.ts +46 -0
  12. package/dist/adapters/git/gitProcessAdapter.js +261 -0
  13. package/dist/adapters/log/nullLogger.d.ts +9 -0
  14. package/dist/adapters/log/nullLogger.js +10 -0
  15. package/dist/adapters/log/stderrLoggerAdapter.d.ts +10 -0
  16. package/dist/adapters/log/stderrLoggerAdapter.js +13 -0
  17. package/dist/adapters/process/nodeProcessAdapter.d.ts +9 -0
  18. package/dist/adapters/process/nodeProcessAdapter.js +56 -0
  19. package/dist/adapters/watch/fsWatchAdapter.d.ts +24 -0
  20. package/dist/adapters/watch/fsWatchAdapter.js +47 -0
  21. package/dist/cli/commands/hookCommand.d.ts +29 -0
  22. package/dist/cli/commands/hookCommand.js +92 -0
  23. package/dist/cli/commands/resumeCommand.d.ts +48 -0
  24. package/dist/cli/commands/resumeCommand.js +124 -0
  25. package/dist/cli/commands/runCommand.d.ts +34 -0
  26. package/dist/cli/commands/runCommand.js +79 -0
  27. package/dist/cli/commands/statusCommand.d.ts +71 -0
  28. package/dist/cli/commands/statusCommand.js +249 -0
  29. package/dist/cli/exitCodes.d.ts +7 -0
  30. package/dist/cli/exitCodes.js +38 -0
  31. package/dist/cli/helpText.d.ts +1 -0
  32. package/dist/cli/helpText.js +16 -0
  33. package/dist/cli/main.d.ts +8 -0
  34. package/dist/cli/main.js +49 -0
  35. package/dist/cli/parseArgs.d.ts +19 -0
  36. package/dist/cli/parseArgs.js +106 -0
  37. package/dist/cli.d.ts +2 -0
  38. package/dist/cli.js +32 -0
  39. package/dist/commit/commitLocalChangesController.d.ts +132 -0
  40. package/dist/commit/commitLocalChangesController.js +357 -0
  41. package/dist/commit/gitIndexLock.d.ts +36 -0
  42. package/dist/commit/gitIndexLock.js +90 -0
  43. package/dist/commit/repairStaleIndexController.d.ts +90 -0
  44. package/dist/commit/repairStaleIndexController.js +150 -0
  45. package/dist/commit/stageGuardedChanges.d.ts +30 -0
  46. package/dist/commit/stageGuardedChanges.js +52 -0
  47. package/dist/contracts/IClock.d.ts +7 -0
  48. package/dist/contracts/IClock.js +1 -0
  49. package/dist/contracts/IDaemon.d.ts +26 -0
  50. package/dist/contracts/IDaemon.js +1 -0
  51. package/dist/contracts/IFileSystem.d.ts +50 -0
  52. package/dist/contracts/IFileSystem.js +1 -0
  53. package/dist/contracts/IGitCommitter.d.ts +60 -0
  54. package/dist/contracts/IGitCommitter.js +1 -0
  55. package/dist/contracts/IGitConfigReader.d.ts +5 -0
  56. package/dist/contracts/IGitConfigReader.js +1 -0
  57. package/dist/contracts/IGitIndexLock.d.ts +44 -0
  58. package/dist/contracts/IGitIndexLock.js +1 -0
  59. package/dist/contracts/IGitReader.d.ts +81 -0
  60. package/dist/contracts/IGitReader.js +1 -0
  61. package/dist/contracts/IGitRunner.d.ts +49 -0
  62. package/dist/contracts/IGitRunner.js +1 -0
  63. package/dist/contracts/IGitWriter.d.ts +39 -0
  64. package/dist/contracts/IGitWriter.js +1 -0
  65. package/dist/contracts/IHaltStore.d.ts +14 -0
  66. package/dist/contracts/IHaltStore.js +1 -0
  67. package/dist/contracts/IHookInstaller.d.ts +14 -0
  68. package/dist/contracts/IHookInstaller.js +1 -0
  69. package/dist/contracts/IInstanceLock.d.ts +26 -0
  70. package/dist/contracts/IInstanceLock.js +1 -0
  71. package/dist/contracts/ILogger.d.ts +11 -0
  72. package/dist/contracts/ILogger.js +1 -0
  73. package/dist/contracts/IOperationJournal.d.ts +12 -0
  74. package/dist/contracts/IOperationJournal.js +1 -0
  75. package/dist/contracts/IPathQuarantine.d.ts +67 -0
  76. package/dist/contracts/IPathQuarantine.js +1 -0
  77. package/dist/contracts/IProcessHost.d.ts +11 -0
  78. package/dist/contracts/IProcessHost.js +1 -0
  79. package/dist/contracts/IRepositoryLocator.d.ts +32 -0
  80. package/dist/contracts/IRepositoryLocator.js +1 -0
  81. package/dist/contracts/IStateRecorder.d.ts +68 -0
  82. package/dist/contracts/IStateRecorder.js +1 -0
  83. package/dist/contracts/IStateStore.d.ts +88 -0
  84. package/dist/contracts/IStateStore.js +1 -0
  85. package/dist/contracts/ISyncCycle.d.ts +23 -0
  86. package/dist/contracts/ISyncCycle.js +1 -0
  87. package/dist/contracts/ISyncCycleRunner.d.ts +4 -0
  88. package/dist/contracts/ISyncCycleRunner.js +1 -0
  89. package/dist/contracts/ISyncScheduler.d.ts +6 -0
  90. package/dist/contracts/ISyncScheduler.js +1 -0
  91. package/dist/contracts/ITextSink.d.ts +3 -0
  92. package/dist/contracts/ITextSink.js +1 -0
  93. package/dist/contracts/IWatcher.d.ts +9 -0
  94. package/dist/contracts/IWatcher.js +1 -0
  95. package/dist/contracts/config.d.ts +26 -0
  96. package/dist/contracts/config.js +1 -0
  97. package/dist/contracts/errors.d.ts +39 -0
  98. package/dist/contracts/errors.js +64 -0
  99. package/dist/contracts/exitCode.d.ts +10 -0
  100. package/dist/contracts/exitCode.js +17 -0
  101. package/dist/contracts/gitTypes.d.ts +113 -0
  102. package/dist/contracts/gitTypes.js +1 -0
  103. package/dist/contracts/library.d.ts +35 -0
  104. package/dist/contracts/library.js +1 -0
  105. package/dist/contracts/logCodes.d.ts +1 -0
  106. package/dist/contracts/logCodes.js +1 -0
  107. package/dist/contracts/runtime.d.ts +16 -0
  108. package/dist/contracts/runtime.js +1 -0
  109. package/dist/contracts/syncTypes.d.ts +75 -0
  110. package/dist/contracts/syncTypes.js +6 -0
  111. package/dist/core/commit/commitMemory.d.ts +3 -0
  112. package/dist/core/commit/commitMemory.js +9 -0
  113. package/dist/core/commit/commitMessage.d.ts +6 -0
  114. package/dist/core/commit/commitMessage.js +12 -0
  115. package/dist/core/config/parseConfig.d.ts +8 -0
  116. package/dist/core/config/parseConfig.js +176 -0
  117. package/dist/core/content/contentRules.d.ts +11 -0
  118. package/dist/core/content/contentRules.js +50 -0
  119. package/dist/core/errors/errorMessage.d.ts +6 -0
  120. package/dist/core/errors/errorMessage.js +14 -0
  121. package/dist/core/errors/withCycleId.d.ts +7 -0
  122. package/dist/core/errors/withCycleId.js +38 -0
  123. package/dist/core/git/enmetuRefs.d.ts +9 -0
  124. package/dist/core/git/enmetuRefs.js +22 -0
  125. package/dist/core/git/porcelain.d.ts +52 -0
  126. package/dist/core/git/porcelain.js +219 -0
  127. package/dist/core/git/rebaseOwnership.d.ts +28 -0
  128. package/dist/core/git/rebaseOwnership.js +29 -0
  129. package/dist/core/git/stoppedRebaseWrites.d.ts +10 -0
  130. package/dist/core/git/stoppedRebaseWrites.js +22 -0
  131. package/dist/core/halt/resumeHint.d.ts +6 -0
  132. package/dist/core/halt/resumeHint.js +9 -0
  133. package/dist/core/log/formatLogLine.d.ts +3 -0
  134. package/dist/core/log/formatLogLine.js +11 -0
  135. package/dist/core/quarantine/keptBlobMessage.d.ts +5 -0
  136. package/dist/core/quarantine/keptBlobMessage.js +8 -0
  137. package/dist/core/quarantine/restoreWarnings.d.ts +16 -0
  138. package/dist/core/quarantine/restoreWarnings.js +36 -0
  139. package/dist/core/schedule/schedulerStep.d.ts +63 -0
  140. package/dist/core/schedule/schedulerStep.js +144 -0
  141. package/dist/core/state/stateGuards.d.ts +21 -0
  142. package/dist/core/state/stateGuards.js +295 -0
  143. package/dist/core/state/stateLiveness.d.ts +10 -0
  144. package/dist/core/state/stateLiveness.js +21 -0
  145. package/dist/core/state/stateRecordStep.d.ts +28 -0
  146. package/dist/core/state/stateRecordStep.js +203 -0
  147. package/dist/core/status/conflictGuide.d.ts +30 -0
  148. package/dist/core/status/conflictGuide.js +86 -0
  149. package/dist/core/status/orphanedCommits.d.ts +15 -0
  150. package/dist/core/status/orphanedCommits.js +24 -0
  151. package/dist/core/status/statusHeadline.d.ts +46 -0
  152. package/dist/core/status/statusHeadline.js +130 -0
  153. package/dist/core/watch/watchFilter.d.ts +2 -0
  154. package/dist/core/watch/watchFilter.js +34 -0
  155. package/dist/daemon/daemon.d.ts +55 -0
  156. package/dist/daemon/daemon.js +277 -0
  157. package/dist/enmetuInstances.d.ts +65 -0
  158. package/dist/enmetuInstances.js +332 -0
  159. package/dist/halt/haltGuardGitWriter.d.ts +23 -0
  160. package/dist/halt/haltGuardGitWriter.js +61 -0
  161. package/dist/halt/haltStore.d.ts +13 -0
  162. package/dist/halt/haltStore.js +49 -0
  163. package/dist/halt/recordHalt.d.ts +19 -0
  164. package/dist/halt/recordHalt.js +20 -0
  165. package/dist/hooks/hookScript.d.ts +22 -0
  166. package/dist/hooks/hookScript.js +52 -0
  167. package/dist/hooks/nullHookInstaller.d.ts +5 -0
  168. package/dist/hooks/nullHookInstaller.js +6 -0
  169. package/dist/hooks/preCommitCheckController.d.ts +23 -0
  170. package/dist/hooks/preCommitCheckController.js +22 -0
  171. package/dist/hooks/preCommitHookInstaller.d.ts +32 -0
  172. package/dist/hooks/preCommitHookInstaller.js +123 -0
  173. package/dist/index.d.ts +18 -0
  174. package/dist/index.js +38 -0
  175. package/dist/library/enmetuLibrary.d.ts +16 -0
  176. package/dist/library/enmetuLibrary.js +62 -0
  177. package/dist/library/readStateController.d.ts +28 -0
  178. package/dist/library/readStateController.js +32 -0
  179. package/dist/library/requestedStopProcessHost.d.ts +28 -0
  180. package/dist/library/requestedStopProcessHost.js +58 -0
  181. package/dist/lock/instanceLock.d.ts +26 -0
  182. package/dist/lock/instanceLock.js +125 -0
  183. package/dist/preflight/repositoryPreflight.d.ts +20 -0
  184. package/dist/preflight/repositoryPreflight.js +42 -0
  185. package/dist/quarantine/pathQuarantine.d.ts +113 -0
  186. package/dist/quarantine/pathQuarantine.js +504 -0
  187. package/dist/recovery/enmetuRebaseAbort.d.ts +108 -0
  188. package/dist/recovery/enmetuRebaseAbort.js +302 -0
  189. package/dist/recovery/operationJournal.d.ts +13 -0
  190. package/dist/recovery/operationJournal.js +37 -0
  191. package/dist/recovery/recoveryController.d.ts +48 -0
  192. package/dist/recovery/recoveryController.js +112 -0
  193. package/dist/state/stateFileStore.d.ts +18 -0
  194. package/dist/state/stateFileStore.js +73 -0
  195. package/dist/state/stateRecorder.d.ts +26 -0
  196. package/dist/state/stateRecorder.js +69 -0
  197. package/dist/state/stateRecordingSyncCycle.d.ts +24 -0
  198. package/dist/state/stateRecordingSyncCycle.js +71 -0
  199. package/dist/sync/hookAbortingSyncScheduler.d.ts +16 -0
  200. package/dist/sync/hookAbortingSyncScheduler.js +27 -0
  201. package/dist/sync/integrateUpstreamController.d.ts +95 -0
  202. package/dist/sync/integrateUpstreamController.js +353 -0
  203. package/dist/sync/syncCycleController.d.ts +71 -0
  204. package/dist/sync/syncCycleController.js +345 -0
  205. package/dist/sync/syncCycleRunner.d.ts +24 -0
  206. package/dist/sync/syncCycleRunner.js +169 -0
  207. package/dist/sync/syncScheduler.d.ts +32 -0
  208. package/dist/sync/syncScheduler.js +97 -0
  209. package/package.json +42 -4
@@ -0,0 +1,14 @@
1
+ export type HookSkipReason = 'HOOKS_PATH_SET' | 'FOREIGN_HOOK';
2
+ export type HookInstallResult = {
3
+ readonly kind: 'installed' | 'updated' | 'unchanged' | 'disabled';
4
+ } | {
5
+ readonly kind: 'skipped';
6
+ readonly reason: HookSkipReason;
7
+ readonly instruction: string;
8
+ } | {
9
+ readonly kind: 'failed';
10
+ readonly detail: string;
11
+ };
12
+ export interface IHookInstaller {
13
+ ensure(): Promise<HookInstallResult>;
14
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,26 @@
1
+ export interface LockHolder {
2
+ readonly pid: number;
3
+ /** Informational; the state directory is assumed to be on a local disk. */
4
+ readonly hostname: string;
5
+ readonly startedAt: string;
6
+ }
7
+ export type LockAcquisition = {
8
+ readonly kind: 'acquired';
9
+ readonly tookOverStale: boolean;
10
+ } | {
11
+ readonly kind: 'held';
12
+ readonly holder: LockHolder;
13
+ } | {
14
+ readonly kind: 'unreadable';
15
+ readonly path: string;
16
+ };
17
+ /** The read half of the lock, all that status and the library need (design 12.1, 12.5). */
18
+ export interface ILockInspector {
19
+ /** The live holder, or null when none (a holder whose pid is dead is no holder). */
20
+ inspect(): Promise<LockHolder | null>;
21
+ }
22
+ export interface IInstanceLock extends ILockInspector {
23
+ acquire(): Promise<LockAcquisition>;
24
+ /** Removes the lock only if it carries the token this instance wrote at acquire. */
25
+ release(): Promise<void>;
26
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,11 @@
1
+ import type { LogCode } from './logCodes.js';
2
+ export type LogLevel = 'INFO' | 'WARN' | 'ERROR';
3
+ export interface LogEntry {
4
+ readonly level: LogLevel;
5
+ readonly code: LogCode;
6
+ readonly message: string;
7
+ readonly cycleId?: string | undefined;
8
+ }
9
+ export interface ILogger {
10
+ log(entry: LogEntry): void;
11
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,12 @@
1
+ export interface RebaseJournal {
2
+ readonly origHead: string;
3
+ readonly onto: string;
4
+ readonly cycleId: string;
5
+ readonly at: string;
6
+ }
7
+ export interface IOperationJournal {
8
+ /** An unparseable journal gives null. */
9
+ read(): Promise<RebaseJournal | null>;
10
+ begin(entry: RebaseJournal): Promise<void>;
11
+ clear(): Promise<void>;
12
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,67 @@
1
+ export interface QuarantineEntry {
2
+ readonly path: string;
3
+ /** null: the path was deleted in the working tree. */
4
+ readonly blobId: string | null;
5
+ readonly executable: boolean;
6
+ /**
7
+ * The index's blob for the path when the hold checked it out, which is what
8
+ * the checkout wrote into the working tree; null when the index had no
9
+ * stage-0 entry, or for a memento written before the field existed.
10
+ */
11
+ readonly heldOver: string | null;
12
+ }
13
+ export interface QuarantineMemento {
14
+ readonly version: 1;
15
+ readonly entries: readonly QuarantineEntry[];
16
+ }
17
+ export interface KeptBlob {
18
+ readonly path: string;
19
+ readonly blobId: string;
20
+ }
21
+ export type HoldResult = {
22
+ readonly kind: 'held';
23
+ readonly count: number;
24
+ } | {
25
+ readonly kind: 'unsupported';
26
+ readonly path: string;
27
+ }
28
+ /** A memento from an earlier hold is still pending; nothing was touched. */
29
+ | {
30
+ readonly kind: 'pending';
31
+ };
32
+ export type RestoreResult = {
33
+ readonly kind: 'nothing';
34
+ } | {
35
+ readonly kind: 'restored';
36
+ readonly count: number;
37
+ readonly kept: readonly KeptBlob[];
38
+ /**
39
+ * Paths whose held version went back although HEAD's version changed
40
+ * while they were held (a rebase pulled a change) and the two did not
41
+ * merge cleanly: committing them undoes that change (Decision 99).
42
+ */
43
+ readonly changedUnderHold: readonly string[];
44
+ /**
45
+ * A write that landed on a file after the restore's last check of it and
46
+ * before its own write, which the held (or merged) version replaced; the
47
+ * replaced bytes are kept in a blob (design 13.11, Decision 117).
48
+ */
49
+ readonly overwritten: readonly KeptBlob[];
50
+ } | {
51
+ readonly kind: 'failed';
52
+ readonly detail: string;
53
+ };
54
+ export interface IPathQuarantine {
55
+ /** Persists the memento before touching the tree; tracked worktree changes only; refuses while a memento is pending. */
56
+ hold(paths: readonly string[]): Promise<HoldResult>;
57
+ /**
58
+ * Captures writes that landed while enmetu's rebase was stopped, before the abort
59
+ * resets them (Decision 45): stores the blobs, appends to a pending memento or
60
+ * creates one, then restores the paths from the index. A later entry for a path
61
+ * supersedes an earlier one at restore. An unparseable memento returns pending
62
+ * and nothing is touched.
63
+ */
64
+ extend(paths: readonly string[]): Promise<HoldResult>;
65
+ restore(): Promise<RestoreResult>;
66
+ hasPending(): Promise<boolean>;
67
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,11 @@
1
+ export type StopSignal = 'SIGINT' | 'SIGTERM' | 'SIGHUP';
2
+ export interface IProcessHost {
3
+ readonly pid: number;
4
+ /** Informational only (lock file, status); liveness is judged by pid. */
5
+ readonly hostname: string;
6
+ isAlive(pid: number): boolean;
7
+ /** Returns the unsubscribe function. */
8
+ onStopSignal(handler: (signal: StopSignal) => void): () => void;
9
+ /** Second-signal path only. */
10
+ exitNow(code: number): never;
11
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,32 @@
1
+ export interface RepositoryPaths {
2
+ /** git rev-parse --show-toplevel (D9) */
3
+ readonly root: string;
4
+ /** Per worktree; state files live under it. */
5
+ readonly gitDir: string;
6
+ /**
7
+ * The repository's own hooks directory, <git common dir>/hooks, whatever
8
+ * core.hooksPath says; git runs hooks from it only while core.hooksPath is
9
+ * unset, which is also the only case the installer writes there (design 14.17).
10
+ */
11
+ readonly hooksDir: string;
12
+ /** <gitDir>/enmetu */
13
+ readonly stateDir: string;
14
+ }
15
+ export interface GitVersion {
16
+ readonly major: number;
17
+ readonly minor: number;
18
+ readonly patch: number;
19
+ }
20
+ export type RepositoryLocation = {
21
+ readonly kind: 'found';
22
+ readonly paths: RepositoryPaths;
23
+ readonly version: GitVersion;
24
+ } | {
25
+ readonly kind: 'not-a-repository';
26
+ } | {
27
+ readonly kind: 'bare';
28
+ };
29
+ export interface IRepositoryLocator {
30
+ /** Throws PreflightError PATH_UNSUPPORTED when a repository path contains a newline. */
31
+ locate(startDir: string): Promise<RepositoryLocation>;
32
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,68 @@
1
+ import type { HaltCode } from './IHaltStore.js';
2
+ import type { StateInstance } from './IStateStore.js';
3
+ import type { ConflictKey, CycleOutcome } from './syncTypes.js';
4
+ /** What git says after a cycle about the upstream, read by the recording Decorator. */
5
+ export interface UpstreamFacts {
6
+ /** The verified ref of the checked-out branch; null when absent, detached or without upstream. */
7
+ readonly pushed: string | null;
8
+ /** Ahead count against the tracking ref, else the verified ref; null when neither exists. */
9
+ readonly unpushed: number | null;
10
+ }
11
+ /** Everything that can change the state record (design 12.1). */
12
+ export type StateEvent =
13
+ /** The lock is held; nothing else has run. Carries the lock holder's identity. */
14
+ {
15
+ readonly type: 'started';
16
+ readonly instance: StateInstance;
17
+ } | {
18
+ readonly type: 'cycleFinished';
19
+ readonly cycleId: string;
20
+ readonly outcome: CycleOutcome;
21
+ readonly conflict: ConflictKey | null;
22
+ readonly upstream: UpstreamFacts;
23
+ }
24
+ /** The cycle threw; the state is kept, only the last cycle changes (as in SyncCycleRunner). */
25
+ | {
26
+ readonly type: 'cycleFailed';
27
+ readonly cycleId: string;
28
+ }
29
+ /** Halted outside a cycle: a marker at start, or recovery. */
30
+ | {
31
+ readonly type: 'halted';
32
+ readonly code: HaltCode;
33
+ readonly message: string;
34
+ }
35
+ /** The run ends without a halt (stopped, or refused by the preflight); written before the lock is released. */
36
+ | {
37
+ readonly type: 'stopped';
38
+ readonly message: string;
39
+ }
40
+ /** enmetu's rebase abort left a person's commit on no branch (design 14.15); the state is unchanged. */
41
+ | {
42
+ readonly type: 'commitOrphaned';
43
+ readonly commit: string;
44
+ readonly base: string;
45
+ readonly subject: string;
46
+ };
47
+ /** What the rebase abort knows of a commit it orphaned; the recorder stamps the time. */
48
+ export interface OrphanedCommitFacts {
49
+ readonly commit: string;
50
+ readonly base: string;
51
+ readonly subject: string;
52
+ }
53
+ /**
54
+ * Observer of enmetu's rebase abort (design 14.15): the one event the abort
55
+ * reports, so it never sees the lifecycle events of IStateRecorder. Never
56
+ * throws, like IStateRecorder.
57
+ */
58
+ export interface IOrphanedCommitRecorder {
59
+ orphaned(facts: OrphanedCommitFacts): Promise<void>;
60
+ }
61
+ /**
62
+ * Applies an event to the record and persists it. Never throws: a record that
63
+ * cannot be written is logged STATE_WRITE_FAILED (once until a write succeeds
64
+ * again) and syncing goes on.
65
+ */
66
+ export interface IStateRecorder {
67
+ record(event: StateEvent): Promise<void>;
68
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,88 @@
1
+ import type { HaltCode } from './IHaltStore.js';
2
+ import type { ConflictKey, CycleOutcomeKind, PauseReason, WaitReason } from './syncTypes.js';
3
+ /**
4
+ * What a running enmetu is doing, the machine vocabulary of state.json. A
5
+ * status indicator maps it to its own words; the status headline is one such
6
+ * mapping (design 12.2).
7
+ */
8
+ export type DaemonState = 'starting' | 'synced' | 'waiting' | 'conflict' | 'paused' | 'busy' | 'offline' | 'push-failed' | 'halted' | 'stopped';
9
+ /** Set for paused, waiting and halted; null for every other state. */
10
+ export type StateReason = PauseReason | WaitReason | HaltCode;
11
+ /**
12
+ * The instance that wrote the record. It is alive when the instance lock's
13
+ * live holder has the same pid and startedAt (stateLiveness).
14
+ */
15
+ export interface StateInstance {
16
+ readonly pid: number;
17
+ /** Informational, like the lock's. */
18
+ readonly hostname: string;
19
+ /** The lock's startedAt, copied when the instance started. */
20
+ readonly startedAt: string;
21
+ }
22
+ export interface StateTransition {
23
+ readonly state: DaemonState;
24
+ readonly reason: StateReason | null;
25
+ /** ISO-8601 UTC. */
26
+ readonly at: string;
27
+ }
28
+ export interface LastCycle {
29
+ readonly cycleId: string;
30
+ readonly outcome: CycleOutcomeKind;
31
+ /** ISO-8601 UTC of the cycle's end. */
32
+ readonly at: string;
33
+ }
34
+ /**
35
+ * A commit a person made on enmetu's rebase's detached HEAD, left on no branch
36
+ * when enmetu ended its rebase (COMMIT_ORPHANED, design 14.14, 14.15).
37
+ */
38
+ export interface OrphanedCommit {
39
+ /** Full id of HEAD's commit when the rebase ended: the newest orphaned commit. */
40
+ readonly commit: string;
41
+ /** Full id of the rebase's own last HEAD; base..commit is what was orphaned. */
42
+ readonly base: string;
43
+ /** commit's subject, at most MAX_ORPHAN_SUBJECT_CHARACTERS characters. */
44
+ readonly subject: string;
45
+ /** ISO-8601 UTC of when enmetu named it. */
46
+ readonly at: string;
47
+ }
48
+ /**
49
+ * <stateDir>/state.json, version 1. Rewritten whole (temp file, then rename)
50
+ * at every lifecycle event and at the end of every cycle; never appended to.
51
+ * Fields are written in this order, so the file is byte-stable.
52
+ */
53
+ export interface StateRecord {
54
+ readonly version: 1;
55
+ readonly instance: StateInstance;
56
+ readonly state: DaemonState;
57
+ readonly reason: StateReason | null;
58
+ /** One line for a person, at most MAX_STATE_MESSAGE_CHARACTERS characters. */
59
+ readonly message: string;
60
+ /** ISO-8601 UTC of the last change of state or reason. */
61
+ readonly since: string;
62
+ /**
63
+ * The newest commit known to be on the upstream: enmetu's verified ref
64
+ * (design 1.10), which after enmetu's own push is the commit it pushed.
65
+ * null before the first verification.
66
+ */
67
+ readonly pushed: string | null;
68
+ /** Local commits not on the last known upstream; null when unknown. */
69
+ readonly unpushed: number | null;
70
+ readonly lastCycle: LastCycle | null;
71
+ /** The held conflict's key while state is 'conflict', else null (design 12.3). */
72
+ readonly conflict: ConflictKey | null;
73
+ /** The most recent transitions, oldest first, at most the configured stateHistory. */
74
+ readonly history: readonly StateTransition[];
75
+ /**
76
+ * Orphaned commits enmetu named, oldest first, at most MAX_ORPHANED_COMMITS,
77
+ * kept across restarts. Whether each is still on no branch is git's to say:
78
+ * `enmetu status` asks it at every run and shows only those (design 14.15).
79
+ * A file written before this field existed yields [].
80
+ */
81
+ readonly orphaned: readonly OrphanedCommit[];
82
+ }
83
+ export interface IStateStore {
84
+ /** null when absent, not JSON, of another shape or of another version. */
85
+ read(): Promise<StateRecord | null>;
86
+ /** Replaces the whole file atomically (IFileSystem.writeAtomic in the state directory). */
87
+ write(record: StateRecord): Promise<void>;
88
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,23 @@
1
+ import type { CommitMemory, ConflictKey, CycleOutcome } from './syncTypes.js';
2
+ /** No trigger: every trigger runs the same chain (Decision 2), so the cycle has no use for it. */
3
+ export interface SyncCycleInput {
4
+ readonly cycleId: string;
5
+ readonly knownConflict: ConflictKey | null;
6
+ /** The previous cycle's commit memory (design 12.7); absent is EMPTY_COMMIT_MEMORY. */
7
+ readonly commitMemory?: CommitMemory | undefined;
8
+ /** Consecutive cycles whose rebase git refused to start (Decision 82); absent is 0. */
9
+ readonly notStartedRebases?: number | undefined;
10
+ }
11
+ export interface SyncCycleResult {
12
+ readonly outcome: CycleOutcome;
13
+ /** Set only when a rebase really stopped and its abort was verified. */
14
+ readonly conflict: ConflictKey | null;
15
+ /** The commit memory for the next cycle; absent is EMPTY_COMMIT_MEMORY. */
16
+ readonly commitMemory?: CommitMemory | undefined;
17
+ /** The count for the next cycle (Decision 82); absent is 0. */
18
+ readonly notStartedRebases?: number | undefined;
19
+ }
20
+ /** One cycle with its deps bound: a closure over syncCycleController, built by the composition root. */
21
+ export interface ISyncCycle {
22
+ run(input: SyncCycleInput): Promise<SyncCycleResult>;
23
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,4 @@
1
+ import type { CycleOutcomeKind } from './syncTypes.js';
2
+ export interface ISyncCycleRunner {
3
+ run(): Promise<CycleOutcomeKind>;
4
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,6 @@
1
+ export interface ISyncScheduler {
2
+ /** Dispatches 'started'; resolves when the scheduler ends. */
3
+ run(): Promise<'stopped' | 'halted'>;
4
+ notifyChange(): void;
5
+ stop(): void;
6
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,3 @@
1
+ export interface ITextSink {
2
+ write(text: string): unknown;
3
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,9 @@
1
+ export interface WatchListener {
2
+ onChange(): void;
3
+ onFailure(detail: string): void;
4
+ }
5
+ export interface IWatcher {
6
+ /** Recursive watch of the repository root; noise is filtered inside; no path is passed on. */
7
+ start(listener: WatchListener): void;
8
+ close(): void;
9
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,26 @@
1
+ export interface EnmetuConfig {
2
+ readonly quietMs: number;
3
+ readonly maxDelayMs: number;
4
+ readonly pollMs: number;
5
+ readonly commitSubject: string;
6
+ readonly installHook: boolean;
7
+ readonly skipJsonCheck: readonly string[];
8
+ readonly gitTimeoutMs: number;
9
+ /** Transitions kept in state.json; 0 keeps none (design 12.1). */
10
+ readonly stateHistory: number;
11
+ /**
12
+ * While a person has a partial staging, how long enmetu waits before it
13
+ * commits everything, the staging included; 0 commits at once (design 14.17).
14
+ */
15
+ readonly partialStagingWaitMs: number;
16
+ /** Run the repository's commit hooks for enmetu's commits (design 12.7). */
17
+ readonly commitHooks: boolean;
18
+ /** How long one repository hook may run before it is a rejection (design 12.7, Decision 83). */
19
+ readonly hookTimeoutMs: number;
20
+ }
21
+ /** What parseConfig read: the config, and the keys it does not know, in file order (design 14.17). */
22
+ export interface ParsedConfig {
23
+ readonly config: EnmetuConfig;
24
+ /** Ignored; the run command names each once at start. */
25
+ readonly unknownKeys: readonly string[];
26
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,39 @@
1
+ import type { HaltCode } from './IHaltStore.js';
2
+ export type EnmetuErrorCode = 'GIT_NOT_FOUND' | 'GIT_TIMEOUT' | 'GIT_ABORTED' | 'GIT_FAILED' | 'GIT_INDEX_LOCKED' | 'GIT_OUTPUT_UNPARSEABLE' | 'CONFIG_INVALID_JSON' | 'CONFIG_INVALID_VALUE' | 'NOT_A_REPOSITORY' | 'BARE_REPOSITORY' | 'GIT_TOO_OLD' | 'PATH_UNSUPPORTED' | 'OPERATION_IN_PROGRESS' | 'DETACHED_HEAD' | 'HEAD_UNBORN' | 'NO_UPSTREAM' | 'UPSTREAM_NOT_REMOTE' | 'IDENTITY_MISSING' | 'LOCK_HELD' | 'LOCK_UNREADABLE' | 'HALTED' | 'USAGE';
3
+ export type GitCommandErrorCode = 'GIT_NOT_FOUND' | 'GIT_TIMEOUT' | 'GIT_ABORTED' | 'GIT_FAILED' | 'GIT_INDEX_LOCKED';
4
+ export type ConfigErrorCode = 'CONFIG_INVALID_JSON' | 'CONFIG_INVALID_VALUE';
5
+ export type PreflightErrorCode = 'NOT_A_REPOSITORY' | 'BARE_REPOSITORY' | 'GIT_TOO_OLD' | 'PATH_UNSUPPORTED' | 'OPERATION_IN_PROGRESS' | 'DETACHED_HEAD' | 'HEAD_UNBORN' | 'NO_UPSTREAM' | 'UPSTREAM_NOT_REMOTE' | 'IDENTITY_MISSING';
6
+ export type LockErrorCode = 'LOCK_HELD' | 'LOCK_UNREADABLE';
7
+ export declare class EnmetuError extends Error {
8
+ readonly code: EnmetuErrorCode;
9
+ readonly cycleId?: string | undefined;
10
+ constructor(message: string, code: EnmetuErrorCode, cycleId?: string | undefined);
11
+ }
12
+ export declare class GitCommandError extends EnmetuError {
13
+ readonly args: readonly string[];
14
+ readonly exitCode: number | null;
15
+ readonly stderr: string;
16
+ constructor(message: string, code: GitCommandErrorCode, args: readonly string[], exitCode: number | null, stderr: string, cycleId?: string | undefined);
17
+ }
18
+ /** GIT_OUTPUT_UNPARSEABLE, raised by the porcelain parsers. */
19
+ export declare class GitOutputError extends EnmetuError {
20
+ constructor(message: string, cycleId?: string | undefined);
21
+ }
22
+ export declare class ConfigError extends EnmetuError {
23
+ readonly key: string | null;
24
+ constructor(message: string, code: ConfigErrorCode, key: string | null);
25
+ }
26
+ export declare class PreflightError extends EnmetuError {
27
+ constructor(message: string, code: PreflightErrorCode, cycleId?: string | undefined);
28
+ }
29
+ export declare class LockError extends EnmetuError {
30
+ constructor(message: string, code: LockErrorCode);
31
+ }
32
+ /** Thrown by HaltGuardGitWriter for every write while halt.json exists. */
33
+ export declare class HaltedError extends EnmetuError {
34
+ readonly haltCode: HaltCode;
35
+ constructor(message: string, haltCode: HaltCode, cycleId?: string | undefined);
36
+ }
37
+ export declare class UsageError extends EnmetuError {
38
+ constructor(message: string);
39
+ }
@@ -0,0 +1,64 @@
1
+ export class EnmetuError extends Error {
2
+ code;
3
+ cycleId;
4
+ constructor(message, code, cycleId) {
5
+ super(message);
6
+ this.code = code;
7
+ this.cycleId = cycleId;
8
+ this.name = 'EnmetuError';
9
+ }
10
+ }
11
+ export class GitCommandError extends EnmetuError {
12
+ args;
13
+ exitCode;
14
+ stderr;
15
+ constructor(message, code, args, exitCode, stderr, cycleId) {
16
+ super(message, code, cycleId);
17
+ this.args = args;
18
+ this.exitCode = exitCode;
19
+ this.stderr = stderr;
20
+ this.name = 'GitCommandError';
21
+ }
22
+ }
23
+ /** GIT_OUTPUT_UNPARSEABLE, raised by the porcelain parsers. */
24
+ export class GitOutputError extends EnmetuError {
25
+ constructor(message, cycleId) {
26
+ super(message, 'GIT_OUTPUT_UNPARSEABLE', cycleId);
27
+ this.name = 'GitOutputError';
28
+ }
29
+ }
30
+ export class ConfigError extends EnmetuError {
31
+ key;
32
+ constructor(message, code, key) {
33
+ super(message, code);
34
+ this.key = key;
35
+ this.name = 'ConfigError';
36
+ }
37
+ }
38
+ export class PreflightError extends EnmetuError {
39
+ constructor(message, code, cycleId) {
40
+ super(message, code, cycleId);
41
+ this.name = 'PreflightError';
42
+ }
43
+ }
44
+ export class LockError extends EnmetuError {
45
+ constructor(message, code) {
46
+ super(message, code);
47
+ this.name = 'LockError';
48
+ }
49
+ }
50
+ /** Thrown by HaltGuardGitWriter for every write while halt.json exists. */
51
+ export class HaltedError extends EnmetuError {
52
+ haltCode;
53
+ constructor(message, haltCode, cycleId) {
54
+ super(message, 'HALTED', cycleId);
55
+ this.haltCode = haltCode;
56
+ this.name = 'HaltedError';
57
+ }
58
+ }
59
+ export class UsageError extends EnmetuError {
60
+ constructor(message) {
61
+ super(message, 'USAGE');
62
+ this.name = 'UsageError';
63
+ }
64
+ }
@@ -0,0 +1,10 @@
1
+ export type ExitCode = 0 | 1 | 2 | 3 | 4 | 5 | 130;
2
+ export declare const EXIT: {
3
+ readonly OK: 0;
4
+ readonly FAILURE: 1;
5
+ readonly USAGE: 2;
6
+ readonly HALTED: 3;
7
+ readonly LOCKED: 4;
8
+ readonly NOT_ELIGIBLE: 5;
9
+ readonly SECOND_SIGNAL: 130;
10
+ };
@@ -0,0 +1,17 @@
1
+ // Annotated instead of a const assertion: the house rules allow no type assertion.
2
+ export const EXIT = {
3
+ // stopped by a signal, status printed, halt cleared, hook passed
4
+ OK: 0,
5
+ // unexpected error; also the hook's "commit refused"
6
+ FAILURE: 1,
7
+ // bad arguments or invalid enmetu.config.json
8
+ USAGE: 2,
9
+ // halt marker present (run, status), resume refused, or halted while running
10
+ HALTED: 3,
11
+ // another live instance holds the lock (run, resume)
12
+ LOCKED: 4,
13
+ // not a repository, bare, git missing or too old, detached, unborn, no upstream, operation in progress, no identity
14
+ NOT_ELIGIBLE: 5,
15
+ // second stop signal; exits at once
16
+ SECOND_SIGNAL: 130,
17
+ };