browser-git-ops 0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,294 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.BrowserStorage = exports.NodeFsStorage = void 0;
7
+ const promises_1 = __importDefault(require("fs/promises"));
8
+ const path_1 = __importDefault(require("path"));
9
+ /**
10
+ * ファイルシステム上にデータを永続化する実装
11
+ */
12
+ class NodeFsStorage {
13
+ dir;
14
+ indexPath;
15
+ /**
16
+ * NodeFsStorage を初期化します。
17
+ * @param {string} dir 永続化ディレクトリ
18
+ */
19
+ constructor(dir) {
20
+ this.dir = dir;
21
+ this.indexPath = path_1.default.join(this.dir, 'index.json');
22
+ }
23
+ /**
24
+ * ストレージ用ディレクトリを作成します。
25
+ * @returns {Promise<void>}
26
+ */
27
+ async init() {
28
+ await promises_1.default.mkdir(this.dir, { recursive: true });
29
+ }
30
+ /**
31
+ * index.json を読み込みます。存在しなければ null を返します。
32
+ * @returns {Promise<IndexFile|null>} 読み込んだ Index ファイル、または null
33
+ */
34
+ async readIndex() {
35
+ try {
36
+ const raw = await promises_1.default.readFile(this.indexPath, 'utf8');
37
+ return JSON.parse(raw);
38
+ }
39
+ catch (err) {
40
+ return null;
41
+ }
42
+ }
43
+ /**
44
+ * index.json を書き込みます。
45
+ * @param {IndexFile} index 書き込む Index データ
46
+ * @returns {Promise<void>}
47
+ */
48
+ async writeIndex(index) {
49
+ const data = JSON.stringify(index, null, 2);
50
+ await promises_1.default.writeFile(this.indexPath, data, 'utf8');
51
+ }
52
+ /**
53
+ * 指定パスへファイルを保存します。
54
+ * @param {string} filepath ファイルパス
55
+ * @param {string} content ファイル内容
56
+ * @returns {Promise<void>}
57
+ */
58
+ async writeBlob(filepath, content) {
59
+ const full = path_1.default.join(this.dir, filepath);
60
+ await promises_1.default.mkdir(path_1.default.dirname(full), { recursive: true });
61
+ await promises_1.default.writeFile(full, content, 'utf8');
62
+ }
63
+ /**
64
+ * 指定パスのファイルを読み出します。存在しなければ null を返します。
65
+ * @param {string} filepath ファイルパス
66
+ * @returns {Promise<string|null>} ファイル内容または null
67
+ */
68
+ async readBlob(filepath) {
69
+ try {
70
+ const full = path_1.default.join(this.dir, filepath);
71
+ return await promises_1.default.readFile(full, 'utf8');
72
+ }
73
+ catch (err) {
74
+ return null;
75
+ }
76
+ }
77
+ /**
78
+ * 指定パスのファイルを削除します。存在しない場合は無視されます。
79
+ * @param {string} filepath ファイルパス
80
+ * @returns {Promise<void>}
81
+ */
82
+ async deleteBlob(filepath) {
83
+ try {
84
+ const full = path_1.default.join(this.dir, filepath);
85
+ await promises_1.default.unlink(full);
86
+ }
87
+ catch (err) {
88
+ // ignore
89
+ }
90
+ }
91
+ }
92
+ exports.NodeFsStorage = NodeFsStorage;
93
+ // BrowserStorage: use OPFS when available, otherwise IndexedDB
94
+ /**
95
+ * ブラウザ環境向けの永続化実装: OPFS を優先し、無ければ IndexedDB を使用する
96
+ */
97
+ class BrowserStorage {
98
+ dbName = 'apigit_storage';
99
+ dbPromise;
100
+ /**
101
+ * BrowserStorage を初期化します。内部で IndexedDB 接続を開始します。
102
+ */
103
+ constructor() {
104
+ this.dbPromise = this.openDb();
105
+ }
106
+ /**
107
+ * 初期化を待機します(IndexedDB の準備完了を待つ)。
108
+ * @returns {Promise<void>}
109
+ */
110
+ async init() {
111
+ await this.dbPromise;
112
+ }
113
+ /**
114
+ * IndexedDB を開き、データベースインスタンスを返します。
115
+ * @returns {Promise<IDBDatabase>}
116
+ */
117
+ openDb() {
118
+ return new Promise((resolve, reject) => {
119
+ const idb = globalThis.indexedDB;
120
+ if (!idb)
121
+ return reject(new Error('IndexedDB is not available'));
122
+ const req = idb.open(this.dbName, 1);
123
+ /**
124
+ * データベーススキーマの初期化
125
+ */
126
+ req.onupgradeneeded = (ev) => {
127
+ const db = ev.target.result;
128
+ if (!db.objectStoreNames.contains('blobs'))
129
+ db.createObjectStore('blobs');
130
+ if (!db.objectStoreNames.contains('index'))
131
+ db.createObjectStore('index');
132
+ };
133
+ // 成功時ハンドラ
134
+ /**
135
+ * ハンドラ(成功時) - 戻り値なし
136
+ * @returns {void}
137
+ */
138
+ req.onsuccess = () => resolve(req.result);
139
+ // エラー時ハンドラ
140
+ /**
141
+ * ハンドラ(エラー時) - 戻り値なし
142
+ * @returns {void}
143
+ */
144
+ req.onerror = () => reject(req.error);
145
+ });
146
+ }
147
+ /**
148
+ * IndexedDB トランザクションをラップしてコールバックを実行します。
149
+ * @param {string} storeName ストア名
150
+ * @param {IDBTransactionMode} mode トランザクションモード
151
+ * @param {(store: IDBObjectStore)=>void|Promise<void>} cb 実行コールバック
152
+ * @returns {Promise<void>}
153
+ */
154
+ async tx(storeName, mode, cb) {
155
+ const db = await this.dbPromise;
156
+ return new Promise((resolve, reject) => {
157
+ const tx = db.transaction(storeName, mode);
158
+ const storeObj = tx.objectStore(storeName);
159
+ Promise.resolve(cb(storeObj)).then(() => {
160
+ // トランザクション完了時ハンドラ
161
+ /**
162
+ * トランザクション完了時のコールバック(内部処理)
163
+ * @returns {void}
164
+ */
165
+ tx.oncomplete = () => resolve();
166
+ // トランザクションエラー時ハンドラ
167
+ /**
168
+ * トランザクションエラー時のコールバック(内部処理)
169
+ * @returns {void}
170
+ */
171
+ tx.onerror = () => reject(tx.error);
172
+ }).catch(reject);
173
+ });
174
+ }
175
+ /**
176
+ * index を IndexedDB から読み出します。
177
+ * @returns {Promise<IndexFile|null>} 読み込んだ Index ファイル、または null
178
+ */
179
+ async readIndex() {
180
+ const db = await this.dbPromise;
181
+ return new Promise((resolve, reject) => {
182
+ const tx = db.transaction('index', 'readonly');
183
+ const store = tx.objectStore('index');
184
+ const req = store.get('index');
185
+ // onsuccess handler
186
+ /**
187
+ * onsuccess ハンドラ(内部処理) - 戻り値なし
188
+ * @returns {void}
189
+ */
190
+ req.onsuccess = () => resolve(req.result ?? null);
191
+ // onerror handler
192
+ /**
193
+ * onerror ハンドラ(内部処理) - 戻り値なし
194
+ * @returns {void}
195
+ */
196
+ req.onerror = () => reject(req.error);
197
+ });
198
+ }
199
+ /**
200
+ * index を IndexedDB に書き込みます。
201
+ * @param {IndexFile} index 書き込むデータ
202
+ * @returns {Promise<void>}
203
+ */
204
+ async writeIndex(index) {
205
+ await this.tx('index', 'readwrite', (store) => { store.put(index, 'index'); });
206
+ }
207
+ /**
208
+ * blob を書き込みます。OPFS がある場合は OPFS を優先して使用します。
209
+ * @param {string} filepath ファイルパス
210
+ * @param {string} content ファイル内容
211
+ * @returns {Promise<void>}
212
+ */
213
+ async writeBlob(filepath, content) {
214
+ // try OPFS if available
215
+ // @ts-ignore
216
+ const opfs = globalThis.originPrivateFileSystem;
217
+ if (opfs && opfs.getFileHandle) {
218
+ try {
219
+ // naive OPFS write
220
+ // @ts-ignore
221
+ const root = await opfs.getDirectory();
222
+ const parts = filepath.split('/');
223
+ let dir = root;
224
+ for (let i = 0; i < parts.length - 1; i++) {
225
+ dir = await dir.getDirectory(parts[i]);
226
+ }
227
+ const fh = await dir.getFileHandle(parts[parts.length - 1], { create: true });
228
+ const writable = await fh.createWritable();
229
+ await writable.write(content);
230
+ await writable.close();
231
+ return;
232
+ }
233
+ catch (e) {
234
+ // fallthrough to indexeddb
235
+ }
236
+ }
237
+ await this.tx('blobs', 'readwrite', (store) => { store.put(content, filepath); });
238
+ }
239
+ /**
240
+ * 指定パスの blob を読み出します。存在しなければ null を返します。
241
+ * @param {string} filepath ファイルパス
242
+ * @returns {Promise<string|null>} ファイル内容または null
243
+ */
244
+ async readBlob(filepath) {
245
+ // try OPFS read
246
+ // @ts-ignore
247
+ const opfs = globalThis.originPrivateFileSystem;
248
+ if (opfs && opfs.getFileHandle) {
249
+ try {
250
+ // @ts-ignore
251
+ const root = await opfs.getDirectory();
252
+ const parts = filepath.split('/');
253
+ let dir = root;
254
+ for (let i = 0; i < parts.length - 1; i++) {
255
+ dir = await dir.getDirectory(parts[i]);
256
+ }
257
+ const fh = await dir.getFileHandle(parts[parts.length - 1]);
258
+ const file = await fh.getFile();
259
+ return await file.text();
260
+ }
261
+ catch (e) {
262
+ // fallback
263
+ }
264
+ }
265
+ const db = await this.dbPromise;
266
+ return new Promise((resolve, reject) => {
267
+ const tx = db.transaction('blobs', 'readonly');
268
+ const store = tx.objectStore('blobs');
269
+ const req = store.get(filepath);
270
+ // onsuccess handler
271
+ /**
272
+ * onsuccess ハンドラ(内部処理) - 戻り値なし
273
+ * @returns {void}
274
+ */
275
+ req.onsuccess = () => resolve(req.result ?? null);
276
+ // onerror handler
277
+ /**
278
+ * onerror ハンドラ(内部処理) - 戻り値なし
279
+ * @returns {void}
280
+ */
281
+ req.onerror = () => reject(req.error);
282
+ });
283
+ }
284
+ /**
285
+ * 指定パスの blob を削除します。
286
+ * @param {string} filepath ファイルパス
287
+ * @returns {Promise<void>}
288
+ */
289
+ async deleteBlob(filepath) {
290
+ // delete from IndexedDB
291
+ await this.tx('blobs', 'readwrite', (store) => { store.delete(filepath); });
292
+ }
293
+ }
294
+ exports.BrowserStorage = BrowserStorage;
@@ -0,0 +1,46 @@
1
+ export type FileState = 'base' | 'modified' | 'added' | 'deleted' | 'conflict';
2
+ export interface IndexEntry {
3
+ path: string;
4
+ state: FileState;
5
+ baseSha?: string;
6
+ workspaceSha?: string;
7
+ updatedAt: number;
8
+ }
9
+ export interface IndexFile {
10
+ head: string;
11
+ lastCommitKey?: string;
12
+ entries: Record<string, IndexEntry>;
13
+ }
14
+ export interface TombstoneEntry {
15
+ path: string;
16
+ baseSha: string;
17
+ deletedAt: number;
18
+ }
19
+ export interface ConflictEntry {
20
+ path: string;
21
+ baseSha?: string;
22
+ remoteSha?: string;
23
+ workspaceSha?: string;
24
+ }
25
+ export type Change = {
26
+ type: 'create';
27
+ path: string;
28
+ content: string;
29
+ } | {
30
+ type: 'update';
31
+ path: string;
32
+ content: string;
33
+ baseSha: string;
34
+ } | {
35
+ type: 'delete';
36
+ path: string;
37
+ baseSha: string;
38
+ };
39
+ export interface CommitInput {
40
+ message: string;
41
+ parentSha: string;
42
+ changes: Change[];
43
+ ref?: string;
44
+ commitKey?: string;
45
+ }
46
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/virtualfs/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,UAAU,GAAG,OAAO,GAAG,SAAS,GAAG,UAAU,CAAA;AAE9E,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,SAAS,CAAA;IAChB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAA;IAEZ,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAA;CACpC;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,CAAA;IACf,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB;AAED,MAAM,MAAM,MAAM,GACd;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GACjD;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAClE;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAA;AAErD,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAA;IACf,SAAS,EAAE,MAAM,CAAA;IACjB,OAAO,EAAE,MAAM,EAAE,CAAA;IACjB,GAAG,CAAC,EAAE,MAAM,CAAA;IAEZ,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB"}
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,189 @@
1
+ import { IndexFile, TombstoneEntry } from './types';
2
+ import { StorageBackend } from './persistence';
3
+ /**
4
+ *
5
+ */
6
+ export declare class VirtualFS {
7
+ private storageDir;
8
+ private base;
9
+ private workspace;
10
+ private tombstones;
11
+ private index;
12
+ private backend;
13
+ /**
14
+ *
15
+ */
16
+ constructor(options?: {
17
+ storageDir?: string;
18
+ backend?: StorageBackend;
19
+ });
20
+ /**
21
+ * コンテンツから SHA1 を計算します。
22
+ * @param {string} content コンテンツ
23
+ * @returns {string} 計算された SHA
24
+ */
25
+ private shaOf;
26
+ /**
27
+ * VirtualFS の初期化を行います(バックエンド初期化と index 読み込み)。
28
+ * @returns {Promise<void>}
29
+ */
30
+ init(): Promise<void>;
31
+ /**
32
+ * 永続化レイヤーから index を読み込み、内部マップを初期化します。
33
+ * @returns {Promise<void>}
34
+ */
35
+ private loadIndex;
36
+ /**
37
+ * 内部インデックスを永続化します。
38
+ * @returns {Promise<void>}
39
+ */
40
+ private saveIndex;
41
+ /**
42
+ *
43
+ */
44
+ /**
45
+ * ワークスペースにファイルを書き込みます(ローカル編集)。
46
+ * @param {string} filepath ファイルパス
47
+ * @param {string} content コンテンツ
48
+ * @returns {Promise<void>}
49
+ */
50
+ writeWorkspace(filepath: string, content: string): Promise<void>;
51
+ /**
52
+ * ワークスペース上のファイルを削除します(トゥームストーン作成を含む)。
53
+ * @param {string} filepath ファイルパス
54
+ * @returns {Promise<void>}
55
+ */
56
+ deleteWorkspace(filepath: string): Promise<void>;
57
+ /**
58
+ * rename を delete + create の合成で行うヘルパ
59
+ * @param from 元パス
60
+ * @param to 新パス
61
+ */
62
+ renameWorkspace(from: string, to: string): Promise<void>;
63
+ /**
64
+ * ワークスペース/ベースからファイル内容を読み出します。
65
+ * @param {string} filepath ファイルパス
66
+ * @returns {Promise<string|null>} ファイル内容または null
67
+ */
68
+ readWorkspace(filepath: string): Promise<string | null>;
69
+ /**
70
+ * リモートのベーススナップショットを適用します。
71
+ * @param {{[path:string]:string}} snapshot path->content のマップ
72
+ * @param {string} headSha リモート HEAD
73
+ * @returns {Promise<void>}
74
+ */
75
+ applyBaseSnapshot(snapshot: Record<string, string>, headSha: string): Promise<void>;
76
+ /**
77
+ * インデックス情報を返します。
78
+ * @returns {IndexFile}
79
+ */
80
+ getIndex(): IndexFile;
81
+ /**
82
+ * 登録されているパス一覧を返します。
83
+ * @returns {string[]}
84
+ */
85
+ listPaths(): string[];
86
+ /**
87
+ * tombstone を返します。
88
+ * @returns {TombstoneEntry[]}
89
+ */
90
+ getTombstones(): TombstoneEntry[];
91
+ /**
92
+ * tombstone を返します。
93
+ * @returns {TombstoneEntry[]}
94
+ */
95
+ /**
96
+ *
97
+ */
98
+ getChangeSet(): Promise<({
99
+ type: 'create';
100
+ path: string;
101
+ content: string;
102
+ } | {
103
+ type: 'update';
104
+ path: string;
105
+ content: string;
106
+ baseSha?: string | undefined;
107
+ } | {
108
+ type: 'delete';
109
+ path: string;
110
+ baseSha: string;
111
+ })[]>;
112
+ /**
113
+ * tombstone からの削除変更リストを生成します。
114
+ * @returns {Array<{type:'delete',path:string,baseSha:string}>}
115
+ */
116
+ private _changesFromTombstones;
117
+ /**
118
+ * index entries から create/update の変更リストを生成します。
119
+ * @returns {Array<{type:'create'|'update',path:string,content:string,baseSha?:string}>}
120
+ */
121
+ private _changesFromIndexEntries;
122
+ /**
123
+ * 追加状態のエントリから create 変更を生成します。
124
+ * @returns {Array<{type:'create',path:string,content:string}>}
125
+ */
126
+ private _changesFromAddedEntries;
127
+ /**
128
+ * 変更状態のエントリから update 変更を生成します。
129
+ * @returns {Array<{type:'update',path:string,content:string,baseSha:string}>}
130
+ */
131
+ private _changesFromModifiedEntries;
132
+ /**
133
+ * リモートスナップショットからの差分取り込み時に、単一パスを評価して
134
+ * 必要なら conflicts に追加、もしくは base を更新します。
135
+ * @returns {Promise<void>}
136
+ */
137
+ private _handleRemotePath;
138
+ /**
139
+ * リモートに存在するがローカルにないパスを処理します。
140
+ * @returns {Promise<void>}
141
+ */
142
+ private _handleRemoteNew;
143
+ /**
144
+ * リモートに存在し、かつローカルにエントリがあるパスを処理します。
145
+ * @returns {Promise<void>}
146
+ */
147
+ private _handleRemoteExisting;
148
+ /**
149
+ * ローカルに対する変更(create/update/delete)を適用するヘルパー
150
+ * @param {any} ch 変更オブジェクト
151
+ * @returns {Promise<void>}
152
+ */
153
+ private _applyChangeLocally;
154
+ /**
155
+ * リモート側で削除されたエントリをローカルに反映します。
156
+ * @returns {Promise<void>}
157
+ */
158
+ private _handleRemoteDeletion;
159
+ /**
160
+ * GitLab 風の actions ベースコミットフローで push を実行します。
161
+ * @returns {Promise<{commitSha:string}>}
162
+ */
163
+ private _pushWithActions;
164
+ /**
165
+ * GitHub 風の blob/tree/commit フローで push を実行します。
166
+ * @returns {Promise<{commitSha:string}>}
167
+ */
168
+ private _pushWithGitHubFlow;
169
+ /**
170
+ * リモートのスナップショットを取り込み、コンフリクト情報を返します。
171
+ * @param {string} remoteHead リモート HEAD
172
+ * @param {{[path:string]:string}} baseSnapshot path->content マップ
173
+ * @returns {Promise<{conflicts:Array<import('./types').ConflictEntry>}>}
174
+ */
175
+ pull(remoteHead: string, baseSnapshot: Record<string, string>): Promise<{
176
+ conflicts: import("./types").ConflictEntry[];
177
+ }>;
178
+ /**
179
+ * 変更をコミットしてリモートへ反映します。adapter が無ければローカルシミュレーションします。
180
+ * @param {import('./types').CommitInput} input コミット入力
181
+ * @param {import('../git/adapter').GitAdapter} [adapter] 任意のアダプタ
182
+ * @returns {Promise<{commitSha:string}>}
183
+ */
184
+ push(input: import('./types').CommitInput, adapter?: import('../git/adapter').GitAdapter): Promise<{
185
+ commitSha: any;
186
+ }>;
187
+ }
188
+ export default VirtualFS;
189
+ //# sourceMappingURL=virtualfs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"virtualfs.d.ts","sourceRoot":"","sources":["../../src/virtualfs/virtualfs.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AACnD,OAAO,EAAE,cAAc,EAAiB,MAAM,eAAe,CAAA;AAE7D;;GAEG;AACH,qBAAa,SAAS;IACpB,OAAO,CAAC,UAAU,CAAoB;IACtC,OAAO,CAAC,IAAI,CAAsD;IAClE,OAAO,CAAC,SAAS,CAAsD;IACvE,OAAO,CAAC,UAAU,CAAoC;IACtD,OAAO,CAAC,KAAK,CAAuC;IACpD,OAAO,CAAC,OAAO,CAAgB;IAE/B;;OAEG;gBACS,OAAO,CAAC,EAAE;QAAE,UAAU,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,cAAc,CAAA;KAAE;IAOvE;;;;OAIG;IACH,OAAO,CAAC,KAAK;IAIb;;;OAGG;IACG,IAAI;IAKV;;;OAGG;YACW,SAAS;IAmBvB;;;OAGG;YACW,SAAS;IAIvB;;OAEG;IACH;;;;;OAKG;IACG,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;IAkBtD;;;;OAIG;IACG,eAAe,CAAC,QAAQ,EAAE,MAAM;IAqBtC;;;;OAIG;IACG,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM;IAa9C;;;;OAIG;IACG,aAAa,CAAC,QAAQ,EAAE,MAAM;IAWpC;;;;;OAKG;IACG,iBAAiB,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,MAAM;IA2BzE;;;OAGG;IACH,QAAQ,IAAI,SAAS;IAIrB;;;OAGG;IACH,SAAS,IAAI,MAAM,EAAE;IAIrB;;;OAGG;IACH,aAAa,IAAI,cAAc,EAAE;IAIjC;;;OAGG;IAEH;;OAEG;IACG,YAAY;cAGJ,QAAQ;cAAQ,MAAM;iBAAW,MAAM;;cACvC,QAAQ;cAAQ,MAAM;iBAAW,MAAM;;;cACvC,QAAQ;cAAQ,MAAM;iBAAW,MAAM;;IAQrD;;;OAGG;IACH,OAAO,CAAC,sBAAsB;IAM9B;;;OAGG;IACH,OAAO,CAAC,wBAAwB;IAOhC;;;OAGG;IACH,OAAO,CAAC,wBAAwB;IAWhC;;;OAGG;IACH,OAAO,CAAC,2BAA2B;IAWnC;;;;OAIG;YACW,iBAAiB;IAS/B;;;OAGG;YACW,gBAAgB;IAa9B;;;OAGG;YACW,qBAAqB;IAkBnC;;;;OAIG;YACW,mBAAmB;IAqBjC;;;OAGG;YACW,qBAAqB;IAYnC;;;OAGG;YACW,gBAAgB;IAe9B;;;OAGG;YACW,mBAAmB;IAkBjC;;;;;OAKG;IACG,IAAI,CAAC,UAAU,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;;;IA6BnE;;;;;OAKG;IACG,IAAI,CAAC,KAAK,EAAE,OAAO,SAAS,EAAE,WAAW,EAAE,OAAO,CAAC,EAAE,OAAO,gBAAgB,EAAE,UAAU;;;CAkD/F;AAED,eAAe,SAAS,CAAA"}