marcattacks 1.0.0 → 1.0.1

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.
Files changed (86) hide show
  1. package/dist/httpstream.d.ts +3 -0
  2. package/dist/httpstream.d.ts.map +1 -0
  3. package/{src/httpstream.ts → dist/httpstream.js} +3 -7
  4. package/dist/httpstream.js.map +1 -0
  5. package/dist/index.d.ts +3 -0
  6. package/dist/index.d.ts.map +1 -0
  7. package/dist/index.js.map +1 -0
  8. package/dist/input/alephseq.d.ts +3 -0
  9. package/dist/input/alephseq.d.ts.map +1 -0
  10. package/{src/input/alephseq.ts → dist/input/alephseq.js} +20 -34
  11. package/dist/input/alephseq.js.map +1 -0
  12. package/dist/input/json.d.ts +3 -0
  13. package/dist/input/json.d.ts.map +1 -0
  14. package/{src/input/json.ts → dist/input/json.js} +8 -18
  15. package/dist/input/json.js.map +1 -0
  16. package/dist/input/jsonl.d.ts +3 -0
  17. package/dist/input/jsonl.d.ts.map +1 -0
  18. package/{src/input/jsonl.ts → dist/input/jsonl.js} +11 -21
  19. package/dist/input/jsonl.js.map +1 -0
  20. package/dist/input/xml.d.ts +3 -0
  21. package/dist/input/xml.d.ts.map +1 -0
  22. package/{src/input/xml.ts → dist/input/xml.js} +29 -50
  23. package/dist/input/xml.js.map +1 -0
  24. package/dist/marcmap.d.ts +31 -0
  25. package/dist/marcmap.d.ts.map +1 -0
  26. package/{src/marcmap.ts → dist/marcmap.js} +25 -38
  27. package/dist/marcmap.js.map +1 -0
  28. package/dist/output/alephseq.d.ts +3 -0
  29. package/dist/output/alephseq.d.ts.map +1 -0
  30. package/{src/output/alephseq.ts → dist/output/alephseq.js} +17 -25
  31. package/dist/output/alephseq.js.map +1 -0
  32. package/dist/output/json.d.ts +3 -0
  33. package/dist/output/json.d.ts.map +1 -0
  34. package/{src/output/json.ts → dist/output/json.js} +4 -13
  35. package/dist/output/json.js.map +1 -0
  36. package/dist/output/jsonl.d.ts +3 -0
  37. package/dist/output/jsonl.d.ts.map +1 -0
  38. package/{src/output/jsonl.ts → dist/output/jsonl.js} +5 -8
  39. package/dist/output/jsonl.js.map +1 -0
  40. package/dist/output/rdf.d.ts +3 -0
  41. package/dist/output/rdf.d.ts.map +1 -0
  42. package/dist/output/rdf.js +44 -0
  43. package/dist/output/rdf.js.map +1 -0
  44. package/dist/output/xml.d.ts +6 -0
  45. package/dist/output/xml.d.ts.map +1 -0
  46. package/{src/output/xml.ts → dist/output/xml.js} +18 -35
  47. package/dist/output/xml.js.map +1 -0
  48. package/dist/plugin-loader.d.ts +2 -0
  49. package/dist/plugin-loader.d.ts.map +1 -0
  50. package/dist/plugin-loader.js +24 -0
  51. package/dist/plugin-loader.js.map +1 -0
  52. package/dist/s3stream.d.ts +8 -0
  53. package/dist/s3stream.d.ts.map +1 -0
  54. package/{src/s3stream.ts → dist/s3stream.js} +72 -115
  55. package/dist/s3stream.js.map +1 -0
  56. package/dist/sftpstream.d.ts +12 -0
  57. package/dist/sftpstream.d.ts.map +1 -0
  58. package/{src/sftpstream.ts → dist/sftpstream.js} +9 -39
  59. package/dist/sftpstream.js.map +1 -0
  60. package/dist/slow-writable.d.ts +38 -0
  61. package/dist/slow-writable.d.ts.map +1 -0
  62. package/dist/slow-writable.js +126 -0
  63. package/dist/slow-writable.js.map +1 -0
  64. package/dist/transform/json.d.ts +3 -0
  65. package/dist/transform/json.d.ts.map +1 -0
  66. package/{src/transform/json.ts → dist/transform/json.js} +8 -12
  67. package/dist/transform/json.js.map +1 -0
  68. package/dist/transform/rdf.d.ts +3 -0
  69. package/dist/transform/rdf.d.ts.map +1 -0
  70. package/{src/transform/rdf.ts → dist/transform/rdf.js} +82 -110
  71. package/dist/transform/rdf.js.map +1 -0
  72. package/package.json +6 -2
  73. package/Dockerfile +0 -23
  74. package/README-docker.md +0 -39
  75. package/TYPESCRIPT.txt +0 -6
  76. package/data/output.rdf +0 -12425
  77. package/data/sample.xml +0 -2
  78. package/demo/demo.jsonata +0 -44
  79. package/docker-compose.yaml +0 -37
  80. package/logo.jpg +0 -0
  81. package/plugin/demo.js +0 -12
  82. package/src/index.ts +0 -177
  83. package/src/output/rdf.ts +0 -63
  84. package/src/plugin-loader.ts +0 -27
  85. package/src/slow-writable.ts +0 -165
  86. package/tsconfig.json +0 -46
