@freqhole/api-client 0.1.30

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/auth-test.html +651 -0
  2. package/package.json +42 -0
  3. package/src/AdminClient.ts +154 -0
  4. package/src/CharnelLocalTransport.ts +598 -0
  5. package/src/CharnelTransport.ts +616 -0
  6. package/src/FreqholeClient.ts +261 -0
  7. package/src/WasmTransport.ts +1077 -0
  8. package/src/automerge/IrohNetworkAdapter.ts +727 -0
  9. package/src/automerge/index.ts +14 -0
  10. package/src/codegen/admin_commands.ts +61 -0
  11. package/src/codegen/routes.ts +213 -0
  12. package/src/codegen/schema.ts +6721 -0
  13. package/src/domains/admin.ts +97 -0
  14. package/src/domains/app.ts +98 -0
  15. package/src/domains/auth.ts +191 -0
  16. package/src/domains/favorites.types.ts +52 -0
  17. package/src/domains/jobs.ts +48 -0
  18. package/src/domains/music.ts +1935 -0
  19. package/src/domains/types.ts +19 -0
  20. package/src/domains/upload.ts +214 -0
  21. package/src/index.ts +48 -0
  22. package/src/permissions.ts +468 -0
  23. package/src/playlistz/convert.ts +267 -0
  24. package/src/playlistz/index.ts +6 -0
  25. package/src/playlistz/mutations.ts +181 -0
  26. package/src/playlistz/protocol.ts +222 -0
  27. package/src/playlistz/schema.ts +138 -0
  28. package/src/playlistz/shareLink.ts +76 -0
  29. package/src/storage/blobs.ts +272 -0
  30. package/src/storage/identity.ts +190 -0
  31. package/src/storage/index.ts +5 -0
  32. package/src/storage/webLocks.ts +92 -0
  33. package/src/test/automerge.ts +796 -0
  34. package/src/test/coverage.ts +105 -0
  35. package/src/test/fixtures.ts +460 -0
  36. package/src/test/integration.ts +320 -0
  37. package/src/test/playlistz.ts +631 -0
  38. package/src/test/stateful.ts +561 -0
  39. package/src/test/storage.ts +443 -0
  40. package/src/test.ts +75 -0
  41. package/src/transport.ts +391 -0
  42. package/src/utils.ts +79 -0
  43. package/src/webauthn.ts +228 -0
  44. package/tsconfig.json +22 -0
  45. package/vite.config.js +9 -0
@@ -0,0 +1,92 @@
1
+ // web locks leader election for the iroh node.
2
+ // only one tab per origin runs the midden node at a time.
3
+
4
+ /** lock name used for iroh node leader election */
5
+ export const LOCK_NAME = "freqhole-iroh-node";
6
+
7
+ export interface AcquireNodeLeadershipOpts {
8
+ /** called when this tab becomes the node leader */
9
+ onAcquired: () => void | Promise<void>;
10
+ /** called with state transitions; optional */
11
+ onStateChange?: (state: "leader" | "waiting" | "unsupported") => void;
12
+ }
13
+
14
+ /**
15
+ * attempt to acquire exclusive leadership of the iroh node.
16
+ *
17
+ * behavior:
18
+ * - tries ifAvailable first; if the lock is free, holds it and calls onAcquired
19
+ * - if already held, reports "waiting" and queues a normal request with an AbortController
20
+ * so leadership transfers when the current leader tab closes
21
+ * - if navigator.locks is absent (e.g. non-secure context), reports "unsupported"
22
+ * and calls onAcquired immediately (single-tab fallback)
23
+ *
24
+ * returns a cancel/release function; call it to give up leadership or cancel the wait.
25
+ */
26
+ export function acquireNodeLeadership(
27
+ opts: AcquireNodeLeadershipOpts,
28
+ ): () => void {
29
+ const { onAcquired, onStateChange } = opts;
30
+
31
+ // single-tab fallback for environments without Web Locks
32
+ if (
33
+ typeof navigator === "undefined" ||
34
+ !navigator.locks
35
+ ) {
36
+ onStateChange?.("unsupported");
37
+ void Promise.resolve().then(() => onAcquired());
38
+ return () => {};
39
+ }
40
+
41
+ let released = false;
42
+ // resolving this promise releases the held lock
43
+ let releaseLock: (() => void) | null = null;
44
+ // used to cancel the waiting queue request
45
+ const waitAbort = new AbortController();
46
+
47
+ function cancel() {
48
+ released = true;
49
+ waitAbort.abort();
50
+ releaseLock?.();
51
+ }
52
+
53
+ // try to grab the lock immediately without queuing
54
+ navigator.locks.request(
55
+ LOCK_NAME,
56
+ { ifAvailable: true },
57
+ async (lock: Lock | null) => {
58
+ if (released) return;
59
+
60
+ if (lock !== null) {
61
+ // we are now the leader - call onAcquired, then hold the lock until released
62
+ onStateChange?.("leader");
63
+ await onAcquired();
64
+ await new Promise<void>((resolve) => {
65
+ releaseLock = resolve;
66
+ });
67
+ } else {
68
+ // another tab holds the lock - queue up for leadership
69
+ onStateChange?.("waiting");
70
+
71
+ try {
72
+ await navigator.locks.request(
73
+ LOCK_NAME,
74
+ { signal: waitAbort.signal },
75
+ async () => {
76
+ if (released) return;
77
+ onStateChange?.("leader");
78
+ await onAcquired();
79
+ await new Promise<void>((resolve) => {
80
+ releaseLock = resolve;
81
+ });
82
+ },
83
+ );
84
+ } catch {
85
+ // aborted by cancel() or context destroyed - not an error
86
+ }
87
+ }
88
+ },
89
+ );
90
+
91
+ return cancel;
92
+ }