@agentvault/secure-channel 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/__tests__/functional.test.d.ts +21 -0
- package/dist/__tests__/functional.test.d.ts.map +1 -0
- package/dist/__tests__/multi-session.test.d.ts +2 -0
- package/dist/__tests__/multi-session.test.d.ts.map +1 -0
- package/dist/channel.d.ts +36 -2
- package/dist/channel.d.ts.map +1 -1
- package/dist/cli.d.ts +0 -1
- package/dist/cli.js +45663 -104
- package/dist/cli.js.map +7 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +45568 -3
- package/dist/index.js.map +7 -1
- package/dist/state.d.ts +6 -1
- package/dist/state.d.ts.map +1 -1
- package/dist/types.d.ts +21 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +14 -7
- package/dist/__tests__/crypto-helpers.test.js +0 -73
- package/dist/__tests__/crypto-helpers.test.js.map +0 -1
- package/dist/__tests__/state.test.js +0 -58
- package/dist/__tests__/state.test.js.map +0 -1
- package/dist/__tests__/transport.test.js +0 -101
- package/dist/__tests__/transport.test.js.map +0 -1
- package/dist/channel.js +0 -325
- package/dist/channel.js.map +0 -1
- package/dist/crypto-helpers.js +0 -44
- package/dist/crypto-helpers.js.map +0 -1
- package/dist/state.js +0 -28
- package/dist/state.js.map +0 -1
- package/dist/transport.js +0 -39
- package/dist/transport.js.map +0 -1
- package/dist/types.js +0 -2
- package/dist/types.js.map +0 -1
package/dist/state.d.ts
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import type { PersistedState } from "./types.js";
|
|
2
2
|
export declare function saveState(dataDir: string, state: PersistedState): Promise<void>;
|
|
3
|
-
|
|
3
|
+
/**
|
|
4
|
+
* Load raw persisted state from disk.
|
|
5
|
+
* Returns the raw parsed JSON — caller is responsible for migration
|
|
6
|
+
* from legacy format to current PersistedState.
|
|
7
|
+
*/
|
|
8
|
+
export declare function loadState(dataDir: string): Promise<any | null>;
|
|
4
9
|
export declare function clearState(dataDir: string): Promise<void>;
|
|
5
10
|
//# sourceMappingURL=state.d.ts.map
|
package/dist/state.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../src/state.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAIjD,wBAAsB,SAAS,CAC7B,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,cAAc,GACpB,OAAO,CAAC,IAAI,CAAC,CAIf;AAED,wBAAsB,SAAS,CAC7B,OAAO,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../src/state.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAIjD,wBAAsB,SAAS,CAC7B,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,cAAc,GACpB,OAAO,CAAC,IAAI,CAAC,CAIf;AAED;;;;GAIG;AACH,wBAAsB,SAAS,CAC7B,OAAO,EAAE,MAAM,GAEd,OAAO,CAAC,GAAG,GAAG,IAAI,CAAC,CAQrB;AAED,wBAAsB,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAO/D"}
|
package/dist/types.d.ts
CHANGED
|
@@ -13,7 +13,28 @@ export interface MessageMetadata {
|
|
|
13
13
|
conversationId: string;
|
|
14
14
|
timestamp: string;
|
|
15
15
|
}
|
|
16
|
+
export interface DeviceSession {
|
|
17
|
+
ownerDeviceId: string;
|
|
18
|
+
ratchetState: string;
|
|
19
|
+
}
|
|
16
20
|
export interface PersistedState {
|
|
21
|
+
deviceId: string;
|
|
22
|
+
deviceJwt: string;
|
|
23
|
+
primaryConversationId: string;
|
|
24
|
+
sessions: Record<string, DeviceSession>;
|
|
25
|
+
identityKeypair: {
|
|
26
|
+
publicKey: string;
|
|
27
|
+
privateKey: string;
|
|
28
|
+
};
|
|
29
|
+
ephemeralKeypair: {
|
|
30
|
+
publicKey: string;
|
|
31
|
+
privateKey: string;
|
|
32
|
+
};
|
|
33
|
+
fingerprint: string;
|
|
34
|
+
lastMessageTimestamp?: string;
|
|
35
|
+
}
|
|
36
|
+
/** Legacy state format for backward compatibility */
|
|
37
|
+
export interface LegacyPersistedState {
|
|
17
38
|
deviceId: string;
|
|
18
39
|
deviceJwt: string;
|
|
19
40
|
conversationId: string;
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,mBAAmB;IAClC,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,eAAe,KAAK,IAAI,CAAC;IACnE,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,CAAC;CAC/C;AAED,MAAM,MAAM,YAAY,GACpB,MAAM,GACN,WAAW,GACX,SAAS,GACT,YAAY,GACZ,YAAY,GACZ,OAAO,GACP,cAAc,GACd,OAAO,CAAC;AAEZ,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC;IAC3D,gBAAgB,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC;IAC5D,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC/B"}
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,mBAAmB;IAClC,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,eAAe,KAAK,IAAI,CAAC;IACnE,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,CAAC;CAC/C;AAED,MAAM,MAAM,YAAY,GACpB,MAAM,GACN,WAAW,GACX,SAAS,GACT,YAAY,GACZ,YAAY,GACZ,OAAO,GACP,cAAc,GACd,OAAO,CAAC;AAEZ,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,aAAa;IAC5B,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,qBAAqB,EAAE,MAAM,CAAC;IAC9B,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;IACxC,eAAe,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC;IAC3D,gBAAgB,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC;IAC5D,WAAW,EAAE,MAAM,CAAC;IACpB,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC/B;AAED,qDAAqD;AACrD,MAAM,WAAW,oBAAoB;IACnC,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC;IAC3D,gBAAgB,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC;IAC5D,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC/B"}
|
package/package.json
CHANGED
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agentvault/secure-channel",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"import": "./dist/index.js",
|
|
10
|
+
"types": "./dist/index.d.ts"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
7
13
|
"license": "MIT",
|
|
8
14
|
"scripts": {
|
|
9
|
-
"build": "tsc",
|
|
15
|
+
"build": "tsc --emitDeclarationOnly && node build.mjs",
|
|
10
16
|
"test": "vitest run",
|
|
11
17
|
"test:watch": "vitest"
|
|
12
18
|
},
|
|
@@ -21,14 +27,15 @@
|
|
|
21
27
|
"access": "public"
|
|
22
28
|
},
|
|
23
29
|
"dependencies": {
|
|
24
|
-
"@agentvault/crypto": "^0.1.0",
|
|
25
|
-
"libsodium-wrappers-sumo": "^0.7.15",
|
|
26
30
|
"ws": "^8.16.0"
|
|
27
31
|
},
|
|
28
32
|
"devDependencies": {
|
|
29
|
-
"
|
|
30
|
-
"
|
|
33
|
+
"@agentvault/crypto": "^0.1.0",
|
|
34
|
+
"@types/libsodium-wrappers-sumo": "^0.7.8",
|
|
31
35
|
"@types/ws": "^8.5.10",
|
|
32
|
-
"
|
|
36
|
+
"esbuild": "^0.27.3",
|
|
37
|
+
"libsodium-wrappers-sumo": "^0.7.15",
|
|
38
|
+
"typescript": "^5.4.0",
|
|
39
|
+
"vitest": "^2.0.0"
|
|
33
40
|
}
|
|
34
41
|
}
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeAll } from "vitest";
|
|
2
|
-
import sodium from "libsodium-wrappers-sumo";
|
|
3
|
-
import { hexToBytes, bytesToHex, base64ToBytes, bytesToBase64, encryptedMessageToTransport, transportToEncryptedMessage, } from "../crypto-helpers.js";
|
|
4
|
-
beforeAll(async () => {
|
|
5
|
-
await sodium.ready;
|
|
6
|
-
});
|
|
7
|
-
describe("hex conversion", () => {
|
|
8
|
-
it("round-trips hex encoding", () => {
|
|
9
|
-
const original = sodium.randombytes_buf(32);
|
|
10
|
-
const hex = bytesToHex(original);
|
|
11
|
-
const decoded = hexToBytes(hex);
|
|
12
|
-
expect(decoded).toEqual(original);
|
|
13
|
-
});
|
|
14
|
-
it("handles known value", () => {
|
|
15
|
-
const bytes = new Uint8Array([0xde, 0xad, 0xbe, 0xef]);
|
|
16
|
-
expect(bytesToHex(bytes)).toBe("deadbeef");
|
|
17
|
-
expect(hexToBytes("deadbeef")).toEqual(bytes);
|
|
18
|
-
});
|
|
19
|
-
});
|
|
20
|
-
describe("base64 conversion", () => {
|
|
21
|
-
it("round-trips base64 encoding", () => {
|
|
22
|
-
const original = sodium.randombytes_buf(48);
|
|
23
|
-
const b64 = bytesToBase64(original);
|
|
24
|
-
const decoded = base64ToBytes(b64);
|
|
25
|
-
expect(decoded).toEqual(original);
|
|
26
|
-
});
|
|
27
|
-
});
|
|
28
|
-
describe("transport format", () => {
|
|
29
|
-
it("round-trips EncryptedMessage through transport format", () => {
|
|
30
|
-
const msg = {
|
|
31
|
-
header: {
|
|
32
|
-
dhPublicKey: sodium.randombytes_buf(32),
|
|
33
|
-
previousChainLength: 3,
|
|
34
|
-
messageNumber: 7,
|
|
35
|
-
},
|
|
36
|
-
headerSignature: sodium.randombytes_buf(64),
|
|
37
|
-
nonce: sodium.randombytes_buf(24),
|
|
38
|
-
ciphertext: sodium.randombytes_buf(128),
|
|
39
|
-
};
|
|
40
|
-
const transport = encryptedMessageToTransport(msg);
|
|
41
|
-
const restored = transportToEncryptedMessage(transport);
|
|
42
|
-
expect(restored.header.dhPublicKey).toEqual(msg.header.dhPublicKey);
|
|
43
|
-
expect(restored.header.previousChainLength).toBe(msg.header.previousChainLength);
|
|
44
|
-
expect(restored.header.messageNumber).toBe(msg.header.messageNumber);
|
|
45
|
-
expect(restored.headerSignature).toEqual(msg.headerSignature);
|
|
46
|
-
expect(restored.nonce).toEqual(msg.nonce);
|
|
47
|
-
expect(restored.ciphertext).toEqual(msg.ciphertext);
|
|
48
|
-
});
|
|
49
|
-
it("produces expected transport structure", () => {
|
|
50
|
-
const msg = {
|
|
51
|
-
header: {
|
|
52
|
-
dhPublicKey: sodium.randombytes_buf(32),
|
|
53
|
-
previousChainLength: 0,
|
|
54
|
-
messageNumber: 0,
|
|
55
|
-
},
|
|
56
|
-
headerSignature: sodium.randombytes_buf(64),
|
|
57
|
-
nonce: sodium.randombytes_buf(24),
|
|
58
|
-
ciphertext: sodium.randombytes_buf(16),
|
|
59
|
-
};
|
|
60
|
-
const transport = encryptedMessageToTransport(msg);
|
|
61
|
-
expect(typeof transport.header_blob).toBe("string");
|
|
62
|
-
expect(typeof transport.ciphertext).toBe("string");
|
|
63
|
-
// header_blob should be base64-encoded JSON
|
|
64
|
-
const headerJson = new TextDecoder().decode(base64ToBytes(transport.header_blob));
|
|
65
|
-
const headerObj = JSON.parse(headerJson);
|
|
66
|
-
expect(headerObj).toHaveProperty("dhPublicKey");
|
|
67
|
-
expect(headerObj).toHaveProperty("previousChainLength");
|
|
68
|
-
expect(headerObj).toHaveProperty("messageNumber");
|
|
69
|
-
expect(headerObj).toHaveProperty("headerSignature");
|
|
70
|
-
expect(headerObj).toHaveProperty("nonce");
|
|
71
|
-
});
|
|
72
|
-
});
|
|
73
|
-
//# sourceMappingURL=crypto-helpers.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"crypto-helpers.test.js","sourceRoot":"","sources":["../../src/__tests__/crypto-helpers.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
|
2
|
-
import { mkdtemp, rm } from "node:fs/promises";
|
|
3
|
-
import { join } from "node:path";
|
|
4
|
-
import { tmpdir } from "node:os";
|
|
5
|
-
import { saveState, loadState, clearState } from "../state.js";
|
|
6
|
-
const makeState = () => ({
|
|
7
|
-
deviceId: "d1234567-1234-1234-1234-123456789abc",
|
|
8
|
-
deviceJwt: "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.test.sig",
|
|
9
|
-
conversationId: "c1234567-1234-1234-1234-123456789abc",
|
|
10
|
-
identityKeypair: {
|
|
11
|
-
publicKey: "aabb".repeat(16),
|
|
12
|
-
privateKey: "ccdd".repeat(16),
|
|
13
|
-
},
|
|
14
|
-
ephemeralKeypair: {
|
|
15
|
-
publicKey: "eeff".repeat(16),
|
|
16
|
-
privateKey: "1122".repeat(16),
|
|
17
|
-
},
|
|
18
|
-
fingerprint: "aa:bb:cc:dd:ee:ff:00:11",
|
|
19
|
-
ratchetState: '{"rootKey":"deadbeef"}',
|
|
20
|
-
});
|
|
21
|
-
let tempDir;
|
|
22
|
-
beforeEach(async () => {
|
|
23
|
-
tempDir = await mkdtemp(join(tmpdir(), "av-state-test-"));
|
|
24
|
-
});
|
|
25
|
-
afterEach(async () => {
|
|
26
|
-
await rm(tempDir, { recursive: true, force: true });
|
|
27
|
-
});
|
|
28
|
-
describe("state persistence", () => {
|
|
29
|
-
it("round-trips state through save/load", async () => {
|
|
30
|
-
const state = makeState();
|
|
31
|
-
await saveState(tempDir, state);
|
|
32
|
-
const loaded = await loadState(tempDir);
|
|
33
|
-
expect(loaded).toEqual(state);
|
|
34
|
-
});
|
|
35
|
-
it("returns null for missing state file", async () => {
|
|
36
|
-
const loaded = await loadState(tempDir);
|
|
37
|
-
expect(loaded).toBeNull();
|
|
38
|
-
});
|
|
39
|
-
it("clearState removes the state file", async () => {
|
|
40
|
-
const state = makeState();
|
|
41
|
-
await saveState(tempDir, state);
|
|
42
|
-
await clearState(tempDir);
|
|
43
|
-
const loaded = await loadState(tempDir);
|
|
44
|
-
expect(loaded).toBeNull();
|
|
45
|
-
});
|
|
46
|
-
it("clearState is safe when file doesn't exist", async () => {
|
|
47
|
-
// Should not throw
|
|
48
|
-
await clearState(tempDir);
|
|
49
|
-
});
|
|
50
|
-
it("creates dataDir if it doesn't exist", async () => {
|
|
51
|
-
const nestedDir = join(tempDir, "nested", "deep");
|
|
52
|
-
const state = makeState();
|
|
53
|
-
await saveState(nestedDir, state);
|
|
54
|
-
const loaded = await loadState(nestedDir);
|
|
55
|
-
expect(loaded).toEqual(state);
|
|
56
|
-
});
|
|
57
|
-
});
|
|
58
|
-
//# sourceMappingURL=state.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"state.test.js","sourceRoot":"","sources":["../../src/__tests__/state.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
|
2
|
-
import { enrollDevice, pollDeviceStatus, activateDevice, } from "../transport.js";
|
|
3
|
-
const mockFetch = vi.fn();
|
|
4
|
-
beforeEach(() => {
|
|
5
|
-
vi.stubGlobal("fetch", mockFetch);
|
|
6
|
-
});
|
|
7
|
-
afterEach(() => {
|
|
8
|
-
vi.restoreAllMocks();
|
|
9
|
-
});
|
|
10
|
-
const API = "http://localhost:8000";
|
|
11
|
-
describe("enrollDevice", () => {
|
|
12
|
-
it("sends correct request and returns response", async () => {
|
|
13
|
-
const body = {
|
|
14
|
-
device_id: "d1234567-1234-1234-1234-123456789abc",
|
|
15
|
-
fingerprint: "aa:bb:cc:dd",
|
|
16
|
-
status: "PENDING",
|
|
17
|
-
};
|
|
18
|
-
mockFetch.mockResolvedValueOnce({
|
|
19
|
-
ok: true,
|
|
20
|
-
json: async () => body,
|
|
21
|
-
});
|
|
22
|
-
const result = await enrollDevice(API, "invite-token-123", "aabb".repeat(16), "ccdd".repeat(16), "eeff".repeat(32));
|
|
23
|
-
expect(mockFetch).toHaveBeenCalledOnce();
|
|
24
|
-
const [url, opts] = mockFetch.mock.calls[0];
|
|
25
|
-
expect(url).toBe(`${API}/api/v1/enroll`);
|
|
26
|
-
expect(opts.method).toBe("POST");
|
|
27
|
-
expect(JSON.parse(opts.body)).toEqual({
|
|
28
|
-
invite_token: "invite-token-123",
|
|
29
|
-
identity_public_key: "aabb".repeat(16),
|
|
30
|
-
ephemeral_public_key: "ccdd".repeat(16),
|
|
31
|
-
proof_of_possession: "eeff".repeat(32),
|
|
32
|
-
platform: "node",
|
|
33
|
-
});
|
|
34
|
-
expect(result).toEqual(body);
|
|
35
|
-
});
|
|
36
|
-
it("throws on non-ok response", async () => {
|
|
37
|
-
mockFetch.mockResolvedValueOnce({
|
|
38
|
-
ok: false,
|
|
39
|
-
status: 400,
|
|
40
|
-
text: async () => "Bad request",
|
|
41
|
-
});
|
|
42
|
-
await expect(enrollDevice(API, "bad", "aa", "bb", "cc")).rejects.toThrow("Enrollment failed (400)");
|
|
43
|
-
});
|
|
44
|
-
});
|
|
45
|
-
describe("pollDeviceStatus", () => {
|
|
46
|
-
it("sends GET and returns status", async () => {
|
|
47
|
-
const body = {
|
|
48
|
-
device_id: "d1234567-1234-1234-1234-123456789abc",
|
|
49
|
-
status: "PENDING",
|
|
50
|
-
fingerprint: "aa:bb:cc:dd",
|
|
51
|
-
};
|
|
52
|
-
mockFetch.mockResolvedValueOnce({
|
|
53
|
-
ok: true,
|
|
54
|
-
json: async () => body,
|
|
55
|
-
});
|
|
56
|
-
const result = await pollDeviceStatus(API, "d1234567-1234-1234-1234-123456789abc");
|
|
57
|
-
expect(mockFetch).toHaveBeenCalledOnce();
|
|
58
|
-
const [url] = mockFetch.mock.calls[0];
|
|
59
|
-
expect(url).toBe(`${API}/api/v1/devices/d1234567-1234-1234-1234-123456789abc/status`);
|
|
60
|
-
expect(result).toEqual(body);
|
|
61
|
-
});
|
|
62
|
-
it("throws on non-ok response", async () => {
|
|
63
|
-
mockFetch.mockResolvedValueOnce({
|
|
64
|
-
ok: false,
|
|
65
|
-
status: 429,
|
|
66
|
-
text: async () => "Rate limited",
|
|
67
|
-
});
|
|
68
|
-
await expect(pollDeviceStatus(API, "xxx")).rejects.toThrow("Status poll failed (429)");
|
|
69
|
-
});
|
|
70
|
-
});
|
|
71
|
-
describe("activateDevice", () => {
|
|
72
|
-
it("sends POST and returns activation data", async () => {
|
|
73
|
-
const body = {
|
|
74
|
-
device_id: "d1234567-1234-1234-1234-123456789abc",
|
|
75
|
-
conversation_id: "c1234567-1234-1234-1234-123456789abc",
|
|
76
|
-
status: "ACTIVE",
|
|
77
|
-
device_jwt: "eyJ0eXAi.test.sig",
|
|
78
|
-
owner_identity_public_key: "aabb".repeat(16),
|
|
79
|
-
owner_ephemeral_public_key: "ccdd".repeat(16),
|
|
80
|
-
};
|
|
81
|
-
mockFetch.mockResolvedValueOnce({
|
|
82
|
-
ok: true,
|
|
83
|
-
json: async () => body,
|
|
84
|
-
});
|
|
85
|
-
const result = await activateDevice(API, "d1234567-1234-1234-1234-123456789abc");
|
|
86
|
-
expect(mockFetch).toHaveBeenCalledOnce();
|
|
87
|
-
const [url, opts] = mockFetch.mock.calls[0];
|
|
88
|
-
expect(url).toBe(`${API}/api/v1/devices/d1234567-1234-1234-1234-123456789abc/activate`);
|
|
89
|
-
expect(opts.method).toBe("POST");
|
|
90
|
-
expect(result).toEqual(body);
|
|
91
|
-
});
|
|
92
|
-
it("throws on non-ok response", async () => {
|
|
93
|
-
mockFetch.mockResolvedValueOnce({
|
|
94
|
-
ok: false,
|
|
95
|
-
status: 409,
|
|
96
|
-
text: async () => "Conflict",
|
|
97
|
-
});
|
|
98
|
-
await expect(activateDevice(API, "xxx")).rejects.toThrow("Activation failed (409)");
|
|
99
|
-
});
|
|
100
|
-
});
|
|
101
|
-
//# sourceMappingURL=transport.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"transport.test.js","sourceRoot":"","sources":["../../src/__tests__/transport.test.ts"],"names":[],"mappings":"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"}
|