@fuzdev/fuz_app 0.67.1 → 0.69.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/actions/perform_action.d.ts.map +1 -1
- package/dist/actions/perform_action.js +10 -3
- package/dist/auth/CLAUDE.md +99 -5
- package/dist/auth/account_queries.d.ts +87 -4
- package/dist/auth/account_queries.d.ts.map +1 -1
- package/dist/auth/account_queries.js +107 -17
- package/dist/auth/account_schema.d.ts +19 -0
- package/dist/auth/account_schema.d.ts.map +1 -1
- package/dist/auth/account_schema.js +8 -0
- package/dist/auth/admin_action_specs.d.ts +170 -3
- package/dist/auth/admin_action_specs.d.ts.map +1 -1
- package/dist/auth/admin_action_specs.js +148 -4
- package/dist/auth/admin_actions.d.ts +4 -14
- package/dist/auth/admin_actions.d.ts.map +1 -1
- package/dist/auth/admin_actions.js +246 -40
- package/dist/auth/audit_log_ddl.d.ts +10 -1
- package/dist/auth/audit_log_ddl.d.ts.map +1 -1
- package/dist/auth/audit_log_ddl.js +13 -4
- package/dist/auth/audit_log_schema.d.ts +34 -1
- package/dist/auth/audit_log_schema.d.ts.map +1 -1
- package/dist/auth/audit_log_schema.js +73 -0
- package/dist/auth/auth_ddl.d.ts +2 -2
- package/dist/auth/auth_ddl.d.ts.map +1 -1
- package/dist/auth/auth_ddl.js +10 -2
- package/dist/auth/cell_action_specs.d.ts +1295 -0
- package/dist/auth/cell_action_specs.d.ts.map +1 -0
- package/dist/auth/cell_action_specs.js +397 -0
- package/dist/auth/cell_actions.d.ts +63 -0
- package/dist/auth/cell_actions.d.ts.map +1 -0
- package/dist/auth/cell_actions.js +546 -0
- package/dist/auth/cell_audit_action_specs.d.ts +131 -0
- package/dist/auth/cell_audit_action_specs.d.ts.map +1 -0
- package/dist/auth/cell_audit_action_specs.js +70 -0
- package/dist/auth/cell_audit_actions.d.ts +18 -0
- package/dist/auth/cell_audit_actions.d.ts.map +1 -0
- package/dist/auth/cell_audit_actions.js +59 -0
- package/dist/auth/cell_audit_events.d.ts +28 -0
- package/dist/auth/cell_audit_events.d.ts.map +1 -0
- package/dist/auth/cell_audit_events.js +42 -0
- package/dist/auth/cell_audit_metadata.d.ts +48 -0
- package/dist/auth/cell_audit_metadata.d.ts.map +1 -0
- package/dist/auth/cell_audit_metadata.js +46 -0
- package/dist/auth/cell_authorize.d.ts +88 -0
- package/dist/auth/cell_authorize.d.ts.map +1 -0
- package/dist/auth/cell_authorize.js +172 -0
- package/dist/auth/cell_data_schema.d.ts +44 -0
- package/dist/auth/cell_data_schema.d.ts.map +1 -0
- package/dist/auth/cell_data_schema.js +42 -0
- package/dist/auth/cell_field_action_specs.d.ts +244 -0
- package/dist/auth/cell_field_action_specs.d.ts.map +1 -0
- package/dist/auth/cell_field_action_specs.js +136 -0
- package/dist/auth/cell_field_actions.d.ts +34 -0
- package/dist/auth/cell_field_actions.d.ts.map +1 -0
- package/dist/auth/cell_field_actions.js +153 -0
- package/dist/auth/cell_field_audit_metadata.d.ts +30 -0
- package/dist/auth/cell_field_audit_metadata.d.ts.map +1 -0
- package/dist/auth/cell_field_audit_metadata.js +28 -0
- package/dist/auth/cell_grant_action_specs.d.ts +333 -0
- package/dist/auth/cell_grant_action_specs.d.ts.map +1 -0
- package/dist/auth/cell_grant_action_specs.js +148 -0
- package/dist/auth/cell_grant_actions.d.ts +50 -0
- package/dist/auth/cell_grant_actions.d.ts.map +1 -0
- package/dist/auth/cell_grant_actions.js +208 -0
- package/dist/auth/cell_grant_audit_metadata.d.ts +75 -0
- package/dist/auth/cell_grant_audit_metadata.d.ts.map +1 -0
- package/dist/auth/cell_grant_audit_metadata.js +54 -0
- package/dist/auth/cell_item_action_specs.d.ts +331 -0
- package/dist/auth/cell_item_action_specs.d.ts.map +1 -0
- package/dist/auth/cell_item_action_specs.js +182 -0
- package/dist/auth/cell_item_actions.d.ts +37 -0
- package/dist/auth/cell_item_actions.d.ts.map +1 -0
- package/dist/auth/cell_item_actions.js +204 -0
- package/dist/auth/cell_item_audit_metadata.d.ts +35 -0
- package/dist/auth/cell_item_audit_metadata.d.ts.map +1 -0
- package/dist/auth/cell_item_audit_metadata.js +32 -0
- package/dist/auth/cell_relation_visibility.d.ts +32 -0
- package/dist/auth/cell_relation_visibility.d.ts.map +1 -0
- package/dist/auth/cell_relation_visibility.js +57 -0
- package/dist/auth/deps.d.ts +9 -0
- package/dist/auth/deps.d.ts.map +1 -1
- package/dist/auth/role_grant_queries.d.ts +30 -0
- package/dist/auth/role_grant_queries.d.ts.map +1 -1
- package/dist/auth/role_grant_queries.js +54 -0
- package/dist/auth/signup_routes.d.ts +0 -3
- package/dist/auth/signup_routes.d.ts.map +1 -1
- package/dist/auth/signup_routes.js +9 -3
- package/dist/auth/standard_rpc_actions.d.ts +5 -5
- package/dist/auth/standard_rpc_actions.js +4 -4
- package/dist/db/CLAUDE.md +118 -0
- package/dist/db/cell_audit_queries.d.ts +26 -0
- package/dist/db/cell_audit_queries.d.ts.map +1 -0
- package/dist/db/cell_audit_queries.js +53 -0
- package/dist/db/cell_ddl.d.ts +151 -0
- package/dist/db/cell_ddl.d.ts.map +1 -0
- package/dist/db/cell_ddl.js +247 -0
- package/dist/db/cell_field_queries.d.ts +105 -0
- package/dist/db/cell_field_queries.d.ts.map +1 -0
- package/dist/db/cell_field_queries.js +113 -0
- package/dist/db/cell_grant_queries.d.ts +132 -0
- package/dist/db/cell_grant_queries.d.ts.map +1 -0
- package/dist/db/cell_grant_queries.js +145 -0
- package/dist/db/cell_history_ddl.d.ts +38 -0
- package/dist/db/cell_history_ddl.d.ts.map +1 -0
- package/dist/db/cell_history_ddl.js +61 -0
- package/dist/db/cell_item_queries.d.ts +107 -0
- package/dist/db/cell_item_queries.d.ts.map +1 -0
- package/dist/db/cell_item_queries.js +119 -0
- package/dist/db/cell_queries.d.ts +327 -0
- package/dist/db/cell_queries.d.ts.map +1 -0
- package/dist/db/cell_queries.js +431 -0
- package/dist/db/fact_ddl.d.ts +38 -0
- package/dist/db/fact_ddl.d.ts.map +1 -0
- package/dist/db/fact_ddl.js +71 -0
- package/dist/db/fact_queries.d.ts +140 -0
- package/dist/db/fact_queries.d.ts.map +1 -0
- package/dist/db/fact_queries.js +161 -0
- package/dist/db/fact_store.d.ts +112 -0
- package/dist/db/fact_store.d.ts.map +1 -0
- package/dist/db/fact_store.js +225 -0
- package/dist/server/app_server.d.ts +1 -7
- package/dist/server/app_server.d.ts.map +1 -1
- package/dist/server/app_server.js +1 -5
- package/dist/server/env.d.ts +2 -0
- package/dist/server/env.d.ts.map +1 -1
- package/dist/server/env.js +6 -0
- package/dist/server/fact_write.d.ts +32 -0
- package/dist/server/fact_write.d.ts.map +1 -0
- package/dist/server/fact_write.js +56 -0
- package/dist/server/file_fact_fetcher.d.ts +42 -0
- package/dist/server/file_fact_fetcher.d.ts.map +1 -0
- package/dist/server/file_fact_fetcher.js +60 -0
- package/dist/server/file_fact_url.d.ts +53 -0
- package/dist/server/file_fact_url.d.ts.map +1 -0
- package/dist/server/file_fact_url.js +52 -0
- package/dist/server/serve_fact_route.d.ts +78 -0
- package/dist/server/serve_fact_route.d.ts.map +1 -0
- package/dist/server/serve_fact_route.js +205 -0
- package/dist/testing/CLAUDE.md +142 -6
- package/dist/testing/app_server.d.ts +46 -0
- package/dist/testing/app_server.d.ts.map +1 -1
- package/dist/testing/app_server.js +67 -8
- package/dist/testing/audit_completeness.d.ts.map +1 -1
- package/dist/testing/audit_completeness.js +67 -1
- package/dist/testing/cross_backend/account_lifecycle.d.ts +10 -0
- package/dist/testing/cross_backend/account_lifecycle.d.ts.map +1 -0
- package/dist/testing/cross_backend/account_lifecycle.js +144 -0
- package/dist/testing/cross_backend/actor_lookup.d.ts +10 -0
- package/dist/testing/cross_backend/actor_lookup.d.ts.map +1 -0
- package/dist/testing/cross_backend/actor_lookup.js +83 -0
- package/dist/testing/cross_backend/actor_search.d.ts +6 -0
- package/dist/testing/cross_backend/actor_search.d.ts.map +1 -0
- package/dist/testing/cross_backend/actor_search.js +92 -0
- package/dist/testing/cross_backend/app_settings.d.ts +6 -0
- package/dist/testing/cross_backend/app_settings.d.ts.map +1 -0
- package/dist/testing/cross_backend/app_settings.js +95 -0
- package/dist/testing/cross_backend/backend_config.d.ts +1 -1
- package/dist/testing/cross_backend/capabilities.d.ts +29 -7
- package/dist/testing/cross_backend/capabilities.d.ts.map +1 -1
- package/dist/testing/cross_backend/capabilities.js +3 -1
- package/dist/testing/cross_backend/cell_cross_helpers.d.ts +39 -0
- package/dist/testing/cross_backend/cell_cross_helpers.d.ts.map +1 -0
- package/dist/testing/cross_backend/cell_cross_helpers.js +45 -0
- package/dist/testing/cross_backend/cell_crud.d.ts +4 -0
- package/dist/testing/cross_backend/cell_crud.d.ts.map +1 -0
- package/dist/testing/cross_backend/cell_crud.js +168 -0
- package/dist/testing/cross_backend/cell_grant_role.d.ts +8 -0
- package/dist/testing/cross_backend/cell_grant_role.d.ts.map +1 -0
- package/dist/testing/cross_backend/cell_grant_role.js +102 -0
- package/dist/testing/cross_backend/cell_relations.d.ts +4 -0
- package/dist/testing/cross_backend/cell_relations.d.ts.map +1 -0
- package/dist/testing/cross_backend/cell_relations.js +229 -0
- package/dist/testing/cross_backend/conformance_case.d.ts +144 -0
- package/dist/testing/cross_backend/conformance_case.d.ts.map +1 -0
- package/dist/testing/cross_backend/conformance_case.js +132 -0
- package/dist/testing/cross_backend/conformance_table.d.ts +46 -0
- package/dist/testing/cross_backend/conformance_table.d.ts.map +1 -0
- package/dist/testing/cross_backend/conformance_table.js +199 -0
- package/dist/testing/cross_backend/default_backend_configs.d.ts.map +1 -1
- package/dist/testing/cross_backend/default_backend_configs.js +6 -2
- package/dist/testing/cross_backend/default_spine_surface.d.ts +17 -9
- package/dist/testing/cross_backend/default_spine_surface.d.ts.map +1 -1
- package/dist/testing/cross_backend/default_spine_surface.js +20 -12
- package/dist/testing/cross_backend/origin.d.ts +10 -0
- package/dist/testing/cross_backend/origin.d.ts.map +1 -0
- package/dist/testing/cross_backend/origin.js +73 -0
- package/dist/testing/cross_backend/setup.d.ts +22 -40
- package/dist/testing/cross_backend/setup.d.ts.map +1 -1
- package/dist/testing/cross_backend/setup.js +39 -5
- package/dist/testing/cross_backend/testing_reset_actions.d.ts +90 -2
- package/dist/testing/cross_backend/testing_reset_actions.d.ts.map +1 -1
- package/dist/testing/cross_backend/testing_reset_actions.js +91 -3
- package/dist/testing/cross_backend/xfail.d.ts +15 -0
- package/dist/testing/cross_backend/xfail.d.ts.map +1 -0
- package/dist/testing/cross_backend/xfail.js +37 -0
- package/dist/testing/entities.d.ts.map +1 -1
- package/dist/testing/entities.js +4 -0
- package/dist/testing/integration.d.ts +2 -3
- package/dist/testing/integration.d.ts.map +1 -1
- package/dist/testing/integration.js +20 -85
- package/dist/testing/rate_limiting.d.ts +1 -1
- package/dist/testing/rpc_helpers.d.ts +3 -3
- package/dist/testing/sse_round_trip.d.ts +1 -1
- package/dist/testing/stubs.d.ts.map +1 -1
- package/dist/testing/stubs.js +0 -1
- package/dist/testing/ws_round_trip.d.ts.map +1 -1
- package/dist/testing/ws_round_trip.js +4 -0
- package/dist/ui/AdminAccounts.svelte +84 -35
- package/dist/ui/AdminAccounts.svelte.d.ts.map +1 -1
- package/dist/ui/AdminSessions.svelte +21 -23
- package/dist/ui/AdminSessions.svelte.d.ts.map +1 -1
- package/dist/ui/CLAUDE.md +17 -26
- package/dist/ui/OpenSignupToggle.svelte +2 -5
- package/dist/ui/OpenSignupToggle.svelte.d.ts.map +1 -1
- package/dist/ui/account_sessions_state.svelte.d.ts +9 -10
- package/dist/ui/account_sessions_state.svelte.d.ts.map +1 -1
- package/dist/ui/account_sessions_state.svelte.js +7 -17
- package/dist/ui/admin_accounts_state.svelte.d.ts +41 -20
- package/dist/ui/admin_accounts_state.svelte.d.ts.map +1 -1
- package/dist/ui/admin_accounts_state.svelte.js +52 -22
- package/dist/ui/admin_invites_state.svelte.d.ts +8 -11
- package/dist/ui/admin_invites_state.svelte.d.ts.map +1 -1
- package/dist/ui/admin_invites_state.svelte.js +7 -16
- package/dist/ui/admin_rpc_adapters.d.ts +6 -2
- package/dist/ui/admin_rpc_adapters.d.ts.map +1 -1
- package/dist/ui/admin_rpc_adapters.js +5 -1
- package/dist/ui/admin_sessions_state.svelte.d.ts +6 -10
- package/dist/ui/admin_sessions_state.svelte.d.ts.map +1 -1
- package/dist/ui/admin_sessions_state.svelte.js +4 -14
- package/dist/ui/app_settings_state.svelte.d.ts +8 -12
- package/dist/ui/app_settings_state.svelte.d.ts.map +1 -1
- package/dist/ui/app_settings_state.svelte.js +6 -16
- package/dist/ui/audit_log_state.svelte.d.ts +9 -8
- package/dist/ui/audit_log_state.svelte.d.ts.map +1 -1
- package/dist/ui/audit_log_state.svelte.js +8 -20
- package/package.json +2 -2
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fact_queries.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/db/fact_queries.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Raw queries against the `facts` and `fact_refs` tables.
|
|
3
|
+
*
|
|
4
|
+
* Convention: `deps: QueryDeps` first, no audit side effects, mutations are
|
|
5
|
+
* idempotent (`ON CONFLICT DO NOTHING`) so the same hash can be written by
|
|
6
|
+
* two callers without the second observing an error.
|
|
7
|
+
*
|
|
8
|
+
* Higher-level lifecycle (verify-on-read, JSON ref auto-extraction,
|
|
9
|
+
* embedded-vs-referenced selection) lives in `db/fact_store.ts`. Queries
|
|
10
|
+
* here are deliberately mechanical.
|
|
11
|
+
*
|
|
12
|
+
* @module
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* Idempotently insert a fact row.
|
|
16
|
+
*
|
|
17
|
+
* `bytes` xor `external_url` per the `facts_storage_present` CHECK
|
|
18
|
+
* constraint; the caller is responsible for satisfying it (the queries
|
|
19
|
+
* layer does not second-guess). Returns `true` when a new row was
|
|
20
|
+
* inserted, `false` when a row already existed (caller can use this to
|
|
21
|
+
* decide whether to also write `fact_refs`).
|
|
22
|
+
*/
|
|
23
|
+
export const query_put_fact = async (deps, input) => {
|
|
24
|
+
const row = await deps.db.query_one(`INSERT INTO facts (hash, bytes, external_url, content_type, size)
|
|
25
|
+
VALUES ($1, $2, $3, $4, $5)
|
|
26
|
+
ON CONFLICT (hash) DO NOTHING
|
|
27
|
+
RETURNING hash`, [input.hash, input.bytes, input.external_url, input.content_type, input.size]);
|
|
28
|
+
return row !== undefined;
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Idempotently insert declared refs for a fact. No-ops on `(source_hash,
|
|
32
|
+
* target_hash)` collisions and skips the round trip entirely when
|
|
33
|
+
* `target_hashes` is empty.
|
|
34
|
+
*/
|
|
35
|
+
export const query_put_fact_refs = async (deps, source_hash, target_hashes) => {
|
|
36
|
+
if (target_hashes.length === 0)
|
|
37
|
+
return;
|
|
38
|
+
await deps.db.query(`INSERT INTO fact_refs (source_hash, target_hash)
|
|
39
|
+
SELECT $1::text, unnest($2::text[])
|
|
40
|
+
ON CONFLICT (source_hash, target_hash) DO NOTHING`, [source_hash, target_hashes]);
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* Fetch a fact's full row (including embedded `bytes`). Use this from
|
|
44
|
+
* `FactStore.get`; cheaper accessors live below.
|
|
45
|
+
*/
|
|
46
|
+
export const query_get_fact = async (deps, hash) => {
|
|
47
|
+
const row = await deps.db.query_one(`SELECT hash, bytes, external_url, content_type, size, created_at
|
|
48
|
+
FROM facts WHERE hash = $1`, [hash]);
|
|
49
|
+
return row ?? null;
|
|
50
|
+
};
|
|
51
|
+
/**
|
|
52
|
+
* Fetch metadata only — skips the (potentially large) `bytes` column.
|
|
53
|
+
*/
|
|
54
|
+
export const query_get_fact_meta = async (deps, hash) => {
|
|
55
|
+
const row = await deps.db.query_one(`SELECT hash, external_url, content_type, size, created_at
|
|
56
|
+
FROM facts WHERE hash = $1`, [hash]);
|
|
57
|
+
return row ?? null;
|
|
58
|
+
};
|
|
59
|
+
/**
|
|
60
|
+
* Cheap existence check. Backed by the `facts` PK index.
|
|
61
|
+
*/
|
|
62
|
+
export const query_has_fact = async (deps, hash) => {
|
|
63
|
+
const row = await deps.db.query_one(`SELECT EXISTS(SELECT 1 FROM facts WHERE hash = $1) AS exists`, [hash]);
|
|
64
|
+
return row?.exists ?? false;
|
|
65
|
+
};
|
|
66
|
+
/**
|
|
67
|
+
* List declared targets for a source fact. Order is unspecified; callers
|
|
68
|
+
* that need stable ordering should sort.
|
|
69
|
+
*/
|
|
70
|
+
export const query_get_fact_refs = async (deps, source_hash) => {
|
|
71
|
+
const rows = await deps.db.query(`SELECT target_hash FROM fact_refs WHERE source_hash = $1`, [source_hash]);
|
|
72
|
+
return rows.map((r) => r.target_hash);
|
|
73
|
+
};
|
|
74
|
+
/**
|
|
75
|
+
* Drop a fact row. Cascades `fact_refs` rows via the `ON DELETE CASCADE`
|
|
76
|
+
* FK on `source_hash`. Returns the deleted row's `(size, external_url)`
|
|
77
|
+
* so the caller can unlink the disk file (if any) and tally freed bytes,
|
|
78
|
+
* or `null` when no row matched (idempotent: deleting an absent fact is
|
|
79
|
+
* not an error).
|
|
80
|
+
*
|
|
81
|
+
* NOTE: this is a low-level primitive — callers MUST verify the fact is
|
|
82
|
+
* truly orphan (no referencing cell) before calling. The orphan check
|
|
83
|
+
* lives in `query_orphan_facts_*` below; the lifecycle wrapper in
|
|
84
|
+
* `PgFactStore.delete` handles the disk-file unlink.
|
|
85
|
+
*/
|
|
86
|
+
export const query_delete_fact = async (deps, hash) => {
|
|
87
|
+
const row = await deps.db.query_one(`DELETE FROM facts WHERE hash = $1
|
|
88
|
+
RETURNING size, external_url`, [hash]);
|
|
89
|
+
if (!row)
|
|
90
|
+
return null;
|
|
91
|
+
return { size: Number(row.size), external_url: row.external_url };
|
|
92
|
+
};
|
|
93
|
+
/**
|
|
94
|
+
* Compute the "orphan facts" set: rows in `facts` where no active
|
|
95
|
+
* (non-tombstone) `cell.refs` array contains the hash.
|
|
96
|
+
*
|
|
97
|
+
* The `cell` join is deliberately app-coupled — `facts` lives in the
|
|
98
|
+
* `fuz_facts` namespace and `cell.refs` lives in `fuz_cell`, but the
|
|
99
|
+
* orphan predicate only makes sense in apps that route content through
|
|
100
|
+
* cells. When a non-cell fact consumer ever appears (signed memo
|
|
101
|
+
* outputs? external fact mirrors?) the predicate moves to a generic
|
|
102
|
+
* `fact_consumers` registry; today the cell layer is the only consumer.
|
|
103
|
+
*
|
|
104
|
+
* The `older_than` filter applies to `facts.created_at`. Pass `null`
|
|
105
|
+
* to skip the filter (used by the list-summary preview); the delete
|
|
106
|
+
* handler always passes a non-null cutoff (default 0, meaning "any
|
|
107
|
+
* orphan").
|
|
108
|
+
*
|
|
109
|
+
* @param deps - query deps
|
|
110
|
+
* @param older_than - filter to facts created before this Date (or null
|
|
111
|
+
* to skip)
|
|
112
|
+
* @param sample_limit - row cap for the returned `sample`
|
|
113
|
+
*/
|
|
114
|
+
export const query_orphan_facts_list = async (deps, older_than, sample_limit) => {
|
|
115
|
+
const summary = await deps.db.query_one(`SELECT COUNT(*)::bigint AS count, COALESCE(SUM(size), 0)::bigint AS total
|
|
116
|
+
FROM facts f
|
|
117
|
+
WHERE NOT EXISTS (
|
|
118
|
+
SELECT 1 FROM cell c
|
|
119
|
+
WHERE c.refs @> ARRAY[f.hash]::text[]
|
|
120
|
+
AND c.deleted_at IS NULL
|
|
121
|
+
)
|
|
122
|
+
AND ($1::timestamptz IS NULL OR f.created_at < $1::timestamptz)`, [older_than]);
|
|
123
|
+
const sample_rows = await deps.db.query(`SELECT hash, size, created_at, external_url
|
|
124
|
+
FROM facts f
|
|
125
|
+
WHERE NOT EXISTS (
|
|
126
|
+
SELECT 1 FROM cell c
|
|
127
|
+
WHERE c.refs @> ARRAY[f.hash]::text[]
|
|
128
|
+
AND c.deleted_at IS NULL
|
|
129
|
+
)
|
|
130
|
+
AND ($1::timestamptz IS NULL OR f.created_at < $1::timestamptz)
|
|
131
|
+
ORDER BY f.created_at ASC
|
|
132
|
+
LIMIT $2`, [older_than, sample_limit]);
|
|
133
|
+
return {
|
|
134
|
+
count: Number(summary?.count ?? 0),
|
|
135
|
+
total_size_bytes: Number(summary?.total ?? 0),
|
|
136
|
+
sample: sample_rows.map((r) => ({
|
|
137
|
+
hash: r.hash,
|
|
138
|
+
size: Number(r.size),
|
|
139
|
+
created_at: typeof r.created_at === 'string' ? r.created_at : r.created_at.toISOString(),
|
|
140
|
+
external_url: r.external_url,
|
|
141
|
+
})),
|
|
142
|
+
};
|
|
143
|
+
};
|
|
144
|
+
/**
|
|
145
|
+
* Select the orphan-fact hashes for deletion. Returns the rows directly
|
|
146
|
+
* (no row-count limit) — callers iterate to unlink disk files. The
|
|
147
|
+
* `older_than` cutoff is required (non-null) here: bulk delete should
|
|
148
|
+
* always be operator-scoped to a time window. A "delete all" sweep
|
|
149
|
+
* passes a far-future cutoff, not `null`.
|
|
150
|
+
*/
|
|
151
|
+
export const query_orphan_facts_select_for_delete = async (deps, older_than) => {
|
|
152
|
+
const rows = await deps.db.query(`SELECT hash, size, external_url
|
|
153
|
+
FROM facts f
|
|
154
|
+
WHERE NOT EXISTS (
|
|
155
|
+
SELECT 1 FROM cell c
|
|
156
|
+
WHERE c.refs @> ARRAY[f.hash]::text[]
|
|
157
|
+
AND c.deleted_at IS NULL
|
|
158
|
+
)
|
|
159
|
+
AND f.created_at < $1::timestamptz`, [older_than]);
|
|
160
|
+
return rows.map((r) => ({ hash: r.hash, size: Number(r.size), external_url: r.external_url }));
|
|
161
|
+
};
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PG-backed `FactStore` implementation.
|
|
3
|
+
*
|
|
4
|
+
* Wraps the raw queries in `db/fact_queries.ts` with the lifecycle the
|
|
5
|
+
* `FactStore` interface promises:
|
|
6
|
+
*
|
|
7
|
+
* - sync hash on `put`, stream hash on `put_ref` (counting bytes against
|
|
8
|
+
* the caller-supplied `size`)
|
|
9
|
+
* - idempotent insert (`ON CONFLICT DO NOTHING` in the queries layer)
|
|
10
|
+
* - JSON ref auto-extraction when `content_type` signals JSON and the
|
|
11
|
+
* caller didn't pass an explicit `refs` array
|
|
12
|
+
* - verify-on-read for external content; embedded reads skip verify
|
|
13
|
+
* because PG storage IS the hash table
|
|
14
|
+
* - mismatched external bytes return `null` + log warning (treat as
|
|
15
|
+
* unavailable; GC / repair is a separate concern)
|
|
16
|
+
*
|
|
17
|
+
* Embedded vs referenced split: callers route by size. `put` rejects
|
|
18
|
+
* `bytes.length > embedded_threshold` so oversized content takes the
|
|
19
|
+
* `put_ref` path explicitly. Auto-split inside `put` is a future option.
|
|
20
|
+
*
|
|
21
|
+
* Wired with a filesystem `file:`-URL fetcher (`create_file_fact_fetcher`)
|
|
22
|
+
* at server assembly: bytes ≤ threshold embed via `put`, larger bytes go
|
|
23
|
+
* through atomic temp+rename onto disk then `put_ref('file:<shard>/<rest>',
|
|
24
|
+
* size)` for verified registration.
|
|
25
|
+
*
|
|
26
|
+
* @module
|
|
27
|
+
*/
|
|
28
|
+
import type { QueryDeps } from './query_deps.js';
|
|
29
|
+
import type { Logger } from '@fuzdev/fuz_util/log.js';
|
|
30
|
+
import { type FactHash } from '@fuzdev/fuz_util/fact_hash.js';
|
|
31
|
+
import type { FactMeta, FactPutOptions, FactStore } from '@fuzdev/fuz_util/fact_store.js';
|
|
32
|
+
/** Default embedded-vs-referenced cutoff (1 MiB). */
|
|
33
|
+
export declare const FACT_EMBEDDED_THRESHOLD_DEFAULT: number;
|
|
34
|
+
/** Fetcher abstraction so tests can stub external URL retrieval. */
|
|
35
|
+
export interface FactExternalFetcher {
|
|
36
|
+
fetch_stream: (url: string) => Promise<ReadableStream<Uint8Array>>;
|
|
37
|
+
fetch_bytes: (url: string) => Promise<Uint8Array>;
|
|
38
|
+
}
|
|
39
|
+
/** Default fetcher backed by `globalThis.fetch`. */
|
|
40
|
+
export declare const create_default_fetcher: () => FactExternalFetcher;
|
|
41
|
+
/**
|
|
42
|
+
* Construction-time deps for `PgFactStore`.
|
|
43
|
+
*
|
|
44
|
+
* `embedded_threshold` (bytes) is the inline-vs-external cutoff: payloads
|
|
45
|
+
* at or under it store embedded in the `facts` row, larger ones route to
|
|
46
|
+
* the external fetcher. Defaults to `FACT_EMBEDDED_THRESHOLD_DEFAULT`
|
|
47
|
+
* (1 MiB). Consumers tune it per workload — e.g. a much lower bound
|
|
48
|
+
* (~16 KiB) keeps only small JSON inline and routes image originals +
|
|
49
|
+
* thumbnails external. `fetcher` defaults to a `globalThis.fetch`-backed
|
|
50
|
+
* implementation; tests inject a stub. `log` is optional — the only call
|
|
51
|
+
* site is the verify-mismatch warning path.
|
|
52
|
+
*/
|
|
53
|
+
export interface PgFactStoreDeps {
|
|
54
|
+
deps: QueryDeps;
|
|
55
|
+
embedded_threshold?: number;
|
|
56
|
+
fetcher?: FactExternalFetcher;
|
|
57
|
+
log?: Logger;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* PG-backed `FactStore`. Delegates to `db/fact_queries.ts` for I/O and adds
|
|
61
|
+
* the lifecycle layer described in the module doc.
|
|
62
|
+
*/
|
|
63
|
+
export declare class PgFactStore implements FactStore {
|
|
64
|
+
#private;
|
|
65
|
+
constructor(options: PgFactStoreDeps);
|
|
66
|
+
/**
|
|
67
|
+
* Store small bytes embedded in PG. Rejects oversized content so the
|
|
68
|
+
* caller routes it through `put_ref` explicitly — implicit splitting
|
|
69
|
+
* hides the size decision from the caller.
|
|
70
|
+
*/
|
|
71
|
+
put(bytes: Uint8Array, options?: FactPutOptions): Promise<FactHash>;
|
|
72
|
+
/**
|
|
73
|
+
* Stream-hash external content and record `(hash, external_url, size)`.
|
|
74
|
+
* Throws when the streamed byte count disagrees with the caller's
|
|
75
|
+
* declared `size` — a size mismatch usually means the upload was
|
|
76
|
+
* truncated or the URL points at the wrong content.
|
|
77
|
+
*/
|
|
78
|
+
put_ref(url: string, size: number, options?: FactPutOptions): Promise<FactHash>;
|
|
79
|
+
/**
|
|
80
|
+
* Retrieve bytes. Embedded reads return PG bytes directly; external
|
|
81
|
+
* reads fetch + verify and return `null` (with a warning log) when
|
|
82
|
+
* the bytes don't match the stored hash.
|
|
83
|
+
*/
|
|
84
|
+
get(hash: FactHash): Promise<Uint8Array | null>;
|
|
85
|
+
has(hash: FactHash): Promise<boolean>;
|
|
86
|
+
get_meta(hash: FactHash): Promise<FactMeta | null>;
|
|
87
|
+
get_refs(hash: FactHash): Promise<Array<FactHash>>;
|
|
88
|
+
/**
|
|
89
|
+
* Drop a fact row. `fact_refs` rows referencing this hash as a source
|
|
90
|
+
* cascade via the FK; `fact_refs` targeting this hash do **not** —
|
|
91
|
+
* they remain as dangling pointers, consistent with the federation
|
|
92
|
+
* model where `target_hash` is intentionally not a FK.
|
|
93
|
+
*
|
|
94
|
+
* Idempotent: deleting an absent fact returns `null`. The store does
|
|
95
|
+
* NOT verify the fact is unreferenced — that policy lives one layer
|
|
96
|
+
* up (the orphan-fact admin surface in the consumer; a future GC walker).
|
|
97
|
+
*
|
|
98
|
+
* External-URL unlink is the caller's responsibility — the store
|
|
99
|
+
* doesn't know how to resolve `file:` / `s3:` / etc. URLs to a
|
|
100
|
+
* deletable handle. Caller iterates the returned `external_url`
|
|
101
|
+
* (when non-null) and dispatches to the appropriate cleanup
|
|
102
|
+
* routine. Mirrors the read-side `FactExternalFetcher` split.
|
|
103
|
+
*
|
|
104
|
+
* @returns `{size, external_url}` for the deleted row, or `null` if
|
|
105
|
+
* no row matched the hash.
|
|
106
|
+
*/
|
|
107
|
+
delete(hash: FactHash): Promise<{
|
|
108
|
+
size: number;
|
|
109
|
+
external_url: string | null;
|
|
110
|
+
} | null>;
|
|
111
|
+
}
|
|
112
|
+
//# sourceMappingURL=fact_store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fact_store.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/db/fact_store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,iBAAiB,CAAC;AAC/C,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,yBAAyB,CAAC;AAEpD,OAAO,EAKN,KAAK,QAAQ,EACb,MAAM,+BAA+B,CAAC;AACvC,OAAO,KAAK,EAAC,QAAQ,EAAE,cAAc,EAAE,SAAS,EAAC,MAAM,gCAAgC,CAAC;AAYxF,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;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,eAAe;IAC/B,IAAI,EAAE,SAAS,CAAC;IAChB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,OAAO,CAAC,EAAE,mBAAmB,CAAC;IAC9B,GAAG,CAAC,EAAE,MAAM,CAAC;CACb;AAED;;;GAGG;AACH,qBAAa,WAAY,YAAW,SAAS;;gBAMhC,OAAO,EAAE,eAAe;IAOpC;;;;OAIG;IACG,GAAG,CAAC,KAAK,EAAE,UAAU,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,QAAQ,CAAC;IAoBzE;;;;;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;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;KAAC,GAAG,IAAI,CAAC;CAGzF"}
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PG-backed `FactStore` implementation.
|
|
3
|
+
*
|
|
4
|
+
* Wraps the raw queries in `db/fact_queries.ts` with the lifecycle the
|
|
5
|
+
* `FactStore` interface promises:
|
|
6
|
+
*
|
|
7
|
+
* - sync hash on `put`, stream hash on `put_ref` (counting bytes against
|
|
8
|
+
* the caller-supplied `size`)
|
|
9
|
+
* - idempotent insert (`ON CONFLICT DO NOTHING` in the queries layer)
|
|
10
|
+
* - JSON ref auto-extraction when `content_type` signals JSON and the
|
|
11
|
+
* caller didn't pass an explicit `refs` array
|
|
12
|
+
* - verify-on-read for external content; embedded reads skip verify
|
|
13
|
+
* because PG storage IS the hash table
|
|
14
|
+
* - mismatched external bytes return `null` + log warning (treat as
|
|
15
|
+
* unavailable; GC / repair is a separate concern)
|
|
16
|
+
*
|
|
17
|
+
* Embedded vs referenced split: callers route by size. `put` rejects
|
|
18
|
+
* `bytes.length > embedded_threshold` so oversized content takes the
|
|
19
|
+
* `put_ref` path explicitly. Auto-split inside `put` is a future option.
|
|
20
|
+
*
|
|
21
|
+
* Wired with a filesystem `file:`-URL fetcher (`create_file_fact_fetcher`)
|
|
22
|
+
* at server assembly: bytes ≤ threshold embed via `put`, larger bytes go
|
|
23
|
+
* through atomic temp+rename onto disk then `put_ref('file:<shard>/<rest>',
|
|
24
|
+
* size)` for verified registration.
|
|
25
|
+
*
|
|
26
|
+
* @module
|
|
27
|
+
*/
|
|
28
|
+
import { fact_hash_bytes, fact_hash_stream, fact_hash_verify, fact_hash_extract_refs, } from '@fuzdev/fuz_util/fact_hash.js';
|
|
29
|
+
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';
|
|
30
|
+
/** Default embedded-vs-referenced cutoff (1 MiB). */
|
|
31
|
+
export const FACT_EMBEDDED_THRESHOLD_DEFAULT = 1024 * 1024;
|
|
32
|
+
/** Default fetcher backed by `globalThis.fetch`. */
|
|
33
|
+
export const create_default_fetcher = () => ({
|
|
34
|
+
fetch_stream: async (url) => {
|
|
35
|
+
const response = await fetch(url);
|
|
36
|
+
if (!response.ok) {
|
|
37
|
+
throw new Error(`fact fetch failed: ${response.status} ${url}`);
|
|
38
|
+
}
|
|
39
|
+
if (!response.body) {
|
|
40
|
+
throw new Error(`fact fetch returned no body: ${url}`);
|
|
41
|
+
}
|
|
42
|
+
return response.body;
|
|
43
|
+
},
|
|
44
|
+
fetch_bytes: async (url) => {
|
|
45
|
+
const response = await fetch(url);
|
|
46
|
+
if (!response.ok) {
|
|
47
|
+
throw new Error(`fact fetch failed: ${response.status} ${url}`);
|
|
48
|
+
}
|
|
49
|
+
return new Uint8Array(await response.arrayBuffer());
|
|
50
|
+
},
|
|
51
|
+
});
|
|
52
|
+
/**
|
|
53
|
+
* PG-backed `FactStore`. Delegates to `db/fact_queries.ts` for I/O and adds
|
|
54
|
+
* the lifecycle layer described in the module doc.
|
|
55
|
+
*/
|
|
56
|
+
export class PgFactStore {
|
|
57
|
+
#deps;
|
|
58
|
+
#embedded_threshold;
|
|
59
|
+
#fetcher;
|
|
60
|
+
#log;
|
|
61
|
+
constructor(options) {
|
|
62
|
+
this.#deps = options.deps;
|
|
63
|
+
this.#embedded_threshold = options.embedded_threshold ?? FACT_EMBEDDED_THRESHOLD_DEFAULT;
|
|
64
|
+
this.#fetcher = options.fetcher ?? create_default_fetcher();
|
|
65
|
+
this.#log = options.log;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Store small bytes embedded in PG. Rejects oversized content so the
|
|
69
|
+
* caller routes it through `put_ref` explicitly — implicit splitting
|
|
70
|
+
* hides the size decision from the caller.
|
|
71
|
+
*/
|
|
72
|
+
async put(bytes, options) {
|
|
73
|
+
if (bytes.length > this.#embedded_threshold) {
|
|
74
|
+
throw new Error(`fact bytes exceed embedded threshold (${bytes.length} > ${this.#embedded_threshold}); use put_ref for external storage`);
|
|
75
|
+
}
|
|
76
|
+
const hash = fact_hash_bytes(bytes);
|
|
77
|
+
const inserted = await query_put_fact(this.#deps, {
|
|
78
|
+
hash,
|
|
79
|
+
bytes,
|
|
80
|
+
external_url: null,
|
|
81
|
+
content_type: options?.content_type ?? null,
|
|
82
|
+
size: bytes.length,
|
|
83
|
+
});
|
|
84
|
+
if (inserted) {
|
|
85
|
+
await query_put_fact_refs(this.#deps, hash, resolve_refs(bytes, options));
|
|
86
|
+
}
|
|
87
|
+
return hash;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Stream-hash external content and record `(hash, external_url, size)`.
|
|
91
|
+
* Throws when the streamed byte count disagrees with the caller's
|
|
92
|
+
* declared `size` — a size mismatch usually means the upload was
|
|
93
|
+
* truncated or the URL points at the wrong content.
|
|
94
|
+
*/
|
|
95
|
+
async put_ref(url, size, options) {
|
|
96
|
+
const stream = await this.#fetcher.fetch_stream(url);
|
|
97
|
+
const { hash, byte_count } = await hash_counted_stream(stream);
|
|
98
|
+
if (byte_count !== size) {
|
|
99
|
+
throw new Error(`fact size mismatch for ${url}: caller declared ${size}, streamed ${byte_count}`);
|
|
100
|
+
}
|
|
101
|
+
const inserted = await query_put_fact(this.#deps, {
|
|
102
|
+
hash,
|
|
103
|
+
bytes: null,
|
|
104
|
+
external_url: url,
|
|
105
|
+
content_type: options?.content_type ?? null,
|
|
106
|
+
size,
|
|
107
|
+
});
|
|
108
|
+
if (inserted && options?.refs && options.refs.length > 0) {
|
|
109
|
+
await query_put_fact_refs(this.#deps, hash, options.refs);
|
|
110
|
+
}
|
|
111
|
+
return hash;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Retrieve bytes. Embedded reads return PG bytes directly; external
|
|
115
|
+
* reads fetch + verify and return `null` (with a warning log) when
|
|
116
|
+
* the bytes don't match the stored hash.
|
|
117
|
+
*/
|
|
118
|
+
async get(hash) {
|
|
119
|
+
const row = await query_get_fact(this.#deps, hash);
|
|
120
|
+
if (!row)
|
|
121
|
+
return null;
|
|
122
|
+
if (row.bytes !== null) {
|
|
123
|
+
return to_uint8(row.bytes);
|
|
124
|
+
}
|
|
125
|
+
if (row.external_url === null) {
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
let bytes;
|
|
129
|
+
try {
|
|
130
|
+
bytes = await this.#fetcher.fetch_bytes(row.external_url);
|
|
131
|
+
}
|
|
132
|
+
catch (err) {
|
|
133
|
+
this.#log?.warn(`PgFactStore.get fetch failed for ${hash} at ${row.external_url}:`, err instanceof Error ? err.message : String(err));
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
136
|
+
if (!fact_hash_verify(hash, bytes)) {
|
|
137
|
+
this.#log?.warn(`PgFactStore.get verify mismatch for ${hash} at ${row.external_url}; treating as not-found`);
|
|
138
|
+
return null;
|
|
139
|
+
}
|
|
140
|
+
return bytes;
|
|
141
|
+
}
|
|
142
|
+
async has(hash) {
|
|
143
|
+
return query_has_fact(this.#deps, hash);
|
|
144
|
+
}
|
|
145
|
+
async get_meta(hash) {
|
|
146
|
+
const row = await query_get_fact_meta(this.#deps, hash);
|
|
147
|
+
if (!row)
|
|
148
|
+
return null;
|
|
149
|
+
return {
|
|
150
|
+
content_type: row.content_type,
|
|
151
|
+
size: Number(row.size),
|
|
152
|
+
created_at: row.created_at,
|
|
153
|
+
external: row.external_url !== null,
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
async get_refs(hash) {
|
|
157
|
+
return query_get_fact_refs(this.#deps, hash);
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Drop a fact row. `fact_refs` rows referencing this hash as a source
|
|
161
|
+
* cascade via the FK; `fact_refs` targeting this hash do **not** —
|
|
162
|
+
* they remain as dangling pointers, consistent with the federation
|
|
163
|
+
* model where `target_hash` is intentionally not a FK.
|
|
164
|
+
*
|
|
165
|
+
* Idempotent: deleting an absent fact returns `null`. The store does
|
|
166
|
+
* NOT verify the fact is unreferenced — that policy lives one layer
|
|
167
|
+
* up (the orphan-fact admin surface in the consumer; a future GC walker).
|
|
168
|
+
*
|
|
169
|
+
* External-URL unlink is the caller's responsibility — the store
|
|
170
|
+
* doesn't know how to resolve `file:` / `s3:` / etc. URLs to a
|
|
171
|
+
* deletable handle. Caller iterates the returned `external_url`
|
|
172
|
+
* (when non-null) and dispatches to the appropriate cleanup
|
|
173
|
+
* routine. Mirrors the read-side `FactExternalFetcher` split.
|
|
174
|
+
*
|
|
175
|
+
* @returns `{size, external_url}` for the deleted row, or `null` if
|
|
176
|
+
* no row matched the hash.
|
|
177
|
+
*/
|
|
178
|
+
async delete(hash) {
|
|
179
|
+
return query_delete_fact(this.#deps, hash);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Resolve refs for a `put` call: explicit `refs` win; otherwise auto-extract
|
|
184
|
+
* from JSON content; otherwise no refs.
|
|
185
|
+
*/
|
|
186
|
+
const resolve_refs = (bytes, options) => {
|
|
187
|
+
if (options?.refs !== undefined)
|
|
188
|
+
return options.refs;
|
|
189
|
+
if (options?.content_type !== 'application/json')
|
|
190
|
+
return [];
|
|
191
|
+
let value;
|
|
192
|
+
try {
|
|
193
|
+
value = JSON.parse(new TextDecoder().decode(bytes));
|
|
194
|
+
}
|
|
195
|
+
catch {
|
|
196
|
+
// Malformed JSON — caller mislabeled content_type. Fall back to no refs;
|
|
197
|
+
// the alternative (throwing) would surprise callers who set
|
|
198
|
+
// content_type advisorially.
|
|
199
|
+
return [];
|
|
200
|
+
}
|
|
201
|
+
return fact_hash_extract_refs(value);
|
|
202
|
+
};
|
|
203
|
+
/** Hash a stream while counting bytes. Lets `put_ref` verify size in one pass. */
|
|
204
|
+
const hash_counted_stream = async (stream) => {
|
|
205
|
+
let byte_count = 0;
|
|
206
|
+
const counting = new TransformStream({
|
|
207
|
+
transform(chunk, controller) {
|
|
208
|
+
byte_count += chunk.length;
|
|
209
|
+
controller.enqueue(chunk);
|
|
210
|
+
},
|
|
211
|
+
});
|
|
212
|
+
const piped = stream.pipeThrough(counting);
|
|
213
|
+
const hash = await fact_hash_stream(piped);
|
|
214
|
+
return { hash, byte_count };
|
|
215
|
+
};
|
|
216
|
+
/**
|
|
217
|
+
* Coerce whatever the driver returns for BYTEA into a `Uint8Array`.
|
|
218
|
+
*
|
|
219
|
+
* `pg` returns `Buffer` (a `Uint8Array` subclass), `pglite` already returns
|
|
220
|
+
* `Uint8Array`. Wrapping `Buffer` in a fresh `Uint8Array` keeps the
|
|
221
|
+
* downstream type honest without a copy.
|
|
222
|
+
*/
|
|
223
|
+
const to_uint8 = (value) => value instanceof Uint8Array && value.constructor === Uint8Array
|
|
224
|
+
? value
|
|
225
|
+
: new Uint8Array(value.buffer, value.byteOffset, value.byteLength);
|
|
@@ -14,7 +14,6 @@ import { type SessionOptions } from '../auth/session_cookie.js';
|
|
|
14
14
|
import type { BootstrapAccountSuccess } from '../auth/bootstrap_account.js';
|
|
15
15
|
import type { EventSpec } from '../realtime/sse.js';
|
|
16
16
|
import { type AuditLogSse } from '../realtime/sse_auth_guard.js';
|
|
17
|
-
import type { AppSettings } from '../auth/app_settings_schema.js';
|
|
18
17
|
import { type RateLimiter } from '../rate_limiter.js';
|
|
19
18
|
import type { DaemonTokenState } from '../auth/daemon_token.js';
|
|
20
19
|
import type { MigrationResult } from '../db/migrate.js';
|
|
@@ -193,8 +192,7 @@ export interface AppServerOptions {
|
|
|
193
192
|
* Accepts either an array (evaluated eagerly) or a factory
|
|
194
193
|
* `(ctx: AppServerContext) => Array<RpcEndpointSpec>` (evaluated after the
|
|
195
194
|
* server context is assembled). Use the factory form when action lists
|
|
196
|
-
* depend on `ctx.deps`
|
|
197
|
-
* `create_standard_rpc_actions(ctx.deps, {app_settings: ctx.app_settings})`.
|
|
195
|
+
* depend on `ctx.deps` — e.g. `create_standard_rpc_actions(ctx.deps)`.
|
|
198
196
|
*/
|
|
199
197
|
rpc_endpoints?: Array<RpcEndpointSpec> | ((context: AppServerContext) => Array<RpcEndpointSpec>);
|
|
200
198
|
/**
|
|
@@ -294,8 +292,6 @@ export interface AppServerContext {
|
|
|
294
292
|
action_ip_rate_limiter: RateLimiter | null;
|
|
295
293
|
/** Per-actor action-dispatcher rate limiter — shared across HTTP RPC + WS. `null` when not configured. */
|
|
296
294
|
action_account_rate_limiter: RateLimiter | null;
|
|
297
|
-
/** Global app settings (mutable ref — mutated by settings admin route). */
|
|
298
|
-
app_settings: AppSettings;
|
|
299
295
|
/**
|
|
300
296
|
* Factory-managed audit log SSE. Non-null when the `audit_log_sse`
|
|
301
297
|
* option was passed to `create_app_server`, `null` when omitted.
|
|
@@ -309,8 +305,6 @@ export interface AppServer {
|
|
|
309
305
|
/** Surface spec — serializable surface + raw specs that produced it. */
|
|
310
306
|
surface_spec: AppSurfaceSpec;
|
|
311
307
|
bootstrap_status: BootstrapStatus;
|
|
312
|
-
/** Global app settings (mutable ref — mutated by settings admin route). */
|
|
313
|
-
app_settings: AppSettings;
|
|
314
308
|
/** Migration results from `create_app_backend` (auth + any `migration_namespaces` passed there). */
|
|
315
309
|
migration_results: ReadonlyArray<MigrationResult>;
|
|
316
310
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app_server.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/server/app_server.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAC,IAAI,EAAE,KAAK,OAAO,EAAC,MAAM,MAAM,CAAC;AAGxC,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,SAAS,CAAC;AAC9C,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAEtB,OAAO,EAEN,KAAK,cAAc,EAEnB,MAAM,2BAA2B,CAAC;AACnC,OAAO,KAAK,EAAC,uBAAuB,EAAC,MAAM,8BAA8B,CAAC;AAC1E,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAGN,KAAK,WAAW,EAChB,MAAM,+BAA+B,CAAC;AAEvC,OAAO,
|
|
1
|
+
{"version":3,"file":"app_server.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/server/app_server.ts"],"names":[],"mappings":"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"}
|
|
@@ -14,7 +14,6 @@ import { z } from 'zod';
|
|
|
14
14
|
import { session_cookie_options, } from '../auth/session_cookie.js';
|
|
15
15
|
import { create_audit_log_sse, audit_log_event_specs, } from '../realtime/sse_auth_guard.js';
|
|
16
16
|
import { BaseServerEnv } from './env.js';
|
|
17
|
-
import { query_app_settings_load } from '../auth/app_settings_queries.js';
|
|
18
17
|
import { create_rate_limiter, default_login_account_rate_limit, default_action_account_rate_limit, default_action_ip_rate_limit, } from '../rate_limiter.js';
|
|
19
18
|
// Side-effect import: augments Hono's ContextVariableMap so consumers
|
|
20
19
|
// that import app_server get type-safe c.get('auth_session_id') etc.
|
|
@@ -123,14 +122,13 @@ export const create_app_server = async (options) => {
|
|
|
123
122
|
if (options.transform_middleware) {
|
|
124
123
|
middleware_specs = options.transform_middleware(middleware_specs);
|
|
125
124
|
}
|
|
126
|
-
// Bootstrap status
|
|
125
|
+
// Bootstrap status
|
|
127
126
|
// - undefined / 'disabled': no route mounted; placeholder status.
|
|
128
127
|
// - 'surface_only': route mounted but permanently unavailable; status placeholder.
|
|
129
128
|
// - 'live': real disk + lock check via `check_bootstrap_status`.
|
|
130
129
|
const bootstrap_status = options.bootstrap?.mode === 'live'
|
|
131
130
|
? await check_bootstrap_status(deps, { token_path: options.bootstrap.token_path })
|
|
132
131
|
: { available: false, token_path: null };
|
|
133
|
-
const app_settings = await query_app_settings_load({ db: deps.db });
|
|
134
132
|
// Surface route ref — factory manages the circular ref
|
|
135
133
|
const surface_ref = {
|
|
136
134
|
surface: {
|
|
@@ -154,7 +152,6 @@ export const create_app_server = async (options) => {
|
|
|
154
152
|
signup_account_rate_limiter,
|
|
155
153
|
action_ip_rate_limiter,
|
|
156
154
|
action_account_rate_limiter,
|
|
157
|
-
app_settings,
|
|
158
155
|
audit_sse,
|
|
159
156
|
};
|
|
160
157
|
const consumer_routes = options.create_route_specs(context);
|
|
@@ -404,7 +401,6 @@ export const create_app_server = async (options) => {
|
|
|
404
401
|
app,
|
|
405
402
|
surface_spec,
|
|
406
403
|
bootstrap_status,
|
|
407
|
-
app_settings,
|
|
408
404
|
migration_results: backend.migration_results,
|
|
409
405
|
audit_sse,
|
|
410
406
|
ws_endpoints: mounted_ws_endpoints,
|
package/dist/server/env.d.ts
CHANGED
|
@@ -35,6 +35,8 @@ export declare const BaseServerEnv: z.ZodObject<{
|
|
|
35
35
|
SMTP_HOST: z.ZodOptional<z.ZodString>;
|
|
36
36
|
SMTP_USER: z.ZodOptional<z.ZodUnion<readonly [z.ZodEmail, z.ZodLiteral<"">]>>;
|
|
37
37
|
SMTP_PASSWORD: z.ZodOptional<z.ZodString>;
|
|
38
|
+
FUZ_FACTS_DIR: z.ZodDefault<z.ZodString>;
|
|
39
|
+
FUZ_FACTS_X_ACCEL_REDIRECT_PREFIX: z.ZodOptional<z.ZodString>;
|
|
38
40
|
}, z.core.$strict>;
|
|
39
41
|
export type BaseServerEnv = z.infer<typeof BaseServerEnv>;
|
|
40
42
|
/**
|
package/dist/server/env.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"env.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/server/env.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAEtB,OAAO,EAA2B,KAAK,OAAO,EAAC,MAAM,oBAAoB,CAAC;AAG1E;;;;;;;GAOG;AACH,eAAO,MAAM,aAAa
|
|
1
|
+
{"version":3,"file":"env.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/server/env.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAEtB,OAAO,EAA2B,KAAK,OAAO,EAAC,MAAM,oBAAoB,CAAC;AAG1E;;;;;;;GAOG;AACH,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;kBAwCxB,CAAC;AACH,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,CAAC;AAE1D;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAChC,EAAE,EAAE,IAAI,CAAC;IACT,OAAO,EAAE,OAAO,CAAC;IACjB,eAAe,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAC/B,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAC;CACpC;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACrC,EAAE,EAAE,KAAK,CAAC;IACV,KAAK,EAAE,wBAAwB,GAAG,qBAAqB,CAAC;IACxD,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;CACtB;AAED,MAAM,MAAM,sBAAsB,GAAG,gBAAgB,GAAG,qBAAqB,CAAC;AAE9E;;;;;;;;GAQG;AACH,eAAO,MAAM,mBAAmB,GAAI,KAAK,aAAa,KAAG,sBA4BxD,CAAC"}
|
package/dist/server/env.js
CHANGED
|
@@ -53,6 +53,12 @@ export const BaseServerEnv = z.strictObject({
|
|
|
53
53
|
.string()
|
|
54
54
|
.optional()
|
|
55
55
|
.meta({ description: 'SMTP authentication password', sensitivity: 'secret' }),
|
|
56
|
+
FUZ_FACTS_DIR: z.string().min(1).default('./.facts').meta({
|
|
57
|
+
description: 'Directory for referenced (large) fact bytes, sharded <shard>/<rest>',
|
|
58
|
+
}),
|
|
59
|
+
FUZ_FACTS_X_ACCEL_REDIRECT_PREFIX: z.string().optional().meta({
|
|
60
|
+
description: 'Internal nginx prefix for X-Accel-Redirect fact delivery (production only)',
|
|
61
|
+
}),
|
|
56
62
|
});
|
|
57
63
|
/**
|
|
58
64
|
* Validate a loaded `BaseServerEnv` and produce the artifacts needed for server init.
|