@lat-murmeldjur/weeb_3 0.0.321001 → 0.0.322001

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 CHANGED
@@ -22,43 +22,28 @@ This package contains the browser-targeted WebAssembly build of the `weeb-3` cli
22
22
  The main exports are:
23
23
 
24
24
  - `Weeb3No103` as the higher-level client interface
25
- - `BootstrapNode` for defining bootstrap peers
26
- - `Weeb3` for lower-level direct access to the underlying client
27
25
 
28
26
  The higher-level `Weeb3No103` interface provides the main methods used by the embedding example:
29
27
 
30
28
  - `start(options?)`
31
- - `connect()`
32
29
  - `networkState()`
33
- - `switchMainnet()` / `switch_mainnet()`
34
- - `switchTestnet()` / `switch_testnet()`
35
- - `switchNetwork(mode)` / `switch_network(mode)`
36
- - `connectProfile(mode)` / `connect_profile(mode)`
30
+ - `switchNetwork(mode)`
37
31
  - `retrieve(address)`
38
32
  - `upload(file, encryption, index_string, add_to_feed, feed_topic)`
39
33
  - `uploadWithRedundancy(file, encryption, redundancy_level, index_string, add_to_feed, feed_topic)`
40
34
  - `postUploadBytesWithRedundancy(bytes, mime, filename, encryption, redundancy_level, add_to_feed, feed_topic)`
41
- - `openStreamFeed(owner, topic)`
42
- - `playHlsStream(owner, topic, media_type, index?)`
43
- - `attachHlsStream(media, owner, topic, options)`
44
- - `detachHlsStream()`
45
- - `configureStreamingRoutes(service_worker_url, route_base)`
46
35
  - `renderInterface(container)`
47
36
  - `resetStamp()`
48
37
  - `postPushChunk(data, soc, chunk_address, stamp)`
49
38
 
50
39
  Sequence-feed indexes use Bee's fixed-width eight-byte big-endian encoding.
51
40
 
52
- ## HLS streaming
53
-
54
- HLS playback is an optional dapp integration, not a Bee/Swarm standard. `playHlsStream(...)` displays a stream in the bundled interface after `renderInterface(container)`. For an application-owned `<video>` or `<audio>` element, call `attachHlsStream(media, owner, topic, { start: "beginning" })`; use `"current-window"` for a rolling/live presentation. Call `detachHlsStream()` before removing or replacing that element. The Service Worker must be copied from the package to a same-origin URL whose scope contains the page, then configured with `configureStreamingRoutes(...)`. Canonical mainnet links use `/stream/{owner}/{topic}[/{index}]`; testnet inserts `/testnet` before `/stream`.
55
-
56
41
  ## Basic usage
57
42
 
58
43
  Call `init()` once before creating a client instance so the WebAssembly module is loaded.
59
44
 
60
45
  ```js
61
- import init, { Weeb3No103, BootstrapNode } from "@lat-murmeldjur/weeb_3";
46
+ import init, { Weeb3No103 } from "@lat-murmeldjur/weeb_3";
62
47
 
63
48
  await init();
64
49
 
@@ -69,17 +54,12 @@ weeb3node.start();
69
54
  console.log(await weeb3node.networkState());
70
55
 
71
56
  // Switch explicitly between built-in profiles.
72
- await weeb3node.switchTestnet();
73
- await weeb3node.switchMainnet();
74
-
75
- // Or use the generic form. Accepted values include:
76
- // "mainnet", "gnosis", "1", "testnet", "sepolia", and "10".
77
57
  await weeb3node.switchNetwork("testnet");
78
58
  await weeb3node.switchNetwork("mainnet");
79
59
 
80
60
  // You can still start with explicit browser-dialable bootnodes and network id.
81
61
  const BOOTSTRAP_NODES = [
82
- new BootstrapNode("/ip4/example/tcp/443/wss/p2p/examplePeerId", true),
62
+ { multiaddr: "/ip4/example/tcp/443/wss/p2p/examplePeerId", usable: true },
83
63
  ];
84
64
 
85
65
  weeb3node.start({
@@ -96,36 +76,31 @@ weeb3node.start({ testnet: true });
96
76
  This is a compact npm-import form of the same usage pattern shown in the project's `example.html`:
97
77
 
98
78
  ```js
