@harpy-js/core 0.4.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (147) hide show
  1. package/README.md +326 -0
  2. package/dist/cli.d.ts +12 -0
  3. package/dist/cli.js +53 -0
  4. package/dist/client/Link.d.ts +5 -0
  5. package/dist/client/Link.js +62 -0
  6. package/dist/client/__tests__/getActiveItemId.test.d.ts +1 -0
  7. package/dist/client/__tests__/getActiveItemId.test.js +38 -0
  8. package/dist/client/getActiveItemId.d.ts +7 -0
  9. package/dist/client/getActiveItemId.js +55 -0
  10. package/dist/client/use-i18n.d.ts +7 -0
  11. package/dist/client/use-i18n.js +64 -0
  12. package/dist/core/__tests__/component-analyzer.test.d.ts +1 -0
  13. package/dist/core/__tests__/component-analyzer.test.js +151 -0
  14. package/dist/core/__tests__/hydration-manifest.test.d.ts +1 -0
  15. package/dist/core/__tests__/hydration-manifest.test.js +211 -0
  16. package/dist/core/__tests__/jsx.engine.test.d.ts +1 -0
  17. package/dist/core/__tests__/jsx.engine.test.js +118 -0
  18. package/dist/core/app-setup.d.ts +7 -0
  19. package/dist/core/app-setup.js +79 -0
  20. package/dist/core/auto-register.module.d.ts +9 -0
  21. package/dist/core/auto-register.module.js +18 -0
  22. package/dist/core/auto-wrap-middleware.d.ts +4 -0
  23. package/dist/core/auto-wrap-middleware.js +130 -0
  24. package/dist/core/client-component-wrapper.d.ts +5 -0
  25. package/dist/core/client-component-wrapper.js +37 -0
  26. package/dist/core/client-hydration.d.ts +2 -0
  27. package/dist/core/client-hydration.js +93 -0
  28. package/dist/core/client-wrapper-browser.d.ts +2 -0
  29. package/dist/core/client-wrapper-browser.js +22 -0
  30. package/dist/core/component-analyzer.d.ts +4 -0
  31. package/dist/core/component-analyzer.js +98 -0
  32. package/dist/core/component-auto-wrapper.d.ts +2 -0
  33. package/dist/core/component-auto-wrapper.js +63 -0
  34. package/dist/core/component-client-wrapper.d.ts +4 -0
  35. package/dist/core/component-client-wrapper.js +80 -0
  36. package/dist/core/hydration-generator.d.ts +2 -0
  37. package/dist/core/hydration-generator.js +98 -0
  38. package/dist/core/hydration-manifest.d.ts +7 -0
  39. package/dist/core/hydration-manifest.js +83 -0
  40. package/dist/core/hydration.d.ts +16 -0
  41. package/dist/core/hydration.js +72 -0
  42. package/dist/core/jsx.engine.d.ts +9 -0
  43. package/dist/core/jsx.engine.js +161 -0
  44. package/dist/core/live-reload-client.js +32 -0
  45. package/dist/core/live-reload.controller.d.ts +10 -0
  46. package/dist/core/live-reload.controller.js +38 -0
  47. package/dist/core/navigation.service.d.ts +18 -0
  48. package/dist/core/navigation.service.js +206 -0
  49. package/dist/core/router.module.d.ts +2 -0
  50. package/dist/core/router.module.js +21 -0
  51. package/dist/core/static-assets.controller.d.ts +4 -0
  52. package/dist/core/static-assets.controller.js +51 -0
  53. package/dist/core/types/nav.types.d.ts +22 -0
  54. package/dist/core/types/nav.types.js +2 -0
  55. package/dist/core/views/layout.d.ts +8 -0
  56. package/dist/core/views/layout.js +35 -0
  57. package/dist/decorators/jsx.decorator.d.ts +26 -0
  58. package/dist/decorators/jsx.decorator.js +10 -0
  59. package/dist/decorators/layout.decorator.d.ts +4 -0
  60. package/dist/decorators/layout.decorator.js +29 -0
  61. package/dist/i18n/__tests__/i18n.helper.test.d.ts +1 -0
  62. package/dist/i18n/__tests__/i18n.helper.test.js +105 -0
  63. package/dist/i18n/__tests__/i18n.interceptor.test.d.ts +1 -0
  64. package/dist/i18n/__tests__/i18n.interceptor.test.js +195 -0
  65. package/dist/i18n/__tests__/i18n.module.test.d.ts +1 -0
  66. package/dist/i18n/__tests__/i18n.module.test.js +83 -0
  67. package/dist/i18n/__tests__/i18n.service.test.d.ts +1 -0
  68. package/dist/i18n/__tests__/i18n.service.test.js +109 -0
  69. package/dist/i18n/__tests__/t.test.d.ts +1 -0
  70. package/dist/i18n/__tests__/t.test.js +66 -0
  71. package/dist/i18n/i18n-module.options.d.ts +10 -0
  72. package/dist/i18n/i18n-module.options.js +4 -0
  73. package/dist/i18n/i18n-switcher.controller.d.ts +12 -0
  74. package/dist/i18n/i18n-switcher.controller.js +80 -0
  75. package/dist/i18n/i18n-types.d.ts +8 -0
  76. package/dist/i18n/i18n-types.js +2 -0
  77. package/dist/i18n/i18n.helper.d.ts +14 -0
  78. package/dist/i18n/i18n.helper.js +70 -0
  79. package/dist/i18n/i18n.interceptor.d.ts +9 -0
  80. package/dist/i18n/i18n.interceptor.js +99 -0
  81. package/dist/i18n/i18n.module.d.ts +5 -0
  82. package/dist/i18n/i18n.module.js +51 -0
  83. package/dist/i18n/i18n.service.d.ts +12 -0
  84. package/dist/i18n/i18n.service.js +61 -0
  85. package/dist/i18n/index.d.ts +10 -0
  86. package/dist/i18n/index.js +20 -0
  87. package/dist/i18n/locale.decorator.d.ts +1 -0
  88. package/dist/i18n/locale.decorator.js +8 -0
  89. package/dist/i18n/t.d.ts +3 -0
  90. package/dist/i18n/t.js +16 -0
  91. package/dist/index.d.ts +19 -0
  92. package/dist/index.js +40 -0
  93. package/package.json +79 -0
  94. package/scripts/analyze-styles.ts +124 -0
  95. package/scripts/auto-wrap-exports.ts +239 -0
  96. package/scripts/build-css.ts +38 -0
  97. package/scripts/build-hydration.ts +313 -0
  98. package/scripts/build-page-styles.ts +43 -0
  99. package/scripts/copy-assets.ts +34 -0
  100. package/scripts/dev.sh +3 -0
  101. package/scripts/dev.ts +257 -0
  102. package/src/cli.ts +71 -0
  103. package/src/client/Link.tsx +62 -0
  104. package/src/client/__tests__/getActiveItemId.test.ts +49 -0
  105. package/src/client/getActiveItemId.ts +54 -0
  106. package/src/client/use-i18n.ts +111 -0
  107. package/src/core/__tests__/component-analyzer.test.ts +141 -0
  108. package/src/core/__tests__/hydration-manifest.test.ts +223 -0
  109. package/src/core/__tests__/jsx.engine.test.ts +137 -0
  110. package/src/core/app-setup.ts +114 -0
  111. package/src/core/auto-register.module.ts +30 -0
  112. package/src/core/auto-wrap-middleware.ts +165 -0
  113. package/src/core/client-component-wrapper.ts +72 -0
  114. package/src/core/client-hydration.tsx +99 -0
  115. package/src/core/client-wrapper-browser.ts +40 -0
  116. package/src/core/component-analyzer.ts +89 -0
  117. package/src/core/component-auto-wrapper.ts +68 -0
  118. package/src/core/component-client-wrapper.ts +112 -0
  119. package/src/core/hydration-generator.ts +94 -0
  120. package/src/core/hydration-manifest.ts +79 -0
  121. package/src/core/hydration.ts +70 -0
  122. package/src/core/jsx.engine.ts +205 -0
  123. package/src/core/live-reload-client.js +32 -0
  124. package/src/core/live-reload.controller.ts +55 -0
  125. package/src/core/navigation.service.ts +257 -0
  126. package/src/core/router.module.ts +9 -0
  127. package/src/core/static-assets.controller.ts +19 -0
  128. package/src/core/types/nav.types.ts +53 -0
  129. package/src/core/views/layout.tsx +61 -0
  130. package/src/decorators/jsx.decorator.ts +49 -0
  131. package/src/decorators/layout.decorator.ts +66 -0
  132. package/src/i18n/__tests__/i18n.helper.test.ts +126 -0
  133. package/src/i18n/__tests__/i18n.interceptor.test.ts +229 -0
  134. package/src/i18n/__tests__/i18n.module.test.ts +98 -0
  135. package/src/i18n/__tests__/i18n.service.test.ts +129 -0
  136. package/src/i18n/__tests__/t.test.ts +88 -0
  137. package/src/i18n/i18n-module.options.ts +53 -0
  138. package/src/i18n/i18n-switcher.controller.ts +99 -0
  139. package/src/i18n/i18n-types.ts +56 -0
  140. package/src/i18n/i18n.helper.ts +75 -0
  141. package/src/i18n/i18n.interceptor.ts +114 -0
  142. package/src/i18n/i18n.module.ts +45 -0
  143. package/src/i18n/i18n.service.ts +95 -0
  144. package/src/i18n/index.ts +37 -0
  145. package/src/i18n/locale.decorator.ts +10 -0
  146. package/src/i18n/t.ts +62 -0
  147. package/src/index.ts +31 -0
