@fern-api/replay 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.
- package/README.md +33 -0
- package/dist/ConflictPrompt.d.ts +13 -0
- package/dist/ConflictPrompt.d.ts.map +1 -0
- package/dist/ConflictPrompt.js +38 -0
- package/dist/ConflictPrompt.js.map +1 -0
- package/dist/ConflictResolver.d.ts +11 -0
- package/dist/ConflictResolver.d.ts.map +1 -0
- package/dist/ConflictResolver.js +53 -0
- package/dist/ConflictResolver.js.map +1 -0
- package/dist/FernignoreMigrator.d.ts +71 -0
- package/dist/FernignoreMigrator.d.ts.map +1 -0
- package/dist/FernignoreMigrator.js +243 -0
- package/dist/FernignoreMigrator.js.map +1 -0
- package/dist/LockfileManager.d.ts +27 -0
- package/dist/LockfileManager.d.ts.map +1 -0
- package/dist/LockfileManager.js +108 -0
- package/dist/LockfileManager.js.map +1 -0
- package/dist/ReplayApplicator.d.ts +68 -0
- package/dist/ReplayApplicator.d.ts.map +1 -0
- package/dist/ReplayApplicator.js +492 -0
- package/dist/ReplayApplicator.js.map +1 -0
- package/dist/ReplayCommitter.d.ts +39 -0
- package/dist/ReplayCommitter.d.ts.map +1 -0
- package/dist/ReplayCommitter.js +84 -0
- package/dist/ReplayCommitter.js.map +1 -0
- package/dist/ReplayDetector.d.ts +25 -0
- package/dist/ReplayDetector.d.ts.map +1 -0
- package/dist/ReplayDetector.js +110 -0
- package/dist/ReplayDetector.js.map +1 -0
- package/dist/ReplayService.d.ts +103 -0
- package/dist/ReplayService.d.ts.map +1 -0
- package/dist/ReplayService.js +457 -0
- package/dist/ReplayService.js.map +1 -0
- package/dist/ThreeWayMerge.d.ts +11 -0
- package/dist/ThreeWayMerge.d.ts.map +1 -0
- package/dist/ThreeWayMerge.js +48 -0
- package/dist/ThreeWayMerge.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +464 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/bootstrap.d.ts +44 -0
- package/dist/commands/bootstrap.d.ts.map +1 -0
- package/dist/commands/bootstrap.js +268 -0
- package/dist/commands/bootstrap.js.map +1 -0
- package/dist/commands/forget.d.ts +26 -0
- package/dist/commands/forget.d.ts.map +1 -0
- package/dist/commands/forget.js +37 -0
- package/dist/commands/forget.js.map +1 -0
- package/dist/commands/index.d.ts +6 -0
- package/dist/commands/index.d.ts.map +1 -0
- package/dist/commands/index.js +6 -0
- package/dist/commands/index.js.map +1 -0
- package/dist/commands/reset.d.ts +28 -0
- package/dist/commands/reset.d.ts.map +1 -0
- package/dist/commands/reset.js +37 -0
- package/dist/commands/reset.js.map +1 -0
- package/dist/commands/resolve.d.ts +16 -0
- package/dist/commands/resolve.d.ts.map +1 -0
- package/dist/commands/resolve.js +28 -0
- package/dist/commands/resolve.js.map +1 -0
- package/dist/commands/status.d.ts +34 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +32 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/environment.d.ts +5 -0
- package/dist/environment.d.ts.map +1 -0
- package/dist/environment.js +14 -0
- package/dist/environment.js.map +1 -0
- package/dist/git/CommitDetection.d.ts +17 -0
- package/dist/git/CommitDetection.d.ts.map +1 -0
- package/dist/git/CommitDetection.js +33 -0
- package/dist/git/CommitDetection.js.map +1 -0
- package/dist/git/GitClient.d.ts +28 -0
- package/dist/git/GitClient.d.ts.map +1 -0
- package/dist/git/GitClient.js +104 -0
- package/dist/git/GitClient.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -0
- package/dist/types.d.ts +80 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +9 -0
- package/dist/types.js.map +1 -0
- package/package.json +45 -0
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
export class ReplayCommitter {
|
|
2
|
+
git;
|
|
3
|
+
outputDir;
|
|
4
|
+
constructor(git, outputDir) {
|
|
5
|
+
this.git = git;
|
|
6
|
+
this.outputDir = outputDir;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Commit the generation output (after generator runs, before replay).
|
|
10
|
+
* Uses [fern-generated] prefix for detection.
|
|
11
|
+
*/
|
|
12
|
+
async commitGeneration(message, options) {
|
|
13
|
+
await this.stageAll();
|
|
14
|
+
if (!(await this.hasStagedChanges())) {
|
|
15
|
+
return (await this.git.exec(["rev-parse", "HEAD"])).trim();
|
|
16
|
+
}
|
|
17
|
+
let fullMessage = `[fern-generated] ${message}\n\nGenerated by Fern`;
|
|
18
|
+
if (options?.cliVersion) {
|
|
19
|
+
fullMessage += `\nCLI Version: ${options.cliVersion}`;
|
|
20
|
+
}
|
|
21
|
+
if (options?.generatorVersions && Object.keys(options.generatorVersions).length > 0) {
|
|
22
|
+
fullMessage += "\nGenerators:";
|
|
23
|
+
for (const [name, version] of Object.entries(options.generatorVersions)) {
|
|
24
|
+
fullMessage += `\n - ${name}: ${version}`;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
await this.git.exec(["commit", "-m", fullMessage]);
|
|
28
|
+
return (await this.git.exec(["rev-parse", "HEAD"])).trim();
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Commit the replay result (after patches applied).
|
|
32
|
+
* Uses [fern-replay] prefix for detection.
|
|
33
|
+
*/
|
|
34
|
+
async commitReplay(patchCount, patches) {
|
|
35
|
+
await this.stageAll();
|
|
36
|
+
if (!(await this.hasStagedChanges())) {
|
|
37
|
+
return (await this.git.exec(["rev-parse", "HEAD"])).trim();
|
|
38
|
+
}
|
|
39
|
+
let fullMessage = `[fern-replay] Applied ${patchCount} customization(s)`;
|
|
40
|
+
if (patches && patches.length > 0) {
|
|
41
|
+
fullMessage += "\n\nPatches replayed:";
|
|
42
|
+
for (const patch of patches) {
|
|
43
|
+
fullMessage += `\n - ${patch.id}: ${patch.original_message}`;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
await this.git.exec(["commit", "-m", fullMessage]);
|
|
47
|
+
return (await this.git.exec(["rev-parse", "HEAD"])).trim();
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Create a GenerationRecord for the current state.
|
|
51
|
+
* Captures commit SHA and tree hash for future 3-way merges.
|
|
52
|
+
*/
|
|
53
|
+
async createGenerationRecord(options) {
|
|
54
|
+
const commitSha = (await this.git.exec(["rev-parse", "HEAD"])).trim();
|
|
55
|
+
const treeHash = await this.getTreeHash(commitSha);
|
|
56
|
+
return {
|
|
57
|
+
commit_sha: commitSha,
|
|
58
|
+
tree_hash: treeHash,
|
|
59
|
+
timestamp: new Date().toISOString(),
|
|
60
|
+
cli_version: options?.cliVersion ?? "unknown",
|
|
61
|
+
generator_versions: options?.generatorVersions ?? {},
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Stage all changes in output directory.
|
|
66
|
+
*/
|
|
67
|
+
async stageAll() {
|
|
68
|
+
await this.git.exec(["add", "-A", this.outputDir]);
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Check if there are staged changes to commit.
|
|
72
|
+
*/
|
|
73
|
+
async hasStagedChanges() {
|
|
74
|
+
const output = await this.git.exec(["diff", "--cached", "--name-only"]);
|
|
75
|
+
return output.trim().length > 0;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Get the tree hash for a commit.
|
|
79
|
+
*/
|
|
80
|
+
async getTreeHash(commitSha) {
|
|
81
|
+
return this.git.getTreeHash(commitSha);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=ReplayCommitter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ReplayCommitter.js","sourceRoot":"","sources":["../src/ReplayCommitter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { GitClient } from "./git/GitClient.js";
|
|
2
|
+
import type { LockfileManager } from "./LockfileManager.js";
|
|
3
|
+
import type { StoredPatch } from "./types.js";
|
|
4
|
+
export declare class ReplayDetector {
|
|
5
|
+
private git;
|
|
6
|
+
private lockManager;
|
|
7
|
+
private sdkOutputDir;
|
|
8
|
+
readonly warnings: string[];
|
|
9
|
+
constructor(git: GitClient, lockManager: LockfileManager, sdkOutputDir: string);
|
|
10
|
+
/**
|
|
11
|
+
* Detect new customization patches since last generation.
|
|
12
|
+
* Filters out generation/replay commits, merge commits,
|
|
13
|
+
* and already-tracked patches (by SHA or content hash).
|
|
14
|
+
*/
|
|
15
|
+
detectNewPatches(): Promise<StoredPatch[]>;
|
|
16
|
+
/**
|
|
17
|
+
* Compute content hash for deduplication.
|
|
18
|
+
* Removes commit SHA line and index lines before hashing,
|
|
19
|
+
* so rebased commits with same content produce the same hash.
|
|
20
|
+
*/
|
|
21
|
+
computeContentHash(patchContent: string): string;
|
|
22
|
+
private parseGitLog;
|
|
23
|
+
private getLastGeneration;
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=ReplayDetector.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ReplayDetector.d.ts","sourceRoot":"","sources":["../src/ReplayDetector.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,KAAK,EAA8B,WAAW,EAAE,MAAM,YAAY,CAAC;AAM1E,qBAAa,cAAc;IACvB,OAAO,CAAC,GAAG,CAAY;IACvB,OAAO,CAAC,WAAW,CAAkB;IACrC,OAAO,CAAC,YAAY,CAAS;IAC7B,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,CAAM;gBAErB,GAAG,EAAE,SAAS,EAAE,WAAW,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM;IAM9E;;;;OAIG;IACG,gBAAgB,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC;IAiFhD;;;;OAIG;IACH,kBAAkB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM;IAShD,OAAO,CAAC,WAAW;IAUnB,OAAO,CAAC,iBAAiB;CAG5B"}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { isGenerationCommit } from "./git/CommitDetection.js";
|
|
3
|
+
// Infrastructure files managed by the generation pipeline, not user code.
|
|
4
|
+
// Changes to these should never be captured as customization patches.
|
|
5
|
+
const INFRASTRUCTURE_FILES = new Set([".fernignore"]);
|
|
6
|
+
export class ReplayDetector {
|
|
7
|
+
git;
|
|
8
|
+
lockManager;
|
|
9
|
+
sdkOutputDir;
|
|
10
|
+
warnings = [];
|
|
11
|
+
constructor(git, lockManager, sdkOutputDir) {
|
|
12
|
+
this.git = git;
|
|
13
|
+
this.lockManager = lockManager;
|
|
14
|
+
this.sdkOutputDir = sdkOutputDir;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Detect new customization patches since last generation.
|
|
18
|
+
* Filters out generation/replay commits, merge commits,
|
|
19
|
+
* and already-tracked patches (by SHA or content hash).
|
|
20
|
+
*/
|
|
21
|
+
async detectNewPatches() {
|
|
22
|
+
const lock = this.lockManager.read();
|
|
23
|
+
const lastGen = this.getLastGeneration(lock);
|
|
24
|
+
if (!lastGen) {
|
|
25
|
+
return [];
|
|
26
|
+
}
|
|
27
|
+
const exists = await this.git.commitExists(lastGen.commit_sha);
|
|
28
|
+
if (!exists) {
|
|
29
|
+
this.warnings.push(`Generation commit ${lastGen.commit_sha.slice(0, 7)} not found in git history. ` +
|
|
30
|
+
`Skipping new patch detection. Existing lockfile patches will still be applied.`);
|
|
31
|
+
return [];
|
|
32
|
+
}
|
|
33
|
+
const log = await this.git.exec([
|
|
34
|
+
"log",
|
|
35
|
+
"--format=%H%x00%an%x00%ae%x00%s",
|
|
36
|
+
`${lastGen.commit_sha}..HEAD`,
|
|
37
|
+
"--",
|
|
38
|
+
this.sdkOutputDir
|
|
39
|
+
]);
|
|
40
|
+
if (!log.trim()) {
|
|
41
|
+
return [];
|
|
42
|
+
}
|
|
43
|
+
const commits = this.parseGitLog(log);
|
|
44
|
+
const newPatches = [];
|
|
45
|
+
for (const commit of commits) {
|
|
46
|
+
if (isGenerationCommit(commit)) {
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
const parents = await this.git.getCommitParents(commit.sha);
|
|
50
|
+
if (parents.length > 1) {
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
if (lock.patches.find((p) => p.original_commit === commit.sha)) {
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
const patchContent = await this.git.formatPatch(commit.sha);
|
|
57
|
+
const contentHash = this.computeContentHash(patchContent);
|
|
58
|
+
if (lock.patches.find((p) => p.content_hash === contentHash)) {
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
const filesOutput = await this.git.exec(["diff-tree", "--no-commit-id", "--name-only", "-r", commit.sha]);
|
|
62
|
+
const files = filesOutput
|
|
63
|
+
.trim()
|
|
64
|
+
.split("\n")
|
|
65
|
+
.filter(Boolean)
|
|
66
|
+
.filter((f) => !INFRASTRUCTURE_FILES.has(f));
|
|
67
|
+
// Skip patches that only touch infrastructure files
|
|
68
|
+
if (files.length === 0) {
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
newPatches.push({
|
|
72
|
+
id: `patch-${commit.sha.slice(0, 8)}`,
|
|
73
|
+
content_hash: contentHash,
|
|
74
|
+
original_commit: commit.sha,
|
|
75
|
+
original_message: commit.message,
|
|
76
|
+
original_author: `${commit.authorName} <${commit.authorEmail}>`,
|
|
77
|
+
base_generation: lastGen.commit_sha,
|
|
78
|
+
files,
|
|
79
|
+
patch_content: patchContent
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
// Reverse so patches apply in chronological order (oldest first)
|
|
83
|
+
return newPatches.reverse();
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Compute content hash for deduplication.
|
|
87
|
+
* Removes commit SHA line and index lines before hashing,
|
|
88
|
+
* so rebased commits with same content produce the same hash.
|
|
89
|
+
*/
|
|
90
|
+
computeContentHash(patchContent) {
|
|
91
|
+
const normalized = patchContent
|
|
92
|
+
.split("\n")
|
|
93
|
+
.filter((line) => !line.startsWith("From ") && !line.startsWith("index ") && !line.startsWith("Date: "))
|
|
94
|
+
.join("\n");
|
|
95
|
+
return `sha256:${createHash("sha256").update(normalized).digest("hex")}`;
|
|
96
|
+
}
|
|
97
|
+
parseGitLog(log) {
|
|
98
|
+
return log
|
|
99
|
+
.trim()
|
|
100
|
+
.split("\n")
|
|
101
|
+
.map((line) => {
|
|
102
|
+
const [sha, authorName, authorEmail, message] = line.split("\0");
|
|
103
|
+
return { sha, authorName, authorEmail, message };
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
getLastGeneration(lock) {
|
|
107
|
+
return lock.generations.find((g) => g.commit_sha === lock.current_generation);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
//# sourceMappingURL=ReplayDetector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ReplayDetector.js","sourceRoot":"","sources":["../src/ReplayDetector.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import type { ReplayConfig, StoredPatch, FileResult } from "./types.js";
|
|
2
|
+
export interface ConflictDetail {
|
|
3
|
+
patchId: string;
|
|
4
|
+
patchMessage: string;
|
|
5
|
+
reason?: string;
|
|
6
|
+
files: FileResult[];
|
|
7
|
+
}
|
|
8
|
+
export interface ReplayReport {
|
|
9
|
+
flow: "first-generation" | "no-patches" | "normal-regeneration";
|
|
10
|
+
patchesDetected: number;
|
|
11
|
+
patchesApplied: number;
|
|
12
|
+
patchesWithConflicts: number;
|
|
13
|
+
patchesSkipped: number;
|
|
14
|
+
patchesAbsorbed?: number;
|
|
15
|
+
patchesRepointed?: number;
|
|
16
|
+
patchesContentRebased?: number;
|
|
17
|
+
patchesKeptAsUserOwned?: number;
|
|
18
|
+
conflicts: FileResult[];
|
|
19
|
+
conflictDetails?: ConflictDetail[];
|
|
20
|
+
wouldApply?: StoredPatch[];
|
|
21
|
+
warnings?: string[];
|
|
22
|
+
}
|
|
23
|
+
export declare class ReplayConflictError extends Error {
|
|
24
|
+
readonly report: ReplayReport;
|
|
25
|
+
constructor(report: ReplayReport);
|
|
26
|
+
}
|
|
27
|
+
export interface ReplayOptions {
|
|
28
|
+
/** Log what would happen but don't modify anything */
|
|
29
|
+
dryRun?: boolean;
|
|
30
|
+
/** Write files and stage changes, but don't commit */
|
|
31
|
+
stageOnly?: boolean;
|
|
32
|
+
/** CLI version for commit metadata */
|
|
33
|
+
cliVersion?: string;
|
|
34
|
+
/** Generator versions for commit metadata */
|
|
35
|
+
generatorVersions?: Record<string, string>;
|
|
36
|
+
}
|
|
37
|
+
export declare class ReplayService {
|
|
38
|
+
private git;
|
|
39
|
+
private config;
|
|
40
|
+
private detector;
|
|
41
|
+
private applicator;
|
|
42
|
+
private committer;
|
|
43
|
+
private lockManager;
|
|
44
|
+
private outputDir;
|
|
45
|
+
constructor(outputDir: string, config: ReplayConfig);
|
|
46
|
+
/**
|
|
47
|
+
* Run the full replay flow.
|
|
48
|
+
*/
|
|
49
|
+
runReplay(options?: ReplayOptions): Promise<ReplayReport>;
|
|
50
|
+
/**
|
|
51
|
+
* Determine which flow to use based on lockfile state.
|
|
52
|
+
*/
|
|
53
|
+
private determineFlow;
|
|
54
|
+
/**
|
|
55
|
+
* First generation - just create lockfile, no patches to apply.
|
|
56
|
+
*/
|
|
57
|
+
private handleFirstGeneration;
|
|
58
|
+
/**
|
|
59
|
+
* No existing patches - detect new ones and apply if any.
|
|
60
|
+
*/
|
|
61
|
+
private handleNoPatchesRegeneration;
|
|
62
|
+
/**
|
|
63
|
+
* Normal flow - apply existing patches + detect new ones.
|
|
64
|
+
*/
|
|
65
|
+
private handleNormalRegeneration;
|
|
66
|
+
/**
|
|
67
|
+
* Get the active conflict policy based on config and environment.
|
|
68
|
+
* Returns undefined when on_conflict is not configured (backwards-compatible).
|
|
69
|
+
*/
|
|
70
|
+
private getConflictPolicy;
|
|
71
|
+
/**
|
|
72
|
+
* Auto-resolve all conflict markers in conflicted files.
|
|
73
|
+
* "ours" = keep generated code, "theirs" = keep user customization.
|
|
74
|
+
*/
|
|
75
|
+
private autoResolveConflicts;
|
|
76
|
+
/**
|
|
77
|
+
* Phase 1: Resolve conflict markers on disk BEFORE rebasing.
|
|
78
|
+
* Handles keep-mine, keep-generated, and prompt policies.
|
|
79
|
+
* Must run before rebasePatches() so rebased content captures clean files.
|
|
80
|
+
*/
|
|
81
|
+
private resolveConflictsOnDisk;
|
|
82
|
+
/**
|
|
83
|
+
* Phase 2: Throw for "fail" policy AFTER lockfile save.
|
|
84
|
+
* This ensures the lockfile is preserved even when we reject the commit.
|
|
85
|
+
*/
|
|
86
|
+
private enforceConflictFailPolicy;
|
|
87
|
+
/**
|
|
88
|
+
* Rebase cleanly applied patches so they are relative to the current generation.
|
|
89
|
+
* This prevents recurring conflicts on subsequent regenerations.
|
|
90
|
+
* Returns the number of patches rebased.
|
|
91
|
+
*/
|
|
92
|
+
private rebasePatches;
|
|
93
|
+
/**
|
|
94
|
+
* Read .fernignore patterns from the output directory.
|
|
95
|
+
* Returns an empty array if .fernignore doesn't exist.
|
|
96
|
+
*/
|
|
97
|
+
private readFernignorePatterns;
|
|
98
|
+
/**
|
|
99
|
+
* Build the final report from results.
|
|
100
|
+
*/
|
|
101
|
+
private buildReport;
|
|
102
|
+
}
|
|
103
|
+
//# sourceMappingURL=ReplayService.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ReplayService.d.ts","sourceRoot":"","sources":["../src/ReplayService.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,YAAY,EAAgB,WAAW,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAEtF,MAAM,WAAW,cAAc;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,UAAU,EAAE,CAAC;CACvB;AAED,MAAM,WAAW,YAAY;IACzB,IAAI,EAAE,kBAAkB,GAAG,YAAY,GAAG,qBAAqB,CAAC;IAChE,eAAe,EAAE,MAAM,CAAC;IACxB,cAAc,EAAE,MAAM,CAAC;IACvB,oBAAoB,EAAE,MAAM,CAAC;IAC7B,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,SAAS,EAAE,UAAU,EAAE,CAAC;IACxB,eAAe,CAAC,EAAE,cAAc,EAAE,CAAC;IACnC,UAAU,CAAC,EAAE,WAAW,EAAE,CAAC;IAC3B,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;CACvB;AAED,qBAAa,mBAAoB,SAAQ,KAAK;IAC1C,SAAgB,MAAM,EAAE,YAAY,CAAC;gBAEzB,MAAM,EAAE,YAAY;CAKnC;AAED,MAAM,WAAW,aAAa;IAC1B,sDAAsD;IACtD,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,sDAAsD;IACtD,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,sCAAsC;IACtC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,6CAA6C;IAC7C,iBAAiB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC9C;AAED,qBAAa,aAAa;IACtB,OAAO,CAAC,GAAG,CAAY;IACvB,OAAO,CAAC,MAAM,CAAe;IAC7B,OAAO,CAAC,QAAQ,CAAiB;IACjC,OAAO,CAAC,UAAU,CAAmB;IACrC,OAAO,CAAC,SAAS,CAAkB;IACnC,OAAO,CAAC,WAAW,CAAkB;IACrC,OAAO,CAAC,SAAS,CAAS;gBAGtB,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,YAAY;IAYxB;;OAEG;IACG,SAAS,CAAC,OAAO,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC;IAa/D;;OAEG;IACH,OAAO,CAAC,aAAa;IAarB;;OAEG;YACW,qBAAqB;IA+BnC;;OAEG;YACW,2BAA2B;IAoEzC;;OAEG;YACW,wBAAwB;IA6DtC;;;OAGG;IACH,OAAO,CAAC,iBAAiB;IAOzB;;;OAGG;YACW,oBAAoB;IAsBlC;;;;OAIG;YACW,sBAAsB;IA+DpC;;;OAGG;IACH,OAAO,CAAC,yBAAyB;IAkBjC;;;;OAIG;YACW,aAAa;IAqG3B;;;OAGG;IACH,OAAO,CAAC,sBAAsB;IAS9B;;OAEG;IACH,OAAO,CAAC,WAAW;CAmCtB"}
|