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.js
CHANGED
|
@@ -53,23 +53,28 @@ var APIGitLib = (() => {
|
|
|
53
53
|
ok = true;
|
|
54
54
|
return ok;
|
|
55
55
|
}
|
|
56
|
-
/** 利用可能なサブディレクトリ名の候補を返す
|
|
57
|
-
* @returns {Promise<string[]>} available root directories
|
|
58
|
-
*/
|
|
59
56
|
/**
|
|
60
57
|
* Return available root folder names for OPFS. This method is synchronous
|
|
61
58
|
* to satisfy the StorageBackendConstructor contract; it returns a cached
|
|
62
59
|
* hint if available and kicks off an async probe to populate the cache.
|
|
63
60
|
* If no information is available synchronously an empty array is returned.
|
|
64
|
-
|
|
61
|
+
* @returns {Promise<string[]>} available root directories
|
|
65
62
|
*/
|
|
66
|
-
static async availableRoots() {
|
|
63
|
+
static async availableRoots(namespace) {
|
|
67
64
|
try {
|
|
68
65
|
const root = await OpfsStorage2._getNavigatorStorageRoot();
|
|
69
66
|
if (!root)
|
|
70
67
|
return [];
|
|
71
|
-
|
|
72
|
-
|
|
68
|
+
for await (const handle of root.values()) {
|
|
69
|
+
const name = OpfsStorage2._extractHandleName(handle);
|
|
70
|
+
if (name === namespace && OpfsStorage2._isDirectoryHandle(handle)) {
|
|
71
|
+
return await OpfsStorage2._collectDirectoryNames(handle);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return [];
|
|
75
|
+
} catch (error) {
|
|
76
|
+
if (typeof console !== "undefined" && console.debug)
|
|
77
|
+
console.debug("availableRoots probe failed", error);
|
|
73
78
|
return [];
|
|
74
79
|
}
|
|
75
80
|
}
|
|
@@ -120,6 +125,7 @@ var APIGitLib = (() => {
|
|
|
120
125
|
}
|
|
121
126
|
rootDir = "apigit_storage";
|
|
122
127
|
currentBranch = null;
|
|
128
|
+
namespace = "";
|
|
123
129
|
/**
|
|
124
130
|
* Calculate SHA-1 hex digest of given content.
|
|
125
131
|
* @param content Input string
|
|
@@ -132,10 +138,21 @@ var APIGitLib = (() => {
|
|
|
132
138
|
const hashArray = Array.from(new Uint8Array(hashBuffer));
|
|
133
139
|
return hashArray.map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
134
140
|
}
|
|
135
|
-
/**
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
141
|
+
/**
|
|
142
|
+
* コンストラクタ(OPFS は初期化不要)。
|
|
143
|
+
* `namespace` は必須。挙動:
|
|
144
|
+
* - `new(namespace)` のみの場合は OPFS ルートとして `namespace` を使う(テストの期待値に合わせる)。
|
|
145
|
+
* - `new(namespace, root)` の場合は `namespace/root` を使う。
|
|
146
|
+
*/
|
|
147
|
+
constructor(namespace, root) {
|
|
148
|
+
this.namespace = namespace || "";
|
|
149
|
+
if (root) {
|
|
150
|
+
this.rootDir = this.namespace ? `${this.namespace}/${root}` : root;
|
|
151
|
+
} else if (this.namespace) {
|
|
152
|
+
this.rootDir = this.namespace;
|
|
153
|
+
} else {
|
|
154
|
+
this.rootDir = this.rootDir;
|
|
155
|
+
}
|
|
139
156
|
}
|
|
140
157
|
/**
|
|
141
158
|
* 初期化(OPFS はランタイム判定のみ)
|
|
@@ -163,7 +180,7 @@ var APIGitLib = (() => {
|
|
|
163
180
|
}
|
|
164
181
|
/**
|
|
165
182
|
* Map logical segment to concrete prefix used on OPFS.
|
|
166
|
-
|
|
183
|
+
* @returns {string} concrete prefix path for the given segment
|
|
167
184
|
*/
|
|
168
185
|
_segmentToPrefix(segment) {
|
|
169
186
|
if (segment === "workspace")
|
|
@@ -172,7 +189,6 @@ var APIGitLib = (() => {
|
|
|
172
189
|
const branch = this.currentBranch || "main";
|
|
173
190
|
return `.git/${branch}/${segName}`;
|
|
174
191
|
}
|
|
175
|
-
// legacy canUseOpfs removed; use static canUse() instead
|
|
176
192
|
/**
|
|
177
193
|
* Try to get OPFS root from navigator.storage.getDirectory().
|
|
178
194
|
* @returns {Promise<any|null>}
|
|
@@ -186,7 +202,9 @@ var APIGitLib = (() => {
|
|
|
186
202
|
const maybe = nav.storage.getDirectory();
|
|
187
203
|
const d = await Promise.resolve(maybe);
|
|
188
204
|
return d || null;
|
|
189
|
-
} catch {
|
|
205
|
+
} catch (error) {
|
|
206
|
+
if (typeof console !== "undefined" && console.debug)
|
|
207
|
+
console.debug("_tryNavigatorStorage failed", error);
|
|
190
208
|
return null;
|
|
191
209
|
}
|
|
192
210
|
}
|
|
@@ -201,7 +219,9 @@ var APIGitLib = (() => {
|
|
|
201
219
|
}
|
|
202
220
|
try {
|
|
203
221
|
return await opfs.getDirectory();
|
|
204
|
-
} catch {
|
|
222
|
+
} catch (error) {
|
|
223
|
+
if (typeof console !== "undefined" && console.debug)
|
|
224
|
+
console.debug("_tryOriginPrivateFileSystem failed", error);
|
|
205
225
|
return null;
|
|
206
226
|
}
|
|
207
227
|
}
|
|
@@ -232,10 +252,6 @@ var APIGitLib = (() => {
|
|
|
232
252
|
}
|
|
233
253
|
return directory;
|
|
234
254
|
}
|
|
235
|
-
/**
|
|
236
|
-
* index を読み出す
|
|
237
|
-
* @returns {Promise<IndexFile|null>} 読み出した IndexFile、存在しなければ null
|
|
238
|
-
*/
|
|
239
255
|
/**
|
|
240
256
|
* Read index metadata file from OPFS.
|
|
241
257
|
* @returns {Promise<string|null>}
|
|
@@ -252,7 +268,9 @@ var APIGitLib = (() => {
|
|
|
252
268
|
const fh = await root.getFileHandle("index");
|
|
253
269
|
const file = await fh.getFile();
|
|
254
270
|
return await file.text();
|
|
255
|
-
} catch {
|
|
271
|
+
} catch (error) {
|
|
272
|
+
if (typeof console !== "undefined" && console.debug)
|
|
273
|
+
console.debug("_readIndexMetadata failed", error);
|
|
256
274
|
return null;
|
|
257
275
|
}
|
|
258
276
|
}
|
|
@@ -611,8 +629,8 @@ var APIGitLib = (() => {
|
|
|
611
629
|
* @param root root directory handle
|
|
612
630
|
* @param prefix prefix dir
|
|
613
631
|
* @param filepath path relative to prefix
|
|
614
|
-
|
|
615
|
-
|
|
632
|
+
* @returns {Promise<string|null>} file contents or null when not found
|
|
633
|
+
*/
|
|
616
634
|
async readFromPrefix(root, prefix, filepath) {
|
|
617
635
|
try {
|
|
618
636
|
const fullPath = this.rootDir ? `${this.rootDir}/${prefix}/${filepath}` : `${prefix}/${filepath}`;
|
|
@@ -696,7 +714,6 @@ var APIGitLib = (() => {
|
|
|
696
714
|
await this._handleChildEntry(d, name, base, results);
|
|
697
715
|
}
|
|
698
716
|
}
|
|
699
|
-
/** Handle a single child entry name: try file first, then directory. */
|
|
700
717
|
/**
|
|
701
718
|
* @returns {Promise<void>}
|
|
702
719
|
*/
|
|
@@ -750,7 +767,7 @@ var APIGitLib = (() => {
|
|
|
750
767
|
* @param prefix プレフィックス(例: 'dir/sub')。省略時はルート
|
|
751
768
|
* @param segment セグメント('workspace' 等)。省略時は 'workspace'
|
|
752
769
|
* @param recursive サブディレクトリも含めるか。省略時は true
|
|
753
|
-
|
|
770
|
+
* @returns {Promise<Array<{ path: string; info: string | null }>>}
|
|
754
771
|
*/
|
|
755
772
|
async listFiles(prefix, segment, recursive = true) {
|
|
756
773
|
const root = await this.getOpfsRoot();
|
|
@@ -794,12 +811,9 @@ var APIGitLib = (() => {
|
|
|
794
811
|
*/
|
|
795
812
|
async _findStorageDirectory(navRoot) {
|
|
796
813
|
try {
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
return handle;
|
|
801
|
-
}
|
|
802
|
-
return null;
|
|
814
|
+
const parts = this.rootDir.split("/").filter(Boolean);
|
|
815
|
+
const directoryHandle = await this.traverseDir(navRoot, parts);
|
|
816
|
+
return directoryHandle;
|
|
803
817
|
} catch {
|
|
804
818
|
return null;
|
|
805
819
|
}
|
|
@@ -983,7 +997,6 @@ var APIGitLib = (() => {
|
|
|
983
997
|
setLogger(logger) {
|
|
984
998
|
this.logger = logger;
|
|
985
999
|
}
|
|
986
|
-
// Internal helpers to forward logs only when a logger is injected
|
|
987
1000
|
/**
|
|
988
1001
|
* Log debug messages when a logger is present.
|
|
989
1002
|
* @param _messages messages to log (unused when no logger)
|
|
@@ -1032,14 +1045,6 @@ var APIGitLib = (() => {
|
|
|
1032
1045
|
}
|
|
1033
1046
|
}
|
|
1034
1047
|
}
|
|
1035
|
-
/**
|
|
1036
|
-
* Proxy to shared fetchWithRetry implementation
|
|
1037
|
-
* @param input RequestInfo
|
|
1038
|
-
* @param init RequestInit
|
|
1039
|
-
* @param attempts retry attempts
|
|
1040
|
-
* @param baseDelay base delay ms
|
|
1041
|
-
* @returns Promise resolving to Response
|
|
1042
|
-
*/
|
|
1043
1048
|
/**
|
|
1044
1049
|
* Normalize different header-like shapes into a plain object.
|
|
1045
1050
|
* @param headerLike headers in Headers, array, or plain object form
|
|
@@ -1148,14 +1153,6 @@ var APIGitLib = (() => {
|
|
|
1148
1153
|
const jitter = Math.floor(Math.random() * base * 0.3);
|
|
1149
1154
|
return base + jitter;
|
|
1150
1155
|
}
|
|
1151
|
-
/**
|
|
1152
|
-
* Delegate to shared mapWithConcurrency implementation
|
|
1153
|
-
* @template T,R
|
|
1154
|
-
* @param items items to map
|
|
1155
|
-
* @param mapper async mapper
|
|
1156
|
-
* @param concurrency concurrency limit
|
|
1157
|
-
* @returns Promise resolving to mapped results
|
|
1158
|
-
*/
|
|
1159
1156
|
/**
|
|
1160
1157
|
* Map items with limited concurrency by delegating to the shared helper.
|
|
1161
1158
|
* @template T,R
|
|
@@ -1263,12 +1260,6 @@ var APIGitLib = (() => {
|
|
|
1263
1260
|
parents
|
|
1264
1261
|
};
|
|
1265
1262
|
}
|
|
1266
|
-
/**
|
|
1267
|
-
* コンテンツから sha1 を算出します。
|
|
1268
|
-
* @param {string} content コンテンツ
|
|
1269
|
-
* @returns {string} sha1 ハッシュ
|
|
1270
|
-
*/
|
|
1271
|
-
// shaOf is inherited from AbstractGitAdapter
|
|
1272
1263
|
/**
|
|
1273
1264
|
* ブロブを作成またはキャッシュから取得します。
|
|
1274
1265
|
* @param {any[]} changes 変更一覧(create/update を含む)
|
|
@@ -1283,11 +1274,6 @@ var APIGitLib = (() => {
|
|
|
1283
1274
|
map[r.path] = r.sha;
|
|
1284
1275
|
return map;
|
|
1285
1276
|
}
|
|
1286
|
-
/**
|
|
1287
|
-
* ブロブ作成用のヘルパー(createBlobs から抽出)
|
|
1288
|
-
* @param {any} ch 変更エントリ
|
|
1289
|
-
* @returns {Promise<{path:string,sha:string}>}
|
|
1290
|
-
*/
|
|
1291
1277
|
/**
|
|
1292
1278
|
* Create a blob for a change or return cached blobSha.
|
|
1293
1279
|
* @param {any} ch change entry
|
|
@@ -1494,10 +1480,6 @@ var APIGitLib = (() => {
|
|
|
1494
1480
|
}
|
|
1495
1481
|
throw new NonRetryableError("getRef: sha not found");
|
|
1496
1482
|
}
|
|
1497
|
-
/**
|
|
1498
|
-
* Helper to fetch a ref URL and extract a SHA if present.
|
|
1499
|
-
* Returns null when SHA not found.
|
|
1500
|
-
*/
|
|
1501
1483
|
/**
|
|
1502
1484
|
* Fetch a ref URL and extract a SHA if present.
|
|
1503
1485
|
* @param {string} url API URL to fetch
|
|
@@ -1592,18 +1574,6 @@ var APIGitLib = (() => {
|
|
|
1592
1574
|
const enc = index.encoding || "utf-8";
|
|
1593
1575
|
return { content: index.content, encoding: enc };
|
|
1594
1576
|
}
|
|
1595
|
-
/**
|
|
1596
|
-
* Resolve a commit-ish (branch, tag, or SHA) to a commit SHA.
|
|
1597
|
-
* Resolution order: branch -> tag -> commit endpoint -> treat as SHA
|
|
1598
|
-
* Throws if resolution fails.
|
|
1599
|
-
*/
|
|
1600
|
-
/**
|
|
1601
|
-
* Resolve a commit-ish (branch, tag, or SHA) to a commit SHA.
|
|
1602
|
-
* Resolution order: branch -> tag -> commit endpoint -> treat as SHA
|
|
1603
|
-
* Throws if resolution fails.
|
|
1604
|
-
* @param {string} reference commit-ish to resolve
|
|
1605
|
-
* @returns {Promise<string>} resolved commit SHA
|
|
1606
|
-
*/
|
|
1607
1577
|
/**
|
|
1608
1578
|
* Resolve a commit-ish (branch, tag, or SHA) to a commit SHA.
|
|
1609
1579
|
* Resolution order: branch -> tag -> commit endpoint -> treat as SHA
|
|
@@ -1643,11 +1613,6 @@ var APIGitLib = (() => {
|
|
|
1643
1613
|
}
|
|
1644
1614
|
return null;
|
|
1645
1615
|
}
|
|
1646
|
-
/**
|
|
1647
|
-
* Try to resolve reference as a branch and return its sha.
|
|
1648
|
-
* @param {string} reference branch name
|
|
1649
|
-
* @returns {Promise<string|null>} resolved sha or null
|
|
1650
|
-
*/
|
|
1651
1616
|
/**
|
|
1652
1617
|
* Try to resolve a branch name to a commit SHA.
|
|
1653
1618
|
* @param {string} reference branch name
|
|
@@ -1657,11 +1622,6 @@ var APIGitLib = (() => {
|
|
|
1657
1622
|
const sha = await this.getRef(`heads/${reference}`);
|
|
1658
1623
|
return sha || null;
|
|
1659
1624
|
}
|
|
1660
|
-
/**
|
|
1661
|
-
* Try to resolve reference as a tag and return its sha.
|
|
1662
|
-
* @param {string} reference tag name
|
|
1663
|
-
* @returns {Promise<string|null>} resolved sha or null
|
|
1664
|
-
*/
|
|
1665
1625
|
/**
|
|
1666
1626
|
* Try to resolve a tag name to a commit SHA.
|
|
1667
1627
|
* @param {string} reference tag name
|
|
@@ -1671,11 +1631,6 @@ var APIGitLib = (() => {
|
|
|
1671
1631
|
const sha = await this.getRef(`tags/${reference}`);
|
|
1672
1632
|
return sha || null;
|
|
1673
1633
|
}
|
|
1674
|
-
/**
|
|
1675
|
-
* Try to resolve reference using commits endpoint (could be SHA or other form).
|
|
1676
|
-
* @param {string} reference commit-ish
|
|
1677
|
-
* @returns {Promise<string|null>} resolved sha or null
|
|
1678
|
-
*/
|
|
1679
1634
|
/**
|
|
1680
1635
|
* Try to resolve via commits endpoint (may accept SHA or other forms).
|
|
1681
1636
|
* @param {string} reference commit-ish
|
|
@@ -1691,11 +1646,6 @@ var APIGitLib = (() => {
|
|
|
1691
1646
|
}
|
|
1692
1647
|
return null;
|
|
1693
1648
|
}
|
|
1694
|
-
/**
|
|
1695
|
-
* Blob を取得して content を返す。取得失敗時は content=null を返す。
|
|
1696
|
-
* @param {{sha:string,path:string}} f blob 情報
|
|
1697
|
-
* @returns {Promise<{path:string,content:string|null}>}
|
|
1698
|
-
*/
|
|
1699
1649
|
/**
|
|
1700
1650
|
* Fetch a blob's content; return null content on failure.
|
|
1701
1651
|
* @param {any} f blob metadata
|
|
@@ -1735,16 +1685,6 @@ var APIGitLib = (() => {
|
|
|
1735
1685
|
}
|
|
1736
1686
|
return safe;
|
|
1737
1687
|
}
|
|
1738
|
-
/**
|
|
1739
|
-
* Fetch a blob's content; return null content on failure.
|
|
1740
|
-
* @param {any} f blob metadata
|
|
1741
|
-
* @returns {Promise<{path:string,content:string|null}>}
|
|
1742
|
-
*/
|
|
1743
|
-
/**
|
|
1744
|
-
* リポジトリのスナップショットを取得します。
|
|
1745
|
-
* @param {string} branch ブランチ名 (default: 'main')
|
|
1746
|
-
* @returns {Promise<{headSha:string,shas:Record<string,string>,fetchContent:Function,snapshot:Record<string,string>}>}
|
|
1747
|
-
*/
|
|
1748
1688
|
/**
|
|
1749
1689
|
* Fetch repository snapshot: headSha, shas map and a fetchContent helper.
|
|
1750
1690
|
* @param {string} branch branch name
|
|
@@ -1819,22 +1759,6 @@ var APIGitLib = (() => {
|
|
|
1819
1759
|
out[p] = content;
|
|
1820
1760
|
return null;
|
|
1821
1761
|
}
|
|
1822
|
-
/**
|
|
1823
|
-
* 指定パスのコンテンツを取得し、キャッシュと snapshot を更新します。
|
|
1824
|
-
* @param {Map<string, any>} fileMap ファイルメタ情報マップ
|
|
1825
|
-
* @param {Map<string, string>} contentCache キャッシュマップ
|
|
1826
|
-
* @param {Record<string,string>} snapshot スナップショット出力マップ
|
|
1827
|
-
* @param {string} p 取得対象パス
|
|
1828
|
-
* @returns {Promise<string|null>} ファイル内容または null
|
|
1829
|
-
*/
|
|
1830
|
-
/**
|
|
1831
|
-
* Fetch single path content, update cache and snapshot.
|
|
1832
|
-
* @param {Map<string, any>} fileMap file map
|
|
1833
|
-
* @param {Map<string,string>} contentCache cache map
|
|
1834
|
-
* @param {Record<string,string>} snapshot snapshot map
|
|
1835
|
-
* @param {string} p path to fetch
|
|
1836
|
-
* @returns {Promise<string|null>} content or null
|
|
1837
|
-
*/
|
|
1838
1762
|
/**
|
|
1839
1763
|
* Fetch single path content, update cache and snapshot.
|
|
1840
1764
|
* @param {Map<string, any>} fileMap file map
|
|
@@ -1914,7 +1838,7 @@ var APIGitLib = (() => {
|
|
|
1914
1838
|
}
|
|
1915
1839
|
/**
|
|
1916
1840
|
* GitLab のページングヘッダを解析します(x-next-page / x-total-pages)。
|
|
1917
|
-
|
|
1841
|
+
* @returns {{nextPage?: number, lastPage?: number}} ページ番号情報
|
|
1918
1842
|
*/
|
|
1919
1843
|
_parsePagingHeaders(resp) {
|
|
1920
1844
|
const out = {};
|
|
@@ -1946,12 +1870,6 @@ var APIGitLib = (() => {
|
|
|
1946
1870
|
parents
|
|
1947
1871
|
};
|
|
1948
1872
|
}
|
|
1949
|
-
/**
|
|
1950
|
-
* コンテンツから sha1 を算出します。
|
|
1951
|
-
* @param {string} content コンテンツ
|
|
1952
|
-
* @returns {string} sha1 ハッシュ
|
|
1953
|
-
*/
|
|
1954
|
-
// shaOf is inherited from AbstractGitAdapter
|
|
1955
1873
|
/**
|
|
1956
1874
|
* 変更一覧から blob sha のマップを作成します(疑似実装)。
|
|
1957
1875
|
* @param {any[]} changes 変更一覧
|
|
@@ -2031,9 +1949,6 @@ var APIGitLib = (() => {
|
|
|
2031
1949
|
}
|
|
2032
1950
|
return await this.postCommit(url, body);
|
|
2033
1951
|
}
|
|
2034
|
-
/**
|
|
2035
|
-
* Retrieve project metadata (default branch, name, id) and cache it.
|
|
2036
|
-
*/
|
|
2037
1952
|
/**
|
|
2038
1953
|
* Retrieve project metadata (default branch, name, id) and cache it.
|
|
2039
1954
|
* @returns {Promise<import('../virtualfs/types.ts').RepositoryMetadata>} repository metadata
|
|
@@ -2132,7 +2047,7 @@ var APIGitLib = (() => {
|
|
|
2132
2047
|
}
|
|
2133
2048
|
/**
|
|
2134
2049
|
* Convert change descriptors to GitLab API actions
|
|
2135
|
-
|
|
2050
|
+
* @returns {Array<any>} API-compatible actions array
|
|
2136
2051
|
*/
|
|
2137
2052
|
createActions(changes) {
|
|
2138
2053
|
return changes.map((c) => {
|
|
@@ -2143,11 +2058,6 @@ var APIGitLib = (() => {
|
|
|
2143
2058
|
return { action: "update", file_path: c.path, content: c.content };
|
|
2144
2059
|
});
|
|
2145
2060
|
}
|
|
2146
|
-
/**
|
|
2147
|
-
* Verify remote branch head matches expected parent SHA.
|
|
2148
|
-
* @throws Error if non-fast-forward
|
|
2149
|
-
* @returns {Promise<void>}
|
|
2150
|
-
*/
|
|
2151
2061
|
/**
|
|
2152
2062
|
* Verify that remote branch head matches expected parent SHA.
|
|
2153
2063
|
* Throws when non-fast-forward detected.
|
|
@@ -2182,10 +2092,6 @@ var APIGitLib = (() => {
|
|
|
2182
2092
|
}
|
|
2183
2093
|
return index.id || index.commit || index;
|
|
2184
2094
|
}
|
|
2185
|
-
/**
|
|
2186
|
-
* Post commit request and parse response
|
|
2187
|
-
* @returns {Promise<any>}
|
|
2188
|
-
*/
|
|
2189
2095
|
/**
|
|
2190
2096
|
* Post commit request and return parsed commit response.
|
|
2191
2097
|
* @param {string} url endpoint URL
|
|
@@ -2197,19 +2103,6 @@ var APIGitLib = (() => {
|
|
|
2197
2103
|
const text = await response.text().catch(() => "");
|
|
2198
2104
|
return this.parseCommitResponse(text);
|
|
2199
2105
|
}
|
|
2200
|
-
/**
|
|
2201
|
-
* fetch をリトライ付きで実行します。
|
|
2202
|
-
* @param {string} url リクエスト URL
|
|
2203
|
-
* @param {RequestInit} opts fetch オプション
|
|
2204
|
-
* @param {number} [retries] 最大リトライ回数
|
|
2205
|
-
* @returns {Promise<Response>} レスポンス
|
|
2206
|
-
*/
|
|
2207
|
-
// fetchWithRetry is provided by AbstractGitAdapter
|
|
2208
|
-
/**
|
|
2209
|
-
* Wait helper for fetch retry backoff.
|
|
2210
|
-
* @param attempt Attempt number
|
|
2211
|
-
* @returns {Promise<void>} resolves after backoff
|
|
2212
|
-
*/
|
|
2213
2106
|
/**
|
|
2214
2107
|
* Wait helper for retry backoff.
|
|
2215
2108
|
* @param {number} attempt attempt number
|
|
@@ -2219,28 +2112,6 @@ var APIGitLib = (() => {
|
|
|
2219
2112
|
const wait = this.backoffMs(attempt);
|
|
2220
2113
|
await new Promise((r) => setTimeout(r, wait));
|
|
2221
2114
|
}
|
|
2222
|
-
/**
|
|
2223
|
-
* ステータスが再試行対象か判定します。
|
|
2224
|
-
* @param {number} status ステータスコード
|
|
2225
|
-
* @returns {boolean}
|
|
2226
|
-
*/
|
|
2227
|
-
// isRetryableStatus is provided by AbstractGitAdapter
|
|
2228
|
-
/**
|
|
2229
|
-
* バックオフ時間を計算します。
|
|
2230
|
-
* @param {number} attempt 試行回数(1..)
|
|
2231
|
-
* @returns {number} ミリ秒
|
|
2232
|
-
*/
|
|
2233
|
-
// backoffMs provided by AbstractGitAdapter
|
|
2234
|
-
// small concurrency mapper used for fetching files
|
|
2235
|
-
/**
|
|
2236
|
-
* 並列マッピングユーティリティ
|
|
2237
|
-
* @template T, R
|
|
2238
|
-
* @param {T[]} items 入力配列
|
|
2239
|
-
* @param {(t:T)=>Promise<R>} mapper マッピング関数
|
|
2240
|
-
* @param {number} concurrency 同時実行数
|
|
2241
|
-
* @returns {Promise<R[]>}
|
|
2242
|
-
*/
|
|
2243
|
-
// mapWithConcurrency provided by AbstractGitAdapter
|
|
2244
2115
|
/**
|
|
2245
2116
|
* Prepare JSON body for commit API call.
|
|
2246
2117
|
* @returns {string} JSON body
|
|
@@ -2248,9 +2119,6 @@ var APIGitLib = (() => {
|
|
|
2248
2119
|
_prepareCommitBody(branch, message, actions) {
|
|
2249
2120
|
return JSON.stringify({ branch, commit_message: message, actions });
|
|
2250
2121
|
}
|
|
2251
|
-
/**
|
|
2252
|
-
* Optionally verify parent SHA; swallow non-422 errors.
|
|
2253
|
-
*/
|
|
2254
2122
|
/**
|
|
2255
2123
|
* Optionally verify parent SHA; rethrow errors after logging.
|
|
2256
2124
|
* @param {string} expectedParentSha expected SHA
|
|
@@ -2269,7 +2137,7 @@ var APIGitLib = (() => {
|
|
|
2269
2137
|
/**
|
|
2270
2138
|
* リポジトリのスナップショットを取得します。
|
|
2271
2139
|
* @param {string} branch ブランチ名 (default: 'main')
|
|
2272
|
-
|
|
2140
|
+
* @returns {Promise<{headSha:string,shas:Record<string,string>,fetchContent:(paths:string[])=>Promise<Record<string,string>>}>}
|
|
2273
2141
|
*/
|
|
2274
2142
|
async fetchSnapshot(branch = "main", concurrency = 5) {
|
|
2275
2143
|
const headSha = await this._determineHeadSha(branch);
|
|
@@ -2279,11 +2147,6 @@ var APIGitLib = (() => {
|
|
|
2279
2147
|
const fetchContent = (paths) => this._fetchContentFromFileSet(fileSet, cache, snapshot, paths, branch, concurrency);
|
|
2280
2148
|
return { headSha, shas, fetchContent, snapshot };
|
|
2281
2149
|
}
|
|
2282
|
-
/**
|
|
2283
|
-
* Determine the remote head SHA for a branch. Falls back to branch name on error.
|
|
2284
|
-
* @param {string} branch Branch name
|
|
2285
|
-
* @returns {Promise<string>} head SHA or branch
|
|
2286
|
-
*/
|
|
2287
2150
|
/**
|
|
2288
2151
|
* Determine the head SHA for a branch; fallback to branch name if unavailable.
|
|
2289
2152
|
* @param {string} branch branch name
|
|
@@ -2306,11 +2169,6 @@ var APIGitLib = (() => {
|
|
|
2306
2169
|
}
|
|
2307
2170
|
return branch;
|
|
2308
2171
|
}
|
|
2309
|
-
/**
|
|
2310
|
-
* Fetch repository tree and build shas/fileSet.
|
|
2311
|
-
* @param {string} branch Branch name
|
|
2312
|
-
* @returns {Promise<{shas:Record<string,string>,fileSet:Set<string>}>}
|
|
2313
|
-
*/
|
|
2314
2172
|
/**
|
|
2315
2173
|
* Fetch repository tree and build shas map and fileSet.
|
|
2316
2174
|
* @param {string} branch branch name
|
|
@@ -2322,10 +2180,6 @@ var APIGitLib = (() => {
|
|
|
2322
2180
|
const files = Array.isArray(treeJ) ? treeJ.filter((t) => t.type === "blob") : [];
|
|
2323
2181
|
return this._buildShasAndFileSet(files);
|
|
2324
2182
|
}
|
|
2325
|
-
/**
|
|
2326
|
-
* Helper to fetch files from the repository tree with caching and concurrency.
|
|
2327
|
-
* @returns {Promise<Record<string,string>>}
|
|
2328
|
-
*/
|
|
2329
2183
|
/**
|
|
2330
2184
|
* Fetch contents for requested paths from a FileSet with caching.
|
|
2331
2185
|
* @param {Set<string>} fileSet set of available files
|
|
@@ -2347,14 +2201,6 @@ var APIGitLib = (() => {
|
|
|
2347
2201
|
}, concurrency);
|
|
2348
2202
|
return out;
|
|
2349
2203
|
}
|
|
2350
|
-
/**
|
|
2351
|
-
* 指定パスのファイル内容を取得し、キャッシュと snapshot を更新します。
|
|
2352
|
-
* @param {Map<string,string>} cache キャッシュマップ
|
|
2353
|
-
* @param {Record<string,string>} snapshot スナップショットマップ
|
|
2354
|
-
* @param {string} p ファイルパス
|
|
2355
|
-
* @param {string} branch ブランチ名
|
|
2356
|
-
* @returns {Promise<string|null>} ファイル内容または null
|
|
2357
|
-
*/
|
|
2358
2204
|
/**
|
|
2359
2205
|
* Fetch the content for a single file path, updating cache and snapshot.
|
|
2360
2206
|
* @param {Map<string,string>} cache cache map
|
|
@@ -2377,12 +2223,6 @@ var APIGitLib = (() => {
|
|
|
2377
2223
|
}
|
|
2378
2224
|
return null;
|
|
2379
2225
|
}
|
|
2380
|
-
/**
|
|
2381
|
-
* ファイルの raw コンテンツを取得して返します。失敗時は null を返します。
|
|
2382
|
-
* @param {string} path ファイルパス
|
|
2383
|
-
* @param {string} branch ブランチ名
|
|
2384
|
-
* @returns {Promise<string|null>} ファイル内容または null
|
|
2385
|
-
*/
|
|
2386
2226
|
/**
|
|
2387
2227
|
* Fetch raw file content from GitLab; return null on failure.
|
|
2388
2228
|
* @param {string} path file path
|
|
@@ -2404,7 +2244,6 @@ var APIGitLib = (() => {
|
|
|
2404
2244
|
return null;
|
|
2405
2245
|
}
|
|
2406
2246
|
}
|
|
2407
|
-
/** Build shas map and fileSet from tree entries */
|
|
2408
2247
|
/**
|
|
2409
2248
|
* Build shas map and fileSet from tree entries
|
|
2410
2249
|
* @returns {{shas:Record<string,string>,fileSet:Set<string>}}
|
|
@@ -2421,18 +2260,6 @@ var APIGitLib = (() => {
|
|
|
2421
2260
|
}
|
|
2422
2261
|
return { shas, fileSet };
|
|
2423
2262
|
}
|
|
2424
|
-
/**
|
|
2425
|
-
* Resolve a commit-ish (branch name, tag name, or SHA) to a commit SHA.
|
|
2426
|
-
* Resolution order: branch -> tag -> commits endpoint -> treat as SHA
|
|
2427
|
-
* Throws if not resolvable.
|
|
2428
|
-
*/
|
|
2429
|
-
/**
|
|
2430
|
-
* Resolve a commit-ish (branch, tag, or SHA) to a commit SHA.
|
|
2431
|
-
* Resolution order: branch -> tag -> commits endpoint -> treat as SHA
|
|
2432
|
-
* Throws if not resolvable.
|
|
2433
|
-
* @param {string} reference commit-ish to resolve
|
|
2434
|
-
* @returns {Promise<string>} resolved commit SHA
|
|
2435
|
-
*/
|
|
2436
2263
|
/**
|
|
2437
2264
|
* Resolve a commit-ish (branch, tag, or SHA) to a commit SHA.
|
|
2438
2265
|
* Resolution order: branch -> tag -> commits endpoint -> treat as SHA
|
|
@@ -3536,7 +3363,7 @@ var APIGitLib = (() => {
|
|
|
3536
3363
|
/**
|
|
3537
3364
|
* Read and parse the stored info entry for `path`.
|
|
3538
3365
|
* Returns parsed object or null when missing/invalid.
|
|
3539
|
-
|
|
3366
|
+
* @returns {Promise<any|null>} parsed object or null
|
|
3540
3367
|
*/
|
|
3541
3368
|
async _readInfoEntry(path) {
|
|
3542
3369
|
try {
|
|
@@ -3559,7 +3386,7 @@ var APIGitLib = (() => {
|
|
|
3559
3386
|
/**
|
|
3560
3387
|
* Attempt to fetch base content using adapterInstance.getBlob.
|
|
3561
3388
|
* Returns string when fetched, null when fetch attempted but failed, or undefined when adapter not supported.
|
|
3562
|
-
|
|
3389
|
+
* @returns {Promise<string|null|undefined>} fetched content, null, or undefined
|
|
3563
3390
|
*/
|
|
3564
3391
|
async _tryFetchBaseWithAdapter(adapterInstance, baseSha, path) {
|
|
3565
3392
|
if (!adapterInstance || typeof adapterInstance.getBlob !== "function")
|
|
@@ -3595,7 +3422,7 @@ var APIGitLib = (() => {
|
|
|
3595
3422
|
/**
|
|
3596
3423
|
* Attempt to fetch raw file using adapterInstance._fetchFileRaw if available.
|
|
3597
3424
|
* Returns string when fetched, null when attempted but failed, or undefined when adapter not supported.
|
|
3598
|
-
|
|
3425
|
+
* @returns {Promise<string|null|undefined>} fetched content, null, or undefined
|
|
3599
3426
|
*/
|
|
3600
3427
|
async _tryFetchRawFile(adapterInstance, path, ie) {
|
|
3601
3428
|
if (!adapterInstance || typeof adapterInstance._fetchFileRaw !== "function")
|
|
@@ -3703,7 +3530,7 @@ var APIGitLib = (() => {
|
|
|
3703
3530
|
if (options?.backend)
|
|
3704
3531
|
this.backend = options.backend;
|
|
3705
3532
|
else
|
|
3706
|
-
this.backend = new OpfsStorage();
|
|
3533
|
+
this.backend = new OpfsStorage("default");
|
|
3707
3534
|
if (options && options.logger)
|
|
3708
3535
|
this.logger = options.logger;
|
|
3709
3536
|
this.applier = new LocalChangeApplier(this.backend);
|
|
@@ -3743,22 +3570,6 @@ var APIGitLib = (() => {
|
|
|
3743
3570
|
set lastCommitKey(k) {
|
|
3744
3571
|
this.indexManager.setLastCommitKey(k);
|
|
3745
3572
|
}
|
|
3746
|
-
/**
|
|
3747
|
-
* SHA-1 helper wrapper (delegates to ./hashUtils)
|
|
3748
|
-
* @param {string} content - ハッシュ対象の文字列
|
|
3749
|
-
* @returns {Promise<string>} SHA-1 ハッシュの16進表現
|
|
3750
|
-
*/
|
|
3751
|
-
async shaOf(content) {
|
|
3752
|
-
return await shaOf2(content);
|
|
3753
|
-
}
|
|
3754
|
-
/**
|
|
3755
|
-
* SHA helper for Git blob formatting
|
|
3756
|
-
* @param {string} content - blob コンテンツ
|
|
3757
|
-
* @returns {Promise<string>} SHA-1 ハッシュの16進表現(git blob 用)
|
|
3758
|
-
*/
|
|
3759
|
-
async shaOfGitBlob(content) {
|
|
3760
|
-
return await shaOfGitBlob(content);
|
|
3761
|
-
}
|
|
3762
3573
|
/**
|
|
3763
3574
|
* VirtualFS の初期化を行います(バックエンド初期化と index 読み込み)。
|
|
3764
3575
|
* @returns {Promise<void>}
|
|
@@ -3898,6 +3709,312 @@ var APIGitLib = (() => {
|
|
|
3898
3709
|
}
|
|
3899
3710
|
return null;
|
|
3900
3711
|
}
|
|
3712
|
+
/**
|
|
3713
|
+
* Helper: obtain backend listFilesRaw in a safe manner.
|
|
3714
|
+
* @returns {Promise<any[]>}
|
|
3715
|
+
*/
|
|
3716
|
+
async _getBackendFilesRaw() {
|
|
3717
|
+
try {
|
|
3718
|
+
if (this.backend && typeof this.backend.listFilesRaw === "function") {
|
|
3719
|
+
return await this.backend.listFilesRaw();
|
|
3720
|
+
}
|
|
3721
|
+
} catch (error) {
|
|
3722
|
+
if (typeof console !== "undefined" && console.debug)
|
|
3723
|
+
console.debug("_getBackendFilesRaw failed", error);
|
|
3724
|
+
}
|
|
3725
|
+
return [];
|
|
3726
|
+
}
|
|
3727
|
+
/**
|
|
3728
|
+
* Helper: apply parsed info text into stats object when possible.
|
|
3729
|
+
* @param infoTxt raw info text
|
|
3730
|
+
* @param stats stats object to mutate
|
|
3731
|
+
* @returns {void}
|
|
3732
|
+
*/
|
|
3733
|
+
_applyInfoTxtToStats(infoTxt, stats) {
|
|
3734
|
+
if (!infoTxt)
|
|
3735
|
+
return;
|
|
3736
|
+
try {
|
|
3737
|
+
const info = JSON.parse(infoTxt);
|
|
3738
|
+
if (typeof info.baseSha === "string")
|
|
3739
|
+
stats.gitBlobSha = info.baseSha;
|
|
3740
|
+
if (typeof info.updatedAt === "number")
|
|
3741
|
+
stats.mtime = new Date(info.updatedAt);
|
|
3742
|
+
if (typeof info.size === "number")
|
|
3743
|
+
stats.size = info.size;
|
|
3744
|
+
} catch (error) {
|
|
3745
|
+
if (typeof console !== "undefined" && console.debug)
|
|
3746
|
+
console.debug("parse info failed", error);
|
|
3747
|
+
}
|
|
3748
|
+
}
|
|
3749
|
+
/**
|
|
3750
|
+
* Find a matched backend entry for the given filepath.
|
|
3751
|
+
* @param filepath target filepath
|
|
3752
|
+
* @param filesRaw backend raw listing
|
|
3753
|
+
* @returns {any|null}
|
|
3754
|
+
*/
|
|
3755
|
+
_findMatchedFile(filepath, filesRaw) {
|
|
3756
|
+
if (!Array.isArray(filesRaw))
|
|
3757
|
+
return null;
|
|
3758
|
+
return filesRaw.find((f) => {
|
|
3759
|
+
if (!f || !f.path)
|
|
3760
|
+
return false;
|
|
3761
|
+
return f.path === filepath || f.path.endsWith("/" + filepath) || f.path.endsWith("/" + filepath);
|
|
3762
|
+
});
|
|
3763
|
+
}
|
|
3764
|
+
/**
|
|
3765
|
+
* Create default stats object with consistent shape.
|
|
3766
|
+
* @param now current Date
|
|
3767
|
+
* @returns {any}
|
|
3768
|
+
*/
|
|
3769
|
+
_createDefaultStats(now) {
|
|
3770
|
+
return {
|
|
3771
|
+
dev: 0,
|
|
3772
|
+
ino: 0,
|
|
3773
|
+
mode: 33188,
|
|
3774
|
+
nlink: 1,
|
|
3775
|
+
uid: 0,
|
|
3776
|
+
gid: 0,
|
|
3777
|
+
rdev: 0,
|
|
3778
|
+
size: 0,
|
|
3779
|
+
blksize: void 0,
|
|
3780
|
+
blocks: void 0,
|
|
3781
|
+
atime: now,
|
|
3782
|
+
mtime: now,
|
|
3783
|
+
ctime: now,
|
|
3784
|
+
birthtime: now,
|
|
3785
|
+
/** @returns {boolean} */
|
|
3786
|
+
isFile: () => true,
|
|
3787
|
+
/** @returns {boolean} */
|
|
3788
|
+
isDirectory: () => false
|
|
3789
|
+
};
|
|
3790
|
+
}
|
|
3791
|
+
/**
|
|
3792
|
+
* Populate stats.gitCommitSha from adapterMeta if available.
|
|
3793
|
+
* @param stats stats object to mutate
|
|
3794
|
+
* @returns {void}
|
|
3795
|
+
*/
|
|
3796
|
+
_populateCommitShaFromMeta(stats) {
|
|
3797
|
+
if (!stats.gitCommitSha && this.adapterMeta && this.adapterMeta.opts && this.adapterMeta.opts.branch) {
|
|
3798
|
+
stats.gitCommitSha = this.adapterMeta.opts.branch;
|
|
3799
|
+
}
|
|
3800
|
+
}
|
|
3801
|
+
/**
|
|
3802
|
+
* Try to resolve commit SHA from an instantiated adapter when needed.
|
|
3803
|
+
* @param stats stats object to mutate
|
|
3804
|
+
* @returns {Promise<void>}
|
|
3805
|
+
*/
|
|
3806
|
+
async _resolveCommitShaFromAdapter(stats) {
|
|
3807
|
+
const instAdapter = await this._safeGetAdapterInstance();
|
|
3808
|
+
if (!instAdapter || stats.gitCommitSha)
|
|
3809
|
+
return;
|
|
3810
|
+
if (typeof instAdapter.resolveRef !== "function")
|
|
3811
|
+
return;
|
|
3812
|
+
try {
|
|
3813
|
+
const branch = this.adapterMeta && this.adapterMeta.opts && this.adapterMeta.opts.branch || "main";
|
|
3814
|
+
const resolved = await instAdapter.resolveRef(branch);
|
|
3815
|
+
if (resolved)
|
|
3816
|
+
stats.gitCommitSha = resolved;
|
|
3817
|
+
} catch (error) {
|
|
3818
|
+
if (typeof console !== "undefined" && console.debug)
|
|
3819
|
+
console.debug("_resolveCommitShaFromAdapter resolveRef failed", error);
|
|
3820
|
+
}
|
|
3821
|
+
}
|
|
3822
|
+
/**
|
|
3823
|
+
* Safely get adapter instance, returning null on error.
|
|
3824
|
+
* @returns {Promise<any|null>}
|
|
3825
|
+
*/
|
|
3826
|
+
async _safeGetAdapterInstance() {
|
|
3827
|
+
try {
|
|
3828
|
+
return await this.getAdapterInstance();
|
|
3829
|
+
} catch (error) {
|
|
3830
|
+
if (typeof console !== "undefined" && console.debug)
|
|
3831
|
+
console.debug("_safeGetAdapterInstance failed", error);
|
|
3832
|
+
return null;
|
|
3833
|
+
}
|
|
3834
|
+
}
|
|
3835
|
+
/**
|
|
3836
|
+
* Helper: populate stats.gitCommitSha using adapterMeta or adapter.resolveRef when available.
|
|
3837
|
+
* @param stats stats object to mutate
|
|
3838
|
+
* @returns {Promise<void>}
|
|
3839
|
+
*/
|
|
3840
|
+
async _resolveAdapterCommitShaIfNeeded(stats) {
|
|
3841
|
+
this._populateCommitShaFromMeta(stats);
|
|
3842
|
+
if (!stats.gitCommitSha)
|
|
3843
|
+
await this._resolveCommitShaFromAdapter(stats);
|
|
3844
|
+
}
|
|
3845
|
+
/**
|
|
3846
|
+
* Determine whether a normalized path is an exact file entry in the provided entries.
|
|
3847
|
+
* @param normalizedDirectory normalized directory string
|
|
3848
|
+
* @param keys index keys array
|
|
3849
|
+
* @param entries index entries object
|
|
3850
|
+
* @returns {boolean}
|
|
3851
|
+
*/
|
|
3852
|
+
_isExactFile(normalizedDirectory, keys, entries) {
|
|
3853
|
+
return keys.includes(normalizedDirectory) && entries[normalizedDirectory] && entries[normalizedDirectory].state !== "deleted";
|
|
3854
|
+
}
|
|
3855
|
+
/**
|
|
3856
|
+
* Collect immediate child names from index entries for given directory.
|
|
3857
|
+
* @param normalizedDirectory normalized directory string
|
|
3858
|
+
* @param entries index entries object
|
|
3859
|
+
* @returns {Set<string>} set of immediate child names
|
|
3860
|
+
*/
|
|
3861
|
+
_collectNamesFromIndex(normalizedDirectory, entries) {
|
|
3862
|
+
const outNames = /* @__PURE__ */ new Set();
|
|
3863
|
+
const keys = Object.keys(entries || {});
|
|
3864
|
+
for (const k of keys) {
|
|
3865
|
+
const v = entries[k];
|
|
3866
|
+
if (v && v.state === "deleted")
|
|
3867
|
+
continue;
|
|
3868
|
+
if (normalizedDirectory === "." || normalizedDirectory === "") {
|
|
3869
|
+
this._collectNamesFromIndexRoot(k, outNames);
|
|
3870
|
+
continue;
|
|
3871
|
+
}
|
|
3872
|
+
this._processIndexKeyForDirectory(k, normalizedDirectory, outNames);
|
|
3873
|
+
}
|
|
3874
|
+
return outNames;
|
|
3875
|
+
}
|
|
3876
|
+
/**
|
|
3877
|
+
* Process a single index key for a non-root directory and add immediate child when applicable.
|
|
3878
|
+
* @param key index key
|
|
3879
|
+
* @param normalizedDirectory normalized directory string
|
|
3880
|
+
* @param outNames set to mutate
|
|
3881
|
+
* @returns {void}
|
|
3882
|
+
*/
|
|
3883
|
+
_processIndexKeyForDirectory(key, normalizedDirectory, outNames) {
|
|
3884
|
+
if (key === normalizedDirectory)
|
|
3885
|
+
return;
|
|
3886
|
+
if (key.startsWith(normalizedDirectory + "/")) {
|
|
3887
|
+
const rest = key.slice(normalizedDirectory.length + 1);
|
|
3888
|
+
const first = rest.indexOf("/") === -1 ? rest : rest.slice(0, rest.indexOf("/"));
|
|
3889
|
+
outNames.add(first);
|
|
3890
|
+
}
|
|
3891
|
+
}
|
|
3892
|
+
/**
|
|
3893
|
+
* Safe wrapper for backend.listFiles returning [] on failure.
|
|
3894
|
+
* @param normalizedDirectory directory path
|
|
3895
|
+
* @returns {Promise<any[]>}
|
|
3896
|
+
*/
|
|
3897
|
+
async _getBackendList(normalizedDirectory) {
|
|
3898
|
+
try {
|
|
3899
|
+
return await this.backend.listFiles(normalizedDirectory, void 0, false);
|
|
3900
|
+
} catch (error) {
|
|
3901
|
+
if (typeof console !== "undefined" && console.debug)
|
|
3902
|
+
console.debug("_getBackendList failed", error);
|
|
3903
|
+
return [];
|
|
3904
|
+
}
|
|
3905
|
+
}
|
|
3906
|
+
/**
|
|
3907
|
+
* Helper for collecting names when scanning root directory entries.
|
|
3908
|
+
* @param key index key
|
|
3909
|
+
* @param outNames set to mutate
|
|
3910
|
+
* @returns {void}
|
|
3911
|
+
*/
|
|
3912
|
+
_collectNamesFromIndexRoot(key, outNames) {
|
|
3913
|
+
const first = key.indexOf("/") === -1 ? key : key.slice(0, key.indexOf("/"));
|
|
3914
|
+
outNames.add(first);
|
|
3915
|
+
}
|
|
3916
|
+
/**
|
|
3917
|
+
* Check whether normalizedDirectory corresponds to an exact file entry.
|
|
3918
|
+
* @param normalizedDirectory normalized directory string
|
|
3919
|
+
* @param entries index entries object
|
|
3920
|
+
* @returns {boolean}
|
|
3921
|
+
*/
|
|
3922
|
+
_hasExactEntry(normalizedDirectory, entries) {
|
|
3923
|
+
const keys = Object.keys(entries || {});
|
|
3924
|
+
return this._isExactFile(normalizedDirectory, keys, entries);
|
|
3925
|
+
}
|
|
3926
|
+
/**
|
|
3927
|
+
* Consult backend.listFiles to collect immediate child names for given directory.
|
|
3928
|
+
* Best-effort: logs and returns empty set on failure.
|
|
3929
|
+
* @param normalizedDirectory normalized directory string
|
|
3930
|
+
* @returns {Promise<Set<string>>}
|
|
3931
|
+
*/
|
|
3932
|
+
async _collectNamesFromBackend(normalizedDirectory) {
|
|
3933
|
+
const outNames = /* @__PURE__ */ new Set();
|
|
3934
|
+
if (!this._backendSupportsListFiles())
|
|
3935
|
+
return outNames;
|
|
3936
|
+
const backendList = await this._getBackendList(normalizedDirectory);
|
|
3937
|
+
if (!Array.isArray(backendList) || backendList.length === 0)
|
|
3938
|
+
return outNames;
|
|
3939
|
+
for (const it of backendList)
|
|
3940
|
+
this._processBackendEntry(it, normalizedDirectory, outNames);
|
|
3941
|
+
return outNames;
|
|
3942
|
+
}
|
|
3943
|
+
/**
|
|
3944
|
+
* Return true when backend supports listFiles
|
|
3945
|
+
* @returns {boolean}
|
|
3946
|
+
*/
|
|
3947
|
+
_backendSupportsListFiles() {
|
|
3948
|
+
return !!(this.backend && typeof this.backend.listFiles === "function");
|
|
3949
|
+
}
|
|
3950
|
+
/**
|
|
3951
|
+
* Process a single backend listFiles entry and add immediate child name to outNames when applicable.
|
|
3952
|
+
* @param it backend entry
|
|
3953
|
+
* @param normalizedDirectory normalized directory string
|
|
3954
|
+
* @param outNames set to mutate
|
|
3955
|
+
* @returns {void}
|
|
3956
|
+
*/
|
|
3957
|
+
_processBackendEntry(it, normalizedDirectory, outNames) {
|
|
3958
|
+
try {
|
|
3959
|
+
if (!it || !it.path)
|
|
3960
|
+
return;
|
|
3961
|
+
const p = it.path;
|
|
3962
|
+
if (p === normalizedDirectory)
|
|
3963
|
+
return;
|
|
3964
|
+
if (p.startsWith(normalizedDirectory + "/")) {
|
|
3965
|
+
const rest = p.slice(normalizedDirectory.length + 1);
|
|
3966
|
+
const first = rest.indexOf("/") === -1 ? rest : rest.slice(0, rest.indexOf("/"));
|
|
3967
|
+
outNames.add(first);
|
|
3968
|
+
}
|
|
3969
|
+
} catch (error) {
|
|
3970
|
+
if (typeof console !== "undefined" && console.debug)
|
|
3971
|
+
console.debug("_processBackendEntry failed", error);
|
|
3972
|
+
}
|
|
3973
|
+
}
|
|
3974
|
+
/**
|
|
3975
|
+
* Build Dirent-like lightweight objects for given names.
|
|
3976
|
+
* @param names array of names
|
|
3977
|
+
* @param keys array of index keys
|
|
3978
|
+
* @param entries index entries object
|
|
3979
|
+
* @param normalizedDirectory normalized directory string
|
|
3980
|
+
* @returns {Array<any>} array of Dirent-like objects
|
|
3981
|
+
*/
|
|
3982
|
+
_buildDirentTypes(names, keys, entries, normalizedDirectory) {
|
|
3983
|
+
const out = [];
|
|
3984
|
+
for (const name of names) {
|
|
3985
|
+
const childPath = normalizedDirectory === "." ? name : `${normalizedDirectory}/${name}`;
|
|
3986
|
+
const { isFile, isDirectory } = this._determineChildType(childPath, keys, entries);
|
|
3987
|
+
const _isFileFunction = function() {
|
|
3988
|
+
return isFile && !isDirectory;
|
|
3989
|
+
};
|
|
3990
|
+
const _isDirectoryFunction = function() {
|
|
3991
|
+
return isDirectory;
|
|
3992
|
+
};
|
|
3993
|
+
out.push({ name, isFile: _isFileFunction, isDirectory: _isDirectoryFunction });
|
|
3994
|
+
}
|
|
3995
|
+
return out;
|
|
3996
|
+
}
|
|
3997
|
+
/**
|
|
3998
|
+
* Determine whether a childPath corresponds to a file, directory, or both.
|
|
3999
|
+
* @param childPath path of child
|
|
4000
|
+
* @param keys index keys
|
|
4001
|
+
* @param entries index entries
|
|
4002
|
+
* @returns {{isFile:boolean,isDirectory:boolean}}
|
|
4003
|
+
*/
|
|
4004
|
+
_determineChildType(childPath, keys, entries) {
|
|
4005
|
+
let isDirectory = false;
|
|
4006
|
+
let isFile = false;
|
|
4007
|
+
for (const k of keys) {
|
|
4008
|
+
if (k === childPath && entries[k] && entries[k].state !== "deleted") {
|
|
4009
|
+
isFile = true;
|
|
4010
|
+
}
|
|
4011
|
+
if (k.startsWith(childPath + "/")) {
|
|
4012
|
+
isDirectory = true;
|
|
4013
|
+
break;
|
|
4014
|
+
}
|
|
4015
|
+
}
|
|
4016
|
+
return { isFile, isDirectory };
|
|
4017
|
+
}
|
|
3901
4018
|
/**
|
|
3902
4019
|
* Return persisted adapter metadata (if any).
|
|
3903
4020
|
* @returns {any|null}
|
|
@@ -3915,15 +4032,6 @@ var APIGitLib = (() => {
|
|
|
3915
4032
|
await this.localFileManager.writeFile(filepath, content);
|
|
3916
4033
|
await this.loadIndex();
|
|
3917
4034
|
}
|
|
3918
|
-
/**
|
|
3919
|
-
* ファイルを削除します(トゥームストーン作成を含む)。
|
|
3920
|
-
* @param {string} filepath ファイルパス
|
|
3921
|
-
* @returns {Promise<void>}
|
|
3922
|
-
*/
|
|
3923
|
-
async deleteFile(filepath) {
|
|
3924
|
-
await this.localFileManager.deleteFile(filepath);
|
|
3925
|
-
await this.loadIndex();
|
|
3926
|
-
}
|
|
3927
4035
|
/**
|
|
3928
4036
|
* rename を delete + create の合成で行うヘルパ
|
|
3929
4037
|
* @param from 元パス
|
|
@@ -3934,7 +4042,7 @@ var APIGitLib = (() => {
|
|
|
3934
4042
|
if (content === null)
|
|
3935
4043
|
throw new Error("source not found");
|
|
3936
4044
|
await this.writeFile(to, content);
|
|
3937
|
-
await this.
|
|
4045
|
+
await this.unlink(from);
|
|
3938
4046
|
}
|
|
3939
4047
|
/**
|
|
3940
4048
|
* ワークスペース/ベースからファイル内容を読み出します。
|
|
@@ -3951,7 +4059,9 @@ var APIGitLib = (() => {
|
|
|
3951
4059
|
await this.remoteSynchronizer.fetchBaseIfMissing(filepath, adapter);
|
|
3952
4060
|
content = await this.localFileManager.readFile(filepath);
|
|
3953
4061
|
}
|
|
3954
|
-
} catch {
|
|
4062
|
+
} catch (error) {
|
|
4063
|
+
if (typeof console !== "undefined" && console.debug)
|
|
4064
|
+
console.debug("readFile on-demand fetch failed", error);
|
|
3955
4065
|
}
|
|
3956
4066
|
return content;
|
|
3957
4067
|
}
|
|
@@ -3963,6 +4073,151 @@ var APIGitLib = (() => {
|
|
|
3963
4073
|
async readConflict(filepath) {
|
|
3964
4074
|
return await this.conflictManager.readConflict(filepath);
|
|
3965
4075
|
}
|
|
4076
|
+
/**
|
|
4077
|
+
* fs.stat 互換: 指定ファイルのメタ情報を返す
|
|
4078
|
+
* ワークスペース上の情報を優先し、未取得時は Git のメタ情報で補完する。
|
|
4079
|
+
* @returns {Promise<any>} stats オブジェクト
|
|
4080
|
+
*/
|
|
4081
|
+
async stat(filepath) {
|
|
4082
|
+
if (!filepath || typeof filepath !== "string")
|
|
4083
|
+
throw new TypeError("filepath is required");
|
|
4084
|
+
const filesRaw = await this._getBackendFilesRaw();
|
|
4085
|
+
const matched = this._findMatchedFile(filepath, filesRaw);
|
|
4086
|
+
const now = /* @__PURE__ */ new Date();
|
|
4087
|
+
const stats = this._createDefaultStats(now);
|
|
4088
|
+
try {
|
|
4089
|
+
const infoTxt = await this.backend.readBlob(filepath, "info");
|
|
4090
|
+
this._applyInfoTxtToStats(infoTxt, stats);
|
|
4091
|
+
} catch (error) {
|
|
4092
|
+
if (typeof console !== "undefined" && console.debug)
|
|
4093
|
+
console.debug("stat readBlob failed", error);
|
|
4094
|
+
}
|
|
4095
|
+
if (matched) {
|
|
4096
|
+
stats.workspacePath = matched.path;
|
|
4097
|
+
}
|
|
4098
|
+
await this._resolveAdapterCommitShaIfNeeded(stats);
|
|
4099
|
+
return stats;
|
|
4100
|
+
}
|
|
4101
|
+
/**
|
|
4102
|
+
* fs.unlink 互換: ファイルを削除する
|
|
4103
|
+
*/
|
|
4104
|
+
async unlink(filepath) {
|
|
4105
|
+
if (!filepath || typeof filepath !== "string")
|
|
4106
|
+
throw new TypeError("filepath is required");
|
|
4107
|
+
await this.localFileManager.deleteFile(filepath);
|
|
4108
|
+
await this.loadIndex();
|
|
4109
|
+
}
|
|
4110
|
+
/**
|
|
4111
|
+
* fs.mkdir 互換 (簡易実装): workspace 側にディレクトリ情報を書き込む
|
|
4112
|
+
*/
|
|
4113
|
+
async mkdir(dirpath, _options) {
|
|
4114
|
+
if (!dirpath || typeof dirpath !== "string")
|
|
4115
|
+
throw new TypeError("dirpath is required");
|
|
4116
|
+
const info = { path: dirpath, state: "dir", createdAt: Date.now() };
|
|
4117
|
+
if (this.backend && typeof this.backend.writeBlob === "function") {
|
|
4118
|
+
await this.backend.writeBlob(dirpath, JSON.stringify(info), "info-workspace").catch((error) => {
|
|
4119
|
+
throw Object.assign(new Error("\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u4F5C\u6210\u5931\u6557"), { code: "EEXIST", cause: error });
|
|
4120
|
+
});
|
|
4121
|
+
}
|
|
4122
|
+
}
|
|
4123
|
+
/**
|
|
4124
|
+
* fs.rmdir 互換 (簡易実装)
|
|
4125
|
+
*/
|
|
4126
|
+
async rmdir(dirpath, options) {
|
|
4127
|
+
if (!dirpath || typeof dirpath !== "string")
|
|
4128
|
+
throw new TypeError("dirpath is required");
|
|
4129
|
+
const children = await this._listChildrenOfDir(dirpath);
|
|
4130
|
+
if (children.length > 0 && !(options && options.recursive)) {
|
|
4131
|
+
const errorObject = new Error("Directory not empty");
|
|
4132
|
+
errorObject.code = "ENOTEMPTY";
|
|
4133
|
+
throw errorObject;
|
|
4134
|
+
}
|
|
4135
|
+
if (options && options.recursive)
|
|
4136
|
+
await this._deleteChildrenRecursive(children);
|
|
4137
|
+
}
|
|
4138
|
+
/**
|
|
4139
|
+
* Return list of child paths for given dirpath based on index entries.
|
|
4140
|
+
* @param dirpath directory path
|
|
4141
|
+
* @returns {Promise<string[]>}
|
|
4142
|
+
*/
|
|
4143
|
+
async _listChildrenOfDir(dirpath) {
|
|
4144
|
+
const paths = await this.listPaths();
|
|
4145
|
+
return paths.filter((p) => p === dirpath || p.startsWith(dirpath + "/"));
|
|
4146
|
+
}
|
|
4147
|
+
/**
|
|
4148
|
+
* Delete array of children using localFileManager, logging failures per-child.
|
|
4149
|
+
* @param children array of paths
|
|
4150
|
+
* @returns {Promise<void>}
|
|
4151
|
+
*/
|
|
4152
|
+
async _deleteChildrenRecursive(children) {
|
|
4153
|
+
for (const p of children) {
|
|
4154
|
+
try {
|
|
4155
|
+
await this.localFileManager.deleteFile(p);
|
|
4156
|
+
} catch (error) {
|
|
4157
|
+
if (typeof console !== "undefined" && console.debug)
|
|
4158
|
+
console.debug("rmdir recursive delete failed for", p, error);
|
|
4159
|
+
}
|
|
4160
|
+
}
|
|
4161
|
+
}
|
|
4162
|
+
/**
|
|
4163
|
+
* fs.readdir 互換 (簡易実装)
|
|
4164
|
+
* @returns {Promise<string[]|Array<any>>}
|
|
4165
|
+
*/
|
|
4166
|
+
async readdir(dirpath, options) {
|
|
4167
|
+
if (!dirpath || typeof dirpath !== "string")
|
|
4168
|
+
throw new TypeError("dirpath is required");
|
|
4169
|
+
const index = await this.indexManager.getIndex();
|
|
4170
|
+
const entries = index && index.entries || {};
|
|
4171
|
+
const keys = Object.keys(entries);
|
|
4172
|
+
const names = await this._gatherDirectoryNames(dirpath, entries, keys);
|
|
4173
|
+
const maybeEmpty = this._returnIfNoNames(names, options);
|
|
4174
|
+
if (maybeEmpty !== null)
|
|
4175
|
+
return maybeEmpty;
|
|
4176
|
+
const normalizedDirectory = dirpath === "" ? "." : dirpath;
|
|
4177
|
+
if (options && options.withFileTypes)
|
|
4178
|
+
return this._buildDirentTypes(names, keys, entries, normalizedDirectory);
|
|
4179
|
+
return names;
|
|
4180
|
+
}
|
|
4181
|
+
/**
|
|
4182
|
+
* Return an empty array when names is empty according to options, else null to continue.
|
|
4183
|
+
* @param names array of names
|
|
4184
|
+
* @param options readdir options
|
|
4185
|
+
* @returns {Array<any>|null}
|
|
4186
|
+
*/
|
|
4187
|
+
_returnIfNoNames(names, options) {
|
|
4188
|
+
if (!names || names.length === 0)
|
|
4189
|
+
return options && options.withFileTypes ? [] : [];
|
|
4190
|
+
return null;
|
|
4191
|
+
}
|
|
4192
|
+
/**
|
|
4193
|
+
* Gather immediate child names for a directory using index and backend as fallback.
|
|
4194
|
+
* Throws ENOTDIR when the path represents a file.
|
|
4195
|
+
* @param dirpath original directory path
|
|
4196
|
+
* @param entries index entries object
|
|
4197
|
+
* @param keys array of index keys
|
|
4198
|
+
* @returns {Promise<string[]>} immediate child names
|
|
4199
|
+
*/
|
|
4200
|
+
async _gatherDirectoryNames(dirpath, entries, keys) {
|
|
4201
|
+
const normalizedDirectory = dirpath === "" ? "." : dirpath;
|
|
4202
|
+
const outNames = /* @__PURE__ */ new Set();
|
|
4203
|
+
const isExactFile = this._isExactFile(normalizedDirectory, keys, entries);
|
|
4204
|
+
const indexNames = this._collectNamesFromIndex(normalizedDirectory, entries);
|
|
4205
|
+
for (const n of indexNames)
|
|
4206
|
+
outNames.add(n);
|
|
4207
|
+
if (outNames.size === 0 && normalizedDirectory !== "." && !isExactFile) {
|
|
4208
|
+
const backendNames = await this._collectNamesFromBackend(normalizedDirectory);
|
|
4209
|
+
for (const n of backendNames)
|
|
4210
|
+
outNames.add(n);
|
|
4211
|
+
}
|
|
4212
|
+
if (isExactFile && outNames.size === 0) {
|
|
4213
|
+
const errorObject = new Error("\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u3067\u306F\u3042\u308A\u307E\u305B\u3093");
|
|
4214
|
+
errorObject.code = "ENOTDIR";
|
|
4215
|
+
throw errorObject;
|
|
4216
|
+
}
|
|
4217
|
+
if (outNames.size === 0)
|
|
4218
|
+
return [];
|
|
4219
|
+
return Array.from(outNames);
|
|
4220
|
+
}
|
|
3966
4221
|
/**
|
|
3967
4222
|
* 指定パスのリモート衝突ファイル (.git-conflict/) を削除して
|
|
3968
4223
|
* 競合を解消済とマークします。
|
|
@@ -4006,12 +4261,9 @@ var APIGitLib = (() => {
|
|
|
4006
4261
|
const entries = index && index.entries || {};
|
|
4007
4262
|
const out = [];
|
|
4008
4263
|
for (const k of Object.keys(entries)) {
|
|
4009
|
-
|
|
4010
|
-
|
|
4011
|
-
|
|
4012
|
-
continue;
|
|
4013
|
-
} catch {
|
|
4014
|
-
}
|
|
4264
|
+
const v = entries[k];
|
|
4265
|
+
if (v && v.state === "deleted")
|
|
4266
|
+
continue;
|
|
4015
4267
|
out.push(k);
|
|
4016
4268
|
}
|
|
4017
4269
|
return out;
|
|
@@ -4067,7 +4319,9 @@ var APIGitLib = (() => {
|
|
|
4067
4319
|
if (input.parentSha && typeof adapter.getCommitTreeSha === "function") {
|
|
4068
4320
|
try {
|
|
4069
4321
|
baseTreeSha = await adapter.getCommitTreeSha(input.parentSha);
|
|
4070
|
-
} catch {
|
|
4322
|
+
} catch (error) {
|
|
4323
|
+
if (typeof console !== "undefined" && console.debug)
|
|
4324
|
+
console.debug("getCommitTreeSha failed, continuing without baseTree", error);
|
|
4071
4325
|
baseTreeSha = void 0;
|
|
4072
4326
|
}
|
|
4073
4327
|
}
|
|
@@ -4226,7 +4480,7 @@ apigit-commit-key:${input.commitKey}`;
|
|
|
4226
4480
|
/**
|
|
4227
4481
|
* Persist the requested branch into adapter metadata (best-effort).
|
|
4228
4482
|
* @param {string} branch branch name to persist
|
|
4229
|
-
|
|
4483
|
+
* @returns {Promise<void>}
|
|
4230
4484
|
*/
|
|
4231
4485
|
async _persistAdapterBranchMeta(branch, adapterInstance) {
|
|
4232
4486
|
const meta = this.adapterMeta && this.adapterMeta.opts ? { ...this.adapterMeta } : await this.getAdapter();
|
|
@@ -4430,7 +4684,7 @@ apigit-commit-key:${input.commitKey}`;
|
|
|
4430
4684
|
/**
|
|
4431
4685
|
* Convenience to get default branch name from adapter repository metadata.
|
|
4432
4686
|
* Returns null when adapter not available.
|
|
4433
|
-
|
|
4687
|
+
* @returns {Promise<string|null>}
|
|
4434
4688
|
*/
|
|
4435
4689
|
async getDefaultBranch() {
|
|
4436
4690
|
const instAdapter = await this.getAdapterInstance();
|
|
@@ -4470,11 +4724,6 @@ apigit-commit-key:${input.commitKey}`;
|
|
|
4470
4724
|
console.debug("persist repository metadata aborted", error);
|
|
4471
4725
|
}
|
|
4472
4726
|
}
|
|
4473
|
-
/**
|
|
4474
|
-
* Normalize a resolved descriptor (string headSha or object) into a
|
|
4475
|
-
* RemoteSnapshotDescriptor or null. Helper to reduce cognitive complexity.
|
|
4476
|
-
* @returns {Promise<RemoteSnapshotDescriptor|null>} 正規化された descriptor または null
|
|
4477
|
-
*/
|
|
4478
4727
|
/**
|
|
4479
4728
|
* Try persisting repository metadata when available. Best-effort.
|
|
4480
4729
|
* @param instAdapter adapter instance or null
|
|
@@ -4578,7 +4827,7 @@ apigit-commit-key:${input.commitKey}`;
|
|
|
4578
4827
|
input.changes = await this.getChangeSet();
|
|
4579
4828
|
}
|
|
4580
4829
|
if (!input.commitKey) {
|
|
4581
|
-
input.commitKey = await
|
|
4830
|
+
input.commitKey = await shaOf2((input.parentSha || "") + JSON.stringify(input.changes));
|
|
4582
4831
|
}
|
|
4583
4832
|
const instAdapter = await this.getAdapterInstance();
|
|
4584
4833
|
if (!instAdapter) {
|
|
@@ -4607,7 +4856,7 @@ apigit-commit-key:${input.commitKey}`;
|
|
|
4607
4856
|
var IndexedDatabaseStorage = class IndexedDatabaseStorage2 {
|
|
4608
4857
|
/**
|
|
4609
4858
|
* 環境に IndexedDB が存在するかを同期検査します。
|
|
4610
|
-
|
|
4859
|
+
* @returns {boolean} 利用可能なら true
|
|
4611
4860
|
*/
|
|
4612
4861
|
static canUse() {
|
|
4613
4862
|
try {
|
|
@@ -4619,6 +4868,8 @@ apigit-commit-key:${input.commitKey}`;
|
|
|
4619
4868
|
dbName;
|
|
4620
4869
|
dbPromise;
|
|
4621
4870
|
currentBranch = null;
|
|
4871
|
+
root;
|
|
4872
|
+
rootPrefix = "";
|
|
4622
4873
|
static VAR_WORKSPACE_BASE = "workspace";
|
|
4623
4874
|
// Historically this was a separate workspace-info store, but some test
|
|
4624
4875
|
// fakes expect info entries to be available in 'git-info'. Alias the
|
|
@@ -4632,7 +4883,7 @@ apigit-commit-key:${input.commitKey}`;
|
|
|
4632
4883
|
/** 利用可能な DB 名の一覧を返す
|
|
4633
4884
|
* @returns {string[]} available root names
|
|
4634
4885
|
*/
|
|
4635
|
-
static async availableRoots() {
|
|
4886
|
+
static async availableRoots(namespace) {
|
|
4636
4887
|
const g = globalThis;
|
|
4637
4888
|
const idb = g.indexedDB;
|
|
4638
4889
|
if (!idb)
|
|
@@ -4640,7 +4891,12 @@ apigit-commit-key:${input.commitKey}`;
|
|
|
4640
4891
|
if (typeof idb.databases !== "function")
|
|
4641
4892
|
return [];
|
|
4642
4893
|
try {
|
|
4643
|
-
|
|
4894
|
+
const names = await IndexedDatabaseStorage2._namesFromDatabases(idb);
|
|
4895
|
+
if (!namespace)
|
|
4896
|
+
return names;
|
|
4897
|
+
if (names.includes(namespace))
|
|
4898
|
+
return ["apigit_storage"];
|
|
4899
|
+
return [];
|
|
4644
4900
|
} catch {
|
|
4645
4901
|
return [];
|
|
4646
4902
|
}
|
|
@@ -4660,8 +4916,10 @@ apigit-commit-key:${input.commitKey}`;
|
|
|
4660
4916
|
return Array.from(new Set(names));
|
|
4661
4917
|
}
|
|
4662
4918
|
/** コンストラクタ */
|
|
4663
|
-
constructor(
|
|
4664
|
-
this.dbName =
|
|
4919
|
+
constructor(namespace, _root) {
|
|
4920
|
+
this.dbName = namespace || IndexedDatabaseStorage2.DEFAULT_DB_NAME;
|
|
4921
|
+
this.root = _root || void 0;
|
|
4922
|
+
this.rootPrefix = this.root ? `${this.root}_` : "";
|
|
4665
4923
|
this.dbPromise = this.openDb();
|
|
4666
4924
|
}
|
|
4667
4925
|
/**
|
|
@@ -4693,27 +4951,26 @@ apigit-commit-key:${input.commitKey}`;
|
|
|
4693
4951
|
*/
|
|
4694
4952
|
/**
|
|
4695
4953
|
* Handle DB upgrade event and create required object stores.
|
|
4696
|
-
*
|
|
4954
|
+
* Creates the object stores used by this backend, names are resolved
|
|
4955
|
+
* through `_storeName` to include any configured `_root` prefix.
|
|
4956
|
+
* @param {Event} event - Upgrade event from `indexedDB.open`
|
|
4697
4957
|
* @returns {void}
|
|
4698
4958
|
*/
|
|
4699
4959
|
_handleUpgrade(event) {
|
|
4700
4960
|
const database = event.target.result;
|
|
4701
|
-
if (!database.objectStoreNames.contains(IndexedDatabaseStorage2.VAR_WORKSPACE_BASE))
|
|
4702
|
-
database.createObjectStore(IndexedDatabaseStorage2.VAR_WORKSPACE_BASE);
|
|
4703
|
-
if (!database.objectStoreNames.contains(IndexedDatabaseStorage2.VAR_WORKSPACE_INFO))
|
|
4704
|
-
database.createObjectStore(IndexedDatabaseStorage2.VAR_WORKSPACE_INFO);
|
|
4705
|
-
if (!database.objectStoreNames.contains(IndexedDatabaseStorage2.VAR_BASE))
|
|
4706
|
-
database.createObjectStore(IndexedDatabaseStorage2.VAR_BASE);
|
|
4707
|
-
if (!database.objectStoreNames.contains(IndexedDatabaseStorage2.VAR_CONFLICT))
|
|
4708
|
-
database.createObjectStore(IndexedDatabaseStorage2.VAR_CONFLICT);
|
|
4709
|
-
if (!database.objectStoreNames.contains(IndexedDatabaseStorage2.VAR_INFO))
|
|
4710
|
-
database.createObjectStore(IndexedDatabaseStorage2.VAR_INFO);
|
|
4711
|
-
if (!database.objectStoreNames.contains("index"))
|
|
4712
|
-
database.createObjectStore("index");
|
|
4961
|
+
if (!database.objectStoreNames.contains(this._storeName(IndexedDatabaseStorage2.VAR_WORKSPACE_BASE)))
|
|
4962
|
+
database.createObjectStore(this._storeName(IndexedDatabaseStorage2.VAR_WORKSPACE_BASE));
|
|
4963
|
+
if (!database.objectStoreNames.contains(this._storeName(IndexedDatabaseStorage2.VAR_WORKSPACE_INFO)))
|
|
4964
|
+
database.createObjectStore(this._storeName(IndexedDatabaseStorage2.VAR_WORKSPACE_INFO));
|
|
4965
|
+
if (!database.objectStoreNames.contains(this._storeName(IndexedDatabaseStorage2.VAR_BASE)))
|
|
4966
|
+
database.createObjectStore(this._storeName(IndexedDatabaseStorage2.VAR_BASE));
|
|
4967
|
+
if (!database.objectStoreNames.contains(this._storeName(IndexedDatabaseStorage2.VAR_CONFLICT)))
|
|
4968
|
+
database.createObjectStore(this._storeName(IndexedDatabaseStorage2.VAR_CONFLICT));
|
|
4969
|
+
if (!database.objectStoreNames.contains(this._storeName(IndexedDatabaseStorage2.VAR_INFO)))
|
|
4970
|
+
database.createObjectStore(this._storeName(IndexedDatabaseStorage2.VAR_INFO));
|
|
4971
|
+
if (!database.objectStoreNames.contains(this._storeName("index")))
|
|
4972
|
+
database.createObjectStore(this._storeName("index"));
|
|
4713
4973
|
}
|
|
4714
|
-
/**
|
|
4715
|
-
* 指定 DB に対する onversionchange ハンドラを生成します。
|
|
4716
|
-
*/
|
|
4717
4974
|
/**
|
|
4718
4975
|
* Create a handler to close DB on version change.
|
|
4719
4976
|
* @param dbParam Target DB
|
|
@@ -4724,9 +4981,6 @@ apigit-commit-key:${input.commitKey}`;
|
|
|
4724
4981
|
databaseParameter.close();
|
|
4725
4982
|
};
|
|
4726
4983
|
}
|
|
4727
|
-
/**
|
|
4728
|
-
* DB open の成功ハンドラ
|
|
4729
|
-
*/
|
|
4730
4984
|
/**
|
|
4731
4985
|
* Called when DB open succeeds.
|
|
4732
4986
|
* @param req IDB open request
|
|
@@ -4738,9 +4992,6 @@ apigit-commit-key:${input.commitKey}`;
|
|
|
4738
4992
|
database.onversionchange = this._makeVersionChangeHandler(database);
|
|
4739
4993
|
resolve(database);
|
|
4740
4994
|
}
|
|
4741
|
-
/**
|
|
4742
|
-
* DB open のエラーハンドラ
|
|
4743
|
-
*/
|
|
4744
4995
|
/**
|
|
4745
4996
|
* Called when DB open errors.
|
|
4746
4997
|
* @param req IDB open request
|
|
@@ -4750,22 +5001,19 @@ apigit-commit-key:${input.commitKey}`;
|
|
|
4750
5001
|
_onOpenError(request, reject) {
|
|
4751
5002
|
reject(request.error);
|
|
4752
5003
|
}
|
|
4753
|
-
/**
|
|
4754
|
-
* トランザクションラッパー。cb 内の処理をトランザクションで実行し、
|
|
4755
|
-
* 必要なら再試行します。
|
|
4756
|
-
*/
|
|
4757
5004
|
/**
|
|
4758
5005
|
* トランザクションラッパー。cb 内の処理をトランザクションで実行し、必要なら再試行します。
|
|
4759
5006
|
* @returns {Promise<void>} トランザクション処理完了時に解決
|
|
4760
5007
|
*/
|
|
4761
5008
|
async tx(storeName, mode, callback) {
|
|
5009
|
+
const physical = this._storeName(storeName);
|
|
4762
5010
|
try {
|
|
4763
|
-
return await this._performTxAttempt(
|
|
5011
|
+
return await this._performTxAttempt(physical, mode, callback);
|
|
4764
5012
|
} catch (error) {
|
|
4765
5013
|
const isInvalidState = error && (error.name === "InvalidStateError" || /closing/i.test(String(error.message || "")));
|
|
4766
5014
|
if (isInvalidState) {
|
|
4767
5015
|
this.dbPromise = this.openDb();
|
|
4768
|
-
return await this._performTxAttempt(
|
|
5016
|
+
return await this._performTxAttempt(physical, mode, callback);
|
|
4769
5017
|
}
|
|
4770
5018
|
throw error;
|
|
4771
5019
|
}
|
|
@@ -4862,10 +5110,6 @@ apigit-commit-key:${input.commitKey}`;
|
|
|
4862
5110
|
return orig.apply(target, arguments_);
|
|
4863
5111
|
};
|
|
4864
5112
|
}
|
|
4865
|
-
/**
|
|
4866
|
-
* Schedule a microtask to invoke tx.oncomplete in case fake IndexedDB
|
|
4867
|
-
* implementations never fire it.
|
|
4868
|
-
*/
|
|
4869
5113
|
/**
|
|
4870
5114
|
* Schedule a microtask to invoke tx.oncomplete in case fake IndexedDB
|
|
4871
5115
|
* implementations never fire it.
|
|
@@ -4881,7 +5125,6 @@ apigit-commit-key:${input.commitKey}`;
|
|
|
4881
5125
|
}
|
|
4882
5126
|
}, 0);
|
|
4883
5127
|
}
|
|
4884
|
-
// legacy canUseOpfs removed; use static canUse() instead
|
|
4885
5128
|
/**
|
|
4886
5129
|
* index を読み出す
|
|
4887
5130
|
* @returns {Promise<IndexFile|null>} 読み出した IndexFile、存在しなければ null
|
|
@@ -4913,10 +5156,6 @@ apigit-commit-key:${input.commitKey}`;
|
|
|
4913
5156
|
this.currentBranch = null;
|
|
4914
5157
|
}
|
|
4915
5158
|
}
|
|
4916
|
-
/**
|
|
4917
|
-
* Apply metadata to result. @returns void
|
|
4918
|
-
* @returns {void}
|
|
4919
|
-
*/
|
|
4920
5159
|
/**
|
|
4921
5160
|
* Load workspace-local info entries into result.entries (workspace overrides branch-scoped)
|
|
4922
5161
|
* @param result IndexFile being populated
|
|
@@ -4971,8 +5210,9 @@ apigit-commit-key:${input.commitKey}`;
|
|
|
4971
5210
|
async _readIndexMeta(database) {
|
|
4972
5211
|
return await new Promise((resolve) => {
|
|
4973
5212
|
try {
|
|
4974
|
-
const
|
|
4975
|
-
const
|
|
5213
|
+
const indexName = this._storeName("index");
|
|
5214
|
+
const tx = database.transaction(indexName, "readonly");
|
|
5215
|
+
const store = tx.objectStore(indexName);
|
|
4976
5216
|
const request = store.get("index");
|
|
4977
5217
|
request.onsuccess = () => {
|
|
4978
5218
|
resolve(request.result ?? null);
|
|
@@ -5152,14 +5392,7 @@ apigit-commit-key:${input.commitKey}`;
|
|
|
5152
5392
|
return await this._getFromStore(storeName, key);
|
|
5153
5393
|
}
|
|
5154
5394
|
/**
|
|
5155
|
-
*
|
|
5156
|
-
* @param segment Segment name
|
|
5157
|
-
* @param filepath Path to read
|
|
5158
|
-
* @param branch Current branch
|
|
5159
|
-
* @returns {Promise<string|null>} blob content or null
|
|
5160
|
-
*/
|
|
5161
|
-
/**
|
|
5162
|
-
* blob を削除する
|
|
5395
|
+
* blob を削除する
|
|
5163
5396
|
* @returns {Promise<void>} 削除完了時に解決
|
|
5164
5397
|
*/
|
|
5165
5398
|
async deleteBlob(filepath, segment) {
|
|
@@ -5210,10 +5443,6 @@ apigit-commit-key:${input.commitKey}`;
|
|
|
5210
5443
|
}
|
|
5211
5444
|
return toWrite;
|
|
5212
5445
|
}
|
|
5213
|
-
/**
|
|
5214
|
-
* Gather entries that should be written to workspace-info: those that exist in workspace-base.
|
|
5215
|
-
* @returns {Promise<Array<{k:string,v:any}>>}
|
|
5216
|
-
*/
|
|
5217
5446
|
/**
|
|
5218
5447
|
* Compute store name and key for a given segment and filepath.
|
|
5219
5448
|
* @returns {{storeName:string,key:string}}
|
|
@@ -5223,10 +5452,6 @@ apigit-commit-key:${input.commitKey}`;
|
|
|
5223
5452
|
const key = seg === "workspace" ? filepath : seg === "conflictBlob" ? `${branch}::conflictBlob::${filepath}` : `${branch}::${filepath}`;
|
|
5224
5453
|
return { storeName, key };
|
|
5225
5454
|
}
|
|
5226
|
-
/**
|
|
5227
|
-
* Compute store name and key for a given segment and filepath.
|
|
5228
|
-
* @returns {{storeName:string,key:string}}
|
|
5229
|
-
*/
|
|
5230
5455
|
/**
|
|
5231
5456
|
* Delete a filepath from all relevant stores for the given branch.
|
|
5232
5457
|
* @returns {Promise<void>}
|
|
@@ -5239,10 +5464,6 @@ apigit-commit-key:${input.commitKey}`;
|
|
|
5239
5464
|
await this._deleteFromStore(IndexedDatabaseStorage2.VAR_INFO, filepath);
|
|
5240
5465
|
await this._deleteFromStore(IndexedDatabaseStorage2.VAR_WORKSPACE_INFO, filepath);
|
|
5241
5466
|
}
|
|
5242
|
-
/**
|
|
5243
|
-
* Delete a filepath from all relevant stores for the given branch.
|
|
5244
|
-
* @returns {Promise<void>}
|
|
5245
|
-
*/
|
|
5246
5467
|
/**
|
|
5247
5468
|
* Read a value from a specific object store.
|
|
5248
5469
|
* @param storeName Object store name
|
|
@@ -5253,8 +5474,9 @@ apigit-commit-key:${input.commitKey}`;
|
|
|
5253
5474
|
const database = await this.dbPromise;
|
|
5254
5475
|
return new Promise((resolve) => {
|
|
5255
5476
|
try {
|
|
5256
|
-
const
|
|
5257
|
-
const
|
|
5477
|
+
const physical = this._storeName(storeName);
|
|
5478
|
+
const tx = database.transaction(physical, "readonly");
|
|
5479
|
+
const store = tx.objectStore(physical);
|
|
5258
5480
|
const request = store.get(filepath);
|
|
5259
5481
|
request.onsuccess = () => {
|
|
5260
5482
|
const result = request.result ?? null;
|
|
@@ -5284,8 +5506,9 @@ apigit-commit-key:${input.commitKey}`;
|
|
|
5284
5506
|
const database = await this.dbPromise;
|
|
5285
5507
|
return new Promise((resolve) => {
|
|
5286
5508
|
try {
|
|
5287
|
-
const
|
|
5288
|
-
const
|
|
5509
|
+
const physical = this._storeName(storeName);
|
|
5510
|
+
const tx = database.transaction(physical, "readonly");
|
|
5511
|
+
const store = tx.objectStore(physical);
|
|
5289
5512
|
const keys = [];
|
|
5290
5513
|
const request = store.openKeyCursor();
|
|
5291
5514
|
request.onsuccess = this._makeCursorSuccessHandler(resolve, keys);
|
|
@@ -5320,8 +5543,13 @@ apigit-commit-key:${input.commitKey}`;
|
|
|
5320
5543
|
});
|
|
5321
5544
|
}
|
|
5322
5545
|
/**
|
|
5323
|
-
*
|
|
5546
|
+
* Map a logical store name to its physical store name including root prefix.
|
|
5547
|
+
* @param name logical store identifier
|
|
5548
|
+
* @returns physical store name used in IndexedDB
|
|
5324
5549
|
*/
|
|
5550
|
+
_storeName(name) {
|
|
5551
|
+
return this.rootPrefix ? `${this.rootPrefix}${name}` : name;
|
|
5552
|
+
}
|
|
5325
5553
|
/**
|
|
5326
5554
|
* Create a cursor success handler bound to resolve and keys array.
|
|
5327
5555
|
* @param resolve resolver
|
|
@@ -5362,9 +5590,6 @@ apigit-commit-key:${input.commitKey}`;
|
|
|
5362
5590
|
keys = this._filterKeys(keys, p, recursive);
|
|
5363
5591
|
return await this._collectFiles(keys, seg);
|
|
5364
5592
|
}
|
|
5365
|
-
/**
|
|
5366
|
-
* Raw listing that returns implementation-specific URIs and a normalized path.
|
|
5367
|
-
*/
|
|
5368
5593
|
/**
|
|
5369
5594
|
* Raw listing that returns implementation-specific URIs and a normalized path.
|
|
5370
5595
|
* @param prefix optional prefix to filter
|
|
@@ -5395,9 +5620,6 @@ apigit-commit-key:${input.commitKey}`;
|
|
|
5395
5620
|
}
|
|
5396
5621
|
return out;
|
|
5397
5622
|
}
|
|
5398
|
-
/**
|
|
5399
|
-
* Helper: build entries from store keys with filtering and path normalization
|
|
5400
|
-
*/
|
|
5401
5623
|
/**
|
|
5402
5624
|
* Helper: build entries from store keys with filtering and path normalization
|
|
5403
5625
|
* @returns {Promise<Array<{uri:string,path:string,info?:string|null}>>}
|
|
@@ -5474,10 +5696,6 @@ apigit-commit-key:${input.commitKey}`;
|
|
|
5474
5696
|
}
|
|
5475
5697
|
return keys;
|
|
5476
5698
|
}
|
|
5477
|
-
/**
|
|
5478
|
-
* Collect file info objects for keys array.
|
|
5479
|
-
* @returns {Promise<Array<{path:string, info:string|null}>>}
|
|
5480
|
-
*/
|
|
5481
5699
|
/**
|
|
5482
5700
|
* Collect file info objects for keys array.
|
|
5483
5701
|
* @param keys list of keys
|
|
@@ -5493,15 +5711,6 @@ apigit-commit-key:${input.commitKey}`;
|
|
|
5493
5711
|
}
|
|
5494
5712
|
return out;
|
|
5495
5713
|
}
|
|
5496
|
-
/**
|
|
5497
|
-
* Collect file info objects for keys array.
|
|
5498
|
-
* @param keys key list
|
|
5499
|
-
* @param _seg segment (unused)
|
|
5500
|
-
* @returns {Promise<Array<{path:string, info:string|null}>>}
|
|
5501
|
-
*/
|
|
5502
|
-
/**
|
|
5503
|
-
* Resolve info value for a given key: prefer workspace-local, then branch-scoped.
|
|
5504
|
-
*/
|
|
5505
5714
|
/**
|
|
5506
5715
|
* Resolve info value for a given key: prefer workspace-local, then branch-scoped.
|
|
5507
5716
|
* @param k key
|
|
@@ -5514,10 +5723,6 @@ apigit-commit-key:${input.commitKey}`;
|
|
|
5514
5723
|
info = await this._getFromStore(IndexedDatabaseStorage2.VAR_INFO, k);
|
|
5515
5724
|
return info;
|
|
5516
5725
|
}
|
|
5517
|
-
/**
|
|
5518
|
-
* Resolve info value for a given key: prefer workspace-local, then branch-scoped.
|
|
5519
|
-
* @returns {Promise<string|null>}
|
|
5520
|
-
*/
|
|
5521
5726
|
/**
|
|
5522
5727
|
* Calculate SHA-1 hex digest of given content.
|
|
5523
5728
|
* @param content Input string
|
|
@@ -5596,6 +5801,23 @@ apigit-commit-key:${input.commitKey}`;
|
|
|
5596
5801
|
for (let index = 0; index < bytes.length; index += 4) {
|
|
5597
5802
|
words.push(bytes[index] << 24 | bytes[index + 1] << 16 | bytes[index + 2] << 8 | bytes[index + 3]);
|
|
5598
5803
|
}
|
|
5804
|
+
let h0 = 1732584193;
|
|
5805
|
+
let h1 = 4023233417;
|
|
5806
|
+
let h2 = 2562383102;
|
|
5807
|
+
let h3 = 271733878;
|
|
5808
|
+
let h4 = 3285377520;
|
|
5809
|
+
const results = processWords(words);
|
|
5810
|
+
h0 = results[0];
|
|
5811
|
+
h1 = results[1];
|
|
5812
|
+
h2 = results[2];
|
|
5813
|
+
h3 = results[3];
|
|
5814
|
+
h4 = results[4];
|
|
5815
|
+
function toHex(n) {
|
|
5816
|
+
return n.toString(16).padStart(8, "0");
|
|
5817
|
+
}
|
|
5818
|
+
return (toHex(h0) + toHex(h1) + toHex(h2) + toHex(h3) + toHex(h4)).toLowerCase();
|
|
5819
|
+
}
|
|
5820
|
+
function processWords(words) {
|
|
5599
5821
|
let h0 = 1732584193;
|
|
5600
5822
|
let h1 = 4023233417;
|
|
5601
5823
|
let h2 = 2562383102;
|
|
@@ -5609,37 +5831,40 @@ apigit-commit-key:${input.commitKey}`;
|
|
|
5609
5831
|
const temporary = w[t - 3] ^ w[t - 8] ^ w[t - 14] ^ w[t - 16];
|
|
5610
5832
|
w[t] = (temporary << 1 | temporary >>> 31) >>> 0;
|
|
5611
5833
|
}
|
|
5612
|
-
|
|
5613
|
-
|
|
5614
|
-
|
|
5615
|
-
|
|
5616
|
-
|
|
5617
|
-
|
|
5618
|
-
|
|
5619
|
-
|
|
5620
|
-
|
|
5621
|
-
|
|
5622
|
-
|
|
5623
|
-
|
|
5624
|
-
|
|
5625
|
-
|
|
5626
|
-
|
|
5627
|
-
|
|
5628
|
-
|
|
5629
|
-
|
|
5630
|
-
|
|
5631
|
-
|
|
5632
|
-
|
|
5633
|
-
|
|
5634
|
-
}
|
|
5635
|
-
|
|
5636
|
-
|
|
5637
|
-
|
|
5638
|
-
|
|
5639
|
-
|
|
5640
|
-
|
|
5641
|
-
|
|
5642
|
-
|
|
5834
|
+
const updated = processChunk(w, [h0, h1, h2, h3, h4]);
|
|
5835
|
+
h0 = updated[0];
|
|
5836
|
+
h1 = updated[1];
|
|
5837
|
+
h2 = updated[2];
|
|
5838
|
+
h3 = updated[3];
|
|
5839
|
+
h4 = updated[4];
|
|
5840
|
+
}
|
|
5841
|
+
return [h0, h1, h2, h3, h4];
|
|
5842
|
+
}
|
|
5843
|
+
function processChunk(w, h) {
|
|
5844
|
+
let a = h[0], b = h[1], c = h[2], d = h[3], registerE = h[4];
|
|
5845
|
+
for (let t = 0; t < 80; t++) {
|
|
5846
|
+
let f, k;
|
|
5847
|
+
if (t < 20) {
|
|
5848
|
+
f = b & c | ~b & d;
|
|
5849
|
+
k = 1518500249;
|
|
5850
|
+
} else if (t < 40) {
|
|
5851
|
+
f = b ^ c ^ d;
|
|
5852
|
+
k = 1859775393;
|
|
5853
|
+
} else if (t < 60) {
|
|
5854
|
+
f = b & c | b & d | c & d;
|
|
5855
|
+
k = 2400959708;
|
|
5856
|
+
} else {
|
|
5857
|
+
f = b ^ c ^ d;
|
|
5858
|
+
k = 3395469782;
|
|
5859
|
+
}
|
|
5860
|
+
const temporaryValue = (a << 5 | a >>> 27) + f + registerE + k + (w[t] >>> 0) >>> 0;
|
|
5861
|
+
registerE = d;
|
|
5862
|
+
d = c;
|
|
5863
|
+
c = (b << 30 | b >>> 2) >>> 0;
|
|
5864
|
+
b = a;
|
|
5865
|
+
a = temporaryValue;
|
|
5866
|
+
}
|
|
5867
|
+
return [h[0] + a >>> 0, h[1] + b >>> 0, h[2] + c >>> 0, h[3] + d >>> 0, h[4] + registerE >>> 0];
|
|
5643
5868
|
}
|
|
5644
5869
|
function parseExistingInfo(store, filepath) {
|
|
5645
5870
|
const existingTxt = store.infoBlobs.has(filepath) ? store.infoBlobs.get(filepath) : null;
|
|
@@ -5687,31 +5912,36 @@ apigit-commit-key:${input.commitKey}`;
|
|
|
5687
5912
|
const now = Date.now();
|
|
5688
5913
|
const existing = parseExistingInfo(store, filepath);
|
|
5689
5914
|
if (seg === "info") {
|
|
5690
|
-
|
|
5691
|
-
|
|
5692
|
-
store.infoBlobs.set(filepath, JSON.stringify(parsed));
|
|
5693
|
-
return;
|
|
5694
|
-
} catch {
|
|
5695
|
-
store.infoBlobs.set(filepath, String(content));
|
|
5696
|
-
return;
|
|
5697
|
-
}
|
|
5915
|
+
await handleInfoSegment(store, filepath, content);
|
|
5916
|
+
return;
|
|
5698
5917
|
}
|
|
5699
5918
|
const sha = await shaOf3(content);
|
|
5700
5919
|
let entry;
|
|
5701
|
-
if (seg === "workspace")
|
|
5920
|
+
if (seg === "workspace")
|
|
5702
5921
|
entry = buildWorkspaceEntry(existing, filepath, sha, now);
|
|
5703
|
-
|
|
5922
|
+
else if (seg === "base")
|
|
5704
5923
|
entry = buildBaseEntry(existing, filepath, sha, now);
|
|
5705
|
-
|
|
5924
|
+
else if (seg === "conflict")
|
|
5706
5925
|
entry = buildConflictEntry(existing, filepath, now);
|
|
5707
|
-
|
|
5926
|
+
else
|
|
5708
5927
|
entry = { path: filepath, updatedAt: now };
|
|
5709
|
-
}
|
|
5710
5928
|
store.infoBlobs.set(filepath, JSON.stringify(entry));
|
|
5711
|
-
} catch {
|
|
5929
|
+
} catch (error) {
|
|
5930
|
+
if (typeof console !== "undefined" && console.debug)
|
|
5931
|
+
console.debug("updateInfoForWrite failed", error);
|
|
5712
5932
|
return;
|
|
5713
5933
|
}
|
|
5714
5934
|
}
|
|
5935
|
+
async function handleInfoSegment(store, filepath, content) {
|
|
5936
|
+
try {
|
|
5937
|
+
const parsed = JSON.parse(content);
|
|
5938
|
+
store.infoBlobs.set(filepath, JSON.stringify(parsed));
|
|
5939
|
+
} catch (error) {
|
|
5940
|
+
store.infoBlobs.set(filepath, String(content));
|
|
5941
|
+
if (typeof console !== "undefined" && console.debug)
|
|
5942
|
+
console.debug("handleInfoSegment: stored raw content due to parse error", error);
|
|
5943
|
+
}
|
|
5944
|
+
}
|
|
5715
5945
|
|
|
5716
5946
|
// src/virtualfs/inmemoryStorage.ts
|
|
5717
5947
|
var BRANCH_SEP = "::";
|
|
@@ -5735,17 +5965,26 @@ apigit-commit-key:${input.commitKey}`;
|
|
|
5735
5965
|
* 利用可能なルート名を返します。
|
|
5736
5966
|
* @returns {string[]} ルート名の配列
|
|
5737
5967
|
*/
|
|
5738
|
-
static availableRoots() {
|
|
5968
|
+
static availableRoots(namespace) {
|
|
5739
5969
|
const keys = Array.from(InMemoryStorage2.stores.keys());
|
|
5740
|
-
|
|
5970
|
+
if (namespace) {
|
|
5971
|
+
const filtered = keys.filter((k) => k.startsWith(namespace + "/")).map((k) => k.slice((namespace + "/").length));
|
|
5972
|
+
return filtered.length ? filtered : ["apigit_storage"];
|
|
5973
|
+
}
|
|
5974
|
+
const roots = keys.map((k) => {
|
|
5975
|
+
const parts = k.split("/");
|
|
5976
|
+
return parts.length > 1 ? parts.slice(1).join("/") : parts[0];
|
|
5977
|
+
});
|
|
5978
|
+
const uniq = Array.from(new Set(roots));
|
|
5979
|
+
return uniq.length ? uniq : ["apigit_storage"];
|
|
5741
5980
|
}
|
|
5742
|
-
// legacy canUseOpfs removed; use static canUse() instead
|
|
5743
5981
|
/**
|
|
5744
5982
|
* コンストラクタ。互換性のためにディレクトリ名を受け取るが無視する。
|
|
5745
5983
|
* @param directory 任意のディレクトリ文字列(使用しない)
|
|
5746
5984
|
*/
|
|
5747
|
-
constructor(directory) {
|
|
5748
|
-
|
|
5985
|
+
constructor(namespace, directory) {
|
|
5986
|
+
const directoryName = directory ?? `__inmem_${Math.random().toString(36).slice(2)}`;
|
|
5987
|
+
this.rootKey = namespace ? `${namespace}/${directoryName}` : directoryName;
|
|
5749
5988
|
if (!InMemoryStorage2.stores.has(this.rootKey)) {
|
|
5750
5989
|
InMemoryStorage2.stores.set(this.rootKey, {
|
|
5751
5990
|
index: { head: "", entries: {} },
|
|
@@ -5787,7 +6026,7 @@ apigit-commit-key:${input.commitKey}`;
|
|
|
5787
6026
|
}
|
|
5788
6027
|
/**
|
|
5789
6028
|
* Load workspace-local info entries into result.entries (unprefixed keys)
|
|
5790
|
-
|
|
6029
|
+
* @returns {void}
|
|
5791
6030
|
*/
|
|
5792
6031
|
_loadInMemoryWorkspaceInfo(store, result, branch) {
|
|
5793
6032
|
for (const [k, v] of store.infoBlobs.entries()) {
|
|
@@ -5800,7 +6039,7 @@ apigit-commit-key:${input.commitKey}`;
|
|
|
5800
6039
|
}
|
|
5801
6040
|
/**
|
|
5802
6041
|
* Load branch-scoped info entries into result.entries without overwriting workspace-local entries
|
|
5803
|
-
|
|
6042
|
+
* @returns {void}
|
|
5804
6043
|
*/
|
|
5805
6044
|
_loadInMemoryBranchInfo(store, result, branch) {
|
|
5806
6045
|
for (const [k, v] of store.infoBlobs.entries()) {
|
|
@@ -5873,10 +6112,6 @@ apigit-commit-key:${input.commitKey}`;
|
|
|
5873
6112
|
const wrapped = seg === SEG_WORKSPACE || seg === "conflict" ? this._wrapStoreForInfoNoPrefix(store) : this._wrapStoreForInfoPrefix(store);
|
|
5874
6113
|
await updateInfoForWrite(wrapped, filepath, seg, content);
|
|
5875
6114
|
}
|
|
5876
|
-
/**
|
|
5877
|
-
* Handle workspace blob writes that should be based on existing git-scoped info.
|
|
5878
|
-
* Returns true when the operation is handled and caller should return early.
|
|
5879
|
-
*/
|
|
5880
6115
|
/**
|
|
5881
6116
|
* Handle workspace blob writes that should be based on existing git-scoped info.
|
|
5882
6117
|
* Returns true when the operation is handled and caller should return early.
|
|
@@ -6168,7 +6403,7 @@ apigit-commit-key:${input.commitKey}`;
|
|
|
6168
6403
|
* @param prefix プレフィックス(例: 'dir/sub')
|
|
6169
6404
|
* @param segment セグメント('workspace' 等)。省略時は 'workspace'
|
|
6170
6405
|
* @param recursive サブディレクトリも含めるか。省略時は true
|
|
6171
|
-
|
|
6406
|
+
* @returns {Promise<Array<{ path: string; info: string | null }>>}
|
|
6172
6407
|
*/
|
|
6173
6408
|
async listFiles(prefix, segment, recursive = true) {
|
|
6174
6409
|
const seg = segment || SEG_WORKSPACE;
|
|
@@ -6187,9 +6422,6 @@ apigit-commit-key:${input.commitKey}`;
|
|
|
6187
6422
|
const keys = this._resolveKeysForList(allKeys, String(seg), p, recursive);
|
|
6188
6423
|
return this._collectFilesInMemory(keys, store);
|
|
6189
6424
|
}
|
|
6190
|
-
/**
|
|
6191
|
-
* Resolve and filter keys for `listFiles` into final key list.
|
|
6192
|
-
*/
|
|
6193
6425
|
/**
|
|
6194
6426
|
* Resolve and filter keys for `listFiles` into final key list.
|
|
6195
6427
|
* @returns {string[]} filtered keys
|
|
@@ -6326,9 +6558,15 @@ apigit-commit-key:${input.commitKey}`;
|
|
|
6326
6558
|
static delete(rootName) {
|
|
6327
6559
|
if (InMemoryStorage2.stores.has(rootName)) {
|
|
6328
6560
|
InMemoryStorage2.stores.delete(rootName);
|
|
6329
|
-
|
|
6330
|
-
|
|
6561
|
+
return;
|
|
6562
|
+
}
|
|
6563
|
+
for (const key of Array.from(InMemoryStorage2.stores.keys())) {
|
|
6564
|
+
if (key === rootName || key.endsWith("/" + rootName)) {
|
|
6565
|
+
InMemoryStorage2.stores.delete(key);
|
|
6566
|
+
return;
|
|
6567
|
+
}
|
|
6331
6568
|
}
|
|
6569
|
+
throw new Error(`InMemory root "${rootName}" not found`);
|
|
6332
6570
|
}
|
|
6333
6571
|
};
|
|
6334
6572
|
var inmemoryStorage_default = InMemoryStorage;
|