99
- import init, { Weeb3No103, BootstrapNode } from "@lat-murmeldjur/weeb_3";
79
+ import init, { Weeb3No103 } from "@lat-murmeldjur/weeb_3";
100
80
 
101
81
  await init();
102
82
 
103
83
  const weeb3node = new Weeb3No103();
104
84
 
105
- await weeb3node.switchMainnet();
85
+ await weeb3node.switchNetwork("mainnet");
106
86
 
107
87
  const entries = await weeb3node.retrieve(
108
88
  "695fceb3a8c212cd123e2e40d86ec08b52fe4fe6ca46687ce9ea69b8f05471f6aa25b5d4d41bf78b1db3479c048fd5fd8137ba844604821b71786196306b68e7"
109
89
  );
110
90
  ```
111
91
 
112
- ## Erasure-coding selector
92
+ ## Upload API
113
93
 
114
94
  Legacy upload methods use Bee's Medium level. Explicit upload levels are `0` None, `1` Medium, `2` Strong, `3` Insane, and `4` Paranoid. The generated TypeScript declaration exposes this as `UploadRedundancyLevel = 0 | 1 | 2 | 3 | 4`.
115
95
 
116
- `renderInterface(container)` includes a Medium-default erasure-coding dropdown. A custom interface can populate its own dropdown from the same canonical metadata:
96
+ `renderInterface(container)` includes a Medium-default erasure-coding dropdown. A custom interface can pass the selected numeric level directly:
117
97
 
118
98
  ```js
119
- import init, {
120
- Weeb3No103,
121
- defaultUploadRedundancyLevel,
122
- uploadRedundancyOptions,
123
- } from "@lat-murmeldjur/weeb_3";
99
+ import init, { Weeb3No103 } from "@lat-murmeldjur/weeb_3";
124
100
 
125
101
  await init();
126
102
  const node = new Weeb3No103();
127
- const choices = uploadRedundancyOptions();
128
- const level = defaultUploadRedundancyLevel();
103
+ const level = 1;
129
104
 
130
105
  const result = await node.uploadWithRedundancy(
131
106
  file,
@@ -142,6 +117,5 @@ Retrieval reads the level encoded in the Swarm tree and uses parity when data sh
142
117
  ## Notes
143
118
 
144
119
  - This package is meant for browser applications, not a plain Node.js runtime.
145
- - Use one active `Weeb3No103` node and HLS session per loaded Wasm module.
146
120
  - The package does not publish the standalone site HTML, but `renderInterface(container)` embeds the same interface shell—including its erasure-coding selector—from the Wasm bundle.
147
121
  - The full released browser client remains available in the main project repository and on the project site.
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@lat-murmeldjur/weeb_3",
3
3
  "type": "module",
4
4
  "description": "A Swarm client for browsers",
5
- "version": "0.0.321001",
5
+ "version": "0.0.322001",
6
6
  "license": "MIT",
7
7
  "repository": {
8
8
  "type": "git",
@@ -42,9 +42,6 @@
42
42
  "types": "./weeb_3.d.ts",
43
43
  "import": "./weeb_3.js",
44
44
  "default": "./weeb_3.js"
45
- },
46
- "./service.js": {
47
- "default": "./service.js"
48
45
  }
49
46
  },
50
47
  "dependencies": {
package/service.js CHANGED
@@ -6,13 +6,11 @@ const NETWORK_ROUTE_PREFIXES = ["", "mainnet/", "testnet/"];
6
6
  const RAW_ROUTE_KINDS = [
7
7
  ["hls/bytes", "hls-bytes"],
8
8
  ["bytes", "bytes"],
9
- ["chunks", "chunk"],
10
- ["chunk", "chunk"]
9
+ ["chunks", "chunk"]
11
10
  ];
12
11
  const FETCH_TIMEOUT_MS = 240000;
13
12
  const SERVICE_WORKER_MARKER = "forwarder-default20";
14
13
  const SERVICE_WORKER_PROTOCOL = 5;
15
- const DEBUG_SERVICE_WORKER = false;
16
14
  const MIB_BYTES = 1024 * 1024;
17
15
  const STREAM_STORAGE_WINDOW_BYTES = MIB_BYTES / 2;
18
16
  const STREAM_LOOKAHEAD_CHUNKS = 8;
@@ -22,12 +20,6 @@ const CLIENT_RUNTIME_PROBE_TIMEOUT_MS = 1_500;
22
20
 
23
21
  console.log(`weeb-3 service worker start ${SERVICE_WORKER_MARKER}`);
24
22
 
25
- function debugLog(...args) {
26
- if (DEBUG_SERVICE_WORKER) {
27
- console.log(...args);
28
- }
29
- }
30
-
31
23
  function logServiceWorkerVersion(reason) {
32
24
  console.log(`weeb-3 service worker ${reason} ${SERVICE_WORKER_MARKER}`);
33
25
  }
@@ -79,32 +71,16 @@ function isCanonicalStreamTopic(value) {
79
71
  }
80
72
  }
