Add search API. Improve summaries.

This commit is contained in:
Emmanuel 2022-08-23 18:05:19 +02:00
parent 4421108b02
commit 72399dfb07
27 changed files with 1063 additions and 278 deletions

186
package-lock.json generated
View file

@ -14,10 +14,10 @@
"@iconify/svelte": "^2.2.1", "@iconify/svelte": "^2.2.1",
"@playwright/test": "^1.22.2", "@playwright/test": "^1.22.2",
"@sveltejs/adapter-node": "^1.0.0-next.86", "@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", "@sveltejs/package": "^1.0.0-next.1",
"@tailwindcss/typography": "^0.5.3", "@tailwindcss/typography": "^0.5.3",
"@tricoteuses/explorer-tools": "^0.1.9", "@tricoteuses/explorer-tools": "^0.1.12",
"@types/fs-extra": "^9.0.13", "@types/fs-extra": "^9.0.13",
"@types/he": "^1.1.2", "@types/he": "^1.1.2",
"@typescript-eslint/eslint-plugin": "^5.27.0", "@typescript-eslint/eslint-plugin": "^5.27.0",
@ -32,6 +32,7 @@
"fast-xml-parser": "^4.0.9", "fast-xml-parser": "^4.0.9",
"fs-extra": "^10.1.0", "fs-extra": "^10.1.0",
"he": "^1.2.0", "he": "^1.2.0",
"morgan": "^1.10.0",
"postcss": "^8.4.14", "postcss": "^8.4.14",
"postcss-load-config": "^4.0.1", "postcss-load-config": "^4.0.1",
"postgres": "^3.2.4", "postgres": "^3.2.4",
@ -250,9 +251,9 @@
"dev": true "dev": true
}, },
"node_modules/@sveltejs/kit": { "node_modules/@sveltejs/kit": {
"version": "1.0.0-next.428", "version": "1.0.0-next.430",
"resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-1.0.0-next.428.tgz", "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-1.0.0-next.430.tgz",
"integrity": "sha512-CVp7zltQ+3VOII1s7KuvgvGEFwD0PSqts9R3q4eaj0CNoC3gpdzcbEZayyQwXX/3lI1HdSNSF8gxbX0TRUoFuA==", "integrity": "sha512-0NL6DfFWASDkvjL6iCr3pO/u1oqvoF75lvnO6QTB0rIHMYOL+l9BAkxVETnNxzvgLRFCECi6NSh2C0bdaYaM1g==",
"dev": true, "dev": true,
"hasInstallScript": true, "hasInstallScript": true,
"dependencies": { "dependencies": {
@ -343,9 +344,9 @@
} }
}, },
"node_modules/@tricoteuses/explorer-tools": { "node_modules/@tricoteuses/explorer-tools": {
"version": "0.1.9", "version": "0.1.12",
"resolved": "https://registry.npmjs.org/@tricoteuses/explorer-tools/-/explorer-tools-0.1.9.tgz", "resolved": "https://registry.npmjs.org/@tricoteuses/explorer-tools/-/explorer-tools-0.1.12.tgz",
"integrity": "sha512-qMeJdl4GXNShCHnCRZGLogmAie/TFEEC6BiKBasK4NqewWIyFSCmO8tWKRaDdXU7US641lS0ZRHFZwcVWkO6mA==", "integrity": "sha512-Aachs9YOz1R624H3gl1WuMkdbqhBPmjKIogfO3iukjVH4MBZ7g1jN2a8q4mRj0c9v82fwiKKBdcCz0FLi4S8Cw==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@iconify-icons/codicon": "^1.2.15", "@iconify-icons/codicon": "^1.2.15",
@ -755,6 +756,18 @@
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
"dev": true "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": { "node_modules/binary-extensions": {
"version": "2.2.0", "version": "2.2.0",
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
@ -1073,6 +1086,15 @@
"integrity": "sha512-Y2caI5+ZwS5c3RiNDJ6u53VhQHv+hHKwhkI1iHvceKUHw9Df6EK2zRLfjejRgMuCuxK7PfSWIMwWecceVvThjQ==", "integrity": "sha512-Y2caI5+ZwS5c3RiNDJ6u53VhQHv+hHKwhkI1iHvceKUHw9Df6EK2zRLfjejRgMuCuxK7PfSWIMwWecceVvThjQ==",
"dev": true "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": { "node_modules/detect-indent": {
"version": "6.1.0", "version": "6.1.0",
"resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz", "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz",
@ -1150,6 +1172,12 @@
"node": ">=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": { "node_modules/electron-to-chromium": {
"version": "1.4.225", "version": "1.4.225",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.225.tgz", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.225.tgz",
@ -2444,6 +2472,37 @@
"mkdirp": "bin/cmd.js" "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": { "node_modules/mri": {
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz",
@ -2566,6 +2625,27 @@
"node": ">= 6" "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": { "node_modules/once": {
"version": "1.4.0", "version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
@ -3083,6 +3163,12 @@
"node": ">=6" "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": { "node_modules/sander": {
"version": "0.5.1", "version": "0.5.1",
"resolved": "https://registry.npmjs.org/sander/-/sander-0.5.1.tgz", "resolved": "https://registry.npmjs.org/sander/-/sander-0.5.1.tgz",
@ -3926,9 +4012,9 @@
"dev": true "dev": true
}, },
"@sveltejs/kit": { "@sveltejs/kit": {
"version": "1.0.0-next.428", "version": "1.0.0-next.430",
"resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-1.0.0-next.428.tgz", "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-1.0.0-next.430.tgz",
"integrity": "sha512-CVp7zltQ+3VOII1s7KuvgvGEFwD0PSqts9R3q4eaj0CNoC3gpdzcbEZayyQwXX/3lI1HdSNSF8gxbX0TRUoFuA==", "integrity": "sha512-0NL6DfFWASDkvjL6iCr3pO/u1oqvoF75lvnO6QTB0rIHMYOL+l9BAkxVETnNxzvgLRFCECi6NSh2C0bdaYaM1g==",
"dev": true, "dev": true,
"requires": { "requires": {
"@sveltejs/vite-plugin-svelte": "^1.0.1", "@sveltejs/vite-plugin-svelte": "^1.0.1",
@ -3983,9 +4069,9 @@
} }
}, },
"@tricoteuses/explorer-tools": { "@tricoteuses/explorer-tools": {
"version": "0.1.9", "version": "0.1.12",
"resolved": "https://registry.npmjs.org/@tricoteuses/explorer-tools/-/explorer-tools-0.1.9.tgz", "resolved": "https://registry.npmjs.org/@tricoteuses/explorer-tools/-/explorer-tools-0.1.12.tgz",
"integrity": "sha512-qMeJdl4GXNShCHnCRZGLogmAie/TFEEC6BiKBasK4NqewWIyFSCmO8tWKRaDdXU7US641lS0ZRHFZwcVWkO6mA==", "integrity": "sha512-Aachs9YOz1R624H3gl1WuMkdbqhBPmjKIogfO3iukjVH4MBZ7g1jN2a8q4mRj0c9v82fwiKKBdcCz0FLi4S8Cw==",
"dev": true, "dev": true,
"requires": { "requires": {
"@iconify-icons/codicon": "^1.2.15", "@iconify-icons/codicon": "^1.2.15",
@ -4253,6 +4339,15 @@
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
"dev": true "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": { "binary-extensions": {
"version": "2.2.0", "version": "2.2.0",
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
@ -4476,6 +4571,12 @@
"integrity": "sha512-Y2caI5+ZwS5c3RiNDJ6u53VhQHv+hHKwhkI1iHvceKUHw9Df6EK2zRLfjejRgMuCuxK7PfSWIMwWecceVvThjQ==", "integrity": "sha512-Y2caI5+ZwS5c3RiNDJ6u53VhQHv+hHKwhkI1iHvceKUHw9Df6EK2zRLfjejRgMuCuxK7PfSWIMwWecceVvThjQ==",
"dev": true "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": { "detect-indent": {
"version": "6.1.0", "version": "6.1.0",
"resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz", "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz",
@ -4535,6 +4636,12 @@
"integrity": "sha512-1K6hR6wtk2FviQ4kEiSjFiH5rpzEVi8WW0x96aztHVMhEspNpc4DVOUTEHtEva5VThQ8IaBX1Pe4gSzpVVUsKQ==", "integrity": "sha512-1K6hR6wtk2FviQ4kEiSjFiH5rpzEVi8WW0x96aztHVMhEspNpc4DVOUTEHtEva5VThQ8IaBX1Pe4gSzpVVUsKQ==",
"dev": true "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": { "electron-to-chromium": {
"version": "1.4.225", "version": "1.4.225",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.225.tgz", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.225.tgz",
@ -5413,6 +5520,36 @@
"minimist": "^1.2.6" "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": { "mri": {
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz",
@ -5494,6 +5631,21 @@
"integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==",
"dev": true "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": { "once": {
"version": "1.4.0", "version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
@ -5807,6 +5959,12 @@
"mri": "^1.1.0" "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": { "sander": {
"version": "0.5.1", "version": "0.5.1",
"resolved": "https://registry.npmjs.org/sander/-/sander-0.5.1.tgz", "resolved": "https://registry.npmjs.org/sander/-/sander-0.5.1.tgz",

View file

@ -27,10 +27,10 @@
"@iconify/svelte": "^2.2.1", "@iconify/svelte": "^2.2.1",
"@playwright/test": "^1.22.2", "@playwright/test": "^1.22.2",
"@sveltejs/adapter-node": "^1.0.0-next.86", "@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", "@sveltejs/package": "^1.0.0-next.1",
"@tailwindcss/typography": "^0.5.3", "@tailwindcss/typography": "^0.5.3",
"@tricoteuses/explorer-tools": "^0.1.9", "@tricoteuses/explorer-tools": "^0.1.12",
"@types/fs-extra": "^9.0.13", "@types/fs-extra": "^9.0.13",
"@types/he": "^1.1.2", "@types/he": "^1.1.2",
"@typescript-eslint/eslint-plugin": "^5.27.0", "@typescript-eslint/eslint-plugin": "^5.27.0",
@ -45,6 +45,7 @@
"fast-xml-parser": "^4.0.9", "fast-xml-parser": "^4.0.9",
"fs-extra": "^10.1.0", "fs-extra": "^10.1.0",
"he": "^1.2.0", "he": "^1.2.0",
"morgan": "^1.10.0",
"postcss": "^8.4.14", "postcss": "^8.4.14",
"postcss-load-config": "^4.0.1", "postcss-load-config": "^4.0.1",
"postgres": "^3.2.4", "postgres": "^3.2.4",

6
src/lib/aggregates.ts Normal file
View 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]

View file

@ -2,43 +2,50 @@ import type { Audit, Auditor } from "@auditors/core"
import { import {
auditArray, auditArray,
auditChain, auditChain,
auditCleanArray,
auditFunction, auditFunction,
auditInteger, auditInteger,
auditNullish,
auditOptions,
auditSetNullish, auditSetNullish,
auditStringToNumber, auditStringToNumber,
auditSwitch,
auditTest, auditTest,
auditTrimString, auditTrimString,
auditUnique,
} from "@auditors/core" } from "@auditors/core"
export function auditPaginationQuery( import { allFollowsMutable } from "$lib/aggregates"
export function auditFollowSearchParams(
audit: Audit, audit: Audit,
query: URLSearchParams, data: { [key: string]: unknown },
): [unknown, unknown] { errors: { [key: string]: unknown },
if (query == null) { remainingKeys: Set<string>,
return [query, null] ): void {
} audit.attribute(
if (!(query instanceof URLSearchParams)) { data,
return audit.unexpectedType(query, "URLSearchParams") "follow",
} true,
errors,
const data: { [key: string]: unknown } = {} remainingKeys,
for (const [key, value] of query.entries()) { auditSearchParamsOptionsSet(allFollowsMutable),
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))
export function auditLimitSearchParam(
audit: Audit,
data: { [key: string]: unknown },
errors: { [key: string]: unknown },
remainingKeys: Set<string>,
): void {
audit.attribute( audit.attribute(
data, data,
"limit", "limit",
true, true,
errors, errors,
remainingKeys, remainingKeys,
auditQuerySingleton( auditSingleton(
auditTrimString, auditTrimString,
auditStringToNumber, auditStringToNumber,
auditInteger, auditInteger,
@ -53,13 +60,21 @@ export function auditPaginationQuery(
auditSetNullish(10), auditSetNullish(10),
), ),
) )
}
export function auditOffsetSearchParam(
audit: Audit,
data: { [key: string]: unknown },
errors: { [key: string]: unknown },
remainingKeys: Set<string>,
): void {
audit.attribute( audit.attribute(
data, data,
"offset", "offset",
true, true,
errors, errors,
remainingKeys, remainingKeys,
auditQuerySingleton( auditSingleton(
auditTrimString, auditTrimString,
auditStringToNumber, auditStringToNumber,
auditInteger, auditInteger,
@ -70,26 +85,9 @@ export function auditPaginationQuery(
auditSetNullish(0), auditSetNullish(0),
), ),
) )
// Keep the remaining keys as is.
remainingKeys.clear()
return audit.reduceRemaining(data, errors, remainingKeys, auditSetNullish({}))
} }
export function auditQuerySingleton(...auditors: Auditor[]) { export function auditPaginationSearchParams(
return auditChain(
auditArray(),
auditTest(
(values) => values.length <= 1,
"Parameter must be present only once in query",
),
auditFunction((value) => value[0]),
...auditors,
)
}
export function auditSearchQuery(
audit: Audit, audit: Audit,
query: URLSearchParams, query: URLSearchParams,
): [unknown, unknown] { ): [unknown, unknown] {
@ -111,61 +109,110 @@ export function auditSearchQuery(
const errors: { [key: string]: unknown } = {} const errors: { [key: string]: unknown } = {}
const remainingKeys = new Set(Object.keys(data)) 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({})) return audit.reduceRemaining(data, errors, remainingKeys, auditSetNullish({}))
} }
export function auditSearchQueryContent( export function auditQSearchParam(
audit: Audit, audit: Audit,
data: { [key: string]: unknown }, data: { [key: string]: unknown },
errors: { [key: string]: unknown }, errors: { [key: string]: unknown },
remainingKeys: Set<string>, remainingKeys: Set<string>,
) { ): void {
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),
),
)
audit.attribute( audit.attribute(
data, data,
"q", "q",
true, true,
errors, errors,
remainingKeys, 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,
) )
} }

View file

@ -2,7 +2,7 @@
import { cleanAudit } from "@auditors/core" import { cleanAudit } from "@auditors/core"
import { page } from "$app/stores" import { page } from "$app/stores"
import { auditPaginationQuery } from "$lib/auditors/queries" import { auditPaginationSearchParams } from "$lib/auditors/search_params"
import type { PaginationQuery } from "$lib/queries" import type { PaginationQuery } from "$lib/queries"
export let count: number | undefined | null = undefined // Count is not always known. export let count: number | undefined | null = undefined // Count is not always known.
@ -19,7 +19,7 @@
$: pathname = url.pathname $: pathname = url.pathname
function ensureValidQuery(query: URLSearchParams): PaginationQuery { function ensureValidQuery(query: URLSearchParams): PaginationQuery {
const [validQuery, queryError] = auditPaginationQuery( const [validQuery, queryError] = auditPaginationSearchParams(
cleanAudit, cleanAudit,
query, query,
) as [PaginationQuery, unknown] ) as [PaginationQuery, unknown]

View file

@ -13,20 +13,37 @@ export interface Article {
} }
} }
} }
NOTA: {
CONTENU: string
}
LIENS?: {
LIEN: Lien | Lien
}
CONTEXTE: Contexte CONTEXTE: Contexte
VERSIONS: { VERSIONS: {
VERSION: Array<{ VERSION: ArticleVersion | ArticleVersion[]
"@etat": Etat
LIEN_ART: LienArt
}>
} }
BLOC_TEXTUEL: { BLOC_TEXTUEL: {
CONTENU: string // HTML CONTENU: string // HTML
} }
} }
export interface ArticleVersion {
"@etat": Etat
LIEN_ART: LienArt
}
export interface Contexte { export interface Contexte {
TEXTE: { 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[] 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 { export interface Idcc {
META: { META: {
@ -72,6 +89,9 @@ export interface Jo {
} }
META_COMMUN: MetaCommun META_COMMUN: MetaCommun
} }
STRUCTURE_TXT: {
TM: Tm
}
} }
export type LegalObject = export type LegalObject =
@ -98,6 +118,19 @@ export type LegalObjectType =
| "textelr" | "textelr"
| "versions" | "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 { export interface LienArt {
"@id": string "@id": string
"@fin": string "@fin": string
@ -111,30 +144,35 @@ export interface MetaCommun {
ID: string 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 { export interface SectionTa {
ID: string ID: string
CONTEXTE: Contexte CONTEXTE: Contexte
TITRE_TA: string TITRE_TA: string
STRUCTURE_TA: { LIEN_ART?: LienArt | LienArt[] } STRUCTURE_TA: {
LIEN_ART?: LienArt | LienArt[]
}
} }
export interface Textekali { export interface Textekali {
META: { META: {
META_COMMUN: MetaCommun META_COMMUN: MetaCommun
META_SPEC: { META_SPEC: {
META_TEXTE_CHRONICLE: { META_TEXTE_CHRONICLE: MetaTexteChronicle
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
}
} }
} }
} }
@ -142,12 +180,37 @@ export interface Textekali {
export interface Textelr { export interface Textelr {
META: { META: {
META_COMMUN: MetaCommun 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 { export interface TexteVersion {
ABRO?: {
CONTENU: string
}
META: { META: {
META_SPEC: { META_SPEC: {
META_TEXTE_CHRONICLE: MetaTexteChronicle
META_TEXTE_VERSION: { META_TEXTE_VERSION: {
TITRE: string TITRE: string
TITREFULL: string TITREFULL: string
@ -155,6 +218,34 @@ export interface TexteVersion {
} }
META_COMMUN: MetaCommun 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 { export interface TitreTxt {
@ -165,6 +256,19 @@ export interface TitreTxt {
"#text": string "#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 { export interface Versions {
VERSION: { VERSION: {
"@id": string "@id": string
@ -186,6 +290,15 @@ export interface XmlHeader {
export const appMenu: MenuItem[] = [ export const appMenu: MenuItem[] = [
{ href: "/recherche", label: "Recherche" }, { 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: [ items: [
{ href: "/article", label: "ARTICLE" }, { href: "/article", label: "ARTICLE" },
@ -242,6 +355,38 @@ export function bestItemForDate<T extends { "@debut": string; "@fin": string }>(
return items[0] 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( export function pathnameFromLegalObject(
type: LegalObjectType, type: LegalObjectType,
object: LegalObject, object: LegalObject,

View file

@ -23,7 +23,19 @@ import { walkDir } from "$lib/server/file_systems"
const xmlParser = new XMLParser({ const xmlParser = new XMLParser({
attributeNamePrefix: "@", attributeNamePrefix: "@",
ignoreAttributes: false, 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), tagValueProcessor: (_tagName, tagValue) => he.decode(tagValue),
}) })

View file

@ -22,7 +22,19 @@ import { walkDir } from "$lib/server/file_systems"
const xmlParser = new XMLParser({ const xmlParser = new XMLParser({
attributeNamePrefix: "@", attributeNamePrefix: "@",
ignoreAttributes: false, 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), tagValueProcessor: (_tagName, tagValue) => he.decode(tagValue),
}) })

View file

@ -24,11 +24,14 @@ const xmlParser = new XMLParser({
ignoreAttributes: false, ignoreAttributes: false,
stopNodes: [ stopNodes: [
"ARTICLE.BLOC_TEXTUEL.CONTENU", "ARTICLE.BLOC_TEXTUEL.CONTENU",
"ARTICLE.NOTA.CONTENU",
"ARTICLE.SM.CONTENU",
"TEXTE_VERSION.ABRO.CONTENU", "TEXTE_VERSION.ABRO.CONTENU",
"TEXTE_VERSION.CONTENU",
"TEXTE_VERSION.NOTA.CONTENU", "TEXTE_VERSION.NOTA.CONTENU",
"TEXTE_VERSION.NOTICE.CONTENU",
"TEXTE_VERSION.RECT.CONTENU", "TEXTE_VERSION.RECT.CONTENU",
"TEXTE_VERSION.SIGNATAIRES.CONTENU", "TEXTE_VERSION.SIGNATAIRES.CONTENU",
"TEXTE_VERSION.SM.CONTENU",
"TEXTE_VERSION.TP.CONTENU", "TEXTE_VERSION.TP.CONTENU",
"TEXTE_VERSION.VISAS.CONTENU", "TEXTE_VERSION.VISAS.CONTENU",
], ],

View 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
}
}

View 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
}

View file

@ -3,22 +3,30 @@ import type { Access, Summarizer, Summary } from "augmented-data-viewer"
import { import {
type Article, type Article,
type ArticleVersion,
assertNeverLegalObjectType, assertNeverLegalObjectType,
bestItemForDate, bestItemForDate,
type DossierLegislatif,
type Idcc, type Idcc,
type IdWrapper, type IdWrapper,
type Jo, type Jo,
type LegalObject, type LegalObject,
type LegalObjectType, type LegalObjectType,
type Lien,
type LienArt,
pathnameFromLegalId,
pathnameFromLegalObject, pathnameFromLegalObject,
pathnameFromLegalObjectId, pathnameFromLegalObjectId,
type SectionTa, type SectionTa,
type Textekali, type Textekali,
type Textelr, type Textelr,
type TextelrVersionLienTxt,
type TexteVersion, type TexteVersion,
type LienArt, type TitreTxt,
type TitreTm,
type Tm,
type TmLienTxt,
type VersionsWrapper, type VersionsWrapper,
type DossierLegislatif,
} from "$lib/legal" } from "$lib/legal"
export const summarizeArticleProperties: Summarizer = (access, value) => { export const summarizeArticleProperties: Summarizer = (access, value) => {
@ -29,6 +37,25 @@ export const summarizeArticleProperties: Summarizer = (access, value) => {
return summarizeLegalObjectToLink(access, "article", 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 ( if (
access?.key === "@id" && access?.key === "@id" &&
(access?.access?.key === "LIEN_ART" || (access?.access?.key === "LIEN_ART" ||
@ -37,17 +64,39 @@ export const summarizeArticleProperties: Summarizer = (access, value) => {
) { ) {
return summarizeLienArtId(access.access, access.parent) 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") { if (access?.key === "CONTENU") {
return { content: value as string, type: "html" } return { content: value as string, type: "html" }
} }
if (access?.key === "LIEN_ART" && !Array.isArray(value)) { if (
return summarizeLienArt(access, value) (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) return summarizeLienArt(access, value)
} }
if (access?.access?.key === "VERSION") { if (access?.access?.key === "VERSION") {
const version = value as Article["VERSIONS"]["VERSION"][0] const version = value as ArticleVersion
const lienArt = version.LIEN_ART const lienArt = version.LIEN_ART
return { return {
content: { content: {
@ -139,9 +188,61 @@ export const summarizeJoProperties: Summarizer = (access, value) => {
return summarizeLegalObjectToLink(access, "jo", 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 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( export function summarizeLegalObject(
access: Access | undefined, access: Access | undefined,
type: LegalObjectType, 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) => { export const summarizeLienArt: Summarizer = (access, value) => {
const lienArt = value as LienArt | undefined const lienArt = value as LienArt | undefined
if (lienArt === undefined) { if (lienArt === undefined) {
@ -273,13 +390,39 @@ export const summarizeLienArtId: Summarizer = (access, value) => {
content: JSON.stringify(lienArt["@id"]), content: JSON.stringify(lienArt["@id"]),
type: "raw_data", 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)!, summarizeLienArt(access, lienArt)!,
], ],
type: "concatenation", 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) => { export const summarizeSectionTaProperties: Summarizer = (access, value) => {
if (access?.key === "section_ta" && typeof value !== "number") { if (access?.key === "section_ta" && typeof value !== "number") {
return summarizeLegalObject(access, "section_ta", value) return summarizeLegalObject(access, "section_ta", value)
@ -288,6 +431,9 @@ export const summarizeSectionTaProperties: Summarizer = (access, value) => {
return summarizeLegalObjectToLink(access, "section_ta", value) return summarizeLegalObjectToLink(access, "section_ta", value)
} }
if (access?.key === "@cid" && access?.access?.key === "TEXTE") {
return summarizeLegalIdToLink(access, value)
}
if ( if (
access?.key === "@id" && access?.key === "@id" &&
(access?.access?.key === "LIEN_ART" || (access?.access?.key === "LIEN_ART" ||
@ -296,10 +442,10 @@ export const summarizeSectionTaProperties: Summarizer = (access, value) => {
) { ) {
return summarizeLienArtId(access.access, access.parent) return summarizeLienArtId(access.access, access.parent)
} }
if (access?.key === "LIEN_ART" && !Array.isArray(value)) { if (
return summarizeLienArt(access, value) (access?.key === "LIEN_ART" && !Array.isArray(value)) ||
} (typeof access?.key === "number" && access?.access?.key === "LIEN_ART")
if (typeof access?.key === "number" && access?.access?.key === "LIEN_ART") { ) {
return summarizeLienArt(access, value) return summarizeLienArt(access, value)
} }
return undefined return undefined
@ -313,9 +459,29 @@ export const summarizeTextekaliProperties: Summarizer = (access, value) => {
return summarizeLegalObjectToLink(access, "textekali", value) return summarizeLegalObjectToLink(access, "textekali", value)
} }
if (access?.key === "CID") {
return summarizeLegalIdToLink(access, value)
}
return undefined 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) => { export const summarizeTextelrProperties: Summarizer = (access, value) => {
if (access?.key === "textelr" && typeof value !== "number") { if (access?.key === "textelr" && typeof value !== "number") {
return summarizeLegalObject(access, "textelr", value) return summarizeLegalObject(access, "textelr", value)
@ -324,6 +490,27 @@ export const summarizeTextelrProperties: Summarizer = (access, value) => {
return summarizeLegalObjectToLink(access, "textelr", 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 return undefined
} }
@ -338,9 +525,95 @@ export const summarizeTexteVersionProperties: Summarizer = (access, value) => {
return summarizeLegalObjectToLink(access, "texte_version", 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 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 = ( export const summarizeVersionsWrapperProperties: Summarizer = (
access, access,
value, value,

View 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",
},
})
}

View file

@ -1,13 +1,13 @@
import { type Audit, auditSetNullish, cleanAudit } from "@auditors/core" import { type Audit, auditSetNullish, cleanAudit } from "@auditors/core"
import { error } from "@sveltejs/kit" 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 type { Article } from "$lib/legal"
import { db } from "$lib/server/database" import { db } from "$lib/server/database"
import type { PageServerLoad } from "./$types" import type { PageServerLoad } from "./$types"
export function auditQuery( export function auditSearchParams(
audit: Audit, audit: Audit,
query: URLSearchParams, query: URLSearchParams,
): [unknown, unknown] { ): [unknown, unknown] {
@ -29,16 +29,16 @@ export function auditQuery(
const errors: { [key: string]: unknown } = {} const errors: { [key: string]: unknown } = {}
const remainingKeys = new Set(Object.keys(data)) const remainingKeys = new Set(Object.keys(data))
auditSearchQueryContent(audit, data, errors, remainingKeys) auditSearchSearchParamsContent(audit, data, errors, remainingKeys)
return audit.reduceRemaining(data, errors, remainingKeys, auditSetNullish({})) return audit.reduceRemaining(data, errors, remainingKeys, auditSetNullish({}))
} }
export const load: PageServerLoad = async ({ url }) => { export const load: PageServerLoad = async ({ url }) => {
const [query, queryError] = auditQuery(cleanAudit, url.searchParams) as [ const [query, queryError] = auditSearchParams(
{ limit: number; offset: number; q?: string }, cleanAudit,
unknown, url.searchParams,
] ) as [{ limit: number; offset: number; q?: string }, unknown]
if (queryError !== null) { if (queryError !== null) {
console.error( console.error(
`Error in ${url.pathname} query:\n${JSON.stringify( `Error in ${url.pathname} query:\n${JSON.stringify(

View file

@ -1,13 +1,13 @@
import { type Audit, auditSetNullish, cleanAudit } from "@auditors/core" import { type Audit, auditSetNullish, cleanAudit } from "@auditors/core"
import { error } from "@sveltejs/kit" 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 type { DossierLegislatif } from "$lib/legal"
import { db } from "$lib/server/database" import { db } from "$lib/server/database"
import type { PageServerLoad } from "./$types" import type { PageServerLoad } from "./$types"
export function auditQuery( export function auditSearchParams(
audit: Audit, audit: Audit,
query: URLSearchParams, query: URLSearchParams,
): [unknown, unknown] { ): [unknown, unknown] {
@ -29,16 +29,16 @@ export function auditQuery(
const errors: { [key: string]: unknown } = {} const errors: { [key: string]: unknown } = {}
const remainingKeys = new Set(Object.keys(data)) const remainingKeys = new Set(Object.keys(data))
auditSearchQueryContent(audit, data, errors, remainingKeys) auditSearchSearchParamsContent(audit, data, errors, remainingKeys)
return audit.reduceRemaining(data, errors, remainingKeys, auditSetNullish({})) return audit.reduceRemaining(data, errors, remainingKeys, auditSetNullish({}))
} }
export const load: PageServerLoad = async ({ url }) => { export const load: PageServerLoad = async ({ url }) => {
const [query, queryError] = auditQuery(cleanAudit, url.searchParams) as [ const [query, queryError] = auditSearchParams(
{ limit: number; offset: number; q?: string }, cleanAudit,
unknown, url.searchParams,
] ) as [{ limit: number; offset: number; q?: string }, unknown]
if (queryError !== null) { if (queryError !== null) {
console.error( console.error(
`Error in ${url.pathname} query:\n${JSON.stringify( `Error in ${url.pathname} query:\n${JSON.stringify(

View file

@ -1,13 +1,13 @@
import { type Audit, auditSetNullish, cleanAudit } from "@auditors/core" import { type Audit, auditSetNullish, cleanAudit } from "@auditors/core"
import { error } from "@sveltejs/kit" 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 type { IdWrapper } from "$lib/legal"
import { db } from "$lib/server/database" import { db } from "$lib/server/database"
import type { PageServerLoad } from "./$types" import type { PageServerLoad } from "./$types"
export function auditQuery( export function auditSearchParams(
audit: Audit, audit: Audit,
query: URLSearchParams, query: URLSearchParams,
): [unknown, unknown] { ): [unknown, unknown] {
@ -29,16 +29,16 @@ export function auditQuery(
const errors: { [key: string]: unknown } = {} const errors: { [key: string]: unknown } = {}
const remainingKeys = new Set(Object.keys(data)) const remainingKeys = new Set(Object.keys(data))
auditSearchQueryContent(audit, data, errors, remainingKeys) auditSearchSearchParamsContent(audit, data, errors, remainingKeys)
return audit.reduceRemaining(data, errors, remainingKeys, auditSetNullish({})) return audit.reduceRemaining(data, errors, remainingKeys, auditSetNullish({}))
} }
export const load: PageServerLoad = async ({ url }) => { export const load: PageServerLoad = async ({ url }) => {
const [query, queryError] = auditQuery(cleanAudit, url.searchParams) as [ const [query, queryError] = auditSearchParams(
{ limit: number; offset: number; q?: string }, cleanAudit,
unknown, url.searchParams,
] ) as [{ limit: number; offset: number; q?: string }, unknown]
if (queryError !== null) { if (queryError !== null) {
console.error( console.error(
`Error in ${url.pathname} query:\n${JSON.stringify( `Error in ${url.pathname} query:\n${JSON.stringify(

View file

@ -1,13 +1,13 @@
import { type Audit, auditSetNullish, cleanAudit } from "@auditors/core" import { type Audit, auditSetNullish, cleanAudit } from "@auditors/core"
import { error } from "@sveltejs/kit" 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 type { Idcc } from "$lib/legal"
import { db } from "$lib/server/database" import { db } from "$lib/server/database"
import type { PageServerLoad } from "./$types" import type { PageServerLoad } from "./$types"
export function auditQuery( export function auditSearchParams(
audit: Audit, audit: Audit,
query: URLSearchParams, query: URLSearchParams,
): [unknown, unknown] { ): [unknown, unknown] {
@ -29,16 +29,16 @@ export function auditQuery(
const errors: { [key: string]: unknown } = {} const errors: { [key: string]: unknown } = {}
const remainingKeys = new Set(Object.keys(data)) const remainingKeys = new Set(Object.keys(data))
auditSearchQueryContent(audit, data, errors, remainingKeys) auditSearchSearchParamsContent(audit, data, errors, remainingKeys)
return audit.reduceRemaining(data, errors, remainingKeys, auditSetNullish({})) return audit.reduceRemaining(data, errors, remainingKeys, auditSetNullish({}))
} }
export const load: PageServerLoad = async ({ url }) => { export const load: PageServerLoad = async ({ url }) => {
const [query, queryError] = auditQuery(cleanAudit, url.searchParams) as [ const [query, queryError] = auditSearchParams(
{ limit: number; offset: number; q?: string }, cleanAudit,
unknown, url.searchParams,
] ) as [{ limit: number; offset: number; q?: string }, unknown]
if (queryError !== null) { if (queryError !== null) {
console.error( console.error(
`Error in ${url.pathname} query:\n${JSON.stringify( `Error in ${url.pathname} query:\n${JSON.stringify(

View file

@ -1,13 +1,13 @@
import { type Audit, auditSetNullish, cleanAudit } from "@auditors/core" import { type Audit, auditSetNullish, cleanAudit } from "@auditors/core"
import { error } from "@sveltejs/kit" 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 type { Jo } from "$lib/legal"
import { db } from "$lib/server/database" import { db } from "$lib/server/database"
import type { PageServerLoad } from "./$types" import type { PageServerLoad } from "./$types"
export function auditQuery( export function auditSearchParams(
audit: Audit, audit: Audit,
query: URLSearchParams, query: URLSearchParams,
): [unknown, unknown] { ): [unknown, unknown] {
@ -29,16 +29,16 @@ export function auditQuery(
const errors: { [key: string]: unknown } = {} const errors: { [key: string]: unknown } = {}
const remainingKeys = new Set(Object.keys(data)) const remainingKeys = new Set(Object.keys(data))
auditSearchQueryContent(audit, data, errors, remainingKeys) auditSearchSearchParamsContent(audit, data, errors, remainingKeys)
return audit.reduceRemaining(data, errors, remainingKeys, auditSetNullish({})) return audit.reduceRemaining(data, errors, remainingKeys, auditSetNullish({}))
} }
export const load: PageServerLoad = async ({ url }) => { export const load: PageServerLoad = async ({ url }) => {
const [query, queryError] = auditQuery(cleanAudit, url.searchParams) as [ const [query, queryError] = auditSearchParams(
{ limit: number; offset: number; q?: string }, cleanAudit,
unknown, url.searchParams,
] ) as [{ limit: number; offset: number; q?: string }, unknown]
if (queryError !== null) { if (queryError !== null) {
console.error( console.error(
`Error in ${url.pathname} query:\n${JSON.stringify( `Error in ${url.pathname} query:\n${JSON.stringify(

View file

@ -1,73 +1,7 @@
import { type Audit, auditSetNullish, cleanAudit } from "@auditors/core" import { doGetRecherche } from "$lib/server/doers/recherche"
import { error } from "@sveltejs/kit"
import { auditSearchQueryContent } from "$lib/auditors/queries"
import type { Article } from "$lib/legal"
import { db } from "$lib/server/database"
import type { PageServerLoad } from "./$types" 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 }) => { export const load: PageServerLoad = async ({ url }) => {
const [query, queryError] = auditQuery(cleanAudit, url.searchParams) as [ return (await doGetRecherche(url)) ?? {}
{ 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 {}
} }

View file

@ -10,8 +10,7 @@
import type { PageData } from "./$types" import type { PageData } from "./$types"
export let data: PageData export let data: PageData
let { articles, q } = data
$: ({ articles, q } = data)
</script> </script>
<header class="prose my-6 max-w-full"> <header class="prose my-6 max-w-full">

View file

@ -1,13 +1,13 @@
import { type Audit, auditSetNullish, cleanAudit } from "@auditors/core" import { type Audit, auditSetNullish, cleanAudit } from "@auditors/core"
import { error } from "@sveltejs/kit" 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 type { SectionTa } from "$lib/legal"
import { db } from "$lib/server/database" import { db } from "$lib/server/database"
import type { PageServerLoad } from "./$types" import type { PageServerLoad } from "./$types"
export function auditQuery( export function auditSearchParams(
audit: Audit, audit: Audit,
query: URLSearchParams, query: URLSearchParams,
): [unknown, unknown] { ): [unknown, unknown] {
@ -29,16 +29,16 @@ export function auditQuery(
const errors: { [key: string]: unknown } = {} const errors: { [key: string]: unknown } = {}
const remainingKeys = new Set(Object.keys(data)) const remainingKeys = new Set(Object.keys(data))
auditSearchQueryContent(audit, data, errors, remainingKeys) auditSearchSearchParamsContent(audit, data, errors, remainingKeys)
return audit.reduceRemaining(data, errors, remainingKeys, auditSetNullish({})) return audit.reduceRemaining(data, errors, remainingKeys, auditSetNullish({}))
} }
export const load: PageServerLoad = async ({ url }) => { export const load: PageServerLoad = async ({ url }) => {
const [query, queryError] = auditQuery(cleanAudit, url.searchParams) as [ const [query, queryError] = auditSearchParams(
{ limit: number; offset: number; q?: string }, cleanAudit,
unknown, url.searchParams,
] ) as [{ limit: number; offset: number; q?: string }, unknown]
if (queryError !== null) { if (queryError !== null) {
console.error( console.error(
`Error in ${url.pathname} query:\n${JSON.stringify( `Error in ${url.pathname} query:\n${JSON.stringify(

View file

@ -1,13 +1,13 @@
import { type Audit, auditSetNullish, cleanAudit } from "@auditors/core" import { type Audit, auditSetNullish, cleanAudit } from "@auditors/core"
import { error } from "@sveltejs/kit" 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 type { TexteVersion } from "$lib/legal"
import { db } from "$lib/server/database" import { db } from "$lib/server/database"
import type { PageServerLoad } from "./$types" import type { PageServerLoad } from "./$types"
export function auditQuery( export function auditSearchParams(
audit: Audit, audit: Audit,
query: URLSearchParams, query: URLSearchParams,
): [unknown, unknown] { ): [unknown, unknown] {
@ -29,16 +29,16 @@ export function auditQuery(
const errors: { [key: string]: unknown } = {} const errors: { [key: string]: unknown } = {}
const remainingKeys = new Set(Object.keys(data)) const remainingKeys = new Set(Object.keys(data))
auditSearchQueryContent(audit, data, errors, remainingKeys) auditSearchSearchParamsContent(audit, data, errors, remainingKeys)
return audit.reduceRemaining(data, errors, remainingKeys, auditSetNullish({})) return audit.reduceRemaining(data, errors, remainingKeys, auditSetNullish({}))
} }
export const load: PageServerLoad = async ({ url }) => { export const load: PageServerLoad = async ({ url }) => {
const [query, queryError] = auditQuery(cleanAudit, url.searchParams) as [ const [query, queryError] = auditSearchParams(
{ limit: number; offset: number; q?: string }, cleanAudit,
unknown, url.searchParams,
] ) as [{ limit: number; offset: number; q?: string }, unknown]
if (queryError !== null) { if (queryError !== null) {
console.error( console.error(
`Error in ${url.pathname} query:\n${JSON.stringify( `Error in ${url.pathname} query:\n${JSON.stringify(

View file

@ -1,13 +1,13 @@
import { type Audit, auditSetNullish, cleanAudit } from "@auditors/core" import { type Audit, auditSetNullish, cleanAudit } from "@auditors/core"
import { error } from "@sveltejs/kit" 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 type { Textekali } from "$lib/legal"
import { db } from "$lib/server/database" import { db } from "$lib/server/database"
import type { PageServerLoad } from "./$types" import type { PageServerLoad } from "./$types"
export function auditQuery( export function auditSearchParams(
audit: Audit, audit: Audit,
query: URLSearchParams, query: URLSearchParams,
): [unknown, unknown] { ): [unknown, unknown] {
@ -29,16 +29,16 @@ export function auditQuery(
const errors: { [key: string]: unknown } = {} const errors: { [key: string]: unknown } = {}
const remainingKeys = new Set(Object.keys(data)) const remainingKeys = new Set(Object.keys(data))
auditSearchQueryContent(audit, data, errors, remainingKeys) auditSearchSearchParamsContent(audit, data, errors, remainingKeys)
return audit.reduceRemaining(data, errors, remainingKeys, auditSetNullish({})) return audit.reduceRemaining(data, errors, remainingKeys, auditSetNullish({}))
} }
export const load: PageServerLoad = async ({ url }) => { export const load: PageServerLoad = async ({ url }) => {
const [query, queryError] = auditQuery(cleanAudit, url.searchParams) as [ const [query, queryError] = auditSearchParams(
{ limit: number; offset: number; q?: string }, cleanAudit,
unknown, url.searchParams,
] ) as [{ limit: number; offset: number; q?: string }, unknown]
if (queryError !== null) { if (queryError !== null) {
console.error( console.error(
`Error in ${url.pathname} query:\n${JSON.stringify( `Error in ${url.pathname} query:\n${JSON.stringify(

View file

@ -1,8 +1,4 @@
<script lang="ts"> <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 Icon from "@iconify/svelte"
// import searchIcon from "@iconify-icons/codicon/search" // import searchIcon from "@iconify-icons/codicon/search"
import { TreeView } from "augmented-data-viewer" import { TreeView } from "augmented-data-viewer"

View file

@ -1,13 +1,13 @@
import { type Audit, auditSetNullish, cleanAudit } from "@auditors/core" import { type Audit, auditSetNullish, cleanAudit } from "@auditors/core"
import { error } from "@sveltejs/kit" 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 type { Textelr } from "$lib/legal"
import { db } from "$lib/server/database" import { db } from "$lib/server/database"
import type { PageServerLoad } from "./$types" import type { PageServerLoad } from "./$types"
export function auditQuery( export function auditSearchParams(
audit: Audit, audit: Audit,
query: URLSearchParams, query: URLSearchParams,
): [unknown, unknown] { ): [unknown, unknown] {
@ -29,16 +29,16 @@ export function auditQuery(
const errors: { [key: string]: unknown } = {} const errors: { [key: string]: unknown } = {}
const remainingKeys = new Set(Object.keys(data)) const remainingKeys = new Set(Object.keys(data))
auditSearchQueryContent(audit, data, errors, remainingKeys) auditSearchSearchParamsContent(audit, data, errors, remainingKeys)
return audit.reduceRemaining(data, errors, remainingKeys, auditSetNullish({})) return audit.reduceRemaining(data, errors, remainingKeys, auditSetNullish({}))
} }
export const load: PageServerLoad = async ({ url }) => { export const load: PageServerLoad = async ({ url }) => {
const [query, queryError] = auditQuery(cleanAudit, url.searchParams) as [ const [query, queryError] = auditSearchParams(
{ limit: number; offset: number; q?: string }, cleanAudit,
unknown, url.searchParams,
] ) as [{ limit: number; offset: number; q?: string }, unknown]
if (queryError !== null) { if (queryError !== null) {
console.error( console.error(
`Error in ${url.pathname} query:\n${JSON.stringify( `Error in ${url.pathname} query:\n${JSON.stringify(

View file

@ -1,13 +1,13 @@
import { type Audit, auditSetNullish, cleanAudit } from "@auditors/core" import { type Audit, auditSetNullish, cleanAudit } from "@auditors/core"
import { error } from "@sveltejs/kit" 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 type { VersionsWrapper } from "$lib/legal"
import { db } from "$lib/server/database" import { db } from "$lib/server/database"
import type { PageServerLoad } from "./$types" import type { PageServerLoad } from "./$types"
export function auditQuery( export function auditSearchParams(
audit: Audit, audit: Audit,
query: URLSearchParams, query: URLSearchParams,
): [unknown, unknown] { ): [unknown, unknown] {
@ -29,16 +29,16 @@ export function auditQuery(
const errors: { [key: string]: unknown } = {} const errors: { [key: string]: unknown } = {}
const remainingKeys = new Set(Object.keys(data)) const remainingKeys = new Set(Object.keys(data))
auditSearchQueryContent(audit, data, errors, remainingKeys) auditSearchSearchParamsContent(audit, data, errors, remainingKeys)
return audit.reduceRemaining(data, errors, remainingKeys, auditSetNullish({})) return audit.reduceRemaining(data, errors, remainingKeys, auditSetNullish({}))
} }
export const load: PageServerLoad = async ({ url }) => { export const load: PageServerLoad = async ({ url }) => {
const [query, queryError] = auditQuery(cleanAudit, url.searchParams) as [ const [query, queryError] = auditSearchParams(
{ limit: number; offset: number; q?: string }, cleanAudit,
unknown, url.searchParams,
] ) as [{ limit: number; offset: number; q?: string }, unknown]
if (queryError !== null) { if (queryError !== null) {
console.error( console.error(
`Error in ${url.pathname} query:\n${JSON.stringify( `Error in ${url.pathname} query:\n${JSON.stringify(

View file

@ -1,8 +1,16 @@
import { sveltekit } from "@sveltejs/kit/vite" 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} */ /** @type {import('vite').UserConfig} */
const config = { const config = {
plugins: [sveltekit()], plugins: [sveltekit(), middlewaresPlugin],
} }
export default config export default config