kiriya 0.1.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.
Files changed (282) hide show
  1. package/CHANGELOG.md +58 -0
  2. package/LICENSE +21 -0
  3. package/README.md +87 -0
  4. package/dist/src/config/clean-rules.js +39 -0
  5. package/dist/src/config/config-keys.js +24 -0
  6. package/dist/src/config/dependency-directories.js +16 -0
  7. package/dist/src/config/modules.js +38 -0
  8. package/dist/src/config/protected-paths.js +32 -0
  9. package/dist/src/config/rebuildable-directories.js +20 -0
  10. package/dist/src/core/application/command-registry.js +57 -0
  11. package/dist/src/core/application/config-values.js +33 -0
  12. package/dist/src/core/application/path-guard.js +69 -0
  13. package/dist/src/core/application/paths.js +77 -0
  14. package/dist/src/core/application/plugin-loader.js +100 -0
  15. package/dist/src/core/application/process-ending.js +28 -0
  16. package/dist/src/core/application/root-scope.js +123 -0
  17. package/dist/src/core/application/safety.js +26 -0
  18. package/dist/src/core/application/text-input.js +44 -0
  19. package/dist/src/core/application/walk.js +34 -0
  20. package/dist/src/core/domain/command.js +8 -0
  21. package/dist/src/core/domain/encodings.js +70 -0
  22. package/dist/src/core/domain/errors.js +49 -0
  23. package/dist/src/core/domain/glob.js +57 -0
  24. package/dist/src/core/domain/global-options.js +10 -0
  25. package/dist/src/core/domain/input-schema.js +54 -0
  26. package/dist/src/core/domain/message.js +7 -0
  27. package/dist/src/core/domain/module.js +2 -0
  28. package/dist/src/core/domain/names.js +22 -0
  29. package/dist/src/core/domain/ports/clipboard.js +2 -0
  30. package/dist/src/core/domain/ports/clock.js +2 -0
  31. package/dist/src/core/domain/ports/command-catalog.js +2 -0
  32. package/dist/src/core/domain/ports/compression.js +2 -0
  33. package/dist/src/core/domain/ports/config-store.js +2 -0
  34. package/dist/src/core/domain/ports/confirmation.js +2 -0
  35. package/dist/src/core/domain/ports/environment.js +2 -0
  36. package/dist/src/core/domain/ports/file-content.js +2 -0
  37. package/dist/src/core/domain/ports/file-system.js +2 -0
  38. package/dist/src/core/domain/ports/hasher.js +2 -0
  39. package/dist/src/core/domain/ports/network.js +2 -0
  40. package/dist/src/core/domain/ports/opener.js +2 -0
  41. package/dist/src/core/domain/ports/passthrough.js +2 -0
  42. package/dist/src/core/domain/ports/plugin-inventory.js +2 -0
  43. package/dist/src/core/domain/ports/plugin-source.js +2 -0
  44. package/dist/src/core/domain/ports/port-table.js +2 -0
  45. package/dist/src/core/domain/ports/process-runner.js +2 -0
  46. package/dist/src/core/domain/ports/process-table.js +2 -0
  47. package/dist/src/core/domain/ports/protected-paths.js +2 -0
  48. package/dist/src/core/domain/ports/random-source.js +2 -0
  49. package/dist/src/core/domain/ports/standard-input.js +2 -0
  50. package/dist/src/core/domain/ports/system-info.js +2 -0
  51. package/dist/src/core/domain/ports/trash.js +2 -0
  52. package/dist/src/core/domain/secrets.js +59 -0
  53. package/dist/src/core/domain/targets.js +41 -0
  54. package/dist/src/core/domain/text-case.js +39 -0
  55. package/dist/src/core/domain/values/bytes.js +13 -0
  56. package/dist/src/core/domain/view.js +2 -0
  57. package/dist/src/core/infrastructure/node/can-listen.js +10 -0
  58. package/dist/src/core/infrastructure/node/closed-standard-input.adapter.js +12 -0
  59. package/dist/src/core/infrastructure/node/config-location.js +27 -0
  60. package/dist/src/core/infrastructure/node/fs-errors.js +22 -0
  61. package/dist/src/core/infrastructure/node/json-config-store.adapter.js +69 -0
  62. package/dist/src/core/infrastructure/node/node-compression.adapter.js +82 -0
  63. package/dist/src/core/infrastructure/node/node-environment.adapter.js +21 -0
  64. package/dist/src/core/infrastructure/node/node-file-content.adapter.js +99 -0
  65. package/dist/src/core/infrastructure/node/node-file-system.adapter.js +169 -0
  66. package/dist/src/core/infrastructure/node/node-hasher.adapter.js +32 -0
  67. package/dist/src/core/infrastructure/node/node-network.adapter.js +168 -0
  68. package/dist/src/core/infrastructure/node/node-plugin-source.adapter.js +89 -0
  69. package/dist/src/core/infrastructure/node/node-process-runner.adapter.js +48 -0
  70. package/dist/src/core/infrastructure/node/node-random-source.adapter.js +7 -0
  71. package/dist/src/core/infrastructure/node/node-standard-input.adapter.js +24 -0
  72. package/dist/src/core/infrastructure/node/node-system-info.adapter.js +63 -0
  73. package/dist/src/core/infrastructure/node/process-signals.js +51 -0
  74. package/dist/src/core/infrastructure/node/run-program.js +65 -0
  75. package/dist/src/core/infrastructure/node/system-clock.adapter.js +22 -0
  76. package/dist/src/core/infrastructure/node/typed-programs.js +45 -0
  77. package/dist/src/core/infrastructure/platform/linux/freedesktop-trash.adapter.js +115 -0
  78. package/dist/src/core/infrastructure/platform/linux/linux-clipboard.adapter.js +70 -0
  79. package/dist/src/core/infrastructure/platform/linux/linux-opener.adapter.js +21 -0
  80. package/dist/src/core/infrastructure/platform/linux/linux-port-table.adapter.js +97 -0
  81. package/dist/src/core/infrastructure/platform/linux/linux-process-table.adapter.js +61 -0
  82. package/dist/src/core/infrastructure/platform/macos/macos-clipboard.adapter.js +29 -0
  83. package/dist/src/core/infrastructure/platform/macos/macos-opener.adapter.js +13 -0
  84. package/dist/src/core/infrastructure/platform/macos/macos-port-table.adapter.js +86 -0
  85. package/dist/src/core/infrastructure/platform/macos/macos-process-table.adapter.js +62 -0
  86. package/dist/src/core/infrastructure/platform/macos/macos-trash.adapter.js +64 -0
  87. package/dist/src/core/infrastructure/platform/windows/system32.js +6 -0
  88. package/dist/src/core/infrastructure/platform/windows/windows-clipboard.adapter.js +50 -0
  89. package/dist/src/core/infrastructure/platform/windows/windows-opener.adapter.js +11 -0
  90. package/dist/src/core/infrastructure/platform/windows/windows-port-table.adapter.js +38 -0
  91. package/dist/src/core/infrastructure/platform/windows/windows-process-table.adapter.js +134 -0
  92. package/dist/src/core/infrastructure/platform/windows/windows-trash.adapter.js +86 -0
  93. package/dist/src/core/presentation/cli/argv.js +83 -0
  94. package/dist/src/core/presentation/cli/cli-application.js +201 -0
  95. package/dist/src/core/presentation/cli/cli-view-format.js +46 -0
  96. package/dist/src/core/presentation/cli/help.js +202 -0
  97. package/dist/src/core/presentation/cli/json-output.js +28 -0
  98. package/dist/src/core/presentation/cli/style.js +21 -0
  99. package/dist/src/core/presentation/cli/suggest.js +25 -0
  100. package/dist/src/core/presentation/cli/terminal-confirmation.js +63 -0
  101. package/dist/src/core/presentation/ended-processes.js +18 -0
  102. package/dist/src/core/presentation/i18n/translator.js +23 -0
  103. package/dist/src/core/presentation/list-preview.js +10 -0
  104. package/dist/src/core/presentation/mcp/elicitation.js +55 -0
  105. package/dist/src/core/presentation/mcp/mcp-application.js +60 -0
  106. package/dist/src/core/presentation/mcp/mcp-confirmation.js +59 -0
  107. package/dist/src/core/presentation/mcp/mcp-server.js +355 -0
  108. package/dist/src/core/presentation/mcp/protocol.js +32 -0
  109. package/dist/src/core/presentation/mcp/request-state.js +66 -0
  110. package/dist/src/core/presentation/mcp/tool-definitions.js +149 -0
  111. package/dist/src/core/presentation/mcp/tool-results.js +56 -0
  112. package/dist/src/i18n/locales/en.js +827 -0
  113. package/dist/src/main.js +154 -0
  114. package/dist/src/modules/archive/application/archive-sources.js +55 -0
  115. package/dist/src/modules/archive/application/create-tar.use-case.js +123 -0
  116. package/dist/src/modules/archive/application/create-zip.use-case.js +106 -0
  117. package/dist/src/modules/archive/application/extract-tar.use-case.js +240 -0
  118. package/dist/src/modules/archive/application/extract-zip.use-case.js +181 -0
  119. package/dist/src/modules/archive/application/tar-reader.js +45 -0
  120. package/dist/src/modules/archive/application/zip-reader.js +57 -0
  121. package/dist/src/modules/archive/archive.module.js +27 -0
  122. package/dist/src/modules/archive/domain/crc32.js +18 -0
  123. package/dist/src/modules/archive/domain/tar-format.js +97 -0
  124. package/dist/src/modules/archive/domain/tar-stream.js +223 -0
  125. package/dist/src/modules/archive/domain/zip-format.js +158 -0
  126. package/dist/src/modules/archive/presentation/tar.view.js +17 -0
  127. package/dist/src/modules/archive/presentation/untar.view.js +30 -0
  128. package/dist/src/modules/archive/presentation/unzip.view.js +24 -0
  129. package/dist/src/modules/archive/presentation/zip.view.js +17 -0
  130. package/dist/src/modules/clip/application/copy-text.use-case.js +43 -0
  131. package/dist/src/modules/clip/application/paste-text.use-case.js +25 -0
  132. package/dist/src/modules/clip/clip.module.js +16 -0
  133. package/dist/src/modules/clip/presentation/clip.views.js +7 -0
  134. package/dist/src/modules/completion/application/print-script.use-case.js +38 -0
  135. package/dist/src/modules/completion/application/suggest-words.use-case.js +37 -0
  136. package/dist/src/modules/completion/completion.module.js +14 -0
  137. package/dist/src/modules/completion/domain/scripts.js +91 -0
  138. package/dist/src/modules/completion/domain/suggest.js +134 -0
  139. package/dist/src/modules/completion/presentation/completion.views.js +6 -0
  140. package/dist/src/modules/config/application/config-commands.js +14 -0
  141. package/dist/src/modules/config/application/get-setting.use-case.js +25 -0
  142. package/dist/src/modules/config/application/list-config-keys.use-case.js +26 -0
  143. package/dist/src/modules/config/application/list-settings.use-case.js +28 -0
  144. package/dist/src/modules/config/application/set-setting.use-case.js +53 -0
  145. package/dist/src/modules/config/application/show-config-path.use-case.js +21 -0
  146. package/dist/src/modules/config/application/unset-setting.use-case.js +28 -0
  147. package/dist/src/modules/config/config.module.js +28 -0
  148. package/dist/src/modules/config/presentation/config.views.js +33 -0
  149. package/dist/src/modules/convert/application/convert-case.use-case.js +38 -0
  150. package/dist/src/modules/convert/application/convert-codecs.use-case.js +92 -0
  151. package/dist/src/modules/convert/application/convert-input.js +16 -0
  152. package/dist/src/modules/convert/application/convert-json.use-case.js +51 -0
  153. package/dist/src/modules/convert/application/convert-time.use-case.js +39 -0
  154. package/dist/src/modules/convert/application/decode-jwt.use-case.js +52 -0
  155. package/dist/src/modules/convert/convert.module.js +29 -0
  156. package/dist/src/modules/convert/domain/json-syntax.js +135 -0
  157. package/dist/src/modules/convert/domain/jwt.js +29 -0
  158. package/dist/src/modules/convert/domain/moments.js +20 -0
  159. package/dist/src/modules/convert/presentation/convert.views.js +44 -0
  160. package/dist/src/modules/docker/application/clean-engine.use-case.js +77 -0
  161. package/dist/src/modules/docker/application/compose-project.js +45 -0
  162. package/dist/src/modules/docker/application/docker-engine.js +40 -0
  163. package/dist/src/modules/docker/application/list-containers.use-case.js +69 -0
  164. package/dist/src/modules/docker/application/run-compose.use-case.js +83 -0
  165. package/dist/src/modules/docker/application/show-logs.use-case.js +51 -0
  166. package/dist/src/modules/docker/application/stop-project.use-case.js +56 -0
  167. package/dist/src/modules/docker/docker.module.js +23 -0
  168. package/dist/src/modules/docker/domain/compose-file.js +105 -0
  169. package/dist/src/modules/docker/domain/engine-output.js +65 -0
  170. package/dist/src/modules/docker/presentation/docker.views.js +63 -0
  171. package/dist/src/modules/doctor/application/run-checks.use-case.js +128 -0
  172. package/dist/src/modules/doctor/doctor.module.js +13 -0
  173. package/dist/src/modules/doctor/domain/versions.js +22 -0
  174. package/dist/src/modules/doctor/presentation/doctor.view.js +30 -0
  175. package/dist/src/modules/env/application/check-dotenv.use-case.js +116 -0
  176. package/dist/src/modules/env/application/inspect-path.use-case.js +71 -0
  177. package/dist/src/modules/env/application/path-entries.js +28 -0
  178. package/dist/src/modules/env/application/show-variables.use-case.js +47 -0
  179. package/dist/src/modules/env/domain/dotenv.js +62 -0
  180. package/dist/src/modules/env/env.module.js +17 -0
  181. package/dist/src/modules/env/presentation/env.views.js +43 -0
  182. package/dist/src/modules/files/application/clean-builds.use-case.js +149 -0
  183. package/dist/src/modules/files/application/compare-paths.use-case.js +127 -0
  184. package/dist/src/modules/files/application/copy-paths.use-case.js +101 -0
  185. package/dist/src/modules/files/application/create-paths.use-case.js +85 -0
  186. package/dist/src/modules/files/application/delete-paths.use-case.js +127 -0
  187. package/dist/src/modules/files/application/find-duplicates.use-case.js +105 -0
  188. package/dist/src/modules/files/application/find-files.use-case.js +131 -0
  189. package/dist/src/modules/files/application/hash-files.use-case.js +73 -0
  190. package/dist/src/modules/files/application/list-entries.use-case.js +87 -0
  191. package/dist/src/modules/files/application/measure-folders.use-case.js +102 -0
  192. package/dist/src/modules/files/application/move-entry.js +19 -0
  193. package/dist/src/modules/files/application/move-paths.use-case.js +82 -0
  194. package/dist/src/modules/files/application/read-text.use-case.js +92 -0
  195. package/dist/src/modules/files/application/rename-paths.use-case.js +184 -0
  196. package/dist/src/modules/files/application/replace-text.use-case.js +146 -0
  197. package/dist/src/modules/files/application/search-text.use-case.js +115 -0
  198. package/dist/src/modules/files/application/show-info.use-case.js +80 -0
  199. package/dist/src/modules/files/application/show-tree.use-case.js +75 -0
  200. package/dist/src/modules/files/application/sync-folders.use-case.js +162 -0
  201. package/dist/src/modules/files/application/text-files.js +46 -0
  202. package/dist/src/modules/files/application/transfers.js +35 -0
  203. package/dist/src/modules/files/domain/case-style.js +18 -0
  204. package/dist/src/modules/files/domain/filters.js +9 -0
  205. package/dist/src/modules/files/domain/sync-plan.js +35 -0
  206. package/dist/src/modules/files/domain/text-encoding.js +75 -0
  207. package/dist/src/modules/files/domain/values.js +28 -0
  208. package/dist/src/modules/files/files.module.js +70 -0
  209. package/dist/src/modules/files/presentation/clean.view.js +15 -0
  210. package/dist/src/modules/files/presentation/compare.view.js +50 -0
  211. package/dist/src/modules/files/presentation/copy.view.js +17 -0
  212. package/dist/src/modules/files/presentation/delete.view.js +16 -0
  213. package/dist/src/modules/files/presentation/dupes.view.js +20 -0
  214. package/dist/src/modules/files/presentation/find.view.js +16 -0
  215. package/dist/src/modules/files/presentation/grep.view.js +27 -0
  216. package/dist/src/modules/files/presentation/hash.view.js +15 -0
  217. package/dist/src/modules/files/presentation/info.view.js +36 -0
  218. package/dist/src/modules/files/presentation/list.view.js +17 -0
  219. package/dist/src/modules/files/presentation/move.view.js +14 -0
  220. package/dist/src/modules/files/presentation/new.view.js +8 -0
  221. package/dist/src/modules/files/presentation/read.view.js +6 -0
  222. package/dist/src/modules/files/presentation/rename.view.js +16 -0
  223. package/dist/src/modules/files/presentation/replace.view.js +26 -0
  224. package/dist/src/modules/files/presentation/size.view.js +24 -0
  225. package/dist/src/modules/files/presentation/sync.view.js +37 -0
  226. package/dist/src/modules/files/presentation/tree.view.js +22 -0
  227. package/dist/src/modules/gen/application/gen-options.js +18 -0
  228. package/dist/src/modules/gen/application/generate-passwords.use-case.js +39 -0
  229. package/dist/src/modules/gen/application/generate-tokens.use-case.js +38 -0
  230. package/dist/src/modules/gen/application/generate-ulids.use-case.js +40 -0
  231. package/dist/src/modules/gen/application/generate-uuids.use-case.js +32 -0
  232. package/dist/src/modules/gen/domain/identifiers.js +65 -0
  233. package/dist/src/modules/gen/domain/secrets.js +43 -0
  234. package/dist/src/modules/gen/gen.module.js +20 -0
  235. package/dist/src/modules/git/application/fetch-repositories.use-case.js +50 -0
  236. package/dist/src/modules/git/application/git-client.js +62 -0
  237. package/dist/src/modules/git/application/pull-repositories.use-case.js +67 -0
  238. package/dist/src/modules/git/application/repositories.js +67 -0
  239. package/dist/src/modules/git/application/show-status.use-case.js +49 -0
  240. package/dist/src/modules/git/application/switch-branch.use-case.js +118 -0
  241. package/dist/src/modules/git/domain/repository-status.js +37 -0
  242. package/dist/src/modules/git/git.module.js +25 -0
  243. package/dist/src/modules/git/presentation/git.views.js +100 -0
  244. package/dist/src/modules/net/application/check-connection.use-case.js +71 -0
  245. package/dist/src/modules/net/application/list-addresses.use-case.js +29 -0
  246. package/dist/src/modules/net/application/lookup-name.use-case.js +55 -0
  247. package/dist/src/modules/net/net.module.js +17 -0
  248. package/dist/src/modules/net/presentation/net.views.js +39 -0
  249. package/dist/src/modules/open/application/open-target.use-case.js +53 -0
  250. package/dist/src/modules/open/domain/targets.js +45 -0
  251. package/dist/src/modules/open/open.module.js +12 -0
  252. package/dist/src/modules/open/presentation/open.view.js +6 -0
  253. package/dist/src/modules/port/application/end-port-owners.use-case.js +68 -0
  254. package/dist/src/modules/port/application/find-free-port.use-case.js +40 -0
  255. package/dist/src/modules/port/application/find-listeners.use-case.js +34 -0
  256. package/dist/src/modules/port/application/port-owners.js +25 -0
  257. package/dist/src/modules/port/domain/port-number.js +8 -0
  258. package/dist/src/modules/port/port.module.js +17 -0
  259. package/dist/src/modules/port/presentation/port.views.js +27 -0
  260. package/dist/src/modules/proc/application/end-processes.use-case.js +77 -0
  261. package/dist/src/modules/proc/application/find-processes.use-case.js +50 -0
  262. package/dist/src/modules/proc/application/list-processes.use-case.js +41 -0
  263. package/dist/src/modules/proc/application/show-tree.use-case.js +40 -0
  264. package/dist/src/modules/proc/domain/process-tree.js +55 -0
  265. package/dist/src/modules/proc/presentation/proc.views.js +28 -0
  266. package/dist/src/modules/proc/proc.module.js +19 -0
  267. package/dist/src/modules/sys/application/create-report.use-case.js +30 -0
  268. package/dist/src/modules/sys/application/find-tools.use-case.js +20 -0
  269. package/dist/src/modules/sys/application/machine-facts.js +44 -0
  270. package/dist/src/modules/sys/application/show-info.use-case.js +29 -0
  271. package/dist/src/modules/sys/domain/tools.js +17 -0
  272. package/dist/src/modules/sys/presentation/sys.views.js +67 -0
  273. package/dist/src/modules/sys/sys.module.js +17 -0
  274. package/dist/src/modules/wait/application/poll.js +26 -0
  275. package/dist/src/modules/wait/application/wait-file.use-case.js +55 -0
  276. package/dist/src/modules/wait/application/wait-port.use-case.js +76 -0
  277. package/dist/src/modules/wait/application/wait-url.use-case.js +71 -0
  278. package/dist/src/modules/wait/domain/reasons.js +14 -0
  279. package/dist/src/modules/wait/domain/wait-input.js +50 -0
  280. package/dist/src/modules/wait/presentation/wait.views.js +31 -0
  281. package/dist/src/modules/wait/wait.module.js +21 -0
  282. package/package.json +57 -0
