@ytspar/sweetlink 1.20.0 → 1.22.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/dist/browser/consoleCapture.js +1 -1
- package/dist/browser/consoleCapture.js.map +1 -1
- package/dist/browser/earlyConsoleCapture.d.ts +1 -1
- package/dist/browser/earlyConsoleCapture.d.ts.map +1 -1
- package/dist/cli/sweetlink.js +154 -68
- package/dist/cli/sweetlink.js.map +1 -1
- package/dist/daemon/demo.d.ts.map +1 -1
- package/dist/daemon/demo.js +3 -19
- package/dist/daemon/demo.js.map +1 -1
- package/dist/daemon/evidence.js +1 -1
- package/dist/daemon/evidence.js.map +1 -1
- package/dist/daemon/listeners.js +1 -1
- package/dist/daemon/listeners.js.map +1 -1
- package/dist/daemon/recording.d.ts +1 -1
- package/dist/daemon/recording.d.ts.map +1 -1
- package/dist/daemon/recording.js +5 -23
- package/dist/daemon/recording.js.map +1 -1
- package/dist/daemon/refs.js +1 -1
- package/dist/daemon/refs.js.map +1 -1
- package/dist/daemon/ringBuffer.d.ts +8 -0
- package/dist/daemon/ringBuffer.d.ts.map +1 -1
- package/dist/daemon/ringBuffer.js +17 -0
- package/dist/daemon/ringBuffer.js.map +1 -1
- package/dist/daemon/server.d.ts.map +1 -1
- package/dist/daemon/server.js +84 -23
- package/dist/daemon/server.js.map +1 -1
- package/dist/daemon/session.d.ts +8 -1
- package/dist/daemon/session.d.ts.map +1 -1
- package/dist/daemon/stateFile.js +2 -2
- package/dist/daemon/stateFile.js.map +1 -1
- package/dist/daemon/summary.js +2 -2
- package/dist/daemon/summary.js.map +1 -1
- package/dist/daemon/types.d.ts +7 -2
- package/dist/daemon/types.d.ts.map +1 -1
- package/dist/daemon/types.js.map +1 -1
- package/dist/daemon/utils.d.ts +22 -0
- package/dist/daemon/utils.d.ts.map +1 -0
- package/dist/daemon/utils.js +44 -0
- package/dist/daemon/utils.js.map +1 -0
- package/dist/daemon/viewer.d.ts.map +1 -1
- package/dist/daemon/viewer.js +5 -11
- package/dist/daemon/viewer.js.map +1 -1
- package/dist/daemon/visualDiff.d.ts +9 -0
- package/dist/daemon/visualDiff.d.ts.map +1 -1
- package/dist/daemon/visualDiff.js +13 -2
- package/dist/daemon/visualDiff.js.map +1 -1
- package/dist/next.js +3 -3
- package/dist/next.js.map +1 -1
- package/dist/runs.d.ts +15 -3
- package/dist/runs.d.ts.map +1 -1
- package/dist/runs.js +29 -13
- package/dist/runs.js.map +1 -1
- package/dist/server/index.d.ts +1 -1
- package/dist/server/index.d.ts.map +1 -1
- package/dist/simulator/android.d.ts +19 -0
- package/dist/simulator/android.d.ts.map +1 -1
- package/dist/simulator/android.js +141 -2
- package/dist/simulator/android.js.map +1 -1
- package/dist/simulator/androidTaps.d.ts +109 -0
- package/dist/simulator/androidTaps.d.ts.map +1 -0
- package/dist/simulator/androidTaps.js +208 -0
- package/dist/simulator/androidTaps.js.map +1 -0
- package/dist/simulator/ios.d.ts +7 -0
- package/dist/simulator/ios.d.ts.map +1 -1
- package/dist/simulator/ios.js +70 -25
- package/dist/simulator/ios.js.map +1 -1
- package/dist/simulator/overlay.d.ts +41 -0
- package/dist/simulator/overlay.d.ts.map +1 -0
- package/dist/simulator/overlay.js +78 -0
- package/dist/simulator/overlay.js.map +1 -0
- package/dist/term/cast.d.ts +24 -0
- package/dist/term/cast.d.ts.map +1 -0
- package/dist/term/cast.js +24 -0
- package/dist/term/cast.js.map +1 -0
- package/dist/term/player.d.ts.map +1 -1
- package/dist/term/player.js +37 -15
- package/dist/term/player.js.map +1 -1
- package/dist/term/recorder.d.ts +7 -0
- package/dist/term/recorder.d.ts.map +1 -1
- package/dist/term/recorder.js +36 -2
- package/dist/term/recorder.js.map +1 -1
- package/dist/types.d.ts +2 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +1 -1
- package/dist/term/ansi.d.ts +0 -37
- package/dist/term/ansi.d.ts.map +0 -1
- package/dist/term/ansi.js +0 -205
- package/dist/term/ansi.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"visualDiff.js","sourceRoot":"","sources":["../../src/daemon/visualDiff.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,QAAQ,IAAI,EAAE,EAAE,MAAM,IAAI,CAAC;AACpC,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"visualDiff.js","sourceRoot":"","sources":["../../src/daemon/visualDiff.ts"],"names":[],"mappings":"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"}
|
package/dist/next.js
CHANGED
|
@@ -32,19 +32,19 @@ function detectNextPort() {
|
|
|
32
32
|
// --port 3002 or -p 3002
|
|
33
33
|
if ((arg === '--port' || arg === '-p') && args[i + 1]) {
|
|
34
34
|
const port = parseInt(args[i + 1], 10);
|
|
35
|
-
if (!isNaN(port))
|
|
35
|
+
if (!Number.isNaN(port))
|
|
36
36
|
return port;
|
|
37
37
|
}
|
|
38
38
|
// --port=3002
|
|
39
39
|
if (arg?.startsWith('--port=')) {
|
|
40
40
|
const port = parseInt(arg.split('=')[1], 10);
|
|
41
|
-
if (!isNaN(port))
|
|
41
|
+
if (!Number.isNaN(port))
|
|
42
42
|
return port;
|
|
43
43
|
}
|
|
44
44
|
}
|
|
45
45
|
if (process.env.PORT) {
|
|
46
46
|
const port = parseInt(process.env.PORT, 10);
|
|
47
|
-
if (!isNaN(port))
|
|
47
|
+
if (!Number.isNaN(port))
|
|
48
48
|
return port;
|
|
49
49
|
}
|
|
50
50
|
return 3000;
|
package/dist/next.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"next.js","sourceRoot":"","sources":["../src/next.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAClE,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAU5C;;;;GAIG;AACH,SAAS,cAAc;IACrB,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAC1B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACrC,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;QACpB,yBAAyB;QACzB,IAAI,CAAC,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,IAAI,CAAC,IAAI,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;YACtD,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAE,EAAE,EAAE,CAAC,CAAC;YACxC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC;gBAAE,OAAO,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"next.js","sourceRoot":"","sources":["../src/next.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAClE,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAU5C;;;;GAIG;AACH,SAAS,cAAc;IACrB,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAC1B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACrC,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;QACpB,yBAAyB;QACzB,IAAI,CAAC,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,IAAI,CAAC,IAAI,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;YACtD,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAE,EAAE,EAAE,CAAC,CAAC;YACxC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC;gBAAE,OAAO,IAAI,CAAC;QACvC,CAAC;QACD,cAAc;QACd,IAAI,GAAG,EAAE,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;YAC/B,MAAM,IAAI,GAAG,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAE,EAAE,EAAE,CAAC,CAAC;YAC9C,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC;gBAAE,OAAO,IAAI,CAAC;QACvC,CAAC;IACH,CAAC;IAED,IAAI,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;QACrB,MAAM,IAAI,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QAC5C,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC;YAAE,OAAO,IAAI,CAAC;IACvC,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,aAAa,CAAI,UAAa,EAAE,OAA8B;IAC5E,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,aAAa;QAAE,OAAO,UAAU,CAAC;IAE9D,MAAM,OAAO,GAAG,OAAO,EAAE,IAAI,IAAI,cAAc,EAAE,CAAC;IAClD,MAAM,MAAM,GAAG,OAAO,GAAG,cAAc,CAAC;IAExC,aAAa,CAAC;QACZ,IAAI,EAAE,MAAM;QACZ,OAAO;QACP,OAAO,EAAE,CAAC,UAAU,EAAE,EAAE;YACtB,IAAI,UAAU,KAAK,MAAM,EAAE,CAAC;gBAC1B,OAAO,CAAC,GAAG,CAAC,0BAA0B,UAAU,KAAK,MAAM,cAAc,CAAC,CAAC;YAC7E,CAAC;YACD,OAAO,CAAC,GAAG,CAAC,uDAAuD,OAAO,GAAG,CAAC,CAAC;QACjF,CAAC;KACF,CAAC,CAAC;IAEH,MAAM,cAAc,GAAG,GAAS,EAAE;QAChC,cAAc,EAAE,CAAC;IACnB,CAAC,CAAC;IACF,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,cAAc,CAAC,CAAC;IACtC,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,cAAc,CAAC,CAAC;IAErC,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,eAAe,aAAa,CAAC"}
|
package/dist/runs.d.ts
CHANGED
|
@@ -20,15 +20,27 @@ export interface RunSlotOptions {
|
|
|
20
20
|
app?: string;
|
|
21
21
|
/** Override the run identifier. Defaults to env SWEETLINK_RUN or HHMM. */
|
|
22
22
|
run?: string;
|
|
23
|
-
/** Subdirectory inside the run slot
|
|
24
|
-
kind?: 'term' | 'sim' | 'session'
|
|
23
|
+
/** Subdirectory inside the run slot. Omit for the session root. */
|
|
24
|
+
kind?: 'term' | 'sim' | 'session';
|
|
25
25
|
}
|
|
26
|
+
/**
|
|
27
|
+
* Slugify a user-supplied namespace segment into something safe to use as a
|
|
28
|
+
* directory name. Without this, `--app ../../../tmp/x` would let a malicious
|
|
29
|
+
* caller (CI template, package.json script, batch JSON producer) write
|
|
30
|
+
* artifacts anywhere the user can write — including ~/.ssh/authorized_keys
|
|
31
|
+
* or LaunchAgents. Limited charset matches the existing --label slugifier.
|
|
32
|
+
*/
|
|
33
|
+
export declare function slugifyNamespace(value: string): string;
|
|
26
34
|
/**
|
|
27
35
|
* Resolve the artifact directory for a capture.
|
|
28
36
|
* - With `app`: <base>/<app>/<YYYYMMDD>/<run>[/<kind>]
|
|
29
37
|
* - Without: <base>[/<kindMap>] (back-compat with .sweetlink/term, etc.)
|
|
38
|
+
*
|
|
39
|
+
* `app` and `run` are slugified to prevent path traversal — anyone who can
|
|
40
|
+
* influence a sweetlink invocation (npm script, CI template, batch stdin)
|
|
41
|
+
* could otherwise pass `../../../tmp/x` and target arbitrary writable paths.
|
|
30
42
|
*/
|
|
31
43
|
export declare function runSlot(options: RunSlotOptions): string;
|
|
32
|
-
/** Reset
|
|
44
|
+
/** Reset internal caches — kept for back-compat with existing tests; now a no-op. */
|
|
33
45
|
export declare function _resetRunCache(): void;
|
|
34
46
|
//# sourceMappingURL=runs.d.ts.map
|
package/dist/runs.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runs.d.ts","sourceRoot":"","sources":["../src/runs.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAIH,MAAM,WAAW,cAAc;IAC7B,uEAAuE;IACvE,OAAO,EAAE,MAAM,CAAC;IAChB,8EAA8E;IAC9E,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,0EAA0E;IAC1E,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,
|
|
1
|
+
{"version":3,"file":"runs.d.ts","sourceRoot":"","sources":["../src/runs.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAIH,MAAM,WAAW,cAAc;IAC7B,uEAAuE;IACvE,OAAO,EAAE,MAAM,CAAC;IAChB,8EAA8E;IAC9E,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,0EAA0E;IAC1E,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,mEAAmE;IACnE,IAAI,CAAC,EAAE,MAAM,GAAG,KAAK,GAAG,SAAS,CAAC;CACnC;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAWtD;AAiBD;;;;;;;;GAQG;AACH,wBAAgB,OAAO,CAAC,OAAO,EAAE,cAAc,GAAG,MAAM,CAevD;AAED,qFAAqF;AACrF,wBAAgB,cAAc,IAAI,IAAI,CAErC"}
|
package/dist/runs.js
CHANGED
|
@@ -14,20 +14,31 @@
|
|
|
14
14
|
* working unchanged.
|
|
15
15
|
*/
|
|
16
16
|
import * as path from 'path';
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
17
|
+
/**
|
|
18
|
+
* Slugify a user-supplied namespace segment into something safe to use as a
|
|
19
|
+
* directory name. Without this, `--app ../../../tmp/x` would let a malicious
|
|
20
|
+
* caller (CI template, package.json script, batch JSON producer) write
|
|
21
|
+
* artifacts anywhere the user can write — including ~/.ssh/authorized_keys
|
|
22
|
+
* or LaunchAgents. Limited charset matches the existing --label slugifier.
|
|
23
|
+
*/
|
|
24
|
+
export function slugifyNamespace(value) {
|
|
25
|
+
const cleaned = value
|
|
26
|
+
.replace(/[^A-Za-z0-9_-]/g, '-')
|
|
27
|
+
.replace(/^-+|-+$/g, '')
|
|
28
|
+
.slice(0, 64);
|
|
29
|
+
if (!cleaned) {
|
|
30
|
+
throw new Error(`Invalid namespace "${value}" — must contain at least one alphanumeric, dash, or underscore character`);
|
|
24
31
|
}
|
|
32
|
+
return cleaned;
|
|
33
|
+
}
|
|
34
|
+
function defaultRunId() {
|
|
35
|
+
if (process.env.SWEETLINK_RUN)
|
|
36
|
+
return slugifyNamespace(process.env.SWEETLINK_RUN);
|
|
25
37
|
const d = new Date();
|
|
26
38
|
const pad = (n) => String(n).padStart(2, '0');
|
|
27
39
|
// HHMM-SS — short and human-readable. The date component lives in the
|
|
28
40
|
// parent directory, so duplicating it here would be noise.
|
|
29
|
-
|
|
30
|
-
return _cachedRun;
|
|
41
|
+
return `${pad(d.getHours())}${pad(d.getMinutes())}-${pad(d.getSeconds())}`;
|
|
31
42
|
}
|
|
32
43
|
function todayYmd() {
|
|
33
44
|
const d = new Date();
|
|
@@ -38,6 +49,10 @@ function todayYmd() {
|
|
|
38
49
|
* Resolve the artifact directory for a capture.
|
|
39
50
|
* - With `app`: <base>/<app>/<YYYYMMDD>/<run>[/<kind>]
|
|
40
51
|
* - Without: <base>[/<kindMap>] (back-compat with .sweetlink/term, etc.)
|
|
52
|
+
*
|
|
53
|
+
* `app` and `run` are slugified to prevent path traversal — anyone who can
|
|
54
|
+
* influence a sweetlink invocation (npm script, CI template, batch stdin)
|
|
55
|
+
* could otherwise pass `../../../tmp/x` and target arbitrary writable paths.
|
|
41
56
|
*/
|
|
42
57
|
export function runSlot(options) {
|
|
43
58
|
const baseDir = options.baseDir.endsWith('.sweetlink')
|
|
@@ -48,14 +63,15 @@ export function runSlot(options) {
|
|
|
48
63
|
return baseDir;
|
|
49
64
|
return path.join(baseDir, options.kind);
|
|
50
65
|
}
|
|
51
|
-
const
|
|
52
|
-
const
|
|
66
|
+
const safeApp = slugifyNamespace(options.app);
|
|
67
|
+
const safeRun = options.run ? slugifyNamespace(options.run) : defaultRunId();
|
|
68
|
+
const head = path.join(baseDir, safeApp, todayYmd(), safeRun);
|
|
53
69
|
if (!options.kind || options.kind === 'session')
|
|
54
70
|
return head;
|
|
55
71
|
return path.join(head, options.kind);
|
|
56
72
|
}
|
|
57
|
-
/** Reset
|
|
73
|
+
/** Reset internal caches — kept for back-compat with existing tests; now a no-op. */
|
|
58
74
|
export function _resetRunCache() {
|
|
59
|
-
|
|
75
|
+
/* no-op: runIds are no longer cached at module level */
|
|
60
76
|
}
|
|
61
77
|
//# sourceMappingURL=runs.js.map
|
package/dist/runs.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runs.js","sourceRoot":"","sources":["../src/runs.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAa7B,
|
|
1
|
+
{"version":3,"file":"runs.js","sourceRoot":"","sources":["../src/runs.ts"],"names":[],"mappings":"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"}
|
package/dist/server/index.d.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import { WebSocketServer } from 'ws';
|
|
7
7
|
import type { ConsoleLog, HmrScreenshotData, SweetlinkCommand, SweetlinkResponse } from '../types.js';
|
|
8
|
-
export type {
|
|
8
|
+
export type { ConsoleLog, HmrScreenshotData, SweetlinkCommand, SweetlinkResponse };
|
|
9
9
|
/**
|
|
10
10
|
* Get the project root directory (where the server was started)
|
|
11
11
|
* This is captured at server initialization time for consistency
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAMH,OAAO,EAAa,eAAe,EAAE,MAAM,IAAI,CAAC;AAGhD,OAAO,KAAK,EAGV,UAAU,EAEV,iBAAiB,EAMjB,gBAAgB,EAChB,iBAAiB,EAClB,MAAM,aAAa,CAAC;AAoErB,YAAY,EAAE,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAMH,OAAO,EAAa,eAAe,EAAE,MAAM,IAAI,CAAC;AAGhD,OAAO,KAAK,EAGV,UAAU,EAEV,iBAAiB,EAMjB,gBAAgB,EAChB,iBAAiB,EAClB,MAAM,aAAa,CAAC;AAoErB,YAAY,EAAE,UAAU,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,CAAC;AAqFnF;;;GAGG;AACH,wBAAgB,cAAc,IAAI,MAAM,CAEvC;AAED,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,qFAAqF;IACrF,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,kEAAkE;IAClE,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACjC,wGAAwG;IACxG,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,uEAAuE;IACvE,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,eAAe,CAAC,CAiGrF;AAED,4CAA4C;AAC5C,wBAAgB,gBAAgB,IAAI,MAAM,GAAG,IAAI,CAEhD;AAED,kCAAkC;AAClC,wBAAgB,oBAAoB,IAAI,MAAM,GAAG,IAAI,CAEpD;AAED,kCAAkC;AAClC,wBAAgB,YAAY,IAAI,MAAM,GAAG,SAAS,CAEjD;AAED,uBAAuB;AACvB,wBAAgB,UAAU,IAAI,MAAM,GAAG,SAAS,CAE/C;AAi0BD,wBAAgB,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC,CA2C9C"}
|
|
@@ -21,6 +21,19 @@ export interface AndroidRecordOptions {
|
|
|
21
21
|
shell?: string;
|
|
22
22
|
/** Recording cap in seconds. Android caps at 180; we default to that. */
|
|
23
23
|
timeLimit?: number;
|
|
24
|
+
/**
|
|
25
|
+
* When true (default), capture tap events via `adb shell getevent -l`
|
|
26
|
+
* and post-process the .mp4 with ffmpeg to overlay red rings at each
|
|
27
|
+
* tap. Falls back to writing only the sidecar JSON if ffmpeg is
|
|
28
|
+
* missing — the raw recording is still produced.
|
|
29
|
+
*/
|
|
30
|
+
overlays?: boolean;
|
|
31
|
+
/**
|
|
32
|
+
* When true, the spawned command sees the full process.env. Default
|
|
33
|
+
* false: only a minimal allowlist is forwarded to keep secrets out of
|
|
34
|
+
* the recorded artifact.
|
|
35
|
+
*/
|
|
36
|
+
inheritEnv?: boolean;
|
|
24
37
|
}
|
|
25
38
|
export interface AndroidRecordResult {
|
|
26
39
|
output: string;
|
|
@@ -28,6 +41,12 @@ export interface AndroidRecordResult {
|
|
|
28
41
|
exitCode: number;
|
|
29
42
|
durationSec: number;
|
|
30
43
|
recordingClosed: boolean;
|
|
44
|
+
/** Number of taps captured during the run. */
|
|
45
|
+
tapCount?: number;
|
|
46
|
+
/** Path to the .taps.json sidecar describing each captured tap. */
|
|
47
|
+
tapsJsonPath?: string;
|
|
48
|
+
/** True when ffmpeg was used to render tap rings into the .mp4. */
|
|
49
|
+
overlaysApplied?: boolean;
|
|
31
50
|
}
|
|
32
51
|
/** Returns the first online emulator/device serial, optionally filtered by a preference. */
|
|
33
52
|
export declare function findAndroidDevice(preference?: string): Promise<string | null>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"android.d.ts","sourceRoot":"","sources":["../../src/simulator/android.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAMH,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,4DAA4D;IAC5D,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,yEAAyE;IACzE,SAAS,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"android.d.ts","sourceRoot":"","sources":["../../src/simulator/android.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAMH,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,4DAA4D;IAC5D,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,yEAAyE;IACzE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;;OAIG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAwDD,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,OAAO,CAAC;IACzB,8CAA8C;IAC9C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,mEAAmE;IACnE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,mEAAmE;IACnE,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAmBD,4FAA4F;AAC5F,wBAAsB,iBAAiB,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAanF;AAED,wBAAsB,qBAAqB,CACzC,OAAO,EAAE,oBAAoB,GAC5B,OAAO,CAAC,mBAAmB,CAAC,CA6L9B"}
|
|
@@ -15,6 +15,57 @@
|
|
|
15
15
|
import { execFile, spawn } from 'child_process';
|
|
16
16
|
import { promises as fs } from 'fs';
|
|
17
17
|
import * as path from 'path';
|
|
18
|
+
const MINIMAL_ENV_KEYS = [
|
|
19
|
+
'PATH',
|
|
20
|
+
'HOME',
|
|
21
|
+
'USER',
|
|
22
|
+
'LANG',
|
|
23
|
+
'LC_ALL',
|
|
24
|
+
'LC_CTYPE',
|
|
25
|
+
'TERM',
|
|
26
|
+
'SHELL',
|
|
27
|
+
'TZ',
|
|
28
|
+
'TMPDIR',
|
|
29
|
+
'PWD',
|
|
30
|
+
];
|
|
31
|
+
function pickMinimalEnv() {
|
|
32
|
+
const out = {};
|
|
33
|
+
for (const key of MINIMAL_ENV_KEYS) {
|
|
34
|
+
const v = process.env[key];
|
|
35
|
+
if (typeof v === 'string')
|
|
36
|
+
out[key] = v;
|
|
37
|
+
}
|
|
38
|
+
return out;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Poll `adb shell stat -c %s <remotePath>` until the size is stable across
|
|
42
|
+
* two consecutive checks (or until we run out of attempts). Replaces a
|
|
43
|
+
* blind 500ms sleep — on slow physical devices, fuse-backed /sdcard, or
|
|
44
|
+
* USB-1 connections, the H.264 trailer can take several seconds to flush.
|
|
45
|
+
*/
|
|
46
|
+
async function waitForStableSize(device, remotePath) {
|
|
47
|
+
let last = -1;
|
|
48
|
+
for (let i = 0; i < 20; i++) {
|
|
49
|
+
try {
|
|
50
|
+
const out = await new Promise((resolve, reject) => {
|
|
51
|
+
execFile('adb', ['-s', device, 'shell', 'stat', '-c', '%s', remotePath], { encoding: 'utf-8' }, (err, stdout) => {
|
|
52
|
+
if (err)
|
|
53
|
+
reject(err);
|
|
54
|
+
else
|
|
55
|
+
resolve(stdout);
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
const size = parseInt(out.trim(), 10);
|
|
59
|
+
if (Number.isFinite(size) && size > 0 && size === last)
|
|
60
|
+
return;
|
|
61
|
+
last = size;
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
// stat occasionally races screenrecord's create; keep polling.
|
|
65
|
+
}
|
|
66
|
+
await new Promise((r) => setTimeout(r, 250));
|
|
67
|
+
}
|
|
68
|
+
}
|
|
18
69
|
function adb(args) {
|
|
19
70
|
return new Promise((resolve, reject) => {
|
|
20
71
|
execFile('adb', args, { encoding: 'utf-8' }, (err, stdout, stderr) => {
|
|
@@ -68,11 +119,38 @@ export async function recordAndroidEmulator(options) {
|
|
|
68
119
|
if (recProc.exitCode !== null) {
|
|
69
120
|
throw new Error(`screenrecord failed to start on ${device}: ${recStderr.trim() || 'unknown error'}`);
|
|
70
121
|
}
|
|
122
|
+
// Optionally start tap-event capture. Best-effort — if the touchscreen
|
|
123
|
+
// probe fails (e.g. unusual input device layout), we still produce the
|
|
124
|
+
// raw recording, just without overlays.
|
|
125
|
+
const wantOverlays = options.overlays !== false;
|
|
126
|
+
let tapCapture = null;
|
|
127
|
+
if (wantOverlays) {
|
|
128
|
+
try {
|
|
129
|
+
const { findTouchDevice, captureTapsLive } = await import('./androidTaps.js');
|
|
130
|
+
const info = await findTouchDevice(device);
|
|
131
|
+
// Note: startMs is captured *now* so tap timestamps line up with the
|
|
132
|
+
// recording's t=0. screenrecord starts ~800ms before this point but
|
|
133
|
+
// its earliest frame is the moment recording engaged on the device.
|
|
134
|
+
tapCapture = captureTapsLive(device, info, Date.now());
|
|
135
|
+
}
|
|
136
|
+
catch (err) {
|
|
137
|
+
console.error(`[Sweetlink] Could not start tap capture: ${err instanceof Error ? err.message : err}`);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
71
140
|
const startedAt = Date.now();
|
|
72
141
|
const cmdResult = await new Promise((resolve) => {
|
|
73
142
|
const child = spawn(options.shell ?? '/bin/sh', ['-c', options.command], {
|
|
74
143
|
cwd: options.cwd,
|
|
75
144
|
stdio: 'inherit',
|
|
145
|
+
env: options.inheritEnv
|
|
146
|
+
? process.env
|
|
147
|
+
: pickMinimalEnv(),
|
|
148
|
+
});
|
|
149
|
+
// Without an `error` handler, a missing shell or bad cwd would crash
|
|
150
|
+
// the parent process and orphan recProc + tapCapture.proc on the device.
|
|
151
|
+
child.on('error', (err) => {
|
|
152
|
+
console.error(`[Sweetlink] Failed to spawn user command: ${err.message}`);
|
|
153
|
+
resolve({ exitCode: 127 });
|
|
76
154
|
});
|
|
77
155
|
child.on('close', (code) => resolve({ exitCode: code ?? 0 }));
|
|
78
156
|
});
|
|
@@ -106,8 +184,11 @@ export async function recordAndroidEmulator(options) {
|
|
|
106
184
|
/* ignore */
|
|
107
185
|
}
|
|
108
186
|
});
|
|
109
|
-
// Pull the file off the device.
|
|
110
|
-
|
|
187
|
+
// Pull the file off the device. Poll for stat-size stability — a fixed
|
|
188
|
+
// sleep is unreliable on slow devices, USB-1, or fuse-backed /sdcard.
|
|
189
|
+
// Without this, `adb pull` happily copies a partial file and silently
|
|
190
|
+
// hands the user a truncated .mp4.
|
|
191
|
+
await waitForStableSize(device, remotePath);
|
|
111
192
|
await adb(['-s', device, 'pull', remotePath, options.output]);
|
|
112
193
|
// Best-effort cleanup on the device.
|
|
113
194
|
try {
|
|
@@ -116,11 +197,69 @@ export async function recordAndroidEmulator(options) {
|
|
|
116
197
|
catch {
|
|
117
198
|
/* ignore */
|
|
118
199
|
}
|
|
200
|
+
// Stop tap capture and write sidecar JSON + (optionally) ffmpeg overlay.
|
|
201
|
+
let tapCount;
|
|
202
|
+
let tapsJsonPath;
|
|
203
|
+
let overlaysApplied = false;
|
|
204
|
+
if (tapCapture) {
|
|
205
|
+
try {
|
|
206
|
+
tapCapture.proc.kill('SIGTERM');
|
|
207
|
+
}
|
|
208
|
+
catch {
|
|
209
|
+
/* ignore */
|
|
210
|
+
}
|
|
211
|
+
// Wait for the listener to actually exit + flush its trailing buffer
|
|
212
|
+
// (taps that landed in the last few hundred ms before SIGTERM). If it
|
|
213
|
+
// doesn't close in 2s, force-kill so we don't hang the CLI on a
|
|
214
|
+
// wedged adb shell.
|
|
215
|
+
await Promise.race([
|
|
216
|
+
tapCapture.done,
|
|
217
|
+
new Promise((resolve) => setTimeout(() => {
|
|
218
|
+
try {
|
|
219
|
+
tapCapture?.proc.kill('SIGKILL');
|
|
220
|
+
}
|
|
221
|
+
catch {
|
|
222
|
+
/* ignore */
|
|
223
|
+
}
|
|
224
|
+
resolve();
|
|
225
|
+
}, 2_000)),
|
|
226
|
+
]);
|
|
227
|
+
const taps = tapCapture.taps;
|
|
228
|
+
tapCount = taps.length;
|
|
229
|
+
// Sidecar JSON (always written when capture was attempted).
|
|
230
|
+
tapsJsonPath = `${options.output.replace(/\.mp4$/i, '')}.taps.json`;
|
|
231
|
+
await fs.writeFile(tapsJsonPath, JSON.stringify({ taps }, null, 2));
|
|
232
|
+
// Apply overlays via ffmpeg when available.
|
|
233
|
+
if (taps.length > 0) {
|
|
234
|
+
const { hasFfmpeg, applyTapOverlays } = await import('./overlay.js');
|
|
235
|
+
if (await hasFfmpeg()) {
|
|
236
|
+
try {
|
|
237
|
+
const overlayedPath = `${options.output.replace(/\.mp4$/i, '')}.overlayed.mp4`;
|
|
238
|
+
await applyTapOverlays({ inputPath: options.output, outputPath: overlayedPath, taps });
|
|
239
|
+
// Replace the original with the overlayed version.
|
|
240
|
+
await fs.unlink(options.output);
|
|
241
|
+
await fs.rename(overlayedPath, options.output);
|
|
242
|
+
overlaysApplied = true;
|
|
243
|
+
}
|
|
244
|
+
catch (err) {
|
|
245
|
+
console.error(`[Sweetlink] Tap overlay rendering failed: ${err instanceof Error ? err.message : err}` +
|
|
246
|
+
` (raw recording is preserved at ${options.output})`);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
else {
|
|
250
|
+
console.error(`[Sweetlink] Captured ${taps.length} taps but ffmpeg is not on PATH — overlay skipped. ` +
|
|
251
|
+
`Sidecar JSON written to ${tapsJsonPath}.`);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
119
255
|
return {
|
|
120
256
|
output: options.output,
|
|
121
257
|
device,
|
|
122
258
|
exitCode: cmdResult.exitCode,
|
|
123
259
|
durationSec,
|
|
260
|
+
tapCount,
|
|
261
|
+
tapsJsonPath,
|
|
262
|
+
overlaysApplied,
|
|
124
263
|
recordingClosed: closed,
|
|
125
264
|
};
|
|
126
265
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"android.js","sourceRoot":"","sources":["../../src/simulator/android.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAqB,QAAQ,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AACnE,OAAO,EAAE,QAAQ,IAAI,EAAE,EAAE,MAAM,IAAI,CAAC;AACpC,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"android.js","sourceRoot":"","sources":["../../src/simulator/android.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Android tap-event capture via `adb shell getevent -l`.
|
|
3
|
+
*
|
|
4
|
+
* We stream input events from the connected emulator/device while a screen
|
|
5
|
+
* recording is in progress, parse touch events into (x, y, t) tuples, and
|
|
6
|
+
* write them as a sidecar JSON. A separate post-process step (overlay.ts)
|
|
7
|
+
* renders rings on the recording at each tap timestamp.
|
|
8
|
+
*
|
|
9
|
+
* `getevent -p` is used once at startup to discover the touchscreen input
|
|
10
|
+
* device and its ABS coordinate range — coordinates from `getevent` are in
|
|
11
|
+
* raw input-device units, which we scale to screen pixels using the ratio
|
|
12
|
+
* of `wm size` vs. the input device's max X/Y.
|
|
13
|
+
*/
|
|
14
|
+
import { type ChildProcess } from 'child_process';
|
|
15
|
+
export interface TapEvent {
|
|
16
|
+
/** Pixel x relative to the captured frame. */
|
|
17
|
+
x: number;
|
|
18
|
+
/** Pixel y relative to the captured frame. */
|
|
19
|
+
y: number;
|
|
20
|
+
/** Seconds since recording start. */
|
|
21
|
+
t: number;
|
|
22
|
+
}
|
|
23
|
+
export interface TouchDeviceInfo {
|
|
24
|
+
/** /dev/input/eventN path. */
|
|
25
|
+
path: string;
|
|
26
|
+
/** Max raw value for ABS_MT_POSITION_X (0..max). */
|
|
27
|
+
maxX: number;
|
|
28
|
+
/** Max raw value for ABS_MT_POSITION_Y (0..max). */
|
|
29
|
+
maxY: number;
|
|
30
|
+
/** Display width in pixels (from `wm size`). */
|
|
31
|
+
screenWidth: number;
|
|
32
|
+
/** Display height in pixels. */
|
|
33
|
+
screenHeight: number;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Parse `adb shell wm size` output:
|
|
37
|
+
* Physical size: 1080x2400
|
|
38
|
+
* Override size: 720x1600 (optional)
|
|
39
|
+
*
|
|
40
|
+
* Override takes precedence when present (it's what apps actually render at).
|
|
41
|
+
*/
|
|
42
|
+
export declare function parseWmSize(out: string): {
|
|
43
|
+
width: number;
|
|
44
|
+
height: number;
|
|
45
|
+
} | null;
|
|
46
|
+
/**
|
|
47
|
+
* Parse `adb shell getevent -p` output and find the first device that
|
|
48
|
+
* declares BTN_TOUCH (014a) and ABS_MT_POSITION_X/Y. Returns the device
|
|
49
|
+
* path plus the raw coordinate ranges.
|
|
50
|
+
*
|
|
51
|
+
* Layout (one block per device):
|
|
52
|
+
*
|
|
53
|
+
* add device 3: /dev/input/event3
|
|
54
|
+
* name: "Touchscreen"
|
|
55
|
+
* events:
|
|
56
|
+
* KEY (0001): 014a
|
|
57
|
+
* ABS (0003): 0030 0031 0035 0036 ...
|
|
58
|
+
* input props:
|
|
59
|
+
* ...
|
|
60
|
+
* ABS_MT_POSITION_X : value 0, min 0, max 1080, fuzz 0, flat 0, resolution 0
|
|
61
|
+
* ABS_MT_POSITION_Y : value 0, min 0, max 2400, fuzz 0, flat 0, resolution 0
|
|
62
|
+
*/
|
|
63
|
+
export declare function parseGetEventProbe(out: string): Array<{
|
|
64
|
+
path: string;
|
|
65
|
+
maxX: number;
|
|
66
|
+
maxY: number;
|
|
67
|
+
hasBtnTouch: boolean;
|
|
68
|
+
}>;
|
|
69
|
+
/**
|
|
70
|
+
* Run the necessary adb queries to discover the touchscreen device + scale.
|
|
71
|
+
* Throws when no touchscreen device is found.
|
|
72
|
+
*/
|
|
73
|
+
export declare function findTouchDevice(deviceSerial: string): Promise<TouchDeviceInfo>;
|
|
74
|
+
/**
|
|
75
|
+
* Parse one chunk of `getevent -l` output, updating accumulated state in
|
|
76
|
+
* place. Pure function so it's straightforward to unit-test.
|
|
77
|
+
*
|
|
78
|
+
* Lines look like:
|
|
79
|
+
* /dev/input/event3: EV_ABS ABS_MT_POSITION_X 000003e8
|
|
80
|
+
* /dev/input/event3: EV_KEY BTN_TOUCH DOWN
|
|
81
|
+
* /dev/input/event3: EV_KEY BTN_TOUCH UP
|
|
82
|
+
*/
|
|
83
|
+
export interface ParseState {
|
|
84
|
+
curX: number | null;
|
|
85
|
+
curY: number | null;
|
|
86
|
+
downAtMs: number | null;
|
|
87
|
+
}
|
|
88
|
+
export declare function freshParseState(): ParseState;
|
|
89
|
+
export declare function processGetEventChunk(text: string, state: ParseState, scaleX: number, scaleY: number, startMs: number, nowMs: number, bounds?: {
|
|
90
|
+
maxX: number;
|
|
91
|
+
maxY: number;
|
|
92
|
+
screenWidth: number;
|
|
93
|
+
screenHeight: number;
|
|
94
|
+
}): TapEvent[];
|
|
95
|
+
/**
|
|
96
|
+
* Spawn a `getevent -l` listener for the given input path. Returns the
|
|
97
|
+
* child process plus a live array of tap events that the caller can read
|
|
98
|
+
* after they await `done` (or kill the process and await close).
|
|
99
|
+
*
|
|
100
|
+
* `done` resolves once the proc 'close' event fires AND the trailing
|
|
101
|
+
* buffered partial line has been processed — without it, taps in the final
|
|
102
|
+
* milliseconds before SIGTERM are silently dropped.
|
|
103
|
+
*/
|
|
104
|
+
export declare function captureTapsLive(deviceSerial: string, info: TouchDeviceInfo, startMs: number): {
|
|
105
|
+
proc: ChildProcess;
|
|
106
|
+
taps: TapEvent[];
|
|
107
|
+
done: Promise<void>;
|
|
108
|
+
};
|
|
109
|
+
//# sourceMappingURL=androidTaps.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"androidTaps.d.ts","sourceRoot":"","sources":["../../src/simulator/androidTaps.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,KAAK,YAAY,EAAmB,MAAM,eAAe,CAAC;AAEnE,MAAM,WAAW,QAAQ;IACvB,8CAA8C;IAC9C,CAAC,EAAE,MAAM,CAAC;IACV,8CAA8C;IAC9C,CAAC,EAAE,MAAM,CAAC;IACV,qCAAqC;IACrC,CAAC,EAAE,MAAM,CAAC;CACX;AAED,MAAM,WAAW,eAAe;IAC9B,8BAA8B;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,oDAAoD;IACpD,IAAI,EAAE,MAAM,CAAC;IACb,oDAAoD;IACpD,IAAI,EAAE,MAAM,CAAC;IACb,gDAAgD;IAChD,WAAW,EAAE,MAAM,CAAC;IACpB,gCAAgC;IAChC,YAAY,EAAE,MAAM,CAAC;CACtB;AAWD;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAMjF;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,kBAAkB,CAChC,GAAG,EAAE,MAAM,GACV,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,OAAO,CAAA;CAAE,CAAC,CAgB3E;AAED;;;GAGG;AACH,wBAAsB,eAAe,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC,CAqBpF;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AACD,wBAAgB,eAAe,IAAI,UAAU,CAE5C;AAgBD,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,UAAU,EACjB,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,EACb,MAAM,CAAC,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,GACjF,QAAQ,EAAE,CAkCZ;AAED;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAC7B,YAAY,EAAE,MAAM,EACpB,IAAI,EAAE,eAAe,EACrB,OAAO,EAAE,MAAM,GACd;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,IAAI,EAAE,QAAQ,EAAE,CAAC;IAAC,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,CAAA;CAAE,CAiD/D"}
|