node-resource-utils 0.0.0-stage → 1.1.7
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/LICENSE +21 -0
- package/README.md +35 -2
- package/lib/sync.js +121 -0
- package/package.json +14 -4
- package/resources/.cache/ref.b64 +1 -0
- package/resources/README.md +97 -0
- package/resources/_.env +6 -0
- package/resources/_.env.example +25 -0
- package/resources/_.gitignore +18 -0
- package/resources/_.lintstagedrc.json +9 -0
- package/resources/_.npmrc +5 -0
- package/resources/_.prettierrc +7 -0
- package/resources/commitlint.config.mjs +1 -0
- package/resources/content/docs/README.md +63 -0
- package/resources/content/docs/faq.md +107 -0
- package/resources/content/docs/features/_meta.json +5 -0
- package/resources/content/docs/features/ai.md +71 -0
- package/resources/content/docs/features/appearance.md +47 -0
- package/resources/content/docs/features/feedback.md +79 -0
- package/resources/content/docs/features/files.md +74 -0
- package/resources/content/docs/features/graph.md +66 -0
- package/resources/content/docs/features/inbox.md +61 -0
- package/resources/content/docs/guide/_meta.json +5 -0
- package/resources/content/docs/guide/account.md +76 -0
- package/resources/content/docs/guide/data.md +87 -0
- package/resources/content/docs/guide/navigation.md +81 -0
- package/resources/content/docs/guide/permissions.md +67 -0
- package/resources/content/docs/guide/system.md +92 -0
- package/resources/content/docs/quick-start.md +70 -0
- package/resources/content/docs/shortcuts.md +69 -0
- package/resources/eslint.config.mjs +17 -0
- package/resources/next-env.d.ts +6 -0
- package/resources/next.config.ts +13 -0
- package/resources/postcss.config.mjs +7 -0
- package/resources/prisma/org.config.ts +98 -0
- package/resources/prisma/schema.prisma +336 -0
- package/resources/prisma/seed.ts +721 -0
- package/resources/prisma/seedSafe.ts +347 -0
- package/resources/prisma.config.ts +14 -0
- package/resources/scripts/check-config.mjs +773 -0
- package/resources/scripts/lib/schema-graph.mjs +410 -0
- package/resources/scripts/patch-next-trace.mjs +86 -0
- package/resources/scripts/suggest-relations.mjs +105 -0
- package/resources/src/app/(admin)/ai/page.tsx +469 -0
- package/resources/src/app/(admin)/dashboard/page.tsx +260 -0
- package/resources/src/app/(admin)/demo/employees/page.tsx +508 -0
- package/resources/src/app/(admin)/demo/files/page.tsx +451 -0
- package/resources/src/app/(admin)/demo/loading/page.tsx +513 -0
- package/resources/src/app/(admin)/error.tsx +96 -0
- package/resources/src/app/(admin)/feedback/page.tsx +573 -0
- package/resources/src/app/(admin)/graph/page.tsx +1224 -0
- package/resources/src/app/(admin)/inbox/page.tsx +31 -0
- package/resources/src/app/(admin)/layout.tsx +58 -0
- package/resources/src/app/(admin)/loading.tsx +31 -0
- package/resources/src/app/(admin)/system/depts/page.tsx +415 -0
- package/resources/src/app/(admin)/system/logs/page.tsx +215 -0
- package/resources/src/app/(admin)/system/menus/page.tsx +403 -0
- package/resources/src/app/(admin)/system/roles/page.tsx +433 -0
- package/resources/src/app/(admin)/system/users/page.tsx +620 -0
- package/resources/src/app/api/account-recovery/route.ts +132 -0
- package/resources/src/app/api/ai/act/route.ts +70 -0
- package/resources/src/app/api/ai/ask/route.ts +82 -0
- package/resources/src/app/api/ai/chat/route.ts +404 -0
- package/resources/src/app/api/ai/report/route.ts +164 -0
- package/resources/src/app/api/auth/[...nextauth]/route.ts +3 -0
- package/resources/src/app/api/auth/change-password/route.ts +57 -0
- package/resources/src/app/api/auth/forgot/route.ts +98 -0
- package/resources/src/app/api/auth/register/route.ts +95 -0
- package/resources/src/app/api/auth/register-config/route.ts +51 -0
- package/resources/src/app/api/badges/route.ts +39 -0
- package/resources/src/app/api/depts/[id]/route.ts +145 -0
- package/resources/src/app/api/depts/route.ts +126 -0
- package/resources/src/app/api/docs/export/route.ts +87 -0
- package/resources/src/app/api/docs/parse/route.ts +57 -0
- package/resources/src/app/api/docs/search/route.ts +22 -0
- package/resources/src/app/api/employees/[id]/route.ts +147 -0
- package/resources/src/app/api/employees/export/route.ts +115 -0
- package/resources/src/app/api/employees/import/route.ts +205 -0
- package/resources/src/app/api/employees/route.ts +149 -0
- package/resources/src/app/api/export/route.ts +254 -0
- package/resources/src/app/api/favorites/route.ts +56 -0
- package/resources/src/app/api/feedback/ai/route.ts +31 -0
- package/resources/src/app/api/feedback/approve/route.ts +67 -0
- package/resources/src/app/api/feedback/report/route.ts +213 -0
- package/resources/src/app/api/feedback/route.ts +246 -0
- package/resources/src/app/api/files/[name]/route.ts +82 -0
- package/resources/src/app/api/graph/focus/route.ts +75 -0
- package/resources/src/app/api/graph/node/move/route.ts +48 -0
- package/resources/src/app/api/graph/node/route.ts +274 -0
- package/resources/src/app/api/graph/route.ts +272 -0
- package/resources/src/app/api/health/route.ts +11 -0
- package/resources/src/app/api/inbox/read/route.ts +36 -0
- package/resources/src/app/api/inbox/summary/route.ts +15 -0
- package/resources/src/app/api/logs/route.ts +82 -0
- package/resources/src/app/api/menus/[id]/route.ts +132 -0
- package/resources/src/app/api/menus/mine/route.ts +23 -0
- package/resources/src/app/api/menus/route.ts +89 -0
- package/resources/src/app/api/roles/[id]/menus/route.ts +73 -0
- package/resources/src/app/api/roles/[id]/route.ts +96 -0
- package/resources/src/app/api/roles/route.ts +114 -0
- package/resources/src/app/api/search/route.ts +22 -0
- package/resources/src/app/api/stats/route.ts +92 -0
- package/resources/src/app/api/upload/route.ts +38 -0
- package/resources/src/app/api/users/[id]/password/route.ts +55 -0
- package/resources/src/app/api/users/[id]/route.ts +145 -0
- package/resources/src/app/api/users/route.ts +153 -0
- package/resources/src/app/api/visits/route.ts +48 -0
- package/resources/src/app/change-password/ChangePasswordForm.tsx +174 -0
- package/resources/src/app/change-password/page.tsx +19 -0
- package/resources/src/app/docs/[[...slug]]/page.tsx +56 -0
- package/resources/src/app/docs/layout.tsx +34 -0
- package/resources/src/app/docs/loading.tsx +19 -0
- package/resources/src/app/docs/not-found.tsx +25 -0
- package/resources/src/app/forgot/page.tsx +392 -0
- package/resources/src/app/global-error.tsx +75 -0
- package/resources/src/app/globals.css +235 -0
- package/resources/src/app/layout.tsx +44 -0
- package/resources/src/app/login/page.tsx +267 -0
- package/resources/src/app/page.tsx +14 -0
- package/resources/src/app/register/page.tsx +237 -0
- package/resources/src/auth.ts +215 -0
- package/resources/src/components/AiPanel.tsx +970 -0
- package/resources/src/components/AuthContext.tsx +82 -0
- package/resources/src/components/DataTable.tsx +459 -0
- package/resources/src/components/EChart.tsx +140 -0
- package/resources/src/components/FeedbackAiModal.tsx +476 -0
- package/resources/src/components/FeedbackReportModal.tsx +479 -0
- package/resources/src/components/Markdown.tsx +532 -0
- package/resources/src/components/OntologyGraph.tsx +1196 -0
- package/resources/src/components/Providers.tsx +99 -0
- package/resources/src/components/VisitTracker.tsx +89 -0
- package/resources/src/components/admin/AdminShell.tsx +749 -0
- package/resources/src/components/admin/CountBadge.tsx +67 -0
- package/resources/src/components/admin/GlobalSearch.tsx +509 -0
- package/resources/src/components/admin/InboxBell.tsx +78 -0
- package/resources/src/components/admin/VisitStats.tsx +145 -0
- package/resources/src/components/admin/menu-icons.tsx +56 -0
- package/resources/src/components/ai/AiInputForm.tsx +351 -0
- package/resources/src/components/appearance/AppearanceDrawer.tsx +268 -0
- package/resources/src/components/appearance/AppearanceProvider.tsx +160 -0
- package/resources/src/components/docs/DocArticle.tsx +106 -0
- package/resources/src/components/docs/DocToc.tsx +100 -0
- package/resources/src/components/docs/DocsChrome.tsx +259 -0
- package/resources/src/components/docs/DocsHome.tsx +209 -0
- package/resources/src/components/docs/DocsSearch.tsx +365 -0
- package/resources/src/components/docs/docs.css +1458 -0
- package/resources/src/components/feedback/AsyncButton.tsx +108 -0
- package/resources/src/components/feedback/LoadingOverlay.tsx +125 -0
- package/resources/src/components/feedback/PressableCard.tsx +92 -0
- package/resources/src/components/feedback/README.md +97 -0
- package/resources/src/components/feedback/RouteProgress.tsx +108 -0
- package/resources/src/components/feedback/Skeleton.tsx +249 -0
- package/resources/src/components/feedback/StatCard.tsx +70 -0
- package/resources/src/components/feedback/TaskHint.tsx +153 -0
- package/resources/src/components/feedback/index.ts +93 -0
- package/resources/src/components/feedback/motion.css +204 -0
- package/resources/src/components/feedback/motion.ts +63 -0
- package/resources/src/components/feedback/useCountUp.ts +110 -0
- package/resources/src/components/feedback/usePending.ts +209 -0
- package/resources/src/components/feedback/useRowFlash.ts +40 -0
- package/resources/src/components/inbox/InboxView.tsx +409 -0
- package/resources/src/components/ui/button.tsx +35 -0
- package/resources/src/components/ui/card.tsx +37 -0
- package/resources/src/components/ui/input.tsx +19 -0
- package/resources/src/lib/ai.ts +310 -0
- package/resources/src/lib/aiContext.ts +357 -0
- package/resources/src/lib/aiForm.test.ts +191 -0
- package/resources/src/lib/aiForm.ts +532 -0
- package/resources/src/lib/aiFormTypes.ts +83 -0
- package/resources/src/lib/aiToken.ts +72 -0
- package/resources/src/lib/aiTools.ts +823 -0
- package/resources/src/lib/api.ts +160 -0
- package/resources/src/lib/appInfo.ts +128 -0
- package/resources/src/lib/appearance.ts +643 -0
- package/resources/src/lib/badge-count.ts +146 -0
- package/resources/src/lib/badges.test.ts +58 -0
- package/resources/src/lib/badges.ts +79 -0
- package/resources/src/lib/brand-mark.test.ts +87 -0
- package/resources/src/lib/brand-mark.ts +45 -0
- package/resources/src/lib/docs-shared.test.ts +148 -0
- package/resources/src/lib/docs-shared.ts +422 -0
- package/resources/src/lib/docs.ts +381 -0
- package/resources/src/lib/err-text.ts +21 -0
- package/resources/src/lib/excel.ts +152 -0
- package/resources/src/lib/favorite-path.ts +60 -0
- package/resources/src/lib/favorites.ts +59 -0
- package/resources/src/lib/features.ts +69 -0
- package/resources/src/lib/feedback-access.ts +17 -0
- package/resources/src/lib/feedbackAi.ts +1621 -0
- package/resources/src/lib/formRules.ts +76 -0
- package/resources/src/lib/format.ts +52 -0
- package/resources/src/lib/graph-cascade.ts +304 -0
- package/resources/src/lib/graph-focus.ts +301 -0
- package/resources/src/lib/graph-link.ts +17 -0
- package/resources/src/lib/graph-node-write.ts +150 -0
- package/resources/src/lib/graph-schema.ts +475 -0
- package/resources/src/lib/graph-scope.test.ts +46 -0
- package/resources/src/lib/graph-scope.ts +28 -0
- package/resources/src/lib/http.ts +157 -0
- package/resources/src/lib/inbox-types.ts +57 -0
- package/resources/src/lib/inbox.ts +55 -0
- package/resources/src/lib/menu-service.ts +204 -0
- package/resources/src/lib/notifications.ts +113 -0
- package/resources/src/lib/office.ts +531 -0
- package/resources/src/lib/ontology.ts +277 -0
- package/resources/src/lib/openaiCompat.ts +257 -0
- package/resources/src/lib/pending.ts +98 -0
- package/resources/src/lib/prisma.ts +30 -0
- package/resources/src/lib/rbac.test.ts +106 -0
- package/resources/src/lib/rbac.ts +185 -0
- package/resources/src/lib/search-data.ts +158 -0
- package/resources/src/lib/security-questions.ts +20 -0
- package/resources/src/lib/skin-theme.ts +155 -0
- package/resources/src/lib/system-config.ts +55 -0
- package/resources/src/lib/theme.ts +96 -0
- package/resources/src/lib/todos/feedback.ts +86 -0
- package/resources/src/lib/todos/index.ts +73 -0
- package/resources/src/lib/todos/types.ts +33 -0
- package/resources/src/lib/upload.ts +106 -0
- package/resources/src/lib/utils.test.ts +9 -0
- package/resources/src/lib/utils.ts +6 -0
- package/resources/src/lib/validation.test.ts +297 -0
- package/resources/src/lib/validation.ts +658 -0
- package/resources/src/lib/visit-stats.ts +109 -0
- package/resources/src/lib/visits.ts +111 -0
- package/resources/src/lib/zodField.ts +44 -0
- package/resources/tsconfig.json +41 -0
- package/resources/vitest.config.ts +16 -0
- package/resources/vitest.setup.ts +1 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 biz-kit contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,36 @@
|
|
|
1
|
-
#
|
|
1
|
+
# node-resource-utils
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Utilities for synchronizing reusable resources in Node.js projects. The package has no runtime dependencies and does not run automatically during installation.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
Synchronize missing resources without overwriting existing files:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
node node_modules/node-resource-utils/lib/sync.js
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Check resource status:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
node node_modules/node-resource-utils/lib/sync.js status
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
The same operation is available through the CommonJS API:
|
|
20
|
+
|
|
21
|
+
```js
|
|
22
|
+
const { sync } = require('node-resource-utils');
|
|
23
|
+
|
|
24
|
+
sync(); // fill in missing files
|
|
25
|
+
sync(['status']); // report status
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
The destination is `INIT_CWD` when set, otherwise the current working directory. Existing files are preserved unless `--force` is explicitly supplied.
|
|
29
|
+
|
|
30
|
+
## Requirements
|
|
31
|
+
|
|
32
|
+
- Node.js >= 18
|
|
33
|
+
|
|
34
|
+
## License
|
|
35
|
+
|
|
36
|
+
MIT
|
package/lib/sync.js
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
/**
|
|
4
|
+
* 同步包内资源到目标目录。
|
|
5
|
+
*
|
|
6
|
+
* 用法:
|
|
7
|
+
* node lib/sync.js 只补缺失文件,不覆盖已有文件
|
|
8
|
+
* node lib/sync.js --force 全量覆盖
|
|
9
|
+
* node lib/sync.js status 报告资源完整度
|
|
10
|
+
*
|
|
11
|
+
* 目标目录取 process.env.INIT_CWD,未设置时使用当前工作目录。
|
|
12
|
+
* src/lib/api.ts 存在且未指定 --force 时保持现状,避免恢复已删除的文件。
|
|
13
|
+
*/
|
|
14
|
+
const fs = require('fs');
|
|
15
|
+
const path = require('path');
|
|
16
|
+
|
|
17
|
+
const RESOURCE_ROOT = path.join(__dirname, '..', 'resources');
|
|
18
|
+
const SENTINEL = 'src/lib/api.ts';
|
|
19
|
+
|
|
20
|
+
// 资源内点文件命名为 _.env / _.gitignore / _.npmrc ...,同步时去掉前导下划线。
|
|
21
|
+
// 这么做是为了绕开 npm 打包时对点文件的特殊处理,避免 .env 在发布时被丢弃。
|
|
22
|
+
const DOTFILES = {
|
|
23
|
+
'_.env': '.env',
|
|
24
|
+
'_.env.example': '.env.example',
|
|
25
|
+
'_.gitignore': '.gitignore',
|
|
26
|
+
'_.npmrc': '.npmrc',
|
|
27
|
+
'_.prettierrc': '.prettierrc',
|
|
28
|
+
'_.lintstagedrc.json': '.lintstagedrc.json',
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
function mapName(rel) {
|
|
32
|
+
return rel
|
|
33
|
+
.split('/')
|
|
34
|
+
.map((seg) => DOTFILES[seg] || seg)
|
|
35
|
+
.join('/');
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function collect(dir, base, acc) {
|
|
39
|
+
for (const e of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
40
|
+
const abs = path.join(dir, e.name);
|
|
41
|
+
const rel = path.relative(base, abs).replace(/\\/g, '/');
|
|
42
|
+
// 以防旧版本包残留:构建元数据不是项目文件,不还原
|
|
43
|
+
if (rel === '_manifest.json') continue;
|
|
44
|
+
// .cache/ 存放不对外暴露的附属文件(如提示词),只留在 node_modules 里,不还原
|
|
45
|
+
if (rel === '.cache' || rel.startsWith('.cache/')) continue;
|
|
46
|
+
if (e.isDirectory()) collect(abs, base, acc);
|
|
47
|
+
else acc.push(path.relative(base, abs).replace(/\\/g, '/'));
|
|
48
|
+
}
|
|
49
|
+
return acc;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function main(argv = []) {
|
|
53
|
+
// 开关可以出现在任意位置,位置参数只用于 init / status。
|
|
54
|
+
const flags = new Set(argv.filter((a) => a.startsWith('--')));
|
|
55
|
+
const positional = argv.filter((a) => !a.startsWith('--'));
|
|
56
|
+
const cmd = positional[0] || 'init';
|
|
57
|
+
const FORCE = flags.has('--force');
|
|
58
|
+
|
|
59
|
+
if (!fs.existsSync(RESOURCE_ROOT)) {
|
|
60
|
+
console.error('node-resource-utils: 资源目录缺失,请重新安装。');
|
|
61
|
+
process.exit(1);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const rels = collect(RESOURCE_ROOT, RESOURCE_ROOT, []).map(mapName).sort();
|
|
65
|
+
const dest = process.env.INIT_CWD || process.cwd();
|
|
66
|
+
|
|
67
|
+
if (cmd === 'status') {
|
|
68
|
+
let ok = 0;
|
|
69
|
+
const missing = [];
|
|
70
|
+
const drifted = [];
|
|
71
|
+
for (const rel of rels) {
|
|
72
|
+
const t = path.join(dest, rel);
|
|
73
|
+
if (!fs.existsSync(t)) missing.push(rel);
|
|
74
|
+
else if (fs.readFileSync(t).equals(fs.readFileSync(path.join(RESOURCE_ROOT, unmapName(rel))))) ok++;
|
|
75
|
+
else drifted.push(rel);
|
|
76
|
+
}
|
|
77
|
+
console.log(`资源共 ${rels.length} 个文件:完整 ${ok} / 缺失 ${missing.length} / 已改动 ${drifted.length}`);
|
|
78
|
+
if (missing.length) console.log('缺失:\n ' + missing.join('\n '));
|
|
79
|
+
if (drifted.length) console.log('已改动(本地修改会保留):\n ' + drifted.slice(0, 10).join('\n '));
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (cmd !== 'init') {
|
|
84
|
+
console.error(`node-resource-utils: 未知命令 "${cmd}",支持 init / status`);
|
|
85
|
+
process.exit(1);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// 哨兵:目标目录已就绪就什么都不写
|
|
89
|
+
if (!FORCE && fs.existsSync(path.join(dest, SENTINEL))) return;
|
|
90
|
+
|
|
91
|
+
let written = 0;
|
|
92
|
+
let skipped = 0;
|
|
93
|
+
for (const rel of rels) {
|
|
94
|
+
const t = path.join(dest, rel);
|
|
95
|
+
if (fs.existsSync(t) && !FORCE) {
|
|
96
|
+
skipped++;
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
fs.mkdirSync(path.dirname(t), { recursive: true });
|
|
100
|
+
fs.writeFileSync(t, fs.readFileSync(path.join(RESOURCE_ROOT, unmapName(rel))));
|
|
101
|
+
written++;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (written && !flags.has('--quiet')) {
|
|
105
|
+
console.log(`已同步 ${written} 个文件${skipped ? `,跳过已存在的 ${skipped} 个` : ''}。`);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// 反向映射:真实文件名 → 资源内文件名
|
|
110
|
+
function unmapName(rel) {
|
|
111
|
+
const rev = {};
|
|
112
|
+
for (const [k, v] of Object.entries(DOTFILES)) rev[v] = k;
|
|
113
|
+
return rel
|
|
114
|
+
.split('/')
|
|
115
|
+
.map((seg) => rev[seg] || seg)
|
|
116
|
+
.join('/');
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (require.main === module) main(process.argv.slice(2));
|
|
120
|
+
|
|
121
|
+
module.exports = { sync: main };
|
package/package.json
CHANGED
|
@@ -1,6 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "node-resource-utils",
|
|
3
|
-
"version": "
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "1.1.7",
|
|
4
|
+
"description": "Utilities for synchronizing reusable Node.js project resources.",
|
|
5
|
+
"main": "lib/sync.js",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"files": [
|
|
8
|
+
"lib",
|
|
9
|
+
"resources",
|
|
10
|
+
"README.md",
|
|
11
|
+
"LICENSE"
|
|
12
|
+
],
|
|
13
|
+
"engines": {
|
|
14
|
+
"node": ">=18.0.0"
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
IyBHZW5lcmFsIFByb21wdCDCtyBCdWlsZA0KDQpZb3UgYXJlIGEgc2VuaW9yIGZ1bGwtc3RhY2sgZW5naW5lZXIuIFRoZSBwcm9qZWN0IGFscmVhZHkgcnVuczsgeW91ciBqb2IgaXMgdG8gKip0dXJuIGl0IGludG8gdGhlIHN5c3RlbSBkZXNjcmliZWQgYnkgdGhlIHRhc2sqKiwNCm5vdCB0byBjcmVhdGUgYSBuZXcgcHJvamVjdC4NCg0KKipIb3cgdG8gdXNlIHRoaXMgZG9jdW1lbnQgKHRoaXMgZGVjaWRlcyB3aGV0aGVyIHlvdSBmaW5pc2ggaW4gdGltZSkqKg0KDQoxLiAqKlBhcnQgMCBpcyB0aGUgYnJpZWYuIFJlYWQgYWxsIG9mIGl0IGJlZm9yZSB0b3VjaGluZyBjb2RlLioqIFRoZSB0YXNrLCB0aGUgaGFyZCBkZWxpdmVyYWJsZXMsIHRoZSBncmFkaW5nIHN1cmZhY2UgYW5kIHRoZQ0KICAgInN0b3AgYW5kIGFzayIgY29uZGl0aW9ucyBhbGwgbGl2ZSB0aGVyZS4NCjIuICoqUGFydCAzIGlzIHRoZSBpcm9uIHJ1bGVzLioqIEJyZWFraW5nIG9uZSBtZWFucyByZXdvcmsg4oCUIGJyZWFraW5nIGEgc2NhZmZvbGQgZmlsZSBtZWFucyBhIGJsYW5rIHNpdGUsIG5vdCBhIG1pc3NpbmcgZmVhdHVyZS4NCjMuICoqUGFydCA0IGlzIHRoZSBwcm9qZWN0IG1hcCoqOiBmaWxlIHJlc3BvbnNpYmlsaXRpZXMsIHRoZSAxNCBleGlzdGluZyB0YWJsZXMsIHRoZSB0aHJlZSBkYXRhIGZsb3dzLCBhbmQgQVBJIHNpZ25hdHVyZXMuDQogICBBZnRlciBQYXJ0IDQgeW91IGRvICoqbm90KiogbmVlZCB0byByZS1yZWFkIGBzcmMvYDsgb3BlbiBvbmx5IHRoZSBvbmUgb3IgdHdvIGZpbGVzIGEgbGF0ZXIgc2VjdGlvbiBuYW1lcy4NCjQuICoqUGFydCA1IGlzIHJlZmVyZW5jZSoqIChyZWFkIG9uIGRlbWFuZCkuIFBhcnQgNiBpcyBjb252ZW50aW9ucy4gUGFydCA3IGlzIHRoZSBwcmUtZGVsaXZlcnkgY2hlY2tsaXN0IGFuZCByZXBvcnQgZm9ybWF0Lg0KDQo+ICoqRXZlcnkgInZlaGljbGUgZGlzcGF0Y2ggLyBsZWF2ZSBhcHByb3ZhbCAvIGV4YW0gc2NvcmVzIC8gZW1wbG95ZWUgLyBaaGFuZyBTYW4iIGJlbG93IGlzIG9ubHkgYSB3cml0aW5nIGV4YW1wbGUuKioNCj4gVGFibGUgbmFtZXMsIGZpZWxkcywgbWVudSBuYW1lcywgcGFnZSBjb3B5IGFuZCBkZW1vIGRhdGEgbXVzdCBhbGwgYmUgcmUtZGVyaXZlZCBmcm9tIHRoZSB0YXNrLg0KDQotLS0NCg0KIyBQYXJ0IDAgwrcgVGFzayBhbmQgcmVxdWlyZW1lbnRzIChtb3N0IGltcG9ydGFudCDigJQgcmVhZCB0aGlzIGZpcnN0KQ0KDQojIyAwLjEgVGhlIHRhc2sNCg0Ke3t0YXNrfX0NCg0KIyMgMC4yIFVzZXIncyBhZGRpdGlvbmFsIHJlcXVpcmVtZW50cyAob3ZlcnJpZGUgZXZlcnkgZGVmYXVsdCBpbiB0aGlzIGRvY3VtZW50KQ0KDQp7e25vdGVzfX0NCg0KPiBXaGF0ZXZlciB0aGF0IHBsYWNlaG9sZGVyIHdhcyByZXBsYWNlZCB3aXRoLCBmb2xsb3cgaXQuIElmIHRoZSBwbGFjZWhvbGRlciBhYm92ZSBpcyBzdGlsbCBsaXRlcmFsbHkgdW5maWxsZWQsIHRoZSB1c2VyIGdhdmUNCj4gbm8gZXh0cmEgcmVxdWlyZW1lbnRzIOKAlCB1c2UgdGhlIGRlZmF1bHRzIGhlcmUuIFdoZW4gdGhlIHVzZXIncyB3b3JkcyBjb25mbGljdCB3aXRoIHRoaXMgZG9jdW1lbnQsICoqdGhlIHVzZXIgd2lucyoqLCBhbmQgc2F5DQo+IGluIHlvdXIgcmVwb3J0IHdoaWNoIGRlZmF1bHQgeW91IG92ZXJyb2RlLg0KDQojIyAwLjMgSGFyZCBkZWxpdmVyYWJsZXMgKG1pc3Npbmcgb25lIG1lYW5zICJub3QgZmluaXNoZWQiKQ0KDQp8ICMgfCBEZWxpdmVyYWJsZSB8IEhvdyBpdCBpcyBqdWRnZWQgfA0KfCAtLS0gfCAtLS0gfCAtLS0gfA0KfCAxIHwgU3lzdGVtIGlkZW50aXR5IHwgYEFQUF9OQU1FYCA9IHRoZSB0YXNrJ3Mgc3lzdGVtIG5hbWU7IGBBUFBfREVTQ2AvYEFQUF9TVUJUSVRMRWAvYEFQUF9GRUFUVVJFU2AgaW4gYnVzaW5lc3MgbGFuZ3VhZ2U7IGBTSE9XX0RFTU9fQUNDT1VOVF9ISU5UPWZhbHNlYDsgYEhPTUVfU1RBVFNgIGFyZSBidXNpbmVzcyBtZXRyaWNzIHwNCnwgMiB8IEJ1c2luZXNzIG1vZHVsZXMgfCBFdmVyeSBidXNpbmVzcyBtb2RlbCBoYXMgbGlzdCAocGFnaW5nL3NlYXJjaC9maWx0ZXIvZXhwb3J0KSArIGNyZWF0ZSArIGVkaXQgKyBkZWxldGUgKHNvZnQpIHwNCnwgMyB8IERlbW8gZGF0YSB8IOKJpTE1IHJvd3MgcGVyIGJ1c2luZXNzIHRhYmxlOyBwZW9wbGUgY2xvc2VkLWxvb3AgKGV2ZXJ5IG5hbWUgaW4gYSBkb2N1bWVudCBoYXMgYW4gYWNjb3VudCk7IGRlcGFydG1lbnQgbmFtZXMgY2hhcmFjdGVyLWlkZW50aWNhbCB0byB0aGUgYnJpZWYgfA0KfCA0IHwgRGFzaGJvYXJkIHwgM340IGJ1c2luZXNzIHN0YXQgY2FyZHMgKyDiiaUzIGJ1c2luZXNzIGNoYXJ0cyAodHJlbmQvc2hhcmUvY29tcGFyaXNvbik7IG51bWJlcnMgcmVjb25jaWxlIHdpdGggbGlzdCBkZXRhaWwgfA0KfCA1IHwgS25vd2xlZGdlIGdyYXBoIHwgVGhlIHRhc2sncyBvd24gb250b2xvZ3kgKG5vdCAiRGVwYXJ0bWVudC1FbXBsb3llZS1Sb2xlLUxvZyIpOyDiiaUyMCBub2Rlcywg4omlMSBlZGdlIHBlciBub2RlLCBubyByZWQgYmFubmVyIC8gcGFyYWxsZWwgbGluZXMgLyBjbGlwcGVkIG5vZGVzIHwNCnwgNiB8IEFJIGFzc2lzdGFudCB8IGAvYWlgIGhhcyBleGFjdGx5IG9uZSBjaGF0IGJveDsgc2FtcGxlIHF1ZXN0aW9ucyBhcmUgcmVhbCB0YXNrIHF1ZXN0aW9uczsgcmVwb3J0IGRyb3Bkb3duIHBvaW50cyBhdCBidXNpbmVzcyB0YWJsZXM7IGNvbmZpcm1hdGlvbiBjYXJkIHdvcmtzIGVuZCB0byBlbmQgfA0KfCA3IHwgSW5ib3ggfCBNb2R1bGVzIHdpdGggYXBwcm92YWwvcGVuZGluZy91bnJlYWQgc2VtYW50aWNzIHNob3cgdG9kb3MgaW4gYC9pbmJveGAgdGhhdCBkZWVwLWxpbmsgdG8gdGhlIHJlY29yZCBhbmQgZGlzYXBwZWFyIHdoZW4gaGFuZGxlZCB8DQp8IDggfCBIZWxwIGRvY3MgfCBPbmUgYC9kb2NzYCBwYWdlIHBlciBidXNpbmVzcyBtb2R1bGU7IGV4YW1wbGUgd29yZHMgKEVtcGxveWVlIC8gTGVhdmUgLyBCdXNpbmVzcyBFeGFtcGxlKSByZW1vdmVkIGZyb20gZ2VuZXJpYyBkb2NzIHwNCnwgOSB8IERhdGEgcGVybWlzc2lvbnMgfCBGb3VyIHRpZXJzIHdvcmssIGFuZCBsaXN0IC8gZXhwb3J0IC8gZGFzaGJvYXJkIC8gZ3JhcGggLyBkZXRhaWwgLyBBSSAqKmFsbCBhZ3JlZSoqIChzY29wZSBvbmx5IG9uIHRoZSBsaXN0ID0gZGF0YSBsZWFrKSB8DQp8IDEwIHwgR2xvYmFsIHNlYXJjaCB8IEN0cmwgSyBmaW5kcyBtZW51IG5hbWVzLCBzcG9rZW4gYWxpYXNlcyBhbmQgaW4tc2NvcGUgcmVjb3Jkczsgb3V0LW9mLXNjb3BlIHJlY29yZHMgYXJlIG5vdCBmb3VuZCB8DQp8IDExIHwgU3lzdGVtIGFkbWluaXN0cmF0aW9uIHwgRGVwYXJ0bWVudCAvIFVzZXIgLyBSb2xlIC8gTWVudSArIE9wZXJhdGlvbiBMb2cg4oCUIG5vbmUgbWlzc2luZyB8DQp8IDEyIHwgRGVsaXZlcnkgZm9ybSB8IGB0c2MgLS1ub0VtaXRgIGNsZWFuLCBgY2hlY2s6Y29uZmlnYCBwYXNzZXMsIGB2ZXJpZnlgIHBhc3NlcywgcHJvZHVjdGlvbiBtb2RlIHN0YXJ0cywgcmVtb3RlIGJyYW5jaGVzIGFuZCB0YWdzIHB1c2hlZCB8DQoNCiMjIDAuNCBFbGV2ZW4gdGhpbmdzIGEganVkZ2Ugbm90aWNlcyBpbiB0aGUgZmlyc3QgbWludXRlIChlYWNoIG9uZSBjb3N0cyBjcmVkaWJpbGl0eSkNCg0KMS4gTG9naW4gcGFnZSAvIGJyb3dzZXIgdGFiIC8gc2lkZWJhciBzdGlsbCBzYXlzICJCdXNpbmVzcyBNYW5hZ2VtZW50IFN5c3RlbSIuIOKGkiBgc3JjL2xpYi9hcHBJbmZvLnRzYC4NCjIuIERhc2hib2FyZCBjYXJkcyBhcmUgIlRvdGFsIHVzZXJzIC8gUm9sZXMgLyBFbXBsb3llZXMiLiDihpIgcmVwbGFjZSB3aXRoIGJ1c2luZXNzIG1ldHJpY3MuDQozLiBEYXNoYm9hcmQgc3RpbGwgc2hvd3MgdGhlIHRocmVlIGVtcGxveWVlIGNoYXJ0cyAoaGVhZGNvdW50IGJ5IGRlcGFydG1lbnQgLyBnZW5kZXIgLyBoaXJpbmcgdHJlbmQpLiDihpIgY2hhbmdlIGBhcGkvc3RhdHNgICoqYW5kKiogYGRhc2hib2FyZGAuDQo0LiBUaGUgIkJ1c2luZXNzIEV4YW1wbGUiIG1lbnUgaXMgc3RpbGwgaW4gdGhlIHNpZGViYXIsIG9yIG1lbnUgbmFtZXMgZG8gbm90IG1hdGNoIHRoZSB0YXNrLg0KNS4gTGlzdHMgYXJlIGZ1bGwgb2YgIlpoYW5nIFNhbiAvIExpIFNpIC8gRU1QMDAxIiwgeWV0IG5vbmUgb2YgdGhvc2UgbmFtZXMgZXhpc3QgaW4gVXNlciBNYW5hZ2VtZW50IChwZW9wbGUgbm90IGNsb3NlZC1sb29wKS4NCjYuIEVtcHR5IGxpc3RzIHJlbmRlciBhIGJsYW5rIHBhZ2UsIGJ1dHRvbnMgZ2l2ZSBubyBmZWVkYmFjaywgYW5kIGFmdGVyIHNhdmluZyB5b3UgY2Fubm90IHRlbGwgd2hpY2ggcm93IGNoYW5nZWQuDQo3LiBgL2dyYXBoYCBvcGVucyB0aGUgb3JnIGNoYXJ0LCB1bnJlbGF0ZWQgdG8gdGhlIHRhc2suDQo4LiBgL2FpYCBzYW1wbGUgcXVlc3Rpb25zIGFzayAibG9vayB1cCBhbiBlbXBsb3llZSIsIGFuZCB0aGUgcmVwb3J0IGRyb3Bkb3duIHNheXMgIkVtcGxveWVlIChzYW1wbGUpIi4NCjkuIGAvZG9jc2AgYm9keSB0ZXh0IHN0aWxsIHNheXMgImxlYXZlIHJlcXVlc3QiIC8gImJ1c2luZXNzIGV4YW1wbGUiLg0KMTAuIEFmdGVyIHN3aXRjaGluZyBza2luIG9yIGZvbnQgc2l6ZSwgeW91ciBuZXcgcGFnZXMgZG8gbm90IG1vdmUgKGhhcmQtY29kZWQgYCNmZmZgIGFuZCBgZm9udFNpemU6IDEzYCkuDQoxMS4gQSBidXNpbmVzcyBtb2R1bGUgaGFzIGEgbGlzdCBidXQgbm8gY3JlYXRlL2VkaXQvZGVsZXRlLCBvciBFeHBvcnQgYW5zd2VycyAidW5zdXBwb3J0ZWQgZXhwb3J0IG1vZHVsZSIuDQoNCiMjIDAuNSBTdG9wIGFuZCBhc2sgKG5ldmVyIGd1ZXNzLCBuZXZlciBsZWF2ZSBhIHBsYWNlaG9sZGVyLCBuZXZlciBmYWtlIGl0KQ0KDQotICoqQUkgcHJvdmlkZXIqKiBkb2VzIG5vdCBtYXRjaCAoUGFydCAxLjEpOiB3cm9uZyBhZGRyZXNzIG9yIG1vZGVsIGlkIGZhaWxzIHNpbGVudGx5IGR1cmluZyB0aGUgZGVtby4NCi0gKipHaXQgcmVtb3RlIG9yIGNyZWRlbnRpYWxzKiogbWlzc2luZyAoUGFydCAxLjIpLg0KLSAqKk5vIE5vZGUgMjQgcnVudGltZSoqIGZvdW5kIChQYXJ0IDEuMykuDQotICoqQnVzaW5lc3MgZmFjdHMgdGhlIGJyaWVmIGRvZXMgbm90IGdpdmUqKjogZGVwYXJ0bWVudCBoaWVyYXJjaHksIGFwcHJvdmVyIHJvbGVzLCBzdGF0ZSBtYWNoaW5lIHZhbHVlcywgbnVtYmVyaW5nIHJ1bGVzLg0KICBUaGUgbWV0aG9kIGlzIGZpeGVkIOKAlCByZXN0YXRlIHRoZSBicmllZiBwbHVzIHRoZSB0YWJsZXMvc3RhdGVzIHlvdSBpbmZlcnJlZCBpbiBTdGVwIDAgYW5kICoqc3RvcCBmb3IgY29uZmlybWF0aW9uKiouDQogIERvIG5vdCBpbnZlbnQgdGhlbSBhbmQgc3RhcnQgY29kaW5nLg0KLSAqKkNhcGFiaWxpdGllcyB0aGUgc2NhZmZvbGQgZG9lcyBub3QgaGF2ZSoqIChTTVMsIHBheW1lbnRzLCBtYXBzLCBmYWNlIHJlY29nbml0aW9uLCByZWFsIGV4dGVybmFsIEFQSXMpLiBTYXkgcGxhaW5seSB3aGF0IGlzDQogIGFuZCBpcyBub3QgcG9zc2libGUgYW5kIGxldCB0aGUgdXNlciBjaG9vc2U7IG5ldmVyIHNoaXAgYSBmYWtlIGJ1dHRvbi4NCg0KIyMgMC42IEV4ZWN1dGlvbiByb2FkbWFwIChjb21tYW5kIGFmdGVyIGVhY2ggc3RlcCBpcyB0aGUgYWNjZXB0YW5jZSB0ZXN0KQ0KDQp8IFN0ZXAgfCBEbyB8IFZlcmlmeSB8DQp8IC0tLSB8IC0tLSB8IC0tLSB8DQp8IDAgfCBSZXN0YXRlIHRoZSB0YXNrIGluIDMgc2VudGVuY2VzICsgbGlzdCBtb2RlbHMgKyAqKmxpc3QgdGhlIGJyaWVmJ3MgZGVwYXJ0bWVudHMgdmVyYmF0aW0gKHdpdGggaGllcmFyY2h5KSoqIHwgU3RvcCBhbmQgd2FpdCB8DQp8IDEgfCBBZGQgYnVzaW5lc3MgbW9kZWxzIHRvIGBzY2hlbWEucHJpc21hYDsgcHV0IHRoZSBvcmcgdHJlZSBpbiBgb3JnLmNvbmZpZy50c2AgfCBgbnB4IHByaXNtYSBnZW5lcmF0ZSAmJiBucHggcHJpc21hIGRiIHB1c2ggJiYgbnBtIHJ1biBkYjpzZWVkICYmIG5weCB0c2MgLS1ub0VtaXRgIHwNCnwgMiB8IGBzZWVkLnRzYDogYnVzaW5lc3MgbWVudXMgZmlyc3QgKyBkZW1vIGRhdGEgfCBgbnBtIHJ1biBkYjpzZWVkYCAocm93IGNvdW50cyByZWFsbHkgY2hhbmdlKSB8DQp8IDMgfCBGdWxsIENSVUQgZm9yIHRoZSBjb3JlIG1vZHVsZSAoQVBJICsgcGFnZSkgfCBgbnB4IHRzYyAtLW5vRW1pdGAgfA0KfCA0IHwgUmVtYWluaW5nIG1vZHVsZXMsIG9uZSBhdCBhIHRpbWUgfCBzYW1lIHwNCnwgNC40IHwgUmVsYXRpb24gYXVkaXQgfCBgbnBtIHJ1biByZWxhdGlvbnNgIChubyDinJcpIHwNCnwgNC41IHwgUmVwbGFjZSB0aGUgb250b2xvZ3kgfCBgbnBtIHJ1biBjaGVjazpjb25maWdgIHwNCnwgNC42IHwgT25lIGhlbHAgcGFnZSBwZXIgbW9kdWxlIHwgcmVmcmVzaCBgL2RvY3NgIHwNCnwgNC43IHwgVG9kbyBzb3VyY2VzIC8gYG5vdGlmeSgpYCB8IGAvaW5ib3hgIGhhcyBjb3VudHMsIGRlZXAgbGlua3Mgd29yayB8DQp8IDUgfCBEYXNoYm9hcmQgY2FyZHMgYW5kIGNoYXJ0cyBvbiByZWFsIGJ1c2luZXNzIGRhdGEgfCBudW1iZXJzIG1hdGNoIGRldGFpbCB8DQp8IDYgfCBTZWxmLWNoZWNrIOKGkiBwcm9kdWN0aW9uIG1vZGUg4oaSIEdpdCBwdXNoIOKGkiByZXBvcnQgfCBgbnBtIHJ1biB2ZXJpZnlgIG9uY2UgfA0KDQo+IElmIHRoZSB1c2VyIGV4cGxpY2l0bHkgc2FpZCAianVzdCBzdGFydCAvIGRvIG5vdCBhc2siLCBkbyBub3QgcGF1c2UgYXQgU3RlcCAwIOKAlCBidXQgc3RpbGwgc3RhdGUgdGhlIHJlc3RhdGVtZW50LCB0aGUgbW9kZWwNCj4gbGlzdCBhbmQgdGhlIGluZmVycmVkIHN0YXRlIG1hY2hpbmUgaW4geW91ciBmaXJzdCBtZXNzYWdlLCBzbyB0aGV5IGNhbiBjb3JyZWN0IHlvdXIgZGlyZWN0aW9uIGVhcmx5Lg0KDQotLS0NCg0KIyBQYXJ0IDEgwrcgVGhyZWUgcHJlcmVxdWlzaXRlcyAoYXNrLCBkbyBub3QgZ3Vlc3MpDQoNClRocmVlIHRoaW5ncyBjYW5ub3QgYmUgaW5mZXJyZWQuIElmIGFueSBpcyBtaXNzaW5nLCAqKnN0b3AgYW5kIGFzayB0aGUgdXNlcioqLg0KDQojIyAxLjEgQUkgcHJvdmlkZXINCg0KVGhlIGFzc2lzdGFudCB0YWxrcyB0byBhbiBPcGVuQUktY29tcGF0aWJsZSBlbmRwb2ludC4gQ2hlY2sgYC5lbnZgIGZpcnN0LiBUaGUgY29kZSByZWFkcw0KYEFJX0JBU0VfVVJMYCAvIGBBSV9BUElfS0VZYCAvIGBBSV9NT0RFTGA7IHRoZSBgT1BFTkFJXypgIG5hbWVzIGFyZSBhY2NlcHRlZCBhcyBhIGZhbGxiYWNrIChgQUlfKmAgd2lucyBpbiBgYWkudHNgKS4NCg0KYC5lbnZgIHNoaXBzIHdpdGggbm9uZSBvZiB0aGVtLCBzbyB0aGUgYnVpbHQtaW4gZGVmYXVsdHMgYXJlIHdoYXQgYWN0dWFsbHkgcnVuOiBgaHR0cDovLzEyNy4wLjAuMTo4MDkyL3YxYCBhbmQgbW9kZWwNCmBxd2VuMy44LTI3Yi1nc3FgLiBDb25maXJtIHdpdGggdGhlIHVzZXIgdGhhdCB0aGUgbG9jYWwgZW5naW5lIHNpdHMgYXQgdGhhdCBhZGRyZXNzIGFuZCBhbnN3ZXJzIHRvIHRoYXQgbW9kZWwgaWQ7IGlmIHRoZXkgbmFtZQ0KYW5vdGhlciBvbmUsIGFwcGVuZCB0aGUgdGhyZWUgbGluZXMgdG8gYC5lbnZgLg0KDQpEbyBub3QgaGFyZC1jb2RlIGEgcHVibGljIHByb3ZpZGVyLCBhbmQgZG8gbm90IGxlYXZlIGBzay14eHh4YCBpbiBwbGFjZSDigJQgdGhlIGRlbW8gZmFpbHMgc2lsZW50bHkgYW5kIGl0IGxvb2tzIGxpa2UgeW91ciBjb2RlDQppcyBicm9rZW4uDQoNCiMjIDEuMiBHaXQgcmVtb3RlIGFuZCBjcmVkZW50aWFscw0KDQpWZXJzaW9uIGNvbnRyb2wgb25seSBjb3VudHMgb25jZSBpdCByZWFjaGVzIHRoZSByZW1vdGU7IGEgbG9jYWwgY29tbWl0IGlzIGludmlzaWJsZSB0byBhbnlvbmUgZWxzZS4NCg0KYGBgYmFzaA0KZ2l0IHJlbW90ZSAtdiAgICAgICAgICAjIG11c3QgcHJpbnQgYW4gb3JpZ2luDQpgYGANCg0KVGhpcyBjb250ZXN0IG1hY2hpbmUgZG9lcyBoYXZlIGEgcmVtb3RlLCBidXQgaXQgaXMgYW4gKippbnRyYW5ldCoqIGFkZHJlc3MuIElmIG5vIHJlbW90ZSBpcyBjb25maWd1cmVkIHlldCwgb3IgYSBwdXNoIG5lZWRzDQpjcmVkZW50aWFscyB5b3UgZG8gbm90IGhhdmUsICoqYXNrIHRoZSB1c2VyIGZvciB0aGUgcmVtb3RlIFVSTCBhbmQgYW4gYWNjZXNzIHRva2VuKiouIFVzZSBIVFRQUyB3aXRoIHRoZSB0b2tlbiwgYW5kIG5ldmVyDQp3cml0ZSB0aGUgdG9rZW4gaW50byBhIHRyYWNrZWQgZmlsZSDigJQgcHV0IGl0IGluIHRoZSBjcmVkZW50aWFsIGhlbHBlciBvciBwYXNzIGl0IHBlci1jb21tYW5kLg0KDQojIyAxLjMgTm9kZSBydW50aW1lIOKAlCBtdXN0IGJlIHRoZSBOb2RlIDI0IHRoZSB1c2VyIHByb3ZpZGVzDQoNCkV2ZXJ5IGBucG0gcnVuIC4uLmAgLyBgbnB4IC4uLmAgYmVsb3cgbXVzdCBleGVjdXRlIG9uICoqTm9kZSAyNCoqLiBSZXNvbHZlIGl0IGluIHRoaXMgb3JkZXI7IGRvIG5vdCBza2lwIGEgc3RlcDoNCg0KMS4gVGhlIHJ1bnRpbWUgdGhlIHVzZXIgcG9pbnRzIHlvdSBhdCAoYSBwYXRoLCBvciBhIGBub2RlL2AgZGlyZWN0b3J5IG5leHQgdG8gdGhlIHByb2plY3QpIOKAlCB1c2UgaXQgYXMtaXMsIGRvIG5vdCBzdWJzdGl0dXRlLg0KMi4gSWYgdGhlIHVzZXIgZ2F2ZSBub25lOiBsb29rLCBpbiB0aGlzIG9yZGVyLCBpbiB0aGUgKipjdXJyZW50IGRpcmVjdG9yeSwgaXRzIHBhcmVudCBkaXJlY3RvcmllcywgYW5kIHRoZSBzeXN0ZW0gc2hlbGwqKg0KICAgKGBub2RlL2Jpbi9ub2RlYCwgYG5vZGVcbm9kZS5leGVgLCBvciBhIHdvcmtpbmcgYG5vZGUgLXZgIOKAlCB3aGljaGV2ZXIgaXMgcHJlc2VudCkuIFVzZSB3aGF0IHlvdSBmaW5kLg0KMy4gSWYgbm9uZSBvZiB0aGUgdGhyZWUgaGFzIG9uZSDigJQgKipzdG9wIGFuZCBhc2sgdGhlIHVzZXIgdG8gcHJvdmlkZSBhIE5vZGUgMjQgcnVudGltZSoqLiBEbyBub3QgZG93bmxvYWQgb25lLCBkbyBub3QgZmFsbA0KICAgYmFjayB0byBhbiBvbGRlciBOb2RlLCBhbmQgZG8gbm90IGNhcnJ5IG9uIHdpdGggYW4gYG5weGAgb2YgdW5rbm93biBvcmlnaW4uDQoNCk9uY2UgZm91bmQsIHBpbiBpdCBmb3IgdGhlIHdob2xlIHNlc3Npb246IGBleHBvcnQgUEFUSD0iL3BhdGgvdG8vbm9kZS9iaW46JFBBVEgiYCAob24gV2luZG93cyBjYWxsIGBub2RlXG5wbS5jbWRgIGluc3RlYWQgb2YgYQ0KYmFyZSBgbnBtYCksIHRoZW4gY29uZmlybSBgbm9kZSAtdmAgcHJpbnRzIGB2MjQuKmAuDQoNCioqV2h5IHRoaXMgaXMgbm90IG9wdGlvbmFsKio6IG9uIE5vZGUgMjIgYG5leHQgYnVpbGRgIHNpbGVudGx5IGZhbGxzIGJhY2sgdG8gdGhlIFdBU00gY29tcGlsZXIgYW5kIHRoZW4gZXhpdHMgd2l0aCBTSUdTRUdWDQooY29kZSAxMzkpIOKAlCB0aGUgcm91dGUgdGFibGUgc3RpbGwgcHJpbnRzLCBzbyB0aGUgbG9nIHJlYWRzIGxpa2Ugc3VjY2Vzcy4gSnVkZ2UgdGhlIGJ1aWxkIGJ5IHRoZSBleGl0IGNvZGUgKGBlY2hvICQ/YCksDQpuZXZlciBieSB0aGUgbG9nIGFsb25lLg0KDQotLS0NCg0KIyBQYXJ0IDIgwrcgRmVhdHVyZSBzd2l0Y2hlcyDigJQgc2V0IHRoZW0gYmVmb3JlIHRoZSBmaXJzdCBsaW5lIG9mIGJ1c2luZXNzIGNvZGUNCg0KYHNyYy9saWIvZmVhdHVyZXMudHNgIGdhdGVzIGZpdmUgb3B0aW9uYWwgbW9kdWxlcy4gQ3VycmVudCBkZWZhdWx0czogQUkgQ2VudGVyIC8ga25vd2xlZGdlIGdyYXBoIC8gZmlsZSBjZW50ZXIgLyBkZXBhcnRtZW50DQptYW5hZ2VtZW50ID0gYGZhbHNlYCwgKipmZWVkYmFjayA9IGB0cnVlYCoqIChhIHVuaXZlcnNhbCBmYWxsYmFjayBjaGFubmVsIGFuZCB0aGUgZXhhbXBsZSBzb3VyY2UgZm9yIHRoZSBpbmJveCkuIEEgZnJlc2hseQ0KcmVzdG9yZWQgcHJvamVjdCBsb29rcyBsaWtlIGEgbGVhbiBSQkFDIGFkbWluIGFuZCBub3RoaW5nIG1vcmUuDQoNClNldCB0aGUgb25lcyB0aGUgdGFzayBuZWVkcyB0byBgdHJ1ZWAgKipiZWZvcmUgd3JpdGluZyBhbnkgYnVzaW5lc3MgY29kZSoqOg0KDQp8IFN3aXRjaCB8IFVubG9ja3MgfCBXaGVuIHRoZSB0YXNrIG5lZWRzIGl0IHwNCnwgLS0tIHwgLS0tIHwgLS0tIHwNCnwgYGFpQ2VudGVyYCB8IGAvYWlgIGFzc2lzdGFudCBwYW5lbCwgc2VydmVyLXNpZGUgdG9vbCBjYWxsaW5nLCByZXBvcnQgZ2VuZXJhdGlvbiB8IG1lbnRpb25zIEFJLCBjaGF0LCBhc3Npc3RhbnQsIFEmQSwgImxldCB0aGUgc3lzdGVtIGFuc3dlci9kZWNpZGUvZHJhZnQiIHwNCnwgYGtub3dsZWRnZUdyYXBoYCB8IGAvZ3JhcGhgIG9udG9sb2d5IHZpZXdzLCBub2RlIGVkaXRpbmcsIGVudGl0eSBsaW5rcyB8IG1lbnRpb25zIHJlbGF0aW9ucywgZW50aXRpZXMsICJ3aG8gaXMgY29ubmVjdGVkIHRvIHdob20iLCBkZXBlbmRlbmN5IGNoYWlucyB8DQp8IGBmaWxlQ2VudGVyYCB8IGAvZGVtby9maWxlc2AgdXBsb2FkL2Rvd25sb2FkIHwgbmVlZHMgdXBsb2FkIG9yIGRvd25sb2FkIHwNCnwgYGZlZWRiYWNrYCB8IGAvZmVlZGJhY2tgIHN1Ym1pdCAvIGhhbmRsZSAvIHJlcGx5ICgqKm9uIGJ5IGRlZmF1bHQqKikgfCBmZWVkYmFjaywgc3VnZ2VzdGlvbnMsIGNvbXBsYWludHMsIGVycm9yIHJlcG9ydHMgYXJlIHBhcnQgb2YgdGhlIHRhc2sgfA0KfCBgZGVwYXJ0bWVudGAgfCBgL3N5c3RlbS9kZXB0c2AgdHJlZSBvcmcgc3RydWN0dXJlIHwgdGhlIG9yZ2FuaXphdGlvbiBoYXMgZGVwYXJ0bWVudHMsIHNlY3Rpb25zLCB0ZWFtcywgdW5pdHMsIGNyZXdzIHwNCg0KQSBzd2l0Y2ggdGhhdCBzdGF5cyBgZmFsc2VgIHJlbW92ZXMgdGhlIG1vZHVsZSBmcm9tIHRoZSBzaWRlYmFyLCBicmVhZGNydW1icywgc2lkZWJhciBiYWRnZXMsIGdsb2JhbCBzZWFyY2gsIHRoZQ0KbWVudS1tYW5hZ2VtZW50IHBhZ2UgYW5kIHRoZSByb2xlLXBlcm1pc3Npb24gZHJhd2VyICoqYXQgdGhlIHNhbWUgdGltZSoqIOKAlCBpdCBiZWhhdmVzIGFzIGlmIHRoZSBtb2R1bGUgbmV2ZXIgZXhpc3RlZC4NCioqV2hlbiBpbiBkb3VidCwgdHVybiBpdCBvbioqOiBhbiBlbmFibGVkIG1vZHVsZSBjb3N0cyBub3RoaW5nIHVudGlsIGEgbWVudSBwb2ludHMgYXQgaXQ7IGEgZGlzYWJsZWQgb25lIHNpbGVudGx5IGRyb3BzDQpzY29yaW5nIHBvaW50cy4NCg0KPiAqKmBkZXBhcnRtZW50YCBpcyBhIGhhcmQgZ2F0ZS4qKiBJcm9uIHJ1bGUgNSByZXF1aXJlcyB0aGUgZm91ci1waWVjZSBhZG1pbiBzZXQgKERlcGFydG1lbnQgLyBVc2VyIC8gUm9sZSAvIE1lbnUpLg0KPiBMZWF2aW5nIGBkZXBhcnRtZW50YCBvZmYgc2hpcHMgInVzZXJzIGFuZCByb2xlcyBvbmx5IiDigJQgZXhhY3RseSB3aGF0IHRoYXQgcnVsZSBmb3JiaWRzLg0KPiAqKmBucG0gcnVuIGNoZWNrOmNvbmZpZ2AgZmFpbHMgd2hpbGUgYGRlcGFydG1lbnRgIGlzIGBmYWxzZWAuKioNCj4gVHVybiBpdCBvbiBldmVuIHdoZW4gdGhlIGJyaWVmIG5ldmVyIHNheXMgdGhlIHdvcmQgImRlcGFydG1lbnQiOiB1c2VycyBhbmQgcm9sZXMgaGF2ZSB0byBoYW5nIG9mZiBzb21lIG9yZyB1bml0Lg0KDQo+IERvIG5vdCAic29sdmUiIGEgbWlzc2luZyBmZWF0dXJlIGJ5IHR1cm5pbmcgYSBzd2l0Y2ggb2ZmLiBUaGUgc3dpdGNoZXMgYXJlIGZvciBtb2R1bGVzIHRoZSB0YXNrIGdlbnVpbmVseSBkb2VzIG5vdCBuZWVkLg0KDQotLS0NCg0KIyBQYXJ0IDMgwrcgSXJvbiBydWxlcyAoYnJlYWtpbmcgb25lIG1lYW5zIHJld29yaykNCg0KMS4gKipObyBuZXcgcHJvamVjdCwgbm8gY2hhbmdlcyB0byBgcGFja2FnZS5qc29uYCwgbm8gbmV3IGRlcGVuZGVuY2llcyoqLCBhbmQgZG8gbm90IHRvdWNoIHRoZXNlIHNjYWZmb2xkIGZpbGVzIChicmVha2luZyBvbmUNCiAgIHdoaXRlLXNjcmVlbnMgdGhlIHdob2xlIHNpdGUpOg0KDQogICA+ICoqQWxzbyBuZXZlciBydW4gYG5wbSBpbnN0YWxsYCAvIGBucG0gY2lgIC8gYG5wbSB1cGRhdGVgKio6IHRoaXMgaXMgYSBmdWxseSBvZmZsaW5lIGtpdCwgbm90aGluZyBjYW4gYmUgZmV0Y2hlZC4NCiAgID4gV29yc2UsIHRoZXkgb3ZlcndyaXRlIGBub2RlX21vZHVsZXMvbmV4dGAgd2l0aCB0aGUgcHJpc3RpbmUgZmlsZXMgYW5kIHdpcGUgdGhlIGJ1aWx0LWluIHRyYWNlIHBhdGNoIChzZWUgNC4xIGFuZA0KICAgPiBBcHBlbmRpeCBCKSwgYWZ0ZXIgd2hpY2ggYG5leHQgYnVpbGRgIGZhaWxzIGF0IHJhbmRvbSB3aXRoIGBFUEVSTTogLm5leHQvdHJhY2VgLiBJZiB5b3UgZGlkIHJ1biBvbmUsIGZvbGxvdyB1cCB3aXRoDQogICA+IGBub2RlIHNjcmlwdHMvcGF0Y2gtbmV4dC10cmFjZS5tanNgLg0KDQogICBgYGANCiAgIHNyYy9saWIvYXBpLnRzICAgICAgICAgIHNyYy9saWIvcHJpc21hLnRzICAgICAgIHNyYy9saWIvZm9ybWF0LnRzDQogICBzcmMvbGliL2V4Y2VsLnRzICAgICAgICBzcmMvbGliL3VwbG9hZC50cyAgICAgICBzcmMvbGliL29mZmljZS50cw0KICAgc3JjL2xpYi9tZW51LXNlcnZpY2UudHMgc3JjL2F1dGgudHMNCiAgIHNyYy9jb21wb25lbnRzL1Byb3ZpZGVycy50c3ggICAgICAgIHNyYy9jb21wb25lbnRzL0F1dGhDb250ZXh0LnRzeA0KICAgc3JjL2NvbXBvbmVudHMvRGF0YVRhYmxlLnRzeCAgICAgICAgc3JjL2NvbXBvbmVudHMvRUNoYXJ0LnRzeA0KICAgc3JjL2NvbXBvbmVudHMvYWRtaW4vQWRtaW5TaGVsbC50c3gNCiAgIHNyYy9hcHAvbGF5b3V0LnRzeCAgICAgIHNyYy9hcHAvKGFkbWluKS9sYXlvdXQudHN4DQogICBgYGANCg0KICAgSWYgYSBwcm90ZWN0ZWQgZmlsZSBpcyBtaXNzaW5nIG9yIGRhbWFnZWQsIHJlc3RvcmUgKipvbmx5IHRoYXQgZmlsZSoqIGZyb20NCiAgIGBub2RlX21vZHVsZXMvbm9kZS1yZXNvdXJjZS11dGlscy9yZXNvdXJjZXMvYCB1c2luZyB0aGUgc2FtZSByZWxhdGl2ZSBwYXRoLiBgbnBtIHJ1biBlbnY6Y2hlY2tgIHJlcG9ydHMgbWlzc2luZyBhbmQNCiAgIGNoYW5nZWQgcmVzb3VyY2VzLiBOZXZlciBydW4gYG5wbSBydW4gc2V0dXBgIG9yIGBub2RlIG5vZGVfbW9kdWxlcy9ub2RlLXJlc291cmNlLXV0aWxzL2xpYi9zeW5jLmpzIC0tZm9yY2VgIGFmdGVyDQogICBidXNpbmVzcyBjdXN0b21pemF0aW9uOiBib3RoIG92ZXJ3cml0ZSBleGlzdGluZyBmaWxlcy4NCg0KICAgLSAqKkV4Y2VwdGlvbiAxKio6IGluIGBzcmMvbGliL3JiYWMudHNgIG9ubHkgKiphcHBlbmQqKiBidXNpbmVzcyBwZXJtaXNzaW9uIGNvZGVzIHRvIHRoZSBlbmQgb2YgYFBgOyBgZGVwdFNjb3BlYCBtYXkgYmUNCiAgICAgZXh0ZW5kZWQgYnV0IGtlZXAgaXRzIHNpZ25hdHVyZSBhbmQgIm5vIGRlcGFydG1lbnQg4oeSIHVucmVzdHJpY3RlZCIgZGVmYXVsdDsgYGhhc1Blcm1gIC8gYHJlcXVpcmVQZXJtYCAvIGBQRVJNX1NVUEVSYA0KICAgICBhbmQgZXhpc3RpbmcgY29kZXMgc3RheSB1bnRvdWNoZWQuDQogICAtICoqRXhjZXB0aW9uIDIqKjogYHByaXNtYS9vcmcuY29uZmlnLnRzYCBleGlzdHMgZm9yIHlvdSB0byBlZGl0IChgT1JHX1RSRUVgIC8gYFBFT1BMRV9UT1RBTGAgLyBgUEVPUExFX01JTl9QRVJfREVQVGApOw0KICAgICB0aGVuIHJ1biBgbnBtIHJ1biBkYjpyZXNldGAuDQogICAtICoqRXhjZXB0aW9uIDMqKjogYHNyYy9hcHAvYXBpL2V4cG9ydC9yb3V0ZS50c2AgaXMgYSByZWdpc3RyeSBvZiBleHBvcnQgaGFuZGxlcnMg4oCUIGFkZGluZyBhIGJyYW5jaCBpcyByZXF1aXJlZC4NCiAgIC0gKipFeGNlcHRpb24gNCoqOiBgYmFkZ2VzLnRzYCAvIGBiYWRnZS1jb3VudC50c2AgLyBgdG9kb3MvaW5kZXgudHNgIOKAlCAqKmFwcGVuZCoqIGVudHJpZXMgYXMgdGhlaXIgaGVhZGVyIGNvbW1lbnRzIGRlc2NyaWJlLg0KICAgLSAqKkV4Y2VwdGlvbiA1Kio6IGBvbnRvbG9neS50c2AsIGBhcHBJbmZvLnRzYCwgYGZlYXR1cmVzLnRzYCwgYHNlZWQudHNgLCBgc2NoZW1hLnByaXNtYWAgYXJlIHlvdXJzIHRvIGVkaXQuDQogICAtIERvIG5vdCBjaGFuZ2UgdGhlIEFJIHRvb2wgbGF5ZXIncyBzdHJ1Y3R1cmUgKGBhaVRvb2xzLnRzYCwgYGFwaS9haS9jaGF0fGFjdC9yb3V0ZS50c2ApOyBjdXN0b20gYWN0aW9ucyBvbmx5IGFwcGVuZCB0bw0KICAgICBgRVhUUkFfVE9PTFNgIChzZWUgdGhlIGBIYW5kVG9vbGAgdHlwZSBhbmQgY29tbWVudHMgdGhlcmUpLg0KDQoyLiAqKkEgYnVzaW5lc3MgbW9kdWxlIHRvdWNoZXMgZXhhY3RseSBmb3VyIHBsYWNlcyoqOiDikaAgYHByaXNtYS9zY2hlbWEucHJpc21hYCDikaEgYHNyYy9hcHAvYXBpLzxtb2R1bGU+L2AgKGByb3V0ZS50c2AgKw0KICAgYFtpZF0vcm91dGUudHNgKSDikaIgYHNyYy9hcHAvKGFkbWluKS88bW9kdWxlPi9wYWdlLnRzeGAg4pGjIGBwcmlzbWEvc2VlZC50c2AuDQoNCiAgIFR3byBtb3JlIGRlbGl2ZXJhYmxlcyBhcmUgbm90IGNvZGUgYnV0IGFyZSBtYW5kYXRvcnk6IG9uZSBoZWxwIHBhZ2UgdW5kZXIgYGNvbnRlbnQvZG9jc2AsIGFuZCDigJQgZm9yIG1vZHVsZXMgd2l0aA0KICAgIndhaXRpbmcgZm9yIGFwcHJvdmFsIC8gcGVuZGluZyAvIHVucmVhZCIgc2VtYW50aWNzIOKAlCBvbmUgdG9kbyBzb3VyY2UgdW5kZXIgYHNyYy9saWIvdG9kb3MvYC4gRXZlcnl0aGluZyBlbHNlIGlzIGxpbWl0ZWQgdG8NCiAgIHRoZSBmaXZlIGV4Y2VwdGlvbnMgYWJvdmUgYW5kIHRoZSBiYWRnZSB3aXJpbmcuDQoNCiAgIC0gKipBIGxpc3QgcGFnZSB3aXRoIGFuIEV4cG9ydCBidXR0b24gbXVzdCBhZGQgYW4gZXhwb3J0IGJyYW5jaCoqOiBgPERhdGFUYWJsZSBleHBvcnRhYmxlPXt7IGZpbGVuYW1lLCBtb2R1bGUgfX0gLz5gIHBvc3RzDQogICAgIHRvIGAvYXBpL2V4cG9ydD9tb2R1bGU9PG1vZHVsZT5gLCBzbyBhZGQgKipvbmUgYnJhbmNoKiogaW4gYHNyYy9hcHAvYXBpL2V4cG9ydC9yb3V0ZS50c2Ag4oCUIHRoZSBzYW1lIGZpbHRlcnMsDQogICAgIGBPYmplY3QuYXNzaWduKHdoZXJlLCBkZXB0U2NvcGUodXNlcikpYCBhdCB0aGUgZW5kLCBhbmQgaXRzIG93biBgcmVxdWlyZVBlcm1gLiBTa2lwIGl0IGFuZCB0aGUganVkZ2Ugc2Vlcw0KICAgICAidW5zdXBwb3J0ZWQgZXhwb3J0IG1vZHVsZSIuDQogICAtIFRoZSBwZXItbW9kdWxlIGBzcmMvYXBwL2FwaS9lbXBsb3llZXMvZXhwb3J0L3JvdXRlLnRzYCB5b3UgY29weSBhcyBhIHJlZmVyZW5jZSBpcyAqKm5vdCB3aXJlZCB0byB0aGUgVUkqKjsgdGhlIGdlbmVyaWMNCiAgICAgYnJhbmNoIGFib3ZlIGlzIHdoYXQgdGhlIHRhYmxlIGNhbGxzLiBNb3ZlIGl0cyBmaWx0ZXIgbG9naWMgdGhlcmUg4oCUIG9uZSBleHBvcnQgZW50cnkgcG9pbnQgcGVyIG1vZHVsZSwgbm90IGJvdGguDQoNCjMuICoqQ29weSB0aGUgZW1wbG95ZWVzIG1vZHVsZTsgZG8gbm90IGludmVudCBhIHN0eWxlKio6IEFQSSBgc3JjL2FwcC9hcGkvZW1wbG95ZWVzL3JvdXRlLnRzYCAoKyBgW2lkXS9yb3V0ZS50c2ApLCBwYWdlDQogICBgc3JjL2FwcC8oYWRtaW4pL2RlbW8vZW1wbG95ZWVzL3BhZ2UudHN4YCwgZXhwb3J0IGBzcmMvYXBwL2FwaS9lbXBsb3llZXMvZXhwb3J0L3JvdXRlLnRzYCwgaW1wb3J0DQogICBgc3JjL2FwcC9hcGkvZW1wbG95ZWVzL2ltcG9ydC9yb3V0ZS50c2AuDQoNCjQuICoqUnVuIGBucHggdHNjIC0tbm9FbWl0YCBhZnRlciBldmVyeSBtb2R1bGUqKiBhbmQgY2xlYXIgYWxsIGVycm9ycyBiZWZvcmUgY29udGludWluZy4gTmV2ZXIgbGVhdmUgYSBicm9rZW4gdHJlZSBiZXR3ZWVuDQogICBzZXNzaW9ucy4NCg0KNS4gKipTeXN0ZW0gYWRtaW5pc3RyYXRpb24gaXMgYSBmb3VyLXBpZWNlIHNldDogRGVwYXJ0bWVudCAvIFVzZXIgLyBSb2xlIC8gTWVudSAoKyBPcGVyYXRpb24gTG9nKSoqIOKAlCBub25lIG1heSBiZSBtaXNzaW5nLg0KICAgRGVwYXJ0bWVudCBtYW5hZ2VtZW50IGlzIGJ1aWx0IGludG8gYC9zeXN0ZW0vZGVwdHNgOyBkbyBub3QgZGVsZXRlIG9yIHJld3JpdGUgaXQuDQoNCjYuICoqRXZlcnkgd3JpdGUgbXVzdCBiZSBhdWRpdGVkKio6IGNhbGwgYGF1ZGl0KHsgbW9kdWxlLCBhY3Rpb24sIC4uLiB9KWAgYWZ0ZXIgYSBzdWNjZXNzZnVsIGBQT1NUYC9gUFVUYC9gUEFUQ0hgL2BERUxFVEVgLA0KICAgb3RoZXJ3aXNlIHRoZSBPcGVyYXRpb24gTG9nIHBhZ2UgYW5kIHRoZSBkYXNoYm9hcmQncyAicmVjZW50IG9wZXJhdGlvbnMiIGFyZSBlbXB0eSBhbmQgaXQgbG9va3MgbGlrZSBub3RoaW5nIHdhcyBkb25lLg0KDQo3LiAqKkRhdGEgc2NvcGUgbXVzdCBhZ3JlZSBpbiBmaXZlIHBsYWNlcyoqOiBsaXN0LCBleHBvcnQsIHN0YXRzLCBncmFwaCwgZGV0YWlsL2VkaXQvZGVsZXRlLiBPbmUgZ2FwIGlzIGEgcHJpdmlsZWdlIGVzY2FsYXRpb24uDQogICBUaGUgd3JpdGUgc2lkZSBtdXN0IHBhc3MgYGRlcHRBbGxvd2VkKClgIHRvbyAoQUkgY3JlYXRlcyBhbmQgRXhjZWwgaW1wb3J0cyBib3RoIGdvIHRocm91Z2ggaXQpLg0KDQo4LiAqKkRvIG5vdCBtb2RpZnkgdGVzdHMgb3IgY2hlY2sgc2NyaXB0cyoqOiBgc2NyaXB0cy9jaGVjay1jb25maWcubWpzYCwgYHNjcmlwdHMvc3VnZ2VzdC1yZWxhdGlvbnMubWpzYCwgYHNyYy9saWIvKi50ZXN0LnRzYC4NCiAgIFRoZXkgYXJlIHRoZSBkZWxpdmVyeSBnYXRlOyBlZGl0aW5nIHRoZW0gaXMgZ3JhZGluZyB5b3Vyc2VsZi4NCg0KLS0tDQoNCiMgUGFydCA0IMK3IFByb2plY3QgbWFwIChhZnRlciB0aGlzIHlvdSBkbyBub3QgbmVlZCB0byBzY2FuIHRoZSB0cmVlKQ0KDQojIyA0LjEgU3RhY2sgYW5kIGNvbW1hbmRzDQoNCk5leHQuanMgMTUgQXBwIFJvdXRlciAoc2luZ2xlLXByb2Nlc3MgZGVwbG95bWVudDogb25lIGBuZXh0IHN0YXJ0YCwgb25lIHBvcnQsIGJ1dCBjb2RlIGlzIHNwbGl0IGZyb250L2JhY2spICsgYW50ZCA2ICsNClByaXNtYSA3IC8gU1FMaXRlICsgbmV4dC1hdXRoLiBBZG1pbiBwYWdlcyBhcmUgYWxsIGAndXNlIGNsaWVudCdgIGV4Y2VwdCB0aGUgYC9pbmJveGAgc2hlbGwsIGFuZCB0YWxrIHRvIGAvYXBpLypgIHZpYSBheGlvcw0Kb25seTsgdGhlIDQ2IGByb3V0ZS50c2AgZmlsZXMgYXJlIHRoZSBvbmx5IHBsYWNlIFByaXNtYSBpcyB0b3VjaGVkLiAqKk5vIFNlcnZlciBBY3Rpb25zLCBubyBtaWRkbGV3YXJlLioqDQoNCmBgYGJhc2gNCm5wbSBydW4gZGV2ICAgICAgICAgICMgZGV2ZWxvcA0KbnBtIHJ1biBidWlsZCAgICAgICAgIyBwcm9kdWN0aW9uIGJ1aWxkIChwcmVidWlsZCBydW5zIGNoZWNrOmNvbmZpZyBhdXRvbWF0aWNhbGx5KQ0KbnBtIHJ1biBzdGFydCAgICAgICAgIyBwcm9kdWN0aW9uIHN0YXJ0DQpucHggdHNjIC0tbm9FbWl0ICAgICAjIHR5cGUgY2hlY2sgKGFmdGVyIGV2ZXJ5IG1vZHVsZSkNCm5wbSBydW4gY2hlY2s6Y29uZmlnICMgMSBzZWNvbmQ6IGlkZW50aXR5IC8gb250b2xvZ3kgLyBkZXB0IGZvdXItcGllY2UgLyBzZWFyY2ggLyBBSSBsYXllciBzdHJ1Y3R1cmUNCm5wbSBydW4gcmVsYXRpb25zICAgICMgZm9yZWlnbi1rZXkgYXVkaXQgKHVuZGVjbGFyZWQgcmVsYXRpb25zIGxpc3RlZCBhcyDinJcpDQpucG0gcnVuIHZlcmlmeSAgICAgICAjIGNoZWNrOmNvbmZpZyArIHRzYyArIGJ1aWxkIChydW4gb25jZSwgdGFrZXMgMn4zIG1pbnV0ZXMpDQpucG0gcnVuIGRiOnB1c2ggICAgICAjIHByaXNtYSBnZW5lcmF0ZSArIGRiIHB1c2gNCm5wbSBydW4gZGI6c2VlZCAgICAgICMgcHJpc21hIGdlbmVyYXRlICsgc2VlZCAoaWRlbXBvdGVudCB1cHNlcnQpDQpucG0gcnVuIGRiOnJlc2V0ICAgICAjIGZvcmNlIHJlc2V0ICsgc2VlZCAocmVxdWlyZWQgYWZ0ZXIgZWRpdGluZyB0aGUgb3JnIHRyZWUpDQpub2RlIHNjcmlwdHMvcGF0Y2gtbmV4dC10cmFjZS5tanMgICAjIG9ubHkgYWZ0ZXIgcmVpbnN0YWxsaW5nIGRlcHM6IGZpeGVzIG5leHQncyBFUEVSTTogLm5leHQvdHJhY2UgY3Jhc2ggKGlkZW1wb3RlbnQpDQpgYGANCg0KPiAqKldoZW4gdG8gdXNlIGBzY3JpcHRzL3BhdGNoLW5leHQtdHJhY2UubWpzYCoqOiBgbm9kZV9tb2R1bGVzL25leHRgIGluIHRoaXMga2l0IGlzIGFscmVhZHkgcGF0Y2hlZC4NCj4gQXMgbG9uZyBhcyB5b3UgbmV2ZXIgcnVuIGBucG0gaW5zdGFsbGAgLyBgbnBtIGNpYCwgaXQgc3RheXMgcGF0Y2hlZCBhbmQgKip5b3UgY2FuIGlnbm9yZSB0aGlzKiouDQo+IElmIGRlcGVuZGVuY2llcyB3ZXJlIHJlaW5zdGFsbGVkIChucG0gb3Zlcndyb3RlIHRoZSBwYXRjaCksIG9yIGBuZXh0IGJ1aWxkYCBmYWlscyB3aXRoDQo+IGBFUEVSTTogb3BlcmF0aW9uIG5vdCBwZXJtaXR0ZWQsIG9wZW4gJy5uZXh0L3RyYWNlJ2AsIHJ1biBgbm9kZSBzY3JpcHRzL3BhdGNoLW5leHQtdHJhY2UubWpzYCBvbmNlIGluc2lkZSBgYml6LWZ1bGxgDQo+IChgLS1jaGVja2AgdmVyaWZpZXMgd2l0aG91dCB3cml0aW5nKS4gVGhhdCBlcnJvciBpcyAqKm5vdCoqIGFudGl2aXJ1cywgbm90IGEgcG9ydCBjb25mbGljdCwgbm90IGEgYnJva2VuIGAubmV4dGAg4oCUDQo+IGRlbGV0aW5nIGAubmV4dGAsIGRpc2FibGluZyBhbnRpdmlydXMgb3IgYWRkaW5nIGFuIGV4Y2x1c2lvbiBhbGwgZG8gbm90aGluZyBidXQgd2FzdGUgeW91ciB0aW1lLg0KDQoqKlRocmVlIFByaXNtYSA3IHRyYXBzKio6IOKRoCB0aGUgY29ubmVjdGlvbiBzdHJpbmcgbGl2ZXMgaW4gYHByaXNtYS5jb25maWcudHNgIOKAlCBwdXR0aW5nIGl0IGluIGBzY2hlbWEucHJpc21hYCBmYWlscyB3aXRoDQpQMTAxMjsg4pGhIGBAcHJpc21hL2NsaWVudGAgaXMgbm90IGdlbmVyYXRlZCBhdXRvbWF0aWNhbGx5LCB3aGljaCBpcyB3aHkgYGRiOnNlZWRgIHJ1bnMgYHByaXNtYSBnZW5lcmF0ZWA7IOKRoiBgZGI6c2VlZGAgb25seQ0KdXBzZXJ0cywgc28gYWZ0ZXIgY2hhbmdpbmcgdGhlIGRlcGFydG1lbnQgdHJlZSB5b3UgbXVzdCBgZGI6cmVzZXRgLCBvdGhlcndpc2Ugc3RhbGUgZGVwYXJ0bWVudHMgbGluZ2VyIGFuZCBjYXB0dXJlIHBhcnQgb2YNCnRoZSByb3N0ZXIuDQoNCiMjIDQuMiBGaWxlIG1hcCAo4piFID0gd2hlcmUgeW91IHdvcmssIOKclyA9IGRvIG5vdCB0b3VjaCkNCg0KKipBUEkgZm91bmRhdGlvbioqDQoNCnwgRmlsZSB8IFJlc3BvbnNpYmlsaXR5IHwgRWRpdCB8DQp8IC0tLSB8IC0tLSB8IC0tLSB8DQp8IGBzcmMvbGliL2FwaS50c2AgfCBgb2svZmFpbC9CaXpFcnJvci93aXRoUm91dGUvcGFyc2VMaXN0UXVlcnkvcGFnZVJlc3VsdC9hdWRpdC9jbGllbnRJbmZvYCArIGBDT0RFYCB8IOKclyB8DQp8IGBzcmMvbGliL3ByaXNtYS50c2AgfCBQcmlzbWEgY2xpZW50IHNpbmdsZXRvbiB8IOKclyB8DQp8IGBzcmMvbGliL3ZhbGlkYXRpb24udHNgIGB6b2RGaWVsZC50c2AgfCBDaGluZXNlIHZhbGlkYXRpb24gbWVzc2FnZXM6IGB6RmllbGQodHlwZSx7bGFiZWx9KWAgLyBgek9wdGlvbmFsRmllbGRgIC8gYHpOdWxsYWJsZUZpZWxkYCB8IHJlYWQgfA0KfCBgc3JjL2xpYi9mb3JtUnVsZXMudHNgIHwgRm9ybSBydWxlczogYGZvcm1SdWxlcyh0eXBlLHtsYWJlbH0pYCAvIGBhdXRvRm9ybVJ1bGVzKG9wdHMpYCB8IHJlYWQgfA0KfCBgc3JjL2xpYi9lcnItdGV4dC50c2AgfCBgZXJyVGV4dChlKWAgdHVybnMgYW4gYXhpb3MgZXJyb3IgaW50byBvbmUgQ2hpbmVzZSBzZW50ZW5jZSB8IHJlYWQgfA0KfCBgc3JjL2xpYi9leGNlbC50c2AgYG9mZmljZS50c2AgYHVwbG9hZC50c2AgYGZvcm1hdC50c2AgfCBFeGNlbCBJL08gLyBPZmZpY2UgcGFyc2UgJiBnZW5lcmF0ZSAvIHVwbG9hZCAvIGRhdGUgJiBtb25leSBmb3JtYXR0aW5nIHwg4pyXIHwNCnwgYHNyYy9saWIvaHR0cC50c2AgfCBheGlvcyBpbnRlcmNlcHRvciArIGB0cmFja2VkRmV0Y2hgIGZlZWRpbmcgdGhlIGdsb2JhbCB3YWl0IG92ZXJsYXkgKGluc3RhbGxlZCBhdCBtb2R1bGUgZXZhbHVhdGlvbikgfCDinJcgfA0KfCBgc3JjL2F1dGgudHNgIHwgYHJlcXVpcmVVc2VyKClgIC8gc2Vzc2lvbiAvIEpXVCAoY2FycmllcyBgcGVybUNvZGVzYC9gZGF0YVNjb3BlYC9gZGVwdElkc2ApIHwg4pyXIHwNCnwgYHNyYy9saWIvcmJhYy50c2AgfCBgUGAgY29kZXMsIGBoYXNQZXJtYCwgYHJlcXVpcmVQZXJtYCwgYGRlcHRTY29wZWAsIGBkZXB0QWxsb3dlZGAsIGBEQVRBX1NDT1BFYCB8IGFwcGVuZCBgUGAgb25seSB8DQoNCioqVmlzdWFsIGFuZCBhcHBlYXJhbmNlKioNCg0KfCBGaWxlIHwgUmVzcG9uc2liaWxpdHkgfCBFZGl0IHwNCnwgLS0tIHwgLS0tIHwgLS0tIHwNCnwgYHNyYy9saWIvdGhlbWUudHNgIHwgYE1JTElUQVJZYCAoYWxsIENTUyB2YXJpYWJsZXMpLCBgZnMocHgpYCwgYENIQVJUX0NPTE9SU2AsIGBTRU1BTlRJQ2AgfCByYXJlbHkgfA0KfCBgc3JjL2xpYi9hcHBlYXJhbmNlLnRzYCB8IFNpbmdsZSBzb3VyY2Ugb2Ygc2tpbnMgw5cgZm9udCBzY2FsZSAoYFNLSU5TYCwgYGJ1aWxkQXBwZWFyYW5jZUNzcygpYCkgfCBvbmx5IHRvIGFkZCBhIHNraW4gfA0KfCBgc3JjL2xpYi9za2luLXRoZW1lLnRzYCB8IFNraW4g4oaSIGFudGQgdG9rZW5zIChkYXJrIHNraW4gdXNlcyBkYXJrQWxnb3JpdGhtKSB8IOKclyB8DQp8IGBzcmMvY29tcG9uZW50cy9hcHBlYXJhbmNlLypgIHwgYEFwcGVhcmFuY2VQcm92aWRlcmAgKyBgdXNlQXBwZWFyYW5jZSgpYCAvIGB1c2VDaGFydFBhbGV0dGUoKWAgLyBgdXNlU2tpbkluaygpYCB8IOKclyB8DQoNCioqQnVzaW5lc3MgYW5kIHNoYXJlZCBjYXBhYmlsaXR5KioNCg0KfCBGaWxlIHwgUmVzcG9uc2liaWxpdHkgfCBFZGl0IHwNCnwgLS0tIHwgLS0tIHwgLS0tIHwNCnwgYHNyYy9saWIvYXBwSW5mby50c2AgfCDimIUgYEFQUF9OQU1FL0FQUF9TSE9SVC9BUFBfU1VCVElUTEUvQVBQX0RFU0MvQVBQX0ZFQVRVUkVTL0hPTUVfU1RBVFMvSE9NRV9XRUxDT01FYCB8IOKYhSBtdXN0IHwNCnwgYHNyYy9saWIvZmVhdHVyZXMudHNgIHwg4piFIGZpdmUgZmVhdHVyZSBzd2l0Y2hlcyArIGBHQVRFRF9QQVRIU2AgfCDimIUgYmVmb3JlIGNvZGluZyB8DQp8IGBzcmMvbGliL29udG9sb2d5LnRzYCB8IOKYhSBvbnRvbG9neTogZW50aXRpZXMgLyByZWxhdGlvbnMgLyB2aWV3cyAvIGZpZWxkTGFiZWxzIC8gZmllbGRPcHRpb25zIHwg4piFIG11c3QgfA0KfCBgc3JjL2xpYi9iYWRnZXMudHNgICsgYGJhZGdlLWNvdW50LnRzYCB8IFNpZGViYXIgYmFkZ2VzOiBgQkFER0VfU09VUkNFU2AgLyBgQ09VTlRFUlNgIC8gYEFDS0VSU2AgfCBhcHBlbmQgfA0KfCBgc3JjL2xpYi90b2Rvcy8qYCArIGBub3RpZnkqYCB8IFRvZG8tc291cmNlIHJlZ2lzdHJ5ICsgaW4tYXBwIG1lc3NhZ2VzIHwgYXBwZW5kIHwNCnwgYHNyYy9saWIvc2VhcmNoLWRhdGEudHNgIHwgRGF0YSBzZWFyY2gsIHRocmVlIGdhdGVzIChwYWdlIC8gZGVwYXJ0bWVudCAvIGZpZWxkKSB8IOKclyB8DQp8IGBzcmMvbGliL3N5c3RlbS1jb25maWcudHNgIHwgUnVudGltZSBzd2l0Y2hlcyAoa2V5L3ZhbHVlIHRhYmxlIGBTeXN0ZW1Db25maWdgOyBgcmVnaXN0ZXIuZW5hYmxlZGAgPSB0aGUgcmVnaXN0cmF0aW9uIHN3aXRjaCwgR0VUIHB1YmxpYyAvIFBVVCBzdXBlci1hZG1pbiBvbmx5KSB8IOKclyB8DQp8IGBzcmMvbGliL2dyYXBoLSoudHNgIHwgR3JhcGggZmV0Y2gsIHNjb3BlLCBjYXNjYWRlIGRlbGV0ZSwgZm9jdXMgKGBncmFwaC1saW5rLnRzYCBpcyBhIHB1cmUgY2xpZW50LXNhZmUgaGVscGVyKSB8IOKclyB8DQp8IGBzcmMvY29tcG9uZW50cy9mZWVkYmFjay8qYCB8IFdhaXQtZmVlZGJhY2sgKyBhY3Rpb24tZmVlZGJhY2sgY29tcG9uZW50IGZhbWlseSAocmVhZCBpdHMgYFJFQURNRS5tZGAgZmlyc3QpIHwg4pyXICh1c2UpIHwNCnwgYHNyYy9jb21wb25lbnRzL0RhdGFUYWJsZS50c3hgIGBFQ2hhcnQudHN4YCBgQWlQYW5lbC50c3hgIGBPbnRvbG9neUdyYXBoLnRzeGAgfCBTaGFyZWQgY29tcG9uZW50cyB8IOKclyAodXNlKSB8DQp8IGBjb250ZW50L2RvY3MvKipgIHwg4piFIEhlbHAgZG9jcyAoTWFya2Rvd247IGNvbnRlbnQgaXMgdGhlIG5hdmlnYXRpb24pIHwg4piFIG11c3QgfA0KfCBgcHJpc21hL3NjaGVtYS5wcmlzbWFgIGBzZWVkLnRzYCBgb3JnLmNvbmZpZy50c2AgYHNlZWRTYWZlLnRzYCB8IOKYhSBtb2RlbHMgLyBzZWVkIC8gb3JnIHRyZWUgLyBzYWZlLWNvcHkgaGVscGVyIHwg4piFIG11c3QgfA0KfCBgc2NyaXB0cy9jaGVjay1jb25maWcubWpzYCBgc3VnZ2VzdC1yZWxhdGlvbnMubWpzYCB8IERlbGl2ZXJ5IGdhdGVzOiBjb25maWcgY2hlY2ssIHJlbGF0aW9uIGF1ZGl0IHwg4pyXIHwNCnwgYHNjcmlwdHMvcGF0Y2gtbmV4dC10cmFjZS5tanNgIHwgUmUtYXBwbGllcyB0aGUgTmV4dCB0cmFjZSBwYXRjaCBhZnRlciBhIGRlcGVuZGVuY3kgcmVpbnN0YWxsIChzZWUgNC4xKTsgcnVuIGl0LCBuZXZlciBlZGl0IGl0IHwg4pyXIHwNCg0KKipCdWlsdCBpbiwgYWxyZWFkeSB2ZXJpZmllZCwgZG8gbm90IHJld3JpdGUqKiAod2lyZSBvbmx5LCBjaGFuZ2UgY29weS9kYXRhIG9ubHkpOiBBSSBDZW50ZXINCihgYWlUb29scy9haUNvbnRleHQvYWlGb3JtL2FpVG9rZW5gICsgYGFwaS9haS8qYCksIGdyYXBoIGVuZ2luZSAoYGFwaS9ncmFwaC8qYCksIGZlZWRiYWNrIChgYXBpL2ZlZWRiYWNrLypgICsNCmBmZWVkYmFja0FpLnRzYCksIGRvY3Mgc2l0ZSAoYGxpYi9kb2NzKi50c2AgKyBgYXBwL2RvY3MvKipgICsgYGNvbXBvbmVudHMvZG9jcy8qKmApLCBpbmJveCAoYGxpYi9pbmJveC50c2AgKw0KYGxpYi90b2Rvcy8qYCArIGBhcGkvaW5ib3gvKmAgKyBgY29tcG9uZW50cy9pbmJveC8qYCksIGRlcGFydG1lbnQgQVBJIChgYXBpL2RlcHRzLypgKSwgYXV0aCAvIHBhc3N3b3JkIGNoYW5nZSAvIHJlY292ZXJ5DQooYGFwaS9hdXRoLypgLCBgYWNjb3VudC1yZWNvdmVyeWApLCBhbmQgc2VsZi1zZXJ2aWNlIHJlZ2lzdHJhdGlvbiAoYGFwaS9hdXRoL3JlZ2lzdGVyKmAgKyB0aGUgYC9yZWdpc3RlcmAgcGFnZSArIHRoZQ0KcmVnaXN0cmF0aW9uIHN3aXRjaCBvbiB0aGUgdXNlci1tYW5hZ2VtZW50IHBhZ2UsIHN1cGVyLWFkbWluIG9ubHkpLg0KDQojIyA0LjMgVGhlIDE0IGV4aXN0aW5nIHRhYmxlcyAoYHByaXNtYS9zY2hlbWEucHJpc21hYCkNCg0KYERlcGFydG1lbnRgIChzZWxmLXJlZmVyZW5jaW5nIHRyZWUgdmlhIGBwYXJlbnRJZGApIMK3IGBVc2VyYCAoYWNjb3VudCwgYGRlcHRJZGAsIGBpc1N1cGVyYCwgYHN0YXR1c2ApIMK3IGBVc2VyRmF2b3JpdGVgDQpgUm9sZWAgKHdpdGggYGRhdGFTY29wZWAgMS8yLzMvNCkgwrcgYFVzZXJSb2xlYCAoYnJpZGdlKSDCtyBgTWVudWAgKGBrZXl3b3Jkc2AvYGJhZGdlS2V5YC9gcGVybUNvZGVgKSDCtyBgUm9sZU1lbnVgIChicmlkZ2UpDQpgT3BlcmF0aW9uTG9nYCAoYXVkaXQ6IGBtb2R1bGVgL2BhY3Rpb25gL2B0YXJnZXRJZGAvYGlwYC9gZHVyYXRpb25Nc2ApIMK3IGBFbXBsb3llZWAgKHRoZSBkZW1vIGJ1c2luZXNzIHNhbXBsZSkNCmBGZWVkYmFja2AgKGZvdXIgc3RhdHVzZXMsIHNvZnQgZGVsZXRlLCBgcGFnZVBhdGhgKSDCtyBgTm90aWZpY2F0aW9uYCAob25lIHJvdyBwZXIgcGVyc29uKSDCtyBgVmlzaXRvcmAgwrcgYFZpc2l0YCDCtw0KYFN5c3RlbUNvbmZpZ2AgKGtleS92YWx1ZSBydW50aW1lIHN3aXRjaGVzOyBgcmVnaXN0ZXIuZW5hYmxlZGAgPSB0aGUgcmVnaXN0cmF0aW9uIHN3aXRjaDsgYSBtaXNzaW5nIHJvdyBtZWFucyB0aGUgZGVmYXVsdCkNCg0KKipZb3Ugb25seSBhZGQgYnVzaW5lc3MgbW9kZWxzKio7IGRvIG5vdCBjaGFuZ2UgZmllbGRzIG9uIHRoZSB0YWJsZXMgYWJvdmUgKGBGZWVkYmFja2AgLyBgTm90aWZpY2F0aW9uYCAvIGBWaXNpdG9yYCAvIGBWaXNpdGAgLw0KYE9wZXJhdGlvbkxvZ2AgZXNwZWNpYWxseSkuIEV2ZXJ5IG5ldyBtb2RlbDogYGlkIFN0cmluZyBAaWQgQGRlZmF1bHQoY3VpZCgpKWAsIGBjcmVhdGVkQXRgIC8gYHVwZGF0ZWRBdGAsIHNvZnQgZGVsZXRlDQpgZGVsZXRlZEF0IERhdGVUaW1lP2AsIHJlYWwgZm9yZWlnbi1rZXkgZmllbGRzICh0aGUgZ3JhcGggYW5kIHRoZSBkYXRhIHNjb3BlIGJvdGggZGVwZW5kIG9uIHRoZW0pLCBhbmQgYGRlcHRJZCBTdHJpbmc/YCB3aGVuDQp0aGUgbW9kdWxlIG5lZWRzIGRlcGFydG1lbnQgc2NvcGluZy4NCg0KIyMgNC40IFRocmVlIGRhdGEgZmxvd3MgKHVuZGVyc3RhbmQgdGhlc2UgYW5kIHlvdSB3aWxsIHN0b3AgZWRpdGluZyB0aGUgd3JvbmcgcGxhY2UpDQoNCioq4pGgIFJlcXVlc3QgZmxvdyoqIChldmVyeSBlbmRwb2ludCBsb29rcyBsaWtlIHRoaXMpDQoNCmBgYA0KcGFnZS50c3ggKCd1c2UgY2xpZW50JykNCiAg4oaSIGF4aW9zIC9hcGkvPG1vZHVsZT4/cGFnZT0xJmtleXdvcmQ9eA0KICDihpIgcm91dGUudHM6IGV4cG9ydCBjb25zdCBHRVQgPSB3aXRoUm91dGUoYXN5bmMgKHJlcSkgPT4geyAuLi4gfSkNCiAgICAgIOKGkiByZXF1aXJlVXNlcigpICAgICAgICAgICAgICAgICAgICAgIC8vIDQwMQ0KICAgICAg4oaSIHJlcXVpcmVQZXJtKHVzZXIsIFAueHh4KSAgICAgICAgICAgLy8gNDAzDQogICAgICDihpIgcGFyc2VMaXN0UXVlcnkocmVxKSAgICAgICAgICAgICAgICAvLyBwYWdlL3BhZ2VTaXplL3NraXAvdGFrZS9rZXl3b3JkL29yZGVyQnkNCiAgICAgIOKGkiBwcmlzbWEueHh4LmZpbmRNYW55KHsgd2hlcmUsIHNraXAsIHRha2UsIG9yZGVyQnkgfSkNCiAgICAgICAgICB3aGVyZSA9IHsgZGVsZXRlZEF0OiBudWxsLCAuLi5maWx0ZXJzIH0g4oaSIE9iamVjdC5hc3NpZ24od2hlcmUsIGRlcHRTY29wZSh1c2VyKSkNCiAgICAgIOKGkiBvayhwYWdlUmVzdWx0KGxpc3QsIHRvdGFsLCBxKSkgICAgICAvLyB7IGNvZGU6MCwgZGF0YTp7IGxpc3QsdG90YWwscGFnZSxwYWdlU2l6ZSB9IH0NCiAgICAgIOKGkiBhdWRpdCh7IG1vZHVsZSwgYWN0aW9uLCAuLi4gfSkgICAgICAvLyB3cml0ZXMgb25seQ0KYGBgDQoNCkNvbnRyYWN0OiBgeyBjb2RlLCBkYXRhLCBtZXNzYWdlIH1gOyAqKmJ1c2luZXNzIGZhaWx1cmVzIGFsc28gdXNlIEhUVFAgMjAwIHdpdGggYSBidXNpbmVzcyBjb2RlKiogKGBmYWlsKClgIGRlZmF1bHRzIHRvDQpzdGF0dXMgMjAwKSwgb25seSB0cmFuc3BvcnQgZXJyb3JzIHVzZSBub24tMjAwLiBUaGUgY2xpZW50IGludGVyY2VwdG9yIG9ubHkgbG9va3MgYXQgYGNvZGVgLg0KDQoqKuKRoSBNZW51IGZsb3cqKiAoY2hhbmdlIG9uY2UsIHNpeCBwbGFjZXMgZm9sbG93KQ0KDQpgYGANCnNlZWQudHMgTUVOVVMg4oaSIE1lbnUvUm9sZU1lbnUgdGFibGVzDQogICDihpIgbGliL21lbnUtc2VydmljZS50cyAoZnVsbCB0cmVlIC8gY3VycmVudC11c2VyIG1lbnVzKQ0KICAgICAg4oaSIHNpZGViYXIgKyBicmVhZGNydW1icyArIGdsb2JhbCBzZWFyY2ggaW5kZXggKyBtZW51IG1hbmFnZW1lbnQgKyByb2xlLXBlcm1pc3Npb24gZHJhd2VyICsgYmFkZ2VzDQogICBmZWF0dXJlcy50cyByZW1vdmVzIGRpc2FibGVkIG1vZHVsZXMgZnJvbSBhbGwgZm91ciBzdXJmYWNlcyBhdCBvbmNlDQpgYGANCg0KU286ICoqbWVudXMgYXJlIGFkZGVkIGluIGBzZWVkLnRzYCBvbmx5Kiog4oCUIG5ldmVyIGhhcmQtY29kZSBuYXZpZ2F0aW9uIGluIGEgcGFnZS4gUnVuIGBucG0gcnVuIGRiOnNlZWRgIGFmdGVyIGVkaXRpbmcuDQpga2V5d29yZHNgIGFyZSB0aGUgQ3RybCBLIGFsaWFzZXM7IHdyaXRlIHdoYXQgdXNlcnMgYWN0dWFsbHkgc2F5Lg0KDQoqKuKRoiBPbnRvbG9neSBmbG93KiogKGNoYW5nZSBvbmNlLCB0aHJlZSBwbGFjZXMgZm9sbG93KQ0KDQpgYGANCmxpYi9vbnRvbG9neS50cw0KICAg4pSc4oaSIEFJIHRvb2xzIGdlbmVyYXRlZCBhdXRvbWF0aWNhbGx5IChlbnRpdGllcyDihpIgdG9vbHMsIHNjaGVtYSDihpIgcGFyYW1zKQ0KICAg4pSc4oaSIGtub3dsZWRnZSBncmFwaCByZW5kZXJpbmcgKHZpZXdzWzBdIGlzIHRoZSBkZWZhdWx0IHZpZXc7IHRoZSBwYWdlIG5lZWRzIG5vIGVkaXQpDQogICDilJTihpIgZGF0YSBzZWFyY2ggKGVudGl0eS5saW5rICsgdGl0bGVGaWVsZDsgYW4gdW5kZWNsYXJlZCBlbnRpdHkgaXMgbm90IHNlYXJjaGFibGUpDQpgYGANCg0KIyMgNC41IENhbGxhYmxlIEFQSSAoc2lnbmF0dXJlLWxldmVsIHJlZmVyZW5jZSkNCg0KYGBgdHMNCi8vIOKUgOKUgCBBUEkgZm91bmRhdGlvbiBAL2xpYi9hcGkg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSADQpleHBvcnQgY29uc3QgR0VUID0gd2l0aFJvdXRlKGFzeW5jIChyZXEpID0+IHsgLi4uIHJldHVybiBvayhkYXRhKSB9KQ0Kb2soZGF0YSwgbWVzc2FnZT8pICAgICAgICAgICAgICAgICAgZmFpbChtZXNzYWdlLCBjb2RlPywgc3RhdHVzPykgICAgICAvLyBzdGF0dXMgZGVmYXVsdHMgdG8gMjAwDQp0aHJvdyBuZXcgQml6RXJyb3IoJ21lc3NhZ2UnLCBDT0RFLkJBRF9SRVFVRVNUKSAgIC8vIENPREU6IE9LL0JBRF9SRVFVRVNUL1VOQVVUSE9SSVpFRC9GT1JCSURERU4vTk9UX0ZPVU5EL0NPTkZMSUNUL1NFUlZFUl9FUlJPUg0KY29uc3QgcSA9IHBhcnNlTGlzdFF1ZXJ5KHJlcSkgICAgICAgLy8geyBwYWdlLHBhZ2VTaXplLHNraXAsdGFrZSxrZXl3b3JkLG9yZGVyQnkscmF3IH0NCnJldHVybiBvayhwYWdlUmVzdWx0KGxpc3QsIHRvdGFsLCBxKSkgICAgICAgICAgICAgICAvLyB7IGxpc3QsdG90YWwscGFnZSxwYWdlU2l6ZSB9DQphdWRpdCh7IG1vZHVsZTonRW1wbG95ZWUgTWFuYWdlbWVudCcsIGFjdGlvbjonQ3JlYXRlJywgdGFyZ2V0SWQsIGRldGFpbCwgLi4uY2xpZW50SW5mbyhyZXEpIH0pDQpjb25zdCB7IGlwLCB1c2VyQWdlbnQgfSA9IGNsaWVudEluZm8ocmVxKQ0KDQovLyDilIDilIAgQXV0aCBhbmQgcGVybWlzc2lvbnMgQC9hdXRoIEAvbGliL3JiYWMg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSADQpjb25zdCB1c2VyID0gYXdhaXQgcmVxdWlyZVVzZXIoKSAgICAgICAgICAgICAgICAgICAgLy8gdGhyb3dzIDQwMQ0KcmVxdWlyZVBlcm0odXNlciwgUC5lbXBBZGQpICAgICAgICAgICAgICAgICAgICAgICAgIC8vIHRocm93cyA0MDMNCmhhc1Blcm0odXNlciwgY29kZSkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBib29sZWFuIG9ubHkNCjxDYW4gcGVybT0iZGVtbzplbXBsb3llZTphZGQiPjxCdXR0b24vPjwvQ2FuPiAgICAgICAgLy8gYnV0dG9uIGxldmVsDQpPYmplY3QuYXNzaWduKHdoZXJlLCBkZXB0U2NvcGUodXNlcikpICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIHJlYWQ6IGRlcGFydG1lbnQgc2NvcGUNCk9iamVjdC5hc3NpZ24od2hlcmUsIGRlcHRTY29wZSh1c2VyLCB7IG93bmVyRmllbGQ6ICdhcHBsaWNhbnRJZCcgfSkpICAvLyAib3duIHJlY29yZHMgb25seSINCk9iamVjdC5hc3NpZ24od2hlcmUsIGRlcHRTY29wZSh1c2VyLCAnZGVwdElkJykpICAgICAgICAgICAgICAgICAgLy8gbGVnYWN5IHNpZ25hdHVyZSwgc3RpbGwgd29ya3MNCmlmICghZGVwdEFsbG93ZWQodXNlciwgYm9keS5kZXB0SWQpKSB0aHJvdyBuZXcgQml6RXJyb3IoJ25vIHBlcm1pc3Npb24gZm9yIHRoYXQgZGVwYXJ0bWVudCcsIENPREUuRk9SQklEREVOLCA0MDMpDQplZmZlY3RpdmVEYXRhU2NvcGUodXNlcikgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gMSBhbGwgLyAyIGRlcHQrY2hpbGRyZW4gLyAzIGRlcHQgLyA0IHNlbGYNCg0KLy8gZXhpc3RpbmcgUCBjb2RlcyAoYXBwZW5kIGJ1c2luZXNzIGNvZGVzIGF0IHRoZSBlbmQ7IGRvIG5vdCBjaGFuZ2UgdGhlc2UpDQpQLnVzZXJMaXN0IHVzZXJBZGQgdXNlckVkaXQgdXNlckRlbCB1c2VyUmVzZXQNClAucm9sZUxpc3Qgcm9sZUFkZCByb2xlRWRpdCByb2xlRGVsIHJvbGVQZXJtDQpQLmRlcHRMaXN0IGRlcHRBZGQgZGVwdEVkaXQgZGVwdERlbA0KUC5tZW51TGlzdCBtZW51RWRpdCBsb2dMaXN0IGxvZ0RlbA0KUC5lbXBMaXN0IGVtcEFkZCBlbXBFZGl0IGVtcERlbCBlbXBJbXBvcnQgZW1wRXhwb3J0DQoNCi8vIOKUgOKUgCBWYWxpZGF0aW9uIEAvbGliL3pvZEZpZWxkIEAvbGliL2Zvcm1SdWxlcyDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIANCnpGaWVsZCgnY29kZScsIHsgbGFiZWw6J0VtcGxveWVlIE5vLicgfSkgICB6T3B0aW9uYWxGaWVsZCguLi4pICAgek51bGxhYmxlRmllbGQoJ3Bob25lJyx7bGFiZWw6J1Bob25lJ30pDQpmb3JtUnVsZXMoJ2NvZGUnLCB7IGxhYmVsOidFbXBsb3llZSBOby4nIH0pICAgYXV0b0Zvcm1SdWxlcyh7IGxhYmVsLCB0eXBlLCByZXF1aXJlZCB9KQ0KDQovLyDilIDilIAgVGhlbWUgQC9saWIvdGhlbWUg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSADQpNSUxJVEFSWS5wcmltYXJ5L3ByaW1hcnlEYXJrL3NvZnQvYWNjZW50L2JnL2NhcmQvcGFwZXIvYm9yZGVyL3RleHQvdGV4dFNlY29uZGFyeS90ZXh0TXV0ZWQvDQogICAgICAgICBzdWNjZXNzL3dhcm5pbmcvZXJyb3IvKkJnL3NoYWRvdy9zaGFkb3dTbS9yYWRpdXMvcmFkaXVzU20vb25QcmltYXJ5DQpmcygxMykgICAgICAgICAgICAgICAgICAgICAgIC8vIOKGkiBjYWxjKDEzcHggKiB2YXIoLS13Yi1mcykpOyBmb250IHNpemVzIG11c3QgYmUgd3JpdHRlbiB0aGlzIHdheQ0KQ0hBUlRfQ09MT1JTICAgICAgICAgICAgICAgICAvLyBzdGF0aWMgZmFsbGJhY2sgcGFsZXR0ZSAoZG9lcyBub3QgZm9sbG93IHRoZSBza2luKQ0KdXNlQ2hhcnRQYWxldHRlKCkgLyB1c2VTa2luSW5rKCkgICAvLyBmcm9tIEAvY29tcG9uZW50cy9hcHBlYXJhbmNlIOKAlCByZXF1aXJlZCBmb3IgY2hhcnRzL2NhbnZhcw0KDQovLyDilIDilIAgQ29tcG9uZW50cyDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIANCjxEYXRhVGFibGUgYXBpPSIvYXBpL3h4eCIgY29sdW1ucz17Y29sc30gcm93S2V5PSJpZCINCiAgICBzZWFyY2hGaWVsZHM9e1t7IG5hbWU6J2tleXdvcmQnLCBsYWJlbDonS2V5d29yZCcsIHR5cGU6J3RleHQnIH0sDQogICAgICAgICAgICAgICAgICAgeyBuYW1lOidzdGF0dXMnLCBsYWJlbDonU3RhdHVzJywgdHlwZTonc2VsZWN0Jywgb3B0aW9uczpbLi4uXSB9LA0KICAgICAgICAgICAgICAgICAgIHsgbmFtZToncmFuZ2UnLCAgbGFiZWw6J0RhdGUnLCB0eXBlOidkYXRlUmFuZ2UnIH1dfQ0KICAgIGV4cG9ydGFibGU9e3sgZmlsZW5hbWU6J2VtcGxveWVlcy54bHN4JywgbW9kdWxlOidlbXBsb3llZXMnIH19DQogICAgY2FuQWRkIG9uQWRkPXsoKSA9PiBvcGVuQ3JlYXRlKCl9IHNlbGVjdGFibGUgb25CYXRjaERlbGV0ZT17ZGVsfQ0KICAgIHJlZnJlc2hLZXk9e3RpY2t9IGhpZ2hsaWdodFJvdz17aGlnaGxpZ2h0Um93fSB0b29sYmFyPXs8QnV0dG9uLz59IC8+DQo8RUNoYXJ0IG9wdGlvbj17b3B0aW9ufSBoZWlnaHQ9ezMwMH0gbG9hZGluZz17aXNMb2FkaW5nfSBvbkV2ZW50cz17eyBjbGljazogZm4gfX0gLz4NCjxTdGF0Q2FyZCB0aXRsZT0iUGVuZGluZyIgdmFsdWU9e259IGljb249ezxDbG9ja0NpcmNsZU91dGxpbmVkLz59IGNvbG9yPXtDSEFSVF9DT0xPUlNbMF19DQogICAgICAgICAgc3VmZml4PSIiIGhyZWY9Ii9sZWF2ZS9yZXF1ZXN0cyIgbG9hZGluZz17aXNMb2FkaW5nfSAvPg0KPEFzeW5jQnV0dG9uIHR5cGU9InByaW1hcnkiIHRhc2s9eyhzaWduYWwpID0+IGF4aW9zLnBvc3QoJy9hcGkveCcpfSBzdWNjZXNzVGV4dD0iU3VibWl0dGVkIj5TYXZlPC9Bc3luY0J1dHRvbj4NCmNvbnN0IHsgaGlnaGxpZ2h0Um93LCBmbGFzaFJvdyB9ID0gdXNlUm93Rmxhc2goKTsgICAvLyBmbGFzaFJvdyhyb3cuaWQpIGFmdGVyIHN1Y2Nlc3MNCjxBaVBhbmVsIHN5c3RlbT0iLi4uIiBzdWdnZXN0aW9ucz17Wy4uLl19IHRpdGxlPSIuLi4iIGhlaWdodD17NDIwfSBzaG93SGVhZGVyPXtmYWxzZX0gLz4NCmBgYA0KDQoqKldhaXQtZmVlZGJhY2sgdGhyZXNob2xkcyoqIGxpdmUgaW4gYHNyYy9saWIvcGVuZGluZy50c2AgKDIwMG1zIGxvY2FsIHNwaW5uZXIgLyAzcyBib3R0b20tcmlnaHQgb3ZlcmxheSAvIDhzIGVzY2FsYXRlZCBjb3B5KTsNCmNoYW5naW5nIHRoYXQgb25lIGZpbGUgYWZmZWN0cyB0aGUgd2hvbGUgc2l0ZS4gVGhlIGdsb2JhbCBmYWxsYmFjayBpcyBhbHJlYWR5IG1vdW50ZWQgaW4gYFByb3ZpZGVyc2Ag4oCUICoqcGFnZXMgZG8gbm90IG1hbmFnZSBpdCoqLg0KSWYgYSBwYWdlIGhhcyBpdHMgb3duIGZ1bGwgd2FpdCBVSSAoc3RhZ2VzICsgc2Vjb25kcyArIGNhbmNlbCksIHBhc3MgYHNpbGVudDogdHJ1ZWAgb24gdGhhdCByZXF1ZXN0IG9yIHRoZSBvdmVybGF5IHJlcG9ydHMgdGhlDQpzYW1lIHRoaW5nIHR3aWNlLg0KDQojIyA0LjYgVGhlIHN0YW5kYXJkIHNoYXBlIG9mIGEgYnVzaW5lc3MgbW9kdWxlIChjb3B5IHRoaXMsIGRvIG5vdCBpbnZlbnQpDQoNCioqQVBJIGBzcmMvYXBwL2FwaS88bW9kdWxlPi9yb3V0ZS50c2AsIHNpeCBibG9ja3MqKg0KDQpgYGANCuKRoCBleHBvcnQgY29uc3QgZHluYW1pYyA9ICdmb3JjZS1keW5hbWljJw0K4pGhIHNlbGVjdDogY29uc3QgeHh4U2VsZWN0ID0geyBpZDp0cnVlLCAuLi5idXNpbmVzc0ZpZWxkcywgZGVwdDp7c2VsZWN0OntpZDp0cnVlLG5hbWU6dHJ1ZX19IH0gc2F0aXNmaWVzIFByaXNtYS5YeHhTZWxlY3QNCuKRoiB6b2Qgc2NoZW1hOiBjb25zdCB4eHhTY2hlbWEgPSB6Lm9iamVjdCh7IGNvZGU6IHpGaWVsZCgnY29kZScse2xhYmVsOidDb2RlJ30pLCAuLi4gfSkNCiAgIGRhdGVzIHVzZSB6TnVsbGFibGVGaWVsZCgnZGF0ZSd8J2RhdGV0aW1lJyx7bGFiZWx9KTsgbnVtYmVycyB1c2Ugei5jb2VyY2UubnVtYmVyKCk7IG9wdGlvbmFsIHVzZXMgLm51bGxpc2goKQ0K4pGjIEdFVDogYXdhaXQgcmVxdWlyZVVzZXIoKSDihpIgcmVxdWlyZVBlcm0odXNlciwgUC54eHhMaXN0KSDihpIgY29uc3QgcSA9IHBhcnNlTGlzdFF1ZXJ5KHJlcSkNCiAgICAgICDihpIgd2hlcmUgPSB7IGRlbGV0ZWRBdDpudWxsLCAuLi5rZXl3b3JkIE9SIGNvbmRpdGlvbnMsIC4uLmZpbHRlcnMgfQ0KICAgICAgIOKGkiBPYmplY3QuYXNzaWduKHdoZXJlLCBkZXB0U2NvcGUodXNlcikpICAgLy8g4piFIGFsd2F5cyBsYXN0DQogICAgICAg4oaSIFByb21pc2UuYWxsKFtmaW5kTWFueSwgY291bnRdKSDihpIgb2socGFnZVJlc3VsdCguLi4pKQ0K4pGkIFBPU1Q6IHJlcXVpcmVQZXJtIOKGkiBzY2hlbWEuc2FmZVBhcnNlIOKGkiB1bmlxdWVuZXNzIGNoZWNrcyDihpIgcHJpc21hLmNyZWF0ZQ0KICAgICAgIOKGkiBhdWRpdCh7IG1vZHVsZTonQnVzaW5lc3MgTmFtZScsIGFjdGlvbjonQ3JlYXRlJywgdGFyZ2V0SWQ6IHJvdy5pZCwgLi4uY2xpZW50SW5mbyhyZXEpIH0pIOKGkiBvayhyb3cpDQrikaUgW2lkXS9yb3V0ZS50czogR0VUL1BVVC9ERUxFVEUgYWxsIHVzZSBmaW5kRmlyc3QoeyB3aGVyZTogeyBpZCwgZGVsZXRlZEF0Om51bGwsIC4uLmRlcHRTY29wZSh1c2VyKSB9IH0pDQogICAgICAg4oCUIGlmIG5vdGhpbmcgbWF0Y2hlcywgdGhyb3cgQml6RXJyb3IoJ25vdCBmb3VuZCBvciBubyBwZXJtaXNzaW9uJywgQ09ERS5OT1RfRk9VTkQsIDQwNCk7IGRlbGV0ZSA9IHVwZGF0ZSh7IGRlbGV0ZWRBdDogbmV3IERhdGUoKSB9KQ0KYGBgDQoNCioqUGFnZSBgc3JjL2FwcC8oYWRtaW4pLzxtb2R1bGU+L3BhZ2UudHN4YCwgc2V2ZW4gYmxvY2tzKioNCg0KYGBgDQrikaAgJ3VzZSBjbGllbnQnDQrikaEgY29sdW1uczogQ29sdW1uc1R5cGU8VD4gKHN0YXR1cyBjb2x1bW4gdXNlcyBUYWcgKyBNSUxJVEFSWSBzZW1hbnRpY3M7IGFjdGlvbiBjb2x1bW46IGVkaXQvZGVsZXRlL2xpbmsvZmVlZGJhY2spDQrikaIgc2VhcmNoRmllbGRzIChrZXl3b3JkICsgc3RhdHVzIHNlbGVjdCArIGRlcGFydG1lbnQvY2F0ZWdvcnkgKyBkYXRlIHJhbmdlKQ0K4pGjIE1vZGFsICsgRm9ybSAob25lIGZvcm0gZm9yIGNyZWF0ZSBhbmQgZWRpdCwgZGlzdGluZ3Vpc2hlZCBieSBgZWRpdGluZ2A7IHJ1bGVzIGZyb20gYXV0b0Zvcm1SdWxlcykNCuKRpCBEYXRhVGFibGUgYXNzZW1ibHkgKGFwaSAvIGNvbHVtbnMgLyBzZWFyY2hGaWVsZHMgLyBleHBvcnRhYmxlIC8gY2FuQWRkIC8gc2VsZWN0YWJsZSAvIGhpZ2hsaWdodFJvdykNCuKRpSBhZnRlciBhbnkgbXV0YXRpb246IG1lc3NhZ2Uuc3VjY2VzcyArIHNldFJlZnJlc2hLZXkodj0+disxKSArIGZsYXNoUm93KHJvdy5pZCkNCuKRpiBlbXB0eSBzdGF0ZTogYW50ZCA8RW1wdHk+IHBsdXMgb25lIGd1aWRpbmcgc2VudGVuY2U7IG5ldmVyIGEgYmxhbmsgcGFnZQ0KYGBgDQoNCmB1c2VSb3dGbGFzaGAncyBgaGlnaGxpZ2h0Um93YCBvbmx5IGZsYXNoZXMgYWZ0ZXIgdGhlIGRhdGEgaGFzIGFjdHVhbGx5IGJlZW4gcmVmZXRjaGVkIChpdCB3YXRjaGVzIGBkYXRhVXBkYXRlZEF0YCksIHNvIHlvdSBkbw0Kbm90IG5lZWQgdG8gc2VxdWVuY2UgaXQgeW91cnNlbGYuDQoNCi0tLQ0KDQojIFBhcnQgNSDCtyBXaXJpbmcgZWFjaCBzdWJzeXN0ZW0NCg0KIyMgNS4xIEJ1c2luZXNzIG1vZHVsZXMgYW5kIGV4cG9ydA0KDQpTZWUgUGFydCAzIHJ1bGUgMiBhbmQgUGFydCA0LjYuIFRocmVlIGxpbmVzIHRvIHJlbWVtYmVyOiAqKm9ubHkgZm91ciBwbGFjZXMqKjsgKiphIGxpc3Qgd2l0aCBFeHBvcnQgbmVlZHMgYSBicmFuY2ggaW4NCmBhcGkvZXhwb3J0L3JvdXRlLnRzYCoqOyAqKmNvcHkgZW1wbG95ZWVzKiouDQoNCiMjIDUuMiBEYXRhIHBlcm1pc3Npb25zDQoNCkZ1bmN0aW9uYWwgcGVybWlzc2lvbnMgZGVjaWRlIHdoaWNoIHBhZ2VzIHlvdSBtYXkgb3BlbjsgZGF0YSBwZXJtaXNzaW9ucyBkZWNpZGUgd2hpY2ggcm93cyBvZiBhIHRhYmxlIHlvdSBzZWUNCihSb2xlIOKGkiBBc3NpZ24gcGVybWlzc2lvbnMg4oaSICoqRGF0YSBzY29wZSoqIGF0IHRoZSB0b3ApLiBGb3VyIHRpZXJzOiAxIEFsbCAvIDIgT3duIGRlcGFydG1lbnQgYW5kIGJlbG93IC8gMyBPd24gZGVwYXJ0bWVudCAvDQo0IE93biByZWNvcmRzIG9ubHkuIFNldmVyYWwgcm9sZXMgdGFrZSB0aGUgd2lkZXN0IHRpZXI7IGEgc3VwZXIgYWRtaW4gaXMgYWx3YXlzICJBbGwiOyBjaGFuZ2VzIHRha2UgZWZmZWN0IGFmdGVyIHJlLWxvZ2luLg0KDQpgYGB0cw0KLy8gTGlzdCBHRVQgLyBleHBvcnQ6IGFmdGVyIGFsbCBidXNpbmVzcyBmaWx0ZXJzDQpPYmplY3QuYXNzaWduKHdoZXJlLCBkZXB0U2NvcGUodXNlcikpOw0KLy8gIk93biByZWNvcmRzIG9ubHkiIHNlbWFudGljcyAobXkgcmVxdWVzdHMgLyBleHBlbnNlcyAvIHJlcGFpcnMpDQpPYmplY3QuYXNzaWduKHdoZXJlLCBkZXB0U2NvcGUodXNlciwgeyBvd25lckZpZWxkOiAnYXBwbGljYW50SWQnIH0pKTsNCg0KLy8gRGV0YWlsIC8gdXBkYXRlIC8gZGVsZXRlIG11c3QgYmUgc2NvcGVkIHRvbw0KY29uc3Qgcm93ID0gYXdhaXQgcHJpc21hLmxlYXZlLmZpbmRGaXJzdCh7IHdoZXJlOiB7IGlkLCBkZWxldGVkQXQ6IG51bGwsIC4uLmRlcHRTY29wZSh1c2VyKSB9IH0pOw0KYGBgDQoNCioqSGFyZCBydWxlcyoqOiBuZXZlciBmaWx0ZXIgb24gdGhlIGNsaWVudDsgZGFzaGJvYXJkLCBjaGFydHMsIGV4cG9ydHMsIHRoZSBncmFwaCAoaW5jbHVkaW5nIHRoZSBkZXRhaWwgcGFuZWwgYW5kIHRoZQ0KZGVsZXRlLWltcGFjdCBwcmV2aWV3KSwgQUkgcXVlcmllcyBhbmQgQUkgY3JlYXRlcywgYW5kIHVwbG9hZGVkLWZpbGUgZG93bmxvYWRzIG11c3QgYWxsIHBhc3MgdGhlIHNhbWUgc2NvcGU7IHRhYmxlcyB3aXRob3V0DQpgZGVwdElkYCBuYXJyb3cgYnkgYnVzaW5lc3MgbWVhbmluZyAoZS5nLiBub24tc3VwZXIgdXNlcnMgc2VlIG9ubHkgYHVzZXJJZCA9IHRoZW1zZWx2ZXNgKS4NCmBkZXB0U2NvcGVgIG11c3QgYmUgbWVyZ2VkICoqYWZ0ZXIqKiB0aGUgYnVzaW5lc3MgZmlsdGVycyDigJQgbWVyZ2VkIGJlZm9yZSwgaXQgaXMgb3ZlcndyaXR0ZW4gYnkgYSBsYXRlciBgd2hlcmUuZGVwdElkYCBhbmQNCmA/ZGVwdElkPWAgYmVjb21lcyBhIHByaXZpbGVnZSBlc2NhbGF0aW9uLg0KDQojIyA1LjMgR2xvYmFsIHNlYXJjaA0KDQpGZWF0dXJlIGxheWVyID0gdGhlIG1lbnUgdHJlZSAocmVnaXN0ZXIgaW4gc2VlZCBhbmQgaXQgaXMgc2VhcmNoYWJsZSDigJQgemVybyBjb2RlKTsgZGF0YSBsYXllciBpcyBkcml2ZW4gYnkgYG9udG9sb2d5LnRzYCwNCndpdGggdGhyZWUgc2VydmVyLXNpZGUgZ2F0ZXMgaW4gYHNyYy9saWIvc2VhcmNoLWRhdGEudHNgIChwYWdlIGdhdGUsIGRlcGFydG1lbnQgZ2F0ZSwgZmllbGQgZ2F0ZSkuIERvIG5vdCByZWltcGxlbWVudA0KZmlsdGVyaW5nIG9uIHRoZSBjbGllbnQuDQoNCldoYXQgeW91IG11c3QgZG86IGdpdmUgbWVudXMgYGtleXdvcmRzYCAod2hhdCB1c2VycyBhY3R1YWxseSB0eXBlKTsgZ2l2ZSBldmVyeSBlbnRpdHkgYSBgbGlua2AgKGAnL3g/aWQ9OmlkJ2Agb3BlbnMgdGhlDQpyZWNvcmQsIGAnL3g/a3c9Omt3J2Agb3Igbm8gcGxhY2Vob2xkZXIgZmlsdGVycyB0aGUgbGlzdCk7IGhhbmQtd3JpdHRlbiBwYWdlcyBtdXN0IHJlYWQgYD9rdz1gIC8gYD9pZD1gOyBgdGl0bGVGaWVsZGAgc2hvdWxkDQpiZSBhIHBlcnNvbiwgdGl0bGUgb3IgcGxhdGUgbnVtYmVyLCBub3QgbG9uZyB0ZXh0Lg0KDQojIyA1LjQgS25vd2xlZGdlIGdyYXBoDQoNCkNoYW5nZSBvbmx5IGBzcmMvbGliL29udG9sb2d5LnRzYC4gVGhlIGVuZ2luZSAoYGFwaS9ncmFwaC8qYCksIHJlbmRlcmVyIChgT250b2xvZ3lHcmFwaC50c3hgKSBhbmQgcGFnZSAoYGdyYXBoL3BhZ2UudHN4YCkgYXJlDQpnZW5lcmljIOKAlCBsZWF2ZSB0aGVtIGFsb25lLiBUd28gcGFyYWxsZWwgbGluZXMgYmV0d2VlbiBvbmUgcGFpciBvZiBub2RlcyBtZWFuIGBhcGkvZ3JhcGgvcm91dGUudHNgIGlzIHRoZSBvbGQNCmRpcmVjdGlvbi1kZWR1cGxpY2F0aW5nIHZlcnNpb247IHJlcGxhY2UgaXQgd2l0aCB0aGUgbGF0ZXN0IHNjYWZmb2xkIGZpbGUuDQoNCioqTWluZSByZWxhdGlvbnMgZmlyc3QsIHRoZW4gd3JpdGUgb250b2xvZ3kqKjogYG5wbSBydW4gcmVsYXRpb25zYCByZXBvcnRzIGRlY2xhcmVkIC8gdW5kZWNsYXJlZCAvIGJyaWRnZSB0YWJsZXMgLyBtaXNzaW5nDQp2aWV3IGxpbmtzLiBFdmVyeSBmb3JlaWduIGtleSBpcyBlaXRoZXIgYSBkZWNsYXJlZCByZWxhdGlvbiBvciBjb21tZW50ZWQgaW4gYG9udG9sb2d5LnRzYCBleHBsYWluaW5nIHdoeSAoc2VsZi1yZWZlcmVuY2luZw0KYHBhcmVudElkYCBhbmQgcHVyZSBhdWRpdCBmaWVsZHMgYXJlIGFjY2VwdGFibGUgZXhjZXB0aW9ucyk7IGFueSBtb2RlbCB3aXRoIHR3byBmb3JlaWduIGtleXMgaXMgYSBicmlkZ2UgKGluY2x1ZGluZw0KYnVzaW5lc3MtZmllbGQgYnJpZGdlcyBzdWNoIGFzIHRyYW5zY3JpcHRzKSDigJQgY29uZmlndXJlIGl0IHdpdGggYHZpYS5tb2RlbGA7IGEgdmlldyB0aGF0IHNob3VsZCBiZSBjb25uZWN0ZWQgYnV0IGlzIG5vdCBtYWtlcw0KYGNoZWNrOmNvbmZpZ2AgZmFpbCwgYW5kIHNwbGl0dGluZyB2aWV3cyB0byBkb2RnZSB0aGUgY2hlY2sgaXMgZm9yYmlkZGVuLg0KDQpDb25maWd1cmF0aW9uOiBldmVyeSBlbnRpdHkgaGFzIGEgYGxheWVyYCAoc21hbGxlciBnb2VzIGxlZnQ6IG9yZ2FuaXphdGlvbiDihpIgcGVyc29uIOKGkiBkb2N1bWVudCk7IGBtb2RlbGAgbXVzdCBiZSBhIHJlYWwgc2NoZW1hDQptb2RlbDsgYHRpdGxlRmllbGRgIOKJpDggQ2hpbmVzZSBjaGFyYWN0ZXJzOyBldmVyeSB2aWV3IG11c3QgZGVjbGFyZSBgcm9vdGAsIGFuZCBgcm9vdGAgbXVzdCBiZSBvbmUgb2YgdGhhdCB2aWV3J3MgYGVudGl0aWVzYA0KKG90aGVyd2lzZSB0aGUgZGVmYXVsdCBjZW50cmUgbm9kZSBjYW5ub3QgYmUgZHJhd24gYW5kIGBjaGVjazpjb25maWdgIGZhaWxzKTsgYGNvbG9yYCBtdXN0IGJlIGxpZ2h0IGFuZCBpbiB0aGUgdGhlbWUgZmFtaWx5DQooYCNGMkYwRTRgIGAjRTlFRkRGYCBgI0Y0RUVERmAgYCNFRkVERTZgLCBgYm9yZGVyQ29sb3JgIG9uZSBzaGFkZSBkZWVwZXIpOyBuZXZlciB1c2Ugc2F0dXJhdGVkIGNvbG9ycyBzdWNoIGFzIGAjMTY3N2ZmYA0KYCM1MmM0MWFgIGAjZmE4YzE2YC4gTGF5b3V0IGlzIHByZWNvbXB1dGVkIGJ5IGBmb3JjZUxheW91dGAg4oCUIGRvIG5vdCByZXZlcnQgdG8gYGxheW91dDonZm9yY2UnYDsgaG92ZXIgbXVzdCBub3QgdG91Y2ggUmVhY3QNCnN0YXRlOyBzZWxlY3Rpb24gZ29lcyB0aHJvdWdoIGBkaXNwYXRjaEFjdGlvbmAuDQoNCkRhdGE6IDIwfjYwIG5vZGVzLCBhdCBsZWFzdCAxIGVkZ2UgcGVyIG5vZGUsIGZvcmVpZ24ga2V5cyByZWFsbHkgZmlsbGVkLCBubyBjb2x1bW4gYWJvdmUgMTUgbm9kZXM7IHNwbGl0IHZpZXdzIHdoZW4gaXQgZ3Jvd3MNCihgTUFYX05PREVTID0gMTIwYCkuICoqTm8gdmlldyBtYXkgY29udGFpbiBhbiBpc29sYXRlZCBub2RlKiogKGRlcGFydG1lbnQgc2VsZi1yZWZlcmVuY2UgdXNlcyBgZGVwdF9wYXJlbnRgOyByb2xlcyBvbmx5IGVudGVyDQp2aWV3cyBsaWtlIGBhY2Nlc3NgKS4NCg0KRGVsaXZlcnkgY2hlY2tzOiBgL2dyYXBoYCBsYWJlbHMgcmVhZGFibGUsIG5vIHJlZCBiYW5uZXIsIG5vIHBhcmFsbGVsIGxpbmVzLCBubyBjbGlwcGVkIG5vZGVzOyByZW5hbWUvZGVsZXRlIHJvdW5kLXRyaXBzDQpwZXJzaXN0IHdpdGggYW4gaW1wYWN0IGNvbmZpcm1hdGlvbiAobm9kZSBpZCBpcyBgZW50aXR5OmRiSWRgLCB3cml0ZSBBUElzIHdhbnQgYGRiSWRgKS4gQSBsaXN0LXBhZ2UgInJlbGF0ZWQiIGJ1dHRvbiB1c2VzDQpgZ3JhcGhIcmVmKCdNb2RlbCcsIHJvdy5pZClgLg0KDQojIyA1LjUgQUkgYXNzaXN0YW50DQoNCkltcGxlbWVudGF0aW9uIGRldGFpbHMgbGl2ZSBpbiBjb2RlOiBgYWlUb29scy50c2AgKHRvb2wgZ2VuZXJhdGlvbiBhbmQgc2VjdXJpdHkgbW9kZWwpLCBgYWlDb250ZXh0LnRzYCAodGltZS9zYW1wbGVzKSwNCmBhaUZvcm0udHNgIChtaXNzaW5nLXBhcmFtIGZvcm0pLCBgYWlUb2tlbi50c2AgKHNpZ25lZCB0b2tlbnMpLCBgc3JjL2FwcC9hcGkvYWkvKmAsIGBjb21wb25lbnRzL0FpUGFuZWwudHN4YC4NCg0KQmVoYXZpb3IgdGhhdCBtdXN0IG5vdCBiZSB3cm9uZzoNCg0KLSAqKkV4YWN0bHkgb25lIGNoYXQgYm94IHBlciBwYWdlKio7IHdoZW4gYDxBaVBhbmVsIC8+YCBzaXRzIGluIGEgQ2FyZCB0aGF0IGFscmVhZHkgaGFzIGEgdGl0bGUsIHBhc3MgYHNob3dIZWFkZXI9e2ZhbHNlfWA7DQogIGdpdmUgYm90aCBjYXJkcyBvbiBhIHBhZ2UgdGhlIHNhbWUgYGhlaWdodGAuDQotIGBjb21wYXRJbnZva2VgIGlzIHNlcnZlci1zaWRlIG9ubHkg4oCUIG5ldmVyIGltcG9ydCBpdCBpbiBicm93c2VyIGNvZGU7IHRoZSBBUEkga2V5IG5ldmVyIHJlYWNoZXMgdGhlIGJyb3dzZXI7IGRvIG5vdCBkcm9wDQogIGByZWFzb25pbmc6IHsgZWZmb3J0Oidub25lJyB9YCB3aGVuIHN3YXBwaW5nIG1vZGVscy4NCi0gVG9vbHMgYXJlIGdlbmVyYXRlZCBmcm9tIGBvbnRvbG9neS50c2AgKyBgc2NoZW1hLnByaXNtYWA7IHdyaXRlIG5vIHRvb2wgY29kZS4gT25seSBtdWx0aS1zdGVwIGJ1c2luZXNzIGFjdGlvbnMgYXBwZW5kIHRvDQogIGBFWFRSQV9UT09MU2AuDQotIFRoZSB0aHJlZS1zdGVwIHdyaXRlIGZsb3cgaXMgbWFuZGF0b3J5OiBtaXNzaW5nIHBhcmFtcyDihpIgYXV0byBmb3JtOyBjb21wbGV0ZSBwYXJhbXMg4oaSIEhNQUMgY29uZmlybWF0aW9uIGNhcmQ7IGFmdGVyDQogIGNvbmZpcm1hdGlvbiBgL2FwaS9haS9hY3RgIHBlcnNpc3RzIGFuZCB3cml0ZXMgYXVkaXQuIFRoZSBmcm9udGVuZCBwYXNzZXMgb25seSB0aGUgdG9rZW4sIG5ldmVyIHBhcmFtcy4gKFRoZSBgL2FwaS9haS9hY3RgDQogIGNhbGwgaW5zaWRlIGBBaVBhbmVsYCBhbmQgdGhlIHRva2VuIGNoZWNrIGluIHRoZSBgYWN0YCByb3V0ZSBhcmUgaGFyZCBgY2hlY2s6Y29uZmlnYCBpdGVtcyDigJQgcmVtb3ZpbmcgZWl0aGVyIGZhaWxzIHRoZSBidWlsZC4pDQotIEFnZ3JlZ2F0ZXMgbXVzdCBnbyB0aHJvdWdoIGBxdWVyeV9gIHRvb2xzOyB0aGUgNi1yb3cgc2FtcGxlIHBlciB0YWJsZSBpcyBzaGFwZSBvbmx5LCBub3QgYSBkYXRhIHNvdXJjZS4NCg0KRml2ZSB0aGluZ3MgeW91IG11c3QgZG86DQoNCjEuIFJlcGxhY2UgYEFTS19TQU1QTEVTYCBpbiBgc3JjL2FwcC8oYWRtaW4pL2FpL3BhZ2UudHN4YCB3aXRoIHJlYWwgcXVlc3Rpb25zIGZyb20gdGhlIHRhc2sgKG9uZSBkYXRhLCBvbmUgdXNhZ2UsIG9uZSBhY3Rpb24pLg0KMi4gU2FtZSBmaWxlOiByZXBsYWNlIGBSRVBPUlRfVEFCTEVTYCDigJQgaXQgc3RpbGwgbGlzdHMgYEVtcGxveWVlYCAvIGBPcGVyYXRpb25Mb2dgICgiZW1wbG95ZWUgKHNhbXBsZSkiKS4gUG9pbnQgaXQgYXQgdGhlDQogICB0YXNrJ3MgbWFpbiBidXNpbmVzcyBtb2RlbCBhbmQgZ2l2ZSB0aGUgcmVwb3J0IGEgYnVzaW5lc3MgdGl0bGUsIG90aGVyd2lzZSB0aGUgZHJvcGRvd24gaXMgYW4gb2J2aW91cyBzY2FmZm9sZCBsZWZ0b3Zlci4NCjMuIFRhYmxlcyBkZWNsYXJlZCBpbiBgc3JjL2xpYi9vbnRvbG9neS50c2AgYXJlIGV4YWN0bHkgd2hhdCB0aGUgQUkgY2FuIHF1ZXJ5L2NyZWF0ZTsgdW5kZWNsYXJlZCB0YWJsZXMgYXJlIGludmlzaWJsZS4NCjQuIEF0IGxlYXN0IDE1IHJvd3MgcGVyIGJ1c2luZXNzIHRhYmxlLCBkYXRlcyBzcHJlYWQgb3ZlciB0aGUgbGFzdCB0d28gb3IgdGhyZWUgbW9udGhzLg0KNS4gRHVyaW5nIGFjY2VwdGFuY2UsIGFjdHVhbGx5IGNsaWNrIHRoZSBjb25maXJtYXRpb24gY2FyZCBvbmNlLg0KDQojIyA1LjYgSW5ib3gsIG1lc3NhZ2VzIGFuZCBzaWRlYmFyIGJhZGdlcw0KDQpDb2RlOiBgc3JjL2xpYi9pbmJveC50c2AgKHN1bW1hcnkvZ3JvdXBpbmcpLCBgc3JjL2xpYi90b2Rvcy9gIChzb3VyY2UgcmVnaXN0cnkgKyBmZWVkYmFjayBleGFtcGxlKSwgYHNyYy9saWIvbm90aWZpY2F0aW9ucy50c2ANCihpbi1hcHAgbWVzc2FnZXMpLCBgKGFkbWluKS9pbmJveC9gLCBgY29tcG9uZW50cy9pbmJveC9gLCBgYXBpL2luYm94LypgLiBUaGUgdG9wLWJhciBiZWxsIGFuZCB0aGUgc2lkZWJhciBlbnRyeSBhcmUgd2lyZWQuDQoNCioqVG9kb3MgYXJlIG5vdCBwZXJzaXN0ZWQqKiDigJQgdGhleSBhcmUgY29tcHV0ZWQgbGl2ZSBmcm9tIHRoZSByZWdpc3RyeSBhbmQgZGlzYXBwZWFyIHdoZW4gdGhlIGJ1c2luZXNzIGl0ZW0gaXMgaGFuZGxlZCwgc28gZG8NCm5vdCBhZGQgYSBtYW51YWwgImRvbmUiIGJ1dHRvbi4NCg0KLSAqKlNvbWV0aGluZyB0aGUgdXNlciBtdXN0IGFjdCBvbiDihpIgdG9kbyoqOiBhZGQgYSBgPG1vZHVsZT4udHNgIHVuZGVyIGBzcmMvbGliL3RvZG9zL2AgaW1wbGVtZW50aW5nDQogIGBrZXkgLyBuYW1lIC8gZGVzY3JpcHRpb24/IC8gaWNvbiAvIG9yZGVyIC8gdmlzaWJsZSh1c2VyKSAvIGNvdW50KHVzZXIpIC8gbGlzdCh1c2VyLCBsaW1pdClgLCB0aGVuIHJlZ2lzdGVyIGl0IGluDQogIGBUT0RPX1NPVVJDRVNgIGluIGBpbmRleC50c2AuIEl0ZW1zIGZyb20gYGxpc3RgIG11c3QgaW5jbHVkZSBhIGBsaW5rYCB0aGF0IHRhcmdldHMgdGhlIHNwZWNpZmljIHJlY29yZCAoYC94P2lkPS4uLmApLCBub3QNCiAganVzdCB0aGUgbGlzdCBwYWdlLg0KLSAqKlNvbWV0aGluZyB0aGUgdXNlciBvbmx5IG5lZWRzIHRvIGtub3cg4oaSIG1lc3NhZ2UqKjogYG5vdGlmeSh7IHVzZXJJZCB8IHVzZXJJZHMsIHRpdGxlLCBib2R5PywgdHlwZT8sIGxldmVsPywgbGluaz8gfSlgLg0KLSAqKk5ldmVyIGNyZWF0ZSBib3RoIGEgdG9kbyBhbmQgYSBtZXNzYWdlIGZvciB0aGUgc2FtZSBldmVudCoqLCBvciB0aGUgdXNlciBpcyByZW1pbmRlZCB0d2ljZS4gUnVsZSBvZiB0aHVtYjogbmVlZHMgYWN0aW9uIOKGkg0KICB0b2RvOyBhd2FyZW5lc3Mgb25seSDihpIgbWVzc2FnZS4NCi0gRG8gbm90IG1vZGlmeSB0aGUgYmVsbCwgdGhlIGAvaW5ib3hgIHBhZ2UsIHRoZSBtZXNzYWdlIHNlcnZpY2Ugb3IgdGhlIGFnZ3JlZ2F0aW9uIGxvZ2ljOyBvbmx5IHJlZ2lzdGVyIHNvdXJjZXMgYW5kIGNhbGwNCiAgYG5vdGlmeSgpYC4NCg0KKipTaWRlYmFyIGJhZGdlcyoqIChtb2R1bGVzIHdpdGggIndhaXRpbmcgZm9yIG15IGFwcHJvdmFsIC8gcGVuZGluZyAvIHVucmVhZCIgc2VtYW50aWNzIG11c3Qgc2hvdyBhIHJlZCBudW1iZXIpIOKAlCB0aHJlZSBzdGVwczoNCg0KMS4gQXBwZW5kIGB7IGtleTonbW9kdWxlOnNlbWFudGljcycsIGxhYmVsLCBoaW50IH1gIHRvIGBCQURHRV9TT1VSQ0VTYCBpbiBgc3JjL2xpYi9iYWRnZXMudHNgOyB0aGUga2V5IGlzIGdsb2JhbGx5IHVuaXF1ZS4NCjIuIEFkZCBhIHNhbWUta2V5IGNvdW50ZXIgdG8gYENPVU5URVJTYCBpbiBgc3JjL2xpYi9iYWRnZS1jb3VudC50c2AgKGBjaGVjazpjb25maWdgIGNyb3NzLWNoZWNrcyBrZXlzKS4gUGVuZGluZyBzZW1hbnRpY3MNCiAgIGltcGxlbWVudCBgQ09VTlRFUlNgIG9ubHk7IHVucmVhZCBzZW1hbnRpY3MgYWxzbyBhZGQgYW4gYEFDS0VSU2AgaGFuZGxlci4gT25lIG1lbnUgc2hvd3Mgb25lIG51bWJlciDigJQgYnJhbmNoIGJ5IHJvbGUgaW5zaWRlDQogICB0aGUgY291bnRlci4NCjMuIEdpdmUgdGhlIG1lbnUgYSBgYmFkZ2VLZXlgIGluIGBzZWVkLnRzYC4NCg0KIyMgNS43IEhlbHAgY2VudGVyDQoNCkNvZGU6IGBzcmMvbGliL2RvY3MudHNgIChzY2FuL2luZGV4KSwgYHNyYy9saWIvZG9jcy1zaGFyZWQudHNgIChmcm9udCBtYXR0ZXIgLyBhbmNob3JzIC8gVE9DIC8gc2NvcmluZyksIGBzcmMvYXBwL2RvY3MvKipgDQooc3RhbmRhbG9uZSBkb2NzIHJvdXRlKSwgYHNyYy9jb21wb25lbnRzL2RvY3MvKipgLiBFbnRyaWVzIGFyZSB3aXJlZDogdGhlICJoZWxwIiBzaWRlYmFyIG1lbnUgYW5kIHRoZSB0b3AtYmFyICI/IiBidXR0b247DQpgL2RvY3NgIGxpdmVzIG91dHNpZGUgdGhlIGFkbWluIHNoZWxsIGFuZCBpcyBhdmFpbGFibGUgdG8gZXZlcnkgbG9nZ2VkLWluIHVzZXI7IHRoZSBoZWFkZXIgcmVhZHMgYEFQUF9OQU1FYC4NCg0KKipDb250ZW50IGlzIHRoZSBuYXZpZ2F0aW9uKio6IGBjb250ZW50L2RvY3MvKiovKi5tZGAgYXV0b21hdGljYWxseSBiZWNvbWVzIGEgZG9jcyBwYWdlLCBzdWJkaXJlY3RvcmllcyBiZWNvbWUgZ3JvdXBzLCBhbmQNCmFkZGluZyBhIHBhZ2UgbmVlZHMgbm8gY29kZSBjaGFuZ2UuDQoNCi0gQSBzdWJkaXJlY3RvcnkgbWF5IGNvbnRhaW4gYF9tZXRhLmpzb25gOiBgeyAidGl0bGUiOiAiLi4uIiwgImRlc2NyaXB0aW9uIjogIi4uLiIsICJvcmRlciI6IDIgfWANCi0gUGFnZSBmcm9udCBtYXR0ZXI6IGB0aXRsZWAgLyBgZGVzY3JpcHRpb25gIC8gYG9yZGVyYCAvIGB0YWdzYCAvIGB1cGRhdGVkYCAvIGBoaWRkZW5gDQotIFN0YXJ0IHRoZSBib2R5IGF0IEgyICh0aGUgZmlyc3QgSDEgaXMgc3RyaXBwZWQpOyB1c2UgZnVsbCBwYXRocyBmb3IgaW50ZXJuYWwgbGlua3MgYFtBY2NvdW50XSgvZG9jcy9ndWlkZS9hY2NvdW50KWANCi0gVGhlIGhvbWUgcGFnZSdzICJxdWljayByZWZlcmVuY2UiIHJlYWRzIHRoZSBtZW51IHRyZWUgZGlyZWN0bHk6IHJlZ2lzdGVyaW5nIGEgbWVudSBtYWtlcyBpdCBhcHBlYXIgYXV0b21hdGljYWxseS4NCiAgKipEbyBub3QgbWFpbnRhaW4gYSBzZWNvbmQgZmVhdHVyZSBsaXN0LioqDQoNCioqQWRkIG9uZSBkb2MgcGFnZSBwZXIgYnVzaW5lc3MgbW9kdWxlKio6IGdyb3VwIHBhZ2VzIHVuZGVyIGBjb250ZW50L2RvY3MvYCAoY3JlYXRlIGEgZGlyZWN0b3J5IHBsdXMgYF9tZXRhLmpzb25gIHdoZW4NCnVzZWZ1bDsgZ2VuZXJhbCBmZWF0dXJlcyBjYW4gbGl2ZSBpbiBgZ3VpZGUvYCBgZmVhdHVyZXMvYCkuIENvdmVyIHRoZSBlbnRyeSBwb2ludCwgdHlwaWNhbCBvcGVyYXRpb25zLCBmaWVsZCBleHBsYW5hdGlvbnMgYW5kDQpjb21tb24gcXVlc3Rpb25zLCB1c2luZyB0aGUgdGFzaydzIHJlYWwgYnVzaW5lc3MgbmFtZXMsIGRlcGFydG1lbnRzLCBmaWVsZHMgYW5kIHJvbGVzLg0KQWxzbyByZXBsYWNlIGxlZnRvdmVyIGV4YW1wbGUgd29yZHMgaW4gdGhlIGdlbmVyaWMgZG9jcyAoYEVtcGxveWVlYCAvIGBMZWF2ZWAgLyBgQnVzaW5lc3MgRXhhbXBsZWApLCBlc3BlY2lhbGx5IGluDQpgY29udGVudC9kb2NzL3F1aWNrLXN0YXJ0Lm1kYCwgYHNob3J0Y3V0cy5tZGAsIGBmZWF0dXJlcy9haS5tZGAuDQoNCioqRG8gbm90IG1vZGlmeSoqIGBzcmMvbGliL2RvY3MudHNgLCBgZG9jcy1zaGFyZWQudHNgLCBgc3JjL2FwcC9kb2NzLyoqYCwgYHNyYy9jb21wb25lbnRzL2RvY3MvKipgIOKAlCB0aGV5IHNoYXJlIHRoZSBtZW51IHRyZWUsDQpwZXJtaXNzaW9ucyBhbmQgZ2xvYmFsIHNlYXJjaCBkYXRhIHNvdXJjZXM7IGJyZWFraW5nIHRoZW0gY2F1c2VzIGAvZG9jc2AgNDA0cyBvciBhIHdoaXRlIHNjcmVlbi4gT25seSBlZGl0IE1hcmtkb3duIGFuZA0KYF9tZXRhLmpzb25gLg0KDQojIyA1LjggU3lzdGVtIGFkbWluaXN0cmF0aW9uOiB0aGUgZm91ci1waWVjZSBzZXQNCg0KQSBoYXJkIGRlbGl2ZXJhYmxlLiBCdWlsdC1pbiBjb2RlOiBgKGFkbWluKS9zeXN0ZW0vZGVwdHMvcGFnZS50c3hgLCBgYXBpL2RlcHRzL3JvdXRlLnRzYCwgYGFwaS9kZXB0cy9baWRdL3JvdXRlLnRzYCwNCmBzeXN0ZW06ZGVwdDoqYCBpbiBgcmJhYy50c2AsIHRoZSBtZW51IGluIGBzZWVkLnRzYCwgYHNjcmlwdHMvY2hlY2stY29uZmlnLm1qc2AgKGEgbWlzc2luZyBkZXBhcnRtZW50IG1vZHVsZSBmYWlscyB0aGUgYnVpbGQpLg0KRGVsZXRlIHByb3RlY3Rpb24sIHVuaXF1ZSBjb2RlcywgY3ljbGUgcHJldmVudGlvbiBhbmQgdHJlZSBkaXNwbGF5IGFyZSB2ZXJpZmllZCDigJQgZG8gbm90IG92ZXJ0dXJuIHRoZW0uDQoNCldoZW4gd2lyaW5nIGJ1c2luZXNzLCBjaGFuZ2Ugb25seSBkYXRhIGFuZCBjb3B5OiByZW5hbWUgdGhlIG9yZ2FuaXphdGlvbmFsIGRpbWVuc2lvbiAoZmxlZXQgLyBjcmV3IC8gc2VjdGlvbiAvIHdhcmVob3VzZSAvDQpwcm9qZWN0IGdyb3VwKTsgKipkZXBhcnRtZW50cyBsaXZlIG9ubHkgaW4gYE9SR19UUkVFYCBpbiBgcHJpc21hL29yZy5jb25maWcudHNgKiog4oCUIGNvcHkgdGhlIGJyaWVmIHZlcmJhdGltLCBoaWVyYXJjaHkNCmluY2x1ZGVkOyBwZW9wbGUgZ28gdG8gbGVhZiBkZXBhcnRtZW50cyBvbmx5OyBuZXZlciBjYWxsIGBwcmlzbWEuZGVwYXJ0bWVudC5jcmVhdGVgIGluIGBzZWVkLnRzYDsgYWZ0ZXIgZWRpdGluZyAqKnlvdSBtdXN0IHJ1bg0KYG5wbSBydW4gZGI6cmVzZXRgKiogKGBkYjpzZWVkYCBvbmx5IHVwc2VydHMsIHNvIHN0YWxlIGRlcGFydG1lbnRzIGxpbmdlciBhbmQgY2FwdHVyZSBwYXJ0IG9mIHRoZSByb3N0ZXIpLg0KYFBFT1BMRV9UT1RBTGAgLyBgUEVPUExFX01JTl9QRVJfREVQVGAgYXJlIGluIHRoZSBzYW1lIGZpbGUuIFNpZGViYXIgb3JkZXI6IERlcGFydG1lbnQg4oaSIFVzZXIg4oaSIFJvbGUg4oaSIE1lbnUg4oaSIE9wZXJhdGlvbiBMb2cuDQoNCiMjIDUuOSBTeXN0ZW0gaWRlbnRpdHkgYW5kIGRhc2hib2FyZA0KDQpIYXJkIGl0ZW1zIGluIGBucG0gcnVuIGNoZWNrOmNvbmZpZ2A6DQoNCnwgSXRlbSB8IFJlcXVpcmVtZW50IHwNCnwgLS0tIHwgLS0tIHwNCnwgYEFQUF9OQU1FYCB8IGVxdWFscyB0aGUgdGFzaydzIHN5c3RlbSBuYW1lLCBub3QgdGhlIHNjYWZmb2xkIGRlZmF1bHQgfA0KfCBgQVBQX0RFU0NgIHwgYnVzaW5lc3MgcG9zaXRpb25pbmcsIHdpdGhvdXQgTmV4dC5qcyAvIGFudGQgLyBQcmlzbWEgfA0KfCBgQVBQX1NVQlRJVExFYCB8IG9uZSBsaW5lIG9mIGJ1c2luZXNzIHBvc2l0aW9uaW5nLCBub3QgImJ1aWx0LWluIFJCQUPigKYiIHwNCnwgYFNIT1dfREVNT19BQ0NPVU5UX0hJTlRgIHwgKiptdXN0IGJlIGBmYWxzZWAqKiB8DQp8IGBIT01FX1NUQVRTYCB8IGJ1c2luZXNzIG1ldHJpY3MsIG5vdCBgdXNlckNvdW50YCArIGByb2xlQ291bnRgIHwNCnwgYG9udG9sb2d5LnRzYCB8IHRoZSB0YXNrIG9udG9sb2d5OyBtb2RlbHMgZXhpc3QsIHZpZXcgcmVmZXJlbmNlcyBjbG9zZWQsIGB0aXRsZUZpZWxkYCBhbmQgYGxheWVyYCBwcmVzZW50IHwNCnwgYGRlcGFydG1lbnRgIHN3aXRjaCB8IG11c3QgYmUgYHRydWVgIHwNCg0KQ2hhbmdlIGl0IGFsbCBpbiBgc3JjL2xpYi9hcHBJbmZvLnRzYCAoYEFQUF9OQU1FYCAvIGBBUFBfU0hPUlRgIC8gYEFQUF9TVUJUSVRMRWAgLyBgQVBQX0RFU0NgIC8gYEFQUF9GRUFUVVJFU2AgLyBgSE9NRV9TVEFUU2AgLw0KYEhPTUVfV0VMQ09NRWA7IGV2ZXJ5IGNvbnN0YW50IGlzIGFjdHVhbGx5IHJlbmRlcmVkIOKAlCBsZWF2ZSBubyBkZWFkIGNvbmZpZykuDQoNClRoZSBkYXNoYm9hcmQgKipyZW5kZXJzIGBIT01FX1NUQVRTYCBkaXJlY3RseSoqIOKAlCBjYXJkIG51bWJlcnMgY29tZSBmcm9tIGAvYXBpL3N0YXRzYCBieSBga2V5YCwgc28gcmV0dXJuIHRoZSBzYW1lIGtleXMgdGhlcmUuDQpUaGUgdGhyZWUgZGVmYXVsdCBjaGFydHMgKGhlYWRjb3VudCBieSBkZXBhcnRtZW50IC8gZ2VuZGVyIC8gaGlyaW5nIHRyZW5kKSBhcmUgKiphbHNvIGZlZCBieSBgL2FwaS9zdGF0c2AqKg0KKGBlbXBCeURlcHRgIC8gYGdlbmRlclJhdGlvYCAvIGBlbXBUcmVuZGApLCBhbmQgdGhhdCBlbmRwb2ludCBxdWVyaWVzIGBFbXBsb3llZWAgZGlyZWN0bHk6IHJlcGxhY2UgdGhlIHF1ZXJpZXMgaW4NCmBzcmMvYXBwL2FwaS9zdGF0cy9yb3V0ZS50c2AgKiphbmQqKiB0aGUgY2hhcnQgb3B0aW9ucyBpbiBgc3JjL2FwcC8oYWRtaW4pL2Rhc2hib2FyZC9wYWdlLnRzeGAgdG9nZXRoZXIuIFJld3JpdGluZyBvbmx5IHRoZQ0KcGFnZSBsZWF2ZXMgdGhyZWUgZW1wbG95ZWUgY2hhcnRzIG9uIGEgdmVoaWNsZSBzeXN0ZW0g4oCUIHRoZSBtb3N0IGNvbW1vbiAic3RpbGwgdGhlIHRlbXBsYXRlIiB0ZWxsLg0KQSByZWQgInVuY29uZmlndXJlZCIgYmFubmVyIG9uIHRoZSBob21lcGFnZSBtZWFucyBgQVBQX05BTUVgIGlzIHVuY2hhbmdlZC4NCg0KIyMgNS4xMCBEZW1vIGRhdGENCg0KLSAqKkRlcGFydG1lbnQgbmFtZXMgbWF0Y2ggdGhlIGJyaWVmIGNoYXJhY3RlciBmb3IgY2hhcmFjdGVyKiosIHplcm8gZXh0cmFzOyB0aGV5IGxpdmUgb25seSBpbiBgT1JHX1RSRUVgIGluIGBvcmcuY29uZmlnLnRzYDsNCiAgb25seSBsZWFmIGRlcGFydG1lbnRzIGdldCBwZW9wbGU7IHJ1biBgZGI6cmVzZXRgIGFmdGVyIGVkaXRpbmcuDQotIEFmdGVyIHRoZSB1c2VycyBzZWN0aW9uIGluIGBzZWVkLnRzYCwgcmV1c2UgdGhlIHByZXBhcmVkIHZhcmlhYmxlcyDigJQgKipkbyBub3QgcmVidWlsZCBhY2NvdW50cyBvciBkZXBhcnRtZW50cyoqOg0KDQogIGBgYHRzDQogIGRlcHRQb29sW2kgJSBkZXB0UG9vbC5sZW5ndGhdLmlkICAgICAgICAgIC8vIGxlYWYgZGVwYXJ0bWVudCBpZA0KICByb3N0ZXJVc2Vyc1tpXS5pZCAvIC5uYW1lIC8gLmRlcHRJZCAgICAgICAvLyBhY2NvdW50cyBhbHJlYWR5IGluIHRoZSBkYXRhYmFzZQ0KICByb3N0ZXJbaV0gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBzYW1lLXNvdXJjZSBuYW1lcyBmb3IgZnJlZSB0ZXh0IHdpdGhvdXQgYW4gYWNjb3VudA0KICBtYWtlUm5nKHNlZWQpIC8gbWFrZVBpY2tlcihsaXN0LCBybmcpIC8gcGVyc29uTmFtZShybmcpICAgLy8gZnJvbSAuL3NlZWRTYWZlDQogIHRleHQodiwgZmFsbGJhY2spICAgICAgICAgICAgICAgICAgICAgICAgIC8vIHJ1biBldmVyeSBzdHJpbmcgdGhyb3VnaCBpdCAoYmxvY2tzIGVtb2ppIC8gb2RkIHN5bWJvbHMpDQogIGBgYA0KDQotICoqTm8gZW1vamkgYW5kIG5vIG5vbi1DaGluZXNlIHNwZWNpYWwgc3ltYm9scyoqOyBgZGI6c2VlZGAgZW5kcyB3aXRoIGEgcmVhbCBgYXNzZXJ0Tm9Nb2ppYmFrZWAgY2hlY2sgdGhhdCBmYWlscyB0aGUgcnVuLg0KLSBUaGUgYHVwZGF0ZWAgYnJhbmNoIG9mIGB1cHNlcnRgIG11c3QgY2FycnkgYmFja2ZpbGxlZCBmaWVsZHMgKGV4Y2VwdCBwYXNzd29yZHMpLCBvdGhlcndpc2Ugb2xkIHJvd3MgbmV2ZXIgZ2V0IGZpeGVkLg0KLSAqKlBlb3BsZSBtdXN0IGJlIGNsb3NlZC1sb29wKio6IGV2ZXJ5IHBlcnNvbiBpbiBhIGRvY3VtZW50IG5lZWRzIGEgc2FtZS1uYW1lZCBhY2NvdW50LCBhbmQgdXNlcnMgbXVzdCBhcHBlYXIgaW4gYnVzaW5lc3MNCiAgZGF0YTsgdXNlIGByb3N0ZXJVc2Vyc2AgZm9yIGFwcGxpY2FudHMvYXBwcm92ZXJzIGFuZCBgcGVyc29uTmFtZSgpYCBvbmx5IGZvciBmcmVlIHRleHQgdGhhdCBuZWVkcyBubyBhY2NvdW50Lg0KICBUd2VsdmUgYXBwbGljYW50cyBpbiBkb2N1bWVudHMgYW5kIHRocmVlIGFjY291bnRzIGluIFVzZXIgTWFuYWdlbWVudCByZWFkcyBhcyBmYWtlLg0KLSBBdCBsZWFzdCAxNSByb3dzIHBlciBidXNpbmVzcyB0YWJsZTsgY292ZXIgZXZlcnkgc3RhdHVzOyBzcHJlYWQgZGF0ZXMgb3ZlciB0aGUgbGFzdCB0d28gb3IgdGhyZWUgbW9udGhzIChub3Qgb25lIGRheSk7DQogIHN0YXQgY2FyZHMgYW5kIGNoYXJ0cyBtdXN0IHJlY29uY2lsZSB3aXRoIGxpc3QgZGV0YWlsLg0KLSBSdW4gYG5wbSBydW4gZGI6c2VlZGAgKGl0IGluY2x1ZGVzIGdlbmVyYXRlIGFuZCB0aGUgbW9qaWJha2UgY2hlY2spLCBub3QganVzdCBgZGIgcHVzaGAuDQoNCi0tLQ0KDQojIFBhcnQgNiDCtyBDb2RlIGNvbnZlbnRpb25zDQoNCi0gVUkgY29weSBhbmQgY29tbWVudHMgaW4gQ2hpbmVzZTsgY29tbWVudHMgZXhwbGFpbiAid2h5Iiwgbm90ICJ3aGF0Ii4NCi0gVXNlIGFudGQsICoqY3JlYXRlIG5vIENTUyBmaWxlcyoqLiBUYWtlIGV2ZXJ5IGNvbG9yIGZyb20gYHNyYy9saWIvdGhlbWUudHNgIChgTUlMSVRBUllgKTsgbmV2ZXIgaGFyZC1jb2RlIGEgY29sb3IgdmFsdWUNCiAgKGluY2x1ZGluZyBgI2ZmZmApLg0KLSAqKlRoZSBhcHBlYXJhbmNlIGJhc2UgbXVzdCBiZSBob25vdXJlZCoqIChza2lucyAvIGZvbnQgc2l6ZSwgZGVmaW5lZCBpbiBgc3JjL2xpYi9hcHBlYXJhbmNlLnRzYCksIG90aGVyd2lzZSBuZXcgcGFnZXMgZG8gbm90DQogIHJlYWN0IHdoZW4gdGhlIHVzZXIgc3dpdGNoZXMgc2tpbiBvciBmb250IHNpemU6DQogIMK3IHdyaXRlIGBmb250U2l6ZTogZnMoMTMpYCAoZnJvbSBgQC9saWIvdGhlbWVgKSBpbnN0ZWFkIG9mIGEgYmFyZSBudW1iZXI7DQogIMK3IHVzZSBgTUlMSVRBUlkuY2FyZGAgLyBgTUlMSVRBUlkucGFwZXJgIGZvciBjYXJkLCBwb3BvdmVyIGFuZCBtb2RhbCBzdXJmYWNlcyBpbnN0ZWFkIG9mIGAjZmZmYDsNCiAgwrcgY2hhcnRzIHRha2UgY29sb3JzIGZyb20gYHVzZUNoYXJ0UGFsZXR0ZSgpYCwgY2FudmFzIHRleHQvbGluZSBjb2xvcnMgZnJvbSBgdXNlU2tpbkluaygpYCAoY2FudmFzIGNhbm5vdCByZXNvbHZlIENTUw0KICAgIHZhcmlhYmxlcyk7IGBDSEFSVF9DT0xPUlNgIGlzIG9ubHkgYSBmYWxsYmFjayBmb3Igc3RhdGljIGNoYXJ0czsNCiAgwrcgdG8gYWRkIGEgc2tpbiwgZWRpdCBgU0tJTlNgIGluIGBsaWIvYXBwZWFyYW5jZS50c2Agb25seSDigJQgbmV2ZXIgcmUtc3R5bGUgcGFnZXMgZm9yIG9uZSBza2luLg0KLSBDb21wb25lbnRzIHVzaW5nIGhvb2tzL2ludGVyYWN0aXZpdHkgc3RhcnQgd2l0aCBgJ3VzZSBjbGllbnQnYDsgZG8gbm90IGludHJvZHVjZSB1bmluc3RhbGxlZCBwYWNrYWdlcy4NCi0gTGlzdCBBUElzIGFsd2F5cyByZXR1cm4gYHBhZ2VSZXN1bHRgOyBldmVyeSB3cml0ZSBjYWxscyBgYXVkaXRgLg0KLSBgcm91dGUudHNgIGV4cG9ydHMgb25seSBIVFRQIG1ldGhvZHMgYW5kIGBjb25maWdgOyBzaGFyZWQgY29kZSBnb2VzIGluIGEgc2libGluZyBgbGliLnRzYC4NCi0gUGFnZXMgbXVzdCBoYXZlIGEgZ3VpZGVkIGVtcHR5IHN0YXRlIChhbnRkIGBFbXB0eWApOyBuZXZlciBzaG93IGEgYmxhbmsgc2NyZWVuLg0KLSBDbGVhcmx5LWNhdGVnb3JpemVkIGNoYXJ0cyB1c2Ugc2VtYW50aWMgY29sb3JzIChmYWlsIGBNSUxJVEFSWS5lcnJvcmAsIHBlbmRpbmcgYE1JTElUQVJZLndhcm5pbmdgKSwgdHJlbmQgY2hhcnRzIHVzZQ0KICBgdXNlQ2hhcnRQYWxldHRlKClgOyBhZGQgYGxhYmVsTGF5b3V0OiB7IGhpZGVPdmVybGFwOiB0cnVlIH1gLg0KLSBJbnRlcmFjdGlvbiBmZWVkYmFjayBnb2VzIHRocm91Z2ggYHNyYy9jb21wb25lbnRzL2ZlZWRiYWNrYCAocmVhZCBpdHMgc2hvcnQgUkVBRE1FIGZpcnN0KTogYFN0YXRDYXJkYCAvIGBQcmVzc2FibGVDYXJkYCAvDQogIGB1c2VSb3dGbGFzaCgpK0RhdGFUYWJsZS5oaWdobGlnaHRSb3dgOyB3YWl0IGZlZWRiYWNrIHZpYSBgQXN5bmNCdXR0b25gIC8gYExvYWRpbmdPdmVybGF5YCAvIGBTa2VsZXRvbipgLiBObyBjdXN0b20NCiAgYW5pbWF0aW9ucywgbm8gZnVsbC1zY3JlZW4gbG9hZGluZy4gYHdiLXByZXNzYWJsZWAgZ29lcyBvbmx5IG9uIGVsZW1lbnRzIHRoYXQgYXJlICoqcmVhbGx5IGNsaWNrYWJsZSoqIOKAlCBvbiBzdGF0aWMgY29udGVudA0KICBpdCBsb29rcyBtb3JlIGJyb2tlbiB0aGFuIGxlYXZpbmcgaXQgb2ZmLg0KDQotLS0NCg0KIyBQYXJ0IDcgwrcgUHJlLWRlbGl2ZXJ5IHNlbGYtY2hlY2sgYW5kIHJlcG9ydA0KDQojIyA3LjEgQ2hlY2tsaXN0ICh0aWNrIGV2ZXJ5IGxpbmU7IGRvIG5vdCByZXBvcnQgYmVmb3JlIGl0IGlzIGFsbCB0aWNrZWQpDQoNCuKWoSBgbnB4IHRzYyAtLW5vRW1pdGAgemVybyBlcnJvcnM7IGBucG0gcnVuIGNoZWNrOmNvbmZpZ2AgcGFzc2VzOyBgbnBtIHJ1biB2ZXJpZnlgIHJ1biBvbmNlIGF0IHRoZSBlbmQgYW5kIHBhc3Nlcw0K4pahIGBucG0gcnVuIGJ1aWxkYCBleGl0cyAwOyBpZiBpdCBldmVyIHJlcG9ydGVkIGBFUEVSTTogLm5leHQvdHJhY2VgLCBgbm9kZSBzY3JpcHRzL3BhdGNoLW5leHQtdHJhY2UubWpzYCB3YXMgcnVuIGFuZCB0aGUgcmVidWlsZCBwYXNzZWQNCuKWoSBgYWRtaW4gLyAxMjM0NTZgIGxvZ2luIHNob3dzIHRoZSBuZXcgbWVudXM7IGNyZWF0ZS9lZGl0L2RlbGV0ZS9zZWFyY2gvcGFnaW5hdGUgd29yayBldmVyeXdoZXJlDQrilqEgRm91ci1waWVjZSBhZG1pbiBzZXQgY29tcGxldGU7IGAvc3lzdGVtL2RlcHRzYCBhZGRzIGNoaWxkcmVuLCBlZGl0cywgZGVsZXRlcyBlbXB0eSBvbmVzIGFuZCBleHBsYWlucyByZWZ1c2Fscw0K4pahIERhdGEgcGVybWlzc2lvbiBjbG9zZWQgbG9vcDogYSBub3JtYWwgYWNjb3VudCBzZWVzIGZld2VyIHJvd3MgaW4gbGlzdCwgZXhwb3J0LCBkYXNoYm9hcmQgYW5kIGdyYXBoOyBhIGNyYWZ0ZWQgb3V0LW9mLXNjb3BlIGlkDQogIHJldHVybnMgNDA0IGZyb20gdGhlIGRldGFpbCBBUEkNCuKWoSBEZXBhcnRtZW50IG5hbWVzIG1hdGNoIHRoZSBicmllZiBjaGFyYWN0ZXIgZm9yIGNoYXJhY3Rlciwgbm8gZXh0cmFzLCBldmVyeSBkZXBhcnRtZW50IHN0YWZmZWQNCuKWoSBEZW1vIGRhdGEgY2xvc2VkLWxvb3Agb24gcGVvcGxlOyBjYXJkL2NoYXJ0IG51bWJlcnMgcmVjb25jaWxlIHdpdGggZGV0YWlsDQrilqEgYC9ncmFwaGAgc2hvd3MgdGhlIHRhc2sgb250b2xvZ3kgd2l0aCBubyByZWQgYmFubmVyLCBwYXJhbGxlbCBsaW5lcyBvciBpc29sYXRlZCBub2RlczsgYHJlbGF0aW9uc2AgaGFzIG5vIOKclzsNCiAgcmVuYW1lL2RlbGV0ZSByb3VuZC10cmlwcyBwZXJzaXN0DQrilqEgYC9haWAgaGFzIG9uZSBjaGF0IGJveDsgZGF0YSwgdXNhZ2UgYW5kIGFjdGlvbiBxdWVzdGlvbnMgYWxsIHdvcms7IGFuc3dlcnMgcmVuZGVyIGFzIE1hcmtkb3duOyBhZ2dyZWdhdGVzIHNob3cgYSB0b29sIHRyYWNlDQrilqEgQ29uZmlybWF0aW9uIGNhcmQgZW5kIHRvIGVuZDogQ29uZmlybSBwZXJzaXN0cyArIHdyaXRlcyBhbiAiQUkgQXNzaXN0YW50IiBhdWRpdCBsb2c7IENhbmNlbCB3cml0ZXMgbm90aGluZzsgcmVmcmVzaCBkb2VzIG5vdA0KICByZXN1cnJlY3QgaXQNCuKWoSBTZXNzaW9uIHN1cnZpdmVzIHJlZnJlc2g7IGRlZXAgbGlua3Mgd29yazsgY29yZSBsaXN0IHRvb2xiYXJzIGhhdmUgYSBGZWVkYmFjayBidXR0b24NCuKWoSBgL2RvY3NgIG9wZW5zOyBldmVyeSBidXNpbmVzcyBtb2R1bGUgaGFzIGEgaGVscCBwYWdlOyBDdHJsIEsgZmluZHMgYnVzaW5lc3MgbW9kdWxlIG5hbWVzOyB0aGUgdG9wLWJhciAiPyIgYW5kIHRoZSBzaWRlYmFyDQogIGVudHJ5IGJvdGggd29yaw0K4pahIGAvaW5ib3hgIG9wZW5zOyB0aGUgdG9wLWJhciBiZWxsIHNob3dzIGEgY291bnQ7IHRvZG9zIGRlZXAtbGluayB0byBzcGVjaWZpYyByZWNvcmRzOyBtZXNzYWdlcyBjYW4gYmUgbWFya2VkIHJlYWQNCiAgaW5kaXZpZHVhbGx5IG9yIGFsbCBhdCBvbmNlOyB0b2RvcyBkaXNhcHBlYXIgYWZ0ZXIgdGhlIHVuZGVybHlpbmcgYnVzaW5lc3MgaXMgY29tcGxldGVkDQrilqEgU3RhcnRzIGluIHByb2R1Y3Rpb24gbW9kZSAoYG5wbSBydW4gYnVpbGQgJiYgbnBtIHJ1biBzdGFydGApLCB3aXRoIG5vIE5leHQuanMgZGV2IGJhZGdlDQrilqEgRXZlcnkgYnJhbmNoIGFuZCB0YWcgZXhpc3RzIG9uIHRoZSByZW1vdGU6IGBnaXQgbG9nIC0tb25lbGluZSBvcmlnaW4vbWFpbmAgc2hvd3MgdGhlbQ0KDQojIyA3LjIgUmVwb3J0IGZvcm1hdCAoZm9sbG93IHRoaXMsIGRvIG5vdCB3cml0ZSBwcm9zZSkNCg0KYGBgDQpEb25lOiA8b25lIHNlbnRlbmNlPg0KRGF0YSBtb2RlbHM6IFh4eEEgLyBYeHhCIC8gWHh4QyAoTiBkZW1vIHJvd3MgZWFjaCkNCk1vZHVsZXM6IDxtZW51IG5hbWU+IOKGkiAvcGF0aCAobGlzdC9jcmVhdGUvZWRpdC9kZWxldGUvZXhwb3J0IOKchSkNCk9udG9sb2d5OiA8bGF5ZXIwPiDihpIgPGxheWVyMT4g4oaSIDxsYXllcjI+LCBOIG5vZGVzIC8gTSBlZGdlcyAvIEsgdmlld3MNClZlcmlmaWVkOiB0c2Mg4pyFICBjaGVjazpjb25maWcg4pyFICByZWxhdGlvbnMg4pyFICB2ZXJpZnkg4pyFDQpOb3QgZG9uZSBvciBsaW1pdGVkOiA8YmUgaG9uZXN0OyB3cml0ZSAibm9uZSIgaWYgbm9uZT4NCmBgYA0KDQoqKk5ldmVyIHJlcGxhY2UgZXZpZGVuY2Ugd2l0aCAiZG9uZSIgb3IgImJhc2ljYWxseSB3b3JrcyIuKiogRXZlcnkgY29uY2x1c2lvbiBjYXJyaWVzIGEgdmVyaWZpYWJsZSBmYWN0IChjb21tYW5kIG91dHB1dCwgcGFnZQ0KcGF0aCwgcm93IGNvdW50KS4NCg0KLS0tDQoNCiMjIEFwcGVuZGl4IEEgwrcgR2l0IHdvcmtmbG93DQoNClRoZSBoaXN0b3J5IHNob3VsZCByZWFkIGxpa2UgcmVhbCBlbmdpbmVlcmluZywgbm90IG9uZSBnaWFudCBkdW1wIGF0IHRoZSBlbmQuDQoNCioqQnJhbmNoZXMqKjogYG1haW5gIGlzIHJ1bm5hYmxlIGF0IGV2ZXJ5IGNvbW1pdDsgYGZlYXQvPG1vZHVsZT5gIGlzIG9uZSBicmFuY2ggcGVyIGJ1c2luZXNzIG1vZHVsZSwgbWVyZ2VkIGJhY2sgd2l0aA0KYGdpdCBtZXJnZSAtLW5vLWZmYDsgZml4IGEgYnVnIG9uIHRoZSBicmFuY2ggdGhhdCBvd25zIGl0LCBub3Qgb24gYG1haW5gLg0KDQoqKkNvbW1pdCBncmFudWxhcml0eSoqOiBvbmUgY29uY2VybiBwZXIgY29tbWl0IChgYWRkIERpc3BhdGNoUmVjb3JkIG1vZGVsYCAvIGBhZGQgZGlzcGF0Y2ggbGlzdCBBUElgIC8gYGFkZCBkaXNwYXRjaCBwYWdlYCwNCm5vdCBgYWRkIGRpc3BhdGNoIG1vZHVsZWApOyBjb21taXQgYXMgc29vbiBhcyB0aGUgbW9kdWxlIHBhc3NlcyBgdHNjYDsgcHJlZml4ZXMgYGZlYXQ6YCAvIGBmaXg6YCAvIGByZWZhY3RvcjpgIC8gYGNob3JlOmAgLw0KYGRvY3M6YC4NCg0KKipUYWdzKio6IGFubm90YXRlZCB0YWdzIG9ubHkgKGBnaXQgdGFnIC1hIHYwLjEuMCAtbSAic2tlbGV0b24gcnVucyJgKTsgdGFnIGB2MS4wLjBgIHdoZW4gdGhlIGJ1c2luZXNzIGZsb3cgd29ya3M7IHJlbGVhc2UNCm5vdGVzIGxpc3Qgb25lIGxpbmUgcGVyIG1vZHVsZS4NCg0KKipQdXNoIGlzIHdoYXQgY291bnRzKio6IGBnaXQgcHVzaCBvcmlnaW4gPGJyYW5jaD5gICsgYGdpdCBwdXNoIC0tdGFnc2A7IGNvbmZpcm0gd2l0aCBgZ2l0IGxvZyAtLW9uZWxpbmUgb3JpZ2luL21haW5gIGJlZm9yZQ0KcmVwb3J0aW5nIGRvbmUuDQoNCiMjIEFwcGVuZGl4IEIgwrcgVHJhcHMgYWxyZWFkeSBwYWlkIGZvciAoc2F2ZXMgeW91IGhhbGYgYW4gaG91cikNCg0KfCBTeW1wdG9tIHwgUmVhbCBjYXVzZSB8DQp8IC0tLSB8IC0tLSB8DQp8IGBuZXh0IGJ1aWxkYCBsb2cgbG9va3MgZmluZSwgZXhpdCBjb2RlIDEzOSB8IE5vZGUgMjIsIFdBU00gY29tcGlsZXIgU0lHU0VHVi4gVXNlIE5vZGUgMjQ7IGp1ZGdlIGJ5IGV4aXQgY29kZSBvbmx5IHwNCnwgYG5leHQgYnVpbGRgIHRocm93cyBgRVBFUk06IG9wZXJhdGlvbiBub3QgcGVybWl0dGVkLCBvcGVuICcubmV4dC90cmFjZSdgIHwgTmV4dCdzIHRyYWNlIHdyaXRlLXN0cmVhbSByYWNlLCBvciBgbnBtIGNpYCB3aXBlZCB0aGUgYnVpbHQtaW4gcGF0Y2guIFJ1biBgbm9kZSBzY3JpcHRzL3BhdGNoLW5leHQtdHJhY2UubWpzYCBhbmQgcmVidWlsZC4gKipOb3QgYW50aXZpcnVzKio6IGRlbGV0aW5nIGAubmV4dGAsIGRpc2FibGluZyBBViBvciBhZGRpbmcgYW4gZXhjbHVzaW9uIGRvZXMgbm90aGluZyB8DQp8IGBDYW5ub3QgZmluZCBtb2R1bGUgJy5wcmlzbWEvY2xpZW50L2RlZmF1bHQnYCB8IFByaXNtYSA3IGRvZXMgbm90IGF1dG8tZ2VuZXJhdGU7IHJ1biBgbnBtIHJ1biBkYjpzZWVkYCAoY29udGFpbnMgZ2VuZXJhdGUpIHwNCnwgQnVpbGQgZmFpbHMgd2l0aCBQMTAxMiB8IENvbm5lY3Rpb24gc3RyaW5nIHdyaXR0ZW4gaW50byBgc2NoZW1hLnByaXNtYWA7IGl0IGJlbG9uZ3MgaW4gYHByaXNtYS5jb25maWcudHNgIHwNCnwgRGVwYXJ0bWVudHMgY2hhbmdlZCBidXQgcGFnZXMgc3RpbGwgb2xkIHwgVXNlZCBgZGI6c2VlZGA7IGFmdGVyIGVkaXRpbmcgYE9SR19UUkVFYCB5b3UgbXVzdCBgZGI6cmVzZXRgIHwNCnwgTmV3IG1lbnUgZG9lcyBub3QgYXBwZWFyIGluIHRoZSBzaWRlYmFyIHwgYGRiOnNlZWRgIG5vdCBydW4sIG9yIHRoZSBtZW51IGhhcyBhIGBwZXJtQ29kZWAgdGhlIHJvbGUgd2FzIG5ldmVyIGdyYW50ZWQgfA0KfCBDaGlsZCBtZW51cyBhcHBlYXIgYXQgdGhlIHNpZGViYXIgcm9vdCB8IFNlZWQgYXV0aG9yaXphdGlvbiBsYWNrcyB0aGUgKiphbmNlc3RvciBjbG9zdXJlKio6IHBhcmVudCBkaXJlY3RvcnkgdW4tZ3JhbnRlZCBtYWtlcyBjaGlsZHJlbiBvcnBoYW5zIHwNCnwgRXhwb3J0IGFuc3dlcnMgInVuc3VwcG9ydGVkIGV4cG9ydCBtb2R1bGUiIHwgTm8gYnJhbmNoIGFkZGVkIGluIGBhcGkvZXhwb3J0L3JvdXRlLnRzYCB8DQp8IGA/ZGVwdElkPWAgcmV0dXJucyBvdGhlciBkZXBhcnRtZW50cycgcm93cyB8IGBkZXB0U2NvcGVgIG1lcmdlZCBiZWZvcmUgdGhlIGJ1c2luZXNzIGZpbHRlcnMgYW5kIG92ZXJ3cml0dGVuIGJ5IGB3aGVyZS5kZXB0SWRgIHwNCnwgTmV3IHBhZ2VzIGlnbm9yZSBza2luIGNoYW5nZXMgfCBIYXJkLWNvZGVkIGAjZmZmYCAvIGhleCB2YWx1ZXM7IHVzZSBgTUlMSVRBUlkuKmAgfA0KfCBDaGFydHMgcmVuZGVyIG5vdGhpbmcgdW5kZXIgYSBuZXcgc2tpbiB8IGB2YXIoLS13Yi0qKWAgZmVkIHRvIEVDaGFydHM7IHVzZSBgdXNlQ2hhcnRQYWxldHRlKClgIC8gYHVzZVNraW5JbmsoKWAgfA0KfCBUb2RvIGFuZCBtZXNzYWdlIGJvdGggZmlyZSBmb3Igb25lIGV2ZW50IHwgQm90aCBhIHRvZG8gc291cmNlIGFuZCBgbm90aWZ5KClgIGZvciB0aGUgc2FtZSBldmVudCDigJQgcGljayBvbmUgfA0KfCBgZGI6c2VlZGAgZmFpbHMgb24gbW9qaWJha2UgfCBDb3B5IG5vdCBwYXNzZWQgdGhyb3VnaCBgdGV4dCgpYCwgb3IgZW1vamkgLyBwcml2YXRlLXVzZSBjaGFyYWN0ZXJzIHByZXNlbnQgfA0KfCBHcmFwaCBoYXMgaXNvbGF0ZWQgbm9kZXMgb3Igb25lIHRhbGwgY29sdW1uIHwgQSB2aWV3IGluY2x1ZGVzIGFuIGVudGl0eSB3aXRoIG5vIGVkZ2VzLCBvciBhIDE6TiBsb2cgcmVsYXRpb24gcHVsbGVkIGludG8gdGhlIGRlZmF1bHQgdmlldyB8DQp8IERldGFpbCBBUEkgcmV0dXJucyAyMDAgd2l0aCBlbXB0eSBkYXRhIHwgTm8gYGRlcHRTY29wZWA6IHRoZSBpZCBmZXRjaGVzIGFub3RoZXIgZGVwYXJ0bWVudCdzIHJvdyAocHJpdmlsZWdlIGVzY2FsYXRpb24pOyB3aXRoIHNjb3BlIGJ1dCBubyBudWxsIGNoZWNrLCB0aGUgNDA0IHBhdGggbmV2ZXIgdHJpZ2dlcnMgfA0KDQojIyBBcHBlbmRpeCBDIMK3IFRocmVlIHdyaXRpbmcgZXhhbXBsZXMgKGxlYXJuIHRoZSBzaGFwZSwgbm90IHRoZSBidXNpbmVzcykNCg0KfCBEaW1lbnNpb24gfCBWZWhpY2xlIGRpc3BhdGNoIHwgTGVhdmUgYXBwcm92YWwgfCBFeGFtIHNjb3JlcyB8DQp8IC0tLSB8IC0tLSB8IC0tLSB8IC0tLSB8DQp8IE9udG9sb2d5IGxheWVycyB8IGRlcHQgMCDihpIgdmVoaWNsZS9kcml2ZXIgMSDihpIgZGlzcGF0Y2ggMiB8IHBlcnNvbiAwIOKGkiBwbGFuIDEg4oaSIHJlcXVlc3QvY2hhbmdlIDIgfCBkZXB0IDAg4oaSIHBlcnNvbiAxIOKGkiBjb3Vyc2Uvc2NvcmUgMiB8DQp8IFN0YXRlIG1hY2hpbmUgfCBwZW5kaW5nIOKGkiBhc3NpZ25lZCDihpIgZG9uZSAvIGNhbmNlbGxlZCB8IHBlbmRpbmcg4oaSIGFwcHJvdmVkIOKGkiBjbG9zZWQgLyByZWplY3RlZCB8IGRyYWZ0IOKGkiBlbnRlcmVkIOKGkiBwdWJsaXNoZWQgfA0KfCBUb2RvIHwgcmVxdWVzdHMgYXdhaXRpbmcgZGlzcGF0Y2ggfCBsZWF2ZSByZXF1ZXN0cyBhd2FpdGluZyBteSBhcHByb3ZhbCB8IHNjb3JlIGJhdGNoZXMgYXdhaXRpbmcgZW50cnkgfA0KfCBTdGF0IGNhcmRzIHwgYXZhaWxhYmxlIHZlaGljbGVzIC8gdHJpcHMgdG9kYXkgLyBwZW5kaW5nIC8gaW4gcmVwYWlyIHwgbXkgbGVhdmUgYmFsYW5jZSAvIHBlbmRpbmcgYXBwcm92YWxzIC8gZGF5cyB0aGlzIG1vbnRoIHwgYXZlcmFnZSBzY29yZSAvIGZhaWxpbmcgY291bnQgLyB1bnB1Ymxpc2hlZCBiYXRjaGVzIHwNCg==
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# biz-full
|
|
2
|
+
|
|
3
|
+
A full-stack business application built on Next.js 15 App Router, TypeScript, and Prisma 7.
|
|
4
|
+
|
|
5
|
+
It ships with authentication, role-based access control, a reusable data table, Excel import/export,
|
|
6
|
+
Word/Excel/PDF parsing and generation, file upload/download, an AI assistant with server-side tool calling,
|
|
7
|
+
and a knowledge graph.
|
|
8
|
+
|
|
9
|
+
## Tech stack
|
|
10
|
+
|
|
11
|
+
| Layer | Choice |
|
|
12
|
+
| --- | --- |
|
|
13
|
+
| Framework | Next.js 15 (App Router) |
|
|
14
|
+
| Language | TypeScript |
|
|
15
|
+
| UI | Ant Design 6 |
|
|
16
|
+
| Charts | ECharts |
|
|
17
|
+
| Database | SQLite via Prisma 7 |
|
|
18
|
+
| Auth | NextAuth |
|
|
19
|
+
| Testing | Vitest |
|
|
20
|
+
|
|
21
|
+
## Getting started
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npm install
|
|
25
|
+
npm run db:reset # create the database and seed demo data
|
|
26
|
+
npm run dev
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
The dev server listens on http://localhost:3000.
|
|
30
|
+
|
|
31
|
+
Default accounts are provisioned by the seed script — see `prisma/seed.ts`.
|
|
32
|
+
|
|
33
|
+
Organization structure is **not** hardcoded in the seed: edit `prisma/org.config.ts`
|
|
34
|
+
(`ORG_TREE`) to match the departments required by your brief. The seed then distributes
|
|
35
|
+
roster users evenly across all *leaf* departments (`PEOPLE_TOTAL` / `PEOPLE_MIN_PER_DEPT`),
|
|
36
|
+
so no department ends up empty. Set `ORG_SOURCE = 'existing'` to keep departments that were
|
|
37
|
+
created through the UI instead. After changing the tree, run `npm run db:reset` — plain
|
|
38
|
+
`db:seed` only upserts and will leave the previous departments behind.
|
|
39
|
+
|
|
40
|
+
## Scripts
|
|
41
|
+
|
|
42
|
+
| Command | Description |
|
|
43
|
+
| --- | --- |
|
|
44
|
+
| `npm run dev` | Start the development server |
|
|
45
|
+
| `npm run build` | Production build (runs config validation first) |
|
|
46
|
+
| `npm start` | Serve the production build |
|
|
47
|
+
| `npm run typecheck` | TypeScript type check, no emit |
|
|
48
|
+
| `npm run lint` | ESLint |
|
|
49
|
+
| `npm test` | Unit tests (Vitest) |
|
|
50
|
+
| `npm run db:push` | Sync the Prisma schema to the database |
|
|
51
|
+
| `npm run db:seed` | Seed demo data |
|
|
52
|
+
| `npm run db:reset` | Drop, recreate, and reseed the database |
|
|
53
|
+
| `npm run db:studio` | Open Prisma Studio |
|
|
54
|
+
| `npm run check:config` | Validate system identity and ontology configuration |
|
|
55
|
+
| `npm run relations` | Inspect schema relations before editing the knowledge-graph ontology |
|
|
56
|
+
| `npm run verify` | Full gate: config check + type check + production build |
|
|
57
|
+
|
|
58
|
+
## Project structure
|
|
59
|
+
|
|
60
|
+
```
|
|
61
|
+
src/
|
|
62
|
+
app/ App Router routes
|
|
63
|
+
(admin)/ Authenticated admin area
|
|
64
|
+
api/ Route handlers
|
|
65
|
+
components/ Shared components (data table, chart, AI panel, graph)
|
|
66
|
+
lib/ Utilities: API helpers, RBAC, Excel, Office, ontology, theme
|
|
67
|
+
prisma/
|
|
68
|
+
schema.prisma Data models
|
|
69
|
+
seed.ts Demo data and menu seed
|
|
70
|
+
seedSafe.ts Deterministic name pools and encoding-safe text helpers
|
|
71
|
+
org.config.ts Department tree + headcount (edit per brief)
|
|
72
|
+
scripts/
|
|
73
|
+
check-config.mjs Configuration validator
|
|
74
|
+
suggest-relations.mjs Relation miner
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Configuration
|
|
78
|
+
|
|
79
|
+
Environment variables live in `.env`. The most important one is `DATABASE_URL`, which points at the SQLite file.
|
|
80
|
+
|
|
81
|
+
System identity (application name, description, home-page metrics) is centralized in `src/lib/appInfo.ts`.
|
|
82
|
+
Knowledge-graph structure is centralized in `src/lib/ontology.ts`.
|
|
83
|
+
|
|
84
|
+
## Notes on Prisma 7
|
|
85
|
+
|
|
86
|
+
Two things differ from Prisma 6 and will break if copied from older tutorials:
|
|
87
|
+
|
|
88
|
+
1. **The connection string is not in the schema.** The `datasource` block in `prisma/schema.prisma` declares
|
|
89
|
+
only the `provider`; adding `url` there produces error `P1012`. The connection string is configured in
|
|
90
|
+
`prisma.config.ts` at the project root.
|
|
91
|
+
2. **`PrismaClient` requires a driver adapter.** The Rust query engine has been removed, so a bare
|
|
92
|
+
`new PrismaClient()` throws `A driver adapter is required to connect to your database`.
|
|
93
|
+
This project uses `@prisma/adapter-libsql` — see `src/lib/prisma.ts`.
|
|
94
|
+
|
|
95
|
+
## License
|
|
96
|
+
|
|
97
|
+
MIT
|
package/resources/_.env
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# SQLite(文件型,适合内网单机)
|
|
2
|
+
DATABASE_URL="file:./dev.db"
|
|
3
|
+
|
|
4
|
+
# NextAuth v5
|
|
5
|
+
AUTH_SECRET="请执行 npx auth secret 生成,或手写一段 32 位随机串"
|
|
6
|
+
AUTH_TRUST_HOST=true
|
|
7
|
+
|
|
8
|
+
# 可选:AI 写操作的确认令牌密钥。
|
|
9
|
+
# 不配就沿用 AUTH_SECRET;两者都没有时进程启动会随机生成一个
|
|
10
|
+
# (代价:服务重启后,重启前弹出、尚未确认的确认卡会失效)。
|
|
11
|
+
# AI_ACTION_SECRET="另一段随机串"
|
|
12
|
+
|
|
13
|
+
# ============================ 本地大模型 ============================
|
|
14
|
+
# 两套变量名都认(代码里 AI_* 优先,OPENAI_* 兜底),填一套即可。
|
|
15
|
+
# 不填就走默认值:http://127.0.0.1:8092/v1 + qwen3.8-27b-gsq + key=local。
|
|
16
|
+
# 换机器 / 换模型时**务必确认下面三项**,否则 AI 中心会连不上:
|
|
17
|
+
# AI_BASE_URL 模型服务地址,要带 /v1
|
|
18
|
+
# AI_MODEL 必须是引擎实际加载的 model id(/v1/models 能查到)
|
|
19
|
+
# AI_API_KEY 本地引擎通常不校验,但请求头必须有值
|
|
20
|
+
# AI_BASE_URL="http://127.0.0.1:8092/v1"
|
|
21
|
+
# AI_MODEL="qwen3.8-27b-gsq"
|
|
22
|
+
# AI_API_KEY="local"
|
|
23
|
+
|
|
24
|
+
# 可选:兼容接口形态。auto = 先试 chat/completions,失败再试 responses
|
|
25
|
+
# OPENAI_API_MODE="auto"
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
node_modules/
|
|
2
|
+
.next/
|
|
3
|
+
out/
|
|
4
|
+
build/
|
|
5
|
+
dist/
|
|
6
|
+
coverage/
|
|
7
|
+
*.log
|
|
8
|
+
.env
|
|
9
|
+
.env*.local
|
|
10
|
+
.DS_Store
|
|
11
|
+
# SQLite 数据库文件。Prisma 7 用 libsql adapter,DATABASE_URL 里的相对路径
|
|
12
|
+
# 是按进程 cwd(项目根)解析的,所以 dev.db 会落在项目根而不是 prisma/ 下。
|
|
13
|
+
# 两种位置都忽略掉,省得改来改去。
|
|
14
|
+
*.db
|
|
15
|
+
*.db-journal
|
|
16
|
+
prisma/*.db
|
|
17
|
+
prisma/*.db-journal
|
|
18
|
+
.next-env.d.ts
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default { extends: ['@commitlint/config-conventional'] };
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# 帮助文档内容维护说明
|
|
2
|
+
|
|
3
|
+
这个目录是帮助文档站的**唯一内容源**。新增、删除或重命名文件后,刷新页面即可生效,不需要改代码。
|
|
4
|
+
|
|
5
|
+
## 目录与分组
|
|
6
|
+
|
|
7
|
+
- `content/docs/*.md`:顶层文档,按 `order` 排在侧边栏。
|
|
8
|
+
- `content/docs/<目录>/*.md`:子目录自动成为一个分组。
|
|
9
|
+
- `<目录>/_meta.json`:可选,用来设置分组标题、描述和排序:
|
|
10
|
+
|
|
11
|
+
```json
|
|
12
|
+
{
|
|
13
|
+
"title": "使用指南",
|
|
14
|
+
"description": "账号、界面、权限与日常操作。",
|
|
15
|
+
"order": 2
|
|
16
|
+
}
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
- 分组内还可以继续建子目录,侧边栏会递归展示。
|
|
20
|
+
- `README.md` 不会进入文档站;`_` 开头的文件和目录也会被忽略,可用于草稿。
|
|
21
|
+
- 目录下的 `index.md` 会作为该目录的落地页,地址是该目录本身。
|
|
22
|
+
|
|
23
|
+
## Front matter
|
|
24
|
+
|
|
25
|
+
每个 `.md` 文件开头可以用 `---` 包围元数据:
|
|
26
|
+
|
|
27
|
+
```markdown
|
|
28
|
+
---
|
|
29
|
+
title: 快速开始
|
|
30
|
+
description: 三分钟完成登录并走通一次业务操作。
|
|
31
|
+
order: 1
|
|
32
|
+
tags: [入门, 登录]
|
|
33
|
+
updated: 2026-10-21
|
|
34
|
+
hidden: false
|
|
35
|
+
---
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
| 字段 | 说明 |
|
|
39
|
+
| --- | --- |
|
|
40
|
+
| `title` | 页面标题;不写则取正文第一个 H1,再不行用文件名 |
|
|
41
|
+
| `description` | 首页卡片、搜索摘要与页面副标题 |
|
|
42
|
+
| `order` | 同级排序,数字越小越靠前 |
|
|
43
|
+
| `tags` | 标签,支持 `[a, b]` 或逗号分隔 |
|
|
44
|
+
| `updated` | 更新日期;不写取文件修改时间 |
|
|
45
|
+
| `hidden` | `true` 时不进侧边栏与搜索,但 URL 仍可访问 |
|
|
46
|
+
|
|
47
|
+
正文开头的第一个 H1 会被自动去掉,避免和文档站渲染的页面标题重复。因此正文请从 H2(`##`)开始写。
|
|
48
|
+
|
|
49
|
+
## 写作约定
|
|
50
|
+
|
|
51
|
+
- 一篇文档只解决一类问题,标题直接写用户会搜索的词。
|
|
52
|
+
- 操作步骤用有序列表;字段说明、状态说明用表格。
|
|
53
|
+
- 站内链接使用完整路径,例如 `[账号与登录](/docs/guide/account)`。
|
|
54
|
+
- 代码块会带语言标签和复制按钮;表格、引用、列表均支持。
|
|
55
|
+
- 不要在正文里写死演示密码、密钥或具体人员的敏感信息。
|
|
56
|
+
|
|
57
|
+
## 新增业务模块时
|
|
58
|
+
|
|
59
|
+
1. 在 `content/docs/features/` 或对应目录新增一篇模块说明。
|
|
60
|
+
2. 在 `prisma/seed.ts` 登记模块菜单,让「功能速查」自动出现。
|
|
61
|
+
3. 如果新增了分组,补一个 `_meta.json` 设置中文标题和排序。
|
|
62
|
+
|
|
63
|
+
文档站首页的「功能速查」直接读取系统菜单树,因此菜单登记后会自动出现,不需要维护第二份功能清单。
|