@p4pilot/core 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/dist/browser.cjs +873 -0
- package/dist/browser.d.cts +3 -0
- package/dist/browser.d.ts +3 -0
- package/dist/browser.js +26 -0
- package/dist/changelist-BD8jDKA6.d.ts +123 -0
- package/dist/changelist-D-VWhdnq.d.cts +123 -0
- package/dist/chunk-HLG24B6W.js +387 -0
- package/dist/chunk-QIZ2GXWH.js +453 -0
- package/dist/index.cjs +566 -0
- package/dist/index.d.cts +25 -0
- package/dist/index.d.ts +25 -0
- package/dist/index.js +97 -0
- package/dist/p4-runner-CmZD1E6u.d.cts +69 -0
- package/dist/p4-runner-CmZD1E6u.d.ts +69 -0
- package/dist/testing/mock-runner.cjs +411 -0
- package/dist/testing/mock-runner.d.cts +48 -0
- package/dist/testing/mock-runner.d.ts +48 -0
- package/dist/testing/mock-runner.js +6 -0
- package/package.json +67 -0
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { C as ChangelistSummary, D as DescribeResult, F as FileStat, O as OpenedFile, P as P4Action, a as P4PilotError, b as P4PilotErrorCode, c as P4Result, d as P4RunOptions, e as P4Runner } from './p4-runner-CmZD1E6u.cjs';
|
|
2
|
+
export { A as AssetClassification, a as AssetGuardConfig, b as AssetKind, C as CheckoutResult, c as CheckoutStatus, D as DEFAULT_ASSET_GUARD_CONFIG, F as FilelogEntry, P as P4Client, Z as ZtagRecord, d as buildChangelistDescription, e as classifyAsset, f as ensureOpenForEdit, g as ensureOpenForEditMany, h as groupIndexed, p as parseZtag } from './changelist-D-VWhdnq.cjs';
|
|
3
|
+
export { FakeDepotState, FakeFile, MockP4Runner } from './testing/mock-runner.cjs';
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { C as ChangelistSummary, D as DescribeResult, F as FileStat, O as OpenedFile, P as P4Action, a as P4PilotError, b as P4PilotErrorCode, c as P4Result, d as P4RunOptions, e as P4Runner } from './p4-runner-CmZD1E6u.js';
|
|
2
|
+
export { A as AssetClassification, a as AssetGuardConfig, b as AssetKind, C as CheckoutResult, c as CheckoutStatus, D as DEFAULT_ASSET_GUARD_CONFIG, F as FilelogEntry, P as P4Client, Z as ZtagRecord, d as buildChangelistDescription, e as classifyAsset, f as ensureOpenForEdit, g as ensureOpenForEditMany, h as groupIndexed, p as parseZtag } from './changelist-BD8jDKA6.js';
|
|
3
|
+
export { FakeDepotState, FakeFile, MockP4Runner } from './testing/mock-runner.js';
|
package/dist/browser.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DEFAULT_ASSET_GUARD_CONFIG,
|
|
3
|
+
P4Client,
|
|
4
|
+
P4PilotError,
|
|
5
|
+
buildChangelistDescription,
|
|
6
|
+
classifyAsset,
|
|
7
|
+
ensureOpenForEdit,
|
|
8
|
+
ensureOpenForEditMany,
|
|
9
|
+
groupIndexed,
|
|
10
|
+
parseZtag
|
|
11
|
+
} from "./chunk-QIZ2GXWH.js";
|
|
12
|
+
import {
|
|
13
|
+
MockP4Runner
|
|
14
|
+
} from "./chunk-HLG24B6W.js";
|
|
15
|
+
export {
|
|
16
|
+
DEFAULT_ASSET_GUARD_CONFIG,
|
|
17
|
+
MockP4Runner,
|
|
18
|
+
P4Client,
|
|
19
|
+
P4PilotError,
|
|
20
|
+
buildChangelistDescription,
|
|
21
|
+
classifyAsset,
|
|
22
|
+
ensureOpenForEdit,
|
|
23
|
+
ensureOpenForEditMany,
|
|
24
|
+
groupIndexed,
|
|
25
|
+
parseZtag
|
|
26
|
+
};
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { P as P4Action, e as P4Runner, O as OpenedFile, F as FileStat, C as ChangelistSummary, D as DescribeResult } from './p4-runner-CmZD1E6u.js';
|
|
2
|
+
|
|
3
|
+
type ZtagRecord = Map<string, string>;
|
|
4
|
+
declare function parseZtag(stdout: string): ZtagRecord[];
|
|
5
|
+
declare function groupIndexed(record: ZtagRecord): Record<string, string | string[]>;
|
|
6
|
+
|
|
7
|
+
/** A single revision entry returned by {@link P4Client.filelog}. */
|
|
8
|
+
interface FilelogEntry {
|
|
9
|
+
rev: number;
|
|
10
|
+
change: string;
|
|
11
|
+
action: P4Action;
|
|
12
|
+
user: string;
|
|
13
|
+
description: string;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Typed wrapper over a {@link P4Runner}. Each method builds `p4` args, runs them,
|
|
17
|
+
* parses the `-ztag` output, and maps it to a domain type. Non-zero exits raise
|
|
18
|
+
* a {@link P4PilotError} with code `P4_COMMAND_FAILED`.
|
|
19
|
+
*/
|
|
20
|
+
declare class P4Client {
|
|
21
|
+
#private;
|
|
22
|
+
constructor(runner: P4Runner);
|
|
23
|
+
info(): Promise<Record<string, string>>;
|
|
24
|
+
opened(opts?: {
|
|
25
|
+
changelist?: string;
|
|
26
|
+
}): Promise<OpenedFile[]>;
|
|
27
|
+
fstat(files: string[]): Promise<FileStat[]>;
|
|
28
|
+
edit(files: string[], opts?: {
|
|
29
|
+
changelist?: string;
|
|
30
|
+
}): Promise<OpenedFile[]>;
|
|
31
|
+
add(files: string[], opts?: {
|
|
32
|
+
changelist?: string;
|
|
33
|
+
}): Promise<OpenedFile[]>;
|
|
34
|
+
deleteFiles(files: string[], opts?: {
|
|
35
|
+
changelist?: string;
|
|
36
|
+
}): Promise<OpenedFile[]>;
|
|
37
|
+
revert(files: string[]): Promise<string[]>;
|
|
38
|
+
sync(paths?: string[]): Promise<{
|
|
39
|
+
synced: number;
|
|
40
|
+
}>;
|
|
41
|
+
where(file: string): Promise<{
|
|
42
|
+
depotFile: string;
|
|
43
|
+
clientFile: string;
|
|
44
|
+
path: string;
|
|
45
|
+
}>;
|
|
46
|
+
changes(opts?: {
|
|
47
|
+
status?: "pending" | "submitted";
|
|
48
|
+
max?: number;
|
|
49
|
+
user?: string;
|
|
50
|
+
}): Promise<ChangelistSummary[]>;
|
|
51
|
+
describe(change: string, opts?: {
|
|
52
|
+
diff?: boolean;
|
|
53
|
+
}): Promise<DescribeResult>;
|
|
54
|
+
filelog(file: string, opts?: {
|
|
55
|
+
max?: number;
|
|
56
|
+
}): Promise<FilelogEntry[]>;
|
|
57
|
+
newChangelist(description: string): Promise<string>;
|
|
58
|
+
reopen(files: string[], changelist: string): Promise<OpenedFile[]>;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** How p4pilot classifies a file for agent consumption. */
|
|
62
|
+
type AssetKind = "text" | "binary" | "large-asset";
|
|
63
|
+
interface AssetClassification {
|
|
64
|
+
path: string;
|
|
65
|
+
kind: AssetKind;
|
|
66
|
+
/** p4 filetype when known (e.g. "text", "binary+l"). */
|
|
67
|
+
filetype?: string;
|
|
68
|
+
sizeBytes?: number;
|
|
69
|
+
/** True only for text — agents should not read binary/large-asset bytes. */
|
|
70
|
+
shouldRead: boolean;
|
|
71
|
+
reason: string;
|
|
72
|
+
}
|
|
73
|
+
interface AssetGuardConfig {
|
|
74
|
+
binaryExtensions: string[];
|
|
75
|
+
largeAssetExtensions: string[];
|
|
76
|
+
maxTextBytes: number;
|
|
77
|
+
}
|
|
78
|
+
declare const DEFAULT_ASSET_GUARD_CONFIG: AssetGuardConfig;
|
|
79
|
+
/**
|
|
80
|
+
* Classify a file so an agent knows whether to read its bytes. Decision order:
|
|
81
|
+
* large-asset extension → binary p4 filetype → binary extension → oversized → text.
|
|
82
|
+
*/
|
|
83
|
+
declare function classifyAsset(path: string, opts?: {
|
|
84
|
+
stat?: FileStat;
|
|
85
|
+
sizeBytes?: number;
|
|
86
|
+
config?: Partial<AssetGuardConfig>;
|
|
87
|
+
}): AssetClassification;
|
|
88
|
+
|
|
89
|
+
type CheckoutStatus = "already-open" | "opened" | "added" | "skipped-untracked-ignored";
|
|
90
|
+
interface CheckoutResult {
|
|
91
|
+
path: string;
|
|
92
|
+
status: CheckoutStatus;
|
|
93
|
+
action?: P4Action;
|
|
94
|
+
changelist?: string;
|
|
95
|
+
asset?: AssetClassification;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Ensure `localPath` is open for edit before an agent modifies it.
|
|
99
|
+
*
|
|
100
|
+
* fstat the file, then: already open → "already-open"; tracked in depot →
|
|
101
|
+
* `p4 edit` → "opened"; otherwise (new file) → `p4 add` → "added". Attaches to
|
|
102
|
+
* `opts.changelist` when provided, and classifies the file so hosts can warn
|
|
103
|
+
* on binary edits.
|
|
104
|
+
*/
|
|
105
|
+
declare function ensureOpenForEdit(client: P4Client, localPath: string, opts?: {
|
|
106
|
+
changelist?: string;
|
|
107
|
+
assetConfig?: Partial<AssetGuardConfig>;
|
|
108
|
+
}): Promise<CheckoutResult>;
|
|
109
|
+
/**
|
|
110
|
+
* Batch variant. Preserves input order and never lets one bad file abort the
|
|
111
|
+
* rest — a failure yields a `skipped-untracked-ignored` result for that path.
|
|
112
|
+
*/
|
|
113
|
+
declare function ensureOpenForEditMany(client: P4Client, localPaths: string[], opts?: {
|
|
114
|
+
changelist?: string;
|
|
115
|
+
}): Promise<CheckoutResult[]>;
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Build a changelist description from an agent's task intent, prefixing it so
|
|
119
|
+
* p4pilot-created changelists are recognizable. Never double-prefixes.
|
|
120
|
+
*/
|
|
121
|
+
declare function buildChangelistDescription(intent: string, prefix?: string): string;
|
|
122
|
+
|
|
123
|
+
export { type AssetClassification as A, type CheckoutResult as C, DEFAULT_ASSET_GUARD_CONFIG as D, type FilelogEntry as F, P4Client as P, type ZtagRecord as Z, type AssetGuardConfig as a, type AssetKind as b, type CheckoutStatus as c, buildChangelistDescription as d, classifyAsset as e, ensureOpenForEdit as f, ensureOpenForEditMany as g, groupIndexed as h, parseZtag as p };
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { P as P4Action, e as P4Runner, O as OpenedFile, F as FileStat, C as ChangelistSummary, D as DescribeResult } from './p4-runner-CmZD1E6u.cjs';
|
|
2
|
+
|
|
3
|
+
type ZtagRecord = Map<string, string>;
|
|
4
|
+
declare function parseZtag(stdout: string): ZtagRecord[];
|
|
5
|
+
declare function groupIndexed(record: ZtagRecord): Record<string, string | string[]>;
|
|
6
|
+
|
|
7
|
+
/** A single revision entry returned by {@link P4Client.filelog}. */
|
|
8
|
+
interface FilelogEntry {
|
|
9
|
+
rev: number;
|
|
10
|
+
change: string;
|
|
11
|
+
action: P4Action;
|
|
12
|
+
user: string;
|
|
13
|
+
description: string;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Typed wrapper over a {@link P4Runner}. Each method builds `p4` args, runs them,
|
|
17
|
+
* parses the `-ztag` output, and maps it to a domain type. Non-zero exits raise
|
|
18
|
+
* a {@link P4PilotError} with code `P4_COMMAND_FAILED`.
|
|
19
|
+
*/
|
|
20
|
+
declare class P4Client {
|
|
21
|
+
#private;
|
|
22
|
+
constructor(runner: P4Runner);
|
|
23
|
+
info(): Promise<Record<string, string>>;
|
|
24
|
+
opened(opts?: {
|
|
25
|
+
changelist?: string;
|
|
26
|
+
}): Promise<OpenedFile[]>;
|
|
27
|
+
fstat(files: string[]): Promise<FileStat[]>;
|
|
28
|
+
edit(files: string[], opts?: {
|
|
29
|
+
changelist?: string;
|
|
30
|
+
}): Promise<OpenedFile[]>;
|
|
31
|
+
add(files: string[], opts?: {
|
|
32
|
+
changelist?: string;
|
|
33
|
+
}): Promise<OpenedFile[]>;
|
|
34
|
+
deleteFiles(files: string[], opts?: {
|
|
35
|
+
changelist?: string;
|
|
36
|
+
}): Promise<OpenedFile[]>;
|
|
37
|
+
revert(files: string[]): Promise<string[]>;
|
|
38
|
+
sync(paths?: string[]): Promise<{
|
|
39
|
+
synced: number;
|
|
40
|
+
}>;
|
|
41
|
+
where(file: string): Promise<{
|
|
42
|
+
depotFile: string;
|
|
43
|
+
clientFile: string;
|
|
44
|
+
path: string;
|
|
45
|
+
}>;
|
|
46
|
+
changes(opts?: {
|
|
47
|
+
status?: "pending" | "submitted";
|
|
48
|
+
max?: number;
|
|
49
|
+
user?: string;
|
|
50
|
+
}): Promise<ChangelistSummary[]>;
|
|
51
|
+
describe(change: string, opts?: {
|
|
52
|
+
diff?: boolean;
|
|
53
|
+
}): Promise<DescribeResult>;
|
|
54
|
+
filelog(file: string, opts?: {
|
|
55
|
+
max?: number;
|
|
56
|
+
}): Promise<FilelogEntry[]>;
|
|
57
|
+
newChangelist(description: string): Promise<string>;
|
|
58
|
+
reopen(files: string[], changelist: string): Promise<OpenedFile[]>;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** How p4pilot classifies a file for agent consumption. */
|
|
62
|
+
type AssetKind = "text" | "binary" | "large-asset";
|
|
63
|
+
interface AssetClassification {
|
|
64
|
+
path: string;
|
|
65
|
+
kind: AssetKind;
|
|
66
|
+
/** p4 filetype when known (e.g. "text", "binary+l"). */
|
|
67
|
+
filetype?: string;
|
|
68
|
+
sizeBytes?: number;
|
|
69
|
+
/** True only for text — agents should not read binary/large-asset bytes. */
|
|
70
|
+
shouldRead: boolean;
|
|
71
|
+
reason: string;
|
|
72
|
+
}
|
|
73
|
+
interface AssetGuardConfig {
|
|
74
|
+
binaryExtensions: string[];
|
|
75
|
+
largeAssetExtensions: string[];
|
|
76
|
+
maxTextBytes: number;
|
|
77
|
+
}
|
|
78
|
+
declare const DEFAULT_ASSET_GUARD_CONFIG: AssetGuardConfig;
|
|
79
|
+
/**
|
|
80
|
+
* Classify a file so an agent knows whether to read its bytes. Decision order:
|
|
81
|
+
* large-asset extension → binary p4 filetype → binary extension → oversized → text.
|
|
82
|
+
*/
|
|
83
|
+
declare function classifyAsset(path: string, opts?: {
|
|
84
|
+
stat?: FileStat;
|
|
85
|
+
sizeBytes?: number;
|
|
86
|
+
config?: Partial<AssetGuardConfig>;
|
|
87
|
+
}): AssetClassification;
|
|
88
|
+
|
|
89
|
+
type CheckoutStatus = "already-open" | "opened" | "added" | "skipped-untracked-ignored";
|
|
90
|
+
interface CheckoutResult {
|
|
91
|
+
path: string;
|
|
92
|
+
status: CheckoutStatus;
|
|
93
|
+
action?: P4Action;
|
|
94
|
+
changelist?: string;
|
|
95
|
+
asset?: AssetClassification;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Ensure `localPath` is open for edit before an agent modifies it.
|
|
99
|
+
*
|
|
100
|
+
* fstat the file, then: already open → "already-open"; tracked in depot →
|
|
101
|
+
* `p4 edit` → "opened"; otherwise (new file) → `p4 add` → "added". Attaches to
|
|
102
|
+
* `opts.changelist` when provided, and classifies the file so hosts can warn
|
|
103
|
+
* on binary edits.
|
|
104
|
+
*/
|
|
105
|
+
declare function ensureOpenForEdit(client: P4Client, localPath: string, opts?: {
|
|
106
|
+
changelist?: string;
|
|
107
|
+
assetConfig?: Partial<AssetGuardConfig>;
|
|
108
|
+
}): Promise<CheckoutResult>;
|
|
109
|
+
/**
|
|
110
|
+
* Batch variant. Preserves input order and never lets one bad file abort the
|
|
111
|
+
* rest — a failure yields a `skipped-untracked-ignored` result for that path.
|
|
112
|
+
*/
|
|
113
|
+
declare function ensureOpenForEditMany(client: P4Client, localPaths: string[], opts?: {
|
|
114
|
+
changelist?: string;
|
|
115
|
+
}): Promise<CheckoutResult[]>;
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Build a changelist description from an agent's task intent, prefixing it so
|
|
119
|
+
* p4pilot-created changelists are recognizable. Never double-prefixes.
|
|
120
|
+
*/
|
|
121
|
+
declare function buildChangelistDescription(intent: string, prefix?: string): string;
|
|
122
|
+
|
|
123
|
+
export { type AssetClassification as A, type CheckoutResult as C, DEFAULT_ASSET_GUARD_CONFIG as D, type FilelogEntry as F, P4Client as P, type ZtagRecord as Z, type AssetGuardConfig as a, type AssetKind as b, type CheckoutStatus as c, buildChangelistDescription as d, classifyAsset as e, ensureOpenForEdit as f, ensureOpenForEditMany as g, groupIndexed as h, parseZtag as p };
|
|
@@ -0,0 +1,387 @@
|
|
|
1
|
+
// src/testing/mock-runner.ts
|
|
2
|
+
var success = (stdout = "") => ({
|
|
3
|
+
stdout,
|
|
4
|
+
stderr: "",
|
|
5
|
+
exitCode: 0
|
|
6
|
+
});
|
|
7
|
+
var failure = (stderr) => ({
|
|
8
|
+
stdout: "",
|
|
9
|
+
stderr,
|
|
10
|
+
exitCode: 1
|
|
11
|
+
});
|
|
12
|
+
function formatRecord(fields) {
|
|
13
|
+
return fields.filter(
|
|
14
|
+
(field) => field[1] !== void 0
|
|
15
|
+
).map(([key, rawValue]) => {
|
|
16
|
+
const [firstLine = "", ...continuationLines] = String(rawValue).split("\n");
|
|
17
|
+
return [`... ${key} ${firstLine}`, ...continuationLines].join("\n");
|
|
18
|
+
}).join("\n");
|
|
19
|
+
}
|
|
20
|
+
function formatRecords(records) {
|
|
21
|
+
if (records.length === 0) {
|
|
22
|
+
return "";
|
|
23
|
+
}
|
|
24
|
+
return `${records.map(formatRecord).join("\n\n")}
|
|
25
|
+
`;
|
|
26
|
+
}
|
|
27
|
+
function normalizePath(value) {
|
|
28
|
+
return value.replaceAll("\\", "/");
|
|
29
|
+
}
|
|
30
|
+
function relativePosix(from, to) {
|
|
31
|
+
const fromParts = from.replace(/\/+$/, "").split("/").filter(Boolean);
|
|
32
|
+
const toParts = to.split("/").filter(Boolean);
|
|
33
|
+
let i = 0;
|
|
34
|
+
while (i < fromParts.length && i < toParts.length && fromParts[i] === toParts[i])
|
|
35
|
+
i += 1;
|
|
36
|
+
const up = fromParts.slice(i).map(() => "..");
|
|
37
|
+
return [...up, ...toParts.slice(i)].join("/");
|
|
38
|
+
}
|
|
39
|
+
function parseCommandFiles(args) {
|
|
40
|
+
let change = "default";
|
|
41
|
+
const files = [];
|
|
42
|
+
for (let index = 1; index < args.length; index += 1) {
|
|
43
|
+
const argument = args[index];
|
|
44
|
+
if (argument === void 0) {
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
if (argument === "-c") {
|
|
48
|
+
change = args[index + 1] ?? "default";
|
|
49
|
+
index += 1;
|
|
50
|
+
} else if (!argument.startsWith("-")) {
|
|
51
|
+
files.push(argument);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return { change, files };
|
|
55
|
+
}
|
|
56
|
+
var MockP4Runner = class {
|
|
57
|
+
#state;
|
|
58
|
+
constructor(state) {
|
|
59
|
+
this.#state = state;
|
|
60
|
+
}
|
|
61
|
+
get state() {
|
|
62
|
+
return this.#state;
|
|
63
|
+
}
|
|
64
|
+
async run(args, opts) {
|
|
65
|
+
const commandArgs = args[0] === "-ztag" ? args.slice(1) : [...args];
|
|
66
|
+
const command = commandArgs[0];
|
|
67
|
+
switch (command) {
|
|
68
|
+
case "info":
|
|
69
|
+
return this.runInfo();
|
|
70
|
+
case "fstat":
|
|
71
|
+
return this.runFstat(commandArgs);
|
|
72
|
+
case "opened":
|
|
73
|
+
return this.runOpened(commandArgs);
|
|
74
|
+
case "edit":
|
|
75
|
+
return this.runEdit(commandArgs);
|
|
76
|
+
case "add":
|
|
77
|
+
return this.runAdd(commandArgs);
|
|
78
|
+
case "revert":
|
|
79
|
+
return this.runRevert(commandArgs);
|
|
80
|
+
case "where":
|
|
81
|
+
return this.runWhere(commandArgs);
|
|
82
|
+
case "changes":
|
|
83
|
+
return this.runChanges(commandArgs);
|
|
84
|
+
case "describe":
|
|
85
|
+
return this.runDescribe(commandArgs);
|
|
86
|
+
case "change":
|
|
87
|
+
return this.runChange(commandArgs, opts);
|
|
88
|
+
case "sync":
|
|
89
|
+
return this.runSync();
|
|
90
|
+
case "filelog":
|
|
91
|
+
return this.runFilelog(commandArgs);
|
|
92
|
+
default:
|
|
93
|
+
return failure(`Unsupported mock p4 command: ${command ?? ""}`);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
runInfo() {
|
|
97
|
+
return success(
|
|
98
|
+
formatRecords([
|
|
99
|
+
[
|
|
100
|
+
["userName", this.#state.user],
|
|
101
|
+
["clientName", this.#state.client],
|
|
102
|
+
["clientRoot", this.#state.root],
|
|
103
|
+
["serverAddress", this.#state.port]
|
|
104
|
+
]
|
|
105
|
+
])
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
runFstat(args) {
|
|
109
|
+
const requested = args.slice(1).filter((argument) => !argument.startsWith("-"));
|
|
110
|
+
const files = requested.length === 0 ? this.#state.files : requested.flatMap((file) => this.findFiles(file));
|
|
111
|
+
return success(
|
|
112
|
+
formatRecords(files.map((file) => this.fileStatFields(file)))
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
runOpened(args) {
|
|
116
|
+
const { files } = parseCommandFiles(args);
|
|
117
|
+
const changeIndex = args.indexOf("-c");
|
|
118
|
+
const requestedChange = changeIndex === -1 ? void 0 : args[changeIndex + 1];
|
|
119
|
+
const openedFiles = this.#state.files.filter((file) => {
|
|
120
|
+
if (file.opened === void 0) {
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
if (requestedChange !== void 0 && file.opened.change !== requestedChange) {
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
return files.length === 0 || files.some((requested) => this.matches(file, requested));
|
|
127
|
+
});
|
|
128
|
+
return success(
|
|
129
|
+
formatRecords(openedFiles.map((file) => this.openedFields(file)))
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
runEdit(args) {
|
|
133
|
+
const { change, files } = parseCommandFiles(args);
|
|
134
|
+
const edited = [];
|
|
135
|
+
for (const requested of files) {
|
|
136
|
+
const file = this.findFile(requested);
|
|
137
|
+
if (file === void 0 || file.headRev === void 0) {
|
|
138
|
+
return failure(`${requested} - no such file(s).`);
|
|
139
|
+
}
|
|
140
|
+
file.opened = { action: "edit", change };
|
|
141
|
+
edited.push(file);
|
|
142
|
+
}
|
|
143
|
+
return success(
|
|
144
|
+
formatRecords(edited.map((file) => this.openedFields(file)))
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
runAdd(args) {
|
|
148
|
+
const { change, files } = parseCommandFiles(args);
|
|
149
|
+
const added = [];
|
|
150
|
+
for (const requested of files) {
|
|
151
|
+
let file = this.findFile(requested);
|
|
152
|
+
if (file === void 0) {
|
|
153
|
+
const clientFile = normalizePath(requested);
|
|
154
|
+
file = {
|
|
155
|
+
depotFile: this.toDepotFile(clientFile),
|
|
156
|
+
clientFile,
|
|
157
|
+
headType: "text"
|
|
158
|
+
};
|
|
159
|
+
this.#state.files.push(file);
|
|
160
|
+
}
|
|
161
|
+
file.opened = { action: "add", change };
|
|
162
|
+
added.push(file);
|
|
163
|
+
}
|
|
164
|
+
return success(formatRecords(added.map((file) => this.openedFields(file))));
|
|
165
|
+
}
|
|
166
|
+
runRevert(args) {
|
|
167
|
+
const { files } = parseCommandFiles(args);
|
|
168
|
+
const reverted = [];
|
|
169
|
+
for (const requested of files) {
|
|
170
|
+
const fileIndex = this.#state.files.findIndex(
|
|
171
|
+
(file2) => this.matches(file2, requested)
|
|
172
|
+
);
|
|
173
|
+
const file = this.#state.files[fileIndex];
|
|
174
|
+
if (file === void 0 || file.opened === void 0) {
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
177
|
+
reverted.push([["depotFile", file.depotFile]]);
|
|
178
|
+
if (file.opened.action === "add" && file.headRev === void 0) {
|
|
179
|
+
this.#state.files.splice(fileIndex, 1);
|
|
180
|
+
} else {
|
|
181
|
+
delete file.opened;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
return success(formatRecords(reverted));
|
|
185
|
+
}
|
|
186
|
+
runWhere(args) {
|
|
187
|
+
const requested = args.find(
|
|
188
|
+
(argument, index) => index > 0 && !argument.startsWith("-")
|
|
189
|
+
);
|
|
190
|
+
const file = requested === void 0 ? void 0 : this.findFile(requested);
|
|
191
|
+
if (file === void 0) {
|
|
192
|
+
return failure(`${requested ?? ""} - file(s) not in client view.`);
|
|
193
|
+
}
|
|
194
|
+
return success(
|
|
195
|
+
formatRecords([
|
|
196
|
+
[
|
|
197
|
+
["depotFile", file.depotFile],
|
|
198
|
+
["clientFile", file.clientFile],
|
|
199
|
+
["path", file.clientFile]
|
|
200
|
+
]
|
|
201
|
+
])
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
runChanges(args) {
|
|
205
|
+
let status;
|
|
206
|
+
let max;
|
|
207
|
+
for (let index = 1; index < args.length; index += 1) {
|
|
208
|
+
if (args[index] === "-s") {
|
|
209
|
+
status = args[index + 1];
|
|
210
|
+
index += 1;
|
|
211
|
+
} else if (args[index] === "-m") {
|
|
212
|
+
max = Number(args[index + 1]);
|
|
213
|
+
index += 1;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
let changelists = [...this.#state.changelists ?? []];
|
|
217
|
+
if (status !== void 0) {
|
|
218
|
+
changelists = changelists.filter(
|
|
219
|
+
(changelist) => changelist.status === status
|
|
220
|
+
);
|
|
221
|
+
}
|
|
222
|
+
if (max !== void 0 && Number.isFinite(max)) {
|
|
223
|
+
changelists = changelists.slice(0, max);
|
|
224
|
+
}
|
|
225
|
+
return success(
|
|
226
|
+
formatRecords(
|
|
227
|
+
changelists.map((changelist) => [
|
|
228
|
+
["change", changelist.change],
|
|
229
|
+
["desc", changelist.description],
|
|
230
|
+
["status", changelist.status],
|
|
231
|
+
["user", changelist.user],
|
|
232
|
+
["client", changelist.client]
|
|
233
|
+
])
|
|
234
|
+
)
|
|
235
|
+
);
|
|
236
|
+
}
|
|
237
|
+
runDescribe(args) {
|
|
238
|
+
const change = [...args].reverse().find((argument) => !argument.startsWith("-"));
|
|
239
|
+
const changelist = this.#state.changelists?.find(
|
|
240
|
+
(item) => item.change === change
|
|
241
|
+
);
|
|
242
|
+
if (changelist === void 0) {
|
|
243
|
+
return failure(`Change ${change ?? ""} unknown.`);
|
|
244
|
+
}
|
|
245
|
+
const fields = [
|
|
246
|
+
["change", changelist.change],
|
|
247
|
+
["desc", changelist.description],
|
|
248
|
+
["user", changelist.user],
|
|
249
|
+
["status", changelist.status]
|
|
250
|
+
];
|
|
251
|
+
for (const [index, depotFile] of (changelist.files ?? []).entries()) {
|
|
252
|
+
const file = this.findFile(depotFile);
|
|
253
|
+
fields.push([`depotFile${index}`, depotFile]);
|
|
254
|
+
fields.push([`action${index}`, file?.opened?.action ?? "edit"]);
|
|
255
|
+
fields.push([`rev${index}`, file?.headRev]);
|
|
256
|
+
}
|
|
257
|
+
return success(formatRecords([fields]));
|
|
258
|
+
}
|
|
259
|
+
runChange(args, opts) {
|
|
260
|
+
if (!args.includes("-i")) {
|
|
261
|
+
return failure("MockP4Runner only supports change -i.");
|
|
262
|
+
}
|
|
263
|
+
const changelists = this.#state.changelists ?? [];
|
|
264
|
+
this.#state.changelists = changelists;
|
|
265
|
+
const nextChange = String(
|
|
266
|
+
changelists.reduce((highest, changelist) => {
|
|
267
|
+
const value = Number(changelist.change);
|
|
268
|
+
return Number.isFinite(value) ? Math.max(highest, value) : highest;
|
|
269
|
+
}, 0) + 1
|
|
270
|
+
);
|
|
271
|
+
const description = this.parseDescription(opts?.input ?? "");
|
|
272
|
+
changelists.push({
|
|
273
|
+
change: nextChange,
|
|
274
|
+
description,
|
|
275
|
+
status: "pending",
|
|
276
|
+
user: this.#state.user,
|
|
277
|
+
client: this.#state.client,
|
|
278
|
+
files: []
|
|
279
|
+
});
|
|
280
|
+
return success(formatRecords([[["change", nextChange]]]));
|
|
281
|
+
}
|
|
282
|
+
runSync() {
|
|
283
|
+
const records = this.#state.files.filter((file) => file.headRev !== void 0).map((file) => [
|
|
284
|
+
["depotFile", file.depotFile],
|
|
285
|
+
["rev", file.headRev]
|
|
286
|
+
]);
|
|
287
|
+
return success(formatRecords(records));
|
|
288
|
+
}
|
|
289
|
+
runFilelog(args) {
|
|
290
|
+
const files = [];
|
|
291
|
+
for (let index = 1; index < args.length; index += 1) {
|
|
292
|
+
const argument = args[index];
|
|
293
|
+
if (argument === "-m") {
|
|
294
|
+
index += 1;
|
|
295
|
+
} else if (argument !== void 0 && !argument.startsWith("-")) {
|
|
296
|
+
files.push(argument);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
const requested = files[0];
|
|
300
|
+
const file = requested === void 0 ? void 0 : this.findFile(requested);
|
|
301
|
+
if (file === void 0 || file.headRev === void 0) {
|
|
302
|
+
return failure(`${requested ?? ""} - no such file(s).`);
|
|
303
|
+
}
|
|
304
|
+
const fields = [
|
|
305
|
+
["depotFile", file.depotFile],
|
|
306
|
+
["rev0", file.headRev],
|
|
307
|
+
["change0", "1"],
|
|
308
|
+
["action0", "add"],
|
|
309
|
+
["user0", this.#state.user],
|
|
310
|
+
["desc0", "initial revision"]
|
|
311
|
+
];
|
|
312
|
+
return success(formatRecords([fields]));
|
|
313
|
+
}
|
|
314
|
+
fileStatFields(file) {
|
|
315
|
+
return [
|
|
316
|
+
["depotFile", file.depotFile],
|
|
317
|
+
["clientFile", file.clientFile],
|
|
318
|
+
["headType", file.headType],
|
|
319
|
+
["headRev", file.headRev],
|
|
320
|
+
["fileSize", file.sizeBytes],
|
|
321
|
+
["action", file.opened?.action],
|
|
322
|
+
["change", file.opened?.change]
|
|
323
|
+
];
|
|
324
|
+
}
|
|
325
|
+
openedFields(file) {
|
|
326
|
+
return [
|
|
327
|
+
["depotFile", file.depotFile],
|
|
328
|
+
["clientFile", file.clientFile],
|
|
329
|
+
["rev", file.headRev],
|
|
330
|
+
["action", file.opened?.action],
|
|
331
|
+
["change", file.opened?.change],
|
|
332
|
+
["type", file.headType ?? "text"]
|
|
333
|
+
];
|
|
334
|
+
}
|
|
335
|
+
findFiles(requested) {
|
|
336
|
+
return this.#state.files.filter((file) => this.matches(file, requested));
|
|
337
|
+
}
|
|
338
|
+
findFile(requested) {
|
|
339
|
+
return this.#state.files.find((file) => this.matches(file, requested));
|
|
340
|
+
}
|
|
341
|
+
matches(file, requested) {
|
|
342
|
+
const normalized = normalizePath(requested);
|
|
343
|
+
if (normalized.endsWith("/...")) {
|
|
344
|
+
const prefix = normalized.slice(0, -3);
|
|
345
|
+
return normalizePath(file.clientFile).startsWith(prefix) || file.depotFile.startsWith(prefix);
|
|
346
|
+
}
|
|
347
|
+
return normalizePath(file.clientFile) === normalized || file.depotFile === normalized;
|
|
348
|
+
}
|
|
349
|
+
toDepotFile(clientFile) {
|
|
350
|
+
const normalizedRoot = normalizePath(this.#state.root).replace(/\/$/, "");
|
|
351
|
+
const relative = relativePosix(normalizedRoot, clientFile);
|
|
352
|
+
const trackedFile = this.#state.files.find(
|
|
353
|
+
(file) => file.headRev !== void 0
|
|
354
|
+
);
|
|
355
|
+
if (trackedFile !== void 0) {
|
|
356
|
+
const trackedRelative = relativePosix(
|
|
357
|
+
normalizedRoot,
|
|
358
|
+
normalizePath(trackedFile.clientFile)
|
|
359
|
+
);
|
|
360
|
+
const suffix = `/${trackedRelative}`;
|
|
361
|
+
const depotRoot = trackedFile.depotFile.endsWith(suffix) ? trackedFile.depotFile.slice(0, -suffix.length) : "//depot";
|
|
362
|
+
return `${depotRoot}/${relative}`;
|
|
363
|
+
}
|
|
364
|
+
return `//depot/${relative}`;
|
|
365
|
+
}
|
|
366
|
+
parseDescription(input) {
|
|
367
|
+
const lines = input.split(/\r?\n/);
|
|
368
|
+
const descriptionIndex = lines.findIndex(
|
|
369
|
+
(line) => line.startsWith("Description:")
|
|
370
|
+
);
|
|
371
|
+
if (descriptionIndex === -1) {
|
|
372
|
+
return "";
|
|
373
|
+
}
|
|
374
|
+
const descriptionLines = [];
|
|
375
|
+
for (const line of lines.slice(descriptionIndex + 1)) {
|
|
376
|
+
if (/^[A-Za-z][A-Za-z0-9-]*:/.test(line)) {
|
|
377
|
+
break;
|
|
378
|
+
}
|
|
379
|
+
descriptionLines.push(line.replace(/^\t/, ""));
|
|
380
|
+
}
|
|
381
|
+
return descriptionLines.join("\n").trim();
|
|
382
|
+
}
|
|
383
|
+
};
|
|
384
|
+
|
|
385
|
+
export {
|
|
386
|
+
MockP4Runner
|
|
387
|
+
};
|