dsh-desktop-kit 0.0.0-stage → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +152 -2
- package/client.js +4101 -0
- package/cordis.patch.yml +5 -0
- package/docs/ARCHITECTURE.md +114 -0
- package/docs/COMPATIBILITY.md +61 -0
- package/docs/INSTALL.md +84 -0
- package/docs/PRIVACY.md +50 -0
- package/docs/ROADMAP.md +57 -0
- package/docs/TOKENS.md +118 -0
- package/docs/TROUBLESHOOTING.md +68 -0
- package/docs/images/hero.png +0 -0
- package/index.js +84 -0
- package/package.json +70 -4
- package/plugins/dsh-gkb-ru-ui/cordis.patch.yml +6 -0
- package/plugins/dsh-gkb-ru-ui/lib/client.js +1331 -0
- package/plugins/dsh-gkb-ru-ui/lib/index.js +186 -0
- package/plugins/dsh-gkb-ru-ui/lib/skills.js +570 -0
- package/plugins/dsh-gkb-ru-ui/package.json +28 -0
- package/plugins/dsh-gkb-ru-ui/screenshots/01-home-tiles-1440-light.png +0 -0
- package/plugins/dsh-gkb-ru-ui/screenshots/02-skills-1440-light.png +0 -0
- package/plugins/dsh-gkb-ru-ui/screenshots/03-skills-editor-1440-light.png +0 -0
- package/plugins/dsh-gkb-ru-ui/screenshots/04-skills-1440-dark.png +0 -0
- package/plugins/dsh-gkb-ru-ui/screenshots/05-skills-390-light.png +0 -0
- package/plugins/dsh-gkb-ru-ui/screenshots/06-home-tiles-1440-dark.png +0 -0
- package/plugins/dsh-peak-guard/README.md +70 -0
- package/plugins/dsh-peak-guard/client.js +741 -0
- package/plugins/dsh-peak-guard/cordis.patch.yml +5 -0
- package/plugins/dsh-peak-guard/icon.svg +12 -0
- package/plugins/dsh-peak-guard/index.js +2 -0
- package/plugins/dsh-peak-guard/locale/en.json +6 -0
- package/plugins/dsh-peak-guard/locale/ru.json +6 -0
- package/plugins/dsh-peak-guard/locale/zh.json +6 -0
- package/plugins/dsh-peak-guard/package.json +33 -0
- package/plugins/dsh-peak-guard/screenshots/01-real-time-peak-start.png +0 -0
- package/plugins/dsh-peak-guard/screenshots/02-real-time-peak-end.png +0 -0
- package/plugins/dsh-peak-guard/screenshots/03-peak-card-1440-light.png +0 -0
- package/plugins/dsh-peak-guard/screenshots/04-peak-card-1440-dark.png +0 -0
- package/plugins/dsh-peak-guard/screenshots/05-peak-card-390-light.png +0 -0
- package/plugins/dsh-peak-guard/screenshots/05-peak-card-768-light.png +0 -0
- package/plugins/dsh-peak-guard/screenshots/06-peak-card-390-light.png +0 -0
- package/plugins/dsh-peak-guard/screenshots/07-live-preview-check.png +0 -0
- package/plugins/dsh-peak-guard/screenshots/07-pre-peak-1440-light.png +0 -0
- package/plugins/dsh-peak-guard/screenshots/08-peak-start-1440-light.png +0 -0
- package/plugins/dsh-peak-guard/screenshots/09-peak-end-1440-light.png +0 -0
- package/plugins/dsh-peak-guard/screenshots/10-pre-peak-card-1440-light.png +0 -0
- package/plugins/dsh-peak-guard/screenshots/11-pre-peak-card-1440-dark.png +0 -0
- package/plugins/dsh-peak-guard/screenshots/12-pre-peak-card-390-light.png +0 -0
- package/plugins/dsh-peak-guard/screenshots/qa-report.json +74 -0
- package/plugins/dsh-profile-transfer/README.md +62 -0
- package/plugins/dsh-profile-transfer/bootstrap/01-INSTALL.cmd +7 -0
- package/plugins/dsh-profile-transfer/bootstrap/README-ARCHIVE.md +54 -0
- package/plugins/dsh-profile-transfer/bootstrap/README-/320/243/320/241/320/242/320/220/320/235/320/236/320/222/320/232/320/220.md +42 -0
- package/plugins/dsh-profile-transfer/bootstrap/install-on-clean-machine.ps1 +119 -0
- package/plugins/dsh-profile-transfer/bootstrap//320/243/320/241/320/242/320/220/320/235/320/236/320/222/320/230/320/242/320/254.cmd +5 -0
- package/plugins/dsh-profile-transfer/client.js +806 -0
- package/plugins/dsh-profile-transfer/cordis.patch.yml +3 -0
- package/plugins/dsh-profile-transfer/icon.svg +13 -0
- package/plugins/dsh-profile-transfer/index.js +375 -0
- package/plugins/dsh-profile-transfer/locale/en.json +6 -0
- package/plugins/dsh-profile-transfer/locale/ru.json +6 -0
- package/plugins/dsh-profile-transfer/locale/zh.json +6 -0
- package/plugins/dsh-profile-transfer/package.json +36 -0
- package/plugins/dsh-profile-transfer/screenshots/01-settings-transfer-1440-light.png +0 -0
- package/plugins/dsh-profile-transfer/screenshots/02-settings-transfer-preview-1440-light.png +0 -0
- package/plugins/dsh-profile-transfer/screenshots/03-settings-transfer-1440-dark.png +0 -0
- package/plugins/dsh-profile-transfer/screenshots/04-settings-transfer-390-light.png +0 -0
- package/plugins/dsh-profile-transfer/screenshots/05-settings-transfer-768-light.png +0 -0
- package/plugins/dsh-profile-transfer/screenshots/06-settings-transfer-390-dark.png +0 -0
- package/plugins/dsh-profile-transfer/screenshots/07-settings-transfer-export-result.png +0 -0
- package/plugins/dsh-profile-transfer/screenshots/08-settings-transfer-workspaces-1440-light.png +0 -0
- package/plugins/dsh-profile-transfer/screenshots/09-settings-transfer-workspaces-1440-dark.png +0 -0
- package/plugins/dsh-profile-transfer/screenshots/10-settings-transfer-workspaces-390-light.png +0 -0
- package/plugins/dsh-profile-transfer/transfer.js +1004 -0
- package/plugins/dsh-project-memory/README.md +71 -0
- package/plugins/dsh-project-memory/cordis.patch.yml +4 -0
- package/plugins/dsh-project-memory/icon.svg +12 -0
- package/plugins/dsh-project-memory/index.js +1240 -0
- package/plugins/dsh-project-memory/locale/en.json +6 -0
- package/plugins/dsh-project-memory/locale/ru.json +6 -0
- package/plugins/dsh-project-memory/locale/zh.json +6 -0
- package/plugins/dsh-project-memory/package.json +26 -0
- package/plugins/dsh-session-move/README.md +67 -0
- package/plugins/dsh-session-move/client.js +719 -0
- package/plugins/dsh-session-move/cordis.patch.yml +5 -0
- package/plugins/dsh-session-move/icon.svg +14 -0
- package/plugins/dsh-session-move/index.js +497 -0
- package/plugins/dsh-session-move/locale/en.json +6 -0
- package/plugins/dsh-session-move/locale/ru.json +6 -0
- package/plugins/dsh-session-move/locale/zh.json +6 -0
- package/plugins/dsh-session-move/package.json +31 -0
- package/plugins/dsh-session-move/screenshots/01-row-menu-1440-light.png +0 -0
- package/plugins/dsh-session-move/screenshots/02-dialog-1440-light.png +0 -0
- package/plugins/dsh-session-move/screenshots/03-dialog-390-light.png +0 -0
- package/plugins/dsh-session-move/screenshots/04-dialog-1440-dark.png +0 -0
- package/plugins/dsh-session-move/screenshots/05-drag-highlight-1440-light.png +0 -0
- package/plugins/dsh-session-move/screenshots/06-drop-ungrouped-highlight.png +0 -0
- package/plugins/dsh-session-move/screenshots/07-after-drop-ungrouped.png +0 -0
- package/plugins/dsh-session-move/screenshots/08-move-dialog.png +0 -0
- package/plugins/dsh-session-move/screenshots/09-moved-to-second-workspace.png +0 -0
- package/plugins/dsh-session-move/screenshots/qa-report.json +62 -0
- package/plugins/dsh-skill-autopilot/cordis.patch.yml +3 -0
- package/plugins/dsh-skill-autopilot/index.js +444 -0
- package/plugins/dsh-skill-autopilot/package.json +19 -0
- package/plugins/dsh-token-lab/cordis.patch.yml +3 -0
- package/plugins/dsh-token-lab/lib/client.js +504 -0
- package/plugins/dsh-token-lab/lib/index.js +275 -0
- package/plugins/dsh-token-lab/package.json +26 -0
- package/plugins/dsh-token-lab/screenshots/01-token-lab-1440-light.png +0 -0
- package/plugins/dsh-token-lab/screenshots/02-token-lab-1440-dark.png +0 -0
- package/skills/code-review-checklist/SKILL.md +55 -0
- package/skills/content-writing/README.md +87 -0
- package/skills/content-writing/SKILL.md +85 -0
- package/skills/content-writing/assets/checklists/fact-check.md +53 -0
- package/skills/content-writing/assets/checklists/publish.md +65 -0
- package/skills/content-writing/assets/checklists/social.md +40 -0
- package/skills/content-writing/assets/templates/article.md +47 -0
- package/skills/content-writing/assets/templates/brief.md +44 -0
- package/skills/content-writing/assets/templates/doctor-card.md +41 -0
- package/skills/content-writing/assets/templates/image-prompts.md +128 -0
- package/skills/content-writing/assets/templates/post-telegram.md +36 -0
- package/skills/content-writing/assets/templates/post-vk.md +42 -0
- package/skills/content-writing/assets/templates/press-release.md +56 -0
- package/skills/content-writing/assets/templates/science-pop-note.md +55 -0
- package/skills/content-writing/assets/templates/service-card.md +46 -0
- package/skills/content-writing/references/anti-ai-style.md +248 -0
- package/skills/content-writing/references/editing.md +185 -0
- package/skills/content-writing/references/fact-checking.md +205 -0
- package/skills/content-writing/references/government-medical.md +246 -0
- package/skills/content-writing/references/images.md +212 -0
- package/skills/content-writing/references/journalism.md +252 -0
- package/skills/content-writing/references/process.md +123 -0
- package/skills/content-writing/references/quick-recipes.md +99 -0
- package/skills/content-writing/references/rules.md +85 -0
- package/skills/content-writing/references/science-popular.md +218 -0
- package/skills/content-writing/references/seo-content.md +172 -0
- package/skills/content-writing/references/social-media.md +253 -0
- package/skills/content-writing/references/sources.md +49 -0
- package/skills/content-writing/references/templates.md +291 -0
- package/skills/context-economy/SKILL.md +60 -0
- package/skills/context-economy/references/long-session.md +35 -0
- package/skills/context-economy/references/token-math.md +42 -0
- package/skills/context-economy/references/tool-recipes.md +34 -0
- package/skills/database-safety/SKILL.md +47 -0
- package/skills/debugging-playbook/SKILL.md +42 -0
- package/skills/design-critique/SKILL.md +65 -0
- package/skills/design-critique/references/a11y.md +25 -0
- package/skills/design-critique/references/diagnoses.md +33 -0
- package/skills/design-critique/references/quick-tests.md +30 -0
- package/skills/design-critique/references/report-template.md +63 -0
- package/skills/design-critique/references/rubric-fast.md +73 -0
- package/skills/dev-workflow/SKILL.md +48 -0
- package/skills/dev-workflow/references/definition-of-done.md +44 -0
- package/skills/dev-workflow/references/memory-and-boundaries.md +31 -0
- package/skills/dev-workflow/references/stages.md +67 -0
- package/skills/git-and-ci/SKILL.md +60 -0
- package/skills/pg-upgrade-verify/SKILL.md +67 -0
- package/skills/project-deep-dive/SKILL.md +45 -0
- package/skills/project-deep-dive/references/recon-commands.md +90 -0
- package/skills/project-deep-dive/references/section-templates.md +104 -0
- package/skills/project-deep-dive/references/tool-and-process.md +86 -0
- package/skills/project-memory-maintenance/SKILL.md +55 -0
- package/skills/project-memory-maintenance/references/api-and-checklist.md +99 -0
- package/skills/project-memory-maintenance/references/journal-template.md +50 -0
- package/skills/project-memory-maintenance/references/section-rules.md +59 -0
- package/skills/release-and-deploy/SKILL.md +53 -0
- package/skills/requirements-and-planning/SKILL.md +49 -0
- package/skills/requirements-and-planning/references/antipatterns-and-checklist.md +20 -0
- package/skills/requirements-and-planning/references/process-steps.md +52 -0
- package/skills/requirements-and-planning/references/templates-and-estimates.md +29 -0
- package/skills/security-review/SKILL.md +40 -0
- package/skills/seo/README.md +60 -0
- package/skills/seo/SKILL.md +77 -0
- package/skills/seo/assets/README.md +66 -0
- package/skills/seo/assets/scripts/LICENSE-playwright-core.txt +202 -0
- package/skills/seo/assets/scripts/README.md +179 -0
- package/skills/seo/assets/scripts/lib/args.mjs +105 -0
- package/skills/seo/assets/scripts/lib/browser.mjs +620 -0
- package/skills/seo/assets/scripts/lib/zip.mjs +191 -0
- package/skills/seo/assets/scripts/seo-audit.mjs +1675 -0
- package/skills/seo/assets/snippets/jsonld-breadcrumb.json +30 -0
- package/skills/seo/assets/snippets/jsonld-faq.json +39 -0
- package/skills/seo/assets/snippets/jsonld-medical-clinic.json +49 -0
- package/skills/seo/assets/snippets/jsonld-medical-webpage.json +33 -0
- package/skills/seo/assets/snippets/jsonld-organization.json +74 -0
- package/skills/seo/assets/snippets/jsonld-physician.json +46 -0
- package/skills/seo/assets/snippets/meta.blade.php +61 -0
- package/skills/seo/assets/snippets/robots.txt +61 -0
- package/skills/seo/assets/snippets/sitemap-urlset-example.xml +72 -0
- package/skills/seo/assets/snippets/sitemap.xml +36 -0
- package/skills/seo/references/antipatterns.md +66 -0
- package/skills/seo/references/audit-checklist.md +319 -0
- package/skills/seo/references/content-seo.md +204 -0
- package/skills/seo/references/eeat-medical.md +219 -0
- package/skills/seo/references/measurement.md +250 -0
- package/skills/seo/references/on-page.md +214 -0
- package/skills/seo/references/page-templates.md +56 -0
- package/skills/seo/references/stack-recipes.md +695 -0
- package/skills/seo/references/structured-data.md +487 -0
- package/skills/seo/references/technical-seo.md +296 -0
- package/skills/service-desk-conventions/SKILL.md +52 -0
- package/skills/service-desk-db-safety/SKILL.md +63 -0
- package/skills/service-desk-release/SKILL.md +61 -0
- package/skills/service-desk-testing/SKILL.md +61 -0
- package/skills/service-desk-ui/SKILL.md +52 -0
- package/skills/skills-router/SKILL.md +53 -0
- package/skills/skills-router/references/applying-skills.md +25 -0
- package/skills/skills-router/references/routing-matrix.md +52 -0
- package/skills/testing-strategy/SKILL.md +52 -0
- package/skills/ui-design-system/SKILL.md +50 -0
- package/skills/ui-design-system/references/accessibility-and-responsive.md +50 -0
- package/skills/ui-design-system/references/tokens-and-components.md +67 -0
- package/skills/ui-design-system/references/visual-qa.md +44 -0
- package/skills/web-research/SKILL.md +47 -0
- package/skills/web-research/references/answer-and-citations.md +60 -0
- package/skills/web-research/references/query-recipes.md +44 -0
- package/skills/wow-design/SKILL.md +89 -0
- package/skills/wow-design/assets/kit/README.md +195 -0
- package/skills/wow-design/assets/kit/assets/css/base.css +158 -0
- package/skills/wow-design/assets/kit/assets/css/components.css +1001 -0
- package/skills/wow-design/assets/kit/assets/css/fonts.css +73 -0
- package/skills/wow-design/assets/kit/assets/css/pages.css +737 -0
- package/skills/wow-design/assets/kit/assets/css/tokens.css +218 -0
- package/skills/wow-design/assets/kit/assets/fonts/LICENSE-golos-text.txt +93 -0
- package/skills/wow-design/assets/kit/assets/fonts/LICENSE-lora.txt +93 -0
- package/skills/wow-design/assets/kit/assets/fonts/LICENSE-unbounded.txt +93 -0
- package/skills/wow-design/assets/kit/assets/fonts/golos-text-cyrillic-400-normal.woff2 +0 -0
- package/skills/wow-design/assets/kit/assets/fonts/golos-text-cyrillic-500-normal.woff2 +0 -0
- package/skills/wow-design/assets/kit/assets/fonts/golos-text-cyrillic-600-normal.woff2 +0 -0
- package/skills/wow-design/assets/kit/assets/fonts/golos-text-cyrillic-700-normal.woff2 +0 -0
- package/skills/wow-design/assets/kit/assets/fonts/golos-text-latin-400-normal.woff2 +0 -0
- package/skills/wow-design/assets/kit/assets/fonts/golos-text-latin-500-normal.woff2 +0 -0
- package/skills/wow-design/assets/kit/assets/fonts/golos-text-latin-600-normal.woff2 +0 -0
- package/skills/wow-design/assets/kit/assets/fonts/golos-text-latin-700-normal.woff2 +0 -0
- package/skills/wow-design/assets/kit/assets/fonts/lora-cyrillic-400-italic.woff2 +0 -0
- package/skills/wow-design/assets/kit/assets/fonts/lora-cyrillic-400-normal.woff2 +0 -0
- package/skills/wow-design/assets/kit/assets/fonts/lora-cyrillic-500-normal.woff2 +0 -0
- package/skills/wow-design/assets/kit/assets/fonts/lora-cyrillic-600-normal.woff2 +0 -0
- package/skills/wow-design/assets/kit/assets/fonts/lora-latin-400-normal.woff2 +0 -0
- package/skills/wow-design/assets/kit/assets/fonts/lora-latin-500-normal.woff2 +0 -0
- package/skills/wow-design/assets/kit/assets/fonts/lora-latin-600-normal.woff2 +0 -0
- package/skills/wow-design/assets/kit/assets/fonts/unbounded-cyrillic-400-normal.woff2 +0 -0
- package/skills/wow-design/assets/kit/assets/fonts/unbounded-cyrillic-500-normal.woff2 +0 -0
- package/skills/wow-design/assets/kit/assets/fonts/unbounded-cyrillic-600-normal.woff2 +0 -0
- package/skills/wow-design/assets/kit/assets/fonts/unbounded-latin-400-normal.woff2 +0 -0
- package/skills/wow-design/assets/kit/assets/fonts/unbounded-latin-500-normal.woff2 +0 -0
- package/skills/wow-design/assets/kit/assets/fonts/unbounded-latin-600-normal.woff2 +0 -0
- package/skills/wow-design/assets/kit/assets/js/kit.js +550 -0
- package/skills/wow-design/assets/kit/pages/dashboard.html +539 -0
- package/skills/wow-design/assets/kit/pages/hospital.html +430 -0
- package/skills/wow-design/assets/kit/showcase.html +1164 -0
- package/skills/wow-design/assets/scripts/LICENSE-axe-core.txt +362 -0
- package/skills/wow-design/assets/scripts/LICENSE-playwright-core.txt +202 -0
- package/skills/wow-design/assets/scripts/README.md +312 -0
- package/skills/wow-design/assets/scripts/audit.mjs +1200 -0
- package/skills/wow-design/assets/scripts/capture.mjs +337 -0
- package/skills/wow-design/assets/scripts/package.json +30 -0
- package/skills/wow-design/references/anti-patterns.md +252 -0
- package/skills/wow-design/references/critique-rubric.md +262 -0
- package/skills/wow-design/references/design-brief-template.md +48 -0
- package/skills/wow-design/references/inspiration.md +203 -0
- package/skills/wow-design/references/presets.md +99 -0
- package/skills/wow-design/references/principles.md +371 -0
- package/skills/wow-design/references/quick-presets.md +50 -0
- package/skills/wow-design/references/report-template.md +21 -0
- package/skills/wow-design/references/stack-recipes.md +96 -0
- package/skills/wow-design/references/visual-qa.md +82 -0
- package/skills/wow-design/references/wow-levers.md +43 -0
- package/tools/skill-cost.mjs +76 -0
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
// Минимальный ZIP-ридер без внешних зависимостей (store + deflate, CRC32).
|
|
2
|
+
// Нужен валидатору, чтобы читать нужные файлы прямо из архива, не распаковывая всё в temp.
|
|
3
|
+
// ZIP64 и шифрованные архивы не поддерживаются — для портативного пакета DSH это не нужно.
|
|
4
|
+
|
|
5
|
+
import fs from 'node:fs';
|
|
6
|
+
import zlib from 'node:zlib';
|
|
7
|
+
import crypto from 'node:crypto';
|
|
8
|
+
|
|
9
|
+
const EOCD_SIG = 0x06054b50;
|
|
10
|
+
const CD_SIG = 0x02014b50;
|
|
11
|
+
const LFH_SIG = 0x04034b50;
|
|
12
|
+
|
|
13
|
+
export class ZipError extends Error {
|
|
14
|
+
constructor(message, code = 'zip') {
|
|
15
|
+
super(message);
|
|
16
|
+
this.name = 'ZipError';
|
|
17
|
+
this.code = code;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const CRC_TABLE = (() => {
|
|
22
|
+
const table = new Uint32Array(256);
|
|
23
|
+
for (let n = 0; n < 256; n++) {
|
|
24
|
+
let c = n;
|
|
25
|
+
for (let k = 0; k < 8; k++) c = c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1;
|
|
26
|
+
table[n] = c >>> 0;
|
|
27
|
+
}
|
|
28
|
+
return table;
|
|
29
|
+
})();
|
|
30
|
+
|
|
31
|
+
export function crc32(buf) {
|
|
32
|
+
let c = 0xffffffff;
|
|
33
|
+
for (let i = 0; i < buf.length; i++) c = CRC_TABLE[(c ^ buf[i]) & 0xff] ^ (c >>> 8);
|
|
34
|
+
return (c ^ 0xffffffff) >>> 0;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function sha256(buf) {
|
|
38
|
+
return crypto.createHash('sha256').update(buf).digest('hex');
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Декодирование имени записи: UTF-8 по флагу, иначе UTF-8/ibm866/latin1. */
|
|
42
|
+
function decodeName(buf, flags) {
|
|
43
|
+
if (flags & 0x800) {
|
|
44
|
+
try {
|
|
45
|
+
return new TextDecoder('utf-8', { fatal: true }).decode(buf);
|
|
46
|
+
} catch {
|
|
47
|
+
/* fallthrough */
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
try {
|
|
51
|
+
return new TextDecoder('utf-8', { fatal: true }).decode(buf);
|
|
52
|
+
} catch {
|
|
53
|
+
try {
|
|
54
|
+
return new TextDecoder('ibm866').decode(buf); // OEM-кодировка русской Windows
|
|
55
|
+
} catch {
|
|
56
|
+
return buf.toString('latin1');
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Прочитать ZIP-архив целиком в память и разобрать оглавление.
|
|
63
|
+
* @returns {{filePath:string, fileSize:number, archiveSha256:string, entries:Array, files:Array, dirs:Array, read:Function, readPath:Function, warnings:string[]}}
|
|
64
|
+
*/
|
|
65
|
+
export function readZip(filePath) {
|
|
66
|
+
const buf = fs.readFileSync(filePath);
|
|
67
|
+
const warnings = [];
|
|
68
|
+
|
|
69
|
+
let eocd = -1;
|
|
70
|
+
const minPos = Math.max(0, buf.length - 22 - 65535);
|
|
71
|
+
for (let i = buf.length - 22; i >= minPos; i--) {
|
|
72
|
+
if (buf.readUInt32LE(i) === EOCD_SIG) {
|
|
73
|
+
eocd = i;
|
|
74
|
+
break;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
if (eocd < 0) throw new ZipError('Не найден EOCD (конец центрального каталога) — файл повреждён или это не ZIP', 'corrupt');
|
|
78
|
+
|
|
79
|
+
const disk = buf.readUInt16LE(eocd + 4);
|
|
80
|
+
const cdDisk = buf.readUInt16LE(eocd + 6);
|
|
81
|
+
const entriesDisk = buf.readUInt16LE(eocd + 8);
|
|
82
|
+
const entriesTotal = buf.readUInt16LE(eocd + 10);
|
|
83
|
+
const cdSize = buf.readUInt32LE(eocd + 12);
|
|
84
|
+
const cdOffset = buf.readUInt32LE(eocd + 16);
|
|
85
|
+
const commentLen = buf.readUInt16LE(eocd + 20);
|
|
86
|
+
if (eocd + 22 + commentLen > buf.length) warnings.push('EOCD-комментарий выходит за границу файла');
|
|
87
|
+
if (disk !== 0 || cdDisk !== 0 || entriesDisk !== entriesTotal) {
|
|
88
|
+
throw new ZipError('Многотомные ZIP не поддерживаются', 'unsupported');
|
|
89
|
+
}
|
|
90
|
+
if (cdOffset === 0xffffffff || cdSize === 0xffffffff || entriesTotal === 0xffff) {
|
|
91
|
+
throw new ZipError('ZIP64 не поддерживается валидатором', 'unsupported');
|
|
92
|
+
}
|
|
93
|
+
if (cdOffset + cdSize > buf.length) {
|
|
94
|
+
throw new ZipError('Центральный каталог выходит за границу файла — архив обрезан', 'corrupt');
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const entries = [];
|
|
98
|
+
let p = cdOffset;
|
|
99
|
+
for (let i = 0; i < entriesTotal; i++) {
|
|
100
|
+
if (p + 46 > buf.length || buf.readUInt32LE(p) !== CD_SIG) {
|
|
101
|
+
throw new ZipError(`Повреждён центральный каталог ZIP (запись ${i + 1})`, 'corrupt');
|
|
102
|
+
}
|
|
103
|
+
const flags = buf.readUInt16LE(p + 8);
|
|
104
|
+
const method = buf.readUInt16LE(p + 10);
|
|
105
|
+
const crc = buf.readUInt32LE(p + 16);
|
|
106
|
+
const compSize = buf.readUInt32LE(p + 20);
|
|
107
|
+
const uncompSize = buf.readUInt32LE(p + 24);
|
|
108
|
+
const nameLen = buf.readUInt16LE(p + 28);
|
|
109
|
+
const extraLen = buf.readUInt16LE(p + 30);
|
|
110
|
+
const commentLen2 = buf.readUInt16LE(p + 32);
|
|
111
|
+
const extAttr = buf.readUInt32LE(p + 38);
|
|
112
|
+
const localOffset = buf.readUInt32LE(p + 42);
|
|
113
|
+
if (compSize === 0xffffffff || uncompSize === 0xffffffff || localOffset === 0xffffffff) {
|
|
114
|
+
throw new ZipError(`ZIP64-запись не поддерживается: ${decodeName(buf.subarray(p + 46, p + 46 + nameLen), flags)}`, 'unsupported');
|
|
115
|
+
}
|
|
116
|
+
const name = decodeName(buf.subarray(p + 46, p + 46 + nameLen), flags);
|
|
117
|
+
p += 46 + nameLen + extraLen + commentLen2;
|
|
118
|
+
|
|
119
|
+
const unixMode = (extAttr >>> 16) & 0xffff;
|
|
120
|
+
const isDir = name.endsWith('/') || (unixMode & 0o170000) === 0o040000 || (extAttr & 0x10) !== 0;
|
|
121
|
+
entries.push({
|
|
122
|
+
path: name.replace(/\\/g, '/'),
|
|
123
|
+
rawName: name,
|
|
124
|
+
isDir,
|
|
125
|
+
size: uncompSize,
|
|
126
|
+
compSize,
|
|
127
|
+
crc,
|
|
128
|
+
method,
|
|
129
|
+
flags,
|
|
130
|
+
encrypted: (flags & 1) !== 0,
|
|
131
|
+
localOffset,
|
|
132
|
+
extAttr,
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const byPath = new Map(entries.map((e) => [e.path, e]));
|
|
137
|
+
|
|
138
|
+
function read(entry) {
|
|
139
|
+
if (!entry || entry.isDir) return Buffer.alloc(0);
|
|
140
|
+
if (entry.encrypted) throw new ZipError(`Запись зашифрована: ${entry.path}`, 'unsupported');
|
|
141
|
+
const off = entry.localOffset;
|
|
142
|
+
if (off + 30 > buf.length || buf.readUInt32LE(off) !== LFH_SIG) {
|
|
143
|
+
throw new ZipError(`Повреждён локальный заголовок записи: ${entry.path}`, 'corrupt');
|
|
144
|
+
}
|
|
145
|
+
const nameLen = buf.readUInt16LE(off + 26);
|
|
146
|
+
const extraLen = buf.readUInt16LE(off + 28);
|
|
147
|
+
const dataStart = off + 30 + nameLen + extraLen;
|
|
148
|
+
if (dataStart + entry.compSize > buf.length) {
|
|
149
|
+
throw new ZipError(`Обрезаны данные записи: ${entry.path}`, 'corrupt');
|
|
150
|
+
}
|
|
151
|
+
const compressed = buf.subarray(dataStart, dataStart + entry.compSize);
|
|
152
|
+
let data;
|
|
153
|
+
if (entry.method === 0) data = Buffer.from(compressed);
|
|
154
|
+
else if (entry.method === 8) {
|
|
155
|
+
try {
|
|
156
|
+
data = zlib.inflateRawSync(compressed);
|
|
157
|
+
} catch (e) {
|
|
158
|
+
throw new ZipError(`Ошибка распаковки (deflate): ${entry.path} — ${e.message}`, 'corrupt');
|
|
159
|
+
}
|
|
160
|
+
} else {
|
|
161
|
+
throw new ZipError(`Метод сжатия ${entry.method} не поддерживается: ${entry.path}`, 'unsupported');
|
|
162
|
+
}
|
|
163
|
+
if (data.length !== entry.size) {
|
|
164
|
+
throw new ZipError(`Размер записи не совпал (${data.length} != ${entry.size}): ${entry.path}`, 'corrupt');
|
|
165
|
+
}
|
|
166
|
+
const actual = crc32(data);
|
|
167
|
+
if (actual !== entry.crc) {
|
|
168
|
+
throw new ZipError(`CRC32 не совпал: ${entry.path}`, 'corrupt');
|
|
169
|
+
}
|
|
170
|
+
return data;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return {
|
|
174
|
+
filePath,
|
|
175
|
+
fileSize: buf.length,
|
|
176
|
+
archiveSha256: sha256(buf),
|
|
177
|
+
entries,
|
|
178
|
+
files: entries.filter((e) => !e.isDir),
|
|
179
|
+
dirs: entries.filter((e) => e.isDir),
|
|
180
|
+
read,
|
|
181
|
+
readPath(name) {
|
|
182
|
+
const entry = byPath.get(name.replace(/\\/g, '/'));
|
|
183
|
+
if (!entry) throw new ZipError(`Нет записи в архиве: ${name}`, 'missing');
|
|
184
|
+
return read(entry);
|
|
185
|
+
},
|
|
186
|
+
hasPath(name) {
|
|
187
|
+
return byPath.has(name.replace(/\\/g, '/'));
|
|
188
|
+
},
|
|
189
|
+
warnings,
|
|
190
|
+
};
|
|
191
|
+
}
|