@checkdigit/eslint-plugin 7.17.1 → 7.18.0-PR.143-9946
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-mjs/athena/api-locator.mjs +30 -0
- package/dist-mjs/athena/api-matcher.mjs +108 -0
- package/dist-mjs/athena/athena.mjs +328 -0
- package/dist-mjs/athena/column.mjs +1 -0
- package/dist-mjs/athena/context.mjs +21 -0
- package/dist-mjs/athena/index.mjs +1 -0
- package/dist-mjs/athena/service-table.mjs +32 -0
- package/dist-mjs/athena/types.mjs +1 -0
- package/dist-mjs/athena/visitor.mjs +258 -0
- package/dist-mjs/index.mjs +8 -4
- package/dist-mjs/no-status-code-assert.mjs +1 -1
- package/dist-mjs/openapi/deref-schema.mjs +14 -0
- package/dist-mjs/openapi/generate-schema.mjs +273 -0
- package/dist-mjs/openapi/service-schema-generator.mjs +147 -0
- package/dist-mjs/peggy/athena-peggy.mjs +20629 -0
- package/dist-types/athena/api-locator.d.ts +2 -0
- package/dist-types/athena/api-matcher.d.ts +14 -0
- package/dist-types/athena/athena.d.ts +6 -0
- package/dist-types/athena/column.d.ts +1 -0
- package/dist-types/athena/context.d.ts +21 -0
- package/dist-types/athena/index.d.ts +8 -0
- package/dist-types/athena/service-table.d.ts +8 -0
- package/dist-types/athena/types.d.ts +474 -0
- package/dist-types/athena/visitor.d.ts +63 -0
- package/dist-types/no-status-code-assert.d.ts +1 -1
- package/dist-types/openapi/deref-schema.d.ts +1 -0
- package/dist-types/openapi/generate-schema.d.ts +33 -0
- package/dist-types/openapi/service-schema-generator.d.ts +5 -0
- package/dist-types/peggy/athena-peggy.d.ts +13 -0
- package/package.json +1 -96
- package/src/athena/ATHENA.md +387 -0
- package/src/athena/PLAN.md +355 -0
- package/src/athena/api-locator.ts +39 -0
- package/src/athena/api-matcher.ts +169 -0
- package/src/athena/athena.ts +488 -0
- package/src/athena/column.ts +2 -0
- package/src/athena/context.ts +47 -0
- package/src/athena/index.ts +11 -0
- package/src/athena/service-table.ts +55 -0
- package/src/athena/types.ts +526 -0
- package/src/athena/visitor.ts +365 -0
- package/src/index.ts +4 -0
- package/src/no-side-effects.ts +1 -1
- package/src/no-status-code-assert.ts +2 -2
- package/src/openapi/deref-schema.ts +14 -0
- package/src/openapi/generate-schema.ts +422 -0
- package/src/openapi/service-schema-generator.ts +189 -0
- package/src/peggy/athena-chat.peggy +608 -0
- package/src/peggy/athena-peggy.ts +22078 -0
- package/src/peggy/athena.peggy +2967 -0
- package/src/require-service-call-response-declaration.ts +2 -2
- package/src/services/interchange/v1/swagger.schema.deref.json +849 -0
- package/src/services/interchange/v1/swagger.schema.json +473 -0
- package/src/services/interchange/v1/swagger.yml +414 -0
- package/src/services/ledger/v1/swagger.schema.deref.json +6694 -0
- package/src/services/ledger/v1/swagger.schema.json +1820 -0
- package/src/services/ledger/v1/swagger.yml +1094 -0
- package/src/services/link/v1/swagger.schema.deref.json +648 -0
- package/src/services/link/v1/swagger.schema.json +444 -0
- package/src/services/link/v1/swagger.yml +343 -0
- package/src/services/message/v1/swagger.schema.deref.json +22049 -0
- package/src/services/message/v1/swagger.schema.json +3470 -0
- package/src/services/message/v1/swagger.yml +2798 -0
- package/src/services/message/v2/swagger.schema.deref.json +72221 -0
- package/src/services/message/v2/swagger.schema.json +3558 -0
- package/src/services/message/v2/swagger.yml +3009 -0
- package/src/services/paymentCard/v1/swagger.schema.deref.json +4346 -0
- package/src/services/paymentCard/v1/swagger.schema.json +2181 -0
- package/src/services/paymentCard/v1/swagger.yml +1161 -0
- package/src/services/paymentCard/v2/swagger.schema.deref.json +4336 -0
- package/src/services/paymentCard/v2/swagger.schema.json +2155 -0
- package/src/services/paymentCard/v2/swagger.yml +1149 -0
- package/src/services/person/v1/swagger.schema.deref.json +6786 -0
- package/src/services/person/v1/swagger.schema.json +1445 -0
- package/src/services/person/v1/swagger.yml +1157 -0
- package/src/services/teampayApproval/v1/swagger.schema.deref.json +9898 -0
- package/src/services/teampayCardManagement/v1/swagger.schema.deref.json +6187 -0
- package/src/services/teampayClientManagement/v1/swagger.schema.deref.json +4914 -0
- package/src/services/teampayClientManagement/v1/swagger.schema.json +1964 -0
- package/src/services/teampayClientManagement/v1/swagger.yml +1376 -0
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
// src/openapi/service-schema-generator.ts
|
|
2
|
+
import { execFileSync } from "node:child_process";
|
|
3
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
4
|
+
import debug from "debug";
|
|
5
|
+
import { buildApiSchemaFromYaml } from "./generate-schema.mjs";
|
|
6
|
+
var log = debug("eslint-plugin:athena:service-schema-generator");
|
|
7
|
+
var GITHUB_ORGANIZATIONS = ["checkdigit", "rebolt-checkdigit"];
|
|
8
|
+
var SWAGGER_SCHEMA_DEREF_FILENAME = "swagger.schema.deref.json";
|
|
9
|
+
function derefApiSchemas(schemas) {
|
|
10
|
+
const definitions = schemas.definitions ?? {};
|
|
11
|
+
function derefValue(value, resolving = /* @__PURE__ */ new Set()) {
|
|
12
|
+
if (value === null || typeof value !== "object") {
|
|
13
|
+
return value;
|
|
14
|
+
}
|
|
15
|
+
if (Array.isArray(value)) {
|
|
16
|
+
return value.map((item) => derefValue(item, resolving));
|
|
17
|
+
}
|
|
18
|
+
const obj = value;
|
|
19
|
+
if (typeof obj["$ref"] === "string") {
|
|
20
|
+
const refName = /\/definitions\/(?<name>[^/]+)$/u.exec(obj["$ref"])?.groups?.["name"];
|
|
21
|
+
if (refName !== void 0 && !resolving.has(refName)) {
|
|
22
|
+
return derefValue(definitions[refName], /* @__PURE__ */ new Set([...resolving, refName]));
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return Object.fromEntries(Object.entries(obj).map(([key, val]) => [key, derefValue(val, resolving)]));
|
|
26
|
+
}
|
|
27
|
+
const resolvedApis = derefValue(schemas.apis);
|
|
28
|
+
if (schemas.definitions !== void 0) {
|
|
29
|
+
return {
|
|
30
|
+
apis: resolvedApis,
|
|
31
|
+
definitions: derefValue(schemas.definitions)
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
return { apis: resolvedApis };
|
|
35
|
+
}
|
|
36
|
+
function fetchUrlSync(url) {
|
|
37
|
+
try {
|
|
38
|
+
return execFileSync(process.execPath, ["--input-type=module"], {
|
|
39
|
+
input: `const r=await fetch(${JSON.stringify(url)});if(!r.ok)process.exit(1);process.stdout.write(await r.text());`,
|
|
40
|
+
encoding: "utf-8",
|
|
41
|
+
timeout: 15e3
|
|
42
|
+
});
|
|
43
|
+
} catch {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
function readServiceConfig(packageJsonContent, org, serviceName) {
|
|
48
|
+
const packageJson = JSON.parse(packageJsonContent);
|
|
49
|
+
const apiRoot = packageJson.service?.api?.root;
|
|
50
|
+
const apiEndpoints = packageJson.service?.api?.endpoints;
|
|
51
|
+
if (apiRoot === void 0 || apiEndpoints === void 0 || apiEndpoints.length === 0) {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
const pkgOrg = packageJson.name?.slice(1).split("/")[0] ?? org;
|
|
55
|
+
const pkgServiceName = packageJson.name?.slice(1).split("/")[1] ?? serviceName;
|
|
56
|
+
return { organization: pkgOrg, serviceName: pkgServiceName, apiRoot, endpoints: apiEndpoints };
|
|
57
|
+
}
|
|
58
|
+
function findServiceInNodeModules(serviceName) {
|
|
59
|
+
for (const org of GITHUB_ORGANIZATIONS) {
|
|
60
|
+
const packageDir = `node_modules/@${org}/${serviceName}`;
|
|
61
|
+
const packageJsonPath = `${packageDir}/package.json`;
|
|
62
|
+
if (!existsSync(packageJsonPath)) {
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
try {
|
|
66
|
+
const config = readServiceConfig(readFileSync(packageJsonPath, "utf-8"), org, serviceName);
|
|
67
|
+
if (config === null) {
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
const endpoints = [];
|
|
71
|
+
for (const endpoint of config.endpoints) {
|
|
72
|
+
const swaggerPath = `${packageDir}/${config.apiRoot}/${endpoint}/swagger.yml`;
|
|
73
|
+
if (existsSync(swaggerPath)) {
|
|
74
|
+
endpoints.push({ path: endpoint, yamlContent: readFileSync(swaggerPath, "utf-8") });
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
if (endpoints.length > 0) {
|
|
78
|
+
return { organization: config.organization, serviceName: config.serviceName, endpoints };
|
|
79
|
+
}
|
|
80
|
+
} catch {
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
function findServiceOnGitHub(serviceName) {
|
|
86
|
+
for (const org of GITHUB_ORGANIZATIONS) {
|
|
87
|
+
const pkgUrl = `https://raw.githubusercontent.com/${org}/${serviceName}/main/package.json`;
|
|
88
|
+
log(`fetching package.json from ${pkgUrl}`);
|
|
89
|
+
const pkgContent = fetchUrlSync(pkgUrl);
|
|
90
|
+
if (pkgContent === null) {
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
try {
|
|
94
|
+
const config = readServiceConfig(pkgContent, org, serviceName);
|
|
95
|
+
if (config === null) {
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
const endpoints = [];
|
|
99
|
+
for (const endpoint of config.endpoints) {
|
|
100
|
+
const swaggerUrl = `https://raw.githubusercontent.com/${org}/${serviceName}/main/${config.apiRoot}/${endpoint}/swagger.yml`;
|
|
101
|
+
log(`fetching swagger.yml from ${swaggerUrl}`);
|
|
102
|
+
const yamlContent = fetchUrlSync(swaggerUrl);
|
|
103
|
+
if (yamlContent !== null) {
|
|
104
|
+
endpoints.push({ path: endpoint, yamlContent });
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
if (endpoints.length > 0) {
|
|
108
|
+
return { organization: config.organization, serviceName: config.serviceName, endpoints };
|
|
109
|
+
}
|
|
110
|
+
} catch {
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
function generateSchemasForService(serviceName, outputDir) {
|
|
116
|
+
log("generating schemas for service", serviceName);
|
|
117
|
+
const source = findServiceInNodeModules(serviceName) ?? findServiceOnGitHub(serviceName);
|
|
118
|
+
if (source === null) {
|
|
119
|
+
log("no swagger source found for service", serviceName);
|
|
120
|
+
return [];
|
|
121
|
+
}
|
|
122
|
+
const results = [];
|
|
123
|
+
for (const { path: endpoint, yamlContent } of source.endpoints) {
|
|
124
|
+
try {
|
|
125
|
+
const rawSchema = buildApiSchemaFromYaml(yamlContent, source.organization, source.serviceName);
|
|
126
|
+
if (rawSchema === null) {
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
const schema = derefApiSchemas(rawSchema);
|
|
130
|
+
results.push({ schema, endpoint });
|
|
131
|
+
if (outputDir !== void 0) {
|
|
132
|
+
const versionFolder = endpoint.split("/").at(-1) ?? endpoint;
|
|
133
|
+
const dir = `${outputDir}/${versionFolder}`;
|
|
134
|
+
mkdirSync(dir, { recursive: true });
|
|
135
|
+
writeFileSync(`${dir}/${SWAGGER_SCHEMA_DEREF_FILENAME}`, JSON.stringify(schema, void 0, 2));
|
|
136
|
+
log(`cached schema to ${dir}/${SWAGGER_SCHEMA_DEREF_FILENAME}`);
|
|
137
|
+
}
|
|
138
|
+
} catch (error) {
|
|
139
|
+
log("error generating schema for endpoint", endpoint, error);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return results;
|
|
143
|
+
}
|
|
144
|
+
export {
|
|
145
|
+
generateSchemasForService
|
|
146
|
+
};
|
|
147
|
+
//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsiLi4vLi4vc3JjL29wZW5hcGkvc2VydmljZS1zY2hlbWEtZ2VuZXJhdG9yLnRzIl0sCiAgIm1hcHBpbmdzIjogIjtBQUVBLFNBQVMsb0JBQW9CO0FBQzdCLFNBQVMsWUFBWSxXQUFXLGNBQWMscUJBQXFCO0FBRW5FLE9BQU8sV0FBVztBQUVsQixTQUEwQiw4QkFBOEI7QUFFeEQsSUFBTSxNQUFNLE1BQU0sK0NBQStDO0FBRWpFLElBQU0sdUJBQXVCLENBQUMsY0FBYyxtQkFBbUI7QUFDL0QsSUFBTSxnQ0FBZ0M7QUFhdEMsU0FBUyxnQkFBZ0IsU0FBaUM7QUFDeEQsUUFBTSxjQUFjLFFBQVEsZUFBZSxDQUFDO0FBRTVDLFdBQVMsV0FBVyxPQUFnQixZQUF5QixvQkFBSSxJQUFZLEdBQVk7QUFDdkYsUUFBSSxVQUFVLFFBQVEsT0FBTyxVQUFVLFVBQVU7QUFDL0MsYUFBTztBQUFBLElBQ1Q7QUFDQSxRQUFJLE1BQU0sUUFBUSxLQUFLLEdBQUc7QUFDeEIsYUFBTyxNQUFNLElBQUksQ0FBQyxTQUFTLFdBQVcsTUFBTSxTQUFTLENBQUM7QUFBQSxJQUN4RDtBQUNBLFVBQU0sTUFBTTtBQUNaLFFBQUksT0FBTyxJQUFJLE1BQU0sTUFBTSxVQUFVO0FBQ25DLFlBQU0sVUFBVSxrQ0FBa0MsS0FBSyxJQUFJLE1BQU0sQ0FBQyxHQUFHLFNBQVMsTUFBTTtBQUNwRixVQUFJLFlBQVksVUFBYSxDQUFDLFVBQVUsSUFBSSxPQUFPLEdBQUc7QUFDcEQsZUFBTyxXQUFXLFlBQVksT0FBTyxHQUFHLG9CQUFJLElBQVksQ0FBQyxHQUFHLFdBQVcsT0FBTyxDQUFDLENBQUM7QUFBQSxNQUNsRjtBQUFBLElBQ0Y7QUFDQSxXQUFPLE9BQU8sWUFBWSxPQUFPLFFBQVEsR0FBRyxFQUFFLElBQUksQ0FBQyxDQUFDLEtBQUssR0FBRyxNQUFNLENBQUMsS0FBSyxXQUFXLEtBQUssU0FBUyxDQUFDLENBQUMsQ0FBQztBQUFBLEVBQ3RHO0FBRUEsUUFBTSxlQUFlLFdBQVcsUUFBUSxJQUFJO0FBQzVDLE1BQUksUUFBUSxnQkFBZ0IsUUFBVztBQUNyQyxXQUFPO0FBQUEsTUFDTCxNQUFNO0FBQUEsTUFDTixhQUFhLFdBQVcsUUFBUSxXQUFXO0FBQUEsSUFDN0M7QUFBQSxFQUNGO0FBQ0EsU0FBTyxFQUFFLE1BQU0sYUFBYTtBQUM5QjtBQUVBLFNBQVMsYUFBYSxLQUE0QjtBQUNoRCxNQUFJO0FBQ0YsV0FBTyxhQUFhLFFBQVEsVUFBVSxDQUFDLHFCQUFxQixHQUFHO0FBQUEsTUFDN0QsT0FBTyx1QkFBdUIsS0FBSyxVQUFVLEdBQUcsQ0FBQztBQUFBLE1BQ2pELFVBQVU7QUFBQSxNQUNWLFNBQVM7QUFBQSxJQUNYLENBQUM7QUFBQSxFQUNILFFBQVE7QUFDTixXQUFPO0FBQUEsRUFDVDtBQUNGO0FBRUEsU0FBUyxrQkFDUCxvQkFDQSxLQUNBLGFBQzRGO0FBQzVGLFFBQU0sY0FBYyxLQUFLLE1BQU0sa0JBQWtCO0FBSWpELFFBQU0sVUFBVSxZQUFZLFNBQVMsS0FBSztBQUMxQyxRQUFNLGVBQWUsWUFBWSxTQUFTLEtBQUs7QUFDL0MsTUFBSSxZQUFZLFVBQWEsaUJBQWlCLFVBQWEsYUFBYSxXQUFXLEdBQUc7QUFDcEYsV0FBTztBQUFBLEVBQ1Q7QUFDQSxRQUFNLFNBQVMsWUFBWSxNQUFNLE1BQU0sQ0FBQyxFQUFFLE1BQU0sR0FBRyxFQUFFLENBQUMsS0FBSztBQUMzRCxRQUFNLGlCQUFpQixZQUFZLE1BQU0sTUFBTSxDQUFDLEVBQUUsTUFBTSxHQUFHLEVBQUUsQ0FBQyxLQUFLO0FBQ25FLFNBQU8sRUFBRSxjQUFjLFFBQVEsYUFBYSxnQkFBZ0IsU0FBUyxXQUFXLGFBQWE7QUFDL0Y7QUFFQSxTQUFTLHlCQUF5QixhQUEyQztBQUMzRSxhQUFXLE9BQU8sc0JBQXNCO0FBQ3RDLFVBQU0sYUFBYSxpQkFBaUIsR0FBRyxJQUFJLFdBQVc7QUFDdEQsVUFBTSxrQkFBa0IsR0FBRyxVQUFVO0FBQ3JDLFFBQUksQ0FBQyxXQUFXLGVBQWUsR0FBRztBQUNoQztBQUFBLElBQ0Y7QUFFQSxRQUFJO0FBQ0YsWUFBTSxTQUFTLGtCQUFrQixhQUFhLGlCQUFpQixPQUFPLEdBQUcsS0FBSyxXQUFXO0FBQ3pGLFVBQUksV0FBVyxNQUFNO0FBQ25CO0FBQUEsTUFDRjtBQUVBLFlBQU0sWUFBK0IsQ0FBQztBQUN0QyxpQkFBVyxZQUFZLE9BQU8sV0FBVztBQUN2QyxjQUFNLGNBQWMsR0FBRyxVQUFVLElBQUksT0FBTyxPQUFPLElBQUksUUFBUTtBQUMvRCxZQUFJLFdBQVcsV0FBVyxHQUFHO0FBQzNCLG9CQUFVLEtBQUssRUFBRSxNQUFNLFVBQVUsYUFBYSxhQUFhLGFBQWEsT0FBTyxFQUFFLENBQUM7QUFBQSxRQUNwRjtBQUFBLE1BQ0Y7QUFFQSxVQUFJLFVBQVUsU0FBUyxHQUFHO0FBQ3hCLGVBQU8sRUFBRSxjQUFjLE9BQU8sY0FBYyxhQUFhLE9BQU8sYUFBYSxVQUFVO0FBQUEsTUFDekY7QUFBQSxJQUNGLFFBQVE7QUFBQSxJQUVSO0FBQUEsRUFDRjtBQUNBLFNBQU87QUFDVDtBQUVBLFNBQVMsb0JBQW9CLGFBQTJDO0FBQ3RFLGFBQVcsT0FBTyxzQkFBc0I7QUFDdEMsVUFBTSxTQUFTLHFDQUFxQyxHQUFHLElBQUksV0FBVztBQUN0RSxRQUFJLDhCQUE4QixNQUFNLEVBQUU7QUFDMUMsVUFBTSxhQUFhLGFBQWEsTUFBTTtBQUN0QyxRQUFJLGVBQWUsTUFBTTtBQUN2QjtBQUFBLElBQ0Y7QUFFQSxRQUFJO0FBQ0YsWUFBTSxTQUFTLGtCQUFrQixZQUFZLEtBQUssV0FBVztBQUM3RCxVQUFJLFdBQVcsTUFBTTtBQUNuQjtBQUFBLE1BQ0Y7QUFFQSxZQUFNLFlBQStCLENBQUM7QUFDdEMsaUJBQVcsWUFBWSxPQUFPLFdBQVc7QUFDdkMsY0FBTSxhQUFhLHFDQUFxQyxHQUFHLElBQUksV0FBVyxTQUFTLE9BQU8sT0FBTyxJQUFJLFFBQVE7QUFDN0csWUFBSSw2QkFBNkIsVUFBVSxFQUFFO0FBQzdDLGNBQU0sY0FBYyxhQUFhLFVBQVU7QUFDM0MsWUFBSSxnQkFBZ0IsTUFBTTtBQUN4QixvQkFBVSxLQUFLLEVBQUUsTUFBTSxVQUFVLFlBQVksQ0FBQztBQUFBLFFBQ2hEO0FBQUEsTUFDRjtBQUVBLFVBQUksVUFBVSxTQUFTLEdBQUc7QUFDeEIsZUFBTyxFQUFFLGNBQWMsT0FBTyxjQUFjLGFBQWEsT0FBTyxhQUFhLFVBQVU7QUFBQSxNQUN6RjtBQUFBLElBQ0YsUUFBUTtBQUFBLElBRVI7QUFBQSxFQUNGO0FBQ0EsU0FBTztBQUNUO0FBRU8sU0FBUywwQkFDZCxhQUNBLFdBQzRDO0FBQzVDLE1BQUksa0NBQWtDLFdBQVc7QUFFakQsUUFBTSxTQUFTLHlCQUF5QixXQUFXLEtBQUssb0JBQW9CLFdBQVc7QUFDdkYsTUFBSSxXQUFXLE1BQU07QUFDbkIsUUFBSSx1Q0FBdUMsV0FBVztBQUN0RCxXQUFPLENBQUM7QUFBQSxFQUNWO0FBRUEsUUFBTSxVQUFzRCxDQUFDO0FBQzdELGFBQVcsRUFBRSxNQUFNLFVBQVUsWUFBWSxLQUFLLE9BQU8sV0FBVztBQUM5RCxRQUFJO0FBQ0YsWUFBTSxZQUFZLHVCQUF1QixhQUFhLE9BQU8sY0FBYyxPQUFPLFdBQVc7QUFDN0YsVUFBSSxjQUFjLE1BQU07QUFDdEI7QUFBQSxNQUNGO0FBQ0EsWUFBTSxTQUFTLGdCQUFnQixTQUFTO0FBQ3hDLGNBQVEsS0FBSyxFQUFFLFFBQVEsU0FBUyxDQUFDO0FBRWpDLFVBQUksY0FBYyxRQUFXO0FBQzNCLGNBQU0sZ0JBQWdCLFNBQVMsTUFBTSxHQUFHLEVBQUUsR0FBRyxFQUFFLEtBQUs7QUFDcEQsY0FBTSxNQUFNLEdBQUcsU0FBUyxJQUFJLGFBQWE7QUFDekMsa0JBQVUsS0FBSyxFQUFFLFdBQVcsS0FBSyxDQUFDO0FBQ2xDLHNCQUFjLEdBQUcsR0FBRyxJQUFJLDZCQUE2QixJQUFJLEtBQUssVUFBVSxRQUFRLFFBQVcsQ0FBQyxDQUFDO0FBQzdGLFlBQUksb0JBQW9CLEdBQUcsSUFBSSw2QkFBNkIsRUFBRTtBQUFBLE1BQ2hFO0FBQUEsSUFDRixTQUFTLE9BQU87QUFDZCxVQUFJLHdDQUF3QyxVQUFVLEtBQUs7QUFBQSxJQUM3RDtBQUFBLEVBQ0Y7QUFFQSxTQUFPO0FBQ1Q7IiwKICAibmFtZXMiOiBbXQp9Cg==
|