@@ -1,147 +1,119 @@
1
- import {
2
- S3Client,
3
- GetObjectCommand,
4
- PutObjectCommand,
5
- UploadPartCommand,
6
- CreateMultipartUploadCommand,
7
- CompleteMultipartUploadCommand,
8
- type S3ClientConfig
9
- } from "@aws-sdk/client-s3";
1
+ import { S3Client, GetObjectCommand, PutObjectCommand, UploadPartCommand, CreateMultipartUploadCommand, CompleteMultipartUploadCommand } from "@aws-sdk/client-s3";
10
2
  import { Readable, Writable } from "stream";
11
3
  import log4js from 'log4js';
12
-
13
4
  const logger = log4js.getLogger();
14
- type S3Config = {
15
- region: string;
16
- endpoint: string;
17
- bucket: string;
18
- key: string;
19
- accessKeyId?: string;
20
- secretAccessKey?: string;
21
- };
22
-
23
- export async function s3ReaderStream(url: URL, options: { range?: string }): Promise<Readable> {
5
+ export async function s3ReaderStream(url, options) {
24
6
  const config = parseURL(url);
25
-
26
- logger.debug(`s3 config:`,config);
27
-
7
+ logger.debug(`s3 config:`, config);
28
8
  const bucket = config.bucket;
29
- const key = config.key;
30
- const range = options.range;
9
+ const key = config.key;
10
+ const range = options.range;
31
11
  const s3 = makeClient(config);
32
-
33
12
  const res = await s3.send(new GetObjectCommand({
34
13
  Bucket: bucket,
35
14
  Key: key,
36
15
  Range: range,
37
16
  }));
38
-
39
17
  const body = res.Body;
40
-
41
18
  if (!body) {
42
19
  throw new Error("S3 GetObject returned an empty body");
43
20
  }
44
-
45
21
  // 1) If SDK returned a Node.js readable stream (typical in Node)
46
22
  if (isNodeReadable(body)) {
47
- return body as Readable;
23
+ return body;
48
24
  }
49
-
50
25
  // 2) If SDK returned a WHATWG ReadableStream (browser-ish or newer runtimes)
51
26
  if (isReadableStream(body)) {
52
27
  // Node.js v17+ has Readable.fromWeb
53
28
  // Fallback: wrap async iterator
54
- if (typeof (Readable as any).fromWeb === "function") {
55
- return (Readable as any).fromWeb(body as ReadableStream<Uint8Array>);
56
- } else {
57
- // Convert using async iterator produced by the stream
58
- const reader = (body as ReadableStream<Uint8Array>).getReader();
59
- const nodeStream = new Readable({
60
- read() {
61
- // no-op. We'll push from async loop below
62
- }
63
- });
64
- (async () => {
65
- try {
66
- while (true) {
67
- const { done, value } = await reader.read();
68
- if (done) break;
69
- nodeStream.push(Buffer.from(value));
70
- }
71
- nodeStream.push(null);
72
- } catch (err) {
73
- nodeStream.destroy(err as Error);
74
- }
75
- })();
76
- return nodeStream;
29
+ if (typeof Readable.fromWeb === "function") {
30
+ return Readable.fromWeb(body);
31
+ }
32
+ else {
33
+ // Convert using async iterator produced by the stream
34
+ const reader = body.getReader();
35
+ const nodeStream = new Readable({
36
+ read() {
37
+ // no-op. We'll push from async loop below
38
+ }
39
+ });
40
+ (async () => {
41
+ try {
42
+ while (true) {
43
+ const { done, value } = await reader.read();
44
+ if (done)
45
+ break;
46
+ nodeStream.push(Buffer.from(value));
47
+ }
48
+ nodeStream.push(null);
49
+ }
50
+ catch (err) {
51
+ nodeStream.destroy(err);
52
+ }
53
+ })();
54
+ return nodeStream;
77
55
  }
78
56
  }
79
-
80
57
  // 3) If SDK returned a Blob (browsers)
81
58
  if (typeof Blob !== "undefined" && body instanceof Blob) {
82
- const stream = (body as Blob).stream();
83
- if (typeof (Readable as any).fromWeb === "function") {
84
- return (Readable as any).fromWeb(stream);
59
+ const stream = body.stream();
60
+ if (typeof Readable.fromWeb === "function") {
61
+ return Readable.fromWeb(stream);
85
62
  }
86
63
  // fallback same as above
87
64
  const reader = stream.getReader();
88
65
  const nodeStream = new Readable({
89
- read() {}
66
+ read() { }
90
67
  });
91
68
  (async () => {
92
- try {
93
- while (true) {
94
- const { done, value } = await reader.read();
95
- if (done) break;
96
- nodeStream.push(Buffer.from(value));
69
+ try {
70
+ while (true) {
71
+ const { done, value } = await reader.read();
72
+ if (done)
73
+ break;
74
+ nodeStream.push(Buffer.from(value));
75
+ }
76
+ nodeStream.push(null);
77
+ }
78
+ catch (err) {
79
+ nodeStream.destroy(err);
97
80
  }
98
- nodeStream.push(null);
99
- } catch (err) {
100
- nodeStream.destroy(err as Error);
101
- }
102
81
  })();
103
82
  return nodeStream;
104
83
  }
105
-
106
84
  // 4) If it's an async iterable (some runtimes)
107
85
  if (isAsyncIterable(body)) {
108
- return Readable.from(body as AsyncIterable<Uint8Array | string | Buffer>);
86
+ return Readable.from(body);
109
87
  }
110
-
111
88
  // Unknown body shape
112
89
  throw new Error("Unsupported S3 GetObject body type");
113
90
  }
114
-
115
- export function s3WriterStream(url: URL, options: { partSize?: number;}) : Promise<Writable> {
116
- return new Promise<Writable>( (resolve) => {
91
+ export function s3WriterStream(url, options) {
92
+ return new Promise((resolve) => {
117
93
  const config = parseURL(url);
118
-
119
94
  logger.debug(`s3 config:`, config);
120
95
  const bucket = config.bucket;
121
96
  const key = config.key;
122
97
  const s3 = makeClient(config);
123
98
  const partSize = options.partSize ?? 5 * 1024 * 1024;
124
-
125
- let uploadId: string | null = null;
126
- let parts: Array<{ ETag: string | undefined; PartNumber: number }> = [];
99
+ let uploadId = null;
100
+ let parts = [];
127
101
  let buffer = Buffer.alloc(0);
128
102
  let partNumber = 1;
129
-
130
103
  const writer = new Writable({
131
104
  async write(chunk, _encoding, callback) {
132
105
  logger.debug("write chunk...");
133
106
  try {
134
107
  buffer = Buffer.concat([buffer, chunk]);
135
-
136
108
  if (buffer.length >= partSize) {
137
109
  await flushPart();
138
110
  }
139
111
  callback();
140
- } catch (err) {
141
- callback(err as Error);
112
+ }
113
+ catch (err) {
114
+ callback(err);
142
115
  }
143
116
  },
144
-
145
117
  async final(callback) {
146
118
  logger.debug("final...");
147
119
  try {
@@ -150,42 +122,38 @@ export function s3WriterStream(url: URL, options: { partSize?: number;}) : Promi
150
122
  logger.debug("finishUpload...");
151
123
  await finishUpload();
152
124
  callback();
153
- } catch (err) {
154
- callback(err as Error);
125
+ }
126
+ catch (err) {
127
+ callback(err);
155
128
  }
156
129
  }
157
130
  });
158
-
159
131
  async function ensureUpload() {
160
132
  if (!uploadId) {
161
133
  const res = await s3.send(new CreateMultipartUploadCommand({
162
134
  Bucket: bucket,
163
135
  Key: key
164
136
  }));
165
- uploadId = res.UploadId!;
137
+ uploadId = res.UploadId;
166
138
  }
167
139
  }
168
-
169
140
  async function flushPart(isLast = false) {
170
- if (buffer.length === 0 && !isLast) return;
171
-
141
+ if (buffer.length === 0 && !isLast)
142
+ return;
172
143
  logger.debug("ensureUpload...");
173
144
  await ensureUpload();
174
-
175
145
  logger.debug("s3.send...");
176
146
  const res = await s3.send(new UploadPartCommand({
177
147
  Bucket: bucket,
178
148
  Key: key,
179
149
  PartNumber: partNumber,
180
- UploadId: uploadId!,
150
+ UploadId: uploadId,
181
151
  Body: buffer
182
152
  }));
183
-
184
153
  parts.push({ ETag: res.ETag, PartNumber: partNumber });
185
154
  buffer = Buffer.alloc(0);
186
155
  partNumber++;
187
156
  }
188
-
189
157
  async function finishUpload() {
190
158
  if (!uploadId) {
191
159
  // No parts written, upload empty object
@@ -196,39 +164,32 @@ export function s3WriterStream(url: URL, options: { partSize?: number;}) : Promi
196
164
  }));
197
165
  return;
198
166
  }
199
-
200
167
  await s3.send(new CompleteMultipartUploadCommand({
201
168
  Bucket: bucket,
202
169
  Key: key,
203
- UploadId: uploadId!,
170
+ UploadId: uploadId,
204
171
  MultipartUpload: { Parts: parts }
205
172
  }));
206
173
  }
207
-
208
174
  resolve(writer);
209
175
  });
210
176
  }
211
-
212
- function isNodeReadable(x: any): x is Readable {
177
+ function isNodeReadable(x) {
213
178
  return x && typeof x.pipe === "function" && typeof x.read === "function";
214
179
  }
215
-
216
- function isReadableStream(x: any): x is ReadableStream {
180
+ function isReadableStream(x) {
217
181
  return typeof x?.getReader === "function";
218
182
  }
219
-
220
- function isAsyncIterable(x: any): x is AsyncIterable<any> {
183
+ function isAsyncIterable(x) {
221
184
  return x && typeof x[Symbol.asyncIterator] === "function";
222
185
  }
223
-
224
- function makeClient(config: S3Config) : S3Client {
186
+ function makeClient(config) {
225
187
  logger.debug(config);
226
- const myConfig : S3ClientConfig = {
188
+ const myConfig = {
227
189
  endpoint: config.endpoint,
228
190
  forcePathStyle: true,
229
191
  region: config.region,
230
192
  };
231
-
232
193
  if (config.accessKeyId && config.secretAccessKey) {
233
194
  myConfig.credentials = {
234
195
  accessKeyId: config.accessKeyId,
@@ -237,15 +198,13 @@ function makeClient(config: S3Config) : S3Client {
237
198
  }
238
199
  return new S3Client(myConfig);
239
200
  }
240
-
241
- function parseURL(url: URL) : S3Config {
242
- const config : S3Config = {
201
+ function parseURL(url) {
202
+ const config = {
243
203
  region: "us-east-1",
244
204
  endpoint: "http://localhost:3371",
245
205
  bucket: "bbl",
246
206
  key: "test.txt"
247
207
  };
248
-
249
208
  const scheme = url.protocol.startsWith("s3s") ? "https" : "http";
250
209
  config.endpoint = `${scheme}://${url.hostname}`;
251
210
  if (url.port) {
@@ -253,14 +212,12 @@ function parseURL(url: URL) : S3Config {
253
212
  }
254
213
  config.bucket = url.pathname.split("/")[1] ?? "";
255
214
  config.key = url.pathname.split("/").splice(2).join("/");
256
-
257
215
  if (url.username) {
258
216
  config.accessKeyId = url.username;
259
217
  }
260
-
261
218
  if (url.password) {
262
219
  config.secretAccessKey = url.password;
263
220
  }
264
-
265
221
  return config;
266
- }
222
+ }
223
+ //# sourceMappingURL=s3stream.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"s3stream.js","sourceRoot":"","sources":["../src/s3stream.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,QAAQ,EACR,gBAAgB,EAChB,gBAAgB,EAChB,iBAAiB,EACjB,4BAA4B,EAC5B,8BAA8B,EAEjC,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAC5C,OAAO,MAAM,MAAM,QAAQ,CAAC;AAE5B,MAAM,MAAM,GAAG,MAAM,CAAC,SAAS,EAAE,CAAC;AAUlC,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,GAAQ,EAAE,OAA2B;IACtE,MAAM,MAAM,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC;IAE7B,MAAM,CAAC,KAAK,CAAC,YAAY,EAAC,MAAM,CAAC,CAAC;IAElC,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;IAC7B,MAAM,GAAG,GAAM,MAAM,CAAC,GAAG,CAAC;IAC1B,MAAM,KAAK,GAAI,OAAO,CAAC,KAAK,CAAC;IAC7B,MAAM,EAAE,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC;IAE9B,MAAM,GAAG,GAAG,MAAM,EAAE,CAAC,IAAI,CAAC,IAAI,gBAAgB,CAAC;QAC3C,MAAM,EAAE,MAAM;QACd,GAAG,EAAE,GAAG;QACR,KAAK,EAAE,KAAK;KACf,CAAC,CAAC,CAAC;IAEJ,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,CAAC;IAEtB,IAAI,CAAC,IAAI,EAAE,CAAC;QACR,MAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAC;IAC3D,CAAC;IAED,iEAAiE;IACjE,IAAI,cAAc,CAAC,IAAI,CAAC,EAAE,CAAC;QACvB,OAAO,IAAgB,CAAC;IAC5B,CAAC;IAED,6EAA6E;IAC7E,IAAI,gBAAgB,CAAC,IAAI,CAAC,EAAE,CAAC;QACzB,oCAAoC;QACpC,gCAAgC;QAChC,IAAI,OAAQ,QAAgB,CAAC,OAAO,KAAK,UAAU,EAAE,CAAC;YACtD,OAAQ,QAAgB,CAAC,OAAO,CAAC,IAAkC,CAAC,CAAC;QACrE,CAAC;aAAM,CAAC;YACR,sDAAsD;YACtD,MAAM,MAAM,GAAI,IAAmC,CAAC,SAAS,EAAE,CAAC;YAChE,MAAM,UAAU,GAAG,IAAI,QAAQ,CAAC;gBAC5B,IAAI;oBACA,0CAA0C;gBAC9C,CAAC;aACJ,CAAC,CAAC;YACH,CAAC,KAAK,IAAI,EAAE;gBACR,IAAI,CAAC;oBACL,OAAO,IAAI,EAAE,CAAC;wBACV,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;wBAC5C,IAAI,IAAI;4BAAE,MAAM;wBAChB,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;oBACxC,CAAC;oBACD,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBACtB,CAAC;gBAAC,OAAO,GAAG,EAAE,CAAC;oBACf,UAAU,CAAC,OAAO,CAAC,GAAY,CAAC,CAAC;gBACjC,CAAC;YACL,CAAC,CAAC,EAAE,CAAC;YACL,OAAO,UAAU,CAAC;QAClB,CAAC;IACL,CAAC;IAED,uCAAuC;IACvC,IAAI,OAAO,IAAI,KAAK,WAAW,IAAI,IAAI,YAAY,IAAI,EAAE,CAAC;QACtD,MAAM,MAAM,GAAI,IAAa,CAAC,MAAM,EAAE,CAAC;QACvC,IAAI,OAAQ,QAAgB,CAAC,OAAO,KAAK,UAAU,EAAE,CAAC;YACtD,OAAQ,QAAgB,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QACzC,CAAC;QACD,yBAAyB;QACzB,MAAM,MAAM,GAAG,MAAM,CAAC,SAAS,EAAE,CAAC;QAClC,MAAM,UAAU,GAAG,IAAI,QAAQ,CAAC;YAChC,IAAI,KAAI,CAAC;SACR,CAAC,CAAC;QACH,CAAC,KAAK,IAAI,EAAE;YACZ,IAAI,CAAC;gBACD,OAAO,IAAI,EAAE,CAAC;oBACd,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;oBAC5C,IAAI,IAAI;wBAAE,MAAM;oBAChB,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;gBACpC,CAAC;gBACD,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC1B,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACX,UAAU,CAAC,OAAO,CAAC,GAAY,CAAC,CAAC;YACrC,CAAC;QACD,CAAC,CAAC,EAAE,CAAC;QACL,OAAO,UAAU,CAAC;IACtB,CAAC;IAED,+CAA+C;IAC/C,IAAI,eAAe,CAAC,IAAI,CAAC,EAAE,CAAC;QACxB,OAAO,QAAQ,CAAC,IAAI,CAAC,IAAmD,CAAC,CAAC;IAC9E,CAAC;IAED,qBAAqB;IACrB,MAAM,IAAI,KAAK,CAAC,oCAAoC,CAAC,CAAC;AAC1D,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,GAAQ,EAAE,OAA8B;IACnE,OAAO,IAAI,OAAO,CAAY,CAAC,OAAO,EAAE,EAAE;QACtC,MAAM,MAAM,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC;QAE7B,MAAM,CAAC,KAAK,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC;QACnC,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;QAC7B,MAAM,GAAG,GAAG,MAAM,CAAC,GAAG,CAAC;QACvB,MAAM,EAAE,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC;QAC9B,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,CAAC,GAAG,IAAI,GAAG,IAAI,CAAC;QAErD,IAAI,QAAQ,GAAkB,IAAI,CAAC;QACnC,IAAI,KAAK,GAA4D,EAAE,CAAC;QACxE,IAAI,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QAC7B,IAAI,UAAU,GAAG,CAAC,CAAC;QAEnB,MAAM,MAAM,GAAG,IAAI,QAAQ,CAAC;YACxB,KAAK,CAAC,KAAK,CAAC,KAAK,EAAE,SAAS,EAAE,QAAQ;gBAClC,MAAM,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC;gBAC/B,IAAI,CAAC;oBACD,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC;oBAExC,IAAI,MAAM,CAAC,MAAM,IAAI,QAAQ,EAAE,CAAC;wBAC5B,MAAM,SAAS,EAAE,CAAC;oBACtB,CAAC;oBACD,QAAQ,EAAE,CAAC;gBACf,CAAC;gBAAC,OAAO,GAAG,EAAE,CAAC;oBACX,QAAQ,CAAC,GAAY,CAAC,CAAC;gBAC3B,CAAC;YACL,CAAC;YAED,KAAK,CAAC,KAAK,CAAC,QAAQ;gBAChB,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;gBACzB,IAAI,CAAC;oBACD,MAAM,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC;oBAC7B,MAAM,SAAS,CAAC,IAAI,CAAC,CAAC;oBACtB,MAAM,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC;oBAChC,MAAM,YAAY,EAAE,CAAC;oBACrB,QAAQ,EAAE,CAAC;gBACf,CAAC;gBAAC,OAAO,GAAG,EAAE,CAAC;oBACX,QAAQ,CAAC,GAAY,CAAC,CAAC;gBAC3B,CAAC;YACL,CAAC;SACJ,CAAC,CAAC;QAEH,KAAK,UAAU,YAAY;YACvB,IAAI,CAAC,QAAQ,EAAE,CAAC;gBACZ,MAAM,GAAG,GAAG,MAAM,EAAE,CAAC,IAAI,CAAC,IAAI,4BAA4B,CAAC;oBACvD,MAAM,EAAE,MAAM;oBACd,GAAG,EAAE,GAAG;iBACX,CAAC,CAAC,CAAC;gBACJ,QAAQ,GAAG,GAAG,CAAC,QAAS,CAAC;YAC7B,CAAC;QACL,CAAC;QAED,KAAK,UAAU,SAAS,CAAC,MAAM,GAAG,KAAK;YACnC,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,MAAM;gBAAE,OAAO;YAE3C,MAAM,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC;YAChC,MAAM,YAAY,EAAE,CAAC;YAErB,MAAM,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;YAC3B,MAAM,GAAG,GAAG,MAAM,EAAE,CAAC,IAAI,CAAC,IAAI,iBAAiB,CAAC;gBAC5C,MAAM,EAAE,MAAM;gBACd,GAAG,EAAE,GAAG;gBACR,UAAU,EAAE,UAAU;gBACtB,QAAQ,EAAE,QAAS;gBACnB,IAAI,EAAE,MAAM;aACf,CAAC,CAAC,CAAC;YAEJ,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,UAAU,EAAE,UAAU,EAAE,CAAC,CAAC;YACvD,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;YACzB,UAAU,EAAE,CAAC;QACjB,CAAC;QAED,KAAK,UAAU,YAAY;YACvB,IAAI,CAAC,QAAQ,EAAE,CAAC;gBACZ,wCAAwC;gBACxC,MAAM,EAAE,CAAC,IAAI,CAAC,IAAI,gBAAgB,CAAC;oBAC/B,MAAM,EAAE,MAAM;oBACd,GAAG,EAAE,GAAG;oBACR,IAAI,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;iBACxB,CAAC,CAAC,CAAC;gBACJ,OAAO;YACX,CAAC;YAED,MAAM,EAAE,CAAC,IAAI,CAAC,IAAI,8BAA8B,CAAC;gBAC7C,MAAM,EAAE,MAAM;gBACd,GAAG,EAAE,GAAG;gBACR,QAAQ,EAAE,QAAS;gBACnB,eAAe,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE;aACpC,CAAC,CAAC,CAAC;QACR,CAAC;QAED,OAAO,CAAC,MAAM,CAAC,CAAC;IACpB,CAAC,CAAC,CAAC;AACP,CAAC;AAED,SAAS,cAAc,CAAC,CAAM;IAC1B,OAAO,CAAC,IAAI,OAAO,CAAC,CAAC,IAAI,KAAK,UAAU,IAAI,OAAO,CAAC,CAAC,IAAI,KAAK,UAAU,CAAC;AAC7E,CAAC;AAED,SAAS,gBAAgB,CAAC,CAAM;IAC5B,OAAO,OAAO,CAAC,EAAE,SAAS,KAAK,UAAU,CAAC;AAC9C,CAAC;AAED,SAAS,eAAe,CAAC,CAAM;IAC3B,OAAO,CAAC,IAAI,OAAO,CAAC,CAAC,MAAM,CAAC,aAAa,CAAC,KAAK,UAAU,CAAC;AAC9D,CAAC;AAED,SAAS,UAAU,CAAC,MAAgB;IAChC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IACrB,MAAM,QAAQ,GAAoB;QAC9B,QAAQ,EAAE,MAAM,CAAC,QAAQ;QACzB,cAAc,EAAE,IAAI;QACpB,MAAM,EAAE,MAAM,CAAC,MAAM;KACxB,CAAC;IAEF,IAAI,MAAM,CAAC,WAAW,IAAI,MAAM,CAAC,eAAe,EAAE,CAAC;QAC/C,QAAQ,CAAC,WAAW,GAAG;YACnB,WAAW,EAAE,MAAM,CAAC,WAAW;YAC/B,eAAe,EAAE,MAAM,CAAC,eAAe;SAC1C,CAAC;IACN,CAAC;IACD,OAAO,IAAI,QAAQ,CAAC,QAAQ,CAAC,CAAC;AAClC,CAAC;AAED,SAAS,QAAQ,CAAC,GAAQ;IACtB,MAAM,MAAM,GAAc;QACtB,MAAM,EAAE,WAAW;QACnB,QAAQ,EAAE,uBAAuB;QACjC,MAAM,EAAE,KAAK;QACb,GAAG,EAAE,UAAU;KAClB,CAAC;IAEF,MAAM,MAAM,GAAG,GAAG,CAAC,QAAQ,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC;IACjE,MAAM,CAAC,QAAQ,GAAG,GAAG,MAAM,MAAM,GAAG,CAAC,QAAQ,EAAE,CAAC;IAChD,IAAI,GAAG,CAAC,IAAI,EAAE,CAAC;QACX,MAAM,CAAC,QAAQ,IAAI,IAAI,GAAG,CAAC,IAAI,EAAE,CAAC;IACtC,CAAC;IACD,MAAM,CAAC,MAAM,GAAG,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IACjD,MAAM,CAAC,GAAG,GAAG,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAEzD,IAAI,GAAG,CAAC,QAAQ,EAAE,CAAC;QACf,MAAM,CAAC,WAAW,GAAG,GAAG,CAAC,QAAQ,CAAC;IACtC,CAAC;IAED,IAAI,GAAG,CAAC,QAAQ,EAAE,CAAC;QACf,MAAM,CAAC,eAAe,GAAG,GAAG,CAAC,QAAQ,CAAC;IAC1C,CAAC;IAED,OAAO,MAAM,CAAC;AAClB,CAAC"}
@@ -0,0 +1,12 @@
1
+ import { Readable, Writable } from "stream";
2
+ export interface SftpConfig {
3
+ host: string;
4
+ port?: number;
5
+ username: string;
6
+ password?: string;
7
+ privateKey?: Buffer | string;
8
+ }
9
+ export declare function sftpReadStream(remotePath: string, config: SftpConfig): Promise<Readable>;
10
+ export declare function sftpWriteStream(remotePath: string, config: SftpConfig): Promise<Writable>;
11
+ export declare function sftpLatestFile(config: SftpConfig, remoteDir: string, extension: string): Promise<string>;
12
+ //# sourceMappingURL=sftpstream.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sftpstream.d.ts","sourceRoot":"","sources":["../src/sftpstream.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAG,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAE7C,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CAC9B;AAED,wBAAsB,cAAc,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,QAAQ,CAAC,CA2B9F;AAED,wBAAsB,eAAe,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,QAAQ,CAAC,CA2B/F;AAED,wBAAsB,cAAc,CAAC,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CA4C9G"}
@@ -1,114 +1,84 @@
1
1
  import { Client } from "ssh2";
2
- import { Readable , Writable } from "stream";
3
-
4
- export interface SftpConfig {
5
- host: string;
6
- port?: number;
7
- username: string;
8
- password?: string;
9
- privateKey?: Buffer | string;
10
- }
11
-
12
- export async function sftpReadStream(remotePath: string, config: SftpConfig): Promise<Readable> {
2
+ import { Readable, Writable } from "stream";
3
+ export async function sftpReadStream(remotePath, config) {
13
4
  return new Promise((resolve, reject) => {
14
5
  const conn = new Client();
15
-
16
6
  conn.on("ready", () => {
17
7
  conn.sftp((err, sftp) => {
18
8
  if (err) {
19
9
  conn.end();
20
10
  return reject(err);
21
11
  }
22
-
23
12
  const stream = sftp.createReadStream(remotePath);
24
-
25
13
  // Close SSH connection when stream ends or errors
26
14
  stream.on("close", () => conn.end());
27
- stream.on("error", (err: any) => {
15
+ stream.on("error", (err) => {
28
16
  conn.end();
29
17
  reject(err);
30
18
  });
31
-
32
19
  resolve(stream);
33
20
  });
34
21
  });
35
-
36
22
  conn.on("error", (err) => reject(err));
37
23
  conn.connect(config);
38
24
  });
39
25
  }
40
-
41
- export async function sftpWriteStream(remotePath: string, config: SftpConfig): Promise<Writable> {
26
+ export async function sftpWriteStream(remotePath, config) {
42
27
  return new Promise((resolve, reject) => {
43
28
  const conn = new Client();
44
-
45
29
  conn.on("ready", () => {
46
30
  conn.sftp((err, sftp) => {
47
31
  if (err) {
48
32
  conn.end();
49
33
  return reject(err);
50
34
  }
51
-
52
35
  const stream = sftp.createWriteStream(remotePath, { encoding: "utf-8" });
53
-
54
36
  // Close SSH connection when stream ends or errors
55
37
  stream.on("close", () => conn.end());
56
- stream.on("error", (err: any) => {
38
+ stream.on("error", (err) => {
57
39
  conn.end();
58
40
  reject(err);
59
41
  });
60
-
61
42
  resolve(stream);
62
43
  });
63
44
  });
64
-
65
45
  conn.on("error", (err) => reject(err));
66
46
  conn.connect(config);
67
47
  });
68
48
  }
69
-
70
- export async function sftpLatestFile(config: SftpConfig, remoteDir: string, extension: string): Promise<string> {
49
+ export async function sftpLatestFile(config, remoteDir, extension) {
71
50
  return new Promise((resolve, reject) => {
72
51
  const conn = new Client();
73
-
74
52
  conn.on("ready", () => {
75
53
  conn.sftp((err, sftp) => {
76
54
  if (err) {
77
55
  conn.end();
78
56
  return reject(err);
79
57
  }
80
-
81
58
  sftp.readdir(remoteDir, (err, list) => {
82
59
  if (err) {
83
60
  conn.end();
84
61
  return reject(err);
85
62
  }
86
-
87
63
  if (!list || list.length === 0) {
88
64
  conn.end();
89
65
  return reject(new Error("No files found in directory"));
90
66
  }
91
-
92
67
  // Filter only .xml files
93
68
  const myFiles = list.filter(f => f.filename.toLowerCase().endsWith(extension));
94
-
95
69
  if (myFiles.length === 0) {
96
70
  conn.end();
97
71
  return reject(new Error(`No ${extension} files found in directory`));
98
72
  }
99
-
100
- const latest = myFiles.reduce((prev, curr) =>
101
- (prev.attrs.mtime > curr.attrs.mtime) ? prev : curr
102
- );
103
-
73
+ const latest = myFiles.reduce((prev, curr) => (prev.attrs.mtime > curr.attrs.mtime) ? prev : curr);
104
74
  const latestPath = `${remoteDir}/${latest.filename}`;
105
75
  conn.end();
106
76
  resolve(latestPath);
107
77
  });
108
78
  });
109
79
  });
110
-
111
80
  conn.on("error", (err) => reject(err));
112
81
  conn.connect(config);
113
82
  });
114
- }
83
+ }
84
+ //# sourceMappingURL=sftpstream.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sftpstream.js","sourceRoot":"","sources":["../src/sftpstream.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,38 @@
1
+ import { Writable, type WritableOptions } from "stream";
2
+ export interface SlowWritableOptions extends WritableOptions {
3
+ /**
4
+ * Delay per chunk (ms). Default 200 ms.
5
+ */
6
+ delayMs?: number;
7
+ /**
8
+ * Maximum number of concurrent "in-flight" asynchronous writes.
9
+ * While more writes may be queued, only up to this number will be processed in parallel.
10
+ * Default 1.
11
+ */
12
+ maxConcurrency?: number;
13
+ /**
14
+ * If set to a positive integer n, every nth chunk will produce an error (for testing).
15
+ * Default 0 (never error).
16
+ */
17
+ simulateErrorEveryN?: number;
18
+ }
19
+ /**
20
+ * SlowWritable: a Writable stream that processes writes asynchronously
21
+ * with an artificial delay and optional concurrency control.
22
+ */
23
+ export declare class SlowWritable extends Writable {
24
+ private delayMs;
25
+ private maxConcurrency;
26
+ private simulateErrorEveryN;
27
+ private inFlight;
28
+ private queue;
29
+ private seqCounter;
30
+ private destroyedFlag;
31
+ constructor(opts?: SlowWritableOptions);
32
+ _write(chunk: any, encoding: BufferEncoding, callback: (err?: Error | null) => void): void;
33
+ private processQueue;
34
+ private performAsyncWrite;
35
+ _final(callback: (err?: Error | null) => void): void;
36
+ _destroy(err: Error | null, callback: (error?: Error | null) => void): void;
37
+ }
38
+ //# sourceMappingURL=slow-writable.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slow-writable.d.ts","sourceRoot":"","sources":["../src/slow-writable.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,KAAK,eAAe,EAAE,MAAM,QAAQ,CAAC;AAKxD,MAAM,WAAW,mBAAoB,SAAQ,eAAe;IAC1D;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;;;OAIG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB;;;OAGG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED;;;GAGG;AACH,qBAAa,YAAa,SAAQ,QAAQ;IACxC,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,cAAc,CAAS;IAC/B,OAAO,CAAC,mBAAmB,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAK;IACrB,OAAO,CAAC,KAAK,CAKL;IACR,OAAO,CAAC,UAAU,CAAK;IACvB,OAAO,CAAC,aAAa,CAAS;gBAElB,IAAI,GAAE,mBAAwB;IAU1C,MAAM,CAAC,KAAK,EAAE,GAAG,EAAE,QAAQ,EAAE,cAAc,EAAE,QAAQ,EAAE,CAAC,GAAG,CAAC,EAAE,KAAK,GAAG,IAAI,KAAK,IAAI,GAAG,IAAI;IAY1F,OAAO,CAAC,YAAY;YAwBN,iBAAiB;IA6C/B,MAAM,CAAC,QAAQ,EAAE,CAAC,GAAG,CAAC,EAAE,KAAK,GAAG,IAAI,KAAK,IAAI,GAAG,IAAI;IAgBpD,QAAQ,CAAC,GAAG,EAAE,KAAK,GAAG,IAAI,EAAE,QAAQ,EAAE,CAAC,KAAK,CAAC,EAAE,KAAK,GAAG,IAAI,KAAK,IAAI,GAAG,IAAI;CAa5E"}
@@ -0,0 +1,126 @@
1
+ // slow-writable.ts
2
+ import { Writable } from "stream";
3
+ import log4js from 'log4js';
4
+ const logger = log4js.getLogger();
5
+ /**
6
+ * SlowWritable: a Writable stream that processes writes asynchronously
7
+ * with an artificial delay and optional concurrency control.
8
+ */
9
+ export class SlowWritable extends Writable {
10
+ delayMs;
11
+ maxConcurrency;
12
+ simulateErrorEveryN;
13
+ inFlight = 0;
14
+ queue = [];
15
+ seqCounter = 0;
16
+ destroyedFlag = false;
17
+ constructor(opts = {}) {
18
+ // Keep objectMode/encoding behavior from user but default to object mode false
19
+ const { delayMs = 200, maxConcurrency = 1, simulateErrorEveryN = 0, ...writableOpts } = opts;
20
+ super(writableOpts);
21
+ this.delayMs = delayMs;
22
+ this.maxConcurrency = Math.max(1, Math.floor(maxConcurrency));
23
+ this.simulateErrorEveryN = Math.max(0, Math.floor(simulateErrorEveryN));
24
+ }
25
+ // Node will call _write for each chunk
26
+ _write(chunk, encoding, callback) {
27
+ if (this.destroyedFlag) {
28
+ callback(new Error("Stream is destroyed"));
29
+ return;
30
+ }
31
+ const seq = ++this.seqCounter;
32
+ this.queue.push({ chunk, encoding, callback, seq });
33
+ this.processQueue();
34
+ }
35
+ // Process queued writes honoring maxConcurrency
36
+ processQueue() {
37
+ // If nothing to do or already at concurrency limit, return
38
+ while (this.inFlight < this.maxConcurrency && this.queue.length > 0 && !this.destroyedFlag) {
39
+ const item = this.queue.shift();
40
+ this.inFlight++;
41
+ this.performAsyncWrite(item)
42
+ .then(() => {
43
+ this.inFlight--;
44
+ // After finishing one, try to process more
45
+ // Use nextTick to avoid deep recursion
46
+ process.nextTick(() => this.processQueue());
47
+ })
48
+ .catch((err) => {
49
+ this.inFlight--;
50
+ // propagate error via callback; stream will emit 'error' as well
51
+ item.callback(err);
52
+ this.emit("error", err);
53
+ // continue processing queue
54
+ process.nextTick(() => this.processQueue());
55
+ });
56
+ }
57
+ }
58
+ // Simulate an asynchronous write that takes `delayMs` ms
59
+ async performAsyncWrite(item) {
60
+ return new Promise((resolve, reject) => {
61
+ const maybeError = this.simulateErrorEveryN > 0 && item.seq % this.simulateErrorEveryN === 0;
62
+ const timer = setTimeout(() => {
63
+ // simulate processing chunk here. For demonstration we just log.
64
+ // In real use, replace with actual async I/O.
65
+ // eslint-disable-next-line no-console
66
+ logger.info(`SlowWritable processed seq=${item.seq}`);
67
+ if (maybeError) {
68
+ const err = new Error(`Simulated error at seq ${item.seq}`);
69
+ item.callback(err);
70
+ reject(err);
71
+ }
72
+ else {
73
+ item.callback();
74
+ resolve();
75
+ }
76
+ }, this.delayMs);
77
+ // If stream was destroyed meantime, cancel timer and callback with error
78
+ const onDestroy = () => {
79
+ clearTimeout(timer);
80
+ const err = new Error("Stream destroyed while writing");
81
+ try {
82
+ item.callback(err);
83
+ }
84
+ catch (_) {
85
+ // ignore
86
+ }
87
+ reject(err);
88
+ };
89
+ // Ensure we don't leak listeners. If destroyedFlag becomes true quickly, call onDestroy.
90
+ if (this.destroyedFlag) {
91
+ onDestroy();
92
+ }
93
+ });
94
+ }
95
+ _final(callback) {
96
+ // Wait until queue emptied and inFlight is zero
97
+ const check = () => {
98
+ if (this.destroyedFlag) {
99
+ callback(new Error("Stream destroyed before finalizing"));
100
+ return;
101
+ }
102
+ if (this.queue.length === 0 && this.inFlight === 0) {
103
+ callback();
104
+ }
105
+ else {
106
+ setTimeout(check, 10);
107
+ }
108
+ };
109
+ check();
110
+ }
111
+ _destroy(err, callback) {
112
+ this.destroyedFlag = true;
113
+ // flush callbacks in queue with error
114
+ while (this.queue.length > 0) {
115
+ const item = this.queue.shift();
116
+ try {
117
+ item.callback(err ?? new Error("Stream destroyed"));
118
+ }
119
+ catch (_) {
120
+ // ignore
121
+ }
122
+ }
123
+ callback(err);
124
+ }
125
+ }
126
+ //# sourceMappingURL=slow-writable.js.map