소스 검색

Merge branch 'feature/V8-6050-trouver-une-solution-pour-viter-' into develop

Olivier Massot 1 년 전
부모
커밋
382126d448
13개의 변경된 파일711개의 추가작업 그리고 41개의 파일을 삭제
  1. 7 1
      .eslintrc.cjs
  2. 1 0
      .gitignore
  3. 1 1
      .gitlab-ci.yml
  4. 3 3
      env/setupEnv.mjs
  5. 0 14
      models/models.ts
  6. 5 0
      nuxt.config.ts
  7. 6 2
      package.json
  8. 45 0
      pages/dev/poc_models_index.vue
  9. 70 0
      prepare/buildIndex.ts
  10. 8 5
      services/data/entityManager.ts
  11. 1 1
      stores/sse.ts
  12. 21 13
      tests/units/services/data/entityManager.test.ts
  13. 543 1
      yarn.lock

+ 7 - 1
.eslintrc.cjs

@@ -19,7 +19,13 @@ module.exports = {
     'plugin:vue/vue3-recommended',
     'plugin:prettier/recommended',
   ],
-  ignorePatterns: ['.nuxt', 'coverage/*', 'vendor/*', 'dist/*'],
+  ignorePatterns: [
+    '.nuxt',
+    'coverage/*',
+    'vendor/*',
+    'dist/*',
+    'models/models.ts',
+  ],
   plugins: ['vue', '@typescript-eslint'],
   // add your custom rules here
   rules: {

+ 1 - 0
.gitignore

@@ -31,3 +31,4 @@ coverage/
 !.yarn/releases
 !.yarn/sdks
 !.yarn/versions
+models/models.ts

+ 1 - 1
.gitlab-ci.yml

@@ -11,7 +11,7 @@ before_script:
   - corepack enable
   - yarn set version berry
   - yarn install --network-timeout 10000
-  - HOST=ci yarn prepare
+  - HOSTNAME=ci yarn prepare
 
 cache:
   paths:

+ 3 - 3
env/setupEnv.mjs

@@ -2,9 +2,9 @@
  * Post install script: create or replace the symlink .env
  * to the .env file matching the current environment
  *
- * To force an hostname, define an env variable named HOST :
+ * To force a hostname, define an env variable named HOSTNAME :
  *
- *     HOST=ci node ./env/setupEnv.mjs
+ *     HOSTNAME=ci node ./env/setupEnv.mjs
  *
  */
 import os from 'os'
@@ -14,7 +14,7 @@ import path from 'path'
 
 const projectDir = path.join(path.dirname(fileURLToPath(import.meta.url)), '..')
 
-const hostname = process.env.HOST ?? os.hostname()
+const hostname = process.env.HOSTNAME || os.hostname()
 
 const environments = {
   app: '.env.docker',

+ 0 - 14
models/models.ts

@@ -1,14 +0,0 @@
-import ApiResource from '~/models/ApiResource'
-
-const modules = import.meta.glob('~/models/*/*.ts')
-
-const models: Record<string, typeof ApiResource> = {}
-
-for (const path in modules) {
-  modules[path]().then((mod) => {
-    // @ts-expect-error On est dépendant du retour de import.meta.glob pour le type
-    models[mod.default.entity] = mod.default
-  })
-}
-
-export default models

+ 5 - 0
nuxt.config.ts

@@ -158,6 +158,8 @@ export default defineNuxtConfig({
     '@nuxtjs/i18n',
     '@nuxt/devtools',
     '@nuxt/image',
+    'nuxt-prepare',
+    'nuxt-vitalizer',
   ],
   vite: {
     esbuild: {
@@ -217,4 +219,7 @@ export default defineNuxtConfig({
     transpile,
   },
   ignore: [process.env.NUXT_ENV === 'prod' ? 'pages/dev/*' : ''],
+  prepare: {
+    scripts: ['prepare/buildIndex.ts'],
+  },
 })

+ 6 - 2
package.json

@@ -1,5 +1,6 @@
 {
   "name": "app",
+  "type": "module",
   "description": "SPA Opentalent",
   "repository": "https://gitlab.2iopenservice.com/opentalent/app",
   "private": true,
@@ -9,9 +10,9 @@
   },
   "scripts": {
     "setupenv": "node ./env/setupEnv.mjs",
-    "dev": "rm -rf /tmp/nitro && yarn setupenv && nuxt dev",
-    "build": "yarn setupenv && nuxt build && cp -r config/ .output/config",
     "prepare": "yarn setupenv && nuxt prepare",
+    "dev": "yarn setupenv && rm -rf /tmp/nitro && nuxt dev",
+    "build": "yarn setupenv && nuxt build && cp -r config/ .output/config",
     "start": "nuxt start",
     "deploy": "git pull && yarn install & yarn build & supervisorctl restart app:app_00",
     "test": "vitest run",
@@ -37,11 +38,14 @@
     "eslint-import-resolver-typescript": "^3.6.1",
     "event-source-polyfill": "^1.0.31",
     "file-saver": "^2.0.5",
+    "glob": "^10.4.2",
     "js-yaml": "^4.1.0",
     "libphonenumber-js": "1.10.51",
     "lodash": "^4.17.21",
     "lodash-es": "^4.17.21",
     "nuxt": "^3.11.2",
+    "nuxt-prepare": "^2.1.0",
+    "nuxt-vitalizer": "^0.10.0",
     "pinia": "^2.1.7",
     "pinia-orm": "^1.7.2",
     "sass": "^1.69.5",

+ 45 - 0
pages/dev/poc_models_index.vue

@@ -0,0 +1,45 @@
+<!--
+Permet de tester l'index des modèles, l'idée étant de n'importer que les modèles utilisés,
+dans ce cas ci : Organization (importé dans le setup), Access (importé dynamiquement via
+la méthode `getModelFor` de l'entity manager), et Person (importée depuis la classe Access).
+
+On pourra vérifier que les fichiers suivants sont bien fetchés : Organization.ts, Access.ts
+Ainsi que les classes liées importées depuis celles ci : Person.ts
+
+
+Mais que les autres ne sont pas importés, par ex. : Country.ts ou File.ts
+-->
+<template>
+  <div>
+    <h1>POC Models index</h1>
+    <span>check result in console</span>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { useEntityManager } from '~/composables/data/useEntityManager'
+import Organization from '~/models/Organization/Organization'
+
+definePageMeta({
+  layout: false,
+})
+
+const { em } = useEntityManager()
+
+const accessModel = await em.getModelFor('accesses')
+
+console.log(
+  'Classe importée directement : ' +
+    Organization.name +
+    ' (entity: ' +
+    Organization.entity +
+    ')',
+)
+console.log(
+  'Classe importée dynamiquement : ' +
+    accessModel.name +
+    ' (entity: ' +
+    accessModel.entity +
+    ')',
+)
+</script>

+ 70 - 0
prepare/buildIndex.ts

@@ -0,0 +1,70 @@
+/**
+ * Build an index of models in models/models.ts
+ *
+ * The index is of the form :
+ *
+ *     {
+ *       [entityName]: async () => [entityClass],
+ *       ...
+ *     }
+ */
+import fs from 'fs'
+import { glob } from 'glob'
+
+console.log('Build entity index')
+
+const modules: Array<{ entity: string; path: string }> = []
+
+const files = await glob('./models/*/*.ts')
+
+files.forEach((file) => {
+  const data = fs.readFileSync(file, 'utf8')
+  const lines = data.split('\n')
+  let entity = null
+
+  for (const line of lines) {
+    const match = line.match(/static entity = ['"]([\w-/]+)['"]/)
+    if (match) {
+      // afficher le groupe capturant
+      entity = match[1]
+      break
+    }
+  }
+
+  if (entity) {
+    modules.push({ entity, path: file })
+  } else {
+    console.warn('No match found for entity name in ' + file)
+  }
+})
+
+const code = []
+code.push('/**')
+code.push(' * /!\\ Auto-generated file : do not modify directly /!\\')
+code.push(' *')
+code.push(
+  ' * > This file is generated by the script prepare/buildIndex.ts when running `nuxt prepare`',
+)
+code.push('*/')
+code.push("import type ApiResource from '~/models/ApiResource'")
+code.push('')
+
+// noinspection JSAnnotator
+code.push(
+  'const modelsIndex: Record<string, () => Promise<typeof ApiResource>> = {',
+)
+
+for (const module of modules) {
+  code.push("  '" + module.entity + "': async () => {")
+  code.push(
+    "    const module = await import('~/" + module.path.slice(0, -3) + "')",
+  )
+  code.push('    return module.default')
+  code.push('  },')
+}
+
+code.push('}')
+code.push('')
+code.push('export default modelsIndex')
+
+fs.writeFileSync('models/models.ts', code.join('\n'))

+ 8 - 5
services/data/entityManager.ts

@@ -8,7 +8,7 @@ import UrlUtils from '~/services/utils/urlUtils'
 import ApiModel from '~/models/ApiModel'
 import ApiResource from '~/models/ApiResource'
 import type { AnyJson, AssociativeArray, Collection } from '~/types/data.d'
-import models from '~/models/models'
+import modelsIndex from '~/models/models'
 import HydraNormalizer from '~/services/data/normalizer/hydraNormalizer'
 import ObjectUtils from '~/services/utils/objectUtils'
 import Query from '~/services/data/Query'
@@ -87,8 +87,11 @@ class EntityManager {
    *
    * @param entityName
    */
-  public getModelFor(entityName: string): typeof ApiResource {
-    return models[entityName]
+  public async getModelFor(entityName: string): Promise<typeof ApiResource> {
+    if (!Object.prototype.hasOwnProperty.call(modelsIndex, entityName)) {
+      throw new Error("No model found for entity name '" + entityName + "'")
+    }
+    return await modelsIndex[entityName]()
   }
 
   /**
@@ -96,12 +99,12 @@ class EntityManager {
    *
    * @param iri An IRI of the form .../api/<entity>/...
    */
-  public getModelFromIri(iri: string): typeof ApiResource {
+  public async getModelFromIri(iri: string): Promise<typeof ApiResource> {
     const matches = iri.match(/^\/api\/(\w+)\/.*/)
     if (!matches || !matches[1]) {
       throw new Error('cannot parse the IRI')
     }
-    return this.getModelFor(matches[1])
+    return await this.getModelFor(matches[1])
   }
 
   /**

+ 1 - 1
stores/sse.ts

@@ -11,7 +11,7 @@ export const useSseStore = defineStore('sse', () => {
   const addEvent = async (event: MercureEntityUpdate) => {
     const { em } = useEntityManager()
 
-    const model = em.getModelFromIri(event.iri)
+    const model = await em.getModelFromIri(event.iri)
 
     switch (event.operation) {
       case 'update':

+ 21 - 13
tests/units/services/data/entityManager.test.ts

@@ -50,13 +50,13 @@ const _console: any = {
 
 vi.mock('~/models/models', async () => {
   class MyModel {
-    static entity = 'myModel'
+    static entity = 'my-model'
   }
 
-  const models: Record<string, any> = { myModel: MyModel }
+  const modelsIndex: Record<string, any> = { 'my-model': () => MyModel }
 
   return {
-    default: models,
+    default: modelsIndex,
   }
 })
 
@@ -134,27 +134,35 @@ describe('cast', () => {
 })
 
 describe('getModelFor', () => {
-  test('simple call', () => {
-    expect(entityManager.getModelFor('myModel').entity).toEqual('myModel')
+  test('simple call', async () => {
+    const model = await entityManager.getModelFor('my-model')
+    expect(model!.entity).toEqual('my-model')
+  })
+  test('non existing model', async () => {
+    expect(
+      async () => await entityManager.getModelFor('non-existing-model'),
+    ).rejects.toThrowError(
+      "No model found for entity name 'non-existing-model'",
+    )
   })
 })
 
 describe('getModelFromIri', () => {
-  test('simple call', () => {
+  test('simple call', async () => {
     // @ts-ignore
-    entityManager.getModelFor = vi.fn((entityName: string) =>
-      entityName === 'dummy' ? DummyApiResource : null,
+    entityManager.getModelFor = vi.fn(
+      async (entityName: string) => DummyApiResource,
     )
 
     // @ts-ignore
-    const result = entityManager.getModelFromIri('/api/dummy/123')
-
+    const result = await entityManager.getModelFromIri('/api/dummy/123')
+    console.log(result)
     expect(result).toEqual(DummyApiResource)
   })
   test('invalide Iri', () => {
-    expect(() => entityManager.getModelFromIri('/invalid')).toThrowError(
-      'cannot parse the IRI',
-    )
+    expect(
+      async () => await entityManager.getModelFromIri('/invalid'),
+    ).rejects.toThrowError('cannot parse the IRI')
   })
 })
 

+ 543 - 1
yarn.lock

@@ -483,6 +483,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@esbuild/aix-ppc64@npm:0.21.5":
+  version: 0.21.5
+  resolution: "@esbuild/aix-ppc64@npm:0.21.5"
+  conditions: os=aix & cpu=ppc64
+  languageName: node
+  linkType: hard
+
 "@esbuild/android-arm64@npm:0.20.2":
   version: 0.20.2
   resolution: "@esbuild/android-arm64@npm:0.20.2"
@@ -490,6 +497,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@esbuild/android-arm64@npm:0.21.5":
+  version: 0.21.5
+  resolution: "@esbuild/android-arm64@npm:0.21.5"
+  conditions: os=android & cpu=arm64
+  languageName: node
+  linkType: hard
+
 "@esbuild/android-arm@npm:0.20.2":
   version: 0.20.2
   resolution: "@esbuild/android-arm@npm:0.20.2"
@@ -497,6 +511,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@esbuild/android-arm@npm:0.21.5":
+  version: 0.21.5
+  resolution: "@esbuild/android-arm@npm:0.21.5"
+  conditions: os=android & cpu=arm
+  languageName: node
+  linkType: hard
+
 "@esbuild/android-x64@npm:0.20.2":
   version: 0.20.2
   resolution: "@esbuild/android-x64@npm:0.20.2"
@@ -504,6 +525,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@esbuild/android-x64@npm:0.21.5":
+  version: 0.21.5
+  resolution: "@esbuild/android-x64@npm:0.21.5"
+  conditions: os=android & cpu=x64
+  languageName: node
+  linkType: hard
+
 "@esbuild/darwin-arm64@npm:0.20.2":
   version: 0.20.2
   resolution: "@esbuild/darwin-arm64@npm:0.20.2"
@@ -511,6 +539,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@esbuild/darwin-arm64@npm:0.21.5":
+  version: 0.21.5
+  resolution: "@esbuild/darwin-arm64@npm:0.21.5"
+  conditions: os=darwin & cpu=arm64
+  languageName: node
+  linkType: hard
+
 "@esbuild/darwin-x64@npm:0.20.2":
   version: 0.20.2
   resolution: "@esbuild/darwin-x64@npm:0.20.2"
@@ -518,6 +553,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@esbuild/darwin-x64@npm:0.21.5":
+  version: 0.21.5
+  resolution: "@esbuild/darwin-x64@npm:0.21.5"
+  conditions: os=darwin & cpu=x64
+  languageName: node
+  linkType: hard
+
 "@esbuild/freebsd-arm64@npm:0.20.2":
   version: 0.20.2
   resolution: "@esbuild/freebsd-arm64@npm:0.20.2"
@@ -525,6 +567,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@esbuild/freebsd-arm64@npm:0.21.5":
+  version: 0.21.5
+  resolution: "@esbuild/freebsd-arm64@npm:0.21.5"
+  conditions: os=freebsd & cpu=arm64
+  languageName: node
+  linkType: hard
+
 "@esbuild/freebsd-x64@npm:0.20.2":
   version: 0.20.2
   resolution: "@esbuild/freebsd-x64@npm:0.20.2"
@@ -532,6 +581,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@esbuild/freebsd-x64@npm:0.21.5":
+  version: 0.21.5
+  resolution: "@esbuild/freebsd-x64@npm:0.21.5"
+  conditions: os=freebsd & cpu=x64
+  languageName: node
+  linkType: hard
+
 "@esbuild/linux-arm64@npm:0.20.2":
   version: 0.20.2
   resolution: "@esbuild/linux-arm64@npm:0.20.2"
@@ -539,6 +595,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@esbuild/linux-arm64@npm:0.21.5":
+  version: 0.21.5
+  resolution: "@esbuild/linux-arm64@npm:0.21.5"
+  conditions: os=linux & cpu=arm64
+  languageName: node
+  linkType: hard
+
 "@esbuild/linux-arm@npm:0.20.2":
   version: 0.20.2
   resolution: "@esbuild/linux-arm@npm:0.20.2"
@@ -546,6 +609,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@esbuild/linux-arm@npm:0.21.5":
+  version: 0.21.5
+  resolution: "@esbuild/linux-arm@npm:0.21.5"
+  conditions: os=linux & cpu=arm
+  languageName: node
+  linkType: hard
+
 "@esbuild/linux-ia32@npm:0.20.2":
   version: 0.20.2
   resolution: "@esbuild/linux-ia32@npm:0.20.2"
@@ -553,6 +623,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@esbuild/linux-ia32@npm:0.21.5":
+  version: 0.21.5
+  resolution: "@esbuild/linux-ia32@npm:0.21.5"
+  conditions: os=linux & cpu=ia32
+  languageName: node
+  linkType: hard
+
 "@esbuild/linux-loong64@npm:0.20.2":
   version: 0.20.2
   resolution: "@esbuild/linux-loong64@npm:0.20.2"
@@ -560,6 +637,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@esbuild/linux-loong64@npm:0.21.5":
+  version: 0.21.5
+  resolution: "@esbuild/linux-loong64@npm:0.21.5"
+  conditions: os=linux & cpu=loong64
+  languageName: node
+  linkType: hard
+
 "@esbuild/linux-mips64el@npm:0.20.2":
   version: 0.20.2
   resolution: "@esbuild/linux-mips64el@npm:0.20.2"
@@ -567,6 +651,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@esbuild/linux-mips64el@npm:0.21.5":
+  version: 0.21.5
+  resolution: "@esbuild/linux-mips64el@npm:0.21.5"
+  conditions: os=linux & cpu=mips64el
+  languageName: node
+  linkType: hard
+
 "@esbuild/linux-ppc64@npm:0.20.2":
   version: 0.20.2
   resolution: "@esbuild/linux-ppc64@npm:0.20.2"
@@ -574,6 +665,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@esbuild/linux-ppc64@npm:0.21.5":
+  version: 0.21.5
+  resolution: "@esbuild/linux-ppc64@npm:0.21.5"
+  conditions: os=linux & cpu=ppc64
+  languageName: node
+  linkType: hard
+
 "@esbuild/linux-riscv64@npm:0.20.2":
   version: 0.20.2
   resolution: "@esbuild/linux-riscv64@npm:0.20.2"
@@ -581,6 +679,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@esbuild/linux-riscv64@npm:0.21.5":
+  version: 0.21.5
+  resolution: "@esbuild/linux-riscv64@npm:0.21.5"
+  conditions: os=linux & cpu=riscv64
+  languageName: node
+  linkType: hard
+
 "@esbuild/linux-s390x@npm:0.20.2":
   version: 0.20.2
   resolution: "@esbuild/linux-s390x@npm:0.20.2"
@@ -588,6 +693,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@esbuild/linux-s390x@npm:0.21.5":
+  version: 0.21.5
+  resolution: "@esbuild/linux-s390x@npm:0.21.5"
+  conditions: os=linux & cpu=s390x
+  languageName: node
+  linkType: hard
+
 "@esbuild/linux-x64@npm:0.20.2":
   version: 0.20.2
   resolution: "@esbuild/linux-x64@npm:0.20.2"
@@ -595,6 +707,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@esbuild/linux-x64@npm:0.21.5":
+  version: 0.21.5
+  resolution: "@esbuild/linux-x64@npm:0.21.5"
+  conditions: os=linux & cpu=x64
+  languageName: node
+  linkType: hard
+
 "@esbuild/netbsd-x64@npm:0.20.2":
   version: 0.20.2
   resolution: "@esbuild/netbsd-x64@npm:0.20.2"
@@ -602,6 +721,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@esbuild/netbsd-x64@npm:0.21.5":
+  version: 0.21.5
+  resolution: "@esbuild/netbsd-x64@npm:0.21.5"
+  conditions: os=netbsd & cpu=x64
+  languageName: node
+  linkType: hard
+
 "@esbuild/openbsd-x64@npm:0.20.2":
   version: 0.20.2
   resolution: "@esbuild/openbsd-x64@npm:0.20.2"
@@ -609,6 +735,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@esbuild/openbsd-x64@npm:0.21.5":
+  version: 0.21.5
+  resolution: "@esbuild/openbsd-x64@npm:0.21.5"
+  conditions: os=openbsd & cpu=x64
+  languageName: node
+  linkType: hard
+
 "@esbuild/sunos-x64@npm:0.20.2":
   version: 0.20.2
   resolution: "@esbuild/sunos-x64@npm:0.20.2"
@@ -616,6 +749,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@esbuild/sunos-x64@npm:0.21.5":
+  version: 0.21.5
+  resolution: "@esbuild/sunos-x64@npm:0.21.5"
+  conditions: os=sunos & cpu=x64
+  languageName: node
+  linkType: hard
+
 "@esbuild/win32-arm64@npm:0.20.2":
   version: 0.20.2
   resolution: "@esbuild/win32-arm64@npm:0.20.2"
@@ -623,6 +763,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@esbuild/win32-arm64@npm:0.21.5":
+  version: 0.21.5
+  resolution: "@esbuild/win32-arm64@npm:0.21.5"
+  conditions: os=win32 & cpu=arm64
+  languageName: node
+  linkType: hard
+
 "@esbuild/win32-ia32@npm:0.20.2":
   version: 0.20.2
   resolution: "@esbuild/win32-ia32@npm:0.20.2"
@@ -630,6 +777,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@esbuild/win32-ia32@npm:0.21.5":
+  version: 0.21.5
+  resolution: "@esbuild/win32-ia32@npm:0.21.5"
+  conditions: os=win32 & cpu=ia32
+  languageName: node
+  linkType: hard
+
 "@esbuild/win32-x64@npm:0.20.2":
   version: 0.20.2
   resolution: "@esbuild/win32-x64@npm:0.20.2"
@@ -637,6 +791,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@esbuild/win32-x64@npm:0.21.5":
+  version: 0.21.5
+  resolution: "@esbuild/win32-x64@npm:0.21.5"
+  conditions: os=win32 & cpu=x64
+  languageName: node
+  linkType: hard
+
 "@eslint-community/eslint-utils@npm:^4.1.2, @eslint-community/eslint-utils@npm:^4.2.0, @eslint-community/eslint-utils@npm:^4.4.0":
   version: 4.4.0
   resolution: "@eslint-community/eslint-utils@npm:4.4.0"
@@ -1340,6 +1501,34 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@nuxt/kit@npm:^3.12.2":
+  version: 3.12.2
+  resolution: "@nuxt/kit@npm:3.12.2"
+  dependencies:
+    "@nuxt/schema": "npm:3.12.2"
+    c12: "npm:^1.11.1"
+    consola: "npm:^3.2.3"
+    defu: "npm:^6.1.4"
+    destr: "npm:^2.0.3"
+    globby: "npm:^14.0.1"
+    hash-sum: "npm:^2.0.0"
+    ignore: "npm:^5.3.1"
+    jiti: "npm:^1.21.6"
+    klona: "npm:^2.0.6"
+    knitwork: "npm:^1.1.0"
+    mlly: "npm:^1.7.1"
+    pathe: "npm:^1.1.2"
+    pkg-types: "npm:^1.1.1"
+    scule: "npm:^1.3.0"
+    semver: "npm:^7.6.2"
+    ufo: "npm:^1.5.3"
+    unctx: "npm:^2.3.1"
+    unimport: "npm:^3.7.2"
+    untyped: "npm:^1.4.2"
+  checksum: 10c0/358e15b0e2305f41f21f814f88795eb1c0f4eb81bb3965a6d3da9ead7b7b1104e921006b03394733cff021636a1c0c59d19355b29ab97141650d2064f29466f8
+  languageName: node
+  linkType: hard
+
 "@nuxt/schema@npm:3.11.2, @nuxt/schema@npm:^3.11.1, @nuxt/schema@npm:^3.11.2":
   version: 3.11.2
   resolution: "@nuxt/schema@npm:3.11.2"
@@ -1359,6 +1548,26 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@nuxt/schema@npm:3.12.2":
+  version: 3.12.2
+  resolution: "@nuxt/schema@npm:3.12.2"
+  dependencies:
+    compatx: "npm:^0.1.8"
+    consola: "npm:^3.2.3"
+    defu: "npm:^6.1.4"
+    hookable: "npm:^5.5.3"
+    pathe: "npm:^1.1.2"
+    pkg-types: "npm:^1.1.1"
+    scule: "npm:^1.3.0"
+    std-env: "npm:^3.7.0"
+    ufo: "npm:^1.5.3"
+    uncrypto: "npm:^0.1.3"
+    unimport: "npm:^3.7.2"
+    untyped: "npm:^1.4.2"
+  checksum: 10c0/faf17d5d97cd601c4805cc1d5394af24ee8080afd80ef7ae1edb9f926904af4aed03cd67d122fa498832bcae7bdb868e39810a1082129fc1b384bc35dd5275bd
+  languageName: node
+  linkType: hard
+
 "@nuxt/schema@npm:@nuxt/schema-edge@3.8.0-28284309.b3d3d7f4":
   version: 3.8.0-28284309.b3d3d7f4
   resolution: "@nuxt/schema-edge@npm:3.8.0-28284309.b3d3d7f4"
@@ -3745,12 +3954,15 @@ __metadata:
     eslint-plugin-vue: "npm:^9.19.2"
     event-source-polyfill: "npm:^1.0.31"
     file-saver: "npm:^2.0.5"
+    glob: "npm:^10.4.2"
     js-yaml: "npm:^4.1.0"
     jsdom: "npm:^23.0.1"
     libphonenumber-js: "npm:1.10.51"
     lodash: "npm:^4.17.21"
     lodash-es: "npm:^4.17.21"
     nuxt: "npm:^3.11.2"
+    nuxt-prepare: "npm:^2.1.0"
+    nuxt-vitalizer: "npm:^0.10.0"
     pinia: "npm:^2.1.7"
     pinia-orm: "npm:^1.7.2"
     prettier: "npm:^3.1.0"
@@ -4360,6 +4572,17 @@ __metadata:
   languageName: node
   linkType: hard
 
+"bundle-require@npm:^5.0.0":
+  version: 5.0.0
+  resolution: "bundle-require@npm:5.0.0"
+  dependencies:
+    load-tsconfig: "npm:^0.2.3"
+  peerDependencies:
+    esbuild: ">=0.18"
+  checksum: 10c0/92c46df02586e0ebd66ee4831c9b5775adb3c32a43fe2b2aaf7bc675135c141f751de6a9a26b146d64c607c5b40f9eef5f10dce3c364f602d4bed268444c32c6
+  languageName: node
+  linkType: hard
+
 "c12@npm:^1.10.0, c12@npm:^1.4.2":
   version: 1.10.0
   resolution: "c12@npm:1.10.0"
@@ -4380,6 +4603,31 @@ __metadata:
   languageName: node
   linkType: hard
 
+"c12@npm:^1.11.1":
+  version: 1.11.1
+  resolution: "c12@npm:1.11.1"
+  dependencies:
+    chokidar: "npm:^3.6.0"
+    confbox: "npm:^0.1.7"
+    defu: "npm:^6.1.4"
+    dotenv: "npm:^16.4.5"
+    giget: "npm:^1.2.3"
+    jiti: "npm:^1.21.6"
+    mlly: "npm:^1.7.1"
+    ohash: "npm:^1.1.3"
+    pathe: "npm:^1.1.2"
+    perfect-debounce: "npm:^1.0.0"
+    pkg-types: "npm:^1.1.1"
+    rc9: "npm:^2.1.2"
+  peerDependencies:
+    magicast: ^0.3.4
+  peerDependenciesMeta:
+    magicast:
+      optional: true
+  checksum: 10c0/4711a399b8ce54258982ffa4df15c88a1f12bbb23a806ebdb1d1b9e17134a1a3bf65be4ab0095b648bfc8f21646e3d343f6a8f12b130d459c3c7ef13437f5e92
+  languageName: node
+  linkType: hard
+
 "cac@npm:^6.7.14":
   version: 6.7.14
   resolution: "cac@npm:6.7.14"
@@ -4805,6 +5053,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"compatx@npm:^0.1.8":
+  version: 0.1.8
+  resolution: "compatx@npm:0.1.8"
+  checksum: 10c0/042b8ed40cd3041a843836dab730848c1bcea97ebdac207c9a04b4f8af116259a2147fdda0ce823cf161363b4def76f9b60019a1315cb3ea55f991f54b06c40e
+  languageName: node
+  linkType: hard
+
 "compress-commons@npm:^6.0.2":
   version: 6.0.2
   resolution: "compress-commons@npm:6.0.2"
@@ -5247,6 +5502,18 @@ __metadata:
   languageName: node
   linkType: hard
 
+"debug@npm:^4.3.5":
+  version: 4.3.5
+  resolution: "debug@npm:4.3.5"
+  dependencies:
+    ms: "npm:2.1.2"
+  peerDependenciesMeta:
+    supports-color:
+      optional: true
+  checksum: 10c0/082c375a2bdc4f4469c99f325ff458adad62a3fc2c482d59923c260cb08152f34e2659f72b3767db8bb2f21ca81a60a42d1019605a412132d7b9f59363a005cc
+  languageName: node
+  linkType: hard
+
 "decimal.js@npm:^10.4.3":
   version: 10.4.3
   resolution: "decimal.js@npm:10.4.3"
@@ -5876,6 +6143,86 @@ __metadata:
   languageName: node
   linkType: hard
 
+"esbuild@npm:~0.21.4":
+  version: 0.21.5
+  resolution: "esbuild@npm:0.21.5"
+  dependencies:
+    "@esbuild/aix-ppc64": "npm:0.21.5"
+    "@esbuild/android-arm": "npm:0.21.5"
+    "@esbuild/android-arm64": "npm:0.21.5"
+    "@esbuild/android-x64": "npm:0.21.5"
+    "@esbuild/darwin-arm64": "npm:0.21.5"
+    "@esbuild/darwin-x64": "npm:0.21.5"
+    "@esbuild/freebsd-arm64": "npm:0.21.5"
+    "@esbuild/freebsd-x64": "npm:0.21.5"
+    "@esbuild/linux-arm": "npm:0.21.5"
+    "@esbuild/linux-arm64": "npm:0.21.5"
+    "@esbuild/linux-ia32": "npm:0.21.5"
+    "@esbuild/linux-loong64": "npm:0.21.5"
+    "@esbuild/linux-mips64el": "npm:0.21.5"
+    "@esbuild/linux-ppc64": "npm:0.21.5"
+    "@esbuild/linux-riscv64": "npm:0.21.5"
+    "@esbuild/linux-s390x": "npm:0.21.5"
+    "@esbuild/linux-x64": "npm:0.21.5"
+    "@esbuild/netbsd-x64": "npm:0.21.5"
+    "@esbuild/openbsd-x64": "npm:0.21.5"
+    "@esbuild/sunos-x64": "npm:0.21.5"
+    "@esbuild/win32-arm64": "npm:0.21.5"
+    "@esbuild/win32-ia32": "npm:0.21.5"
+    "@esbuild/win32-x64": "npm:0.21.5"
+  dependenciesMeta:
+    "@esbuild/aix-ppc64":
+      optional: true
+    "@esbuild/android-arm":
+      optional: true
+    "@esbuild/android-arm64":
+      optional: true
+    "@esbuild/android-x64":
+      optional: true
+    "@esbuild/darwin-arm64":
+      optional: true
+    "@esbuild/darwin-x64":
+      optional: true
+    "@esbuild/freebsd-arm64":
+      optional: true
+    "@esbuild/freebsd-x64":
+      optional: true
+    "@esbuild/linux-arm":
+      optional: true
+    "@esbuild/linux-arm64":
+      optional: true
+    "@esbuild/linux-ia32":
+      optional: true
+    "@esbuild/linux-loong64":
+      optional: true
+    "@esbuild/linux-mips64el":
+      optional: true
+    "@esbuild/linux-ppc64":
+      optional: true
+    "@esbuild/linux-riscv64":
+      optional: true
+    "@esbuild/linux-s390x":
+      optional: true
+    "@esbuild/linux-x64":
+      optional: true
+    "@esbuild/netbsd-x64":
+      optional: true
+    "@esbuild/openbsd-x64":
+      optional: true
+    "@esbuild/sunos-x64":
+      optional: true
+    "@esbuild/win32-arm64":
+      optional: true
+    "@esbuild/win32-ia32":
+      optional: true
+    "@esbuild/win32-x64":
+      optional: true
+  bin:
+    esbuild: bin/esbuild
+  checksum: 10c0/fa08508adf683c3f399e8a014a6382a6b65542213431e26206c0720e536b31c09b50798747c2a105a4bbba1d9767b8d3615a74c2f7bf1ddf6d836cd11eb672de
+  languageName: node
+  linkType: hard
+
 "escalade@npm:^3.1.1, escalade@npm:^3.1.2":
   version: 3.1.2
   resolution: "escalade@npm:3.1.2"
@@ -6935,7 +7282,16 @@ __metadata:
   languageName: node
   linkType: hard
 
-"giget@npm:^1.2.1":
+"get-tsconfig@npm:^4.7.5":
+  version: 4.7.5
+  resolution: "get-tsconfig@npm:4.7.5"
+  dependencies:
+    resolve-pkg-maps: "npm:^1.0.0"
+  checksum: 10c0/a917dff2ba9ee187c41945736bf9bbab65de31ce5bc1effd76267be483a7340915cff232199406379f26517d2d0a4edcdbcda8cca599c2480a0f2cf1e1de3efa
+  languageName: node
+  linkType: hard
+
+"giget@npm:^1.2.1, giget@npm:^1.2.3":
   version: 1.2.3
   resolution: "giget@npm:1.2.3"
   dependencies:
@@ -7019,6 +7375,22 @@ __metadata:
   languageName: node
   linkType: hard
 
+"glob@npm:^10.4.2":
+  version: 10.4.2
+  resolution: "glob@npm:10.4.2"
+  dependencies:
+    foreground-child: "npm:^3.1.0"
+    jackspeak: "npm:^3.1.2"
+    minimatch: "npm:^9.0.4"
+    minipass: "npm:^7.1.2"
+    package-json-from-dist: "npm:^1.0.0"
+    path-scurry: "npm:^1.11.1"
+  bin:
+    glob: dist/esm/bin.mjs
+  checksum: 10c0/2c7296695fa75a935f3ad17dc62e4e170a8bb8752cf64d328be8992dd6ad40777939003754e10e9741ff8fbe43aa52fba32d6930d0ffa0e3b74bc3fb5eebaa2f
+  languageName: node
+  linkType: hard
+
 "glob@npm:^7.1.3, glob@npm:^7.1.4":
   version: 7.2.3
   resolution: "glob@npm:7.2.3"
@@ -7444,6 +7816,21 @@ __metadata:
   languageName: node
   linkType: hard
 
+"importx@npm:^0.3.1":
+  version: 0.3.7
+  resolution: "importx@npm:0.3.7"
+  dependencies:
+    bundle-require: "npm:^5.0.0"
+    debug: "npm:^4.3.5"
+    esbuild: "npm:^0.20.2"
+    jiti: "npm:^1.21.6"
+    pathe: "npm:^1.1.2"
+    pkg-types: "npm:^1.1.1"
+    tsx: "npm:^4.15.6"
+  checksum: 10c0/967d58940255ff2fd95bf09f037707bc729b1ef0c11167a944291051484b9793477bd3a0cc9c5fba0aa3d71584d9f08daef47d4827c86b7a2efc97303b6d198a
+  languageName: node
+  linkType: hard
+
 "imurmurhash@npm:^0.1.4":
   version: 0.1.4
   resolution: "imurmurhash@npm:0.1.4"
@@ -8006,6 +8393,19 @@ __metadata:
   languageName: node
   linkType: hard
 
+"jackspeak@npm:^3.1.2":
+  version: 3.4.0
+  resolution: "jackspeak@npm:3.4.0"
+  dependencies:
+    "@isaacs/cliui": "npm:^8.0.2"
+    "@pkgjs/parseargs": "npm:^0.11.0"
+  dependenciesMeta:
+    "@pkgjs/parseargs":
+      optional: true
+  checksum: 10c0/7e42d1ea411b4d57d43ea8a6afbca9224382804359cb72626d0fc45bb8db1de5ad0248283c3db45fe73e77210750d4fcc7c2b4fe5d24fda94aaa24d658295c5f
+  languageName: node
+  linkType: hard
+
 "jest-diff@npm:^29.7.0":
   version: 29.7.0
   resolution: "jest-diff@npm:29.7.0"
@@ -8089,6 +8489,15 @@ __metadata:
   languageName: node
   linkType: hard
 
+"jiti@npm:^1.21.6":
+  version: 1.21.6
+  resolution: "jiti@npm:1.21.6"
+  bin:
+    jiti: bin/jiti.js
+  checksum: 10c0/05b9ed58cd30d0c3ccd3c98209339e74f50abd9a17e716f65db46b6a35812103f6bde6e134be7124d01745586bca8cc5dae1d0d952267c3ebe55171949c32e56
+  languageName: node
+  linkType: hard
+
 "js-beautify@npm:^1.14.9, js-beautify@npm:^1.6.14":
   version: 1.15.1
   resolution: "js-beautify@npm:1.15.1"
@@ -8447,6 +8856,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"load-tsconfig@npm:^0.2.3":
+  version: 0.2.5
+  resolution: "load-tsconfig@npm:0.2.5"
+  checksum: 10c0/bf2823dd26389d3497b6567f07435c5a7a58d9df82e879b0b3892f87d8db26900f84c85bc329ef41c0540c0d6a448d1c23ddc64a80f3ff6838b940f3915a3fcb
+  languageName: node
+  linkType: hard
+
 "local-pkg@npm:^0.4.3":
   version: 0.4.3
   resolution: "local-pkg@npm:0.4.3"
@@ -8922,6 +9338,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"minipass@npm:^7.1.2":
+  version: 7.1.2
+  resolution: "minipass@npm:7.1.2"
+  checksum: 10c0/b0fd20bb9fb56e5fa9a8bfac539e8915ae07430a619e4b86ff71f5fc757ef3924b23b2c4230393af1eda647ed3d75739e4e0acb250a6b1eb277cf7f8fe449557
+  languageName: node
+  linkType: hard
+
 "minizlib@npm:^2.1.1, minizlib@npm:^2.1.2":
   version: 2.1.2
   resolution: "minizlib@npm:2.1.2"
@@ -8974,6 +9397,18 @@ __metadata:
   languageName: node
   linkType: hard
 
+"mlly@npm:^1.7.0, mlly@npm:^1.7.1":
+  version: 1.7.1
+  resolution: "mlly@npm:1.7.1"
+  dependencies:
+    acorn: "npm:^8.11.3"
+    pathe: "npm:^1.1.2"
+    pkg-types: "npm:^1.1.1"
+    ufo: "npm:^1.5.3"
+  checksum: 10c0/d836a7b0adff4d118af41fb93ad4d9e57f80e694a681185280ba220a4607603c19e86c80f9a6c57512b04280567f2599e3386081705c5b5fd74c9ddfd571d0fa
+  languageName: node
+  linkType: hard
+
 "mri@npm:^1.2.0":
   version: 1.2.0
   resolution: "mri@npm:1.2.0"
@@ -9460,6 +9895,32 @@ __metadata:
   languageName: node
   linkType: hard
 
+"nuxt-prepare@npm:^2.1.0":
+  version: 2.1.0
+  resolution: "nuxt-prepare@npm:2.1.0"
+  dependencies:
+    "@nuxt/kit": "npm:^3.11.2"
+    defu: "npm:^6.1.4"
+    importx: "npm:^0.3.1"
+    mlly: "npm:^1.7.0"
+    pathe: "npm:^1.1.2"
+    scule: "npm:^1.3.0"
+    type-fest: "npm:^4.18.2"
+  checksum: 10c0/2af5f8cb456eba8ade0863d94f0274536cd6e3f54695aa8717722f642b726f63965cc4cb7a7108f766ac7fd7717595c890c5c90e248ffade780a7ec2369a4906
+  languageName: node
+  linkType: hard
+
+"nuxt-vitalizer@npm:^0.10.0":
+  version: 0.10.0
+  resolution: "nuxt-vitalizer@npm:0.10.0"
+  dependencies:
+    "@nuxt/kit": "npm:^3.12.2"
+    defu: "npm:^6.1.4"
+    knitwork: "npm:^1.1.0"
+  checksum: 10c0/228ea512010178291837d4f98f8d124cdf22068fba65982a4b47b21cdd4aa1fa46ef74777853723633476ed5b0309468b02af350bb42c0b189c91d4d38c22d1d
+  languageName: node
+  linkType: hard
+
 "nuxt@npm:^3.11.2":
   version: 3.11.2
   resolution: "nuxt@npm:3.11.2"
@@ -9783,6 +10244,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"package-json-from-dist@npm:^1.0.0":
+  version: 1.0.0
+  resolution: "package-json-from-dist@npm:1.0.0"
+  checksum: 10c0/e3ffaf6ac1040ab6082a658230c041ad14e72fabe99076a2081bb1d5d41210f11872403fc09082daf4387fc0baa6577f96c9c0e94c90c394fd57794b66aa4033
+  languageName: node
+  linkType: hard
+
 "pacote@npm:^18.0.0":
   version: 18.0.5
   resolution: "pacote@npm:18.0.5"
@@ -9927,6 +10395,16 @@ __metadata:
   languageName: node
   linkType: hard
 
+"path-scurry@npm:^1.11.1":
+  version: 1.11.1
+  resolution: "path-scurry@npm:1.11.1"
+  dependencies:
+    lru-cache: "npm:^10.2.0"
+    minipass: "npm:^5.0.0 || ^6.0.2 || ^7.0.0"
+  checksum: 10c0/32a13711a2a505616ae1cc1b5076801e453e7aae6ac40ab55b388bb91b9d0547a52f5aaceff710ea400205f18691120d4431e520afbe4266b836fadede15872d
+  languageName: node
+  linkType: hard
+
 "path-type@npm:^4.0.0":
   version: 4.0.0
   resolution: "path-type@npm:4.0.0"
@@ -10033,6 +10511,17 @@ __metadata:
   languageName: node
   linkType: hard
 
+"pkg-types@npm:^1.1.1":
+  version: 1.1.1
+  resolution: "pkg-types@npm:1.1.1"
+  dependencies:
+    confbox: "npm:^0.1.7"
+    mlly: "npm:^1.7.0"
+    pathe: "npm:^1.1.2"
+  checksum: 10c0/c7d167935de7207479e5829086040d70bea289f31fc1331f17c83e996a4440115c9deba2aa96de839ea66e1676d083c9ca44b33886f87bffa6b49740b67b6fcb
+  languageName: node
+  linkType: hard
+
 "pluralize@npm:^8.0.0":
   version: 8.0.0
   resolution: "pluralize@npm:8.0.0"
@@ -11140,6 +11629,15 @@ __metadata:
   languageName: node
   linkType: hard
 
+"semver@npm:^7.6.2":
+  version: 7.6.2
+  resolution: "semver@npm:7.6.2"
+  bin:
+    semver: bin/semver.js
+  checksum: 10c0/97d3441e97ace8be4b1976433d1c32658f6afaff09f143e52c593bae7eef33de19e3e369c88bd985ce1042c6f441c80c6803078d1de2a9988080b66684cbb30c
+  languageName: node
+  linkType: hard
+
 "send@npm:0.18.0":
   version: 0.18.0
   resolution: "send@npm:0.18.0"
@@ -12226,6 +12724,22 @@ __metadata:
   languageName: node
   linkType: hard
 
+"tsx@npm:^4.15.6":
+  version: 4.15.7
+  resolution: "tsx@npm:4.15.7"
+  dependencies:
+    esbuild: "npm:~0.21.4"
+    fsevents: "npm:~2.3.3"
+    get-tsconfig: "npm:^4.7.5"
+  dependenciesMeta:
+    fsevents:
+      optional: true
+  bin:
+    tsx: dist/cli.mjs
+  checksum: 10c0/e960f4ee084b48cd3183e65946725fd9b0de4afae32a0fd9cd47416a41259fb2c72838b7aeba26adaecc2d89d70e976add9722e72ea5c876b3b493f137cbbf12
+  languageName: node
+  linkType: hard
+
 "tuf-js@npm:^2.2.0":
   version: 2.2.1
   resolution: "tuf-js@npm:2.2.1"
@@ -12297,6 +12811,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"type-fest@npm:^4.18.2":
+  version: 4.20.1
+  resolution: "type-fest@npm:4.20.1"
+  checksum: 10c0/c31da16fe170a74c5b7e102e70e764ba8ec6ade128e782a56f842aefa07307df5a6353e6b5601d3b30ff2d856d8b955f89813df639e4758fedcf8e426b2d854e
+  languageName: node
+  linkType: hard
+
 "typed-array-buffer@npm:^1.0.2":
   version: 1.0.2
   resolution: "typed-array-buffer@npm:1.0.2"
@@ -12483,6 +13004,27 @@ __metadata:
   languageName: node
   linkType: hard
 
+"unimport@npm:^3.7.2":
+  version: 3.7.2
+  resolution: "unimport@npm:3.7.2"
+  dependencies:
+    "@rollup/pluginutils": "npm:^5.1.0"
+    acorn: "npm:^8.11.3"
+    escape-string-regexp: "npm:^5.0.0"
+    estree-walker: "npm:^3.0.3"
+    fast-glob: "npm:^3.3.2"
+    local-pkg: "npm:^0.5.0"
+    magic-string: "npm:^0.30.10"
+    mlly: "npm:^1.7.0"
+    pathe: "npm:^1.1.2"
+    pkg-types: "npm:^1.1.1"
+    scule: "npm:^1.3.0"
+    strip-literal: "npm:^2.1.0"
+    unplugin: "npm:^1.10.1"
+  checksum: 10c0/d07f41c210854f1b58364bec985882e7c521fc4e7ee46bd7e98c9c797ce14cf0bdf2ab9c2d1fd618bdd188e470bf005d15053727bf5b08a098ccc309313de40e
+  languageName: node
+  linkType: hard
+
 "unique-filename@npm:^3.0.0":
   version: 3.0.0
   resolution: "unique-filename@npm:3.0.0"