@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,295 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { readFileSync, realpathSync } from 'node:fs';
3
+ import { isAbsolute, relative, resolve, sep } from 'node:path';
4
+ import { release } from 'node:os';
5
+ /** The public native ABI never exposes August runtime layouts. */
6
+ export const nativeProfile = 'aug-native-abi-1';
7
+ const digest = /^[0-9a-f]{64}$/;
8
+ const identifier = /^[A-Za-z_][A-Za-z0-9_]*$/;
9
+ const moduleName = /^[A-Za-z][A-Za-z0-9_]*(?:\/[A-Za-z][A-Za-z0-9_]*)*$/;
10
+ const fail = (message) => { throw new Error('NATIVE_ABI: ' + message); };
11
+ const object = (value, label) => {
12
+ if (!value || typeof value !== 'object' || Array.isArray(value))
13
+ fail(label + ' must be an object');
14
+ return value;
15
+ };
16
+ const fields = (value, allowed, label) => {
17
+ for (const key of Object.keys(value))
18
+ if (!allowed.includes(key))
19
+ fail(`${label} contains unsupported field ${key}`);
20
+ };
21
+ const string = (value, label) => {
22
+ if (typeof value !== 'string' || !value || value.includes('\0'))
23
+ fail(label + ' must be a nonempty string');
24
+ return value;
25
+ };
26
+ const array = (value, label) => {
27
+ if (!Array.isArray(value) || value.length > 10000)
28
+ fail(label + ' must be an array of at most 10,000 entries');
29
+ return value;
30
+ };
31
+ const strings = (value, label) => array(value, label).map(item => string(item, label));
32
+ const positive = (value, label) => { if (!Number.isSafeInteger(value) || Number(value) <= 0)
33
+ fail(label + ' must be a positive safe integer'); };
34
+ const hash = (value, label) => { if (typeof value !== 'string' || !digest.test(value))
35
+ fail(label + ' must be a lowercase SHA-256 digest'); };
36
+ const symbol = (value, label) => { if (typeof value !== 'string' || !identifier.test(value))
37
+ fail(label + ' must be a C identifier'); };
38
+ const https = (value, label) => {
39
+ let url;
40
+ try {
41
+ url = new URL(string(value, label));
42
+ }
43
+ catch {
44
+ fail(label + ' must be an HTTPS URL');
45
+ }
46
+ if (url.protocol !== 'https:' || url.username || url.password || url.hash)
47
+ fail(label + ' must be an HTTPS URL without credentials or a fragment');
48
+ };
49
+ /** Artifact paths are portable and cannot name a file outside their archive. */
50
+ export function nativePath(value) {
51
+ const path = string(value, 'relative artifact path');
52
+ if (isAbsolute(path) || path.includes('\\') || path.split('/').some(part => !part || part === '.' || part === '..' || part.startsWith('.')) || /[:\r\n]/.test(path))
53
+ fail('Expected a relative artifact path without traversal: ' + path);
54
+ return path;
55
+ }
56
+ /** Source installation validates metadata; it does not run a native build recipe. */
57
+ export function validateNativeManifest(value) {
58
+ const native = object(value, 'native metadata');
59
+ fields(native, ['profile', 'bindings', 'bindingsSha256', 'upstream', 'artifacts', 'sourceBuild'], 'native metadata');
60
+ if (native.profile !== nativeProfile)
61
+ fail('Unsupported native profile ' + native.profile);
62
+ nativePath(native.bindings);
63
+ if (native.bindings !== 'native.abi.json')
64
+ fail('Bindings must be declared in native.abi.json at the package root');
65
+ hash(native.bindingsSha256, 'bindingsSha256');
66
+ const upstream = object(native.upstream, 'upstream');
67
+ fields(upstream, ['repository', 'version', 'sourceRevision'], 'upstream');
68
+ https(upstream.repository, 'upstream.repository');
69
+ string(upstream.version, 'upstream.version');
70
+ if (!/^[0-9a-f]{40,64}$/.test(upstream.sourceRevision ?? ''))
71
+ fail('upstream.sourceRevision must pin a full source revision or archive digest');
72
+ const artifacts = array(native.artifacts, 'artifacts');
73
+ if (!artifacts.length)
74
+ fail('At least one prebuilt artifact is required');
75
+ const ids = new Set(), selections = new Set();
76
+ for (const entry of artifacts) {
77
+ const artifact = object(entry, 'artifact');
78
+ fields(artifact, ['id', 'target', 'url', 'sha256', 'maximumDownloadBytes', 'maximumUnpackedBytes', 'link', 'runtime', 'components', 'fileManifest', 'provenance', 'notices'], 'artifact');
79
+ string(artifact.id, 'artifact.id');
80
+ if (ids.has(artifact.id))
81
+ fail('Duplicate artifact id ' + artifact.id);
82
+ ids.add(artifact.id);
83
+ const target = object(artifact.target, 'artifact.target');
84
+ fields(target, ['triple', 'os', 'arch', 'minimumOS', 'minimumLibc', 'cpuBaseline', 'libc', 'cxxRuntime', 'cxxABI', 'features'], 'artifact.target');
85
+ const expected = nativeTarget(string(target.triple, 'target.triple'));
86
+ if (expected.os !== target.os || expected.arch !== target.arch || expected.libc !== target.libc)
87
+ fail('Target triple does not match its OS, architecture, or libc');
88
+ string(target.cpuBaseline, 'target.cpuBaseline');
89
+ if (target.os === 'macos' && (!/^\d+\.\d+(?:\.\d+)?$/.test(target.minimumOS ?? '')))
90
+ fail('macOS artifacts declare minimumOS');
91
+ if (target.os === 'linux' && target.libc === 'glibc' && !/^\d+\.\d+(?:\.\d+)?$/.test(target.minimumLibc ?? ''))
92
+ fail('GNU/Linux artifacts declare minimumLibc');
93
+ if (target.minimumLibc !== undefined && (target.os !== 'linux' || target.libc !== 'glibc'))
94
+ fail('minimumLibc belongs to GNU/Linux artifacts');
95
+ for (const key of ['cxxRuntime', 'cxxABI'])
96
+ if (target[key] !== undefined)
97
+ string(target[key], key);
98
+ if (target.features !== undefined)
99
+ strings(target.features, 'target.features');
100
+ const selection = JSON.stringify([target.triple, target.minimumOS ?? '', target.minimumLibc ?? '', target.features ?? []]);
101
+ if (selections.has(selection))
102
+ fail('Ambiguous artifact selection for ' + target.triple);
103
+ selections.add(selection);
104
+ https(artifact.url, 'artifact.url');
105
+ hash(artifact.sha256, 'artifact.sha256');
106
+ positive(artifact.maximumDownloadBytes, 'maximumDownloadBytes');
107
+ positive(artifact.maximumUnpackedBytes, 'maximumUnpackedBytes');
108
+ const link = object(artifact.link, 'link');
109
+ fields(link, ['kind', 'libraries'], 'link');
110
+ if (!['dynamic', 'static'].includes(link.kind))
111
+ fail('link.kind must be dynamic or static');
112
+ if (!array(link.libraries, 'link.libraries').length)
113
+ fail('An artifact must supply link libraries');
114
+ link.libraries.forEach(nativePath);
115
+ const runtime = object(artifact.runtime, 'runtime');
116
+ fields(runtime, ['files', 'relocation', 'closureManifest'], 'runtime');
117
+ array(runtime.files, 'runtime.files').forEach(nativePath);
118
+ if (runtime.relocation !== 'loader-relative')
119
+ fail('Runtime libraries must use loader-relative relocation');
120
+ if (runtime.closureManifest !== undefined)
121
+ nativePath(runtime.closureManifest);
122
+ for (const component of array(artifact.components, 'components')) {
123
+ const c = object(component, 'component');
124
+ fields(c, ['id', 'version', 'compatibilityKey', 'linkage', 'required'], 'component');
125
+ for (const key of ['id', 'version', 'compatibilityKey'])
126
+ string(c[key], key);
127
+ if (!['static', 'dynamic'].includes(c.linkage) || typeof c.required !== 'boolean')
128
+ fail('Invalid component linkage/required');
129
+ }
130
+ for (const key of ['fileManifest', 'provenance', 'notices'])
131
+ nativePath(artifact[key]);
132
+ }
133
+ if (native.sourceBuild !== undefined) {
134
+ const build = object(native.sourceBuild, 'sourceBuild');
135
+ fields(build, ['recipe', 'inputs', 'tools', 'automatic'], 'sourceBuild');
136
+ nativePath(build.recipe);
137
+ nativePath(build.inputs);
138
+ strings(build.tools, 'sourceBuild.tools');
139
+ if (build.automatic !== false)
140
+ fail('Native source builds must be explicitly requested; automatic must be false');
141
+ }
142
+ return native;
143
+ }
144
+ function view(value, result) {
145
+ const v = object(value, 'native view');
146
+ fields(v, ['kind', 'resource', 'ownership', 'release', 'releaseLength', 'minimum', 'maximum', ...(result ? [] : ['name'])], 'native view');
147
+ if (!['void', 'i64', 'i32', 'f64', 'bool', 'utf8', 'bytes', 'f64-list', 'utf8-list', 'resource'].includes(v.kind))
148
+ fail('Unsupported native kind ' + v.kind);
149
+ if (v.kind === 'void' && !result)
150
+ fail('void is only a result');
151
+ if (v.kind === 'resource') {
152
+ string(v.resource, 'resource identity');
153
+ if (!result && !['read', 'borrow', 'consume'].includes(v.ownership))
154
+ fail('A resource input requires read, borrow, or consume ownership');
155
+ if (result && v.ownership !== undefined)
156
+ fail('Resource results always transfer ownership; do not declare an input loan');
157
+ }
158
+ else if (v.resource !== undefined || v.ownership !== undefined)
159
+ fail('Resource ownership is only valid on resources');
160
+ if (result && ['utf8', 'bytes', 'f64-list', 'utf8-list'].includes(v.kind) && !v.release)
161
+ fail('Allocated native results require a matching release symbol');
162
+ if (result && v.kind === 'utf8-list')
163
+ fail('utf8-list outputs are not in this ABI profile');
164
+ if (v.release !== undefined)
165
+ symbol(v.release, 'result.release');
166
+ if ((v.release !== undefined || v.releaseLength !== undefined) && (!result || !['utf8', 'bytes', 'f64-list'].includes(v.kind)))
167
+ fail('Release metadata belongs only to allocated buffer results');
168
+ if (v.releaseLength !== undefined && typeof v.releaseLength !== 'boolean')
169
+ fail('releaseLength must be boolean');
170
+ if ((v.minimum !== undefined || v.maximum !== undefined) && !['i64', 'i32'].includes(v.kind))
171
+ fail('Integer bounds require i64 or i32');
172
+ for (const key of ['minimum', 'maximum'])
173
+ if (v[key] !== undefined && !Number.isSafeInteger(v[key]))
174
+ fail(key + ' must be a safe integer');
175
+ if (v.minimum !== undefined && v.maximum !== undefined && v.minimum > v.maximum)
176
+ fail('Empty native integer range');
177
+ return v;
178
+ }
179
+ export function validateNativeDescriptor(value) {
180
+ const descriptor = object(value, 'binding descriptor');
181
+ fields(descriptor, ['format', 'profile', 'resources', 'functions'], 'binding descriptor');
182
+ if (descriptor.format !== 1 || descriptor.profile !== nativeProfile)
183
+ fail('Unsupported binding descriptor version/profile');
184
+ const ids = new Set();
185
+ for (const entry of array(descriptor.resources, 'resources')) {
186
+ const r = object(entry, 'resource');
187
+ fields(r, ['module', 'name', 'release'], 'resource');
188
+ if (!moduleName.test(r.module ?? ''))
189
+ fail('Resource module must be a source-relative module path');
190
+ symbol(r.name, 'resource.name');
191
+ symbol(r.release, 'resource.release');
192
+ const id = r.module + '.' + r.name;
193
+ if (ids.has(id))
194
+ fail('Duplicate native resource ' + id);
195
+ ids.add(id);
196
+ }
197
+ const resources = new Set(ids);
198
+ ids.clear();
199
+ for (const entry of array(descriptor.functions, 'functions')) {
200
+ const fn = object(entry, 'function');
201
+ fields(fn, ['module', 'name', 'symbol', 'params', 'result', 'error', 'callingConvention', 'status', 'uses', 'changes', 'thread', 'retainsInputs'], 'function');
202
+ if (!moduleName.test(fn.module ?? ''))
203
+ fail('Function module must be a source-relative module path');
204
+ symbol(fn.name, 'function.name');
205
+ symbol(fn.symbol, 'function.symbol');
206
+ const id = fn.module + '.' + fn.name;
207
+ if (ids.has(id))
208
+ fail('Duplicate native function ' + id);
209
+ ids.add(id);
210
+ const names = new Set();
211
+ for (const p of array(fn.params, 'function.params')) {
212
+ view(p, false);
213
+ if (p.kind === 'resource' && !resources.has(p.resource))
214
+ fail('Unknown resource identity ' + p.resource);
215
+ symbol(p.name, 'parameter.name');
216
+ if (names.has(p.name))
217
+ fail('Duplicate parameter ' + p.name);
218
+ names.add(p.name);
219
+ }
220
+ const result = view(fn.result, true);
221
+ if (result.kind === 'resource' && !resources.has(result.resource))
222
+ fail('Unknown result resource identity ' + result.resource);
223
+ if (fn.callingConvention !== 'C' || !['i32', 'direct'].includes(fn.status) || fn.thread !== 'caller' || fn.retainsInputs !== false)
224
+ fail('Only C calls on the caller thread with call-duration loans are supported');
225
+ if (fn.error !== undefined) {
226
+ string(fn.error, 'function.error');
227
+ const dot = fn.error.lastIndexOf('.');
228
+ if (!moduleName.test(fn.error.slice(0, dot)) || !identifier.test(fn.error.slice(dot + 1)))
229
+ fail('Native checked errors require a module-qualified declaration identity');
230
+ }
231
+ if (fn.status === 'i32' && !fn.error)
232
+ fail('Status-returning native calls require a checked error type');
233
+ if (fn.status === 'direct' && (fn.error || result.kind === 'resource' || ['utf8', 'bytes', 'f64-list', 'utf8-list'].includes(result.kind)))
234
+ fail('Direct native calls support scalar results only');
235
+ strings(fn.uses, 'function.uses');
236
+ strings(fn.changes, 'function.changes');
237
+ }
238
+ return descriptor;
239
+ }
240
+ export function readNativeDescriptor(directory, native) {
241
+ const root = realpathSync(directory), file = realpathSync(resolve(root, native.bindings));
242
+ if (relative(root, file).split(sep).includes('..'))
243
+ fail('Binding descriptor escapes its package');
244
+ const bytes = readFileSync(file);
245
+ if (createHash('sha256').update(bytes).digest('hex') !== native.bindingsSha256)
246
+ throw new Error('NATIVE_INTEGRITY: binding descriptor differs from bindingsSha256 in ' + directory);
247
+ return validateNativeDescriptor(JSON.parse(bytes.toString('utf8')));
248
+ }
249
+ /** A target is a deployment identity, not an assumption that the host can build it. */
250
+ export function nativeTarget(triple, minimumOS) {
251
+ triple ??= process.platform === 'darwin' ? (process.arch === 'arm64' ? 'aarch64' : 'x86_64') + '-apple-darwin' : process.platform === 'linux' ? (process.arch === 'arm64' ? 'aarch64' : 'x86_64') + '-unknown-linux-gnu' : (process.arch === 'arm64' ? 'aarch64' : 'x86_64') + '-pc-windows-msvc';
252
+ const match = /^(aarch64|x86_64)-(apple-darwin|unknown-linux-(gnu|musl)|pc-windows-msvc)$/.exec(triple);
253
+ if (!match)
254
+ throw new Error('NATIVE_TARGET: Unsupported target ' + triple);
255
+ const os = match[2] === 'apple-darwin' ? 'macos' : match[2].startsWith('unknown-linux') ? 'linux' : 'windows';
256
+ const arch = match[1] === 'aarch64' ? 'arm64' : 'x64';
257
+ return { triple, os, arch, cpuBaseline: arch === 'arm64' ? 'armv8-a' : 'x86-64', libc: os === 'macos' ? 'libSystem' : os === 'windows' ? 'ucrt' : match[3] === 'musl' ? 'musl' : 'glibc', ...(os === 'macos' ? { minimumOS: minimumOS ?? '14.0' } : {}) };
258
+ }
259
+ export function nativeHostTarget() {
260
+ // Darwin 23/24 are macOS 14/15. Apple's 2025 numbering change maps Darwin 25 to macOS 26.
261
+ const darwin = Number(release().split('.')[0]), macos = darwin >= 25 ? darwin + 1 : darwin - 9;
262
+ const target = nativeTarget(undefined, process.platform === 'darwin' ? `${macos}.0` : undefined);
263
+ if (process.platform === 'linux') {
264
+ const header = process.report.getReport()?.header;
265
+ if (header?.glibcVersionRuntime)
266
+ target.minimumLibc = header.glibcVersionRuntime;
267
+ else {
268
+ target.libc = 'musl';
269
+ target.triple = target.triple.replace(/-gnu$/, '-musl');
270
+ }
271
+ }
272
+ return target;
273
+ }
274
+ const versionAtLeast = (actual, required) => {
275
+ const a = actual.split('.').map(Number), b = required.split('.').map(Number);
276
+ for (let i = 0; i < Math.max(a.length, b.length); i++) {
277
+ if ((a[i] ?? 0) !== (b[i] ?? 0))
278
+ return (a[i] ?? 0) > (b[i] ?? 0);
279
+ }
280
+ return true;
281
+ };
282
+ export function selectNativeArtifact(artifacts, target) {
283
+ const matching = artifacts.filter(a => a.target.triple === target.triple && a.target.libc === target.libc &&
284
+ a.target.cpuBaseline === target.cpuBaseline && (!a.target.cxxRuntime ||
285
+ a.target.os === 'macos' && a.target.cxxRuntime === 'system-libc++' && a.target.cxxABI === 'apple-libc++' ||
286
+ a.target.os === 'linux' && a.target.cxxRuntime === 'bundled-libstdc++' && a.target.cxxABI === 'itanium-cxx11') &&
287
+ (!a.target.minimumLibc || !!target.minimumLibc && versionAtLeast(target.minimumLibc, a.target.minimumLibc)) &&
288
+ (!a.target.minimumOS || !!target.minimumOS && versionAtLeast(target.minimumOS, a.target.minimumOS)));
289
+ if (matching.length !== 1) {
290
+ const floors = artifacts.filter(a => a.target.triple === target.triple).map(a => a.target.minimumOS ?? a.target.minimumLibc).filter(Boolean);
291
+ const runtime = target.os === 'linux' ? 'glibc' : 'macOS', actual = target.os === 'linux' ? target.minimumLibc : target.minimumOS;
292
+ throw new Error('NATIVE_TARGET: ' + (matching.length ? 'Ambiguous prebuilt artifacts' : floors.length ? `This package requires ${runtime} ${floors.join(' or ')}+ and its declared C++ ABI; requested ${actual ?? 'unknown'}.` : 'No compatible prebuilt artifact for ' + target.triple) + ` Supported artifacts: ${artifacts.map(a => a.id + ' (' + a.target.triple + ')').join(', ')}. No source build was started.`);
293
+ }
294
+ return matching[0];
295
+ }
@@ -0,0 +1,68 @@
1
+ import { resolve } from 'node:path';
2
+ import { readNativeDescriptor } from "./native-contracts.js";
3
+ /** Resolve descriptors against exact source declarations, never unqualified spellings. */
4
+ export function nativeDeclarations(project) {
5
+ const result = { functions: new Map(), resources: new Map(), providers: new Map(), providerMetadata: new Map(), providerDescriptors: new Map(), errors: new Map(), diagnostics: [] };
6
+ const issue = (span, message) => result.diagnostics.push({ ...span, code: 'NATIVE_ABI', message });
7
+ const sources = [];
8
+ const symbols = new Map();
9
+ if (project.library?.native)
10
+ sources.push({ directory: project.root, sourceRoot: project.sourceRoot, native: project.library.native, identity: project.library.name + '@' + project.library.version });
11
+ for (const scope of project.packages.scopes.values()) {
12
+ // readPackage already verified the descriptor hash; retain that source identity.
13
+ const native = scope.native;
14
+ if (native)
15
+ sources.push({ directory: scope.directory, sourceRoot: scope.sourceRoot, native, identity: scope.name + '@' + scope.version });
16
+ }
17
+ for (const source of sources) {
18
+ result.providerMetadata.set(source.identity, source.native);
19
+ result.providerDescriptors.set(source.identity, resolve(source.directory, source.native.bindings));
20
+ let descriptor;
21
+ try {
22
+ descriptor = readNativeDescriptor(source.directory, source.native);
23
+ }
24
+ catch (error) {
25
+ issue({ file: resolve(source.directory, source.native.bindings), start: 0, end: 0, line: 1, column: 1 }, error instanceof Error ? error.message : String(error));
26
+ continue;
27
+ }
28
+ for (const name of [...descriptor.resources.map(r => r.release), ...descriptor.functions.flatMap(f => [f.symbol, ...(f.result.release ? [f.result.release] : [])])]) {
29
+ const provider = symbols.get(name);
30
+ if (provider && provider !== source.identity)
31
+ issue({ file: resolve(source.directory, source.native.bindings), start: 0, end: 0, line: 1, column: 1 }, `Native symbol ${name} has ambiguous providers ${provider} and ${source.identity}`);
32
+ else
33
+ symbols.set(name, source.identity);
34
+ }
35
+ for (const resource of descriptor.resources) {
36
+ const file = resolve(source.sourceRoot, resource.module + '.aug');
37
+ const def = project.scopes.get(file)?.get(resource.name);
38
+ if (!def || def.file !== file || def.node.kind !== 'resource') {
39
+ issue({ file, start: 0, end: 0, line: 1, column: 1 }, 'Native descriptor has no matching resource declaration ' + resource.module + '.' + resource.name);
40
+ continue;
41
+ }
42
+ result.resources.set(def.node, resource);
43
+ result.providers.set(def.node, source.identity);
44
+ }
45
+ for (const fn of descriptor.functions) {
46
+ const file = resolve(source.sourceRoot, fn.module + '.aug');
47
+ const def = project.scopes.get(file)?.get(fn.name);
48
+ if (!def || def.file !== file || def.node.kind !== 'function' || !def.node.externC || def.node.valueAbi) {
49
+ issue({ file, start: 0, end: 0, line: 1, column: 1 }, 'Native descriptor has no matching extern C declaration ' + fn.module + '.' + fn.name);
50
+ continue;
51
+ }
52
+ result.functions.set(def.node, fn);
53
+ result.providers.set(def.node, source.identity);
54
+ if (fn.error) {
55
+ const dot = fn.error.lastIndexOf('.'), errorFile = resolve(source.sourceRoot, fn.error.slice(0, dot) + '.aug');
56
+ const error = project.scopes.get(errorFile)?.get(fn.error.slice(dot + 1));
57
+ if (!error || error.file !== errorFile || error.node.kind !== 'class')
58
+ issue(def.node.span, 'Native descriptor has no matching checked error declaration ' + fn.error);
59
+ else
60
+ result.errors.set(def.node, error);
61
+ }
62
+ }
63
+ }
64
+ for (const def of project.definitions.values())
65
+ if (def.node.kind === 'resource' && !result.resources.has(def.node))
66
+ issue(def.node.span, 'extern C resource requires a release identity in its package native.abi.json');
67
+ return result;
68
+ }
@@ -0,0 +1,63 @@
1
+ /** Keep checked binding facts separate from promises made by foreign code. */
2
+ export function nativeFact(checked, node) {
3
+ const provider = checked.native.providers.get(node), manifest = provider && checked.native.providerMetadata.get(provider);
4
+ if (!provider || !manifest)
5
+ return;
6
+ const common = { provider, profile: manifest.profile, descriptor: manifest.bindings, descriptorSha256: manifest.bindingsSha256,
7
+ upstream: manifest.upstream, supportedTargets: manifest.artifacts.map(artifact => artifact.target) };
8
+ if (node.kind === 'resource') {
9
+ const contract = checked.native.resources.get(node);
10
+ return contract ? { ...common, kind: 'resource', contract, cleanup: 'scope-exit' } : undefined;
11
+ }
12
+ const contract = checked.native.functions.get(node);
13
+ return contract ? { ...common, kind: 'function', contract,
14
+ compilerChecks: ['provider identity', 'descriptor digest', 'August signature', 'ownership at August call sites'],
15
+ nativeAuthorPromises: ['inputs are not retained', 'boundary entry stays on the caller thread', 'foreign threads do not enter August', 'C ABI boundary does not unwind into August'] } : undefined;
16
+ }
17
+ /** Resolve only standalone calls. Member dispatch is not claimed as complete. */
18
+ export function nativeDependencies(checked, method) {
19
+ const found = new Map(), seen = new Set();
20
+ const visit = (current) => {
21
+ if (seen.has(current))
22
+ return;
23
+ seen.add(current);
24
+ const fact = nativeFact(checked, current);
25
+ if (fact?.kind === 'function') {
26
+ found.set(current, fact);
27
+ return;
28
+ }
29
+ const walk = (value) => {
30
+ if (!value || typeof value !== 'object')
31
+ return;
32
+ if (Array.isArray(value)) {
33
+ value.forEach(walk);
34
+ return;
35
+ }
36
+ const call = value;
37
+ if (call.kind === 'call' && call.callee?.kind === 'name' && call.span) {
38
+ const target = checked.project.scopes.get(call.span.file)?.get(call.callee.name);
39
+ if (target?.node.kind === 'function')
40
+ visit(target.node);
41
+ }
42
+ for (const [key, child] of Object.entries(value))
43
+ if (!['span', 'nameSpan', 'sourceSpan'].includes(key))
44
+ walk(child);
45
+ };
46
+ walk(current.body);
47
+ };
48
+ visit(method);
49
+ return [...found.values()].sort((a, b) => (a.provider + '/' + a.contract.symbol).localeCompare(b.provider + '/' + b.contract.symbol, 'en'));
50
+ }
51
+ export function nativeDescription(fact, contractLink) {
52
+ const quote = (text) => '`' + text + '`';
53
+ const targets = fact.supportedTargets.map(target => target.os + ' ' + target.arch + (target.minimumOS ? ' ' + target.minimumOS + '+' : '') + (target.minimumLibc ? ' glibc ' + target.minimumLibc + '+' : '') + (target.cxxABI ? ' ' + target.cxxABI : '')).join(', ');
54
+ const origin = `Native implementation: ${quote(fact.provider)}, ${quote(fact.upstream.version)}. Supported targets: ${targets}. Binding contract: ${contractLink ?? quote(fact.descriptor)} (SHA-256 ${quote(fact.descriptorSha256)}).`;
55
+ if (fact.kind === 'resource')
56
+ return `${origin} An owned value releases its opaque handle through ${quote(fact.contract.release)} when its scope ends, including error and return paths.`;
57
+ const contract = fact.contract;
58
+ const loans = contract.params.filter(param => param.kind === 'resource').map(param => quote(param.name) + (param.ownership === 'consume' ? ' transfers ownership' : param.ownership === 'borrow' ? ' lends mutable access for this call' : ' lends read access for this call'));
59
+ const result = contract.result.kind === 'resource' ? 'The caller owns the returned handle.' : contract.result.release ?
60
+ `August copies the returned buffer, then calls ${quote(contract.result.release)} to release it.` : '';
61
+ return `${origin} It calls ${quote(contract.symbol)} through the C ABI on the caller thread; a blocking native call blocks that thread. ${loans.length ? loans.join('; ') + '. ' : ''}${result ? result + ' ' : ''}` +
62
+ 'The compiler checks the provider, descriptor digest, signature and ownership at August call sites. The native author promises not to retain inputs, enter August from foreign threads, or unwind across the C boundary; internal native workers may run. The compiler does not prove those promises.';
63
+ }
@@ -43,3 +43,46 @@ export function withPackageLock(path, action) {
43
43
  rmSync(path, { recursive: true, force: true });
44
44
  }
