@fuzdev/fuz_app 0.118.0 → 0.120.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.
Files changed (169) hide show
  1. package/dist/actions/CLAUDE.md +301 -73
  2. package/dist/actions/action_rpc.d.ts.map +1 -1
  3. package/dist/actions/action_rpc.js +16 -1
  4. package/dist/actions/connection_closer.d.ts +136 -51
  5. package/dist/actions/connection_closer.d.ts.map +1 -1
  6. package/dist/actions/connection_closer.js +133 -34
  7. package/dist/actions/register_action_ws.d.ts +176 -24
  8. package/dist/actions/register_action_ws.d.ts.map +1 -1
  9. package/dist/actions/register_action_ws.js +581 -241
  10. package/dist/actions/register_ws_endpoint.d.ts +1 -0
  11. package/dist/actions/register_ws_endpoint.d.ts.map +1 -1
  12. package/dist/actions/register_ws_endpoint.js +1 -0
  13. package/dist/actions/socket.svelte.d.ts +43 -9
  14. package/dist/actions/socket.svelte.d.ts.map +1 -1
  15. package/dist/actions/socket.svelte.js +59 -8
  16. package/dist/actions/transports.d.ts +55 -1
  17. package/dist/actions/transports.d.ts.map +1 -1
  18. package/dist/actions/transports.js +55 -1
  19. package/dist/actions/transports_ws_auth_guard.d.ts +53 -67
  20. package/dist/actions/transports_ws_auth_guard.d.ts.map +1 -1
  21. package/dist/actions/transports_ws_auth_guard.js +87 -111
  22. package/dist/actions/transports_ws_backend.d.ts +212 -30
  23. package/dist/actions/transports_ws_backend.d.ts.map +1 -1
  24. package/dist/actions/transports_ws_backend.js +327 -70
  25. package/dist/actions/ws_endpoint_spec.d.ts +26 -13
  26. package/dist/actions/ws_endpoint_spec.d.ts.map +1 -1
  27. package/dist/auth/CLAUDE.md +113 -32
  28. package/dist/auth/account_actions.d.ts +12 -18
  29. package/dist/auth/account_actions.d.ts.map +1 -1
  30. package/dist/auth/account_actions.js +47 -33
  31. package/dist/auth/account_routes.d.ts +5 -11
  32. package/dist/auth/account_routes.d.ts.map +1 -1
  33. package/dist/auth/account_routes.js +40 -36
  34. package/dist/auth/account_schema.d.ts +27 -0
  35. package/dist/auth/account_schema.d.ts.map +1 -1
  36. package/dist/auth/account_schema.js +22 -0
  37. package/dist/auth/admin_actions.d.ts +8 -17
  38. package/dist/auth/admin_actions.d.ts.map +1 -1
  39. package/dist/auth/admin_actions.js +33 -21
  40. package/dist/auth/api_token_queries.d.ts +23 -2
  41. package/dist/auth/api_token_queries.d.ts.map +1 -1
  42. package/dist/auth/api_token_queries.js +39 -3
  43. package/dist/auth/audit_emitter.d.ts +61 -38
  44. package/dist/auth/audit_emitter.d.ts.map +1 -1
  45. package/dist/auth/audit_emitter.js +57 -14
  46. package/dist/auth/audit_log_route_schema.js +7 -7
  47. package/dist/auth/audit_log_routes.d.ts +31 -6
  48. package/dist/auth/audit_log_routes.d.ts.map +1 -1
  49. package/dist/auth/audit_log_routes.js +85 -11
  50. package/dist/auth/audit_log_schema.d.ts +48 -0
  51. package/dist/auth/audit_log_schema.d.ts.map +1 -1
  52. package/dist/auth/audit_log_schema.js +58 -0
  53. package/dist/auth/cleanup.d.ts +203 -28
  54. package/dist/auth/cleanup.d.ts.map +1 -1
  55. package/dist/auth/cleanup.js +222 -26
  56. package/dist/auth/deps.d.ts +31 -3
  57. package/dist/auth/deps.d.ts.map +1 -1
  58. package/dist/auth/resolved_auth.d.ts +105 -0
  59. package/dist/auth/resolved_auth.d.ts.map +1 -0
  60. package/dist/auth/resolved_auth.js +124 -0
  61. package/dist/auth/session_middleware.d.ts +8 -1
  62. package/dist/auth/session_middleware.d.ts.map +1 -1
  63. package/dist/auth/session_middleware.js +9 -3
  64. package/dist/auth/session_queries.d.ts +31 -6
  65. package/dist/auth/session_queries.d.ts.map +1 -1
  66. package/dist/auth/session_queries.js +31 -6
  67. package/dist/auth/standard_rpc_actions.d.ts +9 -9
  68. package/dist/auth/standard_rpc_actions.d.ts.map +1 -1
  69. package/dist/auth/standard_rpc_actions.js +3 -3
  70. package/dist/db/CLAUDE.md +19 -8
  71. package/dist/db/fact_disk_storage.d.ts +10 -8
  72. package/dist/db/fact_disk_storage.d.ts.map +1 -1
  73. package/dist/db/fact_disk_storage.js +13 -17
  74. package/dist/db/fact_store.d.ts +39 -38
  75. package/dist/db/fact_store.d.ts.map +1 -1
  76. package/dist/db/fact_store.js +48 -86
  77. package/dist/db/file_fact_url.d.ts +6 -6
  78. package/dist/db/file_fact_url.js +6 -6
  79. package/dist/db/status.d.ts +58 -1
  80. package/dist/db/status.d.ts.map +1 -1
  81. package/dist/db/status.js +205 -77
  82. package/dist/http/CLAUDE.md +7 -3
  83. package/dist/http/db_routes.d.ts +6 -2
  84. package/dist/http/db_routes.d.ts.map +1 -1
  85. package/dist/http/db_routes.js +2 -2
  86. package/dist/periodic.d.ts +80 -0
  87. package/dist/periodic.d.ts.map +1 -0
  88. package/dist/periodic.js +122 -0
  89. package/dist/realtime/sse_auth_guard.d.ts +79 -49
  90. package/dist/realtime/sse_auth_guard.d.ts.map +1 -1
  91. package/dist/realtime/sse_auth_guard.js +103 -73
  92. package/dist/realtime/subscriber_registry.d.ts +119 -8
  93. package/dist/realtime/subscriber_registry.d.ts.map +1 -1
  94. package/dist/realtime/subscriber_registry.js +184 -25
  95. package/dist/server/app_backend.d.ts +12 -1
  96. package/dist/server/app_backend.d.ts.map +1 -1
  97. package/dist/server/app_backend.js +5 -2
  98. package/dist/server/app_server.d.ts +55 -15
  99. package/dist/server/app_server.d.ts.map +1 -1
  100. package/dist/server/app_server.js +41 -13
  101. package/dist/server/serve_fact_route.d.ts +10 -7
  102. package/dist/server/serve_fact_route.d.ts.map +1 -1
  103. package/dist/server/serve_fact_route.js +11 -8
  104. package/dist/testing/CLAUDE.md +140 -48
  105. package/dist/testing/app_server.d.ts +2 -2
  106. package/dist/testing/app_server.d.ts.map +1 -1
  107. package/dist/testing/app_server.js +11 -2
  108. package/dist/testing/audit_drift_guard.d.ts +1 -48
  109. package/dist/testing/audit_drift_guard.d.ts.map +1 -1
  110. package/dist/testing/audit_drift_guard.js +0 -42
  111. package/dist/testing/connection_closer_helpers.d.ts +9 -22
  112. package/dist/testing/connection_closer_helpers.d.ts.map +1 -1
  113. package/dist/testing/connection_closer_helpers.js +11 -17
  114. package/dist/testing/cross_backend/capabilities.d.ts +67 -13
  115. package/dist/testing/cross_backend/capabilities.d.ts.map +1 -1
  116. package/dist/testing/cross_backend/capabilities.js +27 -12
  117. package/dist/testing/cross_backend/default_backend_configs.d.ts.map +1 -1
  118. package/dist/testing/cross_backend/default_backend_configs.js +21 -0
  119. package/dist/testing/cross_backend/role_grant_offer_notification_ws.d.ts.map +1 -1
  120. package/dist/testing/cross_backend/role_grant_offer_notification_ws.js +13 -4
  121. package/dist/testing/cross_backend/rust_spine_stub_backend_config.d.ts.map +1 -1
  122. package/dist/testing/cross_backend/rust_spine_stub_backend_config.js +4 -1
  123. package/dist/testing/cross_backend/session_cap.d.ts +13 -0
  124. package/dist/testing/cross_backend/session_cap.d.ts.map +1 -1
  125. package/dist/testing/cross_backend/session_cap.js +55 -19
  126. package/dist/testing/cross_backend/sse_round_trip.d.ts +10 -2
  127. package/dist/testing/cross_backend/sse_round_trip.d.ts.map +1 -1
  128. package/dist/testing/cross_backend/sse_round_trip.js +93 -26
  129. package/dist/testing/cross_backend/testing_server_bun.js +4 -4
  130. package/dist/testing/cross_backend/ts_spine_backend_config.d.ts.map +1 -1
  131. package/dist/testing/cross_backend/ts_spine_backend_config.js +10 -2
  132. package/dist/testing/cross_backend/ws_connection_cap.d.ts +35 -0
  133. package/dist/testing/cross_backend/ws_connection_cap.d.ts.map +1 -0
  134. package/dist/testing/cross_backend/ws_connection_cap.js +131 -0
  135. package/dist/testing/cross_backend/ws_round_trip.d.ts +2 -1
  136. package/dist/testing/cross_backend/ws_round_trip.d.ts.map +1 -1
  137. package/dist/testing/cross_backend/ws_round_trip.js +156 -19
  138. package/dist/testing/sse_round_trip.d.ts +6 -4
  139. package/dist/testing/sse_round_trip.d.ts.map +1 -1
  140. package/dist/testing/stubs.d.ts +1 -1
  141. package/dist/testing/stubs.d.ts.map +1 -1
  142. package/dist/testing/stubs.js +6 -4
  143. package/dist/testing/transports/sse_transport.d.ts +1 -1
  144. package/dist/testing/transports/ws_client.d.ts +7 -0
  145. package/dist/testing/transports/ws_client.d.ts.map +1 -1
  146. package/dist/testing/transports/ws_raw_client.d.ts +51 -0
  147. package/dist/testing/transports/ws_raw_client.d.ts.map +1 -0
  148. package/dist/testing/transports/ws_raw_client.js +156 -0
  149. package/dist/testing/transports/ws_transport.d.ts +28 -0
  150. package/dist/testing/transports/ws_transport.d.ts.map +1 -1
  151. package/dist/testing/transports/ws_transport.js +45 -1
  152. package/dist/testing/ws_round_trip.d.ts +8 -3
  153. package/dist/testing/ws_round_trip.d.ts.map +1 -1
  154. package/dist/testing/ws_round_trip.js +19 -4
  155. package/dist/ui/AdminAccounts.svelte +4 -3
  156. package/dist/ui/AdminAccounts.svelte.d.ts.map +1 -1
  157. package/dist/ui/AdminOverview.svelte +3 -1
  158. package/dist/ui/AdminOverview.svelte.d.ts.map +1 -1
  159. package/dist/ui/CLAUDE.md +22 -6
  160. package/dist/ui/admin_accounts_state.svelte.d.ts +15 -1
  161. package/dist/ui/admin_accounts_state.svelte.d.ts.map +1 -1
  162. package/dist/ui/admin_accounts_state.svelte.js +16 -0
  163. package/dist/ui/auth_state.svelte.d.ts +25 -1
  164. package/dist/ui/auth_state.svelte.d.ts.map +1 -1
  165. package/dist/ui/auth_state.svelte.js +28 -1
  166. package/package.json +2 -2
  167. package/dist/server/file_fact_fetcher.d.ts +0 -42
  168. package/dist/server/file_fact_fetcher.d.ts.map +0 -1
  169. package/dist/server/file_fact_fetcher.js +0 -60
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Filesystem CAS for externally-stored fact bytes — the disk half of
2
+ * Filesystem CAS for fact bytes stored outside Postgres — the disk half of
3
3
  * `PgFactStore`, threaded over the injectable `runtime/*Deps` rather than raw
