@vcgstudiosy-beep/dlof 1.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 +103 -0
- package/bin/dlof.js +2 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +237 -0
- package/dist/dlof/parseDlof.d.ts +6 -0
- package/dist/dlof/parseDlof.js +181 -0
- package/dist/dlof/serializeDlof.d.ts +3 -0
- package/dist/dlof/serializeDlof.js +176 -0
- package/dist/dlof/validate.d.ts +11 -0
- package/dist/dlof/validate.js +76 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +47 -0
- package/dist/loop/loopUtils.d.ts +16 -0
- package/dist/loop/loopUtils.js +66 -0
- package/dist/packages/dlofSeries.d.ts +9 -0
- package/dist/packages/dlofSeries.js +60 -0
- package/dist/packages/dlofpkg.d.ts +10 -0
- package/dist/packages/dlofpkg.js +72 -0
- package/dist/player/renderPlayerHtml.d.ts +13 -0
- package/dist/player/renderPlayerHtml.js +235 -0
- package/dist/types.d.ts +141 -0
- package/dist/types.js +6 -0
- package/dist/viewer/renderContent.d.ts +3 -0
- package/dist/viewer/renderContent.js +77 -0
- package/dist/viewer/renderViewerHtml.d.ts +11 -0
- package/dist/viewer/renderViewerHtml.js +46 -0
- package/dist/viewer/theme.d.ts +13 -0
- package/dist/viewer/theme.js +102 -0
- package/dist/xml/parser.d.ts +22 -0
- package/dist/xml/parser.js +199 -0
- package/dist/xml/serializer.d.ts +11 -0
- package/dist/xml/serializer.js +46 -0
- package/dist/zip/crc32.d.ts +2 -0
- package/dist/zip/crc32.js +22 -0
- package/dist/zip/zipReader.d.ts +14 -0
- package/dist/zip/zipReader.js +109 -0
- package/dist/zip/zipWriter.d.ts +9 -0
- package/dist/zip/zipWriter.js +106 -0
- package/package.json +42 -0
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* محلّل XML خفيف بلا اعتمادات خارجية.
|
|
4
|
+
* غير مصمم كمحلل XML عام كامل المواصفة، بل مخصّص لتغطية كل ما تحتاجه صيغة DLoF:
|
|
5
|
+
* عناصر متداخلة، صفات، نص، CDATA، تعليقات، عناصر ذاتية الإغلاق، وترميز UTF-8.
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.encodeEntities = encodeEntities;
|
|
9
|
+
exports.parseXml = parseXml;
|
|
10
|
+
exports.child = child;
|
|
11
|
+
exports.children = children;
|
|
12
|
+
exports.childText = childText;
|
|
13
|
+
const ENTITY_MAP = {
|
|
14
|
+
amp: "&",
|
|
15
|
+
lt: "<",
|
|
16
|
+
gt: ">",
|
|
17
|
+
quot: '"',
|
|
18
|
+
apos: "'",
|
|
19
|
+
};
|
|
20
|
+
function decodeEntities(input) {
|
|
21
|
+
return input.replace(/&(#x?[0-9a-fA-F]+|[a-zA-Z]+);/g, (match, ent) => {
|
|
22
|
+
if (ent[0] === "#") {
|
|
23
|
+
const isHex = ent[1] === "x" || ent[1] === "X";
|
|
24
|
+
const code = parseInt(ent.slice(isHex ? 2 : 1), isHex ? 16 : 10);
|
|
25
|
+
return Number.isFinite(code) ? String.fromCodePoint(code) : match;
|
|
26
|
+
}
|
|
27
|
+
return ENTITY_MAP[ent] ?? match;
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
function encodeEntities(input) {
|
|
31
|
+
return input
|
|
32
|
+
.replace(/&/g, "&")
|
|
33
|
+
.replace(/</g, "<")
|
|
34
|
+
.replace(/>/g, ">")
|
|
35
|
+
.replace(/"/g, """);
|
|
36
|
+
}
|
|
37
|
+
class Cursor {
|
|
38
|
+
constructor(src) {
|
|
39
|
+
this.src = src;
|
|
40
|
+
this.pos = 0;
|
|
41
|
+
}
|
|
42
|
+
eof() {
|
|
43
|
+
return this.pos >= this.src.length;
|
|
44
|
+
}
|
|
45
|
+
peek(len = 1) {
|
|
46
|
+
return this.src.slice(this.pos, this.pos + len);
|
|
47
|
+
}
|
|
48
|
+
startsWith(s) {
|
|
49
|
+
return this.src.startsWith(s, this.pos);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
function skipWhitespace(c) {
|
|
53
|
+
while (!c.eof() && /\s/.test(c.peek()))
|
|
54
|
+
c.pos++;
|
|
55
|
+
}
|
|
56
|
+
function skipProlog(c) {
|
|
57
|
+
// <?xml ... ?>
|
|
58
|
+
skipWhitespace(c);
|
|
59
|
+
while (c.startsWith("<?")) {
|
|
60
|
+
const end = c.src.indexOf("?>", c.pos);
|
|
61
|
+
c.pos = end === -1 ? c.src.length : end + 2;
|
|
62
|
+
skipWhitespace(c);
|
|
63
|
+
}
|
|
64
|
+
// تعليقات وDOCTYPE قبل الجذر
|
|
65
|
+
for (;;) {
|
|
66
|
+
if (c.startsWith("<!--")) {
|
|
67
|
+
const end = c.src.indexOf("-->", c.pos);
|
|
68
|
+
c.pos = end === -1 ? c.src.length : end + 3;
|
|
69
|
+
skipWhitespace(c);
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
if (c.startsWith("<!DOCTYPE")) {
|
|
73
|
+
const end = c.src.indexOf(">", c.pos);
|
|
74
|
+
c.pos = end === -1 ? c.src.length : end + 1;
|
|
75
|
+
skipWhitespace(c);
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
break;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
function readTagName(c) {
|
|
82
|
+
const start = c.pos;
|
|
83
|
+
while (!c.eof() && /[^\s/>]/.test(c.peek()))
|
|
84
|
+
c.pos++;
|
|
85
|
+
return c.src.slice(start, c.pos);
|
|
86
|
+
}
|
|
87
|
+
function readAttrs(c) {
|
|
88
|
+
const attrs = {};
|
|
89
|
+
for (;;) {
|
|
90
|
+
skipWhitespace(c);
|
|
91
|
+
if (c.eof() || c.peek() === "/" || c.peek() === ">")
|
|
92
|
+
break;
|
|
93
|
+
const nameStart = c.pos;
|
|
94
|
+
while (!c.eof() && /[^\s=/>]/.test(c.peek()))
|
|
95
|
+
c.pos++;
|
|
96
|
+
const name = c.src.slice(nameStart, c.pos);
|
|
97
|
+
skipWhitespace(c);
|
|
98
|
+
if (c.peek() === "=") {
|
|
99
|
+
c.pos++; // =
|
|
100
|
+
skipWhitespace(c);
|
|
101
|
+
const quote = c.peek();
|
|
102
|
+
let value = "";
|
|
103
|
+
if (quote === '"' || quote === "'") {
|
|
104
|
+
c.pos++;
|
|
105
|
+
const valStart = c.pos;
|
|
106
|
+
const endIdx = c.src.indexOf(quote, c.pos);
|
|
107
|
+
const valEnd = endIdx === -1 ? c.src.length : endIdx;
|
|
108
|
+
value = c.src.slice(valStart, valEnd);
|
|
109
|
+
c.pos = valEnd + 1;
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
const valStart = c.pos;
|
|
113
|
+
while (!c.eof() && /[^\s/>]/.test(c.peek()))
|
|
114
|
+
c.pos++;
|
|
115
|
+
value = c.src.slice(valStart, c.pos);
|
|
116
|
+
}
|
|
117
|
+
if (name)
|
|
118
|
+
attrs[name] = decodeEntities(value);
|
|
119
|
+
}
|
|
120
|
+
else if (name) {
|
|
121
|
+
attrs[name] = "";
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return attrs;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* يحلّل مستند XML كاملاً ويعيد العنصر الجذر.
|
|
128
|
+
*/
|
|
129
|
+
function parseXml(source) {
|
|
130
|
+
const c = new Cursor(source);
|
|
131
|
+
skipProlog(c);
|
|
132
|
+
function parseElement() {
|
|
133
|
+
// نتوقع c.peek() === '<'
|
|
134
|
+
c.pos++; // تجاوز '<'
|
|
135
|
+
const tag = readTagName(c);
|
|
136
|
+
const attrs = readAttrs(c);
|
|
137
|
+
skipWhitespace(c);
|
|
138
|
+
const node = { tag, attrs, children: [], text: "" };
|
|
139
|
+
if (c.peek(2) === "/>") {
|
|
140
|
+
c.pos += 2;
|
|
141
|
+
return node;
|
|
142
|
+
}
|
|
143
|
+
if (c.peek() === ">") {
|
|
144
|
+
c.pos += 1;
|
|
145
|
+
}
|
|
146
|
+
const textParts = [];
|
|
147
|
+
for (;;) {
|
|
148
|
+
if (c.eof())
|
|
149
|
+
break;
|
|
150
|
+
if (c.startsWith("<!--")) {
|
|
151
|
+
const end = c.src.indexOf("-->", c.pos);
|
|
152
|
+
c.pos = end === -1 ? c.src.length : end + 3;
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
if (c.startsWith("<![CDATA[")) {
|
|
156
|
+
const end = c.src.indexOf("]]>", c.pos);
|
|
157
|
+
const contentEnd = end === -1 ? c.src.length : end;
|
|
158
|
+
textParts.push(c.src.slice(c.pos + 9, contentEnd));
|
|
159
|
+
c.pos = end === -1 ? c.src.length : end + 3;
|
|
160
|
+
continue;
|
|
161
|
+
}
|
|
162
|
+
if (c.startsWith("</")) {
|
|
163
|
+
const end = c.src.indexOf(">", c.pos);
|
|
164
|
+
c.pos = end === -1 ? c.src.length : end + 1;
|
|
165
|
+
break; // نهاية هذا العنصر
|
|
166
|
+
}
|
|
167
|
+
if (c.peek() === "<") {
|
|
168
|
+
const child = parseElement();
|
|
169
|
+
node.children.push(child);
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
// نص عادي حتى أول '<'
|
|
173
|
+
const nextLt = c.src.indexOf("<", c.pos);
|
|
174
|
+
const textEnd = nextLt === -1 ? c.src.length : nextLt;
|
|
175
|
+
textParts.push(c.src.slice(c.pos, textEnd));
|
|
176
|
+
c.pos = textEnd;
|
|
177
|
+
}
|
|
178
|
+
node.text = decodeEntities(textParts.join("")).trim();
|
|
179
|
+
return node;
|
|
180
|
+
}
|
|
181
|
+
skipWhitespace(c);
|
|
182
|
+
if (c.eof() || c.peek() !== "<") {
|
|
183
|
+
throw new Error("مستند XML غير صالح: لا يوجد عنصر جذر");
|
|
184
|
+
}
|
|
185
|
+
return parseElement();
|
|
186
|
+
}
|
|
187
|
+
/** يبحث عن أول عنصر فرعي مباشر بالاسم المحدد */
|
|
188
|
+
function child(node, tag) {
|
|
189
|
+
return node?.children.find((n) => n.tag === tag);
|
|
190
|
+
}
|
|
191
|
+
/** يعيد كل العناصر الفرعية المباشرة بالاسم المحدد */
|
|
192
|
+
function children(node, tag) {
|
|
193
|
+
return node?.children.filter((n) => n.tag === tag) ?? [];
|
|
194
|
+
}
|
|
195
|
+
/** نص عنصر فرعي مباشر، أو undefined إن لم يوجد */
|
|
196
|
+
function childText(node, tag) {
|
|
197
|
+
const n = child(node, tag);
|
|
198
|
+
return n ? n.text : undefined;
|
|
199
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export interface EBuilder {
|
|
2
|
+
tag: string;
|
|
3
|
+
attrs?: Record<string, string | number | boolean | undefined>;
|
|
4
|
+
text?: string;
|
|
5
|
+
children?: EBuilder[];
|
|
6
|
+
selfClosing?: boolean;
|
|
7
|
+
/** تعليق XML يُدرج قبل هذا العنصر (اختياري) */
|
|
8
|
+
commentBefore?: string;
|
|
9
|
+
}
|
|
10
|
+
/** يبني مستند XML كامل بترويسة UTF-8 من عنصر جذر واحد */
|
|
11
|
+
export declare function buildXmlDocument(root: EBuilder): string;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.buildXmlDocument = buildXmlDocument;
|
|
4
|
+
const parser_1 = require("./parser");
|
|
5
|
+
function attrString(attrs) {
|
|
6
|
+
if (!attrs)
|
|
7
|
+
return "";
|
|
8
|
+
const parts = [];
|
|
9
|
+
for (const [k, v] of Object.entries(attrs)) {
|
|
10
|
+
if (v === undefined)
|
|
11
|
+
continue;
|
|
12
|
+
parts.push(`${k}="${(0, parser_1.encodeEntities)(String(v))}"`);
|
|
13
|
+
}
|
|
14
|
+
return parts.length ? " " + parts.join(" ") : "";
|
|
15
|
+
}
|
|
16
|
+
function renderNode(node, indent) {
|
|
17
|
+
const pad = " ".repeat(indent);
|
|
18
|
+
const lines = [];
|
|
19
|
+
if (node.commentBefore) {
|
|
20
|
+
lines.push(`${pad}<!-- ${node.commentBefore} -->`);
|
|
21
|
+
}
|
|
22
|
+
const attrs = attrString(node.attrs);
|
|
23
|
+
const hasChildren = node.children && node.children.length > 0;
|
|
24
|
+
const hasText = node.text !== undefined && node.text !== "";
|
|
25
|
+
if (!hasChildren && !hasText) {
|
|
26
|
+
lines.push(`${pad}<${node.tag}${attrs}/>`);
|
|
27
|
+
return lines.join("\n");
|
|
28
|
+
}
|
|
29
|
+
if (hasText && !hasChildren) {
|
|
30
|
+
lines.push(`${pad}<${node.tag}${attrs}>${(0, parser_1.encodeEntities)(node.text)}</${node.tag}>`);
|
|
31
|
+
return lines.join("\n");
|
|
32
|
+
}
|
|
33
|
+
lines.push(`${pad}<${node.tag}${attrs}>`);
|
|
34
|
+
if (hasText) {
|
|
35
|
+
lines.push(`${" ".repeat(indent + 1)}${(0, parser_1.encodeEntities)(node.text)}`);
|
|
36
|
+
}
|
|
37
|
+
for (const ch of node.children ?? []) {
|
|
38
|
+
lines.push(renderNode(ch, indent + 1));
|
|
39
|
+
}
|
|
40
|
+
lines.push(`${pad}</${node.tag}>`);
|
|
41
|
+
return lines.join("\n");
|
|
42
|
+
}
|
|
43
|
+
/** يبني مستند XML كامل بترويسة UTF-8 من عنصر جذر واحد */
|
|
44
|
+
function buildXmlDocument(root) {
|
|
45
|
+
return `<?xml version="1.0" encoding="UTF-8"?>\n${renderNode(root, 0)}\n`;
|
|
46
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.crc32 = crc32;
|
|
4
|
+
const TABLE = (() => {
|
|
5
|
+
const table = new Array(256);
|
|
6
|
+
for (let n = 0; n < 256; n++) {
|
|
7
|
+
let c = n;
|
|
8
|
+
for (let k = 0; k < 8; k++) {
|
|
9
|
+
c = c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1;
|
|
10
|
+
}
|
|
11
|
+
table[n] = c >>> 0;
|
|
12
|
+
}
|
|
13
|
+
return table;
|
|
14
|
+
})();
|
|
15
|
+
/** يحسب CRC-32 لبيانات ثنائية، كما يتطلبه رأس ملف ZIP */
|
|
16
|
+
function crc32(buf) {
|
|
17
|
+
let crc = 0xffffffff;
|
|
18
|
+
for (let i = 0; i < buf.length; i++) {
|
|
19
|
+
crc = TABLE[(crc ^ buf[i]) & 0xff] ^ (crc >>> 8);
|
|
20
|
+
}
|
|
21
|
+
return (crc ^ 0xffffffff) >>> 0;
|
|
22
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export interface ZipEntry {
|
|
2
|
+
name: string;
|
|
3
|
+
data: Buffer;
|
|
4
|
+
isDirectory: boolean;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* يقرأ أرشيف ZIP من الذاكرة عبر تحليل الفهرس المركزي (Central Directory)
|
|
8
|
+
* ثم استخراج كل مُدخل حسب إزاحته في رؤوس الملفات المحلية (Local File Headers).
|
|
9
|
+
* يدعم أساليب الضغط STORE (0) و DEFLATE (8) فقط، وهو ما يستخدمه كل من
|
|
10
|
+
* .dlofpkg و .dlofSeries و أدوات zip القياسية.
|
|
11
|
+
*/
|
|
12
|
+
export declare function readZip(buf: Buffer): ZipEntry[];
|
|
13
|
+
/** يبحث عن مُدخل باسم مطابق (نسبي) داخل قائمة المُدخلات */
|
|
14
|
+
export declare function findEntry(entries: ZipEntry[], name: string): ZipEntry | undefined;
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.readZip = readZip;
|
|
37
|
+
exports.findEntry = findEntry;
|
|
38
|
+
const zlib = __importStar(require("zlib"));
|
|
39
|
+
const EOCD_SIG = 0x06054b50;
|
|
40
|
+
const CDH_SIG = 0x02014b50;
|
|
41
|
+
/**
|
|
42
|
+
* يقرأ أرشيف ZIP من الذاكرة عبر تحليل الفهرس المركزي (Central Directory)
|
|
43
|
+
* ثم استخراج كل مُدخل حسب إزاحته في رؤوس الملفات المحلية (Local File Headers).
|
|
44
|
+
* يدعم أساليب الضغط STORE (0) و DEFLATE (8) فقط، وهو ما يستخدمه كل من
|
|
45
|
+
* .dlofpkg و .dlofSeries و أدوات zip القياسية.
|
|
46
|
+
*/
|
|
47
|
+
function readZip(buf) {
|
|
48
|
+
// ابحث عن توقيع نهاية الفهرس المركزي (EOCD) من نهاية الملف
|
|
49
|
+
let eocdPos = -1;
|
|
50
|
+
const minEocd = 22;
|
|
51
|
+
for (let i = buf.length - minEocd; i >= 0 && i >= buf.length - minEocd - 65557; i--) {
|
|
52
|
+
if (buf.readUInt32LE(i) === EOCD_SIG) {
|
|
53
|
+
eocdPos = i;
|
|
54
|
+
break;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
if (eocdPos === -1) {
|
|
58
|
+
throw new Error("ملف ZIP غير صالح: لم يُعثر على EOCD (نهاية الفهرس المركزي)");
|
|
59
|
+
}
|
|
60
|
+
const totalEntries = buf.readUInt16LE(eocdPos + 10);
|
|
61
|
+
const cdSize = buf.readUInt32LE(eocdPos + 12);
|
|
62
|
+
const cdOffset = buf.readUInt32LE(eocdPos + 16);
|
|
63
|
+
const entries = [];
|
|
64
|
+
let pos = cdOffset;
|
|
65
|
+
for (let i = 0; i < totalEntries; i++) {
|
|
66
|
+
if (buf.readUInt32LE(pos) !== CDH_SIG) {
|
|
67
|
+
throw new Error(`رأس الفهرس المركزي غير صالح عند الموضع ${pos}`);
|
|
68
|
+
}
|
|
69
|
+
const method = buf.readUInt16LE(pos + 10);
|
|
70
|
+
const compSize = buf.readUInt32LE(pos + 20);
|
|
71
|
+
const uncompSize = buf.readUInt32LE(pos + 24);
|
|
72
|
+
const nameLen = buf.readUInt16LE(pos + 28);
|
|
73
|
+
const extraLen = buf.readUInt16LE(pos + 30);
|
|
74
|
+
const commentLen = buf.readUInt16LE(pos + 32);
|
|
75
|
+
const localHeaderOffset = buf.readUInt32LE(pos + 42);
|
|
76
|
+
const name = buf.toString("utf8", pos + 46, pos + 46 + nameLen);
|
|
77
|
+
entries.push(readLocalEntry(buf, localHeaderOffset, name, method, compSize, uncompSize));
|
|
78
|
+
pos += 46 + nameLen + extraLen + commentLen;
|
|
79
|
+
}
|
|
80
|
+
return entries;
|
|
81
|
+
}
|
|
82
|
+
function readLocalEntry(buf, localOffset, name, method, compSize, uncompSize) {
|
|
83
|
+
const LFH_SIG = 0x04034b50;
|
|
84
|
+
if (buf.readUInt32LE(localOffset) !== LFH_SIG) {
|
|
85
|
+
throw new Error(`رأس الملف المحلي غير صالح للمُدخل "${name}"`);
|
|
86
|
+
}
|
|
87
|
+
const nameLen = buf.readUInt16LE(localOffset + 26);
|
|
88
|
+
const extraLen = buf.readUInt16LE(localOffset + 28);
|
|
89
|
+
const dataStart = localOffset + 30 + nameLen + extraLen;
|
|
90
|
+
const raw = buf.subarray(dataStart, dataStart + compSize);
|
|
91
|
+
let data;
|
|
92
|
+
if (method === 0) {
|
|
93
|
+
data = Buffer.from(raw);
|
|
94
|
+
}
|
|
95
|
+
else if (method === 8) {
|
|
96
|
+
data = zlib.inflateRawSync(raw);
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
throw new Error(`أسلوب ضغط غير مدعوم (${method}) للمُدخل "${name}"`);
|
|
100
|
+
}
|
|
101
|
+
if (uncompSize && data.length !== uncompSize) {
|
|
102
|
+
// بعض الأدوات تكتب 0 في compSize للمجلدات؛ لا نرمي خطأ صارماً هنا لمرونة أكبر
|
|
103
|
+
}
|
|
104
|
+
return { name, data, isDirectory: name.endsWith("/") };
|
|
105
|
+
}
|
|
106
|
+
/** يبحث عن مُدخل باسم مطابق (نسبي) داخل قائمة المُدخلات */
|
|
107
|
+
function findEntry(entries, name) {
|
|
108
|
+
return entries.find((e) => e.name === name || e.name === name.replace(/^\//, ""));
|
|
109
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export interface ZipInputEntry {
|
|
2
|
+
name: string;
|
|
3
|
+
data: Buffer;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* يبني أرشيف ZIP كامل في الذاكرة (رؤوس ملفات محلية + فهرس مركزي + EOCD)
|
|
7
|
+
* باستخدام ضغط DEFLATE عبر zlib المدمجة في Node، بلا أي اعتماد خارجي.
|
|
8
|
+
*/
|
|
9
|
+
export declare function writeZip(entries: ZipInputEntry[]): Buffer;
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.writeZip = writeZip;
|
|
37
|
+
const zlib = __importStar(require("zlib"));
|
|
38
|
+
const crc32_1 = require("./crc32");
|
|
39
|
+
function dosDateTime(date) {
|
|
40
|
+
const time = (date.getHours() << 11) | (date.getMinutes() << 5) | (Math.floor(date.getSeconds() / 2));
|
|
41
|
+
const dosDate = ((date.getFullYear() - 1980) << 9) | ((date.getMonth() + 1) << 5) | date.getDate();
|
|
42
|
+
return { time, date: dosDate };
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* يبني أرشيف ZIP كامل في الذاكرة (رؤوس ملفات محلية + فهرس مركزي + EOCD)
|
|
46
|
+
* باستخدام ضغط DEFLATE عبر zlib المدمجة في Node، بلا أي اعتماد خارجي.
|
|
47
|
+
*/
|
|
48
|
+
function writeZip(entries) {
|
|
49
|
+
const now = dosDateTime(new Date());
|
|
50
|
+
const localChunks = [];
|
|
51
|
+
const centralChunks = [];
|
|
52
|
+
let offset = 0;
|
|
53
|
+
for (const entry of entries) {
|
|
54
|
+
const nameBuf = Buffer.from(entry.name, "utf8");
|
|
55
|
+
const isDir = entry.data.length === 0 && entry.name.endsWith("/");
|
|
56
|
+
const compressed = isDir ? Buffer.alloc(0) : zlib.deflateRawSync(entry.data);
|
|
57
|
+
const method = isDir ? 0 : 8;
|
|
58
|
+
const crc = isDir ? 0 : (0, crc32_1.crc32)(entry.data);
|
|
59
|
+
const localHeader = Buffer.alloc(30);
|
|
60
|
+
localHeader.writeUInt32LE(0x04034b50, 0);
|
|
61
|
+
localHeader.writeUInt16LE(20, 4); // version needed
|
|
62
|
+
localHeader.writeUInt16LE(0x0800, 6); // general purpose flag: bit 11 = UTF-8 filename
|
|
63
|
+
localHeader.writeUInt16LE(method, 8);
|
|
64
|
+
localHeader.writeUInt16LE(now.time, 10);
|
|
65
|
+
localHeader.writeUInt16LE(now.date, 12);
|
|
66
|
+
localHeader.writeUInt32LE(crc, 14);
|
|
67
|
+
localHeader.writeUInt32LE(compressed.length, 18);
|
|
68
|
+
localHeader.writeUInt32LE(entry.data.length, 22);
|
|
69
|
+
localHeader.writeUInt16LE(nameBuf.length, 26);
|
|
70
|
+
localHeader.writeUInt16LE(0, 28); // extra length
|
|
71
|
+
localChunks.push(localHeader, nameBuf, compressed);
|
|
72
|
+
const centralHeader = Buffer.alloc(46);
|
|
73
|
+
centralHeader.writeUInt32LE(0x02014b50, 0);
|
|
74
|
+
centralHeader.writeUInt16LE(20, 4); // version made by
|
|
75
|
+
centralHeader.writeUInt16LE(20, 6); // version needed
|
|
76
|
+
centralHeader.writeUInt16LE(0x0800, 8); // flags
|
|
77
|
+
centralHeader.writeUInt16LE(method, 10);
|
|
78
|
+
centralHeader.writeUInt16LE(now.time, 12);
|
|
79
|
+
centralHeader.writeUInt16LE(now.date, 14);
|
|
80
|
+
centralHeader.writeUInt32LE(crc, 16);
|
|
81
|
+
centralHeader.writeUInt32LE(compressed.length, 20);
|
|
82
|
+
centralHeader.writeUInt32LE(entry.data.length, 24);
|
|
83
|
+
centralHeader.writeUInt16LE(nameBuf.length, 28);
|
|
84
|
+
centralHeader.writeUInt16LE(0, 30); // extra length
|
|
85
|
+
centralHeader.writeUInt16LE(0, 32); // comment length
|
|
86
|
+
centralHeader.writeUInt16LE(0, 34); // disk number start
|
|
87
|
+
centralHeader.writeUInt16LE(0, 36); // internal attrs
|
|
88
|
+
centralHeader.writeUInt32LE(isDir ? 0x10 << 16 : 0, 38); // external attrs (dir flag)
|
|
89
|
+
centralHeader.writeUInt32LE(offset, 42);
|
|
90
|
+
centralChunks.push(centralHeader, nameBuf);
|
|
91
|
+
offset += localHeader.length + nameBuf.length + compressed.length;
|
|
92
|
+
}
|
|
93
|
+
const centralDirStart = offset;
|
|
94
|
+
const centralDir = Buffer.concat(centralChunks);
|
|
95
|
+
const centralDirSize = centralDir.length;
|
|
96
|
+
const eocd = Buffer.alloc(22);
|
|
97
|
+
eocd.writeUInt32LE(0x06054b50, 0);
|
|
98
|
+
eocd.writeUInt16LE(0, 4); // disk number
|
|
99
|
+
eocd.writeUInt16LE(0, 6); // disk with central dir
|
|
100
|
+
eocd.writeUInt16LE(entries.length, 8);
|
|
101
|
+
eocd.writeUInt16LE(entries.length, 10);
|
|
102
|
+
eocd.writeUInt32LE(centralDirSize, 12);
|
|
103
|
+
eocd.writeUInt32LE(centralDirStart, 16);
|
|
104
|
+
eocd.writeUInt16LE(0, 20); // comment length
|
|
105
|
+
return Buffer.concat([...localChunks, centralDir, eocd]);
|
|
106
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@vcgstudiosy-beep/dlof",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "مكتبة وأداة سطر أوامر رسمية لصيغة DLoF (Document Loop Format): تحليل وإنشاء ملفات .dlof، حزم .dlofpkg و .dlofSeries، عارض HTML، ومشغّل حلقات تفاعلي.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"dlof",
|
|
7
|
+
"dlofpkg",
|
|
8
|
+
"dlofSeries",
|
|
9
|
+
"document-loop-format",
|
|
10
|
+
"xml",
|
|
11
|
+
"arabic",
|
|
12
|
+
"reader",
|
|
13
|
+
"player"
|
|
14
|
+
],
|
|
15
|
+
"license": "MIT",
|
|
16
|
+
"author": "Droy",
|
|
17
|
+
"type": "commonjs",
|
|
18
|
+
"main": "dist/index.js",
|
|
19
|
+
"types": "dist/index.d.ts",
|
|
20
|
+
"bin": {
|
|
21
|
+
"dlof": "bin/dlof.js"
|
|
22
|
+
},
|
|
23
|
+
"files": [
|
|
24
|
+
"dist",
|
|
25
|
+
"bin",
|
|
26
|
+
"README.md",
|
|
27
|
+
"LICENSE"
|
|
28
|
+
],
|
|
29
|
+
"engines": {
|
|
30
|
+
"node": ">=16"
|
|
31
|
+
},
|
|
32
|
+
"scripts": {
|
|
33
|
+
"build": "tsc -p tsconfig.json",
|
|
34
|
+
"prepublishOnly": "npm run build",
|
|
35
|
+
"test": "node dist/test/run.js"
|
|
36
|
+
},
|
|
37
|
+
"dependencies": {},
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"typescript": "^5.4.0",
|
|
40
|
+
"@types/node": "^20.0.0"
|
|
41
|
+
}
|
|
42
|
+
}
|