@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
package/src/project.js CHANGED
@@ -91,14 +91,14 @@ export function loadProject(projectRoot, overrides = new Map(), cache) {
91
91
  if (!isExport && item.kind === 'export') {
92
92
  diagnostics.push(diagnostic(file.path, item.span.line, item.span.column, 'export declarations belong in export.aug', 'EXPORT'));
93
93
  }
94
- if (file === main && ['class', 'interface', 'function', 'interceptor', 'test', 'composition'].includes(item.kind)) {
94
+ if (file === main && ['class', 'interface', 'function', 'interceptor', 'test', 'composition', 'resource'].includes(item.kind)) {
95
95
  diagnostics.push(diagnostic(file.path, item.span.line, item.span.column, 'Define classes, interfaces, functions, and interceptors outside main.aug', 'MAIN'));
96
96
  }
97
97
  if (file !== main && ['bind', 'include', 'expr', 'assign', 'return', 'throw', 'if', 'while', 'for', 'destructure', 'match', 'scope', 'freeze', 'serve', 'lock',
98
98
  'try', 'unsafe', 'borrow'].includes(item.kind)) {
99
99
  diagnostics.push(diagnostic(file.path, item.span.line, item.span.column, 'Executable statements and bindings belong in main.aug', 'MAIN'));
100
100
  }
101
- if (item.kind === 'class' || item.kind === 'interface' || item.kind === 'function' || item.kind === 'interceptor' || item.kind === 'composition') {
101
+ if (item.kind === 'class' || item.kind === 'interface' || item.kind === 'function' || item.kind === 'interceptor' || item.kind === 'composition' || item.kind === 'resource') {
102
102
  if (item.name in builtinTypes || builtinFunctions.some(operation => operation.name === item.name) || item.name === 'next') {
103
103
  diagnostics.push(diagnostic(file.path, item.span.line, item.span.column, `${item.name} is a reserved built-in name`, 'NAME'));
104
104
  continue;
@@ -0,0 +1,23 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { httpPolicyNames, httpPolicyNativeName } from "./http-policies.js";
3
+ /** Compiler-private identifiers, checked against the compiled target headers. */
4
+ export const runtimeOperations = ['PRINT', 'BINARY', 'UNARY', 'FIELD', 'SET_FIELD', 'LIST', 'TUPLE', 'SET', 'MAP', 'MAP_SET', 'LIST_LENGTH', 'LIST_GET', 'LIST_AT', 'LIST_APPEND', 'TUPLE_LENGTH', 'TUPLE_GET', 'SET_LENGTH', 'SET_ADD', 'SET_CONTAINS', 'MAP_LENGTH', 'MAP_GET', 'MAP_TAKE', 'MAP_CONTAINS', 'STRING_LENGTH', 'STRING_BYTES', 'STRING_SPLIT', 'STRING_STARTS_WITH', 'STRING_IS_TOKEN', 'BYTES_LENGTH', 'BYTES_TEXT', 'BYTES_BASE64URL', 'BASE64URL_DECODE', 'C_INT', 'READ_FILE', 'WRITE_FILE', 'ARGUMENTS', 'FREEZE', 'ITER', 'MAP_ITER', 'IS_TYPE', 'SHARED', 'SHARED_LOCK', 'JSON_WRAP', 'JSON_PARSE', 'JSON_STRINGIFY', 'JSON_GET', 'JSON_REQUIRE', 'JSON_STRING', 'JSON_INTEGER', 'JSON_BOOLEAN', 'JSON_ITEMS', 'TIME_NOW', 'EXIT', 'TEST_CASE'];
5
+ export const httpOperations = ['HTTP_HEADERS', 'HTTP_HEADERS_WITH', 'HTTP_HEADERS_GET', 'HTTP_HEADERS_ALL', 'HTTP_RESPONSE', 'HTTP_RESPONSE_STATUS', 'HTTP_FINISH', 'HTTP_EVENT', 'HTTP_YIELD', 'HTTP_CLIENT_REQUEST', 'HTTP_ACTION'];
6
+ /** Every private operation declares either a fixed or a collection arity. */
7
+ export const runtimeArities = {
8
+ PRINT: 1, BINARY: 2, UNARY: 1, FIELD: 1, SET_FIELD: 2, LIST: 'variadic', TUPLE: 'variadic', SET: 'variadic', MAP: 0, MAP_SET: 3,
9
+ LIST_LENGTH: 1, LIST_GET: 2, LIST_AT: 2, LIST_APPEND: 2, TUPLE_LENGTH: 1, TUPLE_GET: 2, SET_LENGTH: 1, SET_ADD: 2, SET_CONTAINS: 2,
10
+ MAP_LENGTH: 1, MAP_GET: 2, MAP_TAKE: 2, MAP_CONTAINS: 2, STRING_LENGTH: 1, STRING_BYTES: 1, STRING_SPLIT: 2, STRING_STARTS_WITH: 2, STRING_IS_TOKEN: 3,
11
+ BYTES_LENGTH: 1, BYTES_TEXT: 1, BYTES_BASE64URL: 1, BASE64URL_DECODE: 1, C_INT: 1, READ_FILE: 1, WRITE_FILE: 2, ARGUMENTS: 0, FREEZE: 1, ITER: 1, MAP_ITER: 1, IS_TYPE: 1,
12
+ SHARED: 1, SHARED_LOCK: 1, JSON_WRAP: 1, JSON_PARSE: 1, JSON_STRINGIFY: 1, JSON_GET: 2, JSON_REQUIRE: 2, JSON_STRING: 1, JSON_INTEGER: 1, JSON_BOOLEAN: 1, JSON_ITEMS: 1,
13
+ TIME_NOW: 0, EXIT: 1, TEST_CASE: 0, HTTP_HEADERS: 0, HTTP_HEADERS_WITH: 3, HTTP_HEADERS_GET: 2, HTTP_HEADERS_ALL: 2, HTTP_RESPONSE: 3, HTTP_RESPONSE_STATUS: 2,
14
+ HTTP_FINISH: 1, HTTP_EVENT: 4, HTTP_YIELD: 1, HTTP_CLIENT_REQUEST: 5, HTTP_ACTION: 'variadic',
15
+ };
16
+ export const schemaKinds = ['INT', 'C_INT', 'FLOAT', 'BOOL', 'STRING', 'LIST', 'MAP', 'SET', 'TUPLE', 'RECORD', 'JSON'];
17
+ export const runtimeIdentifiers = Object.fromEntries([
18
+ ...runtimeOperations.map((name, index) => ['AUG_IR_' + name, index + 1]),
19
+ ...httpOperations.map((name, index) => ['AUG_IR_' + name, index + 1]),
20
+ ...schemaKinds.map((name, index) => ['AUG_SCHEMA_' + name, index]),
21
+ ...httpPolicyNames.map((name, index) => [httpPolicyNativeName(name), index + 1]),
22
+ ]);
23
+ export const runtimeIdentifierSha256 = createHash('sha256').update(JSON.stringify(runtimeIdentifiers)).digest('hex');
@@ -0,0 +1,18 @@
1
+ export const runtimeAdapters = [
2
+ { name: '_aug_crypto_random', parameters: ['int'], returns: 'Bytes', component: 'crypto' },
3
+ { name: '_aug_crypto_sha256', parameters: ['Bytes'], returns: 'Bytes', component: 'crypto' },
4
+ { name: '_aug_crypto_generate_rsa', parameters: [], returns: 'RsaPrivateKey', component: 'crypto' },
5
+ { name: '_aug_crypto_public_rsa', parameters: ['RsaPrivateKey'], returns: 'RsaPublicKey', component: 'crypto' },
6
+ { name: '_aug_crypto_sign_rsa', parameters: ['RsaPrivateKey', 'Bytes'], returns: 'Bytes', component: 'crypto' },
7
+ { name: '_aug_crypto_verify_rsa', parameters: ['RsaPublicKey', 'Bytes', 'Bytes'], returns: 'bool', component: 'crypto' },
8
+ { name: '_aug_crypto_decode_base64url', parameters: ['string'], returns: 'Bytes', component: 'crypto' },
9
+ { name: '_aug_crypto_equal', parameters: ['Bytes', 'Bytes'], returns: 'bool', component: 'crypto' },
10
+ { name: '_aug_crypto_export_rsa', parameters: ['RsaPublicKey'], returns: 'Tuple<Bytes,Bytes>', component: 'crypto' },
11
+ { name: '_aug_crypto_import_rsa', parameters: ['Bytes', 'Bytes'], returns: 'RsaPublicKey', component: 'crypto' },
12
+ { name: '_aug_crypto_password_hash', parameters: ['Bytes', 'Bytes', 'int'], returns: 'Bytes', component: 'crypto' },
13
+ { name: '_aug_http_log', parameters: ['string', 'string', 'int', 'int'], returns: 'void', component: 'http' },
14
+ { name: '_aug_http_request', parameters: ['string', 'string', 'optional Headers', 'optional Bytes'], returns: 'HttpResponse<Bytes>', component: 'http' },
15
+ { name: '_aug_http_url_encode', parameters: ['string'], returns: 'string', component: 'http' },
16
+ { name: '_aug_http_cookie', parameters: ['string', 'string', 'string', 'int', 'bool'], returns: 'Headers', component: 'http' }
17
+ ];
18
+ export function adapterSymbol(adapter) { return 'aug_ir_private' + adapter.name; }
package/src/schemas.js CHANGED
@@ -21,11 +21,10 @@ export function jsonDataType(project, type, seen = new Set()) {
21
21
  const params = new Map(node.typeParams.map((name, i) => [name, type.args[i]]));
22
22
  return node.fields.every(field => jsonDataType(project, schemaType(project, field.type, type.def.file, params), seen));
23
23
  }
24
- /** One shared native schema per concrete type, used by HTTP and explicit JSON decoding. */
25
- export class NativeSchemas {
24
+ /** Backend-neutral, resolved schema graph shared by HTTP and JSON decoding. */
25
+ export class DataSchemas {
26
26
  names = new Map();
27
- output = [];
28
- forwards = [];
27
+ nodes = [];
29
28
  project;
30
29
  constructorName;
31
30
  constructor(project, constructorName) {
@@ -39,24 +38,32 @@ export class NativeSchemas {
39
38
  return previous;
40
39
  const name = 'aug_schema_' + this.names.size;
41
40
  this.names.set(key, name);
42
- this.forwards.push(`static const AugSchema ${name};`);
43
- let kind = 'AUG_SCHEMA_' + type.name.toUpperCase(), fields = [], labels = [], maker = 'NULL';
41
+ const schema = { name, kind: type.name.toUpperCase(), nullable: type.nullable,
42
+ optional: !!type.optional, fields: [], labels: [] };
43
+ this.nodes.push(schema);
44
44
  if (type.def?.node.kind === 'class' && type.def.node.record) {
45
- kind = 'AUG_SCHEMA_RECORD';
45
+ schema.kind = 'RECORD';
46
46
  const node = type.def.node, params = new Map(node.typeParams.map((param, i) => [param, type.args[i]]));
47
- fields = node.fields.map(field => '&' + this.request(schemaType(this.project, field.type, type.def.file, params)));
48
- labels = node.fields.map(field => JSON.stringify(field.name));
49
- maker = this.constructorName(type.def);
47
+ schema.fields = node.fields.map(field => this.request(schemaType(this.project, field.type, type.def.file, params)));
48
+ schema.labels = node.fields.map(field => field.name);
49
+ schema.maker = this.constructorName(type.def);
50
50
  }
51
51
  else
52
- fields = type.args.map(arg => '&' + this.request(arg));
53
- if (fields.length)
54
- this.output.push(`static const AugSchema *const ${name}_fields[] = {${fields.join(', ')}};`);
55
- if (labels.length)
56
- this.output.push(`static const char *const ${name}_names[] = {${labels.join(', ')}};`);
57
- this.output.push(`static const AugSchema ${name} = {${kind}, ${type.nullable ? 'true' : 'false'}, ${type.optional ? 'true' : 'false'}, ${fields.length}, ${fields.length ? name + '_fields' : 'NULL'}, ${labels.length ? name + '_names' : 'NULL'}, ${maker}};`);
52
+ schema.fields = type.args.map(arg => this.request(arg));
58
53
  return name;
59
54
  }
60
55
  identity(type) { return [type.id, type.nullable, !!type.optional, type.args.map(arg => this.identity(arg))]; }
61
- declarations() { return [...this.forwards, ...this.output].join('\n'); }
56
+ }
57
+ /** C representation of the same checked concrete schema graph. */
58
+ export class NativeSchemas extends DataSchemas {
59
+ declarations() {
60
+ return [...this.nodes.map(schema => `static const AugSchema ${schema.name};`), ...this.nodes.flatMap(schema => {
61
+ const { name, fields, labels } = schema;
62
+ return [
63
+ ...(fields.length ? [`static const AugSchema *const ${name}_fields[] = {${fields.map(field => '&' + field).join(', ')}};`] : []),
64
+ ...(labels.length ? [`static const char *const ${name}_names[] = {${labels.map(label => JSON.stringify(label)).join(', ')}};`] : []),
65
+ `static const AugSchema ${name} = {AUG_SCHEMA_${schema.kind}, ${schema.nullable ? 'true' : 'false'}, ${schema.optional ? 'true' : 'false'}, ${fields.length}, ${fields.length ? name + '_fields' : 'NULL'}, ${labels.length ? name + '_names' : 'NULL'}, ${schema.maker ?? 'NULL'}, NULL};`
66
+ ];
67
+ })].join('\n');
68
+ }
62
69
  }
package/src/semantic.js CHANGED
@@ -12,6 +12,7 @@ import { checkUnitTests, discoverTests, mergeTestAnalysis, uniqueDiagnostics } f
12
12
  import { formatFile } from "./formatter.js";
13
13
  import { interceptorBehavior } from "./interceptors.js";
14
14
  import { callableResult, callableErrors } from "./contracts.js";
15
+ import { nativeFact, nativeDependencies } from "./native-facts.js";
15
16
  const genericFacts = (header) => header.typeParams.map(name => ({ name,
16
17
  variance: header.typeVariance?.[name] ?? 'invariant', constraints: (header.typeConstraints?.[name] ?? []).map(typeName) }));
17
18
  export function contractFacts(checked) {
@@ -19,7 +20,9 @@ export function contractFacts(checked) {
19
20
  const callable = (method, constructor) => {
20
21
  const contract = checked.effectContracts.get(method);
21
22
  const layers = checked.interceptorPlans.get(constructor ?? method) ?? [];
23
+ const native = nativeFact(checked, method);
22
24
  return { name: method.name, location: method.span, genericParameters: genericFacts(method),
25
+ native: native?.kind === 'function' ? native : undefined, nativeDependencies: nativeDependencies(checked, method), nativeCoverage: 'resolved-standalone-calls',
23
26
  inputs: method.params.map(param => ({ label: param.label ?? param.name, name: param.name,
24
27
  type: typeName(param.type), ownership: param.ownership, injected: param.injected, source: param.source })),
25
28
  http: method.endpoint ? { method: method.endpoint.method, path: method.endpoint.path, status: method.endpoint.status,
@@ -28,7 +31,7 @@ export function contractFacts(checked) {
28
31
  dependencies: policy.dependencies.map(index => method.params[index]?.label ?? method.params[index]?.name ?? '') })),
29
32
  result: `${method.returnOwnership === 'own' ? 'own ' : ''}${tyName(callableResult(checked, method))}`,
30
33
  changes: [...(contract?.changes ?? method.changes ?? [])],
31
- capabilities: [...(contract?.uses.values() ?? [])].map(effect => `${effect.source}.${effect.operation}`).concat(method.externC ? [`C.${method.name}`] : []),
34
+ capabilities: [...(contract?.uses.values() ?? [])].map(effect => `${effect.source}.${effect.operation}`).concat(method.externC && !native ? [`C.${method.name}`] : []),
32
35
  inferredEffects: !!contract?.inferred,
33
36
  errors: constructor ? [...new Set([...(checked.constructorContracts.get(constructor)?.errors.map(tyName) ?? constructor.validationErrors?.map(typeName) ?? []),
34
37
  ...layers.flatMap(layer => layer.errors.map(tyName))])].sort() : callableErrors(checked, method),
@@ -68,8 +71,10 @@ export function contractFacts(checked) {
68
71
  visit(child);
69
72
  };
70
73
  visit(node);
74
+ const native = node.kind === 'resource' ? nativeFact(checked, node) : undefined;
71
75
  return { id: def.id, name: def.name, kind: node.kind === 'class' && node.record ? 'record' :
72
76
  node.kind === 'interface' && node.capability ? 'capability' : node.kind,
77
+ native: native?.kind === 'resource' ? native : undefined,
73
78
  location: node.span, public: !node.name.startsWith('_'), typeParameters: node.typeParams, genericParameters: genericFacts(node),
74
79
  documentation: javadocBefore(file.source, 'annotations' in node ? node.annotations?.[0]?.span.start ?? node.span.start : node.span.start)?.markdown,
75
80
  interfaces: node.kind === 'class' ? node.implements.map(typeName) : node.kind === 'interface' ? node.extends.map(typeName) : [],
@@ -79,7 +84,7 @@ export function contractFacts(checked) {
79
84
  typeConstraints: node.typeConstraints, typeVariance: node.typeVariance, params: node.fields,
80
85
  returns: syntheticType(node.name, node.span), returnOwnership: 'managed',
81
86
  throws: node.validationErrors ?? [], changes: [], uses: [], body: node.constructorBody, externC: false, span: node.span }, node)] : []),
82
- ...methods.filter(method => !method.name.startsWith('_')).map(method => callable(method))], calls,
87
+ ...methods.filter(method => node.kind === 'function' || !method.name.startsWith('_')).map(method => callable(method))], calls,
83
88
  tests: file.items.flatMap(item => item.kind === 'test' && item.type.name === def.name ? item.groups.flatMap(group => group.cases.map(test => ({ group: group.name, name: test.name, location: test.span }))) : []) };
84
89
  });
85
90
  }
@@ -147,7 +152,7 @@ export function describe(checked, fileName, options = {}) {
147
152
  const module = { file: source.path, dependencies: [...new Set(source.items.flatMap(item => item.kind === 'import' ?
148
153
  (checked.project.imports.get(item) ?? []).map(def => def.file) : []))].sort(),
149
154
  public: publicFacts.map(fact => ({ name: fact.name, shape: createHash('sha256').update(JSON.stringify({ kind: fact.kind,
150
- generics: fact.genericParameters, fields: fact.fields.filter(field => !field.storage.startsWith('_')),
155
+ native: fact.native, generics: fact.genericParameters, fields: fact.fields.filter(field => !field.storage.startsWith('_')),
151
156
  interfaces: fact.interfaces, callables: fact.callables.map(({ location, interceptors, inputs, ...contract }) => ({ ...contract,
152
157
  inputs: inputs.map(({ name, ...input }) => input),
153
158
  interceptors: interceptors.map(({ location, ...layer }) => layer) })) })).digest('hex') })),
package/src/snippets.js CHANGED
@@ -24,6 +24,7 @@ export const snippetCatalog = [
24
24
  { prefix: 'waitall', description: 'Join two computations', body: 'wait for ${1:loadingUsers} and ${2:loadingOrders} as ${3:users} and ${4:orders}' },
25
25
  { prefix: 'lock', description: 'Access shared mutable state inside a bounded lock', body: 'lock ${1:shared} as ${2:value}:\n $0' },
26
26
  { prefix: 'unsafe', description: 'Contain a native C call', body: 'unsafe:\n $0' },
27
+ { prefix: 'resource', description: 'An opaque native package resource with checked cleanup', body: 'extern C resource ${1:Handle}' },
27
28
  { prefix: 'serve', description: 'Start a native HTTP listener', body: 'serve ${1:endpoint} on port ${2:8787}' },
28
29
  ...['GET', 'POST', 'PATCH', 'DELETE'].map(method => ({ prefix: 'endpoint' + method.toLowerCase(), description: `A typed ${method} endpoint`, body: `endpoint ${method} "\${1:/items}" as \${2:handler}(\${3:}):\n $0` })),
29
30
  { prefix: 'stream', description: 'An endpoint yielding server-sent events', body: 'endpoint GET "${1:/events}" as ${2:events}() streams ServerEvent<string>:\n yield ServerEvent(data=${3:"hello"})' },
package/src/spec.js CHANGED
@@ -11,6 +11,7 @@ import { compilerVersion } from "./package-manager.js";
11
11
  import { tyName } from "./types.js";
12
12
  import { action, attempt, branch, choice, coordinate, flow, loop, paragraph, renderSpecTree, scope, section, sequence, step } from "./spec-tree.js";
13
13
  import { specHint } from "./spec-hints.js";
14
+ import { nativeFact, nativeDescription } from "./native-facts.js";
14
15
  const generated = '<!-- Generated by aug spec. Edit the August source, then regenerate. -->';
15
16
  const copied = '// Generated by aug spec. This is a copy of the installed dependency source.\n';
16
17
  const code = (value) => { const text = value.replaceAll('\n', '\\n'); const fence = '`'.repeat(1 + Math.max(0, ...(text.match(/`+/g) ?? []).map(part => part.length))); return fence + (text.startsWith('`') ? ' ' : '') + text + (text.endsWith('`') ? ' ' : '') + fence; };
@@ -44,12 +45,19 @@ export function generateSpecs(checked, options = {}) {
44
45
  }
45
46
  const queue = [...owned].sort((a, b) => compare(a.path, b.path));
46
47
  const seen = new Set(), outputs = hints.map(hint => ({ path: hint.file.path, text: hint.text, source: hint.file.path, kind: 'source-hint' }));
48
+ const contracts = new Map();
49
+ for (const [provider, path] of checked.native.providerDescriptors) {
50
+ const at = provider.lastIndexOf('@'), name = provider.slice(0, at), version = provider.slice(at + 1);
51
+ const destination = join(project.root, '.aug-spec', 'packages', name, version, 'native.abi.json');
52
+ contracts.set(provider, destination);
53
+ outputs.push({ path: destination, text: readFileSync(path, 'utf8'), source: path, kind: 'native-descriptor' });
54
+ }
47
55
  for (let index = 0; index < queue.length; index++) {
48
56
  const file = queue[index];
49
57
  if (seen.has(file.path))
50
58
  continue;
51
59
  seen.add(file.path);
52
- const writer = new SpecWriter(checked, file, docs, sources, own, offsets, path => {
60
+ const writer = new SpecWriter(checked, file, docs, sources, own, offsets, contracts, path => {
53
61
  const dependency = project.files.get(path);
54
62
  if (dependency && !seen.has(path))
55
63
  queue.push(dependency);
@@ -67,20 +75,26 @@ class SpecWriter {
67
75
  sources;
68
76
  own;
69
77
  offsets;
78
+ contracts;
70
79
  enqueue;
71
80
  used = new Map();
72
81
  builtins = new Map();
73
82
  properties = new Map();
74
83
  locals = new Map();
75
- constructor(checked, file, docs, sources, own, offsets, enqueue) {
84
+ constructor(checked, file, docs, sources, own, offsets, contracts, enqueue) {
76
85
  this.checked = checked;
77
86
  this.file = file;
78
87
  this.docs = docs;
79
88
  this.sources = sources;
80
89
  this.own = own;
81
90
  this.offsets = offsets;
91
+ this.contracts = contracts;
82
92
  this.enqueue = enqueue;
83
93
  }
94
+ nativeDescription(native) {
95
+ const contract = this.contracts.get(native.provider);
96
+ return nativeDescription(native, `[${code(native.descriptor)}](${url(relative(dirname(this.docs.get(this.file.path)), contract))})`);
97
+ }
84
98
  definition(name) { return this.checked.project.scopes.get(this.file.path)?.get(name); }
85
99
  use(def, method, field) {
86
100
  if (!def || def.file === this.file.path)
@@ -804,8 +818,10 @@ class SpecWriter {
804
818
  children.push(...this.contract(method, documentation), ...this.layers(method));
805
819
  if (method.body)
806
820
  children.push(flow(this.statements(method.body)));
807
- else if (method.externC)
808
- children.push(paragraph('Native C implementation; only its declared contract is visible here.'));
821
+ else if (method.externC) {
822
+ const native = nativeFact(this.checked, method);
823
+ children.push(paragraph(native ? this.nativeDescription(native) : 'Native C implementation; only its declared contract is visible here.'));
824
+ }
809
825
  return this.heading(name, method.span, owner && owner.node.kind !== 'function' ? 3 : 2, children);
810
826
  }
811
827
  exportLine(item) {
@@ -822,6 +838,10 @@ class SpecWriter {
822
838
  }
823
839
  declaration(item) {
824
840
  switch (item.kind) {
841
+ case 'resource': {
842
+ const native = nativeFact(this.checked, item);
843
+ return this.heading(item.name, item.span, 2, [paragraph(native ? this.nativeDescription(native) : code(item.name) + ' is an opaque native resource with an unresolved package release contract.')], 'native resource');
844
+ }
825
845
  case 'function': return this.callable(item);
826
846
  case 'class': {
827
847
  const def = this.definition(item.name);
@@ -932,7 +952,7 @@ class SpecWriter {
932
952
  }
933
953
  render() {
934
954
  const exports = this.file.items.filter(item => item.kind === 'export');
935
- const declarations = this.file.items.filter(item => ['class', 'interface', 'interceptor', 'function', 'composition'].includes(item.kind));
955
+ const declarations = this.file.items.filter(item => ['class', 'interface', 'interceptor', 'function', 'composition', 'resource'].includes(item.kind));
936
956
  const providers = this.file.items.filter(item => item.kind === 'bind' || item.kind === 'include');
937
957
  const startup = this.file.items.filter(item => item.kind !== 'import' && item.kind !== 'export' && item.kind !== 'test' && item.kind !== 'bind' && item.kind !== 'include' && !declarations.includes(item));
938
958
  const children = [];
@@ -982,18 +1002,28 @@ export function updateSpecs(checked, check = false, options = {}) {
982
1002
  const outputs = generateSpecs(checked, options), root = checked.project.root;
983
1003
  const manifest = join(root, '.aug-spec', 'manifest.json');
984
1004
  let previous = [];
1005
+ const previousDescriptors = new Map();
1006
+ const descriptorPath = (path) => /^\.aug-spec\/packages\/(?:@[^/]+\/)?[^/]+\/[^/]+\/native\.abi\.json$/.test(path) && !path.includes('\\') && !path.split('/').some(part => part === '.' || part === '..');
985
1007
  if (options.manifest !== false && existsSync(manifest)) {
986
1008
  const saved = JSON.parse(readFileSync(manifest, 'utf8'));
987
- if (saved.format !== 1 || !Array.isArray(saved.files) || saved.files.some(path => typeof path !== 'string' || relative(root, resolve(root, path)).startsWith('..') || !path.endsWith('.aug') && !path.endsWith('.aug.md')))
1009
+ if (saved.format !== 1 || !Array.isArray(saved.files) || saved.files.some(path => typeof path !== 'string' || relative(root, resolve(root, path)).startsWith('..') || !path.endsWith('.aug') && !path.endsWith('.aug.md') && !descriptorPath(path)))
988
1010
  throw new Error('Invalid generated specification manifest');
989
1011
  previous = saved.files.map(path => resolve(root, path));
1012
+ for (const [path, digest] of Object.entries(saved.nativeDescriptors ?? {})) {
1013
+ if (!descriptorPath(path) || !saved.files.includes(path) || typeof digest !== 'string' || !/^[0-9a-f]{64}$/.test(digest))
1014
+ throw new Error('Invalid generated native descriptor manifest');
1015
+ previousDescriptors.set(resolve(root, path), digest);
1016
+ }
990
1017
  }
991
1018
  const artifacts = outputs.filter(output => output.kind !== 'source-hint');
992
1019
  const names = new Set(artifacts.map(output => output.path));
993
1020
  const removed = previous.filter(path => !names.has(path) && existsSync(path));
994
1021
  const stale = outputs.filter(output => !existsSync(output.path) || readFileSync(output.path, 'utf8') !== output.text).map(output => relative(root, output.path));
995
1022
  stale.push(...removed.map(path => relative(root, path)));
996
- const manifestText = JSON.stringify({ format: 1, files: artifacts.map(output => relative(root, output.path).replaceAll('\\', '/')), digest: hash(artifacts.map(output => output.text).join('\0')) }, null, 2) + '\n';
1023
+ const descriptors = artifacts.filter(output => output.kind === 'native-descriptor');
1024
+ const manifestText = JSON.stringify({ format: 1, files: artifacts.map(output => relative(root, output.path).replaceAll('\\', '/')),
1025
+ ...(descriptors.length ? { nativeDescriptors: Object.fromEntries(descriptors.map(output => [relative(root, output.path).replaceAll('\\', '/'), hash(output.text)])) } : {}),
1026
+ digest: hash(artifacts.map(output => output.text).join('\0')) }, null, 2) + '\n';
997
1027
  if (options.manifest !== false && (!existsSync(manifest) || readFileSync(manifest, 'utf8') !== manifestText))
998
1028
  stale.push('.aug-spec/manifest.json');
999
1029
  if (check)
@@ -1016,10 +1046,21 @@ export function updateSpecs(checked, check = false, options = {}) {
1016
1046
  if (text !== checked.project.files.get(output.source)?.source)
1017
1047
  throw new Error('Source changed during compilation; retry before generating specifications');
1018
1048
  }
1049
+ else if (output.kind === 'native-descriptor') {
1050
+ // An unchanged descriptor from an earlier preview may be adopted. A
1051
+ // tracked copy must still match its recorded bytes before replacement.
1052
+ const tracked = previousDescriptors.get(output.path);
1053
+ if (tracked ? hash(text) !== tracked : text !== output.text)
1054
+ throw new Error(`Refusing to overwrite edited native descriptor ${output.path}`);
1055
+ }
1019
1056
  else if (!text.startsWith(generated) && !text.startsWith(copied))
1020
1057
  throw new Error(`Refusing to overwrite handwritten file ${output.path}`);
1021
1058
  }
1022
1059
  }
1060
+ for (const path of removed)
1061
+ if (previousDescriptors.has(path) &&
1062
+ (lstatSync(path).isSymbolicLink() || hash(readFileSync(path, 'utf8')) !== previousDescriptors.get(path)))
1063
+ throw new Error(`Refusing to remove edited native descriptor ${path}`);
1023
1064
  for (const output of outputs) {
1024
1065
  if (existsSync(output.path) && readFileSync(output.path, 'utf8') === output.text)
1025
1066
  continue;
@@ -1038,7 +1079,7 @@ export function updateSpecs(checked, check = false, options = {}) {
1038
1079
  }
1039
1080
  for (const path of removed) {
1040
1081
  const text = readFileSync(path, 'utf8');
1041
- if (text.startsWith(generated) || text.startsWith(copied))
1082
+ if (previousDescriptors.has(path) || text.startsWith(generated) || text.startsWith(copied))
1042
1083
  rmSync(path);
1043
1084
  }
1044
1085
  if (options.manifest !== false) {