@ledgerhq/device-mockserver-react 0.0.0-develop-20261101001031 → 0.0.0-develop-20261104000847
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 +3 -1
- package/lib/cjs/package.json +3 -2
- package/lib/cjs/src/DeviceOsInfo.js +1 -1
- package/lib/cjs/src/DeviceOsInfo.js.map +3 -3
- package/lib/cjs/src/DeviceScreen.js +1 -1
- package/lib/cjs/src/DeviceScreen.js.map +3 -3
- package/lib/cjs/src/DeviceScreen.test.js.map +2 -2
- package/lib/cjs/src/DeviceScreenButtons.js +1 -1
- package/lib/cjs/src/DeviceScreenButtons.js.map +3 -3
- package/lib/cjs/src/DeviceScreenImage.js +1 -1
- package/lib/cjs/src/DeviceScreenImage.js.map +3 -3
- package/lib/cjs/src/DeviceScreenImage.test.js +1 -1
- package/lib/cjs/src/DeviceScreenImage.test.js.map +3 -3
- package/lib/cjs/src/MockServerDevice.js +1 -1
- package/lib/cjs/src/MockServerDevice.js.map +3 -3
- package/lib/cjs/src/deviceModels.js +1 -1
- package/lib/cjs/src/deviceModels.js.map +3 -3
- package/lib/cjs/src/index.js +1 -1
- package/lib/cjs/src/index.js.map +3 -3
- package/lib/cjs/src/webScreenPlatform.js +2 -0
- package/lib/cjs/src/webScreenPlatform.js.map +7 -0
- package/lib/esm/package.json +3 -2
- package/lib/esm/src/DeviceOsInfo.js +1 -1
- package/lib/esm/src/DeviceOsInfo.js.map +3 -3
- package/lib/esm/src/DeviceScreen.js +1 -1
- package/lib/esm/src/DeviceScreen.js.map +3 -3
- package/lib/esm/src/DeviceScreen.test.js.map +2 -2
- package/lib/esm/src/DeviceScreenButtons.js +1 -1
- package/lib/esm/src/DeviceScreenButtons.js.map +3 -3
- package/lib/esm/src/DeviceScreenImage.js +1 -1
- package/lib/esm/src/DeviceScreenImage.js.map +3 -3
- package/lib/esm/src/DeviceScreenImage.test.js +1 -1
- package/lib/esm/src/DeviceScreenImage.test.js.map +3 -3
- package/lib/esm/src/MockServerDevice.js +1 -1
- package/lib/esm/src/MockServerDevice.js.map +3 -3
- package/lib/esm/src/deviceModels.js +1 -1
- package/lib/esm/src/deviceModels.js.map +3 -3
- package/lib/esm/src/index.js +1 -1
- package/lib/esm/src/index.js.map +2 -2
- package/lib/esm/src/webScreenPlatform.js +2 -0
- package/lib/esm/src/webScreenPlatform.js.map +7 -0
- package/lib/types/src/DeviceOsInfo.d.ts.map +1 -1
- package/lib/types/src/DeviceScreen.d.ts +1 -1
- package/lib/types/src/DeviceScreen.d.ts.map +1 -1
- package/lib/types/src/DeviceScreenButtons.d.ts.map +1 -1
- package/lib/types/src/DeviceScreenImage.d.ts.map +1 -1
- package/lib/types/src/MockServerDevice.d.ts.map +1 -1
- package/lib/types/src/deviceModels.d.ts +2 -9
- package/lib/types/src/deviceModels.d.ts.map +1 -1
- package/lib/types/src/index.d.ts +1 -3
- package/lib/types/src/index.d.ts.map +1 -1
- package/lib/types/src/webScreenPlatform.d.ts +3 -0
- package/lib/types/src/webScreenPlatform.d.ts.map +1 -0
- package/lib/types/tsconfig.prod.tsbuildinfo +1 -1
- package/package.json +6 -5
- package/lib/cjs/src/deviceModels.test.js +0 -2
- package/lib/cjs/src/deviceModels.test.js.map +0 -7
- package/lib/cjs/src/mockServerScreenApi.js +0 -2
- package/lib/cjs/src/mockServerScreenApi.js.map +0 -7
- package/lib/cjs/src/mockServerScreenApi.test.js +0 -2
- package/lib/cjs/src/mockServerScreenApi.test.js.map +0 -7
- package/lib/cjs/src/types.js +0 -2
- package/lib/cjs/src/types.js.map +0 -7
- package/lib/cjs/src/useMockServerDevice.js +0 -2
- package/lib/cjs/src/useMockServerDevice.js.map +0 -7
- package/lib/cjs/src/useMockServerDevice.test.js +0 -2
- package/lib/cjs/src/useMockServerDevice.test.js.map +0 -7
- package/lib/cjs/src/useScreenPolling.js +0 -2
- package/lib/cjs/src/useScreenPolling.js.map +0 -7
- package/lib/esm/src/deviceModels.test.js +0 -2
- package/lib/esm/src/deviceModels.test.js.map +0 -7
- package/lib/esm/src/mockServerScreenApi.js +0 -2
- package/lib/esm/src/mockServerScreenApi.js.map +0 -7
- package/lib/esm/src/mockServerScreenApi.test.js +0 -2
- package/lib/esm/src/mockServerScreenApi.test.js.map +0 -7
- package/lib/esm/src/types.js +0 -1
- package/lib/esm/src/types.js.map +0 -7
- package/lib/esm/src/useMockServerDevice.js +0 -2
- package/lib/esm/src/useMockServerDevice.js.map +0 -7
- package/lib/esm/src/useMockServerDevice.test.js +0 -2
- package/lib/esm/src/useMockServerDevice.test.js.map +0 -7
- package/lib/esm/src/useScreenPolling.js +0 -2
- package/lib/esm/src/useScreenPolling.js.map +0 -7
- package/lib/types/src/deviceModels.test.d.ts +0 -2
- package/lib/types/src/deviceModels.test.d.ts.map +0 -1
- package/lib/types/src/mockServerScreenApi.d.ts +0 -3
- package/lib/types/src/mockServerScreenApi.d.ts.map +0 -1
- package/lib/types/src/mockServerScreenApi.test.d.ts +0 -2
- package/lib/types/src/mockServerScreenApi.test.d.ts.map +0 -1
- package/lib/types/src/types.d.ts +0 -46
- package/lib/types/src/types.d.ts.map +0 -1
- package/lib/types/src/useMockServerDevice.d.ts +0 -9
- package/lib/types/src/useMockServerDevice.d.ts.map +0 -1
- package/lib/types/src/useMockServerDevice.test.d.ts +0 -2
- package/lib/types/src/useMockServerDevice.test.d.ts.map +0 -1
- package/lib/types/src/useScreenPolling.d.ts +0 -3
- package/lib/types/src/useScreenPolling.d.ts.map +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"dependencies": {
|
|
3
|
-
"@ledgerhq/device-mockserver-
|
|
3
|
+
"@ledgerhq/device-mockserver-react-core": "0.0.0-develop-20261104000847",
|
|
4
|
+
"@ledgerhq/device-mockserver-client": "0.0.0-develop-20261104000847"
|
|
4
5
|
},
|
|
5
6
|
"devDependencies": {
|
|
6
7
|
"@ledgerhq/lumen-design-core": "0.1.34",
|
|
@@ -9,11 +10,11 @@
|
|
|
9
10
|
"@types/react-dom": "18.3.1",
|
|
10
11
|
"react": "18.3.1",
|
|
11
12
|
"react-dom": "18.3.1",
|
|
12
|
-
"@ledgerhq/device-management-kit": "0.0.0-develop-
|
|
13
|
+
"@ledgerhq/device-management-kit": "0.0.0-develop-20261104000847",
|
|
13
14
|
"@ledgerhq/eslint-config-dsdk": "0.0.2",
|
|
15
|
+
"@ledgerhq/ldmk-tool": "0.0.1",
|
|
14
16
|
"@ledgerhq/prettier-config-dsdk": "0.0.2",
|
|
15
17
|
"@ledgerhq/tsconfig-dsdk": "1.0.1",
|
|
16
|
-
"@ledgerhq/ldmk-tool": "0.0.1",
|
|
17
18
|
"@ledgerhq/vitest-config-dmk": "0.0.0"
|
|
18
19
|
},
|
|
19
20
|
"exports": {
|
|
@@ -40,7 +41,7 @@
|
|
|
40
41
|
"@ledgerhq/lumen-ui-react": "^0.1.66",
|
|
41
42
|
"react": "18.3.1",
|
|
42
43
|
"react-dom": "18.3.1",
|
|
43
|
-
"@ledgerhq/device-management-kit": "0.0.0-develop-
|
|
44
|
+
"@ledgerhq/device-management-kit": "0.0.0-develop-20261104000847"
|
|
44
45
|
},
|
|
45
46
|
"private": false,
|
|
46
47
|
"repository": {
|
|
@@ -48,7 +49,7 @@
|
|
|
48
49
|
"url": "https://github.com/LedgerHQ/device-sdk-ts.git"
|
|
49
50
|
},
|
|
50
51
|
"types": "lib/index.d.ts",
|
|
51
|
-
"version": "0.0.0-develop-
|
|
52
|
+
"version": "0.0.0-develop-20261104000847",
|
|
52
53
|
"scripts": {
|
|
53
54
|
"build": "pnpm ldmk-tool build --entryPoints index.ts,src/*.tsx,src/*.ts --tsconfig tsconfig.prod.json",
|
|
54
55
|
"dev": "concurrently \"pnpm watch:builds\" \"pnpm watch:types\"",
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
"use strict";var e=require("./deviceModels");describe("findDeviceScreenModel",()=>{it("finds a model by the mock server's device type",()=>{expect((0,e.findDeviceScreenModel)("stax")).toMatchObject({label:"Stax",touch:!0,speculos:!0})}),it("marks Nano S as having no emulator",()=>{expect((0,e.findDeviceScreenModel)("nanoS").speculos).toBe(!1)}),it("falls back to a button-driven model for an unknown type",()=>{expect((0,e.findDeviceScreenModel)("unknown")).toMatchObject({label:"Device",touch:!1})})});
|
|
2
|
-
//# sourceMappingURL=deviceModels.test.js.map
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../../src/deviceModels.test.ts"],
|
|
4
|
-
"sourcesContent": ["import { findDeviceScreenModel } from \"./deviceModels\";\n\ndescribe(\"findDeviceScreenModel\", () => {\n it(\"finds a model by the mock server's device type\", () => {\n expect(findDeviceScreenModel(\"stax\")).toMatchObject({\n label: \"Stax\",\n touch: true,\n speculos: true,\n });\n });\n\n it(\"marks Nano S as having no emulator\", () => {\n expect(findDeviceScreenModel(\"nanoS\").speculos).toBe(false);\n });\n\n it(\"falls back to a button-driven model for an unknown type\", () => {\n expect(findDeviceScreenModel(\"unknown\")).toMatchObject({\n label: \"Device\",\n touch: false,\n });\n });\n});\n"],
|
|
5
|
-
"mappings": "aAAA,IAAAA,EAAsC,0BAEtC,SAAS,wBAAyB,IAAM,CACtC,GAAG,iDAAkD,IAAM,CACzD,UAAO,yBAAsB,MAAM,CAAC,EAAE,cAAc,CAClD,MAAO,OACP,MAAO,GACP,SAAU,EACZ,CAAC,CACH,CAAC,EAED,GAAG,qCAAsC,IAAM,CAC7C,UAAO,yBAAsB,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAK,CAC5D,CAAC,EAED,GAAG,0DAA2D,IAAM,CAClE,UAAO,yBAAsB,SAAS,CAAC,EAAE,cAAc,CACrD,MAAO,SACP,MAAO,EACT,CAAC,CACH,CAAC,CACH,CAAC",
|
|
6
|
-
"names": ["import_deviceModels"]
|
|
7
|
-
}
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
"use strict";var s=Object.defineProperty;var a=Object.getOwnPropertyDescriptor;var u=Object.getOwnPropertyNames;var p=Object.prototype.hasOwnProperty;var i=(n,o)=>{for(var e in o)s(n,e,{get:o[e],enumerable:!0})},w=(n,o,e,t)=>{if(o&&typeof o=="object"||typeof o=="function")for(let r of u(o))!p.call(n,r)&&r!==e&&s(n,r,{get:()=>o[r],enumerable:!(t=a(o,r))||t.enumerable});return n};var h=n=>w(s({},"__esModule",{value:!0}),n);var S={};i(S,{mockServerScreenApi:()=>f});module.exports=h(S);const l=n=>n?.status===409,f=(n,o)=>({screenshot:async()=>{try{return await n.getScreenshot(o)}catch(e){if(l(e))return null;throw e}},idle:async()=>({kind:"os-info",device:await n.getDevice(o)}),pressButton:(e,t)=>n.pressButton(o,e,t),touch:(e,t,r)=>n.touchScreen(o,e,t,r),openApp:async e=>{const t=new TextEncoder().encode(e);if(t.length>255)throw new Error(`App name too long to open: ${e}`);const{response:r}=await n.sendApdu(o,Uint8Array.from([224,216,0,0,t.length,...t])),c=r.slice(-4);if(c!=="9000")throw new Error(`Could not open ${e} (${c})`)}});0&&(module.exports={mockServerScreenApi});
|
|
2
|
-
//# sourceMappingURL=mockServerScreenApi.js.map
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../../src/mockServerScreenApi.ts"],
|
|
4
|
-
"sourcesContent": ["import { type MockServerClient, type ScreenApi } from \"./types\";\n\n/** The proxy answers 409 when the device has no Speculos instance. */\nconst isNoInstance = (error: unknown): boolean =>\n (error as { status?: unknown } | null)?.status === 409;\n\nexport const mockServerScreenApi = (\n client: MockServerClient,\n deviceId: string,\n): ScreenApi => ({\n screenshot: async () => {\n try {\n return await client.getScreenshot(deviceId);\n } catch (error) {\n if (isNoInstance(error)) return null;\n throw error;\n }\n },\n idle: async () => ({\n kind: \"os-info\",\n device: await client.getDevice(deviceId),\n }),\n pressButton: (button, action) => client.pressButton(deviceId, button, action),\n touch: (x, y, action) => client.touchScreen(deviceId, x, y, action),\n openApp: async (appName) => {\n const name = new TextEncoder().encode(appName);\n // Lc is a single byte.\n if (name.length > 0xff) {\n throw new Error(`App name too long to open: ${appName}`);\n }\n const { response } = await client.sendApdu(\n deviceId,\n Uint8Array.from([0xe0, 0xd8, 0x00, 0x00, name.length, ...name]),\n );\n const statusWord = response.slice(-4);\n if (statusWord !== \"9000\") {\n throw new Error(`Could not open ${appName} (${statusWord})`);\n }\n },\n});\n"],
|
|
5
|
-
"mappings": "yaAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,yBAAAE,IAAA,eAAAC,EAAAH,GAGA,MAAMI,EAAgBC,GACnBA,GAAuC,SAAW,IAExCH,EAAsB,CACjCI,EACAC,KACe,CACf,WAAY,SAAY,CACtB,GAAI,CACF,OAAO,MAAMD,EAAO,cAAcC,CAAQ,CAC5C,OAASF,EAAO,CACd,GAAID,EAAaC,CAAK,EAAG,OAAO,KAChC,MAAMA,CACR,CACF,EACA,KAAM,UAAa,CACjB,KAAM,UACN,OAAQ,MAAMC,EAAO,UAAUC,CAAQ,CACzC,GACA,YAAa,CAACC,EAAQC,IAAWH,EAAO,YAAYC,EAAUC,EAAQC,CAAM,EAC5E,MAAO,CAACC,EAAGC,EAAGF,IAAWH,EAAO,YAAYC,EAAUG,EAAGC,EAAGF,CAAM,EAClE,QAAS,MAAOG,GAAY,CAC1B,MAAMC,EAAO,IAAI,YAAY,EAAE,OAAOD,CAAO,EAE7C,GAAIC,EAAK,OAAS,IAChB,MAAM,IAAI,MAAM,8BAA8BD,CAAO,EAAE,EAEzD,KAAM,CAAE,SAAAE,CAAS,EAAI,MAAMR,EAAO,SAChCC,EACA,WAAW,KAAK,CAAC,IAAM,IAAM,EAAM,EAAMM,EAAK,OAAQ,GAAGA,CAAI,CAAC,CAChE,EACME,EAAaD,EAAS,MAAM,EAAE,EACpC,GAAIC,IAAe,OACjB,MAAM,IAAI,MAAM,kBAAkBH,CAAO,KAAKG,CAAU,GAAG,CAE/D,CACF",
|
|
6
|
-
"names": ["mockServerScreenApi_exports", "__export", "mockServerScreenApi", "__toCommonJS", "isNoInstance", "error", "client", "deviceId", "button", "action", "x", "y", "appName", "name", "response", "statusWord"]
|
|
7
|
-
}
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
"use strict";var o=require("./mockServerScreenApi");const n=e=>({getScreenshot:vi.fn(),getDevice:vi.fn(),pressButton:vi.fn(()=>Promise.resolve()),touchScreen:vi.fn(()=>Promise.resolve()),...e});describe("mockServerScreenApi",()=>{it("resolves the screenshot of the device",async()=>{const e=new Blob(["png"]),t=n({getScreenshot:vi.fn(()=>Promise.resolve(e))});await expect((0,o.mockServerScreenApi)(t,"device-1").screenshot()).resolves.toBe(e),expect(t.getScreenshot).toHaveBeenCalledWith("device-1")}),it("resolves no screenshot when the device has no Speculos instance",async()=>{const e=n({getScreenshot:vi.fn(()=>Promise.reject({status:409}))});await expect((0,o.mockServerScreenApi)(e,"device-1").screenshot()).resolves.toBeNull()}),it("rethrows any other screenshot failure",async()=>{const e=Object.assign(new Error("boom"),{status:500}),t=n({getScreenshot:vi.fn(()=>Promise.reject(e))});await expect((0,o.mockServerScreenApi)(t,"device-1").screenshot()).rejects.toBe(e)}),it("shows the device record while idle",async()=>{const e={id:"device-1",device_type:"nanoX"},t=n({getDevice:vi.fn(()=>Promise.resolve(e))});await expect((0,o.mockServerScreenApi)(t,"device-1").idle?.()).resolves.toEqual({kind:"os-info",device:e})}),it("forwards buttons and touches to the device",async()=>{const e=n({}),t=(0,o.mockServerScreenApi)(e,"device-1");await t.pressButton("left","press"),await t.touch(10,20,"release"),expect(e.pressButton).toHaveBeenCalledWith("device-1","left","press"),expect(e.touchScreen).toHaveBeenCalledWith("device-1",10,20,"release")}),it("opens an app by name",async()=>{const e=vi.fn(()=>Promise.resolve({response:"9000"}));await(0,o.mockServerScreenApi)(n({sendApdu:e}),"device-1").openApp?.("Bitcoin"),expect(e).toHaveBeenCalledWith("device-1",Uint8Array.from([224,216,0,0,7,...new TextEncoder().encode("Bitcoin")]))}),it("fails to open an app the device refuses",async()=>{const e=vi.fn(()=>Promise.resolve({response:"6807"})),t=(0,o.mockServerScreenApi)(n({sendApdu:e}),"device-1");await expect(t.openApp?.("Bitcoin")).rejects.toThrow("6807")}),it("refuses an app name too long for one APDU",async()=>{const e=vi.fn(()=>Promise.resolve({response:"9000"})),t=(0,o.mockServerScreenApi)(n({sendApdu:e}),"device-1");await expect(t.openApp?.("a".repeat(256))).rejects.toThrow("too long"),expect(e).not.toHaveBeenCalled()})});
|
|
2
|
-
//# sourceMappingURL=mockServerScreenApi.test.js.map
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../../src/mockServerScreenApi.test.ts"],
|
|
4
|
-
"sourcesContent": ["import { mockServerScreenApi } from \"./mockServerScreenApi\";\nimport { type MockServerClient } from \"./types\";\n\nconst aClient = (overrides: Partial<Record<keyof MockServerClient, unknown>>) =>\n ({\n getScreenshot: vi.fn(),\n getDevice: vi.fn(),\n pressButton: vi.fn(() => Promise.resolve()),\n touchScreen: vi.fn(() => Promise.resolve()),\n ...overrides,\n }) as unknown as MockServerClient;\n\ndescribe(\"mockServerScreenApi\", () => {\n it(\"resolves the screenshot of the device\", async () => {\n const blob = new Blob([\"png\"]);\n const client = aClient({\n getScreenshot: vi.fn(() => Promise.resolve(blob)),\n });\n\n await expect(\n mockServerScreenApi(client, \"device-1\").screenshot(),\n ).resolves.toBe(blob);\n expect(client.getScreenshot).toHaveBeenCalledWith(\"device-1\");\n });\n\n it(\"resolves no screenshot when the device has no Speculos instance\", async () => {\n const client = aClient({\n getScreenshot: vi.fn(() => Promise.reject({ status: 409 })),\n });\n\n await expect(\n mockServerScreenApi(client, \"device-1\").screenshot(),\n ).resolves.toBeNull();\n });\n\n it(\"rethrows any other screenshot failure\", async () => {\n const error = Object.assign(new Error(\"boom\"), { status: 500 });\n const client = aClient({\n getScreenshot: vi.fn(() => Promise.reject(error)),\n });\n\n await expect(\n mockServerScreenApi(client, \"device-1\").screenshot(),\n ).rejects.toBe(error);\n });\n\n it(\"shows the device record while idle\", async () => {\n const device = { id: \"device-1\", device_type: \"nanoX\" };\n const client = aClient({ getDevice: vi.fn(() => Promise.resolve(device)) });\n\n await expect(\n mockServerScreenApi(client, \"device-1\").idle?.(),\n ).resolves.toEqual({\n kind: \"os-info\",\n device,\n });\n });\n\n it(\"forwards buttons and touches to the device\", async () => {\n const client = aClient({});\n const api = mockServerScreenApi(client, \"device-1\");\n\n await api.pressButton(\"left\", \"press\");\n await api.touch(10, 20, \"release\");\n\n expect(client.pressButton).toHaveBeenCalledWith(\n \"device-1\",\n \"left\",\n \"press\",\n );\n expect(client.touchScreen).toHaveBeenCalledWith(\n \"device-1\",\n 10,\n 20,\n \"release\",\n );\n });\n\n it(\"opens an app by name\", async () => {\n const sendApdu = vi.fn(() => Promise.resolve({ response: \"9000\" }));\n const api = mockServerScreenApi(aClient({ sendApdu }), \"device-1\");\n\n await api.openApp?.(\"Bitcoin\");\n\n expect(sendApdu).toHaveBeenCalledWith(\n \"device-1\",\n Uint8Array.from([\n 0xe0,\n 0xd8,\n 0x00,\n 0x00,\n 0x07,\n ...new TextEncoder().encode(\"Bitcoin\"),\n ]),\n );\n });\n\n it(\"fails to open an app the device refuses\", async () => {\n const sendApdu = vi.fn(() => Promise.resolve({ response: \"6807\" }));\n const api = mockServerScreenApi(aClient({ sendApdu }), \"device-1\");\n\n await expect(api.openApp?.(\"Bitcoin\")).rejects.toThrow(\"6807\");\n });\n\n it(\"refuses an app name too long for one APDU\", async () => {\n const sendApdu = vi.fn(() => Promise.resolve({ response: \"9000\" }));\n const api = mockServerScreenApi(aClient({ sendApdu }), \"device-1\");\n\n await expect(api.openApp?.(\"a\".repeat(256))).rejects.toThrow(\"too long\");\n expect(sendApdu).not.toHaveBeenCalled();\n });\n});\n"],
|
|
5
|
-
"mappings": "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",
|
|
6
|
-
"names": ["import_mockServerScreenApi", "aClient", "overrides", "blob", "client", "error", "device", "api", "sendApdu"]
|
|
7
|
-
}
|
package/lib/cjs/src/types.js
DELETED
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
"use strict";var i=Object.defineProperty;var r=Object.getOwnPropertyDescriptor;var p=Object.getOwnPropertyNames;var s=Object.prototype.hasOwnProperty;var u=(t,e,c,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let o of p(e))!s.call(t,o)&&o!==c&&i(t,o,{get:()=>e[o],enumerable:!(n=r(e,o))||n.enumerable});return t};var S=t=>u(i({},"__esModule",{value:!0}),t);var l={};module.exports=S(l);
|
|
2
|
-
//# sourceMappingURL=types.js.map
|
package/lib/cjs/src/types.js.map
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../../src/types.ts"],
|
|
4
|
-
"sourcesContent": ["import {\n type Device,\n type MockClient,\n type SpeculosAction,\n type SpeculosButton,\n} from \"@ledgerhq/device-mockserver-client\";\n\n/**\n * The MockClient calls this library makes. Picking them drops the class's\n * private fields, so a client from another copy of the package still fits.\n */\nexport type MockServerClient = Pick<\n MockClient,\n \"getDevice\" | \"getScreenshot\" | \"pressButton\" | \"touchScreen\" | \"sendApdu\"\n>;\n\n/**\n * How the screen is reached. The mock server proxies to the Speculos instance\n * backing a device and a Speculos transport talks to one directly, but both\n * answer the same calls.\n */\nexport interface ScreenApi {\n /** Resolves null when there is no screen to capture right now. */\n screenshot(): Promise<Blob | null>;\n /** What to show while `screenshot` keeps resolving null. */\n idle?(): Promise<DeviceScreenState>;\n pressButton(button: SpeculosButton, action: SpeculosAction): Promise<void>;\n /** Coordinates in device screen pixels. */\n touch(x: number, y: number, action: SpeculosAction): Promise<void>;\n /** Omitted by apis whose device always runs an app. */\n openApp?(appName: string): Promise<void>;\n}\n\n/**\n * Press and release are separate so a hold reaches the device as a hold: Stax\n * and Flex gate their confirmations behind one.\n */\nexport interface DeviceScreenInput {\n pressButton(button: SpeculosButton, action: SpeculosAction): void;\n touch(x: number, y: number, action: SpeculosAction): void;\n}\n\nexport type DeviceScreenState =\n | { kind: \"loading\" }\n /** No app runs, so no Speculos instance: the device record stands in. */\n | { kind: \"os-info\"; device: Device }\n | { kind: \"image\"; src: string; input: DeviceScreenInput }\n | { kind: \"error\"; message: string };\n"],
|
|
5
|
-
"mappings": "+WAAA,IAAAA,EAAA,kBAAAC,EAAAD",
|
|
6
|
-
"names": ["types_exports", "__toCommonJS"]
|
|
7
|
-
}
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
"use strict";var u=Object.defineProperty;var s=Object.getOwnPropertyDescriptor;var m=Object.getOwnPropertyNames;var p=Object.prototype.hasOwnProperty;var v=(t,e)=>{for(var n in e)u(t,n,{get:e[n],enumerable:!0})},D=(t,e,n,i)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of m(e))!p.call(t,r)&&r!==n&&u(t,r,{get:()=>e[r],enumerable:!(i=s(e,r))||i.enumerable});return t};var a=t=>D(u({},"__esModule",{value:!0}),t);var S={};v(S,{useMockServerDevice:()=>y});module.exports=a(S);var o=require("react");const d=2e3;function y(t,e){const[n,i]=(0,o.useState)();return(0,o.useEffect)(()=>{let r=!1,c;if(i(void 0),!e)return;const f=()=>{t.getDevice(e).then(l=>{r||i(l)},()=>{r||(i(null),c=setTimeout(f,d))})};return f(),()=>{r=!0,clearTimeout(c)}},[t,e]),n}0&&(module.exports={useMockServerDevice});
|
|
2
|
-
//# sourceMappingURL=useMockServerDevice.js.map
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../../src/useMockServerDevice.ts"],
|
|
4
|
-
"sourcesContent": ["import { useEffect, useState } from \"react\";\nimport { type Device } from \"@ledgerhq/device-mockserver-client\";\n\nimport { type MockServerClient } from \"./types\";\n\nconst RETRY_MS = 2000;\n\n/**\n * The mock server's record of a device: undefined while it loads, or while no\n * device id is given, and null while it cannot be read. A failed read is\n * retried, so a transient error does not leave the device without its model.\n */\nexport function useMockServerDevice(\n client: MockServerClient,\n deviceId: string,\n): Device | null | undefined {\n const [device, setDevice] = useState<Device | null>();\n\n useEffect(() => {\n let cancelled = false;\n let retry: ReturnType<typeof setTimeout> | undefined;\n setDevice(undefined);\n if (!deviceId) return;\n\n const load = () => {\n client.getDevice(deviceId).then(\n (record) => {\n if (!cancelled) setDevice(record);\n },\n () => {\n if (cancelled) return;\n setDevice(null);\n retry = setTimeout(load, RETRY_MS);\n },\n );\n };\n load();\n\n return () => {\n cancelled = true;\n clearTimeout(retry);\n };\n }, [client, deviceId]);\n\n return device;\n}\n"],
|
|
5
|
-
"mappings": "yaAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,yBAAAE,IAAA,eAAAC,EAAAH,GAAA,IAAAI,EAAoC,iBAKpC,MAAMC,EAAW,IAOV,SAASH,EACdI,EACAC,EAC2B,CAC3B,KAAM,CAACC,EAAQC,CAAS,KAAI,YAAwB,EAEpD,sBAAU,IAAM,CACd,IAAIC,EAAY,GACZC,EAEJ,GADAF,EAAU,MAAS,EACf,CAACF,EAAU,OAEf,MAAMK,EAAO,IAAM,CACjBN,EAAO,UAAUC,CAAQ,EAAE,KACxBM,GAAW,CACLH,GAAWD,EAAUI,CAAM,CAClC,EACA,IAAM,CACAH,IACJD,EAAU,IAAI,EACdE,EAAQ,WAAWC,EAAMP,CAAQ,EACnC,CACF,CACF,EACA,OAAAO,EAAK,EAEE,IAAM,CACXF,EAAY,GACZ,aAAaC,CAAK,CACpB,CACF,EAAG,CAACL,EAAQC,CAAQ,CAAC,EAEdC,CACT",
|
|
6
|
-
"names": ["useMockServerDevice_exports", "__export", "useMockServerDevice", "__toCommonJS", "import_react", "RETRY_MS", "client", "deviceId", "device", "setDevice", "cancelled", "retry", "load", "record"]
|
|
7
|
-
}
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
"use strict";var u=Object.create;var d=Object.defineProperty;var f=Object.getOwnPropertyDescriptor;var C=Object.getOwnPropertyNames;var y=Object.getPrototypeOf,h=Object.prototype.hasOwnProperty;var k=(i,n,t,l)=>{if(n&&typeof n=="object"||typeof n=="function")for(let r of C(n))!h.call(i,r)&&r!==t&&d(i,r,{get:()=>n[r],enumerable:!(l=f(n,r))||l.enumerable});return i};var S=(i,n,t)=>(t=i!=null?u(y(i)):{},k(n||!i||!i.__esModule?d(t,"default",{value:i,enumerable:!0}):t,i));var c=S(require("react")),m=require("react-dom/client"),p=require("./useMockServerDevice");Object.assign(globalThis,{IS_REACT_ACT_ENVIRONMENT:!0});const s={id:"device-1",device_type:"stax"};describe("useMockServerDevice",()=>{let i,n,t;const l=({client:e,deviceId:o})=>(t=(0,p.useMockServerDevice)(e,o),null),r=e=>({getDevice:vi.fn(e)}),a=(e,o="device-1")=>(0,c.act)(()=>n.render(c.default.createElement(l,{client:e,deviceId:o}))),v=()=>(0,c.act)(()=>vi.advanceTimersByTimeAsync(0));beforeEach(()=>{vi.useFakeTimers(),t=void 0,i=document.createElement("div"),n=(0,m.createRoot)(i)}),afterEach(()=>{(0,c.act)(()=>n.unmount()),vi.useRealTimers()}),it("reads the device record",async()=>{const e=r(()=>Promise.resolve(s));a(e),expect(t).toBeUndefined(),await v(),expect(t).toBe(s),expect(e.getDevice).toHaveBeenCalledWith("device-1")}),it("accepts a client from another copy of the package",async()=>{class e{record=s;getDevice=vi.fn(()=>Promise.resolve(this.record));getScreenshot=vi.fn();pressButton=vi.fn();touchScreen=vi.fn();sendApdu=vi.fn()}const o=new e;a(o),await v(),expect(t).toBe(s)}),it("reads nothing without a device id",async()=>{const e=r(()=>Promise.resolve(s));a(e,""),await v(),expect(t).toBeUndefined(),expect(e.getDevice).not.toHaveBeenCalled()}),it("retries a failed read until it succeeds",async()=>{const e=vi.fn().mockRejectedValueOnce(new Error("offline")).mockResolvedValue(s),o=r(e);a(o),await v(),expect(t).toBeNull(),await(0,c.act)(()=>vi.advanceTimersByTimeAsync(2e3)),expect(t).toBe(s),expect(e).toHaveBeenCalledTimes(2)}),it("stops retrying once unmounted",async()=>{const e=r(()=>Promise.reject(new Error("offline")));a(e),await v(),(0,c.act)(()=>n.render(c.default.createElement(c.default.Fragment,null))),await(0,c.act)(()=>vi.advanceTimersByTimeAsync(1e4)),expect(e.getDevice).toHaveBeenCalledOnce()})});
|
|
2
|
-
//# sourceMappingURL=useMockServerDevice.test.js.map
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../../src/useMockServerDevice.test.tsx"],
|
|
4
|
-
"sourcesContent": ["import React, { act } from \"react\";\nimport { createRoot, type Root } from \"react-dom/client\";\nimport { type Device } from \"@ledgerhq/device-mockserver-client\";\n\nimport { type MockServerClient } from \"./types\";\nimport { useMockServerDevice } from \"./useMockServerDevice\";\n\nObject.assign(globalThis, { IS_REACT_ACT_ENVIRONMENT: true });\n\nconst DEVICE = { id: \"device-1\", device_type: \"stax\" } as Device;\n\ndescribe(\"useMockServerDevice\", () => {\n let container: HTMLDivElement;\n let root: Root;\n let result: Device | null | undefined;\n\n const Probe: React.FC<{ client: MockServerClient; deviceId: string }> = ({\n client,\n deviceId,\n }) => {\n result = useMockServerDevice(client, deviceId);\n return null;\n };\n const aClient = (getDevice: () => Promise<Device>) =>\n ({ getDevice: vi.fn(getDevice) }) as unknown as MockServerClient;\n const render = (client: MockServerClient, deviceId = \"device-1\") =>\n act(() => root.render(<Probe client={client} deviceId={deviceId} />));\n const flush = () => act(() => vi.advanceTimersByTimeAsync(0));\n\n beforeEach(() => {\n vi.useFakeTimers();\n result = undefined;\n container = document.createElement(\"div\");\n root = createRoot(container);\n });\n\n afterEach(() => {\n act(() => root.unmount());\n vi.useRealTimers();\n });\n\n it(\"reads the device record\", async () => {\n const client = aClient(() => Promise.resolve(DEVICE));\n\n render(client);\n expect(result).toBeUndefined();\n await flush();\n\n expect(result).toBe(DEVICE);\n expect(client.getDevice).toHaveBeenCalledWith(\"device-1\");\n });\n\n it(\"accepts a client from another copy of the package\", async () => {\n // Its own private field, as a MockClient from a second install would have:\n // only the public calls must match.\n class OtherCopyClient implements MockServerClient {\n private readonly record = DEVICE;\n getDevice = vi.fn(() => Promise.resolve(this.record));\n getScreenshot = vi.fn<MockServerClient[\"getScreenshot\"]>();\n pressButton = vi.fn<MockServerClient[\"pressButton\"]>();\n touchScreen = vi.fn<MockServerClient[\"touchScreen\"]>();\n sendApdu = vi.fn<MockServerClient[\"sendApdu\"]>();\n }\n const client = new OtherCopyClient();\n\n render(client);\n await flush();\n\n expect(result).toBe(DEVICE);\n });\n\n it(\"reads nothing without a device id\", async () => {\n const client = aClient(() => Promise.resolve(DEVICE));\n\n render(client, \"\");\n await flush();\n\n expect(result).toBeUndefined();\n expect(client.getDevice).not.toHaveBeenCalled();\n });\n\n it(\"retries a failed read until it succeeds\", async () => {\n const getDevice = vi\n .fn<() => Promise<Device>>()\n .mockRejectedValueOnce(new Error(\"offline\"))\n .mockResolvedValue(DEVICE);\n const client = aClient(getDevice);\n\n render(client);\n await flush();\n expect(result).toBeNull();\n\n await act(() => vi.advanceTimersByTimeAsync(2000));\n\n expect(result).toBe(DEVICE);\n expect(getDevice).toHaveBeenCalledTimes(2);\n });\n\n it(\"stops retrying once unmounted\", async () => {\n const client = aClient(() => Promise.reject(new Error(\"offline\")));\n\n render(client);\n await flush();\n act(() => root.render(<></>));\n await act(() => vi.advanceTimersByTimeAsync(10_000));\n\n expect(client.getDevice).toHaveBeenCalledOnce();\n });\n});\n"],
|
|
5
|
-
"mappings": "wdAAA,IAAAA,EAA2B,oBAC3BC,EAAsC,4BAItCC,EAAoC,iCAEpC,OAAO,OAAO,WAAY,CAAE,yBAA0B,EAAK,CAAC,EAE5D,MAAMC,EAAS,CAAE,GAAI,WAAY,YAAa,MAAO,EAErD,SAAS,sBAAuB,IAAM,CACpC,IAAIC,EACAC,EACAC,EAEJ,MAAMC,EAAkE,CAAC,CACvE,OAAAC,EACA,SAAAC,CACF,KACEH,KAAS,uBAAoBE,EAAQC,CAAQ,EACtC,MAEHC,EAAWC,IACd,CAAE,UAAW,GAAG,GAAGA,CAAS,CAAE,GAC3BC,EAAS,CAACJ,EAA0BC,EAAW,gBACnD,OAAI,IAAMJ,EAAK,OAAO,EAAAQ,QAAA,cAACN,EAAA,CAAM,OAAQC,EAAQ,SAAUC,EAAU,CAAE,CAAC,EAChEK,EAAQ,OAAM,OAAI,IAAM,GAAG,yBAAyB,CAAC,CAAC,EAE5D,WAAW,IAAM,CACf,GAAG,cAAc,EACjBR,EAAS,OACTF,EAAY,SAAS,cAAc,KAAK,EACxCC,KAAO,cAAWD,CAAS,CAC7B,CAAC,EAED,UAAU,IAAM,IACd,OAAI,IAAMC,EAAK,QAAQ,CAAC,EACxB,GAAG,cAAc,CACnB,CAAC,EAED,GAAG,0BAA2B,SAAY,CACxC,MAAMG,EAASE,EAAQ,IAAM,QAAQ,QAAQP,CAAM,CAAC,EAEpDS,EAAOJ,CAAM,EACb,OAAOF,CAAM,EAAE,cAAc,EAC7B,MAAMQ,EAAM,EAEZ,OAAOR,CAAM,EAAE,KAAKH,CAAM,EAC1B,OAAOK,EAAO,SAAS,EAAE,qBAAqB,UAAU,CAC1D,CAAC,EAED,GAAG,oDAAqD,SAAY,CAGlE,MAAMO,CAA4C,CAC/B,OAASZ,EAC1B,UAAY,GAAG,GAAG,IAAM,QAAQ,QAAQ,KAAK,MAAM,CAAC,EACpD,cAAgB,GAAG,GAAsC,EACzD,YAAc,GAAG,GAAoC,EACrD,YAAc,GAAG,GAAoC,EACrD,SAAW,GAAG,GAAiC,CACjD,CACA,MAAMK,EAAS,IAAIO,EAEnBH,EAAOJ,CAAM,EACb,MAAMM,EAAM,EAEZ,OAAOR,CAAM,EAAE,KAAKH,CAAM,CAC5B,CAAC,EAED,GAAG,oCAAqC,SAAY,CAClD,MAAMK,EAASE,EAAQ,IAAM,QAAQ,QAAQP,CAAM,CAAC,EAEpDS,EAAOJ,EAAQ,EAAE,EACjB,MAAMM,EAAM,EAEZ,OAAOR,CAAM,EAAE,cAAc,EAC7B,OAAOE,EAAO,SAAS,EAAE,IAAI,iBAAiB,CAChD,CAAC,EAED,GAAG,0CAA2C,SAAY,CACxD,MAAMG,EAAY,GACf,GAA0B,EAC1B,sBAAsB,IAAI,MAAM,SAAS,CAAC,EAC1C,kBAAkBR,CAAM,EACrBK,EAASE,EAAQC,CAAS,EAEhCC,EAAOJ,CAAM,EACb,MAAMM,EAAM,EACZ,OAAOR,CAAM,EAAE,SAAS,EAExB,QAAM,OAAI,IAAM,GAAG,yBAAyB,GAAI,CAAC,EAEjD,OAAOA,CAAM,EAAE,KAAKH,CAAM,EAC1B,OAAOQ,CAAS,EAAE,sBAAsB,CAAC,CAC3C,CAAC,EAED,GAAG,gCAAiC,SAAY,CAC9C,MAAMH,EAASE,EAAQ,IAAM,QAAQ,OAAO,IAAI,MAAM,SAAS,CAAC,CAAC,EAEjEE,EAAOJ,CAAM,EACb,MAAMM,EAAM,KACZ,OAAI,IAAMT,EAAK,OAAO,EAAAQ,QAAA,gBAAAA,QAAA,aAAE,CAAG,CAAC,EAC5B,QAAM,OAAI,IAAM,GAAG,yBAAyB,GAAM,CAAC,EAEnD,OAAOL,EAAO,SAAS,EAAE,qBAAqB,CAChD,CAAC,CACH,CAAC",
|
|
6
|
-
"names": ["import_react", "import_client", "import_useMockServerDevice", "DEVICE", "container", "root", "result", "Probe", "client", "deviceId", "aClient", "getDevice", "render", "React", "flush", "OtherCopyClient"]
|
|
7
|
-
}
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
"use strict";var L=Object.defineProperty;var p=Object.getOwnPropertyDescriptor;var R=Object.getOwnPropertyNames;var b=Object.prototype.hasOwnProperty;var y=(e,r)=>{for(var i in r)L(e,i,{get:r[i],enumerable:!0})},E=(e,r,i,u)=>{if(r&&typeof r=="object"||typeof r=="function")for(let c of R(r))!b.call(e,c)&&c!==i&&L(e,c,{get:()=>r[c],enumerable:!(u=p(r,c))||u.enumerable});return e};var D=e=>E(L({},"__esModule",{value:!0}),e);var I={};y(I,{useScreenPolling:()=>w});module.exports=D(I);var t=require("react");const U=500,O=2e3,T=3,_=e=>e instanceof Error?e.message:String(e);function w(e,r){const[i,u]=(0,t.useState)({kind:"loading"}),c=(0,t.useRef)(null),f=(0,t.useRef)(!1),d=(0,t.useRef)(!1);d.current=i.kind==="image";const g=(0,t.useRef)(0),S=(0,t.useRef)(0),l=(0,t.useCallback)(o=>{d.current&&++g.current<=T||u({kind:"error",message:_(o)})},[]),a=(0,t.useCallback)(()=>{c.current&&(URL.revokeObjectURL(c.current),c.current=null)},[]),k=(0,t.useRef)(()=>{}),h=(0,t.useMemo)(()=>{const o=n=>void n.then(()=>k.current()).catch(l);return{pressButton:(n,s)=>o(e.pressButton(n,s)),touch:(n,s,v)=>o(e.touch(n,s,v))}},[e,l]),m=(0,t.useCallback)(async()=>{if(f.current)return;f.current=!0;const o=S.current,n=()=>o!==S.current;try{const s=await e.screenshot();if(n())return;if(g.current=0,s)a(),c.current=URL.createObjectURL(s),u({kind:"image",src:c.current,input:h});else{const v=await e.idle?.();if(n())return;a(),u(v??{kind:"error",message:"No screen to capture"})}}catch(s){n()||l(s)}finally{f.current=!1}},[e,h,l,a]);return k.current=()=>void m(),(0,t.useEffect)(()=>{if(!r){a(),u({kind:"loading"});return}let o,n=!1;const s=async()=>{document.hidden||await m(),!n&&(o=setTimeout(()=>void s(),d.current?U:O))};return s(),()=>{n=!0,clearTimeout(o),S.current++}},[r,m,a]),(0,t.useEffect)(()=>a,[a]),i}0&&(module.exports={useScreenPolling});
|
|
2
|
-
//# sourceMappingURL=useScreenPolling.js.map
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../../src/useScreenPolling.ts"],
|
|
4
|
-
"sourcesContent": ["import { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\n\nimport {\n type DeviceScreenInput,\n type DeviceScreenState,\n type ScreenApi,\n} from \"./types\";\n\nconst SCREEN_POLL_MS = 500;\nconst IDLE_POLL_MS = 2000;\n/**\n * An app quitting fails a few calls while Speculos shuts down; the last frame\n * stays up through them rather than flashing an error before the dashboard.\n */\nconst LIVE_FAILURES_TOLERATED = 3;\n\nconst describe = (error: unknown): string =>\n error instanceof Error ? error.message : String(error);\n\nexport function useScreenPolling(\n api: ScreenApi,\n polling: boolean,\n): DeviceScreenState {\n const [state, setState] = useState<DeviceScreenState>({ kind: \"loading\" });\n\n const objectUrl = useRef<string | null>(null);\n const inFlight = useRef(false);\n const isLive = useRef(false);\n isLive.current = state.kind === \"image\";\n const liveFailures = useRef(0);\n /** Bumped when polling stops, so a response still in flight is dropped. */\n const generation = useRef(0);\n\n const fail = useCallback((error: unknown) => {\n if (isLive.current && ++liveFailures.current <= LIVE_FAILURES_TOLERATED) {\n return;\n }\n setState({ kind: \"error\", message: describe(error) });\n }, []);\n\n const releaseObjectUrl = useCallback(() => {\n if (objectUrl.current) {\n URL.revokeObjectURL(objectUrl.current);\n objectUrl.current = null;\n }\n }, []);\n\n /**\n * Input handlers refresh straight after acting. Going through a ref keeps\n * them stable, so the <img> is not remounted on every poll.\n */\n const refreshRef = useRef<() => void>(() => {});\n\n const input = useMemo<DeviceScreenInput>(() => {\n const send = (call: Promise<void>) =>\n void call.then(() => refreshRef.current()).catch(fail);\n\n return {\n pressButton: (button, action) => send(api.pressButton(button, action)),\n touch: (x, y, action) => send(api.touch(x, y, action)),\n };\n }, [api, fail]);\n\n const refresh = useCallback(async () => {\n if (inFlight.current) return;\n inFlight.current = true;\n const started = generation.current;\n const stale = () => started !== generation.current;\n try {\n const blob = await api.screenshot();\n if (stale()) return;\n liveFailures.current = 0;\n if (blob) {\n releaseObjectUrl();\n objectUrl.current = URL.createObjectURL(blob);\n setState({ kind: \"image\", src: objectUrl.current, input });\n } else {\n const idle = await api.idle?.();\n if (stale()) return;\n releaseObjectUrl();\n setState(idle ?? { kind: \"error\", message: \"No screen to capture\" });\n }\n } catch (error) {\n if (!stale()) fail(error);\n } finally {\n inFlight.current = false;\n }\n }, [api, input, fail, releaseObjectUrl]);\n refreshRef.current = () => void refresh();\n\n useEffect(() => {\n if (!polling) {\n // Drop the frame rather than keep a revoked object URL around, which\n // would flash a broken image when the panel reopens.\n releaseObjectUrl();\n setState({ kind: \"loading\" });\n return;\n }\n\n let timer: ReturnType<typeof setTimeout>;\n let cancelled = false;\n\n const tick = async () => {\n if (!document.hidden) await refresh();\n if (cancelled) return;\n timer = setTimeout(\n () => void tick(),\n isLive.current ? SCREEN_POLL_MS : IDLE_POLL_MS,\n );\n };\n void tick();\n\n return () => {\n cancelled = true;\n clearTimeout(timer);\n generation.current++;\n };\n }, [polling, refresh, releaseObjectUrl]);\n\n useEffect(() => releaseObjectUrl, [releaseObjectUrl]);\n\n return state;\n}\n"],
|
|
5
|
-
"mappings": "yaAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,sBAAAE,IAAA,eAAAC,EAAAH,GAAA,IAAAI,EAAkE,iBAQlE,MAAMC,EAAiB,IACjBC,EAAe,IAKfC,EAA0B,EAE1BC,EAAYC,GAChBA,aAAiB,MAAQA,EAAM,QAAU,OAAOA,CAAK,EAEhD,SAASP,EACdQ,EACAC,EACmB,CACnB,KAAM,CAACC,EAAOC,CAAQ,KAAI,YAA4B,CAAE,KAAM,SAAU,CAAC,EAEnEC,KAAY,UAAsB,IAAI,EACtCC,KAAW,UAAO,EAAK,EACvBC,KAAS,UAAO,EAAK,EAC3BA,EAAO,QAAUJ,EAAM,OAAS,QAChC,MAAMK,KAAe,UAAO,CAAC,EAEvBC,KAAa,UAAO,CAAC,EAErBC,KAAO,eAAaV,GAAmB,CACvCO,EAAO,SAAW,EAAEC,EAAa,SAAWV,GAGhDM,EAAS,CAAE,KAAM,QAAS,QAASL,EAASC,CAAK,CAAE,CAAC,CACtD,EAAG,CAAC,CAAC,EAECW,KAAmB,eAAY,IAAM,CACrCN,EAAU,UACZ,IAAI,gBAAgBA,EAAU,OAAO,EACrCA,EAAU,QAAU,KAExB,EAAG,CAAC,CAAC,EAMCO,KAAa,UAAmB,IAAM,CAAC,CAAC,EAExCC,KAAQ,WAA2B,IAAM,CAC7C,MAAMC,EAAQC,GACZ,KAAKA,EAAK,KAAK,IAAMH,EAAW,QAAQ,CAAC,EAAE,MAAMF,CAAI,EAEvD,MAAO,CACL,YAAa,CAACM,EAAQC,IAAWH,EAAKb,EAAI,YAAYe,EAAQC,CAAM,CAAC,EACrE,MAAO,CAACC,EAAGC,EAAGF,IAAWH,EAAKb,EAAI,MAAMiB,EAAGC,EAAGF,CAAM,CAAC,CACvD,CACF,EAAG,CAAChB,EAAKS,CAAI,CAAC,EAERU,KAAU,eAAY,SAAY,CACtC,GAAId,EAAS,QAAS,OACtBA,EAAS,QAAU,GACnB,MAAMe,EAAUZ,EAAW,QACrBa,EAAQ,IAAMD,IAAYZ,EAAW,QAC3C,GAAI,CACF,MAAMc,EAAO,MAAMtB,EAAI,WAAW,EAClC,GAAIqB,EAAM,EAAG,OAEb,GADAd,EAAa,QAAU,EACnBe,EACFZ,EAAiB,EACjBN,EAAU,QAAU,IAAI,gBAAgBkB,CAAI,EAC5CnB,EAAS,CAAE,KAAM,QAAS,IAAKC,EAAU,QAAS,MAAAQ,CAAM,CAAC,MACpD,CACL,MAAMW,EAAO,MAAMvB,EAAI,OAAO,EAC9B,GAAIqB,EAAM,EAAG,OACbX,EAAiB,EACjBP,EAASoB,GAAQ,CAAE,KAAM,QAAS,QAAS,sBAAuB,CAAC,CACrE,CACF,OAASxB,EAAO,CACTsB,EAAM,GAAGZ,EAAKV,CAAK,CAC1B,QAAE,CACAM,EAAS,QAAU,EACrB,CACF,EAAG,CAACL,EAAKY,EAAOH,EAAMC,CAAgB,CAAC,EACvC,OAAAC,EAAW,QAAU,IAAM,KAAKQ,EAAQ,KAExC,aAAU,IAAM,CACd,GAAI,CAAClB,EAAS,CAGZS,EAAiB,EACjBP,EAAS,CAAE,KAAM,SAAU,CAAC,EAC5B,MACF,CAEA,IAAIqB,EACAC,EAAY,GAEhB,MAAMC,EAAO,SAAY,CAClB,SAAS,QAAQ,MAAMP,EAAQ,EAChC,CAAAM,IACJD,EAAQ,WACN,IAAM,KAAKE,EAAK,EAChBpB,EAAO,QAAUX,EAAiBC,CACpC,EACF,EACA,OAAK8B,EAAK,EAEH,IAAM,CACXD,EAAY,GACZ,aAAaD,CAAK,EAClBhB,EAAW,SACb,CACF,EAAG,CAACP,EAASkB,EAAST,CAAgB,CAAC,KAEvC,aAAU,IAAMA,EAAkB,CAACA,CAAgB,CAAC,EAE7CR,CACT",
|
|
6
|
-
"names": ["useScreenPolling_exports", "__export", "useScreenPolling", "__toCommonJS", "import_react", "SCREEN_POLL_MS", "IDLE_POLL_MS", "LIVE_FAILURES_TOLERATED", "describe", "error", "api", "polling", "state", "setState", "objectUrl", "inFlight", "isLive", "liveFailures", "generation", "fail", "releaseObjectUrl", "refreshRef", "input", "send", "call", "button", "action", "x", "y", "refresh", "started", "stale", "blob", "idle", "timer", "cancelled", "tick"]
|
|
7
|
-
}
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import{findDeviceScreenModel as e}from"./deviceModels";describe("findDeviceScreenModel",()=>{it("finds a model by the mock server's device type",()=>{expect(e("stax")).toMatchObject({label:"Stax",touch:!0,speculos:!0})}),it("marks Nano S as having no emulator",()=>{expect(e("nanoS").speculos).toBe(!1)}),it("falls back to a button-driven model for an unknown type",()=>{expect(e("unknown")).toMatchObject({label:"Device",touch:!1})})});
|
|
2
|
-
//# sourceMappingURL=deviceModels.test.js.map
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../../src/deviceModels.test.ts"],
|
|
4
|
-
"sourcesContent": ["import { findDeviceScreenModel } from \"./deviceModels\";\n\ndescribe(\"findDeviceScreenModel\", () => {\n it(\"finds a model by the mock server's device type\", () => {\n expect(findDeviceScreenModel(\"stax\")).toMatchObject({\n label: \"Stax\",\n touch: true,\n speculos: true,\n });\n });\n\n it(\"marks Nano S as having no emulator\", () => {\n expect(findDeviceScreenModel(\"nanoS\").speculos).toBe(false);\n });\n\n it(\"falls back to a button-driven model for an unknown type\", () => {\n expect(findDeviceScreenModel(\"unknown\")).toMatchObject({\n label: \"Device\",\n touch: false,\n });\n });\n});\n"],
|
|
5
|
-
"mappings": "AAAA,OAAS,yBAAAA,MAA6B,iBAEtC,SAAS,wBAAyB,IAAM,CACtC,GAAG,iDAAkD,IAAM,CACzD,OAAOA,EAAsB,MAAM,CAAC,EAAE,cAAc,CAClD,MAAO,OACP,MAAO,GACP,SAAU,EACZ,CAAC,CACH,CAAC,EAED,GAAG,qCAAsC,IAAM,CAC7C,OAAOA,EAAsB,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAK,CAC5D,CAAC,EAED,GAAG,0DAA2D,IAAM,CAClE,OAAOA,EAAsB,SAAS,CAAC,EAAE,cAAc,CACrD,MAAO,SACP,MAAO,EACT,CAAC,CACH,CAAC,CACH,CAAC",
|
|
6
|
-
"names": ["findDeviceScreenModel"]
|
|
7
|
-
}
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
const c=o=>o?.status===409,a=(o,t)=>({screenshot:async()=>{try{return await o.getScreenshot(t)}catch(n){if(c(n))return null;throw n}},idle:async()=>({kind:"os-info",device:await o.getDevice(t)}),pressButton:(n,e)=>o.pressButton(t,n,e),touch:(n,e,r)=>o.touchScreen(t,n,e,r),openApp:async n=>{const e=new TextEncoder().encode(n);if(e.length>255)throw new Error(`App name too long to open: ${n}`);const{response:r}=await o.sendApdu(t,Uint8Array.from([224,216,0,0,e.length,...e])),s=r.slice(-4);if(s!=="9000")throw new Error(`Could not open ${n} (${s})`)}});export{a as mockServerScreenApi};
|
|
2
|
-
//# sourceMappingURL=mockServerScreenApi.js.map
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../../src/mockServerScreenApi.ts"],
|
|
4
|
-
"sourcesContent": ["import { type MockServerClient, type ScreenApi } from \"./types\";\n\n/** The proxy answers 409 when the device has no Speculos instance. */\nconst isNoInstance = (error: unknown): boolean =>\n (error as { status?: unknown } | null)?.status === 409;\n\nexport const mockServerScreenApi = (\n client: MockServerClient,\n deviceId: string,\n): ScreenApi => ({\n screenshot: async () => {\n try {\n return await client.getScreenshot(deviceId);\n } catch (error) {\n if (isNoInstance(error)) return null;\n throw error;\n }\n },\n idle: async () => ({\n kind: \"os-info\",\n device: await client.getDevice(deviceId),\n }),\n pressButton: (button, action) => client.pressButton(deviceId, button, action),\n touch: (x, y, action) => client.touchScreen(deviceId, x, y, action),\n openApp: async (appName) => {\n const name = new TextEncoder().encode(appName);\n // Lc is a single byte.\n if (name.length > 0xff) {\n throw new Error(`App name too long to open: ${appName}`);\n }\n const { response } = await client.sendApdu(\n deviceId,\n Uint8Array.from([0xe0, 0xd8, 0x00, 0x00, name.length, ...name]),\n );\n const statusWord = response.slice(-4);\n if (statusWord !== \"9000\") {\n throw new Error(`Could not open ${appName} (${statusWord})`);\n }\n },\n});\n"],
|
|
5
|
-
"mappings": "AAGA,MAAMA,EAAgBC,GACnBA,GAAuC,SAAW,IAExCC,EAAsB,CACjCC,EACAC,KACe,CACf,WAAY,SAAY,CACtB,GAAI,CACF,OAAO,MAAMD,EAAO,cAAcC,CAAQ,CAC5C,OAASH,EAAO,CACd,GAAID,EAAaC,CAAK,EAAG,OAAO,KAChC,MAAMA,CACR,CACF,EACA,KAAM,UAAa,CACjB,KAAM,UACN,OAAQ,MAAME,EAAO,UAAUC,CAAQ,CACzC,GACA,YAAa,CAACC,EAAQC,IAAWH,EAAO,YAAYC,EAAUC,EAAQC,CAAM,EAC5E,MAAO,CAACC,EAAGC,EAAGF,IAAWH,EAAO,YAAYC,EAAUG,EAAGC,EAAGF,CAAM,EAClE,QAAS,MAAOG,GAAY,CAC1B,MAAMC,EAAO,IAAI,YAAY,EAAE,OAAOD,CAAO,EAE7C,GAAIC,EAAK,OAAS,IAChB,MAAM,IAAI,MAAM,8BAA8BD,CAAO,EAAE,EAEzD,KAAM,CAAE,SAAAE,CAAS,EAAI,MAAMR,EAAO,SAChCC,EACA,WAAW,KAAK,CAAC,IAAM,IAAM,EAAM,EAAMM,EAAK,OAAQ,GAAGA,CAAI,CAAC,CAChE,EACME,EAAaD,EAAS,MAAM,EAAE,EACpC,GAAIC,IAAe,OACjB,MAAM,IAAI,MAAM,kBAAkBH,CAAO,KAAKG,CAAU,GAAG,CAE/D,CACF",
|
|
6
|
-
"names": ["isNoInstance", "error", "mockServerScreenApi", "client", "deviceId", "button", "action", "x", "y", "appName", "name", "response", "statusWord"]
|
|
7
|
-
}
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import{mockServerScreenApi as o}from"./mockServerScreenApi";const n=e=>({getScreenshot:vi.fn(),getDevice:vi.fn(),pressButton:vi.fn(()=>Promise.resolve()),touchScreen:vi.fn(()=>Promise.resolve()),...e});describe("mockServerScreenApi",()=>{it("resolves the screenshot of the device",async()=>{const e=new Blob(["png"]),t=n({getScreenshot:vi.fn(()=>Promise.resolve(e))});await expect(o(t,"device-1").screenshot()).resolves.toBe(e),expect(t.getScreenshot).toHaveBeenCalledWith("device-1")}),it("resolves no screenshot when the device has no Speculos instance",async()=>{const e=n({getScreenshot:vi.fn(()=>Promise.reject({status:409}))});await expect(o(e,"device-1").screenshot()).resolves.toBeNull()}),it("rethrows any other screenshot failure",async()=>{const e=Object.assign(new Error("boom"),{status:500}),t=n({getScreenshot:vi.fn(()=>Promise.reject(e))});await expect(o(t,"device-1").screenshot()).rejects.toBe(e)}),it("shows the device record while idle",async()=>{const e={id:"device-1",device_type:"nanoX"},t=n({getDevice:vi.fn(()=>Promise.resolve(e))});await expect(o(t,"device-1").idle?.()).resolves.toEqual({kind:"os-info",device:e})}),it("forwards buttons and touches to the device",async()=>{const e=n({}),t=o(e,"device-1");await t.pressButton("left","press"),await t.touch(10,20,"release"),expect(e.pressButton).toHaveBeenCalledWith("device-1","left","press"),expect(e.touchScreen).toHaveBeenCalledWith("device-1",10,20,"release")}),it("opens an app by name",async()=>{const e=vi.fn(()=>Promise.resolve({response:"9000"}));await o(n({sendApdu:e}),"device-1").openApp?.("Bitcoin"),expect(e).toHaveBeenCalledWith("device-1",Uint8Array.from([224,216,0,0,7,...new TextEncoder().encode("Bitcoin")]))}),it("fails to open an app the device refuses",async()=>{const e=vi.fn(()=>Promise.resolve({response:"6807"})),t=o(n({sendApdu:e}),"device-1");await expect(t.openApp?.("Bitcoin")).rejects.toThrow("6807")}),it("refuses an app name too long for one APDU",async()=>{const e=vi.fn(()=>Promise.resolve({response:"9000"})),t=o(n({sendApdu:e}),"device-1");await expect(t.openApp?.("a".repeat(256))).rejects.toThrow("too long"),expect(e).not.toHaveBeenCalled()})});
|
|
2
|
-
//# sourceMappingURL=mockServerScreenApi.test.js.map
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../../src/mockServerScreenApi.test.ts"],
|
|
4
|
-
"sourcesContent": ["import { mockServerScreenApi } from \"./mockServerScreenApi\";\nimport { type MockServerClient } from \"./types\";\n\nconst aClient = (overrides: Partial<Record<keyof MockServerClient, unknown>>) =>\n ({\n getScreenshot: vi.fn(),\n getDevice: vi.fn(),\n pressButton: vi.fn(() => Promise.resolve()),\n touchScreen: vi.fn(() => Promise.resolve()),\n ...overrides,\n }) as unknown as MockServerClient;\n\ndescribe(\"mockServerScreenApi\", () => {\n it(\"resolves the screenshot of the device\", async () => {\n const blob = new Blob([\"png\"]);\n const client = aClient({\n getScreenshot: vi.fn(() => Promise.resolve(blob)),\n });\n\n await expect(\n mockServerScreenApi(client, \"device-1\").screenshot(),\n ).resolves.toBe(blob);\n expect(client.getScreenshot).toHaveBeenCalledWith(\"device-1\");\n });\n\n it(\"resolves no screenshot when the device has no Speculos instance\", async () => {\n const client = aClient({\n getScreenshot: vi.fn(() => Promise.reject({ status: 409 })),\n });\n\n await expect(\n mockServerScreenApi(client, \"device-1\").screenshot(),\n ).resolves.toBeNull();\n });\n\n it(\"rethrows any other screenshot failure\", async () => {\n const error = Object.assign(new Error(\"boom\"), { status: 500 });\n const client = aClient({\n getScreenshot: vi.fn(() => Promise.reject(error)),\n });\n\n await expect(\n mockServerScreenApi(client, \"device-1\").screenshot(),\n ).rejects.toBe(error);\n });\n\n it(\"shows the device record while idle\", async () => {\n const device = { id: \"device-1\", device_type: \"nanoX\" };\n const client = aClient({ getDevice: vi.fn(() => Promise.resolve(device)) });\n\n await expect(\n mockServerScreenApi(client, \"device-1\").idle?.(),\n ).resolves.toEqual({\n kind: \"os-info\",\n device,\n });\n });\n\n it(\"forwards buttons and touches to the device\", async () => {\n const client = aClient({});\n const api = mockServerScreenApi(client, \"device-1\");\n\n await api.pressButton(\"left\", \"press\");\n await api.touch(10, 20, \"release\");\n\n expect(client.pressButton).toHaveBeenCalledWith(\n \"device-1\",\n \"left\",\n \"press\",\n );\n expect(client.touchScreen).toHaveBeenCalledWith(\n \"device-1\",\n 10,\n 20,\n \"release\",\n );\n });\n\n it(\"opens an app by name\", async () => {\n const sendApdu = vi.fn(() => Promise.resolve({ response: \"9000\" }));\n const api = mockServerScreenApi(aClient({ sendApdu }), \"device-1\");\n\n await api.openApp?.(\"Bitcoin\");\n\n expect(sendApdu).toHaveBeenCalledWith(\n \"device-1\",\n Uint8Array.from([\n 0xe0,\n 0xd8,\n 0x00,\n 0x00,\n 0x07,\n ...new TextEncoder().encode(\"Bitcoin\"),\n ]),\n );\n });\n\n it(\"fails to open an app the device refuses\", async () => {\n const sendApdu = vi.fn(() => Promise.resolve({ response: \"6807\" }));\n const api = mockServerScreenApi(aClient({ sendApdu }), \"device-1\");\n\n await expect(api.openApp?.(\"Bitcoin\")).rejects.toThrow(\"6807\");\n });\n\n it(\"refuses an app name too long for one APDU\", async () => {\n const sendApdu = vi.fn(() => Promise.resolve({ response: \"9000\" }));\n const api = mockServerScreenApi(aClient({ sendApdu }), \"device-1\");\n\n await expect(api.openApp?.(\"a\".repeat(256))).rejects.toThrow(\"too long\");\n expect(sendApdu).not.toHaveBeenCalled();\n });\n});\n"],
|
|
5
|
-
"mappings": "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",
|
|
6
|
-
"names": ["mockServerScreenApi", "aClient", "overrides", "blob", "client", "error", "device", "api", "sendApdu"]
|
|
7
|
-
}
|
package/lib/esm/src/types.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
//# sourceMappingURL=types.js.map
|
package/lib/esm/src/types.js.map
DELETED
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import{useEffect as f,useState as l}from"react";const s=2e3;function p(n,e){const[u,t]=l();return f(()=>{let r=!1,i;if(t(void 0),!e)return;const o=()=>{n.getDevice(e).then(c=>{r||t(c)},()=>{r||(t(null),i=setTimeout(o,s))})};return o(),()=>{r=!0,clearTimeout(i)}},[n,e]),u}export{p as useMockServerDevice};
|
|
2
|
-
//# sourceMappingURL=useMockServerDevice.js.map
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../../src/useMockServerDevice.ts"],
|
|
4
|
-
"sourcesContent": ["import { useEffect, useState } from \"react\";\nimport { type Device } from \"@ledgerhq/device-mockserver-client\";\n\nimport { type MockServerClient } from \"./types\";\n\nconst RETRY_MS = 2000;\n\n/**\n * The mock server's record of a device: undefined while it loads, or while no\n * device id is given, and null while it cannot be read. A failed read is\n * retried, so a transient error does not leave the device without its model.\n */\nexport function useMockServerDevice(\n client: MockServerClient,\n deviceId: string,\n): Device | null | undefined {\n const [device, setDevice] = useState<Device | null>();\n\n useEffect(() => {\n let cancelled = false;\n let retry: ReturnType<typeof setTimeout> | undefined;\n setDevice(undefined);\n if (!deviceId) return;\n\n const load = () => {\n client.getDevice(deviceId).then(\n (record) => {\n if (!cancelled) setDevice(record);\n },\n () => {\n if (cancelled) return;\n setDevice(null);\n retry = setTimeout(load, RETRY_MS);\n },\n );\n };\n load();\n\n return () => {\n cancelled = true;\n clearTimeout(retry);\n };\n }, [client, deviceId]);\n\n return device;\n}\n"],
|
|
5
|
-
"mappings": "AAAA,OAAS,aAAAA,EAAW,YAAAC,MAAgB,QAKpC,MAAMC,EAAW,IAOV,SAASC,EACdC,EACAC,EAC2B,CAC3B,KAAM,CAACC,EAAQC,CAAS,EAAIN,EAAwB,EAEpD,OAAAD,EAAU,IAAM,CACd,IAAIQ,EAAY,GACZC,EAEJ,GADAF,EAAU,MAAS,EACf,CAACF,EAAU,OAEf,MAAMK,EAAO,IAAM,CACjBN,EAAO,UAAUC,CAAQ,EAAE,KACxBM,GAAW,CACLH,GAAWD,EAAUI,CAAM,CAClC,EACA,IAAM,CACAH,IACJD,EAAU,IAAI,EACdE,EAAQ,WAAWC,EAAMR,CAAQ,EACnC,CACF,CACF,EACA,OAAAQ,EAAK,EAEE,IAAM,CACXF,EAAY,GACZ,aAAaC,CAAK,CACpB,CACF,EAAG,CAACL,EAAQC,CAAQ,CAAC,EAEdC,CACT",
|
|
6
|
-
"names": ["useEffect", "useState", "RETRY_MS", "useMockServerDevice", "client", "deviceId", "device", "setDevice", "cancelled", "retry", "load", "record"]
|
|
7
|
-
}
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import v,{act as c}from"react";import{createRoot as m}from"react-dom/client";import{useMockServerDevice as p}from"./useMockServerDevice";Object.assign(globalThis,{IS_REACT_ACT_ENVIRONMENT:!0});const n={id:"device-1",device_type:"stax"};describe("useMockServerDevice",()=>{let l,s,t;const d=({client:e,deviceId:i})=>(t=p(e,i),null),a=e=>({getDevice:vi.fn(e)}),r=(e,i="device-1")=>c(()=>s.render(v.createElement(d,{client:e,deviceId:i}))),o=()=>c(()=>vi.advanceTimersByTimeAsync(0));beforeEach(()=>{vi.useFakeTimers(),t=void 0,l=document.createElement("div"),s=m(l)}),afterEach(()=>{c(()=>s.unmount()),vi.useRealTimers()}),it("reads the device record",async()=>{const e=a(()=>Promise.resolve(n));r(e),expect(t).toBeUndefined(),await o(),expect(t).toBe(n),expect(e.getDevice).toHaveBeenCalledWith("device-1")}),it("accepts a client from another copy of the package",async()=>{class e{record=n;getDevice=vi.fn(()=>Promise.resolve(this.record));getScreenshot=vi.fn();pressButton=vi.fn();touchScreen=vi.fn();sendApdu=vi.fn()}const i=new e;r(i),await o(),expect(t).toBe(n)}),it("reads nothing without a device id",async()=>{const e=a(()=>Promise.resolve(n));r(e,""),await o(),expect(t).toBeUndefined(),expect(e.getDevice).not.toHaveBeenCalled()}),it("retries a failed read until it succeeds",async()=>{const e=vi.fn().mockRejectedValueOnce(new Error("offline")).mockResolvedValue(n),i=a(e);r(i),await o(),expect(t).toBeNull(),await c(()=>vi.advanceTimersByTimeAsync(2e3)),expect(t).toBe(n),expect(e).toHaveBeenCalledTimes(2)}),it("stops retrying once unmounted",async()=>{const e=a(()=>Promise.reject(new Error("offline")));r(e),await o(),c(()=>s.render(v.createElement(v.Fragment,null))),await c(()=>vi.advanceTimersByTimeAsync(1e4)),expect(e.getDevice).toHaveBeenCalledOnce()})});
|
|
2
|
-
//# sourceMappingURL=useMockServerDevice.test.js.map
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../../src/useMockServerDevice.test.tsx"],
|
|
4
|
-
"sourcesContent": ["import React, { act } from \"react\";\nimport { createRoot, type Root } from \"react-dom/client\";\nimport { type Device } from \"@ledgerhq/device-mockserver-client\";\n\nimport { type MockServerClient } from \"./types\";\nimport { useMockServerDevice } from \"./useMockServerDevice\";\n\nObject.assign(globalThis, { IS_REACT_ACT_ENVIRONMENT: true });\n\nconst DEVICE = { id: \"device-1\", device_type: \"stax\" } as Device;\n\ndescribe(\"useMockServerDevice\", () => {\n let container: HTMLDivElement;\n let root: Root;\n let result: Device | null | undefined;\n\n const Probe: React.FC<{ client: MockServerClient; deviceId: string }> = ({\n client,\n deviceId,\n }) => {\n result = useMockServerDevice(client, deviceId);\n return null;\n };\n const aClient = (getDevice: () => Promise<Device>) =>\n ({ getDevice: vi.fn(getDevice) }) as unknown as MockServerClient;\n const render = (client: MockServerClient, deviceId = \"device-1\") =>\n act(() => root.render(<Probe client={client} deviceId={deviceId} />));\n const flush = () => act(() => vi.advanceTimersByTimeAsync(0));\n\n beforeEach(() => {\n vi.useFakeTimers();\n result = undefined;\n container = document.createElement(\"div\");\n root = createRoot(container);\n });\n\n afterEach(() => {\n act(() => root.unmount());\n vi.useRealTimers();\n });\n\n it(\"reads the device record\", async () => {\n const client = aClient(() => Promise.resolve(DEVICE));\n\n render(client);\n expect(result).toBeUndefined();\n await flush();\n\n expect(result).toBe(DEVICE);\n expect(client.getDevice).toHaveBeenCalledWith(\"device-1\");\n });\n\n it(\"accepts a client from another copy of the package\", async () => {\n // Its own private field, as a MockClient from a second install would have:\n // only the public calls must match.\n class OtherCopyClient implements MockServerClient {\n private readonly record = DEVICE;\n getDevice = vi.fn(() => Promise.resolve(this.record));\n getScreenshot = vi.fn<MockServerClient[\"getScreenshot\"]>();\n pressButton = vi.fn<MockServerClient[\"pressButton\"]>();\n touchScreen = vi.fn<MockServerClient[\"touchScreen\"]>();\n sendApdu = vi.fn<MockServerClient[\"sendApdu\"]>();\n }\n const client = new OtherCopyClient();\n\n render(client);\n await flush();\n\n expect(result).toBe(DEVICE);\n });\n\n it(\"reads nothing without a device id\", async () => {\n const client = aClient(() => Promise.resolve(DEVICE));\n\n render(client, \"\");\n await flush();\n\n expect(result).toBeUndefined();\n expect(client.getDevice).not.toHaveBeenCalled();\n });\n\n it(\"retries a failed read until it succeeds\", async () => {\n const getDevice = vi\n .fn<() => Promise<Device>>()\n .mockRejectedValueOnce(new Error(\"offline\"))\n .mockResolvedValue(DEVICE);\n const client = aClient(getDevice);\n\n render(client);\n await flush();\n expect(result).toBeNull();\n\n await act(() => vi.advanceTimersByTimeAsync(2000));\n\n expect(result).toBe(DEVICE);\n expect(getDevice).toHaveBeenCalledTimes(2);\n });\n\n it(\"stops retrying once unmounted\", async () => {\n const client = aClient(() => Promise.reject(new Error(\"offline\")));\n\n render(client);\n await flush();\n act(() => root.render(<></>));\n await act(() => vi.advanceTimersByTimeAsync(10_000));\n\n expect(client.getDevice).toHaveBeenCalledOnce();\n });\n});\n"],
|
|
5
|
-
"mappings": "AAAA,OAAOA,GAAS,OAAAC,MAAW,QAC3B,OAAS,cAAAC,MAA6B,mBAItC,OAAS,uBAAAC,MAA2B,wBAEpC,OAAO,OAAO,WAAY,CAAE,yBAA0B,EAAK,CAAC,EAE5D,MAAMC,EAAS,CAAE,GAAI,WAAY,YAAa,MAAO,EAErD,SAAS,sBAAuB,IAAM,CACpC,IAAIC,EACAC,EACAC,EAEJ,MAAMC,EAAkE,CAAC,CACvE,OAAAC,EACA,SAAAC,CACF,KACEH,EAASJ,EAAoBM,EAAQC,CAAQ,EACtC,MAEHC,EAAWC,IACd,CAAE,UAAW,GAAG,GAAGA,CAAS,CAAE,GAC3BC,EAAS,CAACJ,EAA0BC,EAAW,aACnDT,EAAI,IAAMK,EAAK,OAAON,EAAA,cAACQ,EAAA,CAAM,OAAQC,EAAQ,SAAUC,EAAU,CAAE,CAAC,EAChEI,EAAQ,IAAMb,EAAI,IAAM,GAAG,yBAAyB,CAAC,CAAC,EAE5D,WAAW,IAAM,CACf,GAAG,cAAc,EACjBM,EAAS,OACTF,EAAY,SAAS,cAAc,KAAK,EACxCC,EAAOJ,EAAWG,CAAS,CAC7B,CAAC,EAED,UAAU,IAAM,CACdJ,EAAI,IAAMK,EAAK,QAAQ,CAAC,EACxB,GAAG,cAAc,CACnB,CAAC,EAED,GAAG,0BAA2B,SAAY,CACxC,MAAMG,EAASE,EAAQ,IAAM,QAAQ,QAAQP,CAAM,CAAC,EAEpDS,EAAOJ,CAAM,EACb,OAAOF,CAAM,EAAE,cAAc,EAC7B,MAAMO,EAAM,EAEZ,OAAOP,CAAM,EAAE,KAAKH,CAAM,EAC1B,OAAOK,EAAO,SAAS,EAAE,qBAAqB,UAAU,CAC1D,CAAC,EAED,GAAG,oDAAqD,SAAY,CAGlE,MAAMM,CAA4C,CAC/B,OAASX,EAC1B,UAAY,GAAG,GAAG,IAAM,QAAQ,QAAQ,KAAK,MAAM,CAAC,EACpD,cAAgB,GAAG,GAAsC,EACzD,YAAc,GAAG,GAAoC,EACrD,YAAc,GAAG,GAAoC,EACrD,SAAW,GAAG,GAAiC,CACjD,CACA,MAAMK,EAAS,IAAIM,EAEnBF,EAAOJ,CAAM,EACb,MAAMK,EAAM,EAEZ,OAAOP,CAAM,EAAE,KAAKH,CAAM,CAC5B,CAAC,EAED,GAAG,oCAAqC,SAAY,CAClD,MAAMK,EAASE,EAAQ,IAAM,QAAQ,QAAQP,CAAM,CAAC,EAEpDS,EAAOJ,EAAQ,EAAE,EACjB,MAAMK,EAAM,EAEZ,OAAOP,CAAM,EAAE,cAAc,EAC7B,OAAOE,EAAO,SAAS,EAAE,IAAI,iBAAiB,CAChD,CAAC,EAED,GAAG,0CAA2C,SAAY,CACxD,MAAMG,EAAY,GACf,GAA0B,EAC1B,sBAAsB,IAAI,MAAM,SAAS,CAAC,EAC1C,kBAAkBR,CAAM,EACrBK,EAASE,EAAQC,CAAS,EAEhCC,EAAOJ,CAAM,EACb,MAAMK,EAAM,EACZ,OAAOP,CAAM,EAAE,SAAS,EAExB,MAAMN,EAAI,IAAM,GAAG,yBAAyB,GAAI,CAAC,EAEjD,OAAOM,CAAM,EAAE,KAAKH,CAAM,EAC1B,OAAOQ,CAAS,EAAE,sBAAsB,CAAC,CAC3C,CAAC,EAED,GAAG,gCAAiC,SAAY,CAC9C,MAAMH,EAASE,EAAQ,IAAM,QAAQ,OAAO,IAAI,MAAM,SAAS,CAAC,CAAC,EAEjEE,EAAOJ,CAAM,EACb,MAAMK,EAAM,EACZb,EAAI,IAAMK,EAAK,OAAON,EAAA,cAAAA,EAAA,aAAE,CAAG,CAAC,EAC5B,MAAMC,EAAI,IAAM,GAAG,yBAAyB,GAAM,CAAC,EAEnD,OAAOQ,EAAO,SAAS,EAAE,qBAAqB,CAChD,CAAC,CACH,CAAC",
|
|
6
|
-
"names": ["React", "act", "createRoot", "useMockServerDevice", "DEVICE", "container", "root", "result", "Probe", "client", "deviceId", "aClient", "getDevice", "render", "flush", "OtherCopyClient"]
|
|
7
|
-
}
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import{useCallback as m,useEffect as p,useMemo as R,useRef as s,useState as b}from"react";const y=500,E=2e3,D=3,U=r=>r instanceof Error?r.message:String(r);function T(r,v){const[L,i]=b({kind:"loading"}),o=s(null),a=s(!1),l=s(!1);l.current=L.kind==="image";const g=s(0),f=s(0),u=m(n=>{l.current&&++g.current<=D||i({kind:"error",message:U(n)})},[]),c=m(()=>{o.current&&(URL.revokeObjectURL(o.current),o.current=null)},[]),k=s(()=>{}),h=R(()=>{const n=e=>void e.then(()=>k.current()).catch(u);return{pressButton:(e,t)=>n(r.pressButton(e,t)),touch:(e,t,S)=>n(r.touch(e,t,S))}},[r,u]),d=m(async()=>{if(a.current)return;a.current=!0;const n=f.current,e=()=>n!==f.current;try{const t=await r.screenshot();if(e())return;if(g.current=0,t)c(),o.current=URL.createObjectURL(t),i({kind:"image",src:o.current,input:h});else{const S=await r.idle?.();if(e())return;c(),i(S??{kind:"error",message:"No screen to capture"})}}catch(t){e()||u(t)}finally{a.current=!1}},[r,h,u,c]);return k.current=()=>void d(),p(()=>{if(!v){c(),i({kind:"loading"});return}let n,e=!1;const t=async()=>{document.hidden||await d(),!e&&(n=setTimeout(()=>void t(),l.current?y:E))};return t(),()=>{e=!0,clearTimeout(n),f.current++}},[v,d,c]),p(()=>c,[c]),L}export{T as useScreenPolling};
|
|
2
|
-
//# sourceMappingURL=useScreenPolling.js.map
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../../src/useScreenPolling.ts"],
|
|
4
|
-
"sourcesContent": ["import { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\n\nimport {\n type DeviceScreenInput,\n type DeviceScreenState,\n type ScreenApi,\n} from \"./types\";\n\nconst SCREEN_POLL_MS = 500;\nconst IDLE_POLL_MS = 2000;\n/**\n * An app quitting fails a few calls while Speculos shuts down; the last frame\n * stays up through them rather than flashing an error before the dashboard.\n */\nconst LIVE_FAILURES_TOLERATED = 3;\n\nconst describe = (error: unknown): string =>\n error instanceof Error ? error.message : String(error);\n\nexport function useScreenPolling(\n api: ScreenApi,\n polling: boolean,\n): DeviceScreenState {\n const [state, setState] = useState<DeviceScreenState>({ kind: \"loading\" });\n\n const objectUrl = useRef<string | null>(null);\n const inFlight = useRef(false);\n const isLive = useRef(false);\n isLive.current = state.kind === \"image\";\n const liveFailures = useRef(0);\n /** Bumped when polling stops, so a response still in flight is dropped. */\n const generation = useRef(0);\n\n const fail = useCallback((error: unknown) => {\n if (isLive.current && ++liveFailures.current <= LIVE_FAILURES_TOLERATED) {\n return;\n }\n setState({ kind: \"error\", message: describe(error) });\n }, []);\n\n const releaseObjectUrl = useCallback(() => {\n if (objectUrl.current) {\n URL.revokeObjectURL(objectUrl.current);\n objectUrl.current = null;\n }\n }, []);\n\n /**\n * Input handlers refresh straight after acting. Going through a ref keeps\n * them stable, so the <img> is not remounted on every poll.\n */\n const refreshRef = useRef<() => void>(() => {});\n\n const input = useMemo<DeviceScreenInput>(() => {\n const send = (call: Promise<void>) =>\n void call.then(() => refreshRef.current()).catch(fail);\n\n return {\n pressButton: (button, action) => send(api.pressButton(button, action)),\n touch: (x, y, action) => send(api.touch(x, y, action)),\n };\n }, [api, fail]);\n\n const refresh = useCallback(async () => {\n if (inFlight.current) return;\n inFlight.current = true;\n const started = generation.current;\n const stale = () => started !== generation.current;\n try {\n const blob = await api.screenshot();\n if (stale()) return;\n liveFailures.current = 0;\n if (blob) {\n releaseObjectUrl();\n objectUrl.current = URL.createObjectURL(blob);\n setState({ kind: \"image\", src: objectUrl.current, input });\n } else {\n const idle = await api.idle?.();\n if (stale()) return;\n releaseObjectUrl();\n setState(idle ?? { kind: \"error\", message: \"No screen to capture\" });\n }\n } catch (error) {\n if (!stale()) fail(error);\n } finally {\n inFlight.current = false;\n }\n }, [api, input, fail, releaseObjectUrl]);\n refreshRef.current = () => void refresh();\n\n useEffect(() => {\n if (!polling) {\n // Drop the frame rather than keep a revoked object URL around, which\n // would flash a broken image when the panel reopens.\n releaseObjectUrl();\n setState({ kind: \"loading\" });\n return;\n }\n\n let timer: ReturnType<typeof setTimeout>;\n let cancelled = false;\n\n const tick = async () => {\n if (!document.hidden) await refresh();\n if (cancelled) return;\n timer = setTimeout(\n () => void tick(),\n isLive.current ? SCREEN_POLL_MS : IDLE_POLL_MS,\n );\n };\n void tick();\n\n return () => {\n cancelled = true;\n clearTimeout(timer);\n generation.current++;\n };\n }, [polling, refresh, releaseObjectUrl]);\n\n useEffect(() => releaseObjectUrl, [releaseObjectUrl]);\n\n return state;\n}\n"],
|
|
5
|
-
"mappings": "AAAA,OAAS,eAAAA,EAAa,aAAAC,EAAW,WAAAC,EAAS,UAAAC,EAAQ,YAAAC,MAAgB,QAQlE,MAAMC,EAAiB,IACjBC,EAAe,IAKfC,EAA0B,EAE1BC,EAAYC,GAChBA,aAAiB,MAAQA,EAAM,QAAU,OAAOA,CAAK,EAEhD,SAASC,EACdC,EACAC,EACmB,CACnB,KAAM,CAACC,EAAOC,CAAQ,EAAIV,EAA4B,CAAE,KAAM,SAAU,CAAC,EAEnEW,EAAYZ,EAAsB,IAAI,EACtCa,EAAWb,EAAO,EAAK,EACvBc,EAASd,EAAO,EAAK,EAC3Bc,EAAO,QAAUJ,EAAM,OAAS,QAChC,MAAMK,EAAef,EAAO,CAAC,EAEvBgB,EAAahB,EAAO,CAAC,EAErBiB,EAAOpB,EAAaS,GAAmB,CACvCQ,EAAO,SAAW,EAAEC,EAAa,SAAWX,GAGhDO,EAAS,CAAE,KAAM,QAAS,QAASN,EAASC,CAAK,CAAE,CAAC,CACtD,EAAG,CAAC,CAAC,EAECY,EAAmBrB,EAAY,IAAM,CACrCe,EAAU,UACZ,IAAI,gBAAgBA,EAAU,OAAO,EACrCA,EAAU,QAAU,KAExB,EAAG,CAAC,CAAC,EAMCO,EAAanB,EAAmB,IAAM,CAAC,CAAC,EAExCoB,EAAQrB,EAA2B,IAAM,CAC7C,MAAMsB,EAAQC,GACZ,KAAKA,EAAK,KAAK,IAAMH,EAAW,QAAQ,CAAC,EAAE,MAAMF,CAAI,EAEvD,MAAO,CACL,YAAa,CAACM,EAAQC,IAAWH,EAAKb,EAAI,YAAYe,EAAQC,CAAM,CAAC,EACrE,MAAO,CAACC,EAAGC,EAAGF,IAAWH,EAAKb,EAAI,MAAMiB,EAAGC,EAAGF,CAAM,CAAC,CACvD,CACF,EAAG,CAAChB,EAAKS,CAAI,CAAC,EAERU,EAAU9B,EAAY,SAAY,CACtC,GAAIgB,EAAS,QAAS,OACtBA,EAAS,QAAU,GACnB,MAAMe,EAAUZ,EAAW,QACrBa,EAAQ,IAAMD,IAAYZ,EAAW,QAC3C,GAAI,CACF,MAAMc,EAAO,MAAMtB,EAAI,WAAW,EAClC,GAAIqB,EAAM,EAAG,OAEb,GADAd,EAAa,QAAU,EACnBe,EACFZ,EAAiB,EACjBN,EAAU,QAAU,IAAI,gBAAgBkB,CAAI,EAC5CnB,EAAS,CAAE,KAAM,QAAS,IAAKC,EAAU,QAAS,MAAAQ,CAAM,CAAC,MACpD,CACL,MAAMW,EAAO,MAAMvB,EAAI,OAAO,EAC9B,GAAIqB,EAAM,EAAG,OACbX,EAAiB,EACjBP,EAASoB,GAAQ,CAAE,KAAM,QAAS,QAAS,sBAAuB,CAAC,CACrE,CACF,OAASzB,EAAO,CACTuB,EAAM,GAAGZ,EAAKX,CAAK,CAC1B,QAAE,CACAO,EAAS,QAAU,EACrB,CACF,EAAG,CAACL,EAAKY,EAAOH,EAAMC,CAAgB,CAAC,EACvC,OAAAC,EAAW,QAAU,IAAM,KAAKQ,EAAQ,EAExC7B,EAAU,IAAM,CACd,GAAI,CAACW,EAAS,CAGZS,EAAiB,EACjBP,EAAS,CAAE,KAAM,SAAU,CAAC,EAC5B,MACF,CAEA,IAAIqB,EACAC,EAAY,GAEhB,MAAMC,EAAO,SAAY,CAClB,SAAS,QAAQ,MAAMP,EAAQ,EAChC,CAAAM,IACJD,EAAQ,WACN,IAAM,KAAKE,EAAK,EAChBpB,EAAO,QAAUZ,EAAiBC,CACpC,EACF,EACA,OAAK+B,EAAK,EAEH,IAAM,CACXD,EAAY,GACZ,aAAaD,CAAK,EAClBhB,EAAW,SACb,CACF,EAAG,CAACP,EAASkB,EAAST,CAAgB,CAAC,EAEvCpB,EAAU,IAAMoB,EAAkB,CAACA,CAAgB,CAAC,EAE7CR,CACT",
|
|
6
|
-
"names": ["useCallback", "useEffect", "useMemo", "useRef", "useState", "SCREEN_POLL_MS", "IDLE_POLL_MS", "LIVE_FAILURES_TOLERATED", "describe", "error", "useScreenPolling", "api", "polling", "state", "setState", "objectUrl", "inFlight", "isLive", "liveFailures", "generation", "fail", "releaseObjectUrl", "refreshRef", "input", "send", "call", "button", "action", "x", "y", "refresh", "started", "stale", "blob", "idle", "timer", "cancelled", "tick"]
|
|
7
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"deviceModels.test.d.ts","sourceRoot":"","sources":["../../../src/deviceModels.test.ts"],"names":[],"mappings":""}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"mockServerScreenApi.d.ts","sourceRoot":"","sources":["../../../src/mockServerScreenApi.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,gBAAgB,EAAE,KAAK,SAAS,EAAE,MAAM,SAAS,CAAC;AAMhE,eAAO,MAAM,mBAAmB,GAC9B,QAAQ,gBAAgB,EACxB,UAAU,MAAM,KACf,SA8BD,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"mockServerScreenApi.test.d.ts","sourceRoot":"","sources":["../../../src/mockServerScreenApi.test.ts"],"names":[],"mappings":""}
|
package/lib/types/src/types.d.ts
DELETED
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
import { type Device, type MockClient, type SpeculosAction, type SpeculosButton } from "@ledgerhq/device-mockserver-client";
|
|
2
|
-
/**
|
|
3
|
-
* The MockClient calls this library makes. Picking them drops the class's
|
|
4
|
-
* private fields, so a client from another copy of the package still fits.
|
|
5
|
-
*/
|
|
6
|
-
export type MockServerClient = Pick<MockClient, "getDevice" | "getScreenshot" | "pressButton" | "touchScreen" | "sendApdu">;
|
|
7
|
-
/**
|
|
8
|
-
* How the screen is reached. The mock server proxies to the Speculos instance
|
|
9
|
-
* backing a device and a Speculos transport talks to one directly, but both
|
|
10
|
-
* answer the same calls.
|
|
11
|
-
*/
|
|
12
|
-
export interface ScreenApi {
|
|
13
|
-
/** Resolves null when there is no screen to capture right now. */
|
|
14
|
-
screenshot(): Promise<Blob | null>;
|
|
15
|
-
/** What to show while `screenshot` keeps resolving null. */
|
|
16
|
-
idle?(): Promise<DeviceScreenState>;
|
|
17
|
-
pressButton(button: SpeculosButton, action: SpeculosAction): Promise<void>;
|
|
18
|
-
/** Coordinates in device screen pixels. */
|
|
19
|
-
touch(x: number, y: number, action: SpeculosAction): Promise<void>;
|
|
20
|
-
/** Omitted by apis whose device always runs an app. */
|
|
21
|
-
openApp?(appName: string): Promise<void>;
|
|
22
|
-
}
|
|
23
|
-
/**
|
|
24
|
-
* Press and release are separate so a hold reaches the device as a hold: Stax
|
|
25
|
-
* and Flex gate their confirmations behind one.
|
|
26
|
-
*/
|
|
27
|
-
export interface DeviceScreenInput {
|
|
28
|
-
pressButton(button: SpeculosButton, action: SpeculosAction): void;
|
|
29
|
-
touch(x: number, y: number, action: SpeculosAction): void;
|
|
30
|
-
}
|
|
31
|
-
export type DeviceScreenState = {
|
|
32
|
-
kind: "loading";
|
|
33
|
-
}
|
|
34
|
-
/** No app runs, so no Speculos instance: the device record stands in. */
|
|
35
|
-
| {
|
|
36
|
-
kind: "os-info";
|
|
37
|
-
device: Device;
|
|
38
|
-
} | {
|
|
39
|
-
kind: "image";
|
|
40
|
-
src: string;
|
|
41
|
-
input: DeviceScreenInput;
|
|
42
|
-
} | {
|
|
43
|
-
kind: "error";
|
|
44
|
-
message: string;
|
|
45
|
-
};
|
|
46
|
-
//# sourceMappingURL=types.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,MAAM,EACX,KAAK,UAAU,EACf,KAAK,cAAc,EACnB,KAAK,cAAc,EACpB,MAAM,oCAAoC,CAAC;AAE5C;;;GAGG;AACH,MAAM,MAAM,gBAAgB,GAAG,IAAI,CACjC,UAAU,EACV,WAAW,GAAG,eAAe,GAAG,aAAa,GAAG,aAAa,GAAG,UAAU,CAC3E,CAAC;AAEF;;;;GAIG;AACH,MAAM,WAAW,SAAS;IACxB,kEAAkE;IAClE,UAAU,IAAI,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC;IACnC,4DAA4D;IAC5D,IAAI,CAAC,IAAI,OAAO,CAAC,iBAAiB,CAAC,CAAC;IACpC,WAAW,CAAC,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3E,2CAA2C;IAC3C,KAAK,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACnE,uDAAuD;IACvD,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1C;AAED;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IAChC,WAAW,CAAC,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,cAAc,GAAG,IAAI,CAAC;IAClE,KAAK,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc,GAAG,IAAI,CAAC;CAC3D;AAED,MAAM,MAAM,iBAAiB,GACzB;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE;AACrB,yEAAyE;GACvE;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACnC;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,iBAAiB,CAAA;CAAE,GACxD;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC"}
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import { type Device } from "@ledgerhq/device-mockserver-client";
|
|
2
|
-
import { type MockServerClient } from "./types";
|
|
3
|
-
/**
|
|
4
|
-
* The mock server's record of a device: undefined while it loads, or while no
|
|
5
|
-
* device id is given, and null while it cannot be read. A failed read is
|
|
6
|
-
* retried, so a transient error does not leave the device without its model.
|
|
7
|
-
*/
|
|
8
|
-
export declare function useMockServerDevice(client: MockServerClient, deviceId: string): Device | null | undefined;
|
|
9
|
-
//# sourceMappingURL=useMockServerDevice.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"useMockServerDevice.d.ts","sourceRoot":"","sources":["../../../src/useMockServerDevice.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,MAAM,EAAE,MAAM,oCAAoC,CAAC;AAEjE,OAAO,EAAE,KAAK,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAIhD;;;;GAIG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,gBAAgB,EACxB,QAAQ,EAAE,MAAM,GACf,MAAM,GAAG,IAAI,GAAG,SAAS,CA8B3B"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"useMockServerDevice.test.d.ts","sourceRoot":"","sources":["../../../src/useMockServerDevice.test.tsx"],"names":[],"mappings":""}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"useScreenPolling.d.ts","sourceRoot":"","sources":["../../../src/useScreenPolling.ts"],"names":[],"mappings":"AAEA,OAAO,EAEL,KAAK,iBAAiB,EACtB,KAAK,SAAS,EACf,MAAM,SAAS,CAAC;AAajB,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,SAAS,EACd,OAAO,EAAE,OAAO,GACf,iBAAiB,CAoGnB"}
|