45
45
  }
46
+ /** Network-backed installs keep the writer lock until the asynchronous action completes. */
47
+ export async function withPackageLockAsync(path, action) {
48
+ const deadline = Date.now() + 30000;
49
+ while (true) {
50
+ try {
51
+ mkdirSync(path);
52
+ break;
53
+ }
54
+ catch (error) {
55
+ if (!error || typeof error !== 'object' || !('code' in error) || error.code !== 'EEXIST')
56
+ throw error;
57
+ let stale = false;
58
+ try {
59
+ const pid = Number(readFileSync(join(path, 'pid'), 'utf8'));
60
+ if (!Number.isSafeInteger(pid) || pid < 1)
61
+ throw new Error('Invalid lock owner');
62
+ try {
63
+ process.kill(pid, 0);
64
+ }
65
+ catch (e) {
66
+ stale = !!e && typeof e === 'object' && 'code' in e && e.code === 'ESRCH';
67
+ }
68
+ }
69
+ catch {
70
+ stale = existsSync(path) && Date.now() - statSync(path).mtimeMs > 180000;
71
+ }
72
+ if (stale) {
73
+ rmSync(path, { recursive: true, force: true });
74
+ continue;
75
+ }
76
+ if (Date.now() >= deadline)
77
+ throw new Error('Another August installation is still running. Lock: ' + path);
78
+ await new Promise(resolve => setTimeout(resolve, 100));
79
+ }
80
+ }
81
+ try {
82
+ writeFileSync(join(path, 'pid'), String(process.pid));
83
+ return await action();
84
+ }
85
+ finally {
86
+ rmSync(path, { recursive: true, force: true });
87
+ }
88
+ }
@@ -8,6 +8,7 @@ import { parse } from "./parser.js";
8
8
  import { isGitSource, materializeGit, sourceAlias } from "./git-packages.js";
