@tricoteuses/senat 2.9.10 → 2.10.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,359 @@
1
+ import { DateTime } from "luxon";
2
+ import { norm } from "../model/util";
3
+ const PARIS = "Europe/Paris";
4
+ const STOPWORDS = new Set([
5
+ "de", "du", "des",
6
+ "la", "le", "les", "l",
7
+ "d",
8
+ "et",
9
+ "en",
10
+ "au", "aux",
11
+ "pour",
12
+ "sur", "sous", "à", "a", "aux",
13
+ ]);
14
+ export function groupNonSPByTypeOrganeHour(events) {
15
+ const out = { IDC: [], IDM: [], IDO: [], IDI: [] };
16
+ if (!events?.length)
17
+ return out;
18
+ const nonSP = events.filter(e => !isSeancePublique(e?.type));
19
+ if (nonSP.length === 0)
20
+ return out;
21
+ const buckets = new Map();
22
+ for (const e of nonSP) {
23
+ const kind = classifyAgendaType(e?.type);
24
+ if (!kind || kind === "SP")
25
+ continue;
26
+ const { startISO, endISO } = deriveTimesForEvent(e);
27
+ const hourShort = hourShortFromISO(startISO) ?? hourShortFromOriginal(e.timeOriginal);
28
+ const key = [e.date, kind, hourShort || "NA"].join("|");
29
+ if (!buckets.has(key))
30
+ buckets.set(key, []);
31
+ buckets.get(key).push({ ...e, startTime: startISO ?? e.startTime, endTime: endISO ?? e.endTime });
32
+ }
33
+ for (const [key, list] of buckets) {
34
+ const [date, kindStr, hourShort] = key.split("|");
35
+ const kind = kindStr;
36
+ const enriched = list.map(ev => {
37
+ const { startISO, endISO } = deriveTimesForEvent(ev);
38
+ return { ev, startISO: startISO ?? ev.startTime, endISO: endISO ?? ev.endTime };
39
+ }).sort((a, b) => {
40
+ const ta = a.startISO ? parseISO(a.startISO)?.toMillis() ?? Number.MAX_SAFE_INTEGER : Number.MAX_SAFE_INTEGER;
41
+ const tb = b.startISO ? parseISO(b.startISO)?.toMillis() ?? Number.MAX_SAFE_INTEGER : Number.MAX_SAFE_INTEGER;
42
+ return ta - tb;
43
+ });
44
+ const startTime = enriched.find(x => !!x.startISO)?.startISO ?? null;
45
+ const endTime = enriched.reduce((acc, x) => {
46
+ const de = x.endISO ? parseISO(x.endISO)?.toMillis() : null;
47
+ const accMs = acc ? parseISO(acc)?.toMillis() : null;
48
+ if (de != null && (accMs == null || de > accMs))
49
+ return x.endISO;
50
+ return acc;
51
+ }, null);
52
+ const any = enriched[0]?.ev;
53
+ const hour = hourShort !== "NA" ? hourShort : (hourShortFromISO(startTime) ?? hourShortFromOriginal(any?.timeOriginal));
54
+ const uid = makeTypeGroupUid(date, kind, hour ?? null, any?.organe || undefined);
55
+ const suffix = (kind === "COM" ? "IDC" : kind === "MC" ? "IDM" : kind === 'OD' ? 'IDO' : "IDI");
56
+ const group = {
57
+ uid,
58
+ chambre: "SN",
59
+ date,
60
+ type: any?.type || "",
61
+ organe: any?.organe || undefined,
62
+ startTime,
63
+ endTime,
64
+ captationVideo: enriched.some(x => x.ev.captationVideo === true),
65
+ titre: compactTitleList(enriched.map(x => x.ev.titre || "").filter(Boolean), 8),
66
+ objet: joinObjets(enriched.map(x => x.ev)),
67
+ reunions: enriched.map(x => x.ev),
68
+ };
69
+ out[suffix].push(group);
70
+ }
71
+ for (const k of Object.keys(out)) {
72
+ out[k].sort((a, b) => {
73
+ const da = DateTime.fromISO(`${a.date}T${a.startTime || "00:00:00.000+02:00"}`, { zone: PARIS }).toMillis();
74
+ const db = DateTime.fromISO(`${b.date}T${b.startTime || "00:00:00.000+02:00"}`, { zone: PARIS }).toMillis();
75
+ return da - db || (a.organe || "").localeCompare(b.organe || "");
76
+ });
77
+ }
78
+ return out;
79
+ }
80
+ export function groupSeancePubliqueBySlot(events) {
81
+ if (!events?.length)
82
+ return [];
83
+ const sp = events.filter(e => isSeancePublique(e?.type));
84
+ if (sp.length === 0)
85
+ return [];
86
+ const byDate = new Map();
87
+ for (const e of sp) {
88
+ const d = norm(e.date);
89
+ if (!d)
90
+ continue;
91
+ if (!byDate.has(d))
92
+ byDate.set(d, []);
93
+ byDate.get(d).push(e);
94
+ }
95
+ const out = [];
96
+ for (const [date, dayEvents] of byDate) {
97
+ const enriched = dayEvents.map((e) => {
98
+ const { startISO, endISO, slot } = deriveTimesForEvent(e);
99
+ return { ev: e, startISO, endISO, slot };
100
+ });
101
+ enriched.sort((a, b) => {
102
+ const da = a.startISO ? parseISO(a.startISO)?.toMillis() ?? Number.MAX_SAFE_INTEGER : Number.MAX_SAFE_INTEGER;
103
+ const db = b.startISO ? parseISO(b.startISO)?.toMillis() ?? Number.MAX_SAFE_INTEGER : Number.MAX_SAFE_INTEGER;
104
+ return da - db;
105
+ });
106
+ const bySlot = new Map();
107
+ for (const it of enriched) {
108
+ let s = it.slot;
109
+ if (s === "UNKNOWN" && it.startISO) {
110
+ const dt = parseISO(it.startISO);
111
+ if (dt)
112
+ s = slotOf(dt);
113
+ }
114
+ if (!bySlot.has(s))
115
+ bySlot.set(s, []);
116
+ bySlot.get(s).push(it);
117
+ }
118
+ for (const [slot, list] of bySlot) {
119
+ const sorted = list.slice().sort((a, b) => {
120
+ const da = a.startISO ? parseISO(a.startISO)?.toMillis() ?? Number.MAX_SAFE_INTEGER : Number.MAX_SAFE_INTEGER;
121
+ const db = b.startISO ? parseISO(b.startISO)?.toMillis() ?? Number.MAX_SAFE_INTEGER : Number.MAX_SAFE_INTEGER;
122
+ return da - db;
123
+ });
124
+ const startTime = sorted.find((x) => !!x.startISO)?.startISO ?? null;
125
+ const endTime = sorted.reduce((acc, x) => {
126
+ const de = x.endISO ? parseISO(x.endISO)?.toMillis() : null;
127
+ const accMs = acc ? parseISO(acc)?.toMillis() : null;
128
+ if (de != null && (accMs == null || de > accMs))
129
+ return x.endISO;
130
+ return acc;
131
+ }, null);
132
+ const titres = sorted.map((x) => x.ev.titre || "").filter(Boolean);
133
+ const captationVideo = sorted.some((x) => x.ev.captationVideo === true);
134
+ out.push({
135
+ uid: makeGroupUid(date, slot),
136
+ chambre: "SN",
137
+ date,
138
+ slot,
139
+ type: "Séance publique",
140
+ startTime,
141
+ endTime,
142
+ captationVideo,
143
+ titre: compactTitleList(titres, 5),
144
+ objet: joinObjets(sorted.map((x) => x.ev)),
145
+ reunions: sorted.map((x) => x.ev),
146
+ });
147
+ }
148
+ }
149
+ out.sort((a, b) => {
150
+ const da = DateTime.fromISO(`${a.date}T${a.startTime || "00:00:00.000+02:00"}`, { zone: PARIS }).toMillis();
151
+ const db = DateTime.fromISO(`${b.date}T${b.startTime || "00:00:00.000+02:00"}`, { zone: PARIS }).toMillis();
152
+ return da - db || a.slot.localeCompare(b.slot);
153
+ });
154
+ return out;
155
+ }
156
+ function normalizeNoAccents(s) {
157
+ return (s || "")
158
+ .trim()
159
+ .normalize("NFKD")
160
+ .replace(/[\u0300-\u036f]/g, "");
161
+ }
162
+ function isSeancePublique(typeLabel) {
163
+ const s = normalizeNoAccents(typeLabel || "").toLowerCase();
164
+ return /\bseance\b.*\bpublique\b/.test(s);
165
+ }
166
+ function classifyAgendaType(typeLabel) {
167
+ const s = normalizeNoAccents(typeLabel || "").toLowerCase();
168
+ if (/\bseance\b.*\bpublique\b/.test(s))
169
+ return "SP";
170
+ if (/\bcommissions\b/.test(s))
171
+ return "COM";
172
+ if (/\bmission\b.*\bcontrole\b/.test(s))
173
+ return "MC";
174
+ if (/\boffices\b|\bdelegations\b/.test(s))
175
+ return "OD";
176
+ if (/\instances\b|\decisionelles\b/.test(s))
177
+ return "ID";
178
+ return null;
179
+ }
180
+ function typeToSuffixStrict(kind) {
181
+ switch (kind) {
182
+ case "SP": return "IDS";
183
+ case "COM": return "IDC";
184
+ case "MC": return "IDM";
185
+ case "OD": return "IDO";
186
+ case "ID": return "IDI";
187
+ }
188
+ }
189
+ function hourShortFromISO(iso) {
190
+ if (!iso)
191
+ return null;
192
+ const dt = parseISO(iso);
193
+ if (!dt)
194
+ return null;
195
+ const z = DateTime.fromISO(iso, { zone: PARIS });
196
+ const H = String(z.hour);
197
+ const mm = String(z.minute).padStart(2, "0");
198
+ return `${H}${mm}`;
199
+ }
200
+ function hourShortFromOriginal(s) {
201
+ if (!s)
202
+ return null;
203
+ const clean = normalizeNoAccents(s).toLowerCase();
204
+ const m = clean.match(/(\d{1,2})\s*[h:]\s*(\d{2})/);
205
+ if (m) {
206
+ const H = String(parseInt(m[1], 10));
207
+ const mm = m[2].padStart(2, "0");
208
+ return `${H}${mm}`;
209
+ }
210
+ const m2 = clean.match(/(\d{1,2})\s*h\b/);
211
+ if (m2)
212
+ return `${parseInt(m2[1], 10)}00`;
213
+ return null;
214
+ }
215
+ function organeInitials(input, maxLen = 8) {
216
+ if (!input)
217
+ return "";
218
+ const clean = normalizeNoAccents(input)
219
+ .replace(/['’]/g, " ")
220
+ .replace(/[^A-Za-z0-9\s]/g, " ")
221
+ .replace(/\s+/g, " ")
222
+ .trim();
223
+ if (!clean)
224
+ return "";
225
+ const parts = clean.split(" ");
226
+ const letters = [];
227
+ for (const raw of parts) {
228
+ const w = raw.toLowerCase();
229
+ if (!w)
230
+ continue;
231
+ if (STOPWORDS.has(w))
232
+ continue;
233
+ // if all uppercase, keep it
234
+ if (/^[A-Z0-9]{2,}$/.test(raw)) {
235
+ letters.push(raw.toUpperCase());
236
+ continue;
237
+ }
238
+ // otherwise, take first letter if alphanumeric
239
+ const ch = raw[0];
240
+ if (/[A-Za-z0-9]/.test(ch))
241
+ letters.push(ch.toUpperCase());
242
+ }
243
+ const out = letters.join("");
244
+ return out.slice(0, maxLen);
245
+ }
246
+ function makeTypeGroupUid(dateISO, kind, hourShort, organe) {
247
+ const ymd = dateISO ? formatYYYYMMDD(dateISO) : "00000000";
248
+ const suffix = typeToSuffixStrict(kind);
249
+ const hh = hourShort ?? "NA";
250
+ const org = organe ? organeInitials(organe) : "";
251
+ return `RUSN${ymd}${suffix}${org ? org : ""}-${hh}`;
252
+ }
253
+ function parseISO(isoLike) {
254
+ if (!isoLike)
255
+ return null;
256
+ const dt = DateTime.fromISO(isoLike, { zone: PARIS });
257
+ return dt.isValid ? dt : null;
258
+ }
259
+ function slotOf(dt) {
260
+ if (!dt)
261
+ return "UNKNOWN";
262
+ const h = dt.hour + dt.minute / 60;
263
+ if (h < 12.5)
264
+ return "MATIN";
265
+ if (h < 19.0)
266
+ return "APRES-MIDI";
267
+ return "SOIR";
268
+ }
269
+ function trimWords(s, max = 40) {
270
+ const words = norm(s).split(/\s+/).filter(Boolean);
271
+ return words.length <= max ? words.join(" ") : words.slice(0, max).join(" ");
272
+ }
273
+ function compactTitleList(titres, maxTitles = 5) {
274
+ const uniq = Array.from(new Set(titres.map(t => norm(t)).filter(Boolean)));
275
+ return uniq.slice(0, maxTitles).join(" · ") || "(sans titre)";
276
+ }
277
+ export function makeGroupUid(date, slot) {
278
+ const ymd = date ? formatYYYYMMDD(date) : "00000000";
279
+ return `RUSN${ymd}IDS-${slot}`;
280
+ }
281
+ export function formatYYYYMMDD(dateYYYYMMDD) {
282
+ const [y, m, d] = dateYYYYMMDD.split("-");
283
+ return `${y}${m}${d}`;
284
+ }
285
+ export function makeReunionUid(agenda) {
286
+ const ymd = agenda.date ? formatYYYYMMDD(agenda.date) : "00000000";
287
+ return `${ymd}-${agenda.id}`;
288
+ }
289
+ function joinObjets(events) {
290
+ const objets = events
291
+ .map(e => (e.objet || "").trim())
292
+ .filter(Boolean)
293
+ .map(s => trimWords(s, 40));
294
+ if (objets.length === 0)
295
+ return "";
296
+ return objets.join(" · ");
297
+ }
298
+ // Extract hours/minutes from French text like "à 10 h 30", "de 10 h à 12 h", etc.
299
+ function parseTimeOriginalFR(timeOriginal) {
300
+ if (!timeOriginal)
301
+ return { start: null, end: null };
302
+ const txt = (timeOriginal || "")
303
+ .replace(/\u00A0/g, " ") // nbsp → space
304
+ .replace(/\s+/g, " ") // espaces multiples
305
+ .toLowerCase()
306
+ .trim();
307
+ // 1) "de 10 h 30 à 12 heures", "de 10h30 à 12h", "de 9 h à 11 h 15", etc.
308
+ const reRange = /\bde\s+(\d{1,2})\s*(?:h|:)?\s*(\d{1,2})?\s*(?:heures?)?\s*à\s*(\d{1,2})\s*(?:h|:)?\s*(\d{1,2})?\s*(?:heures?)?/i;
309
+ const mRange = txt.match(reRange);
310
+ if (mRange) {
311
+ const h1 = clampHour(+mRange[1]), m1 = clampMinute(mRange[2] ? +mRange[2] : 0);
312
+ const h2 = clampHour(+mRange[3]), m2 = clampMinute(mRange[4] ? +mRange[4] : 0);
313
+ return { start: toIsoTime(h1, m1), end: toIsoTime(h2, m2) };
314
+ }
315
+ // 2) "à 10 h 30", "à 10h", "A 10h30", "A 9 heures", etc.
316
+ const reAt = /\b(?:a|à)\s*(\d{1,2})\s*(?:h|:)?\s*(\d{1,2})?\s*(?:heures?)?/i;
317
+ const mAt = txt.match(reAt);
318
+ if (mAt) {
319
+ const h = clampHour(+mAt[1]), m = clampMinute(mAt[2] ? +mAt[2] : 0);
320
+ return { start: toIsoTime(h, m), end: null };
321
+ }
322
+ // 3) "10 h 30", "15h", "9 heures" sans 'à' / 'de ... à ...'
323
+ const reBare = /\b(\d{1,2})\s*(?:h|:)?\s*(\d{1,2})?\s*(?:heures?)?\b/;
324
+ const mBare = txt.match(reBare);
325
+ if (mBare) {
326
+ const h = clampHour(+mBare[1]), m = clampMinute(mBare[2] ? +mBare[2] : 0);
327
+ return { start: toIsoTime(h, m), end: null };
328
+ }
329
+ return { start: null, end: null };
330
+ }
331
+ function clampHour(h) { return Math.max(0, Math.min(23, h)); }
332
+ function clampMinute(m) { return Math.max(0, Math.min(59, m)); }
333
+ function toIsoTime(h, m) {
334
+ return `${String(h).padStart(2, "0")}:${String(m).padStart(2, "0")}:00.000+02:00`;
335
+ }
336
+ function slotFromTimesOrText(startISO, timeOriginal) {
337
+ if (startISO) {
338
+ const dt = parseISO(startISO);
339
+ if (dt)
340
+ return slotOf(dt);
341
+ }
342
+ const t = (timeOriginal || "").toLowerCase();
343
+ if (/\b(apr(?:è|e)s[-\s]?midi)\b/.test(t))
344
+ return "APRES-MIDI";
345
+ if (/\b(soir(?:ée)?)\b/.test(t))
346
+ return "SOIR";
347
+ if (/\b(matin(?:ée)?)\b/.test(t))
348
+ return "MATIN";
349
+ return "UNKNOWN";
350
+ }
351
+ function deriveTimesForEvent(ev) {
352
+ const directStart = ev.startTime ?? null;
353
+ const directEnd = ev.endTime ?? null;
354
+ const fromText = parseTimeOriginalFR(ev.timeOriginal);
355
+ const startISO = directStart ?? fromText.start ?? null;
356
+ const endISO = directEnd ?? fromText.end ?? null;
357
+ const slot = slotFromTimesOrText(startISO, ev.timeOriginal);
358
+ return { startISO, endISO, slot };
359
+ }
File without changes
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ // import { validateNonEmptyTrimmedString } from "@biryani/core"
3
+ // const acteurUidRegExp = /^PA\d+$/
4
+ // const organeUidRegExp = /^PO\d+$/
5
+ // export function validateSenateurUid(input: any): [any, any] {
6
+ // const [value, error] = validateNonEmptyTrimmedString(input)
7
+ // if (error !== null) {
8
+ // return [value, error]
9
+ // }
10
+ // if (!acteurUidRegExp.test(value)) {
11
+ // return [value, 'Invalid "acteur" unique ID']
12
+ // }
13
+ // return [value, null]
14
+ // }
15
+ // export function validateOrganeUid(input: any): [any, any] {
16
+ // const [value, error] = validateNonEmptyTrimmedString(input)
17
+ // if (error !== null) {
18
+ // return [value, error]
19
+ // }
20
+ // if (!organeUidRegExp.test(value)) {
21
+ // return [value, 'Invalid "organe" unique ID']
22
+ // }
23
+ // return [value, null]
24
+ // }
package/package.json CHANGED
@@ -1,98 +1,98 @@
1
- {
2
- "name": "@tricoteuses/senat",
3
- "version": "2.9.10",
4
- "description": "Handle French Sénat's open data",
5
- "keywords": [
6
- "France",
7
- "open data",
8
- "Parliament",
9
- "Sénat"
10
- ],
11
- "author": "Emmanuel Raviart <emmanuel@raviart.com>",
12
- "bugs": {
13
- "url": "https://git.tricoteuses.fr/logiciels/tricoteuses-senat/issues"
14
- },
15
- "homepage": "https://tricoteuses.fr/",
16
- "license": "AGPL-3.0-or-later",
17
- "repository": {
18
- "type": "git",
19
- "url": "https://git.tricoteuses.fr/logiciels/tricoteuses-senat.git"
20
- },
21
- "type": "module",
22
- "engines": {
23
- "node": ">=22"
24
- },
25
- "files": [
26
- "lib"
27
- ],
28
- "exports": {
29
- ".": {
30
- "import": "./lib/index.js",
31
- "types": "./lib/index.d.ts"
32
- },
33
- "./loaders": {
34
- "import": "./lib/loaders.js",
35
- "types": "./lib/loaders.d.ts"
36
- },
37
- "./package.json": "./package.json"
38
- },
39
- "publishConfig": {
40
- "access": "public"
41
- },
42
- "scripts": {
43
- "build": "tsc",
44
- "build:types": "tsc --emitDeclarationOnly",
45
- "data:convert_data": "tsx src/scripts/convert_data.ts",
46
- "data:download": "tsx src/scripts/data-download.ts",
47
- "data:generate_schemas": "tsx src/scripts/retrieve_open_data.ts --schema",
48
- "data:retrieve_agenda": "cross-env TZ='Etc/UTC' tsx src/scripts/retrieve_agenda.ts",
49
- "data:retrieve_comptes_rendus": "tsx src/scripts/retrieve_comptes_rendus.ts",
50
- "data:retrieve_documents": "tsx src/scripts/retrieve_documents.ts",
51
- "data:retrieve_open_data": "tsx src/scripts/retrieve_open_data.ts --all",
52
- "data:retrieve_senateurs_photos": "tsx src/scripts/retrieve_senateurs_photos.ts --fetch",
53
- "data:retrieve_videos": "tsx src/scripts/retrieve_videos.ts",
54
- "data:parse_textes_lois": "tsx src/scripts/parse_textes.ts",
55
- "prepare": "npm run build",
56
- "prepublishOnly": "npm run build",
57
- "prettier": "prettier --write 'src/**/*.ts' 'tests/**/*.test.ts'",
58
- "type-check": "tsc --noEmit",
59
- "type-check:watch": "npm run type-check -- --watch"
60
- },
61
- "dependencies": {
62
- "@biryani/core": "^0.2.1",
63
- "cheerio": "^1.1.2",
64
- "command-line-args": "^5.1.1",
65
- "dotenv": "^8.2.0",
66
- "fs-extra": "^9.1.0",
67
- "jsdom": "^26.0.0",
68
- "kysely": "^0.27.4",
69
- "luxon": "^3.5.0",
70
- "node-stream-zip": "^1.8.2",
71
- "pg": "^8.13.1",
72
- "pg-cursor": "^2.12.1",
73
- "slug": "^11.0.0",
74
- "tsx": "^4.19.4",
75
- "windows-1252": "^1.0.0"
76
- },
77
- "devDependencies": {
78
- "@typed-code/schemats": "^5.0.1",
79
- "@types/cheerio": "^1.0.0",
80
- "@types/command-line-args": "^5.0.0",
81
- "@types/fs-extra": "^9.0.7",
82
- "@types/jsdom": "^21.1.7",
83
- "@types/luxon": "^3.4.2",
84
- "@types/node": "^20.17.6",
85
- "@types/pg": "^8.11.10",
86
- "@types/pg-cursor": "^2.7.2",
87
- "@types/slug": "^5.0.9",
88
- "@typescript-eslint/eslint-plugin": "^8.13.0",
89
- "@typescript-eslint/parser": "^8.13.0",
90
- "cross-env": "^10.0.0",
91
- "eslint": "^8.57.1",
92
- "iconv-lite": "^0.7.0",
93
- "pg-to-ts": "^4.1.1",
94
- "prettier": "^3.5.3",
95
- "tslib": "^2.1.0",
96
- "typescript": "^5.8.3"
97
- }
98
- }
1
+ {
2
+ "name": "@tricoteuses/senat",
3
+ "version": "2.10.1",
4
+ "description": "Handle French Sénat's open data",
5
+ "keywords": [
6
+ "France",
7
+ "open data",
8
+ "Parliament",
9
+ "Sénat"
10
+ ],
11
+ "author": "Emmanuel Raviart <emmanuel@raviart.com>",
12
+ "bugs": {
13
+ "url": "https://git.tricoteuses.fr/logiciels/tricoteuses-senat/issues"
14
+ },
15
+ "homepage": "https://tricoteuses.fr/",
16
+ "license": "AGPL-3.0-or-later",
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "https://git.tricoteuses.fr/logiciels/tricoteuses-senat.git"
20
+ },
21
+ "type": "module",
22
+ "engines": {
23
+ "node": ">=22"
24
+ },
25
+ "files": [
26
+ "lib"
27
+ ],
28
+ "exports": {
29
+ ".": {
30
+ "import": "./lib/index.js",
31
+ "types": "./lib/index.d.ts"
32
+ },
33
+ "./loaders": {
34
+ "import": "./lib/loaders.js",
35
+ "types": "./lib/loaders.d.ts"
36
+ },
37
+ "./package.json": "./package.json"
38
+ },
39
+ "publishConfig": {
40
+ "access": "public"
41
+ },
42
+ "scripts": {
43
+ "build": "tsc",
44
+ "build:types": "tsc --emitDeclarationOnly",
45
+ "data:convert_data": "tsx src/scripts/convert_data.ts",
46
+ "data:download": "tsx src/scripts/data-download.ts",
47
+ "data:generate_schemas": "tsx src/scripts/retrieve_open_data.ts --schema",
48
+ "data:retrieve_agenda": "cross-env TZ='Etc/UTC' tsx src/scripts/retrieve_agenda.ts",
49
+ "data:retrieve_comptes_rendus": "tsx src/scripts/retrieve_comptes_rendus.ts",
50
+ "data:retrieve_documents": "tsx src/scripts/retrieve_documents.ts",
51
+ "data:retrieve_open_data": "tsx src/scripts/retrieve_open_data.ts --all",
52
+ "data:retrieve_senateurs_photos": "tsx src/scripts/retrieve_senateurs_photos.ts --fetch",
53
+ "data:retrieve_videos": "tsx src/scripts/retrieve_videos.ts",
54
+ "data:parse_textes_lois": "tsx src/scripts/parse_textes.ts",
55
+ "prepare": "npm run build",
56
+ "prepublishOnly": "npm run build",
57
+ "prettier": "prettier --write 'src/**/*.ts' 'tests/**/*.test.ts'",
58
+ "type-check": "tsc --noEmit",
59
+ "type-check:watch": "npm run type-check -- --watch"
60
+ },
61
+ "dependencies": {
62
+ "@biryani/core": "^0.2.1",
63
+ "cheerio": "^1.1.2",
64
+ "command-line-args": "^5.1.1",
65
+ "dotenv": "^8.2.0",
66
+ "fs-extra": "^9.1.0",
67
+ "jsdom": "^26.0.0",
68
+ "kysely": "^0.27.4",
69
+ "luxon": "^3.5.0",
70
+ "node-stream-zip": "^1.8.2",
71
+ "pg": "^8.13.1",
72
+ "pg-cursor": "^2.12.1",
73
+ "slug": "^11.0.0",
74
+ "tsx": "^4.19.4",
75
+ "windows-1252": "^1.0.0"
76
+ },
77
+ "devDependencies": {
78
+ "@typed-code/schemats": "^5.0.1",
79
+ "@types/cheerio": "^1.0.0",
80
+ "@types/command-line-args": "^5.0.0",
81
+ "@types/fs-extra": "^9.0.7",
82
+ "@types/jsdom": "^21.1.7",
83
+ "@types/luxon": "^3.4.2",
84
+ "@types/node": "^20.17.6",
85
+ "@types/pg": "^8.11.10",
86
+ "@types/pg-cursor": "^2.7.2",
87
+ "@types/slug": "^5.0.9",
88
+ "@typescript-eslint/eslint-plugin": "^8.13.0",
89
+ "@typescript-eslint/parser": "^8.13.0",
90
+ "cross-env": "^10.0.0",
91
+ "eslint": "^8.57.1",
92
+ "iconv-lite": "^0.7.0",
93
+ "pg-to-ts": "^4.1.1",
94
+ "prettier": "^3.5.3",
95
+ "tslib": "^2.1.0",
96
+ "typescript": "^5.8.3"
97
+ }
98
+ }
@@ -1,5 +0,0 @@
1
- export type KyselyTableTypes<T extends Record<string, {
2
- select: any;
3
- }>> = {
4
- [K in keyof T]: T[K]['select'];
5
- };
@@ -1 +0,0 @@
1
- export {};