@greenpandastudios/aug-cli 0.20.1 → 0.21.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 (295) hide show
  1. package/README.md +1 -1
  2. package/docs/assets/benchmarks/execution.svg +774 -637
  3. package/docs/assets/benchmarks/http.svg +278 -249
  4. package/docs/assets/benchmarks/improvements.svg +66 -66
  5. package/docs/assets/benchmarks/kernels-mobile.svg +1 -0
  6. package/docs/assets/benchmarks/kernels.svg +1 -0
  7. package/docs/assets/benchmarks/memory.svg +153 -153
  8. package/docs/benchmark-results.json +227089 -90363
  9. package/docs/compatibility.md +6 -6
  10. package/docs/contributing-benchmarks.md +36 -6
  11. package/docs/dev-containers.md +8 -15
  12. package/docs/dgx-consumers.json +122 -0
  13. package/docs/dgx-gyms.json +784 -0
  14. package/docs/dgx-kernels.json +1170 -0
  15. package/docs/dgx-performance.json +227849 -0
  16. package/docs/dgx-spark.md +64 -0
  17. package/docs/diagnostics.md +1 -1
  18. package/docs/docker.md +16 -17
  19. package/docs/example-projects.json +32 -0
  20. package/docs/examples/approved-design/dependencies/august/{0.20.1 → 0.21.0}/io/contracts.md +3 -3
  21. package/docs/examples/approved-design/domain/app.md +4 -4
  22. package/docs/examples/approved-design/main.md +2 -2
  23. package/docs/examples/calls-benchmark/index.md +36 -0
  24. package/docs/examples/calls-benchmark/main.md +78 -0
  25. package/docs/examples/calls-benchmark/operations.md +60 -0
  26. package/docs/examples/developer-workflow/calculator.md +5 -5
  27. package/docs/examples/developer-workflow/dependencies/august/{0.20.1 → 0.21.0}/io/contracts.md +3 -3
  28. package/docs/examples/developer-workflow/logging/console.md +2 -2
  29. package/docs/examples/developer-workflow/logging/logger.md +2 -2
  30. package/docs/examples/developer-workflow/main.md +2 -2
  31. package/docs/examples/errors-benchmark/index.md +36 -0
  32. package/docs/examples/errors-benchmark/main.md +92 -0
  33. package/docs/examples/errors-benchmark/operations.md +65 -0
  34. package/docs/examples/float-benchmark/index.md +35 -0
  35. package/docs/examples/float-benchmark/main.md +75 -0
  36. package/docs/examples/generic-di/dependencies/august/{0.20.1 → 0.21.0}/io/contracts.md +3 -3
  37. package/docs/examples/generic-di/main.md +2 -2
  38. package/docs/examples/generic-di/types.md +3 -3
  39. package/docs/examples/hello/app/greeter.md +3 -3
  40. package/docs/examples/hello/dependencies/august/{0.20.1 → 0.21.0}/io/contracts.md +3 -3
  41. package/docs/examples/hello/logging/console.md +2 -2
  42. package/docs/examples/hello/logging/logger.md +2 -2
  43. package/docs/examples/hello/main.md +2 -2
  44. package/docs/examples/index.md +18 -1
  45. package/docs/examples/interceptors/app.md +5 -5
  46. package/docs/examples/interceptors/dependencies/august/{0.20.1 → 0.21.0}/io/contracts.md +3 -3
  47. package/docs/examples/interceptors/interceptors.md +2 -2
  48. package/docs/examples/interceptors/logging.md +3 -3
  49. package/docs/examples/interceptors/main.md +2 -2
  50. package/docs/examples/list-benchmark/index.md +35 -0
  51. package/docs/examples/list-benchmark/main.md +78 -0
  52. package/docs/examples/map-churn-benchmark/index.md +35 -0
  53. package/docs/examples/map-churn-benchmark/main.md +106 -0
  54. package/docs/examples/native-blake3/dependencies/packages/@greenpandastudios/aug-blake3/0.1.3/api.md +104 -0
  55. package/docs/examples/native-blake3/dependencies/packages/@greenpandastudios/aug-blake3/0.1.3/contracts.md +54 -0
  56. package/docs/examples/native-blake3/dependencies/packages/@greenpandastudios/aug-blake3/0.1.3/native.abi-json.md +48 -0
  57. package/docs/examples/native-blake3/hashing.md +93 -0
  58. package/docs/examples/native-blake3/index.md +38 -0
  59. package/docs/examples/native-blake3/main.md +73 -0
  60. package/docs/examples/native-pytorch/dependencies/packages/@greenpandastudios/aug-pytorch/0.1.4/api.md +406 -0
  61. package/docs/examples/native-pytorch/dependencies/packages/@greenpandastudios/aug-pytorch/0.1.4/bindings.md +51 -0
  62. package/docs/examples/native-pytorch/dependencies/packages/@greenpandastudios/aug-pytorch/0.1.4/contracts.md +63 -0
  63. package/docs/examples/native-pytorch/dependencies/packages/@greenpandastudios/aug-pytorch/0.1.4/native.abi-json.md +161 -0
  64. package/docs/examples/native-pytorch/index.md +38 -0
  65. package/docs/examples/native-pytorch/main.md +73 -0
  66. package/docs/examples/native-pytorch/tensors.md +123 -0
  67. package/docs/examples/native-sqlite/database.md +116 -0
  68. package/docs/examples/native-sqlite/dependencies/packages/@greenpandastudios/aug-sqlite/0.1.3/api.md +284 -0
  69. package/docs/examples/native-sqlite/dependencies/packages/@greenpandastudios/aug-sqlite/0.1.3/bindings.md +51 -0
  70. package/docs/examples/native-sqlite/dependencies/packages/@greenpandastudios/aug-sqlite/0.1.3/contracts.md +75 -0
  71. package/docs/examples/native-sqlite/dependencies/packages/@greenpandastudios/aug-sqlite/0.1.3/native.abi-json.md +119 -0
  72. package/docs/examples/native-sqlite/index.md +38 -0
  73. package/docs/examples/native-sqlite/main.md +73 -0
  74. package/docs/examples/native-zlib/compression.md +99 -0
  75. package/docs/examples/native-zlib/dependencies/packages/@greenpandastudios/aug-zlib/0.1.3/api.md +153 -0
  76. package/docs/examples/native-zlib/dependencies/packages/@greenpandastudios/aug-zlib/0.1.3/contracts.md +54 -0
  77. package/docs/examples/native-zlib/dependencies/packages/@greenpandastudios/aug-zlib/0.1.3/native.abi-json.md +76 -0
  78. package/docs/examples/native-zlib/index.md +38 -0
  79. package/docs/examples/native-zlib/main.md +78 -0
  80. package/docs/examples/new-syntax/console.md +2 -2
  81. package/docs/examples/new-syntax/dependencies/august/{0.20.1 → 0.21.0}/io/contracts.md +3 -3
  82. package/docs/examples/new-syntax/greeter.md +3 -3
  83. package/docs/examples/new-syntax/logger.md +2 -2
  84. package/docs/examples/new-syntax/main.md +2 -2
  85. package/docs/examples/ownership-transfer/dependencies/august/{0.20.1 → 0.21.0}/io/contracts.md +3 -3
  86. package/docs/examples/ownership-transfer/main.md +2 -2
  87. package/docs/examples/ownership-transfer/resource.md +2 -2
  88. package/docs/examples/packages-math/aug-package-json.md +1 -1
  89. package/docs/examples/records-benchmark/data.md +55 -0
  90. package/docs/examples/records-benchmark/index.md +36 -0
  91. package/docs/examples/records-benchmark/main.md +85 -0
  92. package/docs/examples/strings-benchmark/index.md +35 -0
  93. package/docs/examples/strings-benchmark/main.md +76 -0
  94. package/docs/examples/tasks-benchmark/index.md +36 -0
  95. package/docs/examples/tasks-benchmark/main.md +89 -0
  96. package/docs/examples/tasks-benchmark/operations.md +58 -0
  97. package/docs/getting-started.md +3 -3
  98. package/docs/grammar.md +2 -2
  99. package/docs/gym-results.json +784 -0
  100. package/docs/implementation-map.md +6 -0
  101. package/docs/index.md +1 -1
  102. package/docs/kernel-results.json +1170 -0
  103. package/docs/language-conformance.md +6 -0
  104. package/docs/language-constructs.md +9 -1
  105. package/docs/learn/index.md +1 -1
  106. package/docs/maintaining-docs.md +1 -1
  107. package/docs/native-implementation.md +276 -0
  108. package/docs/native-interop-llvm-plan.md +342 -0
  109. package/docs/native-package-examples.md +684 -0
  110. package/docs/native-packages.md +179 -0
  111. package/docs/packages.md +2 -2
  112. package/docs/performance.md +50 -29
  113. package/docs/production-readiness.md +11 -1
  114. package/docs/public/downloads/approved-design.zip +0 -0
  115. package/docs/public/downloads/calls-benchmark.zip +0 -0
  116. package/docs/public/downloads/developer-workflow.zip +0 -0
  117. package/docs/public/downloads/errors-benchmark.zip +0 -0
  118. package/docs/public/downloads/float-benchmark.zip +0 -0
  119. package/docs/public/downloads/generic-di.zip +0 -0
  120. package/docs/public/downloads/hello.zip +0 -0
  121. package/docs/public/downloads/interceptors.zip +0 -0
  122. package/docs/public/downloads/json-benchmark.zip +0 -0
  123. package/docs/public/downloads/list-benchmark.zip +0 -0
  124. package/docs/public/downloads/map-churn-benchmark.zip +0 -0
  125. package/docs/public/downloads/native-blake3.zip +0 -0
  126. package/docs/public/downloads/native-pytorch.zip +0 -0
  127. package/docs/public/downloads/native-sqlite.zip +0 -0
  128. package/docs/public/downloads/native-zlib.zip +0 -0
  129. package/docs/public/downloads/new-syntax.zip +0 -0
  130. package/docs/public/downloads/oidc-login.zip +0 -0
  131. package/docs/public/downloads/ownership-transfer.zip +0 -0
  132. package/docs/public/downloads/packages-app.zip +0 -0
  133. package/docs/public/downloads/packages-math.zip +0 -0
  134. package/docs/public/downloads/records-benchmark.zip +0 -0
  135. package/docs/public/downloads/strings-benchmark.zip +0 -0
  136. package/docs/public/downloads/tasks-benchmark.zip +0 -0
  137. package/docs/qualification-results.md +66 -0
  138. package/docs/reference.md +24 -0
  139. package/docs/releasing.md +81 -6
  140. package/docs/research/libtorch-native-qualification.md +115 -0
  141. package/docs/research/native-interop-llvm.md +197 -0
  142. package/docs/research/qualification-methods.md +15 -0
  143. package/docs/roadmap.md +1 -0
  144. package/docs/safety-gyms.md +41 -0
  145. package/docs/tooling.md +16 -15
  146. package/docs/web-library-gaps.md +1 -1
  147. package/docs/web.md +4 -0
  148. package/examples/approved-design/.aug-spec/manifest.json +3 -3
  149. package/examples/approved-design/domain/app.aug.md +4 -4
  150. package/examples/approved-design/main.aug.md +2 -2
  151. package/examples/developer-workflow/.aug-spec/manifest.json +3 -3
  152. package/examples/developer-workflow/calculator.aug.md +5 -5
  153. package/examples/developer-workflow/logging/console.aug.md +2 -2
  154. package/examples/developer-workflow/logging/logger.aug.md +2 -2
  155. package/examples/developer-workflow/main.aug.md +2 -2
  156. package/examples/generic-di/.aug-spec/manifest.json +3 -3
  157. package/examples/generic-di/main.aug.md +2 -2
  158. package/examples/generic-di/types.aug.md +3 -3
  159. package/examples/hello/.aug-spec/manifest.json +3 -3
  160. package/examples/hello/app/greeter.aug.md +3 -3
  161. package/examples/hello/logging/console.aug.md +2 -2
  162. package/examples/hello/logging/logger.aug.md +2 -2
  163. package/examples/hello/main.aug.md +2 -2
  164. package/examples/interceptors/.aug-spec/manifest.json +3 -3
  165. package/examples/interceptors/app.aug.md +5 -5
  166. package/examples/interceptors/interceptors.aug.md +2 -2
  167. package/examples/interceptors/logging.aug.md +3 -3
  168. package/examples/interceptors/main.aug.md +2 -2
  169. package/examples/native-blake3/.aug-spec/manifest.json +16 -0
  170. package/examples/native-blake3/.aug-spec/packages/@greenpandastudios/aug-blake3/0.1.1/native.abi.json +29 -0
  171. package/examples/native-blake3/.aug-spec/packages/@greenpandastudios/aug-blake3/0.1.3/api.aug +14 -0
  172. package/examples/native-blake3/.aug-spec/packages/@greenpandastudios/aug-blake3/0.1.3/api.aug.md +34 -0
  173. package/examples/native-blake3/.aug-spec/packages/@greenpandastudios/aug-blake3/0.1.3/contracts.aug +4 -0
  174. package/examples/native-blake3/.aug-spec/packages/@greenpandastudios/aug-blake3/0.1.3/contracts.aug.md +8 -0
  175. package/examples/native-blake3/.aug-spec/packages/@greenpandastudios/aug-blake3/0.1.3/native.abi.json +29 -0
  176. package/examples/native-blake3/AGENTS.md +5 -0
  177. package/examples/native-blake3/aug.lock.json +253 -0
  178. package/examples/native-blake3/hashing.aug +11 -0
  179. package/examples/native-blake3/hashing.aug.md +25 -0
  180. package/examples/native-blake3/main.aug +8 -0
  181. package/examples/native-blake3/main.aug.md +13 -0
  182. package/examples/native-pytorch/.aug-spec/manifest.json +18 -0
  183. package/examples/native-pytorch/.aug-spec/packages/@greenpandastudios/aug-pytorch/0.1.1/native.abi.json +142 -0
  184. package/examples/native-pytorch/.aug-spec/packages/@greenpandastudios/aug-pytorch/0.1.3/native.abi.json +142 -0
  185. package/examples/native-pytorch/.aug-spec/packages/@greenpandastudios/aug-pytorch/0.1.4/api.aug +101 -0
  186. package/examples/native-pytorch/.aug-spec/packages/@greenpandastudios/aug-pytorch/0.1.4/api.aug.md +160 -0
  187. package/examples/native-pytorch/.aug-spec/packages/@greenpandastudios/aug-pytorch/0.1.4/bindings.aug +3 -0
  188. package/examples/native-pytorch/.aug-spec/packages/@greenpandastudios/aug-pytorch/0.1.4/bindings.aug.md +8 -0
  189. package/examples/native-pytorch/.aug-spec/packages/@greenpandastudios/aug-pytorch/0.1.4/contracts.aug +6 -0
  190. package/examples/native-pytorch/.aug-spec/packages/@greenpandastudios/aug-pytorch/0.1.4/contracts.aug.md +13 -0
  191. package/examples/native-pytorch/.aug-spec/packages/@greenpandastudios/aug-pytorch/0.1.4/native.abi.json +142 -0
  192. package/examples/native-pytorch/AGENTS.md +5 -0
  193. package/examples/native-pytorch/aug.lock.json +309 -0
  194. package/examples/native-pytorch/main.aug +8 -0
  195. package/examples/native-pytorch/main.aug.md +13 -0
  196. package/examples/native-pytorch/tensors.aug +23 -0
  197. package/examples/native-pytorch/tensors.aug.md +35 -0
  198. package/examples/native-sqlite/.aug-spec/manifest.json +18 -0
  199. package/examples/native-sqlite/.aug-spec/packages/@greenpandastudios/aug-sqlite/0.1.2/native.abi.json +100 -0
  200. package/examples/native-sqlite/.aug-spec/packages/@greenpandastudios/aug-sqlite/0.1.3/api.aug +48 -0
  201. package/examples/native-sqlite/.aug-spec/packages/@greenpandastudios/aug-sqlite/0.1.3/api.aug.md +91 -0
  202. package/examples/native-sqlite/.aug-spec/packages/@greenpandastudios/aug-sqlite/0.1.3/bindings.aug +3 -0
  203. package/examples/native-sqlite/.aug-spec/packages/@greenpandastudios/aug-sqlite/0.1.3/bindings.aug.md +8 -0
  204. package/examples/native-sqlite/.aug-spec/packages/@greenpandastudios/aug-sqlite/0.1.3/contracts.aug +8 -0
  205. package/examples/native-sqlite/.aug-spec/packages/@greenpandastudios/aug-sqlite/0.1.3/contracts.aug.md +22 -0
  206. package/examples/native-sqlite/.aug-spec/packages/@greenpandastudios/aug-sqlite/0.1.3/native.abi.json +100 -0
  207. package/examples/native-sqlite/AGENTS.md +5 -0
  208. package/examples/native-sqlite/aug.lock.json +236 -0
  209. package/examples/native-sqlite/database.aug +15 -0
  210. package/examples/native-sqlite/database.aug.md +27 -0
  211. package/examples/native-sqlite/main.aug +8 -0
  212. package/examples/native-sqlite/main.aug.md +13 -0
  213. package/examples/native-zlib/.aug-spec/manifest.json +16 -0
  214. package/examples/native-zlib/.aug-spec/packages/@greenpandastudios/aug-zlib/0.1.1/native.abi.json +57 -0
  215. package/examples/native-zlib/.aug-spec/packages/@greenpandastudios/aug-zlib/0.1.3/api.aug +29 -0
  216. package/examples/native-zlib/.aug-spec/packages/@greenpandastudios/aug-zlib/0.1.3/api.aug.md +54 -0
  217. package/examples/native-zlib/.aug-spec/packages/@greenpandastudios/aug-zlib/0.1.3/contracts.aug +4 -0
  218. package/examples/native-zlib/.aug-spec/packages/@greenpandastudios/aug-zlib/0.1.3/contracts.aug.md +8 -0
  219. package/examples/native-zlib/.aug-spec/packages/@greenpandastudios/aug-zlib/0.1.3/native.abi.json +57 -0
  220. package/examples/native-zlib/AGENTS.md +5 -0
  221. package/examples/native-zlib/aug.lock.json +236 -0
  222. package/examples/native-zlib/compression.aug +15 -0
  223. package/examples/native-zlib/compression.aug.md +27 -0
  224. package/examples/native-zlib/main.aug +10 -0
  225. package/examples/native-zlib/main.aug.md +13 -0
  226. package/examples/new-syntax/.aug-spec/manifest.json +3 -3
  227. package/examples/new-syntax/console.aug.md +2 -2
  228. package/examples/new-syntax/greeter.aug.md +3 -3
  229. package/examples/new-syntax/logger.aug.md +2 -2
  230. package/examples/new-syntax/main.aug.md +2 -2
  231. package/examples/oidc-login/aug.lock.json +6 -6
  232. package/examples/ownership-transfer/.aug-spec/manifest.json +3 -3
  233. package/examples/ownership-transfer/main.aug.md +2 -2
  234. package/examples/ownership-transfer/resource.aug.md +2 -2
  235. package/examples/packages/math/aug-package.json +1 -1
  236. package/native/compiler-packs.json +43 -0
  237. package/package.json +3 -2
  238. package/runtime/aug_http.c +48 -21
  239. package/runtime/aug_http_ir.c +30 -0
  240. package/runtime/aug_http_ir.h +18 -0
  241. package/runtime/aug_ir.c +209 -0
  242. package/runtime/aug_ir.h +89 -0
  243. package/runtime/aug_json.c +6 -1
  244. package/runtime/aug_runtime.c +82 -16
  245. package/runtime/aug_runtime.h +16 -2
  246. package/runtime/aug_tasks.c +38 -7
  247. package/runtime/aug_values.c +12 -6
  248. package/scripts/bootstrap-native.mjs +4 -1
  249. package/src/ast.js +1 -1
  250. package/src/checker.js +110 -28
  251. package/src/cli.js +92 -14
  252. package/src/codegen.js +31 -6
  253. package/src/compiler-packs.js +66 -0
  254. package/src/config.js +2 -2
  255. package/src/editor.js +15 -6
  256. package/src/formatter.js +4 -2
  257. package/src/git-http.js +137 -0
  258. package/src/git-packages.js +9 -1
  259. package/src/help.js +10 -2
  260. package/src/ir-types.js +5 -0
  261. package/src/ir-verify.js +277 -0
  262. package/src/ir.js +962 -0
  263. package/src/llvm-debug.js +101 -0
  264. package/src/llvm-native.js +152 -0
  265. package/src/llvm-platform.js +20 -0
  266. package/src/llvm.js +781 -0
  267. package/src/native-artifacts.js +207 -0
  268. package/src/native-bindings.js +189 -0
  269. package/src/native-contracts.js +295 -0
  270. package/src/native-declarations.js +68 -0
  271. package/src/native-facts.js +63 -0
  272. package/src/package-locking.js +43 -0
  273. package/src/package-manager.js +23 -8
  274. package/src/parser.js +20 -2
  275. package/src/project.js +2 -2
  276. package/src/runtime-abi.js +23 -0
  277. package/src/runtime-adapters.js +18 -0
  278. package/src/schemas.js +24 -17
  279. package/src/semantic.js +8 -3
  280. package/src/snippets.js +1 -0
  281. package/src/spec.js +49 -8
  282. /package/examples/approved-design/.aug-spec/august/{0.20.1 → 0.21.0}/io/contracts.aug +0 -0
  283. /package/examples/approved-design/.aug-spec/august/{0.20.1 → 0.21.0}/io/contracts.aug.md +0 -0
  284. /package/examples/developer-workflow/.aug-spec/august/{0.20.1 → 0.21.0}/io/contracts.aug +0 -0
  285. /package/examples/developer-workflow/.aug-spec/august/{0.20.1 → 0.21.0}/io/contracts.aug.md +0 -0
  286. /package/examples/generic-di/.aug-spec/august/{0.20.1 → 0.21.0}/io/contracts.aug +0 -0
  287. /package/examples/generic-di/.aug-spec/august/{0.20.1 → 0.21.0}/io/contracts.aug.md +0 -0
  288. /package/examples/hello/.aug-spec/august/{0.20.1 → 0.21.0}/io/contracts.aug +0 -0
  289. /package/examples/hello/.aug-spec/august/{0.20.1 → 0.21.0}/io/contracts.aug.md +0 -0
  290. /package/examples/interceptors/.aug-spec/august/{0.20.1 → 0.21.0}/io/contracts.aug +0 -0
  291. /package/examples/interceptors/.aug-spec/august/{0.20.1 → 0.21.0}/io/contracts.aug.md +0 -0
  292. /package/examples/new-syntax/.aug-spec/august/{0.20.1 → 0.21.0}/io/contracts.aug +0 -0
  293. /package/examples/new-syntax/.aug-spec/august/{0.20.1 → 0.21.0}/io/contracts.aug.md +0 -0
  294. /package/examples/ownership-transfer/.aug-spec/august/{0.20.1 → 0.21.0}/io/contracts.aug +0 -0
  295. /package/examples/ownership-transfer/.aug-spec/august/{0.20.1 → 0.21.0}/io/contracts.aug.md +0 -0