@@ -0,0 +1,181 @@
1
+ import path from "node:path";
2
+ import { isInside } from "../../../core/application/paths.js";
3
+ import { requireTypedConfirmation } from "../../../core/application/safety.js";
4
+ import { done } from "../../../core/domain/command.js";
5
+ import { ConflictError, KiriyaError, NotFoundError } from "../../../core/domain/errors.js";
6
+ import { RawReader } from "../../../core/domain/input-schema.js";
7
+ import { message } from "../../../core/domain/message.js";
8
+ import { invalidNameReason } from "../../../core/domain/names.js";
9
+ import { safeEntrySegments } from "../domain/zip-format.js";
10
+ import { ZipReader } from "./zip-reader.js";
11
+ /** Names listed per kind of problem before the rest are counted. */
12
+ const REPORT_LIMIT = 20;
13
+ export const unzipSpec = {
14
+ id: "archive.unzip",
15
+ summary: "archive.unzip.summary",
16
+ examples: [
17
+ "kiriya archive unzip release.zip",
18
+ "kiriya archive unzip release.zip --list",
19
+ "kiriya archive unzip release.zip --to vendor/tool --overwrite",
20
+ ],
21
+ safety: "destroy",
22
+ idempotent: false,
23
+ usesNetwork: false,
24
+ runsUserCommands: false,
25
+ input: {
26
+ positionals: [
27
+ { name: "archive", description: "archive.unzip.arg.archive", required: true, variadic: false, path: true },
28
+ ],
29
+ options: {
30
+ to: { type: "string", description: "archive.unzip.option.to", valueName: "<folder>", path: true },
31
+ list: { type: "boolean", description: "archive.unzip.option.list" },
32
+ overwrite: { type: "boolean", description: "archive.unzip.option.overwrite" },
33
+ confirm: {
34
+ type: "string",
35
+ description: "archive.unzip.option.confirm",
36
+ valueName: "<count>",
37
+ terminalOnly: true,
38
+ },
39
+ },
40
+ parse(raw) {
41
+ const reader = new RawReader(raw);
42
+ return {
43
+ archive: reader.positional(0) ?? "",
44
+ to: reader.string("to"),
45
+ list: reader.flag("list"),
46
+ overwrite: reader.flag("overwrite"),
47
+ confirm: reader.string("confirm"),
48
+ };
49
+ },
50
+ },
51
+ };
52
+ export class ExtractZip {
53
+ fileSystem;
54
+ content;
55
+ compression;
56
+ spec = unzipSpec;
57
+ constructor(fileSystem, content, compression) {
58
+ this.fileSystem = fileSystem;
59
+ this.content = content;
60
+ this.compression = compression;
61
+ }
62
+ async execute(input, context) {
63
+ const archive = path.resolve(context.cwd, input.archive);
64
+ if ((await this.fileSystem.lstat(archive)) === null)
65
+ throw new NotFoundError("core.fs.not-found", { path: archive });
66
+ const reader = await ZipReader.open(this.content, this.compression, archive);
67
+ try {
68
+ if (input.list) {
69
+ const entries = reader.entries.map((entry) => ({
70
+ name: entry.name,
71
+ isDirectory: entry.isDirectory,
72
+ size: entry.size,
73
+ compressedSize: entry.compressedSize,
74
+ modifiedMs: entry.modifiedMs,
75
+ }));
76
+ return done({ mode: "list", archive, entries, bytes: entries.reduce((sum, entry) => sum + entry.size, 0) });
77
+ }
78
+ return await this.extract(reader, archive, input, context);
79
+ }
80
+ finally {
81
+ await reader.close();
82
+ }
83
+ }
84
+ async extract(reader, archive, input, context) {
85
+ const defaultTarget = path.join(path.dirname(archive), path.basename(archive, path.extname(archive)));
86
+ const target = path.resolve(context.cwd, input.to ?? defaultTarget);
87
+ const plan = [];
88
+ const unsafe = [];
89
+ const skipped = [];
90
+ const failures = [];
91
+ for (const entry of reader.entries) {
92
+ const segments = safeEntrySegments(entry.name);
93
+ if (segments === null) {
94
+ unsafe.push(entry.name);
95
+ continue;
96
+ }
97
+ if (segments.length === 0)
98
+ continue;
99
+ // A tool that could not encode a name may have stored "?" in it, which no Windows folder accepts.
100
+ const invalid = segments
101
+ .map((segment) => ({ segment, reason: invalidNameReason(segment) }))
102
+ .find((item) => item.reason !== null);
103
+ if (invalid !== undefined && invalid.reason !== null) {
104
+ skipped.push(entry.name);
105
+ const reason = message(invalid.reason, { name: invalid.segment });
106
+ failures.push(message("archive.unzip.invalid-name", { name: entry.name, reason }));
107
+ continue;
108
+ }
109
+ const destination = path.join(target, ...segments);
110
+ if (isInside(destination, target))
111
+ plan.push({ entry, destination });
112
+ else
113
+ unsafe.push(entry.name);
114
+ }
115
+ const result = {
116
+ mode: "extract",
117
+ archive,
118
+ target,
119
+ extracted: false,
120
+ files: 0,
121
+ bytes: 0,
122
+ unsafe,
123
+ skipped,
124
+ conflicts: [],
125
+ };
126
+ if (unsafe.length > 0) {
127
+ const names = unsafe.slice(0, REPORT_LIMIT).map((name) => message("archive.unzip.unsafe-name", { name }));
128
+ return done(result, { failures: [...names, message("archive.unzip.unsafe-archive")] });
129
+ }
130
+ const conflicts = [];
131
+ for (const item of plan) {
132
+ if (!item.entry.isDirectory && (await this.fileSystem.lstat(item.destination)) !== null) {
133
+ conflicts.push(item.destination);
134
+ }
135
+ }
136
+ if (conflicts.length > 0) {
137
+ if (!input.overwrite) {
138
+ const existing = conflicts
139
+ .slice(0, REPORT_LIMIT)
140
+ .map((conflict) => message("core.fs.exists", { path: conflict }));
141
+ const warnings = conflicts.length > REPORT_LIMIT
142
+ ? [
143
+ message("core.list.more", { count: conflicts.length - REPORT_LIMIT }),
144
+ message("archive.unzip.nothing-extracted"),
145
+ ]
146
+ : [message("archive.unzip.nothing-extracted")];
147
+ return done({ ...result, conflicts }, { failures: [...failures, ...existing], warnings });
148
+ }
149
+ const warning = message("archive.unzip.warn-overwrite", { count: conflicts.length, target });
150
+ await requireTypedConfirmation(context, warning, String(conflicts.length), input.confirm);
151
+ }
152
+ let files = 0;
153
+ let bytes = 0;
154
+ for (const { entry, destination } of plan) {
155
+ if (context.signal.aborted)
156
+ break;
157
+ try {
158
+ if (entry.isDirectory) {
159
+ await this.fileSystem.createDirectory(destination);
160
+ continue;
161
+ }
162
+ if ((await this.fileSystem.stat(destination))?.kind === "directory") {
163
+ throw new ConflictError("archive.unzip.folder-in-the-way", { name: entry.name });
164
+ }
165
+ await this.fileSystem.createDirectory(path.dirname(destination));
166
+ const data = await reader.read(entry);
167
+ await this.content.write(destination, data);
168
+ await this.fileSystem.setTimes(destination, entry.modifiedMs, entry.modifiedMs);
169
+ files += 1;
170
+ bytes += data.length;
171
+ }
172
+ catch (error) {
173
+ if (!(error instanceof KiriyaError))
174
+ throw error;
175
+ failures.push(message("archive.unzip.entry-failed", { name: entry.name, reason: error.detail }));
176
+ }
177
+ }
178
+ return done({ ...result, conflicts, extracted: true, files, bytes }, { failures });
179
+ }
180
+ }
181
+ //# sourceMappingURL=extract-zip.use-case.js.map
@@ -0,0 +1,45 @@
1
+ import { InterruptedError, OperationFailedError } from "../../../core/domain/errors.js";
2
+ import { TarFormatError, TarParser } from "../domain/tar-stream.js";
3
+ /** The archive is read in pieces this size, so it never has to fit in memory. */
4
+ const CHUNK_BYTES = 1024 * 1024;
5
+ /**
6
+ * Streams a .tar, .tar.gz or .tgz, whichever its first bytes show it to be, and hands
7
+ * each event to `handle` in order. Damaged gzip or tar data becomes one error naming the archive.
8
+ */
9
+ export async function readTar(content, compression, archive, signal, handle) {
10
+ const reader = await content.openForReading(archive);
11
+ try {
12
+ const head = await reader.readAt(0, Math.min(2, reader.size));
13
+ const gunzip = head[0] === 0x1f && head[1] === 0x8b ? compression.gunzip() : null;
14
+ const parser = new TarParser();
15
+ const feed = async (bytes) => {
16
+ for (const event of parser.push(bytes))
17
+ await handle(event);
18
+ };
19
+ try {
20
+ for (let position = 0; position < reader.size && !parser.ended; position += CHUNK_BYTES) {
21
+ if (signal.aborted)
22
+ throw new InterruptedError("core.error.interrupted");
23
+ const raw = await reader.readAt(position, Math.min(CHUNK_BYTES, reader.size - position));
24
+ await feed(gunzip === null ? raw : await gunzip.push(raw));
25
+ }
26
+ if (gunzip !== null && !parser.ended)
27
+ await feed(await gunzip.end());
28
+ // What follows the end blocks does not matter, even when it is not valid gzip.
29
+ if (gunzip !== null && parser.ended)
30
+ await gunzip.end().catch(() => undefined);
31
+ parser.finish();
32
+ }
33
+ catch (error) {
34
+ const damaged = error instanceof TarFormatError ||
35
+ (error instanceof OperationFailedError && error.detail.key === "core.compression.failed");
36
+ if (!damaged)
37
+ throw error;
38
+ throw new OperationFailedError("archive.untar.damaged", { path: archive, reason: error.detail }, { cause: error });
39
+ }
40
+ }
41
+ finally {
42
+ await reader.close();
43
+ }
44
+ }
45
+ //# sourceMappingURL=tar-reader.js.map
@@ -0,0 +1,57 @@
1
+ import { OperationFailedError } from "../../../core/domain/errors.js";
2
+ import { crc32 } from "../domain/crc32.js";
3
+ import { dataOffset, DEFLATED, END_SEARCH_LENGTH, findDirectory, LOCAL_HEADER_LENGTH, parseDirectory, STORED, usesZip64, } from "../domain/zip-format.js";
4
+ /** An open archive: its entries from the central directory, and each entry's bytes on request. */
5
+ export class ZipReader {
6
+ file;
7
+ compression;
8
+ entries;
9
+ constructor(file, compression, entries) {
10
+ this.file = file;
11
+ this.compression = compression;
12
+ this.entries = entries;
13
+ }
14
+ static async open(content, compression, archive) {
15
+ const file = await content.openForReading(archive);
16
+ try {
17
+ const tailLength = Math.min(file.size, END_SEARCH_LENGTH);
18
+ const location = findDirectory(await file.readAt(file.size - tailLength, tailLength));
19
+ if (location === null)
20
+ throw new OperationFailedError("archive.read.not-a-zip", { path: archive });
21
+ if (usesZip64(location))
22
+ throw new OperationFailedError("archive.read.zip64", { path: archive });
23
+ if (location.offset + location.size > file.size) {
24
+ throw new OperationFailedError("archive.read.damaged", { path: archive });
25
+ }
26
+ const entries = parseDirectory(await file.readAt(location.offset, location.size), location.count);
27
+ if (entries === null)
28
+ throw new OperationFailedError("archive.read.damaged", { path: archive });
29
+ return new ZipReader(file, compression, entries);
30
+ }
31
+ catch (error) {
32
+ await file.close();
33
+ throw error;
34
+ }
35
+ }
36
+ /** One entry's bytes, checked against its CRC-32. */
37
+ async read(entry) {
38
+ if (entry.encrypted)
39
+ throw new OperationFailedError("archive.unzip.encrypted", { name: entry.name });
40
+ if (entry.method !== STORED && entry.method !== DEFLATED) {
41
+ throw new OperationFailedError("archive.unzip.method", { name: entry.name, method: entry.method });
42
+ }
43
+ const header = await this.file.readAt(entry.localOffset, LOCAL_HEADER_LENGTH);
44
+ const start = dataOffset(entry.localOffset, header);
45
+ if (start === null)
46
+ throw new OperationFailedError("archive.unzip.damaged-entry", { name: entry.name });
47
+ const raw = await this.file.readAt(start, entry.compressedSize);
48
+ const data = entry.method === DEFLATED ? await this.compression.inflateRaw(raw) : raw;
49
+ if (crc32(data) !== entry.crc)
50
+ throw new OperationFailedError("archive.unzip.checksum", { name: entry.name });
51
+ return data;
52
+ }
53
+ close() {
54
+ return this.file.close();
55
+ }
56
+ }
57
+ //# sourceMappingURL=zip-reader.js.map
@@ -0,0 +1,27 @@
1
+ import { CreateTar } from "./application/create-tar.use-case.js";
2
+ import { CreateZip } from "./application/create-zip.use-case.js";
3
+ import { ExtractTar } from "./application/extract-tar.use-case.js";
4
+ import { ExtractZip } from "./application/extract-zip.use-case.js";
5
+ import { tarView } from "./presentation/tar.view.js";
6
+ import { untarView } from "./presentation/untar.view.js";
7
+ import { unzipView } from "./presentation/unzip.view.js";
8
+ import { zipView } from "./presentation/zip.view.js";
9
+ export const archiveModule = {
10
+ id: "archive",
11
+ summary: "archive.summary",
12
+ about: "archive.about",
13
+ examples: [
14
+ "kiriya archive zip my-project --to my-project.zip --lean",
15
+ "kiriya archive unzip release.zip --list",
16
+ "kiriya archive untar node.tar.gz --to vendor/node",
17
+ ],
18
+ guide: "https://github.com/SatPaingOo/kiriya/blob/main/docs/modules/archive.md",
19
+ register(registrar, ports) {
20
+ const { fileSystem, fileContent, compression } = ports;
21
+ registrar.add(new CreateZip(fileSystem, fileContent, compression), zipView);
22
+ registrar.add(new ExtractZip(fileSystem, fileContent, compression), unzipView);
23
+ registrar.add(new CreateTar(fileSystem, fileContent, compression), tarView);
24
+ registrar.add(new ExtractTar(fileSystem, fileContent, compression), untarView);
25
+ },
26
+ };
27
+ //# sourceMappingURL=archive.module.js.map
@@ -0,0 +1,18 @@
1
+ const TABLE = (() => {
2
+ const table = new Uint32Array(256);
3
+ for (let n = 0; n < 256; n += 1) {
4
+ let c = n;
5
+ for (let k = 0; k < 8; k += 1)
6
+ c = c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1;
7
+ table[n] = c >>> 0;
8
+ }
9
+ return table;
10
+ })();
11
+ /** The CRC-32 that ZIP stores for every entry. */
12
+ export function crc32(data) {
13
+ let crc = 0xffffffff;
14
+ for (const byte of data)
15
+ crc = (TABLE[(crc ^ byte) & 0xff] ?? 0) ^ (crc >>> 8);
16
+ return (crc ^ 0xffffffff) >>> 0;
17
+ }
18
+ //# sourceMappingURL=crc32.js.map
@@ -0,0 +1,97 @@
1
+ import { utf8Bytes } from "../../../core/domain/encodings.js";
2
+ export const TAR_BLOCK = 512;
3
+ /** Two empty blocks end an archive. */
4
+ export const TAR_END = new Uint8Array(TAR_BLOCK * 2);
5
+ /** The largest number eleven octal digits hold, just under 8 GB; a larger size goes in a pax record. */
6
+ const OCTAL_SIZE_LIMIT = 8 ** 11 - 1;
7
+ const NAME_BYTES = 100;
8
+ /** gzip for .tar.gz and .tgz, none for .tar, and null for any other name. */
9
+ export function tarCompression(archive) {
10
+ const lower = archive.toLowerCase();
11
+ if (lower.endsWith(".tar.gz") || lower.endsWith(".tgz"))
12
+ return true;
13
+ return lower.endsWith(".tar") ? false : null;
14
+ }
15
+ /** Zero bytes that bring `size` up to a whole block. */
16
+ export function tarPadding(size) {
17
+ return (TAR_BLOCK - (size % TAR_BLOCK)) % TAR_BLOCK;
18
+ }
19
+ function isPlainAscii(text) {
20
+ return [...text].every((char) => char >= " " && char <= "~");
21
+ }
22
+ function writeAscii(block, offset, length, text) {
23
+ for (let index = 0; index < Math.min(length, text.length); index += 1) {
24
+ block[offset + index] = text.charCodeAt(index);
25
+ }
26
+ }
27
+ /** Zero-padded octal digits, with the field's last byte left as NUL. */
28
+ function writeOctal(block, offset, length, value) {
29
+ writeAscii(block, offset, length - 1, Math.floor(value)
30
+ .toString(8)
31
+ .padStart(length - 1, "0"));
32
+ }
33
+ function concat(parts) {
34
+ const bytes = new Uint8Array(parts.reduce((total, part) => total + part.length, 0));
35
+ let offset = 0;
36
+ for (const part of parts) {
37
+ bytes.set(part, offset);
38
+ offset += part.length;
39
+ }
40
+ return bytes;
41
+ }
42
+ /** One ustar header block. No user or group names are written, so an archive says nothing about who made it. */
43
+ function header(name, typeflag, size, mode, mtimeSeconds) {
44
+ const block = new Uint8Array(TAR_BLOCK);
45
+ writeAscii(block, 0, NAME_BYTES, name);
46
+ writeOctal(block, 100, 8, mode & 0o7777);
47
+ writeOctal(block, 108, 8, 0);
48
+ writeOctal(block, 116, 8, 0);
49
+ writeOctal(block, 124, 12, size);
50
+ writeOctal(block, 136, 12, mtimeSeconds);
51
+ block[156] = typeflag.charCodeAt(0);
52
+ writeAscii(block, 257, 5, "ustar");
53
+ writeAscii(block, 263, 2, "00");
54
+ // The checksum counts its own field as spaces, then takes six octal digits, a NUL and a space.
55
+ block.fill(0x20, 148, 156);
56
+ const sum = block.reduce((total, byte) => total + byte, 0);
57
+ writeAscii(block, 148, 6, sum.toString(8).padStart(6, "0"));
58
+ block[154] = 0;
59
+ return block;
60
+ }
61
+ /** "LENGTH key=value" and a newline, where LENGTH counts the whole record, its own digits included. */
62
+ function paxRecord(key, value) {
63
+ const body = utf8Bytes(` ${key}=${value}\n`).length;
64
+ let length = body + 1;
65
+ while (String(length).length + body !== length)
66
+ length = String(length).length + body;
67
+ return utf8Bytes(`${length} ${key}=${value}\n`);
68
+ }
69
+ /** A plain ASCII stand-in for tools that ignore pax records, keeping the end, where the file name is. */
70
+ function asciiName(name) {
71
+ const ascii = [...name].map((char) => (char >= " " && char <= "~" ? char : "_")).join("");
72
+ return ascii.length <= NAME_BYTES ? ascii : ascii.slice(-NAME_BYTES);
73
+ }
74
+ /**
75
+ * The header blocks for one entry, in POSIX ustar form. A name longer than 100 bytes
76
+ * or not plain ASCII, and a size of 8 GB or more, go in a pax extended header before
77
+ * it, which GNU tar, bsdtar and 7-Zip all read.
78
+ */
79
+ export function tarHeader(entry) {
80
+ const name = entry.isDirectory && !entry.name.endsWith("/") ? `${entry.name}/` : entry.name;
81
+ const mtime = Math.max(0, Math.floor(entry.modifiedMs / 1000));
82
+ const plainName = isPlainAscii(name) && name.length <= NAME_BYTES;
83
+ const largeSize = entry.size > OCTAL_SIZE_LIMIT;
84
+ const records = [];
85
+ if (!plainName)
86
+ records.push(paxRecord("path", name));
87
+ if (largeSize)
88
+ records.push(paxRecord("size", String(entry.size)));
89
+ const typeflag = entry.isDirectory ? "5" : "0";
90
+ const main = header(plainName ? name : asciiName(name), typeflag, largeSize ? 0 : entry.size, entry.mode, mtime);
91
+ if (records.length === 0)
92
+ return main;
93
+ const pax = concat(records);
94
+ const paxHeader = header(asciiName(`PaxHeader/${name}`), "x", pax.length, 0o644, mtime);
95
+ return concat([paxHeader, pax, new Uint8Array(tarPadding(pax.length)), main]);
96
+ }
97
+ //# sourceMappingURL=tar-format.js.map
@@ -0,0 +1,223 @@
1
+ import { utf8Text } from "../../../core/domain/encodings.js";
2
+ import { OperationFailedError } from "../../../core/domain/errors.js";
3
+ const BLOCK = 512;
4
+ /** Extended headers hold a name and a few numbers; anything larger is no real archive. */
5
+ const META_LIMIT = 1024 * 1024;
6
+ /** Tar data that cannot be read; its detail is the reason, such as a checksum that does not match. */
7
+ export class TarFormatError extends OperationFailedError {
8
+ }
9
+ function ascii(bytes, offset, length) {
10
+ let text = "";
11
+ for (let index = offset; index < offset + length; index += 1)
12
+ text += String.fromCharCode(bytes[index] ?? 0);
13
+ return text;
14
+ }
15
+ /** Text up to the first NUL: UTF-8, or Latin-1 from tools that wrote names in another encoding. */
16
+ function nameText(bytes) {
17
+ const end = bytes.indexOf(0);
18
+ const raw = end < 0 ? bytes : bytes.subarray(0, end);
19
+ return utf8Text(raw) ?? ascii(raw, 0, raw.length);
20
+ }
21
+ function trimmedField(bytes, offset, length) {
22
+ return ascii(bytes, offset, length).replace(/^[ \0]+|[ \0]+$/g, "");
23
+ }
24
+ /** An octal field, or GNU's base-256 form, which sets the top bit of the first byte. */
25
+ function readNumber(block, offset, length) {
26
+ const first = block[offset] ?? 0;
27
+ if ((first & 0x80) !== 0) {
28
+ let value = first & 0x7f;
29
+ for (let index = 1; index < length; index += 1)
30
+ value = value * 256 + (block[offset + index] ?? 0);
31
+ return value;
32
+ }
33
+ const text = trimmedField(block, offset, length);
34
+ if (text === "")
35
+ return 0;
36
+ if (!/^[0-7]+$/.test(text))
37
+ throw new TarFormatError("archive.tar.bad-header");
38
+ return parseInt(text, 8);
39
+ }
40
+ /** Old tools summed header bytes as signed numbers, so either sum is accepted. */
41
+ function checksumMatches(block) {
42
+ const text = trimmedField(block, 148, 8);
43
+ if (!/^[0-7]+$/.test(text))
44
+ return false;
45
+ let unsigned = 0;
46
+ let signed = 0;
47
+ for (let index = 0; index < BLOCK; index += 1) {
48
+ const byte = index >= 148 && index < 156 ? 0x20 : (block[index] ?? 0);
49
+ unsigned += byte;
50
+ signed += byte > 127 ? byte - 256 : byte;
51
+ }
52
+ const stored = parseInt(text, 8);
53
+ return stored === unsigned || stored === signed;
54
+ }
55
+ /** Records of "LENGTH key=value" and a newline. */
56
+ function parsePax(bytes) {
57
+ const records = new Map();
58
+ let position = 0;
59
+ while (position < bytes.length) {
60
+ const space = bytes.indexOf(0x20, position);
61
+ const length = space < 0 ? Number.NaN : Number(ascii(bytes, position, space - position));
62
+ if (!Number.isInteger(length) || length <= space - position || position + length > bytes.length) {
63
+ throw new TarFormatError("archive.tar.bad-header");
64
+ }
65
+ const record = utf8Text(bytes.subarray(space + 1, position + length - 1)) ?? "";
66
+ const equals = record.indexOf("=");
67
+ if (equals > 0)
68
+ records.set(record.slice(0, equals), record.slice(equals + 1));
69
+ position += length;
70
+ }
71
+ return records;
72
+ }
73
+ function entryType(typeflag, name) {
74
+ if (typeflag === "5")
75
+ return "directory";
76
+ if (typeflag === "0" || typeflag === "7" || typeflag === String.fromCharCode(0)) {
77
+ return name.endsWith("/") ? "directory" : "file";
78
+ }
79
+ return typeflag === "1" || typeflag === "2" ? "link" : "other";
80
+ }
81
+ function concat(first, second) {
82
+ const bytes = new Uint8Array(first.length + second.length);
83
+ bytes.set(first);
84
+ bytes.set(second, first.length);
85
+ return bytes;
86
+ }
87
+ /**
88
+ * Reads a tar stream from pieces of any size: POSIX ustar, pax extended headers and GNU
89
+ * long names, as GNU tar, bsdtar and most other tools write them. It never touches a file:
90
+ * each entry, its data and its end arrive as events for the caller to act on.
91
+ */
92
+ export class TarParser {
93
+ buffer = new Uint8Array(0);
94
+ state = "header";
95
+ remaining = 0;
96
+ padding = 0;
97
+ metaType = "";
98
+ meta = [];
99
+ pax = new Map();
100
+ longName = null;
101
+ headers = 0;
102
+ emptyBlocks = 0;
103
+ /** The two empty blocks that end an archive have been read. */
104
+ get ended() {
105
+ return this.state === "done";
106
+ }
107
+ push(chunk) {
108
+ const events = [];
109
+ const data = this.buffer.length === 0 ? chunk : concat(this.buffer, chunk);
110
+ let offset = 0;
111
+ while (this.state !== "done") {
112
+ const available = data.length - offset;
113
+ if (this.state === "data" || this.state === "meta") {
114
+ if (this.remaining === 0) {
115
+ if (this.state === "data")
116
+ events.push({ kind: "entry-end" });
117
+ else
118
+ this.applyMeta();
119
+ this.state = this.padding > 0 ? "padding" : "header";
120
+ continue;
121
+ }
122
+ const take = Math.min(this.remaining, available);
123
+ if (take === 0)
124
+ break;
125
+ const bytes = data.slice(offset, offset + take);
126
+ if (this.state === "data")
127
+ events.push({ kind: "data", bytes });
128
+ else
129
+ this.meta.push(bytes);
130
+ offset += take;
131
+ this.remaining -= take;
132
+ continue;
133
+ }
134
+ if (this.state === "padding") {
135
+ const take = Math.min(this.padding, available);
136
+ if (take === 0)
137
+ break;
138
+ offset += take;
139
+ this.padding -= take;
140
+ if (this.padding === 0)
141
+ this.state = "header";
142
+ continue;
143
+ }
144
+ if (available < BLOCK)
145
+ break;
146
+ const event = this.readHeader(data.subarray(offset, offset + BLOCK));
147
+ offset += BLOCK;
148
+ if (event !== null)
149
+ events.push(event);
150
+ }
151
+ this.buffer = data.slice(offset);
152
+ return events;
153
+ }
154
+ /** Called when the input is over. An archive that stops cleanly after an entry, without its end blocks, still counts. */
155
+ finish() {
156
+ if (this.state === "done")
157
+ return;
158
+ if (this.state === "header" && this.buffer.length === 0 && this.headers > 0) {
159
+ this.state = "done";
160
+ return;
161
+ }
162
+ throw new TarFormatError(this.headers === 0 ? "archive.tar.not-tar" : "archive.tar.truncated");
163
+ }
164
+ readHeader(block) {
165
+ if (block.every((byte) => byte === 0)) {
166
+ this.emptyBlocks += 1;
167
+ if (this.emptyBlocks === 2)
168
+ this.state = "done";
169
+ return null;
170
+ }
171
+ this.emptyBlocks = 0;
172
+ if (!checksumMatches(block)) {
173
+ throw new TarFormatError(this.headers === 0 ? "archive.tar.not-tar" : "archive.tar.bad-checksum");
174
+ }
175
+ this.headers += 1;
176
+ const typeflag = String.fromCharCode(block[156] ?? 0);
177
+ const isMeta = "xgLK".includes(typeflag);
178
+ const paxSize = this.pax.get("size");
179
+ const size = !isMeta && paxSize !== undefined ? Number(paxSize) : readNumber(block, 124, 12);
180
+ if (!Number.isSafeInteger(size) || size < 0)
181
+ throw new TarFormatError("archive.tar.bad-header");
182
+ this.remaining = size;
183
+ this.padding = (BLOCK - (size % BLOCK)) % BLOCK;
184
+ if (isMeta) {
185
+ if (size > META_LIMIT)
186
+ throw new TarFormatError("archive.tar.meta-too-large");
187
+ this.state = "meta";
188
+ this.metaType = typeflag;
189
+ this.meta = [];
190
+ return null;
191
+ }
192
+ let name = nameText(block.subarray(0, 100));
193
+ // Only POSIX ustar keeps a name prefix here; GNU's older format stores times in the same place.
194
+ if (ascii(block, 257, 5) === "ustar" && block[262] === 0) {
195
+ const prefix = nameText(block.subarray(345, 500));
196
+ if (prefix !== "")
197
+ name = `${prefix}/${name}`;
198
+ }
199
+ name = this.pax.get("path") ?? this.longName ?? name;
200
+ const mtime = Number(this.pax.get("mtime") ?? readNumber(block, 136, 12));
201
+ const entry = {
202
+ name,
203
+ type: entryType(typeflag, name),
204
+ size,
205
+ mode: readNumber(block, 100, 8),
206
+ modifiedMs: Number.isFinite(mtime) ? Math.round(mtime * 1000) : 0,
207
+ };
208
+ this.pax = new Map();
209
+ this.longName = null;
210
+ this.state = "data";
211
+ return { kind: "entry", entry };
212
+ }
213
+ applyMeta() {
214
+ const bytes = this.meta.reduce((joined, part) => concat(joined, part), new Uint8Array(0));
215
+ this.meta = [];
216
+ if (this.metaType === "x")
217
+ this.pax = parsePax(bytes);
218
+ if (this.metaType === "L")
219
+ this.longName = nameText(bytes);
220
+ // Global pax headers (g) and GNU long link names (K) change nothing kiriya extracts.
221
+ }
222
+ }
223
+ //# sourceMappingURL=tar-stream.js.map