@quazardous/qdadm 1.19.5 → 1.19.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@quazardous/qdadm",
3
- "version": "1.19.5",
3
+ "version": "1.19.7",
4
4
  "description": "Vue 3 framework for admin dashboards with PrimeVue",
5
5
  "author": "quazardous",
6
6
  "license": "MIT",
@@ -17,6 +17,7 @@
17
17
  "scripts": {
18
18
  "test": "vitest run",
19
19
  "test:watch": "vitest",
20
+ "gen:i18n-defaults": "node scripts/gen-i18n-defaults.mjs",
20
21
  "type-check": "vue-tsc --noEmit",
21
22
  "lint": "eslint src/",
22
23
  "lint:fix": "eslint src/ --fix",
@@ -291,7 +291,8 @@ export function useOptionsLookup(config: UseOptionsLookupConfig): UseOptionsLook
291
291
  )
292
292
  // When query exactly matches a single option (e.g. dropdown click with
293
293
  // current value), show all options so the user can browse alternatives
294
- result = (filtered.length === 1 && filtered[0].toLowerCase() === q)
294
+ const sole = filtered.length === 1 ? filtered[0] : undefined
295
+ result = (sole !== undefined && sole.toLowerCase() === q)
295
296
  ? encodedStrings
296
297
  : filtered
297
298
  }
@@ -163,8 +163,8 @@ export function applyCacheMethods(EntityManagerClass: { prototype: any }): void
163
163
  // Evict oldest entries by loadedAt
164
164
  const entries = [...items.entries()].sort((a, b) => a[1].loadedAt - b[1].loadedAt)
165
165
  const toRemove = entries.length - maxSize
166
- for (let i = 0; i < toRemove; i++) {
167
- items.delete(entries[i][0])
166
+ for (const [key] of entries.slice(0, toRemove)) {
167
+ items.delete(key)
168
168
  }
169
169
  }
170
170
 
@@ -2,13 +2,17 @@
2
2
  * Default core provider — ships qdadm's `core.*` defaults (EN + FR) as a
3
3
  * `LazyTranslationProvider` configured with a YAML loader.
4
4
  *
