@kubohiroya/turbowarp-plus-editor-git 0.16.1 → 0.17.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 +14 -0
- package/README.md +14 -0
- package/dist/filesystem.d.ts +2 -2
- package/dist/github-pull-requests.d.ts +20 -0
- package/dist/index.d.ts +3 -2
- package/dist/index.js +177 -14
- package/dist/local-operations.d.ts +8 -1
- package/dist/messages.d.ts +8 -0
- package/dist/{project-asset-paths-KV715791.js → project-asset-paths-nL5sV35A.js} +653 -1
- package/dist/{project-comparison-1rggIJse.js → project-comparison-DjMlwyp8.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-xIv0iZkj.js} +346 -7
- package/dist/repository.d.ts +32 -0
- package/dist/repository.js +1 -1
- package/package.json +1 -1
- package/src/filesystem.ts +2 -1
- package/src/github-pull-requests.ts +7 -0
- package/src/index.tsx +31 -3
- package/src/local-operations.ts +23 -3
- package/src/messages.ts +8 -0
- package/src/pull-request-panel.tsx +125 -14
- package/src/remote-operations.ts +84 -3
- package/src/repository.ts +133 -1
- 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-nL5sV35A.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-DjMlwyp8.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 { f as createTwoFilesPatch, i as isAssetName, n as assetsDirectoryParts, o as createProjectSourceDiff, r as folderSettingsLimit, t as assetLimit } from "./project-asset-paths-nL5sV35A.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;
|
|
@@ -18258,6 +18443,146 @@ async function openRepository(root) {
|
|
|
18258
18443
|
}
|
|
18259
18444
|
};
|
|
18260
18445
|
};
|
|
18446
|
+
const hasCommit = async (oid) => {
|
|
18447
|
+
try {
|
|
18448
|
+
await readCommit(_objectSpread2(_objectSpread2({}, options), {}, {
|
|
18449
|
+
cache,
|
|
18450
|
+
oid
|
|
18451
|
+
}));
|
|
18452
|
+
return true;
|
|
18453
|
+
} catch (_unused5) {
|
|
18454
|
+
return false;
|
|
18455
|
+
}
|
|
18456
|
+
};
|
|
18457
|
+
/** Lists the paths that differ between two trees, skipping subtrees with the same object. */
|
|
18458
|
+
const treeChanges = async (before, after) => {
|
|
18459
|
+
const file = async (entry) => entry && await entry.type() !== "tree" ? {
|
|
18460
|
+
oid: await entry.oid(),
|
|
18461
|
+
mode: await entry.mode()
|
|
18462
|
+
} : null;
|
|
18463
|
+
return (await walk(_objectSpread2(_objectSpread2({}, options), {}, {
|
|
18464
|
+
cache,
|
|
18465
|
+
trees: [TREE({ ref: before }), TREE({ ref: after })],
|
|
18466
|
+
map: async (path, [a, b]) => {
|
|
18467
|
+
if (path === ".") return;
|
|
18468
|
+
if (a && b && await a.oid() === await b.oid() && await a.mode() === await b.mode()) return null;
|
|
18469
|
+
const change = {
|
|
18470
|
+
path,
|
|
18471
|
+
before: await file(a),
|
|
18472
|
+
after: await file(b)
|
|
18473
|
+
};
|
|
18474
|
+
return change.before || change.after ? change : void 0;
|
|
18475
|
+
}
|
|
18476
|
+
}))).sort((left, right) => left.path < right.path ? -1 : left.path > right.path ? 1 : 0);
|
|
18477
|
+
};
|
|
18478
|
+
const readBlobText = async (entry) => {
|
|
18479
|
+
if (!entry) return "{\"targets\":[]}\n";
|
|
18480
|
+
if (entry.mode !== 33188 && entry.mode !== 33261) throw new GitPanelError("unsupported");
|
|
18481
|
+
const { blob } = await readBlob(_objectSpread2(_objectSpread2({}, options), {}, {
|
|
18482
|
+
cache,
|
|
18483
|
+
oid: entry.oid
|
|
18484
|
+
}));
|
|
18485
|
+
if (blob.byteLength > projectSourceLimit) throw new GitPanelError("large");
|
|
18486
|
+
return new TextDecoder("utf-8", { fatal: true }).decode(blob);
|
|
18487
|
+
};
|
|
18488
|
+
const readTreeAsset = async (ref, folder, md5ext, signal) => {
|
|
18489
|
+
if (signal.aborted) return null;
|
|
18490
|
+
const source = {
|
|
18491
|
+
kind: "tree",
|
|
18492
|
+
ref
|
|
18493
|
+
};
|
|
18494
|
+
const settings = await readVersion(source, [...folder, "sb3-source.json"].join("/"), folderSettingsLimit);
|
|
18495
|
+
const directory = assetsDirectoryParts(settings && new TextDecoder().decode(settings));
|
|
18496
|
+
if (!directory || signal.aborted) return null;
|
|
18497
|
+
const bytes = await readVersion(source, [
|
|
18498
|
+
...folder,
|
|
18499
|
+
...directory,
|
|
18500
|
+
md5ext
|
|
18501
|
+
].join("/"), assetLimit);
|
|
18502
|
+
return bytes && !signal.aborted ? new Blob([bytes]) : null;
|
|
18503
|
+
};
|
|
18504
|
+
const pullRequestAssets = (versions) => ({ version, md5ext, signal }) => {
|
|
18505
|
+
const location = versions[version];
|
|
18506
|
+
if (!location || !isAssetName(md5ext) || signal.aborted) return Promise.resolve(null);
|
|
18507
|
+
return serial(() => readTreeAsset(location.ref, pathParts(location.path).slice(0, -1), md5ext, signal)).catch(() => null);
|
|
18508
|
+
};
|
|
18509
|
+
/** Compares a fetched pull request head with its merge base, from local Git objects only. */
|
|
18510
|
+
const readPullRequest = async (head, base) => {
|
|
18511
|
+
var _added$get, _base$branch;
|
|
18512
|
+
if (!isSha(head) || !await hasCommit(head)) throw new GitPanelError("pullRequestNotFound");
|
|
18513
|
+
let sha;
|
|
18514
|
+
if (isSha(base.sha) && await hasCommit(base.sha)) sha = base.sha;
|
|
18515
|
+
else if (base.branch !== void 0 && validBranch(base.branch)) try {
|
|
18516
|
+
sha = await resolveRef(_objectSpread2(_objectSpread2({}, options), {}, { ref: "refs/remotes/origin/" + base.branch }));
|
|
18517
|
+
} catch (_unused6) {}
|
|
18518
|
+
if (!sha || !await hasCommit(sha)) throw new GitPanelError("baseMissing");
|
|
18519
|
+
const [mergeBase = null] = await findMergeBase(_objectSpread2(_objectSpread2({}, options), {}, {
|
|
18520
|
+
cache,
|
|
18521
|
+
oids: [sha, head]
|
|
18522
|
+
}));
|
|
18523
|
+
const changes = await treeChanges(mergeBase !== null && mergeBase !== void 0 ? mergeBase : emptyTree, head);
|
|
18524
|
+
const added = /* @__PURE__ */ new Map();
|
|
18525
|
+
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]);
|
|
18526
|
+
const renamed = /* @__PURE__ */ new Map();
|
|
18527
|
+
for (const change of changes) {
|
|
18528
|
+
const candidates = change.before && !change.after ? added.get(change.before.oid) : void 0;
|
|
18529
|
+
const target = candidates === null || candidates === void 0 ? void 0 : candidates.find((candidate) => !renamed.has(candidate));
|
|
18530
|
+
if (target) renamed.set(target, change);
|
|
18531
|
+
}
|
|
18532
|
+
const removedByRename = new Set(renamed.values());
|
|
18533
|
+
const files = [];
|
|
18534
|
+
for (const change of changes) {
|
|
18535
|
+
var _from$before;
|
|
18536
|
+
if (removedByRename.has(change)) continue;
|
|
18537
|
+
const from = renamed.get(change);
|
|
18538
|
+
const before = (_from$before = from === null || from === void 0 ? void 0 : from.before) !== null && _from$before !== void 0 ? _from$before : change.before;
|
|
18539
|
+
const previousPath = from === null || from === void 0 ? void 0 : from.path;
|
|
18540
|
+
const status = !before ? "added" : !change.after ? "removed" : "updated";
|
|
18541
|
+
if (change.path.split("/").pop() !== "project.source.json") {
|
|
18542
|
+
files.push({
|
|
18543
|
+
path: change.path,
|
|
18544
|
+
previousPath,
|
|
18545
|
+
status
|
|
18546
|
+
});
|
|
18547
|
+
continue;
|
|
18548
|
+
}
|
|
18549
|
+
try {
|
|
18550
|
+
const patch = createProjectSourceDiff(await readBlobText(before), await readBlobText(change.after));
|
|
18551
|
+
const loadAsset = pullRequestAssets({
|
|
18552
|
+
before: before && mergeBase ? {
|
|
18553
|
+
ref: mergeBase,
|
|
18554
|
+
path: previousPath !== null && previousPath !== void 0 ? previousPath : change.path
|
|
18555
|
+
} : null,
|
|
18556
|
+
after: change.after ? {
|
|
18557
|
+
ref: head,
|
|
18558
|
+
path: change.path
|
|
18559
|
+
} : null
|
|
18560
|
+
});
|
|
18561
|
+
files.push({
|
|
18562
|
+
path: change.path,
|
|
18563
|
+
previousPath,
|
|
18564
|
+
patch,
|
|
18565
|
+
loadAsset
|
|
18566
|
+
});
|
|
18567
|
+
} catch (_unused7) {
|
|
18568
|
+
files.push({
|
|
18569
|
+
path: change.path,
|
|
18570
|
+
previousPath,
|
|
18571
|
+
status,
|
|
18572
|
+
unavailable: true
|
|
18573
|
+
});
|
|
18574
|
+
}
|
|
18575
|
+
}
|
|
18576
|
+
return {
|
|
18577
|
+
base: {
|
|
18578
|
+
ref: (_base$branch = base.branch) !== null && _base$branch !== void 0 ? _base$branch : null,
|
|
18579
|
+
sha
|
|
18580
|
+
},
|
|
18581
|
+
head,
|
|
18582
|
+
mergeBase,
|
|
18583
|
+
files
|
|
18584
|
+
};
|
|
18585
|
+
};
|
|
18261
18586
|
const directoryAt = async (path) => {
|
|
18262
18587
|
let directory = root;
|
|
18263
18588
|
for (const part of pathParts(path)) directory = await directory.getDirectoryHandle(part);
|
|
@@ -18335,6 +18660,20 @@ async function openRepository(root) {
|
|
|
18335
18660
|
throw error;
|
|
18336
18661
|
}
|
|
18337
18662
|
}),
|
|
18663
|
+
/**
|
|
18664
|
+
* Fetches a pull request's head (`refs/pull/<number>/head`, which also covers pull requests from forks) and every
|
|
18665
|
+
* branch over the connection; see `runPullRequestFetch`. Only Git objects and refs are written.
|
|
18666
|
+
*/
|
|
18667
|
+
fetchPullRequest: (number, getSession, guard, signal) => serial(async () => {
|
|
18668
|
+
const result = await runPullRequestFetch(root, number, getSession, guard, signal);
|
|
18669
|
+
cache = {};
|
|
18670
|
+
return result;
|
|
18671
|
+
}),
|
|
18672
|
+
/**
|
|
18673
|
+
* Reads the changes of a fetched pull request head against a base, from local Git objects: the changed paths
|
|
18674
|
+
* from the merge base to the head, and each project file's diff with assets read from the same commits.
|
|
18675
|
+
*/
|
|
18676
|
+
readPullRequest: (head, base) => serial(() => readPullRequest(head, base)),
|
|
18338
18677
|
/** Stages, unstages or commits; see `runLocalOperation`. `guard` throws to stop before each write. */
|
|
18339
18678
|
localOperation: (operation, guard) => serial(() => runLocalOperation(root, operation, guard)),
|
|
18340
18679
|
/**
|
|
@@ -18350,7 +18689,7 @@ async function openRepository(root) {
|
|
|
18350
18689
|
filepath: path
|
|
18351
18690
|
}));
|
|
18352
18691
|
return tree.length > 0;
|
|
18353
|
-
} catch (
|
|
18692
|
+
} catch (_unused8) {
|
|
18354
18693
|
return false;
|
|
18355
18694
|
}
|
|
18356
18695
|
}),
|
|
@@ -18378,4 +18717,4 @@ async function openRepository(root) {
|
|
|
18378
18717
|
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
18718
|
};
|
|
18380
18719
|
}
|
|
18381
|
-
export {
|
|
18720
|
+
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;
|
|
@@ -116,6 +138,16 @@ export declare function openRepository(root: FileSystemDirectoryHandle): Promise
|
|
|
116
138
|
probe: (scope?: string) => Promise<string>;
|
|
117
139
|
readOrigin: () => Promise<any>;
|
|
118
140
|
remoteOperation: (operation: RemoteOperation, getSession: GetRemoteSession, guard: () => void, signal?: AbortSignal) => Promise<void>;
|
|
141
|
+
/**
|
|
142
|
+
* Fetches a pull request's head (`refs/pull/<number>/head`, which also covers pull requests from forks) and every
|
|
143
|
+
* branch over the connection; see `runPullRequestFetch`. Only Git objects and refs are written.
|
|
144
|
+
*/
|
|
145
|
+
fetchPullRequest: (number: number, getSession: GetRemoteSession, guard: () => void, signal?: AbortSignal) => Promise<import("./remote-operations.js").PullRequestFetch>;
|
|
146
|
+
/**
|
|
147
|
+
* Reads the changes of a fetched pull request head against a base, from local Git objects: the changed paths
|
|
148
|
+
* from the merge base to the head, and each project file's diff with assets read from the same commits.
|
|
149
|
+
*/
|
|
150
|
+
readPullRequest: (head: string, base: PullRequestBase) => Promise<PullRequestFiles>;
|
|
119
151
|
/** Stages, unstages or commits; see `runLocalOperation`. `guard` throws to stop before each write. */
|
|
120
152
|
localOperation: (operation: LocalOperation, guard: () => void) => Promise<void>;
|
|
121
153
|
/**
|
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-xIv0iZkj.js";
|
package/package.json
CHANGED
package/src/filesystem.ts
CHANGED
|
@@ -2,7 +2,8 @@ export class GitPanelError extends Error {
|
|
|
2
2
|
constructor(public code: 'invalid' | 'changed' | 'large' | 'unsupported' | 'pullUnsafe' | 'diverged' |
|
|
3
3
|
'session' | 'pushUnknown' | 'pushRefresh' | 'rollbackFailed' | 'remoteFailed' |
|
|
4
4
|
'saveFirst' | 'author' | 'nothingStaged' | 'commitMessage' |
|
|
5
|
-
'switchUnsafe' | 'branchExists' | '
|
|
5
|
+
'switchUnsafe' | 'branchExists' | 'branchConflict' | 'pullRequestBranch' | 'branchName' | 'locked' |
|
|
6
|
+
'pullRequestNotFound' | 'forbidden' | 'network' | 'baseMissing', message: string = code) {
|
|
6
7
|
super(message);
|
|
7
8
|
this.name = 'GitPanelError';
|
|
8
9
|
}
|
|
@@ -165,6 +165,13 @@ export function createGitHubPullRequests(repository: string, send: typeof fetch
|
|
|
165
165
|
await request(repoPath(name) + '/contents/' + contentPath(path) + '?ref=' + revision, signal, 'application/vnd.github.raw+json'), sourceLimit);
|
|
166
166
|
|
|
167
167
|
return {
|
|
168
|
+
/** Reads one pull request's title, description, state and both revisions, without its files (one request). */
|
|
169
|
+
async metadata(number: number, signal: AbortSignal) {
|
|
170
|
+
if (!Number.isSafeInteger(number) || number < 1) throw invalidResponse();
|
|
171
|
+
const pr = await metadata(number, signal);
|
|
172
|
+
if (pr.number !== number) throw invalidResponse();
|
|
173
|
+
return {...pr, url: 'https://github.com/' + repository + '/pull/' + number};
|
|
174
|
+
},
|
|
168
175
|
async list(page: number, signal: AbortSignal) {
|
|
169
176
|
const result = await json(repo + '/pulls?state=all&sort=updated&per_page=20&page=' + page, signal);
|
|
170
177
|
if (!Array.isArray(result)) throw invalidResponse();
|