@t4h.framework/fs 0.12.1 → 0.13.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/.ai/skills/framework-fs/SKILL.md +2 -3
- package/.ai/skills/framework-fs-split/SKILL.md +26 -32
- package/CHANGELOG.md +16 -0
- package/README.md +13 -40
- package/dist/activities/FileSplitActivity.d.ts +3 -3
- package/dist/activities/FileSplitActivity.d.ts.map +1 -1
- package/dist/activities/FileSplitActivity.js +18 -32
- package/dist/activities/FileSplitActivity.js.map +1 -1
- package/dist/fs.d.ts +1 -1
- package/dist/fs.d.ts.map +1 -1
- package/dist/fs.js +0 -1
- package/dist/fs.js.map +1 -1
- package/dist/helpers/group-records.d.ts +12 -0
- package/dist/helpers/group-records.d.ts.map +1 -0
- package/dist/helpers/group-records.js +36 -0
- package/dist/helpers/group-records.js.map +1 -0
- package/dist/helpers/split-records.d.ts +30 -0
- package/dist/helpers/split-records.d.ts.map +1 -0
- package/dist/helpers/split-records.js +112 -0
- package/dist/helpers/split-records.js.map +1 -0
- package/package.json +2 -2
- package/dist/helpers/ByteDelimiterScanner.d.ts +0 -50
- package/dist/helpers/ByteDelimiterScanner.d.ts.map +0 -1
- package/dist/helpers/ByteDelimiterScanner.js +0 -103
- package/dist/helpers/ByteDelimiterScanner.js.map +0 -1
- package/dist/helpers/ByteRecordFilter.d.ts +0 -32
- package/dist/helpers/ByteRecordFilter.d.ts.map +0 -1
- package/dist/helpers/ByteRecordFilter.js +0 -89
- package/dist/helpers/ByteRecordFilter.js.map +0 -1
|
@@ -31,7 +31,6 @@ import {
|
|
|
31
31
|
read, FileReadActivity, type FileReadOptions,
|
|
32
32
|
// turn workflow content into a Binary — see the framework-fs-write skill
|
|
33
33
|
write, FileWriteActivity, type FileWriteOptions,
|
|
34
|
-
ByteDelimiterScanner, type FileSplitDelimiter, type ByteRange, type ByteTrim,
|
|
35
34
|
} from '@t4h.framework/fs'
|
|
36
35
|
```
|
|
37
36
|
|
|
@@ -66,7 +65,7 @@ export abstract class FileSystemClaim {
|
|
|
66
65
|
|--------|---------|
|
|
67
66
|
| `write(path, readable, contentType?)` | Persist stream or buffer at `path`; return a `Binary` handle (metadata + `stream()`). When `contentType` is omitted, infer it from the path. |
|
|
68
67
|
| `read(path)` | Load an existing file at `path`; return a `Binary` handle. |
|
|
69
|
-
| `slice(binary, range, encoding?)` | A view over a byte range of an existing binary, **without copying data**. When `encoding` is set, the runtime hydrates the slice as a decoded string instead of a binary — this is how `
|
|
68
|
+
| `slice(binary, range, encoding?)` | A view over a byte range of an existing binary, **without copying data**. When `encoding` is set, the runtime hydrates the slice as a decoded string instead of a binary — this is how `read` returns a window as a string. Implementations must **compose ranges**: slicing a slice is relative to it, and the stored range is absolute against the underlying file. |
|
|
70
69
|
|
|
71
70
|
All methods may return synchronously or as a `Promise`. Implementations choose storage layout; callers should only rely on the returned `Binary` API.
|
|
72
71
|
|
|
@@ -132,7 +131,7 @@ These packages depend on `@t4h.framework/fs` and declare `FileSystemClaim`; read
|
|
|
132
131
|
|---------|--------|
|
|
133
132
|
| `@t4h.framework/http` | The HTTP activity persists response bodies to `fs` and returns them wrapped in a `Body` — you read them with `res.body.json()` / `text()`. |
|
|
134
133
|
| `@t4h.framework/jsx` | Render activities write their output to `fs` and hand back a `Binary` you consume in the workflow. |
|
|
135
|
-
| `@t4h.framework/fs` itself | `FileSplitActivity`
|
|
134
|
+
| `@t4h.framework/fs` itself | `FileSplitActivity` streams the source and hands its slices to `startMany` as strings; `FileJoinActivity` streams batch results into a new file; `FileReadActivity` slices (or streams, when the binary is not file-backed) a small window into a string. See the `framework-fs-split` skill. |
|
|
136
135
|
|
|
137
136
|
Orchestrate these package primitives from your workflow; the `fs` write/read happens inside them.
|
|
138
137
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name: framework-fs-split
|
|
3
3
|
description: >-
|
|
4
4
|
Guides correct use of `split`, `join` and `read` from @t4h.framework/fs:
|
|
5
|
-
fanning one large file out into a batch of workflows (one per
|
|
5
|
+
fanning one large file out into a batch of workflows (one per
|
|
6
6
|
slice), folding the batch results back into a single file, and reading small
|
|
7
7
|
byte windows (headers, trailers) into the workflow — with O(1) parent
|
|
8
8
|
history and bounded memory regardless of file size. Use whenever a workflow
|
|
@@ -20,8 +20,7 @@ description: >-
|
|
|
20
20
|
# `split` / `join` / `read` — file fan-out, fold and windows for workflows
|
|
21
21
|
|
|
22
22
|
`split` turns **one file into a batch of workflows**: it streams the file once,
|
|
23
|
-
cuts it into
|
|
24
|
-
child workflow per slice, and **suspends the parent until every child is
|
|
23
|
+
cuts it into slices, starts one child workflow per slice, and **suspends the parent until every child is
|
|
25
24
|
terminal**. `join` is the inverse: it folds the batch results back into a
|
|
26
25
|
single file, streaming one result at a time.
|
|
27
26
|
|
|
@@ -62,13 +61,13 @@ Package path: `framework/packages/fs`. Import only from the package root.
|
|
|
62
61
|
Everything here exists to keep the parent history **O(1)** whatever the file
|
|
63
62
|
size:
|
|
64
63
|
|
|
65
|
-
1. `split` reads the source
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
`Type.String()
|
|
64
|
+
1. `split` reads the source once, worker side: it cuts records, drops the ones
|
|
65
|
+
`where` rejects, groups the rest by `every` and hands each slice to the
|
|
66
|
+
batch as a decoded string as soon as it is complete. Nothing collects the
|
|
67
|
+
slices, no copy of the file is written, and a slice past 3 MiB
|
|
68
|
+
fails the split while it reads.
|
|
69
|
+
2. Each child receives its slice as a plain string — what its
|
|
70
|
+
`Type.String()` schema declares.
|
|
72
71
|
3. The parent resumes with only `WorkflowBatchRef { id, count }`. Per-child
|
|
73
72
|
results are **never aggregated onto the ref**: they live with each child
|
|
74
73
|
run.
|
|
@@ -128,17 +127,16 @@ the next slice. Forgetting `{ next: 2 }` silently strips the record type code
|
|
|
128
127
|
from every slice.
|
|
129
128
|
|
|
130
129
|
Offsets are always computed over **raw bytes** (never decoded characters), so
|
|
131
|
-
multi-byte encodings cannot skew the
|
|
130
|
+
multi-byte encodings cannot skew the cuts.
|
|
132
131
|
|
|
133
132
|
### Other options
|
|
134
133
|
|
|
135
134
|
| Option | Default | Behavior |
|
|
136
135
|
|--------|---------|----------|
|
|
137
|
-
| `every` | `1` | Cut only every N delimiter occurrences — groups N records per child. One million lines with `every: 10_000` start 100 children, each receiving a raw block (inner delimiters untouched) to iterate locally. `keep` arithmetic applies only at actual cut points. |
|
|
136
|
+
| `every` | `1` | Cut only every N delimiter occurrences — groups N records per child. One million lines with `every: 10_000` start 100 children, each receiving a raw block (inner delimiters untouched) to iterate locally. `keep` arithmetic applies only at actual cut points. A slice may take up to 3 MiB; past that the split fails while it reads — lower `every`. |
|
|
138
137
|
| `trim` | none | `{ start?, end? }` **bytes** cut off each end of the file before scanning — a positional header/trailer. Byte-based on purpose: a record-start delimiter (`'\n10'`) fuses the trailer into the last record's slice, so a record-count skip would drop a valid record along with it. Trimmed content never becomes a slice nor counts toward `every`; fetch it with `read`. Positional layouts make the byte counts constants — getting them right is the caller's business. |
|
|
139
138
|
| `skipEmpty` | `true` | Drops zero-length slices (consecutive delimiters, file starting/ending with one) so they never become child workflows. Turn off when slice index must align 1:1 with record position. "Empty" means zero bytes — a whitespace-only line is a regular slice. |
|
|
140
139
|
| `encoding` | `'utf8'` | Converts the delimiter to bytes and decodes each slice into the string the child receives. |
|
|
141
|
-
| `inlineMaxBytes` | `16_384` | A slice up to this many bytes is handed to the child as a string straight from the scan; a larger one as a reference to its byte range in the durable copy, read back where the child runs. Reading a few hundred bytes back costs a row lookup and a ranged object read per child, which is what a million-line split spends its CPU on. `0` hands every slice by range. Keep it well under the runtime's fan-out message budget (1 MiB by default). |
|
|
142
140
|
| `timeoutInMs` | none | Max milliseconds to await the batch; on expiry the activity **rejects** (reason `timeout`) instead of suspending forever — the safety net for a child that never reaches a terminal state. |
|
|
143
141
|
|
|
144
142
|
---
|
|
@@ -248,8 +246,9 @@ const batch = await split(file, processInvoice, {
|
|
|
248
246
|
|
|
249
247
|
## Runtime requirements
|
|
250
248
|
|
|
251
|
-
- Claim providers: `FileSystemClaim` (with `slice`
|
|
252
|
-
`
|
|
249
|
+
- Claim providers: `FileSystemClaim` (with `slice`, for `read` and `join`),
|
|
250
|
+
`WorkflowClaim` (with `startMany` reading an `AsyncIterable`), and
|
|
251
|
+
`WorkflowBatchClaim` (from `@t4h.framework/core`).
|
|
253
252
|
- Production whitelist: `FileSplitActivity`, `FileJoinActivity`,
|
|
254
253
|
`FileReadActivity` and core's `StartManyWorkflowsActivity` must be
|
|
255
254
|
registered via `runtime.addActivity`.
|
|
@@ -271,26 +270,22 @@ from `src/activities/__tests__/`):
|
|
|
271
270
|
|
|
272
271
|
```typescript
|
|
273
272
|
import { AsyncActivityResultPending, Claim, WorkflowBatchClaim, WorkflowBatchRef, WorkflowClaim } from '@t4h.framework/core'
|
|
274
|
-
import { FileSplitActivity,
|
|
273
|
+
import { FileSplitActivity, Split } from '@t4h.framework/fs'
|
|
275
274
|
|
|
276
|
-
const
|
|
277
|
-
const
|
|
278
|
-
const
|
|
275
|
+
const inputs: unknown[] = []
|
|
276
|
+
const startMany = vi.fn(async (_wf, slices: AsyncIterable<unknown>) => {
|
|
277
|
+
for await (const slice of slices) inputs.push(slice) // must consume the stream
|
|
278
|
+
return new WorkflowBatchRef('batch-1', inputs.length)
|
|
279
|
+
})
|
|
279
280
|
|
|
280
281
|
const result = await Claim.run(
|
|
281
|
-
[
|
|
282
|
-
|
|
283
|
-
{
|
|
284
|
-
],
|
|
285
|
-
() => {
|
|
286
|
-
const activity = new FileSplitActivity()
|
|
287
|
-
activity.id = 'activity-1'
|
|
288
|
-
return activity.bootstrap({ binary, workflow: 'child', options: { delimiter: Split.line() } })
|
|
289
|
-
},
|
|
282
|
+
[{ provide: WorkflowClaim, value: { startMany } as never }],
|
|
283
|
+
() =>
|
|
284
|
+
new FileSplitActivity().bootstrap({ binary, workflow: 'child', options: { delimiter: Split.line() } }),
|
|
290
285
|
)
|
|
291
286
|
|
|
292
287
|
expect(result).toBeInstanceOf(AsyncActivityResultPending)
|
|
293
|
-
expect(
|
|
288
|
+
expect(inputs).toEqual(['foo', 'bar', 'baz'])
|
|
294
289
|
```
|
|
295
290
|
|
|
296
291
|
For `join`, provide `WorkflowBatchClaim` as an async generator:
|
|
@@ -301,9 +296,8 @@ const outputs = vi.fn(async function* () {
|
|
|
301
296
|
})
|
|
302
297
|
```
|
|
303
298
|
|
|
304
|
-
Behaviors worth asserting because they regress easily: the
|
|
305
|
-
|
|
306
|
-
`keep` arithmetic on positional cuts; the empty batch resolving immediately;
|
|
299
|
+
Behaviors worth asserting because they regress easily: the source is streamed
|
|
300
|
+
once; `keep` arithmetic on positional cuts; the empty batch resolving immediately;
|
|
307
301
|
CSV escaping (quotes doubled, delimiter/newline wrapped); the status filter.
|
|
308
302
|
|
|
309
303
|
---
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# @t4h.framework/fs
|
|
2
2
|
|
|
3
|
+
## 0.13.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#214](https://github.com/tech4humans-brasil/framework/pull/214) [`fc4db5f`](https://github.com/tech4humans-brasil/framework/commit/fc4db5fa01454a45a3727b76dbacc215aa6b3ab7) Thanks [@gusteycamargo](https://github.com/gusteycamargo)! - `split` reads the file once, as a stream: it cuts the records, drops the ones
|
|
8
|
+
`where` rejects, groups the rest by `every` and hands each slice to
|
|
9
|
+
`startMany` as a string as soon as it is complete. No durable copy of the file
|
|
10
|
+
is written and no slice is read back by range. A slice may take up to 3 MiB
|
|
11
|
+
(the same as the runtime's default limit for the input of a workflow
|
|
12
|
+
run); past that the split fails while it reads, naming the byte the slice
|
|
13
|
+
starts at, where `where` used to cap a record at 1 MiB and nothing capped a
|
|
14
|
+
slice. `inlineMaxBytes` is removed: every slice reaches its child as a
|
|
15
|
+
string. `ByteDelimiterScanner` is removed from the public API.
|
|
16
|
+
|
|
17
|
+
## 0.12.2
|
|
18
|
+
|
|
3
19
|
## 0.12.1
|
|
4
20
|
|
|
5
21
|
## 0.12.0
|
package/README.md
CHANGED
|
@@ -145,7 +145,7 @@ which stream through storage.
|
|
|
145
145
|
|
|
146
146
|
### split
|
|
147
147
|
|
|
148
|
-
Splits a file into
|
|
148
|
+
Splits a file into slices, starts one child workflow per slice as
|
|
149
149
|
a single batch, and suspends the parent until every child is terminal —
|
|
150
150
|
regardless of each child succeeding or failing (a rejected child never
|
|
151
151
|
rejects the batch):
|
|
@@ -170,14 +170,15 @@ const main = new Workflow(fileSchema, async (file: Binary) => {
|
|
|
170
170
|
})
|
|
171
171
|
```
|
|
172
172
|
|
|
173
|
-
The
|
|
173
|
+
The source is read once, as a stream:
|
|
174
174
|
|
|
175
|
-
1. The source
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
175
|
+
1. The source is read once, worker side: cut into records, filtered by
|
|
176
|
+
`where` when given, grouped by `every`, and each slice goes into the batch
|
|
177
|
+
as a string as soon as it is complete. Nothing collects the slices, and a
|
|
178
|
+
slice may take up to 3 MiB: past that the split fails
|
|
179
|
+
while it reads, at the byte the slice starts.
|
|
180
|
+
2. The runtime reads that stream once and stores the batch as it goes; no copy
|
|
181
|
+
of the file is written and no slice is read back by range.
|
|
181
182
|
3. The activity output is just the batch reference (`{ id, count }`), so the
|
|
182
183
|
parent history stays O(1) whatever the slice count. Per-child results are
|
|
183
184
|
never aggregated: they live with each child run, to be inspected
|
|
@@ -222,8 +223,7 @@ await split(file, workflow, {
|
|
|
222
223
|
every: 10_000, // cut only every N occurrences (groups records)
|
|
223
224
|
trim: { start: 241, end: 241 }, // bytes cut off each end before scanning
|
|
224
225
|
skipEmpty: false, // keep zero-length slices (default: skipped)
|
|
225
|
-
encoding: 'latin1', // delimiter bytes + slice
|
|
226
|
-
inlineMaxBytes: 16_384, // slices up to this size travel as strings (default: 16 KiB)
|
|
226
|
+
encoding: 'latin1', // delimiter bytes + slice decoding (default: utf8)
|
|
227
227
|
timeoutInMs: 3_600_000, // max ms to await the batch; rejects on expiry
|
|
228
228
|
})
|
|
229
229
|
```
|
|
@@ -254,18 +254,9 @@ await split(file, workflow, {
|
|
|
254
254
|
workflows. Turn it off when the slice index must align 1:1 with the record
|
|
255
255
|
position in the source file. "Empty" means zero bytes: a whitespace-only
|
|
256
256
|
line is a regular slice.
|
|
257
|
-
- **`encoding`** converts the delimiter to bytes and
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
- **`inlineMaxBytes`** (default 16 KiB) is the size up to which a slice is
|
|
261
|
-
handed to the child as a string, straight from the scan. A slice above it is
|
|
262
|
-
handed as a reference to its byte range in the durable copy, read back
|
|
263
|
-
where the child runs. A line of a CSV is a few hundred bytes: reading it
|
|
264
|
-
back would cost a row lookup and a ranged object read per child, and that,
|
|
265
|
-
not the scan, is what a million-line split spends its CPU on. Set it to `0`
|
|
266
|
-
to hand every slice by range. Keep it well under the fan-out message budget
|
|
267
|
-
of the runtime (`WORKFLOW_BATCH_FANOUT_CHUNK_MAX_BYTES`, 1 MiB by default):
|
|
268
|
-
an inline slice that does not fit a message is refused.
|
|
257
|
+
- **`encoding`** converts the delimiter to bytes and decodes each slice into
|
|
258
|
+
the string its child receives. Offsets are always computed over raw bytes,
|
|
259
|
+
so multi-byte encodings never skew the cuts.
|
|
269
260
|
- **`timeoutInMs`** is the maximum time in milliseconds to await the batch. On
|
|
270
261
|
expiry the activity rejects (reason `timeout`) instead of suspending
|
|
271
262
|
forever — the safety net for a child that never reaches a terminal state.
|
|
@@ -374,21 +365,3 @@ already hydrated one at a time — the query compiles once and adds one
|
|
|
374
365
|
predicate call per entry, no extra serialization and no change to the memory
|
|
375
366
|
profile. It is applied app-side: every child output is still fetched and
|
|
376
367
|
parsed; `where` decides what enters the file, not what leaves the database.
|
|
377
|
-
|
|
378
|
-
### ByteDelimiterScanner
|
|
379
|
-
|
|
380
|
-
The scanner behind `split` is exported for direct use. It is a pass-through
|
|
381
|
-
`Transform`: chunks flow through unchanged (so the same pass can persist the
|
|
382
|
-
file) while it records slice offsets in absolute byte positions, handling
|
|
383
|
-
delimiters split across chunk boundaries. `ranges` is complete once the stream
|
|
384
|
-
has flushed.
|
|
385
|
-
|
|
386
|
-
```typescript
|
|
387
|
-
import { ByteDelimiterScanner } from '@t4h.framework/fs'
|
|
388
|
-
|
|
389
|
-
const scanner = new ByteDelimiterScanner({ delimiter: { value: '\n' } })
|
|
390
|
-
|
|
391
|
-
await pipeline(source, scanner, destination)
|
|
392
|
-
|
|
393
|
-
scanner.slices // [{ start: 0, end: 3, content: <Buffer> }, ...]
|
|
394
|
-
```
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { AsyncActivity, type Binary, type Workflow, WorkflowBatchCollectionRef, workflowBatchSchema } from '@t4h.framework/core';
|
|
2
|
-
import { type ByteTrim, type FileSplitDelimiter } from '../helpers/
|
|
2
|
+
import { type ByteTrim, type FileSplitDelimiter } from '../helpers/split-records.js';
|
|
3
3
|
type FileSplitWhereOperators = {
|
|
4
4
|
$eq?: string;
|
|
5
5
|
$ne?: string;
|
|
@@ -25,7 +25,6 @@ export type FileSplitOptions = {
|
|
|
25
25
|
trim?: ByteTrim;
|
|
26
26
|
skipEmpty?: boolean;
|
|
27
27
|
encoding?: BufferEncoding;
|
|
28
|
-
inlineMaxBytes?: number;
|
|
29
28
|
timeoutInMs?: number;
|
|
30
29
|
};
|
|
31
30
|
export type FileSplitActivityInput = {
|
|
@@ -48,7 +47,8 @@ export declare class FileSplitActivity extends AsyncActivity<readonly [
|
|
|
48
47
|
batch: import("typebox").TString;
|
|
49
48
|
count: import("typebox").TNumber;
|
|
50
49
|
}>> | import("@t4h.framework/core").AsyncActivityResultFulfilled<FileSplitBatch | undefined>>;
|
|
51
|
-
private
|
|
50
|
+
private slices;
|
|
51
|
+
private filter;
|
|
52
52
|
advance(payload: FileSplitBatch): import("@t4h.framework/core").AsyncActivityResultFulfilled<FileSplitBatch | undefined>;
|
|
53
53
|
toOutput(output: FileSplitBatch): WorkflowBatchCollectionRef;
|
|
54
54
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FileSplitActivity.d.ts","sourceRoot":"","sources":["../../src/activities/FileSplitActivity.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"FileSplitActivity.d.ts","sourceRoot":"","sources":["../../src/activities/FileSplitActivity.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,aAAa,EACb,KAAK,MAAM,EAGX,KAAK,QAAQ,EACb,0BAA0B,EAG1B,mBAAmB,EACpB,MAAM,qBAAqB,CAAA;AAM5B,OAAO,EAEL,KAAK,QAAQ,EACb,KAAK,kBAAkB,EAExB,MAAM,6BAA6B,CAAA;AAIpC,KAAK,uBAAuB,GAAG;IAC7B,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,GAAG,CAAC,EAAE,MAAM,EAAE,CAAA;IACd,IAAI,CAAC,EAAE,MAAM,EAAE,CAAA;IACf,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,IAAI,CAAC,EAAE,cAAc,CAAA;CACtB,CAAA;AAED,MAAM,MAAM,cAAc,GACtB,MAAM,GACN,CAAC,uBAAuB,GAAG;IACzB,IAAI,CAAC,EAAE,cAAc,EAAE,CAAA;IACvB,GAAG,CAAC,EAAE,cAAc,EAAE,CAAA;IACtB,IAAI,CAAC,EAAE,cAAc,EAAE,CAAA;CACxB,CAAC,CAAA;AAEN,MAAM,MAAM,gBAAgB,GAAG;IAC7B,SAAS,EAAE,kBAAkB,CAAA;IAC7B,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,KAAK,CAAC,EAAE,cAAc,CAAA;IACtB,IAAI,CAAC,EAAE,QAAQ,CAAA;IACf,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,QAAQ,CAAC,EAAE,cAAc,CAAA;IACzB,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB,CAAA;AAED,MAAM,MAAM,sBAAsB,GAAG;IACnC,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,gBAAgB,CAAA;CAC1B,CAAA;AAED,MAAM,MAAM,cAAc,GAAG;IAC3B,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;CACd,CAAA;AAED,qBAAa,iBAAkB,SAAQ,aAAa,CAClD,SAAS;IACP,MAAM,EAAE,MAAM;IACd,QAAQ,EAAE,QAAQ,CAAC,GAAG,EAAE,GAAG,CAAC;IAC5B,OAAO,EAAE,gBAAgB;CAC1B,EACD,sBAAsB,EACtB,cAAc,EACd,0BAA0B,EAC1B,sBAAsB,EACtB,OAAO,mBAAmB,CAC3B;IACC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAErB;IAEK,OAAO,CACZ,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,QAAQ,CAAC,GAAG,EAAE,GAAG,CAAC,EAC5B,OAAO,EAAE,gBAAgB,GACxB,sBAAsB;IAsBZ,SAAS,CAAC,EACrB,MAAM,EACN,QAAQ,EACR,OAAO,GACR,EAAE,sBAAsB;;;;YAgBV,MAAM;YA4BN,MAAM;IAad,OAAO,CAAC,OAAO,EAAE,cAAc;IAI/B,QAAQ,CAAC,MAAM,EAAE,cAAc,GAAG,0BAA0B;CAGpE;AAED,wBAAsB,KAAK,CACzB,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,QAAQ,CAAC,GAAG,EAAE,GAAG,CAAC,EAC5B,OAAO,EAAE,gBAAgB,GACxB,OAAO,CAAC,0BAA0B,CAAC,CASrC;AAED,yBAAiB,KAAK,CAAC;IACd,MAAM,IAAI,GAAI,cAAY,KAAG,kBAAiC,CAAA;IAE9D,MAAM,IAAI,GACf,OAAO,MAAM,EACb,OAAO,kBAAkB,CAAC,MAAM,CAAC,KAChC,kBAAuC,CAAA;CAC3C"}
|
|
@@ -1,14 +1,11 @@
|
|
|
1
|
-
import { pipeline } from 'node:stream';
|
|
2
1
|
import { AsyncActivity, Claim, History, WorkflowBatchCollectionRef, WorkflowBatchQuery, WorkflowClaim, workflowBatchSchema, } from '@t4h.framework/core';
|
|
3
2
|
import sift from 'sift';
|
|
4
3
|
import { STRING_OPERATIONS } from '../constants/operations.js';
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import { ByteRecordFilter } from '../helpers/ByteRecordFilter.js';
|
|
4
|
+
import { groupRecords } from '../helpers/group-records.js';
|
|
5
|
+
import { splitRecords, } from '../helpers/split-records.js';
|
|
8
6
|
const { createQueryTester } = sift;
|
|
9
7
|
export class FileSplitActivity extends AsyncActivity {
|
|
10
8
|
claims = new Claim({
|
|
11
|
-
fs: FileSystemClaim,
|
|
12
9
|
workflow: WorkflowClaim,
|
|
13
10
|
});
|
|
14
11
|
toInput(binary, workflow, options) {
|
|
@@ -23,25 +20,7 @@ export class FileSplitActivity extends AsyncActivity {
|
|
|
23
20
|
return { binary, workflow: workflow.id, options };
|
|
24
21
|
}
|
|
25
22
|
async bootstrap({ binary, workflow, options, }) {
|
|
26
|
-
const
|
|
27
|
-
const filter = this.toFilter(options.where, options);
|
|
28
|
-
const scanner = new ByteDelimiterScanner({
|
|
29
|
-
delimiter: options.delimiter,
|
|
30
|
-
every: options.every,
|
|
31
|
-
trim: filter ? undefined : options.trim,
|
|
32
|
-
skipEmpty: options.skipEmpty,
|
|
33
|
-
encoding,
|
|
34
|
-
inlineMaxBytes: options.inlineMaxBytes,
|
|
35
|
-
});
|
|
36
|
-
const source = await binary.stream();
|
|
37
|
-
const durable = await this.claims.fs.write(`split/${this.id}`, filter
|
|
38
|
-
? pipeline(source, filter, scanner, () => { })
|
|
39
|
-
: source.pipe(scanner));
|
|
40
|
-
const kept = filter && filter.accepted === 0 ? [] : scanner.slices;
|
|
41
|
-
const slices = await Promise.all(kept.map(({ start, end, content }) => content
|
|
42
|
-
? content.toString(encoding)
|
|
43
|
-
: this.claims.fs.slice(durable, { start, end }, encoding)));
|
|
44
|
-
const batch = await this.claims.workflow.startMany(workflow, slices);
|
|
23
|
+
const batch = await this.claims.workflow.startMany(workflow, this.slices(binary, options));
|
|
45
24
|
if (batch.count === 0)
|
|
46
25
|
return this.done({ batch: batch.id, count: 0 });
|
|
47
26
|
return this.pending({
|
|
@@ -51,18 +30,25 @@ export class FileSplitActivity extends AsyncActivity {
|
|
|
51
30
|
}),
|
|
52
31
|
});
|
|
53
32
|
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
const matches = createQueryTester(where, {
|
|
58
|
-
operations: STRING_OPERATIONS,
|
|
59
|
-
});
|
|
60
|
-
return new ByteRecordFilter({
|
|
33
|
+
async *slices(binary, { delimiter, every, where, trim, skipEmpty = true, ...options }) {
|
|
34
|
+
const encoding = options.encoding ?? 'utf8';
|
|
35
|
+
const records = splitRecords(await binary.stream(), {
|
|
61
36
|
delimiter,
|
|
62
37
|
trim,
|
|
63
38
|
encoding,
|
|
64
|
-
accept: record => matches(record.toString(encoding)),
|
|
65
39
|
});
|
|
40
|
+
const slices = groupRecords(where === undefined ? records : this.filter(records, where, encoding), { delimiter, every, encoding });
|
|
41
|
+
for await (const { content } of slices)
|
|
42
|
+
if (!skipEmpty || content.length > 0)
|
|
43
|
+
yield content.toString(encoding);
|
|
44
|
+
}
|
|
45
|
+
async *filter(records, where, encoding) {
|
|
46
|
+
const matches = createQueryTester(where, {
|
|
47
|
+
operations: STRING_OPERATIONS,
|
|
48
|
+
});
|
|
49
|
+
for await (const record of records)
|
|
50
|
+
if (matches(Buffer.concat(record.pieces).toString(encoding)))
|
|
51
|
+
yield record;
|
|
66
52
|
}
|
|
67
53
|
advance(payload) {
|
|
68
54
|
return this.done(payload);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FileSplitActivity.js","sourceRoot":"","sources":["../../src/activities/FileSplitActivity.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"FileSplitActivity.js","sourceRoot":"","sources":["../../src/activities/FileSplitActivity.ts"],"names":[],"mappings":"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"}
|
package/dist/fs.d.ts
CHANGED
|
@@ -4,5 +4,5 @@ export * from './activities/FileJoinActivity.js';
|
|
|
4
4
|
export * from './activities/FileReadActivity.js';
|
|
5
5
|
export * from './activities/FileSplitActivity.js';
|
|
6
6
|
export * from './activities/FileWriteActivity.js';
|
|
7
|
-
export
|
|
7
|
+
export type { ByteTrim, FileSplitDelimiter } from './helpers/split-records.js';
|
|
8
8
|
//# sourceMappingURL=fs.d.ts.map
|
package/dist/fs.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fs.d.ts","sourceRoot":"","sources":["../src/fs.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAA;AAC/B,cAAc,sBAAsB,CAAA;AACpC,cAAc,kCAAkC,CAAA;AAChD,cAAc,kCAAkC,CAAA;AAChD,cAAc,mCAAmC,CAAA;AACjD,cAAc,mCAAmC,CAAA;AACjD,
|
|
1
|
+
{"version":3,"file":"fs.d.ts","sourceRoot":"","sources":["../src/fs.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAA;AAC/B,cAAc,sBAAsB,CAAA;AACpC,cAAc,kCAAkC,CAAA;AAChD,cAAc,kCAAkC,CAAA;AAChD,cAAc,mCAAmC,CAAA;AACjD,cAAc,mCAAmC,CAAA;AACjD,YAAY,EAAE,QAAQ,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAA"}
|
package/dist/fs.js
CHANGED
|
@@ -4,5 +4,4 @@ export * from './activities/FileJoinActivity.js';
|
|
|
4
4
|
export * from './activities/FileReadActivity.js';
|
|
5
5
|
export * from './activities/FileSplitActivity.js';
|
|
6
6
|
export * from './activities/FileWriteActivity.js';
|
|
7
|
-
export * from './helpers/ByteDelimiterScanner.js';
|
|
8
7
|
//# sourceMappingURL=fs.js.map
|
package/dist/fs.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fs.js","sourceRoot":"","sources":["../src/fs.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAA;AAC/B,cAAc,sBAAsB,CAAA;AACpC,cAAc,kCAAkC,CAAA;AAChD,cAAc,kCAAkC,CAAA;AAChD,cAAc,mCAAmC,CAAA;AACjD,cAAc,mCAAmC,CAAA
|
|
1
|
+
{"version":3,"file":"fs.js","sourceRoot":"","sources":["../src/fs.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAA;AAC/B,cAAc,sBAAsB,CAAA;AACpC,cAAc,kCAAkC,CAAA;AAChD,cAAc,kCAAkC,CAAA;AAChD,cAAc,mCAAmC,CAAA;AACjD,cAAc,mCAAmC,CAAA"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type ByteRecord, type FileSplitDelimiter } from './split-records.js';
|
|
2
|
+
export type ByteSlice = {
|
|
3
|
+
start: number;
|
|
4
|
+
content: Buffer;
|
|
5
|
+
};
|
|
6
|
+
export type GroupRecordsOptions = {
|
|
7
|
+
delimiter: FileSplitDelimiter;
|
|
8
|
+
every?: number;
|
|
9
|
+
encoding?: BufferEncoding;
|
|
10
|
+
};
|
|
11
|
+
export declare function groupRecords(source: AsyncIterable<ByteRecord>, { delimiter, every, encoding }: GroupRecordsOptions): AsyncGenerator<ByteSlice>;
|
|
12
|
+
//# sourceMappingURL=group-records.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"group-records.d.ts","sourceRoot":"","sources":["../../src/helpers/group-records.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,UAAU,EACf,KAAK,kBAAkB,EAIxB,MAAM,oBAAoB,CAAA;AAE3B,MAAM,MAAM,SAAS,GAAG;IACtB,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,MAAM,CAAA;CAChB,CAAA;AAED,MAAM,MAAM,mBAAmB,GAAG;IAChC,SAAS,EAAE,kBAAkB,CAAA;IAC7B,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,QAAQ,CAAC,EAAE,cAAc,CAAA;CAC1B,CAAA;AAED,wBAAgB,YAAY,CAC1B,MAAM,EAAE,aAAa,CAAC,UAAU,CAAC,EACjC,EAAE,SAAS,EAAE,KAAS,EAAE,QAAiB,EAAE,EAAE,mBAAmB,GAC/D,cAAc,CAAC,SAAS,CAAC,CAW3B"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { MAX_SLICE_BYTES, sliceTooLarge, toDelimiter, } from './split-records.js';
|
|
2
|
+
export function groupRecords(source, { delimiter, every = 1, encoding = 'utf8' }) {
|
|
3
|
+
const { bytes, keepPrevious, keepNext } = toDelimiter(delimiter, encoding);
|
|
4
|
+
if (!Number.isInteger(every) || every < 1)
|
|
5
|
+
throw new TypeError('Split every must be a positive integer');
|
|
6
|
+
return groups(source, bytes.subarray(keepPrevious, bytes.length - keepNext), every);
|
|
7
|
+
}
|
|
8
|
+
async function* groups(source, joint, every) {
|
|
9
|
+
let parts = [];
|
|
10
|
+
let sizeInBytes = 0;
|
|
11
|
+
let count = 0;
|
|
12
|
+
let start = 0;
|
|
13
|
+
for await (const record of source) {
|
|
14
|
+
if (count === 0)
|
|
15
|
+
start = record.start;
|
|
16
|
+
else {
|
|
17
|
+
parts.push(joint);
|
|
18
|
+
sizeInBytes += joint.length;
|
|
19
|
+
}
|
|
20
|
+
for (const piece of record.pieces) {
|
|
21
|
+
parts.push(piece);
|
|
22
|
+
sizeInBytes += piece.length;
|
|
23
|
+
}
|
|
24
|
+
if (sizeInBytes > MAX_SLICE_BYTES)
|
|
25
|
+
throw sliceTooLarge(start, sizeInBytes);
|
|
26
|
+
if (++count < every)
|
|
27
|
+
continue;
|
|
28
|
+
yield { start, content: Buffer.concat(parts, sizeInBytes) };
|
|
29
|
+
parts = [];
|
|
30
|
+
sizeInBytes = 0;
|
|
31
|
+
count = 0;
|
|
32
|
+
}
|
|
33
|
+
if (count > 0)
|
|
34
|
+
yield { start, content: Buffer.concat(parts, sizeInBytes) };
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=group-records.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"group-records.js","sourceRoot":"","sources":["../../src/helpers/group-records.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,eAAe,EACf,aAAa,EACb,WAAW,GACZ,MAAM,oBAAoB,CAAA;AAa3B,MAAM,UAAU,YAAY,CAC1B,MAAiC,EACjC,EAAE,SAAS,EAAE,KAAK,GAAG,CAAC,EAAE,QAAQ,GAAG,MAAM,EAAuB;IAEhE,MAAM,EAAE,KAAK,EAAE,YAAY,EAAE,QAAQ,EAAE,GAAG,WAAW,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAA;IAE1E,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,KAAK,GAAG,CAAC;QACvC,MAAM,IAAI,SAAS,CAAC,wCAAwC,CAAC,CAAA;IAE/D,OAAO,MAAM,CACX,MAAM,EACN,KAAK,CAAC,QAAQ,CAAC,YAAY,EAAE,KAAK,CAAC,MAAM,GAAG,QAAQ,CAAC,EACrD,KAAK,CACN,CAAA;AACH,CAAC;AAED,KAAK,SAAS,CAAC,CAAC,MAAM,CACpB,MAAiC,EACjC,KAAa,EACb,KAAa;IAEb,IAAI,KAAK,GAAa,EAAE,CAAA;IACxB,IAAI,WAAW,GAAG,CAAC,CAAA;IACnB,IAAI,KAAK,GAAG,CAAC,CAAA;IACb,IAAI,KAAK,GAAG,CAAC,CAAA;IAEb,IAAI,KAAK,EAAE,MAAM,MAAM,IAAI,MAAM,EAAE,CAAC;QAClC,IAAI,KAAK,KAAK,CAAC;YAAE,KAAK,GAAG,MAAM,CAAC,KAAK,CAAA;aAChC,CAAC;YACJ,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;YAEjB,WAAW,IAAI,KAAK,CAAC,MAAM,CAAA;QAC7B,CAAC;QAED,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;YAClC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;YAEjB,WAAW,IAAI,KAAK,CAAC,MAAM,CAAA;QAC7B,CAAC;QAED,IAAI,WAAW,GAAG,eAAe;YAAE,MAAM,aAAa,CAAC,KAAK,EAAE,WAAW,CAAC,CAAA;QAE1E,IAAI,EAAE,KAAK,GAAG,KAAK;YAAE,SAAQ;QAE7B,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,WAAW,CAAC,EAAE,CAAA;QAE3D,KAAK,GAAG,EAAE,CAAA;QACV,WAAW,GAAG,CAAC,CAAA;QACf,KAAK,GAAG,CAAC,CAAA;IACX,CAAC;IAED,IAAI,KAAK,GAAG,CAAC;QAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,WAAW,CAAC,EAAE,CAAA;AAC5E,CAAC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export type FileSplitDelimiter = {
|
|
2
|
+
value: string;
|
|
3
|
+
keep?: {
|
|
4
|
+
previous?: number;
|
|
5
|
+
next?: number;
|
|
6
|
+
};
|
|
7
|
+
};
|
|
8
|
+
export type ByteTrim = {
|
|
9
|
+
start?: number;
|
|
10
|
+
end?: number;
|
|
11
|
+
};
|
|
12
|
+
export type ByteRecord = {
|
|
13
|
+
start: number;
|
|
14
|
+
pieces: Buffer[];
|
|
15
|
+
};
|
|
16
|
+
export type SplitRecordsOptions = {
|
|
17
|
+
delimiter: FileSplitDelimiter;
|
|
18
|
+
trim?: ByteTrim;
|
|
19
|
+
encoding?: BufferEncoding;
|
|
20
|
+
};
|
|
21
|
+
export declare const MAX_SLICE_BYTES: number;
|
|
22
|
+
export type ByteDelimiter = {
|
|
23
|
+
bytes: Buffer;
|
|
24
|
+
keepPrevious: number;
|
|
25
|
+
keepNext: number;
|
|
26
|
+
};
|
|
27
|
+
export declare function toDelimiter({ value, keep }: FileSplitDelimiter, encoding: BufferEncoding): ByteDelimiter;
|
|
28
|
+
export declare function sliceTooLarge(start: number, sizeInBytes: number): RangeError;
|
|
29
|
+
export declare function splitRecords(source: AsyncIterable<Buffer>, { delimiter, trim, encoding }: SplitRecordsOptions): AsyncGenerator<ByteRecord>;
|
|
30
|
+
//# sourceMappingURL=split-records.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"split-records.d.ts","sourceRoot":"","sources":["../../src/helpers/split-records.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,kBAAkB,GAAG;IAC/B,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE;QACL,QAAQ,CAAC,EAAE,MAAM,CAAA;QACjB,IAAI,CAAC,EAAE,MAAM,CAAA;KACd,CAAA;CACF,CAAA;AAED,MAAM,MAAM,QAAQ,GAAG;IACrB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,GAAG,CAAC,EAAE,MAAM,CAAA;CACb,CAAA;AAED,MAAM,MAAM,UAAU,GAAG;IACvB,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,EAAE,CAAA;CACjB,CAAA;AAED,MAAM,MAAM,mBAAmB,GAAG;IAChC,SAAS,EAAE,kBAAkB,CAAA;IAC7B,IAAI,CAAC,EAAE,QAAQ,CAAA;IACf,QAAQ,CAAC,EAAE,cAAc,CAAA;CAC1B,CAAA;AAED,eAAO,MAAM,eAAe,QAAkB,CAAA;AAE9C,MAAM,MAAM,aAAa,GAAG;IAC1B,KAAK,EAAE,MAAM,CAAA;IACb,YAAY,EAAE,MAAM,CAAA;IACpB,QAAQ,EAAE,MAAM,CAAA;CACjB,CAAA;AAED,wBAAgB,WAAW,CACzB,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,kBAAkB,EACnC,QAAQ,EAAE,cAAc,GACvB,aAAa,CAiBf;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,cAI/D;AAED,wBAAgB,YAAY,CAC1B,MAAM,EAAE,aAAa,CAAC,MAAM,CAAC,EAC7B,EAAE,SAAS,EAAE,IAAI,EAAE,QAAiB,EAAE,EAAE,mBAAmB,GAC1D,cAAc,CAAC,UAAU,CAAC,CAkB5B"}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
export const MAX_SLICE_BYTES = 3 * 1024 * 1024;
|
|
2
|
+
export function toDelimiter({ value, keep }, encoding) {
|
|
3
|
+
const bytes = Buffer.from(value, encoding);
|
|
4
|
+
const keepPrevious = keep?.previous ?? 0;
|
|
5
|
+
const keepNext = keep?.next ?? 0;
|
|
6
|
+
if (bytes.length === 0)
|
|
7
|
+
throw new TypeError('Split delimiter must not be empty');
|
|
8
|
+
if (keepPrevious < 0 || keepNext < 0)
|
|
9
|
+
throw new TypeError('Split delimiter keep sizes must not be negative');
|
|
10
|
+
if (keepPrevious + keepNext > bytes.length)
|
|
11
|
+
throw new TypeError('Split delimiter keep sizes must not exceed the delimiter length');
|
|
12
|
+
return { bytes, keepPrevious, keepNext };
|
|
13
|
+
}
|
|
14
|
+
export function sliceTooLarge(start, sizeInBytes) {
|
|
15
|
+
return new RangeError(`Split slice starting at byte ${start} is larger than ${MAX_SLICE_BYTES} bytes (${sizeInBytes} so far): lower every or check the delimiter`);
|
|
16
|
+
}
|
|
17
|
+
export function splitRecords(source, { delimiter, trim, encoding = 'utf8' }) {
|
|
18
|
+
const cut = toDelimiter(delimiter, encoding);
|
|
19
|
+
const trimStart = trim?.start ?? 0;
|
|
20
|
+
const trimEnd = trim?.end ?? 0;
|
|
21
|
+
if (!Number.isInteger(trimStart) ||
|
|
22
|
+
!Number.isInteger(trimEnd) ||
|
|
23
|
+
trimStart < 0 ||
|
|
24
|
+
trimEnd < 0)
|
|
25
|
+
throw new TypeError('Split trim sizes must be non-negative integers');
|
|
26
|
+
return records(skipLast(skipFirst(source, trimStart), trimEnd), cut, trimStart);
|
|
27
|
+
}
|
|
28
|
+
async function* records(source, { bytes, keepPrevious, keepNext }, offset) {
|
|
29
|
+
let pieces = [];
|
|
30
|
+
let start = offset;
|
|
31
|
+
let consumed = offset;
|
|
32
|
+
let searchAt = offset;
|
|
33
|
+
let tail = Buffer.alloc(0);
|
|
34
|
+
const take = (end) => {
|
|
35
|
+
const taken = [];
|
|
36
|
+
let remaining = end - start;
|
|
37
|
+
while (remaining > 0) {
|
|
38
|
+
const piece = pieces[0];
|
|
39
|
+
if (piece.length <= remaining) {
|
|
40
|
+
taken.push(piece);
|
|
41
|
+
pieces.shift();
|
|
42
|
+
remaining -= piece.length;
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
taken.push(piece.subarray(0, remaining));
|
|
46
|
+
pieces[0] = piece.subarray(remaining);
|
|
47
|
+
remaining = 0;
|
|
48
|
+
}
|
|
49
|
+
start = end;
|
|
50
|
+
return taken;
|
|
51
|
+
};
|
|
52
|
+
for await (const chunk of source) {
|
|
53
|
+
if (chunk.length === 0)
|
|
54
|
+
continue;
|
|
55
|
+
const chunkAt = consumed;
|
|
56
|
+
pieces.push(chunk);
|
|
57
|
+
consumed += chunk.length;
|
|
58
|
+
const matches = [];
|
|
59
|
+
if (tail.length > 0) {
|
|
60
|
+
const probe = Buffer.concat([tail, chunk.subarray(0, bytes.length - 1)]);
|
|
61
|
+
const probeAt = chunkAt - tail.length;
|
|
62
|
+
const index = probe.indexOf(bytes, Math.max(0, searchAt - probeAt));
|
|
63
|
+
if (index !== -1 && probeAt + index < chunkAt) {
|
|
64
|
+
matches.push(probeAt + index);
|
|
65
|
+
searchAt = probeAt + index + bytes.length;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
let index;
|
|
69
|
+
while ((index = chunk.indexOf(bytes, Math.max(0, searchAt - chunkAt))) !== -1) {
|
|
70
|
+
matches.push(chunkAt + index);
|
|
71
|
+
searchAt = chunkAt + index + bytes.length;
|
|
72
|
+
}
|
|
73
|
+
for (const at of matches) {
|
|
74
|
+
const record = { start, pieces: take(at + keepPrevious) };
|
|
75
|
+
take(at + bytes.length - keepNext);
|
|
76
|
+
yield record;
|
|
77
|
+
}
|
|
78
|
+
if (consumed - start > MAX_SLICE_BYTES)
|
|
79
|
+
throw sliceTooLarge(start, consumed - start);
|
|
80
|
+
tail =
|
|
81
|
+
chunk.length >= bytes.length - 1
|
|
82
|
+
? Buffer.from(chunk.subarray(chunk.length - (bytes.length - 1)))
|
|
83
|
+
: Buffer.concat([tail, chunk]).subarray(-(bytes.length - 1));
|
|
84
|
+
}
|
|
85
|
+
const last = { start, pieces };
|
|
86
|
+
pieces = [];
|
|
87
|
+
yield last;
|
|
88
|
+
}
|
|
89
|
+
async function* skipFirst(source, count) {
|
|
90
|
+
let remaining = count;
|
|
91
|
+
for await (const chunk of source) {
|
|
92
|
+
if (remaining >= chunk.length) {
|
|
93
|
+
remaining -= chunk.length;
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
yield chunk.subarray(remaining);
|
|
97
|
+
remaining = 0;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
async function* skipLast(source, count) {
|
|
101
|
+
if (count === 0)
|
|
102
|
+
return yield* source;
|
|
103
|
+
let tail = Buffer.alloc(0);
|
|
104
|
+
for await (const chunk of source) {
|
|
105
|
+
const buffer = Buffer.concat([tail, chunk]);
|
|
106
|
+
const end = Math.max(0, buffer.length - count);
|
|
107
|
+
if (end > 0)
|
|
108
|
+
yield buffer.subarray(0, end);
|
|
109
|
+
tail = buffer.subarray(end);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
//# sourceMappingURL=split-records.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"split-records.js","sourceRoot":"","sources":["../../src/helpers/split-records.ts"],"names":[],"mappings":"AAwBA,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,GAAG,IAAI,GAAG,IAAI,CAAA;AAQ9C,MAAM,UAAU,WAAW,CACzB,EAAE,KAAK,EAAE,IAAI,EAAsB,EACnC,QAAwB;IAExB,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAA;IAC1C,MAAM,YAAY,GAAG,IAAI,EAAE,QAAQ,IAAI,CAAC,CAAA;IACxC,MAAM,QAAQ,GAAG,IAAI,EAAE,IAAI,IAAI,CAAC,CAAA;IAEhC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QACpB,MAAM,IAAI,SAAS,CAAC,mCAAmC,CAAC,CAAA;IAE1D,IAAI,YAAY,GAAG,CAAC,IAAI,QAAQ,GAAG,CAAC;QAClC,MAAM,IAAI,SAAS,CAAC,iDAAiD,CAAC,CAAA;IAExE,IAAI,YAAY,GAAG,QAAQ,GAAG,KAAK,CAAC,MAAM;QACxC,MAAM,IAAI,SAAS,CACjB,iEAAiE,CAClE,CAAA;IAEH,OAAO,EAAE,KAAK,EAAE,YAAY,EAAE,QAAQ,EAAE,CAAA;AAC1C,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,KAAa,EAAE,WAAmB;IAC9D,OAAO,IAAI,UAAU,CACnB,gCAAgC,KAAK,mBAAmB,eAAe,WAAW,WAAW,8CAA8C,CAC5I,CAAA;AACH,CAAC;AAED,MAAM,UAAU,YAAY,CAC1B,MAA6B,EAC7B,EAAE,SAAS,EAAE,IAAI,EAAE,QAAQ,GAAG,MAAM,EAAuB;IAE3D,MAAM,GAAG,GAAG,WAAW,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAA;IAC5C,MAAM,SAAS,GAAG,IAAI,EAAE,KAAK,IAAI,CAAC,CAAA;IAClC,MAAM,OAAO,GAAG,IAAI,EAAE,GAAG,IAAI,CAAC,CAAA;IAE9B,IACE,CAAC,MAAM,CAAC,SAAS,CAAC,SAAS,CAAC;QAC5B,CAAC,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC;QAC1B,SAAS,GAAG,CAAC;QACb,OAAO,GAAG,CAAC;QAEX,MAAM,IAAI,SAAS,CAAC,gDAAgD,CAAC,CAAA;IAEvE,OAAO,OAAO,CACZ,QAAQ,CAAC,SAAS,CAAC,MAAM,EAAE,SAAS,CAAC,EAAE,OAAO,CAAC,EAC/C,GAAG,EACH,SAAS,CACV,CAAA;AACH,CAAC;AAED,KAAK,SAAS,CAAC,CAAC,OAAO,CACrB,MAA6B,EAC7B,EAAE,KAAK,EAAE,YAAY,EAAE,QAAQ,EAAiB,EAChD,MAAc;IAEd,IAAI,MAAM,GAAa,EAAE,CAAA;IACzB,IAAI,KAAK,GAAG,MAAM,CAAA;IAClB,IAAI,QAAQ,GAAG,MAAM,CAAA;IACrB,IAAI,QAAQ,GAAG,MAAM,CAAA;IACrB,IAAI,IAAI,GAAW,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;IAElC,MAAM,IAAI,GAAG,CAAC,GAAW,EAAE,EAAE;QAC3B,MAAM,KAAK,GAAa,EAAE,CAAA;QAE1B,IAAI,SAAS,GAAG,GAAG,GAAG,KAAK,CAAA;QAE3B,OAAO,SAAS,GAAG,CAAC,EAAE,CAAC;YACrB,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,CAAE,CAAA;YAExB,IAAI,KAAK,CAAC,MAAM,IAAI,SAAS,EAAE,CAAC;gBAC9B,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;gBAEjB,MAAM,CAAC,KAAK,EAAE,CAAA;gBAEd,SAAS,IAAI,KAAK,CAAC,MAAM,CAAA;gBAEzB,SAAQ;YACV,CAAC;YAED,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC,CAAA;YAExC,MAAM,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAA;YAErC,SAAS,GAAG,CAAC,CAAA;QACf,CAAC;QAED,KAAK,GAAG,GAAG,CAAA;QAEX,OAAO,KAAK,CAAA;IACd,CAAC,CAAA;IAED,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QACjC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE,SAAQ;QAEhC,MAAM,OAAO,GAAG,QAAQ,CAAA;QAExB,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;QAElB,QAAQ,IAAI,KAAK,CAAC,MAAM,CAAA;QAExB,MAAM,OAAO,GAAa,EAAE,CAAA;QAE5B,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACpB,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC,EAAE,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAA;YACxE,MAAM,OAAO,GAAG,OAAO,GAAG,IAAI,CAAC,MAAM,CAAA;YACrC,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,QAAQ,GAAG,OAAO,CAAC,CAAC,CAAA;YAEnE,IAAI,KAAK,KAAK,CAAC,CAAC,IAAI,OAAO,GAAG,KAAK,GAAG,OAAO,EAAE,CAAC;gBAC9C,OAAO,CAAC,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC,CAAA;gBAE7B,QAAQ,GAAG,OAAO,GAAG,KAAK,GAAG,KAAK,CAAC,MAAM,CAAA;YAC3C,CAAC;QACH,CAAC;QAED,IAAI,KAAa,CAAA;QAEjB,OACE,CAAC,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,QAAQ,GAAG,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EACtE,CAAC;YACD,OAAO,CAAC,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC,CAAA;YAE7B,QAAQ,GAAG,OAAO,GAAG,KAAK,GAAG,KAAK,CAAC,MAAM,CAAA;QAC3C,CAAC;QAED,KAAK,MAAM,EAAE,IAAI,OAAO,EAAE,CAAC;YACzB,MAAM,MAAM,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,GAAG,YAAY,CAAC,EAAE,CAAA;YAEzD,IAAI,CAAC,EAAE,GAAG,KAAK,CAAC,MAAM,GAAG,QAAQ,CAAC,CAAA;YAElC,MAAM,MAAM,CAAA;QACd,CAAC;QAED,IAAI,QAAQ,GAAG,KAAK,GAAG,eAAe;YACpC,MAAM,aAAa,CAAC,KAAK,EAAE,QAAQ,GAAG,KAAK,CAAC,CAAA;QAE9C,IAAI;YACF,KAAK,CAAC,MAAM,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC;gBAC9B,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC;gBAChE,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAA;IAClE,CAAC;IAED,MAAM,IAAI,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,CAAA;IAE9B,MAAM,GAAG,EAAE,CAAA;IAEX,MAAM,IAAI,CAAA;AACZ,CAAC;AAED,KAAK,SAAS,CAAC,CAAC,SAAS,CAAC,MAA6B,EAAE,KAAa;IACpE,IAAI,SAAS,GAAG,KAAK,CAAA;IAErB,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QACjC,IAAI,SAAS,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;YAC9B,SAAS,IAAI,KAAK,CAAC,MAAM,CAAA;YAEzB,SAAQ;QACV,CAAC;QAED,MAAM,KAAK,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAA;QAE/B,SAAS,GAAG,CAAC,CAAA;IACf,CAAC;AACH,CAAC;AAED,KAAK,SAAS,CAAC,CAAC,QAAQ,CAAC,MAA6B,EAAE,KAAa;IACnE,IAAI,KAAK,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC,CAAC,MAAM,CAAA;IAErC,IAAI,IAAI,GAAW,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;IAElC,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QACjC,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,CAAA;QAC3C,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,MAAM,GAAG,KAAK,CAAC,CAAA;QAE9C,IAAI,GAAG,GAAG,CAAC;YAAE,MAAM,MAAM,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAA;QAE1C,IAAI,GAAG,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAA;IAC7B,CAAC;AACH,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@t4h.framework/fs",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.0",
|
|
4
4
|
"description": "File system module for the T4H Framework",
|
|
5
5
|
"homepage": "https://github.com/tech4humans-brasil/framework/tree/main/packages/fs",
|
|
6
6
|
"bugs": "https://github.com/tech4humans-brasil/framework/issues",
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"test:watch": "vitest"
|
|
33
33
|
},
|
|
34
34
|
"devDependencies": {
|
|
35
|
-
"@t4h.framework/core": "^0.
|
|
35
|
+
"@t4h.framework/core": "^0.19.0",
|
|
36
36
|
"typescript": "^5.9.3",
|
|
37
37
|
"vitest": "^4.0.18"
|
|
38
38
|
},
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
import { Transform, type TransformCallback } from 'node:stream';
|
|
2
|
-
export type ByteRange = {
|
|
3
|
-
start: number;
|
|
4
|
-
end: number;
|
|
5
|
-
};
|
|
6
|
-
export type ByteSlice = ByteRange & {
|
|
7
|
-
content?: Buffer;
|
|
8
|
-
};
|
|
9
|
-
export type FileSplitDelimiter = {
|
|
10
|
-
value: string;
|
|
11
|
-
keep?: {
|
|
12
|
-
previous?: number;
|
|
13
|
-
next?: number;
|
|
14
|
-
};
|
|
15
|
-
};
|
|
16
|
-
export type ByteTrim = {
|
|
17
|
-
start?: number;
|
|
18
|
-
end?: number;
|
|
19
|
-
};
|
|
20
|
-
export type ByteDelimiterScannerOptions = {
|
|
21
|
-
delimiter: FileSplitDelimiter;
|
|
22
|
-
every?: number;
|
|
23
|
-
trim?: ByteTrim;
|
|
24
|
-
skipEmpty?: boolean;
|
|
25
|
-
encoding?: BufferEncoding;
|
|
26
|
-
inlineMaxBytes?: number;
|
|
27
|
-
};
|
|
28
|
-
export declare const DEFAULT_INLINE_MAX_BYTES: number;
|
|
29
|
-
export declare class ByteDelimiterScanner extends Transform {
|
|
30
|
-
readonly slices: ByteSlice[];
|
|
31
|
-
private readonly delimiter;
|
|
32
|
-
private readonly keepPrevious;
|
|
33
|
-
private readonly keepNext;
|
|
34
|
-
private readonly cutEvery;
|
|
35
|
-
private readonly trimStart;
|
|
36
|
-
private readonly trimEnd;
|
|
37
|
-
private readonly skipEmpty;
|
|
38
|
-
private readonly inlineMaxBytes;
|
|
39
|
-
private pending;
|
|
40
|
-
private pendingAt;
|
|
41
|
-
private searchAt;
|
|
42
|
-
private sliceStart;
|
|
43
|
-
private matches;
|
|
44
|
-
constructor({ delimiter, every, trim, skipEmpty, encoding, inlineMaxBytes, }: ByteDelimiterScannerOptions);
|
|
45
|
-
_transform(chunk: Buffer, _encoding: BufferEncoding, callback: TransformCallback): void;
|
|
46
|
-
_flush(callback: TransformCallback): void;
|
|
47
|
-
private scan;
|
|
48
|
-
private cut;
|
|
49
|
-
}
|
|
50
|
-
//# sourceMappingURL=ByteDelimiterScanner.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ByteDelimiterScanner.d.ts","sourceRoot":"","sources":["../../src/helpers/ByteDelimiterScanner.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,KAAK,iBAAiB,EAAE,MAAM,aAAa,CAAA;AAE/D,MAAM,MAAM,SAAS,GAAG;IACtB,KAAK,EAAE,MAAM,CAAA;IACb,GAAG,EAAE,MAAM,CAAA;CACZ,CAAA;AAED,MAAM,MAAM,SAAS,GAAG,SAAS,GAAG;IAClC,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB,CAAA;AAED,MAAM,MAAM,kBAAkB,GAAG;IAC/B,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE;QACL,QAAQ,CAAC,EAAE,MAAM,CAAA;QACjB,IAAI,CAAC,EAAE,MAAM,CAAA;KACd,CAAA;CACF,CAAA;AAED,MAAM,MAAM,QAAQ,GAAG;IACrB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,GAAG,CAAC,EAAE,MAAM,CAAA;CACb,CAAA;AAED,MAAM,MAAM,2BAA2B,GAAG;IACxC,SAAS,EAAE,kBAAkB,CAAA;IAC7B,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,IAAI,CAAC,EAAE,QAAQ,CAAA;IACf,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,QAAQ,CAAC,EAAE,cAAc,CAAA;IACzB,cAAc,CAAC,EAAE,MAAM,CAAA;CACxB,CAAA;AAED,eAAO,MAAM,wBAAwB,QAAY,CAAA;AAEjD,qBAAa,oBAAqB,SAAQ,SAAS;IACjD,SAAgB,MAAM,EAAE,SAAS,EAAE,CAAK;IAExC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAQ;IAClC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAQ;IACrC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAQ;IACjC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAQ;IACjC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAQ;IAClC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAQ;IAChC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAQ;IAEvC,OAAO,CAAC,OAAO,CAA0B;IACzC,OAAO,CAAC,SAAS,CAAI;IACrB,OAAO,CAAC,QAAQ,CAAI;IACpB,OAAO,CAAC,UAAU,CAAI;IACtB,OAAO,CAAC,OAAO,CAAI;gBAEP,EACV,SAAS,EACT,KAAS,EACT,IAAI,EACJ,SAAgB,EAChB,QAAiB,EACjB,cAAyC,GAC1C,EAAE,2BAA2B;IAuCvB,UAAU,CACf,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,cAAc,EACzB,QAAQ,EAAE,iBAAiB;IAStB,MAAM,CAAC,QAAQ,EAAE,iBAAiB;IAyBzC,OAAO,CAAC,IAAI;IAyCZ,OAAO,CAAC,GAAG;CAkBZ"}
|
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
import { Transform } from 'node:stream';
|
|
2
|
-
export const DEFAULT_INLINE_MAX_BYTES = 16 * 1024;
|
|
3
|
-
export class ByteDelimiterScanner extends Transform {
|
|
4
|
-
slices = [];
|
|
5
|
-
delimiter;
|
|
6
|
-
keepPrevious;
|
|
7
|
-
keepNext;
|
|
8
|
-
cutEvery;
|
|
9
|
-
trimStart;
|
|
10
|
-
trimEnd;
|
|
11
|
-
skipEmpty;
|
|
12
|
-
inlineMaxBytes;
|
|
13
|
-
pending = Buffer.alloc(0);
|
|
14
|
-
pendingAt = 0;
|
|
15
|
-
searchAt = 0;
|
|
16
|
-
sliceStart = 0;
|
|
17
|
-
matches = 0;
|
|
18
|
-
constructor({ delimiter, every = 1, trim, skipEmpty = true, encoding = 'utf8', inlineMaxBytes = DEFAULT_INLINE_MAX_BYTES, }) {
|
|
19
|
-
super();
|
|
20
|
-
this.delimiter = Buffer.from(delimiter.value, encoding);
|
|
21
|
-
this.keepPrevious = delimiter.keep?.previous ?? 0;
|
|
22
|
-
this.keepNext = delimiter.keep?.next ?? 0;
|
|
23
|
-
this.cutEvery = every;
|
|
24
|
-
this.trimStart = trim?.start ?? 0;
|
|
25
|
-
this.trimEnd = trim?.end ?? 0;
|
|
26
|
-
this.skipEmpty = skipEmpty;
|
|
27
|
-
this.inlineMaxBytes = inlineMaxBytes;
|
|
28
|
-
this.sliceStart = this.trimStart;
|
|
29
|
-
if (this.delimiter.length === 0)
|
|
30
|
-
throw new TypeError('Split delimiter must not be empty');
|
|
31
|
-
if (this.keepPrevious < 0 || this.keepNext < 0)
|
|
32
|
-
throw new TypeError('Split delimiter keep sizes must not be negative');
|
|
33
|
-
if (this.keepPrevious + this.keepNext > this.delimiter.length)
|
|
34
|
-
throw new TypeError('Split delimiter keep sizes must not exceed the delimiter length');
|
|
35
|
-
if (!Number.isInteger(every) || every < 1)
|
|
36
|
-
throw new TypeError('Split every must be a positive integer');
|
|
37
|
-
if (!Number.isInteger(this.trimStart) ||
|
|
38
|
-
!Number.isInteger(this.trimEnd) ||
|
|
39
|
-
this.trimStart < 0 ||
|
|
40
|
-
this.trimEnd < 0)
|
|
41
|
-
throw new TypeError('Split trim sizes must be non-negative integers');
|
|
42
|
-
if (!Number.isInteger(inlineMaxBytes) || inlineMaxBytes < 0)
|
|
43
|
-
throw new TypeError('Split inlineMaxBytes must be a non-negative integer');
|
|
44
|
-
}
|
|
45
|
-
_transform(chunk, _encoding, callback) {
|
|
46
|
-
this.scan(this.pending.length > 0 ? Buffer.concat([this.pending, chunk]) : chunk);
|
|
47
|
-
callback(null, chunk);
|
|
48
|
-
}
|
|
49
|
-
_flush(callback) {
|
|
50
|
-
const limit = this.pendingAt + this.pending.length - this.trimEnd;
|
|
51
|
-
while (true) {
|
|
52
|
-
const last = this.slices.at(-1);
|
|
53
|
-
if (!last || last.end <= limit)
|
|
54
|
-
break;
|
|
55
|
-
if (last.start >= limit) {
|
|
56
|
-
this.slices.pop();
|
|
57
|
-
continue;
|
|
58
|
-
}
|
|
59
|
-
last.end = limit;
|
|
60
|
-
if (last.content)
|
|
61
|
-
last.content = last.content.subarray(0, limit - last.start);
|
|
62
|
-
}
|
|
63
|
-
if (this.sliceStart <= limit)
|
|
64
|
-
this.cut(this.sliceStart, limit, this.pending);
|
|
65
|
-
callback();
|
|
66
|
-
}
|
|
67
|
-
scan(buffer) {
|
|
68
|
-
let searchFrom = Math.max(0, this.searchAt - this.pendingAt);
|
|
69
|
-
while (true) {
|
|
70
|
-
const index = buffer.indexOf(this.delimiter, searchFrom);
|
|
71
|
-
if (index === -1)
|
|
72
|
-
break;
|
|
73
|
-
const at = this.pendingAt + index;
|
|
74
|
-
searchFrom = index + this.delimiter.length;
|
|
75
|
-
if (at < this.trimStart)
|
|
76
|
-
continue;
|
|
77
|
-
this.matches += 1;
|
|
78
|
-
if (this.matches % this.cutEvery !== 0)
|
|
79
|
-
continue;
|
|
80
|
-
this.cut(this.sliceStart, at + this.keepPrevious, buffer);
|
|
81
|
-
this.sliceStart = at + this.delimiter.length - this.keepNext;
|
|
82
|
-
}
|
|
83
|
-
const tail = Math.max(searchFrom, buffer.length - (this.delimiter.length - 1));
|
|
84
|
-
this.searchAt = this.pendingAt + tail;
|
|
85
|
-
const openAt = this.sliceStart - this.pendingAt;
|
|
86
|
-
const isOpenSliceInline = openAt >= 0 && buffer.length - openAt <= this.inlineMaxBytes;
|
|
87
|
-
const keepFrom = isOpenSliceInline ? Math.min(tail, openAt) : tail;
|
|
88
|
-
this.pending = buffer.subarray(keepFrom);
|
|
89
|
-
this.pendingAt += keepFrom;
|
|
90
|
-
}
|
|
91
|
-
cut(start, end, buffer) {
|
|
92
|
-
if (this.skipEmpty && end <= start)
|
|
93
|
-
return;
|
|
94
|
-
const isInline = start >= this.pendingAt && end - start <= this.inlineMaxBytes;
|
|
95
|
-
if (!isInline) {
|
|
96
|
-
this.slices.push({ start, end });
|
|
97
|
-
return;
|
|
98
|
-
}
|
|
99
|
-
const content = Buffer.from(buffer.subarray(start - this.pendingAt, end - this.pendingAt));
|
|
100
|
-
this.slices.push({ start, end, content });
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
//# sourceMappingURL=ByteDelimiterScanner.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ByteDelimiterScanner.js","sourceRoot":"","sources":["../../src/helpers/ByteDelimiterScanner.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import { Transform, type TransformCallback } from 'node:stream';
|
|
2
|
-
import type { ByteTrim, FileSplitDelimiter } from './ByteDelimiterScanner.js';
|
|
3
|
-
export type ByteRecordFilterOptions = {
|
|
4
|
-
delimiter: FileSplitDelimiter;
|
|
5
|
-
accept: (record: Buffer) => boolean;
|
|
6
|
-
trim?: ByteTrim;
|
|
7
|
-
encoding?: BufferEncoding;
|
|
8
|
-
maxRecordBytes?: number;
|
|
9
|
-
};
|
|
10
|
-
export declare const DEFAULT_FILTER_MAX_RECORD_BYTES: number;
|
|
11
|
-
export declare class ByteRecordFilter extends Transform {
|
|
12
|
-
accepted: number;
|
|
13
|
-
private readonly delimiter;
|
|
14
|
-
private readonly joint;
|
|
15
|
-
private readonly keepPrevious;
|
|
16
|
-
private readonly keepNext;
|
|
17
|
-
private readonly trimStart;
|
|
18
|
-
private readonly trimEnd;
|
|
19
|
-
private readonly maxRecordBytes;
|
|
20
|
-
private readonly accept;
|
|
21
|
-
private pending;
|
|
22
|
-
private pendingAt;
|
|
23
|
-
private received;
|
|
24
|
-
private searchAt;
|
|
25
|
-
private recordStart;
|
|
26
|
-
constructor({ delimiter, accept, trim, encoding, maxRecordBytes, }: ByteRecordFilterOptions);
|
|
27
|
-
_transform(chunk: Buffer, _encoding: BufferEncoding, callback: TransformCallback): void;
|
|
28
|
-
_flush(callback: TransformCallback): void;
|
|
29
|
-
private scan;
|
|
30
|
-
private settle;
|
|
31
|
-
}
|
|
32
|
-
//# sourceMappingURL=ByteRecordFilter.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ByteRecordFilter.d.ts","sourceRoot":"","sources":["../../src/helpers/ByteRecordFilter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,KAAK,iBAAiB,EAAE,MAAM,aAAa,CAAA;AAE/D,OAAO,KAAK,EAAE,QAAQ,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAA;AAE7E,MAAM,MAAM,uBAAuB,GAAG;IACpC,SAAS,EAAE,kBAAkB,CAAA;IAC7B,MAAM,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAA;IACnC,IAAI,CAAC,EAAE,QAAQ,CAAA;IACf,QAAQ,CAAC,EAAE,cAAc,CAAA;IACzB,cAAc,CAAC,EAAE,MAAM,CAAA;CACxB,CAAA;AAED,eAAO,MAAM,+BAA+B,QAAc,CAAA;AAE1D,qBAAa,gBAAiB,SAAQ,SAAS;IACtC,QAAQ,SAAI;IAEnB,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAQ;IAClC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAQ;IAC9B,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAQ;IACrC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAQ;IACjC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAQ;IAClC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAQ;IAChC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAQ;IACvC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA6B;IAEpD,OAAO,CAAC,OAAO,CAA0B;IACzC,OAAO,CAAC,SAAS,CAAI;IACrB,OAAO,CAAC,QAAQ,CAAI;IACpB,OAAO,CAAC,QAAQ,CAAQ;IACxB,OAAO,CAAC,WAAW,CAAQ;gBAEf,EACV,SAAS,EACT,MAAM,EACN,IAAI,EACJ,QAAiB,EACjB,cAAgD,GACjD,EAAE,uBAAuB;IAqBnB,UAAU,CACf,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,cAAc,EACzB,QAAQ,EAAE,iBAAiB;IAiBtB,MAAM,CAAC,QAAQ,EAAE,iBAAiB;IAYzC,OAAO,CAAC,IAAI;IA8CZ,OAAO,CAAC,MAAM;CAcf"}
|
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
import { Transform } from 'node:stream';
|
|
2
|
-
export const DEFAULT_FILTER_MAX_RECORD_BYTES = 1024 * 1024;
|
|
3
|
-
export class ByteRecordFilter extends Transform {
|
|
4
|
-
accepted = 0;
|
|
5
|
-
delimiter;
|
|
6
|
-
joint;
|
|
7
|
-
keepPrevious;
|
|
8
|
-
keepNext;
|
|
9
|
-
trimStart;
|
|
10
|
-
trimEnd;
|
|
11
|
-
maxRecordBytes;
|
|
12
|
-
accept;
|
|
13
|
-
pending = Buffer.alloc(0);
|
|
14
|
-
pendingAt = 0;
|
|
15
|
-
received = 0;
|
|
16
|
-
searchAt;
|
|
17
|
-
recordStart;
|
|
18
|
-
constructor({ delimiter, accept, trim, encoding = 'utf8', maxRecordBytes = DEFAULT_FILTER_MAX_RECORD_BYTES, }) {
|
|
19
|
-
super();
|
|
20
|
-
this.delimiter = Buffer.from(delimiter.value, encoding);
|
|
21
|
-
this.keepPrevious = delimiter.keep?.previous ?? 0;
|
|
22
|
-
this.keepNext = delimiter.keep?.next ?? 0;
|
|
23
|
-
this.joint = this.delimiter.subarray(this.keepPrevious, this.delimiter.length - this.keepNext);
|
|
24
|
-
this.trimStart = trim?.start ?? 0;
|
|
25
|
-
this.trimEnd = trim?.end ?? 0;
|
|
26
|
-
this.maxRecordBytes = maxRecordBytes;
|
|
27
|
-
this.accept = accept;
|
|
28
|
-
this.searchAt = this.trimStart;
|
|
29
|
-
this.recordStart = this.trimStart;
|
|
30
|
-
if (this.delimiter.length === 0)
|
|
31
|
-
throw new TypeError('Split delimiter must not be empty');
|
|
32
|
-
}
|
|
33
|
-
_transform(chunk, _encoding, callback) {
|
|
34
|
-
this.pending =
|
|
35
|
-
this.pending.length > 0 ? Buffer.concat([this.pending, chunk]) : chunk;
|
|
36
|
-
this.received += chunk.length;
|
|
37
|
-
try {
|
|
38
|
-
const output = this.scan(this.received - this.trimEnd);
|
|
39
|
-
if (output.length > 0)
|
|
40
|
-
this.push(output);
|
|
41
|
-
callback();
|
|
42
|
-
}
|
|
43
|
-
catch (error) {
|
|
44
|
-
callback(error);
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
_flush(callback) {
|
|
48
|
-
const limit = this.received - this.trimEnd;
|
|
49
|
-
const output = [];
|
|
50
|
-
if (this.recordStart <= limit)
|
|
51
|
-
this.settle(this.recordStart, limit, output);
|
|
52
|
-
if (output.length > 0)
|
|
53
|
-
this.push(Buffer.concat(output));
|
|
54
|
-
callback();
|
|
55
|
-
}
|
|
56
|
-
scan(limit) {
|
|
57
|
-
const output = [];
|
|
58
|
-
while (true) {
|
|
59
|
-
const index = this.pending.indexOf(this.delimiter, this.searchAt - this.pendingAt);
|
|
60
|
-
if (index === -1) {
|
|
61
|
-
this.searchAt = Math.max(this.searchAt, this.pendingAt + this.pending.length - (this.delimiter.length - 1));
|
|
62
|
-
break;
|
|
63
|
-
}
|
|
64
|
-
const at = this.pendingAt + index;
|
|
65
|
-
if (at + this.delimiter.length > limit)
|
|
66
|
-
break;
|
|
67
|
-
this.settle(this.recordStart, at + this.keepPrevious, output);
|
|
68
|
-
this.recordStart = at + this.delimiter.length - this.keepNext;
|
|
69
|
-
this.searchAt = at + this.delimiter.length;
|
|
70
|
-
}
|
|
71
|
-
const end = this.pendingAt + this.pending.length;
|
|
72
|
-
const keepFrom = Math.min(Math.max(Math.min(this.recordStart, this.searchAt), this.pendingAt), end);
|
|
73
|
-
this.pending = this.pending.subarray(keepFrom - this.pendingAt);
|
|
74
|
-
this.pendingAt = keepFrom;
|
|
75
|
-
if (end - this.recordStart > this.maxRecordBytes)
|
|
76
|
-
throw new RangeError(`Split where can only test records up to ${this.maxRecordBytes} bytes, found a record starting at byte ${this.recordStart} without a delimiter`);
|
|
77
|
-
return Buffer.concat(output);
|
|
78
|
-
}
|
|
79
|
-
settle(start, end, output) {
|
|
80
|
-
const record = this.pending.subarray(start - this.pendingAt, end - this.pendingAt);
|
|
81
|
-
if (!this.accept(record))
|
|
82
|
-
return;
|
|
83
|
-
if (this.accepted > 0)
|
|
84
|
-
output.push(this.joint);
|
|
85
|
-
output.push(record);
|
|
86
|
-
this.accepted += 1;
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
//# sourceMappingURL=ByteRecordFilter.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ByteRecordFilter.js","sourceRoot":"","sources":["../../src/helpers/ByteRecordFilter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAA0B,MAAM,aAAa,CAAA;AAY/D,MAAM,CAAC,MAAM,+BAA+B,GAAG,IAAI,GAAG,IAAI,CAAA;AAE1D,MAAM,OAAO,gBAAiB,SAAQ,SAAS;IACtC,QAAQ,GAAG,CAAC,CAAA;IAEF,SAAS,CAAQ;IACjB,KAAK,CAAQ;IACb,YAAY,CAAQ;IACpB,QAAQ,CAAQ;IAChB,SAAS,CAAQ;IACjB,OAAO,CAAQ;IACf,cAAc,CAAQ;IACtB,MAAM,CAA6B;IAE5C,OAAO,GAAW,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;IACjC,SAAS,GAAG,CAAC,CAAA;IACb,QAAQ,GAAG,CAAC,CAAA;IACZ,QAAQ,CAAQ;IAChB,WAAW,CAAQ;IAE3B,YAAY,EACV,SAAS,EACT,MAAM,EACN,IAAI,EACJ,QAAQ,GAAG,MAAM,EACjB,cAAc,GAAG,+BAA+B,GACxB;QACxB,KAAK,EAAE,CAAA;QAEP,IAAI,CAAC,SAAS,GAAG,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAA;QACvD,IAAI,CAAC,YAAY,GAAG,SAAS,CAAC,IAAI,EAAE,QAAQ,IAAI,CAAC,CAAA;QACjD,IAAI,CAAC,QAAQ,GAAG,SAAS,CAAC,IAAI,EAAE,IAAI,IAAI,CAAC,CAAA;QACzC,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,SAAS,CAAC,QAAQ,CAClC,IAAI,CAAC,YAAY,EACjB,IAAI,CAAC,SAAS,CAAC,MAAM,GAAG,IAAI,CAAC,QAAQ,CACtC,CAAA;QACD,IAAI,CAAC,SAAS,GAAG,IAAI,EAAE,KAAK,IAAI,CAAC,CAAA;QACjC,IAAI,CAAC,OAAO,GAAG,IAAI,EAAE,GAAG,IAAI,CAAC,CAAA;QAC7B,IAAI,CAAC,cAAc,GAAG,cAAc,CAAA;QACpC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAA;QACpB,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAA;QAC9B,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,SAAS,CAAA;QAEjC,IAAI,IAAI,CAAC,SAAS,CAAC,MAAM,KAAK,CAAC;YAC7B,MAAM,IAAI,SAAS,CAAC,mCAAmC,CAAC,CAAA;IAC5D,CAAC;IAEM,UAAU,CACf,KAAa,EACb,SAAyB,EACzB,QAA2B;QAE3B,IAAI,CAAC,OAAO;YACV,IAAI,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAA;QACxE,IAAI,CAAC,QAAQ,IAAI,KAAK,CAAC,MAAM,CAAA;QAE7B,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,CAAA;YAEtD,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC;gBAAE,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;YAExC,QAAQ,EAAE,CAAA;QACZ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,QAAQ,CAAC,KAAc,CAAC,CAAA;QAC1B,CAAC;IACH,CAAC;IAEM,MAAM,CAAC,QAA2B;QACvC,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAA;QAE1C,MAAM,MAAM,GAAa,EAAE,CAAA;QAE3B,IAAI,IAAI,CAAC,WAAW,IAAI,KAAK;YAAE,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,WAAW,EAAE,KAAK,EAAE,MAAM,CAAC,CAAA;QAE3E,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC;YAAE,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAA;QAEvD,QAAQ,EAAE,CAAA;IACZ,CAAC;IAEO,IAAI,CAAC,KAAa;QACxB,MAAM,MAAM,GAAa,EAAE,CAAA;QAE3B,OAAO,IAAI,EAAE,CAAC;YACZ,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAChC,IAAI,CAAC,SAAS,EACd,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,SAAS,CAC/B,CAAA;YAED,IAAI,KAAK,KAAK,CAAC,CAAC,EAAE,CAAC;gBACjB,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,GAAG,CACtB,IAAI,CAAC,QAAQ,EACb,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC,CACnE,CAAA;gBAED,MAAK;YACP,CAAC;YAED,MAAM,EAAE,GAAG,IAAI,CAAC,SAAS,GAAG,KAAK,CAAA;YAEjC,IAAI,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,GAAG,KAAK;gBAAE,MAAK;YAE7C,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE,GAAG,IAAI,CAAC,YAAY,EAAE,MAAM,CAAC,CAAA;YAE7D,IAAI,CAAC,WAAW,GAAG,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAA;YAC7D,IAAI,CAAC,QAAQ,GAAG,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,CAAA;QAC5C,CAAC;QAED,MAAM,GAAG,GAAG,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAA;QAEhD,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CACvB,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,QAAQ,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,EACnE,GAAG,CACJ,CAAA;QAED,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,CAAA;QAC/D,IAAI,CAAC,SAAS,GAAG,QAAQ,CAAA;QAEzB,IAAI,GAAG,GAAG,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,cAAc;YAC9C,MAAM,IAAI,UAAU,CAClB,2CAA2C,IAAI,CAAC,cAAc,2CAA2C,IAAI,CAAC,WAAW,sBAAsB,CAChJ,CAAA;QAEH,OAAO,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;IAC9B,CAAC;IAEO,MAAM,CAAC,KAAa,EAAE,GAAW,EAAE,MAAgB;QACzD,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAClC,KAAK,GAAG,IAAI,CAAC,SAAS,EACtB,GAAG,GAAG,IAAI,CAAC,SAAS,CACrB,CAAA;QAED,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC;YAAE,OAAM;QAEhC,IAAI,IAAI,CAAC,QAAQ,GAAG,CAAC;YAAE,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;QAE9C,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;QAEnB,IAAI,CAAC,QAAQ,IAAI,CAAC,CAAA;IACpB,CAAC;CACF"}
|