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.
Files changed (96) hide show
  1. package/dist/api/sonamu.d.ts.map +1 -1
  2. package/dist/api/sonamu.js +2 -3
  3. package/dist/auth/auth-generator.d.ts +8 -0
  4. package/dist/auth/auth-generator.d.ts.map +1 -1
  5. package/dist/auth/auth-generator.js +33 -1
  6. package/dist/auth/better-auth-entities.d.ts.map +1 -1
  7. package/dist/auth/better-auth-entities.js +12 -2
  8. package/dist/bin/cli.js +18 -3
  9. package/dist/cone/cone-generator.js +10 -4
  10. package/dist/database/knex.d.ts.map +1 -1
  11. package/dist/database/knex.js +18 -2
  12. package/dist/database/puri.d.ts +9 -1
  13. package/dist/database/puri.d.ts.map +1 -1
  14. package/dist/database/puri.js +42 -1
  15. package/dist/database/puri.types.d.ts +2 -0
  16. package/dist/database/puri.types.d.ts.map +1 -1
  17. package/dist/database/puri.types.js +6 -2
  18. package/dist/entity/entity-manager.d.ts +149 -1
  19. package/dist/entity/entity-manager.d.ts.map +1 -1
  20. package/dist/entity/entity-manager.js +68 -4
  21. package/dist/migration/__tests__/code-generation.search-text.test.js +435 -0
  22. package/dist/migration/code-generation.d.ts.map +1 -1
  23. package/dist/migration/code-generation.js +696 -32
  24. package/dist/migration/migration-set.js +3 -1
  25. package/dist/migration/postgresql-schema-reader.d.ts +16 -2
  26. package/dist/migration/postgresql-schema-reader.d.ts.map +1 -1
  27. package/dist/migration/postgresql-schema-reader.js +281 -7
  28. package/dist/stream/sse.js +5 -3
  29. package/dist/template/__tests__/generated.template.search-text.test.js +99 -0
  30. package/dist/template/generated.template.test-d.js +24 -0
  31. package/dist/template/implementations/generated.template.d.ts.map +1 -1
  32. package/dist/template/implementations/generated.template.js +2 -2
  33. package/dist/template/implementations/init_types.template.d.ts.map +1 -1
  34. package/dist/template/implementations/init_types.template.js +11 -3
  35. package/dist/template/zod-converter.d.ts.map +1 -1
  36. package/dist/template/zod-converter.js +6 -2
  37. package/dist/testing/dev-test-routes.d.ts.map +1 -1
  38. package/dist/testing/dev-test-routes.js +5 -3
  39. package/dist/testing/fixture-generator.d.ts +13 -0
  40. package/dist/testing/fixture-generator.d.ts.map +1 -1
  41. package/dist/testing/fixture-generator.js +105 -8
  42. package/dist/testing/fixture-manager.d.ts.map +1 -1
  43. package/dist/testing/fixture-manager.js +19 -2
  44. package/dist/types/__tests__/entity-json-schema-search-text.test.js +256 -0
  45. package/dist/types/types.d.ts +494 -1
  46. package/dist/types/types.d.ts.map +1 -1
  47. package/dist/types/types.js +117 -13
  48. package/dist/ui/api.d.ts.map +1 -1
  49. package/dist/ui/api.js +14 -2
  50. package/dist/ui/cdd-service.d.ts +16 -14
  51. package/dist/ui/cdd-service.d.ts.map +1 -1
  52. package/dist/ui/cdd-service.js +145 -37
  53. package/dist/ui/cdd-types.d.ts +60 -0
  54. package/dist/ui/cdd-types.d.ts.map +1 -0
  55. package/dist/ui/cdd-types.js +3 -0
  56. package/dist/ui-web/assets/index-D4XFBV-f.css +1 -0
  57. package/dist/ui-web/assets/{index-CQ_S40bD.js → index-D_19-Pi4.js} +87 -87
  58. package/dist/ui-web/index.html +2 -2
  59. package/package.json +8 -4
  60. package/src/api/sonamu.ts +1 -2
  61. package/src/auth/auth-generator.ts +38 -0
  62. package/src/auth/better-auth-entities.ts +18 -1
  63. package/src/bin/cli.ts +15 -1
  64. package/src/cone/cone-generator.ts +9 -3
  65. package/src/database/knex.ts +26 -4
  66. package/src/database/puri.ts +71 -0
  67. package/src/database/puri.types.ts +2 -0
  68. package/src/entity/entity-manager.ts +95 -3
  69. package/src/migration/__tests__/code-generation.search-text.test.ts +390 -0
  70. package/src/migration/code-generation.ts +848 -34
  71. package/src/migration/migration-set.ts +2 -0
  72. package/src/migration/postgresql-schema-reader.ts +366 -9
  73. package/src/skills/sonamu/auth-migration.md +80 -0
  74. package/src/skills/sonamu/cdd.md +148 -28
  75. package/src/skills/sonamu/cone.md +16 -0
  76. package/src/skills/sonamu/entity-relations.md +1 -1
  77. package/src/skills/sonamu/fixture-cli.md +4 -0
  78. package/src/skills/sonamu/frontend.md +65 -0
  79. package/src/skills/sonamu/migration.md +3 -1
  80. package/src/skills/sonamu/model.md +28 -0
  81. package/src/skills/sonamu/workflow.md +12 -5
  82. package/src/stream/sse.ts +4 -2
  83. package/src/template/__tests__/generated.template.search-text.test.ts +89 -0
  84. package/src/template/generated.template.test-d.ts +46 -0
  85. package/src/template/implementations/generated.template.ts +4 -1
  86. package/src/template/implementations/init_types.template.ts +20 -5
  87. package/src/template/zod-converter.ts +5 -0
  88. package/src/testing/dev-test-routes.ts +4 -2
  89. package/src/testing/fixture-generator.ts +157 -9
  90. package/src/testing/fixture-manager.ts +15 -1
  91. package/src/types/__tests__/entity-json-schema-search-text.test.ts +179 -0
  92. package/src/types/types.ts +168 -12
  93. package/src/ui/api.ts +24 -1
  94. package/src/ui/cdd-service.ts +195 -55
  95. package/src/ui/cdd-types.ts +73 -0
  96. 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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