@inlang/sdk 0.1.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.
- package/README.md +25 -0
- package/dist/adapter/solidAdapter.d.ts +32 -0
- package/dist/adapter/solidAdapter.d.ts.map +1 -0
- package/dist/adapter/solidAdapter.js +39 -0
- package/dist/adapter/solidAdapter.test.d.ts +2 -0
- package/dist/adapter/solidAdapter.test.d.ts.map +1 -0
- package/dist/adapter/solidAdapter.test.js +284 -0
- package/dist/api.d.ts +88 -0
- package/dist/api.d.ts.map +1 -0
- package/dist/api.js +1 -0
- package/dist/createMessageLintReportsQuery.d.ts +9 -0
- package/dist/createMessageLintReportsQuery.d.ts.map +1 -0
- package/dist/createMessageLintReportsQuery.js +48 -0
- package/dist/createMessagesQuery.d.ts +7 -0
- package/dist/createMessagesQuery.d.ts.map +1 -0
- package/dist/createMessagesQuery.js +57 -0
- package/dist/createMessagesQuery.test.d.ts +2 -0
- package/dist/createMessagesQuery.test.d.ts.map +1 -0
- package/dist/createMessagesQuery.test.js +304 -0
- package/dist/errors.d.ts +22 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +39 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +13 -0
- package/dist/lint/index.d.ts +3 -0
- package/dist/lint/index.d.ts.map +1 -0
- package/dist/lint/index.js +2 -0
- package/dist/lint/message/errors.d.ts +7 -0
- package/dist/lint/message/errors.d.ts.map +1 -0
- package/dist/lint/message/errors.js +9 -0
- package/dist/lint/message/lintMessages.d.ts +17 -0
- package/dist/lint/message/lintMessages.d.ts.map +1 -0
- package/dist/lint/message/lintMessages.js +12 -0
- package/dist/lint/message/lintMessages.test.d.ts +2 -0
- package/dist/lint/message/lintMessages.test.d.ts.map +1 -0
- package/dist/lint/message/lintMessages.test.js +105 -0
- package/dist/lint/message/lintSingleMessage.d.ts +23 -0
- package/dist/lint/message/lintSingleMessage.d.ts.map +1 -0
- package/dist/lint/message/lintSingleMessage.js +36 -0
- package/dist/lint/message/lintSingleMessage.test.d.ts +2 -0
- package/dist/lint/message/lintSingleMessage.test.d.ts.map +1 -0
- package/dist/lint/message/lintSingleMessage.test.js +155 -0
- package/dist/messages/errors.d.ts +13 -0
- package/dist/messages/errors.d.ts.map +1 -0
- package/dist/messages/errors.js +18 -0
- package/dist/messages/index.d.ts +3 -0
- package/dist/messages/index.d.ts.map +1 -0
- package/dist/messages/index.js +2 -0
- package/dist/messages/variant.d.ts +46 -0
- package/dist/messages/variant.d.ts.map +1 -0
- package/dist/messages/variant.js +177 -0
- package/dist/messages/variant.test.d.ts +2 -0
- package/dist/messages/variant.test.d.ts.map +1 -0
- package/dist/messages/variant.test.js +379 -0
- package/dist/openInlangProject.d.ts +18 -0
- package/dist/openInlangProject.d.ts.map +1 -0
- package/dist/openInlangProject.js +226 -0
- package/dist/openInlangProject.test.d.ts +2 -0
- package/dist/openInlangProject.test.d.ts.map +1 -0
- package/dist/openInlangProject.test.js +627 -0
- package/dist/parseConfig.d.ts +8 -0
- package/dist/parseConfig.d.ts.map +1 -0
- package/dist/parseConfig.js +26 -0
- package/dist/reactivity/map.d.ts +66 -0
- package/dist/reactivity/map.d.ts.map +1 -0
- package/dist/reactivity/map.js +143 -0
- package/dist/reactivity/solid.d.ts +12 -0
- package/dist/reactivity/solid.d.ts.map +1 -0
- package/dist/reactivity/solid.js +13 -0
- package/dist/reactivity/trigger.d.ts +11 -0
- package/dist/reactivity/trigger.d.ts.map +1 -0
- package/dist/reactivity/trigger.js +46 -0
- package/dist/resolve-modules/errors.d.ts +34 -0
- package/dist/resolve-modules/errors.d.ts.map +1 -0
- package/dist/resolve-modules/errors.js +35 -0
- package/dist/resolve-modules/import.d.ts +35 -0
- package/dist/resolve-modules/import.d.ts.map +1 -0
- package/dist/resolve-modules/import.js +40 -0
- package/dist/resolve-modules/import.test.d.ts +2 -0
- package/dist/resolve-modules/import.test.d.ts.map +1 -0
- package/dist/resolve-modules/import.test.js +45 -0
- package/dist/resolve-modules/index.d.ts +3 -0
- package/dist/resolve-modules/index.d.ts.map +1 -0
- package/dist/resolve-modules/index.js +2 -0
- package/dist/resolve-modules/message-lint-rules/errors.d.ts +8 -0
- package/dist/resolve-modules/message-lint-rules/errors.d.ts.map +1 -0
- package/dist/resolve-modules/message-lint-rules/errors.js +8 -0
- package/dist/resolve-modules/message-lint-rules/resolveMessageLintRules.d.ts +9 -0
- package/dist/resolve-modules/message-lint-rules/resolveMessageLintRules.d.ts.map +1 -0
- package/dist/resolve-modules/message-lint-rules/resolveMessageLintRules.js +21 -0
- package/dist/resolve-modules/plugins/errors.d.ts +28 -0
- package/dist/resolve-modules/plugins/errors.d.ts.map +1 -0
- package/dist/resolve-modules/plugins/errors.js +44 -0
- package/dist/resolve-modules/plugins/resolvePlugins.d.ts +3 -0
- package/dist/resolve-modules/plugins/resolvePlugins.d.ts.map +1 -0
- package/dist/resolve-modules/plugins/resolvePlugins.js +108 -0
- package/dist/resolve-modules/plugins/resolvePlugins.test.d.ts +2 -0
- package/dist/resolve-modules/plugins/resolvePlugins.test.d.ts.map +1 -0
- package/dist/resolve-modules/plugins/resolvePlugins.test.js +289 -0
- package/dist/resolve-modules/plugins/types.d.ts +60 -0
- package/dist/resolve-modules/plugins/types.d.ts.map +1 -0
- package/dist/resolve-modules/plugins/types.js +1 -0
- package/dist/resolve-modules/plugins/types.test.d.ts +2 -0
- package/dist/resolve-modules/plugins/types.test.d.ts.map +1 -0
- package/dist/resolve-modules/plugins/types.test.js +49 -0
- package/dist/resolve-modules/resolveModules.d.ts +3 -0
- package/dist/resolve-modules/resolveModules.d.ts.map +1 -0
- package/dist/resolve-modules/resolveModules.js +70 -0
- package/dist/resolve-modules/resolveModules.test.d.ts +2 -0
- package/dist/resolve-modules/resolveModules.test.d.ts.map +1 -0
- package/dist/resolve-modules/resolveModules.test.js +143 -0
- package/dist/resolve-modules/types.d.ts +62 -0
- package/dist/resolve-modules/types.d.ts.map +1 -0
- package/dist/resolve-modules/types.js +1 -0
- package/dist/test-utilities/createMessage.d.ts +17 -0
- package/dist/test-utilities/createMessage.d.ts.map +1 -0
- package/dist/test-utilities/createMessage.js +16 -0
- package/dist/test-utilities/createMessage.test.d.ts +2 -0
- package/dist/test-utilities/createMessage.test.d.ts.map +1 -0
- package/dist/test-utilities/createMessage.test.js +91 -0
- package/dist/test-utilities/index.d.ts +2 -0
- package/dist/test-utilities/index.d.ts.map +1 -0
- package/dist/test-utilities/index.js +1 -0
- package/dist/versionedInterfaces.d.ts +8 -0
- package/dist/versionedInterfaces.d.ts.map +1 -0
- package/dist/versionedInterfaces.js +8 -0
- package/package.json +58 -0
- package/src/adapter/solidAdapter.test.ts +363 -0
- package/src/adapter/solidAdapter.ts +77 -0
- package/src/api.ts +86 -0
- package/src/createMessageLintReportsQuery.ts +77 -0
- package/src/createMessagesQuery.test.ts +435 -0
- package/src/createMessagesQuery.ts +64 -0
- package/src/errors.ts +46 -0
- package/src/index.ts +29 -0
- package/src/lint/index.ts +2 -0
- package/src/lint/message/errors.ts +9 -0
- package/src/lint/message/lintMessages.test.ts +122 -0
- package/src/lint/message/lintMessages.ts +33 -0
- package/src/lint/message/lintSingleMessage.test.ts +183 -0
- package/src/lint/message/lintSingleMessage.ts +62 -0
- package/src/messages/errors.ts +25 -0
- package/src/messages/index.ts +2 -0
- package/src/messages/variant.test.ts +444 -0
- package/src/messages/variant.ts +242 -0
- package/src/openInlangProject.test.ts +734 -0
- package/src/openInlangProject.ts +337 -0
- package/src/parseConfig.ts +33 -0
- package/src/reactivity/map.ts +135 -0
- package/src/reactivity/solid.ts +36 -0
- package/src/reactivity/trigger.ts +46 -0
- package/src/resolve-modules/errors.ts +39 -0
- package/src/resolve-modules/import.test.ts +58 -0
- package/src/resolve-modules/import.ts +69 -0
- package/src/resolve-modules/index.ts +2 -0
- package/src/resolve-modules/message-lint-rules/errors.ts +9 -0
- package/src/resolve-modules/message-lint-rules/resolveMessageLintRules.ts +24 -0
- package/src/resolve-modules/plugins/errors.ts +57 -0
- package/src/resolve-modules/plugins/resolvePlugins.test.ts +340 -0
- package/src/resolve-modules/plugins/resolvePlugins.ts +170 -0
- package/src/resolve-modules/plugins/types.test.ts +57 -0
- package/src/resolve-modules/plugins/types.ts +77 -0
- package/src/resolve-modules/resolveModules.test.ts +176 -0
- package/src/resolve-modules/resolveModules.ts +97 -0
- package/src/resolve-modules/types.ts +71 -0
- package/src/test-utilities/createMessage.test.ts +100 -0
- package/src/test-utilities/createMessage.ts +20 -0
- package/src/test-utilities/index.ts +1 -0
- package/src/versionedInterfaces.ts +9 -0
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { MessagedLintRuleThrowedError } from "./errors.js";
|
|
2
|
+
/**
|
|
3
|
+
* Lint a single message.
|
|
4
|
+
*
|
|
5
|
+
* - the lint rule levels defaults to `warning`.
|
|
6
|
+
*/
|
|
7
|
+
export const lintSingleMessage = async (args) => {
|
|
8
|
+
const reports = [];
|
|
9
|
+
const errors = [];
|
|
10
|
+
const promises = args.rules.map(async (rule) => {
|
|
11
|
+
const ruleId = rule.meta.id;
|
|
12
|
+
const settings = args.ruleSettings?.[ruleId] ?? {};
|
|
13
|
+
const level = args.ruleLevels?.[ruleId];
|
|
14
|
+
if (level === undefined) {
|
|
15
|
+
throw Error("No lint level provided for lint rule: " + ruleId);
|
|
16
|
+
}
|
|
17
|
+
try {
|
|
18
|
+
await rule.message({
|
|
19
|
+
...args,
|
|
20
|
+
settings,
|
|
21
|
+
report: (reportArgs) => {
|
|
22
|
+
reports.push({
|
|
23
|
+
ruleId,
|
|
24
|
+
level,
|
|
25
|
+
...reportArgs,
|
|
26
|
+
});
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
catch (error) {
|
|
31
|
+
errors.push(new MessagedLintRuleThrowedError(`Lint rule '${ruleId}' throwed while linting message "${args.message.id}".`, { cause: error }));
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
await Promise.all(promises);
|
|
35
|
+
return { data: reports, errors };
|
|
36
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lintSingleMessage.test.d.ts","sourceRoot":"","sources":["../../../src/lint/message/lintSingleMessage.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, test, vi } from "vitest";
|
|
2
|
+
import { lintSingleMessage } from "./lintSingleMessage.js";
|
|
3
|
+
import { tryCatch } from "@inlang/result";
|
|
4
|
+
const lintRule1 = {
|
|
5
|
+
meta: {
|
|
6
|
+
id: "messageLintRule.r.1",
|
|
7
|
+
displayName: { en: "" },
|
|
8
|
+
description: { en: "" },
|
|
9
|
+
},
|
|
10
|
+
message: vi.fn(),
|
|
11
|
+
};
|
|
12
|
+
const lintRule2 = {
|
|
13
|
+
meta: {
|
|
14
|
+
id: "messageLintRule.r.2",
|
|
15
|
+
displayName: { en: "" },
|
|
16
|
+
description: { en: "" },
|
|
17
|
+
},
|
|
18
|
+
message: vi.fn(),
|
|
19
|
+
};
|
|
20
|
+
const message1 = {};
|
|
21
|
+
const messages = [message1];
|
|
22
|
+
describe("lintSingleMessage", async () => {
|
|
23
|
+
beforeEach(() => {
|
|
24
|
+
vi.resetAllMocks();
|
|
25
|
+
});
|
|
26
|
+
describe("resolve rules and settings", async () => {
|
|
27
|
+
// the lint function is un-opinionated and does not set a default level.
|
|
28
|
+
// opinionated users like the inlang instance can very well set a default level (separation of concerns)
|
|
29
|
+
test("it should throw if a lint level is not provided for a given lint rule", async () => {
|
|
30
|
+
lintRule1.message.mockImplementation(({ report }) => report({}));
|
|
31
|
+
const result = await tryCatch(() => lintSingleMessage({
|
|
32
|
+
ruleLevels: {},
|
|
33
|
+
ruleSettings: {},
|
|
34
|
+
sourceLanguageTag: "en",
|
|
35
|
+
languageTags: ["en"],
|
|
36
|
+
messages,
|
|
37
|
+
message: message1,
|
|
38
|
+
rules: [lintRule1],
|
|
39
|
+
}));
|
|
40
|
+
expect(result.error).toBeDefined();
|
|
41
|
+
expect(result.data).toBeUndefined();
|
|
42
|
+
});
|
|
43
|
+
test("it should override the default lint level", async () => {
|
|
44
|
+
lintRule1.message.mockImplementation(({ report }) => report({}));
|
|
45
|
+
const reports = await lintSingleMessage({
|
|
46
|
+
ruleLevels: {
|
|
47
|
+
[lintRule1.meta.id]: "error",
|
|
48
|
+
},
|
|
49
|
+
ruleSettings: {},
|
|
50
|
+
sourceLanguageTag: "en",
|
|
51
|
+
languageTags: ["en"],
|
|
52
|
+
messages,
|
|
53
|
+
message: message1,
|
|
54
|
+
rules: [lintRule1],
|
|
55
|
+
});
|
|
56
|
+
expect(reports.data[0]?.level).toBe("error");
|
|
57
|
+
});
|
|
58
|
+
test("it should pass the correct settings", async () => {
|
|
59
|
+
const settings = {};
|
|
60
|
+
const fn = vi.fn();
|
|
61
|
+
lintRule1.message.mockImplementation(({ settings }) => fn(settings));
|
|
62
|
+
await lintSingleMessage({
|
|
63
|
+
ruleLevels: {
|
|
64
|
+
[lintRule1.meta.id]: "warning",
|
|
65
|
+
},
|
|
66
|
+
ruleSettings: {
|
|
67
|
+
[lintRule1.meta.id]: settings,
|
|
68
|
+
},
|
|
69
|
+
sourceLanguageTag: "en",
|
|
70
|
+
languageTags: ["en"],
|
|
71
|
+
messages,
|
|
72
|
+
message: message1,
|
|
73
|
+
rules: [lintRule1],
|
|
74
|
+
});
|
|
75
|
+
expect(fn).toHaveBeenCalledWith(settings);
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
test("it should await all rules", async () => {
|
|
79
|
+
let m1Called = false;
|
|
80
|
+
let m2Called = false;
|
|
81
|
+
lintRule1.message.mockImplementation(() => {
|
|
82
|
+
m1Called = true;
|
|
83
|
+
});
|
|
84
|
+
lintRule2.message.mockImplementation(async () => {
|
|
85
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
86
|
+
m2Called = true;
|
|
87
|
+
});
|
|
88
|
+
await lintSingleMessage({
|
|
89
|
+
ruleLevels: {
|
|
90
|
+
[lintRule1.meta.id]: "warning",
|
|
91
|
+
[lintRule2.meta.id]: "warning",
|
|
92
|
+
},
|
|
93
|
+
ruleSettings: {},
|
|
94
|
+
sourceLanguageTag: "en",
|
|
95
|
+
languageTags: ["en"],
|
|
96
|
+
messages,
|
|
97
|
+
message: message1,
|
|
98
|
+
rules: [lintRule1, lintRule2],
|
|
99
|
+
});
|
|
100
|
+
expect(m1Called).toBe(true);
|
|
101
|
+
expect(m2Called).toBe(true);
|
|
102
|
+
});
|
|
103
|
+
test("it should process all rules in parallel", async () => {
|
|
104
|
+
const fn = vi.fn();
|
|
105
|
+
lintRule1.message.mockImplementation(async () => {
|
|
106
|
+
fn(lintRule1.meta.id, "before");
|
|
107
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
108
|
+
fn(lintRule1.meta.id, "after");
|
|
109
|
+
});
|
|
110
|
+
lintRule2.message.mockImplementation(async () => {
|
|
111
|
+
fn(lintRule2.meta.id, "before");
|
|
112
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
113
|
+
fn(lintRule2.meta.id, "after");
|
|
114
|
+
});
|
|
115
|
+
await lintSingleMessage({
|
|
116
|
+
ruleLevels: {
|
|
117
|
+
[lintRule1.meta.id]: "warning",
|
|
118
|
+
[lintRule2.meta.id]: "warning",
|
|
119
|
+
},
|
|
120
|
+
ruleSettings: {},
|
|
121
|
+
sourceLanguageTag: "en",
|
|
122
|
+
languageTags: ["en"],
|
|
123
|
+
messages,
|
|
124
|
+
message: message1,
|
|
125
|
+
rules: [lintRule1, lintRule2],
|
|
126
|
+
});
|
|
127
|
+
expect(fn).toHaveBeenCalledTimes(4);
|
|
128
|
+
expect(fn).toHaveBeenNthCalledWith(1, lintRule1.meta.id, "before");
|
|
129
|
+
expect(fn).toHaveBeenNthCalledWith(2, lintRule2.meta.id, "before");
|
|
130
|
+
expect(fn).toHaveBeenNthCalledWith(3, lintRule1.meta.id, "after");
|
|
131
|
+
expect(fn).toHaveBeenNthCalledWith(4, lintRule2.meta.id, "after");
|
|
132
|
+
});
|
|
133
|
+
test("it should not abort the linting process when errors occur", async () => {
|
|
134
|
+
lintRule1.message.mockImplementation(() => {
|
|
135
|
+
throw new Error("error");
|
|
136
|
+
});
|
|
137
|
+
lintRule2.message.mockImplementation(({ report }) => {
|
|
138
|
+
report({});
|
|
139
|
+
});
|
|
140
|
+
const result = await lintSingleMessage({
|
|
141
|
+
ruleLevels: {
|
|
142
|
+
[lintRule1.meta.id]: "warning",
|
|
143
|
+
[lintRule2.meta.id]: "warning",
|
|
144
|
+
},
|
|
145
|
+
ruleSettings: {},
|
|
146
|
+
sourceLanguageTag: "en",
|
|
147
|
+
languageTags: ["en"],
|
|
148
|
+
messages,
|
|
149
|
+
message: message1,
|
|
150
|
+
rules: [lintRule1, lintRule2],
|
|
151
|
+
});
|
|
152
|
+
expect(result.data).length(1);
|
|
153
|
+
expect(result.errors).length(1);
|
|
154
|
+
});
|
|
155
|
+
});
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export declare class MessageVariantDoesNotExistError extends Error {
|
|
2
|
+
#private;
|
|
3
|
+
constructor(messageId: string, languageTag: string);
|
|
4
|
+
}
|
|
5
|
+
export declare class MessageVariantAlreadyExistsError extends Error {
|
|
6
|
+
#private;
|
|
7
|
+
constructor(messageId: string, languageTag: string);
|
|
8
|
+
}
|
|
9
|
+
export declare class MessagePatternsForLanguageTagDoNotExistError extends Error {
|
|
10
|
+
#private;
|
|
11
|
+
constructor(messageId: string, languageTag: string);
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/messages/errors.ts"],"names":[],"mappings":"AAAA,qBAAa,+BAAgC,SAAQ,KAAK;;gBAG7C,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM;CAKlD;AACD,qBAAa,gCAAiC,SAAQ,KAAK;;gBAG9C,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM;CAKlD;AACD,qBAAa,4CAA6C,SAAQ,KAAK;;gBAG1D,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM;CAGlD"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export class MessageVariantDoesNotExistError extends Error {
|
|
2
|
+
#id = "MessageVariantDoesNotExistError";
|
|
3
|
+
constructor(messageId, languageTag) {
|
|
4
|
+
super(`For message '${messageId}' and '${languageTag}', there doesn't exist a variant for this specific matchers.`);
|
|
5
|
+
}
|
|
6
|
+
}
|
|
7
|
+
export class MessageVariantAlreadyExistsError extends Error {
|
|
8
|
+
#id = "MessageVariantAlreadyExistsError";
|
|
9
|
+
constructor(messageId, languageTag) {
|
|
10
|
+
super(`For message '${messageId}' and '${languageTag}', there already exists a variant for this specific matchers.`);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
export class MessagePatternsForLanguageTagDoNotExistError extends Error {
|
|
14
|
+
#id = "MessagePatternsForLanguageTagDoNotExistError";
|
|
15
|
+
constructor(messageId, languageTag) {
|
|
16
|
+
super(`For message '${messageId}' there are no patterns with the languageTag '${languageTag}'.`);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/messages/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAA;AAC5B,cAAc,aAAa,CAAA"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { LanguageTag, Message, Variant } from "../versionedInterfaces.js";
|
|
2
|
+
import type { Result } from "@inlang/result";
|
|
3
|
+
import { MessagePatternsForLanguageTagDoNotExistError, MessageVariantAlreadyExistsError, MessageVariantDoesNotExistError } from "./errors.js";
|
|
4
|
+
/**
|
|
5
|
+
* Tries to match the most specific variant of a message.
|
|
6
|
+
*
|
|
7
|
+
* The selectors determine the specificity of a variant. If no selectors are provided,
|
|
8
|
+
* or if the selectors do not match any variant, the catch all variant is returned
|
|
9
|
+
* (if it exists).
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* const variant = getVariant(message, { where: { languageTag: "en", selectors: { gender: "male" }}});
|
|
13
|
+
*/
|
|
14
|
+
export declare function getVariant(message: Message, args: {
|
|
15
|
+
where: {
|
|
16
|
+
languageTag: LanguageTag;
|
|
17
|
+
selectors?: Variant["match"];
|
|
18
|
+
};
|
|
19
|
+
}): Variant | undefined;
|
|
20
|
+
/**
|
|
21
|
+
* Create a variant for a message
|
|
22
|
+
*
|
|
23
|
+
* All actions are immutable.
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* const message = createVariant(message, { languageTag: "en", data: variant })
|
|
27
|
+
*/
|
|
28
|
+
export declare function createVariant(message: Message, args: {
|
|
29
|
+
data: Variant;
|
|
30
|
+
}): Result<Message, MessageVariantAlreadyExistsError>;
|
|
31
|
+
/**
|
|
32
|
+
* Update a variant of a message
|
|
33
|
+
*
|
|
34
|
+
* All actions are immutable.
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* const message = updateVariant(message, { languageTag: "en", selectors: { gender: "male" }, pattern: []})
|
|
38
|
+
*/
|
|
39
|
+
export declare function updateVariantPattern(message: Message, args: {
|
|
40
|
+
where: {
|
|
41
|
+
languageTag: LanguageTag;
|
|
42
|
+
selectors: Record<string, string>;
|
|
43
|
+
};
|
|
44
|
+
data: Variant["pattern"];
|
|
45
|
+
}): Result<Message, MessageVariantDoesNotExistError | MessagePatternsForLanguageTagDoNotExistError>;
|
|
46
|
+
//# sourceMappingURL=variant.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"variant.d.ts","sourceRoot":"","sources":["../../src/messages/variant.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,2BAA2B,CAAA;AAC9E,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAA;AAC5C,OAAO,EACN,4CAA4C,EAC5C,gCAAgC,EAChC,+BAA+B,EAC/B,MAAM,aAAa,CAAA;AAEpB;;;;;;;;;GASG;AACH,wBAAgB,UAAU,CACzB,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE;IACL,KAAK,EAAE;QACN,WAAW,EAAE,WAAW,CAAA;QACxB,SAAS,CAAC,EAAE,OAAO,CAAC,OAAO,CAAC,CAAA;KAC5B,CAAA;CACD,GACC,OAAO,GAAG,SAAS,CASrB;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAC5B,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE;IACL,IAAI,EAAE,OAAO,CAAA;CACb,GACC,MAAM,CAAC,OAAO,EAAE,gCAAgC,CAAC,CAcnD;AAED;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CACnC,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE;IACL,KAAK,EAAE;QACN,WAAW,EAAE,WAAW,CAAA;QACxB,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;KACjC,CAAA;IACD,IAAI,EAAE,OAAO,CAAC,SAAS,CAAC,CAAA;CACxB,GACC,MAAM,CAAC,OAAO,EAAE,+BAA+B,GAAG,4CAA4C,CAAC,CAqBjG"}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import { MessagePatternsForLanguageTagDoNotExistError, MessageVariantAlreadyExistsError, MessageVariantDoesNotExistError, } from "./errors.js";
|
|
2
|
+
/**
|
|
3
|
+
* Tries to match the most specific variant of a message.
|
|
4
|
+
*
|
|
5
|
+
* The selectors determine the specificity of a variant. If no selectors are provided,
|
|
6
|
+
* or if the selectors do not match any variant, the catch all variant is returned
|
|
7
|
+
* (if it exists).
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* const variant = getVariant(message, { where: { languageTag: "en", selectors: { gender: "male" }}});
|
|
11
|
+
*/
|
|
12
|
+
export function getVariant(message, args) {
|
|
13
|
+
const variant = matchMostSpecificVariant(message, args.where.languageTag, args.where.selectors);
|
|
14
|
+
if (variant) {
|
|
15
|
+
//! do not return a reference to the message in a resource
|
|
16
|
+
//! modifications to the returned message will leak into the
|
|
17
|
+
//! resource which is considered to be immutable.
|
|
18
|
+
return structuredClone(variant);
|
|
19
|
+
}
|
|
20
|
+
return undefined;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Create a variant for a message
|
|
24
|
+
*
|
|
25
|
+
* All actions are immutable.
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* const message = createVariant(message, { languageTag: "en", data: variant })
|
|
29
|
+
*/
|
|
30
|
+
export function createVariant(message, args) {
|
|
31
|
+
const copy = structuredClone(message);
|
|
32
|
+
// check if variant already exists
|
|
33
|
+
if (matchVariant(copy, args.data.languageTag, args.data.match)) {
|
|
34
|
+
return { error: new MessageVariantAlreadyExistsError(message.id, args.data.languageTag) };
|
|
35
|
+
}
|
|
36
|
+
// need to resolve selectors to match length and order of message selectors
|
|
37
|
+
copy.variants.push({
|
|
38
|
+
...args.data,
|
|
39
|
+
match: resolveSelector(copy.selectors, args.data.match),
|
|
40
|
+
});
|
|
41
|
+
return { data: copy };
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Update a variant of a message
|
|
45
|
+
*
|
|
46
|
+
* All actions are immutable.
|
|
47
|
+
*
|
|
48
|
+
* @example
|
|
49
|
+
* const message = updateVariant(message, { languageTag: "en", selectors: { gender: "male" }, pattern: []})
|
|
50
|
+
*/
|
|
51
|
+
export function updateVariantPattern(message, args) {
|
|
52
|
+
const copy = structuredClone(message);
|
|
53
|
+
const containsLanguageTag = message.variants.some((variant) => variant.languageTag === args.where.languageTag);
|
|
54
|
+
if (!containsLanguageTag) {
|
|
55
|
+
return {
|
|
56
|
+
error: new MessagePatternsForLanguageTagDoNotExistError(message.id, args.where.languageTag),
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
const variant = matchVariant(copy, args.where.languageTag, args.where.selectors);
|
|
60
|
+
if (variant === undefined) {
|
|
61
|
+
return { error: new MessageVariantDoesNotExistError(message.id, args.where.languageTag) };
|
|
62
|
+
}
|
|
63
|
+
if (variant) {
|
|
64
|
+
variant.pattern = args.data;
|
|
65
|
+
return { data: copy };
|
|
66
|
+
}
|
|
67
|
+
return { error: new MessageVariantDoesNotExistError(message.id, args.where.languageTag) };
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Returns the specific variant defined by selectors or undefined
|
|
71
|
+
*
|
|
72
|
+
* @example
|
|
73
|
+
* const variant = matchVariant(message, languageTag: "en", selectors: { gender: "male" })
|
|
74
|
+
*/
|
|
75
|
+
const matchVariant = (message, languageTag, selectors) => {
|
|
76
|
+
// resolve preferenceSelectors to match length and order of message selectors
|
|
77
|
+
const resolvedSelectors = resolveSelector(message.selectors, selectors);
|
|
78
|
+
const languageVariants = message.variants.filter((variant) => variant.languageTag === languageTag);
|
|
79
|
+
if (languageVariants.length === 0)
|
|
80
|
+
return undefined;
|
|
81
|
+
for (const variant of languageVariants) {
|
|
82
|
+
let isMatch = true;
|
|
83
|
+
//check if vaiant is a match
|
|
84
|
+
for (const [key, value] of Object.entries(variant.match)) {
|
|
85
|
+
if (resolvedSelectors[key] !== value) {
|
|
86
|
+
isMatch = false;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
if (isMatch) {
|
|
90
|
+
return variant;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return undefined;
|
|
94
|
+
};
|
|
95
|
+
/**
|
|
96
|
+
* Returns the most specific variant of a message.
|
|
97
|
+
*
|
|
98
|
+
* @example
|
|
99
|
+
* const variant = matchMostSpecificVariant(message, languageTag: "en", selectors: { gender: "male" })
|
|
100
|
+
*/
|
|
101
|
+
const matchMostSpecificVariant = (message, languageTag, selectors) => {
|
|
102
|
+
// make selector undefined if empty object
|
|
103
|
+
selectors = JSON.stringify(selectors) === "{}" ? undefined : selectors;
|
|
104
|
+
// resolve preferenceSelectors to match length and order of message selectors
|
|
105
|
+
const resolvedSelectors = resolveSelector(message.selectors, selectors);
|
|
106
|
+
const index = {};
|
|
107
|
+
const languageVariants = message.variants.filter((variant) => variant.languageTag === languageTag);
|
|
108
|
+
if (languageVariants.length === 0)
|
|
109
|
+
return undefined;
|
|
110
|
+
for (const variant of languageVariants) {
|
|
111
|
+
let isMatch = true;
|
|
112
|
+
//check if variant is a match
|
|
113
|
+
for (const [key, value] of Object.entries(variant.match)) {
|
|
114
|
+
if (resolvedSelectors[key] !== value && value !== "*") {
|
|
115
|
+
isMatch = false;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
if (isMatch && selectors) {
|
|
119
|
+
// add variant to nested index
|
|
120
|
+
// eslint-disable-next-line no-inner-declarations
|
|
121
|
+
function recursiveAddToIndex(currentIndex, currentKeys, variant) {
|
|
122
|
+
if (currentKeys[0]?.name) {
|
|
123
|
+
const key = variant.match[currentKeys[0].name];
|
|
124
|
+
if (key) {
|
|
125
|
+
if (currentKeys.length === 1) {
|
|
126
|
+
currentIndex[key] = variant;
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
if (!currentIndex[key]) {
|
|
130
|
+
currentIndex[key] = {};
|
|
131
|
+
}
|
|
132
|
+
recursiveAddToIndex(currentIndex[key], currentKeys.slice(1), variant);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
recursiveAddToIndex(index, message.selectors, variant);
|
|
138
|
+
}
|
|
139
|
+
else if (isMatch && !selectors) {
|
|
140
|
+
return variant;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
//find the most specific variant
|
|
144
|
+
const findOptimalMatch = (index, selectors) => {
|
|
145
|
+
const keys = Object.keys(index);
|
|
146
|
+
for (const key of keys) {
|
|
147
|
+
if (key === selectors[0] || key === "*") {
|
|
148
|
+
const nextOptimal = selectors.slice(1);
|
|
149
|
+
if (nextOptimal.length === 0) {
|
|
150
|
+
return index[key] || undefined;
|
|
151
|
+
}
|
|
152
|
+
const match = findOptimalMatch(index[key], nextOptimal);
|
|
153
|
+
if (match !== undefined) {
|
|
154
|
+
return match;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return undefined;
|
|
159
|
+
};
|
|
160
|
+
return findOptimalMatch(index, Object.values(resolvedSelectors));
|
|
161
|
+
};
|
|
162
|
+
/**
|
|
163
|
+
* Returns resolved selector.
|
|
164
|
+
* -> Adds all possible selectors, if not defined it adds '*'. Order is determined by messageSelectors
|
|
165
|
+
*
|
|
166
|
+
* @example
|
|
167
|
+
* const variant = resolveSelector(["gender","count"], selector: {count: "2"})
|
|
168
|
+
*/
|
|
169
|
+
const resolveSelector = (messageSelectors, selectors) => {
|
|
170
|
+
const resolvedSelectors = {};
|
|
171
|
+
if (!selectors)
|
|
172
|
+
return {};
|
|
173
|
+
for (const messageSelector of messageSelectors) {
|
|
174
|
+
resolvedSelectors[messageSelector.name] = selectors[messageSelector.name] ?? "*";
|
|
175
|
+
}
|
|
176
|
+
return resolvedSelectors;
|
|
177
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"variant.test.d.ts","sourceRoot":"","sources":["../../src/messages/variant.test.ts"],"names":[],"mappings":""}
|