@xenon-device-management/xenon 2.8.2 → 2.9.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.
Files changed (85) hide show
  1. package/lib/package.json +1 -1
  2. package/lib/public/assets/{AnnotationOverlay-BqBip3BK.js → AnnotationOverlay-DhRFPZ9-.js} +1 -1
  3. package/lib/public/assets/{ApiKeyGate-DUvekoqc.js → ApiKeyGate-BmZy1Srn.js} +1 -1
  4. package/lib/public/assets/{BugReportButton-BG0CrInp.js → BugReportButton-ByLSqwSk.js} +1 -1
  5. package/lib/public/assets/{DeviceMosaicView-B0SnOzKM.js → DeviceMosaicView-DVfXSoXH.js} +2 -2
  6. package/lib/public/assets/{EmptyState-Dq-Bq8TT.js → EmptyState-Bib6wal8.js} +1 -1
  7. package/lib/public/assets/{FieldGroup-BIgXTGgC.js → FieldGroup-D5sTaKGR.js} +1 -1
  8. package/lib/public/assets/{FilterMenu-CeJOFd6_.js → FilterMenu-D3XXKTwF.js} +1 -1
  9. package/lib/public/assets/{Menu-B4H6_Xk9.js → Menu-BiZhr0F7.js} +1 -1
  10. package/lib/public/assets/{Modal-DJG1w3KG.js → Modal-BxGNa3Zp.js} +1 -1
  11. package/lib/public/assets/{RecordingPage-CuzbuRlh.js → RecordingPage-Czml77Wb.js} +1 -1
  12. package/lib/public/assets/{RecordingsPage-CW2IMjO4.js → RecordingsPage-Di7nyHqE.js} +1 -1
  13. package/lib/public/assets/{SegmentedControl-DhQ3uYdc.js → SegmentedControl-XEKvCRwO.js} +1 -1
  14. package/lib/public/assets/{SettingCard-C5Gmj98i.js → SettingCard-YIpOQAjO.js} +1 -1
  15. package/lib/public/assets/{Table-D-WtrnfJ.js → Table-CqwX6v-1.js} +1 -1
  16. package/lib/public/assets/{activity-CvT65tfF.js → activity-Cur7Syd6.js} +1 -1
  17. package/lib/public/assets/{ai-settings-DuCpE_dA.js → ai-settings-B1OGdSKu.js} +1 -1
  18. package/lib/public/assets/{api-keys-DEIUB9Bz.js → api-keys-abTul2Oa.js} +1 -1
  19. package/lib/public/assets/{apps-B0z6NLY9.js → apps-yQL1MLfG.js} +1 -1
  20. package/lib/public/assets/{arrow-left-CCUPd12W.js → arrow-left-D0ipNN6o.js} +1 -1
  21. package/lib/public/assets/{arrow-right-CrLsc2tN.js → arrow-right-DwVz_Fly.js} +1 -1
  22. package/lib/public/assets/{arrow-up-right-CiQmHVQo.js → arrow-up-right-xKuKJfZf.js} +1 -1
  23. package/lib/public/assets/{auth-shell-CHgiSQ9B.js → auth-shell-00zHLFCk.js} +2 -2
  24. package/lib/public/assets/{builds-page-BvxRnAet.js → builds-page-D4RJAbOZ.js} +1 -1
  25. package/lib/public/assets/{button-pgE9Zb7y.js → button-Cic7S8s2.js} +1 -1
  26. package/lib/public/assets/{calendar-CNJBqo5s.js → calendar-DyNZ2bq9.js} +1 -1
  27. package/lib/public/assets/{check-BOAfxviA.js → check-CXGZdojb.js} +1 -1
  28. package/lib/public/assets/{chevron-left-DAYay1pY.js → chevron-left-BcNiIt1C.js} +1 -1
  29. package/lib/public/assets/{chevron-right-B0FDOdzU.js → chevron-right-BxDjkb1I.js} +1 -1
  30. package/lib/public/assets/{circle-check-DOIAC8XC.js → circle-check-9isFAWQ6.js} +1 -1
  31. package/lib/public/assets/{circle-x-z8FaSlcA.js → circle-x-CfnPCFLK.js} +1 -1
  32. package/lib/public/assets/{clock-BtaZSeEx.js → clock-Dc4TvnFF.js} +1 -1
  33. package/lib/public/assets/{copy-D60KmcdV.js → copy-TFQpalJ2.js} +1 -1
  34. package/lib/public/assets/{device-explorer-Cph43t8H.js → device-explorer-CB4bSJtl.js} +1 -1
  35. package/lib/public/assets/{download-s-xM4AGY.js → download-1_bq_Ikd.js} +1 -1
  36. package/lib/public/assets/{forgot-password-CKw6RVHP.js → forgot-password-B92TZ1GF.js} +1 -1
  37. package/lib/public/assets/{funnel-BvgEGRdQ.js → funnel-DC7DsjHe.js} +1 -1
  38. package/lib/public/assets/{index-BB9sO7Nt.js → index-BSsnx09Q.js} +1 -1
  39. package/lib/public/assets/{index-BpIf5nE2.js → index-CC2N_uMU.js} +2 -2
  40. package/lib/public/assets/{input-CPS4hChq.js → input-D2DBpxK2.js} +1 -1
  41. package/lib/public/assets/{keycodes-D5ZobFRW.js → keycodes-BQuIOMw0.js} +1 -1
  42. package/lib/public/assets/{layers-CrAsrOG4.js → layers-DxtoRlvi.js} +1 -1
  43. package/lib/public/assets/{list-checks-BnEjyKfv.js → list-checks-jQ7YUgdE.js} +1 -1
  44. package/lib/public/assets/{lock-Dm0hfbuM.js → lock-WHSsj2dj.js} +1 -1
  45. package/lib/public/assets/{login-DdlbwQwy.js → login-BB_xyUWw.js} +1 -1
  46. package/lib/public/assets/{maintenance-settings-kocnslUP.js → maintenance-settings-Dwadp8IQ.js} +1 -1
  47. package/lib/public/assets/{monitor-CGU-u9zq.js → monitor-DIWQjUlh.js} +1 -1
  48. package/lib/public/assets/{mouse-pointer-2-CWu4a7Ez.js → mouse-pointer-2-D11Re9Gj.js} +1 -1
  49. package/lib/public/assets/{network-TV7kV1jl.js → network-BYBgSBjv.js} +1 -1
  50. package/lib/public/assets/{overview-C3pWhCtz.js → overview-BISAwoyi.js} +1 -1
  51. package/lib/public/assets/{page-header-DCNc9cYT.js → page-header-ChX4YiMP.js} +1 -1
  52. package/lib/public/assets/{plus-C6Uuyu5Z.js → plus-BWNj1XSQ.js} +1 -1
  53. package/lib/public/assets/{profile-page-CjUENuCw.js → profile-page-VZD3gRjV.js} +1 -1
  54. package/lib/public/assets/{reset-password-rpbIm1Go.js → reset-password-Dz9LzFeX.js} +1 -1
  55. package/lib/public/assets/{runbook-page-BGkOmIkY.js → runbook-page-Bl2FodZf.js} +1 -1
  56. package/lib/public/assets/{select-6Q8XgEfq.js → select-Bs9CQu5Z.js} +1 -1
  57. package/lib/public/assets/{selector-detail-page-BPIr14Yp.js → selector-detail-page-By7ihhbY.js} +1 -1
  58. package/lib/public/assets/{selector-health-page-Vycvp0ED.js → selector-health-page-Y4CTbOLH.js} +1 -1
  59. package/lib/public/assets/{selector-health-DhZ490H5.js → selector-health-xiBb8vNz.js} +1 -1
  60. package/lib/public/assets/{session-detail-page-D2rvJIgL.js → session-detail-page-DqeKRP6c.js} +1 -1
  61. package/lib/public/assets/{settings-bF2S7k19.js → settings-DDhV_Dnz.js} +1 -1
  62. package/lib/public/assets/stat-tile-DMBTDbwt.js +8 -0
  63. package/lib/public/assets/{tablet-1Gmx6FV4.js → tablet-Cr5bOgng.js} +1 -1
  64. package/lib/public/assets/{teams-DHBNN_a7.js → teams-Bm0UQkR6.js} +1 -1
  65. package/lib/public/assets/{trash-2-D3idPlqv.js → trash-2-Di_lMeiP.js} +1 -1
  66. package/lib/public/assets/{upload-HOW_42FA.js → upload-RvXDBmCX.js} +1 -1
  67. package/lib/public/assets/{use-password-reset-mode-CVPBPGSb.js → use-password-reset-mode-C7pUQ7xe.js} +1 -1
  68. package/lib/public/assets/{users-DjGgZFe_.js → users-CIILdbvy.js} +1 -1
  69. package/lib/public/assets/{users-BnKiArxK.js → users-DZ7B5ePh.js} +1 -1
  70. package/lib/public/assets/{video-off-CuipXFJK.js → video-off-aYil8B-X.js} +1 -1
  71. package/lib/public/assets/{webhook-settings-7_s2Zky-.js → webhook-settings-17JYW1ZR.js} +1 -1
  72. package/lib/public/assets/{zap-Gl0D1sD4.js → zap-D9hchm-a.js} +1 -1
  73. package/lib/public/index.html +1 -1
  74. package/lib/src/app/routers/dashboard.js +6 -0
  75. package/lib/src/app/routers/sessionInsights.js +23 -1
  76. package/lib/src/device-managers/ios/IOSStreamService.js +12 -0
  77. package/lib/src/device-managers/ios/IOSTunnels.js +147 -23
  78. package/lib/src/device-managers/ios/WDAClient.js +18 -4
  79. package/lib/test/unit/ios-go-ios-tunnel-env.spec.js +38 -1
  80. package/lib/test/unit/ios-stream-appium-tunnel.spec.js +10 -0
  81. package/lib/test/unit/ios-stream-port-leases.spec.js +26 -0
  82. package/lib/test/unit/ios-tunnels.spec.js +90 -0
  83. package/lib/test/unit/session-list-insights.spec.js +57 -0
  84. package/package.json +1 -1
  85. package/lib/public/assets/stat-tile-DOrnbGTr.js +0 -8