4
4
  * `node:fs`, so it runs unchanged under Node, Deno, and a mock runtime.
5
5
  *
@@ -204,27 +204,23 @@ export const write_fact_bytes_to_disk = async (deps, facts_dir, hash, bytes) =>
204
204
  return mint_file_fact_url(shard, rest);
205
205
  };
206
206
  /**
207
- * `FactExternalFetcher` reading from the `<facts_dir>/<shard>/<rest>` layout the
208
- * writers above produce, over the injected `*Deps`. Does NOT verify hash content
209
- * — `PgFactStore.get` calls `fact_hash_verify(hash, bytes)` after the fetch and
210
- * returns `null` on mismatch.
207
+ * Read a disk-backed fact's bytes from the `<facts_dir>/<shard>/<rest>` layout
208
+ * the writers above produce, addressed by the row's `file:<shard>/<rest>`
209
+ * `external_url`. The only read of external bytes `PgFactStore.get` performs.
210
+ * Does NOT verify hash content — `PgFactStore.get` calls
211
+ * `fact_hash_verify(hash, bytes)` on the result and returns `null` on mismatch.
211
212
  *
212
213
  * Defense at the read seam is the `FILE_FACT_URL_PATTERN` regex (via
213
214
  * `parse_file_fact_url`) — `..` segments, foreign schemes, and non-hex chars
214
215
  * fail before any disk access.
216
+ *
217
+ * @throws Error when `url` is not a `file:<shard>/<rest>` URL, or when the read fails
215
218
  */
