Add search API. Improve summaries.
This commit is contained in:
parent
4421108b02
commit
72399dfb07
27 changed files with 1063 additions and 278 deletions
186
package-lock.json
generated
186
package-lock.json
generated
|
@ -14,10 +14,10 @@
|
|||
"@iconify/svelte": "^2.2.1",
|
||||
"@playwright/test": "^1.22.2",
|
||||
"@sveltejs/adapter-node": "^1.0.0-next.86",
|
||||
"@sveltejs/kit": "^1.0.0-next.428",
|
||||
"@sveltejs/kit": "^1.0.0-next.430",
|
||||
"@sveltejs/package": "^1.0.0-next.1",
|
||||
"@tailwindcss/typography": "^0.5.3",
|
||||
"@tricoteuses/explorer-tools": "^0.1.9",
|
||||
"@tricoteuses/explorer-tools": "^0.1.12",
|
||||
"@types/fs-extra": "^9.0.13",
|
||||
"@types/he": "^1.1.2",
|
||||
"@typescript-eslint/eslint-plugin": "^5.27.0",
|
||||
|
@ -32,6 +32,7 @@
|
|||
"fast-xml-parser": "^4.0.9",
|
||||
"fs-extra": "^10.1.0",
|
||||
"he": "^1.2.0",
|
||||
"morgan": "^1.10.0",
|
||||
"postcss": "^8.4.14",
|
||||
"postcss-load-config": "^4.0.1",
|
||||
"postgres": "^3.2.4",
|
||||
|
@ -250,9 +251,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"node_modules/@sveltejs/kit": {
|
||||
"version": "1.0.0-next.428",
|
||||
"resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-1.0.0-next.428.tgz",
|
||||
"integrity": "sha512-CVp7zltQ+3VOII1s7KuvgvGEFwD0PSqts9R3q4eaj0CNoC3gpdzcbEZayyQwXX/3lI1HdSNSF8gxbX0TRUoFuA==",
|
||||
"version": "1.0.0-next.430",
|
||||
"resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-1.0.0-next.430.tgz",
|
||||
"integrity": "sha512-0NL6DfFWASDkvjL6iCr3pO/u1oqvoF75lvnO6QTB0rIHMYOL+l9BAkxVETnNxzvgLRFCECi6NSh2C0bdaYaM1g==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
|
@ -343,9 +344,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@tricoteuses/explorer-tools": {
|
||||
"version": "0.1.9",
|
||||
"resolved": "https://registry.npmjs.org/@tricoteuses/explorer-tools/-/explorer-tools-0.1.9.tgz",
|
||||
"integrity": "sha512-qMeJdl4GXNShCHnCRZGLogmAie/TFEEC6BiKBasK4NqewWIyFSCmO8tWKRaDdXU7US641lS0ZRHFZwcVWkO6mA==",
|
||||
"version": "0.1.12",
|
||||
"resolved": "https://registry.npmjs.org/@tricoteuses/explorer-tools/-/explorer-tools-0.1.12.tgz",
|
||||
"integrity": "sha512-Aachs9YOz1R624H3gl1WuMkdbqhBPmjKIogfO3iukjVH4MBZ7g1jN2a8q4mRj0c9v82fwiKKBdcCz0FLi4S8Cw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@iconify-icons/codicon": "^1.2.15",
|
||||
|
@ -755,6 +756,18 @@
|
|||
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/basic-auth": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz",
|
||||
"integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"safe-buffer": "5.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/binary-extensions": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
|
||||
|
@ -1073,6 +1086,15 @@
|
|||
"integrity": "sha512-Y2caI5+ZwS5c3RiNDJ6u53VhQHv+hHKwhkI1iHvceKUHw9Df6EK2zRLfjejRgMuCuxK7PfSWIMwWecceVvThjQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/depd": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
|
||||
"integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/detect-indent": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz",
|
||||
|
@ -1150,6 +1172,12 @@
|
|||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/ee-first": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
||||
"integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/electron-to-chromium": {
|
||||
"version": "1.4.225",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.225.tgz",
|
||||
|
@ -2444,6 +2472,37 @@
|
|||
"mkdirp": "bin/cmd.js"
|
||||
}
|
||||
},
|
||||
"node_modules/morgan": {
|
||||
"version": "1.10.0",
|
||||
"resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz",
|
||||
"integrity": "sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"basic-auth": "~2.0.1",
|
||||
"debug": "2.6.9",
|
||||
"depd": "~2.0.0",
|
||||
"on-finished": "~2.3.0",
|
||||
"on-headers": "~1.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/morgan/node_modules/debug": {
|
||||
"version": "2.6.9",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"ms": "2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/morgan/node_modules/ms": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/mri": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz",
|
||||
|
@ -2566,6 +2625,27 @@
|
|||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/on-finished": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
|
||||
"integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"ee-first": "1.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/on-headers": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz",
|
||||
"integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/once": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||
|
@ -3083,6 +3163,12 @@
|
|||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/safe-buffer": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/sander": {
|
||||
"version": "0.5.1",
|
||||
"resolved": "https://registry.npmjs.org/sander/-/sander-0.5.1.tgz",
|
||||
|
@ -3926,9 +4012,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"@sveltejs/kit": {
|
||||
"version": "1.0.0-next.428",
|
||||
"resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-1.0.0-next.428.tgz",
|
||||
"integrity": "sha512-CVp7zltQ+3VOII1s7KuvgvGEFwD0PSqts9R3q4eaj0CNoC3gpdzcbEZayyQwXX/3lI1HdSNSF8gxbX0TRUoFuA==",
|
||||
"version": "1.0.0-next.430",
|
||||
"resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-1.0.0-next.430.tgz",
|
||||
"integrity": "sha512-0NL6DfFWASDkvjL6iCr3pO/u1oqvoF75lvnO6QTB0rIHMYOL+l9BAkxVETnNxzvgLRFCECi6NSh2C0bdaYaM1g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@sveltejs/vite-plugin-svelte": "^1.0.1",
|
||||
|
@ -3983,9 +4069,9 @@
|
|||
}
|
||||
},
|
||||
"@tricoteuses/explorer-tools": {
|
||||
"version": "0.1.9",
|
||||
"resolved": "https://registry.npmjs.org/@tricoteuses/explorer-tools/-/explorer-tools-0.1.9.tgz",
|
||||
"integrity": "sha512-qMeJdl4GXNShCHnCRZGLogmAie/TFEEC6BiKBasK4NqewWIyFSCmO8tWKRaDdXU7US641lS0ZRHFZwcVWkO6mA==",
|
||||
"version": "0.1.12",
|
||||
"resolved": "https://registry.npmjs.org/@tricoteuses/explorer-tools/-/explorer-tools-0.1.12.tgz",
|
||||
"integrity": "sha512-Aachs9YOz1R624H3gl1WuMkdbqhBPmjKIogfO3iukjVH4MBZ7g1jN2a8q4mRj0c9v82fwiKKBdcCz0FLi4S8Cw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@iconify-icons/codicon": "^1.2.15",
|
||||
|
@ -4253,6 +4339,15 @@
|
|||
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
|
||||
"dev": true
|
||||
},
|
||||
"basic-auth": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz",
|
||||
"integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"safe-buffer": "5.1.2"
|
||||
}
|
||||
},
|
||||
"binary-extensions": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
|
||||
|
@ -4476,6 +4571,12 @@
|
|||
"integrity": "sha512-Y2caI5+ZwS5c3RiNDJ6u53VhQHv+hHKwhkI1iHvceKUHw9Df6EK2zRLfjejRgMuCuxK7PfSWIMwWecceVvThjQ==",
|
||||
"dev": true
|
||||
},
|
||||
"depd": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
|
||||
"integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
|
||||
"dev": true
|
||||
},
|
||||
"detect-indent": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz",
|
||||
|
@ -4535,6 +4636,12 @@
|
|||
"integrity": "sha512-1K6hR6wtk2FviQ4kEiSjFiH5rpzEVi8WW0x96aztHVMhEspNpc4DVOUTEHtEva5VThQ8IaBX1Pe4gSzpVVUsKQ==",
|
||||
"dev": true
|
||||
},
|
||||
"ee-first": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
||||
"integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",
|
||||
"dev": true
|
||||
},
|
||||
"electron-to-chromium": {
|
||||
"version": "1.4.225",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.225.tgz",
|
||||
|
@ -5413,6 +5520,36 @@
|
|||
"minimist": "^1.2.6"
|
||||
}
|
||||
},
|
||||
"morgan": {
|
||||
"version": "1.10.0",
|
||||
"resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz",
|
||||
"integrity": "sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"basic-auth": "~2.0.1",
|
||||
"debug": "2.6.9",
|
||||
"depd": "~2.0.0",
|
||||
"on-finished": "~2.3.0",
|
||||
"on-headers": "~1.0.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"debug": {
|
||||
"version": "2.6.9",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ms": "2.0.0"
|
||||
}
|
||||
},
|
||||
"ms": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"mri": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz",
|
||||
|
@ -5494,6 +5631,21 @@
|
|||
"integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==",
|
||||
"dev": true
|
||||
},
|
||||
"on-finished": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
|
||||
"integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ee-first": "1.1.1"
|
||||
}
|
||||
},
|
||||
"on-headers": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz",
|
||||
"integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==",
|
||||
"dev": true
|
||||
},
|
||||
"once": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||
|
@ -5807,6 +5959,12 @@
|
|||
"mri": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"safe-buffer": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
|
||||
"dev": true
|
||||
},
|
||||
"sander": {
|
||||
"version": "0.5.1",
|
||||
"resolved": "https://registry.npmjs.org/sander/-/sander-0.5.1.tgz",
|
||||
|
|
|
@ -27,10 +27,10 @@
|
|||
"@iconify/svelte": "^2.2.1",
|
||||
"@playwright/test": "^1.22.2",
|
||||
"@sveltejs/adapter-node": "^1.0.0-next.86",
|
||||
"@sveltejs/kit": "^1.0.0-next.428",
|
||||
"@sveltejs/kit": "^1.0.0-next.430",
|
||||
"@sveltejs/package": "^1.0.0-next.1",
|
||||
"@tailwindcss/typography": "^0.5.3",
|
||||
"@tricoteuses/explorer-tools": "^0.1.9",
|
||||
"@tricoteuses/explorer-tools": "^0.1.12",
|
||||
"@types/fs-extra": "^9.0.13",
|
||||
"@types/he": "^1.1.2",
|
||||
"@typescript-eslint/eslint-plugin": "^5.27.0",
|
||||
|
@ -45,6 +45,7 @@
|
|||
"fast-xml-parser": "^4.0.9",
|
||||
"fs-extra": "^10.1.0",
|
||||
"he": "^1.2.0",
|
||||
"morgan": "^1.10.0",
|
||||
"postcss": "^8.4.14",
|
||||
"postcss-load-config": "^4.0.1",
|
||||
"postgres": "^3.2.4",
|
||||
|
|
6
src/lib/aggregates.ts
Normal file
6
src/lib/aggregates.ts
Normal file
|
@ -0,0 +1,6 @@
|
|||
export type Follow = typeof allFollows[number]
|
||||
|
||||
export const allFollows = [
|
||||
"LIENS.LIEN[@sens=cible,@typelien=CREATION].@id",
|
||||
] as const
|
||||
export const allFollowsMutable = [...allFollows]
|
|
@ -2,43 +2,50 @@ import type { Audit, Auditor } from "@auditors/core"
|
|||
import {
|
||||
auditArray,
|
||||
auditChain,
|
||||
auditCleanArray,
|
||||
auditFunction,
|
||||
auditInteger,
|
||||
auditNullish,
|
||||
auditOptions,
|
||||
auditSetNullish,
|
||||
auditStringToNumber,
|
||||
auditSwitch,
|
||||
auditTest,
|
||||
auditTrimString,
|
||||
auditUnique,
|
||||
} from "@auditors/core"
|
||||
|
||||
export function auditPaginationQuery(
|
||||
import { allFollowsMutable } from "$lib/aggregates"
|
||||
|
||||
export function auditFollowSearchParams(
|
||||
audit: Audit,
|
||||
query: URLSearchParams,
|
||||
): [unknown, unknown] {
|
||||
if (query == null) {
|
||||
return [query, null]
|
||||
}
|
||||
if (!(query instanceof URLSearchParams)) {
|
||||
return audit.unexpectedType(query, "URLSearchParams")
|
||||
}
|
||||
|
||||
const data: { [key: string]: unknown } = {}
|
||||
for (const [key, value] of query.entries()) {
|
||||
let values = data[key] as string[] | undefined
|
||||
if (values === undefined) {
|
||||
values = data[key] = []
|
||||
}
|
||||
values.push(value)
|
||||
}
|
||||
const errors: { [key: string]: unknown } = {}
|
||||
const remainingKeys = new Set(Object.keys(data))
|
||||
data: { [key: string]: unknown },
|
||||
errors: { [key: string]: unknown },
|
||||
remainingKeys: Set<string>,
|
||||
): void {
|
||||
audit.attribute(
|
||||
data,
|
||||
"follow",
|
||||
true,
|
||||
errors,
|
||||
remainingKeys,
|
||||
auditSearchParamsOptionsSet(allFollowsMutable),
|
||||
)
|
||||
}
|
||||
|
||||
export function auditLimitSearchParam(
|
||||
audit: Audit,
|
||||
data: { [key: string]: unknown },
|
||||
errors: { [key: string]: unknown },
|
||||
remainingKeys: Set<string>,
|
||||
): void {
|
||||
audit.attribute(
|
||||
data,
|
||||
"limit",
|
||||
true,
|
||||
errors,
|
||||
remainingKeys,
|
||||
auditQuerySingleton(
|
||||
auditSingleton(
|
||||
auditTrimString,
|
||||
auditStringToNumber,
|
||||
auditInteger,
|
||||
|
@ -53,13 +60,21 @@ export function auditPaginationQuery(
|
|||
auditSetNullish(10),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
export function auditOffsetSearchParam(
|
||||
audit: Audit,
|
||||
data: { [key: string]: unknown },
|
||||
errors: { [key: string]: unknown },
|
||||
remainingKeys: Set<string>,
|
||||
): void {
|
||||
audit.attribute(
|
||||
data,
|
||||
"offset",
|
||||
true,
|
||||
errors,
|
||||
remainingKeys,
|
||||
auditQuerySingleton(
|
||||
auditSingleton(
|
||||
auditTrimString,
|
||||
auditStringToNumber,
|
||||
auditInteger,
|
||||
|
@ -70,26 +85,9 @@ export function auditPaginationQuery(
|
|||
auditSetNullish(0),
|
||||
),
|
||||
)
|
||||
|
||||
// Keep the remaining keys as is.
|
||||
remainingKeys.clear()
|
||||
|
||||
return audit.reduceRemaining(data, errors, remainingKeys, auditSetNullish({}))
|
||||
}
|
||||
|
||||
export function auditQuerySingleton(...auditors: Auditor[]) {
|
||||
return auditChain(
|
||||
auditArray(),
|
||||
auditTest(
|
||||
(values) => values.length <= 1,
|
||||
"Parameter must be present only once in query",
|
||||
),
|
||||
auditFunction((value) => value[0]),
|
||||
...auditors,
|
||||
)
|
||||
}
|
||||
|
||||
export function auditSearchQuery(
|
||||
export function auditPaginationSearchParams(
|
||||
audit: Audit,
|
||||
query: URLSearchParams,
|
||||
): [unknown, unknown] {
|
||||
|
@ -111,61 +109,110 @@ export function auditSearchQuery(
|
|||
const errors: { [key: string]: unknown } = {}
|
||||
const remainingKeys = new Set(Object.keys(data))
|
||||
|
||||
auditSearchQueryContent(audit, data, errors, remainingKeys)
|
||||
auditLimitSearchParam(audit, data, errors, remainingKeys)
|
||||
auditOffsetSearchParam(audit, data, errors, remainingKeys)
|
||||
|
||||
// Keep the remaining keys as is.
|
||||
remainingKeys.clear()
|
||||
|
||||
return audit.reduceRemaining(data, errors, remainingKeys, auditSetNullish({}))
|
||||
}
|
||||
|
||||
export function auditSearchQueryContent(
|
||||
export function auditQSearchParam(
|
||||
audit: Audit,
|
||||
data: { [key: string]: unknown },
|
||||
errors: { [key: string]: unknown },
|
||||
remainingKeys: Set<string>,
|
||||
) {
|
||||
audit.attribute(
|
||||
data,
|
||||
"limit",
|
||||
true,
|
||||
errors,
|
||||
remainingKeys,
|
||||
auditQuerySingleton(
|
||||
auditTrimString,
|
||||
auditStringToNumber,
|
||||
auditInteger,
|
||||
auditTest(
|
||||
(value: number) => value >= 1,
|
||||
"Value must be greater than or equal to 1",
|
||||
),
|
||||
auditTest(
|
||||
(value: number) => value <= 1000,
|
||||
"Value must be less than or equal to 1000",
|
||||
),
|
||||
auditSetNullish(20),
|
||||
),
|
||||
)
|
||||
audit.attribute(
|
||||
data,
|
||||
"offset",
|
||||
true,
|
||||
errors,
|
||||
remainingKeys,
|
||||
auditQuerySingleton(
|
||||
auditTrimString,
|
||||
auditStringToNumber,
|
||||
auditInteger,
|
||||
auditTest(
|
||||
(value: number) => value >= 0,
|
||||
"Value must be greater than or equal to 0",
|
||||
),
|
||||
auditSetNullish(0),
|
||||
),
|
||||
)
|
||||
): void {
|
||||
audit.attribute(
|
||||
data,
|
||||
"q",
|
||||
true,
|
||||
errors,
|
||||
remainingKeys,
|
||||
auditQuerySingleton(auditTrimString),
|
||||
auditSingleton(auditTrimString),
|
||||
)
|
||||
}
|
||||
export const auditSearchParamsArray = auditChain(
|
||||
auditSwitch(
|
||||
auditNullish,
|
||||
[auditTrimString, auditFunction((value) => [value])],
|
||||
auditCleanArray(auditTrimString),
|
||||
),
|
||||
auditSetNullish([]),
|
||||
)
|
||||
|
||||
export function auditSearchParamsOptionsArray(
|
||||
possibleValues: string[],
|
||||
): Auditor {
|
||||
return auditChain(
|
||||
auditSearchParamsArray,
|
||||
auditArray(auditOptions(possibleValues)),
|
||||
auditUnique,
|
||||
auditSetNullish([]),
|
||||
)
|
||||
}
|
||||
|
||||
export function auditSearchParamsOptionsSet(possibleValues: string[]): Auditor {
|
||||
return auditChain(
|
||||
auditSearchParamsOptionsArray(possibleValues),
|
||||
auditFunction((values) => new Set(values)),
|
||||
auditSetNullish(new Set()),
|
||||
)
|
||||
}
|
||||
|
||||
export const auditSearchParamsSet = auditChain(
|
||||
auditSearchParamsArray,
|
||||
auditFunction((values) => new Set(values)),
|
||||
auditSetNullish(new Set()),
|
||||
)
|
||||
|
||||
export function auditSearchSearchParams(
|
||||
audit: Audit,
|
||||
query: URLSearchParams,
|
||||
): [unknown, unknown] {
|
||||
if (query == null) {
|
||||
return [query, null]
|
||||
}
|
||||
if (!(query instanceof URLSearchParams)) {
|
||||
return audit.unexpectedType(query, "URLSearchParams")
|
||||
}
|
||||
|
||||
const data: { [key: string]: unknown } = {}
|
||||
for (const [key, value] of query.entries()) {
|
||||
let values = data[key] as string[] | undefined
|
||||
if (values === undefined) {
|
||||
values = data[key] = []
|
||||
}
|
||||
values.push(value)
|
||||
}
|
||||
const errors: { [key: string]: unknown } = {}
|
||||
const remainingKeys = new Set(Object.keys(data))
|
||||
|
||||
auditSearchSearchParamsContent(audit, data, errors, remainingKeys)
|
||||
|
||||
return audit.reduceRemaining(data, errors, remainingKeys, auditSetNullish({}))
|
||||
}
|
||||
|
||||
export function auditSearchSearchParamsContent(
|
||||
audit: Audit,
|
||||
data: { [key: string]: unknown },
|
||||
errors: { [key: string]: unknown },
|
||||
remainingKeys: Set<string>,
|
||||
): void {
|
||||
auditLimitSearchParam(audit, data, errors, remainingKeys)
|
||||
auditOffsetSearchParam(audit, data, errors, remainingKeys)
|
||||
auditQSearchParam(audit, data, errors, remainingKeys)
|
||||
}
|
||||
|
||||
export function auditSingleton(...auditors: Auditor[]): Auditor {
|
||||
return auditChain(
|
||||
auditArray(),
|
||||
auditTest(
|
||||
(values) => values.length <= 1,
|
||||
"Parameter must be present only once in query",
|
||||
),
|
||||
auditFunction((value) => value[0]),
|
||||
...auditors,
|
||||
)
|
||||
}
|
|
@ -2,7 +2,7 @@
|
|||
import { cleanAudit } from "@auditors/core"
|
||||
|
||||
import { page } from "$app/stores"
|
||||
import { auditPaginationQuery } from "$lib/auditors/queries"
|
||||
import { auditPaginationSearchParams } from "$lib/auditors/search_params"
|
||||
import type { PaginationQuery } from "$lib/queries"
|
||||
|
||||
export let count: number | undefined | null = undefined // Count is not always known.
|
||||
|
@ -19,7 +19,7 @@
|
|||
$: pathname = url.pathname
|
||||
|
||||
function ensureValidQuery(query: URLSearchParams): PaginationQuery {
|
||||
const [validQuery, queryError] = auditPaginationQuery(
|
||||
const [validQuery, queryError] = auditPaginationSearchParams(
|
||||
cleanAudit,
|
||||
query,
|
||||
) as [PaginationQuery, unknown]
|
||||
|
|
183
src/lib/legal.ts
183
src/lib/legal.ts
|
@ -13,20 +13,37 @@ export interface Article {
|
|||
}
|
||||
}
|
||||
}
|
||||
NOTA: {
|
||||
CONTENU: string
|
||||
}
|
||||
LIENS?: {
|
||||
LIEN: Lien | Lien
|
||||
}
|
||||
CONTEXTE: Contexte
|
||||
VERSIONS: {
|
||||
VERSION: Array<{
|
||||
"@etat": Etat
|
||||
LIEN_ART: LienArt
|
||||
}>
|
||||
VERSION: ArticleVersion | ArticleVersion[]
|
||||
}
|
||||
BLOC_TEXTUEL: {
|
||||
CONTENU: string // HTML
|
||||
}
|
||||
}
|
||||
|
||||
export interface ArticleVersion {
|
||||
"@etat": Etat
|
||||
LIEN_ART: LienArt
|
||||
}
|
||||
|
||||
export interface Contexte {
|
||||
TEXTE: {
|
||||
"@cid": string
|
||||
"@nor": string
|
||||
"@num": string
|
||||
"@nature": string
|
||||
"@autorite": string
|
||||
"@ministere": string
|
||||
"@date_publi": string
|
||||
"@date_signature": string
|
||||
"@num_parution_jo": string
|
||||
TITRE_TXT: TitreTxt | TitreTxt[]
|
||||
}
|
||||
}
|
||||
|
@ -40,7 +57,7 @@ export interface DossierLegislatif {
|
|||
}
|
||||
}
|
||||
|
||||
export type Etat = "MODIFIE" | "PERIME" | "VIGUEUR"
|
||||
export type Etat = "" | "MODIFIE" | "PERIME" | "VIGUEUR"
|
||||
|
||||
export interface Idcc {
|
||||
META: {
|
||||
|
@ -72,6 +89,9 @@ export interface Jo {
|
|||
}
|
||||
META_COMMUN: MetaCommun
|
||||
}
|
||||
STRUCTURE_TXT: {
|
||||
TM: Tm
|
||||
}
|
||||
}
|
||||
|
||||
export type LegalObject =
|
||||
|
@ -98,6 +118,19 @@ export type LegalObjectType =
|
|||
| "textelr"
|
||||
| "versions"
|
||||
|
||||
export interface Lien {
|
||||
"@id": string
|
||||
"@num": string
|
||||
"@sens": "cible" | "source"
|
||||
"@cidtexte": string
|
||||
"@nortexte": string
|
||||
"@numtexte": string
|
||||
"@typelien": "CITATION" | "CODIFICATION" | "CREATION" | "SPEC_APPLI"
|
||||
"@naturetexte": string
|
||||
"@datesignatexte": string
|
||||
"#text": string
|
||||
}
|
||||
|
||||
export interface LienArt {
|
||||
"@id": string
|
||||
"@fin": string
|
||||
|
@ -111,30 +144,35 @@ export interface MetaCommun {
|
|||
ID: string
|
||||
}
|
||||
|
||||
export interface MetaTexteChronicle {
|
||||
CID: string
|
||||
NUM: string
|
||||
NUM_PARUTION: string
|
||||
NUM_SEQUENCE: number
|
||||
NOR: string
|
||||
DATE_PUBLI: string
|
||||
DATE_TEXTE: string
|
||||
DERNIERE_MODIFICATION: string
|
||||
ORIGINE_PUBLI: string
|
||||
PAGE_DEB_PUBLI: number
|
||||
PAGE_FIN_PUBLI: number
|
||||
VERSIONS_A_VENIR?: string
|
||||
}
|
||||
|
||||
export interface SectionTa {
|
||||
ID: string
|
||||
CONTEXTE: Contexte
|
||||
TITRE_TA: string
|
||||
STRUCTURE_TA: { LIEN_ART?: LienArt | LienArt[] }
|
||||
STRUCTURE_TA: {
|
||||
LIEN_ART?: LienArt | LienArt[]
|
||||
}
|
||||
}
|
||||
|
||||
export interface Textekali {
|
||||
META: {
|
||||
META_COMMUN: MetaCommun
|
||||
META_SPEC: {
|
||||
META_TEXTE_CHRONICLE: {
|
||||
CID: string
|
||||
NUM: string
|
||||
NUM_PARUTION: string
|
||||
NUM_SEQUENCE: string
|
||||
NOR: string
|
||||
DATE_PUBLI: string
|
||||
DATE_TEXTE: string
|
||||
DERNIERE_MODIFICATION: string
|
||||
ORIGINE_PUBLI: string
|
||||
PAGE_DEB_PUBLI: string
|
||||
PAGE_FIN_PUBLI: string
|
||||
}
|
||||
META_TEXTE_CHRONICLE: MetaTexteChronicle
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -142,12 +180,37 @@ export interface Textekali {
|
|||
export interface Textelr {
|
||||
META: {
|
||||
META_COMMUN: MetaCommun
|
||||
META_SPEC: {
|
||||
META_TEXTE_CHRONICLE: MetaTexteChronicle
|
||||
}
|
||||
}
|
||||
STRUCT: {
|
||||
LIEN_ART?: LienArt | LienArt[]
|
||||
}
|
||||
VERSIONS: {
|
||||
VERSION: TextelrVersion | TextelrVersion[]
|
||||
}
|
||||
}
|
||||
|
||||
export interface TextelrVersion {
|
||||
"@etat": Etat
|
||||
LIEN_TXT: TextelrVersionLienTxt
|
||||
}
|
||||
|
||||
export interface TextelrVersionLienTxt {
|
||||
"@id": string
|
||||
"@fin": string
|
||||
"@num": string
|
||||
"@debut": string
|
||||
}
|
||||
|
||||
export interface TexteVersion {
|
||||
ABRO?: {
|
||||
CONTENU: string
|
||||
}
|
||||
META: {
|
||||
META_SPEC: {
|
||||
META_TEXTE_CHRONICLE: MetaTexteChronicle
|
||||
META_TEXTE_VERSION: {
|
||||
TITRE: string
|
||||
TITREFULL: string
|
||||
|
@ -155,6 +218,34 @@ export interface TexteVersion {
|
|||
}
|
||||
META_COMMUN: MetaCommun
|
||||
}
|
||||
NOTA?: {
|
||||
CONTENU: string
|
||||
}
|
||||
NOTICE?: {
|
||||
CONTENU: string
|
||||
}
|
||||
RECT?: {
|
||||
CONTENU: string
|
||||
}
|
||||
SIGNATAIRES?: {
|
||||
CONTENU: string
|
||||
}
|
||||
SM?: {
|
||||
CONTENU: string
|
||||
}
|
||||
TP?: {
|
||||
CONTENU: string
|
||||
}
|
||||
VISAS?: {
|
||||
CONTENU: string
|
||||
}
|
||||
}
|
||||
|
||||
export interface TitreTm {
|
||||
"@id": string
|
||||
"@fin": string
|
||||
"#text": string
|
||||
"@debut": string
|
||||
}
|
||||
|
||||
export interface TitreTxt {
|
||||
|
@ -165,6 +256,19 @@ export interface TitreTxt {
|
|||
"#text": string
|
||||
}
|
||||
|
||||
/// Table des matières (TM)
|
||||
export interface Tm {
|
||||
"@niv": string // 1, 2, 3…
|
||||
LIEN_TXT?: TmLienTxt | TmLienTxt[]
|
||||
TITRE_TM: string
|
||||
TM?: Tm | Tm[]
|
||||
}
|
||||
|
||||
export interface TmLienTxt {
|
||||
"@idtxt": string
|
||||
"@titretxt": string
|
||||
}
|
||||
|
||||
export interface Versions {
|
||||
VERSION: {
|
||||
"@id": string
|
||||
|
@ -186,6 +290,15 @@ export interface XmlHeader {
|
|||
|
||||
export const appMenu: MenuItem[] = [
|
||||
{ href: "/recherche", label: "Recherche" },
|
||||
{
|
||||
items: [
|
||||
{ href: "/idcc", label: "Accords de branche et conventions collectives" },
|
||||
{ href: "/texte_version", label: "Codes, lois et règlements" },
|
||||
{ href: "/dossier_legislatif", label: "Dossiers législatifs" },
|
||||
{ href: "/jo", label: "Journal officiel" },
|
||||
],
|
||||
label: "Fonds",
|
||||
},
|
||||
{
|
||||
items: [
|
||||
{ href: "/article", label: "ARTICLE" },
|
||||
|
@ -242,6 +355,38 @@ export function bestItemForDate<T extends { "@debut": string; "@fin": string }>(
|
|||
return items[0]
|
||||
}
|
||||
|
||||
export function pathnameFromLegalId(id: string): string | undefined {
|
||||
if (id.match(/^[A-Z]{4}ARTI/) !== null) {
|
||||
return `/article/${id}`
|
||||
}
|
||||
if (id.match(/^[A-Z]{4}DOLE/) !== null) {
|
||||
return `/dossier_legislatif/${id}`
|
||||
}
|
||||
if (id.match(/^KALICONT/) !== null) {
|
||||
return `/idcc/${id}`
|
||||
}
|
||||
if (id.match(/^JORFCONT/) !== null) {
|
||||
return `/jo/${id}`
|
||||
}
|
||||
if (id.match(/^[A-Z]{4}SCTA/) !== null) {
|
||||
return `/section_ta/${id}`
|
||||
}
|
||||
if (id.match(/^[A-Z]{4}TEXT/) !== null) {
|
||||
return `/texte_version/${id}`
|
||||
}
|
||||
// TODO: Ce test est redondant avec le précédent (pour KALITEXT).
|
||||
// => Trouver quel lien mettre par défaut.
|
||||
// if (id.match(/^KALITEXT/) !== null) {
|
||||
// return `/textekali/${id}`
|
||||
// }
|
||||
// TODO: Ce test est redondant avec le précédent (pour JORFTEXT & LEGITEXT).
|
||||
// => Trouver quel lien mettre par défaut.
|
||||
// if (id.match(/^[A-Z]{4}TEXT/) !== null) {
|
||||
// return `/textelr/${id}`
|
||||
// }
|
||||
return undefined
|
||||
}
|
||||
|
||||
export function pathnameFromLegalObject(
|
||||
type: LegalObjectType,
|
||||
object: LegalObject,
|
||||
|
|
|
@ -23,7 +23,19 @@ import { walkDir } from "$lib/server/file_systems"
|
|||
const xmlParser = new XMLParser({
|
||||
attributeNamePrefix: "@",
|
||||
ignoreAttributes: false,
|
||||
stopNodes: ["ARTICLE.BLOC_TEXTUEL.CONTENU", "ARTICLE.SM.CONTENU"],
|
||||
stopNodes: [
|
||||
"ARTICLE.BLOC_TEXTUEL.CONTENU",
|
||||
"ARTICLE.NOTA.CONTENU",
|
||||
"ARTICLE.SM.CONTENU",
|
||||
"TEXTE_VERSION.ABRO.CONTENU",
|
||||
"TEXTE_VERSION.NOTA.CONTENU",
|
||||
"TEXTE_VERSION.NOTICE.CONTENU",
|
||||
"TEXTE_VERSION.RECT.CONTENU",
|
||||
"TEXTE_VERSION.SIGNATAIRES.CONTENU",
|
||||
"TEXTE_VERSION.SM.CONTENU",
|
||||
"TEXTE_VERSION.TP.CONTENU",
|
||||
"TEXTE_VERSION.VISAS.CONTENU",
|
||||
],
|
||||
tagValueProcessor: (_tagName, tagValue) => he.decode(tagValue),
|
||||
})
|
||||
|
||||
|
|
|
@ -22,7 +22,19 @@ import { walkDir } from "$lib/server/file_systems"
|
|||
const xmlParser = new XMLParser({
|
||||
attributeNamePrefix: "@",
|
||||
ignoreAttributes: false,
|
||||
stopNodes: ["ARTICLE.BLOC_TEXTUEL.CONTENU", "ARTICLE.SM.CONTENU"],
|
||||
stopNodes: [
|
||||
"ARTICLE.BLOC_TEXTUEL.CONTENU",
|
||||
"ARTICLE.NOTA.CONTENU",
|
||||
"ARTICLE.SM.CONTENU",
|
||||
"TEXTE_VERSION.ABRO.CONTENU",
|
||||
"TEXTE_VERSION.NOTA.CONTENU",
|
||||
"TEXTE_VERSION.NOTICE.CONTENU",
|
||||
"TEXTE_VERSION.RECT.CONTENU",
|
||||
"TEXTE_VERSION.SIGNATAIRES.CONTENU",
|
||||
"TEXTE_VERSION.SM.CONTENU",
|
||||
"TEXTE_VERSION.TP.CONTENU",
|
||||
"TEXTE_VERSION.VISAS.CONTENU",
|
||||
],
|
||||
tagValueProcessor: (_tagName, tagValue) => he.decode(tagValue),
|
||||
})
|
||||
|
||||
|
|
|
@ -24,11 +24,14 @@ const xmlParser = new XMLParser({
|
|||
ignoreAttributes: false,
|
||||
stopNodes: [
|
||||
"ARTICLE.BLOC_TEXTUEL.CONTENU",
|
||||
"ARTICLE.NOTA.CONTENU",
|
||||
"ARTICLE.SM.CONTENU",
|
||||
"TEXTE_VERSION.ABRO.CONTENU",
|
||||
"TEXTE_VERSION.CONTENU",
|
||||
"TEXTE_VERSION.NOTA.CONTENU",
|
||||
"TEXTE_VERSION.NOTICE.CONTENU",
|
||||
"TEXTE_VERSION.RECT.CONTENU",
|
||||
"TEXTE_VERSION.SIGNATAIRES.CONTENU",
|
||||
"TEXTE_VERSION.SM.CONTENU",
|
||||
"TEXTE_VERSION.TP.CONTENU",
|
||||
"TEXTE_VERSION.VISAS.CONTENU",
|
||||
],
|
||||
|
|
86
src/lib/server/aggregates.ts
Normal file
86
src/lib/server/aggregates.ts
Normal file
|
@ -0,0 +1,86 @@
|
|||
import { iterArrayOrSingleton } from "@tricoteuses/explorer-tools"
|
||||
|
||||
import type { Follow } from "$lib/aggregates"
|
||||
import type { Article, Lien } from "$lib/legal"
|
||||
import { db } from "$lib/server/database"
|
||||
|
||||
export interface Aggregate {
|
||||
article?: { [id: string]: Article }
|
||||
id?: string
|
||||
ids?: string[]
|
||||
}
|
||||
|
||||
export class Aggregator {
|
||||
article: { [id: string]: Article } = {}
|
||||
follow: Set<Follow>
|
||||
requestedIds: Set<string> = new Set()
|
||||
visitedIds: Set<string> = new Set()
|
||||
|
||||
constructor(follow: Iterable<Follow>) {
|
||||
this.follow = new Set(follow)
|
||||
}
|
||||
|
||||
addArticle(article: Article): void {
|
||||
const id = article.META.META_COMMUN.ID
|
||||
this.article[id] = article
|
||||
|
||||
if (this.follow.has("LIENS.LIEN[@sens=cible,@typelien=CREATION].@id")) {
|
||||
for (const lien of iterArrayOrSingleton(article.LIENS?.LIEN)) {
|
||||
if (lien["@sens"] === "cible" && lien["@typelien"] === "CREATION") {
|
||||
this.requestId(lien["@id"])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
addToVisitedIds(ids: string[]) {
|
||||
for (const id of ids) {
|
||||
this.visitedIds.add(id)
|
||||
}
|
||||
}
|
||||
|
||||
deleteFromRequestedIds(ids: string[]) {
|
||||
for (const id of ids) {
|
||||
this.requestedIds.delete(id)
|
||||
}
|
||||
}
|
||||
|
||||
async getAll(): Promise<void> {
|
||||
while (true) {
|
||||
if (this.requestedIds.size === 0) {
|
||||
break
|
||||
}
|
||||
console.log("this.requestedIds.size", this.requestedIds.size)
|
||||
|
||||
{
|
||||
const articleIds = [...this.requestedIds].filter(
|
||||
(id) => id.match(/^[A-Z]{4}ARTI/) !== null,
|
||||
)
|
||||
if (articleIds.length > 0) {
|
||||
this.deleteFromRequestedIds(articleIds)
|
||||
for (const { data } of await db<{ id: string; data: Article }[]>`
|
||||
SELECT id, data FROM article
|
||||
WHERE id IN ${db(articleIds)}
|
||||
`) {
|
||||
this.addArticle(data)
|
||||
}
|
||||
this.addToVisitedIds(articleIds)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
requestId(id: string): void {
|
||||
if (!this.visitedIds.has(id)) {
|
||||
this.requestedIds.add(id)
|
||||
}
|
||||
}
|
||||
|
||||
toJson(): Aggregate {
|
||||
const json: Aggregate = {}
|
||||
if (Object.keys(this.article).length > 0) {
|
||||
json.article = this.article
|
||||
}
|
||||
return json
|
||||
}
|
||||
}
|
91
src/lib/server/doers/recherche.ts
Normal file
91
src/lib/server/doers/recherche.ts
Normal file
|
@ -0,0 +1,91 @@
|
|||
import { type Audit, auditSetNullish, cleanAudit } from "@auditors/core"
|
||||
import { error } from "@sveltejs/kit"
|
||||
|
||||
import type { Follow } from "$lib/aggregates"
|
||||
import {
|
||||
auditFollowSearchParams,
|
||||
auditQSearchParam,
|
||||
} from "$lib/auditors/search_params"
|
||||
import type { Article } from "$lib/legal"
|
||||
import { type Aggregate, Aggregator } from "$lib/server/aggregates"
|
||||
import { db } from "$lib/server/database"
|
||||
|
||||
export function auditSearchParams(
|
||||
audit: Audit,
|
||||
query: URLSearchParams,
|
||||
): [unknown, unknown] {
|
||||
if (query == null) {
|
||||
return [query, null]
|
||||
}
|
||||
if (!(query instanceof URLSearchParams)) {
|
||||
return audit.unexpectedType(query, "URLSearchParams")
|
||||
}
|
||||
|
||||
const data: { [key: string]: unknown } = {}
|
||||
for (const [key, value] of query.entries()) {
|
||||
let values = data[key] as string[] | undefined
|
||||
if (values === undefined) {
|
||||
values = data[key] = []
|
||||
}
|
||||
values.push(value)
|
||||
}
|
||||
const errors: { [key: string]: unknown } = {}
|
||||
const remainingKeys = new Set(Object.keys(data))
|
||||
|
||||
auditFollowSearchParams(audit, data, errors, remainingKeys)
|
||||
auditQSearchParam(audit, data, errors, remainingKeys)
|
||||
|
||||
return audit.reduceRemaining(data, errors, remainingKeys, auditSetNullish({}))
|
||||
}
|
||||
|
||||
export const doGetRecherche = async (
|
||||
url: URL,
|
||||
): Promise<
|
||||
| (Aggregate & {
|
||||
q: string
|
||||
})
|
||||
| null
|
||||
> => {
|
||||
const [query, queryError] = auditSearchParams(
|
||||
cleanAudit,
|
||||
url.searchParams,
|
||||
) as [{ follow: Follow[]; q?: string }, unknown]
|
||||
if (queryError !== null) {
|
||||
console.error(
|
||||
`Error in ${url.pathname} query:\n${JSON.stringify(
|
||||
query,
|
||||
null,
|
||||
2,
|
||||
)}\n\nError:\n${JSON.stringify(queryError, null, 2)}`,
|
||||
)
|
||||
throw error(400, JSON.stringify(queryError, null, 2))
|
||||
}
|
||||
const { follow, q } = query
|
||||
|
||||
if (q !== undefined) {
|
||||
// https://www.legifrance.gouv.fr/codes/article_lc/LEGIARTI000006308296/
|
||||
// https://www.legifrance.gouv.fr/codes/article_lc/LEGIARTI000006308296/1983-12-30/
|
||||
// https://www.legifrance.gouv.fr/codes/article_lc/LEGIARTI000006308296/1984-12-30/
|
||||
// https://www.legifrance.gouv.fr/loda/article_lc/LEGIARTI000036456533
|
||||
// https://www.legifrance.gouv.fr/loda/article_lc/LEGIARTI000036456533/2018-01-01
|
||||
// https://www.legifrance.gouv.fr/loda/id/LEGIARTI000006317314/1983-12-30
|
||||
const articleId = q.match(/LEGIARTI\d+/)?.[0]
|
||||
if (articleId != null) {
|
||||
const article = (
|
||||
await db<{ data: Article }[]>`
|
||||
SELECT data FROM article
|
||||
WHERE id = ${articleId}
|
||||
LIMIT 1
|
||||
`
|
||||
).map(({ data }) => data)[0]
|
||||
if (article !== undefined) {
|
||||
const aggregator = new Aggregator(follow)
|
||||
aggregator.addArticle(article)
|
||||
await aggregator.getAll()
|
||||
|
||||
return { ...aggregator.toJson(), id: article.META.META_COMMUN.ID, q }
|
||||
}
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
|
@ -3,22 +3,30 @@ import type { Access, Summarizer, Summary } from "augmented-data-viewer"
|
|||
|
||||
import {
|
||||
type Article,
|
||||
type ArticleVersion,
|
||||
assertNeverLegalObjectType,
|
||||
bestItemForDate,
|
||||
type DossierLegislatif,
|
||||
type Idcc,
|
||||
type IdWrapper,
|
||||
type Jo,
|
||||
type LegalObject,
|
||||
type LegalObjectType,
|
||||
type Lien,
|
||||
type LienArt,
|
||||
pathnameFromLegalId,
|
||||
pathnameFromLegalObject,
|
||||
pathnameFromLegalObjectId,
|
||||
type SectionTa,
|
||||
type Textekali,
|
||||
type Textelr,
|
||||
type TextelrVersionLienTxt,
|
||||
type TexteVersion,
|
||||
type LienArt,
|
||||
type TitreTxt,
|
||||
type TitreTm,
|
||||
type Tm,
|
||||
type TmLienTxt,
|
||||
type VersionsWrapper,
|
||||
type DossierLegislatif,
|
||||
} from "$lib/legal"
|
||||
|
||||
export const summarizeArticleProperties: Summarizer = (access, value) => {
|
||||
|
@ -29,6 +37,25 @@ export const summarizeArticleProperties: Summarizer = (access, value) => {
|
|||
return summarizeLegalObjectToLink(access, "article", value)
|
||||
}
|
||||
|
||||
if (access?.key === "@cid" && access?.access?.key === "TEXTE") {
|
||||
return summarizeLegalIdToLink(access, value)
|
||||
}
|
||||
if (
|
||||
access?.key === "@cidtexte" &&
|
||||
(access?.access?.key === "LIEN" ||
|
||||
(typeof access?.access?.key === "number" &&
|
||||
access?.access?.access?.key === "LIEN"))
|
||||
) {
|
||||
return summarizeLegalIdToLink(access, value)
|
||||
}
|
||||
if (
|
||||
access?.key === "@id" &&
|
||||
(access?.access?.key === "LIEN" ||
|
||||
(typeof access?.access?.key === "number" &&
|
||||
access?.access?.access?.key === "LIEN"))
|
||||
) {
|
||||
return summarizeLienId(access.access, access.parent)
|
||||
}
|
||||
if (
|
||||
access?.key === "@id" &&
|
||||
(access?.access?.key === "LIEN_ART" ||
|
||||
|
@ -37,17 +64,39 @@ export const summarizeArticleProperties: Summarizer = (access, value) => {
|
|||
) {
|
||||
return summarizeLienArtId(access.access, access.parent)
|
||||
}
|
||||
if (
|
||||
access?.key === "@id" &&
|
||||
(access?.access?.key === "TITRE_TM" ||
|
||||
(typeof access?.access?.key === "number" &&
|
||||
access?.access?.access?.key === "TITRE_TM"))
|
||||
) {
|
||||
return summarizeTitreTmId(access.access, access.parent)
|
||||
}
|
||||
if (
|
||||
access?.key === "@id_txt" &&
|
||||
(access?.access?.key === "TITRE_TXT" ||
|
||||
(typeof access?.access?.key === "number" &&
|
||||
access?.access?.access?.key === "TITRE_TXT"))
|
||||
) {
|
||||
return summarizeTitreTxtIdTxt(access.access, access.parent)
|
||||
}
|
||||
if (access?.key === "CONTENU") {
|
||||
return { content: value as string, type: "html" }
|
||||
}
|
||||
if (access?.key === "LIEN_ART" && !Array.isArray(value)) {
|
||||
return summarizeLienArt(access, value)
|
||||
if (
|
||||
(access?.key === "LIEN" && !Array.isArray(value)) ||
|
||||
(typeof access?.key === "number" && access?.access?.key === "LIEN")
|
||||
) {
|
||||
return summarizeLien(access, value)
|
||||
}
|
||||
if (typeof access?.key === "number" && access?.access?.key === "LIEN_ART") {
|
||||
if (
|
||||
(access?.key === "LIEN_ART" && !Array.isArray(value)) ||
|
||||
(typeof access?.key === "number" && access?.access?.key === "LIEN_ART")
|
||||
) {
|
||||
return summarizeLienArt(access, value)
|
||||
}
|
||||
if (access?.access?.key === "VERSION") {
|
||||
const version = value as Article["VERSIONS"]["VERSION"][0]
|
||||
const version = value as ArticleVersion
|
||||
const lienArt = version.LIEN_ART
|
||||
return {
|
||||
content: {
|
||||
|
@ -139,9 +188,61 @@ export const summarizeJoProperties: Summarizer = (access, value) => {
|
|||
return summarizeLegalObjectToLink(access, "jo", value)
|
||||
}
|
||||
|
||||
if (
|
||||
(access?.key === "LIEN_TXT" && !Array.isArray(value)) ||
|
||||
(typeof access?.key === "number" && access?.access?.key === "LIEN_TXT")
|
||||
) {
|
||||
return summarizeTmLienTxt(access, value)
|
||||
}
|
||||
if (
|
||||
(access?.key === "TM" && !Array.isArray(value)) ||
|
||||
(typeof access?.key === "number" && access?.access?.key === "TM")
|
||||
) {
|
||||
return summarizeTm(access, value)
|
||||
}
|
||||
|
||||
return undefined
|
||||
}
|
||||
|
||||
export function summarizeLegalIdToLink(
|
||||
access: Access | undefined,
|
||||
value: unknown,
|
||||
): Summary | undefined {
|
||||
const id = value as string | undefined
|
||||
if (id === undefined) {
|
||||
return undefined
|
||||
}
|
||||
const pathname = pathnameFromLegalId(id)
|
||||
if (pathname === undefined) {
|
||||
return undefined
|
||||
}
|
||||
return {
|
||||
content: id,
|
||||
href: pathname,
|
||||
type: "link",
|
||||
}
|
||||
}
|
||||
|
||||
export function summarizeLegalIdToValueArrowLink(
|
||||
access: Access | undefined,
|
||||
value: unknown,
|
||||
): Summary | undefined {
|
||||
const uidLinkSummary = summarizeLegalIdToLink(access, value)
|
||||
return uidLinkSummary === undefined
|
||||
? undefined
|
||||
: {
|
||||
items: [
|
||||
{
|
||||
content: JSON.stringify(value),
|
||||
type: "raw_data",
|
||||
},
|
||||
{ class: "mx-1 mt-1", icon: arrowRight, inline: true, type: "icon" },
|
||||
uidLinkSummary,
|
||||
],
|
||||
type: "concatenation",
|
||||
}
|
||||
}
|
||||
|
||||
export function summarizeLegalObject(
|
||||
access: Access | undefined,
|
||||
type: LegalObjectType,
|
||||
|
@ -250,6 +351,22 @@ export function summarizeLegalObjectToLink(
|
|||
}
|
||||
}
|
||||
|
||||
export const summarizeLien: Summarizer = (access, value) => {
|
||||
const lien = value as Lien | undefined
|
||||
if (lien === undefined) {
|
||||
return undefined
|
||||
}
|
||||
const pathname = pathnameFromLegalId(lien["@id"])
|
||||
if (pathname === undefined) {
|
||||
return undefined
|
||||
}
|
||||
return {
|
||||
content: `${lien["@typelien"]} ${lien["@sens"]} : ${lien["#text"]}`,
|
||||
href: pathname,
|
||||
type: "link",
|
||||
}
|
||||
}
|
||||
|
||||
export const summarizeLienArt: Summarizer = (access, value) => {
|
||||
const lienArt = value as LienArt | undefined
|
||||
if (lienArt === undefined) {
|
||||
|
@ -273,13 +390,39 @@ export const summarizeLienArtId: Summarizer = (access, value) => {
|
|||
content: JSON.stringify(lienArt["@id"]),
|
||||
type: "raw_data",
|
||||
},
|
||||
{ class: "mx-1", icon: arrowRight, inline: true, type: "icon" },
|
||||
{ class: "mt-1 mx-1", icon: arrowRight, inline: true, type: "icon" },
|
||||
summarizeLienArt(access, lienArt)!,
|
||||
],
|
||||
type: "concatenation",
|
||||
}
|
||||
}
|
||||
|
||||
export const summarizeLienId: Summarizer = (access, value) => {
|
||||
const lien = value as Lien | undefined
|
||||
if (lien === undefined) {
|
||||
return undefined
|
||||
}
|
||||
const pathname = pathnameFromLegalId(lien["@id"])
|
||||
if (pathname === undefined) {
|
||||
return undefined
|
||||
}
|
||||
return {
|
||||
items: [
|
||||
{
|
||||
content: JSON.stringify(lien["@id"]),
|
||||
type: "raw_data",
|
||||
},
|
||||
{ class: "mt-1 mx-1", icon: arrowRight, inline: true, type: "icon" },
|
||||
{
|
||||
content: lien["#text"],
|
||||
href: pathname,
|
||||
type: "link",
|
||||
},
|
||||
],
|
||||
type: "concatenation",
|
||||
}
|
||||
}
|
||||
|
||||
export const summarizeSectionTaProperties: Summarizer = (access, value) => {
|
||||
if (access?.key === "section_ta" && typeof value !== "number") {
|
||||
return summarizeLegalObject(access, "section_ta", value)
|
||||
|
@ -288,6 +431,9 @@ export const summarizeSectionTaProperties: Summarizer = (access, value) => {
|
|||
return summarizeLegalObjectToLink(access, "section_ta", value)
|
||||
}
|
||||
|
||||
if (access?.key === "@cid" && access?.access?.key === "TEXTE") {
|
||||
return summarizeLegalIdToLink(access, value)
|
||||
}
|
||||
if (
|
||||
access?.key === "@id" &&
|
||||
(access?.access?.key === "LIEN_ART" ||
|
||||
|
@ -296,10 +442,10 @@ export const summarizeSectionTaProperties: Summarizer = (access, value) => {
|
|||
) {
|
||||
return summarizeLienArtId(access.access, access.parent)
|
||||
}
|
||||
if (access?.key === "LIEN_ART" && !Array.isArray(value)) {
|
||||
return summarizeLienArt(access, value)
|
||||
}
|
||||
if (typeof access?.key === "number" && access?.access?.key === "LIEN_ART") {
|
||||
if (
|
||||
(access?.key === "LIEN_ART" && !Array.isArray(value)) ||
|
||||
(typeof access?.key === "number" && access?.access?.key === "LIEN_ART")
|
||||
) {
|
||||
return summarizeLienArt(access, value)
|
||||
}
|
||||
return undefined
|
||||
|
@ -313,9 +459,29 @@ export const summarizeTextekaliProperties: Summarizer = (access, value) => {
|
|||
return summarizeLegalObjectToLink(access, "textekali", value)
|
||||
}
|
||||
|
||||
if (access?.key === "CID") {
|
||||
return summarizeLegalIdToLink(access, value)
|
||||
}
|
||||
|
||||
return undefined
|
||||
}
|
||||
|
||||
export const summarizeTextelrVersionLienTxt: Summarizer = (access, value) => {
|
||||
const lienTxt = value as TextelrVersionLienTxt | undefined
|
||||
if (lienTxt === undefined) {
|
||||
return undefined
|
||||
}
|
||||
const pathname = pathnameFromLegalId(lienTxt["@id"])
|
||||
if (pathname === undefined) {
|
||||
return undefined
|
||||
}
|
||||
return {
|
||||
content: lienTxt["@id"],
|
||||
href: pathname,
|
||||
type: "link",
|
||||
}
|
||||
}
|
||||
|
||||
export const summarizeTextelrProperties: Summarizer = (access, value) => {
|
||||
if (access?.key === "textelr" && typeof value !== "number") {
|
||||
return summarizeLegalObject(access, "textelr", value)
|
||||
|
@ -324,6 +490,27 @@ export const summarizeTextelrProperties: Summarizer = (access, value) => {
|
|||
return summarizeLegalObjectToLink(access, "textelr", value)
|
||||
}
|
||||
|
||||
if (
|
||||
access?.key === "@id" &&
|
||||
(access?.access?.key === "LIEN_ART" ||
|
||||
(typeof access?.access?.key === "number" &&
|
||||
access?.access?.access?.key === "LIEN_ART"))
|
||||
) {
|
||||
return summarizeLienArtId(access.access, access.parent)
|
||||
}
|
||||
if (access?.key === "CID") {
|
||||
return summarizeLegalIdToLink(access, value)
|
||||
}
|
||||
if (
|
||||
(access?.key === "LIEN_ART" && !Array.isArray(value)) ||
|
||||
(typeof access?.key === "number" && access?.access?.key === "LIEN_ART")
|
||||
) {
|
||||
return summarizeLienArt(access, value)
|
||||
}
|
||||
if (access?.key === "LIEN_TXT") {
|
||||
return summarizeTextelrVersionLienTxt(access, value)
|
||||
}
|
||||
|
||||
return undefined
|
||||
}
|
||||
|
||||
|
@ -338,9 +525,95 @@ export const summarizeTexteVersionProperties: Summarizer = (access, value) => {
|
|||
return summarizeLegalObjectToLink(access, "texte_version", value)
|
||||
}
|
||||
|
||||
if (access?.key === "CID") {
|
||||
return summarizeLegalIdToLink(access, value)
|
||||
}
|
||||
if (access?.key === "CONTENU") {
|
||||
return { content: value as string, type: "html" }
|
||||
}
|
||||
|
||||
return undefined
|
||||
}
|
||||
|
||||
export const summarizeTitreTmId: Summarizer = (access, value) => {
|
||||
const titreTm = value as TitreTm | undefined
|
||||
if (titreTm === undefined) {
|
||||
return undefined
|
||||
}
|
||||
const pathname = pathnameFromLegalId(titreTm["@id"])
|
||||
if (pathname === undefined) {
|
||||
return undefined
|
||||
}
|
||||
return {
|
||||
items: [
|
||||
{
|
||||
content: JSON.stringify(titreTm["@id"]),
|
||||
type: "raw_data",
|
||||
},
|
||||
{ class: "mt-1 mx-1", icon: arrowRight, inline: true, type: "icon" },
|
||||
{
|
||||
content: titreTm["#text"],
|
||||
href: pathname,
|
||||
type: "link",
|
||||
},
|
||||
],
|
||||
type: "concatenation",
|
||||
}
|
||||
}
|
||||
|
||||
export const summarizeTitreTxtIdTxt: Summarizer = (access, value) => {
|
||||
const titreTxt = value as TitreTxt | undefined
|
||||
if (titreTxt === undefined) {
|
||||
return undefined
|
||||
}
|
||||
const pathname = pathnameFromLegalId(titreTxt["@id_txt"])
|
||||
if (pathname === undefined) {
|
||||
return undefined
|
||||
}
|
||||
return {
|
||||
items: [
|
||||
{
|
||||
content: JSON.stringify(titreTxt["@id_txt"]),
|
||||
type: "raw_data",
|
||||
},
|
||||
{ class: "mt-1 mx-1", icon: arrowRight, inline: true, type: "icon" },
|
||||
{
|
||||
content: titreTxt["#text"],
|
||||
href: pathname,
|
||||
type: "link",
|
||||
},
|
||||
],
|
||||
type: "concatenation",
|
||||
}
|
||||
}
|
||||
|
||||
export function summarizeTm(
|
||||
access: Access | undefined,
|
||||
value: unknown,
|
||||
): Summary | undefined {
|
||||
const tm = value as Tm | undefined
|
||||
if (tm === undefined) {
|
||||
return undefined
|
||||
}
|
||||
return tm.TITRE_TM
|
||||
}
|
||||
|
||||
export const summarizeTmLienTxt: Summarizer = (access, value) => {
|
||||
const lienTxt = value as TmLienTxt | undefined
|
||||
if (lienTxt === undefined) {
|
||||
return undefined
|
||||
}
|
||||
const pathname = pathnameFromLegalId(lienTxt["@idtxt"])
|
||||
if (pathname === undefined) {
|
||||
return undefined
|
||||
}
|
||||
return {
|
||||
content: lienTxt["@titretxt"],
|
||||
href: pathname,
|
||||
type: "link",
|
||||
}
|
||||
}
|
||||
|
||||
export const summarizeVersionsWrapperProperties: Summarizer = (
|
||||
access,
|
||||
value,
|
||||
|
|
14
src/routes/api/recherche/+server.ts
Normal file
14
src/routes/api/recherche/+server.ts
Normal file
|
@ -0,0 +1,14 @@
|
|||
import { doGetRecherche } from "$lib/server/doers/recherche"
|
||||
|
||||
import type { RequestHandler } from "./$types"
|
||||
|
||||
export const GET: RequestHandler = async ({ url }) => {
|
||||
const result = await doGetRecherche(url)
|
||||
return result === null
|
||||
? new Response(null, { status: 204 })
|
||||
: new Response(JSON.stringify(result, null, 2), {
|
||||
headers: {
|
||||
"Content-Type": "application/json; charset=utf-8",
|
||||
},
|
||||
})
|
||||
}
|
|
@ -1,13 +1,13 @@
|
|||
import { type Audit, auditSetNullish, cleanAudit } from "@auditors/core"
|
||||
import { error } from "@sveltejs/kit"
|
||||
|
||||
import { auditSearchQueryContent } from "$lib/auditors/queries"
|
||||
import { auditSearchSearchParamsContent } from "$lib/auditors/search_params"
|
||||
import type { Article } from "$lib/legal"
|
||||
import { db } from "$lib/server/database"
|
||||
|
||||
import type { PageServerLoad } from "./$types"
|
||||
|
||||
export function auditQuery(
|
||||
export function auditSearchParams(
|
||||
audit: Audit,
|
||||
query: URLSearchParams,
|
||||
): [unknown, unknown] {
|
||||
|
@ -29,16 +29,16 @@ export function auditQuery(
|
|||
const errors: { [key: string]: unknown } = {}
|
||||
const remainingKeys = new Set(Object.keys(data))
|
||||
|
||||
auditSearchQueryContent(audit, data, errors, remainingKeys)
|
||||
auditSearchSearchParamsContent(audit, data, errors, remainingKeys)
|
||||
|
||||
return audit.reduceRemaining(data, errors, remainingKeys, auditSetNullish({}))
|
||||
}
|
||||
|
||||
export const load: PageServerLoad = async ({ url }) => {
|
||||
const [query, queryError] = auditQuery(cleanAudit, url.searchParams) as [
|
||||
{ limit: number; offset: number; q?: string },
|
||||
unknown,
|
||||
]
|
||||
const [query, queryError] = auditSearchParams(
|
||||
cleanAudit,
|
||||
url.searchParams,
|
||||
) as [{ limit: number; offset: number; q?: string }, unknown]
|
||||
if (queryError !== null) {
|
||||
console.error(
|
||||
`Error in ${url.pathname} query:\n${JSON.stringify(
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
import { type Audit, auditSetNullish, cleanAudit } from "@auditors/core"
|
||||
import { error } from "@sveltejs/kit"
|
||||
|
||||
import { auditSearchQueryContent } from "$lib/auditors/queries"
|
||||
import { auditSearchSearchParamsContent } from "$lib/auditors/search_params"
|
||||
import type { DossierLegislatif } from "$lib/legal"
|
||||
import { db } from "$lib/server/database"
|
||||
|
||||
import type { PageServerLoad } from "./$types"
|
||||
|
||||
export function auditQuery(
|
||||
export function auditSearchParams(
|
||||
audit: Audit,
|
||||
query: URLSearchParams,
|
||||
): [unknown, unknown] {
|
||||
|
@ -29,16 +29,16 @@ export function auditQuery(
|
|||
const errors: { [key: string]: unknown } = {}
|
||||
const remainingKeys = new Set(Object.keys(data))
|
||||
|
||||
auditSearchQueryContent(audit, data, errors, remainingKeys)
|
||||
auditSearchSearchParamsContent(audit, data, errors, remainingKeys)
|
||||
|
||||
return audit.reduceRemaining(data, errors, remainingKeys, auditSetNullish({}))
|
||||
}
|
||||
|
||||
export const load: PageServerLoad = async ({ url }) => {
|
||||
const [query, queryError] = auditQuery(cleanAudit, url.searchParams) as [
|
||||
{ limit: number; offset: number; q?: string },
|
||||
unknown,
|
||||
]
|
||||
const [query, queryError] = auditSearchParams(
|
||||
cleanAudit,
|
||||
url.searchParams,
|
||||
) as [{ limit: number; offset: number; q?: string }, unknown]
|
||||
if (queryError !== null) {
|
||||
console.error(
|
||||
`Error in ${url.pathname} query:\n${JSON.stringify(
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
import { type Audit, auditSetNullish, cleanAudit } from "@auditors/core"
|
||||
import { error } from "@sveltejs/kit"
|
||||
|
||||
import { auditSearchQueryContent } from "$lib/auditors/queries"
|
||||
import { auditSearchSearchParamsContent } from "$lib/auditors/search_params"
|
||||
import type { IdWrapper } from "$lib/legal"
|
||||
import { db } from "$lib/server/database"
|
||||
|
||||
import type { PageServerLoad } from "./$types"
|
||||
|
||||
export function auditQuery(
|
||||
export function auditSearchParams(
|
||||
audit: Audit,
|
||||
query: URLSearchParams,
|
||||
): [unknown, unknown] {
|
||||
|
@ -29,16 +29,16 @@ export function auditQuery(
|
|||
const errors: { [key: string]: unknown } = {}
|
||||
const remainingKeys = new Set(Object.keys(data))
|
||||
|
||||
auditSearchQueryContent(audit, data, errors, remainingKeys)
|
||||
auditSearchSearchParamsContent(audit, data, errors, remainingKeys)
|
||||
|
||||
return audit.reduceRemaining(data, errors, remainingKeys, auditSetNullish({}))
|
||||
}
|
||||
|
||||
export const load: PageServerLoad = async ({ url }) => {
|
||||
const [query, queryError] = auditQuery(cleanAudit, url.searchParams) as [
|
||||
{ limit: number; offset: number; q?: string },
|
||||
unknown,
|
||||
]
|
||||
const [query, queryError] = auditSearchParams(
|
||||
cleanAudit,
|
||||
url.searchParams,
|
||||
) as [{ limit: number; offset: number; q?: string }, unknown]
|
||||
if (queryError !== null) {
|
||||
console.error(
|
||||
`Error in ${url.pathname} query:\n${JSON.stringify(
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
import { type Audit, auditSetNullish, cleanAudit } from "@auditors/core"
|
||||
import { error } from "@sveltejs/kit"
|
||||
|
||||
import { auditSearchQueryContent } from "$lib/auditors/queries"
|
||||
import { auditSearchSearchParamsContent } from "$lib/auditors/search_params"
|
||||
import type { Idcc } from "$lib/legal"
|
||||
import { db } from "$lib/server/database"
|
||||
|
||||
import type { PageServerLoad } from "./$types"
|
||||
|
||||
export function auditQuery(
|
||||
export function auditSearchParams(
|
||||
audit: Audit,
|
||||
query: URLSearchParams,
|
||||
): [unknown, unknown] {
|
||||
|
@ -29,16 +29,16 @@ export function auditQuery(
|
|||
const errors: { [key: string]: unknown } = {}
|
||||
const remainingKeys = new Set(Object.keys(data))
|
||||
|
||||
auditSearchQueryContent(audit, data, errors, remainingKeys)
|
||||
auditSearchSearchParamsContent(audit, data, errors, remainingKeys)
|
||||
|
||||
return audit.reduceRemaining(data, errors, remainingKeys, auditSetNullish({}))
|
||||
}
|
||||
|
||||
export const load: PageServerLoad = async ({ url }) => {
|
||||
const [query, queryError] = auditQuery(cleanAudit, url.searchParams) as [
|
||||
{ limit: number; offset: number; q?: string },
|
||||
unknown,
|
||||
]
|
||||
const [query, queryError] = auditSearchParams(
|
||||
cleanAudit,
|
||||
url.searchParams,
|
||||
) as [{ limit: number; offset: number; q?: string }, unknown]
|
||||
if (queryError !== null) {
|
||||
console.error(
|
||||
`Error in ${url.pathname} query:\n${JSON.stringify(
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
import { type Audit, auditSetNullish, cleanAudit } from "@auditors/core"
|
||||
import { error } from "@sveltejs/kit"
|
||||
|
||||
import { auditSearchQueryContent } from "$lib/auditors/queries"
|
||||
import { auditSearchSearchParamsContent } from "$lib/auditors/search_params"
|
||||
import type { Jo } from "$lib/legal"
|
||||
import { db } from "$lib/server/database"
|
||||
|
||||
import type { PageServerLoad } from "./$types"
|
||||
|
||||
export function auditQuery(
|
||||
export function auditSearchParams(
|
||||
audit: Audit,
|
||||
query: URLSearchParams,
|
||||
): [unknown, unknown] {
|
||||
|
@ -29,16 +29,16 @@ export function auditQuery(
|
|||
const errors: { [key: string]: unknown } = {}
|
||||
const remainingKeys = new Set(Object.keys(data))
|
||||
|
||||
auditSearchQueryContent(audit, data, errors, remainingKeys)
|
||||
auditSearchSearchParamsContent(audit, data, errors, remainingKeys)
|
||||
|
||||
return audit.reduceRemaining(data, errors, remainingKeys, auditSetNullish({}))
|
||||
}
|
||||
|
||||
export const load: PageServerLoad = async ({ url }) => {
|
||||
const [query, queryError] = auditQuery(cleanAudit, url.searchParams) as [
|
||||
{ limit: number; offset: number; q?: string },
|
||||
unknown,
|
||||
]
|
||||
const [query, queryError] = auditSearchParams(
|
||||
cleanAudit,
|
||||
url.searchParams,
|
||||
) as [{ limit: number; offset: number; q?: string }, unknown]
|
||||
if (queryError !== null) {
|
||||
console.error(
|
||||
`Error in ${url.pathname} query:\n${JSON.stringify(
|
||||
|
|
|
@ -1,73 +1,7 @@
|
|||
import { type Audit, auditSetNullish, cleanAudit } from "@auditors/core"
|
||||
import { error } from "@sveltejs/kit"
|
||||
|
||||
import { auditSearchQueryContent } from "$lib/auditors/queries"
|
||||
import type { Article } from "$lib/legal"
|
||||
import { db } from "$lib/server/database"
|
||||
import { doGetRecherche } from "$lib/server/doers/recherche"
|
||||
|
||||
import type { PageServerLoad } from "./$types"
|
||||
|
||||
export function auditQuery(
|
||||
audit: Audit,
|
||||
query: URLSearchParams,
|
||||
): [unknown, unknown] {
|
||||
if (query == null) {
|
||||
return [query, null]
|
||||
}
|
||||
if (!(query instanceof URLSearchParams)) {
|
||||
return audit.unexpectedType(query, "URLSearchParams")
|
||||
}
|
||||
|
||||
const data: { [key: string]: unknown } = {}
|
||||
for (const [key, value] of query.entries()) {
|
||||
let values = data[key] as string[] | undefined
|
||||
if (values === undefined) {
|
||||
values = data[key] = []
|
||||
}
|
||||
values.push(value)
|
||||
}
|
||||
const errors: { [key: string]: unknown } = {}
|
||||
const remainingKeys = new Set(Object.keys(data))
|
||||
|
||||
auditSearchQueryContent(audit, data, errors, remainingKeys)
|
||||
|
||||
return audit.reduceRemaining(data, errors, remainingKeys, auditSetNullish({}))
|
||||
}
|
||||
|
||||
export const load: PageServerLoad = async ({ url }) => {
|
||||
const [query, queryError] = auditQuery(cleanAudit, url.searchParams) as [
|
||||
{ limit: number; offset: number; q?: string },
|
||||
unknown,
|
||||
]
|
||||
if (queryError !== null) {
|
||||
console.error(
|
||||
`Error in ${url.pathname} query:\n${JSON.stringify(
|
||||
query,
|
||||
null,
|
||||
2,
|
||||
)}\n\nError:\n${JSON.stringify(queryError, null, 2)}`,
|
||||
)
|
||||
throw error(400, JSON.stringify(queryError, null, 2))
|
||||
}
|
||||
const { limit, offset, q } = query
|
||||
|
||||
if (q !== undefined) {
|
||||
// https://www.legifrance.gouv.fr/codes/article_lc/LEGIARTI000006308296/
|
||||
// https://www.legifrance.gouv.fr/codes/article_lc/LEGIARTI000006308296/1983-12-30/
|
||||
// https://www.legifrance.gouv.fr/codes/article_lc/LEGIARTI000006308296/1984-12-30/
|
||||
// https://www.legifrance.gouv.fr/loda/article_lc/LEGIARTI000036456533
|
||||
// https://www.legifrance.gouv.fr/loda/article_lc/LEGIARTI000036456533/2018-01-01
|
||||
// https://www.legifrance.gouv.fr/loda/id/LEGIARTI000006317314/1983-12-30
|
||||
const articleId = q.match(/LEGIARTI\d+/)?.[0]
|
||||
if (articleId != null) {
|
||||
const articles = (
|
||||
await db<{ data: Article }[]>`
|
||||
SELECT data FROM article
|
||||
WHERE id = ${articleId}
|
||||
`
|
||||
).map(({ data }) => data)
|
||||
return { articles, q }
|
||||
}
|
||||
}
|
||||
return {}
|
||||
return (await doGetRecherche(url)) ?? {}
|
||||
}
|
||||
|
|
|
@ -10,8 +10,7 @@
|
|||
import type { PageData } from "./$types"
|
||||
|
||||
export let data: PageData
|
||||
|
||||
$: ({ articles, q } = data)
|
||||
let { articles, q } = data
|
||||
</script>
|
||||
|
||||
<header class="prose my-6 max-w-full">
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
import { type Audit, auditSetNullish, cleanAudit } from "@auditors/core"
|
||||
import { error } from "@sveltejs/kit"
|
||||
|
||||
import { auditSearchQueryContent } from "$lib/auditors/queries"
|
||||
import { auditSearchSearchParamsContent } from "$lib/auditors/search_params"
|
||||
import type { SectionTa } from "$lib/legal"
|
||||
import { db } from "$lib/server/database"
|
||||
|
||||
import type { PageServerLoad } from "./$types"
|
||||
|
||||
export function auditQuery(
|
||||
export function auditSearchParams(
|
||||
audit: Audit,
|
||||
query: URLSearchParams,
|
||||
): [unknown, unknown] {
|
||||
|
@ -29,16 +29,16 @@ export function auditQuery(
|
|||
const errors: { [key: string]: unknown } = {}
|
||||
const remainingKeys = new Set(Object.keys(data))
|
||||
|
||||
auditSearchQueryContent(audit, data, errors, remainingKeys)
|
||||
auditSearchSearchParamsContent(audit, data, errors, remainingKeys)
|
||||
|
||||
return audit.reduceRemaining(data, errors, remainingKeys, auditSetNullish({}))
|
||||
}
|
||||
|
||||
export const load: PageServerLoad = async ({ url }) => {
|
||||
const [query, queryError] = auditQuery(cleanAudit, url.searchParams) as [
|
||||
{ limit: number; offset: number; q?: string },
|
||||
unknown,
|
||||
]
|
||||
const [query, queryError] = auditSearchParams(
|
||||
cleanAudit,
|
||||
url.searchParams,
|
||||
) as [{ limit: number; offset: number; q?: string }, unknown]
|
||||
if (queryError !== null) {
|
||||
console.error(
|
||||
`Error in ${url.pathname} query:\n${JSON.stringify(
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
import { type Audit, auditSetNullish, cleanAudit } from "@auditors/core"
|
||||
import { error } from "@sveltejs/kit"
|
||||
|
||||
import { auditSearchQueryContent } from "$lib/auditors/queries"
|
||||
import { auditSearchSearchParamsContent } from "$lib/auditors/search_params"
|
||||
import type { TexteVersion } from "$lib/legal"
|
||||
import { db } from "$lib/server/database"
|
||||
|
||||
import type { PageServerLoad } from "./$types"
|
||||
|
||||
export function auditQuery(
|
||||
export function auditSearchParams(
|
||||
audit: Audit,
|
||||
query: URLSearchParams,
|
||||
): [unknown, unknown] {
|
||||
|
@ -29,16 +29,16 @@ export function auditQuery(
|
|||
const errors: { [key: string]: unknown } = {}
|
||||
const remainingKeys = new Set(Object.keys(data))
|
||||
|
||||
auditSearchQueryContent(audit, data, errors, remainingKeys)
|
||||
auditSearchSearchParamsContent(audit, data, errors, remainingKeys)
|
||||
|
||||
return audit.reduceRemaining(data, errors, remainingKeys, auditSetNullish({}))
|
||||
}
|
||||
|
||||
export const load: PageServerLoad = async ({ url }) => {
|
||||
const [query, queryError] = auditQuery(cleanAudit, url.searchParams) as [
|
||||
{ limit: number; offset: number; q?: string },
|
||||
unknown,
|
||||
]
|
||||
const [query, queryError] = auditSearchParams(
|
||||
cleanAudit,
|
||||
url.searchParams,
|
||||
) as [{ limit: number; offset: number; q?: string }, unknown]
|
||||
if (queryError !== null) {
|
||||
console.error(
|
||||
`Error in ${url.pathname} query:\n${JSON.stringify(
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
import { type Audit, auditSetNullish, cleanAudit } from "@auditors/core"
|
||||
import { error } from "@sveltejs/kit"
|
||||
|
||||
import { auditSearchQueryContent } from "$lib/auditors/queries"
|
||||
import { auditSearchSearchParamsContent } from "$lib/auditors/search_params"
|
||||
import type { Textekali } from "$lib/legal"
|
||||
import { db } from "$lib/server/database"
|
||||
|
||||
import type { PageServerLoad } from "./$types"
|
||||
|
||||
export function auditQuery(
|
||||
export function auditSearchParams(
|
||||
audit: Audit,
|
||||
query: URLSearchParams,
|
||||
): [unknown, unknown] {
|
||||
|
@ -29,16 +29,16 @@ export function auditQuery(
|
|||
const errors: { [key: string]: unknown } = {}
|
||||
const remainingKeys = new Set(Object.keys(data))
|
||||
|
||||
auditSearchQueryContent(audit, data, errors, remainingKeys)
|
||||
auditSearchSearchParamsContent(audit, data, errors, remainingKeys)
|
||||
|
||||
return audit.reduceRemaining(data, errors, remainingKeys, auditSetNullish({}))
|
||||
}
|
||||
|
||||
export const load: PageServerLoad = async ({ url }) => {
|
||||
const [query, queryError] = auditQuery(cleanAudit, url.searchParams) as [
|
||||
{ limit: number; offset: number; q?: string },
|
||||
unknown,
|
||||
]
|
||||
const [query, queryError] = auditSearchParams(
|
||||
cleanAudit,
|
||||
url.searchParams,
|
||||
) as [{ limit: number; offset: number; q?: string }, unknown]
|
||||
if (queryError !== null) {
|
||||
console.error(
|
||||
`Error in ${url.pathname} query:\n${JSON.stringify(
|
||||
|
|
|
@ -1,8 +1,4 @@
|
|||
<script lang="ts">
|
||||
throw new Error(
|
||||
"@migration task: Add data prop (https://github.com/sveltejs/kit/discussions/5774#discussioncomment-3292707)",
|
||||
)
|
||||
|
||||
// import Icon from "@iconify/svelte"
|
||||
// import searchIcon from "@iconify-icons/codicon/search"
|
||||
import { TreeView } from "augmented-data-viewer"
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
import { type Audit, auditSetNullish, cleanAudit } from "@auditors/core"
|
||||
import { error } from "@sveltejs/kit"
|
||||
|
||||
import { auditSearchQueryContent } from "$lib/auditors/queries"
|
||||
import { auditSearchSearchParamsContent } from "$lib/auditors/search_params"
|
||||
import type { Textelr } from "$lib/legal"
|
||||
import { db } from "$lib/server/database"
|
||||
|
||||
import type { PageServerLoad } from "./$types"
|
||||
|
||||
export function auditQuery(
|
||||
export function auditSearchParams(
|
||||
audit: Audit,
|
||||
query: URLSearchParams,
|
||||
): [unknown, unknown] {
|
||||
|
@ -29,16 +29,16 @@ export function auditQuery(
|
|||
const errors: { [key: string]: unknown } = {}
|
||||
const remainingKeys = new Set(Object.keys(data))
|
||||
|
||||
auditSearchQueryContent(audit, data, errors, remainingKeys)
|
||||
auditSearchSearchParamsContent(audit, data, errors, remainingKeys)
|
||||
|
||||
return audit.reduceRemaining(data, errors, remainingKeys, auditSetNullish({}))
|
||||
}
|
||||
|
||||
export const load: PageServerLoad = async ({ url }) => {
|
||||
const [query, queryError] = auditQuery(cleanAudit, url.searchParams) as [
|
||||
{ limit: number; offset: number; q?: string },
|
||||
unknown,
|
||||
]
|
||||
const [query, queryError] = auditSearchParams(
|
||||
cleanAudit,
|
||||
url.searchParams,
|
||||
) as [{ limit: number; offset: number; q?: string }, unknown]
|
||||
if (queryError !== null) {
|
||||
console.error(
|
||||
`Error in ${url.pathname} query:\n${JSON.stringify(
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
import { type Audit, auditSetNullish, cleanAudit } from "@auditors/core"
|
||||
import { error } from "@sveltejs/kit"
|
||||
|
||||
import { auditSearchQueryContent } from "$lib/auditors/queries"
|
||||
import { auditSearchSearchParamsContent } from "$lib/auditors/search_params"
|
||||
import type { VersionsWrapper } from "$lib/legal"
|
||||
import { db } from "$lib/server/database"
|
||||
|
||||
import type { PageServerLoad } from "./$types"
|
||||
|
||||
export function auditQuery(
|
||||
export function auditSearchParams(
|
||||
audit: Audit,
|
||||
query: URLSearchParams,
|
||||
): [unknown, unknown] {
|
||||
|
@ -29,16 +29,16 @@ export function auditQuery(
|
|||
const errors: { [key: string]: unknown } = {}
|
||||
const remainingKeys = new Set(Object.keys(data))
|
||||
|
||||
auditSearchQueryContent(audit, data, errors, remainingKeys)
|
||||
auditSearchSearchParamsContent(audit, data, errors, remainingKeys)
|
||||
|
||||
return audit.reduceRemaining(data, errors, remainingKeys, auditSetNullish({}))
|
||||
}
|
||||
|
||||
export const load: PageServerLoad = async ({ url }) => {
|
||||
const [query, queryError] = auditQuery(cleanAudit, url.searchParams) as [
|
||||
{ limit: number; offset: number; q?: string },
|
||||
unknown,
|
||||
]
|
||||
const [query, queryError] = auditSearchParams(
|
||||
cleanAudit,
|
||||
url.searchParams,
|
||||
) as [{ limit: number; offset: number; q?: string }, unknown]
|
||||
if (queryError !== null) {
|
||||
console.error(
|
||||
`Error in ${url.pathname} query:\n${JSON.stringify(
|
||||
|
|
|
@ -1,8 +1,16 @@
|
|||
import { sveltekit } from "@sveltejs/kit/vite"
|
||||
import morgan from "morgan"
|
||||
|
||||
const middlewaresPlugin = {
|
||||
name: "tricoteuses-middlewares",
|
||||
async configureServer(server) {
|
||||
server.middlewares.use(morgan("dev"))
|
||||
},
|
||||
}
|
||||
|
||||
/** @type {import('vite').UserConfig} */
|
||||
const config = {
|
||||
plugins: [sveltekit()],
|
||||
plugins: [sveltekit(), middlewaresPlugin],
|
||||
}
|
||||
|
||||
export default config
|
||||
|
|
Loading…
Reference in a new issue