sonamu 0.8.13 → 0.8.15
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/api/sonamu.d.ts.map +1 -1
- package/dist/api/sonamu.js +2 -3
- package/dist/auth/auth-generator.d.ts +8 -0
- package/dist/auth/auth-generator.d.ts.map +1 -1
- package/dist/auth/auth-generator.js +33 -1
- package/dist/auth/better-auth-entities.d.ts.map +1 -1
- package/dist/auth/better-auth-entities.js +12 -2
- package/dist/bin/cli.js +18 -3
- package/dist/cone/cone-generator.js +10 -4
- package/dist/database/knex.d.ts.map +1 -1
- package/dist/database/knex.js +18 -2
- package/dist/database/puri.d.ts +9 -1
- package/dist/database/puri.d.ts.map +1 -1
- package/dist/database/puri.js +42 -1
- package/dist/database/puri.types.d.ts +2 -0
- package/dist/database/puri.types.d.ts.map +1 -1
- package/dist/database/puri.types.js +6 -2
- package/dist/entity/entity-manager.d.ts +149 -1
- package/dist/entity/entity-manager.d.ts.map +1 -1
- package/dist/entity/entity-manager.js +68 -4
- package/dist/migration/__tests__/code-generation.search-text.test.js +435 -0
- package/dist/migration/code-generation.d.ts.map +1 -1
- package/dist/migration/code-generation.js +696 -32
- package/dist/migration/migration-set.js +3 -1
- package/dist/migration/postgresql-schema-reader.d.ts +16 -2
- package/dist/migration/postgresql-schema-reader.d.ts.map +1 -1
- package/dist/migration/postgresql-schema-reader.js +281 -7
- package/dist/stream/sse.js +5 -3
- package/dist/template/__tests__/generated.template.search-text.test.js +99 -0
- package/dist/template/generated.template.test-d.js +24 -0
- package/dist/template/implementations/generated.template.d.ts.map +1 -1
- package/dist/template/implementations/generated.template.js +2 -2
- package/dist/template/implementations/init_types.template.d.ts.map +1 -1
- package/dist/template/implementations/init_types.template.js +11 -3
- package/dist/template/zod-converter.d.ts.map +1 -1
- package/dist/template/zod-converter.js +6 -2
- package/dist/testing/dev-test-routes.d.ts.map +1 -1
- package/dist/testing/dev-test-routes.js +5 -3
- package/dist/testing/fixture-generator.d.ts +13 -0
- package/dist/testing/fixture-generator.d.ts.map +1 -1
- package/dist/testing/fixture-generator.js +105 -8
- package/dist/testing/fixture-manager.d.ts.map +1 -1
- package/dist/testing/fixture-manager.js +19 -2
- package/dist/types/__tests__/entity-json-schema-search-text.test.js +256 -0
- package/dist/types/types.d.ts +494 -1
- package/dist/types/types.d.ts.map +1 -1
- package/dist/types/types.js +117 -13
- package/dist/ui/api.d.ts.map +1 -1
- package/dist/ui/api.js +14 -2
- package/dist/ui/cdd-service.d.ts +16 -14
- package/dist/ui/cdd-service.d.ts.map +1 -1
- package/dist/ui/cdd-service.js +145 -37
- package/dist/ui/cdd-types.d.ts +60 -0
- package/dist/ui/cdd-types.d.ts.map +1 -0
- package/dist/ui/cdd-types.js +3 -0
- package/dist/ui-web/assets/index-D4XFBV-f.css +1 -0
- package/dist/ui-web/assets/{index-CQ_S40bD.js → index-D_19-Pi4.js} +87 -87
- package/dist/ui-web/index.html +2 -2
- package/package.json +8 -4
- package/src/api/sonamu.ts +1 -2
- package/src/auth/auth-generator.ts +38 -0
- package/src/auth/better-auth-entities.ts +18 -1
- package/src/bin/cli.ts +15 -1
- package/src/cone/cone-generator.ts +9 -3
- package/src/database/knex.ts +26 -4
- package/src/database/puri.ts +71 -0
- package/src/database/puri.types.ts +2 -0
- package/src/entity/entity-manager.ts +95 -3
- package/src/migration/__tests__/code-generation.search-text.test.ts +390 -0
- package/src/migration/code-generation.ts +848 -34
- package/src/migration/migration-set.ts +2 -0
- package/src/migration/postgresql-schema-reader.ts +366 -9
- package/src/skills/sonamu/auth-migration.md +80 -0
- package/src/skills/sonamu/cdd.md +148 -28
- package/src/skills/sonamu/cone.md +16 -0
- package/src/skills/sonamu/entity-relations.md +1 -1
- package/src/skills/sonamu/fixture-cli.md +4 -0
- package/src/skills/sonamu/frontend.md +65 -0
- package/src/skills/sonamu/migration.md +3 -1
- package/src/skills/sonamu/model.md +28 -0
- package/src/skills/sonamu/workflow.md +12 -5
- package/src/stream/sse.ts +4 -2
- package/src/template/__tests__/generated.template.search-text.test.ts +89 -0
- package/src/template/generated.template.test-d.ts +46 -0
- package/src/template/implementations/generated.template.ts +4 -1
- package/src/template/implementations/init_types.template.ts +20 -5
- package/src/template/zod-converter.ts +5 -0
- package/src/testing/dev-test-routes.ts +4 -2
- package/src/testing/fixture-generator.ts +157 -9
- package/src/testing/fixture-manager.ts +15 -1
- package/src/types/__tests__/entity-json-schema-search-text.test.ts +179 -0
- package/src/types/types.ts +168 -12
- package/src/ui/api.ts +24 -1
- package/src/ui/cdd-service.ts +195 -55
- package/src/ui/cdd-types.ts +73 -0
- package/dist/ui-web/assets/index-egkMxKos.css +0 -1
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
import { describe, expect, test } from "vitest";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { EntityJsonSchema, isSearchTextJsonSourceZodType, TemplateOptions } from "../types.js";
|
|
4
|
+
function createBaseEntity() {
|
|
5
|
+
return {
|
|
6
|
+
id: "SearchTextTest",
|
|
7
|
+
title: "SearchText Test",
|
|
8
|
+
table: "search_text_tests",
|
|
9
|
+
props: [
|
|
10
|
+
{
|
|
11
|
+
name: "id",
|
|
12
|
+
type: "integer"
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
name: "title",
|
|
16
|
+
type: "string"
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
name: "tags",
|
|
20
|
+
type: "string[]"
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
name: "aliases",
|
|
24
|
+
type: "json",
|
|
25
|
+
id: "StringArray"
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
name: "search_text",
|
|
29
|
+
type: "searchText",
|
|
30
|
+
sourceColumns: [
|
|
31
|
+
{
|
|
32
|
+
name: "title",
|
|
33
|
+
caseInsensitive: true
|
|
34
|
+
}
|
|
35
|
+
]
|
|
36
|
+
}
|
|
37
|
+
],
|
|
38
|
+
indexes: [
|
|
39
|
+
{
|
|
40
|
+
type: "index",
|
|
41
|
+
name: "search_text_tests_search_text_index",
|
|
42
|
+
using: "gin",
|
|
43
|
+
columns: [
|
|
44
|
+
{
|
|
45
|
+
name: "search_text",
|
|
46
|
+
opclass: "gin_trgm_ops"
|
|
47
|
+
}
|
|
48
|
+
]
|
|
49
|
+
}
|
|
50
|
+
],
|
|
51
|
+
subsets: {
|
|
52
|
+
A: [
|
|
53
|
+
"id",
|
|
54
|
+
"title",
|
|
55
|
+
"tags",
|
|
56
|
+
"aliases",
|
|
57
|
+
"search_text"
|
|
58
|
+
]
|
|
59
|
+
},
|
|
60
|
+
enums: {}
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
describe("EntityJsonSchema searchText/opclass validation", ()=>{
|
|
64
|
+
test("opclass known/free string과 vectorOps 하위호환을 허용해야 한다", ()=>{
|
|
65
|
+
const knownOpclass = createBaseEntity();
|
|
66
|
+
const knownResult = EntityJsonSchema.safeParse(knownOpclass);
|
|
67
|
+
expect(knownResult.success).toBe(true);
|
|
68
|
+
const freeOpclass = createBaseEntity();
|
|
69
|
+
freeOpclass.indexes[0] = {
|
|
70
|
+
...freeOpclass.indexes[0],
|
|
71
|
+
columns: [
|
|
72
|
+
{
|
|
73
|
+
name: "search_text",
|
|
74
|
+
opclass: "custom_text_ops"
|
|
75
|
+
}
|
|
76
|
+
]
|
|
77
|
+
};
|
|
78
|
+
const freeResult = EntityJsonSchema.safeParse(freeOpclass);
|
|
79
|
+
expect(freeResult.success).toBe(true);
|
|
80
|
+
const legacyVectorOps = createBaseEntity();
|
|
81
|
+
legacyVectorOps.indexes[0] = {
|
|
82
|
+
type: "hnsw",
|
|
83
|
+
name: "search_text_tests_embedding_hnsw",
|
|
84
|
+
columns: [
|
|
85
|
+
{
|
|
86
|
+
name: "embedding",
|
|
87
|
+
vectorOps: "vector_cosine_ops"
|
|
88
|
+
}
|
|
89
|
+
],
|
|
90
|
+
m: 16,
|
|
91
|
+
efConstruction: 64
|
|
92
|
+
};
|
|
93
|
+
const legacyResult = EntityJsonSchema.safeParse(legacyVectorOps);
|
|
94
|
+
expect(legacyResult.success).toBe(true);
|
|
95
|
+
});
|
|
96
|
+
test("searchText source column 존재/타입 검증이 동작해야 한다", ()=>{
|
|
97
|
+
const unknownSource = createBaseEntity();
|
|
98
|
+
unknownSource.props[4] = {
|
|
99
|
+
name: "search_text",
|
|
100
|
+
type: "searchText",
|
|
101
|
+
sourceColumns: [
|
|
102
|
+
{
|
|
103
|
+
name: "missing_col",
|
|
104
|
+
caseInsensitive: true
|
|
105
|
+
}
|
|
106
|
+
]
|
|
107
|
+
};
|
|
108
|
+
const unknownResult = EntityJsonSchema.safeParse(unknownSource);
|
|
109
|
+
expect(unknownResult.success).toBe(false);
|
|
110
|
+
const unsupportedSourceType = createBaseEntity();
|
|
111
|
+
unsupportedSourceType.props[4] = {
|
|
112
|
+
name: "search_text",
|
|
113
|
+
type: "searchText",
|
|
114
|
+
sourceColumns: [
|
|
115
|
+
{
|
|
116
|
+
name: "id",
|
|
117
|
+
caseInsensitive: true
|
|
118
|
+
}
|
|
119
|
+
]
|
|
120
|
+
};
|
|
121
|
+
const unsupportedResult = EntityJsonSchema.safeParse(unsupportedSourceType);
|
|
122
|
+
expect(unsupportedResult.success).toBe(false);
|
|
123
|
+
});
|
|
124
|
+
test("schema 단계에서는 searchText json source id naming을 강제하지 않아야 한다", ()=>{
|
|
125
|
+
const nullableWrapper = createBaseEntity();
|
|
126
|
+
nullableWrapper.props[3] = {
|
|
127
|
+
name: "aliases",
|
|
128
|
+
type: "json",
|
|
129
|
+
id: "NullableStringArray"
|
|
130
|
+
};
|
|
131
|
+
nullableWrapper.props[4] = {
|
|
132
|
+
name: "search_text",
|
|
133
|
+
type: "searchText",
|
|
134
|
+
sourceColumns: [
|
|
135
|
+
{
|
|
136
|
+
name: "aliases",
|
|
137
|
+
caseInsensitive: true
|
|
138
|
+
}
|
|
139
|
+
]
|
|
140
|
+
};
|
|
141
|
+
const wrapperResult = EntityJsonSchema.safeParse(nullableWrapper);
|
|
142
|
+
expect(wrapperResult.success).toBe(true);
|
|
143
|
+
const nullableElement = createBaseEntity();
|
|
144
|
+
nullableElement.props[3] = {
|
|
145
|
+
name: "aliases",
|
|
146
|
+
type: "json",
|
|
147
|
+
id: "StringNullableArray"
|
|
148
|
+
};
|
|
149
|
+
nullableElement.props[4] = {
|
|
150
|
+
name: "search_text",
|
|
151
|
+
type: "searchText",
|
|
152
|
+
sourceColumns: [
|
|
153
|
+
{
|
|
154
|
+
name: "aliases",
|
|
155
|
+
caseInsensitive: true
|
|
156
|
+
}
|
|
157
|
+
]
|
|
158
|
+
};
|
|
159
|
+
const nullableElementResult = EntityJsonSchema.safeParse(nullableElement);
|
|
160
|
+
expect(nullableElementResult.success).toBe(true);
|
|
161
|
+
const customNamedType = createBaseEntity();
|
|
162
|
+
customNamedType.props[3] = {
|
|
163
|
+
name: "aliases",
|
|
164
|
+
type: "json",
|
|
165
|
+
id: "CustomAliasType"
|
|
166
|
+
};
|
|
167
|
+
customNamedType.props[4] = {
|
|
168
|
+
name: "search_text",
|
|
169
|
+
type: "searchText",
|
|
170
|
+
sourceColumns: [
|
|
171
|
+
{
|
|
172
|
+
name: "aliases",
|
|
173
|
+
caseInsensitive: true
|
|
174
|
+
}
|
|
175
|
+
]
|
|
176
|
+
};
|
|
177
|
+
const customNamedTypeResult = EntityJsonSchema.safeParse(customNamedType);
|
|
178
|
+
expect(customNamedTypeResult.success).toBe(true);
|
|
179
|
+
});
|
|
180
|
+
test("TemplateOptions.entity 경로도 searchText source 검증을 동일하게 적용해야 한다", ()=>{
|
|
181
|
+
const validEntity = createBaseEntity();
|
|
182
|
+
const validTemplateEntity = {
|
|
183
|
+
entityId: validEntity.id,
|
|
184
|
+
title: validEntity.title,
|
|
185
|
+
table: validEntity.table,
|
|
186
|
+
props: validEntity.props,
|
|
187
|
+
indexes: validEntity.indexes,
|
|
188
|
+
subsets: validEntity.subsets,
|
|
189
|
+
enums: validEntity.enums
|
|
190
|
+
};
|
|
191
|
+
const validResult = TemplateOptions.shape.entity.safeParse(validTemplateEntity);
|
|
192
|
+
expect(validResult.success).toBe(true);
|
|
193
|
+
const invalidEntity = createBaseEntity();
|
|
194
|
+
invalidEntity.props[4] = {
|
|
195
|
+
name: "search_text",
|
|
196
|
+
type: "searchText",
|
|
197
|
+
sourceColumns: [
|
|
198
|
+
{
|
|
199
|
+
name: "missing_col",
|
|
200
|
+
caseInsensitive: true
|
|
201
|
+
}
|
|
202
|
+
]
|
|
203
|
+
};
|
|
204
|
+
const invalidTemplateEntity = {
|
|
205
|
+
entityId: invalidEntity.id,
|
|
206
|
+
title: invalidEntity.title,
|
|
207
|
+
table: invalidEntity.table,
|
|
208
|
+
props: invalidEntity.props,
|
|
209
|
+
indexes: invalidEntity.indexes,
|
|
210
|
+
subsets: invalidEntity.subsets,
|
|
211
|
+
enums: invalidEntity.enums
|
|
212
|
+
};
|
|
213
|
+
const invalidResult = TemplateOptions.shape.entity.safeParse(invalidTemplateEntity);
|
|
214
|
+
expect(invalidResult.success).toBe(false);
|
|
215
|
+
});
|
|
216
|
+
test("TemplateOptions.entity 경로도 json id naming에 의존하지 않아야 한다", ()=>{
|
|
217
|
+
const customNamedType = createBaseEntity();
|
|
218
|
+
customNamedType.props[3] = {
|
|
219
|
+
name: "aliases",
|
|
220
|
+
type: "json",
|
|
221
|
+
id: "CustomAliasType"
|
|
222
|
+
};
|
|
223
|
+
customNamedType.props[4] = {
|
|
224
|
+
name: "search_text",
|
|
225
|
+
type: "searchText",
|
|
226
|
+
sourceColumns: [
|
|
227
|
+
{
|
|
228
|
+
name: "aliases",
|
|
229
|
+
caseInsensitive: true
|
|
230
|
+
}
|
|
231
|
+
]
|
|
232
|
+
};
|
|
233
|
+
const customTemplateEntity = {
|
|
234
|
+
entityId: customNamedType.id,
|
|
235
|
+
title: customNamedType.title,
|
|
236
|
+
table: customNamedType.table,
|
|
237
|
+
props: customNamedType.props,
|
|
238
|
+
indexes: customNamedType.indexes,
|
|
239
|
+
subsets: customNamedType.subsets,
|
|
240
|
+
enums: customNamedType.enums
|
|
241
|
+
};
|
|
242
|
+
const customTemplateResult = TemplateOptions.shape.entity.safeParse(customTemplateEntity);
|
|
243
|
+
expect(customTemplateResult.success).toBe(true);
|
|
244
|
+
});
|
|
245
|
+
test("searchText json source Zod 구조 검증 유틸이 wrapper/element 타입을 정확히 판정해야 한다", ()=>{
|
|
246
|
+
expect(isSearchTextJsonSourceZodType(z.array(z.string()))).toBe(true);
|
|
247
|
+
expect(isSearchTextJsonSourceZodType(z.array(z.string()).optional())).toBe(true);
|
|
248
|
+
expect(isSearchTextJsonSourceZodType(z.array(z.string()).nullable())).toBe(true);
|
|
249
|
+
expect(isSearchTextJsonSourceZodType(z.array(z.string()).nullish())).toBe(true);
|
|
250
|
+
expect(isSearchTextJsonSourceZodType(z.array(z.string().nullable()))).toBe(false);
|
|
251
|
+
expect(isSearchTextJsonSourceZodType(z.array(z.number()))).toBe(false);
|
|
252
|
+
expect(isSearchTextJsonSourceZodType(z.string())).toBe(false);
|
|
253
|
+
});
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy90eXBlcy9fX3Rlc3RzX18vZW50aXR5LWpzb24tc2NoZW1hLXNlYXJjaC10ZXh0LnRlc3QudHMiXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgZGVzY3JpYmUsIGV4cGVjdCwgdGVzdCB9IGZyb20gXCJ2aXRlc3RcIjtcbmltcG9ydCB7IHogfSBmcm9tIFwiem9kXCI7XG5pbXBvcnQgeyBFbnRpdHlKc29uU2NoZW1hLCBpc1NlYXJjaFRleHRKc29uU291cmNlWm9kVHlwZSwgVGVtcGxhdGVPcHRpb25zIH0gZnJvbSBcIi4uL3R5cGVzXCI7XG5cbmZ1bmN0aW9uIGNyZWF0ZUJhc2VFbnRpdHkoKSB7XG4gIHJldHVybiB7XG4gICAgaWQ6IFwiU2VhcmNoVGV4dFRlc3RcIixcbiAgICB0aXRsZTogXCJTZWFyY2hUZXh0IFRlc3RcIixcbiAgICB0YWJsZTogXCJzZWFyY2hfdGV4dF90ZXN0c1wiLFxuICAgIHByb3BzOiBbXG4gICAgICB7IG5hbWU6IFwiaWRcIiwgdHlwZTogXCJpbnRlZ2VyXCIgfSxcbiAgICAgIHsgbmFtZTogXCJ0aXRsZVwiLCB0eXBlOiBcInN0cmluZ1wiIH0sXG4gICAgICB7IG5hbWU6IFwidGFnc1wiLCB0eXBlOiBcInN0cmluZ1tdXCIgfSxcbiAgICAgIHsgbmFtZTogXCJhbGlhc2VzXCIsIHR5cGU6IFwianNvblwiLCBpZDogXCJTdHJpbmdBcnJheVwiIH0sXG4gICAgICB7XG4gICAgICAgIG5hbWU6IFwic2VhcmNoX3RleHRcIixcbiAgICAgICAgdHlwZTogXCJzZWFyY2hUZXh0XCIsXG4gICAgICAgIHNvdXJjZUNvbHVtbnM6IFt7IG5hbWU6IFwidGl0bGVcIiwgY2FzZUluc2Vuc2l0aXZlOiB0cnVlIH1dLFxuICAgICAgfSxcbiAgICBdLFxuICAgIGluZGV4ZXM6IFtcbiAgICAgIHtcbiAgICAgICAgdHlwZTogXCJpbmRleFwiLFxuICAgICAgICBuYW1lOiBcInNlYXJjaF90ZXh0X3Rlc3RzX3NlYXJjaF90ZXh0X2luZGV4XCIsXG4gICAgICAgIHVzaW5nOiBcImdpblwiLFxuICAgICAgICBjb2x1bW5zOiBbeyBuYW1lOiBcInNlYXJjaF90ZXh0XCIsIG9wY2xhc3M6IFwiZ2luX3RyZ21fb3BzXCIgfV0sXG4gICAgICB9LFxuICAgIF0sXG4gICAgc3Vic2V0czoge1xuICAgICAgQTogW1wiaWRcIiwgXCJ0aXRsZVwiLCBcInRhZ3NcIiwgXCJhbGlhc2VzXCIsIFwic2VhcmNoX3RleHRcIl0sXG4gICAgfSxcbiAgICBlbnVtczoge30sXG4gIH0gYXMgY29uc3Q7XG59XG5cbmRlc2NyaWJlKFwiRW50aXR5SnNvblNjaGVtYSBzZWFyY2hUZXh0L29wY2xhc3MgdmFsaWRhdGlvblwiLCAoKSA9PiB7XG4gIHRlc3QoXCJvcGNsYXNzIGtub3duL2ZyZWUgc3RyaW5n6rO8IHZlY3Rvck9wcyDtlZjsnITtmLjtmZjsnYQg7ZeI7Jqp7ZW07JW8IO2VnOuLpFwiLCAoKSA9PiB7XG4gICAgY29uc3Qga25vd25PcGNsYXNzID0gY3JlYXRlQmFzZUVudGl0eSgpO1xuICAgIGNvbnN0IGtub3duUmVzdWx0ID0gRW50aXR5SnNvblNjaGVtYS5zYWZlUGFyc2Uoa25vd25PcGNsYXNzKTtcbiAgICBleHBlY3Qoa25vd25SZXN1bHQuc3VjY2VzcykudG9CZSh0cnVlKTtcblxuICAgIGNvbnN0IGZyZWVPcGNsYXNzID0gY3JlYXRlQmFzZUVudGl0eSgpO1xuICAgIGZyZWVPcGNsYXNzLmluZGV4ZXNbMF0gPSB7XG4gICAgICAuLi5mcmVlT3BjbGFzcy5pbmRleGVzWzBdLFxuICAgICAgY29sdW1uczogW3sgbmFtZTogXCJzZWFyY2hfdGV4dFwiLCBvcGNsYXNzOiBcImN1c3RvbV90ZXh0X29wc1wiIH1dLFxuICAgIH07XG4gICAgY29uc3QgZnJlZVJlc3VsdCA9IEVudGl0eUpzb25TY2hlbWEuc2FmZVBhcnNlKGZyZWVPcGNsYXNzKTtcbiAgICBleHBlY3QoZnJlZVJlc3VsdC5zdWNjZXNzKS50b0JlKHRydWUpO1xuXG4gICAgY29uc3QgbGVnYWN5VmVjdG9yT3BzID0gY3JlYXRlQmFzZUVudGl0eSgpO1xuICAgIGxlZ2FjeVZlY3Rvck9wcy5pbmRleGVzWzBdID0ge1xuICAgICAgdHlwZTogXCJobnN3XCIsXG4gICAgICBuYW1lOiBcInNlYXJjaF90ZXh0X3Rlc3RzX2VtYmVkZGluZ19obnN3XCIsXG4gICAgICBjb2x1bW5zOiBbeyBuYW1lOiBcImVtYmVkZGluZ1wiLCB2ZWN0b3JPcHM6IFwidmVjdG9yX2Nvc2luZV9vcHNcIiB9XSxcbiAgICAgIG06IDE2LFxuICAgICAgZWZDb25zdHJ1Y3Rpb246IDY0LFxuICAgIH07XG4gICAgY29uc3QgbGVnYWN5UmVzdWx0ID0gRW50aXR5SnNvblNjaGVtYS5zYWZlUGFyc2UobGVnYWN5VmVjdG9yT3BzKTtcbiAgICBleHBlY3QobGVnYWN5UmVzdWx0LnN1Y2Nlc3MpLnRvQmUodHJ1ZSk7XG4gIH0pO1xuXG4gIHRlc3QoXCJzZWFyY2hUZXh0IHNvdXJjZSBjb2x1bW4g7KG07J6sL+2DgOyehSDqsoDspp3snbQg64+Z7J6R7ZW07JW8IO2VnOuLpFwiLCAoKSA9PiB7XG4gICAgY29uc3QgdW5rbm93blNvdXJjZSA9IGNyZWF0ZUJhc2VFbnRpdHkoKTtcbiAgICB1bmtub3duU291cmNlLnByb3BzWzRdID0ge1xuICAgICAgbmFtZTogXCJzZWFyY2hfdGV4dFwiLFxuICAgICAgdHlwZTogXCJzZWFyY2hUZXh0XCIsXG4gICAgICBzb3VyY2VDb2x1bW5zOiBbeyBuYW1lOiBcIm1pc3NpbmdfY29sXCIsIGNhc2VJbnNlbnNpdGl2ZTogdHJ1ZSB9XSxcbiAgICB9O1xuICAgIGNvbnN0IHVua25vd25SZXN1bHQgPSBFbnRpdHlKc29uU2NoZW1hLnNhZmVQYXJzZSh1bmtub3duU291cmNlKTtcbiAgICBleHBlY3QodW5rbm93blJlc3VsdC5zdWNjZXNzKS50b0JlKGZhbHNlKTtcblxuICAgIGNvbnN0IHVuc3VwcG9ydGVkU291cmNlVHlwZSA9IGNyZWF0ZUJhc2VFbnRpdHkoKTtcbiAgICB1bnN1cHBvcnRlZFNvdXJjZVR5cGUucHJvcHNbNF0gPSB7XG4gICAgICBuYW1lOiBcInNlYXJjaF90ZXh0XCIsXG4gICAgICB0eXBlOiBcInNlYXJjaFRleHRcIixcbiAgICAgIHNvdXJjZUNvbHVtbnM6IFt7IG5hbWU6IFwiaWRcIiwgY2FzZUluc2Vuc2l0aXZlOiB0cnVlIH1dLFxuICAgIH07XG4gICAgY29uc3QgdW5zdXBwb3J0ZWRSZXN1bHQgPSBFbnRpdHlKc29uU2NoZW1hLnNhZmVQYXJzZSh1bnN1cHBvcnRlZFNvdXJjZVR5cGUpO1xuICAgIGV4cGVjdCh1bnN1cHBvcnRlZFJlc3VsdC5zdWNjZXNzKS50b0JlKGZhbHNlKTtcbiAgfSk7XG5cbiAgdGVzdChcInNjaGVtYSDri6jqs4Tsl5DshJzripQgc2VhcmNoVGV4dCBqc29uIHNvdXJjZSBpZCBuYW1pbmfsnYQg6rCV7KCc7ZWY7KeAIOyViuyVhOyVvCDtlZzri6RcIiwgKCkgPT4ge1xuICAgIGNvbnN0IG51bGxhYmxlV3JhcHBlciA9IGNyZWF0ZUJhc2VFbnRpdHkoKTtcbiAgICBudWxsYWJsZVdyYXBwZXIucHJvcHNbM10gPSB7IG5hbWU6IFwiYWxpYXNlc1wiLCB0eXBlOiBcImpzb25cIiwgaWQ6IFwiTnVsbGFibGVTdHJpbmdBcnJheVwiIH07XG4gICAgbnVsbGFibGVXcmFwcGVyLnByb3BzWzRdID0ge1xuICAgICAgbmFtZTogXCJzZWFyY2hfdGV4dFwiLFxuICAgICAgdHlwZTogXCJzZWFyY2hUZXh0XCIsXG4gICAgICBzb3VyY2VDb2x1bW5zOiBbeyBuYW1lOiBcImFsaWFzZXNcIiwgY2FzZUluc2Vuc2l0aXZlOiB0cnVlIH1dLFxuICAgIH07XG4gICAgY29uc3Qgd3JhcHBlclJlc3VsdCA9IEVudGl0eUpzb25TY2hlbWEuc2FmZVBhcnNlKG51bGxhYmxlV3JhcHBlcik7XG4gICAgZXhwZWN0KHdyYXBwZXJSZXN1bHQuc3VjY2VzcykudG9CZSh0cnVlKTtcblxuICAgIGNvbnN0IG51bGxhYmxlRWxlbWVudCA9IGNyZWF0ZUJhc2VFbnRpdHkoKTtcbiAgICBudWxsYWJsZUVsZW1lbnQucHJvcHNbM10gPSB7IG5hbWU6IFwiYWxpYXNlc1wiLCB0eXBlOiBcImpzb25cIiwgaWQ6IFwiU3RyaW5nTnVsbGFibGVBcnJheVwiIH07XG4gICAgbnVsbGFibGVFbGVtZW50LnByb3BzWzRdID0ge1xuICAgICAgbmFtZTogXCJzZWFyY2hfdGV4dFwiLFxuICAgICAgdHlwZTogXCJzZWFyY2hUZXh0XCIsXG4gICAgICBzb3VyY2VDb2x1bW5zOiBbeyBuYW1lOiBcImFsaWFzZXNcIiwgY2FzZUluc2Vuc2l0aXZlOiB0cnVlIH1dLFxuICAgIH07XG4gICAgY29uc3QgbnVsbGFibGVFbGVtZW50UmVzdWx0ID0gRW50aXR5SnNvblNjaGVtYS5zYWZlUGFyc2UobnVsbGFibGVFbGVtZW50KTtcbiAgICBleHBlY3QobnVsbGFibGVFbGVtZW50UmVzdWx0LnN1Y2Nlc3MpLnRvQmUodHJ1ZSk7XG5cbiAgICBjb25zdCBjdXN0b21OYW1lZFR5cGUgPSBjcmVhdGVCYXNlRW50aXR5KCk7XG4gICAgY3VzdG9tTmFtZWRUeXBlLnByb3BzWzNdID0geyBuYW1lOiBcImFsaWFzZXNcIiwgdHlwZTogXCJqc29uXCIsIGlkOiBcIkN1c3RvbUFsaWFzVHlwZVwiIH07XG4gICAgY3VzdG9tTmFtZWRUeXBlLnByb3BzWzRdID0ge1xuICAgICAgbmFtZTogXCJzZWFyY2hfdGV4dFwiLFxuICAgICAgdHlwZTogXCJzZWFyY2hUZXh0XCIsXG4gICAgICBzb3VyY2VDb2x1bW5zOiBbeyBuYW1lOiBcImFsaWFzZXNcIiwgY2FzZUluc2Vuc2l0aXZlOiB0cnVlIH1dLFxuICAgIH07XG4gICAgY29uc3QgY3VzdG9tTmFtZWRUeXBlUmVzdWx0ID0gRW50aXR5SnNvblNjaGVtYS5zYWZlUGFyc2UoY3VzdG9tTmFtZWRUeXBlKTtcbiAgICBleHBlY3QoY3VzdG9tTmFtZWRUeXBlUmVzdWx0LnN1Y2Nlc3MpLnRvQmUodHJ1ZSk7XG4gIH0pO1xuXG4gIHRlc3QoXCJUZW1wbGF0ZU9wdGlvbnMuZW50aXR5IOqyveuhnOuPhCBzZWFyY2hUZXh0IHNvdXJjZSDqsoDspp3snYQg64+Z7J287ZWY6rKMIOyggeyaqe2VtOyVvCDtlZzri6RcIiwgKCkgPT4ge1xuICAgIGNvbnN0IHZhbGlkRW50aXR5ID0gY3JlYXRlQmFzZUVudGl0eSgpO1xuICAgIGNvbnN0IHZhbGlkVGVtcGxhdGVFbnRpdHkgPSB7XG4gICAgICBlbnRpdHlJZDogdmFsaWRFbnRpdHkuaWQsXG4gICAgICB0aXRsZTogdmFsaWRFbnRpdHkudGl0bGUsXG4gICAgICB0YWJsZTogdmFsaWRFbnRpdHkudGFibGUsXG4gICAgICBwcm9wczogdmFsaWRFbnRpdHkucHJvcHMsXG4gICAgICBpbmRleGVzOiB2YWxpZEVudGl0eS5pbmRleGVzLFxuICAgICAgc3Vic2V0czogdmFsaWRFbnRpdHkuc3Vic2V0cyxcbiAgICAgIGVudW1zOiB2YWxpZEVudGl0eS5lbnVtcyxcbiAgICB9O1xuICAgIGNvbnN0IHZhbGlkUmVzdWx0ID0gVGVtcGxhdGVPcHRpb25zLnNoYXBlLmVudGl0eS5zYWZlUGFyc2UodmFsaWRUZW1wbGF0ZUVudGl0eSk7XG4gICAgZXhwZWN0KHZhbGlkUmVzdWx0LnN1Y2Nlc3MpLnRvQmUodHJ1ZSk7XG5cbiAgICBjb25zdCBpbnZhbGlkRW50aXR5ID0gY3JlYXRlQmFzZUVudGl0eSgpO1xuICAgIGludmFsaWRFbnRpdHkucHJvcHNbNF0gPSB7XG4gICAgICBuYW1lOiBcInNlYXJjaF90ZXh0XCIsXG4gICAgICB0eXBlOiBcInNlYXJjaFRleHRcIixcbiAgICAgIHNvdXJjZUNvbHVtbnM6IFt7IG5hbWU6IFwibWlzc2luZ19jb2xcIiwgY2FzZUluc2Vuc2l0aXZlOiB0cnVlIH1dLFxuICAgIH07XG4gICAgY29uc3QgaW52YWxpZFRlbXBsYXRlRW50aXR5ID0ge1xuICAgICAgZW50aXR5SWQ6IGludmFsaWRFbnRpdHkuaWQsXG4gICAgICB0aXRsZTogaW52YWxpZEVudGl0eS50aXRsZSxcbiAgICAgIHRhYmxlOiBpbnZhbGlkRW50aXR5LnRhYmxlLFxuICAgICAgcHJvcHM6IGludmFsaWRFbnRpdHkucHJvcHMsXG4gICAgICBpbmRleGVzOiBpbnZhbGlkRW50aXR5LmluZGV4ZXMsXG4gICAgICBzdWJzZXRzOiBpbnZhbGlkRW50aXR5LnN1YnNldHMsXG4gICAgICBlbnVtczogaW52YWxpZEVudGl0eS5lbnVtcyxcbiAgICB9O1xuICAgIGNvbnN0IGludmFsaWRSZXN1bHQgPSBUZW1wbGF0ZU9wdGlvbnMuc2hhcGUuZW50aXR5LnNhZmVQYXJzZShpbnZhbGlkVGVtcGxhdGVFbnRpdHkpO1xuICAgIGV4cGVjdChpbnZhbGlkUmVzdWx0LnN1Y2Nlc3MpLnRvQmUoZmFsc2UpO1xuICB9KTtcblxuICB0ZXN0KFwiVGVtcGxhdGVPcHRpb25zLmVudGl0eSDqsr3roZzrj4QganNvbiBpZCBuYW1pbmfsl5Ag7J2Y7KG07ZWY7KeAIOyViuyVhOyVvCDtlZzri6RcIiwgKCkgPT4ge1xuICAgIGNvbnN0IGN1c3RvbU5hbWVkVHlwZSA9IGNyZWF0ZUJhc2VFbnRpdHkoKTtcbiAgICBjdXN0b21OYW1lZFR5cGUucHJvcHNbM10gPSB7IG5hbWU6IFwiYWxpYXNlc1wiLCB0eXBlOiBcImpzb25cIiwgaWQ6IFwiQ3VzdG9tQWxpYXNUeXBlXCIgfTtcbiAgICBjdXN0b21OYW1lZFR5cGUucHJvcHNbNF0gPSB7XG4gICAgICBuYW1lOiBcInNlYXJjaF90ZXh0XCIsXG4gICAgICB0eXBlOiBcInNlYXJjaFRleHRcIixcbiAgICAgIHNvdXJjZUNvbHVtbnM6IFt7IG5hbWU6IFwiYWxpYXNlc1wiLCBjYXNlSW5zZW5zaXRpdmU6IHRydWUgfV0sXG4gICAgfTtcblxuICAgIGNvbnN0IGN1c3RvbVRlbXBsYXRlRW50aXR5ID0ge1xuICAgICAgZW50aXR5SWQ6IGN1c3RvbU5hbWVkVHlwZS5pZCxcbiAgICAgIHRpdGxlOiBjdXN0b21OYW1lZFR5cGUudGl0bGUsXG4gICAgICB0YWJsZTogY3VzdG9tTmFtZWRUeXBlLnRhYmxlLFxuICAgICAgcHJvcHM6IGN1c3RvbU5hbWVkVHlwZS5wcm9wcyxcbiAgICAgIGluZGV4ZXM6IGN1c3RvbU5hbWVkVHlwZS5pbmRleGVzLFxuICAgICAgc3Vic2V0czogY3VzdG9tTmFtZWRUeXBlLnN1YnNldHMsXG4gICAgICBlbnVtczogY3VzdG9tTmFtZWRUeXBlLmVudW1zLFxuICAgIH07XG4gICAgY29uc3QgY3VzdG9tVGVtcGxhdGVSZXN1bHQgPSBUZW1wbGF0ZU9wdGlvbnMuc2hhcGUuZW50aXR5LnNhZmVQYXJzZShjdXN0b21UZW1wbGF0ZUVudGl0eSk7XG4gICAgZXhwZWN0KGN1c3RvbVRlbXBsYXRlUmVzdWx0LnN1Y2Nlc3MpLnRvQmUodHJ1ZSk7XG4gIH0pO1xuXG4gIHRlc3QoXCJzZWFyY2hUZXh0IGpzb24gc291cmNlIFpvZCDqtazsobAg6rKA7KadIOycoO2LuOydtCB3cmFwcGVyL2VsZW1lbnQg7YOA7J6F7J2EIOygle2Zle2eiCDtjJDsoJXtlbTslbwg7ZWc64ukXCIsICgpID0+IHtcbiAgICBleHBlY3QoaXNTZWFyY2hUZXh0SnNvblNvdXJjZVpvZFR5cGUoei5hcnJheSh6LnN0cmluZygpKSkpLnRvQmUodHJ1ZSk7XG4gICAgZXhwZWN0KGlzU2VhcmNoVGV4dEpzb25Tb3VyY2Vab2RUeXBlKHouYXJyYXkoei5zdHJpbmcoKSkub3B0aW9uYWwoKSkpLnRvQmUodHJ1ZSk7XG4gICAgZXhwZWN0KGlzU2VhcmNoVGV4dEpzb25Tb3VyY2Vab2RUeXBlKHouYXJyYXkoei5zdHJpbmcoKSkubnVsbGFibGUoKSkpLnRvQmUodHJ1ZSk7XG4gICAgZXhwZWN0KGlzU2VhcmNoVGV4dEpzb25Tb3VyY2Vab2RUeXBlKHouYXJyYXkoei5zdHJpbmcoKSkubnVsbGlzaCgpKSkudG9CZSh0cnVlKTtcblxuICAgIGV4cGVjdChpc1NlYXJjaFRleHRKc29uU291cmNlWm9kVHlwZSh6LmFycmF5KHouc3RyaW5nKCkubnVsbGFibGUoKSkpKS50b0JlKGZhbHNlKTtcbiAgICBleHBlY3QoaXNTZWFyY2hUZXh0SnNvblNvdXJjZVpvZFR5cGUoei5hcnJheSh6Lm51bWJlcigpKSkpLnRvQmUoZmFsc2UpO1xuICAgIGV4cGVjdChpc1NlYXJjaFRleHRKc29uU291cmNlWm9kVHlwZSh6LnN0cmluZygpKSkudG9CZShmYWxzZSk7XG4gIH0pO1xufSk7XG4iXSwibmFtZXMiOlsiZGVzY3JpYmUiLCJleHBlY3QiLCJ0ZXN0IiwieiIsIkVudGl0eUpzb25TY2hlbWEiLCJpc1NlYXJjaFRleHRKc29uU291cmNlWm9kVHlwZSIsIlRlbXBsYXRlT3B0aW9ucyIsImNyZWF0ZUJhc2VFbnRpdHkiLCJpZCIsInRpdGxlIiwidGFibGUiLCJwcm9wcyIsIm5hbWUiLCJ0eXBlIiwic291cmNlQ29sdW1ucyIsImNhc2VJbnNlbnNpdGl2ZSIsImluZGV4ZXMiLCJ1c2luZyIsImNvbHVtbnMiLCJvcGNsYXNzIiwic3Vic2V0cyIsIkEiLCJlbnVtcyIsImtub3duT3BjbGFzcyIsImtub3duUmVzdWx0Iiwic2FmZVBhcnNlIiwic3VjY2VzcyIsInRvQmUiLCJmcmVlT3BjbGFzcyIsImZyZWVSZXN1bHQiLCJsZWdhY3lWZWN0b3JPcHMiLCJ2ZWN0b3JPcHMiLCJtIiwiZWZDb25zdHJ1Y3Rpb24iLCJsZWdhY3lSZXN1bHQiLCJ1bmtub3duU291cmNlIiwidW5rbm93blJlc3VsdCIsInVuc3VwcG9ydGVkU291cmNlVHlwZSIsInVuc3VwcG9ydGVkUmVzdWx0IiwibnVsbGFibGVXcmFwcGVyIiwid3JhcHBlclJlc3VsdCIsIm51bGxhYmxlRWxlbWVudCIsIm51bGxhYmxlRWxlbWVudFJlc3VsdCIsImN1c3RvbU5hbWVkVHlwZSIsImN1c3RvbU5hbWVkVHlwZVJlc3VsdCIsInZhbGlkRW50aXR5IiwidmFsaWRUZW1wbGF0ZUVudGl0eSIsImVudGl0eUlkIiwidmFsaWRSZXN1bHQiLCJzaGFwZSIsImVudGl0eSIsImludmFsaWRFbnRpdHkiLCJpbnZhbGlkVGVtcGxhdGVFbnRpdHkiLCJpbnZhbGlkUmVzdWx0IiwiY3VzdG9tVGVtcGxhdGVFbnRpdHkiLCJjdXN0b21UZW1wbGF0ZVJlc3VsdCIsImFycmF5Iiwic3RyaW5nIiwib3B0aW9uYWwiLCJudWxsYWJsZSIsIm51bGxpc2giLCJudW1iZXIiXSwibWFwcGluZ3MiOiJBQUFBLFNBQVNBLFFBQVEsRUFBRUMsTUFBTSxFQUFFQyxJQUFJLFFBQVEsU0FBUztBQUNoRCxTQUFTQyxDQUFDLFFBQVEsTUFBTTtBQUN4QixTQUFTQyxnQkFBZ0IsRUFBRUMsNkJBQTZCLEVBQUVDLGVBQWUsUUFBUSxjQUFXO0FBRTVGLFNBQVNDO0lBQ1AsT0FBTztRQUNMQyxJQUFJO1FBQ0pDLE9BQU87UUFDUEMsT0FBTztRQUNQQyxPQUFPO1lBQ0w7Z0JBQUVDLE1BQU07Z0JBQU1DLE1BQU07WUFBVTtZQUM5QjtnQkFBRUQsTUFBTTtnQkFBU0MsTUFBTTtZQUFTO1lBQ2hDO2dCQUFFRCxNQUFNO2dCQUFRQyxNQUFNO1lBQVc7WUFDakM7Z0JBQUVELE1BQU07Z0JBQVdDLE1BQU07Z0JBQVFMLElBQUk7WUFBYztZQUNuRDtnQkFDRUksTUFBTTtnQkFDTkMsTUFBTTtnQkFDTkMsZUFBZTtvQkFBQzt3QkFBRUYsTUFBTTt3QkFBU0csaUJBQWlCO29CQUFLO2lCQUFFO1lBQzNEO1NBQ0Q7UUFDREMsU0FBUztZQUNQO2dCQUNFSCxNQUFNO2dCQUNORCxNQUFNO2dCQUNOSyxPQUFPO2dCQUNQQyxTQUFTO29CQUFDO3dCQUFFTixNQUFNO3dCQUFlTyxTQUFTO29CQUFlO2lCQUFFO1lBQzdEO1NBQ0Q7UUFDREMsU0FBUztZQUNQQyxHQUFHO2dCQUFDO2dCQUFNO2dCQUFTO2dCQUFRO2dCQUFXO2FBQWM7UUFDdEQ7UUFDQUMsT0FBTyxDQUFDO0lBQ1Y7QUFDRjtBQUVBdEIsU0FBUyxrREFBa0Q7SUFDekRFLEtBQUssc0RBQXNEO1FBQ3pELE1BQU1xQixlQUFlaEI7UUFDckIsTUFBTWlCLGNBQWNwQixpQkFBaUJxQixTQUFTLENBQUNGO1FBQy9DdEIsT0FBT3VCLFlBQVlFLE9BQU8sRUFBRUMsSUFBSSxDQUFDO1FBRWpDLE1BQU1DLGNBQWNyQjtRQUNwQnFCLFlBQVla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
|