@greenpandastudios/aug-cli 0.19.0 → 0.20.1
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.
- package/README.md +9 -6
- package/THIRD_PARTY_NOTICES.md +4 -0
- package/bin/aug.mjs +12 -2
- package/docs/about.md +27 -0
- package/docs/api/crypto.md +103 -69
- package/docs/api/io.md +35 -33
- package/docs/api/json.md +3 -3
- package/docs/api/memory.md +23 -19
- package/docs/api/time.md +10 -9
- package/docs/api/web.md +33 -38
- package/docs/compatibility.md +3 -3
- package/docs/contributing-benchmarks.md +38 -0
- package/docs/dev-containers.md +95 -0
- package/docs/diagnostics.md +15 -5
- package/docs/docker.md +174 -12
- package/docs/editor.md +35 -0
- package/docs/example-projects.json +206 -24
- package/docs/examples/approved-design/counters.md +34 -126
- package/docs/examples/approved-design/dependencies/august/0.20.1/io/contracts.md +178 -0
- package/docs/examples/approved-design/domain/app.md +13 -77
- package/docs/examples/approved-design/domain/export.md +5 -19
- package/docs/examples/approved-design/domain/models.md +4 -23
- package/docs/examples/approved-design/domain/numbers.md +24 -114
- package/docs/examples/approved-design/index.md +16 -7
- package/docs/examples/approved-design/main.md +19 -96
- package/docs/examples/benchmark/index.md +6 -5
- package/docs/examples/benchmark/main.md +7 -35
- package/docs/examples/cli-args/index.md +6 -5
- package/docs/examples/cli-args/main.md +6 -31
- package/docs/examples/collections/index.md +6 -5
- package/docs/examples/collections/main.md +7 -35
- package/docs/examples/collections-benchmark/index.md +6 -5
- package/docs/examples/collections-benchmark/main.md +7 -35
- package/docs/examples/cpu-benchmark/index.md +6 -5
- package/docs/examples/cpu-benchmark/main.md +5 -24
- package/docs/examples/developer-workflow/calculator.md +39 -186
- package/docs/examples/developer-workflow/dependencies/august/0.20.1/io/contracts.md +178 -0
- package/docs/examples/developer-workflow/index.md +16 -7
- package/docs/examples/developer-workflow/logging/console.md +10 -56
- package/docs/examples/developer-workflow/logging/export.md +4 -14
- package/docs/examples/developer-workflow/logging/logger.md +7 -43
- package/docs/examples/developer-workflow/main.md +17 -69
- package/docs/examples/drop/index.md +6 -5
- package/docs/examples/drop/main.md +7 -27
- package/docs/examples/drop/resource.md +7 -30
- package/docs/examples/errors/errors.md +7 -27
- package/docs/examples/errors/index.md +6 -5
- package/docs/examples/errors/main.md +7 -28
- package/docs/examples/ffi/index.md +6 -5
- package/docs/examples/ffi/main.md +6 -20
- package/docs/examples/ffi/native.md +8 -34
- package/docs/examples/generic-di/dependencies/august/0.20.1/io/contracts.md +178 -0
- package/docs/examples/generic-di/index.md +6 -5
- package/docs/examples/generic-di/main.md +9 -39
- package/docs/examples/generic-di/types.md +23 -92
- package/docs/examples/generics/index.md +6 -5
- package/docs/examples/generics/main.md +10 -47
- package/docs/examples/generics/types.md +25 -100
- package/docs/examples/hello/app/export.md +4 -12
- package/docs/examples/hello/app/greeter.md +13 -91
- package/docs/examples/hello/dependencies/august/0.20.1/io/contracts.md +178 -0
- package/docs/examples/hello/index.md +17 -6
- package/docs/examples/hello/logging/console.md +10 -54
- package/docs/examples/hello/logging/export.md +4 -14
- package/docs/examples/hello/logging/logger.md +7 -45
- package/docs/examples/hello/main.md +9 -39
- package/docs/examples/http-benchmark/index.md +6 -5
- package/docs/examples/http-benchmark/main.md +6 -20
- package/docs/examples/http-benchmark/routes.md +9 -31
- package/docs/examples/index.md +46 -29
- package/docs/examples/interceptors/app.md +20 -139
- package/docs/examples/interceptors/dependencies/august/0.20.1/io/contracts.md +178 -0
- package/docs/examples/interceptors/index.md +6 -5
- package/docs/examples/interceptors/interceptors.md +24 -148
- package/docs/examples/interceptors/logging.md +13 -76
- package/docs/examples/interceptors/main.md +10 -65
- package/docs/examples/json-benchmark/data.md +4 -20
- package/docs/examples/json-benchmark/dependencies/packages/@git/url_2d3c37c690c0fa115be1/0.0.0-git.a39fc582565d4fca40be4f75fa304d71adc69301/contracts.md +67 -0
- package/docs/examples/json-benchmark/index.md +7 -5
- package/docs/examples/json-benchmark/main-yaml.md +20 -0
- package/docs/examples/json-benchmark/main.md +10 -49
- package/docs/examples/new-syntax/console.md +10 -52
- package/docs/examples/new-syntax/dependencies/august/0.20.1/io/contracts.md +178 -0
- package/docs/examples/new-syntax/greeter.md +13 -71
- package/docs/examples/new-syntax/index.md +6 -5
- package/docs/examples/new-syntax/logger.md +7 -43
- package/docs/examples/new-syntax/main.md +10 -53
- package/docs/examples/new-syntax/math.md +6 -24
- package/docs/examples/oidc-login/client/contracts.md +10 -61
- package/docs/examples/oidc-login/client/endpoints.md +32 -154
- package/docs/examples/oidc-login/client/export.md +5 -23
- package/docs/examples/oidc-login/client/login.md +234 -344
- package/docs/examples/oidc-login/client/logout.md +38 -143
- package/docs/examples/oidc-login/client/protocol.md +183 -304
- package/docs/examples/oidc-login/client/session.md +41 -149
- package/docs/examples/oidc-login/client/views.md +13 -58
- package/docs/examples/oidc-login/common/export.md +6 -26
- package/docs/examples/oidc-login/common/headers.md +42 -72
- package/docs/examples/oidc-login/common/keys.md +40 -164
- package/docs/examples/oidc-login/common/settings.md +24 -40
- package/docs/examples/oidc-login/common/views.md +7 -28
- package/docs/examples/oidc-login/dependencies/packages/@git/url_0eb7c89453c87681ed15/0.0.0-git.a39fc582565d4fca40be4f75fa304d71adc69301/store.md +205 -0
- package/docs/examples/oidc-login/dependencies/packages/@git/url_2d3c37c690c0fa115be1/0.0.0-git.a39fc582565d4fca40be4f75fa304d71adc69301/contracts.md +67 -0
- package/docs/examples/oidc-login/dependencies/packages/@git/url_897efafd565158fc4908/0.0.0-git.a39fc582565d4fca40be4f75fa304d71adc69301/contracts.md +267 -0
- package/docs/examples/oidc-login/dependencies/packages/@git/url_9ef654c66d34ab8f5527/0.0.0-git.b14a0f9aa41f1ce58bd51133bcdc424033e40d40/contracts.md +415 -0
- package/docs/examples/oidc-login/dependencies/packages/@git/url_9ef654c66d34ab8f5527/0.0.0-git.b14a0f9aa41f1ce58bd51133bcdc424033e40d40/jose.md +271 -0
- package/docs/examples/oidc-login/dependencies/packages/@git/url_c092cd151499c4e1d8a1/0.0.0-git.a39fc582565d4fca40be4f75fa304d71adc69301/contracts.md +89 -0
- package/docs/examples/oidc-login/index.md +8 -7
- package/docs/examples/oidc-login/main-yaml.md +7 -0
- package/docs/examples/oidc-login/main.md +26 -153
- package/docs/examples/oidc-login/provider/authorization.md +232 -275
- package/docs/examples/oidc-login/provider/contracts.md +24 -146
- package/docs/examples/oidc-login/provider/credentials.md +24 -57
- package/docs/examples/oidc-login/provider/discovery.md +48 -122
- package/docs/examples/oidc-login/provider/export.md +11 -37
- package/docs/examples/oidc-login/provider/token.md +159 -237
- package/docs/examples/oidc-login/provider/userinfo.md +47 -102
- package/docs/examples/oidc-login/provider/views.md +13 -53
- package/docs/examples/ownership/counter.md +17 -63
- package/docs/examples/ownership/index.md +6 -5
- package/docs/examples/ownership/main.md +7 -30
- package/docs/examples/ownership-transfer/dependencies/august/0.20.1/io/contracts.md +178 -0
- package/docs/examples/ownership-transfer/index.md +6 -5
- package/docs/examples/ownership-transfer/main.md +10 -52
- package/docs/examples/ownership-transfer/resource.md +15 -67
- package/docs/examples/packages-app/dependencies/packages/@example/aug-math/0.1.0/arithmetic.md +12 -50
- package/docs/examples/packages-app/index.md +7 -6
- package/docs/examples/packages-app/main.md +7 -25
- package/docs/examples/packages-math/aug-package-json.md +1 -1
- package/docs/examples/packages-math/index.md +7 -6
- package/docs/examples/packages-math/src/arithmetic.md +12 -50
- package/docs/examples/packages-math/src/export.md +4 -12
- package/docs/examples/startup-benchmark/index.md +6 -5
- package/docs/examples/startup-benchmark/main.md +5 -17
- package/docs/examples/visibility/counter.md +19 -67
- package/docs/examples/visibility/index.md +6 -5
- package/docs/examples/visibility/main.md +7 -31
- package/docs/examples/weather-api/forecasts.md +168 -0
- package/docs/examples/weather-api/index.md +42 -0
- package/docs/examples/weather-api/main-yaml.md +21 -0
- package/docs/examples/weather-api/main.md +65 -0
- package/docs/examples.json +10 -0
- package/docs/getting-started.md +105 -2
- package/docs/grammar.md +6 -4
- package/docs/guides/change-a-module.md +64 -0
- package/docs/guides/index.md +27 -0
- package/docs/index.md +51 -29
- package/docs/language-conformance.md +1 -1
- package/docs/language-constructs.md +22 -22
- package/docs/language-design-audit.md +1 -1
- package/docs/learn/data-and-errors.md +77 -0
- package/docs/learn/index.md +30 -0
- package/docs/learn/modules-and-dependencies.md +73 -0
- package/docs/learn/state-and-tests.md +71 -0
- package/docs/learn/values-and-functions.md +62 -0
- package/docs/lesson-failures.json +8 -0
- package/docs/maintaining-docs.md +16 -4
- package/docs/packages.md +72 -126
- package/docs/performance.md +13 -41
- package/docs/production-readiness.md +5 -4
- package/docs/public/downloads/approved-design.zip +0 -0
- package/docs/public/downloads/benchmark.zip +0 -0
- package/docs/public/downloads/cli-args.zip +0 -0
- package/docs/public/downloads/collections-benchmark.zip +0 -0
- package/docs/public/downloads/collections.zip +0 -0
- package/docs/public/downloads/cpu-benchmark.zip +0 -0
- package/docs/public/downloads/developer-workflow.zip +0 -0
- package/docs/public/downloads/drop.zip +0 -0
- package/docs/public/downloads/errors.zip +0 -0
- package/docs/public/downloads/ffi.zip +0 -0
- package/docs/public/downloads/generic-di.zip +0 -0
- package/docs/public/downloads/generics.zip +0 -0
- package/docs/public/downloads/hello.zip +0 -0
- package/docs/public/downloads/http-benchmark.zip +0 -0
- package/docs/public/downloads/interceptors.zip +0 -0
- package/docs/public/downloads/json-benchmark.zip +0 -0
- package/docs/public/downloads/new-syntax.zip +0 -0
- package/docs/public/downloads/oidc-login.zip +0 -0
- package/docs/public/downloads/ownership-transfer.zip +0 -0
- package/docs/public/downloads/ownership.zip +0 -0
- package/docs/public/downloads/packages-app.zip +0 -0
- package/docs/public/downloads/packages-math.zip +0 -0
- package/docs/public/downloads/startup-benchmark.zip +0 -0
- package/docs/public/downloads/visibility.zip +0 -0
- package/docs/public/downloads/weather-api.zip +0 -0
- package/docs/reference.md +23 -17
- package/docs/releasing.md +29 -15
- package/docs/research/code-to-natural-language.md +108 -0
- package/docs/research/ecosystem-workflow.md +41 -0
- package/docs/research/wiki-editorial-design.md +71 -0
- package/docs/roadmap.md +1 -1
- package/docs/specifications.md +27 -11
- package/docs/testing.md +19 -5
- package/docs/tooling.md +25 -12
- package/docs/weather-api.md +65 -0
- package/docs/web.md +50 -18
- package/docs/writing-docs.md +49 -0
- package/examples/{hello/.aug-spec/august/0.19.0 → approved-design/.aug-spec/august/0.20.1}/io/contracts.aug +4 -3
- package/examples/approved-design/.aug-spec/august/0.20.1/io/contracts.aug.md +82 -0
- package/examples/approved-design/.aug-spec/manifest.json +3 -3
- package/examples/approved-design/counters.aug +5 -4
- package/examples/approved-design/counters.aug.md +25 -132
- package/examples/approved-design/domain/app.aug +2 -1
- package/examples/approved-design/domain/app.aug.md +10 -80
- package/examples/approved-design/domain/export.aug +1 -0
- package/examples/approved-design/domain/export.aug.md +4 -20
- package/examples/approved-design/domain/models.aug +1 -0
- package/examples/approved-design/domain/models.aug.md +3 -25
- package/examples/approved-design/domain/numbers.aug +3 -2
- package/examples/approved-design/domain/numbers.aug.md +19 -116
- package/examples/approved-design/main.aug +1 -0
- package/examples/approved-design/main.aug.md +12 -95
- package/examples/benchmark/.aug-spec/manifest.json +1 -1
- package/examples/benchmark/main.aug +1 -0
- package/examples/benchmark/main.aug.md +6 -36
- package/examples/cli-args/.aug-spec/manifest.json +1 -1
- package/examples/cli-args/main.aug +1 -0
- package/examples/cli-args/main.aug.md +5 -32
- package/examples/collections/.aug-spec/manifest.json +1 -1
- package/examples/collections/main.aug +1 -0
- package/examples/collections/main.aug.md +6 -36
- package/examples/{generic-di/.aug-spec/august/0.19.0 → developer-workflow/.aug-spec/august/0.20.1}/io/contracts.aug +4 -3
- package/examples/developer-workflow/.aug-spec/august/0.20.1/io/contracts.aug.md +82 -0
- package/examples/developer-workflow/.aug-spec/manifest.json +3 -3
- package/examples/developer-workflow/calculator.aug +3 -2
- package/examples/developer-workflow/calculator.aug.md +28 -189
- package/examples/developer-workflow/logging/console.aug +2 -1
- package/examples/developer-workflow/logging/console.aug.md +7 -57
- package/examples/developer-workflow/logging/export.aug +1 -0
- package/examples/developer-workflow/logging/export.aug.md +3 -15
- package/examples/developer-workflow/logging/logger.aug +1 -0
- package/examples/developer-workflow/logging/logger.aug.md +6 -46
- package/examples/developer-workflow/main.aug +1 -0
- package/examples/developer-workflow/main.aug.md +10 -68
- package/examples/drop/.aug-spec/manifest.json +1 -1
- package/examples/drop/main.aug +1 -0
- package/examples/drop/main.aug.md +6 -28
- package/examples/drop/resource.aug +1 -0
- package/examples/drop/resource.aug.md +6 -34
- package/examples/errors/.aug-spec/manifest.json +1 -1
- package/examples/errors/errors.aug +2 -1
- package/examples/errors/errors.aug.md +4 -27
- package/examples/errors/main.aug +1 -0
- package/examples/errors/main.aug.md +6 -29
- package/examples/ffi/.aug-spec/manifest.json +1 -1
- package/examples/ffi/main.aug +1 -0
- package/examples/ffi/main.aug.md +5 -21
- package/examples/ffi/native.aug +2 -1
- package/examples/ffi/native.aug.md +5 -35
- package/examples/{approved-design/.aug-spec/august/0.19.0 → generic-di/.aug-spec/august/0.20.1}/io/contracts.aug +4 -3
- package/examples/generic-di/.aug-spec/august/0.20.1/io/contracts.aug.md +82 -0
- package/examples/generic-di/.aug-spec/manifest.json +3 -3
- package/examples/generic-di/main.aug +1 -0
- package/examples/generic-di/main.aug.md +8 -40
- package/examples/generic-di/types.aug +3 -2
- package/examples/generic-di/types.aug.md +18 -97
- package/examples/generics/.aug-spec/manifest.json +1 -1
- package/examples/generics/main.aug +1 -0
- package/examples/generics/main.aug.md +9 -48
- package/examples/generics/types.aug +4 -3
- package/examples/generics/types.aug.md +18 -104
- package/examples/{developer-workflow/.aug-spec/august/0.19.0 → hello/.aug-spec/august/0.20.1}/io/contracts.aug +4 -3
- package/examples/hello/.aug-spec/august/0.20.1/io/contracts.aug.md +82 -0
- package/examples/hello/.aug-spec/manifest.json +3 -3
- package/examples/hello/app/export.aug +1 -0
- package/examples/hello/app/export.aug.md +3 -13
- package/examples/hello/app/greeter.aug +2 -1
- package/examples/hello/app/greeter.aug.md +10 -94
- package/examples/hello/logging/console.aug +2 -1
- package/examples/hello/logging/console.aug.md +7 -55
- package/examples/hello/logging/export.aug +1 -0
- package/examples/hello/logging/export.aug.md +3 -15
- package/examples/hello/logging/logger.aug +1 -0
- package/examples/hello/logging/logger.aug.md +6 -48
- package/examples/hello/main.aug +1 -0
- package/examples/hello/main.aug.md +8 -40
- package/examples/interceptors/.aug-spec/august/0.20.1/io/contracts.aug +37 -0
- package/examples/interceptors/.aug-spec/august/0.20.1/io/contracts.aug.md +82 -0
- package/examples/interceptors/.aug-spec/manifest.json +3 -3
- package/examples/interceptors/app.aug +3 -2
- package/examples/interceptors/app.aug.md +15 -141
- package/examples/interceptors/interceptors.aug +3 -2
- package/examples/interceptors/interceptors.aug.md +19 -152
- package/examples/interceptors/logging.aug +2 -1
- package/examples/interceptors/logging.aug.md +10 -79
- package/examples/interceptors/main.aug +1 -0
- package/examples/interceptors/main.aug.md +9 -66
- package/examples/new-syntax/.aug-spec/august/0.20.1/io/contracts.aug +37 -0
- package/examples/new-syntax/.aug-spec/august/0.20.1/io/contracts.aug.md +82 -0
- package/examples/new-syntax/.aug-spec/manifest.json +3 -3
- package/examples/new-syntax/console.aug +2 -1
- package/examples/new-syntax/console.aug.md +7 -53
- package/examples/new-syntax/greeter.aug +2 -1
- package/examples/new-syntax/greeter.aug.md +10 -74
- package/examples/new-syntax/logger.aug +1 -0
- package/examples/new-syntax/logger.aug.md +6 -46
- package/examples/new-syntax/main.aug +1 -0
- package/examples/new-syntax/main.aug.md +9 -54
- package/examples/new-syntax/math.aug +2 -1
- package/examples/new-syntax/math.aug.md +3 -24
- package/examples/oidc-login/.aug-spec/manifest.json +13 -13
- package/examples/oidc-login/.aug-spec/packages/@git/url_0eb7c89453c87681ed15/0.0.0-git.a39fc582565d4fca40be4f75fa304d71adc69301/store.aug.md +71 -0
- package/examples/oidc-login/.aug-spec/packages/@git/url_2d3c37c690c0fa115be1/0.0.0-git.a39fc582565d4fca40be4f75fa304d71adc69301/contracts.aug.md +17 -0
- package/examples/oidc-login/.aug-spec/packages/@git/url_897efafd565158fc4908/0.0.0-git.a39fc582565d4fca40be4f75fa304d71adc69301/contracts.aug.md +125 -0
- package/examples/oidc-login/.aug-spec/{august/0.19.0/crypto → packages/@git/url_9ef654c66d34ab8f5527/0.0.0-git.b14a0f9aa41f1ce58bd51133bcdc424033e40d40}/contracts.aug +12 -11
- package/examples/oidc-login/.aug-spec/packages/@git/url_9ef654c66d34ab8f5527/0.0.0-git.b14a0f9aa41f1ce58bd51133bcdc424033e40d40/contracts.aug.md +244 -0
- package/examples/oidc-login/.aug-spec/{august/0.19.0/crypto → packages/@git/url_9ef654c66d34ab8f5527/0.0.0-git.b14a0f9aa41f1ce58bd51133bcdc424033e40d40}/jose.aug +6 -5
- package/examples/oidc-login/.aug-spec/packages/@git/url_9ef654c66d34ab8f5527/0.0.0-git.b14a0f9aa41f1ce58bd51133bcdc424033e40d40/jose.aug.md +71 -0
- package/examples/oidc-login/.aug-spec/packages/@git/url_c092cd151499c4e1d8a1/0.0.0-git.a39fc582565d4fca40be4f75fa304d71adc69301/contracts.aug.md +30 -0
- package/examples/oidc-login/aug.lock.json +100 -0
- package/examples/oidc-login/client/contracts.aug +1 -0
- package/examples/oidc-login/client/contracts.aug.md +9 -66
- package/examples/oidc-login/client/endpoints.aug +6 -5
- package/examples/oidc-login/client/endpoints.aug.md +13 -145
- package/examples/oidc-login/client/export.aug +1 -0
- package/examples/oidc-login/client/export.aug.md +4 -24
- package/examples/oidc-login/client/login.aug +7 -6
- package/examples/oidc-login/client/login.aug.md +25 -303
- package/examples/oidc-login/client/logout.aug +5 -4
- package/examples/oidc-login/client/logout.aug.md +13 -135
- package/examples/oidc-login/client/protocol.aug +7 -6
- package/examples/oidc-login/client/protocol.aug.md +36 -283
- package/examples/oidc-login/client/session.aug +5 -4
- package/examples/oidc-login/client/session.aug.md +12 -139
- package/examples/oidc-login/client/views.aug +3 -2
- package/examples/oidc-login/client/views.aug.md +8 -57
- package/examples/oidc-login/common/export.aug +1 -0
- package/examples/oidc-login/common/export.aug.md +5 -27
- package/examples/oidc-login/common/headers.aug +4 -3
- package/examples/oidc-login/common/headers.aug.md +9 -67
- package/examples/oidc-login/common/keys.aug +6 -5
- package/examples/oidc-login/common/keys.aug.md +29 -165
- package/examples/oidc-login/common/settings.aug +2 -1
- package/examples/oidc-login/common/settings.aug.md +5 -39
- package/examples/oidc-login/common/views.aug +2 -1
- package/examples/oidc-login/common/views.aug.md +4 -28
- package/examples/oidc-login/main.aug +6 -16
- package/examples/oidc-login/main.aug.md +17 -146
- package/examples/oidc-login/main.yaml +7 -0
- package/examples/oidc-login/provider/authorization.aug +7 -6
- package/examples/oidc-login/provider/authorization.aug.md +23 -238
- package/examples/oidc-login/provider/contracts.aug +1 -0
- package/examples/oidc-login/provider/contracts.aug.md +23 -158
- package/examples/oidc-login/provider/credentials.aug +3 -2
- package/examples/oidc-login/provider/credentials.aug.md +9 -53
- package/examples/oidc-login/provider/discovery.aug +4 -3
- package/examples/oidc-login/provider/discovery.aug.md +11 -118
- package/examples/oidc-login/provider/export.aug +1 -0
- package/examples/oidc-login/provider/export.aug.md +6 -34
- package/examples/oidc-login/provider/token.aug +6 -5
- package/examples/oidc-login/provider/token.aug.md +16 -204
- package/examples/oidc-login/provider/userinfo.aug +4 -3
- package/examples/oidc-login/provider/userinfo.aug.md +10 -92
- package/examples/oidc-login/provider/views.aug +3 -2
- package/examples/oidc-login/provider/views.aug.md +8 -52
- package/examples/ownership/.aug-spec/manifest.json +1 -1
- package/examples/ownership/counter.aug +3 -2
- package/examples/ownership/counter.aug.md +12 -66
- package/examples/ownership/main.aug +1 -0
- package/examples/ownership/main.aug.md +6 -31
- package/examples/ownership-transfer/.aug-spec/august/0.20.1/io/contracts.aug +37 -0
- package/examples/ownership-transfer/.aug-spec/august/0.20.1/io/contracts.aug.md +82 -0
- package/examples/ownership-transfer/.aug-spec/manifest.json +3 -3
- package/examples/ownership-transfer/main.aug +1 -0
- package/examples/ownership-transfer/main.aug.md +9 -53
- package/examples/ownership-transfer/resource.aug +2 -1
- package/examples/ownership-transfer/resource.aug.md +12 -71
- package/examples/packages/app/.aug-spec/manifest.json +1 -1
- package/examples/packages/app/.aug-spec/packages/@example/aug-math/0.1.0/arithmetic.aug +2 -1
- package/examples/packages/app/.aug-spec/packages/@example/aug-math/0.1.0/arithmetic.aug.md +9 -51
- package/examples/packages/app/main.aug +1 -0
- package/examples/packages/app/main.aug.md +6 -26
- package/examples/packages/math/.aug-spec/manifest.json +1 -1
- package/examples/packages/math/aug-package.json +1 -1
- package/examples/packages/math/src/arithmetic.aug +2 -1
- package/examples/packages/math/src/arithmetic.aug.md +9 -51
- package/examples/packages/math/src/export.aug +1 -0
- package/examples/packages/math/src/export.aug.md +3 -13
- package/examples/visibility/.aug-spec/manifest.json +1 -1
- package/examples/visibility/counter.aug +4 -3
- package/examples/visibility/counter.aug.md +12 -68
- package/examples/visibility/main.aug +1 -0
- package/examples/visibility/main.aug.md +6 -32
- package/examples/weather-api/.aug-spec/manifest.json +8 -0
- package/examples/weather-api/AGENTS.md +17 -0
- package/examples/weather-api/README.md +19 -0
- package/examples/weather-api/forecasts.aug +47 -0
- package/examples/weather-api/forecasts.aug.md +32 -0
- package/examples/weather-api/main.aug +4 -0
- package/examples/weather-api/main.aug.md +15 -0
- package/examples/weather-api/main.yaml +5 -0
- package/examples/weather-api/weather.http +5 -0
- package/package.json +3 -4
- package/scripts/bootstrap-native.mjs +181 -106
- package/scripts/native-setup.mjs +98 -0
- package/scripts/native-toolchain.mjs +28 -0
- package/src/builtins.js +1 -1
- package/src/checker.js +259 -72
- package/src/cli.js +114 -20
- package/src/codegen.js +1 -1
- package/src/config.js +1 -1
- package/src/contracts.js +10 -0
- package/src/editor.js +71 -16
- package/src/fixes.js +90 -2
- package/src/formatter.js +19 -8
- package/src/git-packages.js +110 -0
- package/src/help.js +24 -23
- package/src/http-contracts.js +14 -0
- package/src/http-policies.js +5 -5
- package/src/libraries.js +1 -1
- package/src/lsp.js +14 -5
- package/src/native.js +19 -13
- package/src/navigation.js +4 -2
- package/src/openapi.js +5 -4
- package/src/package-locking.js +45 -0
- package/src/package-manager.js +251 -67
- package/src/parser.js +13 -5
- package/src/policies.js +3 -3
- package/src/project-init.js +78 -4
- package/src/project.js +9 -7
- package/src/semantic.js +46 -2
- package/src/snippets.js +59 -0
- package/src/spec-hints.js +56 -0
- package/src/spec-tree.js +241 -0
- package/src/spec.js +626 -303
- package/docs/examples/approved-design/dependencies/august/0.19.0/io/contracts.md +0 -395
- package/docs/examples/developer-workflow/dependencies/august/0.19.0/io/contracts.md +0 -395
- package/docs/examples/generic-di/dependencies/august/0.19.0/io/contracts.md +0 -395
- package/docs/examples/hello/dependencies/august/0.19.0/io/contracts.md +0 -395
- package/docs/examples/interceptors/dependencies/august/0.19.0/io/contracts.md +0 -395
- package/docs/examples/json-benchmark/dependencies/august/0.19.0/json/contracts.md +0 -103
- package/docs/examples/new-syntax/dependencies/august/0.19.0/io/contracts.md +0 -395
- package/docs/examples/oidc-login/dependencies/august/0.19.0/crypto/contracts.md +0 -925
- package/docs/examples/oidc-login/dependencies/august/0.19.0/crypto/jose.md +0 -434
- package/docs/examples/oidc-login/dependencies/august/0.19.0/json/contracts.md +0 -103
- package/docs/examples/oidc-login/dependencies/august/0.19.0/memory/store.md +0 -374
- package/docs/examples/oidc-login/dependencies/august/0.19.0/time/contracts.md +0 -150
- package/docs/examples/oidc-login/dependencies/august/0.19.0/web/contracts.md +0 -532
- package/docs/examples/ownership-transfer/dependencies/august/0.19.0/io/contracts.md +0 -395
- package/examples/approved-design/.aug-spec/august/0.19.0/io/contracts.aug.md +0 -316
- package/examples/developer-workflow/.aug-spec/august/0.19.0/io/contracts.aug.md +0 -316
- package/examples/generic-di/.aug-spec/august/0.19.0/io/contracts.aug.md +0 -316
- package/examples/hello/.aug-spec/august/0.19.0/io/contracts.aug.md +0 -316
- package/examples/interceptors/.aug-spec/august/0.19.0/io/contracts.aug +0 -36
- package/examples/interceptors/.aug-spec/august/0.19.0/io/contracts.aug.md +0 -316
- package/examples/new-syntax/.aug-spec/august/0.19.0/io/contracts.aug +0 -36
- package/examples/new-syntax/.aug-spec/august/0.19.0/io/contracts.aug.md +0 -316
- package/examples/oidc-login/.aug-spec/august/0.19.0/crypto/contracts.aug.md +0 -791
- package/examples/oidc-login/.aug-spec/august/0.19.0/crypto/jose.aug.md +0 -266
- package/examples/oidc-login/.aug-spec/august/0.19.0/json/contracts.aug.md +0 -55
- package/examples/oidc-login/.aug-spec/august/0.19.0/memory/store.aug.md +0 -250
- package/examples/oidc-login/.aug-spec/august/0.19.0/time/contracts.aug.md +0 -96
- package/examples/oidc-login/.aug-spec/august/0.19.0/web/contracts.aug.md +0 -420
- package/examples/ownership-transfer/.aug-spec/august/0.19.0/io/contracts.aug +0 -36
- package/examples/ownership-transfer/.aug-spec/august/0.19.0/io/contracts.aug.md +0 -316
- /package/examples/oidc-login/.aug-spec/{august/0.19.0/memory → packages/@git/url_0eb7c89453c87681ed15/0.0.0-git.a39fc582565d4fca40be4f75fa304d71adc69301}/store.aug +0 -0
- /package/examples/oidc-login/.aug-spec/{august/0.19.0/json → packages/@git/url_2d3c37c690c0fa115be1/0.0.0-git.a39fc582565d4fca40be4f75fa304d71adc69301}/contracts.aug +0 -0
- /package/examples/oidc-login/.aug-spec/{august/0.19.0/web → packages/@git/url_897efafd565158fc4908/0.0.0-git.a39fc582565d4fca40be4f75fa304d71adc69301}/contracts.aug +0 -0
- /package/examples/oidc-login/.aug-spec/{august/0.19.0/time → packages/@git/url_c092cd151499c4e1d8a1/0.0.0-git.a39fc582565d4fca40be4f75fa304d71adc69301}/contracts.aug +0 -0
package/src/project-init.js
CHANGED
|
@@ -1,18 +1,92 @@
|
|
|
1
1
|
import { existsSync, mkdirSync, readdirSync, writeFileSync } from 'node:fs';
|
|
2
2
|
import { basename, join, resolve } from 'node:path';
|
|
3
|
+
export const agentInstructions = `# Working on this August project
|
|
4
|
+
|
|
5
|
+
Start in main.aug. It shows the imports, dependency bindings, and application startup.
|
|
6
|
+
Read the adjacent .aug.md specification before changing a source file. Generate missing
|
|
7
|
+
or stale explanations with aug spec; they describe checked behavior and link dependencies.
|
|
8
|
+
|
|
9
|
+
Keep tests in the file that declares the behavior. Run aug check, aug test, and aug spec
|
|
10
|
+
after a change, and use aug run to compile and run the application. Run prepares required
|
|
11
|
+
source packages and native libraries. Commit aug.lock.json; do not edit .aug-packages.
|
|
12
|
+
|
|
13
|
+
Use labeled inputs, narrow export.aug files, and underscore-prefixed private helpers.
|
|
14
|
+
Leave return types, effects, and errors to inference when an executable body provides
|
|
15
|
+
the answer. Bodyless interfaces still declare their contracts. Use borrow for mutation,
|
|
16
|
+
and keep unsafe native calls inside small adapters. Explain intent in Javadoc when it
|
|
17
|
+
is not evident from the code. Follow the existing indentation or brace style.
|
|
18
|
+
|
|
19
|
+
Language guide: https://greenpandastudios.github.io/augscript/
|
|
20
|
+
`;
|
|
21
|
+
export const weatherSource = `/** The JSON shape returned by the forecast endpoint. Temperatures use whole degrees. */
|
|
22
|
+
record WeatherForecast(string date, int temperatureC, int temperatureF, string summary)
|
|
23
|
+
|
|
24
|
+
/** Return five simulated forecasts. Fixed data keeps the example and its tests reproducible. */
|
|
25
|
+
endpoint GET "/weatherforecast" as weatherForecast():
|
|
26
|
+
return [
|
|
27
|
+
WeatherForecast(
|
|
28
|
+
date="2026-01-01",
|
|
29
|
+
temperatureC=0,
|
|
30
|
+
temperatureF=32,
|
|
31
|
+
summary="Freezing"
|
|
32
|
+
),
|
|
33
|
+
WeatherForecast(
|
|
34
|
+
date="2026-01-02",
|
|
35
|
+
temperatureC=10,
|
|
36
|
+
temperatureF=50,
|
|
37
|
+
summary="Cool"
|
|
38
|
+
),
|
|
39
|
+
WeatherForecast(
|
|
40
|
+
date="2026-01-03",
|
|
41
|
+
temperatureC=20,
|
|
42
|
+
temperatureF=68,
|
|
43
|
+
summary="Mild"
|
|
44
|
+
),
|
|
45
|
+
WeatherForecast(
|
|
46
|
+
date="2026-01-04",
|
|
47
|
+
temperatureC=30,
|
|
48
|
+
temperatureF=86,
|
|
49
|
+
summary="Warm"
|
|
50
|
+
),
|
|
51
|
+
WeatherForecast(
|
|
52
|
+
date="2026-01-05",
|
|
53
|
+
temperatureC=35,
|
|
54
|
+
temperatureF=95,
|
|
55
|
+
summary="Hot"
|
|
56
|
+
)
|
|
57
|
+
]
|
|
58
|
+
|
|
59
|
+
test endpoint weatherForecast client:
|
|
60
|
+
when forecasts:
|
|
61
|
+
it returns_json:
|
|
62
|
+
response = client.request(method="GET", path="/weatherforecast")
|
|
63
|
+
assert(condition=response.status == 200)
|
|
64
|
+
it rejects_other_methods:
|
|
65
|
+
response = client.request(method="POST", path="/weatherforecast")
|
|
66
|
+
assert(condition=response.status == 405)
|
|
67
|
+
`;
|
|
3
68
|
/** Create a small, runnable application without replacing existing files. */
|
|
4
|
-
export function initProject(destination) {
|
|
69
|
+
export function initProject(destination, template = 'hello') {
|
|
5
70
|
const root = resolve(destination);
|
|
6
71
|
if (existsSync(root) && readdirSync(root).length)
|
|
7
72
|
throw new Error(`Directory is not empty: ${root}`);
|
|
8
73
|
const name = basename(root);
|
|
9
74
|
mkdirSync(root, { recursive: true });
|
|
10
75
|
const files = {
|
|
76
|
+
'AGENTS.md': agentInstructions,
|
|
11
77
|
'main.aug': `import Greeter and SimpleGreeter from greeting\n\nimplement Greeter with SimpleGreeter\nresolve Greeter to greeter\nprint(value=greeter.greet(name="August"))\n`,
|
|
12
|
-
'greeting.aug': `/** Build a greeting for a named person. */\ninterface Greeter:\n /** Return a greeting for the named person. */\n greet(string name) returns string\n\n/** A plain-language greeting. */\nSimpleGreeter() implements Greeter:\n greet(string name)
|
|
13
|
-
'.gitignore': '.aug-build/\n.aug-spec/\n*.aug.md\n',
|
|
14
|
-
'README.md': `# ${name}\n\nThis is an August application. Start in \`main.aug\`; its dependencies and startup are visible there.\n\n\`greeting.aug\` contains a public interface, its implementation, and a same-file test.\n\n\`\`\`sh\naug check
|
|
78
|
+
'greeting.aug': `/** Build a greeting for a named person. */\ninterface Greeter:\n /** Return a greeting for the named person. */\n greet(string name) returns string\n\n/** A plain-language greeting. */\nSimpleGreeter() implements Greeter:\n greet(string name):\n return "Hello, " + name + "!"\n\ntest SimpleGreeter greeter:\n when greetings:\n greeter = SimpleGreeter()\n it greets_a_person:\n assert(greeter.greet(name="August") == "Hello, August!")\n`,
|
|
79
|
+
'.gitignore': '.aug-build/\n.aug-spec/\n.aug-packages/\n.aug-install-*/\n*.aug.md\n',
|
|
80
|
+
'README.md': `# ${name}\n\nThis is an August application. Start in \`main.aug\`; its dependencies and startup are visible there.\n\n\`greeting.aug\` contains a public interface, its implementation, and a same-file test.\n\n\`\`\`sh\naug run\naug check\naug test\naug spec\n\`\`\`\n\nFor the language guide, see https://GreenPandaStudios.github.io/augscript/getting-started.\n`,
|
|
15
81
|
};
|
|
82
|
+
if (template === 'weather') {
|
|
83
|
+
delete files['greeting.aug'];
|
|
84
|
+
files['main.aug'] = 'import weatherForecast from forecasts\n\nserve weatherForecast on port 8787\n';
|
|
85
|
+
files['forecasts.aug'] = weatherSource;
|
|
86
|
+
files['weather.http'] = '### Five simulated forecasts\nGET http://127.0.0.1:8787/weatherforecast\n\n### Generated OpenAPI document\nGET http://127.0.0.1:8787/openapi.json\n';
|
|
87
|
+
files['main.yaml'] = 'block_style: indent\nopenapi:\n enabled: true\n title: "Weather forecast API"\n version: "0.1.0"\n';
|
|
88
|
+
files['README.md'] = `# ${name}\n\nA simulated weather API in August. Its endpoint, JSON record, and tests live in forecasts.aug.\n\nRun the server:\n\n\`\`\`sh\naug run\n\`\`\`\n\nOpen http://127.0.0.1:8787/weatherforecast for five forecasts, or http://127.0.0.1:8787/docs for the OpenAPI viewer. These are fixed examples, not live weather observations.\n\n\`\`\`sh\ncurl http://127.0.0.1:8787/weatherforecast\naug test\naug spec\n\`\`\`\n\nGuide: https://greenpandastudios.github.io/augscript/weather-api\n`;
|
|
89
|
+
}
|
|
16
90
|
for (const [file, contents] of Object.entries(files))
|
|
17
91
|
writeFileSync(join(root, file), contents, { flag: 'wx' });
|
|
18
92
|
return root;
|
package/src/project.js
CHANGED
|
@@ -5,7 +5,8 @@ import { loadConfig } from "./config.js";
|
|
|
5
5
|
import { projectPolicies } from "./policies.js";
|
|
6
6
|
import { builtinFunctions, builtinTypes } from "./builtins.js";
|
|
7
7
|
import { libraryChild, libraryRelative, standardLibraries } from "./libraries.js";
|
|
8
|
-
import { projectPackages, readPackage, sourcePaths } from "./package-manager.js";
|
|
8
|
+
import { packageSpecifications, projectPackages, readPackage, sourcePaths } from "./package-manager.js";
|
|
9
|
+
import { isGitSource, sourceAlias } from "./git-packages.js";
|
|
9
10
|
export function isPrivateName(name) { return name.startsWith('_'); }
|
|
10
11
|
function diagnostic(file, line, column, message, code) {
|
|
11
12
|
return { file, line, column, message, code };
|
|
@@ -35,7 +36,7 @@ export function loadProject(projectRoot, overrides = new Map(), cache) {
|
|
|
35
36
|
const { config, diagnostics } = loadConfig(root);
|
|
36
37
|
let library;
|
|
37
38
|
let sourceRoot = root;
|
|
38
|
-
if (existsSync(join(root, 'aug-package.json'))) {
|
|
39
|
+
if (existsSync(join(root, 'aug-package.json')) || !existsSync(join(root, 'main.aug')) && (existsSync(join(root, 'export.aug')) || existsSync(join(root, 'src/export.aug')))) {
|
|
39
40
|
try {
|
|
40
41
|
const loaded = readPackage(root);
|
|
41
42
|
library = loaded.manifest;
|
|
@@ -45,7 +46,7 @@ export function loadProject(projectRoot, overrides = new Map(), cache) {
|
|
|
45
46
|
diagnostics.push(diagnostic(join(root, 'aug-package.json'), 1, 1, error.message, 'PACKAGE'));
|
|
46
47
|
}
|
|
47
48
|
}
|
|
48
|
-
const packages = projectPackages(root, library?.dependencies ?? config.packages, sourceRoot);
|
|
49
|
+
const packages = projectPackages(root, packageSpecifications(sourceRoot, library?.dependencies ?? config.packages, overrides), sourceRoot);
|
|
49
50
|
diagnostics.push(...packages.diagnostics);
|
|
50
51
|
const read = (path) => {
|
|
51
52
|
const source = overrides.get(path) ?? readFileSync(path, 'utf8');
|
|
@@ -56,7 +57,7 @@ export function loadProject(projectRoot, overrides = new Map(), cache) {
|
|
|
56
57
|
cache?.set(path, parsed);
|
|
57
58
|
return parsed;
|
|
58
59
|
};
|
|
59
|
-
for (const path of new Set([...sourceFiles(sourceRoot), ...overrides.keys()].filter(path => path.endsWith('.aug')))) {
|
|
60
|
+
for (const path of new Set([...sourceFiles(sourceRoot), ...[...overrides.keys()].filter(path => path.startsWith(sourceRoot + '/') || path.startsWith(sourceRoot + '\\'))].filter(path => path.endsWith('.aug')))) {
|
|
60
61
|
const parsed = read(path);
|
|
61
62
|
files.set(path, parsed.file);
|
|
62
63
|
diagnostics.push(...parsed.diagnostics);
|
|
@@ -70,7 +71,7 @@ export function loadProject(projectRoot, overrides = new Map(), cache) {
|
|
|
70
71
|
}
|
|
71
72
|
const libraries = standardLibraries();
|
|
72
73
|
const stdlibRoot = libraries.root;
|
|
73
|
-
const libraryFiles = new Set([stdlibRoot, ...
|
|
74
|
+
const libraryFiles = new Set([join(stdlibRoot, 'export.aug'), ...sourceFiles(join(stdlibRoot, 'io'))]);
|
|
74
75
|
for (const path of libraryFiles) {
|
|
75
76
|
const parsed = read(path);
|
|
76
77
|
parsed.file.builtin = true;
|
|
@@ -216,7 +217,7 @@ export function loadProject(projectRoot, overrides = new Map(), cache) {
|
|
|
216
217
|
}
|
|
217
218
|
return result;
|
|
218
219
|
};
|
|
219
|
-
const privateModule = item.from.find(isPrivateName);
|
|
220
|
+
const privateModule = item.from.slice(isGitSource(item.from[0]) ? 1 : 0).find(isPrivateName);
|
|
220
221
|
if (privateModule) {
|
|
221
222
|
diagnostics.push(diagnostic(importer, item.span.line, item.span.column, `Cannot import from private module ${privateModule}`, 'PRIVATE'));
|
|
222
223
|
return result;
|
|
@@ -234,7 +235,8 @@ export function loadProject(projectRoot, overrides = new Map(), cache) {
|
|
|
234
235
|
}
|
|
235
236
|
const owner = files.get(importer)?.package;
|
|
236
237
|
const ownerScope = owner ? packages.scopes.get(owner) : undefined;
|
|
237
|
-
const
|
|
238
|
+
const alias = isGitSource(item.from[0]) ? sourceAlias(item.from[0]) : item.from[0];
|
|
239
|
+
const dependency = ownerScope ? packages.scopes.get(ownerScope.dependencies[alias]) : packages.roots.get(alias);
|
|
238
240
|
if (dependency) {
|
|
239
241
|
let folder = dependency.sourceRoot;
|
|
240
242
|
for (const child of item.from.slice(1)) {
|
package/src/semantic.js
CHANGED
|
@@ -11,6 +11,7 @@ import { javadocBefore } from "./javadoc.js";
|
|
|
11
11
|
import { checkUnitTests, discoverTests, mergeTestAnalysis, uniqueDiagnostics } from "./testing.js";
|
|
12
12
|
import { formatFile } from "./formatter.js";
|
|
13
13
|
import { interceptorBehavior } from "./interceptors.js";
|
|
14
|
+
import { callableResult, callableErrors } from "./contracts.js";
|
|
14
15
|
const genericFacts = (header) => header.typeParams.map(name => ({ name,
|
|
15
16
|
variance: header.typeVariance?.[name] ?? 'invariant', constraints: (header.typeConstraints?.[name] ?? []).map(typeName) }));
|
|
16
17
|
export function contractFacts(checked) {
|
|
@@ -25,11 +26,12 @@ export function contractFacts(checked) {
|
|
|
25
26
|
streaming: !!method.endpoint.streams, errors: method.endpoint.errors.map(error => ({ type: typeName(error.type), status: error.status })) } : undefined,
|
|
26
27
|
policies: (checked.httpPolicies.get(method) ?? []).map((policy, index) => ({ name: policy.name, order: index + 1, options: policy.options,
|
|
27
28
|
dependencies: policy.dependencies.map(index => method.params[index]?.label ?? method.params[index]?.name ?? '') })),
|
|
28
|
-
result: `${method.returnOwnership === 'own' ? 'own ' : ''}${
|
|
29
|
+
result: `${method.returnOwnership === 'own' ? 'own ' : ''}${tyName(callableResult(checked, method))}`,
|
|
29
30
|
changes: [...(contract?.changes ?? method.changes ?? [])],
|
|
30
31
|
capabilities: [...(contract?.uses.values() ?? [])].map(effect => `${effect.source}.${effect.operation}`).concat(method.externC ? [`C.${method.name}`] : []),
|
|
31
32
|
inferredEffects: !!contract?.inferred,
|
|
32
|
-
errors: [...new Set([...
|
|
33
|
+
errors: constructor ? [...new Set([...(checked.constructorContracts.get(constructor)?.errors.map(tyName) ?? constructor.validationErrors?.map(typeName) ?? []),
|
|
34
|
+
...layers.flatMap(layer => layer.errors.map(tyName))])].sort() : callableErrors(checked, method),
|
|
33
35
|
interceptors: layers.map((layer, order) => {
|
|
34
36
|
const effects = checked.effectContracts.get(layer.around);
|
|
35
37
|
return { name: layer.definition.name, order: order + 1, location: layer.definition.node.span,
|
|
@@ -186,6 +188,48 @@ export class SemanticDocument {
|
|
|
186
188
|
complete(offset) { return completions(this.checked, this.path, offset); }
|
|
187
189
|
tokens() { return semanticTokens(this.checked, this.path); }
|
|
188
190
|
fixes() { return suggestedFixes(this.checked, this.path); }
|
|
191
|
+
/** Non-editable declaration hints. Formatting never adds inferred source clauses. */
|
|
192
|
+
inlayHints(start = 0, end = this.source.length) {
|
|
193
|
+
const hints = [];
|
|
194
|
+
for (const [method, contract] of this.checked.callableContracts) {
|
|
195
|
+
if (method.span.file !== this.path || !method.body || method.headerEnd === undefined)
|
|
196
|
+
continue;
|
|
197
|
+
let offset = method.headerEnd;
|
|
198
|
+
while (offset > method.span.start && /\s/.test(this.source[offset - 1]))
|
|
199
|
+
offset--;
|
|
200
|
+
if (offset < start || offset > end)
|
|
201
|
+
continue;
|
|
202
|
+
const clauses = [];
|
|
203
|
+
if (contract.inferredResult && contract.result.kind !== 'error' && contract.result.name !== 'void')
|
|
204
|
+
clauses.push(contract.result.name === '<target result>' ? 'returns the target result' : `returns ${tyName(contract.result)}`);
|
|
205
|
+
const effects = this.checked.effectContracts.get(method);
|
|
206
|
+
if (effects?.inferredChanges && effects.changes.length)
|
|
207
|
+
clauses.push(`changes ${effects.changes.join(' and ')}`);
|
|
208
|
+
const uses = [...(effects?.uses.values() ?? [])].map(effect => `${effect.source}.${effect.operation}`).sort();
|
|
209
|
+
if (effects?.inferred && uses.length)
|
|
210
|
+
clauses.push(`uses ${[...new Set(uses)].join(' and ')}`);
|
|
211
|
+
const errors = callableErrors(this.checked, method);
|
|
212
|
+
if (contract.inferredErrors && errors.length)
|
|
213
|
+
clauses.push(`unless ${errors.join(' and ')}`);
|
|
214
|
+
const complete = clauses.join(' ');
|
|
215
|
+
const compact = complete.length > 120 ? clauses.map(clause => clause.startsWith('uses ') && clause.length > 50 ? `uses ${new Set(uses).size} operations` :
|
|
216
|
+
clause.startsWith('unless ') && clause.length > 50 ? `unless ${errors.length} errors` : clause).join(' ') : complete;
|
|
217
|
+
if (clauses.length)
|
|
218
|
+
hints.push({ offset, label: compact,
|
|
219
|
+
tooltip: `\`\`\`augscript\n${complete}\n\`\`\`\n\n` + 'Inferred from the body, implemented interface, and interceptor layers. These hints are not source text. The compiler still checks ownership, interface limits, and escaping errors. See the adjacent .aug.md spec for the full explanation.' });
|
|
220
|
+
}
|
|
221
|
+
for (const [record, contract] of this.checked.constructorContracts) {
|
|
222
|
+
if (record.span.file !== this.path || !contract.inferredErrors || !contract.errors.length || record.headerEnd === undefined)
|
|
223
|
+
continue;
|
|
224
|
+
let offset = record.headerEnd;
|
|
225
|
+
while (offset > record.span.start && /\s/.test(this.source[offset - 1]))
|
|
226
|
+
offset--;
|
|
227
|
+
if (offset >= start && offset <= end)
|
|
228
|
+
hints.push({ offset, label: 'unless ' + contract.errors.map(tyName).sort().join(' and '),
|
|
229
|
+
tooltip: 'Checked errors inferred from record validation. Callers must catch or propagate them. These hints are not saved source.' });
|
|
230
|
+
}
|
|
231
|
+
return hints.sort((left, right) => left.offset - right.offset);
|
|
232
|
+
}
|
|
189
233
|
format() { return formatFile(this.checked.project, this.checked.project.files.get(this.path)); }
|
|
190
234
|
describe(options) { return describe(this.checked, this.path, options); }
|
|
191
235
|
definition(offset) {
|
package/src/snippets.js
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/** Small starting points for ordinary August declarations and statements. */
|
|
2
|
+
export const snippetCatalog = [
|
|
3
|
+
{ prefix: 'record', description: 'Immutable data with named fields', body: 'record ${1:User}(${2:string name})' },
|
|
4
|
+
{ prefix: 'interface', description: 'A public behavior contract', body: 'interface ${1:Greeter}:\n ${2:greet}(${3:string name}) returns ${4:string}' },
|
|
5
|
+
{ prefix: 'capability', description: 'An explicit I/O contract', body: 'capability ${1:Logger}:\n ${2:log}(${3:string message}) uses ${1:Logger}.${2:log}' },
|
|
6
|
+
{ prefix: 'implementation', description: 'A class implementing an interface', body: '${1:SimpleGreeter}(${2:resolve Logger logger}) implements ${3:Greeter}:\n ${4:greet}(${5:string name}):\n $0' },
|
|
7
|
+
{ prefix: 'method', description: 'A function or method with an inferred result', body: '${1:calculate}(${2:int value}):\n $0' },
|
|
8
|
+
{ prefix: 'generic', description: 'A generic identity function', body: '${1:identity}<T>(T value):\n return value' },
|
|
9
|
+
{ prefix: 'initialize', description: 'Initialize constructor fields or validate a record', body: 'initialize:\n $0' },
|
|
10
|
+
{ prefix: 'import', description: 'Import a public declaration', body: 'import ${1:Name} from ${2:module}' },
|
|
11
|
+
{ prefix: 'importurl', description: 'Import directly from a public Git repository', body: 'import ${1:Name} from "https://github.com/${2:owner}/${3:library}"' },
|
|
12
|
+
{ prefix: 'export', description: 'Expose one declaration from a sibling file', body: 'export ${1:Name} from ${2:file}' },
|
|
13
|
+
{ prefix: 'exportfolder', description: 'Expose a child module with its own export.aug', body: 'export folder ${1:module}' },
|
|
14
|
+
{ prefix: 'implement', description: 'Select a dependency provider in main.aug', body: 'implement ${1:Contract} with ${2:Provider}' },
|
|
15
|
+
{ prefix: 'resolve', description: 'Resolve a dependency explicitly', body: 'resolve ${1:Contract} to ${2:service}' },
|
|
16
|
+
{ prefix: 'if', description: 'Conditional code', body: 'if ${1:condition}:\n $0' },
|
|
17
|
+
{ prefix: 'ifelse', description: 'Two alternatives', body: 'if ${1:condition}:\n $2\nelse:\n $0' },
|
|
18
|
+
{ prefix: 'for', description: 'Visit collection values', body: 'for ${1:item} in ${2:items}:\n $0' },
|
|
19
|
+
{ prefix: 'while', description: 'Repeat while a condition holds', body: 'while ${1:condition}:\n $0' },
|
|
20
|
+
{ prefix: 'matchoptional', description: 'Handle a value or null', body: 'match ${1:value}:\n when some ${2:item}:\n $3\n when null:\n $0' },
|
|
21
|
+
{ prefix: 'try', description: 'Recover from a checked failure', body: 'try:\n $1\ncatch ${2:FileError} ${3:error}:\n $0' },
|
|
22
|
+
{ prefix: 'borrow', description: 'Grant bounded mutable access', body: 'borrow ${1:value}:\n $0' },
|
|
23
|
+
{ prefix: 'scope', description: 'Start and join a child computation', body: 'scope:\n ${1:pending} = start ${2:load}(${3:})\n wait for ${1:pending} as ${4:result}\n $0' },
|
|
24
|
+
{ prefix: 'waitall', description: 'Join two computations', body: 'wait for ${1:loadingUsers} and ${2:loadingOrders} as ${3:users} and ${4:orders}' },
|
|
25
|
+
{ prefix: 'lock', description: 'Access shared mutable state inside a bounded lock', body: 'lock ${1:shared} as ${2:value}:\n $0' },
|
|
26
|
+
{ prefix: 'unsafe', description: 'Contain a native C call', body: 'unsafe:\n $0' },
|
|
27
|
+
{ prefix: 'serve', description: 'Start a native HTTP listener', body: 'serve ${1:endpoint} on port ${2:8787}' },
|
|
28
|
+
...['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
|
+
{ 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"})' },
|
|
30
|
+
{ prefix: 'component', description: 'An embeddable server-rendered HTML component', body: '${1:Heading}(string text) returns Html:\n return <h1>{text}</h1>' },
|
|
31
|
+
{ prefix: 'test', description: 'A same-file function test', body: 'test ${1:calculate}:\n when ${2:inputs}:\n it ${3:works}:\n assert(condition=${4:true})' },
|
|
32
|
+
{ prefix: 'testclass', description: 'Fresh setup for each class test', body: 'test ${1:Worker} ${2:worker}:\n when ${3:ready}:\n ${2:worker} = ${1:Worker}(${4:})\n it ${5:works}:\n assert(condition=${6:true})' },
|
|
33
|
+
{ prefix: 'testendpoint', description: 'Exercise the real endpoint pipeline', body: 'test endpoint ${1:handler} client:\n when requests:\n it succeeds:\n response = client.request(method="GET", path="${2:/items}")\n assert(condition=response.status == 200)' },
|
|
34
|
+
{ prefix: 'it', description: 'A named test case', body: 'it ${1:works}:\n assert(condition=${2:true})' },
|
|
35
|
+
{ prefix: 'fixture', description: 'A reusable same-file test fixture', body: 'fixture ${1:sample}():\n return ${2:42}' },
|
|
36
|
+
{ prefix: 'interceptor', description: 'Wrap a call and preserve its result', body: 'interceptor ${1:Audit}<T>():\n around():\n result = next()\n $0\n return result' },
|
|
37
|
+
{ prefix: 'javadoc', description: 'Document intent and labeled inputs', body: '/**\n * ${1:Explain why this declaration exists.}\n * @param ${2:name} ${3:What the input means.}\n */' },
|
|
38
|
+
];
|
|
39
|
+
export function snippetBody(body, style, tabs = false) {
|
|
40
|
+
if (style === 'braces') {
|
|
41
|
+
const result = [];
|
|
42
|
+
let depth = 0;
|
|
43
|
+
for (const line of body.split('\n')) {
|
|
44
|
+
const level = Math.floor((/^ */.exec(line)?.[0].length ?? 0) / 4);
|
|
45
|
+
while (depth > level)
|
|
46
|
+
result.push(' '.repeat(--depth) + '}');
|
|
47
|
+
if (line.endsWith(':')) {
|
|
48
|
+
result.push(line.slice(0, -1) + ' {');
|
|
49
|
+
depth = level + 1;
|
|
50
|
+
}
|
|
51
|
+
else
|
|
52
|
+
result.push(line);
|
|
53
|
+
}
|
|
54
|
+
while (depth)
|
|
55
|
+
result.push(' '.repeat(--depth) + '}');
|
|
56
|
+
body = result.join('\n');
|
|
57
|
+
}
|
|
58
|
+
return tabs ? body.replace(/^(?: {4})+/gm, prefix => '\t'.repeat(prefix.length / 4)) : body;
|
|
59
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { existsSync, lstatSync, readFileSync, renameSync, rmSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { basename, dirname, relative } from 'node:path';
|
|
3
|
+
import { checkProject } from "./checker.js";
|
|
4
|
+
import { loadProject } from "./project.js";
|
|
5
|
+
import { checkUnitTests, discoverTests, mergeTestAnalysis } from "./testing.js";
|
|
6
|
+
const marker = '// aug-spec: ';
|
|
7
|
+
/** One compiler-managed pointer. It gives readers an action and a precise neighboring filename. */
|
|
8
|
+
export function specHint(file) {
|
|
9
|
+
const newline = file.source.includes('\r\n') ? '\r\n' : '\n';
|
|
10
|
+
const pointer = marker + JSON.stringify(basename(file.path) + '.md') + ' explains this file. Read it before changes; refresh with aug spec.' + newline;
|
|
11
|
+
const old = file.source.startsWith(marker) ? (file.source.includes('\n') ? file.source.indexOf('\n') + 1 : file.source.length) : 0;
|
|
12
|
+
const body = old ? file.source.slice(old) : file.source;
|
|
13
|
+
return { file, text: pointer + body, lineOffset: old ? 0 : 1 };
|
|
14
|
+
}
|
|
15
|
+
/** Prepare hints before native emission so debugger/source-map lines use the updated sources. */
|
|
16
|
+
export function updateSpecHints(checked) {
|
|
17
|
+
const hints = [...checked.project.files.values()].filter(file => !file.builtin && !file.package).map(specHint)
|
|
18
|
+
.filter(hint => hint.text !== hint.file.source);
|
|
19
|
+
if (!hints.length)
|
|
20
|
+
return checked;
|
|
21
|
+
if (checked.diagnostics.some(issue => issue.severity !== 'warning'))
|
|
22
|
+
throw new Error('Fix compiler errors before adding specification pointers');
|
|
23
|
+
for (const { file } of hints) {
|
|
24
|
+
const root = checked.project.root;
|
|
25
|
+
if (relative(root, file.path).startsWith('..'))
|
|
26
|
+
throw new Error('Specification pointer escapes the project');
|
|
27
|
+
let parent = dirname(file.path);
|
|
28
|
+
while (parent !== root && parent !== dirname(parent)) {
|
|
29
|
+
if (lstatSync(parent).isSymbolicLink())
|
|
30
|
+
throw new Error('Specification source directory is a symbolic link');
|
|
31
|
+
parent = dirname(parent);
|
|
32
|
+
}
|
|
33
|
+
if (lstatSync(file.path).isSymbolicLink())
|
|
34
|
+
throw new Error('Specification source is a symbolic link');
|
|
35
|
+
if (readFileSync(file.path, 'utf8') !== file.source)
|
|
36
|
+
throw new Error('Source changed during compilation; retry before generating specifications');
|
|
37
|
+
}
|
|
38
|
+
for (const { file, text } of hints) {
|
|
39
|
+
const temporary = file.path + '.aug-spec-tmp';
|
|
40
|
+
let created = false;
|
|
41
|
+
try {
|
|
42
|
+
writeFileSync(temporary, text, { flag: 'wx', mode: lstatSync(file.path).mode });
|
|
43
|
+
created = true;
|
|
44
|
+
renameSync(temporary, file.path);
|
|
45
|
+
}
|
|
46
|
+
finally {
|
|
47
|
+
if (created && existsSync(temporary))
|
|
48
|
+
rmSync(temporary);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
const project = loadProject(checked.project.root), fresh = checkProject(project), discovery = discoverTests(project);
|
|
52
|
+
const tests = checkUnitTests(project, discovery.tests);
|
|
53
|
+
fresh.diagnostics.push(...discovery.diagnostics, ...tests.flatMap(test => test.checked.diagnostics));
|
|
54
|
+
mergeTestAnalysis(fresh, tests);
|
|
55
|
+
return fresh;
|
|
56
|
+
}
|
package/src/spec-tree.js
ADDED
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
export const step = (text) => ({ kind: 'step', text });
|
|
2
|
+
export const action = (verb, object) => ({ kind: 'action', verb, object });
|
|
3
|
+
export const sequence = (lead, items, infinitive) => ({ kind: 'sequence', lead, items, infinitive });
|
|
4
|
+
export const branch = (condition, then, otherwise = []) => ({ kind: 'branch', condition, then, otherwise });
|
|
5
|
+
export const loop = (lead, children, end) => ({ kind: 'loop', lead, children, end });
|
|
6
|
+
export const choice = (value, cases) => ({ kind: 'choice', value, cases });
|
|
7
|
+
export const attempt = (children, catches, always) => ({ kind: 'attempt', children, catches, always });
|
|
8
|
+
export const scope = (lead, children, end) => ({ kind: 'scope', lead, children, end });
|
|
9
|
+
export const paragraph = (text) => ({ kind: 'paragraph', text });
|
|
10
|
+
export const flow = (steps) => ({ kind: 'flow', steps });
|
|
11
|
+
export const section = (title, level, children, anchor, source) => ({ kind: 'section', title, level, anchor, source, children });
|
|
12
|
+
/** Coordination is grammatical only; callers retain the original evaluation order. */
|
|
13
|
+
export function coordinate(items, conjunction = 'and') {
|
|
14
|
+
return items.length < 2 ? items[0] ?? '' : items.length === 2 ? items.join(` ${conjunction} `) :
|
|
15
|
+
items.slice(0, -1).join(', ') + `, ${conjunction} ` + items.at(-1);
|
|
16
|
+
}
|
|
17
|
+
const clause = (text) => text.charAt(0).toLowerCase() + text.slice(1).replace(/\.$/, '');
|
|
18
|
+
const bare = (text) => clause(text).replace(/^it /, '');
|
|
19
|
+
const visible = (text) => text.replace(/\[([^\]]+)\]\([^)]*\)/g, '$1').replace(/[`*_]/g, '');
|
|
20
|
+
const words = (text) => visible(text).split(/\s+/).filter(Boolean).length;
|
|
21
|
+
const sentencesIn = (text) => (text.replace(/(`+)[\s\S]*?\1/g, '').match(/[.!?](?:\s|$)/g) ?? []).length;
|
|
22
|
+
/** Small deterministic microplanner. A clause may join only other clauses in its own scope. */
|
|
23
|
+
export function planFlow(nodes) {
|
|
24
|
+
const sentence = (text, sources = [], boundary = false) => ({ text, sources, boundary });
|
|
25
|
+
const plan = (children) => {
|
|
26
|
+
const result = [];
|
|
27
|
+
let transition = '';
|
|
28
|
+
const append = (items) => {
|
|
29
|
+
if (transition && items.length)
|
|
30
|
+
items[0].text = transition + ', ' + clause(items[0].text) + '.';
|
|
31
|
+
transition = '';
|
|
32
|
+
result.push(...items);
|
|
33
|
+
};
|
|
34
|
+
for (let index = 0; index < children.length; index++) {
|
|
35
|
+
const node = children[index];
|
|
36
|
+
const setting = node.kind === 'action' && node.verb === 'set' ? node.object.match(/^(`[^`]+`) to (.+)$/) : undefined;
|
|
37
|
+
if (setting) {
|
|
38
|
+
const settings = [node];
|
|
39
|
+
while (index + 1 < children.length) {
|
|
40
|
+
const next = children[index + 1], match = next.kind === 'action' && next.verb === 'set' ? next.object.match(/^(`[^`]+`) to (.+)$/) : undefined;
|
|
41
|
+
if (!match || match[2] !== setting[2])
|
|
42
|
+
break;
|
|
43
|
+
settings.push(next);
|
|
44
|
+
index++;
|
|
45
|
+
}
|
|
46
|
+
if (settings.length > 1) {
|
|
47
|
+
append([sentence('It sets ' + coordinate(settings.map(item => item.object.match(/^(`[^`]+`)/)[1])) +
|
|
48
|
+
' separately, each to ' + setting[2] + '.', settings.flatMap(item => item.source ? [item.source] : []))]);
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
// Adjacent validation guards have one outcome. Explain the checks together,
|
|
53
|
+
// retaining every guard and failure in the evidence ledger.
|
|
54
|
+
if (node.kind === 'branch' && node.requirement && !node.otherwise.length) {
|
|
55
|
+
const guards = [node];
|
|
56
|
+
while (index + 1 < children.length) {
|
|
57
|
+
const next = children[index + 1];
|
|
58
|
+
if (next.kind !== 'branch' || !next.requirement || next.otherwise.length || next.then[0]?.kind !== 'action' || node.then[0]?.kind !== 'action' ||
|
|
59
|
+
next.then[0].object !== node.then[0].object || words(guards.map(guard => guard.requirement).join(' ') + next.requirement) > 65)
|
|
60
|
+
break;
|
|
61
|
+
guards.push(next);
|
|
62
|
+
index++;
|
|
63
|
+
}
|
|
64
|
+
const failure = node.then[0];
|
|
65
|
+
append([sentence('It checks that ' + coordinate(guards.map(guard => guard.requirement)) + '. It raises ' + failure.object.replace(/^with /, '') + ' at the first failed check.', guards.flatMap(guard => [...(guard.source ? [guard.source] : []), ...plan(guard.then).flatMap(item => item.sources)]))]);
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
append(realize(node, index < children.length - 1));
|
|
69
|
+
if (node.kind === 'loop')
|
|
70
|
+
transition = 'After the loop';
|
|
71
|
+
if (node.kind === 'branch' && (!compact(node.then) && !exits(node.then) || node.otherwise.length && !compact(node.otherwise) && !exits(node.otherwise)))
|
|
72
|
+
transition = 'After that conditional work';
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return result;
|
|
76
|
+
};
|
|
77
|
+
const compact = (children) => {
|
|
78
|
+
if (!children.length)
|
|
79
|
+
return sentence('Do nothing.');
|
|
80
|
+
if (children.length > 3 || children.some(child => !['step', 'action', 'sequence'].includes(child.kind)))
|
|
81
|
+
return;
|
|
82
|
+
const sentences = plan(children);
|
|
83
|
+
if (sentences.some(item => item.boundary || words(item.text) > 42) || words(sentences.map(item => item.text).join(' ')) > 55)
|
|
84
|
+
return;
|
|
85
|
+
return { ...sentence(sentences.map(item => item.text).join(' '), sentences.flatMap(item => item.sources)),
|
|
86
|
+
infinitive: sentences.every(item => item.infinitive) ? sentences.map(item => item.infinitive).join(', then ') : undefined };
|
|
87
|
+
};
|
|
88
|
+
const exits = (children) => {
|
|
89
|
+
const last = children.at(-1);
|
|
90
|
+
if (!last)
|
|
91
|
+
return false;
|
|
92
|
+
return last.kind === 'action' ? (last.verb === 'return' || last.verb === 'fail') :
|
|
93
|
+
last.kind === 'branch' ? exits(last.then) && exits(last.otherwise) :
|
|
94
|
+
last.kind === 'scope' ? exits(last.children) :
|
|
95
|
+
last.kind === 'attempt' ? exits(last.children) && last.catches.every(handler => exits(handler.children)) : false;
|
|
96
|
+
};
|
|
97
|
+
const under = (lead, children, end) => {
|
|
98
|
+
const continuation = /(?: and| always| it)$/.test(lead);
|
|
99
|
+
const join = continuation ? ' ' : ', ';
|
|
100
|
+
const small = compact(children);
|
|
101
|
+
if (small) {
|
|
102
|
+
// One guard governs a coordinated sequence, never an adjacent outer statement.
|
|
103
|
+
const items = plan(children);
|
|
104
|
+
return [sentence(`${lead}${join}${items.length ? items.map((item, index) => (index ? 'then ' : '') + (continuation && !index ? bare(item.text) : clause(item.text))).join('; ') : continuation ? 'does nothing' : 'it does nothing'}.`, small.sources)];
|
|
105
|
+
}
|
|
106
|
+
const items = plan(children);
|
|
107
|
+
if (!items.length)
|
|
108
|
+
return [sentence(`${lead}${join}it does nothing.`)];
|
|
109
|
+
items[0].text = lead + join + (continuation ? bare(items[0].text) : clause(items[0].text)) + '.';
|
|
110
|
+
// The paragraph itself expresses the scope. Only loops and resource scopes
|
|
111
|
+
// need an exit contract; avoid narrating the edges of the syntax tree.
|
|
112
|
+
return items;
|
|
113
|
+
};
|
|
114
|
+
const realize = (node, hasNext) => {
|
|
115
|
+
let result;
|
|
116
|
+
switch (node.kind) {
|
|
117
|
+
case 'step':
|
|
118
|
+
result = [sentence(node.text)];
|
|
119
|
+
break;
|
|
120
|
+
case 'action': {
|
|
121
|
+
const verbs = { set: 'sets', split: 'splits', call: 'calls', construct: 'creates', evaluate: 'evaluates', return: 'returns', fail: 'raises', send: 'sends', freeze: 'freezes', serve: 'serves', increase: 'increases', decrease: 'decreases', continue: 'continues' };
|
|
122
|
+
const object = node.object ? ' ' + node.object : '';
|
|
123
|
+
const realized = node.verb === 'fail' ? `It raises ${node.object.replace(/^with /, '')}.` : `It ${verbs[node.verb]}${object}.`;
|
|
124
|
+
result = [{ ...sentence(realized), infinitive: node.verb === 'fail' ? 'raise ' + node.object.replace(/^with /, '') : node.verb + object }];
|
|
125
|
+
break;
|
|
126
|
+
}
|
|
127
|
+
case 'sequence':
|
|
128
|
+
result = [{ ...sentence(`${node.lead}: ${coordinate(node.items)}.`),
|
|
129
|
+
infinitive: node.infinitive ? node.infinitive + ': ' + coordinate(node.items) : undefined }];
|
|
130
|
+
break;
|
|
131
|
+
case 'branch': {
|
|
132
|
+
const yes = node.then[0], no = node.otherwise[0];
|
|
133
|
+
if (node.then.length === 1 && node.otherwise.length === 1 && yes.kind === 'action' && no.kind === 'action' && yes.verb === 'return' && no.verb === 'return')
|
|
134
|
+
result = [sentence(`It returns ${yes.object} if ${node.condition}, or ${no.object} otherwise.`, [...(yes.source ? [yes.source] : []), ...(no.source ? [no.source] : [])])];
|
|
135
|
+
else
|
|
136
|
+
result = [...under('If ' + node.condition, node.then, ''), ...(node.otherwise.length ? under('Otherwise', node.otherwise, '') : [])];
|
|
137
|
+
break;
|
|
138
|
+
}
|
|
139
|
+
case 'loop':
|
|
140
|
+
result = under(node.lead, node.children, node.end);
|
|
141
|
+
break;
|
|
142
|
+
case 'choice': {
|
|
143
|
+
result = node.cases.length > 2 || node.cases.some(item => item.condition.includes(' satisfies ')) ?
|
|
144
|
+
[sentence('It handles ' + node.value + ' with the first matching case.')] : [];
|
|
145
|
+
for (const alternative of node.cases)
|
|
146
|
+
result.push(...under(alternative.condition, alternative.children, alternative.end ?? 'This ends that case.'));
|
|
147
|
+
break;
|
|
148
|
+
}
|
|
149
|
+
case 'attempt': {
|
|
150
|
+
const small = compact(node.children);
|
|
151
|
+
result = small?.infinitive || !node.children.length ? [sentence(node.children.length ? 'It tries to ' + small.infinitive + '.' : 'This attempt has no operation.', small?.sources ?? [], true)] :
|
|
152
|
+
plan(node.children);
|
|
153
|
+
for (const handler of node.catches) {
|
|
154
|
+
const used = plan(handler.children).some(item => visible(item.text).includes(visible(handler.name)));
|
|
155
|
+
result.push(...under(`If this work raises ${handler.error}${used ? ' as ' + handler.name : ''}, it`, handler.children, ''));
|
|
156
|
+
}
|
|
157
|
+
if (node.always !== undefined)
|
|
158
|
+
result.push(...under('Whether it succeeds or fails, it always', node.always, ''));
|
|
159
|
+
break;
|
|
160
|
+
}
|
|
161
|
+
case 'scope':
|
|
162
|
+
result = [...under(node.lead, node.children, ''), ...(node.end ? [sentence(node.end)] : [])];
|
|
163
|
+
break;
|
|
164
|
+
}
|
|
165
|
+
if (node.source)
|
|
166
|
+
result[0].sources.unshift(node.source);
|
|
167
|
+
return result;
|
|
168
|
+
};
|
|
169
|
+
const sentences = plan(nodes), paragraphs = [];
|
|
170
|
+
let current = '';
|
|
171
|
+
for (const item of sentences) {
|
|
172
|
+
if (current && (item.boundary || words(current + ' ' + item.text) > 110 || sentencesIn(current + ' ' + item.text) > 4)) {
|
|
173
|
+
paragraphs.push(current);
|
|
174
|
+
current = '';
|
|
175
|
+
}
|
|
176
|
+
current += (current ? ' ' : '') + item.text;
|
|
177
|
+
}
|
|
178
|
+
if (current)
|
|
179
|
+
paragraphs.push(current);
|
|
180
|
+
const sources = sentences.flatMap(item => item.sources);
|
|
181
|
+
const evidence = (node) => {
|
|
182
|
+
const children = node.kind === 'branch' ? [...node.then, ...node.otherwise] : node.kind === 'choice' ? node.cases.flatMap(item => item.children) :
|
|
183
|
+
node.kind === 'attempt' ? [...node.children, ...node.catches.flatMap(item => item.children), ...(node.always ?? [])] :
|
|
184
|
+
node.kind === 'loop' || node.kind === 'scope' ? node.children : [];
|
|
185
|
+
return [...(node.source ? [node.source] : []), ...children.flatMap(evidence)];
|
|
186
|
+
};
|
|
187
|
+
if (JSON.stringify(sources) !== JSON.stringify(nodes.flatMap(evidence)))
|
|
188
|
+
throw new Error('Specification planning lost or reordered source facts');
|
|
189
|
+
return { paragraphs, sources };
|
|
190
|
+
}
|
|
191
|
+
function renderChildren(children) {
|
|
192
|
+
const blocks = [];
|
|
193
|
+
let current = '';
|
|
194
|
+
const flush = () => { if (current)
|
|
195
|
+
blocks.push(current); current = ''; };
|
|
196
|
+
const add = (text) => {
|
|
197
|
+
if (!text.trim())
|
|
198
|
+
return;
|
|
199
|
+
// Author Markdown keeps its structure; generated contracts and small bodies form connected prose.
|
|
200
|
+
if (text.includes('\n')) {
|
|
201
|
+
flush();
|
|
202
|
+
blocks.push(text.trim());
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
if (current && (words(current + ' ' + text) > 110 || sentencesIn(current + ' ' + text) > 4))
|
|
206
|
+
flush();
|
|
207
|
+
current += (current ? ' ' : '') + text.trim();
|
|
208
|
+
};
|
|
209
|
+
for (const child of children) {
|
|
210
|
+
if (child.kind === 'paragraph')
|
|
211
|
+
add(child.text);
|
|
212
|
+
else if (child.kind === 'flow') {
|
|
213
|
+
const paragraphs = planFlow(child.steps).paragraphs;
|
|
214
|
+
if (paragraphs.length)
|
|
215
|
+
add(paragraphs[0]);
|
|
216
|
+
for (const text of paragraphs.slice(1)) {
|
|
217
|
+
flush();
|
|
218
|
+
add(text);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
else {
|
|
222
|
+
flush();
|
|
223
|
+
blocks.push(renderNode(child));
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
flush();
|
|
227
|
+
return blocks.join('\n\n');
|
|
228
|
+
}
|
|
229
|
+
function renderNode(node) {
|
|
230
|
+
switch (node.kind) {
|
|
231
|
+
case 'paragraph': return node.text.trim();
|
|
232
|
+
case 'flow': return planFlow(node.steps).paragraphs.join('\n\n');
|
|
233
|
+
case 'section': {
|
|
234
|
+
const heading = `${node.anchor ? `<a id="${node.anchor}"></a>\n` : ''}${'#'.repeat(node.level)} ${node.title}${node.source ? ' · ' + node.source : ''}`;
|
|
235
|
+
const body = renderChildren(node.children);
|
|
236
|
+
return heading + (body ? '\n\n' + body : '');
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
/** Lay out planned prose with one blank line between paragraphs. */
|
|
241
|
+
export function renderSpecTree(root) { return renderNode(root).trimEnd() + '\n'; }
|