9
9
  import { agentInstructions } from "./project-init.js";
10
10
  import { withPackageLock } from "./package-locking.js";
11
+ import { validateNativeManifest, readNativeDescriptor } from "./native-contracts.js";
11
12
  export const compilerVersion = () => JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8')).version;
12
13
  export const packageAlias = (name) => /^[a-z][a-z0-9_]*$/.test(name) && name !== 'august';
13
14
  /** Repository imports declare their dependencies beside the code that uses them. */
@@ -71,9 +72,15 @@ export function readPackage(directory) {
71
72
  format: 1, name: 'aug-' + sourceAlias(realpathSync(directory)), version: '0.0.0', compiler: compilerVersion(),
72
73
  source: existsSync(join(directory, 'export.aug')) ? '.' : 'src', dependencies: {}
73
74
  };
74
- if (manifest.format !== 1 || !npmName.test(manifest.name ?? '') || !version.test(manifest.version ?? '') ||
75
+ if (![1, 2].includes(manifest.format) || !npmName.test(manifest.name ?? '') || !version.test(manifest.version ?? '') ||
75
76
  manifest.compiler !== compilerVersion())
76
77
  throw new Error(`Invalid August package or compiler mismatch in ${directory}; expected compiler ${compilerVersion()}`);
78
+ if (manifest.format === 1 && manifest.native !== undefined)
79
+ throw new Error('Native packages require manifest format 2');
80
+ if (manifest.format === 2) {
81
+ manifest.native = validateNativeManifest(manifest.native);
82
+ readNativeDescriptor(directory, manifest.native);
83
+ }
77
84
  if (typeof manifest.source !== 'string' || !manifest.source || isAbsolute(manifest.source))
78
85
  throw new Error('Package source must be a relative folder');
79
86
  const sourceRoot = realpathSync(resolve(directory, manifest.source));
@@ -127,7 +134,7 @@ function normalizeSpecifier(spec, root, requirePath = true) {
127
134
  }
128
135
  function packageDigest(directory, sourceRoot) {
129
136
  const hash = createHash('sha256');
130
- for (const file of [join(directory, 'aug-package.json'), join(directory, 'package.json'), ...sourcePaths(sourceRoot)].filter(existsSync).sort()) {
137
+ for (const file of [join(directory, 'aug-package.json'), join(directory, 'package.json'), join(directory, 'native.abi.json'), join(directory, 'THIRD_PARTY_NOTICES.md'), ...sourcePaths(sourceRoot)].filter(existsSync).sort()) {
131
138
  hash.update(relative(directory, file).replaceAll('\\', '/'));
132
139
  hash.update('\0');
133
140
  hash.update(readFileSync(file));
@@ -156,10 +163,12 @@ export function projectPackages(root, specifications, sourceFolder = root) {
156
163
  if (manifest.name !== entry.name || manifest.version !== entry.version || manifest.source !== entry.source ||
157
164
  packageDigest(directory, sourceRoot) !== entry.digest)
158
165
  throw new Error(`Installed ${entry.name} changed; run aug install`);
166
+ if (JSON.stringify(entry.native) !== JSON.stringify(manifest.native))
167
+ throw new Error(`NATIVE_LOCK: Native metadata for ${entry.name} differs from its verified source manifest; run aug install`);
159
168
  if (result.scopes.has(entry.path))
160
169
  throw new Error('Duplicate package lock path');
161
170
  manifests.set(entry.path, manifest);
162
- result.scopes.set(entry.path, { ...entry, directory, sourceRoot, specifications: manifest.dependencies ?? {} });
171
+ result.scopes.set(entry.path, { ...entry, native: manifest.native, directory, sourceRoot, specifications: manifest.dependencies ?? {} });
163
172
  }
164
173
  verifyIdentities(lock.packages);
165
174
  for (const scope of result.scopes.values()) {
@@ -212,14 +221,12 @@ function npm(args, cwd) {
212
221
  return process.stdout;
213
222
  }
214
223
  /** Running an application prepares its declared packages; checking remains read-only. */
215
- export function prepareRunPackages(root, offline = false) {
224
+ export function prepareRunPackages(root, offline = false, frozen = false) {
216
225
  const loaded = loadConfig(root);
217
226
  if (loaded.diagnostics.length)
218
227
  return; // The checker renders the configuration's source diagnostics.
219
228
  const library = isLibrary(root) ? readPackage(root) : undefined;
220
229
  const specifications = packageSpecifications(library?.sourceRoot ?? root, library?.manifest.dependencies ?? loaded.config.packages);
221
- if (!Object.keys(specifications).length)
222
- return;
223
230
  const lockPath = join(root, 'aug.lock.json');
224
231
  let lock;
225
232
  if (existsSync(lockPath)) {
@@ -234,6 +241,10 @@ export function prepareRunPackages(root, offline = false) {
234
241
  }
235
242
  const matching = lock?.compiler === compilerVersion() && sameSpecifications(lock.specifications, specifications);
236
243
  if (!matching) {
244
+ if (!lock && !Object.keys(specifications).length)
245
+ return;
246
+ if (frozen)
247
+ throw new Error('PACKAGE_LOCK: Frozen run requires a matching aug.lock.json for the current imports, configuration and compiler. Run aug install before retrying.');
237
248
  process.stderr.write('Installing August packages declared by imports and main.yaml…\n');
238
249
  installPackages(root, false, offline);
239
250
  return;
@@ -272,7 +283,7 @@ function installLocked(root, frozen, offline, update) {
272
283
  }
273
284
  const isLibrary = (root) => existsSync(join(root, 'aug-package.json')) ||
274
285
  !existsSync(join(root, 'main.aug')) && (existsSync(join(root, 'export.aug')) || existsSync(join(root, 'src/export.aug')));
275
- const selectedSource = (file) => file.endsWith('.aug') || ['aug-package.json', 'package.json', 'main.yaml', 'README.md', 'LICENSE'].includes(file);
286
+ const selectedSource = (file) => file.endsWith('.aug') || ['aug-package.json', 'package.json', 'main.yaml', 'README.md', 'LICENSE', 'native.abi.json', 'THIRD_PARTY_NOTICES.md'].includes(file);
276
287
  function copySource(origin, destination) {
277
288
  let bytes = 0, count = 0;
278
289
  const copy = (folder) => {
@@ -381,7 +392,7 @@ function installSourceGraph(root, specifications, frozen, offline, previous) {
381
392
  writeJson(join(directory, 'package.json'), { name: manifest.name, version: manifest.version, private: true, dependencies });
382
393
  const { sourceRoot } = readPackage(directory);
383
394
  const entry = { path, name: manifest.name, version: manifest.version, source: manifest.source,
384
- digest: packageDigest(directory, sourceRoot), dependencies: {} };
395
+ digest: packageDigest(directory, sourceRoot), dependencies: {}, ...(manifest.native ? { native: manifest.native } : {}) };
385
396
  packages.set(path, entry);
386
397
  for (const [alias, request] of Object.entries(dependencies)) {
387
398
  entry.dependencies[alias] = visit(request, origin);
@@ -395,6 +406,8 @@ function installSourceGraph(root, specifications, frozen, offline, previous) {
395
406
  const lock = { format: 1, compiler: compilerVersion(), specifications, roots, npm: registry,
396
407
  git: [...gitSources.values()].sort((a, b) => a.request.localeCompare(b.request)),
397
408
  packages: [...packages.values()].sort((a, b) => a.path.localeCompare(b.path)) };
409
+ if (previous?.native && JSON.stringify(lock.packages) === JSON.stringify(previous.packages))
410
+ lock.native = previous.native;
398
411
  if (frozen && JSON.stringify(lock.packages) !== JSON.stringify(previous?.packages))
399
412
  throw new Error('Frozen package contents changed; use aug install --update to choose new revisions.');
400
413
  rmSync(join(stage, 'transport'), { recursive: true, force: true });
@@ -446,6 +459,8 @@ export function preparePackage(root) {
446
459
  transport.version = manifest.version;
447
460
  transport.dependencies = manifest.dependencies ?? {};
448
461
  transport.files = [...new Set([...(Array.isArray(transport.files) ? transport.files : ['README.md', 'LICENSE']), manifest.source, '.aug-spec', 'aug-package.json'])];
462
+ if (manifest.native)
463
+ transport.files.push('native.abi.json', 'THIRD_PARTY_NOTICES.md');
449
464
  writeJson(join(root, 'package.json'), transport);
450
465
  readPackage(root);
451
466
  }
package/src/parser.js CHANGED
@@ -218,8 +218,14 @@ class Parser {
218
218
  return { kind: 'composition', name, typeParams: [], bindings, span: this.span(start) };
219
219
  }
220
220
  if (this.at('extern')) {
221
- this.take();
221
+ const start = this.take().span;
222
222
  this.expect('C');
223
+ if (this.current().value === 'resource') {
224
+ this.take();
225
+ const name = this.expect('identifier').value;
226
+ this.endStatement();
227
+ return { kind: 'resource', name, typeParams: [], span: this.span(start) };
228
+ }
223
229
  return this.parseFunction(true);
224
230
  }
225
231
  if (this.looksLikeBareClass())
@@ -395,6 +401,11 @@ class Parser {
395
401
  this.parseTypeParams();
396
402
  if (this.at('('))
397
403
  this.parseParams(true);
404
+ if (this.match('unless')) {
405
+ this.parseType();
406
+ while (this.match('and') || this.match(','))
407
+ this.parseType();
408
+ }
398
409
  if (this.match('=>')) {
399
410
  return true;
400
411
  }
@@ -497,6 +508,13 @@ class Parser {
497
508
  const name = this.expect('identifier').value;
498
509
  const { typeParams, typeConstraints, typeVariance } = this.parseTypeParams();
499
510
  const fields = this.at('(') ? this.parseParams(true) : [];
511
+ const validationErrors = [];
512
+ const validationDeclared = !!this.match('unless');
513
+ if (validationDeclared) {
514
+ validationErrors.push(this.parseType());
515
+ while (this.match('and') || this.match(','))
516
+ validationErrors.push(this.parseType());
517
+ }
500
518
  let constructorBody = this.match('=>') ? this.parseBlock(start) : undefined;
501
519
  const implemented = [];
502
520
  if (!this.at('implements'))
@@ -542,7 +560,7 @@ class Parser {
542
560
  }
543
561
  this.closeBrace();
544
562
  return { kind: 'class', name, typeParams, typeConstraints, typeVariance, fields, stateFields, constructorBody,
545
- implements: implemented, methods, headerEnd, span: this.span(start) };
563
+ validationErrors, validationDeclared, implements: implemented, methods, headerEnd, span: this.span(start) };
546
564
  }
547
565
  parseInterface() {
548
566
  const keyword = this.take();