@greenpandastudios/aug-cli 0.19.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 (414) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +13 -0
  3. package/THIRD_PARTY_NOTICES.md +18 -0
  4. package/bin/aug.mjs +3 -0
  5. package/docs/api/crypto.md +351 -0
  6. package/docs/api/io.md +207 -0
  7. package/docs/api/json.md +21 -0
  8. package/docs/api/memory.md +109 -0
  9. package/docs/api/time.md +54 -0
  10. package/docs/api/web.md +184 -0
  11. package/docs/assets/benchmarks/execution.svg +2978 -0
  12. package/docs/assets/benchmarks/http.svg +1674 -0
  13. package/docs/assets/benchmarks/improvements.svg +2315 -0
  14. package/docs/assets/benchmarks/memory.svg +1891 -0
  15. package/docs/benchmark-baseline.json +1087 -0
  16. package/docs/benchmark-results.json +91123 -0
  17. package/docs/benchmarks.json +38 -0
  18. package/docs/compatibility.md +42 -0
  19. package/docs/concurrency-implementation.md +18 -0
  20. package/docs/diagnostics.md +97 -0
  21. package/docs/docker.md +35 -0
  22. package/docs/example-projects.json +26 -0
  23. package/docs/examples/approved-design/counters.md +253 -0
  24. package/docs/examples/approved-design/dependencies/august/0.19.0/io/contracts.md +395 -0
  25. package/docs/examples/approved-design/domain/app.md +165 -0
  26. package/docs/examples/approved-design/domain/export.md +83 -0
  27. package/docs/examples/approved-design/domain/models.md +80 -0
  28. package/docs/examples/approved-design/domain/numbers.md +248 -0
  29. package/docs/examples/approved-design/index.md +39 -0
  30. package/docs/examples/approved-design/main-yaml.md +24 -0
  31. package/docs/examples/approved-design/main.md +215 -0
  32. package/docs/examples/benchmark/index.md +33 -0
  33. package/docs/examples/benchmark/main-yaml.md +17 -0
  34. package/docs/examples/benchmark/main.md +117 -0
  35. package/docs/examples/cli-args/index.md +32 -0
  36. package/docs/examples/cli-args/main.md +107 -0
  37. package/docs/examples/collections/index.md +32 -0
  38. package/docs/examples/collections/main.md +118 -0
  39. package/docs/examples/collections-benchmark/index.md +34 -0
  40. package/docs/examples/collections-benchmark/main.md +115 -0
  41. package/docs/examples/cpu-benchmark/index.md +34 -0
  42. package/docs/examples/cpu-benchmark/main.md +90 -0
  43. package/docs/examples/developer-workflow/calculator.md +357 -0
  44. package/docs/examples/developer-workflow/dependencies/august/0.19.0/io/contracts.md +395 -0
  45. package/docs/examples/developer-workflow/index.md +37 -0
  46. package/docs/examples/developer-workflow/logging/console.md +124 -0
  47. package/docs/examples/developer-workflow/logging/export.md +70 -0
  48. package/docs/examples/developer-workflow/logging/logger.md +111 -0
  49. package/docs/examples/developer-workflow/main.md +172 -0
  50. package/docs/examples/drop/index.md +33 -0
  51. package/docs/examples/drop/main.md +85 -0
  52. package/docs/examples/drop/resource.md +95 -0
  53. package/docs/examples/errors/errors.md +85 -0
  54. package/docs/examples/errors/index.md +33 -0
  55. package/docs/examples/errors/main.md +92 -0
  56. package/docs/examples/ffi/index.md +33 -0
  57. package/docs/examples/ffi/main.md +75 -0
  58. package/docs/examples/ffi/native.md +93 -0
  59. package/docs/examples/generic-di/dependencies/august/0.19.0/io/contracts.md +395 -0
  60. package/docs/examples/generic-di/index.md +33 -0
  61. package/docs/examples/generic-di/main.md +111 -0
  62. package/docs/examples/generic-di/types.md +180 -0
  63. package/docs/examples/generics/index.md +33 -0
  64. package/docs/examples/generics/main.md +120 -0
  65. package/docs/examples/generics/types.md +189 -0
  66. package/docs/examples/hello/app/export.md +67 -0
  67. package/docs/examples/hello/app/greeter.md +190 -0
  68. package/docs/examples/hello/dependencies/august/0.19.0/io/contracts.md +395 -0
  69. package/docs/examples/hello/index.md +37 -0
  70. package/docs/examples/hello/logging/console.md +121 -0
  71. package/docs/examples/hello/logging/export.md +71 -0
  72. package/docs/examples/hello/logging/logger.md +120 -0
  73. package/docs/examples/hello/main.md +115 -0
  74. package/docs/examples/http-benchmark/index.md +35 -0
  75. package/docs/examples/http-benchmark/main.md +79 -0
  76. package/docs/examples/http-benchmark/routes.md +88 -0
  77. package/docs/examples/index.md +60 -0
  78. package/docs/examples/interceptors/app.md +250 -0
  79. package/docs/examples/interceptors/dependencies/august/0.19.0/io/contracts.md +395 -0
  80. package/docs/examples/interceptors/index.md +35 -0
  81. package/docs/examples/interceptors/interceptors.md +284 -0
  82. package/docs/examples/interceptors/logging.md +155 -0
  83. package/docs/examples/interceptors/main.md +162 -0
  84. package/docs/examples/json-benchmark/data.md +71 -0
  85. package/docs/examples/json-benchmark/dependencies/august/0.19.0/json/contracts.md +103 -0
  86. package/docs/examples/json-benchmark/index.md +35 -0
  87. package/docs/examples/json-benchmark/main.md +131 -0
  88. package/docs/examples/new-syntax/console.md +118 -0
  89. package/docs/examples/new-syntax/dependencies/august/0.19.0/io/contracts.md +395 -0
  90. package/docs/examples/new-syntax/greeter.md +145 -0
  91. package/docs/examples/new-syntax/index.md +36 -0
  92. package/docs/examples/new-syntax/logger.md +111 -0
  93. package/docs/examples/new-syntax/main.md +135 -0
  94. package/docs/examples/new-syntax/math.md +79 -0
  95. package/docs/examples/oidc-login/client/contracts.md +151 -0
  96. package/docs/examples/oidc-login/client/endpoints.md +263 -0
  97. package/docs/examples/oidc-login/client/export.md +107 -0
  98. package/docs/examples/oidc-login/client/login.md +515 -0
  99. package/docs/examples/oidc-login/client/logout.md +249 -0
  100. package/docs/examples/oidc-login/client/protocol.md +595 -0
  101. package/docs/examples/oidc-login/client/session.md +297 -0
  102. package/docs/examples/oidc-login/client/views.md +144 -0
  103. package/docs/examples/oidc-login/common/export.md +115 -0
  104. package/docs/examples/oidc-login/common/headers.md +162 -0
  105. package/docs/examples/oidc-login/common/keys.md +336 -0
  106. package/docs/examples/oidc-login/common/settings.md +116 -0
  107. package/docs/examples/oidc-login/common/views.md +103 -0
  108. package/docs/examples/oidc-login/dependencies/august/0.19.0/crypto/contracts.md +925 -0
  109. package/docs/examples/oidc-login/dependencies/august/0.19.0/crypto/jose.md +434 -0
  110. package/docs/examples/oidc-login/dependencies/august/0.19.0/json/contracts.md +103 -0
  111. package/docs/examples/oidc-login/dependencies/august/0.19.0/memory/store.md +374 -0
  112. package/docs/examples/oidc-login/dependencies/august/0.19.0/time/contracts.md +150 -0
  113. package/docs/examples/oidc-login/dependencies/august/0.19.0/web/contracts.md +532 -0
  114. package/docs/examples/oidc-login/index.md +57 -0
  115. package/docs/examples/oidc-login/main-yaml.md +28 -0
  116. package/docs/examples/oidc-login/main.md +283 -0
  117. package/docs/examples/oidc-login/provider/authorization.md +457 -0
  118. package/docs/examples/oidc-login/provider/contracts.md +267 -0
  119. package/docs/examples/oidc-login/provider/credentials.md +148 -0
  120. package/docs/examples/oidc-login/provider/discovery.md +215 -0
  121. package/docs/examples/oidc-login/provider/export.md +131 -0
  122. package/docs/examples/oidc-login/provider/token.md +364 -0
  123. package/docs/examples/oidc-login/provider/userinfo.md +228 -0
  124. package/docs/examples/oidc-login/provider/views.md +137 -0
  125. package/docs/examples/ownership/counter.md +140 -0
  126. package/docs/examples/ownership/index.md +33 -0
  127. package/docs/examples/ownership/main.md +90 -0
  128. package/docs/examples/ownership-transfer/dependencies/august/0.19.0/io/contracts.md +395 -0
  129. package/docs/examples/ownership-transfer/index.md +33 -0
  130. package/docs/examples/ownership-transfer/main.md +125 -0
  131. package/docs/examples/ownership-transfer/resource.md +150 -0
  132. package/docs/examples/packages-app/dependencies/packages/@example/aug-math/0.1.0/arithmetic.md +117 -0
  133. package/docs/examples/packages-app/index.md +34 -0
  134. package/docs/examples/packages-app/main-yaml.md +18 -0
  135. package/docs/examples/packages-app/main.md +80 -0
  136. package/docs/examples/packages-math/aug-package-json.md +24 -0
  137. package/docs/examples/packages-math/index.md +36 -0
  138. package/docs/examples/packages-math/package-json.md +25 -0
  139. package/docs/examples/packages-math/src/arithmetic.md +121 -0
  140. package/docs/examples/packages-math/src/export.md +63 -0
  141. package/docs/examples/startup-benchmark/index.md +34 -0
  142. package/docs/examples/startup-benchmark/main.md +68 -0
  143. package/docs/examples/visibility/counter.md +142 -0
  144. package/docs/examples/visibility/index.md +33 -0
  145. package/docs/examples/visibility/main.md +96 -0
  146. package/docs/examples.json +47 -0
  147. package/docs/getting-started.md +5 -0
  148. package/docs/grammar.md +136 -0
  149. package/docs/implementation-map.md +106 -0
  150. package/docs/index.md +46 -0
  151. package/docs/language-conformance.md +30 -0
  152. package/docs/language-constructs.md +1461 -0
  153. package/docs/language-design-audit.md +147 -0
  154. package/docs/maintaining-docs.md +43 -0
  155. package/docs/packages.md +184 -0
  156. package/docs/performance.md +535 -0
  157. package/docs/production-readiness.md +39 -0
  158. package/docs/reference.md +361 -0
  159. package/docs/release-review.md +23 -0
  160. package/docs/releasing.md +79 -0
  161. package/docs/roadmap.md +13 -0
  162. package/docs/specifications.md +86 -0
  163. package/docs/testing.md +120 -0
  164. package/docs/tooling.md +197 -0
  165. package/docs/web-implementation-plan.md +39 -0
  166. package/docs/web-library-gaps.md +27 -0
  167. package/docs/web.md +197 -0
  168. package/examples/approved-design/.aug-spec/august/0.19.0/io/contracts.aug +36 -0
  169. package/examples/approved-design/.aug-spec/august/0.19.0/io/contracts.aug.md +316 -0
  170. package/examples/approved-design/.aug-spec/manifest.json +14 -0
  171. package/examples/approved-design/counters.aug +22 -0
  172. package/examples/approved-design/counters.aug.md +177 -0
  173. package/examples/approved-design/domain/app.aug +12 -0
  174. package/examples/approved-design/domain/app.aug.md +97 -0
  175. package/examples/approved-design/domain/export.aug +5 -0
  176. package/examples/approved-design/domain/export.aug.md +25 -0
  177. package/examples/approved-design/domain/models.aug +2 -0
  178. package/examples/approved-design/domain/models.aug.md +30 -0
  179. package/examples/approved-design/domain/numbers.aug +29 -0
  180. package/examples/approved-design/domain/numbers.aug.md +141 -0
  181. package/examples/approved-design/main.aug +26 -0
  182. package/examples/approved-design/main.aug.md +108 -0
  183. package/examples/approved-design/main.yaml +8 -0
  184. package/examples/benchmark/.aug-spec/manifest.json +7 -0
  185. package/examples/benchmark/main.aug +17 -0
  186. package/examples/benchmark/main.aug.md +43 -0
  187. package/examples/benchmark/main.yaml +1 -0
  188. package/examples/cli-args/.aug-spec/manifest.json +7 -0
  189. package/examples/cli-args/main.aug +15 -0
  190. package/examples/cli-args/main.aug.md +38 -0
  191. package/examples/collections/.aug-spec/manifest.json +7 -0
  192. package/examples/collections/main.aug +18 -0
  193. package/examples/collections/main.aug.md +43 -0
  194. package/examples/developer-workflow/.aug-spec/august/0.19.0/io/contracts.aug +36 -0
  195. package/examples/developer-workflow/.aug-spec/august/0.19.0/io/contracts.aug.md +316 -0
  196. package/examples/developer-workflow/.aug-spec/manifest.json +13 -0
  197. package/examples/developer-workflow/calculator.aug +55 -0
  198. package/examples/developer-workflow/calculator.aug.md +228 -0
  199. package/examples/developer-workflow/logging/console.aug +8 -0
  200. package/examples/developer-workflow/logging/console.aug.md +67 -0
  201. package/examples/developer-workflow/logging/export.aug +2 -0
  202. package/examples/developer-workflow/logging/export.aug.md +19 -0
  203. package/examples/developer-workflow/logging/logger.aug +6 -0
  204. package/examples/developer-workflow/logging/logger.aug.md +57 -0
  205. package/examples/developer-workflow/main.aug +25 -0
  206. package/examples/developer-workflow/main.aug.md +79 -0
  207. package/examples/drop/.aug-spec/manifest.json +8 -0
  208. package/examples/drop/main.aug +3 -0
  209. package/examples/drop/main.aug.md +35 -0
  210. package/examples/drop/resource.aug +8 -0
  211. package/examples/drop/resource.aug.md +44 -0
  212. package/examples/errors/.aug-spec/manifest.json +8 -0
  213. package/examples/errors/errors.aug +6 -0
  214. package/examples/errors/errors.aug.md +33 -0
  215. package/examples/errors/main.aug +7 -0
  216. package/examples/errors/main.aug.md +36 -0
  217. package/examples/ffi/.aug-spec/manifest.json +8 -0
  218. package/examples/ffi/main.aug +2 -0
  219. package/examples/ffi/main.aug.md +27 -0
  220. package/examples/ffi/native.aug +6 -0
  221. package/examples/ffi/native.aug.md +43 -0
  222. package/examples/generic-di/.aug-spec/august/0.19.0/io/contracts.aug +36 -0
  223. package/examples/generic-di/.aug-spec/august/0.19.0/io/contracts.aug.md +316 -0
  224. package/examples/generic-di/.aug-spec/manifest.json +10 -0
  225. package/examples/generic-di/main.aug +9 -0
  226. package/examples/generic-di/main.aug.md +49 -0
  227. package/examples/generic-di/types.aug +17 -0
  228. package/examples/generic-di/types.aug.md +124 -0
  229. package/examples/generics/.aug-spec/manifest.json +8 -0
  230. package/examples/generics/main.aug +9 -0
  231. package/examples/generics/main.aug.md +58 -0
  232. package/examples/generics/types.aug +19 -0
  233. package/examples/generics/types.aug.md +132 -0
  234. package/examples/hello/.aug-spec/august/0.19.0/io/contracts.aug +36 -0
  235. package/examples/hello/.aug-spec/august/0.19.0/io/contracts.aug.md +316 -0
  236. package/examples/hello/.aug-spec/manifest.json +14 -0
  237. package/examples/hello/app/export.aug +1 -0
  238. package/examples/hello/app/export.aug.md +17 -0
  239. package/examples/hello/app/greeter.aug +22 -0
  240. package/examples/hello/app/greeter.aug.md +109 -0
  241. package/examples/hello/logging/console.aug +7 -0
  242. package/examples/hello/logging/console.aug.md +65 -0
  243. package/examples/hello/logging/export.aug +2 -0
  244. package/examples/hello/logging/export.aug.md +19 -0
  245. package/examples/hello/logging/logger.aug +9 -0
  246. package/examples/hello/logging/logger.aug.md +59 -0
  247. package/examples/hello/main.aug +9 -0
  248. package/examples/hello/main.aug.md +49 -0
  249. package/examples/interceptors/.aug-spec/august/0.19.0/io/contracts.aug +36 -0
  250. package/examples/interceptors/.aug-spec/august/0.19.0/io/contracts.aug.md +316 -0
  251. package/examples/interceptors/.aug-spec/manifest.json +12 -0
  252. package/examples/interceptors/app.aug +28 -0
  253. package/examples/interceptors/app.aug.md +162 -0
  254. package/examples/interceptors/interceptors.aug +40 -0
  255. package/examples/interceptors/interceptors.aug.md +180 -0
  256. package/examples/interceptors/logging.aug +12 -0
  257. package/examples/interceptors/logging.aug.md +96 -0
  258. package/examples/interceptors/main.aug +22 -0
  259. package/examples/interceptors/main.aug.md +76 -0
  260. package/examples/new-syntax/.aug-spec/august/0.19.0/io/contracts.aug +36 -0
  261. package/examples/new-syntax/.aug-spec/august/0.19.0/io/contracts.aug.md +316 -0
  262. package/examples/new-syntax/.aug-spec/manifest.json +13 -0
  263. package/examples/new-syntax/console.aug +7 -0
  264. package/examples/new-syntax/console.aug.md +63 -0
  265. package/examples/new-syntax/greeter.aug +10 -0
  266. package/examples/new-syntax/greeter.aug.md +89 -0
  267. package/examples/new-syntax/logger.aug +6 -0
  268. package/examples/new-syntax/logger.aug.md +57 -0
  269. package/examples/new-syntax/main.aug +12 -0
  270. package/examples/new-syntax/main.aug.md +64 -0
  271. package/examples/new-syntax/math.aug +3 -0
  272. package/examples/new-syntax/math.aug.md +29 -0
  273. package/examples/oidc-login/.aug-spec/august/0.19.0/crypto/contracts.aug +73 -0
  274. package/examples/oidc-login/.aug-spec/august/0.19.0/crypto/contracts.aug.md +791 -0
  275. package/examples/oidc-login/.aug-spec/august/0.19.0/crypto/jose.aug +68 -0
  276. package/examples/oidc-login/.aug-spec/august/0.19.0/crypto/jose.aug.md +266 -0
  277. package/examples/oidc-login/.aug-spec/august/0.19.0/json/contracts.aug +6 -0
  278. package/examples/oidc-login/.aug-spec/august/0.19.0/json/contracts.aug.md +55 -0
  279. package/examples/oidc-login/.aug-spec/august/0.19.0/memory/store.aug +45 -0
  280. package/examples/oidc-login/.aug-spec/august/0.19.0/memory/store.aug.md +250 -0
  281. package/examples/oidc-login/.aug-spec/august/0.19.0/time/contracts.aug +11 -0
  282. package/examples/oidc-login/.aug-spec/august/0.19.0/time/contracts.aug.md +96 -0
  283. package/examples/oidc-login/.aug-spec/august/0.19.0/web/contracts.aug +58 -0
  284. package/examples/oidc-login/.aug-spec/august/0.19.0/web/contracts.aug.md +420 -0
  285. package/examples/oidc-login/.aug-spec/manifest.json +40 -0
  286. package/examples/oidc-login/README.md +29 -0
  287. package/examples/oidc-login/client/contracts.aug +7 -0
  288. package/examples/oidc-login/client/contracts.aug.md +80 -0
  289. package/examples/oidc-login/client/endpoints.aug +21 -0
  290. package/examples/oidc-login/client/endpoints.aug.md +161 -0
  291. package/examples/oidc-login/client/export.aug +7 -0
  292. package/examples/oidc-login/client/export.aug.md +29 -0
  293. package/examples/oidc-login/client/login.aug +58 -0
  294. package/examples/oidc-login/client/login.aug.md +331 -0
  295. package/examples/oidc-login/client/logout.aug +18 -0
  296. package/examples/oidc-login/client/logout.aug.md +150 -0
  297. package/examples/oidc-login/client/protocol.aug +106 -0
  298. package/examples/oidc-login/client/protocol.aug.md +326 -0
  299. package/examples/oidc-login/client/session.aug +34 -0
  300. package/examples/oidc-login/client/session.aug.md +155 -0
  301. package/examples/oidc-login/client/views.aug +21 -0
  302. package/examples/oidc-login/client/views.aug.md +68 -0
  303. package/examples/oidc-login/common/export.aug +9 -0
  304. package/examples/oidc-login/common/export.aug.md +33 -0
  305. package/examples/oidc-login/common/headers.aug +11 -0
  306. package/examples/oidc-login/common/headers.aug.md +79 -0
  307. package/examples/oidc-login/common/keys.aug +37 -0
  308. package/examples/oidc-login/common/keys.aug.md +207 -0
  309. package/examples/oidc-login/common/settings.aug +4 -0
  310. package/examples/oidc-login/common/settings.aug.md +47 -0
  311. package/examples/oidc-login/common/views.aug +16 -0
  312. package/examples/oidc-login/common/views.aug.md +34 -0
  313. package/examples/oidc-login/main.aug +39 -0
  314. package/examples/oidc-login/main.aug.md +166 -0
  315. package/examples/oidc-login/main.yaml +12 -0
  316. package/examples/oidc-login/provider/authorization.aug +59 -0
  317. package/examples/oidc-login/provider/authorization.aug.md +264 -0
  318. package/examples/oidc-login/provider/contracts.aug +15 -0
  319. package/examples/oidc-login/provider/contracts.aug.md +193 -0
  320. package/examples/oidc-login/provider/credentials.aug +10 -0
  321. package/examples/oidc-login/provider/credentials.aug.md +64 -0
  322. package/examples/oidc-login/provider/discovery.aug +13 -0
  323. package/examples/oidc-login/provider/discovery.aug.md +133 -0
  324. package/examples/oidc-login/provider/export.aug +13 -0
  325. package/examples/oidc-login/provider/export.aug.md +41 -0
  326. package/examples/oidc-login/provider/token.aug +37 -0
  327. package/examples/oidc-login/provider/token.aug.md +223 -0
  328. package/examples/oidc-login/provider/userinfo.aug +26 -0
  329. package/examples/oidc-login/provider/userinfo.aug.md +104 -0
  330. package/examples/oidc-login/provider/views.aug +18 -0
  331. package/examples/oidc-login/provider/views.aug.md +63 -0
  332. package/examples/ownership/.aug-spec/manifest.json +8 -0
  333. package/examples/ownership/counter.aug +14 -0
  334. package/examples/ownership/counter.aug.md +85 -0
  335. package/examples/ownership/main.aug +4 -0
  336. package/examples/ownership/main.aug.md +38 -0
  337. package/examples/ownership-transfer/.aug-spec/august/0.19.0/io/contracts.aug +36 -0
  338. package/examples/ownership-transfer/.aug-spec/august/0.19.0/io/contracts.aug.md +316 -0
  339. package/examples/ownership-transfer/.aug-spec/manifest.json +10 -0
  340. package/examples/ownership-transfer/main.aug +9 -0
  341. package/examples/ownership-transfer/main.aug.md +63 -0
  342. package/examples/ownership-transfer/resource.aug +16 -0
  343. package/examples/ownership-transfer/resource.aug.md +89 -0
  344. package/examples/packages/README.md +15 -0
  345. package/examples/packages/app/.aug-spec/manifest.json +9 -0
  346. package/examples/packages/app/.aug-spec/packages/@example/aug-math/0.1.0/arithmetic.aug +9 -0
  347. package/examples/packages/app/.aug-spec/packages/@example/aug-math/0.1.0/arithmetic.aug.md +63 -0
  348. package/examples/packages/app/main.aug +2 -0
  349. package/examples/packages/app/main.aug.md +33 -0
  350. package/examples/packages/app/main.yaml +2 -0
  351. package/examples/packages/math/.aug-spec/manifest.json +8 -0
  352. package/examples/packages/math/README.md +3 -0
  353. package/examples/packages/math/aug-package.json +8 -0
  354. package/examples/packages/math/package.json +9 -0
  355. package/examples/packages/math/src/arithmetic.aug +8 -0
  356. package/examples/packages/math/src/arithmetic.aug.md +63 -0
  357. package/examples/packages/math/src/export.aug +1 -0
  358. package/examples/packages/math/src/export.aug.md +17 -0
  359. package/examples/visibility/.aug-spec/manifest.json +8 -0
  360. package/examples/visibility/counter.aug +14 -0
  361. package/examples/visibility/counter.aug.md +87 -0
  362. package/examples/visibility/main.aug +7 -0
  363. package/examples/visibility/main.aug.md +39 -0
  364. package/package.json +50 -0
  365. package/runtime/aug_crypto.c +121 -0
  366. package/runtime/aug_html.c +93 -0
  367. package/runtime/aug_http.c +829 -0
  368. package/runtime/aug_json.c +165 -0
  369. package/runtime/aug_runtime.c +792 -0
  370. package/runtime/aug_runtime.h +245 -0
  371. package/runtime/aug_tasks.c +172 -0
  372. package/runtime/aug_time.c +5 -0
  373. package/runtime/aug_values.c +80 -0
  374. package/scripts/bootstrap-native.mjs +121 -0
  375. package/scripts/native-dependencies.lock.json +65 -0
  376. package/scripts/native-home.mjs +11 -0
  377. package/src/actions.js +109 -0
  378. package/src/ast.js +19 -0
  379. package/src/builtins.js +93 -0
  380. package/src/checker.js +2896 -0
  381. package/src/cli.js +344 -0
  382. package/src/codegen.js +1159 -0
  383. package/src/config.js +149 -0
  384. package/src/continuation.js +110 -0
  385. package/src/di.js +24 -0
  386. package/src/documentation.js +27 -0
  387. package/src/editor.js +782 -0
  388. package/src/effects.js +29 -0
  389. package/src/fixes.js +222 -0
  390. package/src/formatter.js +296 -0
  391. package/src/freshness.js +154 -0
  392. package/src/help.js +212 -0
  393. package/src/html.js +27 -0
  394. package/src/http-policies.js +90 -0
  395. package/src/inference.js +16 -0
  396. package/src/interceptors.js +70 -0
  397. package/src/javadoc.js +67 -0
  398. package/src/lexer.js +199 -0
  399. package/src/libraries.js +31 -0
  400. package/src/lsp.js +180 -0
  401. package/src/native.js +144 -0
  402. package/src/navigation.js +162 -0
  403. package/src/openapi.js +220 -0
  404. package/src/ownership.js +232 -0
  405. package/src/package-manager.js +299 -0
  406. package/src/parser.js +1155 -0
  407. package/src/policies.js +130 -0
  408. package/src/project-init.js +19 -0
  409. package/src/project.js +292 -0
  410. package/src/schemas.js +62 -0
  411. package/src/semantic.js +255 -0
  412. package/src/spec.js +738 -0
  413. package/src/testing.js +186 -0
  414. package/src/types.js +10 -0
