@cyanheads/congressgov-mcp-server 0.3.31 → 0.4.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/AGENTS.md +26 -5
- package/CLAUDE.md +26 -5
- package/Dockerfile +13 -0
- package/README.md +18 -7
- package/dist/config/server-config.d.ts +15 -0
- package/dist/config/server-config.d.ts.map +1 -1
- package/dist/config/server-config.js +66 -1
- package/dist/config/server-config.js.map +1 -1
- package/dist/index.js +30 -2
- package/dist/index.js.map +1 -1
- package/dist/mcp-server/tools/definitions/committee-lookup.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/committee-lookup.tool.js +40 -10
- package/dist/mcp-server/tools/definitions/committee-lookup.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/search-bills.tool.d.ts +47 -0
- package/dist/mcp-server/tools/definitions/search-bills.tool.d.ts.map +1 -0
- package/dist/mcp-server/tools/definitions/search-bills.tool.js +99 -0
- package/dist/mcp-server/tools/definitions/search-bills.tool.js.map +1 -0
- package/dist/mcp-server/tools/format-helpers.d.ts +2 -0
- package/dist/mcp-server/tools/format-helpers.d.ts.map +1 -1
- package/dist/mcp-server/tools/format-helpers.js +31 -0
- package/dist/mcp-server/tools/format-helpers.js.map +1 -1
- package/dist/services/congress-mirror/congress-mirror-service.d.ts +45 -0
- package/dist/services/congress-mirror/congress-mirror-service.d.ts.map +1 -0
- package/dist/services/congress-mirror/congress-mirror-service.js +116 -0
- package/dist/services/congress-mirror/congress-mirror-service.js.map +1 -0
- package/dist/services/congress-mirror/ingest.d.ts +33 -0
- package/dist/services/congress-mirror/ingest.d.ts.map +1 -0
- package/dist/services/congress-mirror/ingest.js +179 -0
- package/dist/services/congress-mirror/ingest.js.map +1 -0
- package/dist/services/congress-mirror/normalize.d.ts +25 -0
- package/dist/services/congress-mirror/normalize.d.ts.map +1 -0
- package/dist/services/congress-mirror/normalize.js +47 -0
- package/dist/services/congress-mirror/normalize.js.map +1 -0
- package/dist/services/congress-mirror/schema.d.ts +19 -0
- package/dist/services/congress-mirror/schema.d.ts.map +1 -0
- package/dist/services/congress-mirror/schema.js +44 -0
- package/dist/services/congress-mirror/schema.js.map +1 -0
- package/dist/services/congress-mirror/types.d.ts +55 -0
- package/dist/services/congress-mirror/types.d.ts.map +1 -0
- package/dist/services/congress-mirror/types.js +9 -0
- package/dist/services/congress-mirror/types.js.map +1 -0
- package/package.json +33 -7
- package/scripts/_mirror-context.ts +30 -0
- package/scripts/congress-mirror-init.ts +23 -0
- package/scripts/congress-mirror-refresh.ts +24 -0
- package/scripts/congress-mirror-verify.ts +36 -0
- package/server.json +5 -5
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Congress bill mirror service — the read path over the local
|
|
3
|
+
* SQLite FTS index of Congress.gov bills. Built on the framework MirrorService:
|
|
4
|
+
* owns the store + ingester, exposes the mirror instance to the lifecycle CLI and
|
|
5
|
+
* the refresh scheduler, and translates the search tool's query grammar into an
|
|
6
|
+
* FTS5 `MATCH` + indexed filters. There is no live-API fallback — keyword search
|
|
7
|
+
* is the entire point — so a cold mirror is a readiness signal the caller turns
|
|
8
|
+
* into an empty result + notice, never a throw.
|
|
9
|
+
* @module services/congress-mirror/congress-mirror-service
|
|
10
|
+
*/
|
|
11
|
+
import type { Context } from '@cyanheads/mcp-ts-core';
|
|
12
|
+
import { type Mirror } from '@cyanheads/mcp-ts-core/mirror';
|
|
13
|
+
import { type MirrorApi } from './ingest.js';
|
|
14
|
+
import type { SearchBillsFilters, SearchBillsPage } from './types.js';
|
|
15
|
+
/** Options for constructing the mirror service. */
|
|
16
|
+
export interface CongressMirrorOptions {
|
|
17
|
+
/** Congress numbers to mirror. */
|
|
18
|
+
congresses: number[];
|
|
19
|
+
/** API accessor override — defaults to the shared `CongressApiService` singleton. */
|
|
20
|
+
getApi?: () => MirrorApi;
|
|
21
|
+
/** Filesystem path to the SQLite mirror index. */
|
|
22
|
+
mirrorPath: string;
|
|
23
|
+
}
|
|
24
|
+
export declare class CongressMirrorService {
|
|
25
|
+
private readonly mirror;
|
|
26
|
+
constructor(options: CongressMirrorOptions);
|
|
27
|
+
/** The underlying mirror — exposed for the lifecycle CLI and the refresh scheduler. */
|
|
28
|
+
get mirrorInstance(): Mirror;
|
|
29
|
+
/** `true` once a full sync has ever completed (queryable even mid-refresh). */
|
|
30
|
+
ready(): Promise<boolean>;
|
|
31
|
+
/**
|
|
32
|
+
* Keyword-search the mirror. The query is tokenized and each token individually
|
|
33
|
+
* quoted before the FTS5 `MATCH` is built; an input with no searchable tokens
|
|
34
|
+
* yields an empty page rather than a thrown query. Optional exact-match filters
|
|
35
|
+
* ride the indexed columns; results are BM25-ranked.
|
|
36
|
+
*/
|
|
37
|
+
search(filters: SearchBillsFilters, ctx: Context): Promise<SearchBillsPage>;
|
|
38
|
+
}
|
|
39
|
+
/** Initialize the mirror service. Call once in `createApp`'s `setup()` (when enabled). */
|
|
40
|
+
export declare function initCongressMirror(options: CongressMirrorOptions): CongressMirrorService;
|
|
41
|
+
/** Access the initialized mirror service. */
|
|
42
|
+
export declare function getCongressMirror(): CongressMirrorService;
|
|
43
|
+
/** Reset the singleton — test-only. */
|
|
44
|
+
export declare function resetCongressMirror(): void;
|
|
45
|
+
//# sourceMappingURL=congress-mirror-service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"congress-mirror-service.d.ts","sourceRoot":"","sources":["../../../src/services/congress-mirror/congress-mirror-service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AACtD,OAAO,EAEL,KAAK,MAAM,EAIZ,MAAM,+BAA+B,CAAC;AAEvC,OAAO,EAA8B,KAAK,SAAS,EAAE,MAAM,aAAa,CAAC;AAGzE,OAAO,KAAK,EAAoB,kBAAkB,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAKxF,mDAAmD;AACnD,MAAM,WAAW,qBAAqB;IACpC,kCAAkC;IAClC,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,qFAAqF;IACrF,MAAM,CAAC,EAAE,MAAM,SAAS,CAAC;IACzB,kDAAkD;IAClD,UAAU,EAAE,MAAM,CAAC;CACpB;AAwBD,qBAAa,qBAAqB;IAChC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;gBAEpB,OAAO,EAAE,qBAAqB;IAY1C,uFAAuF;IACvF,IAAI,cAAc,IAAI,MAAM,CAE3B;IAED,+EAA+E;IAC/E,KAAK,IAAI,OAAO,CAAC,OAAO,CAAC;IAIzB;;;;;OAKG;IACG,MAAM,CAAC,OAAO,EAAE,kBAAkB,EAAE,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,eAAe,CAAC;CAuClF;AAMD,0FAA0F;AAC1F,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,qBAAqB,GAAG,qBAAqB,CAGxF;AAED,6CAA6C;AAC7C,wBAAgB,iBAAiB,IAAI,qBAAqB,CAOzD;AAED,uCAAuC;AACvC,wBAAgB,mBAAmB,IAAI,IAAI,CAE1C"}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Congress bill mirror service — the read path over the local
|
|
3
|
+
* SQLite FTS index of Congress.gov bills. Built on the framework MirrorService:
|
|
4
|
+
* owns the store + ingester, exposes the mirror instance to the lifecycle CLI and
|
|
5
|
+
* the refresh scheduler, and translates the search tool's query grammar into an
|
|
6
|
+
* FTS5 `MATCH` + indexed filters. There is no live-API fallback — keyword search
|
|
7
|
+
* is the entire point — so a cold mirror is a readiness signal the caller turns
|
|
8
|
+
* into an empty result + notice, never a throw.
|
|
9
|
+
* @module services/congress-mirror/congress-mirror-service
|
|
10
|
+
*/
|
|
11
|
+
import { defineMirror, sqliteMirrorStore, } from '@cyanheads/mcp-ts-core/mirror';
|
|
12
|
+
import { getCongressApi } from '../../services/congress-api/congress-api-service.js';
|
|
13
|
+
import { createCongressBillIngester } from './ingest.js';
|
|
14
|
+
import { toFtsMatch } from './normalize.js';
|
|
15
|
+
import { billStoreSpec } from './schema.js';
|
|
16
|
+
/** Max stored-summary characters surfaced as a search-result preview. */
|
|
17
|
+
const SUMMARY_PREVIEW_LIMIT = 400;
|
|
18
|
+
/** Project a raw mirror row into a search result, truncating the summary preview. */
|
|
19
|
+
function toSearchResult(row) {
|
|
20
|
+
const summary = typeof row.summary === 'string' && row.summary.trim() !== '' ? row.summary : undefined;
|
|
21
|
+
const summaryPreview = summary
|
|
22
|
+
? summary.length > SUMMARY_PREVIEW_LIMIT
|
|
23
|
+
? `${summary.slice(0, SUMMARY_PREVIEW_LIMIT - 1).trimEnd()}…`
|
|
24
|
+
: summary
|
|
25
|
+
: undefined;
|
|
26
|
+
return {
|
|
27
|
+
billId: String(row.billId),
|
|
28
|
+
congress: Number(row.congress),
|
|
29
|
+
billType: String(row.billType),
|
|
30
|
+
billNumber: Number(row.billNumber),
|
|
31
|
+
title: typeof row.title === 'string' ? row.title : '',
|
|
32
|
+
...(row.originChamber ? { originChamber: String(row.originChamber) } : {}),
|
|
33
|
+
...(row.latestActionDate ? { latestActionDate: String(row.latestActionDate) } : {}),
|
|
34
|
+
...(row.latestActionText ? { latestActionText: String(row.latestActionText) } : {}),
|
|
35
|
+
...(summaryPreview ? { summaryPreview } : {}),
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
export class CongressMirrorService {
|
|
39
|
+
mirror;
|
|
40
|
+
constructor(options) {
|
|
41
|
+
const store = sqliteMirrorStore(billStoreSpec(options.mirrorPath));
|
|
42
|
+
this.mirror = defineMirror({
|
|
43
|
+
name: 'congress-bills',
|
|
44
|
+
store,
|
|
45
|
+
sync: createCongressBillIngester(options.getApi ?? (() => getCongressApi()), options.congresses),
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
/** The underlying mirror — exposed for the lifecycle CLI and the refresh scheduler. */
|
|
49
|
+
get mirrorInstance() {
|
|
50
|
+
return this.mirror;
|
|
51
|
+
}
|
|
52
|
+
/** `true` once a full sync has ever completed (queryable even mid-refresh). */
|
|
53
|
+
ready() {
|
|
54
|
+
return this.mirror.ready();
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Keyword-search the mirror. The query is tokenized and each token individually
|
|
58
|
+
* quoted before the FTS5 `MATCH` is built; an input with no searchable tokens
|
|
59
|
+
* yields an empty page rather than a thrown query. Optional exact-match filters
|
|
60
|
+
* ride the indexed columns; results are BM25-ranked.
|
|
61
|
+
*/
|
|
62
|
+
async search(filters, ctx) {
|
|
63
|
+
const match = toFtsMatch(filters.query);
|
|
64
|
+
if (match === '') {
|
|
65
|
+
return { items: [], total: 0, offset: filters.offset, limit: filters.limit };
|
|
66
|
+
}
|
|
67
|
+
const structured = [];
|
|
68
|
+
if (filters.congress !== undefined) {
|
|
69
|
+
structured.push({ column: 'congress', op: 'eq', value: filters.congress });
|
|
70
|
+
}
|
|
71
|
+
if (filters.billType) {
|
|
72
|
+
structured.push({ column: 'billType', op: 'eq', value: filters.billType });
|
|
73
|
+
}
|
|
74
|
+
if (filters.originChamber) {
|
|
75
|
+
structured.push({ column: 'originChamber', op: 'eq', value: filters.originChamber });
|
|
76
|
+
}
|
|
77
|
+
ctx.log.debug('Searching bill mirror', {
|
|
78
|
+
match,
|
|
79
|
+
filters: structured.length,
|
|
80
|
+
limit: filters.limit,
|
|
81
|
+
offset: filters.offset,
|
|
82
|
+
});
|
|
83
|
+
const result = await this.mirror.query({
|
|
84
|
+
match,
|
|
85
|
+
...(structured.length > 0 ? { filters: structured } : {}),
|
|
86
|
+
sort: 'relevance',
|
|
87
|
+
limit: filters.limit,
|
|
88
|
+
offset: filters.offset,
|
|
89
|
+
});
|
|
90
|
+
return {
|
|
91
|
+
items: result.rows.map(toSearchResult),
|
|
92
|
+
total: result.total,
|
|
93
|
+
offset: filters.offset,
|
|
94
|
+
limit: filters.limit,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
// --- Init/accessor pattern ---
|
|
99
|
+
let _service;
|
|
100
|
+
/** Initialize the mirror service. Call once in `createApp`'s `setup()` (when enabled). */
|
|
101
|
+
export function initCongressMirror(options) {
|
|
102
|
+
_service = new CongressMirrorService(options);
|
|
103
|
+
return _service;
|
|
104
|
+
}
|
|
105
|
+
/** Access the initialized mirror service. */
|
|
106
|
+
export function getCongressMirror() {
|
|
107
|
+
if (!_service) {
|
|
108
|
+
throw new Error('CongressMirrorService not initialized — call initCongressMirror() in setup().');
|
|
109
|
+
}
|
|
110
|
+
return _service;
|
|
111
|
+
}
|
|
112
|
+
/** Reset the singleton — test-only. */
|
|
113
|
+
export function resetCongressMirror() {
|
|
114
|
+
_service = undefined;
|
|
115
|
+
}
|
|
116
|
+
//# sourceMappingURL=congress-mirror-service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"congress-mirror-service.js","sourceRoot":"","sources":["../../../src/services/congress-mirror/congress-mirror-service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,EACL,YAAY,EAIZ,iBAAiB,GAClB,MAAM,+BAA+B,CAAC;AACvC,OAAO,EAAE,cAAc,EAAE,MAAM,iDAAiD,CAAC;AACjF,OAAO,EAAE,0BAA0B,EAAkB,MAAM,aAAa,CAAC;AACzE,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAG5C,yEAAyE;AACzE,MAAM,qBAAqB,GAAG,GAAG,CAAC;AAYlC,qFAAqF;AACrF,SAAS,cAAc,CAAC,GAAc;IACpC,MAAM,OAAO,GACX,OAAO,GAAG,CAAC,OAAO,KAAK,QAAQ,IAAI,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC;IACzF,MAAM,cAAc,GAAG,OAAO;QAC5B,CAAC,CAAC,OAAO,CAAC,MAAM,GAAG,qBAAqB;YACtC,CAAC,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,qBAAqB,GAAG,CAAC,CAAC,CAAC,OAAO,EAAE,GAAG;YAC7D,CAAC,CAAC,OAAO;QACX,CAAC,CAAC,SAAS,CAAC;IACd,OAAO;QACL,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC;QAC1B,QAAQ,EAAE,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;QAC9B,QAAQ,EAAE,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;QAC9B,UAAU,EAAE,MAAM,CAAC,GAAG,CAAC,UAAU,CAAC;QAClC,KAAK,EAAE,OAAO,GAAG,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE;QACrD,GAAG,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,aAAa,EAAE,MAAM,CAAC,GAAG,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC1E,GAAG,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC,CAAC,EAAE,gBAAgB,EAAE,MAAM,CAAC,GAAG,CAAC,gBAAgB,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACnF,GAAG,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC,CAAC,EAAE,gBAAgB,EAAE,MAAM,CAAC,GAAG,CAAC,gBAAgB,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACnF,GAAG,CAAC,cAAc,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAC9C,CAAC;AACJ,CAAC;AAED,MAAM,OAAO,qBAAqB;IACf,MAAM,CAAS;IAEhC,YAAY,OAA8B;QACxC,MAAM,KAAK,GAAG,iBAAiB,CAAC,aAAa,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC;QACnE,IAAI,CAAC,MAAM,GAAG,YAAY,CAAC;YACzB,IAAI,EAAE,gBAAgB;YACtB,KAAK;YACL,IAAI,EAAE,0BAA0B,CAC9B,OAAO,CAAC,MAAM,IAAI,CAAC,GAAG,EAAE,CAAC,cAAc,EAAE,CAAC,EAC1C,OAAO,CAAC,UAAU,CACnB;SACF,CAAC,CAAC;IACL,CAAC;IAED,uFAAuF;IACvF,IAAI,cAAc;QAChB,OAAO,IAAI,CAAC,MAAM,CAAC;IACrB,CAAC;IAED,+EAA+E;IAC/E,KAAK;QACH,OAAO,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;IAC7B,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,MAAM,CAAC,OAA2B,EAAE,GAAY;QACpD,MAAM,KAAK,GAAG,UAAU,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;QACxC,IAAI,KAAK,KAAK,EAAE,EAAE,CAAC;YACjB,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,CAAC;QAC/E,CAAC;QAED,MAAM,UAAU,GAAkB,EAAE,CAAC;QACrC,IAAI,OAAO,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;YACnC,UAAU,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC;QAC7E,CAAC;QACD,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC;YACrB,UAAU,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC;QAC7E,CAAC;QACD,IAAI,OAAO,CAAC,aAAa,EAAE,CAAC;YAC1B,UAAU,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,eAAe,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,CAAC,aAAa,EAAE,CAAC,CAAC;QACvF,CAAC;QAED,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,uBAAuB,EAAE;YACrC,KAAK;YACL,OAAO,EAAE,UAAU,CAAC,MAAM;YAC1B,KAAK,EAAE,OAAO,CAAC,KAAK;YACpB,MAAM,EAAE,OAAO,CAAC,MAAM;SACvB,CAAC,CAAC;QAEH,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC;YACrC,KAAK;YACL,GAAG,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACzD,IAAI,EAAE,WAAW;YACjB,KAAK,EAAE,OAAO,CAAC,KAAK;YACpB,MAAM,EAAE,OAAO,CAAC,MAAM;SACvB,CAAC,CAAC;QAEH,OAAO;YACL,KAAK,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,cAAc,CAAC;YACtC,KAAK,EAAE,MAAM,CAAC,KAAK;YACnB,MAAM,EAAE,OAAO,CAAC,MAAM;YACtB,KAAK,EAAE,OAAO,CAAC,KAAK;SACrB,CAAC;IACJ,CAAC;CACF;AAED,gCAAgC;AAEhC,IAAI,QAA2C,CAAC;AAEhD,0FAA0F;AAC1F,MAAM,UAAU,kBAAkB,CAAC,OAA8B;IAC/D,QAAQ,GAAG,IAAI,qBAAqB,CAAC,OAAO,CAAC,CAAC;IAC9C,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,6CAA6C;AAC7C,MAAM,UAAU,iBAAiB;IAC/B,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,MAAM,IAAI,KAAK,CACb,+EAA+E,CAChF,CAAC;IACJ,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,uCAAuC;AACvC,MAAM,UAAU,mBAAmB;IACjC,QAAQ,GAAG,SAAS,CAAC;AACvB,CAAC"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview The Congress bill ingester — the one irreducibly per-source part
|
|
3
|
+
* of the mirror. Merges two upstream Congress.gov sources into one row per bill:
|
|
4
|
+
* the bill list (`/bill/{congress}`, sorted by updateDate) supplies the skeleton
|
|
5
|
+
* (title, chamber, latest action, update date); the CRS summaries list
|
|
6
|
+
* (`/summaries/{congress}`) supplies the summary text. Both reuse the existing
|
|
7
|
+
* `CongressApiService` — no new HTTP-calling code, only merge/orchestration.
|
|
8
|
+
*
|
|
9
|
+
* Merge order matters: the framework upsert writes every declared column for each
|
|
10
|
+
* record, so a partial (summary-only) upsert would null the title. The summaries
|
|
11
|
+
* are therefore loaded into a per-congress map first, then attached as each full
|
|
12
|
+
* bill row is emitted. Summaries are fully re-walked every sync (not date-filtered)
|
|
13
|
+
* so a re-emitted bill never loses a previously-stored summary. The durable
|
|
14
|
+
* checkpoint is the bills' `updateDate` high-water mark; refresh re-scans bills
|
|
15
|
+
* from it inclusively (`sort=updateDate asc`), and the upsert makes the overlap
|
|
16
|
+
* harmless.
|
|
17
|
+
* @module services/congress-mirror/ingest
|
|
18
|
+
*/
|
|
19
|
+
import type { SyncGenerator } from '@cyanheads/mcp-ts-core/mirror';
|
|
20
|
+
import type { CongressApiService } from '../../services/congress-api/congress-api-service.js';
|
|
21
|
+
/** The two service methods the ingester needs — injectable so tests can mock the API. */
|
|
22
|
+
export type MirrorApi = Pick<CongressApiService, 'listBills' | 'listSummaries'>;
|
|
23
|
+
/**
|
|
24
|
+
* Build the Congress bill ingester. The returned generator walks the configured
|
|
25
|
+
* congresses; for each, it loads the summary map, then streams bill pages
|
|
26
|
+
* (`sort=updateDate asc`) as full merged rows, advancing the durable checkpoint
|
|
27
|
+
* to the max `updateDate` seen.
|
|
28
|
+
*
|
|
29
|
+
* @param getApi - Accessor for the Congress API service (injectable for tests).
|
|
30
|
+
* @param congresses - Congress numbers to mirror.
|
|
31
|
+
*/
|
|
32
|
+
export declare function createCongressBillIngester(getApi: () => MirrorApi, congresses: number[]): SyncGenerator;
|
|
33
|
+
//# sourceMappingURL=ingest.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ingest.d.ts","sourceRoot":"","sources":["../../../src/services/congress-mirror/ingest.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAGV,aAAa,EAEd,MAAM,+BAA+B,CAAC;AAEvC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,iDAAiD,CAAC;AAM1F,yFAAyF;AACzF,MAAM,MAAM,SAAS,GAAG,IAAI,CAAC,kBAAkB,EAAE,WAAW,GAAG,eAAe,CAAC,CAAC;AA8GhF;;;;;;;;GAQG;AACH,wBAAgB,0BAA0B,CACxC,MAAM,EAAE,MAAM,SAAS,EACvB,UAAU,EAAE,MAAM,EAAE,GACnB,aAAa,CA+Cf"}
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview The Congress bill ingester — the one irreducibly per-source part
|
|
3
|
+
* of the mirror. Merges two upstream Congress.gov sources into one row per bill:
|
|
4
|
+
* the bill list (`/bill/{congress}`, sorted by updateDate) supplies the skeleton
|
|
5
|
+
* (title, chamber, latest action, update date); the CRS summaries list
|
|
6
|
+
* (`/summaries/{congress}`) supplies the summary text. Both reuse the existing
|
|
7
|
+
* `CongressApiService` — no new HTTP-calling code, only merge/orchestration.
|
|
8
|
+
*
|
|
9
|
+
* Merge order matters: the framework upsert writes every declared column for each
|
|
10
|
+
* record, so a partial (summary-only) upsert would null the title. The summaries
|
|
11
|
+
* are therefore loaded into a per-congress map first, then attached as each full
|
|
12
|
+
* bill row is emitted. Summaries are fully re-walked every sync (not date-filtered)
|
|
13
|
+
* so a re-emitted bill never loses a previously-stored summary. The durable
|
|
14
|
+
* checkpoint is the bills' `updateDate` high-water mark; refresh re-scans bills
|
|
15
|
+
* from it inclusively (`sort=updateDate asc`), and the upsert makes the overlap
|
|
16
|
+
* harmless.
|
|
17
|
+
* @module services/congress-mirror/ingest
|
|
18
|
+
*/
|
|
19
|
+
import { logger } from '@cyanheads/mcp-ts-core/utils';
|
|
20
|
+
import { htmlToPlainText } from './normalize.js';
|
|
21
|
+
/** Congress.gov list endpoints cap the page size at 250 (confirmed live). */
|
|
22
|
+
const PAGE_SIZE = 250;
|
|
23
|
+
function isRecord(value) {
|
|
24
|
+
return typeof value === 'object' && value !== null;
|
|
25
|
+
}
|
|
26
|
+
/** Read a non-empty string (or a stringified number) from a raw field. */
|
|
27
|
+
function str(value) {
|
|
28
|
+
if (typeof value === 'string') {
|
|
29
|
+
const trimmed = value.trim();
|
|
30
|
+
return trimmed === '' ? undefined : trimmed;
|
|
31
|
+
}
|
|
32
|
+
if (typeof value === 'number' && Number.isFinite(value))
|
|
33
|
+
return String(value);
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
/** Read an integer from a raw field (number or numeric string). */
|
|
37
|
+
function int(value) {
|
|
38
|
+
if (typeof value === 'number' && Number.isFinite(value))
|
|
39
|
+
return Math.trunc(value);
|
|
40
|
+
if (typeof value === 'string' && value.trim() !== '') {
|
|
41
|
+
const parsed = Number.parseInt(value.trim(), 10);
|
|
42
|
+
return Number.isNaN(parsed) ? undefined : parsed;
|
|
43
|
+
}
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Derive the composite primary key. Matches the existing
|
|
48
|
+
* `congress://bill/{congress}/{billType}/{billNumber}` resource addressing; the
|
|
49
|
+
* bill type is lowercased so both upstream sources key identically.
|
|
50
|
+
*/
|
|
51
|
+
function deriveBillId(congress, billType, billNumber) {
|
|
52
|
+
return `${congress}/${billType.toLowerCase()}/${billNumber}`;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* The bill list emits date-only `updateDate` ("2026-06-30"); the API's
|
|
56
|
+
* `fromDateTime` filter wants full ISO 8601. Widen a date-only checkpoint to the
|
|
57
|
+
* start of that day so the inclusive re-scan catches same-day updates.
|
|
58
|
+
*/
|
|
59
|
+
function toFromDateTime(checkpoint) {
|
|
60
|
+
return /^\d{4}-\d{2}-\d{2}$/.test(checkpoint) ? `${checkpoint}T00:00:00Z` : checkpoint;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Page the CRS summaries list for one congress into a `billId → plain-text
|
|
64
|
+
* summary` map, keeping the latest version per bill (by summary update/action
|
|
65
|
+
* stamp). Summaries ship as HTML and are stripped to plain text before storage.
|
|
66
|
+
*/
|
|
67
|
+
async function buildSummaryMap(api, congress, signal) {
|
|
68
|
+
const latest = new Map();
|
|
69
|
+
let offset = 0;
|
|
70
|
+
while (!signal.aborted) {
|
|
71
|
+
const result = await api.listSummaries({ congress, limit: PAGE_SIZE, offset });
|
|
72
|
+
const rows = result.data;
|
|
73
|
+
if (rows.length === 0)
|
|
74
|
+
break;
|
|
75
|
+
for (const raw of rows) {
|
|
76
|
+
if (!isRecord(raw) || !isRecord(raw.bill))
|
|
77
|
+
continue;
|
|
78
|
+
const bill = raw.bill;
|
|
79
|
+
const c = int(bill.congress);
|
|
80
|
+
const billType = str(bill.type)?.toLowerCase();
|
|
81
|
+
const billNumber = int(bill.number);
|
|
82
|
+
if (c === undefined || billType === undefined || billNumber === undefined)
|
|
83
|
+
continue;
|
|
84
|
+
const text = typeof raw.text === 'string' ? htmlToPlainText(raw.text) : '';
|
|
85
|
+
if (!text)
|
|
86
|
+
continue;
|
|
87
|
+
const id = deriveBillId(c, billType, billNumber);
|
|
88
|
+
const stamp = str(raw.updateDate) ?? str(raw.actionDate) ?? '';
|
|
89
|
+
const existing = latest.get(id);
|
|
90
|
+
if (!existing || stamp >= existing.stamp)
|
|
91
|
+
latest.set(id, { text, stamp });
|
|
92
|
+
}
|
|
93
|
+
if (rows.length < PAGE_SIZE || result.pagination.nextOffset == null)
|
|
94
|
+
break;
|
|
95
|
+
offset += PAGE_SIZE;
|
|
96
|
+
}
|
|
97
|
+
const map = new Map();
|
|
98
|
+
for (const [id, value] of latest)
|
|
99
|
+
map.set(id, value.text);
|
|
100
|
+
return map;
|
|
101
|
+
}
|
|
102
|
+
/** Map one raw bill-list record to a full mirror row, joining the summary map. */
|
|
103
|
+
function toBillRow(raw, fallbackCongress, summaries) {
|
|
104
|
+
const billType = str(raw.type)?.toLowerCase();
|
|
105
|
+
const billNumber = int(raw.number);
|
|
106
|
+
if (billType === undefined || billNumber === undefined)
|
|
107
|
+
return null;
|
|
108
|
+
const congress = int(raw.congress) ?? fallbackCongress;
|
|
109
|
+
const id = deriveBillId(congress, billType, billNumber);
|
|
110
|
+
const latestAction = isRecord(raw.latestAction) ? raw.latestAction : undefined;
|
|
111
|
+
return {
|
|
112
|
+
billId: id,
|
|
113
|
+
congress,
|
|
114
|
+
billType,
|
|
115
|
+
billNumber,
|
|
116
|
+
title: str(raw.title) ?? null,
|
|
117
|
+
summary: summaries.get(id) ?? null,
|
|
118
|
+
originChamber: str(raw.originChamber) ?? null,
|
|
119
|
+
latestActionDate: latestAction ? (str(latestAction.actionDate) ?? null) : null,
|
|
120
|
+
latestActionText: latestAction ? (str(latestAction.text) ?? null) : null,
|
|
121
|
+
updateDate: str(raw.updateDate) ?? null,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Build the Congress bill ingester. The returned generator walks the configured
|
|
126
|
+
* congresses; for each, it loads the summary map, then streams bill pages
|
|
127
|
+
* (`sort=updateDate asc`) as full merged rows, advancing the durable checkpoint
|
|
128
|
+
* to the max `updateDate` seen.
|
|
129
|
+
*
|
|
130
|
+
* @param getApi - Accessor for the Congress API service (injectable for tests).
|
|
131
|
+
* @param congresses - Congress numbers to mirror.
|
|
132
|
+
*/
|
|
133
|
+
export function createCongressBillIngester(getApi, congresses) {
|
|
134
|
+
return async function* sync({ mode, checkpoint, signal }) {
|
|
135
|
+
const api = getApi();
|
|
136
|
+
const fromDateTime = mode === 'refresh' && checkpoint ? toFromDateTime(checkpoint) : undefined;
|
|
137
|
+
let maxUpdate = checkpoint;
|
|
138
|
+
logger.info(`Congress bill mirror sync starting (mode=${mode}, congresses=${congresses.join(',')}${fromDateTime ? `, since=${fromDateTime}` : ''})`);
|
|
139
|
+
for (const congress of congresses) {
|
|
140
|
+
if (signal.aborted)
|
|
141
|
+
return;
|
|
142
|
+
const summaries = await buildSummaryMap(api, congress, signal);
|
|
143
|
+
logger.info(`Congress ${congress}: ${summaries.size} summaries loaded`);
|
|
144
|
+
let offset = 0;
|
|
145
|
+
let billsMirrored = 0;
|
|
146
|
+
while (!signal.aborted) {
|
|
147
|
+
const result = await api.listBills({
|
|
148
|
+
congress,
|
|
149
|
+
sort: 'updateDate asc',
|
|
150
|
+
limit: PAGE_SIZE,
|
|
151
|
+
offset,
|
|
152
|
+
...(fromDateTime ? { fromDateTime } : {}),
|
|
153
|
+
});
|
|
154
|
+
const rows = result.data;
|
|
155
|
+
if (rows.length === 0)
|
|
156
|
+
break;
|
|
157
|
+
const records = [];
|
|
158
|
+
for (const raw of rows) {
|
|
159
|
+
if (!isRecord(raw))
|
|
160
|
+
continue;
|
|
161
|
+
const record = toBillRow(raw, congress, summaries);
|
|
162
|
+
if (!record)
|
|
163
|
+
continue;
|
|
164
|
+
records.push(record);
|
|
165
|
+
const updated = typeof record.updateDate === 'string' ? record.updateDate : undefined;
|
|
166
|
+
if (updated && (maxUpdate === undefined || updated > maxUpdate))
|
|
167
|
+
maxUpdate = updated;
|
|
168
|
+
}
|
|
169
|
+
billsMirrored += records.length;
|
|
170
|
+
yield { records, ...(maxUpdate ? { checkpoint: maxUpdate } : {}) };
|
|
171
|
+
if (rows.length < PAGE_SIZE || result.pagination.nextOffset == null)
|
|
172
|
+
break;
|
|
173
|
+
offset += PAGE_SIZE;
|
|
174
|
+
}
|
|
175
|
+
logger.info(`Congress ${congress}: ${billsMirrored} bills mirrored`);
|
|
176
|
+
}
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
//# sourceMappingURL=ingest.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ingest.js","sourceRoot":"","sources":["../../../src/services/congress-mirror/ingest.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Pure normalization helpers for the bill mirror — HTML→plain-text
|
|
3
|
+
* stripping (summaries ship as HTML; the FTS index and snippet surface want plain
|
|
4
|
+
* text) and FTS5 `MATCH` escaping (tokenize + individually quote so a stray `-`,
|
|
5
|
+
* an unbalanced quote, or an FTS5 reserved word can't break the query). No I/O.
|
|
6
|
+
* @module services/congress-mirror/normalize
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Strip HTML tags to plain text and decode the handful of entities Congress.gov
|
|
10
|
+
* emits in summary bodies. Block-ish tags collapse to spaces so adjacent words
|
|
11
|
+
* don't fuse (`</p><p>` → space), and whitespace is collapsed to single spaces —
|
|
12
|
+
* a discovery/snippet surface doesn't need paragraph structure, and flat text
|
|
13
|
+
* tokenizes more cleanly for FTS5.
|
|
14
|
+
*/
|
|
15
|
+
export declare function htmlToPlainText(html: string): string;
|
|
16
|
+
/**
|
|
17
|
+
* Build an FTS5 `MATCH` expression from free-text input. Each token is stripped
|
|
18
|
+
* of embedded quotes and wrapped in double quotes, which neutralizes every FTS5
|
|
19
|
+
* operator (`-`, `*`, `:`, `NEAR`, `AND`, `OR`, `NOT`) so punctuation or a
|
|
20
|
+
* reserved word can't raise a SQLite syntax error. Tokens are AND-combined
|
|
21
|
+
* (implicit-AND across terms). Returns an empty string when the input has no
|
|
22
|
+
* searchable tokens — callers treat that as "no match", not a thrown query.
|
|
23
|
+
*/
|
|
24
|
+
export declare function toFtsMatch(input: string): string;
|
|
25
|
+
//# sourceMappingURL=normalize.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"normalize.d.ts","sourceRoot":"","sources":["../../../src/services/congress-mirror/normalize.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAcpD;AAED;;;;;;;GAOG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAQhD"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Pure normalization helpers for the bill mirror — HTML→plain-text
|
|
3
|
+
* stripping (summaries ship as HTML; the FTS index and snippet surface want plain
|
|
4
|
+
* text) and FTS5 `MATCH` escaping (tokenize + individually quote so a stray `-`,
|
|
5
|
+
* an unbalanced quote, or an FTS5 reserved word can't break the query). No I/O.
|
|
6
|
+
* @module services/congress-mirror/normalize
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Strip HTML tags to plain text and decode the handful of entities Congress.gov
|
|
10
|
+
* emits in summary bodies. Block-ish tags collapse to spaces so adjacent words
|
|
11
|
+
* don't fuse (`</p><p>` → space), and whitespace is collapsed to single spaces —
|
|
12
|
+
* a discovery/snippet surface doesn't need paragraph structure, and flat text
|
|
13
|
+
* tokenizes more cleanly for FTS5.
|
|
14
|
+
*/
|
|
15
|
+
export function htmlToPlainText(html) {
|
|
16
|
+
return html
|
|
17
|
+
.replace(/<\s*br\s*\/?\s*>/gi, ' ')
|
|
18
|
+
.replace(/<\s*\/?(p|div|li|ul|ol|h[1-6]|tr|table)\b[^>]*>/gi, ' ')
|
|
19
|
+
.replace(/<[^>]*>/g, '')
|
|
20
|
+
.replace(/ /g, ' ')
|
|
21
|
+
.replace(/'/g, "'")
|
|
22
|
+
.replace(/'/g, "'")
|
|
23
|
+
.replace(/&/g, '&')
|
|
24
|
+
.replace(/</g, '<')
|
|
25
|
+
.replace(/>/g, '>')
|
|
26
|
+
.replace(/"/g, '"')
|
|
27
|
+
.replace(/\s+/g, ' ')
|
|
28
|
+
.trim();
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Build an FTS5 `MATCH` expression from free-text input. Each token is stripped
|
|
32
|
+
* of embedded quotes and wrapped in double quotes, which neutralizes every FTS5
|
|
33
|
+
* operator (`-`, `*`, `:`, `NEAR`, `AND`, `OR`, `NOT`) so punctuation or a
|
|
34
|
+
* reserved word can't raise a SQLite syntax error. Tokens are AND-combined
|
|
35
|
+
* (implicit-AND across terms). Returns an empty string when the input has no
|
|
36
|
+
* searchable tokens — callers treat that as "no match", not a thrown query.
|
|
37
|
+
*/
|
|
38
|
+
export function toFtsMatch(input) {
|
|
39
|
+
return input
|
|
40
|
+
.trim()
|
|
41
|
+
.split(/\s+/)
|
|
42
|
+
.map((t) => t.replace(/"/g, '').trim())
|
|
43
|
+
.filter((t) => t.length > 0)
|
|
44
|
+
.map((t) => `"${t}"`)
|
|
45
|
+
.join(' AND ');
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=normalize.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"normalize.js","sourceRoot":"","sources":["../../../src/services/congress-mirror/normalize.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;;;;;GAMG;AACH,MAAM,UAAU,eAAe,CAAC,IAAY;IAC1C,OAAO,IAAI;SACR,OAAO,CAAC,oBAAoB,EAAE,GAAG,CAAC;SAClC,OAAO,CAAC,mDAAmD,EAAE,GAAG,CAAC;SACjE,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC;SACvB,OAAO,CAAC,SAAS,EAAE,GAAG,CAAC;SACvB,OAAO,CAAC,SAAS,EAAE,GAAG,CAAC;SACvB,OAAO,CAAC,QAAQ,EAAE,GAAG,CAAC;SACtB,OAAO,CAAC,QAAQ,EAAE,GAAG,CAAC;SACtB,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC;SACrB,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC;SACrB,OAAO,CAAC,SAAS,EAAE,GAAG,CAAC;SACvB,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC;SACpB,IAAI,EAAE,CAAC;AACZ,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,UAAU,CAAC,KAAa;IACtC,OAAO,KAAK;SACT,IAAI,EAAE;SACN,KAAK,CAAC,KAAK,CAAC;SACZ,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;SACtC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC;SAC3B,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC;SACpB,IAAI,CAAC,OAAO,CAAC,CAAC;AACnB,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Mirror store schema for the Congress bill FTS index. A single
|
|
3
|
+
* MirrorService-owned table (`bills`) with an FTS5 index over `title` + `summary`
|
|
4
|
+
* and secondary indexes on the filterable columns. The primary key is the derived
|
|
5
|
+
* `billId` (`{congress}/{billType}/{billNumber}`) — a single column, matching the
|
|
6
|
+
* `congress://bill/{congress}/{billType}/{billNumber}` resource addressing.
|
|
7
|
+
* @module services/congress-mirror/schema
|
|
8
|
+
*/
|
|
9
|
+
import type { SqliteMirrorStoreSpec } from '@cyanheads/mcp-ts-core/mirror';
|
|
10
|
+
/** Primary table name (the MirrorService-owned table). */
|
|
11
|
+
export declare const BILLS_TABLE = "bills";
|
|
12
|
+
/**
|
|
13
|
+
* The MirrorService primary-table spec. One row per bill, merged from the bill
|
|
14
|
+
* list (title, chamber, latest action, update date) and the CRS summaries list
|
|
15
|
+
* (summary text). `title` + `summary` are FTS-indexed; `congress`, `billType`,
|
|
16
|
+
* and `originChamber` back the exact-match search filters.
|
|
17
|
+
*/
|
|
18
|
+
export declare function billStoreSpec(path: string): SqliteMirrorStoreSpec;
|
|
19
|
+
//# sourceMappingURL=schema.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../../src/services/congress-mirror/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC;AAE3E,0DAA0D;AAC1D,eAAO,MAAM,WAAW,UAAU,CAAC;AAEnC;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,qBAAqB,CA0BjE"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Mirror store schema for the Congress bill FTS index. A single
|
|
3
|
+
* MirrorService-owned table (`bills`) with an FTS5 index over `title` + `summary`
|
|
4
|
+
* and secondary indexes on the filterable columns. The primary key is the derived
|
|
5
|
+
* `billId` (`{congress}/{billType}/{billNumber}`) — a single column, matching the
|
|
6
|
+
* `congress://bill/{congress}/{billType}/{billNumber}` resource addressing.
|
|
7
|
+
* @module services/congress-mirror/schema
|
|
8
|
+
*/
|
|
9
|
+
/** Primary table name (the MirrorService-owned table). */
|
|
10
|
+
export const BILLS_TABLE = 'bills';
|
|
11
|
+
/**
|
|
12
|
+
* The MirrorService primary-table spec. One row per bill, merged from the bill
|
|
13
|
+
* list (title, chamber, latest action, update date) and the CRS summaries list
|
|
14
|
+
* (summary text). `title` + `summary` are FTS-indexed; `congress`, `billType`,
|
|
15
|
+
* and `originChamber` back the exact-match search filters.
|
|
16
|
+
*/
|
|
17
|
+
export function billStoreSpec(path) {
|
|
18
|
+
return {
|
|
19
|
+
path,
|
|
20
|
+
table: BILLS_TABLE,
|
|
21
|
+
primaryKey: 'billId',
|
|
22
|
+
version: 1,
|
|
23
|
+
columns: {
|
|
24
|
+
billId: 'TEXT',
|
|
25
|
+
congress: 'INTEGER',
|
|
26
|
+
billType: 'TEXT',
|
|
27
|
+
billNumber: 'INTEGER',
|
|
28
|
+
title: 'TEXT',
|
|
29
|
+
summary: 'TEXT',
|
|
30
|
+
originChamber: 'TEXT',
|
|
31
|
+
latestActionDate: 'TEXT',
|
|
32
|
+
latestActionText: 'TEXT',
|
|
33
|
+
updateDate: 'TEXT',
|
|
34
|
+
},
|
|
35
|
+
fts: ['title', 'summary'],
|
|
36
|
+
indexes: [
|
|
37
|
+
{ columns: ['congress'] },
|
|
38
|
+
{ columns: ['billType'] },
|
|
39
|
+
{ columns: ['originChamber'] },
|
|
40
|
+
{ columns: ['updateDate'] },
|
|
41
|
+
],
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../../src/services/congress-mirror/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,0DAA0D;AAC1D,MAAM,CAAC,MAAM,WAAW,GAAG,OAAO,CAAC;AAEnC;;;;;GAKG;AACH,MAAM,UAAU,aAAa,CAAC,IAAY;IACxC,OAAO;QACL,IAAI;QACJ,KAAK,EAAE,WAAW;QAClB,UAAU,EAAE,QAAQ;QACpB,OAAO,EAAE,CAAC;QACV,OAAO,EAAE;YACP,MAAM,EAAE,MAAM;YACd,QAAQ,EAAE,SAAS;YACnB,QAAQ,EAAE,MAAM;YAChB,UAAU,EAAE,SAAS;YACrB,KAAK,EAAE,MAAM;YACb,OAAO,EAAE,MAAM;YACf,aAAa,EAAE,MAAM;YACrB,gBAAgB,EAAE,MAAM;YACxB,gBAAgB,EAAE,MAAM;YACxB,UAAU,EAAE,MAAM;SACnB;QACD,GAAG,EAAE,CAAC,OAAO,EAAE,SAAS,CAAC;QACzB,OAAO,EAAE;YACP,EAAE,OAAO,EAAE,CAAC,UAAU,CAAC,EAAE;YACzB,EAAE,OAAO,EAAE,CAAC,UAAU,CAAC,EAAE;YACzB,EAAE,OAAO,EAAE,CAAC,eAAe,CAAC,EAAE;YAC9B,EAAE,OAAO,EAAE,CAAC,YAAY,CAAC,EAAE;SAC5B;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Domain types for the Congress bill FTS mirror — the merged row
|
|
3
|
+
* shape, the search filters, and the search page envelope. The mirror row is the
|
|
4
|
+
* single-table projection of two upstream Congress.gov sources (the bill list and
|
|
5
|
+
* the CRS summaries list) keyed by a derived `billId`.
|
|
6
|
+
* @module services/congress-mirror/types
|
|
7
|
+
*/
|
|
8
|
+
import type { BillType } from '../../services/congress-api/types.js';
|
|
9
|
+
/**
|
|
10
|
+
* One mirrored bill — a flat row keyed by the derived `billId`
|
|
11
|
+
* (`{congress}/{billType}/{billNumber}`). `title` comes from the bill list;
|
|
12
|
+
* `summary` from the CRS summaries list (HTML-stripped, latest version), nullable
|
|
13
|
+
* because CRS summarizes only a fraction of bills.
|
|
14
|
+
*/
|
|
15
|
+
export interface BillMirrorRow {
|
|
16
|
+
billId: string;
|
|
17
|
+
billNumber: number;
|
|
18
|
+
billType: string;
|
|
19
|
+
congress: number;
|
|
20
|
+
latestActionDate: string | null;
|
|
21
|
+
latestActionText: string | null;
|
|
22
|
+
originChamber: string | null;
|
|
23
|
+
summary: string | null;
|
|
24
|
+
title: string | null;
|
|
25
|
+
updateDate: string | null;
|
|
26
|
+
}
|
|
27
|
+
/** Filters accepted by the keyword search. */
|
|
28
|
+
export interface SearchBillsFilters {
|
|
29
|
+
billType?: BillType | undefined;
|
|
30
|
+
congress?: number | undefined;
|
|
31
|
+
limit: number;
|
|
32
|
+
offset: number;
|
|
33
|
+
originChamber?: string | undefined;
|
|
34
|
+
query: string;
|
|
35
|
+
}
|
|
36
|
+
/** A single search result — a merged mirror row projected for display. */
|
|
37
|
+
export interface SearchBillResult {
|
|
38
|
+
billId: string;
|
|
39
|
+
billNumber: number;
|
|
40
|
+
billType: string;
|
|
41
|
+
congress: number;
|
|
42
|
+
latestActionDate?: string;
|
|
43
|
+
latestActionText?: string;
|
|
44
|
+
originChamber?: string;
|
|
45
|
+
summaryPreview?: string;
|
|
46
|
+
title: string;
|
|
47
|
+
}
|
|
48
|
+
/** Search page returned by the mirror service. */
|
|
49
|
+
export interface SearchBillsPage {
|
|
50
|
+
items: SearchBillResult[];
|
|
51
|
+
limit: number;
|
|
52
|
+
offset: number;
|
|
53
|
+
total: number;
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/services/congress-mirror/types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kCAAkC,CAAC;AAEjE;;;;;GAKG;AACH,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED,8CAA8C;AAC9C,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,EAAE,QAAQ,GAAG,SAAS,CAAC;IAChC,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,KAAK,EAAE,MAAM,CAAC;CACf;AAED,0EAA0E;AAC1E,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,kDAAkD;AAClD,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,gBAAgB,EAAE,CAAC;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;CACf"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Domain types for the Congress bill FTS mirror — the merged row
|
|
3
|
+
* shape, the search filters, and the search page envelope. The mirror row is the
|
|
4
|
+
* single-table projection of two upstream Congress.gov sources (the bill list and
|
|
5
|
+
* the CRS summaries list) keyed by a derived `billId`.
|
|
6
|
+
* @module services/congress-mirror/types
|
|
7
|
+
*/
|
|
8
|
+
export {};
|
|
9
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/services/congress-mirror/types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG"}
|