@endora-commerce/mod-dictionaries 0.100.0

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.
Files changed (143) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +60 -0
  3. package/dist/admin/api/client.d.ts +76 -0
  4. package/dist/admin/api/client.d.ts.map +1 -0
  5. package/dist/admin/api/client.js +87 -0
  6. package/dist/admin/api/client.js.map +1 -0
  7. package/dist/admin/components/EntryStatusBadges.d.ts +9 -0
  8. package/dist/admin/components/EntryStatusBadges.d.ts.map +1 -0
  9. package/dist/admin/components/EntryStatusBadges.js +8 -0
  10. package/dist/admin/components/EntryStatusBadges.js.map +1 -0
  11. package/dist/admin/components/LanguagePicker.d.ts +8 -0
  12. package/dist/admin/components/LanguagePicker.d.ts.map +1 -0
  13. package/dist/admin/components/LanguagePicker.js +30 -0
  14. package/dist/admin/components/LanguagePicker.js.map +1 -0
  15. package/dist/admin/components/TranslationsDrawer.d.ts +10 -0
  16. package/dist/admin/components/TranslationsDrawer.d.ts.map +1 -0
  17. package/dist/admin/components/TranslationsDrawer.js +90 -0
  18. package/dist/admin/components/TranslationsDrawer.js.map +1 -0
  19. package/dist/admin/index.d.ts +48 -0
  20. package/dist/admin/index.d.ts.map +1 -0
  21. package/dist/admin/index.js +82 -0
  22. package/dist/admin/index.js.map +1 -0
  23. package/dist/admin/pages/AuditPage.d.ts +9 -0
  24. package/dist/admin/pages/AuditPage.d.ts.map +1 -0
  25. package/dist/admin/pages/AuditPage.js +80 -0
  26. package/dist/admin/pages/AuditPage.js.map +1 -0
  27. package/dist/admin/pages/DictionaryPage.d.ts +9 -0
  28. package/dist/admin/pages/DictionaryPage.d.ts.map +1 -0
  29. package/dist/admin/pages/DictionaryPage.js +38 -0
  30. package/dist/admin/pages/DictionaryPage.js.map +1 -0
  31. package/dist/admin/tabs/CountriesTab.d.ts +3 -0
  32. package/dist/admin/tabs/CountriesTab.d.ts.map +1 -0
  33. package/dist/admin/tabs/CountriesTab.js +233 -0
  34. package/dist/admin/tabs/CountriesTab.js.map +1 -0
  35. package/dist/admin/tabs/CurrenciesTab.d.ts +3 -0
  36. package/dist/admin/tabs/CurrenciesTab.d.ts.map +1 -0
  37. package/dist/admin/tabs/CurrenciesTab.js +209 -0
  38. package/dist/admin/tabs/CurrenciesTab.js.map +1 -0
  39. package/dist/admin/tabs/LanguagesTab.d.ts +3 -0
  40. package/dist/admin/tabs/LanguagesTab.d.ts.map +1 -0
  41. package/dist/admin/tabs/LanguagesTab.js +268 -0
  42. package/dist/admin/tabs/LanguagesTab.js.map +1 -0
  43. package/dist/backend/entities/country.entity.d.ts +35 -0
  44. package/dist/backend/entities/country.entity.d.ts.map +1 -0
  45. package/dist/backend/entities/country.entity.js +107 -0
  46. package/dist/backend/entities/country.entity.js.map +1 -0
  47. package/dist/backend/entities/dictionary-translation.entity.d.ts +25 -0
  48. package/dist/backend/entities/dictionary-translation.entity.d.ts.map +1 -0
  49. package/dist/backend/entities/dictionary-translation.entity.js +65 -0
  50. package/dist/backend/entities/dictionary-translation.entity.js.map +1 -0
  51. package/dist/backend/entities/language-country.entity.d.ts +17 -0
  52. package/dist/backend/entities/language-country.entity.d.ts.map +1 -0
  53. package/dist/backend/entities/language-country.entity.js +49 -0
  54. package/dist/backend/entities/language-country.entity.js.map +1 -0
  55. package/dist/backend/index.d.ts +85 -0
  56. package/dist/backend/index.d.ts.map +1 -0
  57. package/dist/backend/index.js +155 -0
  58. package/dist/backend/index.js.map +1 -0
  59. package/dist/backend/routes.admin.d.ts +39 -0
  60. package/dist/backend/routes.admin.d.ts.map +1 -0
  61. package/dist/backend/routes.admin.js +317 -0
  62. package/dist/backend/routes.admin.js.map +1 -0
  63. package/dist/backend/routes.storefront.d.ts +7 -0
  64. package/dist/backend/routes.storefront.d.ts.map +1 -0
  65. package/dist/backend/routes.storefront.js +34 -0
  66. package/dist/backend/routes.storefront.js.map +1 -0
  67. package/dist/backend/seed/countries.d.ts +28 -0
  68. package/dist/backend/seed/countries.d.ts.map +1 -0
  69. package/dist/backend/seed/countries.js +121 -0
  70. package/dist/backend/seed/countries.js.map +1 -0
  71. package/dist/backend/seed/currencies.d.ts +9 -0
  72. package/dist/backend/seed/currencies.d.ts.map +1 -0
  73. package/dist/backend/seed/currencies.js +76 -0
  74. package/dist/backend/seed/currencies.js.map +1 -0
  75. package/dist/backend/seed/language-countries.d.ts +7 -0
  76. package/dist/backend/seed/language-countries.d.ts.map +1 -0
  77. package/dist/backend/seed/language-countries.js +19 -0
  78. package/dist/backend/seed/language-countries.js.map +1 -0
  79. package/dist/backend/seed/translations.pl-PL.d.ts +10 -0
  80. package/dist/backend/seed/translations.pl-PL.d.ts.map +1 -0
  81. package/dist/backend/seed/translations.pl-PL.js +74 -0
  82. package/dist/backend/seed/translations.pl-PL.js.map +1 -0
  83. package/dist/backend/services/country-currency-reference.d.ts +13 -0
  84. package/dist/backend/services/country-currency-reference.d.ts.map +1 -0
  85. package/dist/backend/services/country-currency-reference.js +25 -0
  86. package/dist/backend/services/country-currency-reference.js.map +1 -0
  87. package/dist/backend/services/country-reference-registry.d.ts +30 -0
  88. package/dist/backend/services/country-reference-registry.d.ts.map +1 -0
  89. package/dist/backend/services/country-reference-registry.js +54 -0
  90. package/dist/backend/services/country-reference-registry.js.map +1 -0
  91. package/dist/backend/services/country-service.d.ts +69 -0
  92. package/dist/backend/services/country-service.d.ts.map +1 -0
  93. package/dist/backend/services/country-service.js +193 -0
  94. package/dist/backend/services/country-service.js.map +1 -0
  95. package/dist/backend/services/dictionary-cache.d.ts +27 -0
  96. package/dist/backend/services/dictionary-cache.d.ts.map +1 -0
  97. package/dist/backend/services/dictionary-cache.js +57 -0
  98. package/dist/backend/services/dictionary-cache.js.map +1 -0
  99. package/dist/backend/services/dictionary-read-service.d.ts +36 -0
  100. package/dist/backend/services/dictionary-read-service.d.ts.map +1 -0
  101. package/dist/backend/services/dictionary-read-service.js +232 -0
  102. package/dist/backend/services/dictionary-read-service.js.map +1 -0
  103. package/dist/backend/services/dictionary-validator.d.ts +26 -0
  104. package/dist/backend/services/dictionary-validator.d.ts.map +1 -0
  105. package/dist/backend/services/dictionary-validator.js +72 -0
  106. package/dist/backend/services/dictionary-validator.js.map +1 -0
  107. package/dist/backend/services/dispatch-validator-mode.d.ts +19 -0
  108. package/dist/backend/services/dispatch-validator-mode.d.ts.map +1 -0
  109. package/dist/backend/services/dispatch-validator-mode.js +19 -0
  110. package/dist/backend/services/dispatch-validator-mode.js.map +1 -0
  111. package/dist/backend/services/label-resolver.d.ts +57 -0
  112. package/dist/backend/services/label-resolver.d.ts.map +1 -0
  113. package/dist/backend/services/label-resolver.js +116 -0
  114. package/dist/backend/services/label-resolver.js.map +1 -0
  115. package/dist/backend/services/language-country-service.d.ts +26 -0
  116. package/dist/backend/services/language-country-service.d.ts.map +1 -0
  117. package/dist/backend/services/language-country-service.js +108 -0
  118. package/dist/backend/services/language-country-service.js.map +1 -0
  119. package/dist/backend/services/seed-reconciler.d.ts +22 -0
  120. package/dist/backend/services/seed-reconciler.d.ts.map +1 -0
  121. package/dist/backend/services/seed-reconciler.js +149 -0
  122. package/dist/backend/services/seed-reconciler.js.map +1 -0
  123. package/dist/backend/services/translation-service.d.ts +29 -0
  124. package/dist/backend/services/translation-service.d.ts.map +1 -0
  125. package/dist/backend/services/translation-service.js +99 -0
  126. package/dist/backend/services/translation-service.js.map +1 -0
  127. package/dist/manifest.d.ts +174 -0
  128. package/dist/manifest.d.ts.map +1 -0
  129. package/dist/manifest.js +178 -0
  130. package/dist/manifest.js.map +1 -0
  131. package/dist/migrations/20260506T112634_dictionaries_dictionary_init.d.ts +33 -0
  132. package/dist/migrations/20260506T112634_dictionaries_dictionary_init.d.ts.map +1 -0
  133. package/dist/migrations/20260506T112634_dictionaries_dictionary_init.js +157 -0
  134. package/dist/migrations/20260506T112634_dictionaries_dictionary_init.js.map +1 -0
  135. package/dist/migrations/index.d.ts +27 -0
  136. package/dist/migrations/index.d.ts.map +1 -0
  137. package/dist/migrations/index.js +29 -0
  138. package/dist/migrations/index.js.map +1 -0
  139. package/docs/dictionaries/index.md +89 -0
  140. package/i18n/en.json +88 -0
  141. package/i18n/pl.json +88 -0
  142. package/package.json +99 -0
  143. package/tailwind.css +14 -0