5
- * The YAML files (`core.en.yml`, `core.fr.yml`) are imported via Vite's
6
- * `?raw` query so the text is fetched on-demand, then parsed at runtime.
7
- * Splitting the bundle by locale (and, in the future, by domain) keeps the
8
- * provider tree-shakeable and translator-friendly.
5
+ * The YAML text is imported from `core.<locale>.generated.ts` — plain TS
6
+ * modules that `export default` the raw YAML string, generated from the
7
+ * sibling `core.<locale>.yml` source-of-truth (see `scripts/gen-i18n-defaults.mjs`).
8
+ * Using a normal module instead of a Vite `?raw` query keeps the import
9
+ * portable across bundlers and avoids the file:/symlink `server.fs.allow`
10
+ * trap (qdadm #492). The dynamic-import boundary still gives per-locale
11
+ * code-splitting, and the `.yml` files stay translator-friendly.
9
12
  *
10
- * Adding a built-in locale = drop `core.<locale>.yml` next to this file and
11
- * add it to the loader map below.
13
+ * Adding a built-in locale = drop `core.<locale>.yml` next to this file,
14
+ * run `npm run gen:i18n-defaults -w packages/qdadm`, and add the locale to
15
+ * the loader map below.
12
16
  */
13
17
 
14
18
  import { LazyTranslationProvider } from '../LazyTranslationProvider'
@@ -16,8 +20,8 @@ import { createYamlLoader } from '../loaders/yaml'
16
20
 
17
21
  export function createDefaultCoreProvider(): LazyTranslationProvider {
18
22
  const yamlLoader = createYamlLoader({
19
- en: () => import('./core.en.yml?raw'),
20
- fr: () => import('./core.fr.yml?raw'),
23
+ en: () => import('./core.en.generated'),
24
+ fr: () => import('./core.fr.generated'),
21
25
  })
22
26
 
23
27
  return new LazyTranslationProvider({
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Guards the `core.<locale>.yml` → `core.<locale>.generated.ts` pipeline
3
+ * introduced for qdadm #492 (drop the Vite-specific `?raw` import).
4
+ *
5
+ * - Drift guard: the generated module's default export must equal the raw
6
+ * `.yml` text verbatim. The generator wraps the raw text in a template
7
+ * literal, which round-trips back to the original — so if you edit a `.yml`
8
+ * without running `npm run gen:i18n-defaults`, this fails.
9
+ * - End-to-end: the default core provider must load its bundles through the
10
+ * new (non-`?raw`) import path.
11
+ */
12
+ import { readFileSync } from 'node:fs'
13
+ import { fileURLToPath } from 'node:url'
14
+ import { dirname, join } from 'node:path'
15
+ import { describe, it, expect } from 'vitest'
16
+
17
+ import enGenerated from '../core.en.generated'
18
+ import frGenerated from '../core.fr.generated'
19
+ import { createDefaultCoreProvider } from '../DefaultCoreProvider'
20
+
21
+ const defaultsDir = join(dirname(fileURLToPath(import.meta.url)), '..')
22
+ const readYml = (locale: string) =>
23
+ readFileSync(join(defaultsDir, `core.${locale}.yml`), 'utf8')
24
+
25
+ describe('i18n default providers — generated/.yml sync', () => {
26
+ it('core.en.generated.ts round-trips core.en.yml', () => {
27
+ expect(enGenerated).toBe(readYml('en'))
28
+ })
29
+
30
+ it('core.fr.generated.ts round-trips core.fr.yml', () => {
31
+ expect(frGenerated).toBe(readYml('fr'))
32
+ })
33
+
34
+ it('default core provider loads bundles via the generated modules', async () => {
35
+ type CoreBundle = { core: { actions: { save: string } } }
36
+ const provider = createDefaultCoreProvider()
37
+ const en = (await provider.load('en')) as CoreBundle
38
+ const fr = (await provider.load('fr')) as CoreBundle
39
+ expect(en.core.actions.save).toBe('Save')
40
+ expect(fr.core.actions.save).toBe('Enregistrer')
41
+ })
42
+ })
@@ -0,0 +1,58 @@
1
+ /* GENERATED from core.en.yml — DO NOT EDIT.
2
+ * Run `npm run gen:i18n-defaults -w packages/qdadm` to regenerate. */
3
+ export default `core:
4
+ actions:
5
+ save: Save
6
+ cancel: Cancel
7
+ delete: Delete
8
+ edit: Edit
9
+ create: Create
10
+ update: Update
11
+ createAndClose: Create & Close
12
+ updateAndClose: Update & Close
13
+ add: Add
14
+ remove: Remove
15
+ submit: Submit
16
+ reset: Reset
17
+ back: Back
18
+ next: Next
19
+ previous: Previous
20
+ close: Close
21
+ confirm: Confirm
22
+ search: Search
23
+ filter: Filter
24
+ clear: Clear
25
+ refresh: Refresh
26
+ export: Export
27
+ import: Import
28
+ tooltips:
29
+ saveAndContinue: Save and continue editing
30
+ saveAndReturn: Save and return to list
31
+ unsavedChanges: Unsaved changes
32
+ placeholders:
33
+ search: Search…
34
+ noSelection: Click to select…
35
+ fields:
36
+ id: ID
37
+ created_at: Created at
38
+ updated_at: Updated at
39
+ created_by: Created by
40
+ updated_by: Updated by
41
+ messages:
42
+ empty: No items available
43
+ loading: Loading…
44
+ noResults: No results
45
+ noMatching: No matching items
46
+ saved: Saved
47
+ deleted: Deleted
48
+ created: Created
49
+ updated: Updated
50
+ confirmDelete: Are you sure you want to delete this item?
51
+ errors:
52
+ required: Required
53
+ tooShort: Too short (min {min})
54
+ tooLong: Too long (max {max})
55
+ invalid: Invalid value
56
+ notFound: Not found
57
+ unknown: An unknown error occurred
58
+ `
@@ -0,0 +1,58 @@
1
+ /* GENERATED from core.fr.yml — DO NOT EDIT.
2
+ * Run `npm run gen:i18n-defaults -w packages/qdadm` to regenerate. */
3
+ export default `core:
4
+ actions:
5
+ save: Enregistrer
6
+ cancel: Annuler
7
+ delete: Supprimer
8
+ edit: Modifier
9
+ create: Créer
10
+ update: Mettre à jour
11
+ createAndClose: Créer et fermer
12
+ updateAndClose: Mettre à jour et fermer
13
+ add: Ajouter
14
+ remove: Retirer
15
+ submit: Valider
16
+ reset: Réinitialiser
17
+ back: Retour
18
+ next: Suivant
19
+ previous: Précédent
20
+ close: Fermer
21
+ confirm: Confirmer
22
+ search: Rechercher
23
+ filter: Filtrer
24
+ clear: Effacer
25
+ refresh: Actualiser
26
+ export: Exporter
27
+ import: Importer
28
+ tooltips:
29
+ saveAndContinue: Enregistrer et continuer la modification
30
+ saveAndReturn: Enregistrer et revenir à la liste
31
+ unsavedChanges: Modifications non enregistrées
32
+ placeholders:
33
+ search: Rechercher…
34
+ noSelection: Cliquez pour sélectionner…
35
+ fields:
36
+ id: Identifiant
37
+ created_at: Créé le
38
+ updated_at: Modifié le
39
+ created_by: Créé par
40
+ updated_by: Modifié par
41
+ messages:
42
+ empty: Aucun élément
43
+ loading: Chargement…
44
+ noResults: Aucun résultat
45
+ noMatching: Aucune correspondance
46
+ saved: Enregistré
47
+ deleted: Supprimé
48
+ created: Créé
49
+ updated: Modifié
50
+ confirmDelete: Êtes-vous sûr de vouloir supprimer cet élément ?
51
+ errors:
52
+ required: Requis
53
+ tooShort: Trop court (min {min})
54
+ tooLong: Trop long (max {max})
55
+ invalid: Valeur invalide
56
+ notFound: Introuvable
57
+ unknown: Une erreur inconnue est survenue
58
+ `
@@ -5,11 +5,14 @@
5
5
  * `LazyLoader` that resolves the YAML text for the requested locale and
6
6
  * parses it through the `yaml` package.
7
7
  *
8
- * Typical usage with Vite/Vitest `?raw` imports (qdadm-internal default):
8
+ * The importer just needs to resolve to `{ default: <yaml text> }`. qdadm's
9
+ * own defaults import generated `.ts` string modules (see DefaultCoreProvider
10
+ * — bundler-portable, no `?raw`), but a consumer on Vite/Vitest can equally
11
+ * pass `?raw` imports of their own YAML:
9
12
  *
10
13
  * const loader = createYamlLoader({
11
- * en: () => import('./core.en.yml?raw'),
12
- * fr: () => import('./core.fr.yml?raw'),
14
+ * en: () => import('./messages.en.yml?raw'),
15
+ * fr: () => import('./messages.fr.yml?raw'),
13
16
  * })
14
17
  *
15
18
  * The dynamic-import boundary is what gives us code-splitting: the YAML for