browser-git-ops 0.0.4 → 0.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +292 -67
- package/dist/git/abstractAdapter.d.ts +0 -16
- package/dist/git/abstractAdapter.d.ts.map +1 -1
- package/dist/git/githubAdapter.d.ts +2 -74
- package/dist/git/githubAdapter.d.ts.map +1 -1
- package/dist/git/gitlabAdapter.d.ts +3 -94
- package/dist/git/gitlabAdapter.d.ts.map +1 -1
- package/dist/index.js +658 -420
- package/dist/index.js.map +2 -2
- package/dist/index.mjs +658 -420
- package/dist/index.mjs.map +2 -2
- package/dist/virtualfs/indexedDatabaseStorage.d.ts.map +1 -1
- package/dist/virtualfs/inmemoryStorage.d.ts.map +1 -1
- package/dist/virtualfs/metadataManager.d.ts +0 -4
- package/dist/virtualfs/metadataManager.d.ts.map +1 -1
- package/dist/virtualfs/opfsStorage.d.ts.map +1 -1
- package/dist/virtualfs/remoteSynchronizer.d.ts +3 -3
- package/dist/virtualfs/storageBackend.d.ts +2 -2
- package/dist/virtualfs/storageBackend.d.ts.map +1 -1
- package/dist/virtualfs/virtualfs.d.ts +190 -27
- package/dist/virtualfs/virtualfs.d.ts.map +1 -1
- package/package.json +5 -4
package/dist/index.mjs
CHANGED
|
@@ -21,23 +21,28 @@ var OpfsStorage = class OpfsStorage2 {
|
|
|
21
21
|
ok = true;
|
|
22
22
|
return ok;
|
|
23
23
|
}
|
|
24
|
-
/** 利用可能なサブディレクトリ名の候補を返す
|
|
25
|
-
* @returns {Promise<string[]>} available root directories
|
|
26
|
-
*/
|
|
27
24
|
/**
|
|
28
25
|
* Return available root folder names for OPFS. This method is synchronous
|
|
29
26
|
* to satisfy the StorageBackendConstructor contract; it returns a cached
|
|
30
27
|
* hint if available and kicks off an async probe to populate the cache.
|
|
31
28
|
* If no information is available synchronously an empty array is returned.
|
|
32
|
-
|
|
29
|
+
* @returns {Promise<string[]>} available root directories
|
|
33
30
|
*/
|
|
34
|
-
static async availableRoots() {
|
|
31
|
+
static async availableRoots(namespace) {
|
|
35
32
|
try {
|
|
36
33
|
const root = await OpfsStorage2._getNavigatorStorageRoot();
|
|
37
34
|
if (!root)
|
|
38
35
|
return [];
|
|
39
|
-
|
|
40
|
-
|
|
36
|
+
for await (const handle of root.values()) {
|
|
37
|
+
const name = OpfsStorage2._extractHandleName(handle);
|
|
38
|
+
if (name === namespace && OpfsStorage2._isDirectoryHandle(handle)) {
|
|
39
|
+
return await OpfsStorage2._collectDirectoryNames(handle);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return [];
|
|
43
|
+
} catch (error) {
|
|
44
|
+
if (typeof console !== "undefined" && console.debug)
|
|
45
|
+
console.debug("availableRoots probe failed", error);
|
|
41
46
|
return [];
|
|
42
47
|
}
|
|
43
48
|
}
|
|
@@ -88,6 +93,7 @@ var OpfsStorage = class OpfsStorage2 {
|
|
|
88
93
|
}
|
|
89
94
|
rootDir = "apigit_storage";
|
|
90
95
|
currentBranch = null;
|
|
96
|
+
namespace = "";
|
|
91
97
|
/**
|
|
92
98
|
* Calculate SHA-1 hex digest of given content.
|
|
93
99
|
* @param content Input string
|
|
@@ -100,10 +106,21 @@ var OpfsStorage = class OpfsStorage2 {
|
|
|
100
106
|
const hashArray = Array.from(new Uint8Array(hashBuffer));
|
|
101
107
|
return hashArray.map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
102
108
|
}
|
|
103
|
-
/**
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
109
|
+
/**
|
|
110
|
+
* コンストラクタ(OPFS は初期化不要)。
|
|
111
|
+
* `namespace` は必須。挙動:
|
|
112
|
+
* - `new(namespace)` のみの場合は OPFS ルートとして `namespace` を使う(テストの期待値に合わせる)。
|
|
113
|
+
* - `new(namespace, root)` の場合は `namespace/root` を使う。
|
|
114
|
+
*/
|
|
115
|
+
constructor(namespace, root) {
|
|
116
|
+
this.namespace = namespace || "";
|
|
117
|
+
if (root) {
|
|
118
|
+
this.rootDir = this.namespace ? `${this.namespace}/${root}` : root;
|
|
119
|
+
} else if (this.namespace) {
|
|
120
|
+
this.rootDir = this.namespace;
|
|
121
|
+
} else {
|
|
122
|
+
this.rootDir = this.rootDir;
|
|
123
|
+
}
|
|
107
124
|
}
|
|
108
125
|
/**
|
|
109
126
|
* 初期化(OPFS はランタイム判定のみ)
|
|
@@ -131,7 +148,7 @@ var OpfsStorage = class OpfsStorage2 {
|
|
|
131
148
|
}
|
|
132
149
|
/**
|
|
133
150
|
* Map logical segment to concrete prefix used on OPFS.
|
|
134
|
-
|
|
151
|
+
* @returns {string} concrete prefix path for the given segment
|
|
135
152
|
*/
|
|
136
153
|
_segmentToPrefix(segment) {
|
|
137
154
|
if (segment === "workspace")
|
|
@@ -140,7 +157,6 @@ var OpfsStorage = class OpfsStorage2 {
|
|
|
140
157
|
const branch = this.currentBranch || "main";
|
|
141
158
|
return `.git/${branch}/${segName}`;
|
|
142
159
|
}
|
|
143
|
-
// legacy canUseOpfs removed; use static canUse() instead
|
|
144
160
|
/**
|
|
145
161
|
* Try to get OPFS root from navigator.storage.getDirectory().
|
|
146
162
|
* @returns {Promise<any|null>}
|
|
@@ -154,7 +170,9 @@ var OpfsStorage = class OpfsStorage2 {
|
|
|
154
170
|
const maybe = nav.storage.getDirectory();
|
|
155
171
|
const d = await Promise.resolve(maybe);
|
|
156
172
|
return d || null;
|
|
157
|
-
} catch {
|
|
173
|
+
} catch (error) {
|
|
174
|
+
if (typeof console !== "undefined" && console.debug)
|
|
175
|
+
console.debug("_tryNavigatorStorage failed", error);
|
|
158
176
|
return null;
|
|
159
177
|
}
|
|
160
178
|
}
|
|
@@ -169,7 +187,9 @@ var OpfsStorage = class OpfsStorage2 {
|
|
|
169
187
|
}
|
|
170
188
|
try {
|
|
171
189
|
return await opfs.getDirectory();
|
|
172
|
-
} catch {
|
|
190
|
+
} catch (error) {
|
|
191
|
+
if (typeof console !== "undefined" && console.debug)
|
|
192
|
+
console.debug("_tryOriginPrivateFileSystem failed", error);
|
|
173
193
|
return null;
|
|
174
194
|
}
|
|
175
195
|
}
|
|
@@ -200,10 +220,6 @@ var OpfsStorage = class OpfsStorage2 {
|
|
|
200
220
|
}
|
|
201
221
|
return directory;
|
|
202
222
|
}
|
|
203
|
-
/**
|
|
204
|
-
* index を読み出す
|
|
205
|
-
* @returns {Promise<IndexFile|null>} 読み出した IndexFile、存在しなければ null
|
|
206
|
-
*/
|
|
207
223
|
/**
|
|
208
224
|
* Read index metadata file from OPFS.
|
|
209
225
|
* @returns {Promise<string|null>}
|
|
@@ -220,7 +236,9 @@ var OpfsStorage = class OpfsStorage2 {
|
|
|
220
236
|
const fh = await root.getFileHandle("index");
|
|
221
237
|
const file = await fh.getFile();
|
|
222
238
|
return await file.text();
|
|
223
|
-
} catch {
|
|
239
|
+
} catch (error) {
|
|
240
|
+
if (typeof console !== "undefined" && console.debug)
|
|
241
|
+
console.debug("_readIndexMetadata failed", error);
|
|
224
242
|
return null;
|
|
225
243
|
}
|
|
226
244
|
}
|
|
@@ -579,8 +597,8 @@ var OpfsStorage = class OpfsStorage2 {
|
|
|
579
597
|
* @param root root directory handle
|
|
580
598
|
* @param prefix prefix dir
|
|
581
599
|
* @param filepath path relative to prefix
|
|
582
|
-
|
|
583
|
-
|
|
600
|
+
* @returns {Promise<string|null>} file contents or null when not found
|
|
601
|
+
*/
|
|
584
602
|
async readFromPrefix(root, prefix, filepath) {
|
|
585
603
|
try {
|
|
586
604
|
const fullPath = this.rootDir ? `${this.rootDir}/${prefix}/${filepath}` : `${prefix}/${filepath}`;
|
|
@@ -664,7 +682,6 @@ var OpfsStorage = class OpfsStorage2 {
|
|
|
664
682
|
await this._handleChildEntry(d, name, base, results);
|
|
665
683
|
}
|
|
666
684
|
}
|
|
667
|
-
/** Handle a single child entry name: try file first, then directory. */
|
|
668
685
|
/**
|
|
669
686
|
* @returns {Promise<void>}
|
|
670
687
|
*/
|
|
@@ -718,7 +735,7 @@ var OpfsStorage = class OpfsStorage2 {
|
|
|
718
735
|
* @param prefix プレフィックス(例: 'dir/sub')。省略時はルート
|
|
719
736
|
* @param segment セグメント('workspace' 等)。省略時は 'workspace'
|
|
720
737
|
* @param recursive サブディレクトリも含めるか。省略時は true
|
|
721
|
-
|
|
738
|
+
* @returns {Promise<Array<{ path: string; info: string | null }>>}
|
|
722
739
|
*/
|
|
723
740
|
async listFiles(prefix, segment, recursive = true) {
|
|
724
741
|
const root = await this.getOpfsRoot();
|
|
@@ -762,12 +779,9 @@ var OpfsStorage = class OpfsStorage2 {
|
|
|
762
779
|
*/
|
|
763
780
|
async _findStorageDirectory(navRoot) {
|
|
764
781
|
try {
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
return handle;
|
|
769
|
-
}
|
|
770
|
-
return null;
|
|
782
|
+
const parts = this.rootDir.split("/").filter(Boolean);
|
|
783
|
+
const directoryHandle = await this.traverseDir(navRoot, parts);
|
|
784
|
+
return directoryHandle;
|
|
771
785
|
} catch {
|
|
772
786
|
return null;
|
|
773
787
|
}
|
|
@@ -951,7 +965,6 @@ var AbstractGitAdapter = class {
|
|
|
951
965
|
setLogger(logger) {
|
|
952
966
|
this.logger = logger;
|
|
953
967
|
}
|
|
954
|
-
// Internal helpers to forward logs only when a logger is injected
|
|
955
968
|
/**
|
|
956
969
|
* Log debug messages when a logger is present.
|
|
957
970
|
* @param _messages messages to log (unused when no logger)
|
|
@@ -1000,14 +1013,6 @@ var AbstractGitAdapter = class {
|
|
|
1000
1013
|
}
|
|
1001
1014
|
}
|
|
1002
1015
|
}
|
|
1003
|
-
/**
|
|
1004
|
-
* Proxy to shared fetchWithRetry implementation
|
|
1005
|
-
* @param input RequestInfo
|
|
1006
|
-
* @param init RequestInit
|
|
1007
|
-
* @param attempts retry attempts
|
|
1008
|
-
* @param baseDelay base delay ms
|
|
1009
|
-
* @returns Promise resolving to Response
|
|
1010
|
-
*/
|
|
1011
1016
|
/**
|
|
1012
1017
|
* Normalize different header-like shapes into a plain object.
|
|
1013
1018
|
* @param headerLike headers in Headers, array, or plain object form
|
|
@@ -1116,14 +1121,6 @@ var AbstractGitAdapter = class {
|
|
|
1116
1121
|
const jitter = Math.floor(Math.random() * base * 0.3);
|
|
1117
1122
|
return base + jitter;
|
|
1118
1123
|
}
|
|
1119
|
-
/**
|
|
1120
|
-
* Delegate to shared mapWithConcurrency implementation
|
|
1121
|
-
* @template T,R
|
|
1122
|
-
* @param items items to map
|
|
1123
|
-
* @param mapper async mapper
|
|
1124
|
-
* @param concurrency concurrency limit
|
|
1125
|
-
* @returns Promise resolving to mapped results
|
|
1126
|
-
*/
|
|
1127
1124
|
/**
|
|
1128
1125
|
* Map items with limited concurrency by delegating to the shared helper.
|
|
1129
1126
|
* @template T,R
|
|
@@ -1231,12 +1228,6 @@ var GitHubAdapter = class extends abstractAdapter_default {
|
|
|
1231
1228
|
parents
|
|
1232
1229
|
};
|
|
1233
1230
|
}
|
|
1234
|
-
/**
|
|
1235
|
-
* コンテンツから sha1 を算出します。
|
|
1236
|
-
* @param {string} content コンテンツ
|
|
1237
|
-
* @returns {string} sha1 ハッシュ
|
|
1238
|
-
*/
|
|
1239
|
-
// shaOf is inherited from AbstractGitAdapter
|
|
1240
1231
|
/**
|
|
1241
1232
|
* ブロブを作成またはキャッシュから取得します。
|
|
1242
1233
|
* @param {any[]} changes 変更一覧(create/update を含む)
|
|
@@ -1251,11 +1242,6 @@ var GitHubAdapter = class extends abstractAdapter_default {
|
|
|
1251
1242
|
map[r.path] = r.sha;
|
|
1252
1243
|
return map;
|
|
1253
1244
|
}
|
|
1254
|
-
/**
|
|
1255
|
-
* ブロブ作成用のヘルパー(createBlobs から抽出)
|
|
1256
|
-
* @param {any} ch 変更エントリ
|
|
1257
|
-
* @returns {Promise<{path:string,sha:string}>}
|
|
1258
|
-
*/
|
|
1259
1245
|
/**
|
|
1260
1246
|
* Create a blob for a change or return cached blobSha.
|
|
1261
1247
|
* @param {any} ch change entry
|
|
@@ -1462,10 +1448,6 @@ var GitHubAdapter = class extends abstractAdapter_default {
|
|
|
1462
1448
|
}
|
|
1463
1449
|
throw new NonRetryableError("getRef: sha not found");
|
|
1464
1450
|
}
|
|
1465
|
-
/**
|
|
1466
|
-
* Helper to fetch a ref URL and extract a SHA if present.
|
|
1467
|
-
* Returns null when SHA not found.
|
|
1468
|
-
*/
|
|
1469
1451
|
/**
|
|
1470
1452
|
* Fetch a ref URL and extract a SHA if present.
|
|
1471
1453
|
* @param {string} url API URL to fetch
|
|
@@ -1560,18 +1542,6 @@ var GitHubAdapter = class extends abstractAdapter_default {
|
|
|
1560
1542
|
const enc = index.encoding || "utf-8";
|
|
1561
1543
|
return { content: index.content, encoding: enc };
|
|
1562
1544
|
}
|
|
1563
|
-
/**
|
|
1564
|
-
* Resolve a commit-ish (branch, tag, or SHA) to a commit SHA.
|
|
1565
|
-
* Resolution order: branch -> tag -> commit endpoint -> treat as SHA
|
|
1566
|
-
* Throws if resolution fails.
|
|
1567
|
-
*/
|
|
1568
|
-
/**
|
|
1569
|
-
* Resolve a commit-ish (branch, tag, or SHA) to a commit SHA.
|
|
1570
|
-
* Resolution order: branch -> tag -> commit endpoint -> treat as SHA
|
|
1571
|
-
* Throws if resolution fails.
|
|
1572
|
-
* @param {string} reference commit-ish to resolve
|
|
1573
|
-
* @returns {Promise<string>} resolved commit SHA
|
|
1574
|
-
*/
|
|
1575
1545
|
/**
|
|
1576
1546
|
* Resolve a commit-ish (branch, tag, or SHA) to a commit SHA.
|
|
1577
1547
|
* Resolution order: branch -> tag -> commit endpoint -> treat as SHA
|
|
@@ -1611,11 +1581,6 @@ var GitHubAdapter = class extends abstractAdapter_default {
|
|
|
1611
1581
|
}
|
|
1612
1582
|
return null;
|
|
1613
1583
|
}
|
|
1614
|
-
/**
|
|
1615
|
-
* Try to resolve reference as a branch and return its sha.
|
|
1616
|
-
* @param {string} reference branch name
|
|
1617
|
-
* @returns {Promise<string|null>} resolved sha or null
|
|
1618
|
-
*/
|
|
1619
1584
|
/**
|
|
1620
1585
|
* Try to resolve a branch name to a commit SHA.
|
|
1621
1586
|
* @param {string} reference branch name
|
|
@@ -1625,11 +1590,6 @@ var GitHubAdapter = class extends abstractAdapter_default {
|
|
|
1625
1590
|
const sha = await this.getRef(`heads/${reference}`);
|
|
1626
1591
|
return sha || null;
|
|
1627
1592
|
}
|
|
1628
|
-
/**
|
|
1629
|
-
* Try to resolve reference as a tag and return its sha.
|
|
1630
|
-
* @param {string} reference tag name
|
|
1631
|
-
* @returns {Promise<string|null>} resolved sha or null
|
|
1632
|
-
*/
|
|
1633
1593
|
/**
|
|
1634
1594
|
* Try to resolve a tag name to a commit SHA.
|
|
1635
1595
|
* @param {string} reference tag name
|
|
@@ -1639,11 +1599,6 @@ var GitHubAdapter = class extends abstractAdapter_default {
|
|
|
1639
1599
|
const sha = await this.getRef(`tags/${reference}`);
|
|
1640
1600
|
return sha || null;
|
|
1641
1601
|
}
|
|
1642
|
-
/**
|
|
1643
|
-
* Try to resolve reference using commits endpoint (could be SHA or other form).
|
|
1644
|
-
* @param {string} reference commit-ish
|
|
1645
|
-
* @returns {Promise<string|null>} resolved sha or null
|
|
1646
|
-
*/
|
|
1647
1602
|
/**
|
|
1648
1603
|
* Try to resolve via commits endpoint (may accept SHA or other forms).
|
|
1649
1604
|
* @param {string} reference commit-ish
|
|
@@ -1659,11 +1614,6 @@ var GitHubAdapter = class extends abstractAdapter_default {
|
|
|
1659
1614
|
}
|
|
1660
1615
|
return null;
|
|
1661
1616
|
}
|
|
1662
|
-
/**
|
|
1663
|
-
* Blob を取得して content を返す。取得失敗時は content=null を返す。
|
|
1664
|
-
* @param {{sha:string,path:string}} f blob 情報
|
|
1665
|
-
* @returns {Promise<{path:string,content:string|null}>}
|
|
1666
|
-
*/
|
|
1667
1617
|
/**
|
|
1668
1618
|
* Fetch a blob's content; return null content on failure.
|
|
1669
1619
|
* @param {any} f blob metadata
|
|
@@ -1703,16 +1653,6 @@ var GitHubAdapter = class extends abstractAdapter_default {
|
|
|
1703
1653
|
}
|
|
1704
1654
|
return safe;
|
|
1705
1655
|
}
|
|
1706
|
-
/**
|
|
1707
|
-
* Fetch a blob's content; return null content on failure.
|
|
1708
|
-
* @param {any} f blob metadata
|
|
1709
|
-
* @returns {Promise<{path:string,content:string|null}>}
|
|
1710
|
-
*/
|
|
1711
|
-
/**
|
|
1712
|
-
* リポジトリのスナップショットを取得します。
|
|
1713
|
-
* @param {string} branch ブランチ名 (default: 'main')
|
|
1714
|
-
* @returns {Promise<{headSha:string,shas:Record<string,string>,fetchContent:Function,snapshot:Record<string,string>}>}
|
|
1715
|
-
*/
|
|
1716
1656
|
/**
|
|
1717
1657
|
* Fetch repository snapshot: headSha, shas map and a fetchContent helper.
|
|
1718
1658
|
* @param {string} branch branch name
|
|
@@ -1787,22 +1727,6 @@ var GitHubAdapter = class extends abstractAdapter_default {
|
|
|
1787
1727
|
out[p] = content;
|
|
1788
1728
|
return null;
|
|
1789
1729
|
}
|
|
1790
|
-
/**
|
|
1791
|
-
* 指定パスのコンテンツを取得し、キャッシュと snapshot を更新します。
|
|
1792
|
-
* @param {Map<string, any>} fileMap ファイルメタ情報マップ
|
|
1793
|
-
* @param {Map<string, string>} contentCache キャッシュマップ
|
|
1794
|
-
* @param {Record<string,string>} snapshot スナップショット出力マップ
|
|
1795
|
-
* @param {string} p 取得対象パス
|
|
1796
|
-
* @returns {Promise<string|null>} ファイル内容または null
|
|
1797
|
-
*/
|
|
1798
|
-
/**
|
|
1799
|
-
* Fetch single path content, update cache and snapshot.
|
|
1800
|
-
* @param {Map<string, any>} fileMap file map
|
|
1801
|
-
* @param {Map<string,string>} contentCache cache map
|
|
1802
|
-
* @param {Record<string,string>} snapshot snapshot map
|
|
1803
|
-
* @param {string} p path to fetch
|
|
1804
|
-
* @returns {Promise<string|null>} content or null
|
|
1805
|
-
*/
|
|
1806
1730
|
/**
|
|
1807
1731
|
* Fetch single path content, update cache and snapshot.
|
|
1808
1732
|
* @param {Map<string, any>} fileMap file map
|
|
@@ -1882,7 +1806,7 @@ var GitLabAdapter = class extends abstractAdapter_default {
|
|
|
1882
1806
|
}
|
|
1883
1807
|
/**
|
|
1884
1808
|
* GitLab のページングヘッダを解析します(x-next-page / x-total-pages)。
|
|
1885
|
-
|
|
1809
|
+
* @returns {{nextPage?: number, lastPage?: number}} ページ番号情報
|
|
1886
1810
|
*/
|
|
1887
1811
|
_parsePagingHeaders(resp) {
|
|
1888
1812
|
const out = {};
|
|
@@ -1914,12 +1838,6 @@ var GitLabAdapter = class extends abstractAdapter_default {
|
|
|
1914
1838
|
parents
|
|
1915
1839
|
};
|
|
1916
1840
|
}
|
|
1917
|
-
/**
|
|
1918
|
-
* コンテンツから sha1 を算出します。
|
|
1919
|
-
* @param {string} content コンテンツ
|
|
1920
|
-
* @returns {string} sha1 ハッシュ
|
|
1921
|
-
*/
|
|
1922
|
-
// shaOf is inherited from AbstractGitAdapter
|
|
1923
1841
|
/**
|
|
1924
1842
|
* 変更一覧から blob sha のマップを作成します(疑似実装)。
|
|
1925
1843
|
* @param {any[]} changes 変更一覧
|
|
@@ -1999,9 +1917,6 @@ var GitLabAdapter = class extends abstractAdapter_default {
|
|
|
1999
1917
|
}
|
|
2000
1918
|
return await this.postCommit(url, body);
|
|
2001
1919
|
}
|
|
2002
|
-
/**
|
|
2003
|
-
* Retrieve project metadata (default branch, name, id) and cache it.
|
|
2004
|
-
*/
|
|
2005
1920
|
/**
|
|
2006
1921
|
* Retrieve project metadata (default branch, name, id) and cache it.
|
|
2007
1922
|
* @returns {Promise<import('../virtualfs/types.ts').RepositoryMetadata>} repository metadata
|
|
@@ -2100,7 +2015,7 @@ var GitLabAdapter = class extends abstractAdapter_default {
|
|
|
2100
2015
|
}
|
|
2101
2016
|
/**
|
|
2102
2017
|
* Convert change descriptors to GitLab API actions
|
|
2103
|
-
|
|
2018
|
+
* @returns {Array<any>} API-compatible actions array
|
|
2104
2019
|
*/
|
|
2105
2020
|
createActions(changes) {
|
|
2106
2021
|
return changes.map((c) => {
|
|
@@ -2111,11 +2026,6 @@ var GitLabAdapter = class extends abstractAdapter_default {
|
|
|
2111
2026
|
return { action: "update", file_path: c.path, content: c.content };
|
|
2112
2027
|
});
|
|
2113
2028
|
}
|
|
2114
|
-
/**
|
|
2115
|
-
* Verify remote branch head matches expected parent SHA.
|
|
2116
|
-
* @throws Error if non-fast-forward
|
|
2117
|
-
* @returns {Promise<void>}
|
|
2118
|
-
*/
|
|
2119
2029
|
/**
|
|
2120
2030
|
* Verify that remote branch head matches expected parent SHA.
|
|
2121
2031
|
* Throws when non-fast-forward detected.
|
|
@@ -2150,10 +2060,6 @@ var GitLabAdapter = class extends abstractAdapter_default {
|
|
|
2150
2060
|
}
|
|
2151
2061
|
return index.id || index.commit || index;
|
|
2152
2062
|
}
|
|
2153
|
-
/**
|
|
2154
|
-
* Post commit request and parse response
|
|
2155
|
-
* @returns {Promise<any>}
|
|
2156
|
-
*/
|
|
2157
2063
|
/**
|
|
2158
2064
|
* Post commit request and return parsed commit response.
|
|
2159
2065
|
* @param {string} url endpoint URL
|
|
@@ -2165,19 +2071,6 @@ var GitLabAdapter = class extends abstractAdapter_default {
|
|
|
2165
2071
|
const text = await response.text().catch(() => "");
|
|
2166
2072
|
return this.parseCommitResponse(text);
|
|
2167
2073
|
}
|
|
2168
|
-
/**
|
|
2169
|
-
* fetch をリトライ付きで実行します。
|
|
2170
|
-
* @param {string} url リクエスト URL
|
|
2171
|
-
* @param {RequestInit} opts fetch オプション
|
|
2172
|
-
* @param {number} [retries] 最大リトライ回数
|
|
2173
|
-
* @returns {Promise<Response>} レスポンス
|
|
2174
|
-
*/
|
|
2175
|
-
// fetchWithRetry is provided by AbstractGitAdapter
|
|
2176
|
-
/**
|
|
2177
|
-
* Wait helper for fetch retry backoff.
|
|
2178
|
-
* @param attempt Attempt number
|
|
2179
|
-
* @returns {Promise<void>} resolves after backoff
|
|
2180
|
-
*/
|
|
2181
2074
|
/**
|
|
2182
2075
|
* Wait helper for retry backoff.
|
|
2183
2076
|
* @param {number} attempt attempt number
|
|
@@ -2187,28 +2080,6 @@ var GitLabAdapter = class extends abstractAdapter_default {
|
|
|
2187
2080
|
const wait = this.backoffMs(attempt);
|
|
2188
2081
|
await new Promise((r) => setTimeout(r, wait));
|
|
2189
2082
|
}
|
|
2190
|
-
/**
|
|
2191
|
-
* ステータスが再試行対象か判定します。
|
|
2192
|
-
* @param {number} status ステータスコード
|
|
2193
|
-
* @returns {boolean}
|
|
2194
|
-
*/
|
|
2195
|
-
// isRetryableStatus is provided by AbstractGitAdapter
|
|
2196
|
-
/**
|
|
2197
|
-
* バックオフ時間を計算します。
|
|
2198
|
-
* @param {number} attempt 試行回数(1..)
|
|
2199
|
-
* @returns {number} ミリ秒
|
|
2200
|
-
*/
|
|
2201
|
-
// backoffMs provided by AbstractGitAdapter
|
|
2202
|
-
// small concurrency mapper used for fetching files
|
|
2203
|
-
/**
|
|
2204
|
-
* 並列マッピングユーティリティ
|
|
2205
|
-
* @template T, R
|
|
2206
|
-
* @param {T[]} items 入力配列
|
|
2207
|
-
* @param {(t:T)=>Promise<R>} mapper マッピング関数
|
|
2208
|
-
* @param {number} concurrency 同時実行数
|
|
2209
|
-
* @returns {Promise<R[]>}
|
|
2210
|
-
*/
|
|
2211
|
-
// mapWithConcurrency provided by AbstractGitAdapter
|
|
2212
2083
|
/**
|
|
2213
2084
|
* Prepare JSON body for commit API call.
|
|
2214
2085
|
* @returns {string} JSON body
|
|
@@ -2216,9 +2087,6 @@ var GitLabAdapter = class extends abstractAdapter_default {
|
|
|
2216
2087
|
_prepareCommitBody(branch, message, actions) {
|
|
2217
2088
|
return JSON.stringify({ branch, commit_message: message, actions });
|
|
2218
2089
|
}
|
|
2219
|
-
/**
|
|
2220
|
-
* Optionally verify parent SHA; swallow non-422 errors.
|
|
2221
|
-
*/
|
|
2222
2090
|
/**
|
|
2223
2091
|
* Optionally verify parent SHA; rethrow errors after logging.
|
|
2224
2092
|
* @param {string} expectedParentSha expected SHA
|
|
@@ -2237,7 +2105,7 @@ var GitLabAdapter = class extends abstractAdapter_default {
|
|
|
2237
2105
|
/**
|
|
2238
2106
|
* リポジトリのスナップショットを取得します。
|
|
2239
2107
|
* @param {string} branch ブランチ名 (default: 'main')
|
|
2240
|
-
|
|
2108
|
+
* @returns {Promise<{headSha:string,shas:Record<string,string>,fetchContent:(paths:string[])=>Promise<Record<string,string>>}>}
|
|
2241
2109
|
*/
|
|
2242
2110
|
async fetchSnapshot(branch = "main", concurrency = 5) {
|
|
2243
2111
|
const headSha = await this._determineHeadSha(branch);
|
|
@@ -2247,11 +2115,6 @@ var GitLabAdapter = class extends abstractAdapter_default {
|
|
|
2247
2115
|
const fetchContent = (paths) => this._fetchContentFromFileSet(fileSet, cache, snapshot, paths, branch, concurrency);
|
|
2248
2116
|
return { headSha, shas, fetchContent, snapshot };
|
|
2249
2117
|
}
|
|
2250
|
-
/**
|
|
2251
|
-
* Determine the remote head SHA for a branch. Falls back to branch name on error.
|
|
2252
|
-
* @param {string} branch Branch name
|
|
2253
|
-
* @returns {Promise<string>} head SHA or branch
|
|
2254
|
-
*/
|
|
2255
2118
|
/**
|
|
2256
2119
|
* Determine the head SHA for a branch; fallback to branch name if unavailable.
|
|
2257
2120
|
* @param {string} branch branch name
|
|
@@ -2274,11 +2137,6 @@ var GitLabAdapter = class extends abstractAdapter_default {
|
|
|
2274
2137
|
}
|
|
2275
2138
|
return branch;
|
|
2276
2139
|
}
|
|
2277
|
-
/**
|
|
2278
|
-
* Fetch repository tree and build shas/fileSet.
|
|
2279
|
-
* @param {string} branch Branch name
|
|
2280
|
-
* @returns {Promise<{shas:Record<string,string>,fileSet:Set<string>}>}
|
|
2281
|
-
*/
|
|
2282
2140
|
/**
|
|
2283
2141
|
* Fetch repository tree and build shas map and fileSet.
|
|
2284
2142
|
* @param {string} branch branch name
|
|
@@ -2290,10 +2148,6 @@ var GitLabAdapter = class extends abstractAdapter_default {
|
|
|
2290
2148
|
const files = Array.isArray(treeJ) ? treeJ.filter((t) => t.type === "blob") : [];
|
|
2291
2149
|
return this._buildShasAndFileSet(files);
|
|
2292
2150
|
}
|
|
2293
|
-
/**
|
|
2294
|
-
* Helper to fetch files from the repository tree with caching and concurrency.
|
|
2295
|
-
* @returns {Promise<Record<string,string>>}
|
|
2296
|
-
*/
|
|
2297
2151
|
/**
|
|
2298
2152
|
* Fetch contents for requested paths from a FileSet with caching.
|
|
2299
2153
|
* @param {Set<string>} fileSet set of available files
|
|
@@ -2315,14 +2169,6 @@ var GitLabAdapter = class extends abstractAdapter_default {
|
|
|
2315
2169
|
}, concurrency);
|
|
2316
2170
|
return out;
|
|
2317
2171
|
}
|
|
2318
|
-
/**
|
|
2319
|
-
* 指定パスのファイル内容を取得し、キャッシュと snapshot を更新します。
|
|
2320
|
-
* @param {Map<string,string>} cache キャッシュマップ
|
|
2321
|
-
* @param {Record<string,string>} snapshot スナップショットマップ
|
|
2322
|
-
* @param {string} p ファイルパス
|
|
2323
|
-
* @param {string} branch ブランチ名
|
|
2324
|
-
* @returns {Promise<string|null>} ファイル内容または null
|
|
2325
|
-
*/
|
|
2326
2172
|
/**
|
|
2327
2173
|
* Fetch the content for a single file path, updating cache and snapshot.
|
|
2328
2174
|
* @param {Map<string,string>} cache cache map
|
|
@@ -2345,12 +2191,6 @@ var GitLabAdapter = class extends abstractAdapter_default {
|
|
|
2345
2191
|
}
|
|
2346
2192
|
return null;
|
|
2347
2193
|
}
|
|
2348
|
-
/**
|
|
2349
|
-
* ファイルの raw コンテンツを取得して返します。失敗時は null を返します。
|
|
2350
|
-
* @param {string} path ファイルパス
|
|
2351
|
-
* @param {string} branch ブランチ名
|
|
2352
|
-
* @returns {Promise<string|null>} ファイル内容または null
|
|
2353
|
-
*/
|
|
2354
2194
|
/**
|
|
2355
2195
|
* Fetch raw file content from GitLab; return null on failure.
|
|
2356
2196
|
* @param {string} path file path
|
|
@@ -2372,7 +2212,6 @@ var GitLabAdapter = class extends abstractAdapter_default {
|
|
|
2372
2212
|
return null;
|
|
2373
2213
|
}
|
|
2374
2214
|
}
|
|
2375
|
-
/** Build shas map and fileSet from tree entries */
|
|
2376
2215
|
/**
|
|
2377
2216
|
* Build shas map and fileSet from tree entries
|
|
2378
2217
|
* @returns {{shas:Record<string,string>,fileSet:Set<string>}}
|
|
@@ -2389,18 +2228,6 @@ var GitLabAdapter = class extends abstractAdapter_default {
|
|
|
2389
2228
|
}
|
|
2390
2229
|
return { shas, fileSet };
|
|
2391
2230
|
}
|
|
2392
|
-
/**
|
|
2393
|
-
* Resolve a commit-ish (branch name, tag name, or SHA) to a commit SHA.
|
|
2394
|
-
* Resolution order: branch -> tag -> commits endpoint -> treat as SHA
|
|
2395
|
-
* Throws if not resolvable.
|
|
2396
|
-
*/
|
|
2397
|
-
/**
|
|
2398
|
-
* Resolve a commit-ish (branch, tag, or SHA) to a commit SHA.
|
|
2399
|
-
* Resolution order: branch -> tag -> commits endpoint -> treat as SHA
|
|
2400
|
-
* Throws if not resolvable.
|
|
2401
|
-
* @param {string} reference commit-ish to resolve
|
|
2402
|
-
* @returns {Promise<string>} resolved commit SHA
|
|
2403
|
-
*/
|
|
2404
2231
|
/**
|
|
2405
2232
|
* Resolve a commit-ish (branch, tag, or SHA) to a commit SHA.
|
|
2406
2233
|
* Resolution order: branch -> tag -> commits endpoint -> treat as SHA
|
|
@@ -3504,7 +3331,7 @@ var RemoteSynchronizer = class {
|
|
|
3504
3331
|
/**
|
|
3505
3332
|
* Read and parse the stored info entry for `path`.
|
|
3506
3333
|
* Returns parsed object or null when missing/invalid.
|
|
3507
|
-
|
|
3334
|
+
* @returns {Promise<any|null>} parsed object or null
|
|
3508
3335
|
*/
|
|
3509
3336
|
async _readInfoEntry(path) {
|
|
3510
3337
|
try {
|
|
@@ -3527,7 +3354,7 @@ var RemoteSynchronizer = class {
|
|
|
3527
3354
|
/**
|
|
3528
3355
|
* Attempt to fetch base content using adapterInstance.getBlob.
|
|
3529
3356
|
* Returns string when fetched, null when fetch attempted but failed, or undefined when adapter not supported.
|
|
3530
|
-
|
|
3357
|
+
* @returns {Promise<string|null|undefined>} fetched content, null, or undefined
|
|
3531
3358
|
*/
|
|
3532
3359
|
async _tryFetchBaseWithAdapter(adapterInstance, baseSha, path) {
|
|
3533
3360
|
if (!adapterInstance || typeof adapterInstance.getBlob !== "function")
|
|
@@ -3563,7 +3390,7 @@ var RemoteSynchronizer = class {
|
|
|
3563
3390
|
/**
|
|
3564
3391
|
* Attempt to fetch raw file using adapterInstance._fetchFileRaw if available.
|
|
3565
3392
|
* Returns string when fetched, null when attempted but failed, or undefined when adapter not supported.
|
|
3566
|
-
|
|
3393
|
+
* @returns {Promise<string|null|undefined>} fetched content, null, or undefined
|
|
3567
3394
|
*/
|
|
3568
3395
|
async _tryFetchRawFile(adapterInstance, path, ie) {
|
|
3569
3396
|
if (!adapterInstance || typeof adapterInstance._fetchFileRaw !== "function")
|
|
@@ -3671,7 +3498,7 @@ var VirtualFS = class {
|
|
|
3671
3498
|
if (options?.backend)
|
|
3672
3499
|
this.backend = options.backend;
|
|
3673
3500
|
else
|
|
3674
|
-
this.backend = new OpfsStorage();
|
|
3501
|
+
this.backend = new OpfsStorage("default");
|
|
3675
3502
|
if (options && options.logger)
|
|
3676
3503
|
this.logger = options.logger;
|
|
3677
3504
|
this.applier = new LocalChangeApplier(this.backend);
|
|
@@ -3711,22 +3538,6 @@ var VirtualFS = class {
|
|
|
3711
3538
|
set lastCommitKey(k) {
|
|
3712
3539
|
this.indexManager.setLastCommitKey(k);
|
|
3713
3540
|
}
|
|
3714
|
-
/**
|
|
3715
|
-
* SHA-1 helper wrapper (delegates to ./hashUtils)
|
|
3716
|
-
* @param {string} content - ハッシュ対象の文字列
|
|
3717
|
-
* @returns {Promise<string>} SHA-1 ハッシュの16進表現
|
|
3718
|
-
*/
|
|
3719
|
-
async shaOf(content) {
|
|
3720
|
-
return await shaOf2(content);
|
|
3721
|
-
}
|
|
3722
|
-
/**
|
|
3723
|
-
* SHA helper for Git blob formatting
|
|
3724
|
-
* @param {string} content - blob コンテンツ
|
|
3725
|
-
* @returns {Promise<string>} SHA-1 ハッシュの16進表現(git blob 用)
|
|
3726
|
-
*/
|
|
3727
|
-
async shaOfGitBlob(content) {
|
|
3728
|
-
return await shaOfGitBlob(content);
|
|
3729
|
-
}
|
|
3730
3541
|
/**
|
|
3731
3542
|
* VirtualFS の初期化を行います(バックエンド初期化と index 読み込み)。
|
|
3732
3543
|
* @returns {Promise<void>}
|
|
@@ -3866,6 +3677,312 @@ var VirtualFS = class {
|
|
|
3866
3677
|
}
|
|
3867
3678
|
return null;
|
|
3868
3679
|
}
|
|
3680
|
+
/**
|
|
3681
|
+
* Helper: obtain backend listFilesRaw in a safe manner.
|
|
3682
|
+
* @returns {Promise<any[]>}
|
|
3683
|
+
*/
|
|
3684
|
+
async _getBackendFilesRaw() {
|
|
3685
|
+
try {
|
|
3686
|
+
if (this.backend && typeof this.backend.listFilesRaw === "function") {
|
|
3687
|
+
return await this.backend.listFilesRaw();
|
|
3688
|
+
}
|
|
3689
|
+
} catch (error) {
|
|
3690
|
+
if (typeof console !== "undefined" && console.debug)
|
|
3691
|
+
console.debug("_getBackendFilesRaw failed", error);
|
|
3692
|
+
}
|
|
3693
|
+
return [];
|
|
3694
|
+
}
|
|
3695
|
+
/**
|
|
3696
|
+
* Helper: apply parsed info text into stats object when possible.
|
|
3697
|
+
* @param infoTxt raw info text
|
|
3698
|
+
* @param stats stats object to mutate
|
|
3699
|
+
* @returns {void}
|
|
3700
|
+
*/
|
|
3701
|
+
_applyInfoTxtToStats(infoTxt, stats) {
|
|
3702
|
+
if (!infoTxt)
|
|
3703
|
+
return;
|
|
3704
|
+
try {
|
|
3705
|
+
const info = JSON.parse(infoTxt);
|
|
3706
|
+
if (typeof info.baseSha === "string")
|
|
3707
|
+
stats.gitBlobSha = info.baseSha;
|
|
3708
|
+
if (typeof info.updatedAt === "number")
|
|
3709
|
+
stats.mtime = new Date(info.updatedAt);
|
|
3710
|
+
if (typeof info.size === "number")
|
|
3711
|
+
stats.size = info.size;
|
|
3712
|
+
} catch (error) {
|
|
3713
|
+
if (typeof console !== "undefined" && console.debug)
|
|
3714
|
+
console.debug("parse info failed", error);
|
|
3715
|
+
}
|
|
3716
|
+
}
|
|
3717
|
+
/**
|
|
3718
|
+
* Find a matched backend entry for the given filepath.
|
|
3719
|
+
* @param filepath target filepath
|
|
3720
|
+
* @param filesRaw backend raw listing
|
|
3721
|
+
* @returns {any|null}
|
|
3722
|
+
*/
|
|
3723
|
+
_findMatchedFile(filepath, filesRaw) {
|
|
3724
|
+
if (!Array.isArray(filesRaw))
|
|
3725
|
+
return null;
|
|
3726
|
+
return filesRaw.find((f) => {
|
|
3727
|
+
if (!f || !f.path)
|
|
3728
|
+
return false;
|
|
3729
|
+
return f.path === filepath || f.path.endsWith("/" + filepath) || f.path.endsWith("/" + filepath);
|
|
3730
|
+
});
|
|
3731
|
+
}
|
|
3732
|
+
/**
|
|
3733
|
+
* Create default stats object with consistent shape.
|
|
3734
|
+
* @param now current Date
|
|
3735
|
+
* @returns {any}
|
|
3736
|
+
*/
|
|
3737
|
+
_createDefaultStats(now) {
|
|
3738
|
+
return {
|
|
3739
|
+
dev: 0,
|
|
3740
|
+
ino: 0,
|
|
3741
|
+
mode: 33188,
|
|
3742
|
+
nlink: 1,
|
|
3743
|
+
uid: 0,
|
|
3744
|
+
gid: 0,
|
|
3745
|
+
rdev: 0,
|
|
3746
|
+
size: 0,
|
|
3747
|
+
blksize: void 0,
|
|
3748
|
+
blocks: void 0,
|
|
3749
|
+
atime: now,
|
|
3750
|
+
mtime: now,
|
|
3751
|
+
ctime: now,
|
|
3752
|
+
birthtime: now,
|
|
3753
|
+
/** @returns {boolean} */
|
|
3754
|
+
isFile: () => true,
|
|
3755
|
+
/** @returns {boolean} */
|
|
3756
|
+
isDirectory: () => false
|
|
3757
|
+
};
|
|
3758
|
+
}
|
|
3759
|
+
/**
|
|
3760
|
+
* Populate stats.gitCommitSha from adapterMeta if available.
|
|
3761
|
+
* @param stats stats object to mutate
|
|
3762
|
+
* @returns {void}
|
|
3763
|
+
*/
|
|
3764
|
+
_populateCommitShaFromMeta(stats) {
|
|
3765
|
+
if (!stats.gitCommitSha && this.adapterMeta && this.adapterMeta.opts && this.adapterMeta.opts.branch) {
|
|
3766
|
+
stats.gitCommitSha = this.adapterMeta.opts.branch;
|
|
3767
|
+
}
|
|
3768
|
+
}
|
|
3769
|
+
/**
|
|
3770
|
+
* Try to resolve commit SHA from an instantiated adapter when needed.
|
|
3771
|
+
* @param stats stats object to mutate
|
|
3772
|
+
* @returns {Promise<void>}
|
|
3773
|
+
*/
|
|
3774
|
+
async _resolveCommitShaFromAdapter(stats) {
|
|
3775
|
+
const instAdapter = await this._safeGetAdapterInstance();
|
|
3776
|
+
if (!instAdapter || stats.gitCommitSha)
|
|
3777
|
+
return;
|
|
3778
|
+
if (typeof instAdapter.resolveRef !== "function")
|
|
3779
|
+
return;
|
|
3780
|
+
try {
|
|
3781
|
+
const branch = this.adapterMeta && this.adapterMeta.opts && this.adapterMeta.opts.branch || "main";
|
|
3782
|
+
const resolved = await instAdapter.resolveRef(branch);
|
|
3783
|
+
if (resolved)
|
|
3784
|
+
stats.gitCommitSha = resolved;
|
|
3785
|
+
} catch (error) {
|
|
3786
|
+
if (typeof console !== "undefined" && console.debug)
|
|
3787
|
+
console.debug("_resolveCommitShaFromAdapter resolveRef failed", error);
|
|
3788
|
+
}
|
|
3789
|
+
}
|
|
3790
|
+
/**
|
|
3791
|
+
* Safely get adapter instance, returning null on error.
|
|
3792
|
+
* @returns {Promise<any|null>}
|
|
3793
|
+
*/
|
|
3794
|
+
async _safeGetAdapterInstance() {
|
|
3795
|
+
try {
|
|
3796
|
+
return await this.getAdapterInstance();
|
|
3797
|
+
} catch (error) {
|
|
3798
|
+
if (typeof console !== "undefined" && console.debug)
|
|
3799
|
+
console.debug("_safeGetAdapterInstance failed", error);
|
|
3800
|
+
return null;
|
|
3801
|
+
}
|
|
3802
|
+
}
|
|
3803
|
+
/**
|
|
3804
|
+
* Helper: populate stats.gitCommitSha using adapterMeta or adapter.resolveRef when available.
|
|
3805
|
+
* @param stats stats object to mutate
|
|
3806
|
+
* @returns {Promise<void>}
|
|
3807
|
+
*/
|
|
3808
|
+
async _resolveAdapterCommitShaIfNeeded(stats) {
|
|
3809
|
+
this._populateCommitShaFromMeta(stats);
|
|
3810
|
+
if (!stats.gitCommitSha)
|
|
3811
|
+
await this._resolveCommitShaFromAdapter(stats);
|
|
3812
|
+
}
|
|
3813
|
+
/**
|
|
3814
|
+
* Determine whether a normalized path is an exact file entry in the provided entries.
|
|
3815
|
+
* @param normalizedDirectory normalized directory string
|
|
3816
|
+
* @param keys index keys array
|
|
3817
|
+
* @param entries index entries object
|
|
3818
|
+
* @returns {boolean}
|
|
3819
|
+
*/
|
|
3820
|
+
_isExactFile(normalizedDirectory, keys, entries) {
|
|
3821
|
+
return keys.includes(normalizedDirectory) && entries[normalizedDirectory] && entries[normalizedDirectory].state !== "deleted";
|
|
3822
|
+
}
|
|
3823
|
+
/**
|
|
3824
|
+
* Collect immediate child names from index entries for given directory.
|
|
3825
|
+
* @param normalizedDirectory normalized directory string
|
|
3826
|
+
* @param entries index entries object
|
|
3827
|
+
* @returns {Set<string>} set of immediate child names
|
|
3828
|
+
*/
|
|
3829
|
+
_collectNamesFromIndex(normalizedDirectory, entries) {
|
|
3830
|
+
const outNames = /* @__PURE__ */ new Set();
|
|
3831
|
+
const keys = Object.keys(entries || {});
|
|
3832
|
+
for (const k of keys) {
|
|
3833
|
+
const v = entries[k];
|
|
3834
|
+
if (v && v.state === "deleted")
|
|
3835
|
+
continue;
|
|
3836
|
+
if (normalizedDirectory === "." || normalizedDirectory === "") {
|
|
3837
|
+
this._collectNamesFromIndexRoot(k, outNames);
|
|
3838
|
+
continue;
|
|
3839
|
+
}
|
|
3840
|
+
this._processIndexKeyForDirectory(k, normalizedDirectory, outNames);
|
|
3841
|
+
}
|
|
3842
|
+
return outNames;
|
|
3843
|
+
}
|
|
3844
|
+
/**
|
|
3845
|
+
* Process a single index key for a non-root directory and add immediate child when applicable.
|
|
3846
|
+
* @param key index key
|
|
3847
|
+
* @param normalizedDirectory normalized directory string
|
|
3848
|
+
* @param outNames set to mutate
|
|
3849
|
+
* @returns {void}
|
|
3850
|
+
*/
|
|
3851
|
+
_processIndexKeyForDirectory(key, normalizedDirectory, outNames) {
|
|
3852
|
+
if (key === normalizedDirectory)
|
|
3853
|
+
return;
|
|
3854
|
+
if (key.startsWith(normalizedDirectory + "/")) {
|
|
3855
|
+
const rest = key.slice(normalizedDirectory.length + 1);
|
|
3856
|
+
const first = rest.indexOf("/") === -1 ? rest : rest.slice(0, rest.indexOf("/"));
|
|
3857
|
+
outNames.add(first);
|
|
3858
|
+
}
|
|
3859
|
+
}
|
|
3860
|
+
/**
|
|
3861
|
+
* Safe wrapper for backend.listFiles returning [] on failure.
|
|
3862
|
+
* @param normalizedDirectory directory path
|
|
3863
|
+
* @returns {Promise<any[]>}
|
|
3864
|
+
*/
|
|
3865
|
+
async _getBackendList(normalizedDirectory) {
|
|
3866
|
+
try {
|
|
3867
|
+
return await this.backend.listFiles(normalizedDirectory, void 0, false);
|
|
3868
|
+
} catch (error) {
|
|
3869
|
+
if (typeof console !== "undefined" && console.debug)
|
|
3870
|
+
console.debug("_getBackendList failed", error);
|
|
3871
|
+
return [];
|
|
3872
|
+
}
|
|
3873
|
+
}
|
|
3874
|
+
/**
|
|
3875
|
+
* Helper for collecting names when scanning root directory entries.
|
|
3876
|
+
* @param key index key
|
|
3877
|
+
* @param outNames set to mutate
|
|
3878
|
+
* @returns {void}
|
|
3879
|
+
*/
|
|
3880
|
+
_collectNamesFromIndexRoot(key, outNames) {
|
|
3881
|
+
const first = key.indexOf("/") === -1 ? key : key.slice(0, key.indexOf("/"));
|
|
3882
|
+
outNames.add(first);
|
|
3883
|
+
}
|
|
3884
|
+
/**
|
|
3885
|
+
* Check whether normalizedDirectory corresponds to an exact file entry.
|
|
3886
|
+
* @param normalizedDirectory normalized directory string
|
|
3887
|
+
* @param entries index entries object
|
|
3888
|
+
* @returns {boolean}
|
|
3889
|
+
*/
|
|
3890
|
+
_hasExactEntry(normalizedDirectory, entries) {
|
|
3891
|
+
const keys = Object.keys(entries || {});
|
|
3892
|
+
return this._isExactFile(normalizedDirectory, keys, entries);
|
|
3893
|
+
}
|
|
3894
|
+
/**
|
|
3895
|
+
* Consult backend.listFiles to collect immediate child names for given directory.
|
|
3896
|
+
* Best-effort: logs and returns empty set on failure.
|
|
3897
|
+
* @param normalizedDirectory normalized directory string
|
|
3898
|
+
* @returns {Promise<Set<string>>}
|
|
3899
|
+
*/
|
|
3900
|
+
async _collectNamesFromBackend(normalizedDirectory) {
|
|
3901
|
+
const outNames = /* @__PURE__ */ new Set();
|
|
3902
|
+
if (!this._backendSupportsListFiles())
|
|
3903
|
+
return outNames;
|
|
3904
|
+
const backendList = await this._getBackendList(normalizedDirectory);
|
|
3905
|
+
if (!Array.isArray(backendList) || backendList.length === 0)
|
|
3906
|
+
return outNames;
|
|
3907
|
+
for (const it of backendList)
|
|
3908
|
+
this._processBackendEntry(it, normalizedDirectory, outNames);
|
|
3909
|
+
return outNames;
|
|
3910
|
+
}
|
|
3911
|
+
/**
|
|
3912
|
+
* Return true when backend supports listFiles
|
|
3913
|
+
* @returns {boolean}
|
|
3914
|
+
*/
|
|
3915
|
+
_backendSupportsListFiles() {
|
|
3916
|
+
return !!(this.backend && typeof this.backend.listFiles === "function");
|
|
3917
|
+
}
|
|
3918
|
+
/**
|
|
3919
|
+
* Process a single backend listFiles entry and add immediate child name to outNames when applicable.
|
|
3920
|
+
* @param it backend entry
|
|
3921
|
+
* @param normalizedDirectory normalized directory string
|
|
3922
|
+
* @param outNames set to mutate
|
|
3923
|
+
* @returns {void}
|
|
3924
|
+
*/
|
|
3925
|
+
_processBackendEntry(it, normalizedDirectory, outNames) {
|
|
3926
|
+
try {
|
|
3927
|
+
if (!it || !it.path)
|
|
3928
|
+
return;
|
|
3929
|
+
const p = it.path;
|
|
3930
|
+
if (p === normalizedDirectory)
|
|
3931
|
+
return;
|
|
3932
|
+
if (p.startsWith(normalizedDirectory + "/")) {
|
|
3933
|
+
const rest = p.slice(normalizedDirectory.length + 1);
|
|
3934
|
+
const first = rest.indexOf("/") === -1 ? rest : rest.slice(0, rest.indexOf("/"));
|
|
3935
|
+
outNames.add(first);
|
|
3936
|
+
}
|
|
3937
|
+
} catch (error) {
|
|
3938
|
+
if (typeof console !== "undefined" && console.debug)
|
|
3939
|
+
console.debug("_processBackendEntry failed", error);
|
|
3940
|
+
}
|
|
3941
|
+
}
|
|
3942
|
+
/**
|
|
3943
|
+
* Build Dirent-like lightweight objects for given names.
|
|
3944
|
+
* @param names array of names
|
|
3945
|
+
* @param keys array of index keys
|
|
3946
|
+
* @param entries index entries object
|
|
3947
|
+
* @param normalizedDirectory normalized directory string
|
|
3948
|
+
* @returns {Array<any>} array of Dirent-like objects
|
|
3949
|
+
*/
|
|
3950
|
+
_buildDirentTypes(names, keys, entries, normalizedDirectory) {
|
|
3951
|
+
const out = [];
|
|
3952
|
+
for (const name of names) {
|
|
3953
|
+
const childPath = normalizedDirectory === "." ? name : `${normalizedDirectory}/${name}`;
|
|
3954
|
+
const { isFile, isDirectory } = this._determineChildType(childPath, keys, entries);
|
|
3955
|
+
const _isFileFunction = function() {
|
|
3956
|
+
return isFile && !isDirectory;
|
|
3957
|
+
};
|
|
3958
|
+
const _isDirectoryFunction = function() {
|
|
3959
|
+
return isDirectory;
|
|
3960
|
+
};
|
|
3961
|
+
out.push({ name, isFile: _isFileFunction, isDirectory: _isDirectoryFunction });
|
|
3962
|
+
}
|
|
3963
|
+
return out;
|
|
3964
|
+
}
|
|
3965
|
+
/**
|
|
3966
|
+
* Determine whether a childPath corresponds to a file, directory, or both.
|
|
3967
|
+
* @param childPath path of child
|
|
3968
|
+
* @param keys index keys
|
|
3969
|
+
* @param entries index entries
|
|
3970
|
+
* @returns {{isFile:boolean,isDirectory:boolean}}
|
|
3971
|
+
*/
|
|
3972
|
+
_determineChildType(childPath, keys, entries) {
|
|
3973
|
+
let isDirectory = false;
|
|
3974
|
+
let isFile = false;
|
|
3975
|
+
for (const k of keys) {
|
|
3976
|
+
if (k === childPath && entries[k] && entries[k].state !== "deleted") {
|
|
3977
|
+
isFile = true;
|
|
3978
|
+
}
|
|
3979
|
+
if (k.startsWith(childPath + "/")) {
|
|
3980
|
+
isDirectory = true;
|
|
3981
|
+
break;
|
|
3982
|
+
}
|
|
3983
|
+
}
|
|
3984
|
+
return { isFile, isDirectory };
|
|
3985
|
+
}
|
|
3869
3986
|
/**
|
|
3870
3987
|
* Return persisted adapter metadata (if any).
|
|
3871
3988
|
* @returns {any|null}
|
|
@@ -3883,15 +4000,6 @@ var VirtualFS = class {
|
|
|
3883
4000
|
await this.localFileManager.writeFile(filepath, content);
|
|
3884
4001
|
await this.loadIndex();
|
|
3885
4002
|
}
|
|
3886
|
-
/**
|
|
3887
|
-
* ファイルを削除します(トゥームストーン作成を含む)。
|
|
3888
|
-
* @param {string} filepath ファイルパス
|
|
3889
|
-
* @returns {Promise<void>}
|
|
3890
|
-
*/
|
|
3891
|
-
async deleteFile(filepath) {
|
|
3892
|
-
await this.localFileManager.deleteFile(filepath);
|
|
3893
|
-
await this.loadIndex();
|
|
3894
|
-
}
|
|
3895
4003
|
/**
|
|
3896
4004
|
* rename を delete + create の合成で行うヘルパ
|
|
3897
4005
|
* @param from 元パス
|
|
@@ -3902,7 +4010,7 @@ var VirtualFS = class {
|
|
|
3902
4010
|
if (content === null)
|
|
3903
4011
|
throw new Error("source not found");
|
|
3904
4012
|
await this.writeFile(to, content);
|
|
3905
|
-
await this.
|
|
4013
|
+
await this.unlink(from);
|
|
3906
4014
|
}
|
|
3907
4015
|
/**
|
|
3908
4016
|
* ワークスペース/ベースからファイル内容を読み出します。
|
|
@@ -3919,7 +4027,9 @@ var VirtualFS = class {
|
|
|
3919
4027
|
await this.remoteSynchronizer.fetchBaseIfMissing(filepath, adapter);
|
|
3920
4028
|
content = await this.localFileManager.readFile(filepath);
|
|
3921
4029
|
}
|
|
3922
|
-
} catch {
|
|
4030
|
+
} catch (error) {
|
|
4031
|
+
if (typeof console !== "undefined" && console.debug)
|
|
4032
|
+
console.debug("readFile on-demand fetch failed", error);
|
|
3923
4033
|
}
|
|
3924
4034
|
return content;
|
|
3925
4035
|
}
|
|
@@ -3931,6 +4041,151 @@ var VirtualFS = class {
|
|
|
3931
4041
|
async readConflict(filepath) {
|
|
3932
4042
|
return await this.conflictManager.readConflict(filepath);
|
|
3933
4043
|
}
|
|
4044
|
+
/**
|
|
4045
|
+
* fs.stat 互換: 指定ファイルのメタ情報を返す
|
|
4046
|
+
* ワークスペース上の情報を優先し、未取得時は Git のメタ情報で補完する。
|
|
4047
|
+
* @returns {Promise<any>} stats オブジェクト
|
|
4048
|
+
*/
|
|
4049
|
+
async stat(filepath) {
|
|
4050
|
+
if (!filepath || typeof filepath !== "string")
|
|
4051
|
+
throw new TypeError("filepath is required");
|
|
4052
|
+
const filesRaw = await this._getBackendFilesRaw();
|
|
4053
|
+
const matched = this._findMatchedFile(filepath, filesRaw);
|
|
4054
|
+
const now = /* @__PURE__ */ new Date();
|
|
4055
|
+
const stats = this._createDefaultStats(now);
|
|
4056
|
+
try {
|
|
4057
|
+
const infoTxt = await this.backend.readBlob(filepath, "info");
|
|
4058
|
+
this._applyInfoTxtToStats(infoTxt, stats);
|
|
4059
|
+
} catch (error) {
|
|
4060
|
+
if (typeof console !== "undefined" && console.debug)
|
|
4061
|
+
console.debug("stat readBlob failed", error);
|
|
4062
|
+
}
|
|
4063
|
+
if (matched) {
|
|
4064
|
+
stats.workspacePath = matched.path;
|
|
4065
|
+
}
|
|
4066
|
+
await this._resolveAdapterCommitShaIfNeeded(stats);
|
|
4067
|
+
return stats;
|
|
4068
|
+
}
|
|
4069
|
+
/**
|
|
4070
|
+
* fs.unlink 互換: ファイルを削除する
|
|
4071
|
+
*/
|
|
4072
|
+
async unlink(filepath) {
|
|
4073
|
+
if (!filepath || typeof filepath !== "string")
|
|
4074
|
+
throw new TypeError("filepath is required");
|
|
4075
|
+
await this.localFileManager.deleteFile(filepath);
|
|
4076
|
+
await this.loadIndex();
|
|
4077
|
+
}
|
|
4078
|
+
/**
|
|
4079
|
+
* fs.mkdir 互換 (簡易実装): workspace 側にディレクトリ情報を書き込む
|
|
4080
|
+
*/
|
|
4081
|
+
async mkdir(dirpath, _options) {
|
|
4082
|
+
if (!dirpath || typeof dirpath !== "string")
|
|
4083
|
+
throw new TypeError("dirpath is required");
|
|
4084
|
+
const info = { path: dirpath, state: "dir", createdAt: Date.now() };
|
|
4085
|
+
if (this.backend && typeof this.backend.writeBlob === "function") {
|
|
4086
|
+
await this.backend.writeBlob(dirpath, JSON.stringify(info), "info-workspace").catch((error) => {
|
|
4087
|
+
throw Object.assign(new Error("\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u4F5C\u6210\u5931\u6557"), { code: "EEXIST", cause: error });
|
|
4088
|
+
});
|
|
4089
|
+
}
|
|
4090
|
+
}
|
|
4091
|
+
/**
|
|
4092
|
+
* fs.rmdir 互換 (簡易実装)
|
|
4093
|
+
*/
|
|
4094
|
+
async rmdir(dirpath, options) {
|
|
4095
|
+
if (!dirpath || typeof dirpath !== "string")
|
|
4096
|
+
throw new TypeError("dirpath is required");
|
|
4097
|
+
const children = await this._listChildrenOfDir(dirpath);
|
|
4098
|
+
if (children.length > 0 && !(options && options.recursive)) {
|
|
4099
|
+
const errorObject = new Error("Directory not empty");
|
|
4100
|
+
errorObject.code = "ENOTEMPTY";
|
|
4101
|
+
throw errorObject;
|
|
4102
|
+
}
|
|
4103
|
+
if (options && options.recursive)
|
|
4104
|
+
await this._deleteChildrenRecursive(children);
|
|
4105
|
+
}
|
|
4106
|
+
/**
|
|
4107
|
+
* Return list of child paths for given dirpath based on index entries.
|
|
4108
|
+
* @param dirpath directory path
|
|
4109
|
+
* @returns {Promise<string[]>}
|
|
4110
|
+
*/
|
|
4111
|
+
async _listChildrenOfDir(dirpath) {
|
|
4112
|
+
const paths = await this.listPaths();
|
|
4113
|
+
return paths.filter((p) => p === dirpath || p.startsWith(dirpath + "/"));
|
|
4114
|
+
}
|
|
4115
|
+
/**
|
|
4116
|
+
* Delete array of children using localFileManager, logging failures per-child.
|
|
4117
|
+
* @param children array of paths
|
|
4118
|
+
* @returns {Promise<void>}
|
|
4119
|
+
*/
|
|
4120
|
+
async _deleteChildrenRecursive(children) {
|
|
4121
|
+
for (const p of children) {
|
|
4122
|
+
try {
|
|
4123
|
+
await this.localFileManager.deleteFile(p);
|
|
4124
|
+
} catch (error) {
|
|
4125
|
+
if (typeof console !== "undefined" && console.debug)
|
|
4126
|
+
console.debug("rmdir recursive delete failed for", p, error);
|
|
4127
|
+
}
|
|
4128
|
+
}
|
|
4129
|
+
}
|
|
4130
|
+
/**
|
|
4131
|
+
* fs.readdir 互換 (簡易実装)
|
|
4132
|
+
* @returns {Promise<string[]|Array<any>>}
|
|
4133
|
+
*/
|
|
4134
|
+
async readdir(dirpath, options) {
|
|
4135
|
+
if (!dirpath || typeof dirpath !== "string")
|
|
4136
|
+
throw new TypeError("dirpath is required");
|
|
4137
|
+
const index = await this.indexManager.getIndex();
|
|
4138
|
+
const entries = index && index.entries || {};
|
|
4139
|
+
const keys = Object.keys(entries);
|
|
4140
|
+
const names = await this._gatherDirectoryNames(dirpath, entries, keys);
|
|
4141
|
+
const maybeEmpty = this._returnIfNoNames(names, options);
|
|
4142
|
+
if (maybeEmpty !== null)
|
|
4143
|
+
return maybeEmpty;
|
|
4144
|
+
const normalizedDirectory = dirpath === "" ? "." : dirpath;
|
|
4145
|
+
if (options && options.withFileTypes)
|
|
4146
|
+
return this._buildDirentTypes(names, keys, entries, normalizedDirectory);
|
|
4147
|
+
return names;
|
|
4148
|
+
}
|
|
4149
|
+
/**
|
|
4150
|
+
* Return an empty array when names is empty according to options, else null to continue.
|
|
4151
|
+
* @param names array of names
|
|
4152
|
+
* @param options readdir options
|
|
4153
|
+
* @returns {Array<any>|null}
|
|
4154
|
+
*/
|
|
4155
|
+
_returnIfNoNames(names, options) {
|
|
4156
|
+
if (!names || names.length === 0)
|
|
4157
|
+
return options && options.withFileTypes ? [] : [];
|
|
4158
|
+
return null;
|
|
4159
|
+
}
|
|
4160
|
+
/**
|
|
4161
|
+
* Gather immediate child names for a directory using index and backend as fallback.
|
|
4162
|
+
* Throws ENOTDIR when the path represents a file.
|
|
4163
|
+
* @param dirpath original directory path
|
|
4164
|
+
* @param entries index entries object
|
|
4165
|
+
* @param keys array of index keys
|
|
4166
|
+
* @returns {Promise<string[]>} immediate child names
|
|
4167
|
+
*/
|
|
4168
|
+
async _gatherDirectoryNames(dirpath, entries, keys) {
|
|
4169
|
+
const normalizedDirectory = dirpath === "" ? "." : dirpath;
|
|
4170
|
+
const outNames = /* @__PURE__ */ new Set();
|
|
4171
|
+
const isExactFile = this._isExactFile(normalizedDirectory, keys, entries);
|
|
4172
|
+
const indexNames = this._collectNamesFromIndex(normalizedDirectory, entries);
|
|
4173
|
+
for (const n of indexNames)
|
|
4174
|
+
outNames.add(n);
|
|
4175
|
+
if (outNames.size === 0 && normalizedDirectory !== "." && !isExactFile) {
|
|
4176
|
+
const backendNames = await this._collectNamesFromBackend(normalizedDirectory);
|
|
4177
|
+
for (const n of backendNames)
|
|
4178
|
+
outNames.add(n);
|
|
4179
|
+
}
|
|
4180
|
+
if (isExactFile && outNames.size === 0) {
|
|
4181
|
+
const errorObject = new Error("\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u3067\u306F\u3042\u308A\u307E\u305B\u3093");
|
|
4182
|
+
errorObject.code = "ENOTDIR";
|
|
4183
|
+
throw errorObject;
|
|
4184
|
+
}
|
|
4185
|
+
if (outNames.size === 0)
|
|
4186
|
+
return [];
|
|
4187
|
+
return Array.from(outNames);
|
|
4188
|
+
}
|
|
3934
4189
|
/**
|
|
3935
4190
|
* 指定パスのリモート衝突ファイル (.git-conflict/) を削除して
|
|
3936
4191
|
* 競合を解消済とマークします。
|
|
@@ -3974,12 +4229,9 @@ var VirtualFS = class {
|
|
|
3974
4229
|
const entries = index && index.entries || {};
|
|
3975
4230
|
const out = [];
|
|
3976
4231
|
for (const k of Object.keys(entries)) {
|
|
3977
|
-
|
|
3978
|
-
|
|
3979
|
-
|
|
3980
|
-
continue;
|
|
3981
|
-
} catch {
|
|
3982
|
-
}
|
|
4232
|
+
const v = entries[k];
|
|
4233
|
+
if (v && v.state === "deleted")
|
|
4234
|
+
continue;
|
|
3983
4235
|
out.push(k);
|
|
3984
4236
|
}
|
|
3985
4237
|
return out;
|
|
@@ -4035,7 +4287,9 @@ var VirtualFS = class {
|
|
|
4035
4287
|
if (input.parentSha && typeof adapter.getCommitTreeSha === "function") {
|
|
4036
4288
|
try {
|
|
4037
4289
|
baseTreeSha = await adapter.getCommitTreeSha(input.parentSha);
|
|
4038
|
-
} catch {
|
|
4290
|
+
} catch (error) {
|
|
4291
|
+
if (typeof console !== "undefined" && console.debug)
|
|
4292
|
+
console.debug("getCommitTreeSha failed, continuing without baseTree", error);
|
|
4039
4293
|
baseTreeSha = void 0;
|
|
4040
4294
|
}
|
|
4041
4295
|
}
|
|
@@ -4194,7 +4448,7 @@ apigit-commit-key:${input.commitKey}`;
|
|
|
4194
4448
|
/**
|
|
4195
4449
|
* Persist the requested branch into adapter metadata (best-effort).
|
|
4196
4450
|
* @param {string} branch branch name to persist
|
|
4197
|
-
|
|
4451
|
+
* @returns {Promise<void>}
|
|
4198
4452
|
*/
|
|
4199
4453
|
async _persistAdapterBranchMeta(branch, adapterInstance) {
|
|
4200
4454
|
const meta = this.adapterMeta && this.adapterMeta.opts ? { ...this.adapterMeta } : await this.getAdapter();
|
|
@@ -4398,7 +4652,7 @@ apigit-commit-key:${input.commitKey}`;
|
|
|
4398
4652
|
/**
|
|
4399
4653
|
* Convenience to get default branch name from adapter repository metadata.
|
|
4400
4654
|
* Returns null when adapter not available.
|
|
4401
|
-
|
|
4655
|
+
* @returns {Promise<string|null>}
|
|
4402
4656
|
*/
|
|
4403
4657
|
async getDefaultBranch() {
|
|
4404
4658
|
const instAdapter = await this.getAdapterInstance();
|
|
@@ -4438,11 +4692,6 @@ apigit-commit-key:${input.commitKey}`;
|
|
|
4438
4692
|
console.debug("persist repository metadata aborted", error);
|
|
4439
4693
|
}
|
|
4440
4694
|
}
|
|
4441
|
-
/**
|
|
4442
|
-
* Normalize a resolved descriptor (string headSha or object) into a
|
|
4443
|
-
* RemoteSnapshotDescriptor or null. Helper to reduce cognitive complexity.
|
|
4444
|
-
* @returns {Promise<RemoteSnapshotDescriptor|null>} 正規化された descriptor または null
|
|
4445
|
-
*/
|
|
4446
4695
|
/**
|
|
4447
4696
|
* Try persisting repository metadata when available. Best-effort.
|
|
4448
4697
|
* @param instAdapter adapter instance or null
|
|
@@ -4546,7 +4795,7 @@ apigit-commit-key:${input.commitKey}`;
|
|
|
4546
4795
|
input.changes = await this.getChangeSet();
|
|
4547
4796
|
}
|
|
4548
4797
|
if (!input.commitKey) {
|
|
4549
|
-
input.commitKey = await
|
|
4798
|
+
input.commitKey = await shaOf2((input.parentSha || "") + JSON.stringify(input.changes));
|
|
4550
4799
|
}
|
|
4551
4800
|
const instAdapter = await this.getAdapterInstance();
|
|
4552
4801
|
if (!instAdapter) {
|
|
@@ -4575,7 +4824,7 @@ var virtualfs_default = VirtualFS;
|
|
|
4575
4824
|
var IndexedDatabaseStorage = class IndexedDatabaseStorage2 {
|
|
4576
4825
|
/**
|
|
4577
4826
|
* 環境に IndexedDB が存在するかを同期検査します。
|
|
4578
|
-
|
|
4827
|
+
* @returns {boolean} 利用可能なら true
|
|
4579
4828
|
*/
|
|
4580
4829
|
static canUse() {
|
|
4581
4830
|
try {
|
|
@@ -4587,6 +4836,8 @@ var IndexedDatabaseStorage = class IndexedDatabaseStorage2 {
|
|
|
4587
4836
|
dbName;
|
|
4588
4837
|
dbPromise;
|
|
4589
4838
|
currentBranch = null;
|
|
4839
|
+
root;
|
|
4840
|
+
rootPrefix = "";
|
|
4590
4841
|
static VAR_WORKSPACE_BASE = "workspace";
|
|
4591
4842
|
// Historically this was a separate workspace-info store, but some test
|
|
4592
4843
|
// fakes expect info entries to be available in 'git-info'. Alias the
|
|
@@ -4600,7 +4851,7 @@ var IndexedDatabaseStorage = class IndexedDatabaseStorage2 {
|
|
|
4600
4851
|
/** 利用可能な DB 名の一覧を返す
|
|
4601
4852
|
* @returns {string[]} available root names
|
|
4602
4853
|
*/
|
|
4603
|
-
static async availableRoots() {
|
|
4854
|
+
static async availableRoots(namespace) {
|
|
4604
4855
|
const g = globalThis;
|
|
4605
4856
|
const idb = g.indexedDB;
|
|
4606
4857
|
if (!idb)
|
|
@@ -4608,7 +4859,12 @@ var IndexedDatabaseStorage = class IndexedDatabaseStorage2 {
|
|
|
4608
4859
|
if (typeof idb.databases !== "function")
|
|
4609
4860
|
return [];
|
|
4610
4861
|
try {
|
|
4611
|
-
|
|
4862
|
+
const names = await IndexedDatabaseStorage2._namesFromDatabases(idb);
|
|
4863
|
+
if (!namespace)
|
|
4864
|
+
return names;
|
|
4865
|
+
if (names.includes(namespace))
|
|
4866
|
+
return ["apigit_storage"];
|
|
4867
|
+
return [];
|
|
4612
4868
|
} catch {
|
|
4613
4869
|
return [];
|
|
4614
4870
|
}
|
|
@@ -4628,8 +4884,10 @@ var IndexedDatabaseStorage = class IndexedDatabaseStorage2 {
|
|
|
4628
4884
|
return Array.from(new Set(names));
|
|
4629
4885
|
}
|
|
4630
4886
|
/** コンストラクタ */
|
|
4631
|
-
constructor(
|
|
4632
|
-
this.dbName =
|
|
4887
|
+
constructor(namespace, _root) {
|
|
4888
|
+
this.dbName = namespace || IndexedDatabaseStorage2.DEFAULT_DB_NAME;
|
|
4889
|
+
this.root = _root || void 0;
|
|
4890
|
+
this.rootPrefix = this.root ? `${this.root}_` : "";
|
|
4633
4891
|
this.dbPromise = this.openDb();
|
|
4634
4892
|
}
|
|
4635
4893
|
/**
|
|
@@ -4661,27 +4919,26 @@ var IndexedDatabaseStorage = class IndexedDatabaseStorage2 {
|
|
|
4661
4919
|
*/
|
|
4662
4920
|
/**
|
|
4663
4921
|
* Handle DB upgrade event and create required object stores.
|
|
4664
|
-
*
|
|
4922
|
+
* Creates the object stores used by this backend, names are resolved
|
|
4923
|
+
* through `_storeName` to include any configured `_root` prefix.
|
|
4924
|
+
* @param {Event} event - Upgrade event from `indexedDB.open`
|
|
4665
4925
|
* @returns {void}
|
|
4666
4926
|
*/
|
|
4667
4927
|
_handleUpgrade(event) {
|
|
4668
4928
|
const database = event.target.result;
|
|
4669
|
-
if (!database.objectStoreNames.contains(IndexedDatabaseStorage2.VAR_WORKSPACE_BASE))
|
|
4670
|
-
database.createObjectStore(IndexedDatabaseStorage2.VAR_WORKSPACE_BASE);
|
|
4671
|
-
if (!database.objectStoreNames.contains(IndexedDatabaseStorage2.VAR_WORKSPACE_INFO))
|
|
4672
|
-
database.createObjectStore(IndexedDatabaseStorage2.VAR_WORKSPACE_INFO);
|
|
4673
|
-
if (!database.objectStoreNames.contains(IndexedDatabaseStorage2.VAR_BASE))
|
|
4674
|
-
database.createObjectStore(IndexedDatabaseStorage2.VAR_BASE);
|
|
4675
|
-
if (!database.objectStoreNames.contains(IndexedDatabaseStorage2.VAR_CONFLICT))
|
|
4676
|
-
database.createObjectStore(IndexedDatabaseStorage2.VAR_CONFLICT);
|
|
4677
|
-
if (!database.objectStoreNames.contains(IndexedDatabaseStorage2.VAR_INFO))
|
|
4678
|
-
database.createObjectStore(IndexedDatabaseStorage2.VAR_INFO);
|
|
4679
|
-
if (!database.objectStoreNames.contains("index"))
|
|
4680
|
-
database.createObjectStore("index");
|
|
4929
|
+
if (!database.objectStoreNames.contains(this._storeName(IndexedDatabaseStorage2.VAR_WORKSPACE_BASE)))
|
|
4930
|
+
database.createObjectStore(this._storeName(IndexedDatabaseStorage2.VAR_WORKSPACE_BASE));
|
|
4931
|
+
if (!database.objectStoreNames.contains(this._storeName(IndexedDatabaseStorage2.VAR_WORKSPACE_INFO)))
|
|
4932
|
+
database.createObjectStore(this._storeName(IndexedDatabaseStorage2.VAR_WORKSPACE_INFO));
|
|
4933
|
+
if (!database.objectStoreNames.contains(this._storeName(IndexedDatabaseStorage2.VAR_BASE)))
|
|
4934
|
+
database.createObjectStore(this._storeName(IndexedDatabaseStorage2.VAR_BASE));
|
|
4935
|
+
if (!database.objectStoreNames.contains(this._storeName(IndexedDatabaseStorage2.VAR_CONFLICT)))
|
|
4936
|
+
database.createObjectStore(this._storeName(IndexedDatabaseStorage2.VAR_CONFLICT));
|
|
4937
|
+
if (!database.objectStoreNames.contains(this._storeName(IndexedDatabaseStorage2.VAR_INFO)))
|
|
4938
|
+
database.createObjectStore(this._storeName(IndexedDatabaseStorage2.VAR_INFO));
|
|
4939
|
+
if (!database.objectStoreNames.contains(this._storeName("index")))
|
|
4940
|
+
database.createObjectStore(this._storeName("index"));
|
|
4681
4941
|
}
|
|
4682
|
-
/**
|
|
4683
|
-
* 指定 DB に対する onversionchange ハンドラを生成します。
|
|
4684
|
-
*/
|
|
4685
4942
|
/**
|
|
4686
4943
|
* Create a handler to close DB on version change.
|
|
4687
4944
|
* @param dbParam Target DB
|
|
@@ -4692,9 +4949,6 @@ var IndexedDatabaseStorage = class IndexedDatabaseStorage2 {
|
|
|
4692
4949
|
databaseParameter.close();
|
|
4693
4950
|
};
|
|
4694
4951
|
}
|
|
4695
|
-
/**
|
|
4696
|
-
* DB open の成功ハンドラ
|
|
4697
|
-
*/
|
|
4698
4952
|
/**
|
|
4699
4953
|
* Called when DB open succeeds.
|
|
4700
4954
|
* @param req IDB open request
|
|
@@ -4706,9 +4960,6 @@ var IndexedDatabaseStorage = class IndexedDatabaseStorage2 {
|
|
|
4706
4960
|
database.onversionchange = this._makeVersionChangeHandler(database);
|
|
4707
4961
|
resolve(database);
|
|
4708
4962
|
}
|
|
4709
|
-
/**
|
|
4710
|
-
* DB open のエラーハンドラ
|
|
4711
|
-
*/
|
|
4712
4963
|
/**
|
|
4713
4964
|
* Called when DB open errors.
|
|
4714
4965
|
* @param req IDB open request
|
|
@@ -4718,22 +4969,19 @@ var IndexedDatabaseStorage = class IndexedDatabaseStorage2 {
|
|
|
4718
4969
|
_onOpenError(request, reject) {
|
|
4719
4970
|
reject(request.error);
|
|
4720
4971
|
}
|
|
4721
|
-
/**
|
|
4722
|
-
* トランザクションラッパー。cb 内の処理をトランザクションで実行し、
|
|
4723
|
-
* 必要なら再試行します。
|
|
4724
|
-
*/
|
|
4725
4972
|
/**
|
|
4726
4973
|
* トランザクションラッパー。cb 内の処理をトランザクションで実行し、必要なら再試行します。
|
|
4727
4974
|
* @returns {Promise<void>} トランザクション処理完了時に解決
|
|
4728
4975
|
*/
|
|
4729
4976
|
async tx(storeName, mode, callback) {
|
|
4977
|
+
const physical = this._storeName(storeName);
|
|
4730
4978
|
try {
|
|
4731
|
-
return await this._performTxAttempt(
|
|
4979
|
+
return await this._performTxAttempt(physical, mode, callback);
|
|
4732
4980
|
} catch (error) {
|
|
4733
4981
|
const isInvalidState = error && (error.name === "InvalidStateError" || /closing/i.test(String(error.message || "")));
|
|
4734
4982
|
if (isInvalidState) {
|
|
4735
4983
|
this.dbPromise = this.openDb();
|
|
4736
|
-
return await this._performTxAttempt(
|
|
4984
|
+
return await this._performTxAttempt(physical, mode, callback);
|
|
4737
4985
|
}
|
|
4738
4986
|
throw error;
|
|
4739
4987
|
}
|
|
@@ -4830,10 +5078,6 @@ var IndexedDatabaseStorage = class IndexedDatabaseStorage2 {
|
|
|
4830
5078
|
return orig.apply(target, arguments_);
|
|
4831
5079
|
};
|
|
4832
5080
|
}
|
|
4833
|
-
/**
|
|
4834
|
-
* Schedule a microtask to invoke tx.oncomplete in case fake IndexedDB
|
|
4835
|
-
* implementations never fire it.
|
|
4836
|
-
*/
|
|
4837
5081
|
/**
|
|
4838
5082
|
* Schedule a microtask to invoke tx.oncomplete in case fake IndexedDB
|
|
4839
5083
|
* implementations never fire it.
|
|
@@ -4849,7 +5093,6 @@ var IndexedDatabaseStorage = class IndexedDatabaseStorage2 {
|
|
|
4849
5093
|
}
|
|
4850
5094
|
}, 0);
|
|
4851
5095
|
}
|
|
4852
|
-
// legacy canUseOpfs removed; use static canUse() instead
|
|
4853
5096
|
/**
|
|
4854
5097
|
* index を読み出す
|
|
4855
5098
|
* @returns {Promise<IndexFile|null>} 読み出した IndexFile、存在しなければ null
|
|
@@ -4881,10 +5124,6 @@ var IndexedDatabaseStorage = class IndexedDatabaseStorage2 {
|
|
|
4881
5124
|
this.currentBranch = null;
|
|
4882
5125
|
}
|
|
4883
5126
|
}
|
|
4884
|
-
/**
|
|
4885
|
-
* Apply metadata to result. @returns void
|
|
4886
|
-
* @returns {void}
|
|
4887
|
-
*/
|
|
4888
5127
|
/**
|
|
4889
5128
|
* Load workspace-local info entries into result.entries (workspace overrides branch-scoped)
|
|
4890
5129
|
* @param result IndexFile being populated
|
|
@@ -4939,8 +5178,9 @@ var IndexedDatabaseStorage = class IndexedDatabaseStorage2 {
|
|
|
4939
5178
|
async _readIndexMeta(database) {
|
|
4940
5179
|
return await new Promise((resolve) => {
|
|
4941
5180
|
try {
|
|
4942
|
-
const
|
|
4943
|
-
const
|
|
5181
|
+
const indexName = this._storeName("index");
|
|
5182
|
+
const tx = database.transaction(indexName, "readonly");
|
|
5183
|
+
const store = tx.objectStore(indexName);
|
|
4944
5184
|
const request = store.get("index");
|
|
4945
5185
|
request.onsuccess = () => {
|
|
4946
5186
|
resolve(request.result ?? null);
|
|
@@ -5120,14 +5360,7 @@ var IndexedDatabaseStorage = class IndexedDatabaseStorage2 {
|
|
|
5120
5360
|
return await this._getFromStore(storeName, key);
|
|
5121
5361
|
}
|
|
5122
5362
|
/**
|
|
5123
|
-
*
|
|
5124
|
-
* @param segment Segment name
|
|
5125
|
-
* @param filepath Path to read
|
|
5126
|
-
* @param branch Current branch
|
|
5127
|
-
* @returns {Promise<string|null>} blob content or null
|
|
5128
|
-
*/
|
|
5129
|
-
/**
|
|
5130
|
-
* blob を削除する
|
|
5363
|
+
* blob を削除する
|
|
5131
5364
|
* @returns {Promise<void>} 削除完了時に解決
|
|
5132
5365
|
*/
|
|
5133
5366
|
async deleteBlob(filepath, segment) {
|
|
@@ -5178,10 +5411,6 @@ var IndexedDatabaseStorage = class IndexedDatabaseStorage2 {
|
|
|
5178
5411
|
}
|
|
5179
5412
|
return toWrite;
|
|
5180
5413
|
}
|
|
5181
|
-
/**
|
|
5182
|
-
* Gather entries that should be written to workspace-info: those that exist in workspace-base.
|
|
5183
|
-
* @returns {Promise<Array<{k:string,v:any}>>}
|
|
5184
|
-
*/
|
|
5185
5414
|
/**
|
|
5186
5415
|
* Compute store name and key for a given segment and filepath.
|
|
5187
5416
|
* @returns {{storeName:string,key:string}}
|
|
@@ -5191,10 +5420,6 @@ var IndexedDatabaseStorage = class IndexedDatabaseStorage2 {
|
|
|
5191
5420
|
const key = seg === "workspace" ? filepath : seg === "conflictBlob" ? `${branch}::conflictBlob::${filepath}` : `${branch}::${filepath}`;
|
|
5192
5421
|
return { storeName, key };
|
|
5193
5422
|
}
|
|
5194
|
-
/**
|
|
5195
|
-
* Compute store name and key for a given segment and filepath.
|
|
5196
|
-
* @returns {{storeName:string,key:string}}
|
|
5197
|
-
*/
|
|
5198
5423
|
/**
|
|
5199
5424
|
* Delete a filepath from all relevant stores for the given branch.
|
|
5200
5425
|
* @returns {Promise<void>}
|
|
@@ -5207,10 +5432,6 @@ var IndexedDatabaseStorage = class IndexedDatabaseStorage2 {
|
|
|
5207
5432
|
await this._deleteFromStore(IndexedDatabaseStorage2.VAR_INFO, filepath);
|
|
5208
5433
|
await this._deleteFromStore(IndexedDatabaseStorage2.VAR_WORKSPACE_INFO, filepath);
|
|
5209
5434
|
}
|
|
5210
|
-
/**
|
|
5211
|
-
* Delete a filepath from all relevant stores for the given branch.
|
|
5212
|
-
* @returns {Promise<void>}
|
|
5213
|
-
*/
|
|
5214
5435
|
/**
|
|
5215
5436
|
* Read a value from a specific object store.
|
|
5216
5437
|
* @param storeName Object store name
|
|
@@ -5221,8 +5442,9 @@ var IndexedDatabaseStorage = class IndexedDatabaseStorage2 {
|
|
|
5221
5442
|
const database = await this.dbPromise;
|
|
5222
5443
|
return new Promise((resolve) => {
|
|
5223
5444
|
try {
|
|
5224
|
-
const
|
|
5225
|
-
const
|
|
5445
|
+
const physical = this._storeName(storeName);
|
|
5446
|
+
const tx = database.transaction(physical, "readonly");
|
|
5447
|
+
const store = tx.objectStore(physical);
|
|
5226
5448
|
const request = store.get(filepath);
|
|
5227
5449
|
request.onsuccess = () => {
|
|
5228
5450
|
const result = request.result ?? null;
|
|
@@ -5252,8 +5474,9 @@ var IndexedDatabaseStorage = class IndexedDatabaseStorage2 {
|
|
|
5252
5474
|
const database = await this.dbPromise;
|
|
5253
5475
|
return new Promise((resolve) => {
|
|
5254
5476
|
try {
|
|
5255
|
-
const
|
|
5256
|
-
const
|
|
5477
|
+
const physical = this._storeName(storeName);
|
|
5478
|
+
const tx = database.transaction(physical, "readonly");
|
|
5479
|
+
const store = tx.objectStore(physical);
|
|
5257
5480
|
const keys = [];
|
|
5258
5481
|
const request = store.openKeyCursor();
|
|
5259
5482
|
request.onsuccess = this._makeCursorSuccessHandler(resolve, keys);
|
|
@@ -5288,8 +5511,13 @@ var IndexedDatabaseStorage = class IndexedDatabaseStorage2 {
|
|
|
5288
5511
|
});
|
|
5289
5512
|
}
|
|
5290
5513
|
/**
|
|
5291
|
-
*
|
|
5514
|
+
* Map a logical store name to its physical store name including root prefix.
|
|
5515
|
+
* @param name logical store identifier
|
|
5516
|
+
* @returns physical store name used in IndexedDB
|
|
5292
5517
|
*/
|
|
5518
|
+
_storeName(name) {
|
|
5519
|
+
return this.rootPrefix ? `${this.rootPrefix}${name}` : name;
|
|
5520
|
+
}
|
|
5293
5521
|
/**
|
|
5294
5522
|
* Create a cursor success handler bound to resolve and keys array.
|
|
5295
5523
|
* @param resolve resolver
|
|
@@ -5330,9 +5558,6 @@ var IndexedDatabaseStorage = class IndexedDatabaseStorage2 {
|
|
|
5330
5558
|
keys = this._filterKeys(keys, p, recursive);
|
|
5331
5559
|
return await this._collectFiles(keys, seg);
|
|
5332
5560
|
}
|
|
5333
|
-
/**
|
|
5334
|
-
* Raw listing that returns implementation-specific URIs and a normalized path.
|
|
5335
|
-
*/
|
|
5336
5561
|
/**
|
|
5337
5562
|
* Raw listing that returns implementation-specific URIs and a normalized path.
|
|
5338
5563
|
* @param prefix optional prefix to filter
|
|
@@ -5363,9 +5588,6 @@ var IndexedDatabaseStorage = class IndexedDatabaseStorage2 {
|
|
|
5363
5588
|
}
|
|
5364
5589
|
return out;
|
|
5365
5590
|
}
|
|
5366
|
-
/**
|
|
5367
|
-
* Helper: build entries from store keys with filtering and path normalization
|
|
5368
|
-
*/
|
|
5369
5591
|
/**
|
|
5370
5592
|
* Helper: build entries from store keys with filtering and path normalization
|
|
5371
5593
|
* @returns {Promise<Array<{uri:string,path:string,info?:string|null}>>}
|
|
@@ -5442,10 +5664,6 @@ var IndexedDatabaseStorage = class IndexedDatabaseStorage2 {
|
|
|
5442
5664
|
}
|
|
5443
5665
|
return keys;
|
|
5444
5666
|
}
|
|
5445
|
-
/**
|
|
5446
|
-
* Collect file info objects for keys array.
|
|
5447
|
-
* @returns {Promise<Array<{path:string, info:string|null}>>}
|
|
5448
|
-
*/
|
|
5449
5667
|
/**
|
|
5450
5668
|
* Collect file info objects for keys array.
|
|
5451
5669
|
* @param keys list of keys
|
|
@@ -5461,15 +5679,6 @@ var IndexedDatabaseStorage = class IndexedDatabaseStorage2 {
|
|
|
5461
5679
|
}
|
|
5462
5680
|
return out;
|
|
5463
5681
|
}
|
|
5464
|
-
/**
|
|
5465
|
-
* Collect file info objects for keys array.
|
|
5466
|
-
* @param keys key list
|
|
5467
|
-
* @param _seg segment (unused)
|
|
5468
|
-
* @returns {Promise<Array<{path:string, info:string|null}>>}
|
|
5469
|
-
*/
|
|
5470
|
-
/**
|
|
5471
|
-
* Resolve info value for a given key: prefer workspace-local, then branch-scoped.
|
|
5472
|
-
*/
|
|
5473
5682
|
/**
|
|
5474
5683
|
* Resolve info value for a given key: prefer workspace-local, then branch-scoped.
|
|
5475
5684
|
* @param k key
|
|
@@ -5482,10 +5691,6 @@ var IndexedDatabaseStorage = class IndexedDatabaseStorage2 {
|
|
|
5482
5691
|
info = await this._getFromStore(IndexedDatabaseStorage2.VAR_INFO, k);
|
|
5483
5692
|
return info;
|
|
5484
5693
|
}
|
|
5485
|
-
/**
|
|
5486
|
-
* Resolve info value for a given key: prefer workspace-local, then branch-scoped.
|
|
5487
|
-
* @returns {Promise<string|null>}
|
|
5488
|
-
*/
|
|
5489
5694
|
/**
|
|
5490
5695
|
* Calculate SHA-1 hex digest of given content.
|
|
5491
5696
|
* @param content Input string
|
|
@@ -5564,6 +5769,23 @@ function computeSha1Fallback(message) {
|
|
|
5564
5769
|
for (let index = 0; index < bytes.length; index += 4) {
|
|
5565
5770
|
words.push(bytes[index] << 24 | bytes[index + 1] << 16 | bytes[index + 2] << 8 | bytes[index + 3]);
|
|
5566
5771
|
}
|
|
5772
|
+
let h0 = 1732584193;
|
|
5773
|
+
let h1 = 4023233417;
|
|
5774
|
+
let h2 = 2562383102;
|
|
5775
|
+
let h3 = 271733878;
|
|
5776
|
+
let h4 = 3285377520;
|
|
5777
|
+
const results = processWords(words);
|
|
5778
|
+
h0 = results[0];
|
|
5779
|
+
h1 = results[1];
|
|
5780
|
+
h2 = results[2];
|
|
5781
|
+
h3 = results[3];
|
|
5782
|
+
h4 = results[4];
|
|
5783
|
+
function toHex(n) {
|
|
5784
|
+
return n.toString(16).padStart(8, "0");
|
|
5785
|
+
}
|
|
5786
|
+
return (toHex(h0) + toHex(h1) + toHex(h2) + toHex(h3) + toHex(h4)).toLowerCase();
|
|
5787
|
+
}
|
|
5788
|
+
function processWords(words) {
|
|
5567
5789
|
let h0 = 1732584193;
|
|
5568
5790
|
let h1 = 4023233417;
|
|
5569
5791
|
let h2 = 2562383102;
|
|
@@ -5577,37 +5799,40 @@ function computeSha1Fallback(message) {
|
|
|
5577
5799
|
const temporary = w[t - 3] ^ w[t - 8] ^ w[t - 14] ^ w[t - 16];
|
|
5578
5800
|
w[t] = (temporary << 1 | temporary >>> 31) >>> 0;
|
|
5579
5801
|
}
|
|
5580
|
-
|
|
5581
|
-
|
|
5582
|
-
|
|
5583
|
-
|
|
5584
|
-
|
|
5585
|
-
|
|
5586
|
-
|
|
5587
|
-
|
|
5588
|
-
|
|
5589
|
-
|
|
5590
|
-
|
|
5591
|
-
|
|
5592
|
-
|
|
5593
|
-
|
|
5594
|
-
|
|
5595
|
-
|
|
5596
|
-
|
|
5597
|
-
|
|
5598
|
-
|
|
5599
|
-
|
|
5600
|
-
|
|
5601
|
-
|
|
5602
|
-
}
|
|
5603
|
-
|
|
5604
|
-
|
|
5605
|
-
|
|
5606
|
-
|
|
5607
|
-
|
|
5608
|
-
|
|
5609
|
-
|
|
5610
|
-
|
|
5802
|
+
const updated = processChunk(w, [h0, h1, h2, h3, h4]);
|
|
5803
|
+
h0 = updated[0];
|
|
5804
|
+
h1 = updated[1];
|
|
5805
|
+
h2 = updated[2];
|
|
5806
|
+
h3 = updated[3];
|
|
5807
|
+
h4 = updated[4];
|
|
5808
|
+
}
|
|
5809
|
+
return [h0, h1, h2, h3, h4];
|
|
5810
|
+
}
|
|
5811
|
+
function processChunk(w, h) {
|
|
5812
|
+
let a = h[0], b = h[1], c = h[2], d = h[3], registerE = h[4];
|
|
5813
|
+
for (let t = 0; t < 80; t++) {
|
|
5814
|
+
let f, k;
|
|
5815
|
+
if (t < 20) {
|
|
5816
|
+
f = b & c | ~b & d;
|
|
5817
|
+
k = 1518500249;
|
|
5818
|
+
} else if (t < 40) {
|
|
5819
|
+
f = b ^ c ^ d;
|
|
5820
|
+
k = 1859775393;
|
|
5821
|
+
} else if (t < 60) {
|
|
5822
|
+
f = b & c | b & d | c & d;
|
|
5823
|
+
k = 2400959708;
|
|
5824
|
+
} else {
|
|
5825
|
+
f = b ^ c ^ d;
|
|
5826
|
+
k = 3395469782;
|
|
5827
|
+
}
|
|
5828
|
+
const temporaryValue = (a << 5 | a >>> 27) + f + registerE + k + (w[t] >>> 0) >>> 0;
|
|
5829
|
+
registerE = d;
|
|
5830
|
+
d = c;
|
|
5831
|
+
c = (b << 30 | b >>> 2) >>> 0;
|
|
5832
|
+
b = a;
|
|
5833
|
+
a = temporaryValue;
|
|
5834
|
+
}
|
|
5835
|
+
return [h[0] + a >>> 0, h[1] + b >>> 0, h[2] + c >>> 0, h[3] + d >>> 0, h[4] + registerE >>> 0];
|
|
5611
5836
|
}
|
|
5612
5837
|
function parseExistingInfo(store, filepath) {
|
|
5613
5838
|
const existingTxt = store.infoBlobs.has(filepath) ? store.infoBlobs.get(filepath) : null;
|
|
@@ -5655,31 +5880,36 @@ async function updateInfoForWrite(store, filepath, seg, content) {
|
|
|
5655
5880
|
const now = Date.now();
|
|
5656
5881
|
const existing = parseExistingInfo(store, filepath);
|
|
5657
5882
|
if (seg === "info") {
|
|
5658
|
-
|
|
5659
|
-
|
|
5660
|
-
store.infoBlobs.set(filepath, JSON.stringify(parsed));
|
|
5661
|
-
return;
|
|
5662
|
-
} catch {
|
|
5663
|
-
store.infoBlobs.set(filepath, String(content));
|
|
5664
|
-
return;
|
|
5665
|
-
}
|
|
5883
|
+
await handleInfoSegment(store, filepath, content);
|
|
5884
|
+
return;
|
|
5666
5885
|
}
|
|
5667
5886
|
const sha = await shaOf3(content);
|
|
5668
5887
|
let entry;
|
|
5669
|
-
if (seg === "workspace")
|
|
5888
|
+
if (seg === "workspace")
|
|
5670
5889
|
entry = buildWorkspaceEntry(existing, filepath, sha, now);
|
|
5671
|
-
|
|
5890
|
+
else if (seg === "base")
|
|
5672
5891
|
entry = buildBaseEntry(existing, filepath, sha, now);
|
|
5673
|
-
|
|
5892
|
+
else if (seg === "conflict")
|
|
5674
5893
|
entry = buildConflictEntry(existing, filepath, now);
|
|
5675
|
-
|
|
5894
|
+
else
|
|
5676
5895
|
entry = { path: filepath, updatedAt: now };
|
|
5677
|
-
}
|
|
5678
5896
|
store.infoBlobs.set(filepath, JSON.stringify(entry));
|
|
5679
|
-
} catch {
|
|
5897
|
+
} catch (error) {
|
|
5898
|
+
if (typeof console !== "undefined" && console.debug)
|
|
5899
|
+
console.debug("updateInfoForWrite failed", error);
|
|
5680
5900
|
return;
|
|
5681
5901
|
}
|
|
5682
5902
|
}
|
|
5903
|
+
async function handleInfoSegment(store, filepath, content) {
|
|
5904
|
+
try {
|
|
5905
|
+
const parsed = JSON.parse(content);
|
|
5906
|
+
store.infoBlobs.set(filepath, JSON.stringify(parsed));
|
|
5907
|
+
} catch (error) {
|
|
5908
|
+
store.infoBlobs.set(filepath, String(content));
|
|
5909
|
+
if (typeof console !== "undefined" && console.debug)
|
|
5910
|
+
console.debug("handleInfoSegment: stored raw content due to parse error", error);
|
|
5911
|
+
}
|
|
5912
|
+
}
|
|
5683
5913
|
|
|
5684
5914
|
// src/virtualfs/inmemoryStorage.ts
|
|
5685
5915
|
var BRANCH_SEP = "::";
|
|
@@ -5703,17 +5933,26 @@ var InMemoryStorage = class InMemoryStorage2 {
|
|
|
5703
5933
|
* 利用可能なルート名を返します。
|
|
5704
5934
|
* @returns {string[]} ルート名の配列
|
|
5705
5935
|
*/
|
|
5706
|
-
static availableRoots() {
|
|
5936
|
+
static availableRoots(namespace) {
|
|
5707
5937
|
const keys = Array.from(InMemoryStorage2.stores.keys());
|
|
5708
|
-
|
|
5938
|
+
if (namespace) {
|
|
5939
|
+
const filtered = keys.filter((k) => k.startsWith(namespace + "/")).map((k) => k.slice((namespace + "/").length));
|
|
5940
|
+
return filtered.length ? filtered : ["apigit_storage"];
|
|
5941
|
+
}
|
|
5942
|
+
const roots = keys.map((k) => {
|
|
5943
|
+
const parts = k.split("/");
|
|
5944
|
+
return parts.length > 1 ? parts.slice(1).join("/") : parts[0];
|
|
5945
|
+
});
|
|
5946
|
+
const uniq = Array.from(new Set(roots));
|
|
5947
|
+
return uniq.length ? uniq : ["apigit_storage"];
|
|
5709
5948
|
}
|
|
5710
|
-
// legacy canUseOpfs removed; use static canUse() instead
|
|
5711
5949
|
/**
|
|
5712
5950
|
* コンストラクタ。互換性のためにディレクトリ名を受け取るが無視する。
|
|
5713
5951
|
* @param directory 任意のディレクトリ文字列(使用しない)
|
|
5714
5952
|
*/
|
|
5715
|
-
constructor(directory) {
|
|
5716
|
-
|
|
5953
|
+
constructor(namespace, directory) {
|
|
5954
|
+
const directoryName = directory ?? `__inmem_${Math.random().toString(36).slice(2)}`;
|
|
5955
|
+
this.rootKey = namespace ? `${namespace}/${directoryName}` : directoryName;
|
|
5717
5956
|
if (!InMemoryStorage2.stores.has(this.rootKey)) {
|
|
5718
5957
|
InMemoryStorage2.stores.set(this.rootKey, {
|
|
5719
5958
|
index: { head: "", entries: {} },
|
|
@@ -5755,7 +5994,7 @@ var InMemoryStorage = class InMemoryStorage2 {
|
|
|
5755
5994
|
}
|
|
5756
5995
|
/**
|
|
5757
5996
|
* Load workspace-local info entries into result.entries (unprefixed keys)
|
|
5758
|
-
|
|
5997
|
+
* @returns {void}
|
|
5759
5998
|
*/
|
|
5760
5999
|
_loadInMemoryWorkspaceInfo(store, result, branch) {
|
|
5761
6000
|
for (const [k, v] of store.infoBlobs.entries()) {
|
|
@@ -5768,7 +6007,7 @@ var InMemoryStorage = class InMemoryStorage2 {
|
|
|
5768
6007
|
}
|
|
5769
6008
|
/**
|
|
5770
6009
|
* Load branch-scoped info entries into result.entries without overwriting workspace-local entries
|
|
5771
|
-
|
|
6010
|
+
* @returns {void}
|
|
5772
6011
|
*/
|
|
5773
6012
|
_loadInMemoryBranchInfo(store, result, branch) {
|
|
5774
6013
|
for (const [k, v] of store.infoBlobs.entries()) {
|
|
@@ -5841,10 +6080,6 @@ var InMemoryStorage = class InMemoryStorage2 {
|
|
|
5841
6080
|
const wrapped = seg === SEG_WORKSPACE || seg === "conflict" ? this._wrapStoreForInfoNoPrefix(store) : this._wrapStoreForInfoPrefix(store);
|
|
5842
6081
|
await updateInfoForWrite(wrapped, filepath, seg, content);
|
|
5843
6082
|
}
|
|
5844
|
-
/**
|
|
5845
|
-
* Handle workspace blob writes that should be based on existing git-scoped info.
|
|
5846
|
-
* Returns true when the operation is handled and caller should return early.
|
|
5847
|
-
*/
|
|
5848
6083
|
/**
|
|
5849
6084
|
* Handle workspace blob writes that should be based on existing git-scoped info.
|
|
5850
6085
|
* Returns true when the operation is handled and caller should return early.
|
|
@@ -6136,7 +6371,7 @@ var InMemoryStorage = class InMemoryStorage2 {
|
|
|
6136
6371
|
* @param prefix プレフィックス(例: 'dir/sub')
|
|
6137
6372
|
* @param segment セグメント('workspace' 等)。省略時は 'workspace'
|
|
6138
6373
|
* @param recursive サブディレクトリも含めるか。省略時は true
|
|
6139
|
-
|
|
6374
|
+
* @returns {Promise<Array<{ path: string; info: string | null }>>}
|
|
6140
6375
|
*/
|
|
6141
6376
|
async listFiles(prefix, segment, recursive = true) {
|
|
6142
6377
|
const seg = segment || SEG_WORKSPACE;
|
|
@@ -6155,9 +6390,6 @@ var InMemoryStorage = class InMemoryStorage2 {
|
|
|
6155
6390
|
const keys = this._resolveKeysForList(allKeys, String(seg), p, recursive);
|
|
6156
6391
|
return this._collectFilesInMemory(keys, store);
|
|
6157
6392
|
}
|
|
6158
|
-
/**
|
|
6159
|
-
* Resolve and filter keys for `listFiles` into final key list.
|
|
6160
|
-
*/
|
|
6161
6393
|
/**
|
|
6162
6394
|
* Resolve and filter keys for `listFiles` into final key list.
|
|
6163
6395
|
* @returns {string[]} filtered keys
|
|
@@ -6294,9 +6526,15 @@ var InMemoryStorage = class InMemoryStorage2 {
|
|
|
6294
6526
|
static delete(rootName) {
|
|
6295
6527
|
if (InMemoryStorage2.stores.has(rootName)) {
|
|
6296
6528
|
InMemoryStorage2.stores.delete(rootName);
|
|
6297
|
-
|
|
6298
|
-
|
|
6529
|
+
return;
|
|
6530
|
+
}
|
|
6531
|
+
for (const key of Array.from(InMemoryStorage2.stores.keys())) {
|
|
6532
|
+
if (key === rootName || key.endsWith("/" + rootName)) {
|
|
6533
|
+
InMemoryStorage2.stores.delete(key);
|
|
6534
|
+
return;
|
|
6535
|
+
}
|
|
6299
6536
|
}
|
|
6537
|
+
throw new Error(`InMemory root "${rootName}" not found`);
|
|
6300
6538
|
}
|
|
6301
6539
|
};
|
|
6302
6540
|
var inmemoryStorage_default = InMemoryStorage;
|