@@ -0,0 +1,157 @@
1
+ import { Migration } from '@mikro-orm/migrations';
2
+ /**
3
+ * Dictionary module — feature 017 / data-model.md.
4
+ *
5
+ * Schema changes (one atomic migration):
6
+ * - new tables: countries, dictionary_translations, language_countries.
7
+ * - ALTER `languages` ADD COLUMN native_label, is_rtl, fallback_code (self-FK).
8
+ * - ALTER `currencies` ADD COLUMN symbol_position, decimal_places (CHECKs).
9
+ * - partial unique index `uniq_countries_one_default` for the "exactly one
10
+ * default" invariant (mirrors the analogous indexes on languages and
11
+ * currencies introduced by migration 012).
12
+ * - partial unique index `uniq_language_countries_primary_per_country`
13
+ * for the "at most one primary language per country" invariant.
14
+ * - btree indexes: `idx_countries_active`, `idx_countries_sort`,
15
+ * `idx_dict_translations_lang`, `idx_language_countries_country`.
16
+ * - FK `countries.default_currency_code` → `currencies(code)` ON DELETE
17
+ * SET NULL — added LAST (after seed inserts run via the runtime
18
+ * SeedReconciler) so the constraint is satisfied for every seeded row
19
+ * without the migration carrying ~250 inline INSERTs.
20
+ *
21
+ * No seed rows in this migration. The countries seed, the Polish
22
+ * translations seed, the en-US/pl-PL language↔country primary associations,
23
+ * and the back-fill of `currencies.symbol_position` / `decimal_places`
24
+ * and `languages.native_label` / `is_rtl` are inserted by the runtime
25
+ * `SeedReconciler` in `services/seed-reconciler.ts` so admin edits across
26
+ * deploys stay safe (R6 + R8). The reconciler is idempotent and runs on
27
+ * every backend boot via the dictionaries module plugin.
28
+ */
29
+ export class Migration20260506T112634DictionariesDictionaryInit extends Migration {
30
+ async up() {
31
+ // ────────────────────────────────────────────────────────────────────
32
+ // 1) countries — ISO 3166-1 alpha-2 territories
33
+ // FK to currencies(code) is added LAST in this migration (step 8).
34
+ // ────────────────────────────────────────────────────────────────────
35
+ this.addSql(`
36
+ create table "countries" (
37
+ "code" varchar(2) not null,
38
+ "alpha3_code" varchar(3) not null,
39
+ "numeric_code" varchar(3) not null,
40
+ "label" varchar(120) not null,
41
+ "region" varchar(32) not null,
42
+ "subregion" varchar(64) null,
43
+ "dial_code" varchar(8) null,
44
+ "is_eu_member" boolean not null default false,
45
+ "default_currency_code" varchar(3) null,
46
+ "is_active" boolean not null default true,
47
+ "is_default" boolean not null default false,
48
+ "sort_order" int not null default 0,
49
+ "created_at" timestamptz not null,
50
+ "updated_at" timestamptz not null,
51
+ constraint "countries_pkey" primary key ("code"),
52
+ constraint "countries_alpha3_uniq" unique ("alpha3_code"),
53
+ constraint "countries_numeric_uniq" unique ("numeric_code"),
54
+ constraint "countries_region_check" check ("region" in
55
+ ('Africa','Americas','Asia','Europe','Oceania','Antarctic'))
56
+ );
57
+ `);
58
+ this.addSql(`create unique index "uniq_countries_one_default" on "countries" ("is_default") where "is_default" = true;`);
59
+ this.addSql(`create index "idx_countries_active" on "countries" ("is_active");`);
60
+ this.addSql(`create index "idx_countries_sort" on "countries" ("sort_order", "label");`);
61
+ // ────────────────────────────────────────────────────────────────────
62
+ // 2) ALTER languages — extended fields (native_label, is_rtl, fallback_code)
63
+ // ────────────────────────────────────────────────────────────────────
64
+ this.addSql(`alter table "languages" add column "native_label" varchar(64) not null default '';`);
65
+ this.addSql(`alter table "languages" add column "is_rtl" boolean not null default false;`);
66
+ this.addSql(`alter table "languages" add column "fallback_code" varchar(12) null;`);
67
+ this.addSql(`
68
+ alter table "languages"
69
+ add constraint "languages_fallback_fk"
70
+ foreign key ("fallback_code") references "languages" ("code")
71
+ on delete set null;
72
+ `);
73
+ // ────────────────────────────────────────────────────────────────────
74
+ // 3) ALTER currencies — extended fields (symbol_position, decimal_places)
75
+ // ────────────────────────────────────────────────────────────────────
76
+ this.addSql(`alter table "currencies" add column "symbol_position" varchar(8) not null default 'suffix';`);
77
+ this.addSql(`alter table "currencies" add column "decimal_places" smallint not null default 2;`);
78
+ this.addSql(`
79
+ alter table "currencies"
80
+ add constraint "currencies_symbol_position_check"
81
+ check ("symbol_position" in ('prefix','suffix'));
82
+ `);
83
+ this.addSql(`
84
+ alter table "currencies"
85
+ add constraint "currencies_decimal_places_check"
86
+ check ("decimal_places" >= 0 and "decimal_places" <= 6);
87
+ `);
88
+ // ────────────────────────────────────────────────────────────────────
89
+ // 4) dictionary_translations — polymorphic per-Language label override
90
+ // (parent-existence enforced at the service layer, R3)
91
+ // ────────────────────────────────────────────────────────────────────
92
+ this.addSql(`
93
+ create table "dictionary_translations" (
94
+ "entry_type" varchar(16) not null,
95
+ "entry_code" varchar(12) not null,
96
+ "language_code" varchar(12) not null,
97
+ "label" varchar(160) not null,
98
+ "created_at" timestamptz not null,
99
+ "updated_at" timestamptz not null,
100
+ constraint "dictionary_translations_pkey"
101
+ primary key ("entry_type", "entry_code", "language_code"),
102
+ constraint "dictionary_translations_lang_fk"
103
+ foreign key ("language_code") references "languages" ("code") on delete cascade,
104
+ constraint "dictionary_translations_entry_type_check"
105
+ check ("entry_type" in ('country','currency','language'))
106
+ );
107
+ `);
108
+ this.addSql(`create index "idx_dict_translations_lang" on "dictionary_translations" ("language_code");`);
109
+ // ────────────────────────────────────────────────────────────────────
110
+ // 5) language_countries — Language ↔ Country M2M with primary flag
111
+ // ────────────────────────────────────────────────────────────────────
112
+ this.addSql(`
113
+ create table "language_countries" (
114
+ "language_code" varchar(12) not null,
115
+ "country_code" varchar(2) not null,
116
+ "is_primary" boolean not null default false,
117
+ "created_at" timestamptz not null,
118
+ constraint "language_countries_pkey"
119
+ primary key ("language_code", "country_code"),
120
+ constraint "language_countries_lang_fk"
121
+ foreign key ("language_code") references "languages" ("code") on delete cascade,
122
+ constraint "language_countries_country_fk"
123
+ foreign key ("country_code") references "countries" ("code") on delete cascade
124
+ );
125
+ `);
126
+ this.addSql(`create unique index "uniq_language_countries_primary_per_country" on "language_countries" ("country_code") where "is_primary" = true;`);
127
+ this.addSql(`create index "idx_language_countries_country" on "language_countries" ("country_code");`);
128
+ // ────────────────────────────────────────────────────────────────────
129
+ // 6) FK countries.default_currency_code → currencies(code)
130
+ // Added LAST so the runtime SeedReconciler can backfill
131
+ // `default_currency_code` referring to currencies that may not yet
132
+ // exist when the migration runs against an empty database. The FK
133
+ // itself is enforced from this point forward.
134
+ // ────────────────────────────────────────────────────────────────────
135
+ this.addSql(`
136
+ alter table "countries"
137
+ add constraint "countries_default_currency_fk"
138
+ foreign key ("default_currency_code") references "currencies" ("code")
139
+ on delete set null;
140
+ `);
141
+ }
142
+ async down() {
143
+ this.addSql(`alter table "countries" drop constraint if exists "countries_default_currency_fk";`);
144
+ this.addSql(`drop table if exists "language_countries" cascade;`);
145
+ this.addSql(`drop table if exists "dictionary_translations" cascade;`);
146
+ this.addSql(`alter table "currencies" drop constraint if exists "currencies_decimal_places_check";`);
147
+ this.addSql(`alter table "currencies" drop constraint if exists "currencies_symbol_position_check";`);
148
+ this.addSql(`alter table "currencies" drop column if exists "decimal_places";`);
149
+ this.addSql(`alter table "currencies" drop column if exists "symbol_position";`);
150
+ this.addSql(`alter table "languages" drop constraint if exists "languages_fallback_fk";`);
151
+ this.addSql(`alter table "languages" drop column if exists "fallback_code";`);
152
+ this.addSql(`alter table "languages" drop column if exists "is_rtl";`);
153
+ this.addSql(`alter table "languages" drop column if exists "native_label";`);
154
+ this.addSql(`drop table if exists "countries" cascade;`);
155
+ }
156
+ }
157
+ //# sourceMappingURL=20260506T112634_dictionaries_dictionary_init.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"20260506T112634_dictionaries_dictionary_init.js","sourceRoot":"","sources":["../../src/migrations/20260506T112634_dictionaries_dictionary_init.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAElD;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,MAAM,OAAO,kDAAmD,SAAQ,SAAS;IACtE,KAAK,CAAC,EAAE;QACf,uEAAuE;QACvE,gDAAgD;QAChD,sEAAsE;QACtE,uEAAuE;QACvE,IAAI,CAAC,MAAM,CAAC;;;;;;;;;;;;;;;;;;;;;;KAsBX,CAAC,CAAC;QACH,IAAI,CAAC,MAAM,CACT,2GAA2G,CAC5G,CAAC;QACF,IAAI,CAAC,MAAM,CAAC,mEAAmE,CAAC,CAAC;QACjF,IAAI,CAAC,MAAM,CAAC,2EAA2E,CAAC,CAAC;QAEzF,uEAAuE;QACvE,6EAA6E;QAC7E,uEAAuE;QACvE,IAAI,CAAC,MAAM,CAAC,oFAAoF,CAAC,CAAC;QAClG,IAAI,CAAC,MAAM,CAAC,6EAA6E,CAAC,CAAC;QAC3F,IAAI,CAAC,MAAM,CAAC,sEAAsE,CAAC,CAAC;QACpF,IAAI,CAAC,MAAM,CAAC;;;;;KAKX,CAAC,CAAC;QAEH,uEAAuE;QACvE,0EAA0E;QAC1E,uEAAuE;QACvE,IAAI,CAAC,MAAM,CACT,6FAA6F,CAC9F,CAAC;QACF,IAAI,CAAC,MAAM,CAAC,mFAAmF,CAAC,CAAC;QACjG,IAAI,CAAC,MAAM,CAAC;;;;KAIX,CAAC,CAAC;QACH,IAAI,CAAC,MAAM,CAAC;;;;KAIX,CAAC,CAAC;QAEH,uEAAuE;QACvE,uEAAuE;QACvE,0DAA0D;QAC1D,uEAAuE;QACvE,IAAI,CAAC,MAAM,CAAC;;;;;;;;;;;;;;;KAeX,CAAC,CAAC;QACH,IAAI,CAAC,MAAM,CACT,2FAA2F,CAC5F,CAAC;QAEF,uEAAuE;QACvE,mEAAmE;QACnE,uEAAuE;QACvE,IAAI,CAAC,MAAM,CAAC;;;;;;;;;;;;;KAaX,CAAC,CAAC;QACH,IAAI,CAAC,MAAM,CACT,uIAAuI,CACxI,CAAC;QACF,IAAI,CAAC,MAAM,CACT,yFAAyF,CAC1F,CAAC;QAEF,uEAAuE;QACvE,2DAA2D;QAC3D,2DAA2D;QAC3D,sEAAsE;QACtE,qEAAqE;QACrE,iDAAiD;QACjD,uEAAuE;QACvE,IAAI,CAAC,MAAM,CAAC;;;;;KAKX,CAAC,CAAC;IACL,CAAC;IAEQ,KAAK,CAAC,IAAI;QACjB,IAAI,CAAC,MAAM,CAAC,oFAAoF,CAAC,CAAC;QAClG,IAAI,CAAC,MAAM,CAAC,oDAAoD,CAAC,CAAC;QAClE,IAAI,CAAC,MAAM,CAAC,yDAAyD,CAAC,CAAC;QACvE,IAAI,CAAC,MAAM,CAAC,uFAAuF,CAAC,CAAC;QACrG,IAAI,CAAC,MAAM,CAAC,wFAAwF,CAAC,CAAC;QACtG,IAAI,CAAC,MAAM,CAAC,kEAAkE,CAAC,CAAC;QAChF,IAAI,CAAC,MAAM,CAAC,mEAAmE,CAAC,CAAC;QACjF,IAAI,CAAC,MAAM,CAAC,4EAA4E,CAAC,CAAC;QAC1F,IAAI,CAAC,MAAM,CAAC,gEAAgE,CAAC,CAAC;QAC9E,IAAI,CAAC,MAAM,CAAC,yDAAyD,CAAC,CAAC;QACvE,IAAI,CAAC,MAAM,CAAC,+DAA+D,CAAC,CAAC;QAC7E,IAAI,CAAC,MAAM,CAAC,2CAA2C,CAAC,CAAC;IAC3D,CAAC;CACF"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * The `./migrations` subpath — every migration class this module owns, as one
3
+ * ordered `migrations` array.
4
+ *
5
+ * The array is what the platform reads when this module is **installed**:
6
+ * `src/packages/package-runtime.ts` takes `exported['migrations']` and refuses
7
+ * the package outright when it is absent (D-168).
8
+ *
9
+ * Listed in ascending timestamp, which is the order of this module's own
10
+ * migrations and of nothing else (feature 081): a manifest `dependencies` array
11
+ * is the only thing ordering this block against another module's.
12
+ *
13
+ * The **named** exports stay beside the array, and the asymmetry with
14
+ * `./backend` — which publishes an array and no named class (D-168) — is
15
+ * deliberate. `db/migrations-registry.generated.ts` imports each class by name
16
+ * from this specifier, and a migration class name is contract in a way an entity
17
+ * class name is not: `mikro_orm_migrations` persists it, so it is a string every
18
+ * already-migrated database holds.
19
+ *
20
+ * A class that is in neither the array nor the barrel is a migration that does
21
+ * not run: `migration:pending` reports nothing pending and the first symptom is
22
+ * a query against a table nobody created.
23
+ */
24
+ import { Migration20260506T112634DictionariesDictionaryInit } from './20260506T112634_dictionaries_dictionary_init.js';
25
+ export declare const migrations: (typeof Migration20260506T112634DictionariesDictionaryInit)[];
26
+ export { Migration20260506T112634DictionariesDictionaryInit, };
27
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/migrations/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EAAE,kDAAkD,EAAE,MAAM,mDAAmD,CAAC;AAEvH,eAAO,MAAM,UAAU,+DAEtB,CAAC;AAEF,OAAO,EACL,kDAAkD,GACnD,CAAC"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * The `./migrations` subpath — every migration class this module owns, as one
3
+ * ordered `migrations` array.
4
+ *
5
+ * The array is what the platform reads when this module is **installed**:
6
+ * `src/packages/package-runtime.ts` takes `exported['migrations']` and refuses
7
+ * the package outright when it is absent (D-168).
8
+ *
9
+ * Listed in ascending timestamp, which is the order of this module's own
10
+ * migrations and of nothing else (feature 081): a manifest `dependencies` array
11
+ * is the only thing ordering this block against another module's.
12
+ *
13
+ * The **named** exports stay beside the array, and the asymmetry with
14
+ * `./backend` — which publishes an array and no named class (D-168) — is
15
+ * deliberate. `db/migrations-registry.generated.ts` imports each class by name
16
+ * from this specifier, and a migration class name is contract in a way an entity
17
+ * class name is not: `mikro_orm_migrations` persists it, so it is a string every
18
+ * already-migrated database holds.
19
+ *
20
+ * A class that is in neither the array nor the barrel is a migration that does
21
+ * not run: `migration:pending` reports nothing pending and the first symptom is
22
+ * a query against a table nobody created.
23
+ */
24
+ import { Migration20260506T112634DictionariesDictionaryInit } from './20260506T112634_dictionaries_dictionary_init.js';
25
+ export const migrations = [
26
+ Migration20260506T112634DictionariesDictionaryInit,
27
+ ];
28
+ export { Migration20260506T112634DictionariesDictionaryInit, };
29
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/migrations/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EAAE,kDAAkD,EAAE,MAAM,mDAAmD,CAAC;AAEvH,MAAM,CAAC,MAAM,UAAU,GAAG;IACxB,kDAAkD;CACnD,CAAC;AAEF,OAAO,EACL,kDAAkD,GACnD,CAAC"}
@@ -0,0 +1,89 @@
1
+ ---
2
+ title: Dictionary
3
+ sidebar_position: 1
4
+ description: Seeded reference data — Countries, Currencies, Languages — with admin reordering
5
+ ---
6
+
7
+ # Dictionary
8
+
9
+ The Dictionary module is the platform registry for country, currency, and
10
+ language codes. It gives operators one Admin UI for the common reference data
11
+ used by addresses, tax rules, warehouses, sales channels, promotions,
12
+ megamenu bindings, and blog language scopes.
13
+
14
+ The module does not replace the legacy Languages and Currencies HTTP surfaces.
15
+ Those endpoints stay available for backward compatibility, while new admin and
16
+ storefront work should read through the Dictionary module.
17
+
18
+ ## Operator Workflow
19
+
20
+ Open `Admin -> Operations -> Dictionary`. The page has three tabs:
21
+
22
+ - **Countries** — ISO country code, active flag, storefront visibility, default
23
+ label, localized labels, and allowed languages for each country.
24
+ - **Currencies** — ISO currency code, symbol, decimal precision, active flag,
25
+ storefront visibility, default label, and localized labels.
26
+ - **Languages** — BCP-47 language code, native name, active flag, storefront
27
+ visibility, default label, and localized labels.
28
+
29
+ Deactivating an entry prevents new writes from selecting that code, but
30
+ historical records can keep reading the existing value. This is intentional:
31
+ orders, addresses, content scopes, and configuration records must remain
32
+ auditable after a code is retired from active use.
33
+
34
+ Localized labels are edited from each row. Storefront registry reads choose the
35
+ requested locale when present and fall back to the default label when a
36
+ translation is missing.
37
+
38
+ ## Storefront Registry
39
+
40
+ Storefront clients read the combined registry through:
41
+
42
+ ```http
43
+ GET /api/v1/dictionary?locale=pl-PL
44
+ ```
45
+
46
+ The response contains only entries that are active and visible on the
47
+ storefront. It is cached in Redis when Redis is available. Admin writes and the
48
+ operator cache-invalidation endpoint clear that registry cache.
49
+
50
+ Use the registry for UI pickers instead of hard-coded country, currency, or
51
+ language lists. The module ships shared picker components for admin and
52
+ storefront code paths.
53
+
54
+ ## Validator Port
55
+
56
+ Backend consumers receive the shared validator from the composition root:
57
+
58
+ ```ts
59
+ dictionaryValidator: dictionaries.handle.validator
60
+ ```
61
+
62
+ The port exposes:
63
+
64
+ - `validateCountryCode(code, mode)`
65
+ - `validateCurrencyCode(code, mode)`
66
+ - `validateLanguageCode(code, mode)`
67
+
68
+ Use `create-or-change` for a new code assignment. Use `unchanged` only when an
69
+ update keeps the same historical code already stored on that record. Unknown
70
+ codes are always rejected. Inactive codes are rejected for new assignments and
71
+ accepted only for unchanged historical values.
72
+
73
+ The validator has a short in-process LRU. Dictionary writes invalidate the LRU
74
+ and Redis registry cache together.
75
+
76
+ ## Extension Points
77
+
78
+ When a module stores a country, currency, or language code:
79
+
80
+ 1. Accept the validator through the module plugin options.
81
+ 2. Validate at the service boundary before writing.
82
+ 3. Convert `DictionaryReferenceError` into a `409` response with the affected
83
+ field path.
84
+ 4. Preserve historical reads; do not cascade-edit consumer rows when a
85
+ dictionary entry is deactivated.
86
+
87
+ Settings currently has no typed country/currency/language setting value kind,
88
+ so its dictionary validator option is reserved for future metadata-driven
89
+ settings.
package/i18n/en.json ADDED
@@ -0,0 +1,88 @@
1
+ {
2
+ "languagePicker.selectLanguage": "Select language",
3
+ "languagePicker.inactiveGroup": "Inactive",
4
+ "languagePicker.inactiveLabel": "{label} (inactive)",
5
+ "badge.default": "Default",
6
+ "badge.active": "Active",
7
+ "badge.inactive": "Inactive",
8
+ "badge.translationsPending": "Translations pending",
9
+ "badge.translationsComplete": "Translations complete",
10
+ "badge.translationsPartial": "Translations partial",
11
+ "translations.title": "Translations",
12
+ "translations.inactive": "(inactive)",
13
+ "translations.save": "Save",
14
+ "translations.remove": "Remove",
15
+ "translations.noLanguages": "No languages registered.",
16
+ "translations.confirmRemove": "Remove {languageCode} translation?",
17
+ "translations.error.load": "Failed to load translations.",
18
+ "translations.error.save": "Save failed.",
19
+ "translations.error.remove": "Remove failed.",
20
+ "audit.page.title": "Dictionary audit",
21
+ "audit.page.description": "Unresolved country, currency, and language references across platform consumers.",
22
+ "audit.refresh": "Refresh",
23
+ "audit.copyCsv": "Copy CSV",
24
+ "audit.invalidateCache": "Invalidate cache",
25
+ "audit.generatedAt": "Generated {date}",
26
+ "audit.orphanReferences": "Orphan references",
27
+ "audit.loading": "Loading",
28
+ "audit.groupCount": "{count} groups",
29
+ "audit.column.dictionary": "Dictionary",
30
+ "audit.column.consumer": "Consumer",
31
+ "audit.column.table": "Table",
32
+ "audit.column.column": "Column",
33
+ "audit.column.code": "Code",
34
+ "audit.column.rows": "Rows",
35
+ "audit.loadingReport": "Loading audit report.",
36
+ "audit.empty": "No unresolved references found.",
37
+ "audit.info.copied": "Copied audit report as CSV.",
38
+ "audit.info.invalidated": "Dictionary cache invalidated.",
39
+ "audit.error.load": "Failed to load audit report.",
40
+ "audit.error.copy": "Could not copy CSV to clipboard.",
41
+ "audit.error.invalidate": "Cache invalidation failed.",
42
+ "action.moveUp": "Move up",
43
+ "action.moveDown": "Move down",
44
+ "action.edit": "Edit",
45
+ "action.delete": "Delete",
46
+ "action.setDefault": "Set default",
47
+ "countries.title": "Countries",
48
+ "countries.searchPlaceholder": "Search code, label, region",
49
+ "countries.addCountry": "Add country",
50
+ "countries.loading": "Loading countries…",
51
+ "countries.editor.newCountry": "New country",
52
+ "countries.editor.editTitle": "Edit {code}",
53
+ "countries.editor.title": "Country editor",
54
+ "countries.editor.selectHint": "Select a country or create a new one.",
55
+ "countries.editor.euMember": "EU member",
56
+ "currencies.title": "Currencies",
57
+ "currencies.searchPlaceholder": "Search code, label, symbol",
58
+ "currencies.addCurrency": "Add currency",
59
+ "currencies.loading": "Loading currencies…",
60
+ "currencies.decimalPlaces": "{count} decimal places",
61
+ "currencies.editor.newCurrency": "New currency",
62
+ "currencies.editor.editTitle": "Edit {code}",
63
+ "currencies.editor.title": "Currency editor",
64
+ "currencies.editor.selectHint": "Select a currency or create a new one.",
65
+ "languages.title": "Languages",
66
+ "languages.searchPlaceholder": "Search code or label",
67
+ "languages.addLanguage": "Add language",
68
+ "languages.loading": "Loading languages…",
69
+ "languages.editor.newLanguage": "New language",
70
+ "languages.editor.editTitle": "Edit {code}",
71
+ "languages.editor.title": "Language editor",
72
+ "languages.editor.selectHint": "Select a language or create a new one.",
73
+ "languages.editor.associatedCountries": "Associated countries",
74
+ "errors.DICTIONARY_ENTRY_NOT_FOUND": "Dictionary Entry Not Found.",
75
+ "errors.DICTIONARY_ENTRY_INACTIVE": "Dictionary Entry Inactive.",
76
+ "errors.DICTIONARY_ENTRY_HAS_DEPENDENTS": "Dictionary Entry Has Dependents.",
77
+ "errors.DICTIONARY_LAST_ACTIVE_ENTRY": "Dictionary Last Active Entry.",
78
+ "errors.DICTIONARY_DEFAULT_CANNOT_BE_DEACTIVATED": "Dictionary Default Cannot Be Deactivated.",
79
+ "errors.DICTIONARY_FALLBACK_CYCLE": "Dictionary Fallback Cycle.",
80
+ "errors.DICTIONARY_CODE_IMMUTABLE": "Dictionary Code Immutable.",
81
+ "errors.DICTIONARY_TRANSLATION_LANGUAGE_INACTIVE": "Dictionary Translation Language Inactive.",
82
+ "nav.dictionary.label": "Dictionary",
83
+ "nav.dictionaryAudit.label": "Dictionary audit",
84
+ "actions.openDictionary.label": "Dictionary",
85
+ "actions.openDictionary.description": "Countries, currencies, languages",
86
+ "actions.openDictionaryAudit.label": "Dictionary audit",
87
+ "actions.openDictionaryAudit.description": "Unresolved registry references"
88
+ }
package/i18n/pl.json ADDED
@@ -0,0 +1,88 @@
1
+ {
2
+ "languagePicker.selectLanguage": "Wybierz język",
3
+ "languagePicker.inactiveGroup": "Nieaktywne",
4
+ "languagePicker.inactiveLabel": "{label} (nieaktywny)",
5
+ "badge.default": "Domyślny",
6
+ "badge.active": "Aktywny",
7
+ "badge.inactive": "Nieaktywny",
8
+ "badge.translationsPending": "Tłumaczenia oczekujące",
9
+ "badge.translationsComplete": "Tłumaczenia kompletne",
10
+ "badge.translationsPartial": "Tłumaczenia częściowe",
11
+ "translations.title": "Tłumaczenia",
12
+ "translations.inactive": "(nieaktywny)",
13
+ "translations.save": "Zapisz",
14
+ "translations.remove": "Usuń",
15
+ "translations.noLanguages": "Brak zarejestrowanych języków.",
16
+ "translations.confirmRemove": "Usunąć tłumaczenie dla języka {languageCode}?",
17
+ "translations.error.load": "Nie udało się załadować tłumaczeń.",
18
+ "translations.error.save": "Zapis nie powiódł się.",
19
+ "translations.error.remove": "Usunięcie nie powiodło się.",
20
+ "audit.page.title": "Audyt słowników",
21
+ "audit.page.description": "Nierozwiązane referencje krajów, walut i języków u konsumentów platformy.",
22
+ "audit.refresh": "Odśwież",
23
+ "audit.copyCsv": "Kopiuj CSV",
24
+ "audit.invalidateCache": "Unieważnij cache",
25
+ "audit.generatedAt": "Wygenerowano {date}",
26
+ "audit.orphanReferences": "Sieroce referencje",
27
+ "audit.loading": "Ładowanie",
28
+ "audit.groupCount": "{count} grup",
29
+ "audit.column.dictionary": "Słownik",
30
+ "audit.column.consumer": "Konsument",
31
+ "audit.column.table": "Tabela",
32
+ "audit.column.column": "Kolumna",
33
+ "audit.column.code": "Kod",
34
+ "audit.column.rows": "Wierszy",
35
+ "audit.loadingReport": "Ładowanie raportu audytu.",
36
+ "audit.empty": "Nie znaleziono nierozwiązanych referencji.",
37
+ "audit.info.copied": "Skopiowano raport audytu jako CSV.",
38
+ "audit.info.invalidated": "Cache słownika unieważniony.",
39
+ "audit.error.load": "Nie udało się załadować raportu audytu.",
40
+ "audit.error.copy": "Nie udało się skopiować CSV do schowka.",
41
+ "audit.error.invalidate": "Unieważnienie cache nie powiodło się.",
42
+ "action.moveUp": "Przesuń w górę",
43
+ "action.moveDown": "Przesuń w dół",
44
+ "action.edit": "Edytuj",
45
+ "action.delete": "Usuń",
46
+ "action.setDefault": "Ustaw jako domyślny",
47
+ "countries.title": "Kraje",
48
+ "countries.searchPlaceholder": "Szukaj kodu, nazwy, regionu",
49
+ "countries.addCountry": "Dodaj kraj",
50
+ "countries.loading": "Ładowanie krajów…",
51
+ "countries.editor.newCountry": "Nowy kraj",
52
+ "countries.editor.editTitle": "Edytuj {code}",
53
+ "countries.editor.title": "Edytor krajów",
54
+ "countries.editor.selectHint": "Wybierz kraj lub utwórz nowy.",
55
+ "countries.editor.euMember": "Członek UE",
56
+ "currencies.title": "Waluty",
57
+ "currencies.searchPlaceholder": "Szukaj kodu, nazwy, symbolu",
58
+ "currencies.addCurrency": "Dodaj walutę",
59
+ "currencies.loading": "Ładowanie walut…",
60
+ "currencies.decimalPlaces": "{count} miejsc dziesiętnych",
61
+ "currencies.editor.newCurrency": "Nowa waluta",
62
+ "currencies.editor.editTitle": "Edytuj {code}",
63
+ "currencies.editor.title": "Edytor walut",
64
+ "currencies.editor.selectHint": "Wybierz walutę lub utwórz nową.",
65
+ "languages.title": "Języki",
66
+ "languages.searchPlaceholder": "Szukaj kodu lub nazwy",
67
+ "languages.addLanguage": "Dodaj język",
68
+ "languages.loading": "Ładowanie języków…",
69
+ "languages.editor.newLanguage": "Nowy język",
70
+ "languages.editor.editTitle": "Edytuj {code}",
71
+ "languages.editor.title": "Edytor języków",
72
+ "languages.editor.selectHint": "Wybierz język lub utwórz nowy.",
73
+ "languages.editor.associatedCountries": "Powiązane kraje",
74
+ "errors.DICTIONARY_ENTRY_NOT_FOUND": "Błąd: dictionary entry not found.",
75
+ "errors.DICTIONARY_ENTRY_INACTIVE": "Błąd: dictionary entry inactive.",
76
+ "errors.DICTIONARY_ENTRY_HAS_DEPENDENTS": "Błąd: dictionary entry has dependents.",
77
+ "errors.DICTIONARY_LAST_ACTIVE_ENTRY": "Błąd: dictionary last active entry.",
78
+ "errors.DICTIONARY_DEFAULT_CANNOT_BE_DEACTIVATED": "Błąd: dictionary default cannot be deactivated.",
79
+ "errors.DICTIONARY_FALLBACK_CYCLE": "Błąd: dictionary fallback cycle.",
80
+ "errors.DICTIONARY_CODE_IMMUTABLE": "Błąd: dictionary code immutable.",
81
+ "errors.DICTIONARY_TRANSLATION_LANGUAGE_INACTIVE": "Błąd: dictionary translation language inactive.",
82
+ "nav.dictionary.label": "Słownik",
83
+ "nav.dictionaryAudit.label": "Audyt słownika",
84
+ "actions.openDictionary.label": "Słownik",
85
+ "actions.openDictionary.description": "Kraje, waluty, języki",
86
+ "actions.openDictionaryAudit.label": "Audyt słownika",
87
+ "actions.openDictionaryAudit.description": "Nierozwiązane referencje rejestrów"
88
+ }
package/package.json ADDED
@@ -0,0 +1,99 @@
1
+ {
2
+ "name": "@endora-commerce/mod-dictionaries",
3
+ "version": "0.100.0",
4
+ "type": "module",
5
+ "sideEffects": false,
6
+ "description": "Cross-module registry of countries, languages, and currencies — backs every dropdown, validator, and address-form picker on the storefront and admin.",
7
+ "license": "MIT",
8
+ "endora": {
9
+ "type": "module",
10
+ "id": "dictionaries"
11
+ },
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "git+https://github.com/endora-commerce/endora-commerce.git",
15
+ "directory": "packages/modules/dictionaries"
16
+ },
17
+ "publishConfig": {
18
+ "access": "public"
19
+ },
20
+ "exports": {
21
+ ".": {
22
+ "types": "./dist/manifest.d.ts",
23
+ "default": "./dist/manifest.js"
24
+ },
25
+ "./backend": {
26
+ "types": "./dist/backend/index.d.ts",
27
+ "default": "./dist/backend/index.js"
28
+ },
29
+ "./migrations": {
30
+ "types": "./dist/migrations/index.d.ts",
31
+ "default": "./dist/migrations/index.js"
32
+ },
33
+ "./admin": {
34
+ "types": "./dist/admin/index.d.ts",
35
+ "default": "./dist/admin/index.js"
36
+ },
37
+ "./tailwind.css": "./tailwind.css",
38
+ "./package.json": "./package.json"
39
+ },
40
+ "files": [
41
+ "dist",
42
+ "i18n",
43
+ "docs",
44
+ "tailwind.css"
45
+ ],
46
+ "engines": {
47
+ "node": ">=22.18.0"
48
+ },
49
+ "peerDependencies": {
50
+ "@mikro-orm/core": "^6",
51
+ "@mikro-orm/migrations": "^6",
52
+ "@mikro-orm/postgresql": "^6",
53
+ "fastify": "^5",
54
+ "ioredis": "^5",
55
+ "lucide-react": "^1",
56
+ "react": "^19",
57
+ "react-router-dom": "^7",
58
+ "zod": "^4",
59
+ "@endora-commerce/admin-kit": "0.100.0",
60
+ "@endora-commerce/contracts": "0.100.0",
61
+ "@endora-commerce/platform": "0.100.0"
62
+ },
63
+ "peerDependenciesMeta": {
64
+ "@endora-commerce/admin-kit": {
65
+ "optional": true
66
+ },
67
+ "lucide-react": {
68
+ "optional": true
69
+ },
70
+ "react": {
71
+ "optional": true
72
+ },
73
+ "react-router-dom": {
74
+ "optional": true
75
+ }
76
+ },
77
+ "devDependencies": {
78
+ "@mikro-orm/core": "^6.6.13",
79
+ "@mikro-orm/migrations": "^6.6.13",
80
+ "@mikro-orm/postgresql": "^6.6.13",
81
+ "@types/node": "^22.9.0",
82
+ "@types/react": "^19.2.14",
83
+ "fastify": "^5.12.5",
84
+ "ioredis": "^5.10.1",
85
+ "lucide-react": "^1.11.0",
86
+ "react": "^19.2.5",
87
+ "react-router-dom": "^7.18.2",
88
+ "typescript": "^5.9.3",
89
+ "zod": "^4.2.0",
90
+ "@endora-commerce/admin-kit": "0.100.0",
91
+ "@endora-commerce/platform": "0.100.0",
92
+ "@endora-commerce/contracts": "0.100.0"
93
+ },
94
+ "scripts": {
95
+ "build": "tsc -p tsconfig.build.json && tsc -p tsconfig.ui.json",
96
+ "typecheck": "tsc -p tsconfig.json && tsc -p tsconfig.ui.json --noEmit",
97
+ "lint": "eslint src"
98
+ }
99
+ }
package/tailwind.css ADDED
@@ -0,0 +1,14 @@
1
+ /* @endora-commerce/mod-dictionaries — AUTO-GENERATED by `pnpm --filter backend run manifests:generate`.
2
+ *
3
+ * The `@source` directives this package asks its host to scan
4
+ * (`specs/110-instance-repository/contracts/admin-stylesheet-composition.md` R1).
5
+ * They resolve relative to **this file**, so they hold wherever the package is
6
+ * installed — a workspace link here, `node_modules` in a client's instance.
7
+ *
8
+ * The `dist` line is what a published tarball ships and is what an instance
9
+ * scans; the `src` line is inert there and is what keeps `pnpm --filter admin
10
+ * run dev` reading source in this repository. Do not edit: run
11
+ * `pnpm --filter backend run manifests:generate`.
12
+ */
13
+ @source "./dist/admin";
14
+ @source "./src/admin";