81
73
 
82
- function isCanonicalStreamIndex(value) {
83
- if (value === undefined) {
84
- return true;
85
- }
86
- return /^[0-9]+$/.test(value) && BigInt(value) <= 18446744073709551615n;
87
- }
88
-
89
74
  function isDirectShareShellPath(pathname) {
90
75
  if (!pathname.startsWith(SCOPE_PATH)) {
91
76
  return false;
92
77
  }
93
78
 
94
79
  const parts = pathname.substring(SCOPE_PATH.length).split("/");
95
- if (parts[0] === "testnet") {
96
- parts.shift();
97
- }
98
-
99
- const kind = parts.shift();
100
- if (kind !== "stream") {
101
- return false;
102
- }
103
-
104
- return (parts.length === 2 || parts.length === 3) &&
105
- /^(?:0[xX])?[a-fA-F0-9]{40}$/.test(parts[0] || "") &&
106
- isCanonicalStreamTopic(parts[1]) &&
107
- isCanonicalStreamIndex(parts[2]);
80
+ return parts.length === 3 &&
81
+ parts[0] === "stream" &&
82
+ /^[a-fA-F0-9]{40}$/.test(parts[1]) &&
83
+ isCanonicalStreamTopic(parts[2]);
108
84
  }
109
85
 
