@bandeira-tech/b3nd-save 0.8.1

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 (177) hide show
  1. package/LICENSE +21 -0
  2. package/esm/_dnt.shims.d.ts +2 -0
  3. package/esm/_dnt.shims.d.ts.map +1 -0
  4. package/esm/_dnt.shims.js +57 -0
  5. package/esm/byte-entity-shim.d.ts +47 -0
  6. package/esm/byte-entity-shim.d.ts.map +1 -0
  7. package/esm/byte-entity-shim.js +138 -0
  8. package/esm/clients/mod.d.ts +15 -0
  9. package/esm/clients/mod.d.ts.map +1 -0
  10. package/esm/clients/mod.js +14 -0
  11. package/esm/clients/save-client.d.ts +87 -0
  12. package/esm/clients/save-client.d.ts.map +1 -0
  13. package/esm/clients/save-client.js +174 -0
  14. package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/0.22.0/mod.d.ts +40 -0
  15. package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/0.22.0/mod.d.ts.map +1 -0
  16. package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/0.22.0/mod.js +31 -0
  17. package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/0.22.0/src/client-console/client.d.ts +30 -0
  18. package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/0.22.0/src/client-console/client.d.ts.map +1 -0
  19. package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/0.22.0/src/client-console/client.js +70 -0
  20. package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/0.22.0/src/encoding/encoding.d.ts +16 -0
  21. package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/0.22.0/src/encoding/encoding.d.ts.map +1 -0
  22. package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/0.22.0/src/encoding/encoding.js +50 -0
  23. package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/0.22.0/src/encrypt/mod.d.ts +235 -0
  24. package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/0.22.0/src/encrypt/mod.d.ts.map +1 -0
  25. package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/0.22.0/src/encrypt/mod.js +619 -0
  26. package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/0.22.0/src/functional-client/functional-client.d.ts +43 -0
  27. package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/0.22.0/src/functional-client/functional-client.d.ts.map +1 -0
  28. package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/0.22.0/src/functional-client/functional-client.js +54 -0
  29. package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/0.22.0/src/match-pattern/match-pattern.d.ts +47 -0
  30. package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/0.22.0/src/match-pattern/match-pattern.d.ts.map +1 -0
  31. package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/0.22.0/src/match-pattern/match-pattern.js +134 -0
  32. package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/0.22.0/src/network/decorators.d.ts +20 -0
  33. package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/0.22.0/src/network/decorators.d.ts.map +1 -0
  34. package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/0.22.0/src/network/decorators.js +34 -0
  35. package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/0.22.0/src/network/network.d.ts +46 -0
  36. package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/0.22.0/src/network/network.d.ts.map +1 -0
  37. package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/0.22.0/src/network/network.js +131 -0
  38. package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/0.22.0/src/network/peer.d.ts +27 -0
  39. package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/0.22.0/src/network/peer.d.ts.map +1 -0
  40. package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/0.22.0/src/network/peer.js +25 -0
  41. package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/0.22.0/src/network/policies/flood.d.ts +59 -0
  42. package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/0.22.0/src/network/policies/flood.d.ts.map +1 -0
  43. package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/0.22.0/src/network/policies/flood.js +188 -0
  44. package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/0.22.0/src/network/policies/path-vector.d.ts +35 -0
  45. package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/0.22.0/src/network/policies/path-vector.d.ts.map +1 -0
  46. package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/0.22.0/src/network/policies/path-vector.js +57 -0
  47. package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/0.22.0/src/network/policies/tell-and-read.d.ts +117 -0
  48. package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/0.22.0/src/network/policies/tell-and-read.d.ts.map +1 -0
  49. package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/0.22.0/src/network/policies/tell-and-read.js +125 -0
  50. package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/0.22.0/src/network/types.d.ts +112 -0
  51. package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/0.22.0/src/network/types.d.ts.map +1 -0
  52. package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/0.22.0/src/network/types.js +21 -0
  53. package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/0.22.0/src/observe-emitter/observe-emitter.d.ts +35 -0
  54. package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/0.22.0/src/observe-emitter/observe-emitter.d.ts.map +1 -0
  55. package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/0.22.0/src/observe-emitter/observe-emitter.js +106 -0
  56. package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/0.22.0/src/rig/connection.d.ts +88 -0
  57. package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/0.22.0/src/rig/connection.d.ts.map +1 -0
  58. package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/0.22.0/src/rig/connection.js +86 -0
  59. package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/0.22.0/src/rig/events.d.ts +83 -0
  60. package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/0.22.0/src/rig/events.d.ts.map +1 -0
  61. package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/0.22.0/src/rig/events.js +115 -0
  62. package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/0.22.0/src/rig/hooks.d.ts +164 -0
  63. package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/0.22.0/src/rig/hooks.d.ts.map +1 -0
  64. package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/0.22.0/src/rig/hooks.js +67 -0
  65. package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/0.22.0/src/rig/identity.d.ts +99 -0
  66. package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/0.22.0/src/rig/identity.d.ts.map +1 -0
  67. package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/0.22.0/src/rig/identity.js +190 -0
  68. package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/0.22.0/src/rig/operation-handle.d.ts +185 -0
  69. package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/0.22.0/src/rig/operation-handle.d.ts.map +1 -0
  70. package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/0.22.0/src/rig/operation-handle.js +103 -0
  71. package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/0.22.0/src/rig/reactions.d.ts +58 -0
  72. package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/0.22.0/src/rig/reactions.d.ts.map +1 -0
  73. package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/0.22.0/src/rig/reactions.js +64 -0
  74. package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/0.22.0/src/rig/rig.d.ts +295 -0
  75. package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/0.22.0/src/rig/rig.d.ts.map +1 -0
  76. package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/0.22.0/src/rig/rig.js +898 -0
  77. package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/0.22.0/src/rig/types.d.ts +167 -0
  78. package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/0.22.0/src/rig/types.d.ts.map +1 -0
  79. package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/0.22.0/src/rig/types.js +5 -0
  80. package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/0.22.0/src/types/types.d.ts +278 -0
  81. package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/0.22.0/src/types/types.d.ts.map +1 -0
  82. package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/0.22.0/src/types/types.js +89 -0
  83. package/esm/dispatch.d.ts +35 -0
  84. package/esm/dispatch.d.ts.map +1 -0
  85. package/esm/dispatch.js +40 -0
  86. package/esm/elasticsearch/mod.d.ts +31 -0
  87. package/esm/elasticsearch/mod.d.ts.map +1 -0
  88. package/esm/elasticsearch/mod.js +8 -0
  89. package/esm/elasticsearch/store.d.ts +44 -0
  90. package/esm/elasticsearch/store.d.ts.map +1 -0
  91. package/esm/elasticsearch/store.js +213 -0
  92. package/esm/entity-store.d.ts +106 -0
  93. package/esm/entity-store.d.ts.map +1 -0
  94. package/esm/entity-store.js +46 -0
  95. package/esm/entity.d.ts +125 -0
  96. package/esm/entity.d.ts.map +1 -0
  97. package/esm/entity.js +66 -0
  98. package/esm/errors.d.ts +23 -0
  99. package/esm/errors.d.ts.map +1 -0
  100. package/esm/errors.js +25 -0
  101. package/esm/fs/mod.d.ts +28 -0
  102. package/esm/fs/mod.d.ts.map +1 -0
  103. package/esm/fs/mod.js +13 -0
  104. package/esm/fs/store.d.ts +45 -0
  105. package/esm/fs/store.d.ts.map +1 -0
  106. package/esm/fs/store.js +194 -0
  107. package/esm/indexeddb/mod.d.ts +9 -0
  108. package/esm/indexeddb/mod.d.ts.map +1 -0
  109. package/esm/indexeddb/mod.js +8 -0
  110. package/esm/indexeddb/store.d.ts +47 -0
  111. package/esm/indexeddb/store.d.ts.map +1 -0
  112. package/esm/indexeddb/store.js +299 -0
  113. package/esm/ipfs/mod.d.ts +24 -0
  114. package/esm/ipfs/mod.d.ts.map +1 -0
  115. package/esm/ipfs/mod.js +13 -0
  116. package/esm/ipfs/store.d.ts +42 -0
  117. package/esm/ipfs/store.d.ts.map +1 -0
  118. package/esm/ipfs/store.js +197 -0
  119. package/esm/localstorage/mod.d.ts +9 -0
  120. package/esm/localstorage/mod.d.ts.map +1 -0
  121. package/esm/localstorage/mod.js +8 -0
  122. package/esm/localstorage/store.d.ts +45 -0
  123. package/esm/localstorage/store.d.ts.map +1 -0
  124. package/esm/localstorage/store.js +147 -0
  125. package/esm/memory/mod.d.ts +12 -0
  126. package/esm/memory/mod.d.ts.map +1 -0
  127. package/esm/memory/mod.js +11 -0
  128. package/esm/memory/store.d.ts +73 -0
  129. package/esm/memory/store.d.ts.map +1 -0
  130. package/esm/memory/store.js +300 -0
  131. package/esm/mod.d.ts +36 -0
  132. package/esm/mod.d.ts.map +1 -0
  133. package/esm/mod.js +31 -0
  134. package/esm/mongo/mod.d.ts +35 -0
  135. package/esm/mongo/mod.d.ts.map +1 -0
  136. package/esm/mongo/mod.js +8 -0
  137. package/esm/mongo/store.d.ts +42 -0
  138. package/esm/mongo/store.d.ts.map +1 -0
  139. package/esm/mongo/store.js +185 -0
  140. package/esm/package.json +3 -0
  141. package/esm/payload.d.ts +24 -0
  142. package/esm/payload.d.ts.map +1 -0
  143. package/esm/payload.js +30 -0
  144. package/esm/postgres/columns.d.ts +33 -0
  145. package/esm/postgres/columns.d.ts.map +1 -0
  146. package/esm/postgres/columns.js +51 -0
  147. package/esm/postgres/mod.d.ts +19 -0
  148. package/esm/postgres/mod.d.ts.map +1 -0
  149. package/esm/postgres/mod.js +9 -0
  150. package/esm/postgres/store.d.ts +75 -0
  151. package/esm/postgres/store.d.ts.map +1 -0
  152. package/esm/postgres/store.js +364 -0
  153. package/esm/read.d.ts +40 -0
  154. package/esm/read.d.ts.map +1 -0
  155. package/esm/read.js +67 -0
  156. package/esm/s3/mod.d.ts +23 -0
  157. package/esm/s3/mod.d.ts.map +1 -0
  158. package/esm/s3/mod.js +13 -0
  159. package/esm/s3/store.d.ts +46 -0
  160. package/esm/s3/store.d.ts.map +1 -0
  161. package/esm/s3/store.js +183 -0
  162. package/esm/sqlite/mod.d.ts +18 -0
  163. package/esm/sqlite/mod.d.ts.map +1 -0
  164. package/esm/sqlite/mod.js +8 -0
  165. package/esm/sqlite/schema.d.ts +10 -0
  166. package/esm/sqlite/schema.d.ts.map +1 -0
  167. package/esm/sqlite/schema.js +26 -0
  168. package/esm/sqlite/store.d.ts +42 -0
  169. package/esm/sqlite/store.d.ts.map +1 -0
  170. package/esm/sqlite/store.js +184 -0
  171. package/esm/types.d.ts +101 -0
  172. package/esm/types.d.ts.map +1 -0
  173. package/esm/types.js +21 -0
  174. package/esm/url.d.ts +108 -0
  175. package/esm/url.d.ts.map +1 -0
  176. package/esm/url.js +151 -0
  177. package/package.json +93 -0
