@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.
@@ -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 `split` children receive plain strings. Implementations must **compose ranges**: slicing a slice is relative to it, and the stored range is absolute against the underlying file. |
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` persists the durable copy and slices it by ranges; `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. |
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 byte-range
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 **byte-range slices** (never copying slice content), starts one
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 stream **exactly once, worker side**: the same
66
- pass persists a durable copy through `FileSystemClaim.write` while a byte
67
- scanner records slice offsets. No slice content is materialized.
68
- 2. Children receive **range views** over the durable copy
69
- (`FileSystemClaim.slice`); the runtime hydrates each slice only inside that
70
- child's own run — as a plain decoded string when the child's schema is
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 ranges a ranged download will use.
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`), `WorkflowClaim` (with
252
- `startMany`), and `WorkflowBatchClaim` (from `@t4h.framework/core`).
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, FileSystemClaim, Split } from '@t4h.framework/fs'
273
+ import { FileSplitActivity, Split } from '@t4h.framework/fs'
275
274
 
276
- const write = vi.fn(async (_path, readable) => durableBinary) // must consume the stream
277
- const slice = vi.fn((binary, range, encoding) => ({ binary, range, encoding }))
278
- const startMany = vi.fn(async (_wf, inputs) => new WorkflowBatchRef('batch-1', inputs.length))
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
- { provide: FileSystemClaim, value: { write, slice } as never },
283
- { provide: WorkflowClaim, value: { startMany } as never },
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(slice).toHaveBeenCalledWith(durableBinary, { start: 0, end: 3 }, 'utf8')
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 durable copy keeps
305
- the original bytes; slices are views over the durable copy (not the source);
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 byte-range slices, starts one child workflow per slice as
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 design goal is constant memory regardless of file size:
173
+ The source is read once, as a stream:
174
174
 
175
- 1. The source stream is read **exactly once**, worker side. The same pass
176
- persists a durable copy through `FileSystemClaim.write` while a byte
177
- scanner records the slice offsets — no slice content is ever materialized
178
- in the activity.
179
- 2. Children receive **range views** over the durable copy
180
- (`FileSystemClaim.slice`).
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 hydration (default: utf8)
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 tells the runtime how to
258
- hydrate each slice for the child. Offsets are always computed over raw
259
- bytes, so multi-byte encodings never skew the ranges.
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/ByteDelimiterScanner.js';
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 toFilter;
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":"AAEA,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,EACxB,MAAM,oCAAoC,CAAA;AAK3C,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,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,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,CAGrB;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;;;;IA6CzB,OAAO,CAAC,QAAQ;IAkBT,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
+ {"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 { FileSystemClaim } from '../FileSystemClaim.js';
6
- import { ByteDelimiterScanner, } from '../helpers/ByteDelimiterScanner.js';
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 encoding = options.encoding ?? 'utf8';
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
- toFilter(where, { delimiter, trim, encoding = 'utf8' }) {
55
- if (!where)
56
- return;
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":"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"}
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 * from './helpers/ByteDelimiterScanner.js';
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,cAAc,mCAAmC,CAAA"}
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;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":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@t4h.framework/fs",
3
- "version": "0.12.1",
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.17.1",
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":"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"}