@dooboostore/simple-boot-http-server 1.0.29 → 1.0.31
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/README.md +58 -1
- package/dist/cjs/SimpleBootHttpServer.js +9 -4
- package/dist/cjs/SimpleBootHttpServer.js.map +2 -2
- package/dist/cjs/codes/Mimes.js +2 -0
- package/dist/cjs/codes/Mimes.js.map +2 -2
- package/dist/cjs/generators/ContentTypeGenerator.js +320 -0
- package/dist/cjs/generators/ContentTypeGenerator.js.map +7 -0
- package/dist/cjs/index.js +2 -0
- package/dist/cjs/index.js.map +2 -2
- package/dist/cjs/models/RequestResponse.js +3 -2
- package/dist/cjs/models/RequestResponse.js.map +2 -2
- package/dist/esm/SimpleBootHttpServer.js +9 -4
- package/dist/esm/SimpleBootHttpServer.js.map +2 -2
- package/dist/esm/codes/Mimes.js +2 -0
- package/dist/esm/codes/Mimes.js.map +2 -2
- package/dist/esm/generators/ContentTypeGenerator.js +287 -0
- package/dist/esm/generators/ContentTypeGenerator.js.map +7 -0
- package/dist/esm/index.js +1 -0
- package/dist/esm/index.js.map +2 -2
- package/dist/esm/models/RequestResponse.js +3 -2
- package/dist/esm/models/RequestResponse.js.map +2 -2
- package/dist/esm-bundle/dooboostore-simple-boot-http-server.esm.js +628 -5219
- package/dist/esm-bundle/dooboostore-simple-boot-http-server.esm.js.map +4 -4
- package/dist/types/SimpleBootHttpServer.d.ts.map +1 -1
- package/dist/types/codes/Mimes.d.ts +2 -0
- package/dist/types/codes/Mimes.d.ts.map +1 -1
- package/dist/types/generators/ContentTypeGenerator.d.ts +172 -0
- package/dist/types/generators/ContentTypeGenerator.d.ts.map +1 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/models/RequestResponse.d.ts +4 -1
- package/dist/types/models/RequestResponse.d.ts.map +1 -1
- package/dist/umd-bundle/dooboostore-simple-boot-http-server.umd.js +628 -5219
- package/dist/umd-bundle/dooboostore-simple-boot-http-server.umd.js.map +4 -4
- package/package.json +4 -4
- package/src/SimpleBootHttpServer.ts +12 -4
- package/src/codes/Mimes.ts +2 -0
- package/src/generators/ContentTypeGenerator.ts +431 -0
- package/src/index.ts +1 -0
- package/src/models/RequestResponse.ts +3 -2
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
var __create = Object.create;
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
11
|
+
var __copyProps = (to, from, except, desc) => {
|
|
12
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
13
|
+
for (let key of __getOwnPropNames(from))
|
|
14
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
15
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
20
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
21
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
22
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
23
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
24
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
25
|
+
mod
|
|
26
|
+
));
|
|
27
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
28
|
+
var ContentTypeGenerator_exports = {};
|
|
29
|
+
__export(ContentTypeGenerator_exports, {
|
|
30
|
+
ContentTypeGenerator: () => ContentTypeGenerator
|
|
31
|
+
});
|
|
32
|
+
module.exports = __toCommonJS(ContentTypeGenerator_exports);
|
|
33
|
+
var import_crypto = require("crypto");
|
|
34
|
+
var import_mime_types = __toESM(require("mime-types"), 1);
|
|
35
|
+
var import_core = require("@dooboostore/core");
|
|
36
|
+
var import_Mimes = require("../codes/Mimes");
|
|
37
|
+
var ContentTypeGenerator;
|
|
38
|
+
((ContentTypeGenerator2) => {
|
|
39
|
+
ContentTypeGenerator2.isStream = (value) => import_core.ValidUtils.isAsyncGenerator(value) && typeof value.contentType === "string";
|
|
40
|
+
const oneLine = (v) => v.replace(/[\r\n]/g, " ");
|
|
41
|
+
ContentTypeGenerator2.sseChunk = (e) => {
|
|
42
|
+
const ev = typeof e === "string" ? { data: e } : e;
|
|
43
|
+
let out = "";
|
|
44
|
+
if (ev.comment !== void 0) out += ev.comment.split(/\r\n|\r|\n/).map((l) => `: ${l}
|
|
45
|
+
`).join("");
|
|
46
|
+
if (ev.event !== void 0) out += `event: ${oneLine(ev.event)}
|
|
47
|
+
`;
|
|
48
|
+
if (ev.id !== void 0) out += `id: ${oneLine(ev.id)}
|
|
49
|
+
`;
|
|
50
|
+
if (ev.retry !== void 0) out += `retry: ${Math.max(0, Math.floor(ev.retry))}
|
|
51
|
+
`;
|
|
52
|
+
if (ev.data !== void 0) {
|
|
53
|
+
const text2 = typeof ev.data === "string" ? ev.data : JSON.stringify(ev.data);
|
|
54
|
+
out += text2.split(/\r\n|\r|\n/).map((l) => `data: ${l}
|
|
55
|
+
`).join("");
|
|
56
|
+
}
|
|
57
|
+
return out + "\n";
|
|
58
|
+
};
|
|
59
|
+
ContentTypeGenerator2.ssePing = () => ": ping\n\n";
|
|
60
|
+
const sniff = (b) => {
|
|
61
|
+
if (b.length >= 3 && b[0] === 255 && b[1] === 216 && b[2] === 255) return import_Mimes.Mimes.ImageJpeg;
|
|
62
|
+
if (b.length >= 8 && b.readUInt32BE(0) === 2303741511 && b.readUInt32BE(4) === 218765834) return import_Mimes.Mimes.ImagePng;
|
|
63
|
+
if (b.length >= 6 && b.toString("ascii", 0, 4) === "GIF8") return import_Mimes.Mimes.ImageGif;
|
|
64
|
+
if (b.length >= 12 && b.toString("ascii", 0, 4) === "RIFF" && b.toString("ascii", 8, 12) === "WEBP") return "image/webp";
|
|
65
|
+
return void 0;
|
|
66
|
+
};
|
|
67
|
+
ContentTypeGenerator2.partType = (part, fallback) => {
|
|
68
|
+
const p = isPartObject(part) ? part : { body: part };
|
|
69
|
+
if (p.type) return p.type;
|
|
70
|
+
if (p.name) {
|
|
71
|
+
const byName = import_mime_types.default.lookup(p.name);
|
|
72
|
+
if (byName) return import_mime_types.default.contentType(byName) || byName;
|
|
73
|
+
}
|
|
74
|
+
if (typeof p.body === "string") return fallback ?? import_Mimes.Mimes.TextPlainUtf8;
|
|
75
|
+
return sniff(Buffer.from(p.body.buffer, p.body.byteOffset, p.body.byteLength)) ?? fallback ?? import_Mimes.Mimes.ApplicationOctetStream;
|
|
76
|
+
};
|
|
77
|
+
const isPartObject = (part) => typeof part === "object" && !(part instanceof Uint8Array);
|
|
78
|
+
const toBuffer = (body) => typeof body === "string" ? Buffer.from(body, "utf8") : Buffer.isBuffer(body) ? body : Buffer.from(body.buffer, body.byteOffset, body.byteLength);
|
|
79
|
+
ContentTypeGenerator2.multipartPart = (part, boundary2, fallbackType) => {
|
|
80
|
+
const p = isPartObject(part) ? part : { body: part };
|
|
81
|
+
const body = toBuffer(p.body);
|
|
82
|
+
let head = `--${boundary2}\r
|
|
83
|
+
Content-Type: ${(0, ContentTypeGenerator2.partType)(part, fallbackType)}\r
|
|
84
|
+
Content-Length: ${body.length}\r
|
|
85
|
+
`;
|
|
86
|
+
for (const [k, v] of Object.entries(p.headers ?? {})) head += `${oneLine(k)}: ${oneLine(v)}\r
|
|
87
|
+
`;
|
|
88
|
+
return Buffer.concat([Buffer.from(head + "\r\n", "latin1"), body, Buffer.from("\r\n")]);
|
|
89
|
+
};
|
|
90
|
+
ContentTypeGenerator2.multipartEnd = (boundary2) => Buffer.from(`--${boundary2}--\r
|
|
91
|
+
`);
|
|
92
|
+
ContentTypeGenerator2.ndjsonLine = (value) => json(value) + "\n";
|
|
93
|
+
const json = (value) => JSON.stringify(value) ?? "null";
|
|
94
|
+
ContentTypeGenerator2.jsonSeqRecord = (value) => "" + json(value) + "\n";
|
|
95
|
+
ContentTypeGenerator2.csvCell = (value) => {
|
|
96
|
+
if (value === null || value === void 0) return "";
|
|
97
|
+
const s = typeof value === "string" ? value : value instanceof Date ? value.toISOString() : typeof value === "object" ? json(value) : String(value);
|
|
98
|
+
return /[",\r\n]|^\s|\s$/.test(s) ? `"${s.replace(/"/g, '""')}"` : s;
|
|
99
|
+
};
|
|
100
|
+
ContentTypeGenerator2.csvRow = (cells) => cells.map(ContentTypeGenerator2.csvCell).join(",") + "\r\n";
|
|
101
|
+
ContentTypeGenerator2.textLine = (value) => (typeof value === "string" ? value : json(value)) + "\n";
|
|
102
|
+
ContentTypeGenerator2.boundary = (value) => {
|
|
103
|
+
const b = value ?? `boundary-${(0, import_crypto.randomBytes)(12).toString("hex")}`;
|
|
104
|
+
if (!/^[0-9A-Za-z'()+_,\-./:=? ]{0,69}[0-9A-Za-z'()+_,\-./:=?]$/.test(b)) {
|
|
105
|
+
throw new Error(`invalid multipart boundary: ${JSON.stringify(b)}`);
|
|
106
|
+
}
|
|
107
|
+
return b;
|
|
108
|
+
};
|
|
109
|
+
const toStream = (gen, contentType, compress) => Object.assign(gen, { contentType, compress });
|
|
110
|
+
async function* encode(src, fn) {
|
|
111
|
+
for await (const v of src) yield fn(v);
|
|
112
|
+
}
|
|
113
|
+
async function* framed(src, fn, start, end) {
|
|
114
|
+
if (start !== void 0) yield start;
|
|
115
|
+
yield* encode(src, fn);
|
|
116
|
+
if (end !== void 0) yield end;
|
|
117
|
+
}
|
|
118
|
+
const jsonArrayItem = () => {
|
|
119
|
+
let n = 0;
|
|
120
|
+
return (v) => (n++ ? "," : "") + json(v);
|
|
121
|
+
};
|
|
122
|
+
const csvEncoder = (opt) => (r) => (0, ContentTypeGenerator2.csvRow)(Array.isArray(r) ? r : opt.columns ? opt.columns.map((c) => r[c]) : Object.values(r));
|
|
123
|
+
const csvStart = (opt) => opt.bom || opt.columns ? (opt.bom ? "\uFEFF" : "") + (opt.columns ? (0, ContentTypeGenerator2.csvRow)(opt.columns) : "") : void 0;
|
|
124
|
+
const CsvType = `${import_Mimes.Mimes.TextCsv}; charset=utf-8`;
|
|
125
|
+
function sse(src, opt = {}) {
|
|
126
|
+
const contentType = import_Mimes.Mimes.TextEventStream;
|
|
127
|
+
if (!opt.heartbeatMs) return toStream(encode(src, ContentTypeGenerator2.sseChunk), contentType, true);
|
|
128
|
+
const ms = opt.heartbeatMs;
|
|
129
|
+
async function* withPing() {
|
|
130
|
+
const it = Symbol.asyncIterator in src ? src[Symbol.asyncIterator]() : src[Symbol.iterator]();
|
|
131
|
+
let pending;
|
|
132
|
+
try {
|
|
133
|
+
while (true) {
|
|
134
|
+
pending ??= Promise.resolve(it.next());
|
|
135
|
+
let timer;
|
|
136
|
+
const tick = new Promise((r2) => timer = setTimeout(() => r2("ping"), ms));
|
|
137
|
+
const r = await Promise.race([pending, tick]);
|
|
138
|
+
clearTimeout(timer);
|
|
139
|
+
if (r === "ping") {
|
|
140
|
+
yield (0, ContentTypeGenerator2.ssePing)();
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
pending = void 0;
|
|
144
|
+
if (r.done) return;
|
|
145
|
+
yield (0, ContentTypeGenerator2.sseChunk)(r.value);
|
|
146
|
+
}
|
|
147
|
+
} finally {
|
|
148
|
+
await it.return?.();
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
return toStream(withPing(), contentType, true);
|
|
152
|
+
}
|
|
153
|
+
ContentTypeGenerator2.sse = sse;
|
|
154
|
+
function multipartReplace(src, opt = {}) {
|
|
155
|
+
const b = (0, ContentTypeGenerator2.boundary)(opt.boundary);
|
|
156
|
+
return toStream(encode(src, (p) => (0, ContentTypeGenerator2.multipartPart)(p, b, opt.type)), `multipart/x-mixed-replace; boundary=${b}`, false);
|
|
157
|
+
}
|
|
158
|
+
ContentTypeGenerator2.multipartReplace = multipartReplace;
|
|
159
|
+
function multipartMixed(src, opt = {}) {
|
|
160
|
+
const b = (0, ContentTypeGenerator2.boundary)(opt.boundary);
|
|
161
|
+
return toStream(framed(src, (p) => (0, ContentTypeGenerator2.multipartPart)(p, b, opt.type), void 0, (0, ContentTypeGenerator2.multipartEnd)(b)), `multipart/mixed; boundary=${b}`, true);
|
|
162
|
+
}
|
|
163
|
+
ContentTypeGenerator2.multipartMixed = multipartMixed;
|
|
164
|
+
function ndjson(src) {
|
|
165
|
+
return toStream(encode(src, ContentTypeGenerator2.ndjsonLine), import_Mimes.Mimes.ApplicationXndjson, true);
|
|
166
|
+
}
|
|
167
|
+
ContentTypeGenerator2.ndjson = ndjson;
|
|
168
|
+
function jsonSeq(src) {
|
|
169
|
+
return toStream(encode(src, ContentTypeGenerator2.jsonSeqRecord), import_Mimes.Mimes.ApplicationJsonSeq, true);
|
|
170
|
+
}
|
|
171
|
+
ContentTypeGenerator2.jsonSeq = jsonSeq;
|
|
172
|
+
function jsonArray(src) {
|
|
173
|
+
return toStream(framed(src, jsonArrayItem(), "[", "]"), import_Mimes.Mimes.ApplicationJson, true);
|
|
174
|
+
}
|
|
175
|
+
ContentTypeGenerator2.jsonArray = jsonArray;
|
|
176
|
+
function csv(src, opt = {}) {
|
|
177
|
+
return toStream(framed(src, csvEncoder(opt), csvStart(opt)), CsvType, true);
|
|
178
|
+
}
|
|
179
|
+
ContentTypeGenerator2.csv = csv;
|
|
180
|
+
function text(src) {
|
|
181
|
+
return toStream(encode(src, ContentTypeGenerator2.textLine), import_Mimes.Mimes.TextPlainUtf8, true);
|
|
182
|
+
}
|
|
183
|
+
ContentTypeGenerator2.text = text;
|
|
184
|
+
function channel(encodeFn, contentType, compress, opt = {}) {
|
|
185
|
+
const keep = opt.keep ?? "queue";
|
|
186
|
+
const max = Math.max(1, opt.max ?? 1e3);
|
|
187
|
+
const queue = [];
|
|
188
|
+
const closers = [];
|
|
189
|
+
let closed = false;
|
|
190
|
+
let finished = false;
|
|
191
|
+
let startSent = false;
|
|
192
|
+
let endSent = false;
|
|
193
|
+
let wake;
|
|
194
|
+
const finish = () => {
|
|
195
|
+
if (finished) return;
|
|
196
|
+
finished = closed = true;
|
|
197
|
+
queue.length = 0;
|
|
198
|
+
opt.signal?.removeEventListener("abort", onAbort);
|
|
199
|
+
wake?.();
|
|
200
|
+
for (const fn of closers.splice(0)) {
|
|
201
|
+
try {
|
|
202
|
+
fn();
|
|
203
|
+
} catch {
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
};
|
|
207
|
+
const onAbort = () => finish();
|
|
208
|
+
if (opt.signal?.aborted) closed = finished = true;
|
|
209
|
+
else opt.signal?.addEventListener("abort", onAbort, { once: true });
|
|
210
|
+
const next = async () => {
|
|
211
|
+
while (!finished) {
|
|
212
|
+
if (opt.start && !startSent) {
|
|
213
|
+
startSent = true;
|
|
214
|
+
return { value: opt.start(), done: false };
|
|
215
|
+
}
|
|
216
|
+
if (queue.length) return { value: encodeFn(queue.shift()), done: false };
|
|
217
|
+
if (closed) {
|
|
218
|
+
if (opt.end && !endSent) {
|
|
219
|
+
endSent = true;
|
|
220
|
+
return { value: opt.end(), done: false };
|
|
221
|
+
}
|
|
222
|
+
finish();
|
|
223
|
+
break;
|
|
224
|
+
}
|
|
225
|
+
let timer;
|
|
226
|
+
const ping = await new Promise((resolve) => {
|
|
227
|
+
wake = () => resolve(false);
|
|
228
|
+
if (opt.heartbeat) timer = setTimeout(() => resolve(true), opt.heartbeat.ms);
|
|
229
|
+
});
|
|
230
|
+
wake = void 0;
|
|
231
|
+
clearTimeout(timer);
|
|
232
|
+
if (ping && !finished && !queue.length && !closed) return { value: opt.heartbeat.chunk(), done: false };
|
|
233
|
+
}
|
|
234
|
+
return { value: void 0, done: true };
|
|
235
|
+
};
|
|
236
|
+
const iterator = {
|
|
237
|
+
contentType,
|
|
238
|
+
compress,
|
|
239
|
+
get closed() {
|
|
240
|
+
return closed;
|
|
241
|
+
},
|
|
242
|
+
push(value) {
|
|
243
|
+
if (closed) return false;
|
|
244
|
+
if (keep === "latest") queue.length = 0;
|
|
245
|
+
else if (queue.length >= max) queue.shift();
|
|
246
|
+
queue.push(value);
|
|
247
|
+
wake?.();
|
|
248
|
+
return true;
|
|
249
|
+
},
|
|
250
|
+
close() {
|
|
251
|
+
closed = true;
|
|
252
|
+
wake?.();
|
|
253
|
+
},
|
|
254
|
+
onClose(fn) {
|
|
255
|
+
if (finished) fn();
|
|
256
|
+
else closers.push(fn);
|
|
257
|
+
},
|
|
258
|
+
next,
|
|
259
|
+
async return() {
|
|
260
|
+
finish();
|
|
261
|
+
return { value: void 0, done: true };
|
|
262
|
+
},
|
|
263
|
+
async throw(e) {
|
|
264
|
+
finish();
|
|
265
|
+
throw e;
|
|
266
|
+
},
|
|
267
|
+
[Symbol.asyncIterator]() {
|
|
268
|
+
return this;
|
|
269
|
+
}
|
|
270
|
+
};
|
|
271
|
+
const dispose = Symbol.asyncDispose;
|
|
272
|
+
if (dispose) iterator[dispose] = async () => finish();
|
|
273
|
+
return iterator;
|
|
274
|
+
}
|
|
275
|
+
ContentTypeGenerator2.channel = channel;
|
|
276
|
+
((sse2) => {
|
|
277
|
+
sse2.channel = (opt = {}) => ContentTypeGenerator2.channel(ContentTypeGenerator2.sseChunk, import_Mimes.Mimes.TextEventStream, true, {
|
|
278
|
+
...opt,
|
|
279
|
+
heartbeat: opt.heartbeatMs ? { ms: opt.heartbeatMs, chunk: ContentTypeGenerator2.ssePing } : void 0
|
|
280
|
+
});
|
|
281
|
+
})(sse = ContentTypeGenerator2.sse || (ContentTypeGenerator2.sse = {}));
|
|
282
|
+
((multipartReplace2) => {
|
|
283
|
+
multipartReplace2.channel = (opt = {}) => {
|
|
284
|
+
const b = (0, ContentTypeGenerator2.boundary)(opt.boundary);
|
|
285
|
+
return ContentTypeGenerator2.channel((p) => (0, ContentTypeGenerator2.multipartPart)(p, b, opt.type), `multipart/x-mixed-replace; boundary=${b}`, false, opt);
|
|
286
|
+
};
|
|
287
|
+
})(multipartReplace = ContentTypeGenerator2.multipartReplace || (ContentTypeGenerator2.multipartReplace = {}));
|
|
288
|
+
((multipartMixed2) => {
|
|
289
|
+
multipartMixed2.channel = (opt = {}) => {
|
|
290
|
+
const b = (0, ContentTypeGenerator2.boundary)(opt.boundary);
|
|
291
|
+
return ContentTypeGenerator2.channel((p) => (0, ContentTypeGenerator2.multipartPart)(p, b, opt.type), `multipart/mixed; boundary=${b}`, true, {
|
|
292
|
+
...opt,
|
|
293
|
+
end: () => (0, ContentTypeGenerator2.multipartEnd)(b)
|
|
294
|
+
});
|
|
295
|
+
};
|
|
296
|
+
})(multipartMixed = ContentTypeGenerator2.multipartMixed || (ContentTypeGenerator2.multipartMixed = {}));
|
|
297
|
+
((ndjson2) => {
|
|
298
|
+
ndjson2.channel = (opt = {}) => ContentTypeGenerator2.channel(ContentTypeGenerator2.ndjsonLine, import_Mimes.Mimes.ApplicationXndjson, true, opt);
|
|
299
|
+
})(ndjson = ContentTypeGenerator2.ndjson || (ContentTypeGenerator2.ndjson = {}));
|
|
300
|
+
((jsonSeq2) => {
|
|
301
|
+
jsonSeq2.channel = (opt = {}) => ContentTypeGenerator2.channel(ContentTypeGenerator2.jsonSeqRecord, import_Mimes.Mimes.ApplicationJsonSeq, true, opt);
|
|
302
|
+
})(jsonSeq = ContentTypeGenerator2.jsonSeq || (ContentTypeGenerator2.jsonSeq = {}));
|
|
303
|
+
((jsonArray2) => {
|
|
304
|
+
jsonArray2.channel = (opt = {}) => ContentTypeGenerator2.channel(jsonArrayItem(), import_Mimes.Mimes.ApplicationJson, true, { ...opt, start: () => "[", end: () => "]" });
|
|
305
|
+
})(jsonArray = ContentTypeGenerator2.jsonArray || (ContentTypeGenerator2.jsonArray = {}));
|
|
306
|
+
((csv2) => {
|
|
307
|
+
csv2.channel = (opt = {}) => {
|
|
308
|
+
const start = csvStart(opt);
|
|
309
|
+
return ContentTypeGenerator2.channel(csvEncoder(opt), CsvType, true, { ...opt, start: start === void 0 ? void 0 : () => start });
|
|
310
|
+
};
|
|
311
|
+
})(csv = ContentTypeGenerator2.csv || (ContentTypeGenerator2.csv = {}));
|
|
312
|
+
((text2) => {
|
|
313
|
+
text2.channel = (opt = {}) => ContentTypeGenerator2.channel(ContentTypeGenerator2.textLine, import_Mimes.Mimes.TextPlainUtf8, true, opt);
|
|
314
|
+
})(text = ContentTypeGenerator2.text || (ContentTypeGenerator2.text = {}));
|
|
315
|
+
})(ContentTypeGenerator || (ContentTypeGenerator = {}));
|
|
316
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
317
|
+
0 && (module.exports = {
|
|
318
|
+
ContentTypeGenerator
|
|
319
|
+
});
|
|
320
|
+
//# sourceMappingURL=ContentTypeGenerator.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/generators/ContentTypeGenerator.ts"],
|
|
4
|
+
"sourcesContent": ["import { randomBytes } from 'crypto';\nimport mime from 'mime-types';\nimport { ValidUtils } from '@dooboostore/core';\nimport { Mimes } from '../codes/Mimes';\n\n/**\n * Content-Type \uBCC4 \uC2A4\uD2B8\uB9AC\uBC0D \uC751\uB2F5 \uBB36\uC74C.\n * \uD578\uB4E4\uB7EC\uB294 \uB0B4\uC6A9(payload)\uB9CC \uB118\uAE30\uACE0, \uD615\uC2DD(SSE `data:`\u00B7multipart boundary\u00B7CRLF\u00B7Content-Type\u00B7Content-Length\u00B7NDJSON \uC904\uBC14\uAFC8)\uC740 \uC5EC\uAE30\uC11C \uBD99\uC778\uB2E4.\n *\n * \uB3CC\uB824\uC8FC\uB294 Stream \uC740 \uADF8\uB300\uB85C async generator \uB77C\uC11C \uD578\uB4E4\uB7EC\uC5D0\uC11C return \uD558\uBA74 \uD504\uB808\uC784\uC6CC\uD06C\uAC00 yield \uB9C8\uB2E4 \uBC14\uB85C body \uB85C \uC3DC\uB2E4.\n * Stream \uC774 contentType(boundary \uD3EC\uD568)\u00B7compress \uB97C \uB4E4\uACE0 \uC788\uC5B4 \uB370\uCF54\uB808\uC774\uD130\uC5D0 contentType \uC744 \uB530\uB85C \uC801\uC9C0 \uC54A\uC544\uB3C4 \uB41C\uB2E4.\n *\n * pull \u2014 \uB0B4 \uBC18\uBCF5\uBB38\uC774 \uB9CC\uB4E4\uC5B4 yield \uD560 \uB54C:\n * return ContentTypeGenerator.multipartReplace(this.frames(file, signal));\n * push \u2014 \uCF5C\uBC31\u00B7\uC774\uBCA4\uD2B8\uB85C \uC62C \uB54C\uB9C8\uB2E4 \uB123\uC744 \uB54C:\n * const out = ContentTypeGenerator.multipartReplace.channel({ signal, keep: 'latest' });\n * watcher.on('change', buf => out.push(buf));\n * out.onClose(() => watcher.close());\n * return out;\n */\nexport namespace ContentTypeGenerator {\n export type Source<T> = Iterable<T> | AsyncIterable<T>;\n\n /** \uD504\uB808\uC784\uC6CC\uD06C\uAC00 \uBC1B\uB294 \uC2A4\uD2B8\uB9BC: async generator + \uC751\uB2F5 \uD5E4\uB354 \uC815\uBCF4 */\n export type Stream<T = string | Buffer> = AsyncGenerator<T, void, undefined> & {\n readonly contentType: string;\n /** false \uBA74 Accept-Encoding: gzip \uC774\uC5B4\uB3C4 \uC555\uCD95 \uC548 \uD568 (JPEG \uCC98\uB7FC \uC774\uBBF8 \uC555\uCD95\uB41C \uB0B4\uC6A9) */\n readonly compress: boolean;\n };\n\n /** push \uC6A9: Stream + \uB123\uAE30\u00B7\uB2EB\uAE30 */\n export type Channel<In, Out = string | Buffer> = Stream<Out> & {\n /** \uB123\uAE30. \uB2EB\uD614\uC73C\uBA74 false */\n push(value: In): boolean;\n /** \uB354 \uB123\uC744 \uAC8C \uC5C6\uC74C \u2014 \uC313\uC778 \uAC74 \uB9C8\uC800 \uBCF4\uB0B4\uACE0 \uB05D\uB0C4 */\n close(): void;\n readonly closed: boolean;\n /** \uB05D\uB0A0 \uB54C(\uD074\uB77C\uC774\uC5B8\uD2B8 \uB04A\uAE40\u00B7close\u00B7signal abort) \uD55C \uBC88 \u2014 \uAC10\uC2DC\uC790\u00B7\uB9AC\uC2A4\uB108 \uC815\uB9AC\uC6A9 */\n onClose(fn: () => void): void;\n };\n\n export type ChannelOption = {\n /** abort \uB418\uBA74 \uCC44\uB110\uC744 \uB2EB\uC74C */\n signal?: AbortSignal;\n /** 'queue': \uD558\uB098\uB3C4 \uC548 \uBC84\uB9AC\uACE0 \uC21C\uC11C\uB300\uB85C (\uC774\uBCA4\uD2B8) \u00B7 'latest': \uBC00\uB9AC\uBA74 \uCD5C\uC2E0 \uD558\uB098\uB9CC (\uC601\uC0C1 \uD504\uB808\uC784) */\n keep?: 'queue' | 'latest';\n /** 'queue' \uCD5C\uB300 \uAC1C\uC218 \u2014 \uB118\uCE58\uBA74 \uAC00\uC7A5 \uC624\uB798\uB41C \uAC83\uBD80\uD130 \uBC84\uB9BC (\uAE30\uBCF8 1000) */\n max?: number;\n };\n\n export const isStream = (value: any): value is Stream =>\n ValidUtils.isAsyncGenerator(value) && typeof (value as any).contentType === 'string';\n\n // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 \uD615\uC2DD \uADDC\uCE59 (\uC21C\uC218 \uD568\uC218) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n export type SseEvent =\n | string\n | { data?: unknown; event?: string; id?: string; retry?: number; comment?: string };\n\n const oneLine = (v: string) => v.replace(/[\\r\\n]/g, ' ');\n\n /**\n * SSE \uC774\uBCA4\uD2B8 \uD558\uB098 \u2192 `field: value\\n` \uC904\uB4E4 + \uBE48 \uC904(\\n).\n * data \uAC00 \uC5EC\uB7EC \uC904\uC774\uBA74 \uC904\uB9C8\uB2E4 `data: ` (\uADF8\uB0E5 \uB123\uC73C\uBA74 \uC904\uBC14\uAFC8\uC5D0\uC11C \uC774\uBCA4\uD2B8\uAC00 \uB04A\uAE40). \uBB38\uC790\uC5F4\uC774 \uC544\uB2C8\uBA74 JSON.\n */\n export const sseChunk = (e: SseEvent): string => {\n const ev = typeof e === 'string' ? { data: e } : e;\n let out = '';\n if (ev.comment !== undefined) out += ev.comment.split(/\\r\\n|\\r|\\n/).map(l => `: ${l}\\n`).join('');\n if (ev.event !== undefined) out += `event: ${oneLine(ev.event)}\\n`;\n if (ev.id !== undefined) out += `id: ${oneLine(ev.id)}\\n`;\n if (ev.retry !== undefined) out += `retry: ${Math.max(0, Math.floor(ev.retry))}\\n`;\n if (ev.data !== undefined) {\n const text = typeof ev.data === 'string' ? ev.data : JSON.stringify(ev.data);\n out += text.split(/\\r\\n|\\r|\\n/).map(l => `data: ${l}\\n`).join('');\n }\n return out + '\\n';\n };\n\n /** \uC5F0\uACB0 \uC720\uC9C0\uC6A9 SSE \uC8FC\uC11D (\uBE0C\uB77C\uC6B0\uC800 EventSource \uB294 \uBB34\uC2DC) */\n export const ssePing = (): string => ': ping\\n\\n';\n\n export type Part =\n | Buffer\n | Uint8Array\n | string\n | { body: Buffer | Uint8Array | string; type?: string; name?: string; headers?: Record<string, string> };\n\n /** \uBC14\uC774\uD2B8 \uC55E\uBD80\uBD84\uC73C\uB85C \uD754\uD55C \uC774\uBBF8\uC9C0 \uD310\uBCC4 (mime-types \uB294 \uC774\uB984\uC73C\uB85C\uB9CC \uCC3E\uC74C) \u2014 \uC774\uB984 \uC5C6\uC774 Buffer\uB9CC \uC628 \uD504\uB808\uC784\uC6A9 */\n const sniff = (b: Buffer): string | undefined => {\n if (b.length >= 3 && b[0] === 0xff && b[1] === 0xd8 && b[2] === 0xff) return Mimes.ImageJpeg;\n if (b.length >= 8 && b.readUInt32BE(0) === 0x89504e47 && b.readUInt32BE(4) === 0x0d0a1a0a) return Mimes.ImagePng;\n if (b.length >= 6 && b.toString('ascii', 0, 4) === 'GIF8') return Mimes.ImageGif;\n if (b.length >= 12 && b.toString('ascii', 0, 4) === 'RIFF' && b.toString('ascii', 8, 12) === 'WEBP') return 'image/webp';\n return undefined;\n };\n\n /**\n * \uD30C\uD2B8 MIME: type > mime-types(name) > \uBC14\uC774\uD2B8 \uD310\uBCC4 > \uBB38\uC790\uC5F4\uC774\uBA74 text/plain;utf-8 > octet-stream\n */\n export const partType = (part: Part, fallback?: string): string => {\n const p = isPartObject(part) ? part : { body: part };\n if (p.type) return p.type;\n if (p.name) {\n const byName = mime.lookup(p.name);\n if (byName) return mime.contentType(byName) || byName;\n }\n if (typeof p.body === 'string') return fallback ?? Mimes.TextPlainUtf8;\n return sniff(Buffer.from(p.body.buffer, p.body.byteOffset, p.body.byteLength)) ?? fallback ?? Mimes.ApplicationOctetStream;\n };\n\n const isPartObject = (part: Part): part is Exclude<Part, Buffer | Uint8Array | string> =>\n typeof part === 'object' && !(part instanceof Uint8Array);\n\n const toBuffer = (body: Buffer | Uint8Array | string): Buffer =>\n typeof body === 'string' ? Buffer.from(body, 'utf8') : Buffer.isBuffer(body) ? body : Buffer.from(body.buffer, body.byteOffset, body.byteLength);\n\n /**\n * multipart \uD30C\uD2B8 \uD558\uB098: `--boundary\\r\\nContent-Type: \u2026\\r\\nContent-Length: <\uBC14\uC774\uD2B8 \uC218>\\r\\n[\uD5E4\uB354\u2026]\\r\\n\\r\\n<body>\\r\\n`\n * Content-Length \uB294 \uBB38\uC790\uC5F4\uB3C4 \uAE00\uC790 \uC218\uAC00 \uC544\uB2C8\uB77C UTF-8 \uBC14\uC774\uD2B8 \uC218.\n */\n export const multipartPart = (part: Part, boundary: string, fallbackType?: string): Buffer => {\n const p = isPartObject(part) ? part : { body: part };\n const body = toBuffer(p.body);\n let head = `--${boundary}\\r\\nContent-Type: ${partType(part, fallbackType)}\\r\\nContent-Length: ${body.length}\\r\\n`;\n for (const [k, v] of Object.entries(p.headers ?? {})) head += `${oneLine(k)}: ${oneLine(v)}\\r\\n`;\n return Buffer.concat([Buffer.from(head + '\\r\\n', 'latin1'), body, Buffer.from('\\r\\n')]);\n };\n\n /** multipart \uB05D \uD45C\uC2DC (multipart/mixed \uCC98\uB7FC \uB05D\uC774 \uC788\uB294 \uACBD\uC6B0) */\n export const multipartEnd = (boundary: string): Buffer => Buffer.from(`--${boundary}--\\r\\n`);\n\n /** NDJSON \uD55C \uC904: JSON + \\n (\uADF8\uB0E5 \uAC1D\uCCB4\uB97C yield \uD558\uBA74 \uC904\uBC14\uAFC8 \uC5C6\uC774 \uBD99\uC5B4\uC11C \uBC1B\uB294 \uCABD\uC774 \uBABB \uB098\uB214) */\n export const ndjsonLine = (value: unknown): string => json(value) + '\\n';\n\n /** JSON.stringify(undefined) \uB294 undefined \uB77C \uADF8\uB300\uB85C \uBD99\uC774\uBA74 \uAE68\uC9C4 JSON \u2014 \uBC30\uC5F4 \uC548\uCC98\uB7FC null \uB85C */\n const json = (value: unknown): string => JSON.stringify(value) ?? 'null';\n\n /** RFC 7464 application/json-seq \uB808\uCF54\uB4DC: RS(0x1E) + JSON + \\n */\n export const jsonSeqRecord = (value: unknown): string => '\\x1e' + json(value) + '\\n';\n\n /** CSV \uCE78 \uD558\uB098 (RFC 4180): \uC27C\uD45C\u00B7\uB530\uC634\uD45C\u00B7\uC904\uBC14\uAFC8\u00B7\uC55E\uB4A4 \uACF5\uBC31\uC774 \uC788\uC73C\uBA74 \"\u2026\" \uB85C \uAC10\uC2F8\uACE0 \" \uB294 \"\" */\n export const csvCell = (value: unknown): string => {\n if (value === null || value === undefined) return '';\n const s = typeof value === 'string' ? value : value instanceof Date ? value.toISOString() : typeof value === 'object' ? json(value) : String(value);\n return /[\",\\r\\n]|^\\s|\\s$/.test(s) ? `\"${s.replace(/\"/g, '\"\"')}\"` : s;\n };\n\n /** CSV \uD55C \uC904: \uCE78\uB4E4 + CRLF */\n export const csvRow = (cells: readonly unknown[]): string => cells.map(csvCell).join(',') + '\\r\\n';\n\n /** text/plain \uD55C \uC904: \uBB38\uC790\uC5F4\uC740 \uADF8\uB300\uB85C, \uB098\uBA38\uC9C0\uB294 JSON + \\n */\n export const textLine = (value: unknown): string => (typeof value === 'string' ? value : json(value)) + '\\n';\n\n /** RFC 2046: 1~70\uC790, \uD5C8\uC6A9 \uBB38\uC790\uB9CC, \uACF5\uBC31\uC73C\uB85C \uB05D\uB098\uC9C0 \uC54A\uAC8C */\n export const boundary = (value?: string): string => {\n const b = value ?? `boundary-${randomBytes(12).toString('hex')}`;\n if (!/^[0-9A-Za-z'()+_,\\-./:=? ]{0,69}[0-9A-Za-z'()+_,\\-./:=?]$/.test(b)) {\n throw new Error(`invalid multipart boundary: ${JSON.stringify(b)}`);\n }\n return b;\n };\n\n // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 pull: \uC6D0\uBCF8 generator \uAC10\uC2F8\uAE30 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n const toStream = <T>(gen: AsyncGenerator<T, void, undefined>, contentType: string, compress: boolean): Stream<T> =>\n Object.assign(gen, { contentType, compress });\n\n async function* encode<In, Out>(src: Source<In>, fn: (v: In) => Out): AsyncGenerator<Out, void, undefined> {\n // \uBC14\uAE65\uC774 return \uB418\uBA74(\uD074\uB77C\uC774\uC5B8\uD2B8 \uB04A\uAE40) for await \uAC00 \uC6D0\uBCF8\uC5D0\uB3C4 return \uC744 \uBD88\uB7EC \uC6D0\uBCF8\uC758 finally \uAC00 \uB3C8\uB2E4\n for await (const v of src as AsyncIterable<In>) yield fn(v);\n }\n\n /** \uC55E(start)\u00B7\uB0B4\uC6A9\u00B7\uB4A4(end). \uC911\uAC04\uC5D0 \uB04A\uAE30\uBA74(return) end \uB294 \uC548 \uB098\uAC10 */\n async function* framed<In, Out>(src: Source<In>, fn: (v: In) => Out, start?: Out, end?: Out): AsyncGenerator<Out, void, undefined> {\n if (start !== undefined) yield start;\n yield* encode(src, fn);\n if (end !== undefined) yield end;\n }\n\n /** JSON \uBC30\uC5F4 \uD56D\uBAA9: \uCCAB \uD56D\uBAA9 \uB9D0\uACE0\uB294 \uC55E\uC5D0 \uC27C\uD45C (\uC2A4\uD2B8\uB9BC\uB9C8\uB2E4 \uD558\uB098\uC529 \uB9CC\uB4E4\uC5B4 \uC500) */\n const jsonArrayItem = () => {\n let n = 0;\n return (v: unknown) => (n++ ? ',' : '') + json(v);\n };\n\n export type CsvRow = readonly unknown[] | Record<string, unknown>;\n export type CsvOption = {\n /** \uD5E4\uB354 \uC904 + \uAC1D\uCCB4 \uD589\uC744 \uC774 \uC21C\uC11C\uB85C \uCE78\uC5D0 \uB9DE\uCDA4 (\uC5C6\uC73C\uBA74 \uAC1D\uCCB4\uB294 Object.values \uC21C\uC11C, \uD5E4\uB354 \uC5C6\uC74C) */\n columns?: string[];\n /** UTF-8 BOM \u2014 Excel \uC774 \uD55C\uAE00 CSV \uB97C \uAE68\uC9C0\uC9C0 \uC54A\uAC8C \uC5F4\uB824\uBA74 \uD544\uC694 */\n bom?: boolean;\n };\n const csvEncoder = (opt: CsvOption) => (r: CsvRow) =>\n csvRow(Array.isArray(r) ? r : opt.columns ? opt.columns.map(c => (r as Record<string, unknown>)[c]) : Object.values(r));\n const csvStart = (opt: CsvOption): string | undefined =>\n opt.bom || opt.columns ? (opt.bom ? '\uFEFF' : '') + (opt.columns ? csvRow(opt.columns) : '') : undefined;\n const CsvType = `${Mimes.TextCsv}; charset=utf-8`;\n\n /**\n * text/event-stream. heartbeatMs \uB97C \uC8FC\uBA74 \uADF8\uB3D9\uC548 \uC774\uBCA4\uD2B8\uAC00 \uC5C6\uC744 \uB54C `: ping` \uC744 \uB07C\uC6CC \uC5F0\uACB0 \uC720\uC9C0\n * (\uD504\uB85D\uC2DC\u00B7\uBE0C\uB77C\uC6B0\uC800\uB294 \uC624\uB798 \uC870\uC6A9\uD55C \uC5F0\uACB0\uC744 \uB04A\uB294\uB2E4).\n */\n export function sse(src: Source<SseEvent>, opt: { heartbeatMs?: number } = {}): Stream<string> {\n const contentType = Mimes.TextEventStream;\n if (!opt.heartbeatMs) return toStream(encode(src, sseChunk), contentType, true);\n const ms = opt.heartbeatMs;\n async function* withPing(): AsyncGenerator<string, void, undefined> {\n const it = (Symbol.asyncIterator in (src as any)\n ? (src as AsyncIterable<SseEvent>)[Symbol.asyncIterator]()\n : (src as Iterable<SseEvent>)[Symbol.iterator]()) as AsyncIterator<SseEvent> | Iterator<SseEvent>;\n let pending: Promise<IteratorResult<SseEvent>> | undefined;\n try {\n while (true) {\n pending ??= Promise.resolve(it.next());\n let timer: ReturnType<typeof setTimeout> | undefined;\n const tick = new Promise<'ping'>(r => (timer = setTimeout(() => r('ping'), ms)));\n const r = await Promise.race([pending, tick]);\n clearTimeout(timer);\n if (r === 'ping') {\n yield ssePing(); // \uC6D0\uBCF8 next \uB294 \uADF8\uB300\uB85C \uAE30\uB2E4\uB9BC (pending \uC720\uC9C0)\n continue;\n }\n pending = undefined;\n if (r.done) return;\n yield sseChunk(r.value);\n }\n } finally {\n await it.return?.();\n }\n }\n return toStream(withPing(), contentType, true);\n }\n\n /** multipart/x-mixed-replace (MJPEG \uB4F1 \u2014 \uD30C\uD2B8\uAC00 \uC62C \uB54C\uB9C8\uB2E4 \uC55E \uAC83\uC744 \uAC08\uC544\uB07C\uC6C0). \uC774\uBBF8 \uC555\uCD95\uB41C \uB0B4\uC6A9\uC774\uB77C gzip \uC548 \uD568 */\n export function multipartReplace(src: Source<Part>, opt: { boundary?: string; type?: string } = {}): Stream<Buffer> {\n const b = boundary(opt.boundary);\n return toStream(encode(src, p => multipartPart(p, b, opt.type)), `multipart/x-mixed-replace; boundary=${b}`, false);\n }\n\n /** multipart/mixed (\uB05D\uC774 \uC788\uB294 \uC5EC\uB7EC \uD30C\uD2B8). \uC6D0\uBCF8\uC774 \uB05D\uB098\uBA74 `--boundary--` */\n export function multipartMixed(src: Source<Part>, opt: { boundary?: string; type?: string } = {}): Stream<Buffer> {\n const b = boundary(opt.boundary);\n return toStream(framed(src, p => multipartPart(p, b, opt.type), undefined, multipartEnd(b)), `multipart/mixed; boundary=${b}`, true);\n }\n\n /** application/x-ndjson (\uD55C \uC904\uC5D0 JSON \uD558\uB098) */\n export function ndjson(src: Source<unknown>): Stream<string> {\n return toStream(encode(src, ndjsonLine), Mimes.ApplicationXndjson, true);\n }\n\n /** application/json-seq (RFC 7464, \uB808\uCF54\uB4DC\uB9C8\uB2E4 RS \uB85C \uC2DC\uC791) */\n export function jsonSeq(src: Source<unknown>): Stream<string> {\n return toStream(encode(src, jsonSeqRecord), Mimes.ApplicationJsonSeq, true);\n }\n\n /**\n * application/json \uBC30\uC5F4\uC744 \uD56D\uBAA9\uB9C8\uB2E4 \uD758\uB824\uBCF4\uB0C4: `[` \uD56D\uBAA9 `,` \uD56D\uBAA9 \u2026 `]`.\n * \uBC1B\uB294 \uCABD\uC740 \uB2E4 \uBC1B\uC740 \uB4A4 \uD3C9\uBC94\uD55C JSON \uC73C\uB85C \uD30C\uC2F1 (\uB05D\uAE4C\uC9C0 \uBC1B\uC544\uC57C \uC720\uD6A8\uD55C JSON \u2014 \uC911\uAC04\uC5D0 \uB04A\uAE30\uBA74 \uAE68\uC9C4 JSON)\n */\n export function jsonArray(src: Source<unknown>): Stream<string> {\n return toStream(framed(src, jsonArrayItem(), '[', ']'), Mimes.ApplicationJson, true);\n }\n\n /** text/csv; charset=utf-8 \u2014 \uD589\uC740 \uBC30\uC5F4 \uB610\uB294 \uAC1D\uCCB4(columns \uC21C\uC11C). \uC904\uBC14\uAFC8 CRLF */\n export function csv(src: Source<CsvRow>, opt: CsvOption = {}): Stream<string> {\n return toStream(framed(src, csvEncoder(opt), csvStart(opt)), CsvType, true);\n }\n\n /** text/plain; charset=utf-8 \u2014 \uD55C \uC904\uC5D0 \uD558\uB098 (\uB85C\uADF8 tail \uB4F1) */\n export function text(src: Source<unknown>): Stream<string> {\n return toStream(encode(src, textLine), Mimes.TextPlainUtf8, true);\n }\n\n // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 push: \uCC44\uB110 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /**\n * push \uD55C \uAC12\uC744 \uD050\uC5D0 \uB2F4\uC558\uB2E4\uAC00 yield \uD558\uB294 \uC2A4\uD2B8\uB9BC.\n * \uC9C1\uC811 iterator \uB97C \uB9CC\uB4DC\uB294 \uC774\uC720: \uAE30\uB2E4\uB9AC\uB294 \uC911(await)\uC5D0 \uD074\uB77C\uC774\uC5B8\uD2B8\uAC00 \uB04A\uACA8\uB3C4 return() \uC774 \uBC14\uB85C \uB05D\uB0B4\uACE0 onClose \uB97C \uBD80\uB974\uAC8C\n * (async generator \uB294 await \uC911\uC758 return \uC744 \uB2E4\uC74C yield \uAE4C\uC9C0 \uBBF8\uB904\uC11C, \uC870\uC6A9\uD55C \uCC44\uB110\uC774\uBA74 \uC815\uB9AC\uAC00 \uC548 \uB428).\n */\n export function channel<In, Out>(\n encodeFn: (v: In) => Out,\n contentType: string,\n compress: boolean,\n opt: ChannelOption & { start?: () => Out; end?: () => Out; heartbeat?: { ms: number; chunk: () => Out } } = {}\n ): Channel<In, Out> {\n const keep = opt.keep ?? 'queue';\n const max = Math.max(1, opt.max ?? 1000);\n const queue: In[] = [];\n const closers: (() => void)[] = [];\n let closed = false; // \uB354 \uC548 \uBC1B\uC74C (\uC313\uC778 \uAC74 \uBCF4\uB0C4)\n let finished = false; // \uC644\uC804\uD788 \uB05D\uB0A8\n let startSent = false;\n let endSent = false;\n let wake: (() => void) | undefined;\n\n const finish = () => {\n if (finished) return;\n finished = closed = true;\n queue.length = 0;\n opt.signal?.removeEventListener('abort', onAbort);\n wake?.();\n for (const fn of closers.splice(0)) {\n try { fn(); } catch { /* \uC815\uB9AC \uC2E4\uD328\uAC00 \uC751\uB2F5\uC744 \uAE68\uC9C0 \uC54A\uAC8C */ }\n }\n };\n const onAbort = () => finish();\n if (opt.signal?.aborted) closed = finished = true;\n else opt.signal?.addEventListener('abort', onAbort, { once: true });\n\n const next = async (): Promise<IteratorResult<Out, void>> => {\n while (!finished) {\n // \uC55E\uBD80\uBD84(JSON \uBC30\uC5F4 '[', CSV \uD5E4\uB354 \uB4F1)\uC740 \uCCAB next \uC5D0 \uBC14\uB85C \u2014 \uC544\uBB34\uAC83\uB3C4 push \uC548 \uD558\uACE0 close \uD574\uB3C4 \uB098\uAC10\n if (opt.start && !startSent) {\n startSent = true;\n return { value: opt.start(), done: false };\n }\n if (queue.length) return { value: encodeFn(queue.shift() as In), done: false };\n if (closed) {\n if (opt.end && !endSent) {\n endSent = true;\n return { value: opt.end(), done: false };\n }\n finish();\n break;\n }\n let timer: ReturnType<typeof setTimeout> | undefined;\n const ping = await new Promise<boolean>(resolve => {\n wake = () => resolve(false);\n if (opt.heartbeat) timer = setTimeout(() => resolve(true), opt.heartbeat.ms);\n });\n wake = undefined;\n clearTimeout(timer);\n if (ping && !finished && !queue.length && !closed) return { value: opt.heartbeat!.chunk(), done: false };\n }\n return { value: undefined, done: true };\n };\n\n const iterator: Channel<In, Out> = {\n contentType,\n compress,\n get closed() { return closed; },\n push(value: In) {\n if (closed) return false;\n if (keep === 'latest') queue.length = 0;\n else if (queue.length >= max) queue.shift();\n queue.push(value);\n wake?.();\n return true;\n },\n close() {\n closed = true;\n wake?.();\n },\n onClose(fn: () => void) {\n if (finished) fn();\n else closers.push(fn);\n },\n next,\n async return() {\n finish();\n return { value: undefined, done: true };\n },\n async throw(e?: unknown) {\n finish();\n throw e;\n },\n [Symbol.asyncIterator]() { return this; }\n } as unknown as Channel<In, Out>;\n // `await using` \uC815\uB9AC \uC9C0\uC6D0 (Symbol.asyncDispose \uAC00 \uC788\uB294 \uB7F0\uD0C0\uC784\uB9CC \u2014 AsyncGenerator \uD0C0\uC785\uC774 \uC694\uAD6C)\n const dispose = (Symbol as any).asyncDispose as symbol | undefined;\n if (dispose) (iterator as any)[dispose] = async () => finish();\n return iterator;\n }\n\n export namespace sse {\n /** push \uB85C SSE: out.push('hello') \u00B7 out.push({ event: 'tick', data: { n: 1 } }) */\n export const channel = (opt: ChannelOption & { heartbeatMs?: number } = {}): Channel<SseEvent, string> =>\n ContentTypeGenerator.channel(sseChunk, Mimes.TextEventStream, true, {\n ...opt,\n heartbeat: opt.heartbeatMs ? { ms: opt.heartbeatMs, chunk: ssePing } : undefined\n });\n }\n\n export namespace multipartReplace {\n /** push \uB85C MJPEG \uB4F1: out.push(jpegBuffer) \u2014 \uD504\uB808\uC784\uC740 \uBCF4\uD1B5 keep: 'latest' */\n export const channel = (opt: ChannelOption & { boundary?: string; type?: string } = {}): Channel<Part, Buffer> => {\n const b = boundary(opt.boundary);\n return ContentTypeGenerator.channel((p: Part) => multipartPart(p, b, opt.type), `multipart/x-mixed-replace; boundary=${b}`, false, opt);\n };\n }\n\n export namespace multipartMixed {\n export const channel = (opt: ChannelOption & { boundary?: string; type?: string } = {}): Channel<Part, Buffer> => {\n const b = boundary(opt.boundary);\n return ContentTypeGenerator.channel((p: Part) => multipartPart(p, b, opt.type), `multipart/mixed; boundary=${b}`, true, {\n ...opt,\n end: () => multipartEnd(b)\n });\n };\n }\n\n export namespace ndjson {\n export const channel = (opt: ChannelOption = {}): Channel<unknown, string> =>\n ContentTypeGenerator.channel(ndjsonLine, Mimes.ApplicationXndjson, true, opt);\n }\n\n export namespace jsonSeq {\n export const channel = (opt: ChannelOption = {}): Channel<unknown, string> =>\n ContentTypeGenerator.channel(jsonSeqRecord, Mimes.ApplicationJsonSeq, true, opt);\n }\n\n export namespace jsonArray {\n /** close \uD574\uC57C `]` \uAC00 \uB098\uAC00\uC11C \uC720\uD6A8\uD55C JSON \uC774 \uB428 */\n export const channel = (opt: ChannelOption = {}): Channel<unknown, string> =>\n ContentTypeGenerator.channel(jsonArrayItem(), Mimes.ApplicationJson, true, { ...opt, start: () => '[', end: () => ']' });\n }\n\n export namespace csv {\n export const channel = (opt: ChannelOption & CsvOption = {}): Channel<CsvRow, string> => {\n const start = csvStart(opt);\n return ContentTypeGenerator.channel(csvEncoder(opt), CsvType, true, { ...opt, start: start === undefined ? undefined : () => start });\n };\n }\n\n export namespace text {\n export const channel = (opt: ChannelOption = {}): Channel<unknown, string> =>\n ContentTypeGenerator.channel(textLine, Mimes.TextPlainUtf8, true, opt);\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["ContentTypeGenerator", "text", "mime", "boundary", "r", "sse", "multipartReplace", "multipartMixed", "ndjson", "jsonSeq", "jsonArray", "csv"]
|
|
7
|
+
}
|
package/dist/cjs/index.js
CHANGED
|
@@ -26,6 +26,7 @@ __reExport(src_exports, require("./decorators"), module.exports);
|
|
|
26
26
|
__reExport(src_exports, require("./endpoints"), module.exports);
|
|
27
27
|
__reExport(src_exports, require("./errors"), module.exports);
|
|
28
28
|
__reExport(src_exports, require("./filters"), module.exports);
|
|
29
|
+
__reExport(src_exports, require("./generators/ContentTypeGenerator"), module.exports);
|
|
29
30
|
__reExport(src_exports, require("./proxy"), module.exports);
|
|
30
31
|
__reExport(src_exports, require("./inject"), module.exports);
|
|
31
32
|
__reExport(src_exports, require("./lifecycle"), module.exports);
|
|
@@ -42,6 +43,7 @@ var src_default = {};
|
|
|
42
43
|
...require("./endpoints"),
|
|
43
44
|
...require("./errors"),
|
|
44
45
|
...require("./filters"),
|
|
46
|
+
...require("./generators/ContentTypeGenerator"),
|
|
45
47
|
...require("./proxy"),
|
|
46
48
|
...require("./inject"),
|
|
47
49
|
...require("./lifecycle"),
|
package/dist/cjs/index.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/index.ts"],
|
|
4
|
-
"sourcesContent": ["export * from './codes';\nexport * from './decorators';\nexport * from './endpoints';\nexport * from './errors';\nexport * from './filters';\nexport * from './proxy';\nexport * from './inject';\nexport * from './lifecycle';\nexport * from './models';\nexport * from './option';\nexport * from './resolvers';\nexport * from './session';\nexport * from './SimpleBootHttpServer';\nexport default {}"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,wBAAc,oBAAd;AACA,wBAAc,yBADd;AAEA,wBAAc,wBAFd;AAGA,wBAAc,qBAHd;AAIA,wBAAc,sBAJd;AAKA,wBAAc,
|
|
4
|
+
"sourcesContent": ["export * from './codes';\nexport * from './decorators';\nexport * from './endpoints';\nexport * from './errors';\nexport * from './filters';\nexport * from './generators/ContentTypeGenerator';\nexport * from './proxy';\nexport * from './inject';\nexport * from './lifecycle';\nexport * from './models';\nexport * from './option';\nexport * from './resolvers';\nexport * from './session';\nexport * from './SimpleBootHttpServer';\nexport default {}"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,wBAAc,oBAAd;AACA,wBAAc,yBADd;AAEA,wBAAc,wBAFd;AAGA,wBAAc,qBAHd;AAIA,wBAAc,sBAJd;AAKA,wBAAc,8CALd;AAMA,wBAAc,oBANd;AAOA,wBAAc,qBAPd;AAQA,wBAAc,wBARd;AASA,wBAAc,qBATd;AAUA,wBAAc,qBAVd;AAWA,wBAAc,wBAXd;AAYA,wBAAc,sBAZd;AAaA,wBAAc,mCAbd;AAcA,IAAO,cAAQ,CAAC;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -477,11 +477,12 @@ class RequestResponse {
|
|
|
477
477
|
* - 문자열/Buffer 는 그대로, 그 외는 JSON.stringify
|
|
478
478
|
* - Accept-Encoding 에 gzip 이 있으면 gzip 스트림으로 압축하고, 청크마다 Z_SYNC_FLUSH 로 바로 내보낸다
|
|
479
479
|
* (flush 없이 쓰면 zlib 내부 버퍼에 모였다가 늦게 나가서 SSE 실시간성이 깨짐)
|
|
480
|
+
* option.compress === false 면 압축 안 함 (MJPEG 처럼 이미 압축된 내용 — CPU만 쓰고, gzip 못 푸는 클라이언트도 있음)
|
|
480
481
|
* - 클라이언트가 끊으면 source.return() 으로 제너레이터를 종료시켜 finally 가 돌게 한다
|
|
481
482
|
*/
|
|
482
|
-
async resStream(source) {
|
|
483
|
+
async resStream(source, option = {}) {
|
|
483
484
|
const res = this.res;
|
|
484
|
-
const gz = this.reqHeaderFirst(import_HttpHeaders.HttpHeaders.AcceptEncoding)?.includes("gzip") ? (0, import_zlib.createGzip)() : void 0;
|
|
485
|
+
const gz = option.compress !== false && this.reqHeaderFirst(import_HttpHeaders.HttpHeaders.AcceptEncoding)?.includes("gzip") ? (0, import_zlib.createGzip)() : void 0;
|
|
485
486
|
if (gz) {
|
|
486
487
|
res.setHeader(import_HttpHeaders.HttpHeaders.ContentEncoding, "gzip");
|
|
487
488
|
res.removeHeader(import_HttpHeaders.HttpHeaders.ContentLength);
|