@@ -0,0 +1,207 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { chmodSync, closeSync, existsSync, lstatSync, mkdirSync, mkdtempSync, openSync, readFileSync, readSync, readdirSync, renameSync, rmSync, writeFileSync } from 'node:fs';
3
+ import { homedir } from 'node:os';
4
+ import { join, resolve } from 'node:path';
5
+ import { UnpackSync } from 'tar';
6
+ import { withPackageLockAsync } from "./package-locking.js";
7
+ import { nativePath, nativeHostTarget, selectNativeArtifact } from "./native-contracts.js";
8
+ import { compilerVersion, readPackage, sourcePaths, projectPackages } from "./package-manager.js";
9
+ import { sourceAlias } from "./git-packages.js";
10
+ const sha = (bytes) => createHash('sha256').update(bytes).digest('hex');
11
+ const files = (directory, prefix = '') => readdirSync(join(directory, prefix), { withFileTypes: true }).flatMap(entry => {
12
+ const name = prefix ? prefix + '/' + entry.name : entry.name;
13
+ if (entry.isSymbolicLink())
14
+ throw new Error('NATIVE_INTEGRITY: Native archives cannot contain symbolic links');
15
+ return entry.isDirectory() ? files(directory, name) : entry.isFile() ? [name] : (() => { throw new Error('NATIVE_INTEGRITY: Native archives require regular files'); })();
16
+ });
17
+ /** Verify the complete extracted file set. Hashes never authorize arbitrary install scripts. */
18
+ export function verifyArtifactFiles(directory, archive) {
19
+ const path = nativePath(archive.fileManifest), manifest = JSON.parse(readFileSync(join(directory, path), 'utf8'));
20
+ if (manifest.format !== 1 || !manifest.files || Array.isArray(manifest.files) || typeof manifest.files !== 'object')
21
+ throw new Error('NATIVE_INTEGRITY: Invalid artifact file manifest');
22
+ const expected = manifest.files, actual = files(directory).sort();
23
+ if (JSON.stringify(actual) !== JSON.stringify([...Object.keys(expected), path].sort()))
24
+ throw new Error('NATIVE_INTEGRITY: Artifact file set differs from its manifest');
25
+ for (const [file, digest] of Object.entries(expected)) {
26
+ nativePath(file);
27
+ if (!/^[0-9a-f]{64}$/.test(digest) || !lstatSync(join(directory, file)).isFile() || sha(readFileSync(join(directory, file))) !== digest)
28
+ throw new Error('NATIVE_INTEGRITY: Artifact file hash mismatch: ' + file);
29
+ }
30
+ return expected;
31
+ }
32
+ /** Bounded HTTPS downloads with checked redirects. No external downloader/toolchain is used. */
33
+ export async function downloadVerified(archive) {
34
+ if (!/^[0-9a-f]{64}$/.test(archive.sha256) || !Number.isSafeInteger(archive.maximumDownloadBytes) || archive.maximumDownloadBytes < 1)
35
+ throw new Error('NATIVE_INTEGRITY: Invalid archive digest or size bound');
36
+ let url = new URL(archive.url), response;
37
+ const signal = AbortSignal.timeout(120000);
38
+ for (let i = 0; i <= 5; i++) {
39
+ if (url.protocol !== 'https:' || url.username || url.password || url.hash)
40
+ throw new Error('NATIVE_INTEGRITY: Artifact downloads and redirects require HTTPS without credentials');
41
+ response = await fetch(url, { redirect: 'manual', signal });
42
+ if ([301, 302, 303, 307, 308].includes(response.status)) {
43
+ const location = response.headers.get('location');
44
+ await response.body?.cancel();
45
+ if (!location || i === 5)
46
+ throw new Error('NATIVE_DOWNLOAD: Too many or invalid redirects');
47
+ url = new URL(location, url);
48
+ continue;
49
+ }
50
+ break;
51
+ }
52
+ if (!response?.ok || !response.body)
53
+ throw new Error(`NATIVE_DOWNLOAD: Missing artifact (${response?.status}) at ${archive.url}. Ask the package maintainer to publish the matching artifact. No source build was started.`);
54
+ if (Number(response.headers.get('content-length') ?? 0) > archive.maximumDownloadBytes) {
55
+ await response.body.cancel();
56
+ throw new Error('NATIVE_INTEGRITY: Artifact exceeds its download limit');
57
+ }
58
+ const chunks = [];
59
+ let length = 0;
60
+ for await (const chunk of response.body) {
61
+ length += chunk.length;
62
+ if (length > archive.maximumDownloadBytes)
63
+ throw new Error('NATIVE_INTEGRITY: Artifact exceeds its download limit');
64
+ chunks.push(Buffer.from(chunk));
65
+ }
66
+ const bytes = Buffer.concat(chunks);
67
+ if (sha(bytes) !== archive.sha256)
68
+ throw new Error('NATIVE_INTEGRITY: Downloaded artifact SHA-256 does not match the locked package');
69
+ return bytes;
70
+ }
71
+ /** Content-addressed immutable installs; failed extraction never installs an accepted cache. */
72
+ export async function ensureVerifiedArchive(archive, options = {}) {
73
+ if (!/^[0-9a-f]{64}$/.test(archive.sha256) || !Number.isSafeInteger(archive.maximumUnpackedBytes) || archive.maximumUnpackedBytes < 1)
74
+ throw new Error('NATIVE_INTEGRITY: Invalid archive digest or unpacked size bound');
75
+ nativePath(archive.fileManifest);
76
+ const cache = resolve(options.cache ?? process.env.AUG_NATIVE_ARTIFACT_CACHE ?? join(homedir(), '.cache/augscript/native-artifacts'));
77
+ mkdirSync(cache, { recursive: true });
78
+ const destination = join(cache, archive.sha256);
79
+ return withPackageLockAsync(destination + '.lock', async () => {
80
+ if (existsSync(destination)) {
81
+ if (lstatSync(destination).isSymbolicLink())
82
+ throw new Error('NATIVE_INTEGRITY: Artifact cache cannot be a symbolic link');
83
+ verifyArtifactFiles(destination, archive);
84
+ return destination;
85
+ }
86
+ if (options.offline)
87
+ throw new Error('NATIVE_OFFLINE: Locked native artifact is not cached. Run aug install online once. ' + archive.url);
88
+ const stage = mkdtempSync(join(cache, '.install-')), transport = join(stage, 'download.tar.gz'), output = join(stage, 'files');
89
+ mkdirSync(output);
90
+ try {
91
+ writeFileSync(transport, await downloadVerified(archive));
92
+ const seen = new Set();
93
+ let unpacked = 0;
94
+ const extractor = new UnpackSync({ cwd: output, strict: true, preserveOwner: false, filter: (path, entry) => {
95
+ const name = path.replace(/\/$/, '');
96
+ nativePath(name);
97
+ if (!('type' in entry))
98
+ throw new Error('NATIVE_INTEGRITY: Expected an archive entry');
99
+ if (!['File', 'Directory'].includes(entry.type))
100
+ throw new Error('NATIVE_INTEGRITY: Archive links and special files are forbidden');
101
+ if (seen.has(name))
102
+ throw new Error('NATIVE_INTEGRITY: Duplicate archive path ' + name);
103
+ seen.add(name);
104
+ unpacked += entry.size;
105
+ if (seen.size > 20000 || unpacked > archive.maximumUnpackedBytes)
106
+ throw new Error('NATIVE_INTEGRITY: Archive exceeds its unpacked size or file limit');
107
+ return true;
108
+ } });
109
+ // tar's synchronous file convenience API has no error listener. A disk
110
+ // write failure can otherwise become an uncaught stream event instead of
111
+ // the CLI's actionable error and rejected-install cleanup.
112
+ let extractionError;
113
+ extractor.on('error', (error) => { extractionError ??= error; });
114
+ const input = openSync(transport, 'r');
115
+ try {
116
+ let length;
117
+ do {
118
+ const buffer = Buffer.allocUnsafe(16 * 1024 * 1024);
119
+ length = readSync(input, buffer, 0, buffer.length, null);
120
+ if (length)
121
+ extractor.write(buffer.subarray(0, length));
122
+ if (extractionError)
123
+ throw extractionError;
124
+ } while (length);
125
+ extractor.end();
126
+ if (extractionError)
127
+ throw extractionError;
128
+ }
129
+ finally {
130
+ closeSync(input);
131
+ }
132
+ verifyArtifactFiles(output, archive);
133
+ for (const executable of options.executables ?? []) {
134
+ nativePath(executable);
135
+ if (!lstatSync(join(output, executable)).isFile())
136
+ throw new Error('NATIVE_INTEGRITY: Missing verified executable ' + executable);
137
+ chmodSync(join(output, executable), 0o755);
138
+ }
139
+ renameSync(output, destination);
140
+ return destination;
141
+ }
142
+ finally {
143
+ rmSync(stage, { recursive: true, force: true });
144
+ }
145
+ });
146
+ }
147
+ /** Extend the existing source lock with exact native target selections. */
148
+ export async function prepareNativePackages(root, options = {}) {
149
+ const lockFile = join(root, 'aug.lock.json'), self = existsSync(join(root, 'aug-package.json')) ? readPackage(root) : undefined;
150
+ if (!existsSync(lockFile) && !self?.manifest.native)
151
+ return [];
152
+ return withPackageLockAsync(join(root, '.aug-install.lock'), async () => {
153
+ const initial = existsSync(lockFile) ? readFileSync(lockFile, 'utf8') : undefined;
154
+ const lock = initial ? JSON.parse(initial) : { format: 1, compiler: compilerVersion(), specifications: {}, roots: {}, packages: [], npm: {} };
155
+ if (lock.compiler !== compilerVersion())
156
+ throw new Error('NATIVE_LOCK: Source lock compiler differs; run aug install');
157
+ const target = options.target ?? nativeHostTarget(), key = target.triple + '/' + (target.os === 'macos' ? 'macos14' : target.libc);
158
+ const verified = projectPackages(root, lock.specifications);
159
+ if (verified.diagnostics.length)
160
+ throw new Error(verified.diagnostics.map(d => d.message).join('\n'));
161
+ const packages = [...verified.scopes.values()].filter(entry => entry.native).map(entry => ({ sourcePackage: entry.name + '@' + entry.version, sourceDigest: entry.digest,
162
+ sourceCommit: lock.git?.find(g => entry.path === 'packages/' + sourceAlias(g.request))?.commit,
163
+ contractSha256: entry.native.bindingsSha256, artifact: selectNativeArtifact(entry.native.artifacts, target) }));
164
+ if (self?.manifest.native) {
165
+ const digest = createHash('sha256');
166
+ for (const file of ['aug-package.json', 'native.abi.json', ...sourcePaths(self.sourceRoot).map(path => path.slice(root.length + 1))].sort())
167
+ digest.update(file + '\0').update(readFileSync(join(root, file))).update('\0');
168
+ packages.push({ sourcePackage: self.manifest.name + '@' + self.manifest.version, sourceDigest: digest.digest('hex'), sourceCommit: undefined, contractSha256: self.manifest.native.bindingsSha256, artifact: selectNativeArtifact(self.manifest.native.artifacts, target) });
169
+ }
170
+ if (!packages.length)
171
+ return [];
172
+ const native = lock.native ?? { format: 1, targets: {} };
173
+ if (native.format !== 1)
174
+ throw new Error('NATIVE_LOCK: Unsupported native lock schema');
175
+ const previous = native.targets[key];
176
+ if (options.frozen && (!previous || JSON.stringify(previous.packages) !== JSON.stringify(packages)))
177
+ throw new Error('NATIVE_LOCK: Frozen install has no matching native target lock. Run aug install online to record this target.');
178
+ const components = new Map();
179
+ for (const p of packages)
180
+ for (const c of p.artifact.components)
181
+ if (c.required) {
182
+ const identity = c.id + '@' + c.version;
183
+ if (components.has(c.compatibilityKey) && components.get(c.compatibilityKey) !== identity)
184
+ throw new Error('NATIVE_CONFLICT: Incompatible native component ' + c.compatibilityKey);
185
+ components.set(c.compatibilityKey, identity);
186
+ }
187
+ const inputs = [];
188
+ for (const p of packages) {
189
+ const directory = await ensureVerifiedArchive(p.artifact, { offline: options.offline });
190
+ const supplied = verifyArtifactFiles(directory, p.artifact);
191
+ for (const path of [...p.artifact.link.libraries, ...p.artifact.runtime.files, p.artifact.provenance, p.artifact.notices, ...(p.artifact.runtime.closureManifest ? [p.artifact.runtime.closureManifest] : [])])
192
+ if (!supplied[path])
193
+ throw new Error('NATIVE_INTEGRITY: Artifact is missing declared file ' + path);
194
+ inputs.push({ directory, libraries: p.artifact.link.libraries, runtimeFiles: p.artifact.runtime.files, artifactSha256: p.artifact.sha256,
195
+ metadata: [...Object.keys(supplied).filter(path => !p.artifact.link.libraries.includes(path) && !p.artifact.runtime.files.includes(path)), p.artifact.fileManifest] });
196
+ }
197
+ if (packages.length && !options.frozen) {
198
+ native.targets[key] = { target, packages };
199
+ lock.native = native;
200
+ if ((existsSync(lockFile) ? readFileSync(lockFile, 'utf8') : undefined) !== initial)
201
+ throw new Error('NATIVE_LOCK: Source lock changed during native installation; retry aug install');
202
+ writeFileSync(lockFile + '.native.tmp', JSON.stringify(lock, null, 2) + '\n');
203
+ renameSync(lockFile + '.native.tmp', lockFile);
204
+ }
205
+ return inputs;
206
+ });
207
+ }
@@ -0,0 +1,189 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { existsSync, mkdtempSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from 'node:fs';
3
+ import { tmpdir } from 'node:os';
4
+ import { dirname, join, relative, resolve } from 'node:path';
5
+ import { spawnSync } from 'node:child_process';
6
+ import { nativeTarget, validateNativeDescriptor } from "./native-contracts.js";
7
+ const fail = (message) => { throw new Error('NATIVE_HEADER: ' + message); };
8
+ const hash = (path) => createHash('sha256').update(readFileSync(path)).digest('hex');
9
+ const scalar = (view) => ({ void: 'void', i64: 'int64_t', i32: 'int32_t', f64: 'double', bool: 'uint8_t' }[view.kind]);
10
+ /** Check a maintainer-supplied ownership contract against real C declarations.
11
+ * Clang is invoked only by this explicit command, never by package installation.
12
+ * The result records physical checks, not proof of allocator or thread promises.
13
+ */
14
+ export function bindNativeHeader(options) {
15
+ const header = resolve(options.header), contract = resolve(options.contract), output = resolve(options.output), flags = options.flags ?? [];
16
+ nativeTarget(options.target);
17
+ if (existsSync(output))
18
+ fail('Output already exists. Select a new directory; accepted bindings are never overwritten.');
19
+ const descriptor = validateNativeDescriptor(JSON.parse(readFileSync(contract, 'utf8'))), initialHeader = hash(header), initialContract = hash(contract);
20
+ const invoke = (args) => {
21
+ const result = spawnSync(options.clang, args, { encoding: 'utf8', timeout: 60000, maxBuffer: 32 * 1024 * 1024 });
22
+ if (result.status !== 0)
23
+ fail('Clang rejected the header or ABI probe. ' + (result.error?.message ?? result.stderr.split('\n').filter(line => !/^\.+ /.test(line)).join('\n').slice(0, 8000)));
24
+ return result;
25
+ };
26
+ const compiler = invoke(['--version']).stdout.trim();
27
+ if (!/clang version\s+(\d+)/.test(compiler))
28
+ fail('The selected executable is not Clang.');
29
+ // Force parsing and probing as C. C++ libraries expose a C adapter header.
30
+ const base = [...flags, '-target', options.target, '-std=c11', '-x', 'c'];
31
+ const includePaths = (stderr) => stderr.split('\n').flatMap(line => { const match = /^\.+ (.+)$/.exec(line); return match && existsSync(match[1]) ? [resolve(match[1])] : []; });
32
+ const parsed = invoke([...base, '-H', '-Xclang', '-ast-dump=json', '-fsyntax-only', header]);
33
+ const ast = JSON.parse(parsed.stdout);
34
+ const headerInputs = new Map([...new Set([header, ...includePaths(parsed.stderr)])].map(path => [path, hash(path)]));
35
+ const declarations = new Map(), records = new Map(), aliases = new Map();
36
+ const visit = (node) => {
37
+ if (node.kind === 'FunctionDecl' && node.name) {
38
+ const previous = declarations.get(node.name);
39
+ if (previous && previous.type?.qualType !== node.type?.qualType)
40
+ fail('Conflicting declarations for ' + node.name);
41
+ declarations.set(node.name, node);
42
+ }
43
+ if (node.kind === 'RecordDecl' && node.name) {
44
+ records.set(node.name, node);
45
+ if (node.id)
46
+ records.set(node.id, node);
47
+ }
48
+ if (node.kind === 'TypedefDecl' && node.name)
49
+ aliases.set(node.name, node);
50
+ for (const child of node.inner ?? [])
51
+ visit(child);
52
+ };
53
+ visit(ast);
54
+ const get = (symbol) => declarations.get(symbol) ?? fail('Missing C declaration for ' + symbol);
55
+ const params = (node) => (node.inner ?? []).filter(child => child.kind === 'ParmVarDecl').map(child => child.type.qualType);
56
+ const opaque = new Map();
57
+ for (const resource of descriptor.resources) {
58
+ const declaration = get(resource.release), arguments_ = params(declaration);
59
+ if (arguments_.length !== 1)
60
+ fail(resource.release + ' must release exactly one opaque pointer.');
61
+ const type = arguments_[0].replace(/\s+/g, ' ').trim();
62
+ if (!/^(?:struct )?[A-Za-z_]\w* \*$/.test(type) || type.startsWith('const '))
63
+ fail(resource.release + ' requires a mutable opaque pointer.');
64
+ const name = type.replace(/ \*$/, '').replace(/^struct /, '');
65
+ if (name !== 'void') {
66
+ let record = records.get(name);
67
+ const findRecord = (node) => { if (node.kind === 'RecordType' && node.decl)
68
+ record = records.get(node.decl.id); for (const child of node.inner ?? [])
69
+ findRecord(child); };
70
+ const alias = aliases.get(name);
71
+ if (alias)
72
+ findRecord(alias);
73
+ if (!record || record.completeDefinition)
74
+ fail('Resource ' + resource.name + ' requires an incomplete C record or void pointer; use an opaque adapter.');
75
+ }
76
+ opaque.set(resource.module + '.' + resource.name, type);
77
+ }
78
+ const checks = [], signatures = [];
79
+ const check = (symbol, result, arguments_) => {
80
+ get(symbol);
81
+ const index = checks.length;
82
+ checks.push(`typedef ${result} (*aug_check_${index})(${arguments_.join(', ') || 'void'});\n_Static_assert(__builtin_types_compatible_p(__typeof__(&${symbol}), aug_check_${index}), "ABI mismatch: ${symbol}");`);
83
+ signatures.push({ symbol, parameters: arguments_, result });
84
+ };
85
+ for (const resource of descriptor.resources)
86
+ check(resource.release, 'void', [opaque.get(resource.module + '.' + resource.name)]);
87
+ const input = (view, actual) => {
88
+ const primitive = scalar(view);
89
+ if (primitive)
90
+ return [primitive];
91
+ if (view.kind === 'resource') {
92
+ const type = opaque.get(view.resource), unqualified = actual.replace(/^const\s+/, '').replace(/\s+/g, ' ').trim();
93
+ if (unqualified !== type || view.ownership !== 'read' && /^const\s/.test(actual))
94
+ fail('Resource pointer or mutable loan differs for ' + view.resource);
95
+ return [actual];
96
+ }
97
+ if (view.kind === 'f64-list')
98
+ return ['const double *', 'uint64_t'];
99
+ if (view.kind === 'utf8-list')
100
+ return ['const void *const *', 'const uint64_t *', 'uint64_t'];
101
+ return ['const void *', 'uint64_t'];
102
+ };
103
+ for (const fn of descriptor.functions) {
104
+ const actual = params(get(fn.symbol)), arguments_ = [];
105
+ for (const view of fn.params)
106
+ arguments_.push(...input(view, actual[arguments_.length] ?? ''));
107
+ if (fn.status === 'i32') {
108
+ const result = scalar(fn.result);
109
+ if (fn.result.kind === 'resource')
110
+ arguments_.push(opaque.get(fn.result.resource) + '*');
111
+ else if (result && result !== 'void')
112
+ arguments_.push(result + ' *');
113
+ else if (!result)
114
+ arguments_.push(fn.result.kind === 'f64-list' ? 'double **' : 'void **', 'uint64_t *');
115
+ arguments_.push('aug_native_error_v1 *');
116
+ }
117
+ check(fn.symbol, fn.status === 'i32' ? 'int32_t' : scalar(fn.result), arguments_);
118
+ if (fn.result.release)
119
+ check(fn.result.release, 'void', [fn.result.kind === 'f64-list' ? 'double *' : 'void *', ...(fn.result.releaseLength ? ['uint64_t'] : [])]);
120
+ }
121
+ const stage = mkdtempSync(join(tmpdir(), 'aug-header-check-'));
122
+ try {
123
+ const probe = join(stage, 'probe.c');
124
+ const layout = descriptor.functions.some(fn => fn.status === 'i32') ? `
125
+ _Static_assert(sizeof(aug_native_error_v1)==520, "error size");
126
+ _Static_assert(_Alignof(aug_native_error_v1)==4, "error alignment");
127
+ _Static_assert(offsetof(aug_native_error_v1,code)==0, "error code offset");
128
+ _Static_assert(offsetof(aug_native_error_v1,message_length)==4, "error length offset");
129
+ _Static_assert(offsetof(aug_native_error_v1,message)==8, "error buffer offset");
130
+ _Static_assert(__builtin_types_compatible_p(__typeof__(((aug_native_error_v1*)0)->code),int32_t), "error code type");
131
+ _Static_assert(__builtin_types_compatible_p(__typeof__(((aug_native_error_v1*)0)->message_length),uint32_t), "error length type");
132
+ _Static_assert(__builtin_types_compatible_p(__typeof__(((aug_native_error_v1*)0)->message),unsigned char[512]), "error buffer type");` : '';
133
+ writeFileSync(probe, '#include <stdint.h>\n#include <stddef.h>\n#include ' + JSON.stringify(header) + '\n' + layout + '\n' + checks.join('\n') + '\n');
134
+ const verified = invoke([...base, '-Werror', '-H', '-fsyntax-only', probe]);
135
+ const included = [...new Set([header, ...includePaths(verified.stderr)])].sort();
136
+ const inputs = included.map(path => ({ path: relative(dirname(header), path).replaceAll('\\', '/'), sha256: hash(path) }));
137
+ for (const [path, digest] of headerInputs)
138
+ if (!existsSync(path) || hash(path) !== digest)
139
+ fail('An included header changed during validation; retry.');
140
+ if (hash(header) !== initialHeader || hash(contract) !== initialContract)
141
+ fail('Header or ownership contract changed during validation; retry.');
142
+ mkdirSync(dirname(output), { recursive: true });
143
+ const generated = mkdtempSync(join(dirname(output), '.aug-bind-'));
144
+ try {
145
+ writeFileSync(join(generated, 'native.abi.json'), JSON.stringify(descriptor, null, 2) + '\n');
146
+ writeFileSync(join(generated, 'header-check.json'), JSON.stringify({ format: 1, profile: descriptor.profile, target: options.target, compiler, flags, contractSha256: initialContract, inputs, signatures, errorLayout: layout ? { size: 520, alignment: 4, offsets: [0, 4, 8] } : undefined, ownership: 'author-declared', retention: 'author-declared', allocatorPairing: 'author-declared' }, null, 2) + '\n');
147
+ emitDeclarations(descriptor, generated);
148
+ renameSync(generated, output);
149
+ }
150
+ finally {
151
+ rmSync(generated, { recursive: true, force: true });
152
+ }
153
+ }
154
+ finally {
155
+ rmSync(stage, { recursive: true, force: true });
156
+ }
157
+ }
158
+ function emitDeclarations(descriptor, directory) {
159
+ const modules = new Map();
160
+ const module = (name) => { if (!modules.has(name))
161
+ modules.set(name, { imports: new Set(), lines: [] }); return modules.get(name); };
162
+ const type = (view, current) => {
163
+ if (view.kind === 'resource') {
164
+ const dot = view.resource.lastIndexOf('.'), source = view.resource.slice(0, dot), name = view.resource.slice(dot + 1);
165
+ if (source !== current)
166
+ module(current).imports.add(`import ${name} from ${source.replaceAll('/', '.')}`);
167
+ return name;
168
+ }
169
+ return ({ void: 'void', i64: 'int', i32: 'c_int', f64: 'float', bool: 'bool', utf8: 'string', bytes: 'Bytes', 'f64-list': 'List<float>', 'utf8-list': 'List<string>' })[view.kind];
170
+ };
171
+ for (const resource of descriptor.resources)
172
+ module(resource.module).lines.push('extern C resource ' + resource.name);
173
+ for (const fn of descriptor.functions) {
174
+ const parameters = fn.params.map(view => (view.ownership === 'consume' ? 'own ' : view.ownership === 'borrow' ? 'borrow ' : '') + type(view, fn.module) + ' ' + view.name);
175
+ let error = '';
176
+ if (fn.error) {
177
+ const dot = fn.error.lastIndexOf('.'), name = fn.error.slice(dot + 1), source = fn.error.slice(0, dot);
178
+ if (source !== fn.module)
179
+ module(fn.module).imports.add(`import ${name} from ${source.replaceAll('/', '.')}`);
180
+ error = ' unless ' + name;
181
+ }
182
+ module(fn.module).lines.push(`extern C ${fn.name}(${parameters.join(', ')}) returns ${fn.result.kind === 'resource' ? 'own ' : ''}${type(fn.result, fn.module)}${error}${fn.uses.length ? ' uses ' + fn.uses.join(' and ') : ''}${fn.changes.length ? ' changes ' + fn.changes.join(' and ') : ''}`);
183
+ }
184
+ for (const [name, value] of [...modules].sort(([a], [b]) => a.localeCompare(b, 'en'))) {
185
+ const file = join(directory, 'src', name + '.aug');
186
+ mkdirSync(dirname(file), { recursive: true });
187
+ writeFileSync(file, [...value.imports].sort().concat(value.lines).join('\n') + '\n');
188
+ }
189
+ }