@inlang/sdk 0.26.5 → 0.28.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/dist/adapter/solidAdapter.test.js +52 -4
- package/dist/api.d.ts +5 -0
- package/dist/api.d.ts.map +1 -1
- package/dist/createMessageLintReportsQuery.d.ts +1 -1
- package/dist/createMessageLintReportsQuery.d.ts.map +1 -1
- package/dist/createMessageLintReportsQuery.js +30 -22
- package/dist/createMessagesQuery.d.ts.map +1 -1
- package/dist/createMessagesQuery.js +24 -1
- package/dist/createMessagesQuery.test.js +46 -2
- package/dist/createNodeishFsWithAbsolutePaths.d.ts +3 -3
- package/dist/createNodeishFsWithAbsolutePaths.d.ts.map +1 -1
- package/dist/createNodeishFsWithAbsolutePaths.js +9 -1
- package/dist/createNodeishFsWithAbsolutePaths.test.js +7 -1
- package/dist/createNodeishFsWithWatcher.d.ts +3 -3
- package/dist/createNodeishFsWithWatcher.d.ts.map +1 -1
- package/dist/createNodeishFsWithWatcher.js +3 -0
- package/dist/errors.d.ts +14 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +12 -0
- package/dist/loadProject.d.ts.map +1 -1
- package/dist/loadProject.js +474 -39
- package/dist/loadProject.test.js +93 -14
- package/dist/messages/variant.test.js +3 -0
- package/dist/resolve-modules/plugins/resolvePlugins.test.js +4 -2
- package/dist/storage/helper.d.ts +5 -0
- package/dist/storage/helper.d.ts.map +1 -0
- package/dist/storage/helper.js +35 -0
- package/dist/storage/human-id/human-readable-id.d.ts +3 -0
- package/dist/storage/human-id/human-readable-id.d.ts.map +1 -0
- package/dist/storage/human-id/human-readable-id.js +20 -0
- package/dist/storage/human-id/words.d.ts +5 -0
- package/dist/storage/human-id/words.d.ts.map +1 -0
- package/dist/storage/human-id/words.js +1032 -0
- package/dist/storage/human-id/words.test.d.ts +2 -0
- package/dist/storage/human-id/words.test.d.ts.map +1 -0
- package/dist/storage/human-id/words.test.js +25 -0
- package/dist/test-utilities/createMessage.d.ts +1 -0
- package/dist/test-utilities/createMessage.d.ts.map +1 -1
- package/dist/test-utilities/createMessage.js +1 -0
- package/dist/test-utilities/createMessage.test.js +70 -55
- package/package.json +16 -7
- package/src/adapter/solidAdapter.test.ts +76 -4
- package/src/api.ts +6 -0
- package/src/createMessageLintReportsQuery.ts +47 -35
- package/src/createMessagesQuery.test.ts +54 -2
- package/src/createMessagesQuery.ts +30 -1
- package/src/createNodeishFsWithAbsolutePaths.test.ts +10 -3
- package/src/createNodeishFsWithAbsolutePaths.ts +14 -5
- package/src/createNodeishFsWithWatcher.ts +6 -3
- package/src/errors.ts +20 -0
- package/src/loadProject.test.ts +108 -14
- package/src/loadProject.ts +657 -60
- package/src/messages/variant.test.ts +3 -0
- package/src/resolve-modules/plugins/resolvePlugins.test.ts +4 -2
- package/src/storage/helper.ts +48 -0
- package/src/storage/human-id/human-readable-id.ts +27 -0
- package/src/storage/human-id/words.test.ts +27 -0
- package/src/storage/human-id/words.ts +1035 -0
- package/src/test-utilities/createMessage.test.ts +72 -54
- package/src/test-utilities/createMessage.ts +1 -0
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { it, expect, vi } from "vitest"
|
|
2
2
|
import { createNodeishFsWithAbsolutePaths } from "./createNodeishFsWithAbsolutePaths.js"
|
|
3
|
-
import type { NodeishFilesystemSubset } from "./versionedInterfaces.js"
|
|
3
|
+
// import type { NodeishFilesystemSubset } from "./versionedInterfaces.js"
|
|
4
|
+
import type { NodeishFilesystem } from "@lix-js/fs"
|
|
4
5
|
|
|
5
6
|
it("throws an error if projectPath is not an absolute path", () => {
|
|
6
7
|
const relativePath = "relative/path"
|
|
@@ -27,9 +28,16 @@ it("intercepts paths correctly for readFile", async () => {
|
|
|
27
28
|
readFile: vi.fn(),
|
|
28
29
|
readdir: vi.fn(),
|
|
29
30
|
mkdir: vi.fn(),
|
|
31
|
+
rmdir: vi.fn(),
|
|
30
32
|
writeFile: vi.fn(),
|
|
31
33
|
watch: vi.fn(),
|
|
32
|
-
|
|
34
|
+
rm: vi.fn(),
|
|
35
|
+
stat: vi.fn(),
|
|
36
|
+
lstat: vi.fn(),
|
|
37
|
+
symlink: vi.fn(),
|
|
38
|
+
unlink: vi.fn(),
|
|
39
|
+
readlink: vi.fn(),
|
|
40
|
+
} satisfies Record<keyof NodeishFilesystem, any>
|
|
33
41
|
|
|
34
42
|
const interceptedFs = createNodeishFsWithAbsolutePaths({
|
|
35
43
|
projectPath,
|
|
@@ -38,7 +46,6 @@ it("intercepts paths correctly for readFile", async () => {
|
|
|
38
46
|
|
|
39
47
|
for (const [path, expectedPath] of filePaths) {
|
|
40
48
|
for (const fn of Object.keys(mockNodeishFs)) {
|
|
41
|
-
// @ts-expect-error
|
|
42
49
|
await interceptedFs[fn](path)
|
|
43
50
|
// @ts-expect-error
|
|
44
51
|
// expect the first argument to be the expectedPath
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import type
|
|
2
|
-
import { normalizePath } from "@lix-js/fs"
|
|
1
|
+
import { normalizePath, type NodeishFilesystem } from "@lix-js/fs"
|
|
3
2
|
import { isAbsolutePath } from "./isAbsolutePath.js"
|
|
4
3
|
|
|
5
4
|
/**
|
|
@@ -10,8 +9,8 @@ import { isAbsolutePath } from "./isAbsolutePath.js"
|
|
|
10
9
|
*/
|
|
11
10
|
export const createNodeishFsWithAbsolutePaths = (args: {
|
|
12
11
|
projectPath: string
|
|
13
|
-
nodeishFs:
|
|
14
|
-
}):
|
|
12
|
+
nodeishFs: NodeishFilesystem
|
|
13
|
+
}): NodeishFilesystem => {
|
|
15
14
|
if (!isAbsolutePath(args.projectPath)) {
|
|
16
15
|
throw new Error(`Expected an absolute path but received "${args.projectPath}".`)
|
|
17
16
|
}
|
|
@@ -33,11 +32,21 @@ export const createNodeishFsWithAbsolutePaths = (args: {
|
|
|
33
32
|
readFile: (path: string, options: { encoding: "utf-8" | "binary" }) =>
|
|
34
33
|
args.nodeishFs.readFile(makeAbsolute(path), options),
|
|
35
34
|
readdir: (path: string) => args.nodeishFs.readdir(makeAbsolute(path)),
|
|
36
|
-
mkdir: (path: string) =>
|
|
35
|
+
mkdir: (path: string, options: { recursive: boolean }) =>
|
|
36
|
+
args.nodeishFs.mkdir(makeAbsolute(path), options),
|
|
37
37
|
writeFile: (path: string, data: string) => args.nodeishFs.writeFile(makeAbsolute(path), data),
|
|
38
|
+
stat: (path: string) => args.nodeishFs.stat(makeAbsolute(path)),
|
|
39
|
+
rm: (path: string) => args.nodeishFs.rm(makeAbsolute(path)),
|
|
40
|
+
rmdir: (path: string) => (args.nodeishFs as any).rmdir(makeAbsolute(path)),
|
|
38
41
|
watch: (
|
|
39
42
|
path: string,
|
|
40
43
|
options: { signal: AbortSignal | undefined; recursive: boolean | undefined }
|
|
41
44
|
) => args.nodeishFs.watch(makeAbsolute(path), options),
|
|
45
|
+
// This might be surprising when symlinks were intended to be relative
|
|
46
|
+
symlink: (target: string, path: string) =>
|
|
47
|
+
args.nodeishFs.symlink(makeAbsolute(target), makeAbsolute(path)),
|
|
48
|
+
unlink: (path: string) => args.nodeishFs.unlink(makeAbsolute(path)),
|
|
49
|
+
readlink: (path: string) => args.nodeishFs.readlink(makeAbsolute(path)),
|
|
50
|
+
lstat: (path: string) => args.nodeishFs.lstat(makeAbsolute(path)),
|
|
42
51
|
}
|
|
43
52
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { NodeishFilesystem } from "@lix-js/fs"
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Wraps the nodeish filesystem subset with a function that intercepts paths
|
|
@@ -7,9 +7,9 @@ import type { NodeishFilesystemSubset } from "@inlang/plugin"
|
|
|
7
7
|
* The paths are resolved from the `projectPath` argument.
|
|
8
8
|
*/
|
|
9
9
|
export const createNodeishFsWithWatcher = (args: {
|
|
10
|
-
nodeishFs:
|
|
10
|
+
nodeishFs: NodeishFilesystem
|
|
11
11
|
updateMessages: () => void
|
|
12
|
-
}):
|
|
12
|
+
}): NodeishFilesystem => {
|
|
13
13
|
const pathList: string[] = []
|
|
14
14
|
|
|
15
15
|
const makeWatcher = (path: string) => {
|
|
@@ -50,9 +50,12 @@ export const createNodeishFsWithWatcher = (args: {
|
|
|
50
50
|
// @ts-expect-error
|
|
51
51
|
readFile: (path: string, options: { encoding: "utf-8" | "binary" }) =>
|
|
52
52
|
readFileAndExtractPath(path, options),
|
|
53
|
+
rm: args.nodeishFs.rm,
|
|
53
54
|
readdir: args.nodeishFs.readdir,
|
|
54
55
|
mkdir: args.nodeishFs.mkdir,
|
|
56
|
+
rmdir: (args.nodeishFs as any).rmdir,
|
|
55
57
|
writeFile: args.nodeishFs.writeFile,
|
|
56
58
|
watch: args.nodeishFs.watch,
|
|
59
|
+
stat: args.nodeishFs.stat,
|
|
57
60
|
}
|
|
58
61
|
}
|
package/src/errors.ts
CHANGED
|
@@ -50,3 +50,23 @@ export class PluginLoadMessagesError extends Error {
|
|
|
50
50
|
this.name = "PluginLoadMessagesError"
|
|
51
51
|
}
|
|
52
52
|
}
|
|
53
|
+
|
|
54
|
+
export class LoadMessageError extends Error {
|
|
55
|
+
constructor(options: { path: string; messageId: string; cause: ErrorOptions["cause"] }) {
|
|
56
|
+
super(
|
|
57
|
+
`An error occured when loading message ${options.messageId} from path ${options.path} caused by ${options.cause}.`,
|
|
58
|
+
options
|
|
59
|
+
)
|
|
60
|
+
this.name = "LoadMessageError"
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export class SaveMessageError extends Error {
|
|
65
|
+
constructor(options: { path: string; messageId: string; cause: ErrorOptions["cause"] }) {
|
|
66
|
+
super(
|
|
67
|
+
`An error occured when loading message ${options.messageId} from path ${options.path} caused by ${options.cause}.`,
|
|
68
|
+
options
|
|
69
|
+
)
|
|
70
|
+
this.name = "SaveMessageError"
|
|
71
|
+
}
|
|
72
|
+
}
|
package/src/loadProject.test.ts
CHANGED
|
@@ -65,6 +65,7 @@ const mockPlugin: Plugin = {
|
|
|
65
65
|
const exampleMessages: Message[] = [
|
|
66
66
|
{
|
|
67
67
|
id: "a",
|
|
68
|
+
alias: {},
|
|
68
69
|
selectors: [],
|
|
69
70
|
variants: [
|
|
70
71
|
{
|
|
@@ -81,6 +82,48 @@ const exampleMessages: Message[] = [
|
|
|
81
82
|
},
|
|
82
83
|
{
|
|
83
84
|
id: "b",
|
|
85
|
+
alias: {},
|
|
86
|
+
selectors: [],
|
|
87
|
+
variants: [
|
|
88
|
+
{
|
|
89
|
+
languageTag: "en",
|
|
90
|
+
match: [],
|
|
91
|
+
pattern: [
|
|
92
|
+
{
|
|
93
|
+
type: "Text",
|
|
94
|
+
value: "test",
|
|
95
|
+
},
|
|
96
|
+
],
|
|
97
|
+
},
|
|
98
|
+
],
|
|
99
|
+
},
|
|
100
|
+
]
|
|
101
|
+
|
|
102
|
+
const exampleAliasedMessages: Message[] = [
|
|
103
|
+
{
|
|
104
|
+
id: "raw_tapir_pause_grateful",
|
|
105
|
+
alias: {
|
|
106
|
+
default: "a",
|
|
107
|
+
},
|
|
108
|
+
selectors: [],
|
|
109
|
+
variants: [
|
|
110
|
+
{
|
|
111
|
+
languageTag: "en",
|
|
112
|
+
match: [],
|
|
113
|
+
pattern: [
|
|
114
|
+
{
|
|
115
|
+
type: "Text",
|
|
116
|
+
value: "test",
|
|
117
|
+
},
|
|
118
|
+
],
|
|
119
|
+
},
|
|
120
|
+
],
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
id: "dizzy_halibut_dial_vaguely",
|
|
124
|
+
alias: {
|
|
125
|
+
default: "b",
|
|
126
|
+
},
|
|
84
127
|
selectors: [],
|
|
85
128
|
variants: [
|
|
86
129
|
{
|
|
@@ -509,6 +552,7 @@ describe("functionality", () => {
|
|
|
509
552
|
description: mockPlugin.description,
|
|
510
553
|
displayName: mockPlugin.displayName,
|
|
511
554
|
module: settings.modules[0],
|
|
555
|
+
settingsSchema: mockPlugin.settingsSchema,
|
|
512
556
|
})
|
|
513
557
|
|
|
514
558
|
expect(project.installed.messageLintRules()[0]).toEqual({
|
|
@@ -517,6 +561,7 @@ describe("functionality", () => {
|
|
|
517
561
|
displayName: mockMessageLintRule.displayName,
|
|
518
562
|
module: settings.modules[1],
|
|
519
563
|
level: "warning",
|
|
564
|
+
settingsSchema: mockMessageLintRule.settingsSchema,
|
|
520
565
|
})
|
|
521
566
|
})
|
|
522
567
|
|
|
@@ -561,7 +606,7 @@ describe("functionality", () => {
|
|
|
561
606
|
description: { en: "Mock plugin description" },
|
|
562
607
|
displayName: { en: "Mock Plugin" },
|
|
563
608
|
|
|
564
|
-
loadMessages: () => [{ id: "some-message", selectors: [], variants: [] }],
|
|
609
|
+
loadMessages: () => [{ id: "some-message", alias: {}, selectors: [], variants: [] }],
|
|
565
610
|
saveMessages: () => undefined,
|
|
566
611
|
}
|
|
567
612
|
const repo = await mockRepo()
|
|
@@ -613,7 +658,7 @@ describe("functionality", () => {
|
|
|
613
658
|
description: { en: "Mock plugin description" },
|
|
614
659
|
displayName: { en: "Mock Plugin" },
|
|
615
660
|
|
|
616
|
-
loadMessages: () => [{ id: "some-message", selectors: [], variants: [] }],
|
|
661
|
+
loadMessages: () => [{ id: "some-message", alias: {}, selectors: [], variants: [] }],
|
|
617
662
|
saveMessages: () => undefined,
|
|
618
663
|
}
|
|
619
664
|
const repo = await mockRepo()
|
|
@@ -698,6 +743,25 @@ describe("functionality", () => {
|
|
|
698
743
|
})
|
|
699
744
|
})
|
|
700
745
|
|
|
746
|
+
describe("messages with aliases", () => {
|
|
747
|
+
it("should return the messages", async () => {
|
|
748
|
+
const repo = await mockRepo()
|
|
749
|
+
const fs = repo.nodeishFs
|
|
750
|
+
await fs.mkdir("/user/project.inlang", { recursive: true })
|
|
751
|
+
await fs.writeFile(
|
|
752
|
+
"/user/project.inlang/settings.json",
|
|
753
|
+
JSON.stringify({ ...settings, experimental: { aliases: true } })
|
|
754
|
+
)
|
|
755
|
+
const project = await loadProject({
|
|
756
|
+
projectPath: "/user/project.inlang",
|
|
757
|
+
repo,
|
|
758
|
+
_import,
|
|
759
|
+
})
|
|
760
|
+
|
|
761
|
+
expect(Object.values(project.query.messages.getAll())).toEqual(exampleAliasedMessages)
|
|
762
|
+
})
|
|
763
|
+
})
|
|
764
|
+
|
|
701
765
|
describe("query", () => {
|
|
702
766
|
it("should call saveMessages() on updates", async () => {
|
|
703
767
|
const repo = await mockRepo()
|
|
@@ -744,6 +808,7 @@ describe("functionality", () => {
|
|
|
744
808
|
where: { id: "a" },
|
|
745
809
|
data: {
|
|
746
810
|
id: "a",
|
|
811
|
+
alias: {},
|
|
747
812
|
selectors: [],
|
|
748
813
|
variants: [
|
|
749
814
|
{
|
|
@@ -774,6 +839,7 @@ describe("functionality", () => {
|
|
|
774
839
|
where: { id: "b" },
|
|
775
840
|
data: {
|
|
776
841
|
id: "b",
|
|
842
|
+
alias: {},
|
|
777
843
|
selectors: [],
|
|
778
844
|
variants: [
|
|
779
845
|
{
|
|
@@ -800,7 +866,8 @@ describe("functionality", () => {
|
|
|
800
866
|
},
|
|
801
867
|
})
|
|
802
868
|
|
|
803
|
-
|
|
869
|
+
// lets wait for the next tick
|
|
870
|
+
await new Promise((resolve) => setTimeout(resolve, 100))
|
|
804
871
|
|
|
805
872
|
expect(mockSaveFn.mock.calls.length).toBe(1)
|
|
806
873
|
|
|
@@ -809,25 +876,26 @@ describe("functionality", () => {
|
|
|
809
876
|
expect(Object.values(mockSaveFn.mock.calls[0][0].messages)).toStrictEqual([
|
|
810
877
|
{
|
|
811
878
|
id: "a",
|
|
879
|
+
alias: {},
|
|
812
880
|
selectors: [],
|
|
813
881
|
variants: [
|
|
814
882
|
{
|
|
815
|
-
languageTag: "
|
|
883
|
+
languageTag: "de",
|
|
816
884
|
match: [],
|
|
817
885
|
pattern: [
|
|
818
886
|
{
|
|
819
887
|
type: "Text",
|
|
820
|
-
value: "a
|
|
888
|
+
value: "a de",
|
|
821
889
|
},
|
|
822
890
|
],
|
|
823
891
|
},
|
|
824
892
|
{
|
|
825
|
-
languageTag: "
|
|
893
|
+
languageTag: "en",
|
|
826
894
|
match: [],
|
|
827
895
|
pattern: [
|
|
828
896
|
{
|
|
829
897
|
type: "Text",
|
|
830
|
-
value: "a
|
|
898
|
+
value: "a en",
|
|
831
899
|
},
|
|
832
900
|
],
|
|
833
901
|
},
|
|
@@ -835,25 +903,26 @@ describe("functionality", () => {
|
|
|
835
903
|
},
|
|
836
904
|
{
|
|
837
905
|
id: "b",
|
|
906
|
+
alias: {},
|
|
838
907
|
selectors: [],
|
|
839
908
|
variants: [
|
|
840
909
|
{
|
|
841
|
-
languageTag: "
|
|
910
|
+
languageTag: "de",
|
|
842
911
|
match: [],
|
|
843
912
|
pattern: [
|
|
844
913
|
{
|
|
845
914
|
type: "Text",
|
|
846
|
-
value: "b
|
|
915
|
+
value: "b de",
|
|
847
916
|
},
|
|
848
917
|
],
|
|
849
918
|
},
|
|
850
919
|
{
|
|
851
|
-
languageTag: "
|
|
920
|
+
languageTag: "en",
|
|
852
921
|
match: [],
|
|
853
922
|
pattern: [
|
|
854
923
|
{
|
|
855
924
|
type: "Text",
|
|
856
|
-
value: "b
|
|
925
|
+
value: "b en",
|
|
857
926
|
},
|
|
858
927
|
],
|
|
859
928
|
},
|
|
@@ -926,6 +995,20 @@ describe("functionality", () => {
|
|
|
926
995
|
|
|
927
996
|
expect(mockSaveFn.mock.calls.length).toBe(1)
|
|
928
997
|
expect(mockSaveFn.mock.calls[0][0].messages).toHaveLength(4)
|
|
998
|
+
|
|
999
|
+
project.query.messages.create({ data: createMessage("fifth", { en: "fifth message" }) })
|
|
1000
|
+
|
|
1001
|
+
await new Promise((resolve) => setTimeout(resolve, 510))
|
|
1002
|
+
|
|
1003
|
+
expect(mockSaveFn.mock.calls.length).toBe(2)
|
|
1004
|
+
expect(mockSaveFn.mock.calls[1][0].messages).toHaveLength(5)
|
|
1005
|
+
|
|
1006
|
+
project.query.messages.delete({ where: { id: "fourth" } })
|
|
1007
|
+
|
|
1008
|
+
await new Promise((resolve) => setTimeout(resolve, 510))
|
|
1009
|
+
|
|
1010
|
+
expect(mockSaveFn.mock.calls.length).toBe(3)
|
|
1011
|
+
expect(mockSaveFn.mock.calls[2][0].messages).toHaveLength(4)
|
|
929
1012
|
})
|
|
930
1013
|
})
|
|
931
1014
|
|
|
@@ -1040,17 +1123,28 @@ describe("functionality", () => {
|
|
|
1040
1123
|
counter = counter + 1
|
|
1041
1124
|
})
|
|
1042
1125
|
|
|
1126
|
+
// subscribe fires once
|
|
1043
1127
|
expect(counter).toBe(1)
|
|
1044
1128
|
|
|
1045
|
-
//
|
|
1129
|
+
// saving the file without changing should not trigger a message query
|
|
1046
1130
|
await fs.writeFile("./messages.json", JSON.stringify(messages))
|
|
1047
|
-
await new Promise((resolve) => setTimeout(resolve,
|
|
1131
|
+
await new Promise((resolve) => setTimeout(resolve, 200)) // file event will lock a file and be handled sequentially - give it time to pickup the change
|
|
1132
|
+
|
|
1133
|
+
// we didn't change the message we write into message.json - shouldn't change the messages
|
|
1134
|
+
expect(counter).toBe(1)
|
|
1135
|
+
|
|
1136
|
+
// saving the file without changing should trigger a change
|
|
1137
|
+
messages.data[0]!.variants[0]!.pattern[0]!.value = "changed"
|
|
1138
|
+
await fs.writeFile("./messages.json", JSON.stringify(messages))
|
|
1139
|
+
await new Promise((resolve) => setTimeout(resolve, 200)) // file event will lock a file and be handled sequentially - give it time to pickup the change
|
|
1048
1140
|
|
|
1049
1141
|
expect(counter).toBe(2)
|
|
1050
1142
|
|
|
1143
|
+
messages.data[0]!.variants[0]!.pattern[0]!.value = "changed3"
|
|
1144
|
+
|
|
1051
1145
|
// change file
|
|
1052
1146
|
await fs.writeFile("./messages.json", JSON.stringify(messages))
|
|
1053
|
-
await new Promise((resolve) => setTimeout(resolve,
|
|
1147
|
+
await new Promise((resolve) => setTimeout(resolve, 200)) // file event will lock a file and be handled sequentially - give it time to pickup the change
|
|
1054
1148
|
|
|
1055
1149
|
expect(counter).toBe(3)
|
|
1056
1150
|
})
|