@@ -0,0 +1,109 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const testing_1 = require("@nestjs/testing");
4
+ const i18n_service_1 = require("../i18n.service");
5
+ const i18n_module_options_1 = require("../i18n-module.options");
6
+ const core_1 = require("@nestjs/core");
7
+ describe("I18nService", () => {
8
+ let service;
9
+ const mockOptions = {
10
+ defaultLocale: "en",
11
+ locales: ["en", "fr"],
12
+ urlPattern: "query",
13
+ translationsPath: "./dictionaries",
14
+ cookieName: "locale",
15
+ queryParam: "lang",
16
+ };
17
+ const mockRequest = {
18
+ locale: "en",
19
+ };
20
+ beforeEach(async () => {
21
+ const module = await testing_1.Test.createTestingModule({
22
+ providers: [
23
+ i18n_service_1.I18nService,
24
+ {
25
+ provide: i18n_module_options_1.I18N_MODULE_OPTIONS,
26
+ useValue: mockOptions,
27
+ },
28
+ {
29
+ provide: core_1.REQUEST,
30
+ useValue: mockRequest,
31
+ },
32
+ ],
33
+ }).compile();
34
+ service = await module.resolve(i18n_service_1.I18nService);
35
+ });
36
+ describe("getLocale", () => {
37
+ it("should return locale from request", () => {
38
+ expect(service.getLocale()).toBe("en");
39
+ });
40
+ it("should return default locale when request locale is not set", async () => {
41
+ const requestWithoutLocale = {};
42
+ const module = await testing_1.Test.createTestingModule({
43
+ providers: [
44
+ i18n_service_1.I18nService,
45
+ {
46
+ provide: i18n_module_options_1.I18N_MODULE_OPTIONS,
47
+ useValue: mockOptions,
48
+ },
49
+ {
50
+ provide: core_1.REQUEST,
51
+ useValue: requestWithoutLocale,
52
+ },
53
+ ],
54
+ }).compile();
55
+ const serviceInstance = await module.resolve(i18n_service_1.I18nService);
56
+ expect(serviceInstance.getLocale()).toBe("en");
57
+ });
58
+ });
59
+ describe("getDict", () => {
60
+ it("should return empty object when no dictionary loader registered", async () => {
61
+ const dict = await service.getDict();
62
+ expect(dict).toEqual({});
63
+ });
64
+ it("should call registered dictionary loader", async () => {
65
+ const mockDict = { hello: "Hello", goodbye: "Goodbye" };
66
+ const mockLoader = jest.fn().mockResolvedValue(mockDict);
67
+ service.registerDictionaryLoader(mockLoader);
68
+ const dict = await service.getDict();
69
+ expect(mockLoader).toHaveBeenCalledWith("en");
70
+ expect(dict).toEqual(mockDict);
71
+ });
72
+ it("should cache dictionary after first load", async () => {
73
+ const mockDict = { hello: "Hello" };
74
+ const mockLoader = jest.fn().mockResolvedValue(mockDict);
75
+ service.registerDictionaryLoader(mockLoader);
76
+ await service.getDict();
77
+ await service.getDict();
78
+ expect(mockLoader).toHaveBeenCalledTimes(1);
79
+ });
80
+ });
81
+ describe("translate", () => {
82
+ beforeEach(() => {
83
+ const mockDict = {
84
+ welcome: "Welcome",
85
+ greeting: "Hello {{name}}!",
86
+ nested: {
87
+ key: "Nested value",
88
+ },
89
+ };
90
+ service.registerDictionaryLoader(jest.fn().mockResolvedValue(mockDict));
91
+ });
92
+ it("should translate simple key", async () => {
93
+ const result = await service.translate("welcome");
94
+ expect(result).toBe("Welcome");
95
+ });
96
+ it("should translate with variables", async () => {
97
+ const result = await service.translate("greeting", { name: "John" });
98
+ expect(result).toBe("Hello John!");
99
+ });
100
+ it("should translate nested key", async () => {
101
+ const result = await service.translate("nested.key");
102
+ expect(result).toBe("Nested value");
103
+ });
104
+ it("should return empty string for non-existent key", async () => {
105
+ const result = await service.translate("nonexistent");
106
+ expect(result).toBe("");
107
+ });
108
+ });
109
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,66 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const t_1 = require("../t");
4
+ describe("Translation Function (t)", () => {
5
+ const mockDict = {
6
+ simple: "Hello",
7
+ nested: {
8
+ key: "Nested value",
9
+ deep: {
10
+ value: "Deep nested value",
11
+ },
12
+ },
13
+ withVar: "Hello {{name}}!",
14
+ withMultipleVars: "Hello {{name}}, you have {{count}} messages",
15
+ };
16
+ describe("Simple translations", () => {
17
+ it("should translate simple key", () => {
18
+ expect((0, t_1.t)(mockDict, "simple")).toBe("Hello");
19
+ });
20
+ it("should translate nested key", () => {
21
+ expect((0, t_1.t)(mockDict, "nested.key")).toBe("Nested value");
22
+ });
23
+ it("should translate deeply nested key", () => {
24
+ expect((0, t_1.t)(mockDict, "nested.deep.value")).toBe("Deep nested value");
25
+ });
26
+ });
27
+ describe("Variable interpolation", () => {
28
+ it("should interpolate single variable", () => {
29
+ expect((0, t_1.t)(mockDict, "withVar", { name: "John" })).toBe("Hello John!");
30
+ });
31
+ it("should interpolate multiple variables", () => {
32
+ expect((0, t_1.t)(mockDict, "withMultipleVars", { name: "John", count: 5 })).toBe("Hello John, you have 5 messages");
33
+ });
34
+ it("should handle missing variables gracefully", () => {
35
+ expect((0, t_1.t)(mockDict, "withVar", {})).toBe("Hello !");
36
+ });
37
+ it("should handle numeric variables", () => {
38
+ expect((0, t_1.t)(mockDict, "withMultipleVars", { name: "John", count: 0 })).toBe("Hello John, you have 0 messages");
39
+ });
40
+ });
41
+ describe("Edge cases", () => {
42
+ it("should return empty string for non-existent key", () => {
43
+ expect((0, t_1.t)(mockDict, "nonexistent")).toBe("");
44
+ });
45
+ it("should return empty string for non-string value", () => {
46
+ expect((0, t_1.t)(mockDict, "nested")).toBe("");
47
+ });
48
+ it("should handle empty dictionary", () => {
49
+ expect((0, t_1.t)({}, "any")).toBe("");
50
+ });
51
+ });
52
+ describe("tUnsafe function", () => {
53
+ it("should work with dynamic keys", () => {
54
+ expect((0, t_1.tUnsafe)(mockDict, "simple")).toBe("Hello");
55
+ });
56
+ it("should work with nested keys", () => {
57
+ expect((0, t_1.tUnsafe)(mockDict, "nested.key")).toBe("Nested value");
58
+ });
59
+ it("should interpolate variables", () => {
60
+ expect((0, t_1.tUnsafe)(mockDict, "withVar", { name: "Jane" })).toBe("Hello Jane!");
61
+ });
62
+ it("should handle non-existent keys", () => {
63
+ expect((0, t_1.tUnsafe)(mockDict, "invalid.key")).toBe("");
64
+ });
65
+ });
66
+ });
@@ -0,0 +1,10 @@
1
+ export type I18nUrlPattern = "query" | "header";
2
+ export interface I18nModuleOptions {
3
+ defaultLocale: string;
4
+ locales: string[];
5
+ urlPattern?: I18nUrlPattern;
6
+ translationsPath?: string;
7
+ cookieName?: string;
8
+ queryParam?: string;
9
+ }
10
+ export declare const I18N_MODULE_OPTIONS = "I18N_MODULE_OPTIONS";
@@ -0,0 +1,4 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.I18N_MODULE_OPTIONS = void 0;
4
+ exports.I18N_MODULE_OPTIONS = "I18N_MODULE_OPTIONS";
@@ -0,0 +1,12 @@
1
+ import { I18nHelper } from "./i18n.helper";
2
+ export declare class I18nSwitcherController {
3
+ private readonly i18nHelper;
4
+ constructor(i18nHelper: I18nHelper);
5
+ switchLocale(locale: string, req: any, res: any): any;
6
+ getConfig(): {
7
+ locales: string[];
8
+ defaultLocale: string;
9
+ urlPattern: import("./i18n-module.options").I18nUrlPattern;
10
+ queryParam: string;
11
+ };
12
+ }
@@ -0,0 +1,80 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ 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;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
12
+ return function (target, key) { decorator(target, key, paramIndex); }
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.I18nSwitcherController = void 0;
16
+ const common_1 = require("@nestjs/common");
17
+ const i18n_helper_1 = require("./i18n.helper");
18
+ let I18nSwitcherController = class I18nSwitcherController {
19
+ i18nHelper;
20
+ constructor(i18nHelper) {
21
+ this.i18nHelper = i18nHelper;
22
+ }
23
+ switchLocale(locale, req, res) {
24
+ if (!locale) {
25
+ return res
26
+ .status(common_1.HttpStatus.BAD_REQUEST)
27
+ .send({ error: "Locale is required" });
28
+ }
29
+ const validatedLocale = this.i18nHelper.validateLocale(locale);
30
+ this.i18nHelper.setLocaleCookie(res, validatedLocale);
31
+ const referer = req.headers.referer || req.headers.origin || "/";
32
+ let currentPath = "/";
33
+ let query = {};
34
+ try {
35
+ const refererUrl = new URL(referer);
36
+ const requestOrigin = `${req.protocol}://${req.hostname}${req.hostname === "localhost" && req.port ? ":" + req.port : ""}`;
37
+ if (refererUrl.origin !== requestOrigin) {
38
+ currentPath = "/";
39
+ }
40
+ else {
41
+ currentPath = refererUrl.pathname;
42
+ refererUrl.searchParams.forEach((value, key) => {
43
+ query[key] = value;
44
+ });
45
+ }
46
+ }
47
+ catch {
48
+ currentPath = "/";
49
+ }
50
+ const redirectUrl = this.i18nHelper.buildLocaleUrl(validatedLocale, currentPath, query);
51
+ return res.send({
52
+ success: true,
53
+ locale: validatedLocale,
54
+ redirectUrl,
55
+ });
56
+ }
57
+ getConfig() {
58
+ return this.i18nHelper.getClientConfig();
59
+ }
60
+ };
61
+ exports.I18nSwitcherController = I18nSwitcherController;
62
+ __decorate([
63
+ (0, common_1.Post)("switch-locale"),
64
+ __param(0, (0, common_1.Body)("locale")),
65
+ __param(1, (0, common_1.Req)()),
66
+ __param(2, (0, common_1.Res)()),
67
+ __metadata("design:type", Function),
68
+ __metadata("design:paramtypes", [String, Object, Object]),
69
+ __metadata("design:returntype", void 0)
70
+ ], I18nSwitcherController.prototype, "switchLocale", null);
71
+ __decorate([
72
+ (0, common_1.Get)("config"),
73
+ __metadata("design:type", Function),
74
+ __metadata("design:paramtypes", []),
75
+ __metadata("design:returntype", void 0)
76
+ ], I18nSwitcherController.prototype, "getConfig", null);
77
+ exports.I18nSwitcherController = I18nSwitcherController = __decorate([
78
+ (0, common_1.Controller)("api/i18n"),
79
+ __metadata("design:paramtypes", [i18n_helper_1.I18nHelper])
80
+ ], I18nSwitcherController);
@@ -0,0 +1,8 @@
1
+ export type NestedKeyOf<ObjectType extends object> = {
2
+ [Key in keyof ObjectType & string]: ObjectType[Key] extends object ? ObjectType[Key] extends Array<any> ? Key : `${Key}` | `${Key}.${NestedKeyOf<ObjectType[Key]>}` : Key;
3
+ }[keyof ObjectType & string];
4
+ export type DeepValue<T, K extends string> = K extends `${infer First}.${infer Rest}` ? First extends keyof T ? Rest extends string ? DeepValue<T[First], Rest> : never : never : K extends keyof T ? T[K] : never;
5
+ export type ExtractVariables<T extends string> = T extends `${string}{{${infer Var}}}${infer Rest}` ? {
6
+ [K in Var | keyof ExtractVariables<Rest>]: string | number;
7
+ } : Record<string, never>;
8
+ export type RequiresVariables<T> = T extends string ? T extends `${string}{{${string}}}${string}` ? true : false : false;
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,14 @@
1
+ import type { I18nModuleOptions } from "./i18n-module.options";
2
+ export declare class I18nHelper {
3
+ private readonly options;
4
+ constructor(options: I18nModuleOptions);
5
+ buildLocaleUrl(locale: string, currentPath: string, query?: Record<string, string>): string;
6
+ setLocaleCookie(res: any, locale: string): void;
7
+ validateLocale(locale: string): string;
8
+ getClientConfig(): {
9
+ locales: string[];
10
+ defaultLocale: string;
11
+ urlPattern: import("./i18n-module.options").I18nUrlPattern;
12
+ queryParam: string;
13
+ };
14
+ }
@@ -0,0 +1,70 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ 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;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
12
+ return function (target, key) { decorator(target, key, paramIndex); }
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.I18nHelper = void 0;
16
+ const common_1 = require("@nestjs/common");
17
+ const i18n_module_options_1 = require("./i18n-module.options");
18
+ let I18nHelper = class I18nHelper {
19
+ options;
20
+ constructor(options) {
21
+ this.options = options;
22
+ }
23
+ buildLocaleUrl(locale, currentPath, query) {
24
+ const { urlPattern, queryParam = "lang" } = this.options;
25
+ switch (urlPattern) {
26
+ case "query": {
27
+ const params = new URLSearchParams(query || {});
28
+ params.set(queryParam, locale);
29
+ return `${currentPath}?${params.toString()}`;
30
+ }
31
+ case "header":
32
+ default: {
33
+ if (query && Object.keys(query).length > 0) {
34
+ const params = new URLSearchParams(query);
35
+ return `${currentPath}?${params.toString()}`;
36
+ }
37
+ return currentPath;
38
+ }
39
+ }
40
+ }
41
+ setLocaleCookie(res, locale) {
42
+ const { cookieName = "locale" } = this.options;
43
+ res.setCookie(cookieName, locale, {
44
+ path: "/",
45
+ maxAge: 365 * 24 * 60 * 60,
46
+ httpOnly: false,
47
+ sameSite: "lax",
48
+ });
49
+ }
50
+ validateLocale(locale) {
51
+ if (!this.options.locales?.includes(locale)) {
52
+ return this.options.defaultLocale || "en";
53
+ }
54
+ return locale;
55
+ }
56
+ getClientConfig() {
57
+ return {
58
+ locales: this.options.locales || ["en"],
59
+ defaultLocale: this.options.defaultLocale || "en",
60
+ urlPattern: this.options.urlPattern || "query",
61
+ queryParam: this.options.queryParam || "lang",
62
+ };
63
+ }
64
+ };
65
+ exports.I18nHelper = I18nHelper;
66
+ exports.I18nHelper = I18nHelper = __decorate([
67
+ (0, common_1.Injectable)(),
68
+ __param(0, (0, common_1.Inject)(i18n_module_options_1.I18N_MODULE_OPTIONS)),
69
+ __metadata("design:paramtypes", [Object])
70
+ ], I18nHelper);
@@ -0,0 +1,9 @@
1
+ import { NestInterceptor, ExecutionContext, CallHandler } from "@nestjs/common";
2
+ import { Observable } from "rxjs";
3
+ import type { I18nModuleOptions } from "./i18n-module.options";
4
+ export declare class I18nInterceptor implements NestInterceptor {
5
+ private options;
6
+ constructor(options: I18nModuleOptions);
7
+ intercept(context: ExecutionContext, next: CallHandler): Observable<any>;
8
+ private parseCookie;
9
+ }
@@ -0,0 +1,99 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ 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;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
12
+ return function (target, key) { decorator(target, key, paramIndex); }
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.I18nInterceptor = void 0;
16
+ const common_1 = require("@nestjs/common");
17
+ const i18n_module_options_1 = require("./i18n-module.options");
18
+ let I18nInterceptor = class I18nInterceptor {
19
+ options;
20
+ constructor(options) {
21
+ this.options = options;
22
+ }
23
+ intercept(context, next) {
24
+ const request = context.switchToHttp().getRequest();
25
+ const response = context.switchToHttp().getResponse();
26
+ const locales = this.options.locales;
27
+ const defaultLocale = this.options.defaultLocale;
28
+ let locale = undefined;
29
+ let shouldSetCookie = false;
30
+ switch (this.options.urlPattern) {
31
+ case "query":
32
+ const queryLang = request.query?.[this.options.queryParam || "lang"];
33
+ if (queryLang && locales.includes(queryLang)) {
34
+ locale = queryLang;
35
+ shouldSetCookie = true;
36
+ }
37
+ break;
38
+ case "header":
39
+ const xLang = request.headers["x-lang"];
40
+ if (xLang && locales.includes(xLang)) {
41
+ locale = xLang;
42
+ shouldSetCookie = true;
43
+ }
44
+ break;
45
+ }
46
+ const cookieLang = this.parseCookie(request.headers.cookie, this.options.cookieName || "locale");
47
+ if (!locale) {
48
+ if (cookieLang && locales.includes(cookieLang)) {
49
+ locale = cookieLang;
50
+ }
51
+ }
52
+ if (!locale) {
53
+ const acceptLanguage = request.headers["accept-language"];
54
+ if (acceptLanguage) {
55
+ const preferredLocale = acceptLanguage
56
+ .split(",")[0]
57
+ .split("-")[0]
58
+ .toLowerCase();
59
+ if (locales.includes(preferredLocale)) {
60
+ locale = preferredLocale;
61
+ }
62
+ }
63
+ }
64
+ if (!locale) {
65
+ locale = defaultLocale;
66
+ }
67
+ request.locale = locale;
68
+ if (shouldSetCookie && cookieLang !== locale) {
69
+ const cookieName = this.options.cookieName || "locale";
70
+ const maxAge = 365 * 24 * 60 * 60;
71
+ response.header("Set-Cookie", `${cookieName}=${locale}; Path=/; Max-Age=${maxAge}; HttpOnly; SameSite=Lax`);
72
+ }
73
+ return next.handle();
74
+ }
75
+ parseCookie(cookieHeader, name) {
76
+ if (!cookieHeader)
77
+ return undefined;
78
+ const cookies = cookieHeader.split(";").map((c) => c.trim());
79
+ const cookie = cookies.find((c) => c.startsWith(`${name}=`));
80
+ if (!cookie)
81
+ return undefined;
82
+ const eqIndex = cookie.indexOf("=");
83
+ if (eqIndex === -1)
84
+ return undefined;
85
+ const value = cookie.slice(eqIndex + 1);
86
+ try {
87
+ return decodeURIComponent(value);
88
+ }
89
+ catch {
90
+ return value;
91
+ }
92
+ }
93
+ };
94
+ exports.I18nInterceptor = I18nInterceptor;
95
+ exports.I18nInterceptor = I18nInterceptor = __decorate([
96
+ (0, common_1.Injectable)(),
97
+ __param(0, (0, common_1.Inject)(i18n_module_options_1.I18N_MODULE_OPTIONS)),
98
+ __metadata("design:paramtypes", [Object])
99
+ ], I18nInterceptor);
@@ -0,0 +1,5 @@
1
+ import { DynamicModule } from "@nestjs/common";
2
+ import { I18nModuleOptions } from "./i18n-module.options";
3
+ export declare class I18nModule {
4
+ static forRoot(options: I18nModuleOptions): DynamicModule;
5
+ }
@@ -0,0 +1,51 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ 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;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var I18nModule_1;
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.I18nModule = void 0;
11
+ const common_1 = require("@nestjs/common");
12
+ const core_1 = require("@nestjs/core");
13
+ const i18n_service_1 = require("./i18n.service");
14
+ const i18n_interceptor_1 = require("./i18n.interceptor");
15
+ const i18n_helper_1 = require("./i18n.helper");
16
+ const i18n_switcher_controller_1 = require("./i18n-switcher.controller");
17
+ const i18n_module_options_1 = require("./i18n-module.options");
18
+ let I18nModule = I18nModule_1 = class I18nModule {
19
+ static forRoot(options) {
20
+ const moduleOptions = {
21
+ defaultLocale: options.defaultLocale || "en",
22
+ locales: options.locales || ["en"],
23
+ urlPattern: options.urlPattern || "query",
24
+ translationsPath: options.translationsPath || "./dictionaries",
25
+ cookieName: options.cookieName || "locale",
26
+ queryParam: options.queryParam || "lang",
27
+ };
28
+ return {
29
+ module: I18nModule_1,
30
+ controllers: [i18n_switcher_controller_1.I18nSwitcherController],
31
+ providers: [
32
+ {
33
+ provide: i18n_module_options_1.I18N_MODULE_OPTIONS,
34
+ useValue: moduleOptions,
35
+ },
36
+ i18n_service_1.I18nService,
37
+ i18n_helper_1.I18nHelper,
38
+ {
39
+ provide: core_1.APP_INTERCEPTOR,
40
+ useClass: i18n_interceptor_1.I18nInterceptor,
41
+ },
42
+ ],
43
+ exports: [i18n_service_1.I18nService, i18n_helper_1.I18nHelper, i18n_module_options_1.I18N_MODULE_OPTIONS],
44
+ };
45
+ }
46
+ };
47
+ exports.I18nModule = I18nModule;
48
+ exports.I18nModule = I18nModule = I18nModule_1 = __decorate([
49
+ (0, common_1.Global)(),
50
+ (0, common_1.Module)({})
51
+ ], I18nModule);
@@ -0,0 +1,12 @@
1
+ import type { I18nModuleOptions } from "./i18n-module.options";
2
+ export declare class I18nService {
3
+ private request;
4
+ private options;
5
+ private dict;
6
+ private getDictionaryFn;
7
+ constructor(request: any, options: I18nModuleOptions);
8
+ registerDictionaryLoader(fn: (locale: string) => Promise<any>): void;
9
+ getDict(): Promise<Record<string, any>>;
10
+ getLocale(): string;
11
+ translate(key: string, vars?: Record<string, string | number>): Promise<string>;
12
+ }
@@ -0,0 +1,61 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ 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;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
12
+ return function (target, key) { decorator(target, key, paramIndex); }
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.I18nService = void 0;
16
+ const common_1 = require("@nestjs/common");
17
+ const core_1 = require("@nestjs/core");
18
+ const i18n_module_options_1 = require("./i18n-module.options");
19
+ let I18nService = class I18nService {
20
+ request;
21
+ options;
22
+ dict = null;
23
+ getDictionaryFn = null;
24
+ constructor(request, options) {
25
+ this.request = request;
26
+ this.options = options;
27
+ }
28
+ registerDictionaryLoader(fn) {
29
+ this.getDictionaryFn = fn;
30
+ }
31
+ async getDict() {
32
+ if (!this.dict) {
33
+ const locale = this.request.locale || this.options.defaultLocale;
34
+ if (this.getDictionaryFn) {
35
+ this.dict = await this.getDictionaryFn(locale);
36
+ }
37
+ else {
38
+ console.warn("No dictionary loader registered. Call registerDictionaryLoader() in your app.");
39
+ this.dict = {};
40
+ }
41
+ }
42
+ return this.dict ?? {};
43
+ }
44
+ getLocale() {
45
+ return this.request.locale || this.options.defaultLocale;
46
+ }
47
+ async translate(key, vars) {
48
+ const dict = await this.getDict();
49
+ const value = key.split(".").reduce((acc, k) => acc?.[k], dict);
50
+ if (typeof value !== "string")
51
+ return "";
52
+ return value.replace(/\{\{(.*?)\}\}/g, (_match, k) => String(vars?.[k.trim()] ?? ""));
53
+ }
54
+ };
55
+ exports.I18nService = I18nService;
56
+ exports.I18nService = I18nService = __decorate([
57
+ (0, common_1.Injectable)({ scope: common_1.Scope.REQUEST }),
58
+ __param(0, (0, common_1.Inject)(core_1.REQUEST)),
59
+ __param(1, (0, common_1.Inject)(i18n_module_options_1.I18N_MODULE_OPTIONS)),
60
+ __metadata("design:paramtypes", [Object, Object])
61
+ ], I18nService);
@@ -0,0 +1,10 @@
1
+ export { I18nModule } from "./i18n.module";
2
+ export { I18nService } from "./i18n.service";
3
+ export { I18nInterceptor } from "./i18n.interceptor";
4
+ export { I18nHelper } from "./i18n.helper";
5
+ export { I18nSwitcherController } from "./i18n-switcher.controller";
6
+ export { CurrentLocale } from "./locale.decorator";
7
+ export { t, tUnsafe } from "./t";
8
+ export { I18N_MODULE_OPTIONS } from "./i18n-module.options";
9
+ export type { I18nModuleOptions, I18nUrlPattern } from "./i18n-module.options";
10
+ export type { NestedKeyOf, DeepValue, ExtractVariables, RequiresVariables, } from "./i18n-types";