@@ -0,0 +1,54 @@
1
+ /**
2
+ * @module
3
+ * FunctionalClient - A client that takes functions as config.
4
+ *
5
+ * Replaces createNode() for cases where you want to wire up
6
+ * custom behavior without class inheritance.
7
+ */
8
+ /**
9
+ * A client that delegates each method to a config function.
10
+ *
11
+ * If a method is not provided, it returns a sensible default:
12
+ * - receive → { accepted: false, error: "not implemented" }
13
+ * - read → [{ success: false, error: "not implemented" }] per URI
14
+ * - status → { status: "healthy" }
15
+ *
16
+ * @example
17
+ * ```typescript
18
+ * const client = new FunctionalClient({
19
+ * receive: async (msg) => backend.receive(msg),
20
+ * read: async (urls) => backend.read(urls),
21
+ * });
22
+ * ```
23
+ */
24
+ export class FunctionalClient {
25
+ config;
26
+ constructor(config) {
27
+ this.config = config;
28
+ }
29
+ receive(msgs) {
30
+ if (this.config.receive) {
31
+ return this.config.receive(msgs);
32
+ }
33
+ return Promise.resolve(msgs.map(() => ({ accepted: false, error: "not implemented" })));
34
+ }
35
+ read(urls) {
36
+ if (this.config.read) {
37
+ return this.config.read(urls);
38
+ }
39
+ // No read config → empty result (option-A absence semantics).
40
+ return Promise.resolve([]);
41
+ }
42
+ async *observe(urls, signal) {
43
+ if (this.config.observe) {
44
+ yield* this.config.observe(urls, signal);
45
+ }
46
+ // No observe config → empty stream (never yields)
47
+ }
48
+ status() {
49
+ if (this.config.status) {
50
+ return this.config.status();
51
+ }
52
+ return Promise.resolve({ status: "healthy" });
53
+ }
54
+ }
@@ -0,0 +1,47 @@
1
+ /**
2
+ * @module
3
+ * URI pattern matching — shared by the rig (routing, reactions) and the
4
+ * observe emitter.
5
+ *
6
+ * Grammar (segments separated by `/`):
7
+ * - **literal** segments must match exactly.
8
+ * - `*` matches **exactly one** non-empty segment.
9
+ * - `**` matches **zero or more** remaining segments. Only valid as a
10
+ * complete final segment.
11
+ *
12
+ * Patterns compile to a closure once; the closure is called per URI.
13
+ * Three compile paths, picked at compile time:
14
+ *
15
+ * 1. **Pure literal** (no metas) → `(uri) => uri === pattern`
16
+ * 2. **Trailing `**` only** → `(uri) => uri.startsWith(prefix)`
17
+ * 3. **Has a `*` somewhere** → compiled `RegExp`, matched via `test`.
18
+ *
19
+ * Captures are intentionally not exposed. Callers that need a segment
20
+ * value extract it from the URI directly (split on `/`, pick the index).
21
+ * Keeping the matcher bool-only collapses three callers — routing,
22
+ * observe, reactions — onto one fast path with no allocations.
23
+ */
24
+ /** A compiled pattern: takes a URI, returns whether it matches. */
25
+ export type Matcher = (uri: string) => boolean;
26
+ /**
27
+ * Compile a pattern string into a matcher closure.
28
+ *
29
+ * Throws `TypeError` on grammar violations:
30
+ * - `:param`-style segments (legacy; use `*` and extract from the URI).
31
+ * - `*` mixed into a segment with other chars (e.g. `abc*`, `*abc`).
32
+ * - `**` not as a complete final segment (e.g. `** /x`, `a/** b`).
33
+ *
34
+ * @example
35
+ * ```ts
36
+ * const m = compilePattern("mutable://**");
37
+ * m("mutable://users/alice"); // true
38
+ * m("hash://abc"); // false
39
+ * ```
40
+ */
41
+ export declare function compilePattern(pattern: string): Matcher;
42
+ /**
43
+ * One-shot matcher. Compiles the pattern every call — fine for ad-hoc
44
+ * use; prefer `compilePattern` for anything hot.
45
+ */
46
+ export declare function matches(pattern: string, uri: string): boolean;
47
+ //# sourceMappingURL=match-pattern.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"match-pattern.d.ts","sourceRoot":"","sources":["../../../../../../../../src/deps/jsr.io/@bandeira-tech/b3nd-core/0.22.0/src/match-pattern/match-pattern.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,mEAAmE;AACnE,MAAM,MAAM,OAAO,GAAG,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;AAE/C;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAcvD;AAED;;;GAGG;AACH,wBAAgB,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAE7D"}
@@ -0,0 +1,134 @@
1
+ /**
2
+ * @module
3
+ * URI pattern matching — shared by the rig (routing, reactions) and the
4
+ * observe emitter.
5
+ *
6
+ * Grammar (segments separated by `/`):
7
+ * - **literal** segments must match exactly.
8
+ * - `*` matches **exactly one** non-empty segment.
9
+ * - `**` matches **zero or more** remaining segments. Only valid as a
10
+ * complete final segment.
11
+ *
12
+ * Patterns compile to a closure once; the closure is called per URI.
13
+ * Three compile paths, picked at compile time:
14
+ *
15
+ * 1. **Pure literal** (no metas) → `(uri) => uri === pattern`
16
+ * 2. **Trailing `**` only** → `(uri) => uri.startsWith(prefix)`
17
+ * 3. **Has a `*` somewhere** → compiled `RegExp`, matched via `test`.
18
+ *
19
+ * Captures are intentionally not exposed. Callers that need a segment
20
+ * value extract it from the URI directly (split on `/`, pick the index).
21
+ * Keeping the matcher bool-only collapses three callers — routing,
22
+ * observe, reactions — onto one fast path with no allocations.
23
+ */
24
+ /**
25
+ * Compile a pattern string into a matcher closure.
26
+ *
27
+ * Throws `TypeError` on grammar violations:
28
+ * - `:param`-style segments (legacy; use `*` and extract from the URI).
29
+ * - `*` mixed into a segment with other chars (e.g. `abc*`, `*abc`).
30
+ * - `**` not as a complete final segment (e.g. `** /x`, `a/** b`).
31
+ *
32
+ * @example
33
+ * ```ts
34
+ * const m = compilePattern("mutable://**");
35
+ * m("mutable://users/alice"); // true
36
+ * m("hash://abc"); // false
37
+ * ```
38
+ */
39
+ export function compilePattern(pattern) {
40
+ const analysis = analyze(pattern);
41
+ if (analysis.kind === "literal") {
42
+ return (uri) => uri === pattern;
43
+ }
44
+ if (analysis.kind === "prefix") {
45
+ const prefix = analysis.prefix;
46
+ return (uri) => uri.startsWith(prefix);
47
+ }
48
+ const re = analysis.re;
49
+ return (uri) => re.test(uri);
50
+ }
51
+ /**
52
+ * One-shot matcher. Compiles the pattern every call — fine for ad-hoc
53
+ * use; prefer `compilePattern` for anything hot.
54
+ */
55
+ export function matches(pattern, uri) {
56
+ return compilePattern(pattern)(uri);
57
+ }
58
+ /**
59
+ * Walk the pattern once, segment-aware:
60
+ * - reject `:foo` segments (legacy capture syntax),
61
+ * - reject mixed-meta segments like `abc*` or `**x`,
62
+ * - detect a trailing `**` for the `startsWith` fast path,
63
+ * - detect any `*` to switch to the regex path.
64
+ */
65
+ function analyze(pattern) {
66
+ const len = pattern.length;
67
+ let hasSingleStar = false;
68
+ let trailingDoubleStarAt = -1;
69
+ let segStart = 0;
70
+ for (let i = 0; i <= len; i++) {
71
+ if (i === len || pattern.charCodeAt(i) === 47 /* / */) {
72
+ const seg = pattern.slice(segStart, i);
73
+ classifySegment(seg, i === len);
74
+ if (seg === "**") {
75
+ if (i !== len) {
76
+ // `**` only valid as the last segment.
77
+ throw new TypeError(`pattern "${pattern}": "**" must be the final segment`);
78
+ }
79
+ // Position of the `**` in the original string (segStart).
80
+ trailingDoubleStarAt = segStart;
81
+ }
82
+ else if (seg === "*") {
83
+ hasSingleStar = true;
84
+ }
85
+ segStart = i + 1;
86
+ }
87
+ }
88
+ if (!hasSingleStar && trailingDoubleStarAt < 0) {
89
+ return { kind: "literal" };
90
+ }
91
+ if (!hasSingleStar && trailingDoubleStarAt >= 0) {
92
+ // Strip the trailing `**` (and the `/` before it, if any).
93
+ // segStart for `**` is the char *after* the leading `/`, so
94
+ // `pattern.slice(0, trailingDoubleStarAt)` keeps the trailing `/`.
95
+ return { kind: "prefix", prefix: pattern.slice(0, trailingDoubleStarAt) };
96
+ }
97
+ return { kind: "regex", re: toRegex(pattern) };
98
+ }
99
+ function classifySegment(seg, _isLast) {
100
+ if (seg === "*" || seg === "**" || !seg.includes("*")) {
101
+ if (seg.startsWith(":")) {
102
+ throw new TypeError(`pattern segment ":${seg.slice(1)}": ":param" syntax was removed; ` +
103
+ `use "*" and extract the value from the URI`);
104
+ }
105
+ return;
106
+ }
107
+ // Segment contains `*` mixed with other chars (e.g. `abc*`, `*x`, `a**b`).
108
+ throw new TypeError(`pattern segment "${seg}": "*" and "**" must be complete segments`);
109
+ }
110
+ /** Build a regex from a pattern containing `*` (and optionally trailing `**`). */
111
+ function toRegex(pattern) {
112
+ // Replace meta segments with placeholders that survive escaping, then
113
+ // escape, then swap placeholders for their regex equivalents.
114
+ //
115
+ // `*` (full segment) → `[^/]+` (one non-empty segment)
116
+ // `**` (trailing seg) → `.*` (zero or more remaining chars; the
117
+ // leading `/` before `**` is preserved
118
+ // by the surrounding pattern when the
119
+ // pattern has more than one segment,
120
+ // and absent when `**` is the whole
121
+ // pattern — both fall out naturally).
122
+ const ONE = "\x00ONE\x00";
123
+ const REST = "\x00REST\x00";
124
+ // Tokenize on `/` so we only swap full-segment metas.
125
+ const swapped = pattern
126
+ .split("/")
127
+ .map((seg) => seg === "*" ? ONE : seg === "**" ? REST : seg)
128
+ .join("/");
129
+ const escaped = swapped.replace(/[.+?^${}()|[\]\\]/g, "\\$&");
130
+ const body = escaped
131
+ .replaceAll(ONE, "[^/]+")
132
+ .replaceAll(REST, ".*");
133
+ return new RegExp(`^${body}$`);
134
+ }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * @module
3
+ * Peer decorators — middleware that wraps a client while preserving the
4
+ * `ProtocolInterfaceNode` shape. Applied via `peer(client, { via: [decorator] })`.
5
+ */
6
+ import type { PeerDecorator } from "./types.js";
7
+ /**
8
+ * Swallow `receive()` errors and report them as accepted.
9
+ *
10
+ * Use for best-effort push: when you want a one-peer failure to be
11
+ * logged and otherwise ignored rather than aborting the whole fan-out.
12
+ * Every other method (read, observe, status) is passed through
13
+ * unchanged so bridges and observers behave normally.
14
+ *
15
+ * ```ts
16
+ * peer(remoteClient, { via: [bestEffort] })
17
+ * ```
18
+ */
19
+ export declare const bestEffort: PeerDecorator;
20
+ //# sourceMappingURL=decorators.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"decorators.d.ts","sourceRoot":"","sources":["../../../../../../../../src/deps/jsr.io/@bandeira-tech/b3nd-core/0.22.0/src/network/decorators.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,UAAU,EAAE,aAiBvB,CAAC"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * @module
3
+ * Peer decorators — middleware that wraps a client while preserving the
4
+ * `ProtocolInterfaceNode` shape. Applied via `peer(client, { via: [decorator] })`.
5
+ */
6
+ /**
7
+ * Swallow `receive()` errors and report them as accepted.
8
+ *
9
+ * Use for best-effort push: when you want a one-peer failure to be
10
+ * logged and otherwise ignored rather than aborting the whole fan-out.
11
+ * Every other method (read, observe, status) is passed through
12
+ * unchanged so bridges and observers behave normally.
13
+ *
14
+ * ```ts
15
+ * peer(remoteClient, { via: [bestEffort] })
16
+ * ```
17
+ */
18
+ export const bestEffort = (client) => ({
19
+ async receive(msgs) {
20
+ try {
21
+ return await client.receive(msgs);
22
+ }
23
+ catch (err) {
24
+ const msg = err instanceof Error ? err.message : String(err);
25
+ console.warn(`[peer] best-effort receive failed: ${msg}`);
26
+ return msgs.map(() => ({ accepted: true }));
27
+ }
28
+ },
29
+ read: (urls) => client.read(urls),
30
+ observe: (urls, signal) => client.observe(urls, signal),
31
+ // bestEffort decorator preserves the underlying client's observe
32
+ // and read shape; the only enhancement is non-fatal receive.
33
+ status: () => client.status(),
34
+ });
@@ -0,0 +1,46 @@
1
+ /**
2
+ * @module
3
+ * `network(target, peers, policies?, opts?)` — the participant verb.
4
+ *
5
+ * Subscribes each peer's observe stream, passes events through the
6
+ * (optional) chain of `Policy.receive` hooks, and forwards yielded
7
+ * events into `target.receive`. Returns an async unbind that aborts
8
+ * every peer loop and awaits clean teardown.
9
+ *
10
+ * ```ts
11
+ * import { network, peer, pathVector } from "@bandeira-tech/b3nd-core/network";
12
+ *
13
+ * const unbind = network(rig, peers); // no policies
14
+ * const unbind = network(rig, peers, [myPolicy]); // one policy
15
+ * const unbind = network(rig, peers, [filter, tellAndRead], { // multiple, chained
16
+ * pattern: "mutable://chat/**",
17
+ * });
18
+ *
19
+ * await unbind();
20
+ * ```
21
+ *
22
+ * `network()` is not a `ProtocolInterfaceNode`. Passing a peer list to
23
+ * a strategy factory (`flood(peers)`, `pathVector(peers)`) is how you
24
+ * build the remote-client shape for `connection()`.
25
+ */
26
+ import type { ProtocolInterfaceNode } from "../types/types.js";
27
+ import type { NetworkOptions, Peer, Policy } from "./types.js";
28
+ /**
29
+ * Wire peers into a target's `receive` pipeline.
30
+ *
31
+ * Returns an async unbind; calling it aborts each peer's observe signal
32
+ * and awaits the per-peer loops so teardown is resource-sanitizer-clean.
33
+ * Idempotent — calling twice is a no-op.
34
+ */
35
+ export declare function network(target: Pick<ProtocolInterfaceNode, "receive">, peers: Peer[], policies?: Policy[], opts?: NetworkOptions): () => Promise<void>;
36
+ /**
37
+ * Shared peer-list validation. Exported for strategy factories that
38
+ * want the same uniqueness / non-empty guarantees.
39
+ *
40
+ * @internal
41
+ */
42
+ export declare function validatePeers(peers: Peer[]): {
43
+ originId: string;
44
+ peers: readonly Peer[];
45
+ };
46
+ //# sourceMappingURL=network.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"network.d.ts","sourceRoot":"","sources":["../../../../../../../../src/deps/jsr.io/@bandeira-tech/b3nd-core/0.22.0/src/network/network.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,KAAK,EAAU,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AACvE,OAAO,KAAK,EAAc,cAAc,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAE3E;;;;;;GAMG;AACH,wBAAgB,OAAO,CACrB,MAAM,EAAE,IAAI,CAAC,qBAAqB,EAAE,SAAS,CAAC,EAC9C,KAAK,EAAE,IAAI,EAAE,EACb,QAAQ,GAAE,MAAM,EAAO,EACvB,IAAI,GAAE,cAAmB,GACxB,MAAM,OAAO,CAAC,IAAI,CAAC,CAiBrB;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAC3B,KAAK,EAAE,IAAI,EAAE,GACZ;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,SAAS,IAAI,EAAE,CAAA;CAAE,CAe9C"}
@@ -0,0 +1,131 @@
1
+ /**
2
+ * @module
3
+ * `network(target, peers, policies?, opts?)` — the participant verb.
4
+ *
5
+ * Subscribes each peer's observe stream, passes events through the
6
+ * (optional) chain of `Policy.receive` hooks, and forwards yielded
7
+ * events into `target.receive`. Returns an async unbind that aborts
8
+ * every peer loop and awaits clean teardown.
9
+ *
10
+ * ```ts
11
+ * import { network, peer, pathVector } from "@bandeira-tech/b3nd-core/network";
12
+ *
13
+ * const unbind = network(rig, peers); // no policies
14
+ * const unbind = network(rig, peers, [myPolicy]); // one policy
15
+ * const unbind = network(rig, peers, [filter, tellAndRead], { // multiple, chained
16
+ * pattern: "mutable://chat/**",
17
+ * });
18
+ *
19
+ * await unbind();
20
+ * ```
21
+ *
22
+ * `network()` is not a `ProtocolInterfaceNode`. Passing a peer list to
23
+ * a strategy factory (`flood(peers)`, `pathVector(peers)`) is how you
24
+ * build the remote-client shape for `connection()`.
25
+ */
26
+ /**
27
+ * Wire peers into a target's `receive` pipeline.
28
+ *
29
+ * Returns an async unbind; calling it aborts each peer's observe signal
30
+ * and awaits the per-peer loops so teardown is resource-sanitizer-clean.
31
+ * Idempotent — calling twice is a no-op.
32
+ */
33
+ export function network(target, peers, policies = [], opts = {}) {
34
+ const { originId, peers: frozenPeers } = validatePeers(peers);
35
+ const pattern = opts.pattern ?? "**";
36
+ const onError = opts.onError ?? (() => { });
37
+ const ac = new AbortController();
38
+ const tasks = frozenPeers.map((peer) => runPeer(peer, policies, originId, target, pattern, ac.signal, onError));
39
+ let unbound = false;
40
+ return async () => {
41
+ if (unbound)
42
+ return;
43
+ unbound = true;
44
+ ac.abort();
45
+ await Promise.allSettled(tasks);
46
+ };
47
+ }
48
+ /**
49
+ * Shared peer-list validation. Exported for strategy factories that
50
+ * want the same uniqueness / non-empty guarantees.
51
+ *
52
+ * @internal
53
+ */
54
+ export function validatePeers(peers) {
55
+ if (!peers || peers.length === 0) {
56
+ throw new Error("peers[] must be non-empty");
57
+ }
58
+ const ids = new Set();
59
+ for (const p of peers) {
60
+ if (ids.has(p.id)) {
61
+ throw new Error(`duplicate peer id "${p.id}"`);
62
+ }
63
+ ids.add(p.id);
64
+ }
65
+ return {
66
+ originId: `net-${crypto.randomUUID()}`,
67
+ peers: Object.freeze([...peers]),
68
+ };
69
+ }
70
+ // ── Internals: the bridge ─────────────────────────────────────────────
71
+ /**
72
+ * One peer's subscription loop. Kept as a separate function (not an
73
+ * inline IIFE) so error stacks identify the misbehaving peer.
74
+ */
75
+ async function runPeer(peer, policies, originId, target, pattern, signal, onError) {
76
+ const ctx = { originId, source: peer };
77
+ const hooks = policies
78
+ .map((p) => p.receive?.bind(p))
79
+ .filter((r) => r !== undefined);
80
+ try {
81
+ for await (const uris of peer.client.observe([pattern], signal)) {
82
+ if (signal.aborted)
83
+ break;
84
+ // INV bundles: the observe event lists uris that changed. Pull
85
+ // the current state from the peer for each so policies see
86
+ // Outputs with the content attached.
87
+ for (const noteUri of uris) {
88
+ if (signal.aborted)
89
+ break;
90
+ const fetched = await peer.client.read([noteUri]);
91
+ for (const ev of fetched) {
92
+ for await (const out of foldReceive(ev, hooks, peer, ctx)) {
93
+ if (signal.aborted)
94
+ break;
95
+ await forward(target, out, onError, peer.id);
96
+ }
97
+ }
98
+ }
99
+ }
100
+ }
101
+ catch (err) {
102
+ onError(toError(err), { peerId: peer.id });
103
+ }
104
+ }
105
+ /**
106
+ * Fold `hooks` over `ev`: apply `hooks[0]` to `ev`, then `hooks[1]` to
107
+ * each event yielded, and so on. The result is one merged stream of
108
+ * Outputs the target should receive.
109
+ */
110
+ async function* foldReceive(ev, hooks, source, ctx) {
111
+ if (hooks.length === 0) {
112
+ yield ev;
113
+ return;
114
+ }
115
+ const [first, ...rest] = hooks;
116
+ for await (const out of first(ev, source, ctx)) {
117
+ yield* foldReceive(out, rest, source, ctx);
118
+ }
119
+ }
120
+ /** Forward one Output into the target's `receive`. Swallows errors via `onError`. */
121
+ async function forward(target, ev, onError, peerId) {
122
+ try {
123
+ await target.receive([ev]);
124
+ }
125
+ catch (err) {
126
+ onError(toError(err), { peerId });
127
+ }
128
+ }
129
+ function toError(e) {
130
+ return e instanceof Error ? e : new Error(String(e));
131
+ }
@@ -0,0 +1,27 @@
1
+ /**
2
+ * @module
3
+ * `peer()` — construct a Peer from a client, optionally applying decorators
4
+ * and pinning an explicit id.
5
+ */
6
+ import type { ProtocolInterfaceNode } from "../types/types.js";
7
+ import type { Peer, PeerDecorator } from "./types.js";
8
+ /**
9
+ * Create a Peer from a client.
10
+ *
11
+ * @example
12
+ * ```ts
13
+ * // Anonymous peer — id auto-assigned at runtime
14
+ * peer(remoteClient)
15
+ *
16
+ * // Protocol with path-vector loop avoidance — id matches the peer's pubkey
17
+ * peer(client, { id: peerPubkeyHex })
18
+ *
19
+ * // Stack middleware
20
+ * peer(client, { via: [bestEffort, rateLimited(10)] })
21
+ * ```
22
+ */
23
+ export declare function peer(client: ProtocolInterfaceNode, opts?: {
24
+ id?: string;
25
+ via?: PeerDecorator[];
26
+ }): Peer;
27
+ //# sourceMappingURL=peer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"peer.d.ts","sourceRoot":"","sources":["../../../../../../../../src/deps/jsr.io/@bandeira-tech/b3nd-core/0.22.0/src/network/peer.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AAC/D,OAAO,KAAK,EAAE,IAAI,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEtD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,IAAI,CAClB,MAAM,EAAE,qBAAqB,EAC7B,IAAI,GAAE;IAAE,EAAE,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,aAAa,EAAE,CAAA;CAAO,GAChD,IAAI,CAON"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * @module
3
+ * `peer()` — construct a Peer from a client, optionally applying decorators
4
+ * and pinning an explicit id.
5
+ */
6
+ /**
7
+ * Create a Peer from a client.
8
+ *
9
+ * @example
10
+ * ```ts
11
+ * // Anonymous peer — id auto-assigned at runtime
12
+ * peer(remoteClient)
13
+ *
14
+ * // Protocol with path-vector loop avoidance — id matches the peer's pubkey
15
+ * peer(client, { id: peerPubkeyHex })
16
+ *
17
+ * // Stack middleware
18
+ * peer(client, { via: [bestEffort, rateLimited(10)] })
19
+ * ```
20
+ */
21
+ export function peer(client, opts = {}) {
22
+ const id = opts.id ?? `peer-${crypto.randomUUID()}`;
23
+ const decorated = (opts.via ?? []).reduce((c, decorator) => decorator(c), client);
24
+ return { id, client: decorated };
25
+ }
@@ -0,0 +1,59 @@
1
+ /**
2
+ * @module
3
+ * `flood(peers)` — the baseline remote-client strategy.
4
+ *
5
+ * Returns a `ProtocolInterfaceNode` that:
6
+ * - **receive**: fans every write out to every peer in parallel
7
+ * - **read**: tries peers in order and returns the first successful hit
8
+ * - **observe**: merges every peer's observe stream into one iterator
9
+ * - **status**: aggregates peer health (healthy / degraded / unhealthy)
10
+ *
11
+ * Use as a rig connection:
12
+ *
13
+ * ```ts
14
+ * import { connection, Rig } from "@bandeira-tech/b3nd-core";
15
+ * import { flood, peer } from "@bandeira-tech/b3nd-core/network";
16
+ *
17
+ * const peers = connection(flood([peer(remoteClient)]), ["**"]);
18
+ * const rig = new Rig({
19
+ * routes: { receive: [peers], read: [peers], observe: [peers] },
20
+ * });
21
+ * ```
22
+ *
23
+ * For bidirectional signed meshes that need loop avoidance, use
24
+ * `pathVector(peers)` instead — same shape, plus a signer-chain filter
25
+ * on outbound.
26
+ *
27
+ * ## Error handling
28
+ *
29
+ * - `receive` propagates transport-level failures — one peer rejecting
30
+ * aborts the fan-out. Wrap individual peers with the `bestEffort`
31
+ * decorator if you want per-peer failures to be non-fatal.
32
+ * - `read` falls through: a failing peer is skipped, and if no peer
33
+ * returns a hit the result is a `not-found` per input URI.
34
+ * - `observe` silently drops a peer whose stream throws (the merged
35
+ * stream keeps flowing from the remaining peers). This is
36
+ * intentional — `flood` is a one-shot PIN with no `onError` hook.
37
+ * For observability over the inbound side, use `network()`, which
38
+ * accepts an `onError` callback and reports per-peer failures.
39
+ * - `status` aggregates; individual unhealthy peers degrade the
40
+ * overall status rather than throwing.
41
+ */
42
+ import type { Output, ProtocolInterfaceNode } from "../../types/types.js";
43
+ import type { Peer } from "../types.js";
44
+ /**
45
+ * Build a flood PIN from peers. Peers must have unique ids and the list
46
+ * must be non-empty.
47
+ */
48
+ export declare function flood(peers: Peer[]): ProtocolInterfaceNode;
49
+ /**
50
+ * Internal shared implementation used by `flood`, `pathVector`, and
51
+ * `tellAndRead.outbound`. The `transform` rewrites the per-peer
52
+ * outbound batch: return `msgs` unchanged to flood as-is, return a
53
+ * subset to filter, return rewritten outputs to change what the peer
54
+ * receives, or return `[]` to skip the peer entirely.
55
+ *
56
+ * @internal
57
+ */
58
+ export declare function floodImpl(originId: string, peers: readonly Peer[], transform: (msgs: Output[], peer: Peer) => Output[]): ProtocolInterfaceNode;
59
+ //# sourceMappingURL=flood.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flood.d.ts","sourceRoot":"","sources":["../../../../../../../../../src/deps/jsr.io/@bandeira-tech/b3nd-core/0.22.0/src/network/policies/flood.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AAEH,OAAO,KAAK,EACV,MAAM,EACN,qBAAqB,EAGtB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AAGxC;;;GAGG;AACH,wBAAgB,KAAK,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,qBAAqB,CAG1D;AAmBD;;;;;;;;GAQG;AACH,wBAAgB,SAAS,CACvB,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,SAAS,IAAI,EAAE,EACtB,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,IAAI,KAAK,MAAM,EAAE,GAClD,qBAAqB,CAsHvB"}