@freqhole/reliquary 0.0.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/automerge/acl-filtering.d.ts +87 -0
- package/dist/automerge/acl-filtering.d.ts.map +1 -0
- package/dist/automerge/acl-filtering.js +138 -0
- package/dist/automerge/acl-filtering.test.d.ts +2 -0
- package/dist/automerge/acl-filtering.test.d.ts.map +1 -0
- package/dist/automerge/acl-filtering.test.js +302 -0
- package/dist/automerge/index.d.ts +5 -0
- package/dist/automerge/index.d.ts.map +1 -0
- package/dist/automerge/index.js +7 -0
- package/dist/automerge/iroh-network-adapter.d.ts +178 -0
- package/dist/automerge/iroh-network-adapter.d.ts.map +1 -0
- package/dist/automerge/iroh-network-adapter.js +691 -0
- package/dist/automerge/iroh-network-adapter.test.d.ts +2 -0
- package/dist/automerge/iroh-network-adapter.test.d.ts.map +1 -0
- package/dist/automerge/iroh-network-adapter.test.js +818 -0
- package/dist/automerge/types.d.ts +66 -0
- package/dist/automerge/types.d.ts.map +1 -0
- package/dist/automerge/types.js +10 -0
- package/dist/blobs/bytes-backend.d.ts +41 -0
- package/dist/blobs/bytes-backend.d.ts.map +1 -0
- package/dist/blobs/bytes-backend.js +252 -0
- package/dist/blobs/bytes-backend.test.d.ts +2 -0
- package/dist/blobs/bytes-backend.test.d.ts.map +1 -0
- package/dist/blobs/bytes-backend.test.js +241 -0
- package/dist/blobs/db.d.ts +8 -0
- package/dist/blobs/db.d.ts.map +1 -0
- package/dist/blobs/db.js +141 -0
- package/dist/blobs/db.test.d.ts +2 -0
- package/dist/blobs/db.test.d.ts.map +1 -0
- package/dist/blobs/db.test.js +120 -0
- package/dist/blobs/index.d.ts +5 -0
- package/dist/blobs/index.d.ts.map +1 -0
- package/dist/blobs/index.js +2 -0
- package/dist/blobs/store.d.ts +47 -0
- package/dist/blobs/store.d.ts.map +1 -0
- package/dist/blobs/store.js +263 -0
- package/dist/blobs/store.test.d.ts +2 -0
- package/dist/blobs/store.test.d.ts.map +1 -0
- package/dist/blobs/store.test.js +377 -0
- package/dist/blobs/types.d.ts +55 -0
- package/dist/blobs/types.d.ts.map +1 -0
- package/dist/blobs/types.js +8 -0
- package/dist/ensure/client.d.ts +19 -0
- package/dist/ensure/client.d.ts.map +1 -0
- package/dist/ensure/client.js +74 -0
- package/dist/ensure/client.test.d.ts +2 -0
- package/dist/ensure/client.test.d.ts.map +1 -0
- package/dist/ensure/client.test.js +133 -0
- package/dist/ensure/index.d.ts +4 -0
- package/dist/ensure/index.d.ts.map +1 -0
- package/dist/ensure/index.js +4 -0
- package/dist/ensure/responder.d.ts +20 -0
- package/dist/ensure/responder.d.ts.map +1 -0
- package/dist/ensure/responder.js +100 -0
- package/dist/ensure/responder.test.d.ts +2 -0
- package/dist/ensure/responder.test.d.ts.map +1 -0
- package/dist/ensure/responder.test.js +191 -0
- package/dist/ensure/types.d.ts +19 -0
- package/dist/ensure/types.d.ts.map +1 -0
- package/dist/ensure/types.js +4 -0
- package/dist/ensure/types.test.d.ts +2 -0
- package/dist/ensure/types.test.d.ts.map +1 -0
- package/dist/ensure/types.test.js +81 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/index.test.d.ts +2 -0
- package/dist/index.test.d.ts.map +1 -0
- package/dist/index.test.js +7 -0
- package/dist/solid/blob-url.d.ts +10 -0
- package/dist/solid/blob-url.d.ts.map +1 -0
- package/dist/solid/blob-url.js +34 -0
- package/dist/solid/blob-url.test.d.ts +2 -0
- package/dist/solid/blob-url.test.d.ts.map +1 -0
- package/dist/solid/blob-url.test.js +62 -0
- package/dist/solid/connection-summary.d.ts +25 -0
- package/dist/solid/connection-summary.d.ts.map +1 -0
- package/dist/solid/connection-summary.js +29 -0
- package/dist/solid/connection-summary.test.d.ts +2 -0
- package/dist/solid/connection-summary.test.d.ts.map +1 -0
- package/dist/solid/connection-summary.test.js +57 -0
- package/dist/solid/doc-store.d.ts +29 -0
- package/dist/solid/doc-store.d.ts.map +1 -0
- package/dist/solid/doc-store.js +79 -0
- package/dist/solid/doc-store.test.d.ts +2 -0
- package/dist/solid/doc-store.test.d.ts.map +1 -0
- package/dist/solid/doc-store.test.js +234 -0
- package/dist/solid/index.d.ts +4 -0
- package/dist/solid/index.d.ts.map +1 -0
- package/dist/solid/index.js +9 -0
- package/dist/solid/transfer-progress.d.ts +21 -0
- package/dist/solid/transfer-progress.d.ts.map +1 -0
- package/dist/solid/transfer-progress.js +32 -0
- package/dist/solid/transfer-progress.test.d.ts +2 -0
- package/dist/solid/transfer-progress.test.d.ts.map +1 -0
- package/dist/solid/transfer-progress.test.js +76 -0
- package/dist/testing/blob-fixtures.d.ts +19 -0
- package/dist/testing/blob-fixtures.d.ts.map +1 -0
- package/dist/testing/blob-fixtures.js +46 -0
- package/dist/testing/blob-fixtures.test.d.ts +2 -0
- package/dist/testing/blob-fixtures.test.d.ts.map +1 -0
- package/dist/testing/blob-fixtures.test.js +46 -0
- package/dist/testing/idb-harness.d.ts +10 -0
- package/dist/testing/idb-harness.d.ts.map +1 -0
- package/dist/testing/idb-harness.js +17 -0
- package/dist/testing/idb-harness.test.d.ts +2 -0
- package/dist/testing/idb-harness.test.d.ts.map +1 -0
- package/dist/testing/idb-harness.test.js +48 -0
- package/dist/testing/index.d.ts +7 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js +11 -0
- package/dist/testing/media.d.ts +15 -0
- package/dist/testing/media.d.ts.map +1 -0
- package/dist/testing/media.js +37 -0
- package/dist/testing/media.test.d.ts +2 -0
- package/dist/testing/media.test.d.ts.map +1 -0
- package/dist/testing/media.test.js +47 -0
- package/dist/testing/mock-blob-fetch.d.ts +33 -0
- package/dist/testing/mock-blob-fetch.d.ts.map +1 -0
- package/dist/testing/mock-blob-fetch.js +39 -0
- package/dist/testing/mock-blob-fetch.test.d.ts +2 -0
- package/dist/testing/mock-blob-fetch.test.d.ts.map +1 -0
- package/dist/testing/mock-blob-fetch.test.js +65 -0
- package/dist/testing/mock-node.d.ts +36 -0
- package/dist/testing/mock-node.d.ts.map +1 -0
- package/dist/testing/mock-node.js +97 -0
- package/dist/testing/mock-node.test.d.ts +2 -0
- package/dist/testing/mock-node.test.d.ts.map +1 -0
- package/dist/testing/mock-node.test.js +66 -0
- package/dist/testing/streams.d.ts +68 -0
- package/dist/testing/streams.d.ts.map +1 -0
- package/dist/testing/streams.js +99 -0
- package/dist/testing/streams.test.d.ts +2 -0
- package/dist/testing/streams.test.d.ts.map +1 -0
- package/dist/testing/streams.test.js +82 -0
- package/dist/transfer/cancellation.d.ts +8 -0
- package/dist/transfer/cancellation.d.ts.map +1 -0
- package/dist/transfer/cancellation.js +19 -0
- package/dist/transfer/index.d.ts +8 -0
- package/dist/transfer/index.d.ts.map +1 -0
- package/dist/transfer/index.js +4 -0
- package/dist/transfer/prefetch.d.ts +38 -0
- package/dist/transfer/prefetch.d.ts.map +1 -0
- package/dist/transfer/prefetch.js +61 -0
- package/dist/transfer/prefetch.test.d.ts +2 -0
- package/dist/transfer/prefetch.test.d.ts.map +1 -0
- package/dist/transfer/prefetch.test.js +136 -0
- package/dist/transfer/serve.d.ts +66 -0
- package/dist/transfer/serve.d.ts.map +1 -0
- package/dist/transfer/serve.js +126 -0
- package/dist/transfer/serve.test.d.ts +2 -0
- package/dist/transfer/serve.test.d.ts.map +1 -0
- package/dist/transfer/serve.test.js +101 -0
- package/dist/transfer/snatch.d.ts +39 -0
- package/dist/transfer/snatch.d.ts.map +1 -0
- package/dist/transfer/snatch.js +289 -0
- package/dist/transfer/snatch.test.d.ts +2 -0
- package/dist/transfer/snatch.test.d.ts.map +1 -0
- package/dist/transfer/snatch.test.js +337 -0
- package/dist/transfer/types.d.ts +85 -0
- package/dist/transfer/types.d.ts.map +1 -0
- package/dist/transfer/types.js +15 -0
- package/dist/utils/hash.d.ts +13 -0
- package/dist/utils/hash.d.ts.map +1 -0
- package/dist/utils/hash.js +31 -0
- package/dist/utils/hash.test.d.ts +2 -0
- package/dist/utils/hash.test.d.ts.map +1 -0
- package/dist/utils/hash.test.js +56 -0
- package/dist/utils/image-utils.d.ts +28 -0
- package/dist/utils/image-utils.d.ts.map +1 -0
- package/dist/utils/image-utils.js +113 -0
- package/dist/utils/image-utils.test.d.ts +2 -0
- package/dist/utils/image-utils.test.d.ts.map +1 -0
- package/dist/utils/image-utils.test.js +181 -0
- package/dist/utils/index.d.ts +4 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +3 -0
- package/dist/utils/log.d.ts +22 -0
- package/dist/utils/log.d.ts.map +1 -0
- package/dist/utils/log.js +100 -0
- package/dist/utils/log.test.d.ts +2 -0
- package/dist/utils/log.test.d.ts.map +1 -0
- package/dist/utils/log.test.js +113 -0
- package/dist/worker/blob-worker-client.d.ts +118 -0
- package/dist/worker/blob-worker-client.d.ts.map +1 -0
- package/dist/worker/blob-worker-client.js +361 -0
- package/dist/worker/blob-worker-client.test.d.ts +2 -0
- package/dist/worker/blob-worker-client.test.d.ts.map +1 -0
- package/dist/worker/blob-worker-client.test.js +137 -0
- package/dist/worker/blob-worker-logic.d.ts +120 -0
- package/dist/worker/blob-worker-logic.d.ts.map +1 -0
- package/dist/worker/blob-worker-logic.js +368 -0
- package/dist/worker/blob-worker-logic.test.d.ts +2 -0
- package/dist/worker/blob-worker-logic.test.d.ts.map +1 -0
- package/dist/worker/blob-worker-logic.test.js +191 -0
- package/dist/worker/blob-worker.d.ts +21 -0
- package/dist/worker/blob-worker.d.ts.map +1 -0
- package/dist/worker/blob-worker.js +44 -0
- package/dist/worker/index.d.ts +12 -0
- package/dist/worker/index.d.ts.map +1 -0
- package/dist/worker/index.js +6 -0
- package/dist/worker/midden-blake3.d.ts +23 -0
- package/dist/worker/midden-blake3.d.ts.map +1 -0
- package/dist/worker/midden-blake3.js +47 -0
- package/dist/worker/midden-blake3.test.d.ts +2 -0
- package/dist/worker/midden-blake3.test.d.ts.map +1 -0
- package/dist/worker/midden-blake3.test.js +25 -0
- package/dist/worker/midden-not-bundled.stub.d.ts +1 -0
- package/dist/worker/midden-not-bundled.stub.d.ts.map +1 -0
- package/dist/worker/midden-not-bundled.stub.js +10 -0
- package/dist/worker/midden-stub.d.ts +10 -0
- package/dist/worker/midden-stub.d.ts.map +1 -0
- package/dist/worker/midden-stub.js +31 -0
- package/dist/worker/midden-stub.test.d.ts +2 -0
- package/dist/worker/midden-stub.test.d.ts.map +1 -0
- package/dist/worker/midden-stub.test.js +25 -0
- package/dist/worker/midden-worker-client.d.ts +103 -0
- package/dist/worker/midden-worker-client.d.ts.map +1 -0
- package/dist/worker/midden-worker-client.js +246 -0
- package/dist/worker/midden-worker-client.test.d.ts +2 -0
- package/dist/worker/midden-worker-client.test.d.ts.map +1 -0
- package/dist/worker/midden-worker-client.test.js +151 -0
- package/dist/worker/midden-worker-contract.d.ts +87 -0
- package/dist/worker/midden-worker-contract.d.ts.map +1 -0
- package/dist/worker/midden-worker-contract.js +30 -0
- package/package.json +90 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// a solid resource that turns a blob-fetching function into an object url,
|
|
2
|
+
// revoking the previous url whenever a new one replaces it and revoking
|
|
3
|
+
// the current one when the owning computation is disposed.
|
|
4
|
+
//
|
|
5
|
+
// the blob-fetching function is supplied by the caller - this module has
|
|
6
|
+
// no dependency on `./blobs` or any particular blob store, so a consumer
|
|
7
|
+
// wires up its own resolver (a local blob store lookup, a network fetch,
|
|
8
|
+
// whatever it needs).
|
|
9
|
+
import { createEffect, createResource, onCleanup } from "solid-js";
|
|
10
|
+
/**
|
|
11
|
+
* fetches a blob via `getBlob` and exposes its object url as a solid
|
|
12
|
+
* resource. `null` while loading, on a null blob, or after an error.
|
|
13
|
+
* every previous url is revoked as soon as it's superseded (by a refetch)
|
|
14
|
+
* or the resource is disposed - a caller never needs to manage
|
|
15
|
+
* `URL.revokeObjectURL` itself.
|
|
16
|
+
*/
|
|
17
|
+
export function createBlobUrl(getBlob) {
|
|
18
|
+
const [url] = createResource(async () => {
|
|
19
|
+
const blob = await getBlob();
|
|
20
|
+
return blob ? URL.createObjectURL(blob) : null;
|
|
21
|
+
});
|
|
22
|
+
let previous = null;
|
|
23
|
+
createEffect(() => {
|
|
24
|
+
const current = url() ?? null;
|
|
25
|
+
if (previous && previous !== current)
|
|
26
|
+
URL.revokeObjectURL(previous);
|
|
27
|
+
previous = current;
|
|
28
|
+
});
|
|
29
|
+
onCleanup(() => {
|
|
30
|
+
if (previous)
|
|
31
|
+
URL.revokeObjectURL(previous);
|
|
32
|
+
});
|
|
33
|
+
return url;
|
|
34
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"blob-url.test.d.ts","sourceRoot":"","sources":["../../src/solid/blob-url.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
// @vitest-environment happy-dom
|
|
2
|
+
import { createRoot } from "solid-js";
|
|
3
|
+
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
4
|
+
import { createBlobUrl } from "./blob-url.js";
|
|
5
|
+
afterEach(() => {
|
|
6
|
+
vi.unstubAllGlobals();
|
|
7
|
+
});
|
|
8
|
+
function stubObjectUrl() {
|
|
9
|
+
let counter = 0;
|
|
10
|
+
const created = [];
|
|
11
|
+
const revoked = [];
|
|
12
|
+
vi.stubGlobal("URL", {
|
|
13
|
+
createObjectURL: vi.fn(() => {
|
|
14
|
+
const url = `blob:mock-${++counter}`;
|
|
15
|
+
created.push(url);
|
|
16
|
+
return url;
|
|
17
|
+
}),
|
|
18
|
+
revokeObjectURL: vi.fn((url) => {
|
|
19
|
+
revoked.push(url);
|
|
20
|
+
}),
|
|
21
|
+
});
|
|
22
|
+
return { created, revoked };
|
|
23
|
+
}
|
|
24
|
+
describe("createBlobUrl", () => {
|
|
25
|
+
it("resolves to an object url for a non-null blob", async () => {
|
|
26
|
+
const { created } = stubObjectUrl();
|
|
27
|
+
let dispose = () => { };
|
|
28
|
+
let url;
|
|
29
|
+
createRoot((d) => {
|
|
30
|
+
dispose = d;
|
|
31
|
+
url = createBlobUrl(async () => new Blob(["hello"]));
|
|
32
|
+
});
|
|
33
|
+
await vi.waitFor(() => expect(url.loading).toBe(false));
|
|
34
|
+
expect(url()).toBe(created[0]);
|
|
35
|
+
dispose();
|
|
36
|
+
});
|
|
37
|
+
it("resolves to null for a null blob", async () => {
|
|
38
|
+
stubObjectUrl();
|
|
39
|
+
let dispose = () => { };
|
|
40
|
+
let resource;
|
|
41
|
+
createRoot((d) => {
|
|
42
|
+
dispose = d;
|
|
43
|
+
resource = createBlobUrl(async () => null);
|
|
44
|
+
});
|
|
45
|
+
await vi.waitFor(() => expect(resource.loading).toBe(false));
|
|
46
|
+
expect(resource()).toBeNull();
|
|
47
|
+
dispose();
|
|
48
|
+
});
|
|
49
|
+
it("revokes the object url once the owning root is disposed", async () => {
|
|
50
|
+
const { created, revoked } = stubObjectUrl();
|
|
51
|
+
let dispose = () => { };
|
|
52
|
+
let url;
|
|
53
|
+
createRoot((d) => {
|
|
54
|
+
dispose = d;
|
|
55
|
+
url = createBlobUrl(async () => new Blob(["hello"]));
|
|
56
|
+
});
|
|
57
|
+
await vi.waitFor(() => expect(url.loading).toBe(false));
|
|
58
|
+
expect(revoked).not.toContain(created[0]);
|
|
59
|
+
dispose();
|
|
60
|
+
expect(revoked).toContain(created[0]);
|
|
61
|
+
});
|
|
62
|
+
});
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { type Accessor } from "solid-js";
|
|
2
|
+
/** counts describing a network adapter's current peer connections. */
|
|
3
|
+
export interface ConnectionSummaryLike {
|
|
4
|
+
/** number of peers actively connected. */
|
|
5
|
+
connected: number;
|
|
6
|
+
/** number of peers currently reconnecting (in backoff). */
|
|
7
|
+
reconnecting: number;
|
|
8
|
+
/** number of peers whose reconnection attempts gave up. */
|
|
9
|
+
failed: number;
|
|
10
|
+
}
|
|
11
|
+
export interface CreateConnectionSummaryOptions {
|
|
12
|
+
/** how often to re-read the source, in ms. default 2000. */
|
|
13
|
+
pollIntervalMs?: number;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* polls `getSource()` on an interval and exposes the result as a reactive
|
|
17
|
+
* signal. `getSource` may return `null` (adapter not constructed/ready
|
|
18
|
+
* yet) - the summary then stays at its last known value. the poll is
|
|
19
|
+
* cleared automatically via `onCleanup` when the owning computation is
|
|
20
|
+
* disposed.
|
|
21
|
+
*/
|
|
22
|
+
export declare function createConnectionSummary(getSource: () => {
|
|
23
|
+
getConnectionSummary(): ConnectionSummaryLike;
|
|
24
|
+
} | null, options?: CreateConnectionSummaryOptions): Accessor<ConnectionSummaryLike>;
|
|
25
|
+
//# sourceMappingURL=connection-summary.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connection-summary.d.ts","sourceRoot":"","sources":["../../src/solid/connection-summary.ts"],"names":[],"mappings":"AAOA,OAAO,EAA2B,KAAK,QAAQ,EAAE,MAAM,UAAU,CAAC;AAElE,sEAAsE;AACtE,MAAM,WAAW,qBAAqB;IACpC,0CAA0C;IAC1C,SAAS,EAAE,MAAM,CAAC;IAClB,2DAA2D;IAC3D,YAAY,EAAE,MAAM,CAAC;IACrB,2DAA2D;IAC3D,MAAM,EAAE,MAAM,CAAC;CAChB;AAID,MAAM,WAAW,8BAA8B;IAC7C,4DAA4D;IAC5D,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CACrC,SAAS,EAAE,MAAM;IAAE,oBAAoB,IAAI,qBAAqB,CAAA;CAAE,GAAG,IAAI,EACzE,OAAO,GAAE,8BAAmC,GAC3C,QAAQ,CAAC,qBAAqB,CAAC,CAejC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// a small reactive summary over a connection/transfer source's live state,
|
|
2
|
+
// refreshed on a fixed poll interval.
|
|
3
|
+
//
|
|
4
|
+
// the "source" is any object exposing a synchronous `getConnectionSummary()`
|
|
5
|
+
// - typically a network adapter - so this module has no dependency on any
|
|
6
|
+
// particular adapter implementation or transport.
|
|
7
|
+
import { createSignal, onCleanup } from "solid-js";
|
|
8
|
+
const EMPTY_SUMMARY = { connected: 0, reconnecting: 0, failed: 0 };
|
|
9
|
+
/**
|
|
10
|
+
* polls `getSource()` on an interval and exposes the result as a reactive
|
|
11
|
+
* signal. `getSource` may return `null` (adapter not constructed/ready
|
|
12
|
+
* yet) - the summary then stays at its last known value. the poll is
|
|
13
|
+
* cleared automatically via `onCleanup` when the owning computation is
|
|
14
|
+
* disposed.
|
|
15
|
+
*/
|
|
16
|
+
export function createConnectionSummary(getSource, options = {}) {
|
|
17
|
+
const [summary, setSummary] = createSignal(EMPTY_SUMMARY);
|
|
18
|
+
const pollIntervalMs = options.pollIntervalMs ?? 2000;
|
|
19
|
+
function refresh() {
|
|
20
|
+
const source = getSource();
|
|
21
|
+
if (!source)
|
|
22
|
+
return;
|
|
23
|
+
setSummary(source.getConnectionSummary());
|
|
24
|
+
}
|
|
25
|
+
refresh();
|
|
26
|
+
const timer = setInterval(refresh, pollIntervalMs);
|
|
27
|
+
onCleanup(() => clearInterval(timer));
|
|
28
|
+
return summary;
|
|
29
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connection-summary.test.d.ts","sourceRoot":"","sources":["../../src/solid/connection-summary.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { createRoot } from "solid-js";
|
|
2
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
3
|
+
import { createConnectionSummary } from "./connection-summary.js";
|
|
4
|
+
beforeEach(() => {
|
|
5
|
+
vi.useFakeTimers();
|
|
6
|
+
});
|
|
7
|
+
afterEach(() => {
|
|
8
|
+
vi.useRealTimers();
|
|
9
|
+
});
|
|
10
|
+
function makeSource(summary) {
|
|
11
|
+
return { getConnectionSummary: () => summary };
|
|
12
|
+
}
|
|
13
|
+
describe("createConnectionSummary", () => {
|
|
14
|
+
it("reads the source immediately on creation", () => {
|
|
15
|
+
createRoot((dispose) => {
|
|
16
|
+
const source = makeSource({ connected: 2, reconnecting: 1, failed: 0 });
|
|
17
|
+
const summary = createConnectionSummary(() => source);
|
|
18
|
+
expect(summary()).toEqual({ connected: 2, reconnecting: 1, failed: 0 });
|
|
19
|
+
dispose();
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
it("stays at the default when the source is not yet available", () => {
|
|
23
|
+
createRoot((dispose) => {
|
|
24
|
+
const summary = createConnectionSummary(() => null);
|
|
25
|
+
expect(summary()).toEqual({ connected: 0, reconnecting: 0, failed: 0 });
|
|
26
|
+
dispose();
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
it("re-polls the source on the configured interval", () => {
|
|
30
|
+
createRoot((dispose) => {
|
|
31
|
+
let current = { connected: 0, reconnecting: 0, failed: 0 };
|
|
32
|
+
const source = { getConnectionSummary: () => current };
|
|
33
|
+
const summary = createConnectionSummary(() => source, { pollIntervalMs: 1000 });
|
|
34
|
+
current = { connected: 3, reconnecting: 0, failed: 1 };
|
|
35
|
+
expect(summary()).toEqual({ connected: 0, reconnecting: 0, failed: 0 });
|
|
36
|
+
vi.advanceTimersByTime(1000);
|
|
37
|
+
expect(summary()).toEqual({ connected: 3, reconnecting: 0, failed: 1 });
|
|
38
|
+
dispose();
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
it("stops polling once the owning root is disposed", () => {
|
|
42
|
+
createRoot((dispose) => {
|
|
43
|
+
let calls = 0;
|
|
44
|
+
const source = {
|
|
45
|
+
getConnectionSummary: () => {
|
|
46
|
+
calls++;
|
|
47
|
+
return { connected: 0, reconnecting: 0, failed: 0 };
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
createConnectionSummary(() => source, { pollIntervalMs: 500 });
|
|
51
|
+
const callsAtDispose = calls;
|
|
52
|
+
dispose();
|
|
53
|
+
vi.advanceTimersByTime(5000);
|
|
54
|
+
expect(calls).toBe(callsAtDispose);
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
});
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { Accessor } from "solid-js";
|
|
2
|
+
import type { DocHandle } from "@automerge/automerge-repo";
|
|
3
|
+
export interface DocStore<T> {
|
|
4
|
+
doc: Accessor<T>;
|
|
5
|
+
loading: Accessor<boolean>;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* creates a reactive solid store backed by an automerge DocHandle. the doc
|
|
9
|
+
* accessor is always a caller-validated snapshot - corrupt or
|
|
10
|
+
* future-versioned peer data degrades to whatever the parse function
|
|
11
|
+
* returns for unknown/invalid input. loading becomes false once
|
|
12
|
+
* whenReady() resolves or rejects.
|
|
13
|
+
*
|
|
14
|
+
* @param handle - the automerge DocHandle to track
|
|
15
|
+
* @param parse - a function that converts unknown to T, providing defaults
|
|
16
|
+
* for missing/corrupt data (typically a zod schema's parse with a default
|
|
17
|
+
* fallback, but any unknown->T function works)
|
|
18
|
+
*/
|
|
19
|
+
export declare function createDocStore<T>(handle: DocHandle<unknown>, parse: (raw: unknown) => T): DocStore<T>;
|
|
20
|
+
/**
|
|
21
|
+
* convenience wrapper: apply a mutation to an automerge doc handle. the
|
|
22
|
+
* mutatorFn receives a mutable automerge draft and should modify it in
|
|
23
|
+
* place (safe to call shared mutation helpers from consuming apps).
|
|
24
|
+
*
|
|
25
|
+
* @param handle - the automerge DocHandle to mutate
|
|
26
|
+
* @param mutatorFn - a function that modifies the doc's draft in place
|
|
27
|
+
*/
|
|
28
|
+
export declare function changeDoc<T>(handle: DocHandle<T>, mutatorFn: (draft: T) => void): void;
|
|
29
|
+
//# sourceMappingURL=doc-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doc-store.d.ts","sourceRoot":"","sources":["../../src/solid/doc-store.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,KAAK,EACV,SAAS,EAGV,MAAM,2BAA2B,CAAC;AAEnC,MAAM,WAAW,QAAQ,CAAC,CAAC;IACzB,GAAG,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC;IACjB,OAAO,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAC;CAC5B;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAC9B,MAAM,EAAE,SAAS,CAAC,OAAO,CAAC,EAC1B,KAAK,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,CAAC,GACzB,QAAQ,CAAC,CAAC,CAAC,CA8Cb;AAED;;;;;;;GAOG;AACH,wBAAgB,SAAS,CAAC,CAAC,EACzB,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC,EACpB,SAAS,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,GAC5B,IAAI,CAEN"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
// a reactive solid adapter over an automerge DocHandle with
|
|
2
|
+
// caller-supplied parsing/validation. this is a generic pattern for
|
|
3
|
+
// building solid signals around any automerge document type - the caller
|
|
4
|
+
// provides the parse function (their own zod validator, or anything else
|
|
5
|
+
// that turns unknown into a safe default), and this module wires the
|
|
6
|
+
// automerge event lifecycle into solid reactivity.
|
|
7
|
+
//
|
|
8
|
+
// usage inside a solid component or reactive root:
|
|
9
|
+
//
|
|
10
|
+
// const handle = await repo.find<MyDoc>(url);
|
|
11
|
+
// const { doc, loading } = createDocStore(handle, parseMyDoc);
|
|
12
|
+
// // doc() is always a caller-parsed MyDoc (defaults on corrupt/missing)
|
|
13
|
+
// // loading() is true until the handle is ready or terminal
|
|
14
|
+
import { createSignal, onCleanup } from "solid-js";
|
|
15
|
+
/**
|
|
16
|
+
* creates a reactive solid store backed by an automerge DocHandle. the doc
|
|
17
|
+
* accessor is always a caller-validated snapshot - corrupt or
|
|
18
|
+
* future-versioned peer data degrades to whatever the parse function
|
|
19
|
+
* returns for unknown/invalid input. loading becomes false once
|
|
20
|
+
* whenReady() resolves or rejects.
|
|
21
|
+
*
|
|
22
|
+
* @param handle - the automerge DocHandle to track
|
|
23
|
+
* @param parse - a function that converts unknown to T, providing defaults
|
|
24
|
+
* for missing/corrupt data (typically a zod schema's parse with a default
|
|
25
|
+
* fallback, but any unknown->T function works)
|
|
26
|
+
*/
|
|
27
|
+
export function createDocStore(handle, parse) {
|
|
28
|
+
// try to read whatever the handle has now (may be undefined before ready)
|
|
29
|
+
let initialRaw;
|
|
30
|
+
try {
|
|
31
|
+
initialRaw = handle.doc();
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
initialRaw = undefined;
|
|
35
|
+
}
|
|
36
|
+
const [loading, setLoading] = createSignal(initialRaw === undefined);
|
|
37
|
+
const [doc, setDoc] = createSignal(parse(initialRaw), { equals: false });
|
|
38
|
+
// resolve handle readiness in the background
|
|
39
|
+
handle
|
|
40
|
+
.whenReady()
|
|
41
|
+
.then(() => {
|
|
42
|
+
let current;
|
|
43
|
+
try {
|
|
44
|
+
current = handle.doc();
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
current = undefined;
|
|
48
|
+
}
|
|
49
|
+
setLoading(false);
|
|
50
|
+
setDoc(() => parse(current));
|
|
51
|
+
})
|
|
52
|
+
.catch(() => {
|
|
53
|
+
setLoading(false);
|
|
54
|
+
});
|
|
55
|
+
const changeHandler = (payload) => {
|
|
56
|
+
setDoc(() => parse(payload.doc));
|
|
57
|
+
};
|
|
58
|
+
const deleteHandler = (_payload) => {
|
|
59
|
+
setLoading(false);
|
|
60
|
+
};
|
|
61
|
+
handle.on("change", changeHandler);
|
|
62
|
+
handle.on("delete", deleteHandler);
|
|
63
|
+
onCleanup(() => {
|
|
64
|
+
handle.off("change", changeHandler);
|
|
65
|
+
handle.off("delete", deleteHandler);
|
|
66
|
+
});
|
|
67
|
+
return { doc, loading };
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* convenience wrapper: apply a mutation to an automerge doc handle. the
|
|
71
|
+
* mutatorFn receives a mutable automerge draft and should modify it in
|
|
72
|
+
* place (safe to call shared mutation helpers from consuming apps).
|
|
73
|
+
*
|
|
74
|
+
* @param handle - the automerge DocHandle to mutate
|
|
75
|
+
* @param mutatorFn - a function that modifies the doc's draft in place
|
|
76
|
+
*/
|
|
77
|
+
export function changeDoc(handle, mutatorFn) {
|
|
78
|
+
handle.change(mutatorFn);
|
|
79
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doc-store.test.d.ts","sourceRoot":"","sources":["../../src/solid/doc-store.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
// tests for createDocStore solid adapter.
|
|
2
|
+
//
|
|
3
|
+
// uses a lightweight mock DocHandle (no automerge wasm needed) to test
|
|
4
|
+
// the solid reactivity layer independently of any specific doc schema.
|
|
5
|
+
import { describe, it, expect, vi } from "vitest";
|
|
6
|
+
import { createRoot } from "solid-js";
|
|
7
|
+
import { createDocStore, changeDoc } from "./doc-store.js";
|
|
8
|
+
function createMockHandle(initialDoc = undefined) {
|
|
9
|
+
let currentDoc = initialDoc;
|
|
10
|
+
const handlers = new Map();
|
|
11
|
+
let readyResolve;
|
|
12
|
+
let readyReject;
|
|
13
|
+
const readyPromise = new Promise((res, rej) => {
|
|
14
|
+
readyResolve = res;
|
|
15
|
+
readyReject = rej;
|
|
16
|
+
});
|
|
17
|
+
const offSpy = vi.fn();
|
|
18
|
+
const handle = {
|
|
19
|
+
doc: () => currentDoc,
|
|
20
|
+
whenReady: () => readyPromise,
|
|
21
|
+
on: (event, handler) => {
|
|
22
|
+
if (!handlers.has(event))
|
|
23
|
+
handlers.set(event, new Set());
|
|
24
|
+
handlers.get(event).add(handler);
|
|
25
|
+
},
|
|
26
|
+
off: offSpy,
|
|
27
|
+
change: vi.fn((fn) => {
|
|
28
|
+
fn(currentDoc);
|
|
29
|
+
const set = handlers.get("change");
|
|
30
|
+
if (set) {
|
|
31
|
+
const payload = {
|
|
32
|
+
handle: handle,
|
|
33
|
+
doc: currentDoc,
|
|
34
|
+
patches: [],
|
|
35
|
+
patchInfo: {
|
|
36
|
+
before: currentDoc,
|
|
37
|
+
after: currentDoc,
|
|
38
|
+
source: "change",
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
set.forEach((h) => h(payload));
|
|
42
|
+
}
|
|
43
|
+
}),
|
|
44
|
+
};
|
|
45
|
+
return {
|
|
46
|
+
handle,
|
|
47
|
+
setDoc: (d) => {
|
|
48
|
+
currentDoc = d;
|
|
49
|
+
},
|
|
50
|
+
emitChange: (doc) => {
|
|
51
|
+
currentDoc = doc;
|
|
52
|
+
const set = handlers.get("change");
|
|
53
|
+
if (set) {
|
|
54
|
+
const payload = {
|
|
55
|
+
handle: handle,
|
|
56
|
+
doc: doc,
|
|
57
|
+
patches: [],
|
|
58
|
+
patchInfo: {
|
|
59
|
+
before: doc,
|
|
60
|
+
after: doc,
|
|
61
|
+
source: "change",
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
set.forEach((h) => h(payload));
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
emitDelete: () => {
|
|
68
|
+
const set = handlers.get("delete");
|
|
69
|
+
if (set) {
|
|
70
|
+
const payload = {
|
|
71
|
+
handle: handle,
|
|
72
|
+
};
|
|
73
|
+
set.forEach((h) => h(payload));
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
resolveReady: () => readyResolve(),
|
|
77
|
+
rejectReady: (err) => readyReject(err),
|
|
78
|
+
offSpy,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
function parseSimpleDoc(raw) {
|
|
82
|
+
if (typeof raw === "object" &&
|
|
83
|
+
raw !== null &&
|
|
84
|
+
"title" in raw &&
|
|
85
|
+
typeof raw.title === "string" &&
|
|
86
|
+
"count" in raw &&
|
|
87
|
+
typeof raw.count === "number") {
|
|
88
|
+
return { title: raw.title, count: raw.count };
|
|
89
|
+
}
|
|
90
|
+
return { title: "", count: 0 };
|
|
91
|
+
}
|
|
92
|
+
function parseCounterDoc(raw) {
|
|
93
|
+
if (typeof raw === "object" &&
|
|
94
|
+
raw !== null &&
|
|
95
|
+
"count" in raw &&
|
|
96
|
+
typeof raw.count === "number") {
|
|
97
|
+
return { count: raw.count };
|
|
98
|
+
}
|
|
99
|
+
return { count: 0 };
|
|
100
|
+
}
|
|
101
|
+
describe("createDocStore", () => {
|
|
102
|
+
it("returns loading=true and default doc when handle doc is undefined", () => {
|
|
103
|
+
createRoot((dispose) => {
|
|
104
|
+
const { handle } = createMockHandle(undefined);
|
|
105
|
+
const { doc, loading } = createDocStore(handle, parseSimpleDoc);
|
|
106
|
+
expect(loading()).toBe(true);
|
|
107
|
+
// parse function provides defaults
|
|
108
|
+
expect(doc().title).toBe("");
|
|
109
|
+
expect(doc().count).toBe(0);
|
|
110
|
+
dispose();
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
it("returns loading=false immediately when handle already has a doc", () => {
|
|
114
|
+
createRoot((dispose) => {
|
|
115
|
+
const initialDoc = { title: "loaded", count: 42 };
|
|
116
|
+
const { handle } = createMockHandle(initialDoc);
|
|
117
|
+
const { doc, loading } = createDocStore(handle, parseSimpleDoc);
|
|
118
|
+
expect(loading()).toBe(false);
|
|
119
|
+
expect(doc().title).toBe("loaded");
|
|
120
|
+
expect(doc().count).toBe(42);
|
|
121
|
+
dispose();
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
it("updates doc signal on handle change event", () => {
|
|
125
|
+
createRoot((dispose) => {
|
|
126
|
+
const { handle, emitChange } = createMockHandle({
|
|
127
|
+
title: "original",
|
|
128
|
+
count: 1,
|
|
129
|
+
});
|
|
130
|
+
const { doc } = createDocStore(handle, parseSimpleDoc);
|
|
131
|
+
expect(doc().title).toBe("original");
|
|
132
|
+
expect(doc().count).toBe(1);
|
|
133
|
+
emitChange({ title: "updated", count: 2 });
|
|
134
|
+
expect(doc().title).toBe("updated");
|
|
135
|
+
expect(doc().count).toBe(2);
|
|
136
|
+
dispose();
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
it("degrades corrupt doc to parse function defaults on change", () => {
|
|
140
|
+
createRoot((dispose) => {
|
|
141
|
+
const { handle, emitChange } = createMockHandle({ title: "ok", count: 1 });
|
|
142
|
+
const { doc } = createDocStore(handle, parseSimpleDoc);
|
|
143
|
+
// emit a corrupt doc that fails validation
|
|
144
|
+
emitChange({ badField: true, wrongType: "not a number" });
|
|
145
|
+
// parse function degrades to defaults
|
|
146
|
+
expect(doc().title).toBe("");
|
|
147
|
+
expect(doc().count).toBe(0);
|
|
148
|
+
dispose();
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
it("sets loading=false when delete event fires", () => {
|
|
152
|
+
createRoot((dispose) => {
|
|
153
|
+
const { handle, emitDelete } = createMockHandle(undefined);
|
|
154
|
+
const { loading } = createDocStore(handle, parseSimpleDoc);
|
|
155
|
+
expect(loading()).toBe(true);
|
|
156
|
+
emitDelete();
|
|
157
|
+
expect(loading()).toBe(false);
|
|
158
|
+
dispose();
|
|
159
|
+
});
|
|
160
|
+
});
|
|
161
|
+
it("sets loading=false when whenReady resolves", async () => {
|
|
162
|
+
await createRoot(async (dispose) => {
|
|
163
|
+
const { handle, resolveReady, setDoc } = createMockHandle(undefined);
|
|
164
|
+
const { loading } = createDocStore(handle, parseSimpleDoc);
|
|
165
|
+
expect(loading()).toBe(true);
|
|
166
|
+
setDoc({ title: "ready", count: 10 });
|
|
167
|
+
resolveReady();
|
|
168
|
+
// flush microtasks
|
|
169
|
+
await Promise.resolve();
|
|
170
|
+
await Promise.resolve();
|
|
171
|
+
expect(loading()).toBe(false);
|
|
172
|
+
dispose();
|
|
173
|
+
});
|
|
174
|
+
});
|
|
175
|
+
it("sets loading=false when whenReady rejects", async () => {
|
|
176
|
+
await createRoot(async (dispose) => {
|
|
177
|
+
const { handle, rejectReady } = createMockHandle(undefined);
|
|
178
|
+
const { loading } = createDocStore(handle, parseSimpleDoc);
|
|
179
|
+
rejectReady(new Error("unavailable"));
|
|
180
|
+
await Promise.resolve();
|
|
181
|
+
await Promise.resolve();
|
|
182
|
+
expect(loading()).toBe(false);
|
|
183
|
+
dispose();
|
|
184
|
+
});
|
|
185
|
+
});
|
|
186
|
+
it("unsubscribes handlers on cleanup (off called with correct args)", () => {
|
|
187
|
+
const { handle, offSpy } = createMockHandle({ title: "test", count: 1 });
|
|
188
|
+
const dispose = createRoot((disposeRoot) => {
|
|
189
|
+
createDocStore(handle, parseSimpleDoc);
|
|
190
|
+
return disposeRoot;
|
|
191
|
+
});
|
|
192
|
+
dispose();
|
|
193
|
+
// off should have been called for both "change" and "delete"
|
|
194
|
+
expect(offSpy).toHaveBeenCalledWith("change", expect.any(Function));
|
|
195
|
+
expect(offSpy).toHaveBeenCalledWith("delete", expect.any(Function));
|
|
196
|
+
});
|
|
197
|
+
it("degrades null/undefined doc to parse function defaults", () => {
|
|
198
|
+
createRoot((dispose) => {
|
|
199
|
+
const { handle, emitChange } = createMockHandle({ title: "ok", count: 1 });
|
|
200
|
+
const { doc } = createDocStore(handle, parseSimpleDoc);
|
|
201
|
+
emitChange(null);
|
|
202
|
+
expect(doc().title).toBe("");
|
|
203
|
+
expect(doc().count).toBe(0);
|
|
204
|
+
emitChange(undefined);
|
|
205
|
+
expect(doc().title).toBe("");
|
|
206
|
+
expect(doc().count).toBe(0);
|
|
207
|
+
dispose();
|
|
208
|
+
});
|
|
209
|
+
});
|
|
210
|
+
it("works with a different doc type (CounterDoc) proving genericity", () => {
|
|
211
|
+
createRoot((dispose) => {
|
|
212
|
+
const { handle, emitChange } = createMockHandle({ count: 5 });
|
|
213
|
+
const { doc } = createDocStore(handle, parseCounterDoc);
|
|
214
|
+
expect(doc().count).toBe(5);
|
|
215
|
+
emitChange({ count: 10 });
|
|
216
|
+
expect(doc().count).toBe(10);
|
|
217
|
+
// corrupt data degrades to parseCounterDoc's defaults
|
|
218
|
+
emitChange({ wrongField: true });
|
|
219
|
+
expect(doc().count).toBe(0);
|
|
220
|
+
dispose();
|
|
221
|
+
});
|
|
222
|
+
});
|
|
223
|
+
});
|
|
224
|
+
describe("changeDoc", () => {
|
|
225
|
+
it("calls handle.change with the mutator function", () => {
|
|
226
|
+
const mockChange = vi.fn();
|
|
227
|
+
const handle = {
|
|
228
|
+
change: mockChange,
|
|
229
|
+
};
|
|
230
|
+
const mutator = vi.fn();
|
|
231
|
+
changeDoc(handle, mutator);
|
|
232
|
+
expect(mockChange).toHaveBeenCalledWith(mutator);
|
|
233
|
+
});
|
|
234
|
+
});
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { createTransferProgress, type TransferProgress } from "./transfer-progress.js";
|
|
2
|
+
export { createConnectionSummary, type ConnectionSummaryLike, type CreateConnectionSummaryOptions, } from "./connection-summary.js";
|
|
3
|
+
export { createBlobUrl } from "./blob-url.js";
|
|
4
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/solid/index.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,sBAAsB,EAAE,KAAK,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AACvF,OAAO,EACL,uBAAuB,EACvB,KAAK,qBAAqB,EAC1B,KAAK,8BAA8B,GACpC,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// thin solid-js reactive wrappers with no business logic of their own:
|
|
2
|
+
// every helper here just adapts a caller-supplied source of truth (a
|
|
3
|
+
// progress map, a connection-summary source, a blob-fetching function)
|
|
4
|
+
// into a solid signal/resource. this is the only subpath besides
|
|
5
|
+
// `./automerge` allowed to depend on a framework - it may import
|
|
6
|
+
// `solid-js`.
|
|
7
|
+
export { createTransferProgress } from "./transfer-progress.js";
|
|
8
|
+
export { createConnectionSummary, } from "./connection-summary.js";
|
|
9
|
+
export { createBlobUrl } from "./blob-url.js";
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type Accessor } from "solid-js";
|
|
2
|
+
export interface TransferProgress<TState> {
|
|
3
|
+
/** current snapshot of tracked keys and their state. reactive - reading
|
|
4
|
+
* this inside a solid computation subscribes to every map mutation
|
|
5
|
+
* (the underlying signal uses `{ equals: false }` so a mutated map is
|
|
6
|
+
* always seen as a change, even when the reference happens to be
|
|
7
|
+
* reused). */
|
|
8
|
+
states: Accessor<ReadonlyMap<string, TState>>;
|
|
9
|
+
/** set (or clear, with `null`) the state for one key without replacing
|
|
10
|
+
* the whole map. */
|
|
11
|
+
setState(key: string, state: TState | null): void;
|
|
12
|
+
/** clear every tracked key. */
|
|
13
|
+
reset(): void;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* creates a reactive `key -> state` map for tracking the progress of any
|
|
17
|
+
* number of concurrent, independently-keyed operations (blob downloads
|
|
18
|
+
* keyed by hash, uploads keyed by id, ...).
|
|
19
|
+
*/
|
|
20
|
+
export declare function createTransferProgress<TState>(): TransferProgress<TState>;
|
|
21
|
+
//# sourceMappingURL=transfer-progress.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transfer-progress.d.ts","sourceRoot":"","sources":["../../src/solid/transfer-progress.ts"],"names":[],"mappings":"AAUA,OAAO,EAAgB,KAAK,QAAQ,EAAE,MAAM,UAAU,CAAC;AAEvD,MAAM,WAAW,gBAAgB,CAAC,MAAM;IACtC;;;;mBAIe;IACf,MAAM,EAAE,QAAQ,CAAC,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAC9C;yBACqB;IACrB,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAAC;IAClD,+BAA+B;IAC/B,KAAK,IAAI,IAAI,CAAC;CACf;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,KAAK,gBAAgB,CAAC,MAAM,CAAC,CAiBzE"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// a reactive per-key progress/state map: a generalized version of the
|
|
2
|
+
// signal-map pattern used for tracking in-flight blob transfers, where
|
|
3
|
+
// presence in the map means "currently tracked" (in progress, pending, or
|
|
4
|
+
// failed) and absence means "not currently tracked" (either settled/cached
|
|
5
|
+
// already, or never started).
|
|
6
|
+
//
|
|
7
|
+
// the caller supplies the state type - this module has no vocabulary of
|
|
8
|
+
// its own for what a transfer's state looks like (blob download, upload,
|
|
9
|
+
// sync progress, or anything else keyed by a string id).
|
|
10
|
+
import { createSignal } from "solid-js";
|
|
11
|
+
/**
|
|
12
|
+
* creates a reactive `key -> state` map for tracking the progress of any
|
|
13
|
+
* number of concurrent, independently-keyed operations (blob downloads
|
|
14
|
+
* keyed by hash, uploads keyed by id, ...).
|
|
15
|
+
*/
|
|
16
|
+
export function createTransferProgress() {
|
|
17
|
+
const [states, setStates] = createSignal(new Map(), { equals: false });
|
|
18
|
+
function setState(key, state) {
|
|
19
|
+
setStates((prev) => {
|
|
20
|
+
const next = new Map(prev);
|
|
21
|
+
if (state === null)
|
|
22
|
+
next.delete(key);
|
|
23
|
+
else
|
|
24
|
+
next.set(key, state);
|
|
25
|
+
return next;
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
function reset() {
|
|
29
|
+
setStates(new Map());
|
|
30
|
+
}
|
|
31
|
+
return { states, setState, reset };
|
|
32
|
+
}
|