package/src/spec.js ADDED
@@ -0,0 +1,738 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { existsSync, lstatSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from 'node:fs';
3
+ import { basename, dirname, join, relative, resolve } from 'node:path';
4
+ import { fieldsOf, typeName } from "./ast.js";
5
+ import { builtinFunctions, builtinProperties, collectionOperations, operationType } from "./builtins.js";
6
+ import { callableDocumentation } from "./documentation.js";
7
+ import { javadocBefore } from "./javadoc.js";
8
+ import { libraryChild, libraryRelative } from "./libraries.js";
9
+ import { compilerVersion } from "./package-manager.js";
10
+ import { tyName } from "./types.js";
11
+ const generated = '<!-- Generated by aug spec. Edit the August source, then regenerate. -->';
12
+ const copied = '// Generated by aug spec. This is a copy of the installed dependency source.\n';
13
+ 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; };
14
+ const url = (value) => value.replaceAll('\\', '/').split('/').map(encodeURIComponent).join('/');
15
+ const anchor = (name) => 'symbol-' + name;
16
+ const hash = (text) => createHash('sha256').update(text).digest('hex');
17
+ const compare = (left, right) => left < right ? -1 : left > right ? 1 : 0;
18
+ const unreachable = (node) => { throw new Error(`No specification renderer for ${node.kind}`); };
19
+ /** Render checked code, never execute it. All paths and ordering are reproducible. */
20
+ export function generateSpecs(checked, options = {}) {
21
+ if (checked.diagnostics.some(issue => issue.severity !== 'warning'))
22
+ throw new Error('Fix compiler errors before generating specifications');
23
+ const project = checked.project;
24
+ const owned = options.files ?? [...project.files.values()].filter(file => !file.builtin && !file.package);
25
+ const own = new Set(owned.map(file => file.path));
26
+ const docs = new Map(), sources = new Map();
27
+ for (const file of project.files.values()) {
28
+ if (own.has(file.path)) {
29
+ docs.set(file.path, file.path + '.md');
30
+ sources.set(file.path, file.path);
31
+ continue;
32
+ }
33
+ const scope = file.package && project.packages.scopes.get(file.package);
34
+ const identity = scope ? join('packages', scope.name, scope.version, relative(scope.sourceRoot, file.path)) :
35
+ join('august', compilerVersion(), libraryRelative(project.libraries, file.path));
36
+ const source = join(project.root, '.aug-spec', identity);
37
+ docs.set(file.path, source + '.md');
38
+ sources.set(file.path, source);
39
+ }
40
+ const queue = [...owned].sort((a, b) => compare(a.path, b.path));
41
+ const seen = new Set(), outputs = [];
42
+ for (let index = 0; index < queue.length; index++) {
43
+ const file = queue[index];
44
+ if (seen.has(file.path))
45
+ continue;
46
+ seen.add(file.path);
47
+ const writer = new SpecWriter(checked, file, docs, sources, own, path => {
48
+ const dependency = project.files.get(path);
49
+ if (dependency && !seen.has(path))
50
+ queue.push(dependency);
51
+ });
52
+ outputs.push({ path: docs.get(file.path), text: writer.render(), source: file.path });
53
+ if (!own.has(file.path))
54
+ outputs.push({ path: sources.get(file.path), text: copied + file.source, source: file.path });
55
+ }
56
+ return outputs.sort((a, b) => compare(a.path, b.path));
57
+ }
58
+ class SpecWriter {
59
+ checked;
60
+ file;
61
+ docs;
62
+ sources;
63
+ own;
64
+ enqueue;
65
+ used = new Map();
66
+ builtins = new Map();
67
+ properties = new Map();
68
+ locals = new Map();
69
+ constructor(checked, file, docs, sources, own, enqueue) {
70
+ this.checked = checked;
71
+ this.file = file;
72
+ this.docs = docs;
73
+ this.sources = sources;
74
+ this.own = own;
75
+ this.enqueue = enqueue;
76
+ }
77
+ definition(name) { return this.checked.project.scopes.get(this.file.path)?.get(name); }
78
+ use(def, method, field) {
79
+ if (!def || def.file === this.file.path)
80
+ return;
81
+ if (method && 'methods' in def.node && !def.node.methods.includes(method)) {
82
+ const owner = [...this.checked.project.definitions.values()].find(owner => 'methods' in owner.node && owner.node.methods.includes(method));
83
+ this.use(owner, method);
84
+ method = undefined;
85
+ }
86
+ let entry = this.used.get(def.id);
87
+ if (!entry) {
88
+ entry = { def, operations: new Set(), fields: new Set() };
89
+ this.used.set(def.id, entry);
90
+ }
91
+ if (method)
92
+ entry.operations.add(method);
93
+ if (field)
94
+ entry.fields.add(field);
95
+ this.enqueue(def.file);
96
+ }
97
+ link(def, name = def.name, label = name) {
98
+ this.use(def);
99
+ return `[${code(label)}](${url(relative(dirname(this.docs.get(this.file.path)), this.docs.get(def.file)))}#${encodeURIComponent(anchor(name))})`;
100
+ }
101
+ source(span) {
102
+ return `[source](${url(relative(dirname(this.docs.get(this.file.path)), this.sources.get(span.file)))}#L${span.line + (this.own.has(span.file) ? 0 : 1)})`;
103
+ }
104
+ heading(name, span, level = 2) {
105
+ return `<a id="${anchor(name)}"></a>\n\n${'#'.repeat(level)} ${code(name)}\n\n${this.source(span)}\n\n`;
106
+ }
107
+ type(type, file = this.file.path) {
108
+ const def = this.checked.project.scopes.get(file)?.get(type.name);
109
+ this.use(def);
110
+ type.args.forEach(arg => this.type(arg, file));
111
+ return def ? this.link(def, def.name, typeName(type)) : code(typeName(type));
112
+ }
113
+ notes(span, method, owner) {
114
+ const doc = method ? callableDocumentation(this.checked.project, method, owner) : javadocBefore(this.file.source, span.start);
115
+ return doc ? `**Author documentation**\n\n${doc.markdown}\n\n` : '';
116
+ }
117
+ generics(node) {
118
+ return node.typeParams.length ? 'Type parameters: ' + node.typeParams.map(name => code(name) +
119
+ (node.typeVariance?.[name] ? ` (${node.typeVariance[name] === 'out' ? 'produces values' : 'accepts values'})` : '') +
120
+ (node.typeConstraints?.[name]?.length ? ` must satisfy ${node.typeConstraints[name].map(type => this.type(type)).join(' and ')}` : '')).join('; ') + '.\n\n' : '';
121
+ }
122
+ inputs(params, fields = false, file = this.file.path) {
123
+ if (!params.length)
124
+ return '';
125
+ return '**Inputs**\n\n' + params.map(param => {
126
+ this.locals.set(param.name, param.type);
127
+ const parts = [`${code(param.label ?? param.name)} (${this.type(param.type, file)})`];
128
+ if (param.injected)
129
+ parts.push('injected; callers omit it');
130
+ else if (param.source)
131
+ parts.push(`read from the HTTP ${param.source.kind}${param.source.name ? ' named ' + code(param.source.name) : ''}${param.type.optional ? '; absent value becomes null' : ''}`);
132
+ else
133
+ parts.push(param.type.optional ? 'optional labeled input; omission becomes null' : 'required labeled input');
134
+ if (param.ownership === 'own')
135
+ parts.push('transfers ownership');
136
+ else if (param.ownership === 'borrow')
137
+ parts.push('allows exclusive mutation during the call');
138
+ if (fields)
139
+ parts.push(`stored as ${code(param.name)}${param.name.startsWith('_') ? ' (private)' : ''}${param.mutable ? ' and mutable' : ' and read-only after initialization'}`);
140
+ return '- ' + parts.join(' — ') + '.';
141
+ }).join('\n') + '\n\n';
142
+ }
143
+ contract(method) {
144
+ const effects = this.checked.effectContracts.get(method);
145
+ const layers = this.checked.interceptorPlans.get(method) ?? [];
146
+ const errors = [...new Set([...method.throws.map(type => { this.type(type, method.span.file); return typeName(type); }), ...layers.flatMap(layer => layer.errors.map(tyName))])];
147
+ const changes = effects?.changes ?? method.changes ?? [];
148
+ const uses = [...(effects?.uses.values() ?? method.uses ?? [])];
149
+ let text = this.generics(method) + this.inputs(method.params, false, method.span.file);
150
+ text += `Returns: ${method.returns.name === 'void' ? 'no value' : (method.returnOwnership === 'own' ? 'ownership of ' : '') + this.type(method.returns, method.span.file)}.\n\n`;
151
+ if (changes.length)
152
+ text += 'May change: ' + changes.map(code).join(', ') + '.\n\n';
153
+ if (uses.length)
154
+ text += 'Capabilities: ' + uses.map(effect => {
155
+ const def = 'capability' in effect ? effect.capability?.def : undefined;
156
+ const target = def ?? this.definition(effect.source);
157
+ const operation = target && 'methods' in target.node ? target.node.methods.find(method => method.name === effect.operation) : undefined;
158
+ this.use(target, operation);
159
+ return target ? this.link(target, operation ? `${target.name}.${operation.name}` : target.name, `${effect.source}.${effect.operation}`) : code(`${effect.source}.${effect.operation}`);
160
+ }).join(', ') + '.\n\n';
161
+ if (errors.length)
162
+ text += 'Can fail with ' + errors.map(code).join(', ') + '. Callers must catch or propagate these errors.\n\n';
163
+ if (method.endpoint) {
164
+ const endpoint = method.endpoint;
165
+ text += `HTTP route: ${code(endpoint.method)} ${code(endpoint.path)}. ` +
166
+ (endpoint.streams ? `Stream ${this.type(method.returns)} items. ` : `Use status ${endpoint.status} when the handler returns a body; a returned HttpResponse can set its own status. `) +
167
+ 'An unhandled request failure returns status 500 and cancels its request tasks.\n\n';
168
+ if (endpoint.errors.length)
169
+ text += 'Declared HTTP failures: ' + endpoint.errors.map(error => `${this.type(error.type)} returns status ${error.status}`).join('; ') + '.\n\n';
170
+ }
171
+ return text;
172
+ }
173
+ layers(node) {
174
+ const layers = this.checked.interceptorPlans.get(node) ?? [];
175
+ const policies = node.kind === 'function' ? this.checked.httpPolicies.get(node) ?? [] : [];
176
+ if (!layers.length && !policies.length)
177
+ return '';
178
+ let text = '**Interceptors, in execution order**\n\n';
179
+ policies.forEach((policy, index) => {
180
+ text += `${index + 1}. ${this.policy(policy)}` +
181
+ (node.kind === 'function' && policy.dependencies.length ? ' Use ' + policy.dependencies.map(index => code(node.params[index].name)).join(', ') + '.' : '') + '\n';
182
+ });
183
+ layers.forEach((layer, index) => {
184
+ this.use(layer.definition, layer.around);
185
+ text += `${index + policies.length + 1}. Call ${this.link(layer.definition, `${layer.definition.name}.around`)}. ` +
186
+ 'It can call the next layer or finish with its own result or failure. ' +
187
+ (layer.annotation.mappings.length ? 'Map ' + layer.annotation.mappings.map(mapping => code(mapping.source) + ' to ' + code(mapping.name)).join(', ') + '. ' : '') +
188
+ 'Unselected inputs pass through.\n';
189
+ });
190
+ return text + '\nThe first layer wraps the remaining layers. HTTP policies run before wire decoding; custom interceptors run after decoding. Follow linked behavior to see its conditions, input changes, and calls to the next layer.\n\n';
191
+ }
192
+ policy(policy) {
193
+ const options = policy.options;
194
+ switch (policy.name) {
195
+ case 'RequireLogin': return 'Authenticate the request. If no identity is returned, finish with status 401.';
196
+ case 'RequirePermission': return `Authenticate the request and authorize permission ${code(String(options.permission))}. No identity returns 401; denied permission returns 403.`;
197
+ case 'LogRequest': return 'Call the request logger after transport completion or disconnect, in reverse layer order. Record disconnect as status 499. This layer observes rejections from layers inside it.';
198
+ case 'RateLimit': return `Allow ${options.requests} requests per ${options.seconds} seconds for each endpoint and trusted transport peer. Ignore forwarding headers. Excess requests return 429.`;
199
+ case 'Timeout': return `Set a deadline of ${options.milliseconds} milliseconds for the handler, child tasks, and response transport. Before output, expiry returns 504; after output, it closes the stream. Native calls finish before cooperative cancellation; buffered request reception precedes this deadline.`;
200
+ case 'Cors': return `Allow origins ${code(JSON.stringify(options.origins))}. ${options.credentials ? 'Allow' : 'Do not allow'} credentials. ` +
201
+ (options.headers ? `Allow request headers ${code(JSON.stringify(options.headers))}. ` : '') + 'A supplied disallowed origin returns 403. Preflight checks the route method and requested headers.';
202
+ case 'Compress': return 'Negotiate gzip. Respect existing Content-Encoding and bodyless statuses. Encode stream items as complete concatenated gzip members.';
203
+ }
204
+ }
205
+ receiver(expr) {
206
+ const checked = this.checked.expressionTypes.get(expr)?.def;
207
+ if (checked)
208
+ return checked;
209
+ const ref = expr.kind === 'name' ? this.locals.get(expr.name) : undefined;
210
+ if (ref)
211
+ return this.definition(ref.name);
212
+ if (expr.kind === 'resolve')
213
+ return this.checked.bindings.find(binding => binding.key === expr.name)?.exposedType.def;
214
+ if (expr.kind === 'call' && expr.callee.kind === 'name') {
215
+ const def = this.definition(expr.callee.name);
216
+ if (def?.node.kind === 'class')
217
+ return def;
218
+ if (def?.node.kind === 'function')
219
+ return this.definition(def.node.returns.name);
220
+ }
221
+ return undefined;
222
+ }
223
+ method(def, name, seen = new Set()) {
224
+ if (seen.has(def.id))
225
+ return;
226
+ seen.add(def.id);
227
+ if (!('methods' in def.node))
228
+ return;
229
+ const direct = def.node.methods.find(method => method.name === name);
230
+ if (direct)
231
+ return direct;
232
+ const refs = def.node.kind === 'class' ? def.node.implements : def.node.kind === 'interface' ? def.node.extends : [];
233
+ for (const ref of refs) {
234
+ const parent = this.checked.project.scopes.get(def.file)?.get(ref.name);
235
+ const inherited = parent && this.method(parent, name, seen);
236
+ if (inherited)
237
+ return inherited;
238
+ }
239
+ }
240
+ expression(expr, nested = false) {
241
+ switch (expr.kind) {
242
+ case 'literal': return expr.value === null ? 'null' : code(expr.numericText ?? JSON.stringify(expr.value));
243
+ case 'name': {
244
+ const def = !this.locals.has(expr.name) ? this.definition(expr.name) : undefined;
245
+ this.use(def);
246
+ return def ? this.link(def) : code(expr.name);
247
+ }
248
+ case 'member': {
249
+ const def = this.receiver(expr.object);
250
+ this.use(def, undefined, expr.name);
251
+ const name = expr.name, receiverType = this.checked.expressionTypes.get(expr.object)?.name;
252
+ const property = receiverType && builtinProperties[receiverType]?.find(property => property.name === name);
253
+ if (property)
254
+ this.properties.set(receiverType + '.' + name, { ...property, type: this.checked.expressionTypes.get(expr) ? tyName(this.checked.expressionTypes.get(expr)) : property.type });
255
+ return `${code(name)} of ${this.expression(expr.object)}`;
256
+ }
257
+ case 'unary': {
258
+ if (expr.op === '!' && expr.value.kind === 'binary' && (expr.value.op === '==' || expr.value.op === '!='))
259
+ return `${this.expression(expr.value.left, true)} ${expr.value.op === '==' ? 'does not equal' : 'equals'} ${this.expression(expr.value.right, true)}`;
260
+ return `${expr.op === '!' ? 'not' : 'the negative of'} (${this.expression(expr.value)})`;
261
+ }
262
+ case 'binary': {
263
+ const words = { '+': 'plus', '-': 'minus', '*': 'times', '/': 'divided by', '==': 'equals', '!=': 'does not equal', '<': 'is less than', '>': 'is greater than', '<=': 'is at most', '>=': 'is at least', '&&': 'and', '||': 'or' };
264
+ if (!words[expr.op])
265
+ throw new Error(`No specification renderer for operator ${expr.op}`);
266
+ const textParts = this.stringParts(expr);
267
+ if (expr.op === '+' && textParts.length >= 3)
268
+ return 'text formed by joining ' + textParts.map(part => this.expression(part)).join(', ') + ' in order';
269
+ const result = `${this.expression(expr.left, expr.left.kind === 'binary')} ${words[expr.op]} ${this.expression(expr.right, expr.right.kind === 'binary')}`;
270
+ return nested ? `(${result})` : result;
271
+ }
272
+ case 'collection': {
273
+ if (expr.collection === 'Map')
274
+ return 'a map with ' + expr.items.filter((_, index) => index % 2 === 0).map((key, index) => `${this.expression(key)} mapped to ${this.expression(expr.items[index * 2 + 1])}`).join('; ');
275
+ return `a ${expr.collection === 'empty' ? 'context-typed empty collection' : expr.collection.toLowerCase()}` + (expr.items.length ? ' containing ' + expr.items.map(item => this.expression(item)).join(', ') : ' with no items');
276
+ }
277
+ case 'resolve': return `the instance provided for ${code(expr.name)}${expr.typeArgs.length ? ' with type arguments ' + expr.typeArgs.map(type => this.type(type)).join(', ') : ''}`;
278
+ case 'call': return this.call(expr);
279
+ case 'start': return `a child task that starts ${this.expression(expr.call)}; evaluate its receiver and inputs now, and run its operation concurrently`;
280
+ case 'wait': return `the results of waiting for ${expr.tasks.map(task => this.expression(task)).join(' and ')}; keep input order when returning a tuple or a list, join cleanup, and propagate the first failure`;
281
+ case 'handle': {
282
+ const plan = this.checked.actions.get(expr);
283
+ return `a deferred HTTP form action for ${plan ? this.link(plan.endpoint) : this.expression(expr.call)}` +
284
+ (expr.call.kind === 'call' && expr.call.args.length ? '; inputs: ' + expr.call.args.map((value, index) => (expr.call.kind === 'call' ? code(expr.call.argLabels[index] ?? (value.kind === 'name' ? value.name : String(index + 1))) + ' from ' : '') + this.expression(value)).join('; ') : '') +
285
+ '; capture supplied values when rendering, and read form inputs when submitted; send the form to that endpoint with its declared HTTP method';
286
+ }
287
+ case 'formInput': return 'the checked form input supplied when the HTTP form is submitted';
288
+ case 'markupText': return code(expr.text);
289
+ case 'markup': {
290
+ const def = this.definition(expr.tag);
291
+ this.use(def);
292
+ return (def ? 'the server component ' + this.link(def) : 'the HTML element ' + code(expr.tag || 'fragment')) +
293
+ (expr.attributes.length ? ' with ' + expr.attributes.map(attribute => `${code(attribute.name)} = ${this.expression(attribute.value)}`).join(', ') : '') +
294
+ (expr.children.length ? ' containing ' + expr.children.map(child => this.expression(child)).join(', ') : '') +
295
+ ' (rendered on the server with embedded text escaped)';
296
+ }
297
+ default: return unreachable(expr);
298
+ }
299
+ }
300
+ call(expr) {
301
+ if (expr.callee.kind === 'name' && ['List', 'Set', 'Map'].includes(expr.callee.name) && !this.definition(expr.callee.name)) {
302
+ const name = expr.callee.name, types = expr.typeArgs.map(type => this.type(type));
303
+ const items = expr.args.map(arg => this.expression(arg));
304
+ const label = name === 'Map' ? 'map from ' + (types[0] ?? 'any') + ' to ' + (types[1] ?? 'any') :
305
+ name.toLowerCase() + ' of ' + (types[0] ?? 'any');
306
+ return items.length ? 'a ' + label + ' containing ' + items.join(', ') : 'an empty ' + label;
307
+ }
308
+ let target, method;
309
+ if (expr.callee.kind === 'name') {
310
+ const name = expr.callee.name;
311
+ const def = this.definition(expr.callee.name);
312
+ this.use(def);
313
+ if (def?.node.kind === 'class' && def.file !== this.file.path)
314
+ this.used.get(def.id).constructed = true;
315
+ method = def?.node.kind === 'function' ? def.node : undefined;
316
+ const builtin = !def && builtinFunctions.find(operation => operation.name === name);
317
+ if (builtin)
318
+ this.builtins.set(builtin.name, builtin);
319
+ target = def ? this.link(def) : code(expr.callee.name);
320
+ }
321
+ else if (expr.callee.kind === 'member') {
322
+ const name = expr.callee.name;
323
+ const def = this.receiver(expr.callee.object);
324
+ method = def && this.method(def, expr.callee.name);
325
+ this.use(def, method);
326
+ const receiver = this.checked.expressionTypes.get(expr.callee.object);
327
+ const receiverType = receiver?.name ?? (expr.callee.object.kind === 'name' ? this.locals.get(expr.callee.object.name)?.name : undefined);
328
+ const builtin = !def && receiverType && collectionOperations[receiverType]?.find(operation => operation.name === name);
329
+ if (builtin)
330
+ this.builtins.set((receiver ? tyName(receiver) : receiverType) + '.' + builtin.name, {
331
+ ...builtin,
332
+ parameters: builtin.parameters.map(param => ({ ...param, type: receiver ? tyName(operationType(param.type, receiver)) : param.type })),
333
+ returns: this.checked.expressionTypes.get(expr) ? tyName(this.checked.expressionTypes.get(expr)) : builtin.returns,
334
+ });
335
+ // An inherited operation links to the interface that actually declares it.
336
+ const owner = method && [...this.checked.project.definitions.values()].find(def => 'methods' in def.node && def.node.methods.includes(method));
337
+ this.use(owner, method);
338
+ target = (owner ? this.link(owner, `${owner.name}.${method.name}`) : code(expr.callee.name)) + ' on ' + this.expression(expr.callee.object);
339
+ }
340
+ else
341
+ target = this.expression(expr.callee);
342
+ expr.typeArgs.forEach(type => this.type(type));
343
+ const plan = this.checked.callPlans.get(expr);
344
+ const parameters = method?.params ?? (expr.callee.kind === 'name' && this.definition(expr.callee.name)?.node.kind === 'class' ?
345
+ this.definition(expr.callee.name).node.fields : []);
346
+ const inputs = expr.args.map((arg, index) => {
347
+ const slot = plan?.sourceIndices.indexOf(index);
348
+ const label = expr.argLabels[index] ?? (slot !== undefined && slot >= 0 ? parameters[slot]?.label ?? parameters[slot]?.name : arg.kind === 'name' ? arg.name : undefined);
349
+ return (label ? code(label) + ' = ' : '') + (arg.kind === 'binary' ? '(' + this.expression(arg) + ')' : this.expression(arg));
350
+ });
351
+ let text = `call ${target}` + (expr.typeArgs.length ? ' with type arguments ' + expr.typeArgs.map(type => this.type(type)).join(', ') : '') + (inputs.length ? ' with ' + inputs.join('; ') : '');
352
+ const injected = parameters.flatMap((param, index) => param.injected ? [`${code(param.name)} from ${code(plan?.injectionSources?.[index] ?? plan?.bindingKeys[index] ?? typeName(param.type))}`] : []);
353
+ if (injected.length)
354
+ text += '; inject ' + injected.join(', ');
355
+ return text;
356
+ }
357
+ stringParts(expr) {
358
+ if (expr.kind === 'binary' && expr.op === '+' && this.checked.expressionTypes.get(expr)?.name === 'string')
359
+ return [...this.stringParts(expr.left), ...this.stringParts(expr.right)];
360
+ return [expr];
361
+ }
362
+ joinedText(target, value, depth) {
363
+ const parts = this.stringParts(value);
364
+ if (parts.length < 4)
365
+ return;
366
+ const indent = ' '.repeat(depth), child = ' '.repeat(depth + 1);
367
+ return `${indent}- Build ${target} by joining these text parts without separators, in order:\n` +
368
+ parts.map((part, index) => `${child}${index + 1}. ${this.expression(part)}\n`).join('');
369
+ }
370
+ statements(body, depth = 0) {
371
+ if (!body.length)
372
+ return ' '.repeat(depth) + '- Finish this block without another operation.\n';
373
+ return body.map(stmt => this.statement(stmt, depth)).join('');
374
+ }
375
+ statement(stmt, depth) {
376
+ const lead = ' '.repeat(depth) + '- ', nested = (body) => this.statements(body, depth + 1);
377
+ switch (stmt.kind) {
378
+ case 'assign': {
379
+ if (stmt.target.kind === 'name') {
380
+ const type = stmt.declaredType ?? this.checked.expressionTypes.get(stmt.value);
381
+ if (type)
382
+ this.locals.set(stmt.target.name, { name: type.name, args: [], nullable: type.nullable, optional: type.optional, span: stmt.span });
383
+ else if (stmt.value.kind === 'call' && stmt.value.callee.kind === 'name') {
384
+ const def = this.definition(stmt.value.callee.name);
385
+ if (def?.node.kind === 'class')
386
+ this.locals.set(stmt.target.name, { name: def.name, args: [], nullable: false, span: stmt.span });
387
+ }
388
+ }
389
+ const target = this.expression(stmt.target) + (stmt.declaredType ? ' of type ' + this.type(stmt.declaredType) : '');
390
+ return (this.joinedText(target, stmt.value, depth) ?? lead + `Set ${target} to ${this.expression(stmt.value)}.\n`) +
391
+ (stmt.ownership === 'own' ? lead + 'This variable owns the value.\n' : '');
392
+ }
393
+ case 'destructure': return lead + `Split ${this.expression(stmt.value)} into ${stmt.names.map(code).join(', ')}, in that order.\n`;
394
+ case 'expr': return lead + (stmt.expr.kind === 'literal' && stmt.expr.value === null ? 'Continue without another operation' : stmt.expr.kind === 'call' ? 'Call ' + this.call(stmt.expr).slice(5) : 'Evaluate ' + this.expression(stmt.expr)) + '.\n';
395
+ case 'return': {
396
+ if (!stmt.value)
397
+ return lead + 'Finish this operation without a result.\n';
398
+ const joined = this.joinedText('the return value', stmt.value, depth);
399
+ return joined ? joined + lead + 'Return that value.\n' : lead + 'Return ' + this.expression(stmt.value) + '.\n';
400
+ }
401
+ case 'throw': return lead + 'Fail with ' + this.expression(stmt.value) + '. Transfer control to a matching catch or propagate the failure.\n';
402
+ case 'yield': return lead + 'Send ' + this.expression(stmt.value) + ' as the next stream item. Honor backpressure before producing more items.\n';
403
+ case 'if': return lead + 'If ' + this.condition(stmt.test) + ':\n' + nested(stmt.then) + (stmt.otherwise.length ? lead + 'Otherwise:\n' + nested(stmt.otherwise) : '');
404
+ case 'while': return lead + 'While ' + this.condition(stmt.test) + ', repeat:\n' + nested(stmt.body) + ' '.repeat(depth + 1) + '- Check the condition again before the next iteration.\n';
405
+ case 'for': return lead + 'For each ' + stmt.names.map(code).join(' and ') + ' in a snapshot of ' + this.expression(stmt.iterable) + ', in iteration order:\n' + nested(stmt.body);
406
+ case 'match': return lead + 'Select the matching case for ' + this.expression(stmt.value) + ':\n' + stmt.cases.map(clause => {
407
+ const pattern = clause.pattern === 'else' ? 'Any remaining case' : clause.pattern === 'some' ? 'A present, non-null value, named ' + code(clause.name) :
408
+ clause.pattern === 'type' ? 'A value that satisfies ' + this.type(clause.type) + ', named ' + code(clause.name) :
409
+ clause.pattern === 'literal' ? this.expression(clause.literal) : 'A null value, including omitted optional input';
410
+ return ' '.repeat(depth + 1) + '- ' + pattern + ':\n' + this.statements(clause.body, depth + 2);
411
+ }).join('');
412
+ case 'try': return lead + 'Try these operations:\n' + nested(stmt.body) + stmt.catches.map(clause => lead + 'If they fail with ' + this.type(clause.type) + ', name the failure ' + code(clause.name) + ' and recover:\n' + nested(clause.body)).join('') +
413
+ (stmt.always ? lead + 'On both success and failure, perform cleanup:\n' + nested(stmt.always) : '');
414
+ case 'unsafe': return lead + 'Enter an unsafe boundary. Native calls use their declared contracts; their foreign implementation is outside this specification:\n' + nested(stmt.body);
415
+ case 'borrow': return lead + 'Grant exclusive mutable access to ' + code(stmt.name) + ' for this block, then end the borrow:\n' + nested(stmt.body);
416
+ case 'scope': return lead + 'Create a dependency and task scope. Join its child tasks and release scoped values before leaving:\n' + nested(stmt.body);
417
+ case 'lock': return lead + 'Lock ' + this.expression(stmt.value) + ', expose its mutable value as ' + code(stmt.name) + ', and release the lock on every exit:\n' + nested(stmt.body);
418
+ case 'freeze': return lead + 'Freeze ' + this.expression(stmt.value) + ' as ' + code(stmt.name) + '. Share the immutable value without copying it.\n';
419
+ case 'serve': return lead + 'Serve ' + stmt.names.map(name => { const def = this.definition(name); return def ? this.link(def) : code(name); }).join(', ') + ' on port ' + this.expression(stmt.port) + '.\n';
420
+ default: return unreachable(stmt);
421
+ }
422
+ }
423
+ condition(expr) {
424
+ return this.expression(expr) + (['binary', 'unary'].includes(expr.kind) ? '' : ' is true');
425
+ }
426
+ binding(binding) {
427
+ const info = this.checked.bindings.find(info => info.declaration === binding);
428
+ const def = this.definition(binding.target.name);
429
+ this.use(def);
430
+ const lifetime = binding.lifetime ?? info?.lifetime;
431
+ const key = binding.key + (binding.keyTypeArgs.length ? '<' + binding.keyTypeArgs.map(type => typeName(type)).join(', ') + '>' : '');
432
+ return (`Provide ${def ? this.link(def, def.name, typeName(binding.target)) : this.type(binding.target)} when ${code(key)} is requested. ` +
433
+ (lifetime === 'shared' ? 'Reuse one instance. ' : lifetime === 'scoped' ? 'Reuse one instance per explicit scope. ' : lifetime === 'fresh' ? 'Construct a fresh instance for each resolve. ' : 'Use a fresh instance when this provider retains state; otherwise reuse one instance. ') +
434
+ (binding.sharedMutation ? 'Permit explicit shared mutation. ' : '') +
435
+ (info?.dependencies.length ? 'Required dependencies: ' + info.dependencies.map(code).join(', ') + '. ' : '')).trimEnd() + '\n';
436
+ }
437
+ callable(method, owner) {
438
+ this.locals = new Map(owner && 'fields' in owner.node ? fieldsOf(owner.node).map(field => [field.name, field.type]) : []);
439
+ const name = owner && owner.node.kind !== 'function' ? `${owner.name}.${method.name}` : method.name;
440
+ return this.heading(name, method.span, owner && owner.node.kind !== 'function' ? 3 : 2) +
441
+ (method.name.startsWith('_') ? 'Private to its defining scope.\n\n' : '') + this.contract(method) + this.layers(method) +
442
+ (method.body ? '**What it does**\n\n' + this.statements(method.body) + '\n' : method.externC ? 'Native C operation. Its declared inputs, result, effects, and errors are the visible contract. The C implementation is outside this specification.\n\n' : 'Interface contract. A selected implementation supplies the behavior.\n\n') +
443
+ this.notes(method.annotations?.[0]?.span ?? method.span, method, owner);
444
+ }
445
+ declaration(item) {
446
+ switch (item.kind) {
447
+ case 'import': return '';
448
+ case 'export': {
449
+ const folder = this.file.builtin ? libraryChild(this.checked.project.libraries, dirname(this.file.path), item.name) : join(dirname(this.file.path), item.name);
450
+ const file = item.folder ? join(folder, 'export.aug') : join(dirname(this.file.path), item.from + '.aug');
451
+ this.enqueue(file);
452
+ return `- Export ${item.folder ? 'the folder ' : 'the declaration '}${code(item.name)} from [${code(basename(file))}](${url(relative(dirname(this.docs.get(this.file.path)), this.docs.get(file)))}${item.folder ? '' : '#' + encodeURIComponent(anchor(item.name))}).\n`;
453
+ }
454
+ case 'function': return this.callable(item);
455
+ case 'class': {
456
+ const def = this.definition(item.name);
457
+ this.locals = new Map();
458
+ let text = this.heading(item.name, item.span) + `${item.record ? 'Immutable record' : 'Behavioral class'}${item.name.startsWith('_') ? ', private to this file' : ''}.\n\n` + this.generics(item) +
459
+ (item.implements.length ? 'Satisfies ' + item.implements.map(type => { const def = this.definition(type.name); return def ? this.link(def) : this.type(type); }).join(', ') + '.\n\n' : '') +
460
+ this.notes(item.annotations?.[0]?.span ?? item.span) + this.inputs(item.fields, true) + this.layers(item);
461
+ if (item.validationErrors?.length)
462
+ text += 'Construction can fail with ' + item.validationErrors.map(type => this.type(type)).join(', ') + '.\n\n';
463
+ if (item.stateFields?.length)
464
+ text += '**Field initialization**\n\n' + item.stateFields.map(field => { this.locals.set(field.name, field.type); return `- Initialize ${code(field.name)} of type ${this.type(field.type)} to ${this.expression(field.initializer)}. ${field.mutable ? 'Mutable' : 'Read-only'} storage${field.name.startsWith('_') ? ', private to this class' : ''}.\n`; }).join('') + '\n';
465
+ if (item.constructorBody)
466
+ text += this.heading(item.name + '.initialize', item.span, 3) + 'When execution reaches the original constructor, store inputs and initialize local fields, then run this block once before returning the object:\n\n' + this.statements(item.constructorBody) + '\n';
467
+ if (!item.record) {
468
+ const defaults = this.checked.defaults.get(def.id);
469
+ const inherited = [...(defaults?.values() ?? [])].filter(info => !item.methods.some(method => method.name === info.method.name));
470
+ if (inherited.length)
471
+ text += '**Inherited default behavior**\n\n' + inherited.map(info => { const parent = this.checked.project.definitions.get(info.from); this.use(parent, info.method); return '- ' + (parent ? this.link(parent, `${parent.name}.${info.method.name}`) : code(info.method.name)) + '.\n'; }).join('') + '\n';
472
+ }
473
+ return text + item.methods.map(method => this.callable(method, def)).join('');
474
+ }
475
+ case 'interface':
476
+ case 'interceptor': {
477
+ const def = this.definition(item.name);
478
+ this.locals = new Map();
479
+ let text = this.heading(item.name, item.span) + (item.kind === 'interceptor' ? 'Function or constructor middleware' : item.capability ? 'Capability interface' : 'Interface') +
480
+ (item.name.startsWith('_') ? ', private to this file' : '') + '.\n\n' + this.generics(item) + this.notes(item.span);
481
+ if (item.kind === 'interface' && item.extends.length)
482
+ text += 'Inherit contracts and default methods from ' + item.extends.map(type => { const def = this.definition(type.name); return def ? this.link(def) : this.type(type); }).join(', ') + '.\n\n';
483
+ if (item.kind === 'interceptor')
484
+ text += this.inputs(item.fields, true) + 'Create a fresh interceptor for each invocation. Its around operation can delegate once, change selected inputs, or short-circuit with a compatible result or failure.\n\n';
485
+ return text + item.methods.map(method => this.callable(method, def)).join('');
486
+ }
487
+ case 'composition': return this.heading(item.name, item.span) + this.notes(item.span) + 'This composition declares these providers before startup:\n\n' + item.bindings.map(binding => '- ' + this.binding(binding)).join('') + '\n';
488
+ case 'bind': return '- ' + this.binding(item);
489
+ case 'include': {
490
+ const def = this.definition(item.name);
491
+ return '- Include the providers from ' + (def ? this.link(def) : code(item.name)) + ' before execution.\n';
492
+ }
493
+ case 'test': return this.tests(item);
494
+ default: return this.statement(item, 0);
495
+ }
496
+ }
497
+ tests(suite) {
498
+ this.locals = new Map();
499
+ let text = this.heading(this.testTitle(suite), suite.span) + 'Same-file ' + (suite.endpointSuite ? 'HTTP endpoint' : suite.functionSuite ? 'function' : 'class') + ' tests for ' + this.type(suite.type) + '. Each case gets isolated setup and dependency bindings.\n\n';
500
+ for (const group of suite.groups) {
501
+ this.locals = new Map();
502
+ text += '### Group ' + code(group.name) + '\n\n';
503
+ if (group.setup.length)
504
+ text += '**Setup before each case**\n\n' + group.setup.map(entry => this.declaration(entry)).join('') + '\n';
505
+ const setupLocals = new Map(this.locals);
506
+ for (const test of group.cases) {
507
+ this.locals = new Map(setupLocals);
508
+ text += '#### ' + code(test.name) + '\n\n' + this.source(test.span) + '\n\n';
509
+ if (test.parameters)
510
+ text += 'Run once for each row of ' + test.rows.map(row => this.expression(row)).join('; ') + '. Bind row positions to ' + test.parameters.map(code).join(', ') + '.\n\n';
511
+ text += this.statements(test.body) + '\n';
512
+ }
513
+ }
514
+ return text;
515
+ }
516
+ testTitle(suite) {
517
+ return 'test ' + suite.type.name + (suite.name === suite.type.name ? '' : ' ' + suite.name);
518
+ }
519
+ dependencySurface() {
520
+ if (!this.used.size)
521
+ return '';
522
+ const entries = [...this.used.values()].sort((a, b) => compare(relative(this.checked.project.root, this.docs.get(a.def.file)) + ':' + a.def.name, relative(this.checked.project.root, this.docs.get(b.def.file)) + ':' + b.def.name));
523
+ let text = '## Dependencies used by this file\n\nOnly referenced types and operations appear here. Each name links to its complete specification.\n\n';
524
+ for (const entry of entries) {
525
+ const def = entry.def, node = def.node;
526
+ const kind = node.kind === 'class' && node.record ? 'record' : node.kind === 'interface' && node.capability ? 'capability interface' : node.kind;
527
+ text += '### ' + this.link(def) + '\n\n' + kind[0].toUpperCase() + kind.slice(1);
528
+ const imported = this.file.items.find(item => item.kind === 'import' && (this.checked.project.imports.get(item) ?? []).some(imported => imported.id === def.id));
529
+ if (imported?.kind === 'import')
530
+ text += ' from ' + code(imported.from.join('.')) + (imported.everything ? ' through import everything' : '');
531
+ text += '.\n\n';
532
+ if (node.kind === 'function')
533
+ text += this.dependencyOperation(node, def);
534
+ if (node.kind === 'class' && entry.constructed)
535
+ text += '- Construct with ' + this.shortInputs(node.fields, def.file) + ' → ' + this.link(def) +
536
+ (node.validationErrors?.length ? '; can fail with ' + node.validationErrors.map(type => this.type(type, def.file)).join(', ') : '') + '.\n';
537
+ if (node.kind === 'class' && entry.fields.size)
538
+ for (const name of [...entry.fields].sort(compare)) {
539
+ const field = fieldsOf(node).find(field => field.name === name);
540
+ if (field)
541
+ text += '- Read ' + code(name) + ' (' + this.type(field.type, def.file) + ')' + (field.mutable ? '; its owner can change it' : '') + '.\n';
542
+ }
543
+ if ('methods' in node)
544
+ for (const method of [...entry.operations].sort((a, b) => compare(a.name, b.name)))
545
+ text += this.dependencyOperation(method, def);
546
+ if (node.kind !== 'function' && !entry.constructed && !entry.fields.size && !entry.operations.size)
547
+ text += 'Used as a type or provider.\n';
548
+ text += '\n';
549
+ }
550
+ return text;
551
+ }
552
+ shortInputs(params, file) {
553
+ return params.filter(param => !param.injected).map(param => code(param.label ?? param.name) + ': ' + this.type(param.type, file) +
554
+ (param.source ? ' from HTTP ' + param.source.kind + (param.source.name ? ' ' + code(param.source.name) : '') : '')).join(', ') || 'no caller inputs';
555
+ }
556
+ dependencyOperation(method, owner) {
557
+ const effects = this.checked.effectContracts.get(method), layers = this.checked.interceptorPlans.get(method) ?? [];
558
+ const changes = effects?.changes ?? method.changes ?? [];
559
+ const uses = [...(effects?.uses.values() ?? method.uses ?? [])];
560
+ const errors = [...new Set([...method.throws.map(type => typeName(type)), ...layers.flatMap(layer => layer.errors.map(tyName))])];
561
+ const name = owner.node.kind === 'function' ? owner.name : owner.name + '.' + method.name;
562
+ let text = '- ' + this.link(owner, name) + (method.typeParams.length ? '<' + method.typeParams.map(code).join(', ') + '>' : '') +
563
+ ' (' + this.shortInputs(method.params, method.span.file) + ') → ' + this.type(method.returns, method.span.file);
564
+ const injected = method.params.filter(param => param.injected);
565
+ if (injected.length)
566
+ text += '; inject ' + injected.map(param => code(param.name) + ': ' + this.type(param.type, method.span.file)).join(', ');
567
+ if (changes.length)
568
+ text += '; changes ' + changes.map(code).join(', ');
569
+ const otherUses = uses.filter(effect => effect.source !== owner.name || effect.operation !== method.name);
570
+ if (otherUses.length)
571
+ text += '; uses ' + otherUses.map(effect => {
572
+ const target = ('capability' in effect ? effect.capability?.def : undefined) ?? this.checked.project.scopes.get(method.span.file)?.get(effect.source);
573
+ const operation = target && 'methods' in target.node ? target.node.methods.find(method => method.name === effect.operation) : undefined;
574
+ this.use(target, operation);
575
+ return target ? this.link(target, operation ? `${target.name}.${operation.name}` : target.name, `${effect.source}.${effect.operation}`) : code(`${effect.source}.${effect.operation}`);
576
+ }).join(', ');
577
+ if (errors.length)
578
+ text += '; can fail with ' + errors.map(code).join(', ');
579
+ return text + '.\n';
580
+ }
581
+ fileOverview() {
582
+ const lines = [];
583
+ const declaration = (name) => `[${code(name)}](${url(basename(this.docs.get(this.file.path)))}#${encodeURIComponent(anchor(name))})`;
584
+ for (const item of this.file.items) {
585
+ switch (item.kind) {
586
+ case 'class':
587
+ lines.push(`- ${declaration(item.name)} is ${item.record ? 'an immutable record' : 'a class'}${item.implements.length ? ' implementing ' + item.implements.map(type => code(typeName(type))).join(', ') : ''}.`);
588
+ break;
589
+ case 'interface':
590
+ lines.push(`- ${declaration(item.name)} is ${item.capability ? 'a capability interface' : 'an interface'}.`);
591
+ break;
592
+ case 'interceptor':
593
+ lines.push(`- ${declaration(item.name)} is an interceptor.`);
594
+ break;
595
+ case 'function':
596
+ lines.push(`- ${declaration(item.name)} ${item.endpoint ? `handles ${code(item.endpoint.method)} ${code(item.endpoint.path)}` : 'is a function'}${item.returns.name === 'void' ? '' : ` returning ${code(typeName(item.returns))}`}.`);
597
+ break;
598
+ case 'composition':
599
+ lines.push(`- ${declaration(item.name)} declares ${item.bindings.length} ${item.bindings.length === 1 ? 'provider' : 'providers'}.`);
600
+ break;
601
+ case 'bind': break;
602
+ case 'include':
603
+ lines.push(`- Include providers from ${code(item.name)}.`);
604
+ break;
605
+ case 'export':
606
+ lines.push(`- Export ${code(item.name)} from this folder.`);
607
+ break;
608
+ case 'test':
609
+ lines.push(`- ${declaration(this.testTitle(item))} is a same-file test suite.`);
610
+ break;
611
+ default: break;
612
+ }
613
+ }
614
+ const providers = this.file.items.filter(item => item.kind === 'bind');
615
+ if (providers.length)
616
+ lines.push(`- Register ${providers.length} ${providers.length === 1 ? 'dependency provider' : 'dependency providers'} before startup.`);
617
+ const executable = this.file.items.filter(item => !['import', 'export', 'test', 'bind', 'include', 'class', 'interface', 'interceptor', 'function', 'composition'].includes(item.kind));
618
+ if (executable.some(item => item.kind === 'try'))
619
+ lines.push('- Run startup operations with checked error recovery.');
620
+ for (const item of executable)
621
+ if (item.kind === 'serve')
622
+ lines.push(`- Serve ${item.names.length} HTTP ${item.names.length === 1 ? 'route' : 'routes'}.`);
623
+ const otherSteps = executable.filter(item => item.kind !== 'try' && item.kind !== 'serve').length;
624
+ if (otherSteps)
625
+ lines.push(`- Run ${otherSteps} other startup ${otherSteps === 1 ? 'step' : 'steps'} in source order.`);
626
+ return lines.length ? '## In this file\n\n' + lines.join('\n') + '\n\n' : '';
627
+ }
628
+ render() {
629
+ const exports = this.file.items.filter(item => item.kind === 'export');
630
+ const declarations = this.file.items.filter(item => ['class', 'interface', 'interceptor', 'function', 'composition'].includes(item.kind));
631
+ const providers = this.file.items.filter(item => item.kind === 'bind' || item.kind === 'include');
632
+ 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));
633
+ // Render behavior first to discover the exact dependency surface, including wildcard imports.
634
+ const behavior = (exports.length ? '## Folder exports\n\n' + exports.map(item => this.declaration(item)).join('') + '\n' : '') +
635
+ (providers.length ? '## Dependency providers\n\nRegister these providers before startup. Their declaration order does not set initialization order; shared instances initialize in dependency order.\n\n' + providers.map(item => this.declaration(item)).join('') + '\n' : '') +
636
+ (startup.length ? '## Startup, in source order\n\n' + startup.map(item => this.declaration(item)).join('') + '\n' : '') +
637
+ declarations.sort((a, b) => Number('name' in a && a.name.startsWith('_')) - Number('name' in b && b.name.startsWith('_'))).map(item => this.declaration(item)).join('') +
638
+ this.file.items.filter(item => item.kind === 'test').map(item => this.declaration(item)).join('');
639
+ const surface = this.dependencySurface() + this.builtinSurface();
640
+ let config = '';
641
+ if (basename(this.file.path) === 'main.aug') {
642
+ const project = this.checked.project;
643
+ if (project.files.get(this.file.path)?.items.some(item => item.kind === 'serve'))
644
+ config += '## HTTP configuration\n\n' +
645
+ `Listen on ${code(project.config.web.host)}. Limit request bodies to ${project.config.web.body_limit} bytes and buffered responses to ${project.config.web.response_limit} bytes.\n\n` +
646
+ (project.config.web.tls.certificate ? 'Use TLS with the configured certificate and private key.\n\n' : '') +
647
+ (project.config.web.http3 ? 'Enable HTTP/3 over TLS.\n\n' : '') +
648
+ (project.config.openapi.enabled ? `Serve the OpenAPI document at ${code(project.config.openapi.path)} and API documentation at ${code(project.config.openapi.docs)}.\n\n` : '');
649
+ }
650
+ return generated + '\n\n# ' + code(basename(this.file.path)) + ' specification\n\n' +
651
+ `August ${compilerVersion()}. This document is compiled from checked code with deterministic wording guided by Simplified Technical English.\n\n` +
652
+ this.fileOverview() + config + behavior + surface +
653
+ (behavior ? '' : 'This file declares no operations.\n') +
654
+ '## Shared language rules\n\nSee the [language reference](https://greenpandastudios.github.io/augscript/reference) for numeric, equality, ownership, and task rules.\n';
655
+ }
656
+ builtinSurface() {
657
+ if (!this.builtins.size && !this.properties.size)
658
+ return '';
659
+ return '## Built-in operations used by this file\n\n' + [...this.properties].sort(([a], [b]) => compare(a, b)).map(([name, property]) => `- ${code(name)} (${code(property.type)}): ${property.documentation}\n`).join('') +
660
+ [...this.builtins].sort(([a], [b]) => compare(a, b)).map(([name, operation]) => `- ${code(name)} (${operation.parameters.map(param => code(param.label) + ': ' + code(param.type)).join(', ') || 'no inputs'}) → ${code(operation.returns)}: ${operation.documentation}` +
661
+ (operation.changes ? ' Changes the receiver.' : '') +
662
+ (operation.errors?.length ? ' Can fail with ' + operation.errors.map(code).join(', ') + '.' : '') + '\n').join('') +
663
+ '\n[Full built-in reference](https://greenpandastudios.github.io/augscript/language-constructs).\n\n';
664
+ }
665
+ }
666
+ /** Check generated artifacts without writing; protect neighboring handwritten documents. */
667
+ export function updateSpecs(checked, check = false, options = {}) {
668
+ const outputs = generateSpecs(checked, options), root = checked.project.root;
669
+ const manifest = join(root, '.aug-spec', 'manifest.json');
670
+ let previous = [];
671
+ if (options.manifest !== false && existsSync(manifest)) {
672
+ const saved = JSON.parse(readFileSync(manifest, 'utf8'));
673
+ 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')))
674
+ throw new Error('Invalid generated specification manifest');
675
+ previous = saved.files.map(path => resolve(root, path));
676
+ }
677
+ const names = new Set(outputs.map(output => output.path));
678
+ const removed = previous.filter(path => !names.has(path) && existsSync(path));
679
+ const stale = outputs.filter(output => !existsSync(output.path) || readFileSync(output.path, 'utf8') !== output.text).map(output => relative(root, output.path));
680
+ stale.push(...removed.map(path => relative(root, path)));
681
+ const manifestText = JSON.stringify({ format: 1, files: outputs.map(output => relative(root, output.path).replaceAll('\\', '/')), digest: hash(outputs.map(output => output.text).join('\0')) }, null, 2) + '\n';
682
+ if (options.manifest !== false && (!existsSync(manifest) || readFileSync(manifest, 'utf8') !== manifestText))
683
+ stale.push('.aug-spec/manifest.json');
684
+ if (check)
685
+ return { files: outputs.length, stale };
686
+ if (options.manifest !== false && (existsSync(dirname(manifest)) && lstatSync(dirname(manifest)).isSymbolicLink() || existsSync(manifest) && lstatSync(manifest).isSymbolicLink()))
687
+ throw new Error('Specification manifest must not be a symbolic link');
688
+ for (const output of outputs) {
689
+ let parent = dirname(output.path);
690
+ // Trust the project root, including platform aliases such as macOS /var.
691
+ while (parent !== root && !relative(root, parent).startsWith('..') && parent !== dirname(parent)) {
692
+ if (existsSync(parent) && lstatSync(parent).isSymbolicLink())
693
+ throw new Error(`Specification output directory is a symbolic link: ${parent}`);
694
+ parent = dirname(parent);
695
+ }
696
+ if (existsSync(output.path)) {
697
+ if (lstatSync(output.path).isSymbolicLink())
698
+ throw new Error(`Specification output is a symbolic link: ${output.path}`);
699
+ const text = readFileSync(output.path, 'utf8');
700
+ if (!text.startsWith(generated) && !text.startsWith(copied))
701
+ throw new Error(`Refusing to overwrite handwritten file ${output.path}`);
702
+ }
703
+ }
704
+ for (const output of outputs) {
705
+ if (existsSync(output.path) && readFileSync(output.path, 'utf8') === output.text)
706
+ continue;
707
+ mkdirSync(dirname(output.path), { recursive: true });
708
+ const temporary = output.path + '.aug-spec-tmp';
709
+ try {
710
+ writeFileSync(temporary, output.text, { flag: 'wx' });
711
+ renameSync(temporary, output.path);
712
+ }
713
+ finally {
714
+ if (existsSync(temporary))
715
+ rmSync(temporary);
716
+ }
717
+ }
718
+ for (const path of removed) {
719
+ const text = readFileSync(path, 'utf8');
720
+ if (text.startsWith(generated) || text.startsWith(copied))
721
+ rmSync(path);
722
+ }
723
+ if (options.manifest !== false) {
724
+ mkdirSync(dirname(manifest), { recursive: true });
725
+ if (!existsSync(manifest) || readFileSync(manifest, 'utf8') !== manifestText) {
726
+ const temporary = manifest + '.aug-spec-tmp';
727
+ try {
728
+ writeFileSync(temporary, manifestText, { flag: 'wx' });
729
+ renameSync(temporary, manifest);
730
+ }
731
+ finally {
732
+ if (existsSync(temporary))
733
+ rmSync(temporary);
734
+ }
735
+ }
736
+ }
737
+ return { files: outputs.length, stale: [] };
738
+ }