applesauce-wallet 0.0.0-next-20250313084132 → 0.0.0-next-20250313104750
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/helpers/__tests__/animated-qr.test.d.ts +1 -0
- package/dist/helpers/__tests__/animated-qr.test.js +44 -0
- package/dist/helpers/animated-qr.d.ts +30 -0
- package/dist/helpers/animated-qr.js +77 -0
- package/dist/helpers/index.d.ts +1 -0
- package/dist/helpers/index.js +1 -0
- package/package.json +7 -5
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { getDecodedToken } from "@cashu/cashu-ts";
|
|
3
|
+
import { lastValueFrom, take, timer } from "rxjs";
|
|
4
|
+
import { subscribeSpyTo } from "@hirez_io/observer-spy";
|
|
5
|
+
import { receiveAnimated, sendAnimated } from "../animated-qr.js";
|
|
6
|
+
const tokenStr = "cashuBo2FteBtodHRwczovL3Rlc3RudXQuY2FzaHUuc3BhY2VhdWNzYXRhdIGiYWlIAJofKTJT5B5hcIOkYWEQYXN4QDA2M2VlYjgwZDZjODM4NWU4NzYwODVhN2E4NzdkOTkyY2U4N2QwNTRmY2RjYzNiODMwMzhjOWY3MmNmMDY1ZGVhY1ghAynsxZ-OuZfZDcqYTLPYfCqHO7jkGjn97aolgtSYhYykYWSjYWVYIGbp_9B9aztSlZxz7g6Tqx5M_1PuFzJCVEMOVd8XAF8wYXNYINTOU77ODcj28v04pq7ektdf6sq2XuxvMjVE0wK6jFolYXJYIM_gZnUGT5jDOyZiQ-2vG9zYnuWaY8vPoWGe_3sXvrvbpGFhBGFzeEA0MWMwZDk1YTU5ZjkxNTdlYTc5NTJlNGFlMzYzYTI3NTMxNTllNmQ1NGJiNzExMTg5ZDk5YjU1MmYzYjIzZTJiYWNYIQM-49gen_1nPchxbaAiKprVr78VmMRVpHH_Tu9P8TO5mGFko2FlWCB9j7rlpdBH_m7tNYnLpzPhn-nGmS1CcbUfnPzjxy6G92FzWCDdsby7fGM5324T5UEoV858YWzZ9MCY59KgKP362fJDfmFyWCDL73v4FRo7iMe83bfMuEy3RJPtC1Vr1jdOpw2-x-7EAaRhYQFhc3hANjRhZDI3NmExOGNmNDhiMDZmYjdiMGYwOWFiMTU4ZTA0ZmM0NmIxYzA4YzMyNjJlODUxNzZkYTMzMTgyYzQ3YWFjWCECMDpCbNbrgA9FcQEIYxobU7ik_pTl8sByPqHDmkY4azxhZKNhZVggqHGaff9M270EU8LGxRpG_G4rn2bMgjyk3hFFg78ZXRVhc1ggP6DsNsWykwKE94yZF23gpCyapcoqh6DDZdVu0lKn2Z5hclggmPKig-lObsuxi_1XCm7_Y_tqaCcqEDz8eCwVhJ8gq9M";
|
|
7
|
+
const token = getDecodedToken(tokenStr);
|
|
8
|
+
describe("sendAnimated", () => {
|
|
9
|
+
it("should loop", async () => {
|
|
10
|
+
const qr$ = sendAnimated(token, { interval: 0 });
|
|
11
|
+
const spy = subscribeSpyTo(qr$);
|
|
12
|
+
// wait 100ms
|
|
13
|
+
await lastValueFrom(timer(100));
|
|
14
|
+
// should not have competed
|
|
15
|
+
expect(spy.receivedComplete()).toBeFalsy();
|
|
16
|
+
spy.unsubscribe();
|
|
17
|
+
});
|
|
18
|
+
it("should emit parts", async () => {
|
|
19
|
+
const qr$ = sendAnimated(token, { interval: 0 }).pipe(take(6));
|
|
20
|
+
const spy = subscribeSpyTo(qr$);
|
|
21
|
+
// wait 100ms
|
|
22
|
+
await lastValueFrom(qr$);
|
|
23
|
+
// should not have competed
|
|
24
|
+
expect(spy.getValues()).toEqual(Array(6)
|
|
25
|
+
.fill(0)
|
|
26
|
+
.map((_, i) => expect.stringContaining(`ur:bytes/${i + 1}-6/`)));
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
describe("receiveAnimated", () => {
|
|
30
|
+
it("should decode animated qr", async () => {
|
|
31
|
+
const qr$ = sendAnimated(token, { interval: 0 }).pipe(receiveAnimated);
|
|
32
|
+
const spy = subscribeSpyTo(qr$);
|
|
33
|
+
await lastValueFrom(qr$);
|
|
34
|
+
expect(spy.getValues()).toEqual([
|
|
35
|
+
expect.any(Number),
|
|
36
|
+
expect.any(Number),
|
|
37
|
+
expect.any(Number),
|
|
38
|
+
expect.any(Number),
|
|
39
|
+
expect.any(Number),
|
|
40
|
+
expect.any(Number),
|
|
41
|
+
token,
|
|
42
|
+
]);
|
|
43
|
+
});
|
|
44
|
+
});
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { Token } from "@cashu/cashu-ts";
|
|
2
|
+
import { Observable } from "rxjs";
|
|
3
|
+
/** Preset speeds for the animated qr code */
|
|
4
|
+
export declare const ANIMATED_QR_INTERVAL: {
|
|
5
|
+
SLOW: number;
|
|
6
|
+
MEDIUM: number;
|
|
7
|
+
FAST: number;
|
|
8
|
+
};
|
|
9
|
+
/** Presets for fragment length for animated qr code */
|
|
10
|
+
export declare const ANIMATED_QR_FRAGMENTS: {
|
|
11
|
+
SHORT: number;
|
|
12
|
+
MEDIUM: number;
|
|
13
|
+
LONG: number;
|
|
14
|
+
};
|
|
15
|
+
export type SendAnimatedOptions = {
|
|
16
|
+
/**
|
|
17
|
+
* The interval between the parts ( 150 - 500 )
|
|
18
|
+
* @default 150
|
|
19
|
+
*/
|
|
20
|
+
interval?: number;
|
|
21
|
+
/**
|
|
22
|
+
* max fragment length ( 50 - 200 )
|
|
23
|
+
* @default 100
|
|
24
|
+
*/
|
|
25
|
+
fragmentLength?: number;
|
|
26
|
+
};
|
|
27
|
+
/** Creates an observable that iterates through a multi-part animated qr code */
|
|
28
|
+
export declare function sendAnimated(token: Token | string, options?: SendAnimatedOptions): Observable<string>;
|
|
29
|
+
/** Creates an observable that completes with decoded token */
|
|
30
|
+
export declare function receiveAnimated(input: Observable<string>): Observable<Token | number>;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { getDecodedToken, getEncodedTokenV4 } from "@cashu/cashu-ts";
|
|
2
|
+
import { UR, URDecoder, UREncoder } from "@gandlaf21/bc-ur/dist/lib/es6/index.js";
|
|
3
|
+
import { defer, filter, interval, map, Observable, shareReplay } from "rxjs";
|
|
4
|
+
/** Preset speeds for the animated qr code */
|
|
5
|
+
export const ANIMATED_QR_INTERVAL = {
|
|
6
|
+
SLOW: 500,
|
|
7
|
+
MEDIUM: 250,
|
|
8
|
+
FAST: 150,
|
|
9
|
+
};
|
|
10
|
+
/** Presets for fragment length for animated qr code */
|
|
11
|
+
export const ANIMATED_QR_FRAGMENTS = {
|
|
12
|
+
SHORT: 50,
|
|
13
|
+
MEDIUM: 100,
|
|
14
|
+
LONG: 150,
|
|
15
|
+
};
|
|
16
|
+
/** Creates an observable that iterates through a multi-part animated qr code */
|
|
17
|
+
export function sendAnimated(token, options) {
|
|
18
|
+
// start the stream as soon as there is subscriber
|
|
19
|
+
return defer(() => {
|
|
20
|
+
let encoded = typeof token === "string" ? token : getEncodedTokenV4(token);
|
|
21
|
+
let buffer = Buffer.from(encoded);
|
|
22
|
+
let ur = UR.fromBuffer(buffer);
|
|
23
|
+
let encoder = new UREncoder(ur, options?.fragmentLength ?? 100, 0);
|
|
24
|
+
return interval(options?.interval ?? ANIMATED_QR_INTERVAL.FAST).pipe(map(() => encoder.nextPart()));
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
/** An operator that decodes UR, emits progress percent and completes with final result or error */
|
|
28
|
+
function urDecoder() {
|
|
29
|
+
return (source) => new Observable((observer) => {
|
|
30
|
+
const decoder = new URDecoder();
|
|
31
|
+
return source.subscribe((part) => {
|
|
32
|
+
decoder.receivePart(part);
|
|
33
|
+
if (decoder.isComplete() && decoder.isSuccess()) {
|
|
34
|
+
// emit progress
|
|
35
|
+
const progress = decoder.estimatedPercentComplete();
|
|
36
|
+
observer.next(progress);
|
|
37
|
+
// emit result
|
|
38
|
+
const ur = decoder.resultUR();
|
|
39
|
+
const decoded = ur.decodeCBOR();
|
|
40
|
+
const utf8 = new TextDecoder();
|
|
41
|
+
const tokenStr = utf8.decode(decoded);
|
|
42
|
+
observer.next(tokenStr);
|
|
43
|
+
// complete
|
|
44
|
+
observer.complete();
|
|
45
|
+
}
|
|
46
|
+
else if (decoder.isError()) {
|
|
47
|
+
// emit error
|
|
48
|
+
const reason = decoder.resultError();
|
|
49
|
+
observer.error(new Error(reason));
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
// emit progress
|
|
53
|
+
const progress = decoder.estimatedPercentComplete();
|
|
54
|
+
observer.next(progress);
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
/** Creates an observable that completes with decoded token */
|
|
60
|
+
export function receiveAnimated(input) {
|
|
61
|
+
return input.pipe(
|
|
62
|
+
// convert to lower case
|
|
63
|
+
map((str) => str.toLowerCase()),
|
|
64
|
+
// filter out non UR parts
|
|
65
|
+
filter((str) => str.startsWith("ur:")),
|
|
66
|
+
// decode UR and complete
|
|
67
|
+
urDecoder(),
|
|
68
|
+
// decode cashu token
|
|
69
|
+
map((part) => {
|
|
70
|
+
if (typeof part === "string")
|
|
71
|
+
return getDecodedToken(part);
|
|
72
|
+
else
|
|
73
|
+
return part;
|
|
74
|
+
}),
|
|
75
|
+
// only run one decoder
|
|
76
|
+
shareReplay(1));
|
|
77
|
+
}
|
package/dist/helpers/index.d.ts
CHANGED
package/dist/helpers/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "applesauce-wallet",
|
|
3
|
-
"version": "0.0.0-next-
|
|
3
|
+
"version": "0.0.0-next-20250313104750",
|
|
4
4
|
"description": "",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -78,17 +78,19 @@
|
|
|
78
78
|
},
|
|
79
79
|
"dependencies": {
|
|
80
80
|
"@cashu/cashu-ts": "2.0.0-rc1",
|
|
81
|
+
"@gandlaf21/bc-ur": "^1.1.12",
|
|
81
82
|
"@noble/hashes": "^1.7.1",
|
|
82
|
-
"applesauce-actions": "0.0.0-next-
|
|
83
|
-
"applesauce-core": "0.0.0-next-
|
|
84
|
-
"applesauce-factory": "0.0.0-next-
|
|
83
|
+
"applesauce-actions": "0.0.0-next-20250313104750",
|
|
84
|
+
"applesauce-core": "0.0.0-next-20250313104750",
|
|
85
|
+
"applesauce-factory": "0.0.0-next-20250313104750",
|
|
86
|
+
"buffer": "^6.0.3",
|
|
85
87
|
"nostr-tools": "^2.10.4",
|
|
86
88
|
"rxjs": "^7.8.1"
|
|
87
89
|
},
|
|
88
90
|
"devDependencies": {
|
|
89
91
|
"@hirez_io/observer-spy": "^2.2.0",
|
|
90
92
|
"@types/debug": "^4.1.12",
|
|
91
|
-
"applesauce-signers": "0.0.0-next-
|
|
93
|
+
"applesauce-signers": "0.0.0-next-20250313104750",
|
|
92
94
|
"typescript": "^5.7.3",
|
|
93
95
|
"vitest": "^3.0.5"
|
|
94
96
|
},
|