@@ -111,6 +111,63 @@ describe('Sessions page data', () => {
111
111
  (0, chai_1.expect)(ids(res)).to.deep.equal(['a', 'c', 'b', 'd']);
112
112
  }));
113
113
  });
114
+ // The Sessions page re-reads, by id, the rows of its older pages that were
115
+ // still running when loaded. Those pages aren't polled, so a session that
116
+ // had finished kept showing "running" there.
117
+ describe('by id', () => {
118
+ const ids = (res) => res.body.map((r) => r.id);
119
+ beforeEach(() => __awaiter(void 0, void 0, void 0, function* () {
120
+ // A shared-pool phone, so a member may see these sessions too.
121
+ yield scratch.db.device.create({
122
+ data: {
123
+ udid: 'phone-1',
124
+ host: 'http://127.0.0.1:4723',
125
+ nodeId: 'hub-1',
126
+ platform: 'android',
127
+ },
128
+ });
129
+ yield session({ id: 'a', createdAt: ago(1 * MIN) });
130
+ yield session({ id: 'b', createdAt: ago(2 * MIN), status: 'running' });
131
+ yield session({ id: 'c', createdAt: ago(3 * MIN) });
132
+ }));
133
+ it('answers just the sessions asked for, newest first', () => __awaiter(void 0, void 0, void 0, function* () {
134
+ const res = yield (0, loopbackRequest_1.default)(app()).get('/session?ids=c,b');
135
+ (0, chai_1.expect)(res.status).to.equal(200);
136
+ (0, chai_1.expect)(ids(res)).to.deep.equal(['b', 'c']);
137
+ }));
138
+ it('leaves out an id it does not know', () => __awaiter(void 0, void 0, void 0, function* () {
139
+ (0, chai_1.expect)(ids(yield (0, loopbackRequest_1.default)(app()).get('/session?ids=b,nope'))).to.deep.equal(['b']);
140
+ }));
141
+ it('refuses an empty list, or more than 200 ids', () => __awaiter(void 0, void 0, void 0, function* () {
142
+ const many = Array.from({ length: 201 }, (_, i) => `x${i}`).join(',');
143
+ for (const q of ['ids=', 'ids=,', `ids=${many}`]) {
144
+ const res = yield (0, loopbackRequest_1.default)(app()).get(`/session?${q}`);
145
+ (0, chai_1.expect)(res.status, q.slice(0, 12)).to.equal(400);
146
+ (0, chai_1.expect)(res.body.error, q.slice(0, 12)).to.equal('invalid_ids');
147
+ }
148
+ }));
149
+ it('answers only the sessions the caller may see', () => __awaiter(void 0, void 0, void 0, function* () {
150
+ const team = yield scratch.db.team.create({ data: { name: 'by-id-team' } });
151
+ yield scratch.db.device.create({
152
+ data: {
153
+ udid: 'team-b',
154
+ host: 'http://127.0.0.1:4723',
155
+ nodeId: 'hub-1',
156
+ platform: 'android',
157
+ teamId: team.id,
158
+ },
159
+ });
160
+ yield session({ id: 'hidden', device_udid: 'team-b', status: 'running' });
161
+ caller = {
162
+ kind: 'user-session',
163
+ userId: 'member-1',
164
+ role: 'MEMBER',
165
+ scopes: 'sessions',
166
+ teamIds: [],
167
+ };
168
+ (0, chai_1.expect)(ids(yield (0, loopbackRequest_1.default)(app()).get('/session?ids=b,hidden'))).to.deep.equal(['b']);
169
+ }));
170
+ });
114
171
  it('keeps the sessions created since the given time, newest first', () => __awaiter(void 0, void 0, void 0, function* () {
115
172
  yield session({ id: 'old', createdAt: ago(3 * HOUR) });
116
173
  yield session({ id: 'recent', createdAt: ago(30 * MIN) });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xenon-device-management/xenon",
3
- "version": "2.8.2",
3
+ "version": "2.9.0",
4
4
  "description": "Xenon - Intelligent Mobile Infrastructure. A self-healing device orchestration platform for Appium.",
5
5
  "main": "./lib/src/index.js",
6
6
  "exports": {
@@ -1,8 +0,0 @@
1
- import{c as U,r as i,p as X,X as M,j as $}from"./index-BpIf5nE2.js";import{f as Y}from"./time-B8FIgwPE.js";/**
2
- * @license lucide-react v0.555.0 - ISC
3
- *
4
- * This source code is licensed under the ISC license.
5
- * See the LICENSE file in the root directory of this source tree.
6
- */const G=[["path",{d:"M10.1 2.182a10 10 0 0 1 3.8 0",key:"5ilxe3"}],["path",{d:"M13.9 21.818a10 10 0 0 1-3.8 0",key:"11zvb9"}],["path",{d:"M17.609 3.721a10 10 0 0 1 2.69 2.7",key:"1iw5b2"}],["path",{d:"M2.182 13.9a10 10 0 0 1 0-3.8",key:"c0bmvh"}],["path",{d:"M20.279 17.609a10 10 0 0 1-2.7 2.69",key:"1ruxm7"}],["path",{d:"M21.818 10.1a10 10 0 0 1 0 3.8",key:"qkgqxc"}],["path",{d:"M3.721 6.391a10 10 0 0 1 2.7-2.69",key:"1mcia2"}],["path",{d:"M6.391 20.279a10 10 0 0 1-2.69-2.7",key:"1fvljs"}]],ue=U("circle-dashed",G);function E(e){switch(e){case"success":case"passed":case"ended":return"passed";case"failed":case"error":case"timeout":return"failed";case"running":return"running";default:return"other"}}function ce(e){const t={all:e.length,passed:0,failed:0,running:0};for(const n of e){const r=E(n.status);r!=="other"&&(t[r]+=1)}return t}function V(e,t){return t==="all"?!0:E(e.status)===t}function le(e,t,n){const r=n.trim().toLowerCase();return e.filter(s=>{var c,f;return!(!V(s,t)||r&&![s.id,R(s).text,s.name,s.failure_reason,s.device_name,s.device_platform,s.device_version,s.node_id,s.ranOn,(c=s.owner)==null?void 0:c.name,(f=s.owner)==null?void 0:f.email].filter(Boolean).join(" ").toLowerCase().includes(r))})}function Z(e){var n;const t=(n=e.device_name)==null?void 0:n.trim();return t&&t.length>0?t:"Unknown Device"}function K(e){const t=e.device_platform??"";if(!t)return"—";const n=t.toLowerCase();return n==="ios"?"iOS":n==="tvos"?"tvOS":n.charAt(0).toUpperCase()+n.slice(1)}function fe(e){return Y(e)}function de(e){if(!e.startTime)return null;const t=Date.parse(e.startTime);if(!Number.isFinite(t))return null;const n=e.endTime?Date.parse(e.endTime):Date.now();return Number.isFinite(n)?Math.max(0,n-t):null}function me(e){if(e==null||!Number.isFinite(e)||e<0)return"—";const t=e/1e3,n=Math.floor(t/3600),r=Math.floor(t%3600/60),c=(t-n*3600-r*60).toFixed(1);return n>0?`${n}h ${r}m ${c}s`:r>0?`${r}m ${c}s`:`${c}s`}const O=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],D=e=>String(e).padStart(2,"0");function x(e,t){const n=e[`appium:${t}`]??e[t];return typeof n=="string"&&n.trim()?n.trim():null}function Q(e){if(!e)return null;let t;try{t=JSON.parse(e)}catch{return null}if(!t||typeof t!="object")return null;let n=t;if(n.capabilities&&typeof n.capabilities=="object"&&(n=n.capabilities),n.alwaysMatch||n.firstMatch){const r=Array.isArray(n.firstMatch)?n.firstMatch[0]:void 0;return{...n.alwaysMatch||{},...r||{}}}return n}function B(e){const t=Q(e);if(!t)return null;const n=x(t,"appPackage")??x(t,"bundleId");if(n)return n;const r=x(t,"app");if(r){const s=r.split(/[?#]/)[0].split(/[\\/]/).pop()??"";if(s&&s!=="download")return s}return x(t,"browserName")}function R(e){var r;const t=(r=e.name)==null?void 0:r.trim();if(t)return{text:t,source:"name"};const n=B(e.session_capabilities)??B(e.desired_capabilities);return n?{text:n,source:"app"}:{text:`Session ${e.id.slice(0,8)}`,source:"id"}}function k(e){return`${O[e.getMonth()]} ${e.getDate()}, ${D(e.getHours())}:${D(e.getMinutes())}`}function pe(e){const t=e?new Date(e):null;return t&&!Number.isNaN(t.getTime())?k(t):"—"}function W(e){var n;const t=(n=e.name)==null?void 0:n.trim();return!t||t==="Default Build"}function he(e){if(!W(e))return e.name.trim();const t=new Date(e.createdAt);return Number.isNaN(t.getTime())?"Build":`Build · ${k(t)}`}const ge={all:"All time","24h":"Last 24 hours","7d":"Last 7 days","30d":"Last 30 days"},ee={"24h":864e5,"7d":7*864e5,"30d":30*864e5};function te(e,t){return e==="all"?null:new Date(t-ee[e]).toISOString()}function Se(e){if(e==null||!Number.isFinite(e)||e<0)return"—";if(e<1e3)return"<1s";const t=Math.floor(e/1e3),n=Math.floor(t/3600),r=Math.floor(t%3600/60),s=t%60;return n>0?`${n}h ${r}m`:r>0?`${r}m ${s}s`:`${s}s`}function be(e,t=new Date){if(!e)return"—";const n=new Date(e);if(Number.isNaN(n.getTime()))return"—";const r=`${D(n.getHours())}:${D(n.getMinutes())}`;return n.getFullYear()!==t.getFullYear()?`${O[n.getMonth()]} ${n.getDate()}, ${n.getFullYear()} ${r}`:n.getMonth()===t.getMonth()&&n.getDate()===t.getDate()?r:k(n)}function C(e){const t=e.passed+e.failed;return t>0?e.passed/t*100:null}function ve(e,t){const n=C(e),r=t?C(t):null;return n===null||r===null?null:n-r}function ye(e){return e?e==="here"?"This server":e:null}function Me(e,t,n){var d,h;const r=n&&n.size>0?t.filter(o=>n.has(o.id)):t,s=r.filter(o=>E(o.status)==="failed").reverse();if(s.length===0)return{count:0,text:""};const c=n&&n.size>0?`${r.length} selected session${r.length===1?"":"s"}`:`${r.length} session${r.length===1?"":"s"}`,f=[`Failed tests in ${e}: ${s.length} of ${c}`,""];for(const o of s){const w=((h=(d=o.failure_reason)==null?void 0:d.split(`
7
- `)[0])==null?void 0:h.trim())||"No reason recorded",g=[K(o),o.device_version].filter(m=>m&&m!=="—").join(" ");f.push(`- ${R(o).text}`,` ${w}`,` ${[Z(o),g,`session ${o.id}`].filter(Boolean).join(" · ")}`)}return{count:s.length,text:f.join(`
8
- `)}}const ne=3e3,N=200,re=2e3,se=(e,t)=>e.createdAt===t.createdAt?e.id<t.id?1:-1:e.createdAt<t.createdAt?1:-1;function j(e,t){const n=new Map;for(const r of t)n.set(r.id,r);for(const r of e)n.set(r.id,r);return Array.from(n.values()).sort(se)}function $e(e={}){const{withSessions:t=!1,timeFilter:n="all"}=e,[r,s]=i.useState([]),[c,f]=i.useState([]),[d,h]=i.useState([]),o=i.useRef([]),[w,g]=i.useState(!1),[m,L]=i.useState(!1),[S,P]=i.useState(null),[H,q]=i.useState(!0),[J,A]=i.useState(null),l=i.useRef(!0),{on:b}=X(),v=i.useCallback(()=>S?{buildId:S}:{since:te(n,Date.now())??void 0},[S,n]);i.useEffect(()=>{o.current=[],h([]),g(!1)},[S,n]);const I=i.useCallback(async()=>{if(!t)return[];const a=o.current;if(a.length===0){const T=await M.getSessions({...v(),limit:N});return Array.isArray(T)&&g(T.length===N),T}const{since:u,..._}=v(),F=a[0].createdAt;return await M.getSessions({..._,since:u&&u>F?u:F,limit:re})},[t,v]),p=i.useCallback(async()=>{try{const[a,u]=await Promise.all([M.getBuilds(),I()]);if(!l.current)return;s(Array.isArray(a)?a:[]),f(Array.isArray(u)?u:[]),A(null)}catch(a){if(!l.current)return;A(a instanceof Error?a.message:String(a))}finally{l.current&&q(!1)}},[I]);i.useEffect(()=>{l.current=!0,p();const a=setInterval(p,ne);return()=>{l.current=!1,clearInterval(a)}},[p]),i.useEffect(()=>{const a=()=>p(),u=[b("session_started",a),b("session_stopped",a),b("device_added",a),b("device_removed",a)];return()=>{for(const _ of u)_()}},[b,p]);const y=i.useMemo(()=>j(c,d),[c,d]),z=i.useCallback(async()=>{const a=y[y.length-1];if(!(!t||m||!a)){L(!0);try{const u=await M.getSessions({...v(),before:a.createdAt,beforeId:a.id,limit:N});if(!l.current||!Array.isArray(u))return;o.current=j([],[...o.current,...u]),h(o.current),g(u.length===N)}catch(u){l.current&&A(u instanceof Error?u.message:String(u))}finally{l.current&&L(!1)}}},[y,t,m,v]);return{builds:r,selectedBuildId:S,sessions:y,loading:H,error:J,selectBuild:P,refresh:p,hasMore:w,loadingMore:m,showMore:z}}const ae={neutral:"text-[var(--text-muted)]",good:"text-[var(--color-success)]",bad:"text-[var(--color-danger)]"},xe=({label:e,value:t,valueCls:n="text-[var(--text)]",note:r,noteTone:s="neutral"})=>$.jsxs("div",{className:"min-w-0 rounded-lg border border-[var(--border)] bg-[var(--surface)] px-4 py-3",children:[$.jsx("div",{className:"text-xs text-[var(--text-muted)]",children:e}),$.jsx("div",{className:`mt-1 text-2xl font-semibold tabular-nums leading-tight truncate ${n}`,children:t}),$.jsx("div",{className:`mt-0.5 text-[11px] truncate ${ae[s]}`,children:r??" "})]});export{ue as C,ee as P,N as S,ge as T,ce as a,he as b,E as c,R as d,Z as e,pe as f,be as g,fe as h,W as i,Se as j,de as k,le as l,xe as m,C as n,ve as o,K as p,Me as q,ye as r,te as s,me as t,$e as u};