@kubohiroya/turbowarp-plus-editor-git 0.16.1 → 0.18.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.ja.md +18 -2
- package/README.md +17 -1
- package/dist/branch-button.d.ts +32 -0
- package/dist/filesystem.d.ts +2 -2
- package/dist/github-pull-requests.d.ts +20 -0
- package/dist/index.d.ts +12 -6
- package/dist/index.js +739 -158
- package/dist/local-operations.d.ts +8 -1
- package/dist/messages.d.ts +16 -2
- package/dist/{project-asset-paths-KV715791.js → project-asset-paths-BzwVf9HE.js} +653 -1
- package/dist/{project-comparison-1rggIJse.js → project-comparison-BXOQcZ73.js} +10 -654
- package/dist/project-source-diff.js +2 -1
- package/dist/pull-request-panel.d.ts +25 -1
- package/dist/remote-operations.d.ts +23 -0
- package/dist/{repository-BYno-5f8.js → repository-ByKRBftJ.js} +370 -14
- package/dist/repository.d.ts +39 -0
- package/dist/repository.js +1 -1
- package/dist/style.css +28 -0
- package/package.json +1 -1
- package/src/branch-button.tsx +216 -0
- package/src/filesystem.ts +2 -1
- package/src/github-pull-requests.ts +7 -0
- package/src/index.tsx +213 -67
- package/src/local-operations.ts +23 -3
- package/src/messages.ts +14 -4
- package/src/pull-request-panel.tsx +125 -14
- package/src/remote-operations.ts +84 -3
- package/src/repository.ts +155 -9
- package/src/style.css +28 -0
- package/test/smart-http-fixture.js +20 -0
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { a as compareProjectBlocks, o as createProjectSourceDiff, u as readProjectSourceDiff } from "./project-asset-paths-BzwVf9HE.js";
|
|
2
|
+
import { _ as usePreviewSync, a as openProjectSourceTabs, c as gitHubRepositoryFromOrigin, d as assetFileName, f as organizeProjectChanges, g as createPreviewSync, h as BlockPreview, i as createScratchBlocksDiffLocator, l as ProjectSourceComparison, m as compareProjectForReview, n as openProjectComparison, p as projectPropertyLabel, r as createScratchBlocksDiffRenderer, s as createGitHubPullRequests, t as ProjectComparisonDialog, u as ProjectSourceReview, v as currentCostume } from "./project-comparison-BXOQcZ73.js";
|
|
2
3
|
import { n as init_objectSpread2, t as _objectSpread2 } from "./objectSpread2-Dkd68nJH.js";
|
|
3
4
|
import React, { useEffect, useMemo, useRef, useState } from "react";
|
|
4
5
|
// src/project-source-diff.tsx
|
|
@@ -3,11 +3,35 @@ import type { ProjectBlockRenderer } from './scratch-block-diff-renderer.js';
|
|
|
3
3
|
import type { Repository } from './repository.js';
|
|
4
4
|
import type { ProjectBlockLocator } from './block-diff-location.js';
|
|
5
5
|
import type { ProjectSourceTabsFile } from './project-source-tabs.js';
|
|
6
|
+
import type { GetRemoteSession } from './remote-operations.js';
|
|
6
7
|
type OpenTabs = (key: string, files: readonly ProjectSourceTabsFile[], rememberAs?: string) => number;
|
|
7
|
-
|
|
8
|
+
/**
|
|
9
|
+
* Opens a pull request version fetched over the connection in the work tree: the host creates (or reuses) the local
|
|
10
|
+
* branch `pullRequestBranch(number)` at `head` and switches to it. `busy` disables the button.
|
|
11
|
+
*/
|
|
12
|
+
export interface PullRequestOpenLocally {
|
|
13
|
+
open(number: number, head: string): unknown;
|
|
14
|
+
busy: boolean;
|
|
15
|
+
}
|
|
16
|
+
/** The person's GitHub connection: while it is connected, pull request contents are read over Git Smart HTTP. */
|
|
17
|
+
export interface PullRequestConnection {
|
|
18
|
+
getSession: GetRemoteSession;
|
|
19
|
+
connected(): boolean;
|
|
20
|
+
}
|
|
21
|
+
export declare function PullRequestReviewPanel({ repository, locale, renderBlock, locateFor, openTabs, connection, openLocally }: {
|
|
8
22
|
repository: Repository;
|
|
9
23
|
locale: string;
|
|
10
24
|
renderBlock: ProjectBlockRenderer;
|
|
25
|
+
/**
|
|
26
|
+
* While connected, pull request contents are fetched over Git Smart HTTP (`refs/pull/<number>/head`) and compared
|
|
27
|
+
* from local Git objects, so private repositories can be reviewed. Without it, the public GitHub API is read.
|
|
28
|
+
*/
|
|
29
|
+
connection?: PullRequestConnection;
|
|
30
|
+
/**
|
|
31
|
+
* Offers to open a pull request read over the connection in the work tree, on the branch `pr/<number>`. Pull
|
|
32
|
+
* requests read from the public API have no local objects, so the button is not shown for them.
|
|
33
|
+
*/
|
|
34
|
+
openLocally?: PullRequestOpenLocally;
|
|
11
35
|
/** Opens a pull request's project changes as tabs of the host's layout. */
|
|
12
36
|
openTabs?: OpenTabs;
|
|
13
37
|
/** Returns a locator for a project file whose PR head matches the open project, or `undefined`. */
|
|
@@ -2,4 +2,27 @@ import type { GitSmartHttpOptions } from './smart-http.js';
|
|
|
2
2
|
export type RemoteOperation = 'fetch' | 'pull' | 'push';
|
|
3
3
|
export type RemoteSession = NonNullable<Awaited<ReturnType<GitSmartHttpOptions['getSession']>>>;
|
|
4
4
|
export type GetRemoteSession = (remote: string, write: boolean) => Promise<RemoteSession | null>;
|
|
5
|
+
/**
|
|
6
|
+
* Where a fetched pull request head is kept: outside `refs/heads`, `refs/remotes` and `refs/tags`, so Git CLI lists it
|
|
7
|
+
* with `git for-each-ref refs/pull` and `git log --all` but not as a branch or tag, `git fetch --prune` leaves it alone,
|
|
8
|
+
* and `git gc` keeps its objects. Each fetch of the same pull request replaces it.
|
|
9
|
+
*/
|
|
10
|
+
export declare const pullRequestRef: (number: number) => string;
|
|
11
|
+
export interface PullRequestFetch {
|
|
12
|
+
readonly number: number;
|
|
13
|
+
/** The commit at GitHub's `refs/pull/<number>/head`, kept locally at `ref`. */
|
|
14
|
+
readonly head: string;
|
|
15
|
+
readonly ref: string;
|
|
16
|
+
/** GitHub's default branch, from the advertised `HEAD`, or `null` when it is not advertised. */
|
|
17
|
+
readonly defaultBranch: string | null;
|
|
18
|
+
/** The branches on GitHub, as `refs/remotes/origin/<name>` after this fetch. */
|
|
19
|
+
readonly branches: readonly string[];
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Fetches GitHub's `refs/pull/<number>/head` together with every branch, over the connection's Git Smart HTTP, like
|
|
23
|
+
* `git fetch origin refs/pull/<number>/head`. The branches update `refs/remotes/origin/*` as fetch does, and the head
|
|
24
|
+
* is kept at `pullRequestRef(number)`. The work tree, the index, HEAD and `.git/config` are never written. The writes
|
|
25
|
+
* are staged first and applied together, under the same lock as fetch, pull and push.
|
|
26
|
+
*/
|
|
27
|
+
export declare function runPullRequestFetch(root: FileSystemDirectoryHandle, number: number, getSession: GetRemoteSession, guard: () => void, signal?: AbortSignal): Promise<PullRequestFetch>;
|
|
5
28
|
export declare function runRemoteOperation(root: FileSystemDirectoryHandle, operation: RemoteOperation, getSession: GetRemoteSession, guard: () => void, signal?: AbortSignal): Promise<void>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { i as __toESM, n as init_objectSpread2, r as __commonJSMin, t as _objectSpread2 } from "./objectSpread2-Dkd68nJH.js";
|
|
2
|
-
import {
|
|
2
|
+
import { i as isAssetName, n as assetsDirectoryParts, o as createProjectSourceDiff, p as createTwoFilesPatch, r as folderSettingsLimit, t as assetLimit } from "./project-asset-paths-BzwVf9HE.js";
|
|
3
3
|
import { a as _OverloadYield, i as _wrapAsyncGenerator, o as _asyncIterator, r as createGitSmartHttp, t as GitSmartHttpError } from "./smart-http-BmHEkE7K.js";
|
|
4
4
|
// node_modules/inherits/inherits_browser.js
|
|
5
5
|
var require_inherits_browser = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
@@ -16929,6 +16929,79 @@ async function walk({ fs, dir, gitdir = join(dir, ".git"), trees, map, reduce, i
|
|
|
16929
16929
|
throw err;
|
|
16930
16930
|
}
|
|
16931
16931
|
}
|
|
16932
|
+
/**
|
|
16933
|
+
* Write a ref which refers to the specified SHA-1 object id, or a symbolic ref which refers to the specified ref.
|
|
16934
|
+
*
|
|
16935
|
+
* @param {object} args
|
|
16936
|
+
* @param {FsClient} args.fs - a file system client
|
|
16937
|
+
* @param {string} [args.dir] - The [working tree](dir-vs-gitdir.md) directory path
|
|
16938
|
+
* @param {string} [args.gitdir=join(dir,'.git')] - [required] The [git directory](dir-vs-gitdir.md) path
|
|
16939
|
+
* @param {string} args.ref - The name of the ref to write
|
|
16940
|
+
* @param {string} args.value - When `symbolic` is false, a ref or an SHA-1 object id. When true, a ref starting with `refs/`.
|
|
16941
|
+
* @param {boolean} [args.force = false] - Instead of throwing an error if a ref named `ref` already exists, overwrite the existing ref.
|
|
16942
|
+
* @param {boolean} [args.symbolic = false] - Whether the ref is symbolic or not.
|
|
16943
|
+
*
|
|
16944
|
+
* @returns {Promise<void>} Resolves successfully when filesystem operations are complete
|
|
16945
|
+
*
|
|
16946
|
+
* @example
|
|
16947
|
+
* await git.writeRef({
|
|
16948
|
+
* fs,
|
|
16949
|
+
* dir: '/tutorial',
|
|
16950
|
+
* ref: 'refs/heads/another-branch',
|
|
16951
|
+
* value: 'HEAD'
|
|
16952
|
+
* })
|
|
16953
|
+
* await git.writeRef({
|
|
16954
|
+
* fs,
|
|
16955
|
+
* dir: '/tutorial',
|
|
16956
|
+
* ref: 'HEAD',
|
|
16957
|
+
* value: 'refs/heads/another-branch',
|
|
16958
|
+
* force: true,
|
|
16959
|
+
* symbolic: true
|
|
16960
|
+
* })
|
|
16961
|
+
* console.log('done')
|
|
16962
|
+
*
|
|
16963
|
+
*/
|
|
16964
|
+
async function writeRef({ fs: _fs, dir, gitdir = join(dir, ".git"), ref, value, force = false, symbolic = false }) {
|
|
16965
|
+
try {
|
|
16966
|
+
assertParameter("fs", _fs);
|
|
16967
|
+
assertParameter("gitdir", gitdir);
|
|
16968
|
+
assertParameter("ref", ref);
|
|
16969
|
+
assertParameter("value", value);
|
|
16970
|
+
const fs = new FileSystem(_fs);
|
|
16971
|
+
if (!isValidRef(ref, true)) throw new InvalidRefNameError(ref, import_lib.default.clean(ref));
|
|
16972
|
+
const updatedGitdir = await discoverGitdir({
|
|
16973
|
+
fsp: fs,
|
|
16974
|
+
dotgit: gitdir
|
|
16975
|
+
});
|
|
16976
|
+
if (!force && await GitRefManager.exists({
|
|
16977
|
+
fs,
|
|
16978
|
+
gitdir: updatedGitdir,
|
|
16979
|
+
ref
|
|
16980
|
+
})) throw new AlreadyExistsError("ref", ref);
|
|
16981
|
+
if (symbolic) await GitRefManager.writeSymbolicRef({
|
|
16982
|
+
fs,
|
|
16983
|
+
gitdir: updatedGitdir,
|
|
16984
|
+
ref,
|
|
16985
|
+
value
|
|
16986
|
+
});
|
|
16987
|
+
else {
|
|
16988
|
+
value = await GitRefManager.resolve({
|
|
16989
|
+
fs,
|
|
16990
|
+
gitdir: updatedGitdir,
|
|
16991
|
+
ref: value
|
|
16992
|
+
});
|
|
16993
|
+
await GitRefManager.writeRef({
|
|
16994
|
+
fs,
|
|
16995
|
+
gitdir: updatedGitdir,
|
|
16996
|
+
ref,
|
|
16997
|
+
value
|
|
16998
|
+
});
|
|
16999
|
+
}
|
|
17000
|
+
} catch (err) {
|
|
17001
|
+
err.caller = "git.writeRef";
|
|
17002
|
+
throw err;
|
|
17003
|
+
}
|
|
17004
|
+
}
|
|
16932
17005
|
// src/filesystem.ts
|
|
16933
17006
|
var GitPanelError = class extends Error {
|
|
16934
17007
|
constructor(code, message = code) {
|
|
@@ -17342,8 +17415,104 @@ function _asyncGeneratorDelegate(t) {
|
|
|
17342
17415
|
}
|
|
17343
17416
|
// src/remote-operations.ts
|
|
17344
17417
|
init_objectSpread2();
|
|
17345
|
-
|
|
17418
|
+
var sessionCodes = [
|
|
17419
|
+
"SESSION_REQUIRED",
|
|
17420
|
+
"SESSION_UNAVAILABLE",
|
|
17421
|
+
"SESSION_EXPIRED",
|
|
17422
|
+
"REAUTH_REQUIRED",
|
|
17423
|
+
"INVALID_SESSION"
|
|
17424
|
+
];
|
|
17425
|
+
async function checkRefspecs(options) {
|
|
17426
|
+
if ((await getConfigAll(_objectSpread2(_objectSpread2({}, options), {}, { path: "remote.origin.fetch" }))).some((spec) => !/^\+?refs\/heads\/[^:\s]+:refs\/remotes\/origin\/[^:\s]+$/u.test(spec))) throw new GitPanelError("unsupported");
|
|
17427
|
+
}
|
|
17428
|
+
/**
|
|
17429
|
+
* Where a fetched pull request head is kept: outside `refs/heads`, `refs/remotes` and `refs/tags`, so Git CLI lists it
|
|
17430
|
+
* with `git for-each-ref refs/pull` and `git log --all` but not as a branch or tag, `git fetch --prune` leaves it alone,
|
|
17431
|
+
* and `git gc` keeps its objects. Each fetch of the same pull request replaces it.
|
|
17432
|
+
*/
|
|
17433
|
+
var pullRequestRef = (number) => `refs/pull/origin/${number}/head`;
|
|
17434
|
+
/**
|
|
17435
|
+
* Fetches GitHub's `refs/pull/<number>/head` together with every branch, over the connection's Git Smart HTTP, like
|
|
17436
|
+
* `git fetch origin refs/pull/<number>/head`. The branches update `refs/remotes/origin/*` as fetch does, and the head
|
|
17437
|
+
* is kept at `pullRequestRef(number)`. The work tree, the index, HEAD and `.git/config` are never written. The writes
|
|
17438
|
+
* are staged first and applied together, under the same lock as fetch, pull and push.
|
|
17439
|
+
*/
|
|
17440
|
+
async function runPullRequestFetch(root, number, getSession, guard, signal) {
|
|
17346
17441
|
var _globalThis$navigator;
|
|
17442
|
+
if (!Number.isSafeInteger(number) || number < 1) throw new GitPanelError("pullRequestNotFound");
|
|
17443
|
+
const execute = async () => {
|
|
17444
|
+
var _result$defaultBranch;
|
|
17445
|
+
guard();
|
|
17446
|
+
const transaction = createMutationFs(root, false);
|
|
17447
|
+
const options = {
|
|
17448
|
+
fs: transaction.fs,
|
|
17449
|
+
dir: "/",
|
|
17450
|
+
gitdir: "/.git",
|
|
17451
|
+
cache: {}
|
|
17452
|
+
};
|
|
17453
|
+
const remote = await getConfig(_objectSpread2(_objectSpread2({}, options), {}, { path: "remote.origin.url" }));
|
|
17454
|
+
if (typeof remote !== "string") throw new GitPanelError("session");
|
|
17455
|
+
await checkRefspecs(options);
|
|
17456
|
+
const session = await getSession(remote, false);
|
|
17457
|
+
if (!session) throw new GitPanelError("session");
|
|
17458
|
+
guard();
|
|
17459
|
+
const transport = createGitSmartHttp({
|
|
17460
|
+
remote,
|
|
17461
|
+
connection: session.connection,
|
|
17462
|
+
getSession: () => getSession(remote, false),
|
|
17463
|
+
signal
|
|
17464
|
+
});
|
|
17465
|
+
const source = `refs/pull/${number}/head`;
|
|
17466
|
+
let result;
|
|
17467
|
+
try {
|
|
17468
|
+
result = await fetch(_objectSpread2(_objectSpread2(_objectSpread2({}, options), transport), {}, {
|
|
17469
|
+
remote: "origin",
|
|
17470
|
+
remoteRef: source,
|
|
17471
|
+
tags: false
|
|
17472
|
+
}));
|
|
17473
|
+
} catch (error) {
|
|
17474
|
+
const { code, data } = error;
|
|
17475
|
+
if (code === "NotFoundError" && (data === null || data === void 0 ? void 0 : data.what) === source) throw new GitPanelError("pullRequestNotFound");
|
|
17476
|
+
throw remoteError(error);
|
|
17477
|
+
}
|
|
17478
|
+
const head = result.fetchHead;
|
|
17479
|
+
if (!head || !/^[0-9a-f]{40}$/u.test(head)) throw new GitPanelError("remoteFailed");
|
|
17480
|
+
const ref = pullRequestRef(number);
|
|
17481
|
+
await writeRef(_objectSpread2(_objectSpread2({}, options), {}, {
|
|
17482
|
+
ref,
|
|
17483
|
+
value: head,
|
|
17484
|
+
force: true
|
|
17485
|
+
}));
|
|
17486
|
+
const branches = (await listBranches(_objectSpread2(_objectSpread2({}, options), {}, { remote: "origin" }))).filter((name) => name !== "HEAD").sort();
|
|
17487
|
+
const advertised = ((_result$defaultBranch = result.defaultBranch) === null || _result$defaultBranch === void 0 ? void 0 : _result$defaultBranch.startsWith("refs/heads/")) ? result.defaultBranch.slice(11) : null;
|
|
17488
|
+
await transaction.commit(guard);
|
|
17489
|
+
return {
|
|
17490
|
+
number,
|
|
17491
|
+
head,
|
|
17492
|
+
ref,
|
|
17493
|
+
defaultBranch: advertised && branches.includes(advertised) ? advertised : null,
|
|
17494
|
+
branches
|
|
17495
|
+
};
|
|
17496
|
+
};
|
|
17497
|
+
return ((_globalThis$navigator = globalThis.navigator) === null || _globalThis$navigator === void 0 ? void 0 : _globalThis$navigator.locks) ? navigator.locks.request("tw-plus-git-mutation", execute) : execute();
|
|
17498
|
+
}
|
|
17499
|
+
/** Maps a failed read from GitHub to what the person can act on: the connection, access, or the network. */
|
|
17500
|
+
function remoteError(error) {
|
|
17501
|
+
if (error instanceof GitPanelError) return error;
|
|
17502
|
+
if (error instanceof GitSmartHttpError) {
|
|
17503
|
+
if (sessionCodes.includes(error.code)) return new GitPanelError("session");
|
|
17504
|
+
if (error.code === "FORBIDDEN") return new GitPanelError("forbidden");
|
|
17505
|
+
if ([
|
|
17506
|
+
"NETWORK_ERROR",
|
|
17507
|
+
"UPSTREAM_FAILURE",
|
|
17508
|
+
"UPSTREAM_TIMEOUT",
|
|
17509
|
+
"RATE_LIMITED"
|
|
17510
|
+
].includes(error.code)) return new GitPanelError("network");
|
|
17511
|
+
}
|
|
17512
|
+
return new GitPanelError("remoteFailed");
|
|
17513
|
+
}
|
|
17514
|
+
async function runRemoteOperation(root, operation, getSession, guard, signal) {
|
|
17515
|
+
var _globalThis$navigator2;
|
|
17347
17516
|
const execute = async () => {
|
|
17348
17517
|
guard();
|
|
17349
17518
|
const transaction = createMutationFs(root, operation === "pull");
|
|
@@ -17361,7 +17530,7 @@ async function runRemoteOperation(root, operation, getSession, guard, signal) {
|
|
|
17361
17530
|
const trackingRemote = await config("branch." + ref + ".remote");
|
|
17362
17531
|
const trackingRef = await config("branch." + ref + ".merge");
|
|
17363
17532
|
if (trackingRemote && trackingRemote !== "origin" || trackingRef && !trackingRef.startsWith("refs/heads/")) throw new GitPanelError("unsupported");
|
|
17364
|
-
|
|
17533
|
+
await checkRefspecs(options);
|
|
17365
17534
|
if (operation === "pull") {
|
|
17366
17535
|
if ((await statusMatrix(_objectSpread2(_objectSpread2({}, options), {}, { refresh: false }))).some(([, head, worktree, stage]) => head !== worktree || head !== stage)) throw new GitPanelError("pullUnsafe");
|
|
17367
17536
|
await walk(_objectSpread2(_objectSpread2({}, options), {}, {
|
|
@@ -17435,10 +17604,15 @@ async function runRemoteOperation(root, operation, getSession, guard, signal) {
|
|
|
17435
17604
|
throw error;
|
|
17436
17605
|
}
|
|
17437
17606
|
};
|
|
17438
|
-
return ((_globalThis$
|
|
17607
|
+
return ((_globalThis$navigator2 = globalThis.navigator) === null || _globalThis$navigator2 === void 0 ? void 0 : _globalThis$navigator2.locks) ? navigator.locks.request("tw-plus-git-mutation", execute) : execute();
|
|
17439
17608
|
}
|
|
17440
17609
|
// src/local-operations.ts
|
|
17441
17610
|
init_objectSpread2();
|
|
17611
|
+
/**
|
|
17612
|
+
* The local branch a pull request is opened on: `pr/<number>`, a name `git check-ref-format --branch` accepts. It is
|
|
17613
|
+
* created at the fetched head (`pullRequestRef(number)`) and is an ordinary branch afterwards.
|
|
17614
|
+
*/
|
|
17615
|
+
var pullRequestBranch = (number) => `pr/${number}`;
|
|
17442
17616
|
var missing = (error) => {
|
|
17443
17617
|
var _errorCode;
|
|
17444
17618
|
return [
|
|
@@ -17490,6 +17664,7 @@ async function runLocalOperation(root, operation, guard) {
|
|
|
17490
17664
|
}
|
|
17491
17665
|
if (operation.kind === "createBranch") {
|
|
17492
17666
|
let object;
|
|
17667
|
+
if (operation.reuse && operation.at === void 0) throw new GitPanelError("invalid");
|
|
17493
17668
|
if (operation.at !== void 0) {
|
|
17494
17669
|
if (operation.fromRemote || !/^[0-9a-f]{40}$/.test(operation.at)) throw new GitPanelError("invalid");
|
|
17495
17670
|
object = await readCommit(_objectSpread2(_objectSpread2({}, options), {}, { oid: operation.at })).then(() => operation.at, () => {
|
|
@@ -17503,7 +17678,15 @@ async function runLocalOperation(root, operation, guard) {
|
|
|
17503
17678
|
else object = await resolveRef(_objectSpread2(_objectSpread2({}, options), {}, { ref: "HEAD" })).catch(() => {
|
|
17504
17679
|
throw new GitPanelError("unsupported");
|
|
17505
17680
|
});
|
|
17506
|
-
|
|
17681
|
+
let existing;
|
|
17682
|
+
if (operation.reuse && (await listBranches(options)).includes(name)) {
|
|
17683
|
+
existing = await resolveRef(_objectSpread2(_objectSpread2({}, options), {}, { ref: local }));
|
|
17684
|
+
if (existing !== object) throw new GitPanelError("pullRequestBranch");
|
|
17685
|
+
}
|
|
17686
|
+
if (existing === void 0) {
|
|
17687
|
+
if ((await listBranches(options)).some((branch) => branch !== name && (name.startsWith(`${branch}/`) || branch.startsWith(`${name}/`)))) throw new GitPanelError("branchConflict");
|
|
17688
|
+
}
|
|
17689
|
+
if (existing === void 0) try {
|
|
17507
17690
|
await branch(_objectSpread2(_objectSpread2({}, options), {}, {
|
|
17508
17691
|
ref: name,
|
|
17509
17692
|
object,
|
|
@@ -17583,6 +17766,8 @@ init_objectSpread2();
|
|
|
17583
17766
|
var historyPageSize = 50;
|
|
17584
17767
|
var projectSourceLimit = 16777216;
|
|
17585
17768
|
var emptyTree = "4b825dc642cb6eb9a060e54bf8d69288fbee4904";
|
|
17769
|
+
var isSha = (value) => typeof value === "string" && /^[0-9a-f]{40}$/u.test(value);
|
|
17770
|
+
var validBranch = (name) => /^[^\s~^:?*[\\]+$/u.test(name) && !name.startsWith("/") && !name.endsWith("/") && !name.endsWith(".lock") && !name.includes("..") && !name.includes("//") && !name.includes("@{");
|
|
17586
17771
|
var textLimit = 1048576;
|
|
17587
17772
|
var inScope = (path, scope) => !scope || path === scope || path.startsWith(scope + "/");
|
|
17588
17773
|
var probeLimit = 5e3;
|
|
@@ -17829,12 +18014,7 @@ async function openRepository(root) {
|
|
|
17829
18014
|
const assertSnapshot = async (snapshot) => {
|
|
17830
18015
|
if (!sameMetadata(snapshot.metadata, await metadata())) throw new GitPanelError("changed");
|
|
17831
18016
|
};
|
|
17832
|
-
const
|
|
17833
|
-
scope = pathParts(scope).join("/");
|
|
17834
|
-
fs.clearReads();
|
|
17835
|
-
cache = {};
|
|
17836
|
-
const fingerprint = await probe(scope);
|
|
17837
|
-
const before = await metadata();
|
|
18017
|
+
const readRefs = async () => {
|
|
17838
18018
|
let head;
|
|
17839
18019
|
try {
|
|
17840
18020
|
head = await resolveRef(_objectSpread2(_objectSpread2({}, options), {}, { ref: "HEAD" }));
|
|
@@ -17845,6 +18025,20 @@ async function openRepository(root) {
|
|
|
17845
18025
|
const branch = await currentBranch(options);
|
|
17846
18026
|
const branches = await listBranches(options);
|
|
17847
18027
|
const remoteBranches = (await listBranches(_objectSpread2(_objectSpread2({}, options), {}, { remote: "origin" }))).filter((name) => name !== "HEAD" && !branches.includes(name)).sort();
|
|
18028
|
+
return {
|
|
18029
|
+
branch: branch || void 0,
|
|
18030
|
+
head,
|
|
18031
|
+
branches,
|
|
18032
|
+
remoteBranches
|
|
18033
|
+
};
|
|
18034
|
+
};
|
|
18035
|
+
const read = async (scope = "") => {
|
|
18036
|
+
scope = pathParts(scope).join("/");
|
|
18037
|
+
fs.clearReads();
|
|
18038
|
+
cache = {};
|
|
18039
|
+
const fingerprint = await probe(scope);
|
|
18040
|
+
const before = await metadata();
|
|
18041
|
+
const { branch, head, branches, remoteBranches } = await readRefs();
|
|
17848
18042
|
const commits = head ? (await log(_objectSpread2(_objectSpread2({}, options), {}, {
|
|
17849
18043
|
cache,
|
|
17850
18044
|
ref: head,
|
|
@@ -17897,7 +18091,7 @@ async function openRepository(root) {
|
|
|
17897
18091
|
if (!sameMetadata(before, await metadata())) throw new GitPanelError("changed");
|
|
17898
18092
|
await fs.checkReads();
|
|
17899
18093
|
return {
|
|
17900
|
-
branch
|
|
18094
|
+
branch,
|
|
17901
18095
|
head,
|
|
17902
18096
|
branches,
|
|
17903
18097
|
remoteBranches,
|
|
@@ -18258,6 +18452,146 @@ async function openRepository(root) {
|
|
|
18258
18452
|
}
|
|
18259
18453
|
};
|
|
18260
18454
|
};
|
|
18455
|
+
const hasCommit = async (oid) => {
|
|
18456
|
+
try {
|
|
18457
|
+
await readCommit(_objectSpread2(_objectSpread2({}, options), {}, {
|
|
18458
|
+
cache,
|
|
18459
|
+
oid
|
|
18460
|
+
}));
|
|
18461
|
+
return true;
|
|
18462
|
+
} catch (_unused5) {
|
|
18463
|
+
return false;
|
|
18464
|
+
}
|
|
18465
|
+
};
|
|
18466
|
+
/** Lists the paths that differ between two trees, skipping subtrees with the same object. */
|
|
18467
|
+
const treeChanges = async (before, after) => {
|
|
18468
|
+
const file = async (entry) => entry && await entry.type() !== "tree" ? {
|
|
18469
|
+
oid: await entry.oid(),
|
|
18470
|
+
mode: await entry.mode()
|
|
18471
|
+
} : null;
|
|
18472
|
+
return (await walk(_objectSpread2(_objectSpread2({}, options), {}, {
|
|
18473
|
+
cache,
|
|
18474
|
+
trees: [TREE({ ref: before }), TREE({ ref: after })],
|
|
18475
|
+
map: async (path, [a, b]) => {
|
|
18476
|
+
if (path === ".") return;
|
|
18477
|
+
if (a && b && await a.oid() === await b.oid() && await a.mode() === await b.mode()) return null;
|
|
18478
|
+
const change = {
|
|
18479
|
+
path,
|
|
18480
|
+
before: await file(a),
|
|
18481
|
+
after: await file(b)
|
|
18482
|
+
};
|
|
18483
|
+
return change.before || change.after ? change : void 0;
|
|
18484
|
+
}
|
|
18485
|
+
}))).sort((left, right) => left.path < right.path ? -1 : left.path > right.path ? 1 : 0);
|
|
18486
|
+
};
|
|
18487
|
+
const readBlobText = async (entry) => {
|
|
18488
|
+
if (!entry) return "{\"targets\":[]}\n";
|
|
18489
|
+
if (entry.mode !== 33188 && entry.mode !== 33261) throw new GitPanelError("unsupported");
|
|
18490
|
+
const { blob } = await readBlob(_objectSpread2(_objectSpread2({}, options), {}, {
|
|
18491
|
+
cache,
|
|
18492
|
+
oid: entry.oid
|
|
18493
|
+
}));
|
|
18494
|
+
if (blob.byteLength > projectSourceLimit) throw new GitPanelError("large");
|
|
18495
|
+
return new TextDecoder("utf-8", { fatal: true }).decode(blob);
|
|
18496
|
+
};
|
|
18497
|
+
const readTreeAsset = async (ref, folder, md5ext, signal) => {
|
|
18498
|
+
if (signal.aborted) return null;
|
|
18499
|
+
const source = {
|
|
18500
|
+
kind: "tree",
|
|
18501
|
+
ref
|
|
18502
|
+
};
|
|
18503
|
+
const settings = await readVersion(source, [...folder, "sb3-source.json"].join("/"), folderSettingsLimit);
|
|
18504
|
+
const directory = assetsDirectoryParts(settings && new TextDecoder().decode(settings));
|
|
18505
|
+
if (!directory || signal.aborted) return null;
|
|
18506
|
+
const bytes = await readVersion(source, [
|
|
18507
|
+
...folder,
|
|
18508
|
+
...directory,
|
|
18509
|
+
md5ext
|
|
18510
|
+
].join("/"), assetLimit);
|
|
18511
|
+
return bytes && !signal.aborted ? new Blob([bytes]) : null;
|
|
18512
|
+
};
|
|
18513
|
+
const pullRequestAssets = (versions) => ({ version, md5ext, signal }) => {
|
|
18514
|
+
const location = versions[version];
|
|
18515
|
+
if (!location || !isAssetName(md5ext) || signal.aborted) return Promise.resolve(null);
|
|
18516
|
+
return serial(() => readTreeAsset(location.ref, pathParts(location.path).slice(0, -1), md5ext, signal)).catch(() => null);
|
|
18517
|
+
};
|
|
18518
|
+
/** Compares a fetched pull request head with its merge base, from local Git objects only. */
|
|
18519
|
+
const readPullRequest = async (head, base) => {
|
|
18520
|
+
var _added$get, _base$branch;
|
|
18521
|
+
if (!isSha(head) || !await hasCommit(head)) throw new GitPanelError("pullRequestNotFound");
|
|
18522
|
+
let sha;
|
|
18523
|
+
if (isSha(base.sha) && await hasCommit(base.sha)) sha = base.sha;
|
|
18524
|
+
else if (base.branch !== void 0 && validBranch(base.branch)) try {
|
|
18525
|
+
sha = await resolveRef(_objectSpread2(_objectSpread2({}, options), {}, { ref: "refs/remotes/origin/" + base.branch }));
|
|
18526
|
+
} catch (_unused6) {}
|
|
18527
|
+
if (!sha || !await hasCommit(sha)) throw new GitPanelError("baseMissing");
|
|
18528
|
+
const [mergeBase = null] = await findMergeBase(_objectSpread2(_objectSpread2({}, options), {}, {
|
|
18529
|
+
cache,
|
|
18530
|
+
oids: [sha, head]
|
|
18531
|
+
}));
|
|
18532
|
+
const changes = await treeChanges(mergeBase !== null && mergeBase !== void 0 ? mergeBase : emptyTree, head);
|
|
18533
|
+
const added = /* @__PURE__ */ new Map();
|
|
18534
|
+
for (const change of changes) if (!change.before && change.after) added.set(change.after.oid, [...(_added$get = added.get(change.after.oid)) !== null && _added$get !== void 0 ? _added$get : [], change]);
|
|
18535
|
+
const renamed = /* @__PURE__ */ new Map();
|
|
18536
|
+
for (const change of changes) {
|
|
18537
|
+
const candidates = change.before && !change.after ? added.get(change.before.oid) : void 0;
|
|
18538
|
+
const target = candidates === null || candidates === void 0 ? void 0 : candidates.find((candidate) => !renamed.has(candidate));
|
|
18539
|
+
if (target) renamed.set(target, change);
|
|
18540
|
+
}
|
|
18541
|
+
const removedByRename = new Set(renamed.values());
|
|
18542
|
+
const files = [];
|
|
18543
|
+
for (const change of changes) {
|
|
18544
|
+
var _from$before;
|
|
18545
|
+
if (removedByRename.has(change)) continue;
|
|
18546
|
+
const from = renamed.get(change);
|
|
18547
|
+
const before = (_from$before = from === null || from === void 0 ? void 0 : from.before) !== null && _from$before !== void 0 ? _from$before : change.before;
|
|
18548
|
+
const previousPath = from === null || from === void 0 ? void 0 : from.path;
|
|
18549
|
+
const status = !before ? "added" : !change.after ? "removed" : "updated";
|
|
18550
|
+
if (change.path.split("/").pop() !== "project.source.json") {
|
|
18551
|
+
files.push({
|
|
18552
|
+
path: change.path,
|
|
18553
|
+
previousPath,
|
|
18554
|
+
status
|
|
18555
|
+
});
|
|
18556
|
+
continue;
|
|
18557
|
+
}
|
|
18558
|
+
try {
|
|
18559
|
+
const patch = createProjectSourceDiff(await readBlobText(before), await readBlobText(change.after));
|
|
18560
|
+
const loadAsset = pullRequestAssets({
|
|
18561
|
+
before: before && mergeBase ? {
|
|
18562
|
+
ref: mergeBase,
|
|
18563
|
+
path: previousPath !== null && previousPath !== void 0 ? previousPath : change.path
|
|
18564
|
+
} : null,
|
|
18565
|
+
after: change.after ? {
|
|
18566
|
+
ref: head,
|
|
18567
|
+
path: change.path
|
|
18568
|
+
} : null
|
|
18569
|
+
});
|
|
18570
|
+
files.push({
|
|
18571
|
+
path: change.path,
|
|
18572
|
+
previousPath,
|
|
18573
|
+
patch,
|
|
18574
|
+
loadAsset
|
|
18575
|
+
});
|
|
18576
|
+
} catch (_unused7) {
|
|
18577
|
+
files.push({
|
|
18578
|
+
path: change.path,
|
|
18579
|
+
previousPath,
|
|
18580
|
+
status,
|
|
18581
|
+
unavailable: true
|
|
18582
|
+
});
|
|
18583
|
+
}
|
|
18584
|
+
}
|
|
18585
|
+
return {
|
|
18586
|
+
base: {
|
|
18587
|
+
ref: (_base$branch = base.branch) !== null && _base$branch !== void 0 ? _base$branch : null,
|
|
18588
|
+
sha
|
|
18589
|
+
},
|
|
18590
|
+
head,
|
|
18591
|
+
mergeBase,
|
|
18592
|
+
files
|
|
18593
|
+
};
|
|
18594
|
+
};
|
|
18261
18595
|
const directoryAt = async (path) => {
|
|
18262
18596
|
let directory = root;
|
|
18263
18597
|
for (const part of pathParts(path)) directory = await directory.getDirectoryHandle(part);
|
|
@@ -18335,6 +18669,20 @@ async function openRepository(root) {
|
|
|
18335
18669
|
throw error;
|
|
18336
18670
|
}
|
|
18337
18671
|
}),
|
|
18672
|
+
/**
|
|
18673
|
+
* Fetches a pull request's head (`refs/pull/<number>/head`, which also covers pull requests from forks) and every
|
|
18674
|
+
* branch over the connection; see `runPullRequestFetch`. Only Git objects and refs are written.
|
|
18675
|
+
*/
|
|
18676
|
+
fetchPullRequest: (number, getSession, guard, signal) => serial(async () => {
|
|
18677
|
+
const result = await runPullRequestFetch(root, number, getSession, guard, signal);
|
|
18678
|
+
cache = {};
|
|
18679
|
+
return result;
|
|
18680
|
+
}),
|
|
18681
|
+
/**
|
|
18682
|
+
* Reads the changes of a fetched pull request head against a base, from local Git objects: the changed paths
|
|
18683
|
+
* from the merge base to the head, and each project file's diff with assets read from the same commits.
|
|
18684
|
+
*/
|
|
18685
|
+
readPullRequest: (head, base) => serial(() => readPullRequest(head, base)),
|
|
18338
18686
|
/** Stages, unstages or commits; see `runLocalOperation`. `guard` throws to stop before each write. */
|
|
18339
18687
|
localOperation: (operation, guard) => serial(() => runLocalOperation(root, operation, guard)),
|
|
18340
18688
|
/**
|
|
@@ -18350,11 +18698,19 @@ async function openRepository(root) {
|
|
|
18350
18698
|
filepath: path
|
|
18351
18699
|
}));
|
|
18352
18700
|
return tree.length > 0;
|
|
18353
|
-
} catch (
|
|
18701
|
+
} catch (_unused8) {
|
|
18354
18702
|
return false;
|
|
18355
18703
|
}
|
|
18356
18704
|
}),
|
|
18357
18705
|
read: (scope) => serial(() => read(scope)),
|
|
18706
|
+
/**
|
|
18707
|
+
* The current branch, HEAD and the branch names, read from refs only. Cheap enough to follow branch changes made
|
|
18708
|
+
* by other tools while nothing else is read; `read` returns the same fields.
|
|
18709
|
+
*/
|
|
18710
|
+
refs: () => serial(() => {
|
|
18711
|
+
fs.clearReads();
|
|
18712
|
+
return readRefs();
|
|
18713
|
+
}),
|
|
18358
18714
|
readDiff: (snapshot, change) => serial(() => readDiff(snapshot, change)),
|
|
18359
18715
|
historyChanges: (snapshot, oid) => serial(() => historyChanges(snapshot, oid)),
|
|
18360
18716
|
/**
|
|
@@ -18378,4 +18734,4 @@ async function openRepository(root) {
|
|
|
18378
18734
|
projectAssetLoader: (snapshot, folder, comparison) => ({ version, md5ext, signal }) => !isAssetName(md5ext) || signal.aborted ? Promise.resolve(null) : serial(() => readFolderAsset(snapshot, pathParts(folder), () => comparisonSource(snapshot, comparison, version), md5ext, signal)).catch(() => null)
|
|
18379
18735
|
};
|
|
18380
18736
|
}
|
|
18381
|
-
export {
|
|
18737
|
+
export { pullRequestRef as a, pullRequestBranch as i, openRepository as n, GitPanelError as o, upstreamWalkLimit as r, createReadOnlyFs as s, historyPageSize as t };
|
package/dist/repository.d.ts
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
|
+
import type { ProjectSourceReviewFile } from './project-source-review.js';
|
|
1
2
|
import type { LocalOperation } from './local-operations.js';
|
|
2
3
|
import type { GetRemoteSession, RemoteOperation } from './remote-operations.js';
|
|
3
4
|
import type { ProjectAssetLoader } from './project-assets.js';
|
|
4
5
|
export { createReadOnlyFs, GitPanelError } from './filesystem.js';
|
|
6
|
+
export { pullRequestRef } from './remote-operations.js';
|
|
7
|
+
export type { PullRequestFetch } from './remote-operations.js';
|
|
5
8
|
export type ChangeGroup = 'staged' | 'unstaged' | 'untracked' | 'commit';
|
|
6
9
|
export interface FileChange {
|
|
7
10
|
path: string;
|
|
@@ -14,6 +17,25 @@ export interface FileChange {
|
|
|
14
17
|
/** For `commit` changes, the commit compared with its first parent. */
|
|
15
18
|
commit?: string;
|
|
16
19
|
}
|
|
20
|
+
/** A version of the pull request's base to compare with: GitHub's base commit when known, else a branch on origin. */
|
|
21
|
+
export interface PullRequestBase {
|
|
22
|
+
/** The base commit GitHub reported, used when the fetch brought it. */
|
|
23
|
+
sha?: string;
|
|
24
|
+
/** The base branch, read from `refs/remotes/origin/<branch>` when `sha` is missing or not local. */
|
|
25
|
+
branch?: string;
|
|
26
|
+
}
|
|
27
|
+
export interface PullRequestFiles {
|
|
28
|
+
/** The base that was compared: the branch it was named by, if any, and its commit. */
|
|
29
|
+
base: {
|
|
30
|
+
ref: string | null;
|
|
31
|
+
sha: string;
|
|
32
|
+
};
|
|
33
|
+
head: string;
|
|
34
|
+
/** The merge base of `base.sha` and `head`, like GitHub's three-dot comparison; `null` for unrelated histories. */
|
|
35
|
+
mergeBase: string | null;
|
|
36
|
+
/** Every changed path from the merge base to the head, with project data as a project-source diff. */
|
|
37
|
+
files: ProjectSourceReviewFile[];
|
|
38
|
+
}
|
|
17
39
|
export interface Commit {
|
|
18
40
|
oid: string;
|
|
19
41
|
message: string;
|
|
@@ -60,6 +82,8 @@ export interface HistoryPage {
|
|
|
60
82
|
/** Commits read per history page unless asked otherwise. */
|
|
61
83
|
export declare const historyPageSize = 50;
|
|
62
84
|
type Metadata = Map<string, Uint8Array | null>;
|
|
85
|
+
/** The branch fields of a snapshot. */
|
|
86
|
+
export type BranchRefs = Pick<Snapshot, 'branch' | 'head' | 'branches' | 'remoteBranches'>;
|
|
63
87
|
export interface Snapshot {
|
|
64
88
|
branch: string | undefined;
|
|
65
89
|
head: string | null;
|
|
@@ -116,6 +140,16 @@ export declare function openRepository(root: FileSystemDirectoryHandle): Promise
|
|
|
116
140
|
probe: (scope?: string) => Promise<string>;
|
|
117
141
|
readOrigin: () => Promise<any>;
|
|
118
142
|
remoteOperation: (operation: RemoteOperation, getSession: GetRemoteSession, guard: () => void, signal?: AbortSignal) => Promise<void>;
|
|
143
|
+
/**
|
|
144
|
+
* Fetches a pull request's head (`refs/pull/<number>/head`, which also covers pull requests from forks) and every
|
|
145
|
+
* branch over the connection; see `runPullRequestFetch`. Only Git objects and refs are written.
|
|
146
|
+
*/
|
|
147
|
+
fetchPullRequest: (number: number, getSession: GetRemoteSession, guard: () => void, signal?: AbortSignal) => Promise<import("./remote-operations.js").PullRequestFetch>;
|
|
148
|
+
/**
|
|
149
|
+
* Reads the changes of a fetched pull request head against a base, from local Git objects: the changed paths
|
|
150
|
+
* from the merge base to the head, and each project file's diff with assets read from the same commits.
|
|
151
|
+
*/
|
|
152
|
+
readPullRequest: (head: string, base: PullRequestBase) => Promise<PullRequestFiles>;
|
|
119
153
|
/** Stages, unstages or commits; see `runLocalOperation`. `guard` throws to stop before each write. */
|
|
120
154
|
localOperation: (operation: LocalOperation, guard: () => void) => Promise<void>;
|
|
121
155
|
/**
|
|
@@ -124,6 +158,11 @@ export declare function openRepository(root: FileSystemDirectoryHandle): Promise
|
|
|
124
158
|
*/
|
|
125
159
|
hasFolder: (name: string, path: string, remote?: boolean | "commit") => Promise<boolean>;
|
|
126
160
|
read: (scope?: string) => Promise<Snapshot>;
|
|
161
|
+
/**
|
|
162
|
+
* The current branch, HEAD and the branch names, read from refs only. Cheap enough to follow branch changes made
|
|
163
|
+
* by other tools while nothing else is read; `read` returns the same fields.
|
|
164
|
+
*/
|
|
165
|
+
refs: () => Promise<BranchRefs>;
|
|
127
166
|
readDiff: (snapshot: Snapshot, change: FileChange) => Promise<FileDiff>;
|
|
128
167
|
historyChanges: (snapshot: Snapshot, oid: string) => Promise<FileChange[]>;
|
|
129
168
|
/**
|
package/dist/repository.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { o as GitPanelError, s as createReadOnlyFs, t as historyPageSize, n as openRepository, a as pullRequestRef, r as upstreamWalkLimit } from "./repository-ByKRBftJ.js";
|