fwdcast 1.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.
- package/README.md +67 -0
- package/dist/html-generator.d.ts +22 -0
- package/dist/html-generator.d.ts.map +1 -0
- package/dist/html-generator.js +623 -0
- package/dist/html-generator.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +229 -0
- package/dist/index.js.map +1 -0
- package/dist/protocol.d.ts +109 -0
- package/dist/protocol.d.ts.map +1 -0
- package/dist/protocol.js +138 -0
- package/dist/protocol.js.map +1 -0
- package/dist/scanner.d.ts +46 -0
- package/dist/scanner.d.ts.map +1 -0
- package/dist/scanner.js +138 -0
- package/dist/scanner.js.map +1 -0
- package/dist/tunnel-client.d.ts +143 -0
- package/dist/tunnel-client.d.ts.map +1 -0
- package/dist/tunnel-client.js +428 -0
- package/dist/tunnel-client.js.map +1 -0
- package/dist/validator.d.ts +53 -0
- package/dist/validator.d.ts.map +1 -0
- package/dist/validator.js +65 -0
- package/dist/validator.js.map +1 -0
- package/package.json +64 -0
- package/scripts/postinstall.js +23 -0
package/dist/scanner.js
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.scanDirectory = scanDirectory;
|
|
37
|
+
exports.calculateScanResult = calculateScanResult;
|
|
38
|
+
exports.scanDirectoryShallow = scanDirectoryShallow;
|
|
39
|
+
const fs = __importStar(require("fs/promises"));
|
|
40
|
+
const path = __importStar(require("path"));
|
|
41
|
+
/**
|
|
42
|
+
* Recursively scans a directory and returns all entries.
|
|
43
|
+
* Symlinks are skipped.
|
|
44
|
+
*
|
|
45
|
+
* @param dirPath - The directory path to scan
|
|
46
|
+
* @param basePath - The base path for calculating relative paths (defaults to dirPath)
|
|
47
|
+
* @returns Array of DirectoryEntry objects
|
|
48
|
+
*/
|
|
49
|
+
async function scanDirectory(dirPath, basePath) {
|
|
50
|
+
const resolvedPath = path.resolve(dirPath);
|
|
51
|
+
const resolvedBase = basePath ? path.resolve(basePath) : resolvedPath;
|
|
52
|
+
const entries = [];
|
|
53
|
+
const items = await fs.readdir(resolvedPath, { withFileTypes: true });
|
|
54
|
+
for (const item of items) {
|
|
55
|
+
const absolutePath = path.join(resolvedPath, item.name);
|
|
56
|
+
// Skip symlinks as per requirements
|
|
57
|
+
if (item.isSymbolicLink()) {
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
const stat = await fs.stat(absolutePath);
|
|
61
|
+
const relativePath = path.relative(resolvedBase, absolutePath);
|
|
62
|
+
const entry = {
|
|
63
|
+
name: item.name,
|
|
64
|
+
relativePath,
|
|
65
|
+
absolutePath,
|
|
66
|
+
isDirectory: item.isDirectory(),
|
|
67
|
+
size: item.isDirectory() ? 0 : stat.size,
|
|
68
|
+
modifiedAt: stat.mtime,
|
|
69
|
+
};
|
|
70
|
+
entries.push(entry);
|
|
71
|
+
// Recursively scan subdirectories
|
|
72
|
+
if (item.isDirectory()) {
|
|
73
|
+
const subEntries = await scanDirectory(absolutePath, resolvedBase);
|
|
74
|
+
entries.push(...subEntries);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return entries;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Calculates the total size and counts from directory entries.
|
|
81
|
+
*
|
|
82
|
+
* @param entries - Array of DirectoryEntry objects
|
|
83
|
+
* @returns ScanResult with entries, totalSize, fileCount, and directoryCount
|
|
84
|
+
*/
|
|
85
|
+
function calculateScanResult(entries) {
|
|
86
|
+
let totalSize = 0;
|
|
87
|
+
let fileCount = 0;
|
|
88
|
+
let directoryCount = 0;
|
|
89
|
+
for (const entry of entries) {
|
|
90
|
+
if (entry.isDirectory) {
|
|
91
|
+
directoryCount++;
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
fileCount++;
|
|
95
|
+
totalSize += entry.size;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return {
|
|
99
|
+
entries,
|
|
100
|
+
totalSize,
|
|
101
|
+
fileCount,
|
|
102
|
+
directoryCount,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Scans only the immediate children of a directory (non-recursive).
|
|
107
|
+
* Used for dynamic directory listings.
|
|
108
|
+
*
|
|
109
|
+
* @param dirPath - The directory path to scan
|
|
110
|
+
* @param basePath - The base path for calculating relative paths
|
|
111
|
+
* @returns Array of DirectoryEntry objects for immediate children only
|
|
112
|
+
*/
|
|
113
|
+
async function scanDirectoryShallow(dirPath, basePath) {
|
|
114
|
+
const resolvedPath = path.resolve(dirPath);
|
|
115
|
+
const resolvedBase = path.resolve(basePath);
|
|
116
|
+
const entries = [];
|
|
117
|
+
const items = await fs.readdir(resolvedPath, { withFileTypes: true });
|
|
118
|
+
for (const item of items) {
|
|
119
|
+
const absolutePath = path.join(resolvedPath, item.name);
|
|
120
|
+
// Skip symlinks as per requirements
|
|
121
|
+
if (item.isSymbolicLink()) {
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
const stat = await fs.stat(absolutePath);
|
|
125
|
+
const relativePath = path.relative(resolvedBase, absolutePath);
|
|
126
|
+
const entry = {
|
|
127
|
+
name: item.name,
|
|
128
|
+
relativePath,
|
|
129
|
+
absolutePath,
|
|
130
|
+
isDirectory: item.isDirectory(),
|
|
131
|
+
size: item.isDirectory() ? 0 : stat.size,
|
|
132
|
+
modifiedAt: stat.mtime,
|
|
133
|
+
};
|
|
134
|
+
entries.push(entry);
|
|
135
|
+
}
|
|
136
|
+
return entries;
|
|
137
|
+
}
|
|
138
|
+
//# sourceMappingURL=scanner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scanner.js","sourceRoot":"","sources":["../src/scanner.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiCA,sCAwCC;AAQD,kDAoBC;AAUD,oDAkCC;AAjJD,gDAAkC;AAClC,2CAA6B;AAwB7B;;;;;;;GAOG;AACI,KAAK,UAAU,aAAa,CACjC,OAAe,EACf,QAAiB;IAEjB,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IAC3C,MAAM,YAAY,GAAG,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC;IACtE,MAAM,OAAO,GAAqB,EAAE,CAAC;IAErC,MAAM,KAAK,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,YAAY,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;IAEtE,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;QAExD,oCAAoC;QACpC,IAAI,IAAI,CAAC,cAAc,EAAE,EAAE,CAAC;YAC1B,SAAS;QACX,CAAC;QAED,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QACzC,MAAM,YAAY,GAAG,IAAI,CAAC,QAAQ,CAAC,YAAY,EAAE,YAAY,CAAC,CAAC;QAE/D,MAAM,KAAK,GAAmB;YAC5B,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,YAAY;YACZ,YAAY;YACZ,WAAW,EAAE,IAAI,CAAC,WAAW,EAAE;YAC/B,IAAI,EAAE,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI;YACxC,UAAU,EAAE,IAAI,CAAC,KAAK;SACvB,CAAC;QAEF,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAEpB,kCAAkC;QAClC,IAAI,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC;YACvB,MAAM,UAAU,GAAG,MAAM,aAAa,CAAC,YAAY,EAAE,YAAY,CAAC,CAAC;YACnE,OAAO,CAAC,IAAI,CAAC,GAAG,UAAU,CAAC,CAAC;QAC9B,CAAC;IACH,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;;;GAKG;AACH,SAAgB,mBAAmB,CAAC,OAAyB;IAC3D,IAAI,SAAS,GAAG,CAAC,CAAC;IAClB,IAAI,SAAS,GAAG,CAAC,CAAC;IAClB,IAAI,cAAc,GAAG,CAAC,CAAC;IAEvB,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,IAAI,KAAK,CAAC,WAAW,EAAE,CAAC;YACtB,cAAc,EAAE,CAAC;QACnB,CAAC;aAAM,CAAC;YACN,SAAS,EAAE,CAAC;YACZ,SAAS,IAAI,KAAK,CAAC,IAAI,CAAC;QAC1B,CAAC;IACH,CAAC;IAED,OAAO;QACL,OAAO;QACP,SAAS;QACT,SAAS;QACT,cAAc;KACf,CAAC;AACJ,CAAC;AAED;;;;;;;GAOG;AACI,KAAK,UAAU,oBAAoB,CACxC,OAAe,EACf,QAAgB;IAEhB,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IAC3C,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IAC5C,MAAM,OAAO,GAAqB,EAAE,CAAC;IAErC,MAAM,KAAK,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,YAAY,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;IAEtE,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;QAExD,oCAAoC;QACpC,IAAI,IAAI,CAAC,cAAc,EAAE,EAAE,CAAC;YAC1B,SAAS;QACX,CAAC;QAED,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QACzC,MAAM,YAAY,GAAG,IAAI,CAAC,QAAQ,CAAC,YAAY,EAAE,YAAY,CAAC,CAAC;QAE/D,MAAM,KAAK,GAAmB;YAC5B,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,YAAY;YACZ,YAAY;YACZ,WAAW,EAAE,IAAI,CAAC,WAAW,EAAE;YAC/B,IAAI,EAAE,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI;YACxC,UAAU,EAAE,IAAI,CAAC,KAAK;SACvB,CAAC;QAEF,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACtB,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC"}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tunnel Client Module
|
|
3
|
+
*
|
|
4
|
+
* Handles WebSocket connection to the relay server, message handling,
|
|
5
|
+
* and file streaming for the fwdcast CLI.
|
|
6
|
+
*
|
|
7
|
+
* Requirements: 1.5, 1.6, 3.1, 3.2, 3.6, 5.1, 5.3, 5.4, 5.5
|
|
8
|
+
*/
|
|
9
|
+
import { DirectoryEntry } from './scanner';
|
|
10
|
+
/**
|
|
11
|
+
* Configuration for the tunnel client
|
|
12
|
+
*/
|
|
13
|
+
export interface TunnelClientConfig {
|
|
14
|
+
relayUrl: string;
|
|
15
|
+
basePath: string;
|
|
16
|
+
entries: DirectoryEntry[];
|
|
17
|
+
expiresAt: number;
|
|
18
|
+
onUrl?: (url: string) => void;
|
|
19
|
+
onExpired?: () => void;
|
|
20
|
+
onDisconnect?: () => void;
|
|
21
|
+
onError?: (error: Error) => void;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Result of a successful registration
|
|
25
|
+
*/
|
|
26
|
+
export interface RegistrationResult {
|
|
27
|
+
sessionId: string;
|
|
28
|
+
url: string;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* TunnelClient manages the WebSocket connection to the relay server
|
|
32
|
+
* and handles incoming requests by serving files or directory listings.
|
|
33
|
+
*/
|
|
34
|
+
export declare class TunnelClient {
|
|
35
|
+
private ws;
|
|
36
|
+
private config;
|
|
37
|
+
private connected;
|
|
38
|
+
private sessionId;
|
|
39
|
+
private registrationPromise;
|
|
40
|
+
constructor(config: TunnelClientConfig);
|
|
41
|
+
/**
|
|
42
|
+
* Connect to the relay server and register the session.
|
|
43
|
+
* Returns the public URL for the shared directory.
|
|
44
|
+
*
|
|
45
|
+
* Requirements: 1.5, 1.6, 5.1
|
|
46
|
+
*/
|
|
47
|
+
connect(): Promise<RegistrationResult>;
|
|
48
|
+
/**
|
|
49
|
+
* Disconnect from the relay server
|
|
50
|
+
*/
|
|
51
|
+
disconnect(): void;
|
|
52
|
+
/**
|
|
53
|
+
* Check if the client is connected
|
|
54
|
+
*/
|
|
55
|
+
isConnected(): boolean;
|
|
56
|
+
/**
|
|
57
|
+
* Send the register message to the relay server
|
|
58
|
+
* Requirements: 5.1
|
|
59
|
+
*/
|
|
60
|
+
private sendRegisterMessage;
|
|
61
|
+
/**
|
|
62
|
+
* Handle incoming WebSocket messages
|
|
63
|
+
*/
|
|
64
|
+
private handleMessage;
|
|
65
|
+
/**
|
|
66
|
+
* Handle registration response from relay
|
|
67
|
+
* Requirements: 1.6
|
|
68
|
+
*/
|
|
69
|
+
private handleRegistered;
|
|
70
|
+
/**
|
|
71
|
+
* Handle incoming request from relay
|
|
72
|
+
* Routes to file server or directory listing
|
|
73
|
+
*
|
|
74
|
+
* Requirements: 3.1, 3.2, 5.3, 5.4, 5.5
|
|
75
|
+
*/
|
|
76
|
+
private handleRequest;
|
|
77
|
+
/**
|
|
78
|
+
* Handle session expired message
|
|
79
|
+
*/
|
|
80
|
+
private handleExpired;
|
|
81
|
+
/**
|
|
82
|
+
* Serve a ZIP download of a directory
|
|
83
|
+
*/
|
|
84
|
+
private serveZipDownload;
|
|
85
|
+
/**
|
|
86
|
+
* Serve a directory listing as HTML
|
|
87
|
+
* Requirements: 3.5
|
|
88
|
+
*
|
|
89
|
+
* Dynamically scans the directory on each request to reflect
|
|
90
|
+
* any files added/removed since the session started.
|
|
91
|
+
*/
|
|
92
|
+
private serveDirectoryListing;
|
|
93
|
+
/**
|
|
94
|
+
* Stream a file to the relay
|
|
95
|
+
* Requirements: 3.6
|
|
96
|
+
*/
|
|
97
|
+
private serveFile;
|
|
98
|
+
/**
|
|
99
|
+
* Stream file contents in chunks
|
|
100
|
+
* Requirements: 3.6, 5.4, 5.5
|
|
101
|
+
*/
|
|
102
|
+
private streamFile;
|
|
103
|
+
/**
|
|
104
|
+
* Send an error response
|
|
105
|
+
*/
|
|
106
|
+
private sendErrorResponse;
|
|
107
|
+
/**
|
|
108
|
+
* Send a response message
|
|
109
|
+
* Requirements: 5.3
|
|
110
|
+
*/
|
|
111
|
+
sendResponse(id: string, status: number, headers: Record<string, string>): void;
|
|
112
|
+
/**
|
|
113
|
+
* Send a data message with base64 encoded chunk
|
|
114
|
+
* Requirements: 5.4
|
|
115
|
+
*/
|
|
116
|
+
sendData(id: string, chunk: Buffer): void;
|
|
117
|
+
/**
|
|
118
|
+
* Send an end message
|
|
119
|
+
* Requirements: 5.5
|
|
120
|
+
*/
|
|
121
|
+
sendEnd(id: string): void;
|
|
122
|
+
/**
|
|
123
|
+
* Send a message through the WebSocket
|
|
124
|
+
*/
|
|
125
|
+
private send;
|
|
126
|
+
/**
|
|
127
|
+
* Normalize a request path
|
|
128
|
+
*/
|
|
129
|
+
private normalizePath;
|
|
130
|
+
/**
|
|
131
|
+
* Get the content type for a file based on its extension
|
|
132
|
+
*/
|
|
133
|
+
private getContentType;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Create and connect a tunnel client
|
|
137
|
+
* Convenience function for simple usage
|
|
138
|
+
*/
|
|
139
|
+
export declare function createTunnelClient(config: TunnelClientConfig): Promise<{
|
|
140
|
+
client: TunnelClient;
|
|
141
|
+
result: RegistrationResult;
|
|
142
|
+
}>;
|
|
143
|
+
//# sourceMappingURL=tunnel-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tunnel-client.d.ts","sourceRoot":"","sources":["../src/tunnel-client.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAyBH,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAG3C;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,cAAc,EAAE,CAAC;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9B,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;IACvB,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;IAC1B,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CAClC;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;CACb;AAOD;;;GAGG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,EAAE,CAA0B;IACpC,OAAO,CAAC,MAAM,CAAqB;IACnC,OAAO,CAAC,SAAS,CAAkB;IACnC,OAAO,CAAC,SAAS,CAAuB;IACxC,OAAO,CAAC,mBAAmB,CAGX;gBAEJ,MAAM,EAAE,kBAAkB;IAItC;;;;;OAKG;IACG,OAAO,IAAI,OAAO,CAAC,kBAAkB,CAAC;IA6C5C;;OAEG;IACH,UAAU,IAAI,IAAI;IAQlB;;OAEG;IACH,WAAW,IAAI,OAAO;IAItB;;;OAGG;IACH,OAAO,CAAC,mBAAmB;IAQ3B;;OAEG;IACH,OAAO,CAAC,aAAa;IAkBrB;;;OAGG;IACH,OAAO,CAAC,gBAAgB;IAmBxB;;;;;OAKG;YACW,aAAa;IA6C3B;;OAEG;IACH,OAAO,CAAC,aAAa;IAOrB;;OAEG;YACW,gBAAgB;IA4C9B;;;;;;OAMG;YACW,qBAAqB;IA2BnC;;;OAGG;YACW,SAAS;IAwBvB;;;OAGG;IACH,OAAO,CAAC,UAAU;IAwBlB;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAgBzB;;;OAGG;IACH,YAAY,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI;IAK/E;;;OAGG;IACH,QAAQ,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAKzC;;;OAGG;IACH,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAKzB;;OAEG;IACH,OAAO,CAAC,IAAI;IAMZ;;OAEG;IACH,OAAO,CAAC,aAAa;IAQrB;;OAEG;IACH,OAAO,CAAC,cAAc;CAIvB;AAED;;;GAGG;AACH,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,kBAAkB,GACzB,OAAO,CAAC;IAAE,MAAM,EAAE,YAAY,CAAC;IAAC,MAAM,EAAE,kBAAkB,CAAA;CAAE,CAAC,CAI/D"}
|