diff --git a/.github/workflows/compile.yaml b/.github/workflows/compile.yaml index e27d3b2..ef55c58 100644 --- a/.github/workflows/compile.yaml +++ b/.github/workflows/compile.yaml @@ -24,5 +24,9 @@ jobs: name: Compile and test run: yarn test + - id: compile + name: Compile + run: yarn compile + - name: Run pkg.pr.new run: npx pkg-pr-new publish diff --git a/README.md b/README.md index 265c4e7..a562816 100644 --- a/README.md +++ b/README.md @@ -184,6 +184,11 @@ yarn test #### Lancer la documentation +> [!TIP] +> Pour facilement parcourir la documentation et tester différentes situations pendant +> le développement, il est conseillé d'utiliser la commande `yarn dev` à la place. +> La `online-doc` sera bientôt dépréciée. + > Le code de la documentation est disponible dans le dossier > [`online-doc/`](https://github.com/betagouv/publicodes-voiture/tree/main/online-doc). @@ -191,12 +196,18 @@ Pour lancer l'app React en local permettant de parcourir la documentation du modèle, il suffit d'exécuter la commande suivante : ``` + yarn install --cwd doc yarn doc + ``` ## Publier une nouvelle version Afin de publier une nouvelle version il suffit d'exécuter la commande `yarn version`. + +``` + +``` diff --git a/package.json b/package.json index 29aa961..bb44e2e 100644 --- a/package.json +++ b/package.json @@ -44,13 +44,14 @@ "compile:rules": "publicodes compile src/rules", "compile": "yarn compile:rules && tsup", "test": "yarn compile:rules && vitest run", + "dev": "publicodes dev", "doc": "yarn run compile && cd online-doc && yarn run dev", "doc:build": "yarn run compile && cd online-doc && yarn run build" }, "devDependencies": { "@incubateur-ademe/nosgestesclimat": "^3.5.4", "@incubateur-ademe/publicodes-commun": "^1.1.5", - "@publicodes/tools": "^1.5.3", + "@publicodes/tools": "https://pkg.pr.new/publicodes/publicodes/@publicodes/tools@1858c5e", "csv-parser": "^3.0.0", "terser": "^5.36.0", "tsup": "^8.3.5", @@ -62,7 +63,7 @@ "access": "public" }, "dependencies": { - "publicodes": "^1.7.2" + "publicodes": "https://pkg.pr.new/publicodes/publicodes@56e0eea" }, "packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e" } diff --git a/scripts/compile-personas.js b/scripts/compile-personas.js index 23b19a4..d215f06 100644 --- a/scripts/compile-personas.js +++ b/scripts/compile-personas.js @@ -5,7 +5,9 @@ import { readFileSync } from "fs" * Parses the personas.yaml file */ export default function getPersonas(rules) { - const personas = parse(readFileSync("personas.yaml", "utf-8")) + const personas = parse( + readFileSync("situations/personas.publicodes", "utf-8"), + ) let error = false Object.entries(personas).forEach(([personaName, persona]) => { @@ -15,10 +17,10 @@ export default function getPersonas(rules) { if (!persona.description) { console.warn(`[getPersonas] '${personaName}' has no description`) } - if (!persona.situation) { + if (!persona.contexte) { console.warn(`[getPersonas] '${personaName}' has no situation`) } else { - Object.entries(persona.situation).forEach(([name, value]) => { + Object.entries(persona.contexte).forEach(([name, value]) => { if (!(name in rules)) { console.error( `[getPersonas] '${personaName}' has an unknown rule '${name}'`, diff --git a/scripts/precompile.js b/scripts/precompile.js index 8055713..b34aff2 100644 --- a/scripts/precompile.js +++ b/scripts/precompile.js @@ -1,18 +1,18 @@ import { writeFileSync } from "fs" import { join } from "path" -import { stringify } from "yaml" +// import { stringify } from "yaml" import { getModelFromSource } from "@publicodes/tools/compilation" import Engine from "publicodes" import getPersonas from "./compile-personas.js" -import generateAlternatives from "./generate-alternatives.js" +// import generateAlternatives from "./generate-alternatives.js" const ROOT_PATH = new URL(".", import.meta.url).pathname const SRC_FILES = join(ROOT_PATH, "../src/rules/") -const ALTERNATIVES_DEST_PATH = join( - ROOT_PATH, - "../src/rules/alternatives.publicodes", -) +// const ALTERNATIVES_DEST_PATH = join( +// ROOT_PATH, +// "../src/rules/alternatives.publicodes", +// ) const PERSONAS_DEST_PATH = join(ROOT_PATH, "../src/personas/personas.json") const model = getModelFromSource(SRC_FILES) @@ -25,14 +25,14 @@ const resolvedRules = Object.fromEntries( }), ) -const alternatives = generateAlternatives(resolvedRules) -console.log(`✅ './src/rules/alternatives.publicodes' generated`) -writeFileSync( - ALTERNATIVES_DEST_PATH, - `# GENERATED FILE - DO NOT EDIT\n\n${stringify(alternatives, { - aliasDuplicateObjects: false, - })}`, -) +// const alternatives = generateAlternatives(resolvedRules) +// console.log(`✅ './src/rules/alternatives.publicodes' generated`) +// writeFileSync( +// ALTERNATIVES_DEST_PATH, +// `# GENERATED FILE - DO NOT EDIT\n\n${stringify(alternatives, { +// aliasDuplicateObjects: false, +// })}`, +// ) const personas = getPersonas(resolvedRules) writeFileSync(PERSONAS_DEST_PATH, JSON.stringify(personas)) diff --git a/personas.yaml b/situations/personas.publicodes similarity index 97% rename from personas.yaml rename to situations/personas.publicodes index 4d41f0c..c2071d1 100644 --- a/personas.yaml +++ b/situations/personas.publicodes @@ -1,14 +1,14 @@ valeur par défaut: titre: Valeur par défaut - situation: {} empreinte: 3022.851504292707 coûts: 6370.257297587041 + contexte: {} aller-retours travail électrique ville: titre: Aller-retours travail en ville (électrique) description: | Personne qui va travailler avec une petite voiture électrique en ville - situation: + contexte: voiture . motorisation: "'électrique'" voiture . gabarit: "'petite'" voiture . prix d'achat: 5400 @@ -29,7 +29,7 @@ aller-retours travail électrique ville occasion: titre: Aller-retours travail en ville (électrique) - Occasion description: | Personne qui va travailler avec une petite voiture électrique en ville - situation: + contexte: voiture . motorisation: "'électrique'" voiture . gabarit: "'petite'" voiture . prix d'achat: 5400 @@ -45,7 +45,7 @@ grandes vacances familiales: description: | Personne qui utilise une voiture essence pour partir en vacances en famille. - situation: + contexte: voiture . motorisation: "'thermique'" voiture . thermique . carburant: "'essence E5 ou E10'" voiture . gabarit: "'berline'" @@ -63,7 +63,7 @@ famille usage régulier diesel: Elle a besoin d'une voiture spacieuse pour transporter ses enfants à l'école et aller au travail. - situation: + contexte: voiture . motorisation: "'thermique'" voiture . thermique . carburant: "'gazole B7 ou B10'" voiture . gabarit: "'berline'" @@ -81,7 +81,7 @@ famille usage régulier biocarburant: Elle a besoin d'une voiture spacieuse pour transporter ses enfants à l'école et aller au travail. - situation: + contexte: voiture . motorisation: "'thermique'" voiture . thermique . carburant: "'essence E85'" voiture . gabarit: "'berline'" @@ -99,7 +99,7 @@ famille usage régulier moyenne biocarburant: Elle a besoin d'une voiture spacieuse pour transporter ses enfants à l'école et aller au travail. - situation: + contexte: voiture . motorisation: "'thermique'" voiture . thermique . carburant: "'essence E85'" voiture . gabarit: "'moyenne'" diff --git a/src/CarSimulator.ts b/src/CarSimulator.ts index 9d834a2..8af563a 100644 --- a/src/CarSimulator.ts +++ b/src/CarSimulator.ts @@ -1,4 +1,5 @@ import Engine, { + Possibility, Situation as PublicodesSituation, serializeUnit, } from "publicodes" @@ -82,15 +83,6 @@ const engineLogger = { error: (message: string) => console.error(message), } -export const RULE_NAMES = Object.keys(rules) as RuleName[] -export const ALTERNATIVES_VOITURE_NAMESPACE: RuleName = "alternatives . voiture" -export const ALTERNATIVES_RULES = RULE_NAMES.filter( - (rule) => - rule.startsWith(ALTERNATIVES_VOITURE_NAMESPACE) && - `${rule} . coûts` in rules && - `${rule} . empreinte` in rules, -) - /** * A wrapper around the {@link Engine} class to compute the available aids for the * given inputs (which are a subset of the Publicodes situation corresponding to @@ -148,9 +140,7 @@ export class CarSimulator { ...inputs, } } - this.engine.setSituation( - getSituation(this.inputs) as PublicodesSituation, - ) + this.engine.setSituation(getSituation(this.inputs)) return this } @@ -193,55 +183,49 @@ export class CarSimulator { * @note This method is an expensive operation. */ public evaluateAlternatives(): Alternative[] { - const infos = ALTERNATIVES_RULES.map((rule: RuleName) => { - const splittedRule = rule.split(" . ").slice(2) - const sizeOption = splittedRule[0] as Questions["voiture . gabarit"] - const motorisationOption = - splittedRule[1] as Questions["voiture . motorisation"] - const fuelOption = ( - motorisationOption !== "électrique" ? splittedRule[2] : undefined - ) as Questions["voiture . thermique . carburant"] + const localEngine = this.getEngine().shallowCopy() + const localSituation = localEngine.getSituation() + const carSizes = this.getEngine().getPossibilitiesFor("voiture . gabarit")! + const carMotorisations = this.getEngine().getPossibilitiesFor( + "voiture . motorisation", + )! + const carFuels = this.getEngine().getPossibilitiesFor( + "voiture . thermique . carburant", + )! - if (!sizeOption || !motorisationOption) { - throw new Error( - `Invalid alternative rule ${rule}. It should have a size and a motorisation option.`, - ) - } + const res = [] - return { - kind: "car", - title: this.engine.getRule(rule).title, - cost: this.evaluateRule(ruleName(rule, "coûts")), - emissions: this.evaluateRule(ruleName(rule, "empreinte")), - size: { - value: sizeOption, - title: this.engine.getRule(ruleName("voiture . gabarit", sizeOption)) - .title, - isEnumValue: true, - isApplicable: true, - }, - motorisation: { - value: motorisationOption, - title: this.engine.getRule( - ruleName("voiture . motorisation", motorisationOption), - ).title, - isEnumValue: true, - isApplicable: true, - }, - fuel: fuelOption - ? { - value: fuelOption, - title: this.engine.getRule( - ruleName("voiture . thermique . carburant", fuelOption), - ).title, - isEnumValue: true, - isApplicable: true, - } - : undefined, - } as Alternative - }) + // NOTE: we want to use default values for the alternatives as they are + // specific for each alternative. + delete localSituation["voiture . prix d'achat"] + delete localSituation["voiture . électrique . consommation électricité"] + delete localSituation["voiture . thermique . consommation carburant"] + for (const size of carSizes) { + localSituation["voiture . gabarit"] = + size.publicodesValue as Situation["voiture . gabarit"] + for (const motorisation of carMotorisations) { + localSituation["voiture . motorisation"] = + motorisation.publicodesValue as Situation["voiture . motorisation"] + if (motorisation.nodeValue === "électrique") { + localEngine.setSituation( + localSituation as PublicodesSituation, + ) + + res.push(getAlternative(localEngine, size, motorisation, undefined)) + } else { + for (const fuel of carFuels) { + localSituation["voiture . thermique . carburant"] = + fuel.publicodesValue - return infos + localEngine.setSituation(localSituation) + + res.push(getAlternative(localEngine, size, motorisation, fuel)) + } + } + } + } + + return res } /** @@ -333,7 +317,7 @@ export class CarSimulator { } } -function getSituation(inputs: Questions): Situation { +function getSituation(inputs: Questions): PublicodesSituation { return Object.fromEntries( Object.entries(inputs) .filter(([, value]) => value !== undefined) @@ -350,6 +334,48 @@ function getSituation(inputs: Questions): Situation { ) } -function ruleName(namespace: RuleName, rule: string): RuleName { - return (namespace + " . " + rule) as RuleName +function getAlternative( + engine: Engine, + size: Possibility, + motorisation: Possibility, + fuel?: Possibility, +): Alternative { + return { + kind: "car", + title: `${size.title} ${motorisation.title}${fuel ? ` (${fuel.title})` : ""}`, + cost: { + title: "Coûts annuels", + unit: "€/an", + isEnumValue: false, + isApplicable: true, + value: engine.evaluate("coûts").nodeValue, + }, + emissions: { + title: "Empreinte CO2e", + unit: "kgCO2e/an", + isEnumValue: false, + isApplicable: true, + value: engine.evaluate("empreinte").nodeValue, + }, + size: { + value: size.nodeValue, + title: size.title, + isEnumValue: true, + isApplicable: true, + }, + motorisation: { + value: motorisation.nodeValue, + title: motorisation.title, + isEnumValue: true, + isApplicable: true, + }, + fuel: fuel + ? { + value: fuel.nodeValue, + title: fuel.title, + isEnumValue: true, + isApplicable: true, + } + : undefined, + } as Alternative } diff --git a/src/personas/index.ts b/src/personas/index.ts index 0a69522..09d676f 100644 --- a/src/personas/index.ts +++ b/src/personas/index.ts @@ -11,7 +11,7 @@ import _rawPersonas from "./personas.json" export type Persona = { titre: string description?: string - situation: Situation + contexte: Situation empreinte: number coûts: number } diff --git a/src/rules/empreinte/empreinte.publicodes b/src/rules/empreinte/empreinte.publicodes index fd46958..c67bd05 100644 --- a/src/rules/empreinte/empreinte.publicodes +++ b/src/rules/empreinte/empreinte.publicodes @@ -5,27 +5,27 @@ empreinte: pour calculer l'empreinte carbone de votre voiture. valeur: ngc . transport . voiture unité: kgCO2e/an - contexte: - ngc . transport . voiture . utilisateur: "'propriétaire'" - ngc . transport . voiture . gabarit: voiture . gabarit - ngc . transport . voiture . motorisation: voiture . motorisation - ngc . transport . voiture . thermique . carburant: voiture . thermique . carburant - ngc . transport . voiture . km: usage . km annuels - ngc . transport . voiture . thermique . consommation aux 100: - valeur: - # Permet de forcer l'utilisation de la consommation estimée lors des - # recalculs pour les alternatives. - variations: - - si: voiture . thermique . consommation carburant > 1 - alors: voiture . thermique . consommation carburant - - sinon: voiture . thermique . consommation estimée - ngc . transport . voiture . électrique . consommation aux 100: - valeur: - # Permet de forcer l'utilisation de la consommation estimée lors des - # recalculs pour les alternatives. - variations: - - si: voiture . électrique . consommation électricité > 1 - alors: voiture . électrique . consommation électricité - - sinon: voiture . électrique . consommation estimée + # contexte: + # ngc . transport . voiture . utilisateur: "'propriétaire'" + # ngc . transport . voiture . gabarit: voiture . gabarit + # ngc . transport . voiture . motorisation: voiture . motorisation + # ngc . transport . voiture . thermique . carburant: voiture . thermique . carburant + # ngc . transport . voiture . km: usage . km annuels + # ngc . transport . voiture . thermique . consommation aux 100: + # valeur: + # # Permet de forcer l'utilisation de la consommation estimée lors des + # # recalculs pour les alternatives. + # variations: + # - si: voiture . thermique . consommation carburant > 1 + # alors: voiture . thermique . consommation carburant + # - sinon: voiture . thermique . consommation estimée + # ngc . transport . voiture . électrique . consommation aux 100: + # valeur: + # # Permet de forcer l'utilisation de la consommation estimée lors des + # # recalculs pour les alternatives. + # variations: + # - si: voiture . électrique . consommation électricité > 1 + # alors: voiture . électrique . consommation électricité + # - sinon: voiture . électrique . consommation estimée # On souhaite estimer l'empreinte total de la voiture et non par personne. - ngc . transport . voiture . voyageurs: 1 + # ngc . transport . voiture . voyageurs: 1 diff --git a/src/rules/empreinte/ngc.publicodes b/src/rules/empreinte/ngc.publicodes index c977dd8..648822a 100644 --- a/src/rules/empreinte/ngc.publicodes +++ b/src/rules/empreinte/ngc.publicodes @@ -9,22 +9,41 @@ importer!: # règles importées du modèle `@incubateur-ademe/nosgestesclimat` car elles sont # définies à partir des questions de ce modèle. - transport . voiture . km: - question: + formule: usage . km annuels unité: km/an - transport . voiture . utilisateur: - question: + question: Quel est l'utilisateur de la voiture ? DEBUG + formule: "'propriétaire'" - transport . voiture . voyageurs: question: + formule: 1 - transport . voiture . motorisation: question: + formule: voiture . motorisation - transport . voiture . thermique . consommation aux 100: question: + formule: + # Permet de forcer l'utilisation de la consommation estimée lors des + # recalculs pour les alternatives. + variations: + - si: voiture . thermique . consommation carburant > 1 + alors: voiture . thermique . consommation carburant + - sinon: voiture . thermique . consommation estimée - transport . voiture . gabarit: question: + formule: voiture . gabarit - transport . voiture . thermique . carburant: question: + formule: voiture . thermique . carburant - transport . voiture . électrique . consommation aux 100: question: + formule: + # Permet de forcer l'utilisation de la consommation estimée lors des + # recalculs pour les alternatives. + variations: + - si: voiture . électrique . consommation électricité > 1 + alors: voiture . électrique . consommation électricité + - sinon: voiture . électrique . consommation estimée # NOTE: les règles du namespace `logement` sont utilisées pour estimer # l'empreinte de la consommation d'électricité. Actuellement, nous # utilisons les valeurs par défaut de NGC. Cependant, nous pourrions à diff --git a/test/CarSimulator.test.ts b/test/CarSimulator.test.ts index a1905ed..153cc4b 100644 --- a/test/CarSimulator.test.ts +++ b/test/CarSimulator.test.ts @@ -5,7 +5,9 @@ import personas from "../src/personas" describe("CarSimulator", () => { describe("new CarSimulator()", () => { test("should return an instance of AidesVeloEngine with corrects rules parsed", () => { + console.time("CarSimulator init") const engine = new CarSimulator() + console.timeEnd("CarSimulator init") expect(engine).toBeInstanceOf(CarSimulator) const parsedRules = engine.getEngine().getParsedRules() @@ -144,7 +146,7 @@ describe("CarSimulator", () => { test(persona.titre, () => { const evaluatedCar = simulator .shallowCopy() - .setSituation(persona.situation) + .setSituation(persona.contexte) .evaluateCar() expect(evaluatedCar.emissions.value).toEqual(persona["empreinte"]) @@ -273,7 +275,11 @@ describe("CarSimulator", () => { describe("evaluateAlternatives()", () => { test("should return all possible alternatives with default values", () => { const engine = globalTestEngine.shallowCopy() + engine.setInputs({ "usage . km annuels . renseignés": 1000000 }) + console.time("evaluateAlternatives") const alternatives = engine.evaluateAlternatives() + console.timeEnd("evaluateAlternatives") + // TODO: use engine.getOptions const nbMotorisations = 3 const nbFuels = 4 @@ -301,6 +307,96 @@ describe("CarSimulator", () => { } }) }) + + test("increasing the annual distance should increase the cost and emissions", () => { + const engine = globalTestEngine.shallowCopy() + engine.setInputs({ "usage . km annuels . renseignés": 10 }) + const alternatives = engine.evaluateAlternatives() + + engine.setInputs({ "usage . km annuels . renseignés": 10000 }) + const newAlternatives = engine.evaluateAlternatives() + + expect(alternatives).toHaveLength(newAlternatives.length) + alternatives.forEach((alternative, i) => { + expect(alternative.cost.value).toBeLessThan( + newAlternatives[i].cost.value!, + ) + expect(alternative.emissions.value).toBeLessThan( + newAlternatives[i].emissions.value!, + ) + }) + }) + + test("set km to 0 should return 0 for emissions", () => { + const engine = globalTestEngine.shallowCopy() + engine.setInputs({ + "usage . km annuels . renseignés": 0, + "usage . km annuels . connus": true, + }) + const alternatives = engine.evaluateAlternatives() + + alternatives.forEach((alternative) => { + expect(alternative.emissions.value).toEqual(0) + }) + }) + + test("modify the consumption shouldn't modify the cost and emissions", () => { + const engine = globalTestEngine.shallowCopy() + const alternatives = engine.evaluateAlternatives() + + engine.setInputs({ + "voiture . électrique . consommation électricité": 4, + "voiture . thermique . consommation carburant": 10, + }) + const newAlternatives = engine.evaluateAlternatives() + + expect(alternatives).toHaveLength(newAlternatives.length) + alternatives.forEach((alternative, i) => { + expect(alternative.cost.value).toEqual(newAlternatives[i].cost.value) + expect(alternative.emissions.value).toEqual( + newAlternatives[i].emissions.value, + ) + }) + }) + + test("modify the fuel price should modify the cost", () => { + const engine = globalTestEngine.shallowCopy() + const alternatives = engine.evaluateAlternatives() + + engine.setInputs({ + "voiture . thermique . prix carburant": 20, + "voiture . électrique . prix kWh": 20, + }) + const newAlternatives = engine.evaluateAlternatives() + + expect(alternatives).toHaveLength(newAlternatives.length) + alternatives.forEach((alternative, i) => { + expect(alternative.cost.value).toBeLessThan( + newAlternatives[i].cost.value!, + ) + expect(alternative.emissions.value).toEqual( + newAlternatives[i].emissions.value, + ) + }) + }) + + test("modify the car price shouldn't modify the cost", () => { + const engine = globalTestEngine.shallowCopy() + const alternatives = engine.evaluateAlternatives() + + engine.setInputs({ + "voiture . prix d'achat": 20000, + }) + const newAlternatives = engine.evaluateAlternatives() + + expect(alternatives).toHaveLength(newAlternatives.length) + alternatives.forEach((alternative, i) => { + expect(alternative.cost.value).toEqual(newAlternatives[i].cost.value) + expect(alternative.emissions.value).toEqual( + newAlternatives[i].emissions.value, + ) + }) + }) }) describe("evaluateTargetCar()", () => { diff --git a/test/personas.test.ts b/test/personas.test.ts index aba3d52..2318c12 100644 --- a/test/personas.test.ts +++ b/test/personas.test.ts @@ -20,7 +20,7 @@ describe("Personas", () => { test(persona.titre, () => { const localEngine = engine .shallowCopy() - .setSituation(persona.situation as Situation, { + .setSituation(persona.contexte as Situation, { strict: true, }) diff --git a/yarn.lock b/yarn.lock index 0f02edd..a4cad90 100644 --- a/yarn.lock +++ b/yarn.lock @@ -388,22 +388,20 @@ resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== -"@publicodes/react-ui@^1.7.0": +"@publicodes/react-ui@https://pkg.pr.new/publicodes/publicodes/@publicodes/react-ui@1858c5ef8820f2a5fc1e70538e7cd32d3fa5f894": version "1.7.1" - resolved "https://registry.yarnpkg.com/@publicodes/react-ui/-/react-ui-1.7.1.tgz#c43ec02e8b397cbb72433dd485dbd4e54fa0b203" - integrity sha512-DVU/LXlL9IN+dKk8QK7a/+J3qlbeDvLrKQvIzkM3zkL4xXuEigmXl6E+r2t0Js8Zs05BUTeEvAgmOIkokfvkXw== + resolved "https://pkg.pr.new/publicodes/publicodes/@publicodes/react-ui@1858c5ef8820f2a5fc1e70538e7cd32d3fa5f894#cba04a607c781ae1b04e5e1961a82b7e43ccfd53" dependencies: fuse.js "^7.0.0" styled-components "^6.1.1" -"@publicodes/tools@^1.5.3": +"@publicodes/tools@https://pkg.pr.new/publicodes/publicodes/@publicodes/tools@1858c5e": version "1.5.3" - resolved "https://registry.yarnpkg.com/@publicodes/tools/-/tools-1.5.3.tgz#d5d6619da81381adab55be694c4b8489b920e567" - integrity sha512-vgNchM7D9OgIeRWmeMhZa38h2IuJBkzB7yipClgNVIPOUFllUTcCt8h9cWNn9osfbu6FB3QAnUVTHXr8PbeTug== + resolved "https://pkg.pr.new/publicodes/publicodes/@publicodes/tools@1858c5e#e4d445f321d5b632873c7bb2062fd811452a7a9d" dependencies: "@clack/prompts" "^0.7.0" "@oclif/core" "^4.2.5" - "@publicodes/react-ui" "^1.7.0" + "@publicodes/react-ui" "https://pkg.pr.new/publicodes/publicodes/@publicodes/react-ui@1858c5ef8820f2a5fc1e70538e7cd32d3fa5f894" "@tailwindcss/typography" "^0.5.16" "@tailwindcss/vite" "^4.0.0" "@types/node" "^18.11.18" @@ -411,7 +409,7 @@ chokidar "^4.0.3" glob "^10.4.1" path "^0.12.7" - publicodes "^1.7.0" + publicodes "https://pkg.pr.new/publicodes/publicodes/publicodes@1858c5ef8820f2a5fc1e70538e7cd32d3fa5f894" react "^18.0.0" react-dom "^18.0.0" react-router-dom "^7.1.3" @@ -864,7 +862,7 @@ color-name@~1.1.4: resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== -commander@^2.19.0, commander@^2.20.0: +commander@^2.20.0: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== @@ -951,11 +949,6 @@ dir-glob@^3.0.1: dependencies: path-type "^4.0.0" -discontinuous-range@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/discontinuous-range/-/discontinuous-range-1.0.0.tgz#e38331f0844bba49b9a9cb71c771585aab1bc65a" - integrity sha512-c68LpLbO+7kP/b1Hr1qs8/BJ09F5khZGTxqxZuhzxpmwJKOgRFHJWIb9/KmqnqHhLdO55aOxFH/EGBvUQbL/RQ== - eastasianwidth@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" @@ -1423,11 +1416,6 @@ minimatch@^9.0.4, minimatch@^9.0.5: resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.2.tgz#93a9626ce5e5e66bd4db86849e7515e92340a707" integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw== -moo@^0.5.0, moo@^0.5.2: - version "0.5.2" - resolved "https://registry.yarnpkg.com/moo/-/moo-0.5.2.tgz#f9fe82473bc7c184b0d32e2215d3f6e67278733c" - integrity sha512-iSAJLHYKnX41mKcJKjqvnAN9sf0LMDTXDEvFv+ffuRR9a1MIuXLjMNL6EsnDHSkKLTWNqQQ5uo61P4EbU4NU+Q== - ms@^2.1.3: version "2.1.3" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" @@ -1447,16 +1435,6 @@ nanoid@^3.3.7, nanoid@^3.3.8: resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.8.tgz#b1be3030bee36aaff18bacb375e5cce521684baf" integrity sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w== -nearley@^2.20.1: - version "2.20.1" - resolved "https://registry.yarnpkg.com/nearley/-/nearley-2.20.1.tgz#246cd33eff0d012faf197ff6774d7ac78acdd474" - integrity sha512-+Mc8UaAebFzgV+KpI5n7DasuuQCHA89dmwm7JXw3TV43ukfNQ9DnBH3Mdb2g/I4Fdxc26pwimBWvjIw0UAILSQ== - dependencies: - commander "^2.19.0" - moo "^0.5.0" - railroad-diagrams "^1.0.0" - randexp "0.4.6" - object-assign@^4.0.1: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" @@ -1566,13 +1544,13 @@ process@^0.11.1: resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" integrity sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A== -publicodes@^1.7.0, publicodes@^1.7.2: +"publicodes@https://pkg.pr.new/publicodes/publicodes/publicodes@1858c5ef8820f2a5fc1e70538e7cd32d3fa5f894": version "1.7.2" - resolved "https://registry.yarnpkg.com/publicodes/-/publicodes-1.7.2.tgz#8763a500ba3dc420a488a816a35c98b03dd6f43b" - integrity sha512-R9iqut98I1o4KMgmI8oRxfF1UqpPa4kce7ZPSJDvOTj6QMmCPAuIoF7bDTwJ8IkhSBQnFV59GNMggsS/czyQtw== - dependencies: - moo "^0.5.2" - nearley "^2.20.1" + resolved "https://pkg.pr.new/publicodes/publicodes/publicodes@1858c5ef8820f2a5fc1e70538e7cd32d3fa5f894#b6b9abc0b7045535ebc7ad8f1313e1c04b57db60" + +"publicodes@https://pkg.pr.new/publicodes/publicodes@56e0eea": + version "1.7.2" + resolved "https://pkg.pr.new/publicodes/publicodes@56e0eea#2c0c1ae225862c985514c29b615dbbc24d0cc232" punycode@^2.1.0: version "2.3.1" @@ -1584,19 +1562,6 @@ queue-microtask@^1.2.2: resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== -railroad-diagrams@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/railroad-diagrams/-/railroad-diagrams-1.0.0.tgz#eb7e6267548ddedfb899c1b90e57374559cddb7e" - integrity sha512-cz93DjNeLY0idrCNOH6PviZGRN9GJhsdm9hpn1YCS879fj4W+x5IFJhhkRZcwVgMmFF7R82UA/7Oh+R8lLZg6A== - -randexp@0.4.6: - version "0.4.6" - resolved "https://registry.yarnpkg.com/randexp/-/randexp-0.4.6.tgz#e986ad5e5e31dae13ddd6f7b3019aa7c87f60ca3" - integrity sha512-80WNmd9DA0tmZrw9qQa62GPPWfuXJknrmVmLcxvq4uZBdYqb1wYoKTmnlGUchvVWe0XiLupYkBoXVOxz3C8DYQ== - dependencies: - discontinuous-range "1.0.0" - ret "~0.1.10" - react-dom@^18.0.0: version "18.3.1" resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.3.1.tgz#c2265d79511b57d479b3dd3fdfa51536494c5cb4" @@ -1639,11 +1604,6 @@ resolve-from@^5.0.0: resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== -ret@~0.1.10: - version "0.1.15" - resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" - integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== - reusify@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76"