216
- export const create_disk_fact_fetcher = (deps, facts_dir) => {
217
- const resolve_path = (url) => {
218
- const parsed = parse_file_fact_url(url);
219
- if (!parsed)
220
- throw new Error(`invalid file fact url: ${url}`);
221
- return join(facts_dir, parsed.shard, parsed.rest);
222
- };
223
- return {
224
- fetch_bytes: (url) => deps.read_file(resolve_path(url)),
225
- // `async` funnels a synchronous `resolve_path` throw into a rejection.
226
- fetch_stream: async (url) => deps.read_file_stream(resolve_path(url))
227
- };
219
+ export const read_fact_bytes_from_disk = async (deps, facts_dir, url) => {
220
+ const parsed = parse_file_fact_url(url);
221
+ if (!parsed)
222
+ throw new Error('invalid file fact url');
223
+ return deps.read_file(join(facts_dir, parsed.shard, parsed.rest));
228
224
  };
229
225
  /**
230
226
  * Reap stale temp files left under `<facts_dir>/.tmp/` by a hard crash (SIGKILL
@@ -4,14 +4,13 @@
4
4
  * Wraps the raw queries in `db/fact_queries.ts` with the lifecycle the
5
5
  * `FactStore` interface promises:
6
6
  *
7
- * - sync hash on `put`, stream hash on `put_ref` (counting bytes against
8
- * the caller-supplied `size`)
7
+ * - sync hash on `put`; `put_stream` hashes as it streams
9
8
  * - idempotent insert (`ON CONFLICT DO NOTHING` in the queries layer)
10
9
  * - JSON ref auto-extraction when `content_type` signals JSON and the
11
10
  * caller didn't pass an explicit `refs` array
12
- * - verify-on-read for external content; embedded reads skip verify
11
+ * - verify-on-read for disk-backed content; embedded reads skip verify
13
12
  * because PG storage IS the hash table
14
- * - mismatched external bytes return `null` + log warning (treat as
13
+ * - mismatched disk bytes return `null` + log warning (treat as
15
14
  * unavailable; GC / repair is a separate concern)
16
15
  *
17
16
  * Embedded vs disk split: writes route by size. Bytes `<= embedded_threshold`
@@ -19,10 +18,20 @@
19
18
  * `<facts_dir>/<shard>/<rest>` (`db/fact_disk_storage.ts`) and the row records a
20
19
  * `file:<shard>/<rest>` `external_url`. `put` takes fully-buffered bytes;
21
20
  * `put_stream` is the bounded-memory streaming twin (hash BLAKE3 + SHA-256 in
22
- * one pass, spill past the threshold, enforce `max_bytes` / `ENOSPC`). Both need
23
- * `disk_root` + `fs` (the `runtime/*Deps`) configured for the over-threshold
24
- * path; without them, an oversize `put` throws and the caller must `put_ref`
25
- * against an externally-managed URL (federation / stub-fetcher tests).
21
+ * one pass, spill past the threshold, enforce `max_bytes` / `ENOSPC`). `put`
22
+ * needs `disk_root` + `fs` (the `runtime/*Deps`) for the over-threshold path
23
+ * only: without them the store has nowhere to put bytes it cannot embed, and an
24
+ * oversize `put` throws. `put_stream` needs `fs` for a body of any size — with
25
+ * none it throws before reading the stream — and `disk_root` for the
26
+ * over-threshold spill: with none, a body that passes the threshold throws
27
+ * `PayloadTooLargeError`.
28
+ *
29
+ * Every `external_url` the store writes is a `file:<shard>/<rest>` URL into its
30
+ * own disk CAS, and that CAS is the only place `get` reads external bytes from
31
+ * — the store makes no network request. The column is free text
32
+ * (`query_put_fact` inserts what it is given), so `get` treats a row whose
33
+ * `external_url` has any other shape, and any external row on a store with no
34
+ * disk CAS configured, as unavailable: a warning and `null`.
26
35
  *
27
36
  * @module
28
37
  */
@@ -33,13 +42,6 @@ import type { FactMeta, FactPutOptions, FactStore, PutStreamOutcome } from '@fuz
33
42
  import { type FactDiskStorageDeps } from './fact_disk_storage.js';
34
43
  /** Default embedded-vs-referenced cutoff (1 MiB). */
35
44
  export declare const FACT_EMBEDDED_THRESHOLD_DEFAULT: number;
36
- /** Fetcher abstraction so tests can stub external URL retrieval. */
37
- export interface FactExternalFetcher {
38
- fetch_stream: (url: string) => Promise<ReadableStream<Uint8Array>>;
39
- fetch_bytes: (url: string) => Promise<Uint8Array>;
40
- }
41
- /** Default fetcher backed by `globalThis.fetch`. */
42
- export declare const create_default_fetcher: () => FactExternalFetcher;
43
45
  /**
44
46
  * Construction-time deps for `PgFactStore`.
45
47
  *
@@ -52,18 +54,19 @@ export declare const create_default_fetcher: () => FactExternalFetcher;
52
54
  *
53
55
  * `disk_root` is the facts directory backing the `<shard>/<rest>` disk CAS;
54
56
  * `fs` supplies the filesystem capabilities (a `RuntimeDeps` satisfies it).
55
- * When both are set, oversize `put` + `put_stream` write to disk and the
56
- * default `fetcher` reads from it. When unset, oversize `put`/`put_stream`
57
- * spill throws and reads fall back to the `globalThis.fetch`-backed default
58
- * fetcher (or an injected stub). `log` is optional — the only call site is the
59
- * verify-mismatch warning path.
57
+ * When both are set, oversize `put` + `put_stream` write to disk and `get`
58
+ * reads disk-backed facts from it. When either is unset the store writes
59
+ * embedded rows only and `get` returns `null` for an external row: an oversize
60
+ * `put` throws; `put_stream` throws for a body of any size when `fs` is unset,
61
+ * and with `fs` but no `disk_root` throws `PayloadTooLargeError` for a body
62
+ * that passes the threshold. `log` is optional — its call sites are the
63
+ * warnings on the paths where `get` returns `null` for an external row.
60
64
  */
61
65
  export interface PgFactStoreDeps {
62
66
  deps: QueryDeps;
63
67
  embedded_threshold?: number;
64
68
  disk_root?: string;
65
69
  fs?: FactDiskStorageDeps;
66
- fetcher?: FactExternalFetcher;
67
70
  log?: Logger;
68
71
  }
69
72
  /**
@@ -77,8 +80,8 @@ export declare class PgFactStore implements FactStore {
77
80
  * Store fully-buffered bytes, routing by size: `<= embedded_threshold` into
78
81
  * the PG `bytes` column; larger into the disk CAS (when `disk_root` + `fs`
79
82
  * are configured) at `<facts_dir>/<shard>/<rest>` with a `file:` URL. Oversize
80
- * without a disk root throws so the caller routes it through `put_ref`
81
- * explicitly. Idempotent — `ON CONFLICT DO NOTHING` + content-addressed disk
83
+ * without a disk root throws: the store has nowhere to put bytes it cannot
84
+ * embed. Idempotent — `ON CONFLICT DO NOTHING` + content-addressed disk
82
85
  * filenames make a re-write a no-op.
83
86
  */
84
87
  put(bytes: Uint8Array, options?: FactPutOptions): Promise<FactHash>;
@@ -101,16 +104,14 @@ export declare class PgFactStore implements FactStore {
101
104
  */
102
105
  put_stream(stream: ReadableStream<Uint8Array>, max_bytes: number, options?: FactPutOptions): Promise<PutStreamOutcome>;
103
106
  /**
104
- * Stream-hash external content and record `(hash, external_url, size)`.
105
- * Throws when the streamed byte count disagrees with the caller's
106
- * declared `size` — a size mismatch usually means the upload was
107
- * truncated or the URL points at the wrong content.
108
- */
109
- put_ref(url: string, size: number, options?: FactPutOptions): Promise<FactHash>;
110
- /**
111
- * Retrieve bytes. Embedded reads return PG bytes directly; external
112
- * reads fetch + verify and return `null` (with a warning log) when
113
- * the bytes don't match the stored hash.
107
+ * Retrieve bytes. Embedded reads return PG bytes directly. An external
108
+ * read comes from the disk CAS and nowhere else: the bytes at the row's
109
+ * `file:<shard>/<rest>` URL are read and verified against the hash.
110
+ *
111
+ * Returns `null`, with a warning log, for an external row the store cannot
112
+ * serve — an `external_url` that is not a `file:<shard>/<rest>` URL, no disk
113
+ * CAS configured (`disk_root` + `fs`), a failed disk read, or bytes that
114
+ * don't match the hash. Never makes a network request.
114
115
  */
115
116
  get(hash: FactHash): Promise<Uint8Array | null>;
116
117
  has(hash: FactHash): Promise<boolean>;
@@ -126,11 +127,11 @@ export declare class PgFactStore implements FactStore {
126
127
  * NOT verify the fact is unreferenced — that policy lives one layer
127
128
  * up (the orphan-fact admin surface in the consumer; a future GC walker).
128
129
  *
129
- * External-URL unlink is the caller's responsibility — the store
130
- * doesn't know how to resolve `file:` / `s3:` / etc. URLs to a
131
- * deletable handle. Caller iterates the returned `external_url`
132
- * (when non-null) and dispatches to the appropriate cleanup
133
- * routine. Mirrors the read-side `FactExternalFetcher` split.
130
+ * The disk file of a disk-backed fact is NOT unlinked — that is the
131
+ * caller's responsibility, using the returned `external_url` (when
132
+ * non-null): a `file:<shard>/<rest>` URL names
133
+ * `<disk_root>/<shard>/<rest>`. The value is the row's free text — parse
134
+ * it with `parse_file_fact_url` before joining a path.
134
135
  *
135
136
  * @returns `{size, external_url}` for the deleted row, or `null` if
136
137
  * no row matched the hash.
@@ -1 +1 @@
1
- {"version":3,"file":"fact_store.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/db/fact_store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAEjD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AAQtD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kCAAkC,CAAC;AACjE,OAAO,KAAK,EACX,QAAQ,EACR,cAAc,EACd,SAAS,EACT,gBAAgB,EAChB,MAAM,gCAAgC,CAAC;AAWxC,OAAO,EAIN,KAAK,mBAAmB,EACxB,MAAM,wBAAwB,CAAC;AAEhC,qDAAqD;AACrD,eAAO,MAAM,+BAA+B,QAAc,CAAC;AAE3D,oEAAoE;AACpE,MAAM,WAAW,mBAAmB;IACnC,YAAY,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC,CAAC;IACnE,WAAW,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,UAAU,CAAC,CAAC;CAClD;AAED,oDAAoD;AACpD,eAAO,MAAM,sBAAsB,QAAO,mBAkBxC,CAAC;AAEH;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,WAAW,eAAe;IAC/B,IAAI,EAAE,SAAS,CAAC;IAChB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,EAAE,CAAC,EAAE,mBAAmB,CAAC;IACzB,OAAO,CAAC,EAAE,mBAAmB,CAAC;IAC9B,GAAG,CAAC,EAAE,MAAM,CAAC;CACb;AAED;;;GAGG;AACH,qBAAa,WAAY,YAAW,SAAS;;gBAQhC,OAAO,EAAE,eAAe;IAapC;;;;;;;OAOG;IACG,GAAG,CAAC,KAAK,EAAE,UAAU,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,QAAQ,CAAC;IA+BzE;;;;;;;;;;;;;;;;OAgBG;IACG,UAAU,CACf,MAAM,EAAE,cAAc,CAAC,UAAU,CAAC,EAClC,SAAS,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE,cAAc,GACtB,OAAO,CAAC,gBAAgB,CAAC;IA6B5B;;;;;OAKG;IACG,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,QAAQ,CAAC;IAqBrF;;;;OAIG;IACG,GAAG,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;IA4B/C,GAAG,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC;IAIrC,QAAQ,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC;IAWlD,QAAQ,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IAIxD;;;;;;;;;;;;;;;;;;OAkBG;IACG,MAAM,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,GAAG,IAAI,CAAC;CAG3F"}
1
+ {"version":3,"file":"fact_store.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/db/fact_store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAEjD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AAOtD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kCAAkC,CAAC;AACjE,OAAO,KAAK,EACX,QAAQ,EACR,cAAc,EACd,SAAS,EACT,gBAAgB,EAChB,MAAM,gCAAgC,CAAC;AAYxC,OAAO,EAIN,KAAK,mBAAmB,EACxB,MAAM,wBAAwB,CAAC;AAEhC,qDAAqD;AACrD,eAAO,MAAM,+BAA+B,QAAc,CAAC;AAE3D;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,WAAW,eAAe;IAC/B,IAAI,EAAE,SAAS,CAAC;IAChB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,EAAE,CAAC,EAAE,mBAAmB,CAAC;IACzB,GAAG,CAAC,EAAE,MAAM,CAAC;CACb;AAED;;;GAGG;AACH,qBAAa,WAAY,YAAW,SAAS;;gBAOhC,OAAO,EAAE,eAAe;IAQpC;;;;;;;OAOG;IACG,GAAG,CAAC,KAAK,EAAE,UAAU,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,QAAQ,CAAC;IA+BzE;;;;;;;;;;;;;;;;OAgBG;IACG,UAAU,CACf,MAAM,EAAE,cAAc,CAAC,UAAU,CAAC,EAClC,SAAS,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE,cAAc,GACtB,OAAO,CAAC,gBAAgB,CAAC;IA6B5B;;;;;;;;;OASG;IACG,GAAG,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;IA0C/C,GAAG,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC;IAIrC,QAAQ,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC;IAWlD,QAAQ,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IAIxD;;;;;;;;;;;;;;;;;;OAkBG;IACG,MAAM,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,GAAG,IAAI,CAAC;CAG3F"}
@@ -4,14 +4,13 @@
4
4
  * Wraps the raw queries in `db/fact_queries.ts` with the lifecycle the
5
5
  * `FactStore` interface promises:
6
6
  *
7
- * - sync hash on `put`, stream hash on `put_ref` (counting bytes against
8
- * the caller-supplied `size`)
7
+ * - sync hash on `put`; `put_stream` hashes as it streams
9
8
  * - idempotent insert (`ON CONFLICT DO NOTHING` in the queries layer)
10
9
  * - JSON ref auto-extraction when `content_type` signals JSON and the
11
10
  * caller didn't pass an explicit `refs` array
12
- * - verify-on-read for external content; embedded reads skip verify
11
+ * - verify-on-read for disk-backed content; embedded reads skip verify
13
12
  * because PG storage IS the hash table
14
- * - mismatched external bytes return `null` + log warning (treat as
13
+ * - mismatched disk bytes return `null` + log warning (treat as
15
14
  * unavailable; GC / repair is a separate concern)
16
15
  *
17
16
  * Embedded vs disk split: writes route by size. Bytes `<= embedded_threshold`
@@ -19,39 +18,30 @@
19
18
  * `<facts_dir>/<shard>/<rest>` (`db/fact_disk_storage.ts`) and the row records a
20
19
  * `file:<shard>/<rest>` `external_url`. `put` takes fully-buffered bytes;
21
20
  * `put_stream` is the bounded-memory streaming twin (hash BLAKE3 + SHA-256 in
22
- * one pass, spill past the threshold, enforce `max_bytes` / `ENOSPC`). Both need
23
- * `disk_root` + `fs` (the `runtime/*Deps`) configured for the over-threshold
24
- * path; without them, an oversize `put` throws and the caller must `put_ref`
25
- * against an externally-managed URL (federation / stub-fetcher tests).
21
+ * one pass, spill past the threshold, enforce `max_bytes` / `ENOSPC`). `put`
22
+ * needs `disk_root` + `fs` (the `runtime/*Deps`) for the over-threshold path
23
+ * only: without them the store has nowhere to put bytes it cannot embed, and an
24
+ * oversize `put` throws. `put_stream` needs `fs` for a body of any size — with
25
+ * none it throws before reading the stream — and `disk_root` for the
26
+ * over-threshold spill: with none, a body that passes the threshold throws
27
+ * `PayloadTooLargeError`.
28
+ *
29
+ * Every `external_url` the store writes is a `file:<shard>/<rest>` URL into its
30
+ * own disk CAS, and that CAS is the only place `get` reads external bytes from
31
+ * — the store makes no network request. The column is free text
32
+ * (`query_put_fact` inserts what it is given), so `get` treats a row whose
33
+ * `external_url` has any other shape, and any external row on a store with no
34
+ * disk CAS configured, as unavailable: a warning and `null`.
26
35
  *
27
36
  * @module
28
37
  */
29
38
  import { to_error_message } from '@fuzdev/fuz_util/error.ts';
30
- import { fact_hash_bytes, fact_hash_stream, fact_hash_verify, fact_hash_extract_refs } from '@fuzdev/fuz_util/fact_hash.ts';
39
+ import { fact_hash_bytes, fact_hash_verify, fact_hash_extract_refs } from '@fuzdev/fuz_util/fact_hash.ts';
31
40
  import { query_delete_fact, query_get_fact, query_get_fact_meta, query_get_fact_refs, query_has_fact, query_put_fact, query_put_fact_refs } from './fact_queries.js';
32
- import { create_disk_fact_fetcher, stream_fact_to_disk, write_fact_bytes_to_disk } from './fact_disk_storage.js';
41
+ import { is_file_fact_url } from './file_fact_url.js';
42
+ import { read_fact_bytes_from_disk, stream_fact_to_disk, write_fact_bytes_to_disk } from './fact_disk_storage.js';
33
43
  /** Default embedded-vs-referenced cutoff (1 MiB). */
34
44
  export const FACT_EMBEDDED_THRESHOLD_DEFAULT = 1024 * 1024;
35
- /** Default fetcher backed by `globalThis.fetch`. */
36
- export const create_default_fetcher = () => ({
37
- fetch_stream: async (url) => {
38
- const response = await fetch(url);
39
- if (!response.ok) {
40
- throw new Error(`fact fetch failed: ${response.status} ${url}`);
41
- }
42
- if (!response.body) {
43
- throw new Error(`fact fetch returned no body: ${url}`);
44
- }
45
- return response.body;
46
- },
47
- fetch_bytes: async (url) => {
48
- const response = await fetch(url);
49
- if (!response.ok) {
50
- throw new Error(`fact fetch failed: ${response.status} ${url}`);
51
- }
52
- return new Uint8Array(await response.arrayBuffer());
53
- }
54
- });
55
45
  /**
56
46
  * PG-backed `FactStore`. Delegates to `db/fact_queries.ts` for I/O and adds
57
47
  * the lifecycle layer described in the module doc.
@@ -61,26 +51,20 @@ export class PgFactStore {
61
51
  #embedded_threshold;
62
52
  #disk_root;
63
53
  #fs;
64
- #fetcher;
65
54
  #log;
66
55
  constructor(options) {
67
56
  this.#deps = options.deps;
68
57
  this.#embedded_threshold = options.embedded_threshold ?? FACT_EMBEDDED_THRESHOLD_DEFAULT;
69
58
  this.#disk_root = options.disk_root;
70
59
  this.#fs = options.fs;
71
- this.#fetcher =
72
- options.fetcher ??
73
- (options.disk_root !== undefined && options.fs !== undefined
74
- ? create_disk_fact_fetcher(options.fs, options.disk_root)
75
- : create_default_fetcher());
76
60
  this.#log = options.log;
77
61
  }
78
62
  /**
79
63
  * Store fully-buffered bytes, routing by size: `<= embedded_threshold` into
80
64
  * the PG `bytes` column; larger into the disk CAS (when `disk_root` + `fs`
81
65
  * are configured) at `<facts_dir>/<shard>/<rest>` with a `file:` URL. Oversize
82
- * without a disk root throws so the caller routes it through `put_ref`
83
- * explicitly. Idempotent — `ON CONFLICT DO NOTHING` + content-addressed disk
66
+ * without a disk root throws: the store has nowhere to put bytes it cannot
67
+ * embed. Idempotent — `ON CONFLICT DO NOTHING` + content-addressed disk
84
68
  * filenames make a re-write a no-op.
85
69
  */
86
70
  async put(bytes, options) {
@@ -89,7 +73,7 @@ export class PgFactStore {
89
73
  let row_external_url;
90
74
  if (bytes.length > this.#embedded_threshold) {
91
75
  if (this.#disk_root === undefined || this.#fs === undefined) {
92
- throw new Error(`fact bytes exceed embedded threshold (${bytes.length} > ${this.#embedded_threshold}); configure disk_root or use put_ref for external storage`);
76
+ throw new Error(`fact bytes exceed embedded threshold (${bytes.length} > ${this.#embedded_threshold}) and no disk CAS is configured; set disk_root and fs to store them on disk`);
93
77
  }
94
78
  row_bytes = null;
95
79
  row_external_url = await write_fact_bytes_to_disk(this.#fs, this.#disk_root, hash, bytes);
@@ -147,33 +131,14 @@ export class PgFactStore {
147
131
  return { hash: streamed.hash, sha256: streamed.sha256, size: streamed.size };
148
132
  }
149
133
  /**
150
- * Stream-hash external content and record `(hash, external_url, size)`.
151
- * Throws when the streamed byte count disagrees with the caller's
152
- * declared `size` — a size mismatch usually means the upload was
153
- * truncated or the URL points at the wrong content.
154
- */
155
- async put_ref(url, size, options) {
156
- const stream = await this.#fetcher.fetch_stream(url);
157
- const { hash, byte_count } = await hash_counted_stream(stream);
158
- if (byte_count !== size) {
159
- throw new Error(`fact size mismatch for ${url}: caller declared ${size}, streamed ${byte_count}`);
160
- }
161
- const inserted = await query_put_fact(this.#deps, {
162
- hash,
163
- bytes: null,
164
- external_url: url,
165
- content_type: options?.content_type ?? null,
166
- size
167
- });
168
- if (inserted && options?.refs && options.refs.length > 0) {
169
- await query_put_fact_refs(this.#deps, hash, options.refs);
170
- }
171
- return hash;
172
- }
173
- /**
174
- * Retrieve bytes. Embedded reads return PG bytes directly; external
175
- * reads fetch + verify and return `null` (with a warning log) when
176
- * the bytes don't match the stored hash.
134
+ * Retrieve bytes. Embedded reads return PG bytes directly. An external
135
+ * read comes from the disk CAS and nowhere else: the bytes at the row's
136
+ * `file:<shard>/<rest>` URL are read and verified against the hash.
137
+ *
138
+ * Returns `null`, with a warning log, for an external row the store cannot
139
+ * serve — an `external_url` that is not a `file:<shard>/<rest>` URL, no disk
140
+ * CAS configured (`disk_root` + `fs`), a failed disk read, or bytes that
141
+ * don't match the hash. Never makes a network request.
177
142
  */
178
143
  async get(hash) {
179
144
  const row = await query_get_fact(this.#deps, hash);
@@ -185,12 +150,22 @@ export class PgFactStore {
185
150
  if (row.external_url === null) {
186
151
  return null;
187
152
  }
153
+ // the URL is not echoed — the column is free text, so its shape and
154
+ // length are whatever the row's writer chose
155
+ if (!is_file_fact_url(row.external_url)) {
156
+ this.#log?.warn(`PgFactStore.get external_url for ${hash} is not a file:<shard>/<rest> URL; treating as not-found`);
157
+ return null;
158
+ }
159
+ if (this.#disk_root === undefined || this.#fs === undefined) {
160
+ this.#log?.warn(`PgFactStore.get ${hash} is disk-backed and no disk CAS is configured (disk_root + fs); treating as not-found`);
161
+ return null;
162
+ }
188
163
  let bytes;
189
164
  try {
190
- bytes = await this.#fetcher.fetch_bytes(row.external_url);
165
+ bytes = await read_fact_bytes_from_disk(this.#fs, this.#disk_root, row.external_url);
191
166
  }
192
167
  catch (err) {
193
- this.#log?.warn(`PgFactStore.get fetch failed for ${hash} at ${row.external_url}:`, to_error_message(err));
168
+ this.#log?.warn(`PgFactStore.get disk read failed for ${hash} at ${row.external_url}:`, to_error_message(err));
194
169
  return null;
195
170
  }
196
171
  if (!fact_hash_verify(hash, bytes)) {
@@ -226,11 +201,11 @@ export class PgFactStore {
226
201
  * NOT verify the fact is unreferenced — that policy lives one layer
227
202
  * up (the orphan-fact admin surface in the consumer; a future GC walker).
228
203
  *
229
- * External-URL unlink is the caller's responsibility — the store
230
- * doesn't know how to resolve `file:` / `s3:` / etc. URLs to a
231
- * deletable handle. Caller iterates the returned `external_url`
232
- * (when non-null) and dispatches to the appropriate cleanup
233
- * routine. Mirrors the read-side `FactExternalFetcher` split.
204
+ * The disk file of a disk-backed fact is NOT unlinked — that is the
205
+ * caller's responsibility, using the returned `external_url` (when
206
+ * non-null): a `file:<shard>/<rest>` URL names
207
+ * `<disk_root>/<shard>/<rest>`. The value is the row's free text — parse
208
+ * it with `parse_file_fact_url` before joining a path.
234
209
  *
235
210
  * @returns `{size, external_url}` for the deleted row, or `null` if
236
211
  * no row matched the hash.
@@ -260,19 +235,6 @@ const resolve_refs = (bytes, options) => {
260
235
  }
261
236
  return fact_hash_extract_refs(value);
262
237
  };
263
- /** Hash a stream while counting bytes. Lets `put_ref` verify size in one pass. */
264
- const hash_counted_stream = async (stream) => {
265
- let byte_count = 0;
266
- const counting = new TransformStream({
267
- transform(chunk, controller) {
268
- byte_count += chunk.length;
269
- controller.enqueue(chunk);
270
- }
271
- });
272
- const piped = stream.pipeThrough(counting);
273
- const hash = await fact_hash_stream(piped);
274
- return { hash, byte_count };
275
- };
276
238
  /**
277
239
  * Coerce whatever the driver returns for BYTEA into a `Uint8Array`.
278
240
  *
@@ -1,9 +1,9 @@
1
1
  /**
2
2
  * Canonical filesystem-fact URL shape + on-disk layout.
3
3
  *
4
- * `external_url` on the generic `fact` row is `string | null` because the
5
- * `FactStore` interface stays federation-friendly (future
6
- * `https://...` / `s3://...` shapes). Filesystem-minted URLs are exactly
4
+ * `external_url` on the `fact` row is free text (`string | null`), but the
5
+ * only shape `PgFactStore` writes, reads, or serves is the filesystem-minted
6
+ * one — any other reads as unavailable. Those URLs are exactly
7
7
  * `file:<shard>/<rest>` where `<shard>` is the first 2 hex chars of the
8
8
  * blake3 digest and `<rest>` the remaining 62 — files land at
9
9
  * `<facts_dir>/<shard>/<rest>` after the writer atomically temp+renames
@@ -11,9 +11,9 @@
11
11
  *
12
12
  * Centralizing the regex + the `fact_disk_path` split keeps the shape in one
13
13
  * place: `PgFactStore`'s disk CAS (`db/fact_disk_storage.ts`), the
14
- * `serve_fact_route` defense-in-depth check, and the `file_fact_fetcher`
15
- * resolver all derive the layout here, so the write path and the read path
16
- * can't drift. The TS twin of the Rust `fact_disk_path` (`fuz_fact`).
14
+ * `serve_fact_route` defense-in-depth check, and the disk read
15
+ * (`read_fact_bytes_from_disk`) all derive the layout here, so the write path
16
+ * and the read path can't drift. The TS twin of the Rust `fact_disk_path` (`fuz_fact`).
17
17
  *
18
18
  * Defense-in-depth: a `..` segment can't match (`.` isn't in `[0-9a-f]`),
19
19
  * neither can absolute paths, query strings, or any non-hex character.
@@ -1,9 +1,9 @@
1
1
  /**
2
2
  * Canonical filesystem-fact URL shape + on-disk layout.
3
3
  *
4
- * `external_url` on the generic `fact` row is `string | null` because the
5
- * `FactStore` interface stays federation-friendly (future
6
- * `https://...` / `s3://...` shapes). Filesystem-minted URLs are exactly
4
+ * `external_url` on the `fact` row is free text (`string | null`), but the
5
+ * only shape `PgFactStore` writes, reads, or serves is the filesystem-minted
6
+ * one — any other reads as unavailable. Those URLs are exactly
7
7
  * `file:<shard>/<rest>` where `<shard>` is the first 2 hex chars of the
8
8
  * blake3 digest and `<rest>` the remaining 62 — files land at
9
9
  * `<facts_dir>/<shard>/<rest>` after the writer atomically temp+renames
@@ -11,9 +11,9 @@
11
11
  *
12
12
  * Centralizing the regex + the `fact_disk_path` split keeps the shape in one
13
13
  * place: `PgFactStore`'s disk CAS (`db/fact_disk_storage.ts`), the
14
- * `serve_fact_route` defense-in-depth check, and the `file_fact_fetcher`
15
- * resolver all derive the layout here, so the write path and the read path
16
- * can't drift. The TS twin of the Rust `fact_disk_path` (`fuz_fact`).
14
+ * `serve_fact_route` defense-in-depth check, and the disk read
15
+ * (`read_fact_bytes_from_disk`) all derive the layout here, so the write path
16
+ * and the read path can't drift. The TS twin of the Rust `fact_disk_path` (`fuz_fact`).
17
17
  *
18
18
  * Defense-in-depth: a `..` segment can't match (`.` isn't in `[0-9a-f]`),
19
19
  * neither can absolute paths, query strings, or any non-hex character.
@@ -97,10 +97,67 @@ export interface DbStatus {
97
97
  */
98
98
  export declare const query_db_status: (db: Db, namespaces?: Array<MigrationNamespace>) => Promise<DbStatus>;
99
99
  /**
100
- * Format a `DbStatus` as a human-readable string for CLI output.
100
+ * Format a `DbStatus` as the body of the human-facing operator report.
101
+ *
102
+ * A `tables:` count with one aligned `name N rows` line per table, then a
103
+ * `migrations:` header (always printed, even with no namespaces) and one
104
+ * aligned line per namespace: its state (`up-to-date`, `PENDING <n>`, or
105
+ * `DIVERGED`), `applied/declared`, and the latest applied name (`(none)` when
106
+ * nothing is applied). A diverged namespace adds a `diverged:` line, a pending
107
+ * one a `pending:` line naming the tail. A failed connection renders the single
108
+ * line `connection: FAILED (<error>)`. Every line ends in a newline.
109
+ *
110
+ * The body only — the `database status` / `url:` header belongs to the CLI
111
+ * script, which holds the URL a `DbStatus` never sees. For a connected status
112
+ * the output is byte-identical with the Rust twin's `format_db_status`; both
113
+ * test suites pin the same fixture report.
101
114
  *
102
115
  * @param status - the status to format
103
116
  * @returns multi-line string suitable for console output
104
117
  */
105
118
  export declare const format_db_status: (status: DbStatus) => string;
119
+ /**
120
+ * Render a database URL for printing from the parse node-postgres dials —
121
+ * `new URL`, with pg-connection-string's precedence, where a `user`, `host`, or
122
+ * `port` query parameter overrides the URL's own — so the line names the server
123
+ * dialed and no credential can leak however the input spells it.
124
+ *
125
+ * A `postgres://` or `postgresql://` URL renders as
126
+ * `postgres://[user@]host:port[/dbname]`: never the password, and never the
127
+ * query or fragment (`sslmode` and the like are dropped by design). The scheme
128
+ * always prints as `postgres://`. The port always prints, `5432` when the URL
129
+ * names none. The user and database name print percent-encoded with
130
+ * `encodeURIComponent`, so no control character, escape sequence, or bidi
131
+ * override reaches the terminal and an `@` can't pose as the separator; each
132
+ * prints only when non-empty. An IP-literal host prints canonically (IPv6
133
+ * bracketed); a Unix-socket directory prints with each `/` as `%2F`. A PGlite
134
+ * `file://` or `memory://` URL prints as scheme, host, and path.
135
+ *
136
+ * The whole line is `(not a URL; hidden)` for any other scheme, when `new URL`
137
+ * rejects the input, when it holds a malformed percent-escape (`%ZZ`), when
138
+ * there is no host, when the host is neither an IP literal, a hostname of
139
+ * `A-Z a-z 0-9 . _ -`, nor a socket directory of those characters and `/`, when
140
+ * the user holds `?`, `=`, `&`, `/`, or `#`, or when the port isn't a number up
141
+ * to 65535. The host and user rules match the Rust twin's, where they catch
142
+ * tokio-postgres's split at the first `@`.
143
+ *
144
+ * Twin of the Rust `fuz_db` CLI's `display_db_url`, byte-identical on the URLs
145
+ * both drivers read alike. They part where the drivers do: a `host=` or `port=`
146
+ * query parameter overrides the URL's here (tokio-postgres appends it to a list
147
+ * whose first entry the pool dials); a `dbname=` query parameter, which
148
+ * tokio-postgres honours and node-postgres ignores, and a `#…` fragment, which
149
+ * tokio-postgres keeps as part of the database name; tokio-postgres also
150
+ * accepts `key=value` strings, host lists, and an unencoded `/` or `?` in the
151
+ * password (here, the placeholder); an unencoded `@` in the userinfo splits at
152
+ * the last `@` here, rendering the user and host node-postgres dials, where the
153
+ * Rust twin prints the placeholder; node-postgres decodes the database name
154
+ * with `decodeURI`, so a reserved-character escape such as `%2F` stays literal
155
+ * and prints as `%252F`; an IPv4-mapped IPv6 address serializes differently;
156
+ * and tokio-postgres refuses an unknown query parameter (`sslpassword=`) that
157
+ * node-postgres passes.
158
+ *
159
+ * @param url - the database URL
160
+ * @returns the URL without its password, query, or fragment, or the placeholder
161
+ */
162
+ export declare const display_db_url: (url: string) => string;
106
163
  //# sourceMappingURL=status.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"status.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/db/status.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAIH,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAEvD;;;;;;;;GAQG;AACH,MAAM,MAAM,UAAU,GACnB;IACA,mEAAmE;IACnE,IAAI,EAAE,eAAe,CAAC;IACtB,+CAA+C;IAC/C,QAAQ,EAAE,MAAM,CAAC;IACjB,sDAAsD;IACtD,OAAO,EAAE,MAAM,CAAC;IAChB,gDAAgD;IAChD,QAAQ,EAAE,MAAM,CAAC;CAChB,GACD;IACA,kEAAkE;IAClE,IAAI,EAAE,cAAc,CAAC;IACrB,qCAAqC;IACrC,OAAO,EAAE,MAAM,CAAC;IAChB,+BAA+B;IAC/B,QAAQ,EAAE,MAAM,CAAC;CAChB,CAAC;AAEL;;GAEG;AACH,MAAM,WAAW,eAAe;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,uEAAuE;IACvE,aAAa,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAC7B,2EAA2E;IAC3E,aAAa,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAC7B;;;OAGG;IACH,UAAU,EAAE,OAAO,CAAC;IACpB;;;;;OAKG;IACH,UAAU,CAAC,EAAE,UAAU,CAAC;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,QAAQ;IACxB,yCAAyC;IACzC,SAAS,EAAE,OAAO,CAAC;IACnB,0CAA0C;IAC1C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,+BAA+B;IAC/B,WAAW,EAAE,MAAM,CAAC;IACpB,4BAA4B;IAC5B,MAAM,EAAE,KAAK,CAAC,WAAW,CAAC,CAAC;IAC3B,sCAAsC;IACtC,UAAU,EAAE,KAAK,CAAC,eAAe,CAAC,CAAC;CACnC;AAaD;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,eAAe,GAC3B,IAAI,EAAE,EACN,aAAa,KAAK,CAAC,kBAAkB,CAAC,KACpC,OAAO,CAAC,QAAQ,CAsGlB,CAAC;AAeF;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,GAAI,QAAQ,QAAQ,KAAG,MA4CnD,CAAC"}
1
+ {"version":3,"file":"status.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/db/status.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAIH,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAEvD;;;;;;;;GAQG;AACH,MAAM,MAAM,UAAU,GACnB;IACA,mEAAmE;IACnE,IAAI,EAAE,eAAe,CAAC;IACtB,+CAA+C;IAC/C,QAAQ,EAAE,MAAM,CAAC;IACjB,sDAAsD;IACtD,OAAO,EAAE,MAAM,CAAC;IAChB,gDAAgD;IAChD,QAAQ,EAAE,MAAM,CAAC;CAChB,GACD;IACA,kEAAkE;IAClE,IAAI,EAAE,cAAc,CAAC;IACrB,qCAAqC;IACrC,OAAO,EAAE,MAAM,CAAC;IAChB,+BAA+B;IAC/B,QAAQ,EAAE,MAAM,CAAC;CAChB,CAAC;AAEL;;GAEG;AACH,MAAM,WAAW,eAAe;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,uEAAuE;IACvE,aAAa,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAC7B,2EAA2E;IAC3E,aAAa,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAC7B;;;OAGG;IACH,UAAU,EAAE,OAAO,CAAC;IACpB;;;;;OAKG;IACH,UAAU,CAAC,EAAE,UAAU,CAAC;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,QAAQ;IACxB,yCAAyC;IACzC,SAAS,EAAE,OAAO,CAAC;IACnB,0CAA0C;IAC1C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,+BAA+B;IAC/B,WAAW,EAAE,MAAM,CAAC;IACpB,4BAA4B;IAC5B,MAAM,EAAE,KAAK,CAAC,WAAW,CAAC,CAAC;IAC3B,sCAAsC;IACtC,UAAU,EAAE,KAAK,CAAC,eAAe,CAAC,CAAC;CACnC;AA6DD;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,eAAe,GAC3B,IAAI,EAAE,EACN,aAAa,KAAK,CAAC,kBAAkB,CAAC,KACpC,OAAO,CAAC,QAAQ,CA2DlB,CAAC;AA0BF;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,gBAAgB,GAAI,QAAQ,QAAQ,KAAG,MAmCnD,CAAC;AA8BF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AACH,eAAO,MAAM,cAAc,GAAI,KAAK,MAAM,KAAG,MAoC5C,CAAC"}