{"_id":"@astratra/closure","_rev":"2-5754a44fa1c8e2fa7fb544c135141b19","name":"@astratra/closure","dist-tags":{"latest":"1.0.0"},"versions":{"0.1.0":{"name":"@astratra/closure","version":"0.1.0","keywords":["astratra","closure","archive","period","year-end"],"license":"MIT","_id":"@astratra/closure@0.1.0","maintainers":[{"name":"emch99","email":"emchkongo@gmail.com"}],"dist":{"shasum":"04fce537595aea5e260b04d5cf2043ec8661b753","tarball":"https://registry.npmjs.org/@astratra/closure/-/closure-0.1.0.tgz","fileCount":7,"integrity":"sha512-12pOi7G3Fwog21rj/vmZL5m0pme0WVhIBX8oMutVaGLK+LNmsRJYjh6KUUlBDNBwJTyKYiYhLi2LPWOerbZuIQ==","signatures":[{"sig":"MEUCIQChIDSn/WlkXbNVEvaUOH/4jSaEsf/MODRocGcqmo0UpAIgQ+qOVcr4e98cuNmVoPl5UYSAI6Vj/cyhFVGLA7dDkI4=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":14894},"jest":{"testMatch":["**/__tests__/**/*.test.js"]},"main":"src/index.js","type":"commonjs","types":"src/index.d.ts","engines":{"node":">=20"},"gitHead":"a3f7c737294561ba8651e14d507a18b41e199999","scripts":{"test":"jest"},"_npmUser":{"name":"emch99","email":"emchkongo@gmail.com"},"_npmVersion":"11.13.0","description":"Closing a period on purpose: a human-owned checklist with blocking and acknowledged items, and an archive that never carries a credential.","directories":{},"_nodeVersion":"24.16.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jest":"30.4.2"},"_npmOperationalInternal":{"tmp":"tmp/closure_0.1.0_1787666118302_0.548768853036294","host":"s3://npm-registry-packages-npm-production"}},"1.0.0":{"_id":"@astratra/closure@1.0.0","dist":{"shasum":"a4adfa87a870bfaf2f755ac1960277f388993bfd","tarball":"https://registry.npmjs.org/@astratra/closure/-/closure-1.0.0.tgz","fileCount":7,"integrity":"sha512-SUaKVDJPL09cG5w9vtSxLzfqqdO+Ugo8qF3gevGedunlVaidsH8Bzt/kRdSs98/011clLlnes2lT746fvIdSWg==","signatures":[{"sig":"MEYCIQCdog6pUecDqEDJl1lSj8MZkcXLV/8znvf6DBuOmWaZvgIhAJndsSsNSi1bjnYvUrZw6haB2MdyhRQBaJeEUVxZIlwX","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQCdMnHI3dVEKjgfs1eR+ZGOgNXwIrtwYFRLFSSMKpvy+QIgPnuL48DpUHd/nW6dOh5pPlhyO+/LXSnaLC/nl006OKk="}],"unpackedSize":14894},"jest":{"testMatch":["**/__tests__/**/*.test.js"]},"main":"src/index.js","name":"@astratra/closure","type":"commonjs","types":"src/index.d.ts","engines":{"node":">=20"},"gitHead":"28e680aaed8dfdf7c5aa00c7dfbde74ab8e377de","license":"MIT","scripts":{"test":"jest"},"version":"1.0.0","_npmUser":{"name":"emch99","email":"emchkongo@gmail.com"},"keywords":["astratra","closure","archive","period","year-end"],"_npmVersion":"11.13.0","description":"Closing a period on purpose: a human-owned checklist with blocking and acknowledged items, and an archive that never carries a credential.","directories":{},"maintainers":[{"name":"emch99","email":"emchkongo@gmail.com"}],"_nodeVersion":"24.16.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jest":"30.4.2"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/closure_1.0.0_1790882389195_0.9064554160395564"}}},"time":{"created":"2026-08-25T13:55:18.198Z","modified":"2026-10-01T19:19:49.491Z","0.1.0":"2026-08-25T13:55:18.498Z","1.0.0":"2026-10-01T19:19:49.290Z"},"license":"MIT","keywords":["astratra","closure","archive","period","year-end"],"description":"Closing a period on purpose: a human-owned checklist with blocking and acknowledged items, and an archive that never carries a credential.","maintainers":[{"name":"emch99","email":"emchkongo@gmail.com"}],"readme":"# @astratra/closure\n\nFermer une période **volontairement** : une liste de clôture portée par des\nhumains, et une archive qui ne transporte jamais un identifiant de connexion.\n\nAnnée scolaire, exercice comptable, saison, campagne : tout produit à périodes\nfinit par les clore. Et la clôture commence presque toujours comme une échéance\nsubie — une tâche passe à minuit et ferme la période quand une condition tient.\nPersonne ne voit ce qui manque, et personne ne décide vraiment.\n\nAucune dépendance à l'exécution. Logique pure : ton contrôleur compte, ce\npackage juge.\n\n## La liste de clôture\n\n```js\nconst { createClosureChecklist } = require('@astratra/closure');\n\nconst cloture = createClosureChecklist([\n  { id: 'deliberations',   label: 'Délibérations à trancher',    blocking: true },\n  { id: 'absences',        label: 'Absences sans justificatif',  blocking: false },\n  { id: 'impayes',         label: 'Frais impayés',               blocking: false },\n]);\n\n// Ton contrôleur compte ce qui reste ; la liste juge.\nconst liste = cloture.build({ deliberations: 0, absences: 3, impayes: 12 });\n// { items: [...], canClose: true, remaining: 2, blocking: 0 }\n```\n\n### Bloquant contre reconnu — la distinction qui porte tout\n\nCertains points **interdisent** de fermer : une délibération non tranchée engage\nl'avenir d'un enfant. D'autres méritent seulement d'être **vus** : un impayé se\nnégocie et se reporte.\n\nFermer une période en laissant des impayés est légitime. Le faire **sans\nl'avoir vu** ne l'est pas. C'est le rôle de la reconnaissance :\n\n```js\ncloture.canCloseWith(liste, []);\n// { ok: false, reason: 'unacknowledged', unseen: ['absences', 'impayes'] }\n\ncloture.canCloseWith(liste, ['absences', 'impayes']);\n// { ok: true }\n```\n\nDeux règles tenues par les tests :\n\n**Aucune reconnaissance ne passe au-dessus d'un point bloquant.** Cocher toutes\nles cases ne ferme pas une période dont une délibération reste ouverte.\n\n**Cocher d'avance ne pré-approuve rien.** Une reconnaissance pour un point qui\nn'en demande pas est ignorée, pas récompensée — sinon on cocherait tout au\n1er septembre pour être tranquille en juin.\n\n## L'archive\n\nUne archive se transmet, se range, parfois s'envoie par courriel. Deux règles,\napprises au prix fort :\n\n### Ce qui n'y entre jamais\n\nMots de passe, jetons, clés d'API : rien de ce qui permet de se connecter à la\nplace de quelqu'un. Le nettoyage se fait **à la frontière de l'archive** —\nplutôt que de faire confiance à chaque lecteur pour avoir bien choisi ses\nchamps. C'est la seule version qui survit au prochain qui ajoute une collection.\n\n```js\nconst { createArchiveBuilder, createScrubber } = require('@astratra/closure');\n\nconst builder = createArchiveBuilder({\n  scrubber: createScrubber({ alsoNever: ['activationCode'] }),\n  sections: [\n    { name: 'students', read: (scope) => Student.find({ school: scope.school }).lean() },\n    { name: 'results',  read: (scope) => Result.find({ year: scope.yearId }).lean() },\n    { name: 'payments', read: (scope) => paymentsCollection.find({ year: scope.yearId }).toArray() },\n  ],\n});\n\nconst archive = await builder.build({ yearId, school });\n// { builtAt, sections, counts, failed: [], complete: true }\n```\n\n### Une section en échec ne vide pas l'archive — et elle est nommée\n\nLes sections sont lues séparément. Un lecteur qui lève est consigné dans\n`failed` avec sa raison, et le reste de l'archive se construit quand même. Le\nrapport dit exactement ce qui est dedans et ce qui n'y est pas — une archive à\nlaquelle il manque une section **en silence** a l'air complète et ne l'est pas.\n\n## Ce que ce package ne fait pas\n\n- Il ne **compte** rien : les requêtes qui dénombrent ce qui reste sont à toi.\n- Il ne **stocke** pas l'archive — écrire le fichier, le chiffrer, lui donner\n  une durée de vie, c'est ta décision.\n- Il ne connaît ni les écoles, ni les exercices comptables : les points de\n  contrôle et les sections sont ton vocabulaire.\n\n## Tests\n\n```bash\nnpm test --workspace @astratra/closure\n```\n","readmeFilename":"README.md"}