110
86
  function isBzzUploadPath(pathname) {
@@ -138,21 +114,26 @@ function canonicalBzzResource(url) {
138
114
  }
139
115
 
140
116
  function canonicalRawResource(url) {
141
- for (const [marker] of rawRouteMarkers()) {
117
+ for (const [marker, rawType] of rawRouteMarkers()) {
142
118
  if (!url.pathname.startsWith(marker)) {
143
119
  continue;
144
120
  }
145
121
 
146
- const resource = url.pathname.substring(marker.length);
147
- if (!resource) {
122
+ const encodedResource = url.pathname.substring(marker.length);
123
+ if (!encodedResource) {
148
124
  return null;
149
125
  }
150
126
 
127
+ let resource;
151
128
  try {
152
- return decodeURIComponent(resource);
129
+ resource = decodeURIComponent(encodedResource);
153
130
  } catch (_) {
154
- return resource;
131
+ resource = encodedResource;
155
132
  }
133
+ if (rawType === "hls-bytes" && !isSwarmReference(resource)) {
134
+ return null;
135
+ }
136
+ return resource;
156
137
  }
157
138
 
158
139
  return null;
@@ -180,15 +161,6 @@ function canonicalFeedResource(url) {
180
161
  return null;
181
162
  }
182
163
 
183
- function isCanonicalRequest(request) {
184
- try {
185
- const url = new URL(request.url);
186
- return canonicalBzzResource(url) !== null || canonicalRawResource(url) !== null;
187
- } catch (_) {
188
- return false;
189
- }
190
- }
191
-
192
164
  function isAppShellNavigation(request) {
193
165
  const headerDestination = request.headers.get("Sec-Fetch-Dest") || "";
194
166
  return request.method === "GET" &&
@@ -295,9 +267,6 @@ self.addEventListener("fetch", (event) => {
295
267
  return;
296
268
  }
297
269
 
298
- // This worker is safe to mount in an npm application: requests outside the
299
- // explicit weeb-3 routes remain entirely under the host app/browser's
300
- // normal fetch and caching policy.
301
270
  });
302
271
 
303
272
  function clientInScope(client) {
@@ -382,10 +351,7 @@ async function firstReadyClient(candidates, requiredNetworkId) {
382
351
  return [];
383
352
  }
384
353
 
385
- // Start the cheap liveness/network probes together so stale tabs add at
386
- // most one probe timeout. Preserve the originating client's fast path and
387
- // candidate priority; the accounting-sensitive operation itself is still
388
- // sent exactly once by messageFirstClient.
354
+ // Probe candidates concurrently without redispatching work.
389
355
  const probes = candidates.map((candidate) => clientWeeb3NetworkId(candidate));
390
356
  if (await probes[0] === requiredNetworkId) {
391
357
  return [candidates[0]];
@@ -413,12 +379,7 @@ async function requestClients(event, requestUrl, requiredNetworkId) {
413
379
  )
414
380
  );
415
381
 
416
- // HLS fetches originate in the top-level page that attached the shared Rust
417
- // player, whether that page is the bundled shell or an arbitrary npm host.
418
- // Dispatch those directly: the Rust bridge validates networkId before
419
- // retrieval, while a redundant liveness round-trip here can false-timeout
420
- // when a large WASM retrieval wave briefly occupies the browser thread.
421
- // Nested clients retain the probed fallback below.
382
+ // Direct top-level HLS requests skip the redundant liveness probe.
422
383
  if (
423
384
  directHlsRequest &&
424
385
  eventClient &&
@@ -436,9 +397,6 @@ async function requestClients(event, requestUrl, requiredNetworkId) {
436
397
  const seen = new Set();
437
398
  const requestReference = requestUrlObject ? bzzReferenceFromUrl(requestUrlObject) : "";
438
399
 
439
- // Prefer the top-level context that originated the request. A nested BZZ
440
- // site frame does not host the Rust runtime and must fall through to its
441
- // active app-shell tab below.
442
400
  if (eventClient && isTopLevelClient(eventClient) && clientInScope(eventClient)) {
443
401
  pushUniqueClient(candidates, seen, eventClient);
444
402
  }
@@ -532,10 +490,7 @@ function messageFirstClient(clients, message, timeoutMs = FETCH_TIMEOUT_MS) {
532
490
  });
533
491
  }
534
492
 
535
- // Uploads and canonical retrievals can enter postage/accounting state. Send
536
- // each request exactly once. A timeout merely detaches this response port;
537
- // already-dispatched work is deliberately left to drain and is never replayed
538
- // through another tab.
493
+ // Timeouts detach the port; dispatched accounting work is never replayed.
539
494
  return messageClient(clients[0], message, timeoutMs);
540
495
  }
541
496
 
@@ -638,16 +593,6 @@ function toUint8Array(body) {
638
593
  return new Uint8Array();
639
594
  }
640
595
 
641
- function oneChunkResponseBody(body) {
642
- const bytes = toUint8Array(body);
643
- return new ReadableStream({
644
- start(controller) {
645
- controller.enqueue(bytes);
646
- controller.close();
647
- }
648
- });
649
- }
650
-
651
596
  function responseHeaders(headerRows) {
652
597
  const headers = new Headers();
653
598
  for (const row of headerRows || []) {
@@ -774,7 +719,7 @@ async function forwardRequestToRust(request, event) {
774
719
  return new Response(
775
720
  request.method === "HEAD" || status === 304
776
721
  ? null
777
- : oneChunkResponseBody(response.body),
722
+ : toUint8Array(response.body),
778
723
  {
779
724
  status,
780
725
  headers
@@ -788,12 +733,9 @@ async function forwardRequestToRust(request, event) {
788
733
  }
789
734
 
790
735
  function parseUploadRedundancyHeader(value) {
791
- // Bee marks this header `omitempty`, so a present empty value has the same
792
- // Medium default as an omitted header.
793
736
  if (value === null || value === "") {
794
737
  return 1;
795
738
  }
796
- // Bee parses an unsigned base-10 header, so reject Number()-only forms such as hex or exponents.
797
739
  if (!/^[0-9]+$/.test(value)) {
798
740
  return null;
799
741
  }
@@ -1,5 +1,3 @@
1
- // Dynamic `import()` has no CSP-safe Web API callable directly from Wasm.
2
- // Keep this irreducible bridge tiny; player policy and lifecycle live in Rust.
3
1
  export async function loadHls() {
4
2
  const module = await import("hls.js");
5
3
  return module.default ?? module.Hls;