@t4h.framework/zip 0.2.9 → 0.2.11
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-zip/SKILL.md +55 -126
- package/CHANGELOG.md +29 -0
- package/README.md +15 -189
- package/dist/helpers/ZipChecksum.d.ts.map +1 -1
- package/dist/helpers/ZipChecksum.js +12 -6
- package/dist/helpers/ZipChecksum.js.map +1 -1
- package/dist/helpers/ZipSource.js +1 -1
- package/dist/helpers/ZipSource.js.map +1 -1
- package/dist/helpers/extract-entry.js +1 -1
- package/dist/helpers/extract-entry.js.map +1 -1
- package/dist/helpers/open-archive.js +1 -1
- package/dist/helpers/open-archive.js.map +1 -1
- package/dist/tools/match-path.js +7 -8
- package/dist/tools/match-path.js.map +1 -1
- package/package.json +5 -4
|
@@ -1,67 +1,43 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: framework-zip
|
|
3
3
|
description: >-
|
|
4
|
-
Guides
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
lazy `Binary` that inflates on `stream()` in App code. Prefer this skill
|
|
13
|
-
over `node:zlib` in workflow code and over any hand-written ZIP parsing.
|
|
4
|
+
Guides the use of @t4h.framework/zip to read a ZIP archive in a T4H
|
|
5
|
+
Framework workflow: list() to read the entries without their content, and
|
|
6
|
+
unzip() with include/exclude globs to extract entries into Binary files that
|
|
7
|
+
read, split and sftp.put accept (ZipEntry, ZipEntryInfo, ZipExtractActivity,
|
|
8
|
+
ZipListActivity). Use when a workflow receives a .zip (a compressed batch
|
|
9
|
+
file, a bundle of invoices, a partner return package), when someone asks to
|
|
10
|
+
unzip, decompress or inflate, and when someone is about to use node:zlib or
|
|
11
|
+
write a lazy Binary that inflates on stream() in workflow code.
|
|
14
12
|
---
|
|
15
13
|
|
|
16
14
|
# @t4h.framework/zip
|
|
17
15
|
|
|
18
|
-
|
|
19
|
-
Peer dependencies: `@t4h.framework/core`, `@t4h.framework/fs`.
|
|
16
|
+
## Extract the entry you need
|
|
20
17
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
```
|
|
26
|
-
|
|
27
|
-
Two durable activities, nothing else to wire:
|
|
28
|
-
|
|
29
|
-
- `unzip(archive, options?)` — extracts entries into files, resolves with
|
|
30
|
-
`ZipEntry[]` (`{ path, binary, contentLength, compressedLength,
|
|
31
|
-
compressionMethod, crc32, lastModifiedAt, directory }`), directories
|
|
32
|
-
excluded, in central directory order.
|
|
33
|
-
- `list(archive, options?)` — the same metadata **without** `binary` and
|
|
34
|
-
without transferring content, directories included.
|
|
35
|
-
|
|
36
|
-
`archive` is any `Binary`: `sftp.get`, an HTTP response body, `write`, a slice.
|
|
37
|
-
|
|
38
|
-
## Workflow usage
|
|
39
|
-
|
|
40
|
-
The canonical pipeline — fetch a compressed batch, extract the one file that
|
|
41
|
-
carries the records, fan it out:
|
|
18
|
+
1. Get the archive as a `Binary` (`sftp.get`, an HTTP body, `write`).
|
|
19
|
+
2. Call `unzip` once, high in the workflow, with an `include` glob.
|
|
20
|
+
3. Check the result: no match gives an empty array, not an error.
|
|
21
|
+
4. Pass `entry.binary` down to `read`, `split` or `sftp.put`.
|
|
42
22
|
|
|
43
23
|
```typescript
|
|
44
24
|
import { split, Split } from '@t4h.framework/fs'
|
|
45
25
|
import { unzip } from '@t4h.framework/zip'
|
|
46
26
|
|
|
47
|
-
const
|
|
48
|
-
const archive = await sftp.get(input.path)
|
|
27
|
+
const archive = await sftp.get(input.path)
|
|
49
28
|
|
|
50
|
-
|
|
29
|
+
const [file] = await unzip(archive, { include: 'MOVIMENTO.TXT' })
|
|
51
30
|
|
|
52
|
-
|
|
31
|
+
if (!file) throw new Error(`${input.path} has no movement file`)
|
|
53
32
|
|
|
54
|
-
|
|
55
|
-
delimiter: Split.line(),
|
|
56
|
-
})
|
|
57
|
-
})
|
|
33
|
+
return await split(file.binary, processRecord, { delimiter: Split.line() })
|
|
58
34
|
```
|
|
59
35
|
|
|
60
|
-
|
|
36
|
+
When the decision needs only names, sizes or dates, call `list` first. It does
|
|
37
|
+
not extract content:
|
|
61
38
|
|
|
62
39
|
```typescript
|
|
63
40
|
const entries = await list(archive)
|
|
64
|
-
|
|
65
41
|
const invoice = entries.find(entry => entry.path.endsWith(`/${id}.txt`))
|
|
66
42
|
|
|
67
43
|
if (!invoice) return { status: 'missing' }
|
|
@@ -69,86 +45,39 @@ if (!invoice) return { status: 'missing' }
|
|
|
69
45
|
const [extracted] = await unzip(archive, { include: invoice.path })
|
|
70
46
|
```
|
|
71
47
|
|
|
72
|
-
##
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
`
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
-
|
|
107
|
-
-
|
|
108
|
-
`unzip` with no `include` over a 3 000-entry archive puts 3 000 references in
|
|
109
|
-
the history and 3 000 files in storage. Filter.
|
|
110
|
-
- **What is bounded and what is not.** Entry content, the reads over the
|
|
111
|
-
archive and the history are constant whatever the archive weighs. Memory
|
|
112
|
-
grows with the _number_ of entries — the central directory is read whole,
|
|
113
|
-
50 to 100 bytes per entry — not with their size. A zip bomb costs storage,
|
|
114
|
-
not memory: the read stops at the declared compressed size and the inflate is
|
|
115
|
-
aborted the moment the output goes past the declared uncompressed size, so a
|
|
116
|
-
lying entry never writes more than it declares — one that honestly declares
|
|
117
|
-
40 GB still writes 40 GB, and `list` shows `contentLength` beforehand. A
|
|
118
|
-
non-sliceable source adds one durable copy of the archive, and whether that
|
|
119
|
-
copy streams is the `FileSystemClaim`'s contract, as it is for `split`.
|
|
120
|
-
|
|
121
|
-
## Runtime requirements
|
|
122
|
-
|
|
123
|
-
- Claim provider: `FileSystemClaim` (from `@t4h.framework/fs`).
|
|
124
|
-
- Production allowlist: register `ZipExtractActivity` and `ZipListActivity`
|
|
125
|
-
via `runtime.addActivity`.
|
|
126
|
-
|
|
127
|
-
## Anti-patterns
|
|
128
|
-
|
|
129
|
-
- **Hand-rolling a lazy `Binary` that inflates on `stream()`.** The usual shape
|
|
130
|
-
is a `class ZipEntryBinary extends Binary` in App code that parses the local
|
|
131
|
-
header on demand. It passes locally and breaks in production: a `Binary`
|
|
132
|
-
given to a primitive is the input of a durable activity, serialized to
|
|
133
|
-
storage before the activity runs, and rehydrated in a process where the
|
|
134
|
-
closure no longer exists. Its `contentLength` also has to lie — the real size
|
|
135
|
-
lives in the central directory — so every ranged `read` over it truncates.
|
|
136
|
-
`unzip` does the same work inside an activity; that is the supported way.
|
|
137
|
-
- **Inflating in workflow code.** `createInflateRaw` in a workflow re-runs on
|
|
138
|
-
every replay and holds the content in workflow memory. Extraction belongs to
|
|
139
|
-
an activity.
|
|
140
|
-
- **Calling `unzip` per record.** Extract once, high in the workflow, and pass
|
|
141
|
-
the entry `Binary` down to children. `unzip` inside a loop or inside a helper
|
|
142
|
-
every child calls turns one extraction into thousands of activities.
|
|
143
|
-
- **Extracting to read metadata.** If the run only needs a name, a size or a
|
|
144
|
-
date, `list` answers without transferring content.
|
|
145
|
-
- **Trusting `include` to find something.** It is a filter: no match is an
|
|
146
|
-
empty array, not an error.
|
|
147
|
-
|
|
148
|
-
## Related skills
|
|
149
|
-
|
|
150
|
-
- `framework-fs-split` — `split`/`join`/`read`: what to do with the entry once
|
|
151
|
-
it is a `Binary`.
|
|
152
|
-
- `framework-sftp` — `sftp.get` is where most archives come from.
|
|
153
|
-
- `framework-transforms` — JSON inside the archive: normalize to NDJSON before
|
|
154
|
-
fanning out.
|
|
48
|
+
## Traps
|
|
49
|
+
|
|
50
|
+
- Globs match the whole entry path, case sensitive. `*` and `?` do not cross a
|
|
51
|
+
`/`; `**` does. `'*.csv'` matches only the root; use `'**.csv'` for any
|
|
52
|
+
depth.
|
|
53
|
+
- `unzip` skips directory entries; `list` returns them with
|
|
54
|
+
`directory: true`.
|
|
55
|
+
- `unzip` without `include` extracts every entry: one file in storage and one
|
|
56
|
+
reference in the history for each. Filter.
|
|
57
|
+
- Do not call `unzip` in a loop or in a helper that each child workflow runs.
|
|
58
|
+
Extract once and pass the `Binary` down.
|
|
59
|
+
- `lastModifiedAt` is a string in local time with no time zone
|
|
60
|
+
(`'2024-03-07T14:25:38'`). `contentLength` is the uncompressed size in
|
|
61
|
+
bytes.
|
|
62
|
+
- Only stored and deflate entries. An encrypted entry, another compression
|
|
63
|
+
method or a path with `..`, `/` or a drive letter throws a `TypeError`. A
|
|
64
|
+
bad or corrupted archive throws a `SyntaxError`. The package cannot create a
|
|
65
|
+
ZIP.
|
|
66
|
+
- `@t4h.framework/core/no-io-in-workflow` rejects `node:zlib` in a workflow,
|
|
67
|
+
and `@t4h.framework/core/no-binary-subclass` rejects a `Binary` subclass. Do
|
|
68
|
+
not inflate by hand; use `unzip`. See the `framework-fs` skill for `Binary`.
|
|
69
|
+
- The runtime must provide `FileSystemClaim` and allow `ZipExtractActivity`
|
|
70
|
+
and `ZipListActivity`.
|
|
71
|
+
|
|
72
|
+
## Test
|
|
73
|
+
|
|
74
|
+
`unzip` and `list` run in the test. Commit a real `.zip` fixture and mock
|
|
75
|
+
`FileSystemClaim` with `write` (keep the bytes) and `slice` (return a window).
|
|
76
|
+
Make `slice` throw for a binary the mock did not write to test the fallback
|
|
77
|
+
copy. The cookbook recipe below has a complete test.
|
|
78
|
+
|
|
79
|
+
## Reference
|
|
80
|
+
|
|
81
|
+
- Guide: https://tech4humans-brasil.github.io/framework/zip/
|
|
82
|
+
- Recipe with a test: https://tech4humans-brasil.github.io/framework/cookbook/zip-batch
|
|
83
|
+
- Types: `node_modules/@t4h.framework/zip/dist/zip.d.ts`
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,34 @@
|
|
|
1
1
|
# @t4h.framework/zip
|
|
2
2
|
|
|
3
|
+
## 0.2.11
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#257](https://github.com/tech4humans-brasil/framework/pull/257) [`586bfb4`](https://github.com/tech4humans-brasil/framework/commit/586bfb4663f4a478527ee0c8db360603f034d615) Thanks [@gusteycamargo](https://github.com/gusteycamargo)! - The zip README is now short and links to the site. The docs page now has a testing section and all the errors, and it no longer names the internals of sftp, the dev server or the runtime.
|
|
8
|
+
|
|
9
|
+
## 0.2.10
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- [#241](https://github.com/tech4humans-brasil/framework/pull/241) [`f6b8454`](https://github.com/tech4humans-brasil/framework/commit/f6b845484d22e7c8e7240fe2e113f1cdea7c8ce3) Thanks [@gusteycamargo](https://github.com/gusteycamargo)! - Adds seven rules to `@t4h.framework/core/eslint` and turns them on in `configs.recommended`:
|
|
14
|
+
- `no-io-in-workflow` reports `fetch` and calls to network, file, database and mail modules in a workflow and in the `constructor`, `toInput` and `toOutput` of an activity. It also reports `zlib` and `stream` calls there, because they work on the content in memory on each replay.
|
|
15
|
+
- `no-binary-subclass` reports a class that extends `Binary`.
|
|
16
|
+
- `no-framework-activity-in-app` reports `new` and `extends` on an activity of a framework package other than the core.
|
|
17
|
+
- `no-testing-import-in-source` reports an import of a `@t4h.framework/<package>/testing` entry out of a test file.
|
|
18
|
+
- `require-workflow-id` reports a `Workflow` with no `id`.
|
|
19
|
+
- `no-secret-to-string` reports a `Secret` that becomes a string.
|
|
20
|
+
- `no-literal-secret-in-authorization` reports a string literal in the `secret` or `clientSecret` field of an `Authorization`.
|
|
21
|
+
|
|
22
|
+
The package skills now name these rules in place of the text that the rules enforce. The docs and skills give each `Workflow` an `id`, read the HMAC secret from `EnvDesign`, and do not document a custom HTTP activity.
|
|
23
|
+
|
|
24
|
+
- [#240](https://github.com/tech4humans-brasil/framework/pull/240) [`0606115`](https://github.com/tech4humans-brasil/framework/commit/0606115e2e0baa994e9f0ed3559ee54ecac88e44) Thanks [@gusteycamargo](https://github.com/gusteycamargo)! - The skills do not repeat a rule that a lint rule, `tsc` or a runtime error already enforces. Each such rule is now one line that names the gate. The examples that broke a gate (`process.env`, `as any`, an inline secret, an `EnvDesign` in a workflow file, `ref.output` before `wait()`) are correct now.
|
|
25
|
+
|
|
26
|
+
- [#228](https://github.com/tech4humans-brasil/framework/pull/228) [`d29e1ab`](https://github.com/tech4humans-brasil/framework/commit/d29e1ab29a039b13c3e4b075472bd615566ae75e) Thanks [@gusteycamargo](https://github.com/gusteycamargo)! - Uses exact types and `unknown` in place of `any`, and checks external data with guards.
|
|
27
|
+
- harness: the fix-issues prompt lists each sensor as `- checkTypes: PASS`. Before, it printed `- 0: [object Object]`.
|
|
28
|
+
- hmac: an algorithm name such as `constructor` throws `Unsupported algorithm`.
|
|
29
|
+
- oidc: a discovery response with no `introspection_endpoint`, or a null introspection body, throws a clear error.
|
|
30
|
+
- `HttpRequestActivity.toOutput` takes `HttpRequestActivityOutput` in place of `any`.
|
|
31
|
+
|
|
3
32
|
## 0.2.9
|
|
4
33
|
|
|
5
34
|
## 0.2.8
|
package/README.md
CHANGED
|
@@ -1,208 +1,34 @@
|
|
|
1
1
|
# @t4h.framework/zip
|
|
2
2
|
|
|
3
|
-
Reads ZIP archives
|
|
4
|
-
without
|
|
5
|
-
|
|
6
|
-
consumes.
|
|
3
|
+
Reads ZIP archives in a T4H Framework workflow. `list` tells what an archive
|
|
4
|
+
holds without its content, and `unzip` extracts the entries you select into
|
|
5
|
+
files. Each entry is a `Binary`.
|
|
7
6
|
|
|
8
|
-
|
|
9
|
-
the workflow history, and a replay never re-reads the archive.
|
|
10
|
-
|
|
11
|
-
## Installation
|
|
7
|
+
## Install
|
|
12
8
|
|
|
13
9
|
```bash
|
|
14
10
|
npm install @t4h.framework/zip
|
|
15
|
-
# or
|
|
16
|
-
yarn add @t4h.framework/zip
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
Peer dependencies: `@t4h.framework/core` and `@t4h.framework/fs` — entries are
|
|
20
|
-
written through the `FileSystemClaim`, so a file system provider must be
|
|
21
|
-
registered in the runtime.
|
|
22
|
-
|
|
23
|
-
## Basic usage
|
|
24
|
-
|
|
25
|
-
```typescript
|
|
26
|
-
import { unzip } from '@t4h.framework/zip'
|
|
27
|
-
|
|
28
|
-
const [entry] = await unzip(archive)
|
|
29
|
-
|
|
30
|
-
// entry.binary is a regular Binary: read a window, split it, upload it
|
|
31
|
-
const header = await read(entry.binary, { range: { start: 0, end: 240 } })
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
`archive` is any `Binary`: an `sftp.get`, an HTTP response body, a file written
|
|
35
|
-
by `write`. `unzip` resolves with one `ZipEntry` per file in the archive, in
|
|
36
|
-
central directory order, directories excluded.
|
|
37
|
-
|
|
38
|
-
## Extract only what you need
|
|
39
|
-
|
|
40
|
-
An archive with three thousand invoices in it does not have to become three
|
|
41
|
-
thousand files:
|
|
42
|
-
|
|
43
|
-
```typescript
|
|
44
|
-
const [invoice] = await unzip(archive, { include: 'invoices/0001.txt' })
|
|
45
|
-
|
|
46
|
-
const csvs = await unzip(archive, {
|
|
47
|
-
include: '**.csv',
|
|
48
|
-
exclude: '**/draft-*.csv',
|
|
49
|
-
})
|
|
50
11
|
```
|
|
51
12
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
| Pattern | Matches |
|
|
56
|
-
| -------------- | --------------------------------------- |
|
|
57
|
-
| `notes.txt` | exactly that path |
|
|
58
|
-
| `*.txt` | `.txt` files at the root of the archive |
|
|
59
|
-
| `invoices/*` | anything directly inside `invoices/` |
|
|
60
|
-
| `**.csv` | `.csv` files at any depth |
|
|
61
|
-
| `report-?.txt` | `report-1.txt`, not `report-42.txt` |
|
|
13
|
+
Peer dependencies: `@t4h.framework/core` and `@t4h.framework/fs`. The runtime
|
|
14
|
+
must provide the `FileSystemClaim`.
|
|
62
15
|
|
|
63
|
-
|
|
64
|
-
that matches nothing resolves with an empty array rather than throwing.
|
|
65
|
-
|
|
66
|
-
## Listing without unpacking
|
|
16
|
+
## Usage
|
|
67
17
|
|
|
68
18
|
```typescript
|
|
69
|
-
import {
|
|
19
|
+
import { read } from '@t4h.framework/fs'
|
|
20
|
+
import { list, unzip } from '@t4h.framework/zip'
|
|
70
21
|
|
|
71
22
|
const entries = await list(archive)
|
|
72
|
-
// [{ path, contentLength, compressedLength, compressionMethod,
|
|
73
|
-
// crc32, lastModifiedAt, directory }, ...]
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
`list` reads the archive's index (its central directory), which lives in the
|
|
77
|
-
last few kilobytes of the file — so on a sliceable binary a listing costs about
|
|
78
|
-
a kilobyte, whatever the archive weighs. Use it whenever the decision (does
|
|
79
|
-
this archive contain the file I need? how big is it?) can be made from metadata
|
|
80
|
-
alone, and unzip afterwards only if the content is really needed.
|
|
81
|
-
|
|
82
|
-
Unlike `unzip`, `list` reports directory entries too, flagged with
|
|
83
|
-
`directory: true`.
|
|
84
|
-
|
|
85
|
-
## How the bytes move
|
|
86
23
|
|
|
87
|
-
|
|
24
|
+
const [entry] = await unzip(archive, { include: 'invoices/0001.txt' })
|
|
88
25
|
|
|
89
|
-
|
|
90
|
-
at the end of the file and points backwards at each entry — so every read
|
|
91
|
-
goes through `FileSystemClaim.slice`: the tail for the index, one local
|
|
92
|
-
header per entry, then the entry's compressed range.
|
|
93
|
-
2. **Streaming inflate.** That range is streamed through `inflateRaw` and
|
|
94
|
-
straight into `FileSystemClaim.write`, with backpressure all the way.
|
|
95
|
-
Extracting a thousand entries costs the same memory as extracting one.
|
|
96
|
-
3. **One entry at a time.** Entries are extracted sequentially, on purpose:
|
|
97
|
-
parallel extraction would multiply the memory and the storage pressure for
|
|
98
|
-
no gain on a single source stream.
|
|
99
|
-
4. **Verified content.** Every entry is checked against the CRC-32 and the size
|
|
100
|
-
the archive declares while it flows to storage, so a truncated download or a
|
|
101
|
-
corrupted archive fails the activity instead of quietly producing a shorter
|
|
102
|
-
file.
|
|
103
|
-
|
|
104
|
-
### Binaries that cannot be sliced
|
|
105
|
-
|
|
106
|
-
Ranged reads need a binary the file system claim owns. An `sftp.get` reference
|
|
107
|
-
or an HTTP body is not one: for those, the archive is streamed **once** into
|
|
108
|
-
storage (`zip/<activity-id>.zip`) and the ranges are read from that copy. It
|
|
109
|
-
costs one pass and one durable copy of the archive, and everything downstream
|
|
110
|
-
behaves identically.
|
|
111
|
-
|
|
112
|
-
Nothing has to be configured for this; it is the fallback, not a mode.
|
|
113
|
-
|
|
114
|
-
## Memory: what scales with what
|
|
115
|
-
|
|
116
|
-
Bounded, whatever the archive weighs: entry content (source range →
|
|
117
|
-
`inflateRaw` → CRC-32 → `FileSystemClaim.write`, one entry at a time), the
|
|
118
|
-
reads over the archive (a ranged read of the tail plus one 30-byte local header
|
|
119
|
-
per entry) and the workflow history (references only).
|
|
120
|
-
|
|
121
|
-
What scales, and with what:
|
|
122
|
-
|
|
123
|
-
- **The central directory is read whole**, so memory grows with the _number_ of
|
|
124
|
-
entries — 50 to 100 bytes each. Three thousand entries is 200 KB; a million
|
|
125
|
-
is around 100 MB.
|
|
126
|
-
- **`unzip` without `include` extracts everything**: one file in storage and
|
|
127
|
-
one reference in the history per entry.
|
|
128
|
-
- **A zip bomb costs storage, not memory.** The read stops at the declared
|
|
129
|
-
compressed size, and the inflate is aborted the moment the output goes past
|
|
130
|
-
the declared uncompressed size — an entry that lies never writes more than it
|
|
131
|
-
declares. One that honestly declares 40 GB still writes 40 GB, so check
|
|
132
|
-
`contentLength` with `list` beforehand.
|
|
133
|
-
- **A non-sliceable source** is streamed once into a durable copy of the
|
|
134
|
-
archive; whether that copy streams is the `FileSystemClaim`'s contract.
|
|
135
|
-
- **Downstream is yours.** `buffer(await entry.binary.stream())` pulls the file
|
|
136
|
-
into workflow memory; `read` is capped at 1 MiB and `split` streams.
|
|
137
|
-
|
|
138
|
-
## Never build a lazy `Binary` by hand
|
|
139
|
-
|
|
140
|
-
The temptation, when a workflow needs one entry out of a zip, is to write a
|
|
141
|
-
`Binary` subclass that inflates on `stream()` and pass it around. It works in
|
|
142
|
-
the dev server and breaks in production:
|
|
143
|
-
|
|
144
|
-
- A `Binary` handed to a primitive becomes the **input of a durable activity**,
|
|
145
|
-
and that input is serialized to storage before the activity runs. A class
|
|
146
|
-
holding a closure over another stream has nothing to serialize into.
|
|
147
|
-
- After a replay the activity may run in **another process**, where the object
|
|
148
|
-
no longer exists.
|
|
149
|
-
- `contentLength` has to lie (the uncompressed size is only in the central
|
|
150
|
-
directory), and every consumer that trusts it — a ranged `read`, a
|
|
151
|
-
`Content-Length` header — is then wrong.
|
|
152
|
-
|
|
153
|
-
`unzip` is the supported answer: it does the same work inside an activity, and
|
|
154
|
-
what travels is a file reference the runtime knows how to rehydrate.
|
|
155
|
-
|
|
156
|
-
## Cost model
|
|
157
|
-
|
|
158
|
-
`unzip` and `list` are activities, so each call is one history entry with the
|
|
159
|
-
work behind it done exactly once. The reference to watch is **how many times
|
|
160
|
-
you call them**, not how big the archive is:
|
|
161
|
-
|
|
162
|
-
```typescript
|
|
163
|
-
// once per run, however many records follow
|
|
164
|
-
const [entry] = await unzip(archive, { include: 'MOVIMENTO.TXT' })
|
|
165
|
-
|
|
166
|
-
const batch = await split(entry.binary, processRecord, {
|
|
167
|
-
delimiter: Split.line(),
|
|
168
|
-
})
|
|
26
|
+
const header = await read(entry.binary, { range: { start: 0, end: 240 } })
|
|
169
27
|
```
|
|
170
28
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
with its own history entry and its own copy of the entry in storage. Extract
|
|
174
|
-
high, pass the `Binary` down.
|
|
175
|
-
|
|
176
|
-
## Limits
|
|
177
|
-
|
|
178
|
-
- **Deflate and stored only** (methods `0` and `8`) — the two an archiver
|
|
179
|
-
produces without being asked. Anything else (bzip2, LZMA, zstd) is refused
|
|
180
|
-
with a `TypeError` naming the method.
|
|
181
|
-
- **No encryption.** An encrypted entry is refused; the ZIP crypto formats are
|
|
182
|
-
either broken (ZipCrypto) or vendor-specific (AES extensions).
|
|
183
|
-
- **No writing.** This package reads archives; it does not create them.
|
|
184
|
-
- **Reading, not extracting to disk.** Entries land wherever the
|
|
185
|
-
`FileSystemClaim` puts them, under `zip/<activity-id>/<entry path>`.
|
|
186
|
-
- **Hostile paths are refused.** An entry whose path is absolute, drive
|
|
187
|
-
qualified or contains `..` fails the whole activity before anything is
|
|
188
|
-
written (the "zip slip" class of archive).
|
|
189
|
-
|
|
190
|
-
ZIP64 archives, entries written with a data descriptor (the streaming writers'
|
|
191
|
-
flavour, where the local header has no sizes), UTF-8 and legacy entry names,
|
|
192
|
-
archive comments and empty entries are all handled.
|
|
193
|
-
|
|
194
|
-
## Errors
|
|
195
|
-
|
|
196
|
-
| Situation | Error |
|
|
197
|
-
| ----------------------------------------------- | ------------- |
|
|
198
|
-
| Not a ZIP, or truncated beyond repair | `SyntaxError` |
|
|
199
|
-
| CRC-32 or size mismatch on an entry | `SyntaxError` |
|
|
200
|
-
| Encrypted entry, unsupported compression method | `TypeError` |
|
|
201
|
-
| Entry path escaping the extraction directory | `TypeError` |
|
|
202
|
-
| Multi-disk archive | `TypeError` |
|
|
29
|
+
`archive` is any `Binary`, for example from `sftp.get`, an HTTP response body
|
|
30
|
+
or `write`.
|
|
203
31
|
|
|
204
|
-
##
|
|
32
|
+
## Documentation
|
|
205
33
|
|
|
206
|
-
-
|
|
207
|
-
- Production allowlist: register `ZipExtractActivity` and `ZipListActivity`
|
|
208
|
-
through `runtime.addActivity`.
|
|
34
|
+
https://tech4humans-brasil.github.io/framework/zip/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ZipChecksum.d.ts","sourceRoot":"","sources":["../../src/helpers/ZipChecksum.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,KAAK,iBAAiB,EAAE,MAAM,aAAa,CAAA;AAG/D,MAAM,MAAM,sBAAsB,GAAG;IACnC,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;IACb,aAAa,EAAE,MAAM,CAAA;CACtB,CAAA;AAED,qBAAa,WAAY,SAAQ,SAAS;IAK5B,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAJrC,OAAO,CAAC,QAAQ,CAAI;IAEpB,OAAO,CAAC,MAAM,CAAI;gBAEW,QAAQ,EAAE,sBAAsB;IAI7C,UAAU,CACxB,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,cAAc,EACzB,QAAQ,EAAE,iBAAiB,GAC1B,IAAI;
|
|
1
|
+
{"version":3,"file":"ZipChecksum.d.ts","sourceRoot":"","sources":["../../src/helpers/ZipChecksum.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,KAAK,iBAAiB,EAAE,MAAM,aAAa,CAAA;AAG/D,MAAM,MAAM,sBAAsB,GAAG;IACnC,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;IACb,aAAa,EAAE,MAAM,CAAA;CACtB,CAAA;AAED,qBAAa,WAAY,SAAQ,SAAS;IAK5B,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAJrC,OAAO,CAAC,QAAQ,CAAI;IAEpB,OAAO,CAAC,MAAM,CAAI;gBAEW,QAAQ,EAAE,sBAAsB;IAI7C,UAAU,CACxB,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,cAAc,EACzB,QAAQ,EAAE,iBAAiB,GAC1B,IAAI;IAmBS,MAAM,CAAC,QAAQ,EAAE,iBAAiB,GAAG,IAAI;CAqB1D"}
|
|
@@ -10,18 +10,24 @@ export class ZipChecksum extends Transform {
|
|
|
10
10
|
}
|
|
11
11
|
_transform(chunk, _encoding, callback) {
|
|
12
12
|
const { path, contentLength } = this.expected;
|
|
13
|
-
if (this.length + chunk.length > contentLength)
|
|
14
|
-
|
|
13
|
+
if (this.length + chunk.length > contentLength) {
|
|
14
|
+
callback(new SyntaxError(`ZIP entry "${path}" unpacked past the ${String(contentLength)} bytes the archive declares`));
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
15
17
|
this.checksum = crc32(chunk, this.checksum);
|
|
16
18
|
this.length += chunk.length;
|
|
17
19
|
callback(null, chunk);
|
|
18
20
|
}
|
|
19
21
|
_flush(callback) {
|
|
20
22
|
const { path, crc32: expectedChecksum, contentLength } = this.expected;
|
|
21
|
-
if (this.length !== contentLength)
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
if (this.length !== contentLength) {
|
|
24
|
+
callback(new SyntaxError(`ZIP entry "${path}" unpacked to ${String(this.length)} bytes, the archive declares ${String(contentLength)}`));
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
if (this.checksum !== expectedChecksum) {
|
|
28
|
+
callback(new SyntaxError(`ZIP entry "${path}" failed its CRC-32 check`));
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
25
31
|
callback();
|
|
26
32
|
}
|
|
27
33
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ZipChecksum.js","sourceRoot":"","sources":["../../src/helpers/ZipChecksum.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAA0B,MAAM,aAAa,CAAA;AAC/D,OAAO,EAAE,KAAK,EAAE,MAAM,WAAW,CAAA;AAQjC,MAAM,OAAO,WAAY,SAAQ,SAAS;IAKX;IAJrB,QAAQ,GAAG,CAAC,CAAA;IAEZ,MAAM,GAAG,CAAC,CAAA;IAElB,YAA6B,QAAgC;QAC3D,KAAK,EAAE,CAAA;QADoB,aAAQ,GAAR,QAAQ,CAAwB;IAE7D,CAAC;IAEe,UAAU,CACxB,KAAa,EACb,SAAyB,EACzB,QAA2B;QAE3B,MAAM,EAAE,IAAI,EAAE,aAAa,EAAE,GAAG,IAAI,CAAC,QAAQ,CAAA;QAE7C,IAAI,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC,MAAM,GAAG,aAAa;
|
|
1
|
+
{"version":3,"file":"ZipChecksum.js","sourceRoot":"","sources":["../../src/helpers/ZipChecksum.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAA0B,MAAM,aAAa,CAAA;AAC/D,OAAO,EAAE,KAAK,EAAE,MAAM,WAAW,CAAA;AAQjC,MAAM,OAAO,WAAY,SAAQ,SAAS;IAKX;IAJrB,QAAQ,GAAG,CAAC,CAAA;IAEZ,MAAM,GAAG,CAAC,CAAA;IAElB,YAA6B,QAAgC;QAC3D,KAAK,EAAE,CAAA;QADoB,aAAQ,GAAR,QAAQ,CAAwB;IAE7D,CAAC;IAEe,UAAU,CACxB,KAAa,EACb,SAAyB,EACzB,QAA2B;QAE3B,MAAM,EAAE,IAAI,EAAE,aAAa,EAAE,GAAG,IAAI,CAAC,QAAQ,CAAA;QAE7C,IAAI,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC,MAAM,GAAG,aAAa,EAAE,CAAC;YAC/C,QAAQ,CACN,IAAI,WAAW,CACb,cAAc,IAAI,uBAAuB,MAAM,CAAC,aAAa,CAAC,6BAA6B,CAC5F,CACF,CAAA;YAED,OAAM;QACR,CAAC;QAED,IAAI,CAAC,QAAQ,GAAG,KAAK,CAAC,KAAK,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAA;QAC3C,IAAI,CAAC,MAAM,IAAI,KAAK,CAAC,MAAM,CAAA;QAE3B,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;IACvB,CAAC;IAEe,MAAM,CAAC,QAA2B;QAChD,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,gBAAgB,EAAE,aAAa,EAAE,GAAG,IAAI,CAAC,QAAQ,CAAA;QAEtE,IAAI,IAAI,CAAC,MAAM,KAAK,aAAa,EAAE,CAAC;YAClC,QAAQ,CACN,IAAI,WAAW,CACb,cAAc,IAAI,iBAAiB,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,gCAAgC,MAAM,CAAC,aAAa,CAAC,EAAE,CAC9G,CACF,CAAA;YAED,OAAM;QACR,CAAC;QAED,IAAI,IAAI,CAAC,QAAQ,KAAK,gBAAgB,EAAE,CAAC;YACvC,QAAQ,CAAC,IAAI,WAAW,CAAC,cAAc,IAAI,2BAA2B,CAAC,CAAC,CAAA;YAExE,OAAM;QACR,CAAC;QAED,QAAQ,EAAE,CAAA;IACZ,CAAC;CACF"}
|
|
@@ -24,7 +24,7 @@ export class ZipSource {
|
|
|
24
24
|
}
|
|
25
25
|
async stream(start, end) {
|
|
26
26
|
if (start < 0 || end > this.contentLength)
|
|
27
|
-
throw new SyntaxError(`ZIP archive is truncated: bytes ${start}-${end} are outside a file of ${this.contentLength} bytes`);
|
|
27
|
+
throw new SyntaxError(`ZIP archive is truncated: bytes ${String(start)}-${String(end)} are outside a file of ${String(this.contentLength)} bytes`);
|
|
28
28
|
const slice = await this.slice(start, end);
|
|
29
29
|
return await slice.stream();
|
|
30
30
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ZipSource.js","sourceRoot":"","sources":["../../src/helpers/ZipSource.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAA;AAK9C,MAAM,OAAO,wBAAyB,SAAQ,KAAK;IACrB;IAA5B,YAA4B,MAAe;QACzC,KAAK,CAAC,mDAAmD,CAAC,CAAA;QADhC,WAAM,GAAN,MAAM,CAAS;QAGzC,IAAI,CAAC,IAAI,GAAG,0BAA0B,CAAA;IACxC,CAAC;CACF;AAED,MAAM,OAAO,SAAS;IAED;IACD;IAFlB,YACmB,EAAmB,EACpB,MAAc;QADb,OAAE,GAAF,EAAE,CAAiB;QACpB,WAAM,GAAN,MAAM,CAAQ;IAC7B,CAAC;IAEJ,IAAW,aAAa;QACtB,OAAO,IAAI,CAAC,MAAM,CAAC,aAAa,CAAA;IAClC,CAAC;IAEM,KAAK,CAAC,IAAI,CAAC,KAAa,EAAE,GAAW;QAC1C,IAAI,GAAG,IAAI,KAAK;YAAE,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;QAExC,OAAO,MAAM,MAAM,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,CAAA;IACpD,CAAC;IAEM,KAAK,CAAC,MAAM,CAAC,KAAa,EAAE,GAAW;QAC5C,IAAI,KAAK,GAAG,CAAC,IAAI,GAAG,GAAG,IAAI,CAAC,aAAa;YACvC,MAAM,IAAI,WAAW,CACnB,mCAAmC,KAAK,IAAI,GAAG,0BAA0B,IAAI,CAAC,aAAa,QAAQ,
|
|
1
|
+
{"version":3,"file":"ZipSource.js","sourceRoot":"","sources":["../../src/helpers/ZipSource.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAA;AAK9C,MAAM,OAAO,wBAAyB,SAAQ,KAAK;IACrB;IAA5B,YAA4B,MAAe;QACzC,KAAK,CAAC,mDAAmD,CAAC,CAAA;QADhC,WAAM,GAAN,MAAM,CAAS;QAGzC,IAAI,CAAC,IAAI,GAAG,0BAA0B,CAAA;IACxC,CAAC;CACF;AAED,MAAM,OAAO,SAAS;IAED;IACD;IAFlB,YACmB,EAAmB,EACpB,MAAc;QADb,OAAE,GAAF,EAAE,CAAiB;QACpB,WAAM,GAAN,MAAM,CAAQ;IAC7B,CAAC;IAEJ,IAAW,aAAa;QACtB,OAAO,IAAI,CAAC,MAAM,CAAC,aAAa,CAAA;IAClC,CAAC;IAEM,KAAK,CAAC,IAAI,CAAC,KAAa,EAAE,GAAW;QAC1C,IAAI,GAAG,IAAI,KAAK;YAAE,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;QAExC,OAAO,MAAM,MAAM,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,CAAA;IACpD,CAAC;IAEM,KAAK,CAAC,MAAM,CAAC,KAAa,EAAE,GAAW;QAC5C,IAAI,KAAK,GAAG,CAAC,IAAI,GAAG,GAAG,IAAI,CAAC,aAAa;YACvC,MAAM,IAAI,WAAW,CACnB,mCAAmC,MAAM,CAAC,KAAK,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,0BAA0B,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,QAAQ,CAC5H,CAAA;QAEH,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,GAAG,CAAC,CAAA;QAE1C,OAAO,MAAM,KAAK,CAAC,MAAM,EAAE,CAAA;IAC7B,CAAC;IAEO,KAAK,CAAC,KAAK,CAAC,KAAa,EAAE,GAAW;QAC5C,IAAI,CAAC;YACH,OAAO,MAAM,IAAI,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAA;QACzD,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,IAAI,wBAAwB,CAAC,KAAK,CAAC,CAAA;QAC3C,CAAC;IACH,CAAC;CACF"}
|
|
@@ -25,6 +25,6 @@ function assertReadable(entry) {
|
|
|
25
25
|
throw new TypeError(`ZIP entry "${entry.path}" is encrypted; encrypted archives are not supported`);
|
|
26
26
|
if (entry.compressionMethod !== ZIP_COMPRESSION_STORED &&
|
|
27
27
|
entry.compressionMethod !== ZIP_COMPRESSION_DEFLATE)
|
|
28
|
-
throw new TypeError(`ZIP entry "${entry.path}" uses compression method ${entry.compressionMethod}; only stored (0) and deflate (8) are supported`);
|
|
28
|
+
throw new TypeError(`ZIP entry "${entry.path}" uses compression method ${String(entry.compressionMethod)}; only stored (0) and deflate (8) are supported`);
|
|
29
29
|
}
|
|
30
30
|
//# sourceMappingURL=extract-entry.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"extract-entry.js","sourceRoot":"","sources":["../../src/helpers/extract-entry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAiB,MAAM,aAAa,CAAA;AAClE,OAAO,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAA;AAK5C,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAE9C,OAAO,EACL,gBAAgB,EAChB,uBAAuB,EACvB,sBAAsB,EACtB,kBAAkB,EAClB,uBAAuB,GAExB,MAAM,wBAAwB,CAAA;AAO/B,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,EAAmB,EACnB,MAAiB,EACjB,KAAuB,EACvB,OAA4B;IAE5B,cAAc,CAAC,KAAK,CAAC,CAAA;IAErB,IAAI,KAAK,CAAC,aAAa,KAAK,CAAC;QAC3B,OAAO,MAAM,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,CAAA;IAE3E,MAAM,MAAM,GAAG,gBAAgB,CAC7B,MAAM,MAAM,CAAC,IAAI,CACf,KAAK,CAAC,iBAAiB,EACvB,KAAK,CAAC,iBAAiB,GAAG,uBAAuB,CAClD,CACF,CAAA;IAED,MAAM,KAAK,GAAG,KAAK,CAAC,iBAAiB,GAAG,MAAM,CAAC,MAAM,CAAA;IAErD,MAAM,UAAU,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,KAAK,GAAG,KAAK,CAAC,gBAAgB,CAAC,CAAA;IAE7E,OAAO,MAAM,EAAE,CAAC,KAAK,CACnB,OAAO,CAAC,IAAI,EACZ,OAAO,CAAC,UAAU,EAAE,KAAK,CAAC,EAC1B,OAAO,CAAC,WAAW,CACpB,CAAA;AACH,CAAC;AAED,SAAS,OAAO,CAAC,UAAoB,EAAE,KAAuB;IAC5D,MAAM,QAAQ,GAAG,IAAI,WAAW,CAAC,KAAK,CAAC,CAAA;IAEvC,MAAM,MAAM,GAAG,IAAI,WAAW,EAAE,CAAA;IAEhC,MAAM,MAAM,GACV,KAAK,CAAC,iBAAiB,KAAK,sBAAsB;QAChD,CAAC,CAAC,CAAC,UAAU,EAAE,QAAQ,EAAE,MAAM,CAAC;QAChC,CAAC,CAAC,CAAC,UAAU,EAAE,gBAAgB,EAAE,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAA;IAExD,QAAQ,CAAC,MAAM,EAAE,GAAG,EAAE,GAAE,CAAC,CAAC,CAAA;IAE1B,OAAO,MAAM,CAAA;AACf,CAAC;AAED,SAAS,cAAc,CAAC,KAAuB;IAC7C,IAAI,KAAK,CAAC,KAAK,GAAG,kBAAkB;QAClC,MAAM,IAAI,SAAS,CACjB,cAAc,KAAK,CAAC,IAAI,sDAAsD,CAC/E,CAAA;IAEH,IACE,KAAK,CAAC,iBAAiB,KAAK,sBAAsB;QAClD,KAAK,CAAC,iBAAiB,KAAK,uBAAuB;QAEnD,MAAM,IAAI,SAAS,CACjB,cAAc,KAAK,CAAC,IAAI,6BAA6B,KAAK,CAAC,iBAAiB,iDAAiD,
|
|
1
|
+
{"version":3,"file":"extract-entry.js","sourceRoot":"","sources":["../../src/helpers/extract-entry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAiB,MAAM,aAAa,CAAA;AAClE,OAAO,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAA;AAK5C,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAE9C,OAAO,EACL,gBAAgB,EAChB,uBAAuB,EACvB,sBAAsB,EACtB,kBAAkB,EAClB,uBAAuB,GAExB,MAAM,wBAAwB,CAAA;AAO/B,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,EAAmB,EACnB,MAAiB,EACjB,KAAuB,EACvB,OAA4B;IAE5B,cAAc,CAAC,KAAK,CAAC,CAAA;IAErB,IAAI,KAAK,CAAC,aAAa,KAAK,CAAC;QAC3B,OAAO,MAAM,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,CAAA;IAE3E,MAAM,MAAM,GAAG,gBAAgB,CAC7B,MAAM,MAAM,CAAC,IAAI,CACf,KAAK,CAAC,iBAAiB,EACvB,KAAK,CAAC,iBAAiB,GAAG,uBAAuB,CAClD,CACF,CAAA;IAED,MAAM,KAAK,GAAG,KAAK,CAAC,iBAAiB,GAAG,MAAM,CAAC,MAAM,CAAA;IAErD,MAAM,UAAU,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,KAAK,GAAG,KAAK,CAAC,gBAAgB,CAAC,CAAA;IAE7E,OAAO,MAAM,EAAE,CAAC,KAAK,CACnB,OAAO,CAAC,IAAI,EACZ,OAAO,CAAC,UAAU,EAAE,KAAK,CAAC,EAC1B,OAAO,CAAC,WAAW,CACpB,CAAA;AACH,CAAC;AAED,SAAS,OAAO,CAAC,UAAoB,EAAE,KAAuB;IAC5D,MAAM,QAAQ,GAAG,IAAI,WAAW,CAAC,KAAK,CAAC,CAAA;IAEvC,MAAM,MAAM,GAAG,IAAI,WAAW,EAAE,CAAA;IAEhC,MAAM,MAAM,GACV,KAAK,CAAC,iBAAiB,KAAK,sBAAsB;QAChD,CAAC,CAAC,CAAC,UAAU,EAAE,QAAQ,EAAE,MAAM,CAAC;QAChC,CAAC,CAAC,CAAC,UAAU,EAAE,gBAAgB,EAAE,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAA;IAExD,QAAQ,CAAC,MAAM,EAAE,GAAG,EAAE,GAAE,CAAC,CAAC,CAAA;IAE1B,OAAO,MAAM,CAAA;AACf,CAAC;AAED,SAAS,cAAc,CAAC,KAAuB;IAC7C,IAAI,KAAK,CAAC,KAAK,GAAG,kBAAkB;QAClC,MAAM,IAAI,SAAS,CACjB,cAAc,KAAK,CAAC,IAAI,sDAAsD,CAC/E,CAAA;IAEH,IACE,KAAK,CAAC,iBAAiB,KAAK,sBAAsB;QAClD,KAAK,CAAC,iBAAiB,KAAK,uBAAuB;QAEnD,MAAM,IAAI,SAAS,CACjB,cAAc,KAAK,CAAC,IAAI,6BAA6B,MAAM,CAAC,KAAK,CAAC,iBAAiB,CAAC,iDAAiD,CACtI,CAAA;AACL,CAAC"}
|
|
@@ -21,7 +21,7 @@ export async function openArchive(fs, binary, options) {
|
|
|
21
21
|
async function read(source, encoding) {
|
|
22
22
|
const { contentLength } = source;
|
|
23
23
|
if (contentLength < ZIP_END_OF_CENTRAL_DIRECTORY_LENGTH)
|
|
24
|
-
throw new SyntaxError(`A ZIP archive is at least ${ZIP_END_OF_CENTRAL_DIRECTORY_LENGTH} bytes, this file has ${contentLength}`);
|
|
24
|
+
throw new SyntaxError(`A ZIP archive is at least ${String(ZIP_END_OF_CENTRAL_DIRECTORY_LENGTH)} bytes, this file has ${String(contentLength)}`);
|
|
25
25
|
const end = findEndOfCentralDirectory(await readTail(source));
|
|
26
26
|
const { entryCount, centralDirectoryOffset, centralDirectoryLength } = end.zip64Offset === null
|
|
27
27
|
? end
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"open-archive.js","sourceRoot":"","sources":["../../src/helpers/open-archive.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,wBAAwB,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AACpE,OAAO,EACL,yBAAyB,EACzB,wBAAwB,EACxB,kBAAkB,EAClB,+BAA+B,EAC/B,mCAAmC,EACnC,sBAAsB,EACtB,sBAAsB,EACtB,yCAAyC,EACzC,wBAAwB,GAEzB,MAAM,wBAAwB,CAAA;AAY/B,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,EAAmB,EACnB,MAAc,EACd,OAA2B;IAE3B,IAAI,CAAC;QACH,OAAO,MAAM,IAAI,CAAC,IAAI,SAAS,CAAC,EAAE,EAAE,MAAM,CAAC,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAA;IAChE,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,CAAC,CAAC,KAAK,YAAY,wBAAwB,CAAC;YAAE,MAAM,KAAK,CAAA;IAC/D,CAAC;IAED,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,KAAK,CAC5B,OAAO,CAAC,WAAW,EACnB,MAAM,MAAM,CAAC,MAAM,EAAE,EACrB,iBAAiB,CAClB,CAAA;IAED,IAAI,CAAC;QACH,OAAO,MAAM,IAAI,CAAC,IAAI,SAAS,CAAC,EAAE,EAAE,OAAO,CAAC,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAA;IACjE,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,KAAK,YAAY,wBAAwB;YAC3C,MAAM,IAAI,SAAS,CACjB,iGAAiG,EACjG,EAAE,KAAK,EAAE,KAAK,EAAE,CACjB,CAAA;QAEH,MAAM,KAAK,CAAA;IACb,CAAC;AACH,CAAC;AAED,KAAK,UAAU,IAAI,CACjB,MAAiB,EACjB,QAAwB;IAExB,MAAM,EAAE,aAAa,EAAE,GAAG,MAAM,CAAA;IAEhC,IAAI,aAAa,GAAG,mCAAmC;QACrD,MAAM,IAAI,WAAW,CACnB,6BAA6B,mCAAmC,yBAAyB,aAAa,EAAE,
|
|
1
|
+
{"version":3,"file":"open-archive.js","sourceRoot":"","sources":["../../src/helpers/open-archive.ts"],"names":[],"mappings":"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"}
|
package/dist/tools/match-path.js
CHANGED
|
@@ -8,15 +8,14 @@ function toRegExp(pattern) {
|
|
|
8
8
|
return cached;
|
|
9
9
|
let source = '^';
|
|
10
10
|
for (let index = 0; index < pattern.length; index += 1) {
|
|
11
|
-
const char = pattern
|
|
11
|
+
const char = pattern.charAt(index);
|
|
12
|
+
if (char === '*' && pattern[index + 1] === '*') {
|
|
13
|
+
source += '.*';
|
|
14
|
+
index += 1;
|
|
15
|
+
continue;
|
|
16
|
+
}
|
|
12
17
|
if (char === '*') {
|
|
13
|
-
|
|
14
|
-
source += '.*';
|
|
15
|
-
index += 1;
|
|
16
|
-
}
|
|
17
|
-
else {
|
|
18
|
-
source += '[^/]*';
|
|
19
|
-
}
|
|
18
|
+
source += '[^/]*';
|
|
20
19
|
continue;
|
|
21
20
|
}
|
|
22
21
|
source += char === '?' ? '[^/]' : escape(char);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"match-path.js","sourceRoot":"","sources":["../../src/tools/match-path.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,SAAS,CAAC,IAAY,EAAE,QAA2B;IACjE,OAAO,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAA;AAC/D,CAAC;AAED,MAAM,KAAK,GAAG,IAAI,GAAG,EAAkB,CAAA;AAEvC,SAAS,QAAQ,CAAC,OAAe;IAC/B,MAAM,MAAM,GAAG,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA;IAEjC,IAAI,MAAM;QAAE,OAAO,MAAM,CAAA;IAEzB,IAAI,MAAM,GAAG,GAAG,CAAA;IAEhB,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,OAAO,CAAC,MAAM,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;QACvD,MAAM,IAAI,GAAG,OAAO,CAAC,KAAK,
|
|
1
|
+
{"version":3,"file":"match-path.js","sourceRoot":"","sources":["../../src/tools/match-path.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,SAAS,CAAC,IAAY,EAAE,QAA2B;IACjE,OAAO,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAA;AAC/D,CAAC;AAED,MAAM,KAAK,GAAG,IAAI,GAAG,EAAkB,CAAA;AAEvC,SAAS,QAAQ,CAAC,OAAe;IAC/B,MAAM,MAAM,GAAG,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA;IAEjC,IAAI,MAAM;QAAE,OAAO,MAAM,CAAA;IAEzB,IAAI,MAAM,GAAG,GAAG,CAAA;IAEhB,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,OAAO,CAAC,MAAM,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;QACvD,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;QAElC,IAAI,IAAI,KAAK,GAAG,IAAI,OAAO,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC;YAC/C,MAAM,IAAI,IAAI,CAAA;YACd,KAAK,IAAI,CAAC,CAAA;YAEV,SAAQ;QACV,CAAC;QAED,IAAI,IAAI,KAAK,GAAG,EAAE,CAAC;YACjB,MAAM,IAAI,OAAO,CAAA;YAEjB,SAAQ;QACV,CAAC;QAED,MAAM,IAAI,IAAI,KAAK,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;IAChD,CAAC;IAED,MAAM,MAAM,GAAG,IAAI,MAAM,CAAC,GAAG,MAAM,GAAG,CAAC,CAAA;IAEvC,KAAK,CAAC,GAAG,CAAC,OAAO,EAAE,MAAM,CAAC,CAAA;IAE1B,OAAO,MAAM,CAAA;AACf,CAAC;AAED,SAAS,MAAM,CAAC,IAAY;IAC1B,OAAO,oBAAoB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAA;AAC7D,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@t4h.framework/zip",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.11",
|
|
4
4
|
"description": "ZIP extraction module for the T4H Framework",
|
|
5
5
|
"homepage": "https://github.com/tech4humans-brasil/framework/tree/main/packages/zip",
|
|
6
6
|
"bugs": "https://github.com/tech4humans-brasil/framework/issues",
|
|
@@ -26,14 +26,15 @@
|
|
|
26
26
|
"scripts": {
|
|
27
27
|
"build": "tsc --project tsconfig.build.json",
|
|
28
28
|
"check-types": "tsc --noEmit",
|
|
29
|
-
"
|
|
29
|
+
"check-package": "publint --strict && attw --pack . --profile esm-only",
|
|
30
|
+
"lint": "eslint . --max-warnings 0",
|
|
30
31
|
"prepublishOnly": "yarn build",
|
|
31
32
|
"test": "vitest run",
|
|
32
33
|
"test:watch": "vitest"
|
|
33
34
|
},
|
|
34
35
|
"devDependencies": {
|
|
35
|
-
"@t4h.framework/core": "^0.
|
|
36
|
-
"@t4h.framework/fs": "^0.
|
|
36
|
+
"@t4h.framework/core": "^0.21.0",
|
|
37
|
+
"@t4h.framework/fs": "^0.14.0",
|
|
37
38
|
"typescript": "^5.9.3",
|
|
38
39
|
"vitest": "^4.0.18"
|
|
39
40
|
},
|