@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 @@
1
+ {"version":3,"file":"seed-reconciler.js","sourceRoot":"","sources":["../../../src/backend/services/seed-reconciler.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ import type { EntityManager } from '@mikro-orm/postgresql';
2
+ import { type CurrencyReadPort, type DictionaryEntryType, type LanguageReadPort } from '@endora-commerce/contracts';
3
+ import { DictionaryTranslation } from '../entities/dictionary-translation.entity.js';
4
+ import type { AuditPort } from '@endora-commerce/platform/kernel';
5
+ export interface UpsertTranslationInput {
6
+ entryType: DictionaryEntryType;
7
+ entryCode: string;
8
+ languageCode: string;
9
+ label: string;
10
+ }
11
+ export declare class TranslationService {
12
+ private readonly emFactory;
13
+ private readonly currencies;
14
+ private readonly languages;
15
+ private readonly invalidateDictionaryCache?;
16
+ private readonly auditLog?;
17
+ /**
18
+ * Feature 075, Phase C — a translation's parent may be a country (this
19
+ * module's row), a currency or a language (not). The two that are not are
20
+ * read through their owners' ports, so a translation cannot be attached to a
21
+ * parent the platform is no longer serving.
22
+ */
23
+ constructor(emFactory: () => EntityManager, currencies: CurrencyReadPort, languages: LanguageReadPort, invalidateDictionaryCache?: (() => Promise<void>) | undefined, auditLog?: AuditPort | undefined);
24
+ upsert(input: UpsertTranslationInput): Promise<DictionaryTranslation>;
25
+ remove(entryType: DictionaryEntryType, entryCode: string, languageCode: string): Promise<void>;
26
+ listForEntry(entryType: DictionaryEntryType, entryCode: string): Promise<DictionaryTranslation[]>;
27
+ private assertParentExists;
28
+ }
29
+ //# sourceMappingURL=translation-service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"translation-service.d.ts","sourceRoot":"","sources":["../../../src/backend/services/translation-service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,EAEL,KAAK,gBAAgB,EACrB,KAAK,mBAAmB,EACxB,KAAK,gBAAgB,EACtB,MAAM,4BAA4B,CAAC;AAGpC,OAAO,EAAE,qBAAqB,EAAE,MAAM,8CAA8C,CAAC;AAErF,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kCAAkC,CAAC;AAElE,MAAM,WAAW,sBAAsB;IACrC,SAAS,EAAE,mBAAmB,CAAC;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,qBAAa,kBAAkB;IAQ3B,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,yBAAyB,CAAC;IAC3C,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC;IAX5B;;;;;OAKG;gBAEgB,SAAS,EAAE,MAAM,aAAa,EAC9B,UAAU,EAAE,gBAAgB,EAC5B,SAAS,EAAE,gBAAgB,EAC3B,yBAAyB,CAAC,GAAE,MAAM,OAAO,CAAC,IAAI,CAAC,aAAA,EAC/C,QAAQ,CAAC,EAAE,SAAS,YAAA;IAGjC,MAAM,CAAC,KAAK,EAAE,sBAAsB,GAAG,OAAO,CAAC,qBAAqB,CAAC;IA2CrE,MAAM,CACV,SAAS,EAAE,mBAAmB,EAC9B,SAAS,EAAE,MAAM,EACjB,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,IAAI,CAAC;IAiBV,YAAY,CAChB,SAAS,EAAE,mBAAmB,EAC9B,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,qBAAqB,EAAE,CAAC;YAMrB,kBAAkB;CAajC"}
@@ -0,0 +1,99 @@
1
+ import { ERROR_CODES, } from '@endora-commerce/contracts';
2
+ import { HttpError } from '@endora-commerce/platform/http';
3
+ import { Country } from '../entities/country.entity.js';
4
+ import { DictionaryTranslation } from '../entities/dictionary-translation.entity.js';
5
+ import { recordAuditFromContext } from '@endora-commerce/platform/commands';
6
+ export class TranslationService {
7
+ emFactory;
8
+ currencies;
9
+ languages;
10
+ invalidateDictionaryCache;
11
+ auditLog;
12
+ /**
13
+ * Feature 075, Phase C — a translation's parent may be a country (this
14
+ * module's row), a currency or a language (not). The two that are not are
15
+ * read through their owners' ports, so a translation cannot be attached to a
16
+ * parent the platform is no longer serving.
17
+ */
18
+ constructor(emFactory, currencies, languages, invalidateDictionaryCache, auditLog) {
19
+ this.emFactory = emFactory;
20
+ this.currencies = currencies;
21
+ this.languages = languages;
22
+ this.invalidateDictionaryCache = invalidateDictionaryCache;
23
+ this.auditLog = auditLog;
24
+ }
25
+ async upsert(input) {
26
+ const em = this.emFactory();
27
+ await this.assertParentExists(em, input.entryType, input.entryCode);
28
+ const language = await this.languages.findByCode(input.languageCode);
29
+ if (!language)
30
+ throw notFound('language', input.languageCode);
31
+ if (!language.isActive) {
32
+ throw new HttpError(409, ERROR_CODES.DICTIONARY_ENTRY_INACTIVE, `Language ${input.languageCode} is inactive.`);
33
+ }
34
+ let row = await em.findOne(DictionaryTranslation, {
35
+ entryType: input.entryType,
36
+ entryCode: input.entryCode,
37
+ languageCode: input.languageCode,
38
+ });
39
+ if (!row) {
40
+ row = em.create(DictionaryTranslation, {
41
+ entryType: input.entryType,
42
+ entryCode: input.entryCode,
43
+ languageCode: input.languageCode,
44
+ label: input.label,
45
+ });
46
+ em.persist(row);
47
+ }
48
+ else {
49
+ row.label = input.label;
50
+ }
51
+ if (this.auditLog) {
52
+ recordAuditFromContext(this.auditLog, em, {
53
+ action: 'dictionary_translation.upsert',
54
+ objectType: 'dictionary_translation',
55
+ objectId: `${input.entryType}:${input.entryCode}:${input.languageCode}`,
56
+ stateBefore: null,
57
+ stateAfter: { label: input.label },
58
+ });
59
+ }
60
+ await em.flush();
61
+ await this.invalidateDictionaryCache?.();
62
+ return row;
63
+ }
64
+ async remove(entryType, entryCode, languageCode) {
65
+ const em = this.emFactory();
66
+ const row = await em.findOne(DictionaryTranslation, { entryType, entryCode, languageCode });
67
+ if (!row)
68
+ throw notFound(entryType, entryCode);
69
+ if (this.auditLog) {
70
+ recordAuditFromContext(this.auditLog, em, {
71
+ action: 'dictionary_translation.delete',
72
+ objectType: 'dictionary_translation',
73
+ objectId: `${entryType}:${entryCode}:${languageCode}`,
74
+ stateBefore: { label: row.label },
75
+ stateAfter: null,
76
+ });
77
+ }
78
+ await em.removeAndFlush(row);
79
+ await this.invalidateDictionaryCache?.();
80
+ }
81
+ async listForEntry(entryType, entryCode) {
82
+ const em = this.emFactory();
83
+ await this.assertParentExists(em, entryType, entryCode);
84
+ return em.find(DictionaryTranslation, { entryType, entryCode }, { orderBy: { languageCode: 'asc' } });
85
+ }
86
+ async assertParentExists(em, entryType, entryCode) {
87
+ const exists = entryType === 'country'
88
+ ? await em.findOne(Country, { code: entryCode })
89
+ : entryType === 'currency'
90
+ ? await this.currencies.findByCode(entryCode)
91
+ : await this.languages.findByCode(entryCode);
92
+ if (!exists)
93
+ throw notFound(entryType, entryCode);
94
+ }
95
+ }
96
+ function notFound(entryType, entryCode) {
97
+ return new HttpError(404, ERROR_CODES.DICTIONARY_ENTRY_NOT_FOUND, `Dictionary ${entryType} ${entryCode} not found.`);
98
+ }
99
+ //# sourceMappingURL=translation-service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"translation-service.js","sourceRoot":"","sources":["../../../src/backend/services/translation-service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,174 @@
1
+ export declare const DICTIONARY_PERMISSIONS: {
2
+ readonly WRITE: "dictionary.write";
3
+ };
4
+ export type DictionaryPermission = (typeof DICTIONARY_PERMISSIONS)[keyof typeof DICTIONARY_PERMISSIONS];
5
+ /**
6
+ * Module-lifecycle manifest (feature 018) — Pass B retrofit. The
7
+ * `dictionaries` schema is platform-foundational (migration 038),
8
+ * owned cross-cuttingly rather than by an install hook, so this entry
9
+ * declares no `installHook`. Its purpose is to register the module's
10
+ * presence in the static registry so other modules (notably `inventory`
11
+ * and `blog`) can declare `dictionaries` as a dependency without the
12
+ * registry warning "depends on … which is not in the static registry".
13
+ */
14
+ export declare const manifest: {
15
+ id: string;
16
+ name: string;
17
+ version: string;
18
+ dependencies: string[];
19
+ description?: string | undefined;
20
+ acknowledgedDependencies?: {
21
+ moduleId: string;
22
+ port: string;
23
+ reason: string;
24
+ }[] | undefined;
25
+ nonBindingDependencies?: {
26
+ moduleId: string;
27
+ name: string;
28
+ kind: "contributes-to" | "degrades-without" | "refuses-without";
29
+ reason: string;
30
+ whenAbsent?: string | undefined;
31
+ }[] | undefined;
32
+ activation?: {
33
+ settingCode: string;
34
+ default: boolean;
35
+ } | {
36
+ nonDeactivatable: true;
37
+ reason: string;
38
+ } | undefined;
39
+ settings?: {
40
+ moduleCode: string;
41
+ groups: {
42
+ code: string;
43
+ name: string;
44
+ salesChannelCodes?: string[] | undefined;
45
+ isSystemProtected?: boolean | undefined;
46
+ }[];
47
+ settings: {
48
+ code: string;
49
+ name: string;
50
+ valueType: "string" | "number" | "boolean" | "json" | "string_list" | "secret" | "credential_ref";
51
+ defaultValue: unknown;
52
+ description?: string | undefined;
53
+ groupCode?: string | undefined;
54
+ previousDefaultValues?: unknown[] | undefined;
55
+ salesChannelCodes?: string[] | undefined;
56
+ enumOptions?: string[] | undefined;
57
+ configurationType?: string | undefined;
58
+ hidden?: boolean | undefined;
59
+ }[];
60
+ } | undefined;
61
+ i18n?: {
62
+ bundlesDir: string;
63
+ } | undefined;
64
+ docs?: false | {
65
+ dir: string;
66
+ } | undefined;
67
+ demo?: false | {
68
+ summary: string;
69
+ seed: (context: import("@endora-commerce/contracts").ModuleDemoContext<never>) => Promise<import("@endora-commerce/contracts").DemoSeedResult>;
70
+ reset: (context: import("@endora-commerce/contracts").ModuleDemoContext<never>) => Promise<import("@endora-commerce/contracts").DemoResetResult>;
71
+ after?: readonly string[] | undefined;
72
+ package?: string | undefined;
73
+ } | undefined;
74
+ actions?: {
75
+ id: string;
76
+ labelKey: string;
77
+ icon: "Plus" | "Sparkles" | "Settings" | "Search" | "Boxes" | "Layers" | "Menu" | "PlusCircle" | "PlusSquare" | "FilePlus" | "FolderPlus" | "Upload" | "FileUp" | "CloudUpload" | "Download" | "FileDown" | "FileText" | "BookOpen" | "Rss" | "Package" | "Tag" | "ShoppingCart" | "Receipt" | "CreditCard" | "Users" | "UserPlus" | "Inbox" | "ListChecks" | "ClipboardList" | "Image" | "Video" | "LayoutDashboard" | "PanelLeft" | "KeyRound" | "ShieldCheck" | "Edit" | "Archive" | "Box" | "Truck" | "CircleDollarSign" | "Activity" | "LineChart" | "Smartphone" | "Webhook" | "Scale" | "PlugZap" | "PercentDiamond" | "Newspaper" | "Languages" | "Eraser" | "Warehouse" | "TrendingDown" | "Bell" | "PackageOpen" | "Building2" | "Store" | "ClipboardCheck";
78
+ targetRoute: string;
79
+ keywords: string[];
80
+ weight: number;
81
+ descriptionKey?: string | undefined;
82
+ requiredPermission?: string | undefined;
83
+ }[] | undefined;
84
+ permissions?: {
85
+ code: string;
86
+ label: string;
87
+ module?: string | undefined;
88
+ description?: string | undefined;
89
+ requires?: string[] | undefined;
90
+ }[] | undefined;
91
+ transactionalEmails?: {
92
+ code: string;
93
+ name: string;
94
+ variables: {
95
+ key: string;
96
+ label: string;
97
+ sampleValue?: string | undefined;
98
+ description?: string | undefined;
99
+ }[];
100
+ description?: string | undefined;
101
+ group?: string | undefined;
102
+ }[] | undefined;
103
+ capabilities?: string[] | undefined;
104
+ exclusiveCapabilities?: {
105
+ key: string;
106
+ errorCode: string;
107
+ }[] | undefined;
108
+ errorCodes?: {
109
+ code: string;
110
+ tokens?: string[] | undefined;
111
+ }[] | undefined;
112
+ blocks?: {
113
+ name: string;
114
+ labelKey: string;
115
+ category: string;
116
+ contexts: ("invoice" | "email" | "cms" | "newsletter")[];
117
+ fields: Record<string, {
118
+ type: "number" | "object" | "array" | "text" | "textarea" | "select" | "radio" | "external" | "uuid" | "richtext";
119
+ label?: string | undefined;
120
+ required?: boolean | undefined;
121
+ options?: {
122
+ label: string;
123
+ value: string | number;
124
+ }[] | undefined;
125
+ refKind?: string | undefined;
126
+ }>;
127
+ descriptionKey?: string | undefined;
128
+ defaultProps?: Record<string, unknown> | undefined;
129
+ responsiveFields?: string[] | undefined;
130
+ previewIcon?: string | undefined;
131
+ weight?: number | undefined;
132
+ }[] | undefined;
133
+ blockCategories?: {
134
+ key: string;
135
+ titleKey: string;
136
+ contexts: ("invoice" | "email" | "cms" | "newsletter")[];
137
+ weight?: number | undefined;
138
+ visible?: boolean | undefined;
139
+ }[] | undefined;
140
+ env?: {
141
+ name: string;
142
+ describes: {
143
+ en: string;
144
+ pl: string;
145
+ };
146
+ requirement: {
147
+ kind: "required";
148
+ } | {
149
+ kind: "requiredWhen";
150
+ input: string;
151
+ equals: string;
152
+ } | {
153
+ kind: "optional";
154
+ without: {
155
+ en: string;
156
+ pl: string;
157
+ };
158
+ };
159
+ secret: boolean;
160
+ generable: boolean;
161
+ owner: {
162
+ kind: "platform";
163
+ } | {
164
+ kind: "application";
165
+ application: "admin" | "backend" | "storefront";
166
+ } | {
167
+ kind: "module";
168
+ moduleId: string;
169
+ };
170
+ consumers: ("admin" | "backend" | "storefront")[];
171
+ addressOf: "admin" | "backend" | "storefront" | null;
172
+ }[] | undefined;
173
+ };
174
+ //# sourceMappingURL=manifest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"AAeA,eAAO,MAAM,sBAAsB;;CAEzB,CAAC;AAEX,MAAM,MAAM,oBAAoB,GAC9B,CAAC,OAAO,sBAAsB,CAAC,CAAC,MAAM,OAAO,sBAAsB,CAAC,CAAC;AAEvE;;;;;;;;GAQG;AACH,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA0JnB,CAAC"}
@@ -0,0 +1,178 @@
1
+ // Dictionary module manifest — feature 017 / R10 / T012.
2
+ //
3
+ // Single contribution: the `dictionary.write` permission code, used by
4
+ // every admin endpoint under `/api/v1/admin/dictionary/**`. The bootstrap
5
+ // "Platform Administrator" role holds the wildcard `*` permission and
6
+ // therefore auto-inherits this code; operators who need a narrower role
7
+ // can add `dictionary.write` to a custom role via the existing
8
+ // `/api/v1/admin/admin-roles` surface (feature 003).
9
+ //
10
+ // Reads of the Dictionary registry — both storefront (`/api/v1/dictionary`)
11
+ // and the cross-module DictionaryValidator port — DO NOT require this
12
+ // permission. They are public service surfaces.
13
+ import { defineModuleManifest } from '@endora-commerce/contracts';
14
+ export const DICTIONARY_PERMISSIONS = {
15
+ WRITE: 'dictionary.write',
16
+ };
17
+ /**
18
+ * Module-lifecycle manifest (feature 018) — Pass B retrofit. The
19
+ * `dictionaries` schema is platform-foundational (migration 038),
20
+ * owned cross-cuttingly rather than by an install hook, so this entry
21
+ * declares no `installHook`. Its purpose is to register the module's
22
+ * presence in the static registry so other modules (notably `inventory`
23
+ * and `blog`) can declare `dictionaries` as a dependency without the
24
+ * registry warning "depends on … which is not in the static registry".
25
+ */
26
+ export const manifest = defineModuleManifest({
27
+ id: 'dictionaries',
28
+ name: 'Dictionary',
29
+ description: 'Cross-module registry of countries, languages, and currencies — backs every dropdown, validator, and address-form picker on the storefront and admin.',
30
+ version: '1.0.0',
31
+ // `auth` owns the `requireAdmin` port the admin routes are gated by. Feature
32
+ // 072 made it a container resolution rather than an optional argument that
33
+ // decided whether the admin surface existed at all.
34
+ dependencies: ['currencies', 'languages', 'auth'],
35
+ i18n: { bundlesDir: 'i18n' },
36
+ docs: { dir: 'docs' },
37
+ permissions: [{ code: DICTIONARY_PERMISSIONS.WRITE, label: 'Manage dictionary registry' }],
38
+ /**
39
+ * Feature 090 Phase 3 — the eight error codes the incumbent prefix chain
40
+ * routes to `dictionaries`, copied from the frozen capture
41
+ * (`backend/test/fixtures/error-code-routing/chain-answers.ts`, the chain's
42
+ * *answer* at `49f3c6817`) rather than judged. The list is not curated: the
43
+ * migration is answer-preserving over all 289 codes, so a code is here
44
+ * because the chain put it here.
45
+ *
46
+ * **Six of the eight are raised outside this package, and one of those is
47
+ * raised nowhere inside it** — D-95.2 working as intended, since ownership
48
+ * follows the domain noun and never the thrower. `dictionaries` is a screen
49
+ * over other modules' catalogues rather than the owner of their rows: the
50
+ * `currencies` and `languages` services raise
51
+ * `DICTIONARY_DEFAULT_CANNOT_BE_DEACTIVATED`, `DICTIONARY_LAST_ACTIVE_ENTRY`,
52
+ * `DICTIONARY_ENTRY_HAS_DEPENDENTS`, `DICTIONARY_ENTRY_NOT_FOUND` and — for
53
+ * `DICTIONARY_FALLBACK_CYCLE`, whose only three raise sites are in
54
+ * `languages` — all of them. `DICTIONARY_ENTRY_INACTIVE` is raised once here
55
+ * and nine times by consumer modules (`addresses`, `blog` twice, `inventory`,
56
+ * `megamenu`, `organizations`, `promotions`, `sales_channels`, `taxes`),
57
+ * which translate a caught `DictionaryReferenceError` into
58
+ * `new HttpError(409, err.code, …)` — the code is computed there, so the
59
+ * literal never appears as the second argument.
60
+ *
61
+ * **The inverse holds too, and it is a decision an earlier feature made**
62
+ * (trap T2). `UNKNOWN_CURRENCY_CODE` and `UNKNOWN_LANGUAGE_CODE` read like
63
+ * this module's and route to `sales_channels`, which declared them in !1125;
64
+ * feature 017 superseded both with `DICTIONARY_ENTRY_NOT_FOUND`, so the
65
+ * sentences that answer today are here and the ownership of the legacy codes
66
+ * is not. `CURRENCY_MISMATCH` routes to `core`. None of the three is declared
67
+ * here, and re-routing is out of scope
68
+ * (`specs/090-module-owned-error-codes/` §6.5).
69
+ *
70
+ * **Two of the eight are raised by nothing**, searched in both spellings
71
+ * (`ERROR_CODES.<CODE>` and the bare quoted literal) over `backend/src` and
72
+ * `packages`, test files excluded. They are declared anyway — ownership
73
+ * follows the capture (trap T10) — and what happens instead was established
74
+ * rather than classified:
75
+ *
76
+ * - `DICTIONARY_TRANSLATION_LANGUAGE_INACTIVE` — the rule is built and
77
+ * enforced. `TranslationService.upsert` refuses a translation whose
78
+ * language is inactive, in that exact condition, under the coarser
79
+ * `DICTIONARY_ENTRY_INACTIVE`. The specific sentence is unreachable
80
+ * because a live successor answers first, and the two agree about what
81
+ * happened.
82
+ * - `DICTIONARY_CODE_IMMUTABLE` — nothing refuses, because nothing can ask.
83
+ * No write path anywhere renames an entry: `updateCountryRequestSchema`,
84
+ * `updateDictionaryCurrencyRequestSchema` and
85
+ * `updateDictionaryLanguageRequestSchema` carry no `code` field, the three
86
+ * `Update*Input` types carry none either, and the entry code comes off the
87
+ * URL parameter. The schemas are non-strict `z.object()`s, so a `code` in
88
+ * the body is dropped silently — not refused as `VALIDATION_FAILED`. The
89
+ * immutability is structural and the refusal has no occasion to fire.
90
+ *
91
+ * No `tokens`: no code here carries a refusal discriminator. `refusalToken`
92
+ * reads `details.code` and nothing else, and no `new HttpError` raising one
93
+ * of these eight passes a fourth argument with a `code` member. Three of them
94
+ * do pass a fourth argument — all three `DICTIONARY_ENTRY_HAS_DEPENDENTS`
95
+ * sites pass the Zod-style `Array<{ path, issue }>`, which `refusalToken`
96
+ * ignores by construction. The runbook's §5 raise-site scan attributes the
97
+ * tree's ten token-carrying codes to `core`, `invoices` and `carts` and names
98
+ * none of these; the bundles hold no `errors.<CODE>.<token>` key in the other
99
+ * direction.
100
+ */
101
+ errorCodes: [
102
+ { code: 'DICTIONARY_CODE_IMMUTABLE' },
103
+ { code: 'DICTIONARY_DEFAULT_CANNOT_BE_DEACTIVATED' },
104
+ { code: 'DICTIONARY_ENTRY_HAS_DEPENDENTS' },
105
+ { code: 'DICTIONARY_ENTRY_INACTIVE' },
106
+ { code: 'DICTIONARY_ENTRY_NOT_FOUND' },
107
+ { code: 'DICTIONARY_FALLBACK_CYCLE' },
108
+ { code: 'DICTIONARY_LAST_ACTIVE_ENTRY' },
109
+ { code: 'DICTIONARY_TRANSLATION_LANGUAGE_INACTIVE' },
110
+ ],
111
+ /**
112
+ * Feature 091, Phase 4 batch 10 — the two palette entries this module has
113
+ * always advertised, declared where the server can filter them.
114
+ *
115
+ * `AppShell.tsx`'s `PALETTE_ITEMS` carried a hand-written *Navigate* row for
116
+ * each of these two screens, and a hand-written palette row is a copy the
117
+ * server was never asked about: it kept advertising the screens whatever the
118
+ * effective enabled-set said. Both arrive here instead, with the
119
+ * destinations, the code and the keywords those rows carried, so nothing an
120
+ * operator can see changes and the advertisement is now gated the way every
121
+ * other one is.
122
+ *
123
+ * **Both declare `dictionary.write`, which is the only code this module has.**
124
+ * All 23 registrations under `/api/v1/admin/dictionary/*` enforce it, reads
125
+ * included — the registry screen is an editing surface and this module never
126
+ * split read from write — so the action, the route and the API are one code
127
+ * and the palette never advertises a 403.
128
+ */
129
+ actions: [
130
+ {
131
+ id: 'open-dictionary',
132
+ labelKey: 'actions.openDictionary.label',
133
+ descriptionKey: 'actions.openDictionary.description',
134
+ icon: 'Languages',
135
+ targetRoute: '/dictionary',
136
+ requiredPermission: DICTIONARY_PERMISSIONS.WRITE,
137
+ keywords: [
138
+ 'dictionary',
139
+ 'countries',
140
+ 'currencies',
141
+ 'languages',
142
+ 'i18n',
143
+ 'słownik',
144
+ 'kraje',
145
+ 'waluty',
146
+ 'języki',
147
+ ],
148
+ weight: 250,
149
+ },
150
+ {
151
+ id: 'open-dictionary-audit',
152
+ labelKey: 'actions.openDictionaryAudit.label',
153
+ descriptionKey: 'actions.openDictionaryAudit.description',
154
+ icon: 'ListChecks',
155
+ // The `/admin`-prefixed spelling, which is the one the sidebar row
156
+ // advertised and the one this module's nav contribution declares. The
157
+ // unprefixed `/dictionaries/audit` route still resolves and is
158
+ // deliberately not advertised twice.
159
+ targetRoute: '/admin/dictionaries/audit',
160
+ requiredPermission: DICTIONARY_PERMISSIONS.WRITE,
161
+ keywords: ['dictionary', 'audit', 'orphan', 'references', 'audyt', 'słownika'],
162
+ weight: 251,
163
+ },
164
+ ],
165
+ // Feature 074 (Constitution XVII), test C3 — platform primitive. The flag
166
+ // used to rest on `organizations` declaring this module; ruling 2 withdraws
167
+ // a dependent's authority to impose the lock, so the ground is now this
168
+ // module's own. It is the shared reference-data primitive — countries,
169
+ // units, document types — that every other module validates and renders
170
+ // against. There is no business decision underneath it: nobody chooses to
171
+ // stop having countries.
172
+ activation: {
173
+ nonDeactivatable: true,
174
+ reason: 'The shared reference-data primitive — countries, units, document types — that every ' +
175
+ 'other surface validates and renders against.',
176
+ },
177
+ });
178
+ //# sourceMappingURL=manifest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manifest.js","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"AAAA,yDAAyD;AACzD,EAAE;AACF,uEAAuE;AACvE,0EAA0E;AAC1E,sEAAsE;AACtE,wEAAwE;AACxE,+DAA+D;AAC/D,qDAAqD;AACrD,EAAE;AACF,4EAA4E;AAC5E,sEAAsE;AACtE,gDAAgD;AAEhD,OAAO,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AAElE,MAAM,CAAC,MAAM,sBAAsB,GAAG;IACpC,KAAK,EAAE,kBAAkB;CACjB,CAAC;AAKX;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAG,oBAAoB,CAAC;IAC3C,EAAE,EAAE,cAAc;IAClB,IAAI,EAAE,YAAY;IAClB,WAAW,EACT,uJAAuJ;IACzJ,OAAO,EAAE,OAAO;IAChB,6EAA6E;IAC7E,2EAA2E;IAC3E,oDAAoD;IACpD,YAAY,EAAE,CAAC,YAAY,EAAE,WAAW,EAAE,MAAM,CAAC;IACjD,IAAI,EAAE,EAAE,UAAU,EAAE,MAAM,EAAE;IAC5B,IAAI,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE;IACrB,WAAW,EAAE,CAAC,EAAE,IAAI,EAAE,sBAAsB,CAAC,KAAK,EAAE,KAAK,EAAE,4BAA4B,EAAE,CAAC;IAE1F;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA8DG;IACH,UAAU,EAAE;QACV,EAAE,IAAI,EAAE,2BAA2B,EAAE;QACrC,EAAE,IAAI,EAAE,0CAA0C,EAAE;QACpD,EAAE,IAAI,EAAE,iCAAiC,EAAE;QAC3C,EAAE,IAAI,EAAE,2BAA2B,EAAE;QACrC,EAAE,IAAI,EAAE,4BAA4B,EAAE;QACtC,EAAE,IAAI,EAAE,2BAA2B,EAAE;QACrC,EAAE,IAAI,EAAE,8BAA8B,EAAE;QACxC,EAAE,IAAI,EAAE,0CAA0C,EAAE;KACrD;IACD;;;;;;;;;;;;;;;;;OAiBG;IACH,OAAO,EAAE;QACP;YACE,EAAE,EAAE,iBAAiB;YACrB,QAAQ,EAAE,8BAA8B;YACxC,cAAc,EAAE,oCAAoC;YACpD,IAAI,EAAE,WAAW;YACjB,WAAW,EAAE,aAAa;YAC1B,kBAAkB,EAAE,sBAAsB,CAAC,KAAK;YAChD,QAAQ,EAAE;gBACR,YAAY;gBACZ,WAAW;gBACX,YAAY;gBACZ,WAAW;gBACX,MAAM;gBACN,SAAS;gBACT,OAAO;gBACP,QAAQ;gBACR,QAAQ;aACT;YACD,MAAM,EAAE,GAAG;SACZ;QACD;YACE,EAAE,EAAE,uBAAuB;YAC3B,QAAQ,EAAE,mCAAmC;YAC7C,cAAc,EAAE,yCAAyC;YACzD,IAAI,EAAE,YAAY;YAClB,mEAAmE;YACnE,sEAAsE;YACtE,+DAA+D;YAC/D,qCAAqC;YACrC,WAAW,EAAE,2BAA2B;YACxC,kBAAkB,EAAE,sBAAsB,CAAC,KAAK;YAChD,QAAQ,EAAE,CAAC,YAAY,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,OAAO,EAAE,UAAU,CAAC;YAC9E,MAAM,EAAE,GAAG;SACZ;KACF;IACD,0EAA0E;IAC1E,4EAA4E;IAC5E,wEAAwE;IACxE,uEAAuE;IACvE,wEAAwE;IACxE,0EAA0E;IAC1E,yBAAyB;IACzB,UAAU,EAAE;QACV,gBAAgB,EAAE,IAAI;QACtB,MAAM,EACJ,sFAAsF;YACtF,8CAA8C;KACjD;CACF,CAAC,CAAC"}
@@ -0,0 +1,33 @@
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 declare class Migration20260506T112634DictionariesDictionaryInit extends Migration {
30
+ up(): Promise<void>;
31
+ down(): Promise<void>;
32
+ }
33
+ //# sourceMappingURL=20260506T112634_dictionaries_dictionary_init.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"20260506T112634_dictionaries_dictionary_init.d.ts","sourceRoot":"","sources":["../../src/migrations/20260506T112634_dictionaries_dictionary_init.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAElD;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,qBAAa,kDAAmD,SAAQ,SAAS;IAChE,EAAE,IAAI,OAAO,CAAC,IAAI,CAAC;IAgInB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;CAcrC"}