@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,107 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __metadata = (this && this.__metadata) || function (k, v) {
8
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
+ };
10
+ import { Entity, Index, OptionalProps, PrimaryKey, Property } from '@mikro-orm/core';
11
+ import { GlobalEntity } from '@endora-commerce/platform/tenancy';
12
+ /**
13
+ * Country — ISO 3166-1 alpha-2 territories the platform recognises (feature 017).
14
+ * The natural code is the stable handle every consumer module references
15
+ * (Address, Tax, Warehouse, Organisation registered address). The "exactly
16
+ * one default" invariant is enforced by a partial unique index in the
17
+ * migration. Hard-delete is service-layer guarded: refused while any
18
+ * consumer references the row (FR-021).
19
+ */
20
+ let Country = class Country {
21
+ [OptionalProps];
22
+ /** ISO 3166-1 alpha-2 code, immutable. Stable handle for every reference. */
23
+ code;
24
+ alpha3Code;
25
+ /** Zero-padded ISO 3166-1 numeric code (e.g. `616` for `PL`). */
26
+ numericCode;
27
+ /** Canonical English display name. Per-locale overrides live in `dictionary_translations`. */
28
+ label;
29
+ /** UN-style top-level region (`Africa`, `Americas`, `Asia`, `Europe`, `Oceania`, `Antarctic`). */
30
+ region;
31
+ subregion;
32
+ /** Phone-prefix string with leading `+` (e.g. `+48`). */
33
+ dialCode;
34
+ /** Drives VAT logic in the Tax module. */
35
+ isEuMember = false;
36
+ /** Hint for storefront pre-selection; soft FK to `currencies(code)`. */
37
+ defaultCurrencyCode;
38
+ isActive = true;
39
+ /** Exactly one row has `true` (partial unique index). */
40
+ isDefault = false;
41
+ sortOrder = 0;
42
+ createdAt = new Date();
43
+ updatedAt = new Date();
44
+ };
45
+ __decorate([
46
+ PrimaryKey({ type: 'string', length: 2 }),
47
+ __metadata("design:type", String)
48
+ ], Country.prototype, "code", void 0);
49
+ __decorate([
50
+ Property({ type: 'string', length: 3, fieldName: 'alpha3_code' }),
51
+ __metadata("design:type", String)
52
+ ], Country.prototype, "alpha3Code", void 0);
53
+ __decorate([
54
+ Property({ type: 'string', length: 3, fieldName: 'numeric_code' }),
55
+ __metadata("design:type", String)
56
+ ], Country.prototype, "numericCode", void 0);
57
+ __decorate([
58
+ Property({ type: 'string', length: 120 }),
59
+ __metadata("design:type", String)
60
+ ], Country.prototype, "label", void 0);
61
+ __decorate([
62
+ Property({ type: 'string', length: 32 }),
63
+ __metadata("design:type", String)
64
+ ], Country.prototype, "region", void 0);
65
+ __decorate([
66
+ Property({ type: 'string', length: 64, nullable: true }),
67
+ __metadata("design:type", Object)
68
+ ], Country.prototype, "subregion", void 0);
69
+ __decorate([
70
+ Property({ type: 'string', length: 8, nullable: true }),
71
+ __metadata("design:type", Object)
72
+ ], Country.prototype, "dialCode", void 0);
73
+ __decorate([
74
+ Property({ type: 'boolean' }),
75
+ __metadata("design:type", Boolean)
76
+ ], Country.prototype, "isEuMember", void 0);
77
+ __decorate([
78
+ Property({ type: 'string', length: 3, nullable: true }),
79
+ __metadata("design:type", Object)
80
+ ], Country.prototype, "defaultCurrencyCode", void 0);
81
+ __decorate([
82
+ Property({ type: 'boolean' }),
83
+ Index(),
84
+ __metadata("design:type", Boolean)
85
+ ], Country.prototype, "isActive", void 0);
86
+ __decorate([
87
+ Property({ type: 'boolean' }),
88
+ __metadata("design:type", Boolean)
89
+ ], Country.prototype, "isDefault", void 0);
90
+ __decorate([
91
+ Property({ type: 'integer' }),
92
+ __metadata("design:type", Number)
93
+ ], Country.prototype, "sortOrder", void 0);
94
+ __decorate([
95
+ Property({ type: 'datetime', onCreate: () => new Date() }),
96
+ __metadata("design:type", Date)
97
+ ], Country.prototype, "createdAt", void 0);
98
+ __decorate([
99
+ Property({ type: 'datetime', onUpdate: () => new Date() }),
100
+ __metadata("design:type", Date)
101
+ ], Country.prototype, "updatedAt", void 0);
102
+ Country = __decorate([
103
+ GlobalEntity(),
104
+ Entity({ tableName: 'countries' })
105
+ ], Country);
106
+ export { Country };
107
+ //# sourceMappingURL=country.entity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"country.entity.js","sourceRoot":"","sources":["../../../src/backend/entities/country.entity.ts"],"names":[],"mappings":";;;;;;;;;AAAA,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AACrF,OAAO,EAAE,YAAY,EAAE,MAAM,mCAAmC,CAAC;AAEjE;;;;;;;GAOG;AAGI,IAAM,OAAO,GAAb,MAAM,OAAO;IAClB,CAAC,aAAa,CAAC,CASW;IAE1B,6EAA6E;IAE7E,IAAI,CAAU;IAGd,UAAU,CAAU;IAEpB,iEAAiE;IAEjE,WAAW,CAAU;IAErB,8FAA8F;IAE9F,KAAK,CAAU;IAEf,kGAAkG;IAElG,MAAM,CAAU;IAGhB,SAAS,CAAiB;IAE1B,yDAAyD;IAEzD,QAAQ,CAAiB;IAEzB,0CAA0C;IAE1C,UAAU,GAAY,KAAK,CAAC;IAE5B,wEAAwE;IAExE,mBAAmB,CAAiB;IAIpC,QAAQ,GAAY,IAAI,CAAC;IAEzB,yDAAyD;IAEzD,SAAS,GAAY,KAAK,CAAC;IAG3B,SAAS,GAAW,CAAC,CAAC;IAGtB,SAAS,GAAS,IAAI,IAAI,EAAE,CAAC;IAG7B,SAAS,GAAS,IAAI,IAAI,EAAE,CAAC;CAC9B,CAAA;AAhDC;IADC,UAAU,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC;;qCAC5B;AAGd;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,EAAE,SAAS,EAAE,aAAa,EAAE,CAAC;;2CAC9C;AAIpB;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,EAAE,SAAS,EAAE,cAAc,EAAE,CAAC;;4CAC9C;AAIrB;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC;;sCAC3B;AAIf;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;;uCACzB;AAGhB;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;0CAC/B;AAI1B;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;yCAC/B;AAIzB;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;;2CACF;AAI5B;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;oDACpB;AAIpC;IAFC,QAAQ,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;IAC7B,KAAK,EAAE;;yCACiB;AAIzB;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;;0CACH;AAG3B;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;;0CACR;AAGtB;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,GAAG,EAAE,CAAC,IAAI,IAAI,EAAE,EAAE,CAAC;8BAChD,IAAI;0CAAc;AAG7B;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,GAAG,EAAE,CAAC,IAAI,IAAI,EAAE,EAAE,CAAC;8BAChD,IAAI;0CAAc;AA7DlB,OAAO;IAFnB,YAAY,EAAE;IACd,MAAM,CAAC,EAAE,SAAS,EAAE,WAAW,EAAE,CAAC;GACtB,OAAO,CA8DnB"}
@@ -0,0 +1,25 @@
1
+ import { OptionalProps } from '@mikro-orm/core';
2
+ /**
3
+ * DictionaryTranslation — per-Language display-label override for a Country,
4
+ * Currency, or Language entry (feature 017 / R3).
5
+ *
6
+ * Polymorphic by `entryType`. Postgres cannot enforce a polymorphic FK to
7
+ * three different parent tables, so the parent-existence invariant is
8
+ * enforced at the service layer (`TranslationService.upsert`) inside the
9
+ * same transaction that performs the write. The `language_code` FK to
10
+ * `languages(code)` IS database-enforced — that's the only side the schema
11
+ * knows is uniform. ON DELETE CASCADE there: hard-deleting a Language
12
+ * wipes its translations everywhere.
13
+ */
14
+ export declare class DictionaryTranslation {
15
+ [OptionalProps]?: 'createdAt' | 'updatedAt';
16
+ entryType: 'country' | 'currency' | 'language';
17
+ /** The natural code of the parent entry. 2 chars for country, 3 for currency, 2..12 for language. */
18
+ entryCode: string;
19
+ /** The locale this translation is FOR. FK to `languages(code)` ON DELETE CASCADE. */
20
+ languageCode: string;
21
+ label: string;
22
+ createdAt: Date;
23
+ updatedAt: Date;
24
+ }
25
+ //# sourceMappingURL=dictionary-translation.entity.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dictionary-translation.entity.d.ts","sourceRoot":"","sources":["../../../src/backend/entities/dictionary-translation.entity.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,aAAa,EAAwB,MAAM,iBAAiB,CAAC;AAGrF;;;;;;;;;;;GAWG;AACH,qBAEa,qBAAqB;IAChC,CAAC,aAAa,CAAC,CAAC,EAAE,WAAW,GAAG,WAAW,CAAC;IAG5C,SAAS,EAAG,SAAS,GAAG,UAAU,GAAG,UAAU,CAAC;IAEhD,qGAAqG;IAErG,SAAS,EAAG,MAAM,CAAC;IAEnB,qFAAqF;IAGrF,YAAY,EAAG,MAAM,CAAC;IAGtB,KAAK,EAAG,MAAM,CAAC;IAGf,SAAS,EAAE,IAAI,CAAc;IAG7B,SAAS,EAAE,IAAI,CAAc;CAC9B"}
@@ -0,0 +1,65 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __metadata = (this && this.__metadata) || function (k, v) {
8
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
+ };
10
+ import { Entity, Index, OptionalProps, PrimaryKey, Property } from '@mikro-orm/core';
11
+ import { GlobalEntity } from '@endora-commerce/platform/tenancy';
12
+ /**
13
+ * DictionaryTranslation — per-Language display-label override for a Country,
14
+ * Currency, or Language entry (feature 017 / R3).
15
+ *
16
+ * Polymorphic by `entryType`. Postgres cannot enforce a polymorphic FK to
17
+ * three different parent tables, so the parent-existence invariant is
18
+ * enforced at the service layer (`TranslationService.upsert`) inside the
19
+ * same transaction that performs the write. The `language_code` FK to
20
+ * `languages(code)` IS database-enforced — that's the only side the schema
21
+ * knows is uniform. ON DELETE CASCADE there: hard-deleting a Language
22
+ * wipes its translations everywhere.
23
+ */
24
+ let DictionaryTranslation = class DictionaryTranslation {
25
+ [OptionalProps];
26
+ entryType;
27
+ /** The natural code of the parent entry. 2 chars for country, 3 for currency, 2..12 for language. */
28
+ entryCode;
29
+ /** The locale this translation is FOR. FK to `languages(code)` ON DELETE CASCADE. */
30
+ languageCode;
31
+ label;
32
+ createdAt = new Date();
33
+ updatedAt = new Date();
34
+ };
35
+ __decorate([
36
+ PrimaryKey({ type: 'string', length: 16 }),
37
+ __metadata("design:type", String)
38
+ ], DictionaryTranslation.prototype, "entryType", void 0);
39
+ __decorate([
40
+ PrimaryKey({ type: 'string', length: 12 }),
41
+ __metadata("design:type", String)
42
+ ], DictionaryTranslation.prototype, "entryCode", void 0);
43
+ __decorate([
44
+ PrimaryKey({ type: 'string', length: 12 }),
45
+ Index(),
46
+ __metadata("design:type", String)
47
+ ], DictionaryTranslation.prototype, "languageCode", void 0);
48
+ __decorate([
49
+ Property({ type: 'string', length: 160 }),
50
+ __metadata("design:type", String)
51
+ ], DictionaryTranslation.prototype, "label", void 0);
52
+ __decorate([
53
+ Property({ type: 'datetime', onCreate: () => new Date() }),
54
+ __metadata("design:type", Date)
55
+ ], DictionaryTranslation.prototype, "createdAt", void 0);
56
+ __decorate([
57
+ Property({ type: 'datetime', onUpdate: () => new Date() }),
58
+ __metadata("design:type", Date)
59
+ ], DictionaryTranslation.prototype, "updatedAt", void 0);
60
+ DictionaryTranslation = __decorate([
61
+ GlobalEntity(),
62
+ Entity({ tableName: 'dictionary_translations' })
63
+ ], DictionaryTranslation);
64
+ export { DictionaryTranslation };
65
+ //# sourceMappingURL=dictionary-translation.entity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dictionary-translation.entity.js","sourceRoot":"","sources":["../../../src/backend/entities/dictionary-translation.entity.ts"],"names":[],"mappings":";;;;;;;;;AAAA,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AACrF,OAAO,EAAE,YAAY,EAAE,MAAM,mCAAmC,CAAC;AAEjE;;;;;;;;;;;GAWG;AAGI,IAAM,qBAAqB,GAA3B,MAAM,qBAAqB;IAChC,CAAC,aAAa,CAAC,CAA6B;IAG5C,SAAS,CAAuC;IAEhD,qGAAqG;IAErG,SAAS,CAAU;IAEnB,qFAAqF;IAGrF,YAAY,CAAU;IAGtB,KAAK,CAAU;IAGf,SAAS,GAAS,IAAI,IAAI,EAAE,CAAC;IAG7B,SAAS,GAAS,IAAI,IAAI,EAAE,CAAC;CAC9B,CAAA;AAnBC;IADC,UAAU,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;;wDACK;AAIhD;IADC,UAAU,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;;wDACxB;AAKnB;IAFC,UAAU,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;IAC1C,KAAK,EAAE;;2DACc;AAGtB;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC;;oDAC3B;AAGf;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,GAAG,EAAE,CAAC,IAAI,IAAI,EAAE,EAAE,CAAC;8BAChD,IAAI;wDAAc;AAG7B;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,GAAG,EAAE,CAAC,IAAI,IAAI,EAAE,EAAE,CAAC;8BAChD,IAAI;wDAAc;AAtBlB,qBAAqB;IAFjC,YAAY,EAAE;IACd,MAAM,CAAC,EAAE,SAAS,EAAE,yBAAyB,EAAE,CAAC;GACpC,qBAAqB,CAuBjC"}
@@ -0,0 +1,17 @@
1
+ import { OptionalProps } from '@mikro-orm/core';
2
+ /**
3
+ * LanguageCountry — many-to-many association between Languages and the
4
+ * Countries they are primarily associated with (feature 017).
5
+ *
6
+ * Used by storefront locale auto-detection ("user is in DE, prefer de-DE
7
+ * if the channel offers it"). The `is_primary` flag pins at most one
8
+ * primary Language per Country (partial unique index in the migration).
9
+ */
10
+ export declare class LanguageCountry {
11
+ [OptionalProps]?: 'createdAt' | 'isPrimary';
12
+ languageCode: string;
13
+ countryCode: string;
14
+ isPrimary: boolean;
15
+ createdAt: Date;
16
+ }
17
+ //# sourceMappingURL=language-country.entity.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"language-country.entity.d.ts","sourceRoot":"","sources":["../../../src/backend/entities/language-country.entity.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,aAAa,EAAwB,MAAM,iBAAiB,CAAC;AAGrF;;;;;;;GAOG;AACH,qBAEa,eAAe;IAC1B,CAAC,aAAa,CAAC,CAAC,EAAE,WAAW,GAAG,WAAW,CAAC;IAG5C,YAAY,EAAG,MAAM,CAAC;IAItB,WAAW,EAAG,MAAM,CAAC;IAGrB,SAAS,EAAE,OAAO,CAAS;IAG3B,SAAS,EAAE,IAAI,CAAc;CAC9B"}
@@ -0,0 +1,49 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __metadata = (this && this.__metadata) || function (k, v) {
8
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
+ };
10
+ import { Entity, Index, OptionalProps, PrimaryKey, Property } from '@mikro-orm/core';
11
+ import { GlobalEntity } from '@endora-commerce/platform/tenancy';
12
+ /**
13
+ * LanguageCountry — many-to-many association between Languages and the
14
+ * Countries they are primarily associated with (feature 017).
15
+ *
16
+ * Used by storefront locale auto-detection ("user is in DE, prefer de-DE
17
+ * if the channel offers it"). The `is_primary` flag pins at most one
18
+ * primary Language per Country (partial unique index in the migration).
19
+ */
20
+ let LanguageCountry = class LanguageCountry {
21
+ [OptionalProps];
22
+ languageCode;
23
+ countryCode;
24
+ isPrimary = false;
25
+ createdAt = new Date();
26
+ };
27
+ __decorate([
28
+ PrimaryKey({ type: 'string', length: 12 }),
29
+ __metadata("design:type", String)
30
+ ], LanguageCountry.prototype, "languageCode", void 0);
31
+ __decorate([
32
+ PrimaryKey({ type: 'string', length: 2 }),
33
+ Index(),
34
+ __metadata("design:type", String)
35
+ ], LanguageCountry.prototype, "countryCode", void 0);
36
+ __decorate([
37
+ Property({ type: 'boolean' }),
38
+ __metadata("design:type", Boolean)
39
+ ], LanguageCountry.prototype, "isPrimary", void 0);
40
+ __decorate([
41
+ Property({ type: 'datetime', onCreate: () => new Date() }),
42
+ __metadata("design:type", Date)
43
+ ], LanguageCountry.prototype, "createdAt", void 0);
44
+ LanguageCountry = __decorate([
45
+ GlobalEntity(),
46
+ Entity({ tableName: 'language_countries' })
47
+ ], LanguageCountry);
48
+ export { LanguageCountry };
49
+ //# sourceMappingURL=language-country.entity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"language-country.entity.js","sourceRoot":"","sources":["../../../src/backend/entities/language-country.entity.ts"],"names":[],"mappings":";;;;;;;;;AAAA,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AACrF,OAAO,EAAE,YAAY,EAAE,MAAM,mCAAmC,CAAC;AAEjE;;;;;;;GAOG;AAGI,IAAM,eAAe,GAArB,MAAM,eAAe;IAC1B,CAAC,aAAa,CAAC,CAA6B;IAG5C,YAAY,CAAU;IAItB,WAAW,CAAU;IAGrB,SAAS,GAAY,KAAK,CAAC;IAG3B,SAAS,GAAS,IAAI,IAAI,EAAE,CAAC;CAC9B,CAAA;AAXC;IADC,UAAU,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;;qDACrB;AAItB;IAFC,UAAU,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC;IACzC,KAAK,EAAE;;oDACa;AAGrB;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;;kDACH;AAG3B;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,GAAG,EAAE,CAAC,IAAI,IAAI,EAAE,EAAE,CAAC;8BAChD,IAAI;kDAAc;AAdlB,eAAe;IAF3B,YAAY,EAAE;IACd,MAAM,CAAC,EAAE,SAAS,EAAE,oBAAoB,EAAE,CAAC;GAC/B,eAAe,CAe3B"}
@@ -0,0 +1,85 @@
1
+ import type { EntityManager } from '@mikro-orm/postgresql';
2
+ import type { Redis } from 'ioredis';
3
+ import { type CurrencyAdminPort, type CurrencyReadPort, type DictionaryReferenceRegistryPort, type DictionaryValidator as DictionaryValidatorPort, type LanguageAdminPort, type LanguageReadPort } from '@endora-commerce/contracts';
4
+ import type { AuditPort } from '@endora-commerce/platform/kernel';
5
+ import type { ModuleContext } from '@endora-commerce/platform/kernel';
6
+ import type { RequireAdminFactory } from '@endora-commerce/platform/kernel';
7
+ import { DictionaryCache } from './services/dictionary-cache.js';
8
+ import { Country } from './entities/country.entity.js';
9
+ import { DictionaryTranslation } from './entities/dictionary-translation.entity.js';
10
+ import { LanguageCountry } from './entities/language-country.entity.js';
11
+ /**
12
+ * `dictionaries` — the module that owns the cache everybody else announces to
13
+ * (feature 072, wave 2, T112).
14
+ *
15
+ * Two things move *into* this module, and both were in a composition root only
16
+ * because the module could not reach them.
17
+ *
18
+ * **The invalidation listeners.** Each root subscribed to `currencies.changed`
19
+ * and `languages.changed` and reached back in to call
20
+ * `dictionaries.handle.validator.invalidate()` and
21
+ * `cache?.invalidateAll()`. That is this module's own cache being dropped by
22
+ * somebody else, spelled twice. `ctx.subscribe` puts it where it belongs — and
23
+ * gates it, so the drops stop when the module does. The direction stays the one
24
+ * wave 1 established: `currencies` and `languages` *announce*, and whoever
25
+ * caches their data subscribes. Declaring the reverse call as a dependency
26
+ * produces a real cycle, because this module reads both their entities.
27
+ *
28
+ * **The second `LanguageService`.** `plugin.ts` built one here —
29
+ * `new LanguageService(emFactory, invalidateDictionaryState, auditLog)` — while
30
+ * `languages` built another that announces on the EventBus. Two services
31
+ * writing the same table through different invalidation mechanisms, which is
32
+ * the divergence this transition keeps finding. `languages` provides the port
33
+ * now, so this module resolves it and there is one.
34
+ *
35
+ * `requireAdmin` stops being optional. Its absence removed the admin surface
36
+ * rather than opening it — the `comparisons` shape, safe but still a surface
37
+ * that exists or not depending on whether an argument was passed.
38
+ *
39
+ * `dictionaryValidator` is a **port**, and its `HOST_REGISTERED_PORTS` entry
40
+ * goes: `blog`, `taxes`, `organizations`, `orders` and `catalog` resolve it to
41
+ * validate country, region, currency and language codes on write, and a
42
+ * composition with dictionaries off should refuse those writes rather than
43
+ * accept unvalidated ones.
44
+ *
45
+ * **Feature 075, Phase C.** Seventeen imports of `currencies`' and `languages`'
46
+ * files are gone. Ten of them read those modules' *entities* — this module owns
47
+ * the admin screen and the storefront registry for two tables it does not own,
48
+ * so it queried them directly, and deactivation drops no tables: the registry
49
+ * kept listing currencies and the validator kept accepting codes out of modules
50
+ * an operator had switched off. Both are now the four published ports, each
51
+ * resolved with `lazyPort` and handed to the service that needs it, never
52
+ * captured. The two event names are constants in `@endora-commerce/contracts` now, which is
53
+ * where a string belongs; the direction is untouched — `currencies` and
54
+ * `languages` announce, this module subscribes, and neither learns who listens.
55
+ */
56
+ export interface DictionariesCradle {
57
+ readonly emFactory: () => EntityManager;
58
+ readonly auditLogService: AuditPort;
59
+ readonly redis: Redis | undefined;
60
+ readonly requireAdmin: RequireAdminFactory;
61
+ /** Owned by `currencies`; this module's admin surface serves both. */
62
+ readonly currencyReadPort: CurrencyReadPort;
63
+ readonly currencyAdminPort: CurrencyAdminPort;
64
+ /** Owned by `languages`; there used to be a second service built here. */
65
+ readonly languageReadPort: LanguageReadPort;
66
+ readonly languageAdminPort: LanguageAdminPort;
67
+ readonly dictionaryCache: DictionaryCache | undefined;
68
+ readonly dictionaryValidator: DictionaryValidatorPort;
69
+ readonly dictionaryInvalidator: () => Promise<void>;
70
+ readonly countryReferenceRegistry: DictionaryReferenceRegistryPort;
71
+ }
72
+ export declare function registerModule(ctx: ModuleContext): void;
73
+ /**
74
+ * The module's persisted entity classes, on the `./backend` subpath, as one
75
+ * array and **no named class export** (D-168).
76
+ *
77
+ * This is the shape the platform reads when the package is *installed*: the
78
+ * boot-time loader (`src/packages/package-runtime.ts`, `exported['entities']`)
79
+ * and the static declaration reader (`scripts/lib/package-declarations.ts`),
80
+ * which is the third source of `check:module-boundary`'s `table→owner` map and
81
+ * the package pass of `check-entity-tenant-classification`. A missing array is
82
+ * answered with `[]` — zero entities registered, no error anywhere.
83
+ */
84
+ export declare const entities: (typeof Country | typeof LanguageCountry | typeof DictionaryTranslation)[];
85
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/backend/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAGL,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,EAErB,KAAK,+BAA+B,EACpC,KAAK,mBAAmB,IAAI,uBAAuB,EACnD,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,EAEtB,MAAM,4BAA4B,CAAC;AACpC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kCAAkC,CAAC;AAClE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kCAAkC,CAAC;AAEtE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,kCAAkC,CAAC;AAC5E,OAAO,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AAYjE,OAAO,EAAE,OAAO,EAAE,MAAM,8BAA8B,CAAC;AACvD,OAAO,EAAE,qBAAqB,EAAE,MAAM,6CAA6C,CAAC;AACpF,OAAO,EAAE,eAAe,EAAE,MAAM,uCAAuC,CAAC;AAExE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AAEH,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,SAAS,EAAE,MAAM,aAAa,CAAC;IACxC,QAAQ,CAAC,eAAe,EAAE,SAAS,CAAC;IACpC,QAAQ,CAAC,KAAK,EAAE,KAAK,GAAG,SAAS,CAAC;IAClC,QAAQ,CAAC,YAAY,EAAE,mBAAmB,CAAC;IAC3C,sEAAsE;IACtE,QAAQ,CAAC,gBAAgB,EAAE,gBAAgB,CAAC;IAC5C,QAAQ,CAAC,iBAAiB,EAAE,iBAAiB,CAAC;IAC9C,0EAA0E;IAC1E,QAAQ,CAAC,gBAAgB,EAAE,gBAAgB,CAAC;IAC5C,QAAQ,CAAC,iBAAiB,EAAE,iBAAiB,CAAC;IAC9C,QAAQ,CAAC,eAAe,EAAE,eAAe,GAAG,SAAS,CAAC;IACtD,QAAQ,CAAC,mBAAmB,EAAE,uBAAuB,CAAC;IACtD,QAAQ,CAAC,qBAAqB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACpD,QAAQ,CAAC,wBAAwB,EAAE,+BAA+B,CAAC;CACpE;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,aAAa,GAAG,IAAI,CAiLvD;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,QAAQ,4EAIpB,CAAC"}
@@ -0,0 +1,155 @@
1
+ import { CURRENCY_CHANGED_EVENT, LANGUAGE_CHANGED_EVENT, } from '@endora-commerce/contracts';
2
+ import { lazyPort } from '@endora-commerce/platform/kernel';
3
+ import { DictionaryCache } from './services/dictionary-cache.js';
4
+ import { DictionaryValidator as DictionaryValidatorService } from './services/dictionary-validator.js';
5
+ import { DictionaryReadService } from './services/dictionary-read-service.js';
6
+ import { LabelResolver } from './services/label-resolver.js';
7
+ import { TranslationService } from './services/translation-service.js';
8
+ import { CountryService } from './services/country-service.js';
9
+ import { CountryReferenceRegistry } from './services/country-reference-registry.js';
10
+ import { registerCountryCurrencyReference } from './services/country-currency-reference.js';
11
+ import { LanguageCountryService } from './services/language-country-service.js';
12
+ import { runDictionarySeedReconciler } from './services/seed-reconciler.js';
13
+ import { registerDictionaryAdminRoutes } from './routes.admin.js';
14
+ import { registerDictionaryStorefrontRoutes } from './routes.storefront.js';
15
+ import { Country } from './entities/country.entity.js';
16
+ import { DictionaryTranslation } from './entities/dictionary-translation.entity.js';
17
+ import { LanguageCountry } from './entities/language-country.entity.js';
18
+ export function registerModule(ctx) {
19
+ ctx.di.register({
20
+ dictionaryCache: ctx
21
+ .asFunction(({ redis }) => redis ? new DictionaryCache(redis) : undefined)
22
+ .singleton(),
23
+ /** Drops both layers: the validator's in-process map and the Redis cache. */
24
+ dictionaryInvalidator: ctx
25
+ .asFunction(() => async () => {
26
+ const { dictionaryValidator, dictionaryCache } = ctx.cradle();
27
+ dictionaryValidator.invalidate();
28
+ // Swallowed rather than left floating: a drop that lands after the
29
+ // server closed surfaces as an unhandled rejection from ioredis's
30
+ // socket-close path. A cache that could not be dropped because the
31
+ // process is going away has nothing to be stale for.
32
+ await dictionaryCache?.invalidateAll().catch(() => undefined);
33
+ })
34
+ .singleton(),
35
+ });
36
+ // ---------------------------------------------------------------------------
37
+ // Feature 077 (D-87 drain) — who still points at a country?
38
+ //
39
+ // `CountryService.remove` used to answer that with raw counts over
40
+ // `addresses`, `taxes` and `organizations`, and the orphan report with twelve
41
+ // `left join`s over eleven tables in nine modules. Every one of them named a
42
+ // table this module does not own, in a string no import-level boundary check
43
+ // can see. Contributors push a descriptor from their own boot hook now.
44
+ //
45
+ // A plain `ctx.di.register`, not a port: a contributor resolves it from a boot
46
+ // hook, and a boot hook that resolved a transient gate would stop the backend
47
+ // from starting the moment this module was switched off. Enumeration policy is
48
+ // stated at the class.
49
+ ctx.di.register({
50
+ countryReferenceRegistry: ctx
51
+ .asFunction(() => new CountryReferenceRegistry())
52
+ .singleton(),
53
+ });
54
+ ctx.di.providePort('dictionaryValidator', ctx
55
+ .asFunction(({ emFactory }) => new DictionaryValidatorService(emFactory, lazyPort(ctx, 'currencyReadPort'), lazyPort(ctx, 'languageReadPort')))
56
+ .singleton());
57
+ // Whoever caches currency or language data drops its own when told. This
58
+ // module is one of them, and now says so itself rather than being reached
59
+ // into by a root.
60
+ //
61
+ // The two sales-channel events are issue #101's other half. The registry a
62
+ // storefront reads is *scoped to a channel* — `languages` and `currencies`
63
+ // are filtered by the channel's own lists, and `defaults` come off its
64
+ // `defaultLanguage` / `defaultCurrency` — so a channel write invalidates this
65
+ // cache exactly as a currency or language write does. Nothing subscribed
66
+ // before, so a PATCH of the default channel's languages served the old
67
+ // registry until the entry's hour expired, and a flag move (D-51) changed
68
+ // which channel the platform-wide read resolves without dropping anything.
69
+ // `identity_changed` covers the PATCH, `lifecycle_changed` the move and the
70
+ // deactivations. Both are EventBus announcements — no manifest dependency,
71
+ // and none is wanted: `sales_channels` must not know who caches its rows.
72
+ const invalidationEvents = [
73
+ CURRENCY_CHANGED_EVENT,
74
+ LANGUAGE_CHANGED_EVENT,
75
+ 'sales_channels.identity_changed',
76
+ 'sales_channels.lifecycle_changed',
77
+ ];
78
+ for (const event of invalidationEvents) {
79
+ ctx.subscribe(event, async () => {
80
+ await ctx.cradle().dictionaryInvalidator();
81
+ });
82
+ }
83
+ /**
84
+ * `countries.default_currency_code` points at `currencies`, so this module is
85
+ * one of that dictionary's consumers and contributes a descriptor like any
86
+ * other. `blocking: false` — the column's own foreign key is
87
+ * `on delete set null`, so the reference is worth reporting to an operator
88
+ * about to blank it but must not refuse the delete, which is exactly what the
89
+ * hand-written version said in a comment and expressed by leaving the count
90
+ * out of a sum.
91
+ *
92
+ * A contribution hook, kept separate from the seeding hook below: it must run
93
+ * whatever this module's state, and pushes an inert descriptor into an ungated
94
+ * registry (D-62). `product_feeds/backend.ts` is the shipped example of the
95
+ * pair.
96
+ */
97
+ ctx.onBoot(() => {
98
+ registerCountryCurrencyReference(lazyPort(ctx, 'currencyReferenceRegistry'), ctx.cradle().emFactory);
99
+ });
100
+ ctx.onBoot(async () => {
101
+ // Seeds the ISO reference data. The two tables it seeds that this module
102
+ // does not own — `currencies` and `languages` — go through their owners'
103
+ // published ports since feature 077's D-87 drain; before that they were raw
104
+ // statements naming those tables here.
105
+ await runDictionarySeedReconciler(ctx.cradle().emFactory, {
106
+ currencySeed: lazyPort(ctx, 'currencySeedPort'),
107
+ currencyRead: lazyPort(ctx, 'currencyReadPort'),
108
+ languageSeed: lazyPort(ctx, 'languageSeedPort'),
109
+ languageRead: lazyPort(ctx, 'languageReadPort'),
110
+ });
111
+ });
112
+ ctx.routes(async (app) => {
113
+ const { emFactory, auditLogService, requireAdmin, dictionaryCache, dictionaryInvalidator, countryReferenceRegistry, } = ctx.cradle();
114
+ // Resolved once per route registration and handed down, never captured into
115
+ // a singleton: `lazyPort` returns a proxy that resolves the gate per call,
116
+ // so a switched-off owner is answered at the call and not at composition.
117
+ const currencyRead = lazyPort(ctx, 'currencyReadPort');
118
+ const languageRead = lazyPort(ctx, 'languageReadPort');
119
+ await registerDictionaryAdminRoutes(app, {
120
+ emFactory,
121
+ countryService: new CountryService(emFactory, dictionaryInvalidator, auditLogService, () => countryReferenceRegistry),
122
+ currencyRead,
123
+ currencyAdmin: lazyPort(ctx, 'currencyAdminPort'),
124
+ languageRead,
125
+ languageAdmin: lazyPort(ctx, 'languageAdminPort'),
126
+ languageCountryService: new LanguageCountryService(emFactory, languageRead, dictionaryInvalidator, auditLogService),
127
+ translationService: new TranslationService(emFactory, currencyRead, languageRead, dictionaryInvalidator, auditLogService),
128
+ invalidateDictionaryState: dictionaryInvalidator,
129
+ requireAdmin,
130
+ countryReferences: countryReferenceRegistry,
131
+ currencyReferences: lazyPort(ctx, 'currencyReferenceRegistry'),
132
+ languageReferences: lazyPort(ctx, 'languageReferenceRegistry'),
133
+ });
134
+ await registerDictionaryStorefrontRoutes(app, {
135
+ readService: new DictionaryReadService(emFactory, currencyRead, languageRead, dictionaryCache, new LabelResolver(emFactory, currencyRead, languageRead)),
136
+ });
137
+ });
138
+ }
139
+ /**
140
+ * The module's persisted entity classes, on the `./backend` subpath, as one
141
+ * array and **no named class export** (D-168).
142
+ *
143
+ * This is the shape the platform reads when the package is *installed*: the
144
+ * boot-time loader (`src/packages/package-runtime.ts`, `exported['entities']`)
145
+ * and the static declaration reader (`scripts/lib/package-declarations.ts`),
146
+ * which is the third source of `check:module-boundary`'s `table→owner` map and
147
+ * the package pass of `check-entity-tenant-classification`. A missing array is
148
+ * answered with `[]` — zero entities registered, no error anywhere.
149
+ */
150
+ export const entities = [
151
+ Country,
152
+ DictionaryTranslation,
153
+ LanguageCountry,
154
+ ];
155
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/backend/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,39 @@
1
+ import type { FastifyInstance } from 'fastify';
2
+ import type { EntityManager } from '@mikro-orm/postgresql';
3
+ import { type CurrencyAdminPort, type CurrencyReadPort, type DictionaryReferenceRegistryPort, type LanguageAdminPort, type LanguageReadPort } from '@endora-commerce/contracts';
4
+ import type { CountryService } from './services/country-service.js';
5
+ import type { LanguageCountryService } from './services/language-country-service.js';
6
+ import type { TranslationService } from './services/translation-service.js';
7
+ import type { RequireAdminFactory } from '@endora-commerce/platform/kernel';
8
+ /**
9
+ * Feature 075, Phase C — this module hosts the admin screen for two tables it
10
+ * does not own, which is why the write side crosses a boundary by design
11
+ * (`languages.ts` and `currencies.ts` both say so). It used to cross by holding
12
+ * the owners' service classes and serialising their entities; it now holds the
13
+ * four published ports and serialises the records they return, so the screen
14
+ * refuses while an owner is switched off instead of editing rows nobody is
15
+ * serving.
16
+ */
17
+ export interface DictionaryAdminRoutesDeps {
18
+ emFactory: () => EntityManager;
19
+ countryService: CountryService;
20
+ currencyRead: CurrencyReadPort;
21
+ currencyAdmin: CurrencyAdminPort;
22
+ languageRead: LanguageReadPort;
23
+ languageAdmin: LanguageAdminPort;
24
+ languageCountryService: LanguageCountryService;
25
+ translationService: TranslationService;
26
+ invalidateDictionaryState: () => Promise<void>;
27
+ requireAdmin: RequireAdminFactory;
28
+ /**
29
+ * One reference registry per dictionary, each owned by the module that owns
30
+ * the table being pointed at (feature 077, D-87). The orphan report is the
31
+ * one caller that needs all three: it asks each consumer which codes it
32
+ * stores and subtracts the codes the dictionary really has.
33
+ */
34
+ countryReferences: DictionaryReferenceRegistryPort;
35
+ currencyReferences: DictionaryReferenceRegistryPort;
36
+ languageReferences: DictionaryReferenceRegistryPort;
37
+ }
38
+ export declare function registerDictionaryAdminRoutes(app: FastifyInstance, deps: DictionaryAdminRoutesDeps): Promise<void>;
39
+ //# sourceMappingURL=routes.admin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routes.admin.d.ts","sourceRoot":"","sources":["../../src/backend/routes.admin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAC/C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAE3D,OAAO,EAYL,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,EAErB,KAAK,+BAA+B,EACpC,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,EAItB,MAAM,4BAA4B,CAAC;AAEpC,OAAO,KAAK,EACV,cAAc,EAAC,MAAM,+BAA+B,CAAC;AAKvD,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,wCAAwC,CAAC;AAIrF,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mCAAmC,CAAC;AAC5E,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,kCAAkC,CAAC;AAG5E;;;;;;;;GAQG;AACH,MAAM,WAAW,yBAAyB;IACxC,SAAS,EAAE,MAAM,aAAa,CAAC;IAC/B,cAAc,EAAE,cAAc,CAAC;IAC/B,YAAY,EAAE,gBAAgB,CAAC;IAC/B,aAAa,EAAE,iBAAiB,CAAC;IACjC,YAAY,EAAE,gBAAgB,CAAC;IAC/B,aAAa,EAAE,iBAAiB,CAAC;IACjC,sBAAsB,EAAE,sBAAsB,CAAC;IAC/C,kBAAkB,EAAE,kBAAkB,CAAC;IACvC,yBAAyB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/C,YAAY,EAAE,mBAAmB,CAAC;IAClC;;;;;OAKG;IACH,iBAAiB,EAAE,+BAA+B,CAAC;IACnD,kBAAkB,EAAE,+BAA+B,CAAC;IACpD,kBAAkB,EAAE,+BAA+B,CAAC;CACrD;AASD,wBAAsB,6BAA6B,CACjD,GAAG,EAAE,eAAe,EACpB,IAAI,EAAE,yBAAyB,GAC9B,OAAO,CAAC,IAAI,CAAC,CAqPf"}