├── .prettierignore ├── rundir └── .gitkeep ├── backend ├── serialization │ ├── .gitkeep │ ├── vanilla_Microsoft-FSharp-Collections-FSharpMap-2-System-String-System-String-_baseline.json │ ├── oplist-binary-latest.bin │ ├── toplevels-binary-latest.bin │ ├── vanilla_LibCloud-Queue-NotificationData_simple.json │ ├── vanilla_System-Tuple-2-System-Guid-Microsoft-FSharp-Collections-FSharpList-1-System-UInt64-_simple.json │ ├── vanilla_System-Tuple-5-System-String-System-String-System-String-NodaTime-Instant-System-Guid-_simple.json │ └── vanilla_LibService-Rollbar-HoneycombJson_simple.json ├── src │ ├── LibTreeSitter │ │ ├── .gitignore │ │ ├── paket.references │ │ └── Helpers.fs │ ├── DvalReprDeveloper │ │ ├── paket.references │ │ ├── README.md │ │ └── DvalReprDeveloper.fsproj │ ├── ProdExec │ │ ├── paket.references │ │ ├── README.md │ │ └── ProdExec.fsproj │ ├── QueueWorker │ │ ├── paket.references │ │ └── README.md │ ├── BwdServer │ │ ├── paket.references │ │ └── README.md │ ├── CronChecker │ │ ├── paket.references │ │ ├── README.md │ │ └── CronChecker.fsproj │ ├── LibClientTypes │ │ ├── paket.references │ │ ├── ClientPusherTypes.fs │ │ ├── README.md │ │ └── LibClientTypes.fsproj │ ├── LibHttpMiddleware │ │ ├── paket.references │ │ ├── README.md │ │ └── LibHttpMiddleware.fsproj │ ├── LibPackageManager │ │ ├── InMemory │ │ │ └── ProgramTypes.fs │ │ ├── paket.references │ │ ├── Caching.fs │ │ └── Stats.fs │ ├── BuiltinCliHost │ │ ├── paket.references │ │ └── Builtin.fs │ ├── LibBinarySerialization │ │ ├── paket.references │ │ └── Serializers │ │ │ ├── RT │ │ │ └── PackageValue.fs │ │ │ └── PT │ │ │ └── PackageValue.fs │ ├── LibCloudExecution │ │ ├── paket.references │ │ ├── README.md │ │ └── Init.fs │ ├── LibConfig │ │ ├── paket.references │ │ └── README.md │ ├── LocalExec │ │ ├── paket.references │ │ ├── README.md │ │ └── Utils.fs │ ├── BuiltinCloudExecution │ │ ├── paket.references │ │ ├── README.md │ │ └── Builtin.fs │ ├── BuiltinDarkInternal │ │ ├── paket.references │ │ ├── README.md │ │ └── Helpers │ │ │ └── Permissions.fs │ ├── LibClientTypesToCloudTypes │ │ ├── paket.references │ │ └── LibClientTypesToCloudTypes.fsproj │ ├── BuiltinCli │ │ ├── paket.references │ │ └── Builtin.fs │ ├── BuiltinPM │ │ ├── paket.references │ │ └── Builtin.fs │ ├── LibDB │ │ ├── paket.references │ │ └── LibDB.fsproj │ ├── Wasm │ │ ├── paket.references │ │ ├── Program.fs │ │ ├── Builtin.fs │ │ ├── Init.fs │ │ └── README.md │ ├── BuiltinExecution │ │ ├── paket.references │ │ └── Libs │ │ │ └── Bool.fs │ ├── LibExecution │ │ ├── paket.references │ │ └── DarkDateTime.fs │ ├── Cli │ │ ├── paket.references │ │ └── README.md │ ├── LibParser │ │ └── paket.references │ ├── LibService │ │ ├── README.md │ │ ├── paket.references │ │ ├── Init.fs │ │ ├── Kestrel.fs │ │ ├── HSTS.fs │ │ └── FireAndForget.fs │ ├── Prelude │ │ ├── paket.references │ │ ├── Option.fs │ │ ├── Lazy.fs │ │ ├── README.md │ │ ├── HashSet.fs │ │ ├── ResizeArray.fs │ │ ├── Tuple2.fs │ │ ├── Regex.fs │ │ └── Dictionary.fs │ └── LibCloud │ │ ├── README.md │ │ ├── paket.references │ │ ├── Init.fs │ │ ├── DvalReprInternalHash.fs │ │ └── Password.fs ├── testfiles │ ├── data │ │ ├── boring-text │ │ ├── .gitattributes │ │ ├── favicon-32x32.png │ │ └── sample_image_bytes.png │ ├── execution │ │ ├── language │ │ │ ├── basic │ │ │ │ ├── evariable.dark │ │ │ │ ├── dfloat.dark │ │ │ │ ├── estring.dark │ │ │ │ ├── eand.dark │ │ │ │ └── eor.dark │ │ │ ├── collections │ │ │ │ ├── edict.dark │ │ │ │ ├── dlist.dark │ │ │ │ └── dtuple.dark │ │ │ ├── apply │ │ │ │ └── einfix.dark │ │ │ ├── interpreter.dark │ │ │ └── custom-data │ │ │ │ └── record-field-acess.dark │ │ ├── cloud │ │ │ ├── _middleware.dark │ │ │ └── _events.dark │ │ ├── cli │ │ │ └── file.tests │ │ └── stdlib │ │ │ ├── bytes.dark │ │ │ ├── bool.dark │ │ │ └── uuid.dark │ ├── httpclient │ │ ├── v0 │ │ │ ├── todo │ │ │ │ ├── readme.md │ │ │ │ ├── response-redirect-to-ftp.test │ │ │ │ ├── response-content-type-no-charset.test │ │ │ │ ├── response-content-type-latin1.test │ │ │ │ ├── response-content-encoding-invalid.test │ │ │ │ ├── response-body-invalid-string.test │ │ │ │ ├── response-body-unicode.test │ │ │ │ ├── response-redirect-to-same-place-relative.test │ │ │ │ ├── response-content-type-invalid.test │ │ │ │ ├── request-query-param-null.test │ │ │ │ ├── response-header-empty.test │ │ │ │ ├── response-header-int.test │ │ │ │ ├── response-redirect-delete.test │ │ │ │ ├── uri-with-auth-just-username.test │ │ │ │ ├── response-content-encoding-brotli.test │ │ │ │ ├── response-header-object.test │ │ │ │ ├── uri-with-auth-just-password.test │ │ │ │ ├── response-content-encoding-gzipped.test │ │ │ │ ├── response-header-string.test │ │ │ │ ├── uri-with-auth-just-username-with-colon.test │ │ │ │ ├── response-content-encoding-deflate.test │ │ │ │ └── request-header-override-content-length-get.test │ │ │ ├── _request-content-type-invalid.test │ │ │ ├── response-redirect-to-file.test │ │ │ ├── basic-head.test │ │ │ ├── _response-redirect-to-same-place-absolute.test │ │ │ ├── response-redirect-302.test │ │ │ ├── response-redirect-306.test │ │ │ ├── _response-redirect-304.test │ │ │ ├── response-redirect-303.test │ │ │ ├── response-redirect-305.test │ │ │ ├── response-redirect-308.test │ │ │ ├── response-redirect-300.test │ │ │ ├── response-redirect-301.test │ │ │ ├── response-redirect-307.test │ │ │ ├── basic-get-helper-function.test │ │ │ ├── basic-get.test │ │ │ ├── basic-options-helper-function.test │ │ │ ├── basic-options.test │ │ │ ├── basic-delete-helper-function.test │ │ │ ├── basic-delete.test │ │ │ ├── basic-post.test │ │ │ ├── basic-post-helper-function.test │ │ │ ├── _uri-with-path-fragment.test │ │ │ ├── request-query-param-int.test │ │ │ ├── uri-with-path-slash.test │ │ │ ├── request-query-param-float.test │ │ │ ├── _uri-with-auth-both.test │ │ │ ├── _request-content-type-empty.test │ │ │ ├── basic-head-returns-body-helper-function.test │ │ │ ├── basic-head-returns-body.test │ │ │ ├── uri-with-path-basic.test │ │ │ ├── uri-with-path-dots.test │ │ │ ├── response-header-duplicate.test │ │ │ ├── basic-patch.test │ │ │ └── request-form-simple.test │ │ └── README.md │ └── httphandler │ │ ├── query-string.test │ │ ├── url-http.test │ │ ├── url-https.test │ │ ├── injected-icon-post-roundtrip.test │ │ ├── simple-inline-string-post.test │ │ ├── response-with-500.test │ │ ├── simple-injected-string-post.test │ │ ├── injected-icon-post-length.test │ │ ├── url-custom-domain.test │ │ ├── x-forwarded-proto-ignored.test │ │ ├── simple-response-headers.test │ │ └── bad-response-just-int.test ├── tests │ ├── Tests │ │ ├── paket.references │ │ └── TestConfig.fs │ └── TestUtils │ │ └── paket.references ├── static │ ├── favicon-32x32.png │ └── README.md ├── global.json ├── NuGet.Config ├── migrations │ ├── 20250805_152617_add_scripts_table.sql │ ├── 20251125_000000_add_account_name.sql │ ├── 20251119_000000_multi_branch_sync.sql │ └── 20250820_000000_rename_package_constants_to_values.sql └── .config │ └── dotnet-tools.json ├── .dockerignore ├── .github ├── FUNDING.yml └── dependabot.yml ├── user-code └── darklang │ └── scripts │ ├── add.dark │ ├── sample-for-testing-extension.dark │ └── test.dark ├── vscode-extension ├── .gitignore ├── static │ ├── logo-dark.png │ ├── logo-dark-transparent.svg │ └── logo-dark-transparent-low-margin.svg ├── client │ ├── tsconfig.json │ └── package.json ├── tsconfig.json ├── .vscodeignore └── README.md ├── CHANGELOG.md ├── packages ├── darklang │ ├── vscode │ │ └── README.md │ ├── stdlib │ │ ├── fun.dark │ │ ├── canvas.dark │ │ ├── cli │ │ │ ├── curl.dark │ │ │ ├── gunzip.dark │ │ │ └── process.dark │ │ ├── noModule.dark │ │ ├── x509.dark │ │ ├── bytes.dark │ │ ├── print.dark │ │ ├── uuid.dark │ │ ├── alt-json.dark │ │ └── bool.dark │ ├── dark-packages.dark │ ├── modelContextProtocol │ │ ├── aliases.dark │ │ └── serverBuilder │ │ │ ├── aliases.dark │ │ │ ├── logging.dark │ │ │ └── state.dark │ ├── languageServerProtocol │ │ ├── documentSync │ │ │ └── README.md │ │ ├── lifecycle │ │ │ ├── exit.dark │ │ │ ├── shutdown.dark │ │ │ └── initialized.dark │ │ ├── window │ │ │ ├── telemetry.dark │ │ │ └── logMessage.dark │ │ └── tracing.dark │ ├── prettyPrinter │ │ └── canvas.dark │ ├── cli │ │ ├── old_notes │ │ │ ├── README.md │ │ │ └── _msg.dark │ │ ├── clear.dark │ │ ├── quit.dark │ │ ├── installation │ │ │ ├── version.dark │ │ │ └── helpers.dark │ │ ├── packages │ │ │ └── display.dark │ │ └── experiments.dark │ ├── scm │ │ ├── branch.dark │ │ └── packageOps.dark │ └── languageTools │ │ ├── common.dark │ │ └── lsp-server │ │ ├── aaaa-state.dark │ │ ├── showDocument.dark │ │ ├── sync.dark │ │ └── switchBranch.dark └── feriel │ └── modelContextProtocol │ └── push-notification │ └── README.md ├── config ├── production └── local.template ├── canvases ├── dark-editor │ └── config.yml └── dark-packages │ └── config.yml ├── tree-sitter-darklang ├── test │ └── corpus │ │ ├── samples │ │ └── README.md │ │ ├── _template.txt │ │ ├── exhaustive │ │ └── exprs │ │ │ ├── units.txt │ │ │ ├── parens.txt │ │ │ ├── value.txt │ │ │ ├── char.txt │ │ │ ├── floats.txt │ │ │ └── binary_operation.txt │ │ └── README.md ├── binding.gyp ├── Cargo.toml └── package.json ├── scripts ├── build │ ├── clear-all-local-dbs │ ├── dotnet-fsi │ ├── clear-dotnet-build │ ├── dotnet-regen-fsi │ ├── clear-builder-volumes │ ├── regenerate-test-files │ ├── _dotnet-wrapper │ └── reload-packages ├── devcontainer │ ├── _write-config-file │ ├── _create-app-directories │ ├── _assert-in-container │ ├── _create-dark-dev-network │ ├── _setup-circleci-environment │ ├── _setup-hosts │ ├── _allow-docker-access │ ├── _create-cache-directories │ └── _vscode-post-start-command ├── run-parser-tests ├── linting │ ├── _check-linked-libs │ └── yamllinter ├── deployment │ ├── deploy-lock-all-clear │ ├── deploy-lock-all-list │ ├── deploy-lock-one-remove │ ├── deploy-lock-one-get-name │ ├── deploy-lock-one-add │ ├── _deploy-lock-request │ ├── _notify-deployment-rollbar │ ├── new-deploy.sh │ ├── new-build-containers.sh │ ├── deploy-lock-wait-and-acquire │ └── new-push-containers.sh ├── migrations │ ├── mark-not-run │ └── new ├── contributors │ └── checkout-pull-request ├── run-backend-datatests ├── stop-second-instance ├── run-prod-exec ├── run-local-exec ├── production │ └── gcp-get-logs ├── formatting │ └── pre-commit-hook.sh ├── run-pubsub-emulator ├── installers │ ├── install-exe-file │ └── install-gz-file └── package-extension.sh ├── tf ├── locals.tf ├── artifact_registry.tf ├── README.md ├── main.tf ├── apis.tf ├── vpc.tf └── custom-domains.tf ├── .style.yapf ├── docs ├── production │ ├── honeycomb.md │ ├── deployment.md │ ├── auditlogs.md │ └── emergency-login.md ├── benchmarking.md ├── unittests.md └── release.md ├── .gitattributes ├── .prettierrc.toml ├── .fantomasignore ├── LICENSES ├── .yamllint ├── containers ├── prodexec │ ├── README.md │ ├── run.sh │ └── Dockerfile ├── queueworker │ └── Dockerfile ├── cronchecker │ └── Dockerfile └── bwdserver │ └── Dockerfile ├── .vscode ├── extensions.json └── settings.json ├── .circleci └── gcp-workload-identity-config.json ├── .gitignore └── .claude └── settings.local.json /.prettierignore: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /rundir/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /backend/serialization/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | .git 2 | sqldump*.gz 3 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: darklang 2 | -------------------------------------------------------------------------------- /backend/src/LibTreeSitter/.gitignore: -------------------------------------------------------------------------------- 1 | lib -------------------------------------------------------------------------------- /backend/src/LibTreeSitter/paket.references: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /user-code/darklang/scripts/add.dark: -------------------------------------------------------------------------------- 1 | 1L + 2L -------------------------------------------------------------------------------- /backend/src/DvalReprDeveloper/paket.references: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /backend/testfiles/data/boring-text: -------------------------------------------------------------------------------- 1 | boring text -------------------------------------------------------------------------------- /vscode-extension/.gitignore: -------------------------------------------------------------------------------- 1 | out 2 | node_modules -------------------------------------------------------------------------------- /backend/src/ProdExec/paket.references: -------------------------------------------------------------------------------- 1 | FSharp.Core 2 | -------------------------------------------------------------------------------- /backend/src/QueueWorker/paket.references: -------------------------------------------------------------------------------- 1 | FSharp.Core -------------------------------------------------------------------------------- /backend/testfiles/data/.gitattributes: -------------------------------------------------------------------------------- 1 | ** binary 2 | -------------------------------------------------------------------------------- /backend/src/BwdServer/paket.references: -------------------------------------------------------------------------------- 1 | FSharp.Core 2 | -------------------------------------------------------------------------------- /backend/src/CronChecker/paket.references: -------------------------------------------------------------------------------- 1 | FSharp.Core 2 | -------------------------------------------------------------------------------- /backend/src/LibClientTypes/paket.references: -------------------------------------------------------------------------------- 1 | FSharp.Core -------------------------------------------------------------------------------- /backend/src/LibHttpMiddleware/paket.references: -------------------------------------------------------------------------------- 1 | FSharp.Core -------------------------------------------------------------------------------- /backend/src/LibPackageManager/InMemory/ProgramTypes.fs: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /backend/src/BuiltinCliHost/paket.references: -------------------------------------------------------------------------------- 1 | Ply 2 | FSharp.Core -------------------------------------------------------------------------------- /backend/src/LibBinarySerialization/paket.references: -------------------------------------------------------------------------------- 1 | FSharp.Core -------------------------------------------------------------------------------- /backend/src/LibCloudExecution/paket.references: -------------------------------------------------------------------------------- 1 | FSharp.Core 2 | -------------------------------------------------------------------------------- /backend/src/LibConfig/paket.references: -------------------------------------------------------------------------------- 1 | FSharp.Core 2 | FsRegex -------------------------------------------------------------------------------- /backend/src/LocalExec/paket.references: -------------------------------------------------------------------------------- 1 | FSharp.Core 2 | Legivel -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | See the [changelog](https://darklang.com/changelog) 2 | -------------------------------------------------------------------------------- /backend/src/BuiltinCloudExecution/paket.references: -------------------------------------------------------------------------------- 1 | FSharp.Core 2 | -------------------------------------------------------------------------------- /backend/src/BuiltinDarkInternal/paket.references: -------------------------------------------------------------------------------- 1 | FSharp.Core 2 | -------------------------------------------------------------------------------- /backend/src/LibClientTypesToCloudTypes/paket.references: -------------------------------------------------------------------------------- 1 | FSharp.Core -------------------------------------------------------------------------------- /packages/darklang/vscode/README.md: -------------------------------------------------------------------------------- 1 | Interacting with the VS Code API -------------------------------------------------------------------------------- /backend/src/BuiltinCli/paket.references: -------------------------------------------------------------------------------- 1 | Ply 2 | FSharp.Core 3 | FSharpPlus -------------------------------------------------------------------------------- /backend/src/BuiltinPM/paket.references: -------------------------------------------------------------------------------- 1 | Ply 2 | FSharp.Core 3 | FSharpPlus 4 | -------------------------------------------------------------------------------- /config/production: -------------------------------------------------------------------------------- 1 | Production config values are set in tf/service_env_vars.tf -------------------------------------------------------------------------------- /backend/src/LibDB/paket.references: -------------------------------------------------------------------------------- 1 | FSharp.Core 2 | Microsoft.Data.Sqlite 3 | Fumble -------------------------------------------------------------------------------- /backend/src/LibPackageManager/paket.references: -------------------------------------------------------------------------------- 1 | FSharp.Core 2 | Microsoft.Data.Sqlite -------------------------------------------------------------------------------- /backend/src/Wasm/paket.references: -------------------------------------------------------------------------------- 1 | FSharp.Core 2 | Microsoft.AspNetCore.Components.WebAssembly -------------------------------------------------------------------------------- /canvases/dark-editor/config.yml: -------------------------------------------------------------------------------- 1 | id: 11111111-1111-1111-1111-111111111113 2 | main: main 3 | -------------------------------------------------------------------------------- /canvases/dark-packages/config.yml: -------------------------------------------------------------------------------- 1 | id: 11111111-1111-1111-1111-111111111112 2 | main: main 3 | -------------------------------------------------------------------------------- /backend/src/BuiltinExecution/paket.references: -------------------------------------------------------------------------------- 1 | Ply 2 | FSharp.Core 3 | FSharpPlus 4 | Sodium.Core -------------------------------------------------------------------------------- /backend/src/LibExecution/paket.references: -------------------------------------------------------------------------------- 1 | Ply 2 | FSharp.Core 3 | FSharpPlus 4 | System.IO.Hashing -------------------------------------------------------------------------------- /tree-sitter-darklang/test/corpus/samples/README.md: -------------------------------------------------------------------------------- 1 | A few sample dark programs for reference. 2 | -------------------------------------------------------------------------------- /backend/src/Cli/paket.references: -------------------------------------------------------------------------------- 1 | Ply 2 | FSharp.Core 3 | FSharpPlus 4 | Microsoft.Data.Sqlite 5 | Fumble -------------------------------------------------------------------------------- /backend/src/LibParser/paket.references: -------------------------------------------------------------------------------- 1 | Expecto 2 | FSharp.Compiler.Service 3 | NReco.Logging.File 4 | -------------------------------------------------------------------------------- /backend/tests/Tests/paket.references: -------------------------------------------------------------------------------- 1 | FSharp.Core 2 | FsRegEx 3 | Expecto 4 | SimpleBase 5 | Fumble 6 | -------------------------------------------------------------------------------- /backend/src/Wasm/Program.fs: -------------------------------------------------------------------------------- 1 | namespace Wasm 2 | 3 | #nowarn "988" // "main module of Program is empty" 4 | -------------------------------------------------------------------------------- /scripts/build/clear-all-local-dbs: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | docker volume rm pgdata pgconf pglogs 4 | -------------------------------------------------------------------------------- /backend/static/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darklang/dark/HEAD/backend/static/favicon-32x32.png -------------------------------------------------------------------------------- /backend/tests/TestUtils/paket.references: -------------------------------------------------------------------------------- 1 | Expecto 2 | FSharp.Compiler.Service 3 | NReco.Logging.File 4 | FsRegEx -------------------------------------------------------------------------------- /packages/darklang/stdlib/fun.dark: -------------------------------------------------------------------------------- 1 | module Darklang.Stdlib.Fun 2 | 3 | 4 | let identity (value: 'a) : 'a = value -------------------------------------------------------------------------------- /tf/locals.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | project_name = "darklang-next" 3 | project_id = "234768451432" 4 | } 5 | 6 | -------------------------------------------------------------------------------- /backend/global.json: -------------------------------------------------------------------------------- 1 | { 2 | "sdk": { 3 | "version": "8.0.303", 4 | "rollForward": "disable" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /backend/src/BuiltinDarkInternal/README.md: -------------------------------------------------------------------------------- 1 | # BuiltinDarkInternal 2 | 3 | Builtin functions to be used by `dark-editor`. -------------------------------------------------------------------------------- /backend/src/LibService/README.md: -------------------------------------------------------------------------------- 1 | # LibService 2 | 3 | Common utilities for running an F# service as part of Dark 4 | -------------------------------------------------------------------------------- /vscode-extension/static/logo-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darklang/dark/HEAD/vscode-extension/static/logo-dark.png -------------------------------------------------------------------------------- /backend/testfiles/data/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darklang/dark/HEAD/backend/testfiles/data/favicon-32x32.png -------------------------------------------------------------------------------- /backend/serialization/vanilla_Microsoft-FSharp-Collections-FSharpMap-2-System-String-System-String-_baseline.json: -------------------------------------------------------------------------------- 1 | { 2 | "a": "b" 3 | } -------------------------------------------------------------------------------- /.style.yapf: -------------------------------------------------------------------------------- 1 | [style] 2 | based_on_style = pep8 3 | indent_width = 2 4 | blank_lines_around_top_level_definition = 2 5 | column_limit = 85 6 | -------------------------------------------------------------------------------- /backend/serialization/oplist-binary-latest.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darklang/dark/HEAD/backend/serialization/oplist-binary-latest.bin -------------------------------------------------------------------------------- /backend/src/LocalExec/README.md: -------------------------------------------------------------------------------- 1 | # Local Execution host 2 | 3 | Run local scripts with Backend and special F#/dotnet libraries enabled. 4 | -------------------------------------------------------------------------------- /backend/testfiles/data/sample_image_bytes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darklang/dark/HEAD/backend/testfiles/data/sample_image_bytes.png -------------------------------------------------------------------------------- /backend/serialization/toplevels-binary-latest.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darklang/dark/HEAD/backend/serialization/toplevels-binary-latest.bin -------------------------------------------------------------------------------- /backend/NuGet.Config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /docs/production/honeycomb.md: -------------------------------------------------------------------------------- 1 | # Honeycomb notes 2 | 3 | API key is stored in a secret: 4 | https://ui.honeycomb.io/teams/dark/dataset_instructions 5 | -------------------------------------------------------------------------------- /user-code/darklang/scripts/sample-for-testing-extension.dark: -------------------------------------------------------------------------------- 1 | type ID = Int64 2 | let double (i: Int64) : Int64 = i + i 3 | type MyFloat = Float 4 | 1.5 -------------------------------------------------------------------------------- /backend/testfiles/execution/language/basic/evariable.dark: -------------------------------------------------------------------------------- 1 | (let x = 5L in x) = 5L 2 | 3 | myvar = (Builtin.testDerrorMessage "There is no variable named: myvar") -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *Paket.Restore.targets eol=crlf 2 | 3 | backend/serialization/*.bin linguist-generated=true 4 | backend/serialization/*.json linguist-generated=true 5 | -------------------------------------------------------------------------------- /backend/src/Prelude/paket.references: -------------------------------------------------------------------------------- 1 | Ply 2 | FSharp.Core 3 | FSharpX.Extras 4 | FSharpPlus 5 | FSharp.SystemTextJson 6 | NodaTime 7 | NodaTime.Serialization.SystemTextJson -------------------------------------------------------------------------------- /scripts/devcontainer/_write-config-file: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -euo pipefail 4 | 5 | env | grep DARK_CONFIG | sed 's/^/export /' >> /home/dark/.bashrc 6 | 7 | -------------------------------------------------------------------------------- /backend/src/Prelude/Option.fs: -------------------------------------------------------------------------------- 1 | module Option 2 | 3 | let unwrap (default' : 'a) (t : Option<'a>) : 'a = 4 | match t with 5 | | None -> default' 6 | | Some value -> value 7 | -------------------------------------------------------------------------------- /.prettierrc.toml: -------------------------------------------------------------------------------- 1 | tabWidth = 2 2 | printWidth = 80 3 | useTabs = false 4 | semi = true 5 | singleQuote = false 6 | trailingComma = "all" 7 | arrowParens = "avoid" 8 | endOfLine = "lf" 9 | -------------------------------------------------------------------------------- /backend/src/LibCloud/README.md: -------------------------------------------------------------------------------- 1 | # LibCloud 2 | 3 | The core of the backend application around Dark's Cloud runtime. 4 | Everything from user management, web servers, logging, DBs, etc. 5 | -------------------------------------------------------------------------------- /backend/src/Prelude/Lazy.fs: -------------------------------------------------------------------------------- 1 | module Lazy 2 | 3 | let inline force (l : Lazy<_>) = l.Force() 4 | let map f l = lazy ((f << force) l) 5 | let bind f l = lazy ((force << f << force) l) 6 | -------------------------------------------------------------------------------- /backend/testfiles/execution/language/basic/dfloat.dark: -------------------------------------------------------------------------------- 1 | Builtin.testNan != Builtin.testNan = true 2 | Stdlib.Float.multiply -0.0 -1.0 = 0.0 3 | Stdlib.Float.multiply 6.02e22 -10.0 = -6.02e23 -------------------------------------------------------------------------------- /scripts/build/dotnet-fsi: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | . ./scripts/devcontainer/_assert-in-container "$0" "$@" 3 | 4 | cd backend && dotnet fsi --use:../scripts/build/fsi-setup.fsx "${@}" 5 | -------------------------------------------------------------------------------- /backend/serialization/vanilla_LibCloud-Queue-NotificationData_simple.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "31d72f73-0f99-5a9b-949c-b95705ae7c4d", 3 | "canvasID": "31d72f73-0f99-5a9b-949c-b95705ae7c4d" 4 | } -------------------------------------------------------------------------------- /backend/src/CronChecker/README.md: -------------------------------------------------------------------------------- 1 | # CronChecker 2 | 3 | Trigger events for scheduled work. 4 | 5 | Until killed, checks for crons that need corresponding jobs enqueued, and enqueues them. 6 | -------------------------------------------------------------------------------- /scripts/run-parser-tests: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | . ./scripts/devcontainer/_assert-in-container "$0" "$@" 3 | 4 | set -euo pipefail 5 | 6 | cd tree-sitter-darklang 7 | npm run test 8 | cd .. -------------------------------------------------------------------------------- /backend/src/LibCloud/paket.references: -------------------------------------------------------------------------------- 1 | FSharp.Core 2 | PusherServer 3 | System.Diagnostics.DiagnosticSource 4 | FsRegEx 5 | Sodium.Core 6 | Google.Cloud.PubSub.V1 7 | Microsoft.Data.Sqlite 8 | Fumble -------------------------------------------------------------------------------- /backend/migrations/20250805_152617_add_scripts_table.sql: -------------------------------------------------------------------------------- 1 | -- Scripts 2 | CREATE TABLE IF NOT EXISTS 3 | scripts_v0 4 | ( id TEXT PRIMARY KEY 5 | , name TEXT NOT NULL UNIQUE 6 | , text TEXT NOT NULL 7 | ); -------------------------------------------------------------------------------- /packages/darklang/stdlib/canvas.dark: -------------------------------------------------------------------------------- 1 | module Darklang.Canvas 2 | 3 | 4 | type Program = 5 | { dbs: List 6 | handlers: List } -------------------------------------------------------------------------------- /tree-sitter-darklang/test/corpus/_template.txt: -------------------------------------------------------------------------------- 1 | ================== 2 | [TODO name this test] 3 | ================== 4 | 5 | true 6 | 7 | --- 8 | 9 | (source_file (expression (simple_expression (bool_literal)))) -------------------------------------------------------------------------------- /backend/src/BuiltinCloudExecution/README.md: -------------------------------------------------------------------------------- 1 | # BuiltinCloudExecution 2 | 3 | Standard library functions which need to be run on the backend, 4 | due to their connection to the DB, queues, external internet, etc. 5 | -------------------------------------------------------------------------------- /packages/darklang/dark-packages.dark: -------------------------------------------------------------------------------- 1 | /// some types that correspond to the `dark-packages` canvas 2 | module Darklang.DarkPackages 3 | 4 | type Stats = 5 | { types: Int64 6 | values: Int64 7 | fns: Int64 } -------------------------------------------------------------------------------- /tree-sitter-darklang/test/corpus/exhaustive/exprs/units.txt: -------------------------------------------------------------------------------- 1 | ================== 2 | unit 3 | ================== 4 | 5 | () 6 | 7 | --- 8 | 9 | (source_file (expression (simple_expression (unit)))) 10 | 11 | -------------------------------------------------------------------------------- /backend/testfiles/execution/cloud/_middleware.dark: -------------------------------------------------------------------------------- 1 | Http.parseQueryString_v0 "https://darklang.com?x=6&x=7&myString=todo" = 2 | Dict { x = "7"; myString = "todo" } 3 | 4 | Http.parseHeaders_v0 "x: y" = Dict { x = "y" } -------------------------------------------------------------------------------- /packages/darklang/modelContextProtocol/aliases.dark: -------------------------------------------------------------------------------- 1 | // Common type aliases for Model Context Protocol modules 2 | module Darklang.ModelContextProtocol 3 | 4 | // 5 | type Json = Stdlib.AltJson.Json 6 | // -------------------------------------------------------------------------------- /docs/production/deployment.md: -------------------------------------------------------------------------------- 1 | # Deploying 2 | 3 | The backend is built into libraries and binaries that use those libraries. We 4 | build containers that use those binaries, then deploy services that use those 5 | containers. 6 | -------------------------------------------------------------------------------- /scripts/linting/_check-linked-libs: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -euo pipefail 4 | 5 | missing=$(ldd $1) 6 | 7 | if (echo "$missing" | grep not.found) ; then 8 | echo "Missing libraries!" 9 | exit 1 10 | fi 11 | -------------------------------------------------------------------------------- /tf/artifact_registry.tf: -------------------------------------------------------------------------------- 1 | resource "google_artifact_registry_repository" "production-containers" { 2 | location = "us-central1" 3 | repository_id = "production-containers" 4 | format = "DOCKER" 5 | timeouts {} 6 | } 7 | -------------------------------------------------------------------------------- /scripts/build/clear-dotnet-build: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | . ./scripts/devcontainer/_assert-in-container "$0" "$@" 3 | 4 | set -uo pipefail 5 | 6 | rm -Rf backend/Build/* 7 | echo "To build backend/ from scratch, save backend/global.json" 8 | -------------------------------------------------------------------------------- /backend/serialization/vanilla_System-Tuple-2-System-Guid-Microsoft-FSharp-Collections-FSharpList-1-System-UInt64-_simple.json: -------------------------------------------------------------------------------- 1 | [ 2 | "31d72f73-0f99-5a9b-949c-b95705ae7c4d", 3 | [ 4 | 1, 5 | 0, 6 | "18446744073709551615" 7 | ] 8 | ] -------------------------------------------------------------------------------- /backend/serialization/vanilla_System-Tuple-5-System-String-System-String-System-String-NodaTime-Instant-System-Guid-_simple.json: -------------------------------------------------------------------------------- 1 | [ 2 | "HTTP", 3 | "/", 4 | "GET", 5 | "2022-07-04T17:46:57Z", 6 | "31d72f73-0f99-5a9b-949c-b95705ae7c4d" 7 | ] -------------------------------------------------------------------------------- /backend/src/BuiltinDarkInternal/Helpers/Permissions.fs: -------------------------------------------------------------------------------- 1 | /// Helpers for permissions in Dark canvases 2 | module BuiltinDarkInternal.Helpers.Permissions 3 | 4 | open System.Threading.Tasks 5 | 6 | open Prelude 7 | open LibExecution.RuntimeTypes 8 | -------------------------------------------------------------------------------- /.fantomasignore: -------------------------------------------------------------------------------- 1 | # All .dark files 2 | # (in ./.vscode/* somewhere, we tell VS Code that .dark files are F# files, 3 | # so we get syntax highlighting of the near-equivalent syntax (for now), 4 | # but we don't want Fantomas to format them) 5 | *.dark -------------------------------------------------------------------------------- /scripts/deployment/deploy-lock-all-clear: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | . ./scripts/devcontainer/_assert-in-container "$0" "$@" 3 | 4 | # Clear all deploy locks 5 | 6 | set -euo pipefail 7 | 8 | ./scripts/deployment/_deploy-lock-request / DELETE | jq -r . -------------------------------------------------------------------------------- /scripts/devcontainer/_create-app-directories: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -euo pipefail 4 | 5 | RUNDIR="${DARK_CONFIG_RUNDIR}" 6 | mkdir -p "$RUNDIR" 7 | mkdir -p "$RUNDIR/logs" 8 | mkdir -p "$RUNDIR/completed_tests" 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /backend/src/LibCloudExecution/README.md: -------------------------------------------------------------------------------- 1 | # LibCloudExecution 2 | 3 | Pulls together the StdLibs and LibExecution and LibCloud to provide the actual 4 | context we use to execute Dark code in the various Cloud binaries (BwdServer, 5 | QueueWorker, etc). 6 | -------------------------------------------------------------------------------- /scripts/deployment/deploy-lock-all-list: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | . ./scripts/devcontainer/_assert-in-container "$0" "$@" 3 | 4 | # List the deploy locks currently held 5 | 6 | set -euo pipefail 7 | 8 | ./scripts/deployment/_deploy-lock-request / GET | jq -r .[] -------------------------------------------------------------------------------- /tf/README.md: -------------------------------------------------------------------------------- 1 | # Terraform config 2 | 3 | This is our production terraform. The intent is that this matches the configuration 4 | of everything in our production deployment. 5 | 6 | Note that darklang-classic is in a different project, see classic-tf directory. -------------------------------------------------------------------------------- /backend/src/QueueWorker/README.md: -------------------------------------------------------------------------------- 1 | # QueueWorker 2 | 3 | Supports user "Workers"/Queues -- users emit events (using `emit`), 4 | and this pulls those events from the queue and runs them. 5 | 6 | The event algorithm in described in [eventsv2.md](/docs/eventsV2.md) 7 | -------------------------------------------------------------------------------- /scripts/devcontainer/_assert-in-container: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # do not set -euo pipefail here, that's the responsibility of the scripts 4 | 5 | if [[ ! -v IN_DEV_CONTAINER ]]; then 6 | scripts/run-in-docker "${@}" 7 | exit $? 8 | fi 9 | 10 | -------------------------------------------------------------------------------- /scripts/migrations/mark-not-run: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | . ./scripts/devcontainer/_assert-in-container "$0" "$@" 4 | 5 | NAME=$1 6 | 7 | psql -d "$DARK_CONFIG_DB_DBNAME" -c "DELETE FROM system_migrations_v0 WHERE name LIKE '%$NAME.sql';" 8 | -------------------------------------------------------------------------------- /backend/migrations/20251125_000000_add_account_name.sql: -------------------------------------------------------------------------------- 1 | -- Add name column to accounts_v0 2 | ALTER TABLE accounts_v0 ADD COLUMN name TEXT NOT NULL DEFAULT ''; 3 | -- Enforce unique usernames 4 | CREATE UNIQUE INDEX IF NOT EXISTS accounts_name_unique ON accounts_v0(name); 5 | -------------------------------------------------------------------------------- /packages/darklang/stdlib/cli/curl.dark: -------------------------------------------------------------------------------- 1 | module Darklang.Stdlib.Cli.Curl 2 | 3 | let downloadFileToPath 4 | (url: String) 5 | (downloadTo: String) 6 | : Stdlib.Result.Result = 7 | Stdlib.Cli.executeWithUnitOrStdErr $"curl -s -L -o \"{downloadTo}\" {url}" -------------------------------------------------------------------------------- /backend/src/LibHttpMiddleware/README.md: -------------------------------------------------------------------------------- 1 | # LibHttpMiddleware 2 | 3 | HTTP Middleware modules to support BwdServer. 4 | 5 | Designed to move as much of the HTTP framework into "user space" 6 | (or something which could potentially be user space in the future) 7 | as possible. 8 | -------------------------------------------------------------------------------- /user-code/darklang/scripts/test.dark: -------------------------------------------------------------------------------- 1 | val myList = [1L; 2L; 3L] 2 | val recordVal = 3 | Stdlib.Cli.Host.Host { 4 | os = Stdlib.Cli.OS.OS.Linux; 5 | architecture = Stdlib.Cli.Architecture.Architecture.X86_64; 6 | defaultShell = Stdlib.Cli.Shell.Shell.Bash 7 | } 8 | -------------------------------------------------------------------------------- /backend/src/DvalReprDeveloper/README.md: -------------------------------------------------------------------------------- 1 | # DvalReprDeveloper 2 | 3 | Various stringifications from `LibExecution.RuntimeTypes`. 4 | 5 | CLEANUP all of this should be replacd with Darklang code eventually. 6 | It's just a bit cumbersome to do that migration, so punting it for later. 7 | -------------------------------------------------------------------------------- /scripts/migrations/new: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | . ./scripts/devcontainer/_assert-in-container "$0" "$@" 4 | 5 | NAME=$1 6 | DATE=$(date -u +%Y%m%d_%H%M%S) 7 | 8 | FILE=backend/migrations/${DATE}_${NAME}.sql 9 | touch "${FILE}" 10 | git add "${FILE}" 11 | -------------------------------------------------------------------------------- /backend/testfiles/execution/language/collections/edict.dark: -------------------------------------------------------------------------------- 1 | ((Dict { a = 5L }) |> Stdlib.Dict.get "a") = Stdlib.Option.Option.Some 5L 2 | 3 | ((Dict { ___ = 5L }) |> Stdlib.Dict.get "") = Stdlib.Option.Option.Some 5L 4 | 5 | ((Dict { a = 5L }) |> Stdlib.Dict.get "b") = Stdlib.Option.Option.None -------------------------------------------------------------------------------- /LICENSES: -------------------------------------------------------------------------------- 1 | For a license for this repo, see the LICENSE.md file. 2 | 3 | The standard library is, in part, based on the Elm Standard Library, which 4 | is Copyright 2014-present Evan Czaplicki under the BSD 3-Clause "New" or 5 | "Revised" License. See https://github.com/elm/core/blob/1.0.5/LICENSE. 6 | -------------------------------------------------------------------------------- /backend/src/LibConfig/README.md: -------------------------------------------------------------------------------- 1 | # LibConfig 2 | 3 | Most config values are only useful for _services_, and are thus in LibService, 4 | but a few adjustable things are also relevant to our CLI app (along with, future, 5 | our WASM platform), so those values go here. All of these should have defaults. -------------------------------------------------------------------------------- /scripts/build/dotnet-regen-fsi: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | . ./scripts/devcontainer/_assert-in-container "$0" "$@" 3 | 4 | set -euo pipefail 5 | 6 | cd backend 7 | rm -f paket-files/paket.restore.cached 8 | dotnet restore fsdark.sln 9 | dotnet paket generate-load-scripts --framework net8.0 10 | -------------------------------------------------------------------------------- /tree-sitter-darklang/test/corpus/README.md: -------------------------------------------------------------------------------- 1 | Run these with `npm run test` in the `tree-sitter-darklang` directory. 2 | 3 | --- 4 | 5 | Note: 6 | 7 | It's a `tree-sitter`-ism to keep these test files either in `./corpus` or `./test/corpus`. 8 | I settled on following this, and choosing the latter. 9 | -------------------------------------------------------------------------------- /backend/serialization/vanilla_LibService-Rollbar-HoneycombJson_simple.json: -------------------------------------------------------------------------------- 1 | { 2 | "filters": [ 3 | { 4 | "column": "trace.trace_id", 5 | "op": "=", 6 | "value": "31d72f73-0f99-5a9b-949c-b95705ae7c4d" 7 | } 8 | ], 9 | "limit": 100, 10 | "time_range": 604800 11 | } -------------------------------------------------------------------------------- /backend/src/Prelude/README.md: -------------------------------------------------------------------------------- 1 | # Prelude 2 | 3 | To use, `open Prelude` in other modules. 4 | 5 | This adds missing methods and functions to existing standard modules, much like FSharpPlus. 6 | 7 | Eventually this might become the thing we open in all files, encompassing FSharpPlus and others. 8 | 9 | -------------------------------------------------------------------------------- /packages/darklang/stdlib/noModule.dark: -------------------------------------------------------------------------------- 1 | module Darklang.Stdlib 2 | 3 | 4 | /// Returns true if the two value are equal 5 | let equals (a: 'a) (b: 'a) : Bool = Builtin.equals a b 6 | 7 | 8 | /// Returns true if the two value are not equal 9 | let notEquals (a: 'a) (b: 'a) : Bool = Builtin.notEquals a b -------------------------------------------------------------------------------- /packages/darklang/stdlib/x509.dark: -------------------------------------------------------------------------------- 1 | module Darklang.Stdlib.X509 2 | 3 | /// Extract the public key from a PEM encoded certificate and return the key in PEM format. 4 | let pemCertificatePublicKey 5 | (cert: String) 6 | : Stdlib.Result.Result = 7 | Builtin.x509PemCertificatePublicKey cert -------------------------------------------------------------------------------- /vscode-extension/client/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "es2020", 5 | "lib": ["es2020"], 6 | "outDir": "out", 7 | "rootDir": "src", 8 | "sourceMap": true 9 | }, 10 | "include": ["src"], 11 | "exclude": ["node_modules"] 12 | } 13 | -------------------------------------------------------------------------------- /packages/darklang/stdlib/bytes.dark: -------------------------------------------------------------------------------- 1 | module Darklang.Stdlib.Bytes 2 | 3 | 4 | /// Hex (Base16) encodes using an uppercase alphabet. Complies 5 | /// with [RFC 4648 section 8](https://www.rfc-editor.org/rfc/rfc4648.html#section-8 6 | let hexEncode (bytes: List) : String = Builtin.bytesHexEncode bytes -------------------------------------------------------------------------------- /packages/darklang/languageServerProtocol/documentSync/README.md: -------------------------------------------------------------------------------- 1 | Note: there's a clear line drawn in the spec between: 2 | 3 | - text documents (what you normally work with) 4 | - notebooks (those interactive things like "IPython notebooks") 5 | 6 | There's overlap between the two, but they're generally different things here. 7 | -------------------------------------------------------------------------------- /packages/darklang/stdlib/print.dark: -------------------------------------------------------------------------------- 1 | module Darklang.Stdlib 2 | 3 | 4 | let print (str: String): Unit = 5 | Builtin.print str 6 | 7 | 8 | let printLine (str: String): Unit = 9 | Builtin.printLine str 10 | 11 | 12 | let printLines (lines: List): Unit = 13 | lines 14 | |> List.iter(fun l -> printLine l) 15 | -------------------------------------------------------------------------------- /scripts/devcontainer/_create-dark-dev-network: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -euo pipefail 4 | 5 | NETWORK=dark-dev-net 6 | 7 | EXISTING=$(docker network ls --filter name=$NETWORK -q) 8 | if [[ $EXISTING == "" ]]; then 9 | echo "Creating docker network $NETWORK" 10 | docker network create $NETWORK 11 | fi 12 | 13 | -------------------------------------------------------------------------------- /backend/migrations/20251119_000000_multi_branch_sync.sql: -------------------------------------------------------------------------------- 1 | 2 | ALTER TABLE package_ops ADD COLUMN instance_id TEXT NULL 3 | REFERENCES instances(id) ON DELETE SET NULL; 4 | 5 | -- Create index for querying ops by instance 6 | CREATE INDEX IF NOT EXISTS idx_package_ops_instance 7 | ON package_ops(instance_id) WHERE instance_id IS NOT NULL; 8 | -------------------------------------------------------------------------------- /backend/tests/Tests/TestConfig.fs: -------------------------------------------------------------------------------- 1 | module Tests.TestConfig 2 | 3 | open LibConfig.ConfigDsl 4 | 5 | // testing 6 | let bwdServerBackendPort = int "DARK_CONFIG_TEST_BWDSERVER_BACKEND_PORT" 7 | let bwdServerKubernetesPort = int "DARK_CONFIG_TEST_BWDSERVER_KUBERNETES_PORT" 8 | let httpClientPort = int "DARK_CONFIG_TEST_HTTPCLIENT_SERVER_PORT" 9 | -------------------------------------------------------------------------------- /scripts/deployment/deploy-lock-one-remove: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | . ./scripts/devcontainer/_assert-in-container "$0" "$@" 3 | 4 | # Remove the deploy lock for this commit 5 | 6 | set -euo pipefail 7 | 8 | LOCKNAME=$(./scripts/deployment/deploy-lock-one-get-name) 9 | 10 | ./scripts/deployment/_deploy-lock-request "/${LOCKNAME}" DELETE | jq -r . -------------------------------------------------------------------------------- /vscode-extension/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "es2020", 5 | "lib": ["es2020"], 6 | "outDir": "out", 7 | "rootDir": "src", 8 | "sourceMap": true 9 | }, 10 | "include": ["src"], 11 | "exclude": ["node_modules"], 12 | "references": [ 13 | { "path": "./client" }, 14 | ] 15 | } -------------------------------------------------------------------------------- /backend/.config/dotnet-tools.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "isRoot": true, 4 | "tools": { 5 | "paket": { 6 | "version": "9.0.2", 7 | "commands": [ 8 | "paket" 9 | ] 10 | }, 11 | "dotnet-trace": { 12 | "version": "9.0.553101", 13 | "commands": [ 14 | "dotnet-trace" 15 | ] 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /packages/darklang/prettyPrinter/canvas.dark: -------------------------------------------------------------------------------- 1 | // TODO: 2 | //alias PT = LanguageTools.ProgramTypes 3 | //alias RT = Stdlib.RuntimeTypes 4 | 5 | module Darklang.PrettyPrinter 6 | 7 | let canvas (c: Canvas.Program) : String = 8 | // need to look at the names of all types and functions, 9 | // and figure out module tree before actually printing stuff 10 | "TODO" -------------------------------------------------------------------------------- /scripts/deployment/deploy-lock-one-get-name: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | . ./scripts/devcontainer/_assert-in-container "$0" "$@" 3 | 4 | # Returns the name of the lock-file for this commit 5 | 6 | COMMIT=$(git rev-parse --short HEAD) 7 | TIMESTAMP=$(git show -s --format=%at HEAD) 8 | LOCKFILE_ID="${TIMESTAMP}-${COMMIT}" 9 | 10 | echo "deploy-lock-${LOCKFILE_ID}" 11 | -------------------------------------------------------------------------------- /backend/src/Wasm/Builtin.fs: -------------------------------------------------------------------------------- 1 | module Wasm.Builtin 2 | 3 | open Prelude 4 | open LibExecution.RuntimeTypes 5 | 6 | module Builtin = LibExecution.Builtin 7 | 8 | 9 | let fnRenames : Builtin.FnRenames = 10 | // old names, new names 11 | // eg: fn "Http" "respond" 0, fn "Http" "response" 0 12 | [] 13 | 14 | let builtins = Builtin.combine [ Libs.Editor.builtins ] fnRenames 15 | -------------------------------------------------------------------------------- /backend/testfiles/execution/cli/file.tests: -------------------------------------------------------------------------------- 1 | module Write = 2 | let testContents () : String = "Test conrents" 3 | 4 | (let filename = (Builtin.File.createTemp ()) |> Builtin.unwrap 5 | do (testContents ()) |> Stdlib.String.toBytes |> (Builtin.File.write filename) 6 | Builtin.fileRead filename) = Stdlib.Result.Result.Ok( 7 | (testContents ()) |> Stdlib.String.toBytes 8 | ) -------------------------------------------------------------------------------- /scripts/devcontainer/_setup-circleci-environment: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -euo pipefail 4 | 5 | # build-server does an equivalent to this in the dev environment 6 | 7 | ENV_FILE="config/circleci" 8 | echo "Using env: $ENV_FILE" 9 | 10 | grep DARK_CONFIG "$ENV_FILE" | sed 's/^\(DARK_CONFIG_[A-Z_]*\)=\(.*\)$/if [[ ! -v \1 ]]; then export \1=\2; fi/' >> "${BASH_ENV}" 11 | -------------------------------------------------------------------------------- /packages/darklang/cli/old_notes/README.md: -------------------------------------------------------------------------------- 1 | This is a collection of old code/notes that I've had stashed locally. 2 | 3 | I'd like to get them off of my computer, so committing them here for now 4 | as a gentle reminder long-term to revisit and reduce. 5 | 6 | (I think there are things in these remaining files that would be worth 7 | reincorporating in some fashion but it's not high-priority to.) -------------------------------------------------------------------------------- /backend/testfiles/httpclient/v0/todo/readme.md: -------------------------------------------------------------------------------- 1 | These test files were copied over from `httpclient/v5` tests. I've been reviewing 2 | these one by one, seeing if they make sense to test against the HttpClient. 3 | 4 | Many of these may be best to just ignore/delete - for example, query params are 5 | input differently now (just as part of the URL) and likely don't need so many 6 | test files. 7 | -------------------------------------------------------------------------------- /tf/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.6.0" 3 | 4 | required_providers { 5 | google = "5.4.0" 6 | } 7 | } 8 | 9 | provider "google" { 10 | project = local.project_name 11 | region = "us-central1" 12 | } 13 | 14 | terraform { 15 | cloud { 16 | organization = "darklang" 17 | 18 | workspaces { 19 | name = "darklang" 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /backend/src/BuiltinCliHost/Builtin.fs: -------------------------------------------------------------------------------- 1 | module BuiltinCliHost.Builtin 2 | 3 | open Prelude 4 | open LibExecution.RuntimeTypes 5 | 6 | module Builtin = LibExecution.Builtin 7 | 8 | 9 | let fnRenames : Builtin.FnRenames = 10 | // old names, new names 11 | // eg: fn "Http" "respond" 0, fn "Http" "response" 0 12 | [] 13 | 14 | let builtins = Builtin.combine [ Libs.Cli.builtins ] fnRenames 15 | -------------------------------------------------------------------------------- /tree-sitter-darklang/test/corpus/exhaustive/exprs/parens.txt: -------------------------------------------------------------------------------- 1 | ================== 2 | parens expr 3 | ================== 4 | 5 | (true) 6 | 7 | --- 8 | 9 | (source_file 10 | (expression 11 | (simple_expression 12 | (paren_expression 13 | (symbol) 14 | (expression (simple_expression (bool_literal))) 15 | (symbol) 16 | ) 17 | ) 18 | ) 19 | ) 20 | 21 | -------------------------------------------------------------------------------- /tree-sitter-darklang/test/corpus/exhaustive/exprs/value.txt: -------------------------------------------------------------------------------- 1 | ================== 2 | package value 3 | ================== 4 | 5 | Stdlib.List.empty 6 | 7 | --- 8 | 9 | (source_file 10 | (expression 11 | (simple_expression 12 | (qualified_val_or_fn_name 13 | (module_identifier) (symbol) (module_identifier) (symbol) (value_or_fn_identifier) 14 | ) 15 | ) 16 | ) 17 | ) -------------------------------------------------------------------------------- /scripts/devcontainer/_setup-hosts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -euo pipefail 4 | 5 | echo 127.0.0.1 dlio.localhost | sudo tee -a /etc/hosts 6 | echo 127.0.0.1 test.dlio.localhost | sudo tee -a /etc/hosts 7 | echo 127.0.0.1 dark-editor.dlio.localhost | sudo tee -a /etc/hosts 8 | echo 127.0.0.1 dark-repl.dlio.localhost | sudo tee -a /etc/hosts 9 | echo 127.0.0.1 dark-packages.dlio.localhost | sudo tee -a /etc/hosts -------------------------------------------------------------------------------- /scripts/contributors/checkout-pull-request: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | 4 | # Not running in container cause git creds aren't in there 5 | 6 | NUM=$1 7 | LOCAL_BRANCH="pr-${NUM}" 8 | REMOTE_BRANCH="pull/${NUM}/head" 9 | 10 | 11 | git checkout main 12 | 13 | git branch -D "pr-${NUM}" || true 14 | git fetch origin "${REMOTE_BRANCH}:${LOCAL_BRANCH}" 15 | git checkout "${LOCAL_BRANCH}" 16 | 17 | -------------------------------------------------------------------------------- /scripts/deployment/deploy-lock-one-add: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | . ./scripts/devcontainer/_assert-in-container "$0" "$@" 3 | 4 | # Add a lock identifier for the commit and the timestamp. 5 | 6 | set -euo pipefail 7 | 8 | LOCKNAME=$(./scripts/deployment/deploy-lock-one-get-name) 9 | 10 | echo "Adding lock file with name ${LOCKNAME}" 11 | 12 | ./scripts/deployment/_deploy-lock-request "/${LOCKNAME}" POST | jq -r . -------------------------------------------------------------------------------- /scripts/build/clear-builder-volumes: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -euo pipefail 4 | 5 | # Clear all the volumes that we use 6 | 7 | # Must be run from outside (stopped) container 8 | 9 | docker volume rm \ 10 | dark_build \ 11 | dark_nuget \ 12 | tree-sitter-build \ 13 | tree-sitter-node-modules \ 14 | darklang-dark-extension-volume \ 15 | darklang-dark-extension-volume-insiders \ 16 | vscode 17 | -------------------------------------------------------------------------------- /packages/darklang/stdlib/cli/gunzip.dark: -------------------------------------------------------------------------------- 1 | /// Functions to interact with the `gunzip` CLI application 2 | module Darklang.Stdlib.Cli.Gunzip 3 | 4 | 5 | /// Unzip a file to a specified destination path 6 | let unzipToFile 7 | (inputFilePath: String) 8 | (outputFilePath: String) 9 | : Stdlib.Result.Result = 10 | Stdlib.Cli.executeWithUnitOrStdErr 11 | $"gunzip -c {inputFilePath} > {outputFilePath}" -------------------------------------------------------------------------------- /backend/src/LibService/paket.references: -------------------------------------------------------------------------------- 1 | FSharp.Core 2 | FsRegex 3 | Rollbar 4 | Rollbar.NetCore.AspNet 5 | Honeycomb.OpenTelemetry 6 | OpenTelemetry 7 | OpenTelemetry.Exporter.OpenTelemetryProtocol 8 | OpenTelemetry.Exporter.Console 9 | OpenTelemetry.Instrumentation.AspNetCore 10 | OpenTelemetry.Extensions.Hosting 11 | OpenTelemetry.Instrumentation.Http 12 | Microsoft.Extensions.Diagnostics.HealthChecks 13 | LaunchDarkly.ServerSdk -------------------------------------------------------------------------------- /backend/src/Wasm/Init.fs: -------------------------------------------------------------------------------- 1 | namespace Wasm 2 | 3 | open Microsoft.JSInterop 4 | 5 | open Prelude 6 | 7 | type Init = 8 | [] 9 | static member InitializeDarkRuntime() : unit = 10 | System.Environment.SetEnvironmentVariable("TZ", "UTC") 11 | Json.Vanilla.allow 12 | "to 'Export' the editor - just for debugging" 13 | System.Console.WriteLine("Dark runtime initialized") 14 | -------------------------------------------------------------------------------- /packages/darklang/languageServerProtocol/lifecycle/exit.dark: -------------------------------------------------------------------------------- 1 | (* 2 | /// The exit event is sent from the client to the server to 3 | /// ask the server to exit its process. 4 | export namespace ExitNotification { 5 | export const method: 'exit' = 'exit'; 6 | export const messageDirection: MessageDirection = MessageDirection.clientToServer; 7 | export const type = new ProtocolNotificationType0(method); 8 | } 9 | *) -------------------------------------------------------------------------------- /scripts/devcontainer/_allow-docker-access: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -euo pipefail 4 | 5 | if [[ -S "/var/run/docker.sock" ]]; then 6 | sudo chmod 777 /var/run/docker.sock 7 | else 8 | # We might not have the docker socket available on windows. Since we don't 9 | # need this all that much, let's just print a warning rather than expect 10 | # people to set this up. 11 | echo "No docker socket, skipping" 12 | fi 13 | -------------------------------------------------------------------------------- /tf/apis.tf: -------------------------------------------------------------------------------- 1 | variable "google_cloud_services" { 2 | type = set(string) 3 | default = [ 4 | "compute.googleapis.com", 5 | "secretmanager.googleapis.com", 6 | "run.googleapis.com" 7 | ] 8 | } 9 | 10 | resource "google_project_service" "apis" { 11 | for_each = toset(var.google_cloud_services) 12 | provider = google 13 | service = each.value 14 | disable_on_destroy = false 15 | } 16 | -------------------------------------------------------------------------------- /.yamllint: -------------------------------------------------------------------------------- 1 | extends: default 2 | 3 | rules: 4 | line-length: disable 5 | document-start: disable 6 | indentation: 7 | spaces: 2 8 | indent-sequences: true 9 | key-duplicates: enable 10 | brackets: 11 | min-spaces-inside: 0 12 | max-spaces-inside: 0 13 | comments: 14 | require-starting-space: true 15 | min-spaces-from-content: 1 16 | braces: 17 | min-spaces-inside: 1 18 | max-spaces-inside: 1 19 | -------------------------------------------------------------------------------- /backend/src/LibCloud/Init.fs: -------------------------------------------------------------------------------- 1 | module LibCloud.Init 2 | 3 | open System.Threading.Tasks 4 | open FSharp.Control.Tasks 5 | 6 | open Prelude 7 | 8 | let init (serviceName : string) : Task = 9 | task { 10 | printTime $"Initing LibCloud in {serviceName}" 11 | 12 | let queueTask = Queue.init () 13 | let! (_ : List) = Task.flatten [ queueTask ] 14 | 15 | printTime $" Inited LibCloud in {serviceName}" 16 | } 17 | -------------------------------------------------------------------------------- /backend/src/ProdExec/README.md: -------------------------------------------------------------------------------- 1 | # Prod Execution Host 2 | 3 | Based on 4 | https://andrewlock.net/deploying-asp-net-core-applications-to-kubernetes-part-10-creating-an-exec-host-deployment-for-running-one-off-commands/, 5 | this is an executable which runs on a container in production, that can be used 6 | for one off tasks. To start with, the one-off tasks are: 7 | 8 | - run migrations 9 | - emergency login if auth provider is down 10 | -------------------------------------------------------------------------------- /backend/src/LibCloudExecution/Init.fs: -------------------------------------------------------------------------------- 1 | /// Initialize LibCloudExecution 2 | module LibCloudExecution.Init 3 | 4 | open FSharp.Control.Tasks 5 | open System.Threading.Tasks 6 | 7 | open Prelude 8 | 9 | let init (serviceName : string) : Task = 10 | task { 11 | printTime $"Initing LibCloudExecution in {serviceName}" 12 | do! CloudExecution.init () 13 | printTime $" Inited LibCloudExecution in {serviceName}" 14 | return () 15 | } 16 | -------------------------------------------------------------------------------- /packages/darklang/cli/clear.dark: -------------------------------------------------------------------------------- 1 | module Darklang.Cli.Clear 2 | 3 | 4 | let execute (state: AppState) (args: List) : AppState = 5 | Builtin.stdoutClear () 6 | { state with needsFullRedraw = true } 7 | 8 | 9 | let complete (_state: AppState) (_args: List) : List = 10 | [] 11 | 12 | 13 | let help (_state: AppState) : Unit = 14 | [ 15 | "Usage: clear" 16 | "Clear the terminal screen." 17 | ] |> Stdlib.printLines 18 | -------------------------------------------------------------------------------- /tree-sitter-darklang/binding.gyp: -------------------------------------------------------------------------------- 1 | { 2 | "targets": [{ 3 | "target_name": "tree_sitter_darklang_binding", 4 | "include_dirs": [") : AppState = 5 | Stdlib.printLine "Goodbye!" 6 | { state with isExiting = true } 7 | 8 | 9 | let complete (_state: AppState) (_args: List) : List = 10 | [] 11 | 12 | 13 | let help (_state: AppState) : Unit = 14 | [ 15 | "Usage: quit" 16 | "Exit the CLI immediately." 17 | ] |> Stdlib.printLines 18 | 19 | -------------------------------------------------------------------------------- /backend/testfiles/execution/language/collections/dlist.dark: -------------------------------------------------------------------------------- 1 | [] = [] 2 | 3 | [ 1L ] = [ 1L ] 4 | 5 | [ 1L; 2L ] = [ 1L; 2L ] 6 | 7 | [ 5L; Stdlib.Int64.add_v0 1L 5L; 0L ] = [ 5L; 6L; 0L ] 8 | 9 | [ 5L; Builtin.testRuntimeError "test"; 0L ] = 10 | Builtin.testDerrorMessage "Uncaught exception: test" 11 | 12 | [ 5L; Builtin.testRuntimeError "1"; Builtin.testRuntimeError "2" ] = 13 | Builtin.testDerrorMessage "Uncaught exception: 1" 14 | 15 | // TODO mixed data attempts -------------------------------------------------------------------------------- /scripts/run-backend-datatests: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | . ./scripts/devcontainer/_assert-in-container "$0" "$@" 3 | 4 | set -euo pipefail 5 | 6 | trap ctrl_c INT 7 | 8 | function ctrl_c() { 9 | killall -9 Tests 10 | exit 1 11 | } 12 | 13 | killall -9 Tests || true 14 | 15 | EXE=Build/out/DataTests/Debug/net8.0/linux-x64/DataTests 16 | 17 | # No migrations 18 | 19 | cd backend 20 | 21 | DARK_CONFIG_TELEMETRY_EXPORTER=none \ 22 | "${EXE}" --no-spinner "${@}" 23 | -------------------------------------------------------------------------------- /backend/src/BuiltinCloudExecution/Builtin.fs: -------------------------------------------------------------------------------- 1 | /// Builtin functions that can only be run on the backend 2 | /// 3 | /// Aggregates functions in other modules 4 | module BuiltinCloudExecution.Builtin 5 | 6 | module Builtin = LibExecution.Builtin 7 | 8 | let fnRenames : Builtin.FnRenames = 9 | // old names, new names 10 | // eg: fn "Http" "respond" 0, fn "Http" "response" 0 11 | [] 12 | 13 | let builtins = Builtin.combine [ Libs.DB.builtins; Libs.Event.builtins ] fnRenames 14 | -------------------------------------------------------------------------------- /backend/src/LibCloud/DvalReprInternalHash.fs: -------------------------------------------------------------------------------- 1 | module LibExecution.DvalReprInternalHash 2 | 3 | open Prelude 4 | 5 | let supportedHashVersions : int list = [ 2 ] 6 | 7 | let currentHashVersion : int = 2 8 | 9 | let hash (version : int) (arglist : NEList) : string = 10 | match version with 11 | | 2 -> arglist |> NEList.toList |> DvalReprInternalRoundtrippable.toHashV2 12 | | _ -> Exception.raiseInternal $"Invalid Dval.hash version" [ "version", version ] 13 | -------------------------------------------------------------------------------- /backend/src/Prelude/HashSet.fs: -------------------------------------------------------------------------------- 1 | module HashSet 2 | 3 | type HashSet<'v> = System.Collections.Generic.HashSet<'v> 4 | 5 | let add (v : 'v) (s : HashSet<'v>) : unit = 6 | let (_ : bool) = s.Add v 7 | () 8 | 9 | let empty () : HashSet<'v> = System.Collections.Generic.HashSet<'v>() 10 | 11 | let toList (d : HashSet<'v>) : List<'v> = 12 | seq { 13 | let mutable e = d.GetEnumerator() 14 | 15 | while e.MoveNext() do 16 | yield e.Current 17 | } 18 | |> Seq.toList 19 | -------------------------------------------------------------------------------- /scripts/stop-second-instance: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | . ./scripts/devcontainer/_assert-in-container "$0" "$@" 3 | 4 | # TODO remove this and run-second-instance 5 | # (temporary until we have a 'real' server standing.) 6 | 7 | set -euo pipefail 8 | 9 | INSTANCE_DB="data-instance2.db" 10 | 11 | green="\033[0;32m" 12 | reset="\033[0m" 13 | 14 | echo -e "${green}Stopping instance2...${reset}" 15 | pkill -f "DARK_CONFIG_DB_NAME=${INSTANCE_DB}" || true 16 | echo -e "${green}Done${reset}" 17 | -------------------------------------------------------------------------------- /packages/darklang/modelContextProtocol/serverBuilder/aliases.dark: -------------------------------------------------------------------------------- 1 | // Common type aliases for ServerBuilder modules 2 | module Darklang.ModelContextProtocol.ServerBuilder 3 | 4 | // 5 | type Json = Stdlib.AltJson.Json 6 | type McpServerBuilder = Darklang.ModelContextProtocol.ServerBuilder.State.McpServerBuilder 7 | type BuilderServerState = Darklang.ModelContextProtocol.ServerBuilder.State.BuilderServerState 8 | type Logging = Darklang.ModelContextProtocol.ServerBuilder.Logging 9 | // -------------------------------------------------------------------------------- /backend/src/LibCloud/Password.fs: -------------------------------------------------------------------------------- 1 | module LibCloud.Password 2 | 3 | open Prelude 4 | 5 | type T = 6 | private 7 | | Pw of string 8 | 9 | override this.ToString() : string = 10 | let (Pw pw) = this 11 | pw 12 | 13 | let fromPlaintext (password : string) : T = 14 | password 15 | |> Sodium.PasswordHash.ArgonHashString 16 | |> UTF8.toBytes 17 | |> Base64.defaultEncodeToString 18 | |> Pw 19 | 20 | let invalid : T = Pw "" 21 | 22 | let fromHash (hash : string) : T = Pw hash 23 | -------------------------------------------------------------------------------- /containers/prodexec/README.md: -------------------------------------------------------------------------------- 1 | # ProdExec container 2 | 3 | The prodexec container runs as service in Cloud Run. It's purpose is to ssh into it 4 | so we can run commands like DB migrations. 5 | 6 | Since Cloud Run doesn't allow ssh access, we tunnel it through http using chisel. For 7 | security, it should only be accessible via google cloud, and never on the public 8 | internet. 9 | 10 | To connect, run ./scripts/production/connect-to-prod-exec, which will ssh you in if you have 11 | permission to do so. -------------------------------------------------------------------------------- /backend/src/BwdServer/README.md: -------------------------------------------------------------------------------- 1 | # BwdServer 2 | 3 | This is the webserver for darklang.io, often referred to as "BWD" 4 | (named after our old domain - builtwithdark.com). 5 | 6 | All Dark HTTP Handlers hosted by our cloud are hosted with BWD - 7 | our grand-users hit this server, and BWD handles the requests. 8 | 9 | So, BWD is what handles the HTTP "handlers" of a Dark program. 10 | 11 | It uses ASP.NET directly, instead of a web framework, 12 | so we can tune the exact behaviour of headers and such. 13 | -------------------------------------------------------------------------------- /backend/src/Wasm/README.md: -------------------------------------------------------------------------------- 1 | # Wasm 2 | 3 | Some of Dark's "backend" F# source code is compiled to WebAssemby to be usable by JS. 4 | 5 | In Dark-Classic, it was used to support "analysis," in-editor immediate debugging 6 | that looped in 'traces' -- recordings of the inputs/outputs of fn calls in the 7 | eval of a Handler (HTTP, Cron, Queue, Script). 8 | 9 | We'll likely use this for something similar in the new iteration of Dark, but 10 | haven't used it quite yet, so this project is sitting around until we need it. -------------------------------------------------------------------------------- /tree-sitter-darklang/test/corpus/exhaustive/exprs/char.txt: -------------------------------------------------------------------------------- 1 | ================== 2 | basic char 3 | ================== 4 | 5 | 'a' 6 | 7 | --- 8 | 9 | (source_file 10 | (expression 11 | (simple_expression (char_literal (symbol) (character) (symbol))))) 12 | 13 | 14 | ================== 15 | escape sequence 16 | ================== 17 | 18 | '\n' 19 | 20 | --- 21 | 22 | (source_file 23 | (expression 24 | (simple_expression (char_literal (symbol) (character (char_or_string_escape_sequence)) (symbol))))) -------------------------------------------------------------------------------- /backend/static/README.md: -------------------------------------------------------------------------------- 1 | # `backend/static` 2 | 3 | This directory contains static files that are served by a Dark backend for a webpage. 4 | The `dark_wasm` and `tree-sitter` directories should ideally be hosted in a CDN, long-term. 5 | 6 | These assets are currently served by an experimental canvas, `dark-serve-static`. 7 | See the README.md in that directory for more information. 8 | 9 | ## Tree-Sitter WebAssembly/JS Bindings 10 | 11 | If you don't see a `tree-sitter` directory, run `./scripts/build/build-parser`. -------------------------------------------------------------------------------- /backend/testfiles/httpclient/v0/todo/response-redirect-to-ftp.test: -------------------------------------------------------------------------------- 1 | [expected-request] 2 | GET PATH HTTP/1.1 3 | Accept: */* 4 | Accept-Encoding: deflate, gzip, br 5 | Content-Type: text/plain; charset=utf-8 6 | Host: HOST 7 | 8 | [response] 9 | HTTP/1.1 302 Found 10 | Date: xxx, xx xxx xxxx xx:xx:xx xxx 11 | Location: ftp://speedtest.tele2.net/1KB.zip 12 | 13 | 14 | [test] 15 | (match Darklang.Stdlib.HttpClient.request "get" "http://URL" [] [] with 16 | | Ok _ -> "fail" 17 | | Error response -> response) = "Unsupported protocol" 18 | -------------------------------------------------------------------------------- /scripts/run-prod-exec: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | . ./scripts/devcontainer/_assert-in-container "$0" "$@" 3 | 4 | set -euo pipefail 5 | 6 | PUBLISHED=false 7 | 8 | for i in "$@" 9 | do 10 | case "${i}" in 11 | --published) 12 | PUBLISHED=true 13 | shift 14 | ;; 15 | esac 16 | done 17 | 18 | if [[ "$PUBLISHED" == "true" ]]; then 19 | EXE="backend/Build/out/ProdExec/Release/net8.0/linux-x64/publish/ProdExec" 20 | else 21 | EXE="backend/Build/out/ProdExec/Debug/net8.0/ProdExec" 22 | fi 23 | 24 | "${EXE}" "$@" 25 | -------------------------------------------------------------------------------- /backend/testfiles/httpclient/v0/todo/response-content-type-no-charset.test: -------------------------------------------------------------------------------- 1 | [expected-request] 2 | GET PATH HTTP/1.1 3 | Accept: */* 4 | Accept-Encoding: deflate, gzip, br 5 | Content-Type: text/plain; charset=utf-8 6 | Host: HOST 7 | 8 | [response] 9 | HTTP/1.1 200 OK 10 | Date: xxx, xx xxx xxxx xx:xx:xx xxx 11 | Content-type: text/plain 12 | Content-Length: LENGTH 13 | 14 | à æ ç 15 | 16 | [test] 17 | (let response = (Darklang.Stdlib.HttpClient.request "get" "http://URL" [] []) |> Builtin.unwrap 18 | response.body) = 19 | "à æ ç" 20 | -------------------------------------------------------------------------------- /scripts/devcontainer/_create-cache-directories: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -euo pipefail 4 | 5 | prepare () { 6 | sudo mkdir -p $1 7 | 8 | # sudo chown -R dark:dark $1 9 | # note: we updated from the above to the below because of some symlinked 10 | # tree-sitter binary in the tsd/node_modules dir 11 | 12 | sudo find "$1" ! -type l -exec chown dark:dark {} + 13 | } 14 | 15 | prepare "backend/Build" 16 | prepare "/home/dark/.nuget" 17 | prepare "tree-sitter-darklang/build" 18 | prepare "tree-sitter-darklang/node_modules" 19 | -------------------------------------------------------------------------------- /backend/testfiles/httpclient/v0/todo/response-content-type-latin1.test: -------------------------------------------------------------------------------- 1 | [expected-request] 2 | GET PATH HTTP/1.1 3 | Accept: */* 4 | Accept-Encoding: deflate, gzip, br 5 | Content-Type: text/plain; charset=utf-8 6 | Host: HOST 7 | 8 | [response] 9 | HTTP/1.1 200 OK 10 | Date: xxx, xx xxx xxxx xx:xx:xx xxx 11 | Content-type: text/plain; charset=iso-8859-1 12 | Content-Length: 5 13 | 14 | à æ ç 15 | 16 | [test] 17 | (let response = (Darklang.Stdlib.HttpClient.request "get" "http://URL" [] []) |> Builtin.unwrap 18 | response.body) = 19 | "à æ ç" 20 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // See http://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations. 3 | // Extension identifier format: ${publisher}.${name}. Example: vscode.csharp 4 | 5 | // List of extensions which should be recommended for users of this workspace. 6 | "recommendations": ["ionide.ionide-fsharp", "editorconfig.editorconfig", "dbaeumer.vscode-eslint"], 7 | // List of extensions recommended by VS Code that should not be recommended for users of this workspace. 8 | "unwantedRecommendations": [] 9 | } 10 | -------------------------------------------------------------------------------- /backend/src/LibClientTypes/ClientPusherTypes.fs: -------------------------------------------------------------------------------- 1 | /// Payloads that we send to the client via Pusher.com 2 | module LibClientTypes.Pusher 3 | 4 | open Prelude 5 | 6 | module Payload = 7 | type NewTrace = System.Guid * tlid list 8 | 9 | type New404 = string * string * string * NodaTime.Instant * System.Guid 10 | 11 | // type AddOpV1 = { result : Ops.AddOpResultV1; ``params`` : Ops.AddOpParamsV1 } 12 | //type AddOpV1PayloadTooBig = { tlids : List } // this is so-far unused 13 | 14 | // type UpdateWorkerStates = Map 15 | -------------------------------------------------------------------------------- /backend/testfiles/execution/language/collections/dtuple.dark: -------------------------------------------------------------------------------- 1 | (1L, 2L) = (1L, 2L) 2 | (1L, 2L, 3L) = (1L, 2L, 3L) 3 | (1L, 2L + 3L, 4L) = (1L, 5L, 4L) 4 | 5 | // note: there is no upper limit set on Tuple size 6 | (1L, 2L, 3L, 4L, 5L, 6L, 7L, 8L, 9L) = 7 | (1L, 2L, 3L, 4L, 5L, 6L, 7L, 8L, 9L) 8 | 9 | (1L, Builtin.testRuntimeError "test", 3L) = 10 | (Builtin.testDerrorMessage "Uncaught exception: test") 11 | 12 | (1L, Builtin.testRuntimeError "error1", Builtin.testRuntimeError "error2") = 13 | (Builtin.testDerrorMessage "Uncaught exception: error1") -------------------------------------------------------------------------------- /scripts/run-local-exec: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | . ./scripts/devcontainer/_assert-in-container "$0" "$@" 3 | 4 | set -euo pipefail 5 | 6 | PUBLISHED=false 7 | 8 | for i in "$@" 9 | do 10 | case "${i}" in 11 | --published) 12 | PUBLISHED=true 13 | ;; 14 | *) ARGS+=("${i}");; 15 | esac 16 | done 17 | 18 | if [[ "$PUBLISHED" == "true" ]]; then 19 | EXE="backend/Build/out/LocalExec/Release/net8.0/linux-x64/LocalExec" 20 | else 21 | EXE="backend/Build/out/LocalExec/Debug/net8.0/LocalExec" 22 | fi 23 | 24 | "${EXE}" "${ARGS[@]}" 25 | -------------------------------------------------------------------------------- /backend/src/Prelude/ResizeArray.fs: -------------------------------------------------------------------------------- 1 | module ResizeArray 2 | 3 | type T<'v> = ResizeArray<'v> 4 | let empty () = T() 5 | 6 | let iter (f : 'v -> unit) (l : T<'v>) : unit = 7 | FSharpx.Collections.ResizeArray.iter f l 8 | 9 | let map (f : 'v -> 'v2) (l : T<'v>) : T<'v2> = 10 | FSharpx.Collections.ResizeArray.map f l 11 | 12 | let append (v : 'v) (list : T<'v>) : unit = list.Add(v) 13 | 14 | let toList (l : T<'v>) : List<'v> = FSharpx.Collections.ResizeArray.toList l 15 | 16 | let toSeq (l : T<'v>) : seq<'v> = FSharpx.Collections.ResizeArray.toSeq l 17 | -------------------------------------------------------------------------------- /packages/darklang/languageServerProtocol/window/telemetry.dark: -------------------------------------------------------------------------------- 1 | // Server->Client notification to log telemetry data 2 | 3 | (* 4 | /// The telemetry event notification is sent from the server to the client to ask 5 | /// the client to log telemetry data. 6 | export namespace TelemetryEventNotification { 7 | export const method: 'telemetry/event' = 'telemetry/event'; 8 | export const messageDirection: MessageDirection = MessageDirection.serverToClient; 9 | export const type = new ProtocolNotificationType(method); 10 | } 11 | *) -------------------------------------------------------------------------------- /vscode-extension/client/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "darklang-lsp-client", 3 | "description": "Client part of an LSP-powered VS Code extension for Darklang", 4 | "author": "darklang", 5 | "version": "0.0.1", 6 | "publisher": "darklang", 7 | "repository": { 8 | "type": "git", 9 | "url": "https://github.com/darklang/dark" 10 | }, 11 | "engines": { 12 | "vscode": "^1.63.0" 13 | }, 14 | "dependencies": { 15 | "vscode-languageclient": "^7.0.0" 16 | }, 17 | "devDependencies": { 18 | "@types/vscode": "^1.63.0" 19 | } 20 | } -------------------------------------------------------------------------------- /backend/testfiles/httpclient/v0/todo/response-content-encoding-invalid.test: -------------------------------------------------------------------------------- 1 | [expected-request] 2 | GET PATH HTTP/1.1 3 | Accept: */* 4 | Accept-Encoding: deflate, gzip, br 5 | Content-Type: text/plain; charset=utf-8 6 | Host: HOST 7 | 8 | 9 | [response] 10 | HTTP/1.1 200 OK 11 | Date: xxx, xx xxx xxxx xx:xx:xx xxx 12 | Content-type: text/plain; charset=utf-8 13 | Content-Encoding: not-valid 14 | 15 | "Hello back" 16 | 17 | [test] 18 | Darklang.Stdlib.HttpClient.request "get" "http://URL/" [] [] = Error "Unrecognized or bad HTTP Content or Transfer-Encoding" 19 | -------------------------------------------------------------------------------- /scripts/build/regenerate-test-files: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | . ./scripts/devcontainer/_assert-in-container "$0" "$@" 3 | 4 | set -euo pipefail 5 | 6 | 7 | jsonfile=backend/serialization/oplist-format-latest.json 8 | binfile=backend/serialization/oplist-format-latest.bin 9 | 10 | echo "Before: " 11 | sha1sum "$jsonfile" 12 | sha1sum "$binfile" 13 | 14 | echo -e "\n\nRegenerating: " 15 | cd backend 16 | Build/out/Tests/Debug/net8.0/linux-x64/Tests --regenerate-test-files 17 | cd .. 18 | 19 | echo -e "\n\nAfter: " 20 | sha1sum "$jsonfile" 21 | sha1sum "$binfile" 22 | -------------------------------------------------------------------------------- /vscode-extension/static/logo-dark-transparent.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /backend/src/LibTreeSitter/Helpers.fs: -------------------------------------------------------------------------------- 1 | [] 2 | module LibTreeSitter.Helpers 3 | 4 | open System 5 | open System.IO 6 | open System.Reflection 7 | open System.Runtime.InteropServices 8 | 9 | let baseTempPath = Path.Combine(Path.GetTempPath(), "darklang") 10 | 11 | let resourceExtensionForOS = 12 | if RuntimeInformation.IsOSPlatform(OSPlatform.Windows) then ".dll" 13 | elif RuntimeInformation.IsOSPlatform(OSPlatform.Linux) then ".so" 14 | elif RuntimeInformation.IsOSPlatform(OSPlatform.OSX) then ".dylib" 15 | else raise (PlatformNotSupportedException()) 16 | -------------------------------------------------------------------------------- /backend/testfiles/httphandler/query-string.test: -------------------------------------------------------------------------------- 1 | [http-handler GET /] 2 | Darklang.Stdlib.Http.response (request.url |> Darklang.Stdlib.String.toBytes) 200L 3 | 4 | [request] 5 | GET /?key=value HTTP/1.1 6 | Host: HOST 7 | Date: Sun, 08 Nov 2020 15:38:01 GMT 8 | Content-Length: 7 9 | 10 | ignored 11 | 12 | [response] 13 | HTTP/1.1 200 OK 14 | Date: xxx, xx xxx xxxx xx:xx:xx xxx 15 | x-darklang-execution-id: 0123456789 16 | Server: darklang 17 | Strict-Transport-Security: max-age=31536000; includeSubDomains; preload 18 | Content-Length: LENGTH 19 | 20 | http://HOST/?key=value -------------------------------------------------------------------------------- /backend/testfiles/httphandler/url-http.test: -------------------------------------------------------------------------------- 1 | [http-handler GET /a] 2 | Darklang.Stdlib.Http.response (Darklang.Stdlib.String.toBytes request.url) 200L 3 | 4 | [request] 5 | GET /a HTTP/1.1 6 | Host: HOST 7 | Date: Sun, 08 Nov 2020 15:38:01 GMT 8 | Content-Length: 0 9 | X-Forwarded-Proto: http 10 | 11 | 12 | 13 | [response] 14 | HTTP/1.1 200 OK 15 | Date: xxx, xx xxx xxxx xx:xx:xx xxx 16 | x-darklang-execution-id: 0123456789 17 | Server: darklang 18 | Strict-Transport-Security: max-age=31536000; includeSubDomains; preload 19 | Content-Length: LENGTH 20 | 21 | http://HOST/a -------------------------------------------------------------------------------- /backend/testfiles/httphandler/url-https.test: -------------------------------------------------------------------------------- 1 | [http-handler GET /a] 2 | Darklang.Stdlib.Http.response (Darklang.Stdlib.String.toBytes request.url) 200L 3 | 4 | [request] 5 | GET /a HTTP/1.1 6 | Host: HOST 7 | Date: Sun, 08 Nov 2020 15:38:01 GMT 8 | Content-Length: 0 9 | X-Forwarded-Proto: https 10 | 11 | 12 | 13 | [response] 14 | HTTP/1.1 200 OK 15 | Date: xxx, xx xxx xxxx xx:xx:xx xxx 16 | x-darklang-execution-id: 0123456789 17 | Server: darklang 18 | Strict-Transport-Security: max-age=31536000; includeSubDomains; preload 19 | Content-Length: LENGTH 20 | 21 | https://DOMAIN/a -------------------------------------------------------------------------------- /packages/darklang/stdlib/uuid.dark: -------------------------------------------------------------------------------- 1 | module Darklang.Stdlib.Uuid 2 | 3 | 4 | type ParseError = | BadFormat 5 | 6 | /// Generate a new v4 according to RFC 4122 7 | let generate () : Uuid = Builtin.uuidGenerate () 8 | 9 | 10 | /// Parse a of form {{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}} 11 | let parse (uuid: String) : Stdlib.Result.Result = 12 | Builtin.uuidParse uuid 13 | 14 | 15 | /// Stringify to the format XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX 16 | let toString (uuid: Uuid) : String = Builtin.uuidToString uuid -------------------------------------------------------------------------------- /backend/src/Prelude/Tuple2.fs: -------------------------------------------------------------------------------- 1 | module Tuple2 2 | 3 | let fromKeyValuePair 4 | (kvp : System.Collections.Generic.KeyValuePair<'a, 'b>) 5 | : ('a * 'b) = 6 | kvp.Key, kvp.Value 7 | 8 | let toKeyValuePair 9 | ((k, v) : 'a * 'b) 10 | : (System.Collections.Generic.KeyValuePair<'a, 'b>) = 11 | System.Collections.Generic.KeyValuePair<'a, 'b>(k, v) 12 | 13 | let first (v1 : 'a, _ : 'b) : 'a = v1 14 | let second (_ : 'a, v2 : 'b) : 'b = v2 15 | let mapFirst (f : 'x -> 'r) (x : 'x, y : 'y) : 'r * 'y = (f x, y) 16 | let mapSecond (f : 'y -> 'r) (x : 'x, y : 'y) : 'x * 'r = (x, f y) 17 | -------------------------------------------------------------------------------- /packages/darklang/languageServerProtocol/lifecycle/shutdown.dark: -------------------------------------------------------------------------------- 1 | (* 2 | /// A shutdown request is sent from the client to the server. 3 | /// It is sent once when the client decides to shutdown the 4 | /// server. The only notification that is sent after a shutdown request 5 | /// is the exit event. 6 | export namespace ShutdownRequest { 7 | export const method: 'shutdown' = 'shutdown'; 8 | export const messageDirection: MessageDirection = MessageDirection.clientToServer; 9 | export const type = new ProtocolRequestType0(method); 10 | } 11 | *) -------------------------------------------------------------------------------- /scripts/production/gcp-get-logs: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | 4 | gcloud logging read \ 5 | --freshness=1m \ 6 | --flatten=jsonPayload.data \ 7 | --order=asc \ 8 | | grep -v '\-\-\-' \ 9 | | sed "s/^ \'//" \ 10 | | sed "s/'$//" \ 11 | | sed 's/\\e\[6;30m//g' \ 12 | | sed 's/\\e\[0m//g' \ 13 | | sed 's/e\[0m//g' \ 14 | | sed 's/\\e\[6;31m//g' \ 15 | | sed 's/\\\\\\n//g' \ 16 | | sed 's/\\\\n//g' \ 17 | | sed 's/\\n//g' \ 18 | | sed 's/\\\\\\//g' \ 19 | | sed 's/\\\\//g' \ 20 | | sed 's/\\//g' \ 21 | | sed 's/log"/log/' \ 22 | | sed 's/"log/log/' 23 | 24 | -------------------------------------------------------------------------------- /vscode-extension/static/logo-dark-transparent-low-margin.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /scripts/devcontainer/_vscode-post-start-command: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -euo pipefail 4 | 5 | echo "Fetching and building tree-sitter library" 6 | ./scripts/build/build-tree-sitter.sh 7 | 8 | echo "Starting build server" 9 | 10 | mkdir -p rundir/logs/ 11 | 12 | git config --global --add safe.directory /home/dark/app 13 | 14 | nohup ./scripts/build/_build-server --compile --watch &> /home/dark/app/rundir/logs/build-server.log & 15 | 16 | # It seems that if we don't sleep here, the server does not start properly in all cases 17 | sleep 2 18 | 19 | echo "Build server started" 20 | -------------------------------------------------------------------------------- /tree-sitter-darklang/test/corpus/exhaustive/exprs/floats.txt: -------------------------------------------------------------------------------- 1 | ================== 2 | float literal 3 | ================== 4 | 5 | 1.0 6 | 7 | --- 8 | 9 | (source_file (expression (simple_expression (float_literal)))) 10 | 11 | 12 | ================== 13 | float literal (negative) 14 | ================== 15 | 16 | -1.0 17 | 18 | --- 19 | (source_file (expression (simple_expression (float_literal)))) 20 | 21 | 22 | ================== 23 | float literal (zero) 24 | ================== 25 | 26 | -00000000.000 27 | 28 | --- 29 | (source_file (expression (simple_expression (float_literal)))) -------------------------------------------------------------------------------- /vscode-extension/.vscodeignore: -------------------------------------------------------------------------------- 1 | .vscode/** 2 | **/*.ts 3 | **/*.map 4 | .gitignore 5 | **/tsconfig.json 6 | **/tsconfig.base.json 7 | .travis.yml 8 | client/node_modules/** 9 | !client/node_modules/vscode-jsonrpc/** 10 | !client/node_modules/vscode-languageclient/** 11 | !client/node_modules/vscode-languageserver-protocol/** 12 | !client/node_modules/vscode-languageserver-types/** 13 | !client/node_modules/{minimatch,brace-expansion,concat-map,balanced-match}/** 14 | !client/node_modules/{semver,lru-cache,yallist}/** 15 | # TODO figure out what's adding the .mono directory 16 | .mono -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | 4 | - package-ecosystem: docker 5 | directory: "/" 6 | schedule: 7 | interval: daily 8 | open-pull-requests-limit: 10 9 | 10 | - package-ecosystem: npm 11 | directory: "/" 12 | schedule: 13 | interval: daily 14 | open-pull-requests-limit: 10 15 | 16 | - package-ecosystem: nuget 17 | directory: "/backend" 18 | schedule: 19 | interval: daily 20 | open-pull-requests-limit: 10 21 | 22 | - package-ecosystem: terraform 23 | directory: "/tf" 24 | schedule: 25 | interval: daily 26 | open-pull-requests-limit: 10 27 | -------------------------------------------------------------------------------- /backend/src/LibClientTypes/README.md: -------------------------------------------------------------------------------- 1 | # LibClientTypes 2 | 3 | Maintains types used to communicate with the `client`/editor: 4 | 5 | - API request/response payloads 6 | - requests/responses used in Analysis via WebAssembly-compiled code 7 | - Pusher.com payloads 8 | - data injected into `ui.html` 9 | 10 | This project intentionally has no dependencies on other Dark projects (other than 11 | Prelude), to sensure no internal domain types are referenced. 12 | 13 | Translation between these types and "domain types" is provided via separate 14 | `ClientTypes2___Types` projects. 15 | 16 | ## Note 17 | -------------------------------------------------------------------------------- /backend/testfiles/httphandler/injected-icon-post-roundtrip.test: -------------------------------------------------------------------------------- 1 | [http-handler POST /] 2 | Darklang.Stdlib.Http.response request.body 200L 3 | 4 | [request] 5 | POST / HTTP/1.1 6 | Host: HOST 7 | Date: Sun, 08 Nov 2020 15:38:01 GMT 8 | Content-Length: 454 9 | 10 | 11 | 12 | [response] 13 | HTTP/1.1 200 OK 14 | Date: xxx, xx xxx xxxx xx:xx:xx xxx 15 | x-darklang-execution-id: 0123456789 16 | Server: darklang 17 | Strict-Transport-Security: max-age=31536000; includeSubDomains; preload 18 | Content-Length: 454 19 | 20 | -------------------------------------------------------------------------------- /tf/vpc.tf: -------------------------------------------------------------------------------- 1 | 2 | resource "google_compute_network" "default" { 3 | name = "default" 4 | description = "Default network for the project" 5 | auto_create_subnetworks = "true" 6 | } 7 | 8 | resource "google_vpc_access_connector" "serverless_connector_1" { 9 | name = "serverless-connector-1" 10 | network = google_compute_network.default.id 11 | region = "us-central1" 12 | ip_cidr_range = "10.8.100.0/28" 13 | max_instances = 10 14 | min_instances = 2 15 | max_throughput = 1000 16 | machine_type = "e2-micro" 17 | } 18 | 19 | -------------------------------------------------------------------------------- /containers/queueworker/Dockerfile: -------------------------------------------------------------------------------- 1 | # Dockerfile for the processing the event queue 2 | 3 | FROM dark-fsharp-service:latest 4 | 5 | WORKDIR /home/dark 6 | 7 | COPY --chown=dark:dark scripts scripts 8 | 9 | RUN mkdir app 10 | 11 | # Setting this now means we can set the filesystem to readonly 12 | ENV DARK_CONFIG_RUNDIR=/home/dark/gcp-rundir 13 | RUN ./scripts/devcontainer/_create-app-directories 14 | 15 | COPY --chown=dark:dark backend/Build/out/QueueWorker/Release/net8.0/linux-x64/publish/QueueWorker app/ 16 | RUN ./scripts/linting/_check-linked-libs app/QueueWorker 17 | 18 | CMD ./app/QueueWorker 19 | -------------------------------------------------------------------------------- /scripts/deployment/_deploy-lock-request: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | . ./scripts/devcontainer/_assert-in-container "$0" "$@" 3 | 4 | # Make requests to deploy lock server 5 | 6 | set -euo pipefail 7 | 8 | if [ ! -v DEPLOY_LOCK_TOKEN ] ; 9 | then 10 | echo "No deploy lock token, get it from https://darklang.com/a/ops-circleci" >&2 11 | exit 1 12 | fi 13 | 14 | REQUEST_PATH=$1 15 | METHOD=$2 16 | 17 | curl -f -s "https://ops-circleci.builtwithdark.com/deploy-lock${REQUEST_PATH}" \ 18 | -X "${METHOD}" \ 19 | -H 'Content-type: application/json' \ 20 | -H "Authorization: Bearer ${DEPLOY_LOCK_TOKEN}" 21 | -------------------------------------------------------------------------------- /backend/src/BuiltinCli/Builtin.fs: -------------------------------------------------------------------------------- 1 | module BuiltinCli.Builtin 2 | 3 | module Builtin = LibExecution.Builtin 4 | 5 | let fnRenames : Builtin.FnRenames = 6 | // old names, new names 7 | // eg: fn "Http" "respond" 0, fn "Http" "response" 0 8 | [] 9 | 10 | let builtins = 11 | Builtin.combine 12 | [ Libs.Directory.builtins 13 | Libs.Environment.builtins 14 | Libs.File.builtins 15 | Libs.Execution.builtins 16 | Libs.Output.builtins 17 | Libs.Process.builtins 18 | Libs.Stdin.builtins 19 | Libs.Time.builtins 20 | Libs.Terminal.builtins ] 21 | fnRenames 22 | -------------------------------------------------------------------------------- /packages/darklang/cli/installation/version.dark: -------------------------------------------------------------------------------- 1 | module Darklang.Cli.Installation.Version 2 | 3 | 4 | let execute (state: AppState) (args: List) : AppState = 5 | let versionInfo = Helpers.getVersionInfo () 6 | Stdlib.printLine versionInfo 7 | state 8 | 9 | 10 | let complete (state: AppState) (args: List) : List = 11 | [] 12 | 13 | 14 | let help (_state: AppState) : Unit = 15 | [ 16 | "Usage: version" 17 | "Display CLI version and installation information." 18 | "" 19 | "Shows current version, installation mode, and location." 20 | ] |> Stdlib.printLines 21 | -------------------------------------------------------------------------------- /packages/darklang/stdlib/alt-json.dark: -------------------------------------------------------------------------------- 1 | module Darklang.Stdlib.AltJson 2 | 3 | 4 | type Json = 5 | | Null 6 | | Bool of Bool 7 | | Number of Float 8 | | String of String 9 | | Array of List 10 | | Object of List<(String * Json)> 11 | 12 | 13 | module ParseError = 14 | type ParseError = | NotJson 15 | 16 | let toString (e: ParseError) : String = 17 | match e with 18 | | NotJson -> "Not JSON" 19 | 20 | 21 | let format (j: Json) : String = Builtin.altJsonFormat j 22 | 23 | let parse (jsonString: String) : Result.Result = 24 | Builtin.altJsonParse jsonString -------------------------------------------------------------------------------- /scripts/formatting/pre-commit-hook.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | . ./scripts/devcontainer/_assert-in-container "$0" "$@" 3 | 4 | set -euo pipefail 5 | 6 | # To install: 7 | # - cp scripts/formatting/pre-commit-hook.sh .git/hooks/pre-commit 8 | # - chmod +x .git/hooks/pre-commit 9 | 10 | # Works on all filetype, silently ignoring unsupported files 11 | 12 | files=$(git diff --cached --name-only --diff-filter=ACM) 13 | 14 | # format all staged files 15 | echo "$files" | xargs scripts/formatting/format check --quiet 16 | # Add back the modified/formatted files to staging 17 | echo "$files" | xargs git add 18 | 19 | exit 0 20 | -------------------------------------------------------------------------------- /backend/src/Cli/README.md: -------------------------------------------------------------------------------- 1 | # `darklang` CLI executable 2 | 3 | This is a project that yields a `darklang` executable artifact. 4 | It builds to many platforms -- see the .fsproj file for details. 5 | 6 | ## Usage 7 | 8 | - create script with `#!/usr/bin/env darklang` 9 | - runs same as if it was bash 10 | 11 | ## Structure 12 | 13 | - `Cli` 14 | - binary that runs dark code 15 | - supported greatly by `LibCli` 16 | - `LibCli` 17 | - stdlib fns and types for filesystem and other posix stuff 18 | - print to stdout (and stderr?) 19 | - read stdin (start processing once it closes) 20 | - `Directory.\*` 21 | - `File.\*` -------------------------------------------------------------------------------- /packages/darklang/languageServerProtocol/lifecycle/initialized.dark: -------------------------------------------------------------------------------- 1 | (* 2 | export interface InitializedParams {} 3 | 4 | /// The initialized notification is sent from the client to the 5 | /// server after the client is fully initialized and the server 6 | /// is allowed to send requests from the server to the client. 7 | export namespace InitializedNotification { 8 | export const method: 'initialized' = 'initialized'; 9 | export const messageDirection: MessageDirection = MessageDirection.clientToServer; 10 | export const type = new ProtocolNotificationType(method); 11 | } 12 | *) -------------------------------------------------------------------------------- /backend/testfiles/execution/language/apply/einfix.dark: -------------------------------------------------------------------------------- 1 | 5L + 3L = 8L 2 | "xx" ++ "yy" = "xxyy" 3 | (5L + (3L)) = 8L 4 | Stdlib.Int64.add_v0 5L 3L = 8L 5 | 6 | 5L + true = 7 | Builtin.testDerrorMessage "Builtin.int64Add's 2nd parameter `b` expects Int64, but got Bool (true)" 8 | 9 | 5L + (Builtin.testRuntimeError "error") = 10 | Builtin.testDerrorMessage "Uncaught exception: error" 11 | 12 | (Builtin.testRuntimeError "error") + 5L = 13 | Builtin.testDerrorMessage "Uncaught exception: error" 14 | 15 | (Builtin.testRuntimeError "one") + (Builtin.testRuntimeError "two") = 16 | Builtin.testDerrorMessage "Uncaught exception: one" -------------------------------------------------------------------------------- /containers/cronchecker/Dockerfile: -------------------------------------------------------------------------------- 1 | # Dockerfile for the service checking crons and loading them into the event queue 2 | 3 | FROM dark-fsharp-service:latest 4 | 5 | WORKDIR /home/dark 6 | 7 | COPY --chown=dark:dark scripts scripts 8 | 9 | RUN mkdir app 10 | 11 | # Setting this now means we can set the filesystem to readonly 12 | ENV DARK_CONFIG_RUNDIR=/home/dark/gcp-rundir 13 | RUN ./scripts/devcontainer/_create-app-directories 14 | 15 | COPY --chown=dark:dark backend/Build/out/CronChecker/Release/net8.0/linux-x64/publish/CronChecker app/ 16 | 17 | RUN ./scripts/linting/_check-linked-libs app/CronChecker 18 | 19 | CMD ./app/CronChecker -------------------------------------------------------------------------------- /tf/custom-domains.tf: -------------------------------------------------------------------------------- 1 | 2 | resource "google_certificate_manager_certificate_map_entry" "packages-darklang-com" { 3 | name = "packages-darklang-com-entry" 4 | description = "" 5 | map = google_certificate_manager_certificate_map.bwdserver.name 6 | certificates = [google_certificate_manager_certificate.packages-darklang-com.id] 7 | hostname = "packages.darklang.com" 8 | } 9 | 10 | resource "google_certificate_manager_certificate" "packages-darklang-com" { 11 | name = "packages-darklang-com" 12 | description = "" 13 | managed { 14 | domains = ["packages.darklang.com"] 15 | } 16 | } 17 | 18 | -------------------------------------------------------------------------------- /backend/testfiles/httpclient/v0/_request-content-type-invalid.test: -------------------------------------------------------------------------------- 1 | [expected-request] 2 | GET PATH HTTP/1.1 3 | Accept: */* 4 | Accept-Encoding: deflate, gzip, br 5 | Content-Type: just an invalid string 6 | Host: HOST 7 | 8 | [response] 9 | HTTP/1.1 200 OK 10 | Date: xxx, xx xxx xxxx xx:xx:xx xxx 11 | Content-type: text/plain; charset=utf-8 12 | Content-Length: LENGTH 13 | 14 | "Hello back" 15 | 16 | [test] 17 | (let reqHeaders = [("Content-Type", "just an invalid string")] 18 | let response = Darklang.Stdlib.HttpClient.request "get" "http://URL" reqHeaders [] 19 | response) == 20 | Builtin.Test.runtimeError "Invalid content-type header" 21 | -------------------------------------------------------------------------------- /backend/testfiles/httphandler/simple-inline-string-post.test: -------------------------------------------------------------------------------- 1 | [http-handler POST /] 2 | (let body = (request.body |> Darklang.Stdlib.List.length |> Darklang.Stdlib.Int64.toString |> Darklang.Stdlib.String.toBytes) 3 | Darklang.Stdlib.Http.response body 200L) 4 | 5 | [request] 6 | POST / HTTP/1.1 7 | Host: HOST 8 | Date: Sun, 08 Nov 2020 15:38:01 GMT 9 | Content-Length: 13 10 | 11 | 13 characters 12 | 13 | [response] 14 | HTTP/1.1 200 OK 15 | Date: xxx, xx xxx xxxx xx:xx:xx xxx 16 | x-darklang-execution-id: 0123456789 17 | Server: darklang 18 | Strict-Transport-Security: max-age=31536000; includeSubDomains; preload 19 | Content-Length: 2 20 | 21 | 13 -------------------------------------------------------------------------------- /docs/production/auditlogs.md: -------------------------------------------------------------------------------- 1 | # Audit logs 2 | 3 | We now have Cloud Audit Logs turned on for our storage buckets. To see these 4 | logs, go to https://console.cloud.google.com/logs/viewer and select "GCS Bucket" 5 | from the resource dropdown. You can also further drill down to a specific 6 | bucket. 7 | 8 | For more details on why we picked this approach and not access logs, or for 9 | links to docs on exporting these logs to another bucket (for analysis outside of 10 | GCloud), see 11 | https://www.notion.so/darklang/Cloud-Storage-Logs-5a686f3d235f47f395f68a75a1e2c794 . 12 | 13 | Tags for search: 14 | audit log auditlog security storage bucket backups 15 | -------------------------------------------------------------------------------- /scripts/deployment/_notify-deployment-rollbar: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | . ./scripts/devcontainer/_assert-in-container "$0" "$@" 3 | 4 | set -euo pipefail 5 | 6 | # Tell rollbar about a deploy 7 | 8 | LOCAL_USERNAME="$(grep 'account' ~/.config/gcloud/configurations/config_default | awk '{print $3}' | awk -F "@" '{print $1}')" 9 | 10 | curl -s https://api.rollbar.com/api/1/deploy/ \ 11 | -F "access_token=${DARK_CONFIG_ROLLBAR_POST_SERVER_ITEM}" \ 12 | -F "environment=production" \ 13 | -F "revision=$(git rev-parse --short HEAD)" \ 14 | -F "comment=$1" \ 15 | -F "local_username=${LOCAL_USERNAME}" > /dev/null 16 | echo "Rollbar notified." 17 | 18 | -------------------------------------------------------------------------------- /backend/src/BuiltinPM/Builtin.fs: -------------------------------------------------------------------------------- 1 | module BuiltinPM.Builtin 2 | 3 | open Prelude 4 | open LibExecution.RuntimeTypes 5 | 6 | module Builtin = LibExecution.Builtin 7 | module PT = LibExecution.ProgramTypes 8 | 9 | 10 | let fnRenames : Builtin.FnRenames = 11 | // old names, new names 12 | // eg: fn "Http" "respond" 0, fn "Http" "response" 0 13 | [] 14 | 15 | let builtins (pm : PT.PackageManager) : Builtins = 16 | Builtin.combine 17 | [ Libs.Packages.builtins pm 18 | Libs.PackageOps.builtins 19 | Libs.Instances.builtins 20 | Libs.Branches.builtins 21 | Libs.Sync.builtins 22 | Libs.Scripts.builtins ] 23 | fnRenames 24 | -------------------------------------------------------------------------------- /backend/src/LibBinarySerialization/Serializers/RT/PackageValue.fs: -------------------------------------------------------------------------------- 1 | module LibBinarySerialization.Serializers.RT.PackageValue 2 | 3 | open System 4 | open System.IO 5 | open Prelude 6 | 7 | open LibExecution.RuntimeTypes 8 | 9 | open LibBinarySerialization.BinaryFormat 10 | open LibBinarySerialization.Serializers.Common 11 | open LibBinarySerialization.Serializers.RT.Common 12 | 13 | let write (w : BinaryWriter) (c : PackageValue.PackageValue) : unit = 14 | Guid.write w c.id 15 | Dval.write w c.body 16 | 17 | let read (r : BinaryReader) : PackageValue.PackageValue = 18 | let id = Guid.read r 19 | let body = Dval.read r 20 | { id = id; body = body } 21 | -------------------------------------------------------------------------------- /backend/src/LibService/Init.fs: -------------------------------------------------------------------------------- 1 | module LibService.Init 2 | 3 | open Prelude 4 | 5 | let init (serviceName : string) : unit = 6 | printTime $"Initing LibService in {serviceName}" 7 | Rollbar.init serviceName 8 | Telemetry.init serviceName 9 | printTime $" Inited LibService in {serviceName}" 10 | 11 | 12 | /// Called when shutting down cloud services. Used to explicitly flush any buffered 13 | /// connections for LaunchDarkly. 14 | let shutdown (serviceName : string) : unit = 15 | printTime $"Shutting down LibService in {serviceName}" 16 | LaunchDarkly.flush () 17 | Telemetry.flush () 18 | printTime $"Shutting down LibService in {serviceName}" 19 | -------------------------------------------------------------------------------- /backend/testfiles/httphandler/response-with-500.test: -------------------------------------------------------------------------------- 1 | [http-handler POST /] 2 | (let body = (request.body |> Darklang.Stdlib.List.length |> Darklang.Stdlib.Int64.toString |> Darklang.Stdlib.String.toBytes) 3 | Darklang.Stdlib.Http.response body 500L) 4 | 5 | [request] 6 | POST / HTTP/1.1 7 | Host: HOST 8 | Date: Sun, 08 Nov 2020 15:38:01 GMT 9 | Content-Length: 13 10 | 11 | 13 characters 12 | 13 | [response] 14 | HTTP/1.1 500 Internal Server Error 15 | Date: xxx, xx xxx xxxx xx:xx:xx xxx 16 | x-darklang-execution-id: 0123456789 17 | Server: darklang 18 | Strict-Transport-Security: max-age=31536000; includeSubDomains; preload 19 | Content-Length: 2 20 | 21 | 13 -------------------------------------------------------------------------------- /scripts/linting/yamllinter: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | . ./scripts/devcontainer/_assert-in-container "$0" "$@" 3 | 4 | set -euo pipefail 5 | 6 | all_yaml_files() { 7 | find ./* -type f \ 8 | \( -name "*.yml" \ 9 | -o -name "*.yaml" \) -print \ 10 | -o -path ".git" -prune \ 11 | -o -path "rundir" -prune \ 12 | -o -path "./rundir" -prune \ 13 | -o -path "backend/paket-files" -prune \ 14 | -o -path "./backend/paket-files" -prune \ 15 | -o -path ".circleci/config.yml" -prune 16 | } 17 | 18 | if [[ "$#" -eq 1 ]]; then 19 | yamllint "$1" 20 | else 21 | echo "linting yaml files ..." 22 | all_yaml_files | xargs yamllint 23 | fi 24 | -------------------------------------------------------------------------------- /tree-sitter-darklang/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "tree-sitter-darklang" 3 | description = "darklang grammar for the tree-sitter parsing library" 4 | version = "0.0.1" 5 | keywords = ["incremental", "parsing", "darklang"] 6 | categories = ["parsing", "text-editors"] 7 | repository = "https://github.com/darklang/dark" 8 | edition = "2018" 9 | license = "MIT" 10 | 11 | build = "bindings/rust/build.rs" 12 | include = [ 13 | "bindings/rust/*", 14 | "grammar.js", 15 | "queries/*", 16 | "src/*", 17 | ] 18 | 19 | [lib] 20 | path = "bindings/rust/lib.rs" 21 | 22 | [dependencies] 23 | tree-sitter = "~0.20.10" 24 | 25 | [build-dependencies] 26 | cc = "1.0" 27 | -------------------------------------------------------------------------------- /packages/darklang/cli/packages/display.dark: -------------------------------------------------------------------------------- 1 | module Darklang.Cli.Packages.Display 2 | 3 | /// Icon mappings for different entity types 4 | let getIcon (entityType: EntityType) : String = 5 | match entityType with 6 | | Module -> "🗂️" 7 | | Function -> "⚡" 8 | | Type -> "🏷️" 9 | | Value -> "💎" 10 | 11 | 12 | /// Get section header with icon for entity type 13 | let getSectionHeader (entityType: String) : String = 14 | match entityType with 15 | | "module" -> "🗂️ Modules:" 16 | | "function" -> "⚡ Functions:" 17 | | "type" -> "🏷️ Types:" 18 | | "value" -> "💎 Values:" 19 | | "submodule" -> "🗂️ Submodules:" 20 | | _ -> entityType ++ ":" 21 | -------------------------------------------------------------------------------- /backend/testfiles/httphandler/simple-injected-string-post.test: -------------------------------------------------------------------------------- 1 | [http-handler POST /] 2 | (let body = (request.body |> Darklang.Stdlib.List.length |> Darklang.Stdlib.Int64.toString |> Darklang.Stdlib.String.toBytes) 3 | Darklang.Stdlib.Http.response body 200L) 4 | 5 | [request] 6 | POST / HTTP/1.1 7 | Host: HOST 8 | Date: Sun, 08 Nov 2020 15:38:01 GMT 9 | Content-Length: 11 10 | 11 | 12 | 13 | [response] 14 | HTTP/1.1 200 OK 15 | Date: xxx, xx xxx xxxx xx:xx:xx xxx 16 | x-darklang-execution-id: 0123456789 17 | Server: darklang 18 | Strict-Transport-Security: max-age=31536000; includeSubDomains; preload 19 | Content-Length: 2 20 | 21 | 11 -------------------------------------------------------------------------------- /backend/testfiles/httphandler/injected-icon-post-length.test: -------------------------------------------------------------------------------- 1 | [http-handler POST /] 2 | (let body = (request.body |> Darklang.Stdlib.List.length |> Darklang.Stdlib.Int64.toString |> Darklang.Stdlib.String.toBytes) 3 | Darklang.Stdlib.Http.response body 200L) 4 | 5 | [request] 6 | POST / HTTP/1.1 7 | Host: HOST 8 | Date: Sun, 08 Nov 2020 15:38:01 GMT 9 | Content-Length: 454 10 | 11 | 12 | 13 | [response] 14 | HTTP/1.1 200 OK 15 | Date: xxx, xx xxx xxxx xx:xx:xx xxx 16 | x-darklang-execution-id: 0123456789 17 | Server: darklang 18 | Strict-Transport-Security: max-age=31536000; includeSubDomains; preload 19 | Content-Length: 3 20 | 21 | 454 -------------------------------------------------------------------------------- /.circleci/gcp-workload-identity-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "external_account", 3 | "audience": "//iam.googleapis.com/projects/234768451432/locations/global/workloadIdentityPools/circleci/providers/circleci", 4 | "subject_token_type": "urn:ietf:params:oauth:token-type:jwt", 5 | "token_url": "https://sts.googleapis.com/v1/token", 6 | "service_account_impersonation_url": "https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/circleci-deployer@darklang-next.iam.gserviceaccount.com:generateAccessToken", 7 | "credential_source": { 8 | "file": "/home/dark/app/CIRCLE_OIDC_TOKEN_FILE", 9 | "format": { 10 | "type": "text" 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "FSharp.inlayHints.typeAnnotations": false, 3 | "[fsharp]": { 4 | "editor.inlayHints.enabled": "off" 5 | }, 6 | "FSharp.inlayHints.parameterNames": false, 7 | "FSharp.workspacePath": "./backend/fsdark.sln", 8 | "files.associations": { 9 | "*.dark": "fsharp" 10 | }, 11 | "[vue]": { 12 | "editor.defaultFormatter": "esbenp.prettier-vscode" 13 | }, 14 | "[html]": { 15 | "editor.defaultFormatter": "esbenp.prettier-vscode" 16 | }, 17 | "[javascript]": { 18 | "editor.defaultFormatter": "esbenp.prettier-vscode" 19 | }, 20 | "[typescript]": { 21 | "editor.defaultFormatter": "esbenp.prettier-vscode" 22 | }, 23 | } 24 | -------------------------------------------------------------------------------- /backend/testfiles/httpclient/README.md: -------------------------------------------------------------------------------- 1 | # Dark HttpClient test files 2 | 3 | The files in this directory are tests of the `HttpClient::request` standard 4 | library function. The test suite sets up a server that we can make HTTP requests 5 | against, using HttpClient. It then checks the request is as expected, and 6 | returns a response to be parsed by the HTTP client. 7 | 8 | These files are read, parsed, and evaluated by `HttpClient.Tests.fs`. 9 | 10 | --- 11 | 12 | This all works almost exactly how the `HttpClient::` functions are tested, in the 13 | corresponding `httpclient` directory. Please refer to the `README.md` there for 14 | details on how these tests are set up. 15 | -------------------------------------------------------------------------------- /backend/src/Prelude/Regex.fs: -------------------------------------------------------------------------------- 1 | module Regex 2 | 3 | open System.Text.RegularExpressions 4 | 5 | // Active pattern for regexes 6 | let (|Regex|_|) (pattern : string) (input : string) = 7 | let m = Regex.Match(input, pattern) 8 | if m.Success then Some(List.tail [ for g in m.Groups -> g.Value ]) else None 9 | 10 | let (|RegexAny|_|) (pattern : string) (input : string) = 11 | let options = RegexOptions.Singleline 12 | let m = Regex.Match(input, pattern, options) 13 | if m.Success then Some(List.tail [ for g in m.Groups -> g.Value ]) else None 14 | 15 | 16 | let matches (pattern : string) (input : string) : bool = 17 | let m = Regex.Match(input, pattern) 18 | m.Success 19 | -------------------------------------------------------------------------------- /backend/testfiles/httphandler/url-custom-domain.test: -------------------------------------------------------------------------------- 1 | [http-handler GET /a] 2 | Darklang.Stdlib.Http.response (Darklang.Stdlib.String.toBytes request.url) 200L 3 | 4 | [domain customdomain.myspecialdomain.com] 5 | 6 | [request] 7 | GET /a HTTP/1.1 8 | Host: customdomain.myspecialdomain.com 9 | Date: Sun, 08 Nov 2020 15:38:01 GMT 10 | Content-Length: 0 11 | X-forwarded-proto: https 12 | 13 | 14 | 15 | [response] 16 | HTTP/1.1 200 OK 17 | Date: xxx, xx xxx xxxx xx:xx:xx xxx 18 | x-darklang-execution-id: 0123456789 19 | Server: darklang 20 | Strict-Transport-Security: max-age=31536000; includeSubDomains; preload 21 | Content-Length: LENGTH 22 | 23 | https://customdomain.myspecialdomain.com/a -------------------------------------------------------------------------------- /backend/testfiles/httphandler/x-forwarded-proto-ignored.test: -------------------------------------------------------------------------------- 1 | [http-handler GET /a] 2 | Darklang.Stdlib.Http.response (Darklang.Stdlib.String.toBytes request.url) 200L 3 | 4 | 5 | [domain forwarded.myspecialdomain.com] 6 | 7 | [request] 8 | GET /a HTTP/1.1 9 | Host: forwarded.myspecialdomain.com 10 | Date: Sun, 08 Nov 2020 15:38:01 GMT 11 | Content-Length: 0 12 | X-forwarded-proto: ftp 13 | 14 | 15 | 16 | [response] 17 | HTTP/1.1 200 OK 18 | Date: xxx, xx xxx xxxx xx:xx:xx xxx 19 | x-darklang-execution-id: 0123456789 20 | Server: darklang 21 | Strict-Transport-Security: max-age=31536000; includeSubDomains; preload 22 | Content-Length: LENGTH 23 | 24 | http://forwarded.myspecialdomain.com/a -------------------------------------------------------------------------------- /config/local.template: -------------------------------------------------------------------------------- 1 | ###################################### 2 | # XXX: Changes won't take effect until you restart script/builder 3 | # or rebuild the vscode dev container 4 | # 5 | # If using a VS Code devcontainer, you need to uncomment out the lines to enable this 6 | ###################################### 7 | 8 | # Values set here override the defaults in config/dev (or whatever config is loaded) 9 | 10 | ###################################### 11 | # XXX: Changes won't take effect until you restart script/builder 12 | # or rebuild the vscode dev container 13 | # 14 | # If using a VS Code devcontainer, you need to uncomment out the lines to enable this 15 | ###################################### -------------------------------------------------------------------------------- /scripts/deployment/new-deploy.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | . ./scripts/devcontainer/_assert-in-container "$0" "$@" 3 | 4 | set -euo pipefail 5 | 6 | set -x 7 | 8 | # Read each entry from the JSON file and deploy the container 9 | jq -c '.[]' image-digests.json | while IFS= read -r line; do 10 | container=$(echo "$line" | jq -r '.name') 11 | image=$(echo "$line" | jq -r '.digest') 12 | 13 | # ProdExec is only deployed on demand 14 | if [[ "$container" == "prodexec" ]]; then 15 | continue 16 | fi 17 | 18 | gcloud run services update "$container" \ 19 | --project darklang-next \ 20 | --region us-central1 \ 21 | --image "$image" 22 | done 23 | 24 | -------------------------------------------------------------------------------- /backend/testfiles/execution/language/interpreter.dark: -------------------------------------------------------------------------------- 1 | // Tests that don't quite fit in the other files, 2 | // and are more focused on interpreter behavior. 3 | 4 | 5 | // This test used to cause a stack overflow, but now passes everywhere. 6 | // Let's keep this around to ensure we never regress. 7 | ((Stdlib.List.repeat_v0 348L 1L) 8 | |> Builtin.unwrap 9 | |> Stdlib.List.map (fun _f -> 1) 10 | |> Stdlib.List.length) = 348L 11 | 12 | // Just to show off, let's increase the size of the list 13 | // (CLEANUP include this -- it's just a bit slow right now.) 14 | // ((Stdlib.List.repeat_v0 3480L 1L) 15 | // |> Builtin.unwrap 16 | // |> Stdlib.List.map (fun _f -> 1) 17 | // |> Stdlib.List.length) = 3480L -------------------------------------------------------------------------------- /packages/darklang/scm/branch.dark: -------------------------------------------------------------------------------- 1 | module Darklang.SCM.Branch 2 | 3 | type Branch = 4 | { id: Uuid 5 | name: String 6 | createdAt: DateTime 7 | mergedAt: Stdlib.Option.Option } 8 | 9 | 10 | /// List all branches 11 | let list () : List = Builtin.scmBranchList () 12 | 13 | /// Get a specific branch by ID 14 | let get (branchID: Uuid) : Stdlib.Option.Option = 15 | Builtin.scmBranchGet branchID 16 | 17 | /// Find branches by name (may return multiple if names collide) 18 | let findByName (name: String) : List = 19 | Builtin.scmBranchFindByName name 20 | 21 | /// Create a new branch 22 | let create (name: String) : Branch = 23 | Builtin.scmBranchCreate name 24 | -------------------------------------------------------------------------------- /backend/src/LibExecution/DarkDateTime.fs: -------------------------------------------------------------------------------- 1 | module LibExecution.DarkDateTime 2 | 3 | open NodaTime 4 | 5 | open Prelude 6 | 7 | // A datetime in Dark is always in UTC, so we don't include the utc info 8 | type T = LocalDateTime 9 | let utc = DateTimeZone.Utc 10 | 11 | let toZonedDateTime (dt : T) = ZonedDateTime(dt, utc, Offset.Zero) 12 | 13 | let toInstant (dt : T) = (toZonedDateTime dt).ToInstant() 14 | 15 | let toDateTimeUtc (dt : T) = (toInstant dt).ToDateTimeUtc() 16 | 17 | let fromInstant (i : Instant) : T = i.toUtcLocalTimeZone () 18 | 19 | let fromDateTime (dt : System.DateTime) : T = 20 | Instant.FromDateTimeUtc dt |> fromInstant 21 | 22 | let toIsoString (d : T) : string = (toInstant d).toIsoString () 23 | -------------------------------------------------------------------------------- /vscode-extension/README.md: -------------------------------------------------------------------------------- 1 | # Darklang VS Code Extension 2 | 3 | This is a work-in-progress, not yet ready for general consumption. 4 | 5 | Join [our Discord](https://darklang.com/discord-invite) to learn more. 6 | 7 | ## Running it 8 | 9 | - (run whole repo in devcontainer -- see root README) 10 | - `cd` to this dir 11 | - `npm i` 12 | - hit F5 13 | 14 | ## Publishing 15 | 16 | The extension is automatically published to the VS Code Marketplace when: 17 | 18 | 1. The version in [`package.json`](./package.json) is bumped 19 | 2. The change is merged to the `main` branch 20 | 21 | The CI pipeline compares the package.json version with the marketplace version and only publishes if the package.json version is newer. 22 | -------------------------------------------------------------------------------- /scripts/build/_dotnet-wrapper: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | . ./scripts/devcontainer/_assert-in-container "$0" "$@" 3 | 4 | # note: no set -e 5 | set -uo pipefail 6 | set +e 7 | 8 | # run the last segment of a pipeline in the current shell. This allows getting 9 | # the exit code. 10 | shopt -s lastpipe 11 | 12 | cd backend && dotnet "$@" 2>&1 | while read -r line; do 13 | # this error consistently breaks our compile in the build script 14 | if [[ "$line" == *"warning MSB3026: Could not copy "* ]]; then 15 | echo "Saw a failed copy, killing servers" 16 | killall BwdServer || true 17 | killall CronChecker || true 18 | killall QueueWorker || true 19 | killall Tests || true 20 | fi 21 | echo "$line" 22 | done -------------------------------------------------------------------------------- /backend/src/LibService/Kestrel.fs: -------------------------------------------------------------------------------- 1 | module LibService.Kestrel 2 | 3 | open Prelude 4 | 5 | type KestrelServerOptions = 6 | Microsoft.AspNetCore.Server.Kestrel.Core.KestrelServerOptions 7 | 8 | // Apply some simple options so things don't get out of control 9 | let configureKestrel (opts : KestrelServerOptions) : unit = 10 | opts.Limits.MaxConcurrentConnections <- 1000L 11 | opts.Limits.MaxConcurrentUpgradedConnections <- 0L // don't support websockets 12 | opts.Limits.MaxRequestBodySize <- 10L * 1024L * 1024L // 10MB 13 | opts.Limits.RequestHeadersTimeout <- System.TimeSpan.FromSeconds 10.0 14 | opts.AllowSynchronousIO <- false // prevent deadlock of some kind 15 | opts.AddServerHeader <- false // Don't add kestrel 16 | -------------------------------------------------------------------------------- /packages/darklang/languageServerProtocol/tracing.dark: -------------------------------------------------------------------------------- 1 | // Supports: 2 | // - the client setting a trace level 3 | // - the server logging a trace 4 | 5 | (* 6 | namespace SetTraceNotification { 7 | export const method: '$/setTrace' = '$/setTrace'; 8 | export const messageDirection: MessageDirection = MessageDirection.clientToServer; 9 | export const type = new ProtocolNotificationType(method); 10 | } 11 | 12 | namespace LogTraceNotification { 13 | export const method: '$/logTrace' = '$/logTrace'; 14 | export const messageDirection: MessageDirection = MessageDirection.serverToClient; 15 | export const type = new ProtocolNotificationType(method); 16 | } 17 | *) -------------------------------------------------------------------------------- /backend/testfiles/execution/language/custom-data/record-field-acess.dark: -------------------------------------------------------------------------------- 1 | type MyRecord = { col1: Int64 } 2 | 3 | (let x = MyRecord { col1 = 1L } in x.col1) = 1L 4 | 5 | module Errors = 6 | (let x = MyRecord { col1 = 1L } in x.___) = 7 | (Builtin.testDerrorMessage "Field name is empty") 8 | 9 | (let x = MyRecord { col1 = 1L } in x.fieldName) = 10 | (Builtin.testDerrorMessage "Tried to access field `fieldName`, but it doesn't exist") 11 | 12 | (Builtin.testRuntimeError "error").fieldName = 13 | (Builtin.testDerrorMessage "Uncaught exception: error") 14 | 15 | (let x = 6L in x.fieldName) = 16 | (Builtin.testDerrorMessage 17 | "Attempting to perform field access of an Int64, but this only works with records") -------------------------------------------------------------------------------- /backend/testfiles/httphandler/simple-response-headers.test: -------------------------------------------------------------------------------- 1 | [http-handler GET /] 2 | (let body = (Darklang.Stdlib.String.toBytes "1") 3 | let headers = [("header1", "value1"); ("Header2", "Value2"); ("Header-3", "Value-3")] 4 | Darklang.Stdlib.Http.responseWithHeaders body headers 200L) 5 | 6 | [request] 7 | GET / HTTP/1.1 8 | Host: HOST 9 | Date: Sun, 08 Nov 2020 15:38:01 GMT 10 | Content-Length: 7 11 | 12 | ignored 13 | 14 | [response] 15 | HTTP/1.1 200 OK 16 | Date: xxx, xx xxx xxxx xx:xx:xx xxx 17 | header-3: Value-3 18 | header2: Value2 19 | x-darklang-execution-id: 0123456789 20 | Server: darklang 21 | header1: value1 22 | Strict-Transport-Security: max-age=31536000; includeSubDomains; preload 23 | Content-Length: 1 24 | 25 | 1 -------------------------------------------------------------------------------- /backend/testfiles/execution/language/basic/estring.dark: -------------------------------------------------------------------------------- 1 | $"""test {"1"}""" = "test 1" 2 | 3 | (let one = "1" in $"test {one}") = "test 1" 4 | 5 | (let one = 1.0 in $"test {one}") = 6 | Builtin.testDerrorMessage "Expected String in string interpolation, got a Float (1.0) instead" 7 | 8 | (let one = 1L in $"test {one}") = 9 | Builtin.testDerrorMessage "Expected String in string interpolation, got an Int64 (1) instead" 10 | 11 | (let name = "John" 12 | let age = "30" 13 | $"Name: {name}, Age: {age} years old.") = "Name: John, Age: 30 years old." 14 | 15 | (let two = 2L in "test 1" == $"test {one}") = 16 | Builtin.testDerrorMessage "There is no variable named: one" 17 | 18 | (let one = 1L in $"test {Stdlib.Int64.toString one}") = "test 1" -------------------------------------------------------------------------------- /containers/bwdserver/Dockerfile: -------------------------------------------------------------------------------- 1 | # Dockerfile for the grand-user facing Dark app at darklang.io 2 | 3 | FROM darkfsharpservice:latest 4 | 5 | WORKDIR /home/dark 6 | 7 | COPY --chown=dark:dark scripts scripts 8 | 9 | # Add favicon 10 | RUN mkdir -p webroot/static 11 | COPY --chown=dark:dark backend/static/favicon-32x32.png webroot/static/favicon-32x32.png 12 | 13 | RUN mkdir app 14 | 15 | # Setting this now means we can set the filesystem to readonly 16 | ENV DARK_CONFIG_RUNDIR=/home/dark/gcp-rundir 17 | RUN ./scripts/devcontainer/_create-app-directories 18 | 19 | COPY --chown=dark:dark backend/Build/out/BwdServer/Release/net8.0/linux-x64/publish/* app/ 20 | RUN ./scripts/linting/_check-linked-libs app/BwdServer 21 | 22 | CMD ./app/BwdServer -------------------------------------------------------------------------------- /scripts/build/reload-packages: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | . ./scripts/devcontainer/_assert-in-container "$0" "$@" 3 | 4 | set -euo pipefail 5 | 6 | TEST=false 7 | PUBLISHED_FLAG= 8 | 9 | for i in "$@" 10 | do 11 | case "${i}" in 12 | --test) 13 | TEST=true 14 | shift 15 | ;; 16 | --published) 17 | PUBLISHED_FLAG=$i 18 | ;; 19 | esac 20 | done 21 | 22 | if [[ "$TEST" == "true" ]]; then 23 | LOG_CANVAS="${DARK_CONFIG_RUNDIR}/logs/test-packages-canvas.log" 24 | else 25 | LOG_CANVAS="${DARK_CONFIG_RUNDIR}/logs/packages-canvas.log" 26 | fi 27 | 28 | 29 | ./scripts/run-local-exec $PUBLISHED_FLAG reload-packages > $LOG_CANVAS 2>&1 30 | echo -e "Done reloading packages from packages/**/*.dark files to internal SQLite DB" -------------------------------------------------------------------------------- /backend/migrations/20250820_000000_rename_package_constants_to_values.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE IF NOT EXISTS 2 | package_values_v0 3 | ( id TEXT PRIMARY KEY 4 | , owner TEXT NOT NULL -- e.g. Darklang 5 | , modules TEXT NOT NULL -- e.g. Math.Geometry 6 | , name TEXT NOT NULL -- e.g. pi 7 | , pt_def BLOB -- serialized PT.PackageValue 8 | , rt_dval BLOB -- serialized RT.PackageValue 9 | , created_at TEXT NOT NULL DEFAULT (datetime('now')) 10 | ); 11 | 12 | CREATE INDEX IF NOT EXISTS idx_package_values_pt_def 13 | ON package_values_v0(id) WHERE pt_def IS NOT NULL; 14 | 15 | CREATE INDEX IF NOT EXISTS idx_package_values_rt_dval 16 | ON package_values_v0(id) WHERE rt_dval IS NOT NULL; 17 | 18 | -- Drop the package_constants_v0 table 19 | DROP TABLE package_constants_v0; -------------------------------------------------------------------------------- /packages/darklang/languageTools/common.dark: -------------------------------------------------------------------------------- 1 | module Darklang.LanguageTools 2 | 3 | 4 | // TODO: should this be UInt64? 5 | type ID = Int64 6 | 7 | type TLID = UInt64 8 | 9 | type Sign = 10 | | Positive 11 | | Negative 12 | 13 | type BuiltinFunctionParameter = 14 | { name: String 15 | ``type``: RuntimeTypes.TypeReference } 16 | 17 | /// A Darklang builtin function 18 | type BuiltinFunction = 19 | { name: RuntimeTypes.FQFnName.Builtin 20 | description: String 21 | parameters: List 22 | returnType: RuntimeTypes.TypeReference } 23 | 24 | /// A Darklang builtin value 25 | type BuiltinValue = 26 | { name: RuntimeTypes.FQValueName.Builtin 27 | description: String 28 | ``type``: RuntimeTypes.TypeReference } 29 | -------------------------------------------------------------------------------- /docs/benchmarking.md: -------------------------------------------------------------------------------- 1 | # Some notes on benchmarking 2 | 3 | ## General 4 | 5 | - Benchmarking in a VS Code container doesn't work - VS Code seems to do something to each request 6 | - the F# server will happily spread out to lots of cores and it's hard to siturate it. At `--cpus 2` it's easier to watch 7 | - ensure you optimize by passing the `--optimize` flag to `scripts/builder` or `scripts/build/_build-server` 8 | 9 | ## Profiling .NET 10 | 11 | - dotnet tool install dotnet-trace 12 | - dotnet trace ps # get id of BwdServer 13 | - dotnet trace collect --format SpeedScope -p ID 14 | - upload the file to https://speedscope.app for a flame graph 15 | 16 | ## Using ab 17 | 18 | - sudo apt install apache2-utils # install 19 | - ab -n 2000 -c 50 URL # good settings 20 | -------------------------------------------------------------------------------- /backend/src/LibClientTypes/LibClientTypes.fsproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Library 5 | net8.0 6 | 8.0 7 | 8 | false 9 | false 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /backend/src/LibPackageManager/Caching.fs: -------------------------------------------------------------------------------- 1 | module LibPackageManager.Caching 2 | 3 | open System.Threading.Tasks 4 | open FSharp.Control.Tasks 5 | open System.Collections.Concurrent 6 | 7 | open Prelude 8 | 9 | 10 | let withCache (f : 'key -> Ply>) = 11 | let cache = ConcurrentDictionary<'key, 'value>() 12 | fun (key : 'key) -> 13 | uply { 14 | let mutable cached = Unchecked.defaultof<'value> 15 | let inCache = cache.TryGetValue(key, &cached) 16 | if inCache then 17 | return Some cached 18 | else 19 | //debuG "missed" key 20 | let! result = f key 21 | match result with 22 | | Some v -> cache.TryAdd(key, v) |> ignore 23 | | None -> () 24 | return result 25 | } 26 | -------------------------------------------------------------------------------- /backend/testfiles/execution/stdlib/bytes.dark: -------------------------------------------------------------------------------- 1 | Stdlib.Bytes.hexEncode_v0 (Stdlib.String.toBytes_v0 "123qwc") = "313233717763" 2 | 3 | Stdlib.Bytes.hexEncode_v0 (Stdlib.String.toBytes_v0 "اختبار النص") = "D8A7D8AED8AAD8A8D8A7D8B120D8A7D984D986D8B5" 4 | 5 | Stdlib.Bytes.hexEncode_v0 (Stdlib.String.toBytes_v0 "👱👱🏻👱🏼👱🏽👱🏾👱🏿") = "F09F91B1F09F91B1F09F8FBBF09F91B1F09F8FBCF09F91B1F09F8FBDF09F91B1F09F8FBEF09F91B1F09F8FBF" 6 | 7 | Stdlib.Bytes.hexEncode_v0 ( 8 | Stdlib.String.toBytes_v0 9 | "dlkjkd329823333333333fjfidjsfudsdhs}{||!|!|!|!!$%^&^&﷽﷽﷽﷽﷽﷽﷽﷽﷽﷽﷽﷽﷽﷽﷽﷽" 10 | ) = "646C6B6A6B64333239383233333333333333333333666A6669646A73667564736468737D7B7C7C217C217C217C212124255E265E26EFB7BDEFB7BDEFB7BDEFB7BDEFB7BDEFB7BDEFB7BDEFB7BDEFB7BDEFB7BDEFB7BDEFB7BDEFB7BDEFB7BDEFB7BDEFB7BD" -------------------------------------------------------------------------------- /packages/darklang/cli/installation/helpers.dark: -------------------------------------------------------------------------------- 1 | // CLEANUP this might as well be one dir up 2 | // or moved to the version namespace 3 | module Darklang.Cli.Installation.Helpers 4 | 5 | let cliVersion () : String = 6 | let hash = Builtin.getBuildHash () 7 | "alpha-" ++ hash 8 | 9 | let getVersionInfo () : String = 10 | let currentVersion = cliVersion () 11 | match GitHub.Releases.getLatestReleaseBuildHash () with 12 | | Ok latestVersion -> 13 | if currentVersion == latestVersion then 14 | "Darklang CLI " ++ currentVersion ++ " (up to date)" 15 | else 16 | "Darklang CLI " ++ currentVersion ++ " (latest: " ++ latestVersion ++ " - update available!)" 17 | | Error _ -> 18 | "Darklang CLI " ++ currentVersion ++ " (unable to check for updates)" -------------------------------------------------------------------------------- /docs/unittests.md: -------------------------------------------------------------------------------- 1 | # Unit tests 2 | 3 | ## Overview 4 | 5 | Unit tests run automatically on the backend, as part of the builder script, if 6 | enabled with `--test`. This is actually super slow, so I wouldn't recommend it. 7 | 8 | ## Backend 9 | 10 | The entry point is `backend/tests/Tests/Tests.fs`. Run tests from the 11 | command line using: 12 | 13 | `scripts/run-backend-tests` 14 | 15 | Run `scripts/run-backend-tests --help` for options. In particular, to run only 16 | tests with XXX in their names: 17 | 18 | `scripts/run-backend-tests --filter-test-case XXX` 19 | 20 | Or to run only testlists with XXX in their names: 21 | 22 | `scripts/run-backend-tests --filter-test-list XXX` 23 | 24 | Tests are _not_ automatically discovered; they must be added to `Tests.fs`. 25 | -------------------------------------------------------------------------------- /packages/darklang/languageTools/lsp-server/aaaa-state.dark: -------------------------------------------------------------------------------- 1 | module Darklang.LanguageTools.LspServer 2 | 3 | 4 | type DocumentInScope = 5 | { uri: String 6 | text: String 7 | parsed: Stdlib.Result.Result 8 | parsedToPT: Stdlib.Option.Option } 9 | 10 | type LspState = 11 | { 12 | initialized: Bool 13 | 14 | shouldShutdown: Bool 15 | 16 | /// The branch context for this LSP session 17 | branchID: Stdlib.Option.Option 18 | 19 | /// Documents that are currently open in the editor 20 | /// (i.e. have been `textDocument/didOpen`ed, and not yet `textDocument/didClose`d) 21 | /// note: the string key here is the URI 22 | documentsInScope: Dict 23 | } -------------------------------------------------------------------------------- /tree-sitter-darklang/test/corpus/exhaustive/exprs/binary_operation.txt: -------------------------------------------------------------------------------- 1 | ================== 2 | logical AND 3 | ================== 4 | 5 | true && true 6 | 7 | --- 8 | 9 | (source_file 10 | (expression 11 | (simple_expression 12 | (infix_operation 13 | (simple_expression (bool_literal)) 14 | (operator) 15 | (simple_expression (bool_literal)) 16 | ) 17 | ) 18 | ) 19 | ) 20 | 21 | 22 | 23 | ================== 24 | logical OR 25 | ================== 26 | true || true 27 | 28 | --- 29 | 30 | (source_file 31 | (expression 32 | (simple_expression 33 | (infix_operation 34 | (simple_expression (bool_literal)) 35 | (operator) 36 | (simple_expression (bool_literal)) 37 | ) 38 | ) 39 | ) 40 | ) 41 | -------------------------------------------------------------------------------- /backend/src/LibService/HSTS.fs: -------------------------------------------------------------------------------- 1 | module LibService.HSTS 2 | 3 | open Microsoft.AspNetCore.HttpsPolicy 4 | 5 | // The traditional methods of using `UseHsts` and `AddHsts` within BwdServer 6 | // were ineffective. Somehow, the Strict-Transport-Security header was not 7 | // included in HTTP Reponses as a result of these efforts. Here, we manually 8 | // work around this by setting it manually. 9 | // CLEANUP: replace this with the more traditional approach, somehow 10 | 11 | // If you update these, please ensure the below match each other 12 | 13 | let setConfig (options : HstsOptions) = 14 | options.Preload <- true 15 | options.IncludeSubDomains <- true 16 | options.MaxAge <- System.TimeSpan.FromDays 365 17 | 18 | let stringConfig = "max-age=31536000; includeSubDomains; preload" 19 | -------------------------------------------------------------------------------- /backend/testfiles/execution/language/basic/eand.dark: -------------------------------------------------------------------------------- 1 | (true && true) = true 2 | (true && false) = false 3 | (false && true) = false 4 | (false && false) = false 5 | (true && Builtin.testRuntimeError "msg") = Builtin.testDerrorMessage "Uncaught exception: msg" 6 | (true && 5L) = Builtin.testDerrorMessage "&& only supports Booleans" 7 | (false && 5L) = false 8 | //TODO bring back short-circuiting 9 | //(false && Builtin.testRuntimeError "msg") = false 10 | 11 | (Builtin.testRuntimeError "msg1" && Builtin.testRuntimeError "msg2") = Builtin.testDerrorMessage "Uncaught exception: msg1" 12 | 13 | (5 && true) = Builtin.testDerrorMessage "&& only supports Booleans" 14 | (true |> (&&) true) = true 15 | (true |> (&&) false) = false 16 | (false |> (&&) true) = false 17 | (false |> (&&) false) = false -------------------------------------------------------------------------------- /backend/testfiles/execution/language/basic/eor.dark: -------------------------------------------------------------------------------- 1 | (true || true) = true 2 | (true || false) = true 3 | (false || true) = true 4 | (false || false) = false 5 | 6 | (true || 5L) = true 7 | (false || 5L) = Builtin.testDerrorMessage "|| only supports Booleans" 8 | (5L || true) = Builtin.testDerrorMessage "|| only supports Booleans" 9 | 10 | (true |> (||) true) = true 11 | (true |> (||) false) = true 12 | (false |> (||) true) = true 13 | (false |> (||) false) = false 14 | 15 | // TODO bring back short-circuiting 16 | //(true || Builtin.testRuntimeError "msg") = true 17 | (false || Builtin.testRuntimeError "msg") = Builtin.testDerrorMessage "Uncaught exception: msg" 18 | 19 | (Builtin.testRuntimeError "msg1" || Builtin.testRuntimeError "msg2") = Builtin.testDerrorMessage "Uncaught exception: msg1" -------------------------------------------------------------------------------- /packages/darklang/stdlib/cli/process.dark: -------------------------------------------------------------------------------- 1 | module Darklang.Stdlib.Cli.Process 2 | 3 | 4 | /// Process handle for interacting with spawned processes 5 | type ProcessHandle = Int64 6 | 7 | /// Spawn a new process and return a handle for interaction 8 | let spawn (command: String) : ProcessHandle = 9 | Builtin.cliSpawnProcess command 10 | 11 | /// Send input to a process and read any available output (non-blocking) 12 | /// Use empty string for input to just read output without sending anything 13 | let communicate (handle: ProcessHandle) (input: String) : Stdlib.Cli.ExecutionOutcome = 14 | Builtin.cliProcessIO handle input 15 | 16 | /// Terminate a process and get final output 17 | let terminate (handle: ProcessHandle) : Stdlib.Cli.ExecutionOutcome = 18 | Builtin.cliTerminateProcess handle 19 | -------------------------------------------------------------------------------- /backend/src/LibDB/LibDB.fsproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Library 5 | net8.0 6 | 8.0 7 | 8 | false 9 | true 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /packages/darklang/languageTools/lsp-server/showDocument.dark: -------------------------------------------------------------------------------- 1 | module Darklang.LanguageTools.LspServer.ShowDocument 2 | 3 | 4 | let makeShowDocumentRequest 5 | (uri: String) 6 | (requestId: JsonRPC.RequestId) 7 | : String = 8 | let showDocParams = 9 | (LanguageServerProtocol.Window.ShowDocument.ShowDocumentRequest.ShowDocumentParams.ShowDocumentParams 10 | { uri = uri 11 | external = Stdlib.Option.Option.None 12 | takeFocus = Stdlib.Option.Option.Some(true) 13 | selection = Stdlib.Option.Option.None }) 14 | 15 | |> LanguageServerProtocol.Window.ShowDocument.ShowDocumentRequest.ShowDocumentParams.toJson 16 | 17 | JsonRPC.Request.makeString 18 | ("window/showDocument") 19 | (Stdlib.Option.Option.Some(requestId)) 20 | (Stdlib.Option.Option.Some(showDocParams)) -------------------------------------------------------------------------------- /scripts/run-pubsub-emulator: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | . ./scripts/devcontainer/_assert-in-container "$0" "$@" 3 | 4 | set -euo pipefail 5 | 6 | 7 | LOGS="${DARK_CONFIG_RUNDIR}/logs" 8 | LOG="$LOGS/pub-sub-emulator.log" 9 | 10 | grey="\033[1;30m" 11 | reset="\033[0m" 12 | 13 | echo -e "Running PubSub emulator ${grey}($LOG)${reset}" 14 | 15 | # This is slow to start up, so if it's already loaded, don't restart 16 | if [[ $(pgrep --full 'cloud-pubsub-emulator-.*.jar') == "" ]] ; then 17 | gcloud beta emulators pubsub start \ 18 | --host-port=$PUBSUB_EMULATOR_HOST \ 19 | > "$LOG" 2>&1 & 20 | fi 21 | 22 | # Pubsub takes ~1s to startup 23 | while ! curl --output /dev/null --silent --head http://$PUBSUB_EMULATOR_HOST ; do 24 | sleep 0.3 25 | echo . 26 | done 27 | 28 | echo "Finished loading PubSub emulator" 29 | -------------------------------------------------------------------------------- /backend/testfiles/httpclient/v0/response-redirect-to-file.test: -------------------------------------------------------------------------------- 1 | [expected-request] 2 | GET PATH HTTP/1.1 3 | Host: HOST 4 | Content-Length: 0 5 | 6 | [response] 7 | HTTP/1.1 301 Moved Permanently 8 | Date: xxx, xx xxx xxxx xx:xx:xx xxx 9 | Location: file:////etc/passwd 10 | 11 | 12 | [test] 13 | (let response = (Darklang.Stdlib.HttpClient.request "get" "http://URL" [] [] |> Builtin.unwrap) 14 | let respHeaders = (response.headers |> Darklang.Stdlib.List.filter (fun h -> (Darklang.Stdlib.Tuple2.first h) != "date")) 15 | {response with headers = respHeaders}) == 16 | Darklang.Stdlib.HttpClient.Response 17 | { statusCode = 301L 18 | headers = 19 | [ 20 | ("server", "kestrel") 21 | ("location", "file://etc/passwd") 22 | ("transfer-encoding", "chunked") 23 | ] 24 | body = [] } -------------------------------------------------------------------------------- /packages/darklang/languageServerProtocol/window/logMessage.dark: -------------------------------------------------------------------------------- 1 | // Server->Client notification to log a message (to console or something) 2 | 3 | (* 4 | /// The log message notification is sent from the server to the client to ask 5 | /// the client to log a particular message. 6 | export namespace LogMessageNotification { 7 | export const method: 'window/logMessage' = 'window/logMessage'; 8 | export const messageDirection: MessageDirection = MessageDirection.serverToClient; 9 | export const type = new ProtocolNotificationType(method); 10 | } 11 | 12 | /// The log message parameters. 13 | export interface LogMessageParams { 14 | /// The message type. See {@link MessageType} 15 | type: MessageType; 16 | 17 | /// The actual message. 18 | message: string; 19 | } 20 | *) -------------------------------------------------------------------------------- /backend/testfiles/httpclient/v0/basic-head.test: -------------------------------------------------------------------------------- 1 | [expected-request] 2 | HEAD PATH HTTP/1.1 3 | Host: HOST 4 | Content-Length: 0 5 | 6 | [response] 7 | HTTP/1.1 200 OK 8 | Date: xxx, xx xxx xxxx xx:xx:xx xxx 9 | Content-type: text/plain; charset=utf-8 10 | Content-Length: 568 11 | 12 | 13 | [test] 14 | (let response = (Darklang.Stdlib.HttpClient.request "head" "http://URL" [] [] |> Builtin.unwrap) 15 | let respHeaders = (response.headers |> Darklang.Stdlib.List.filter (fun h -> (Darklang.Stdlib.Tuple2.first h) != "date")) 16 | {response with headers = respHeaders}) == 17 | Darklang.Stdlib.HttpClient.Response 18 | { statusCode = 200L 19 | headers = [ 20 | ("server", "kestrel") 21 | ("content-length", "568") 22 | ("content-type", "text/plain; charset=utf-8") 23 | ] 24 | body = [] } 25 | -------------------------------------------------------------------------------- /packages/darklang/cli/experiments.dark: -------------------------------------------------------------------------------- 1 | module Darklang.Cli.Experiments 2 | 3 | 4 | let execute (state: AppState) (_args: List) : AppState = 5 | let _ = Experiments.Launcher.experiments () 6 | state 7 | 8 | 9 | let complete (_state: AppState) (_args: List) : List = 10 | // No completions needed for this command 11 | [] 12 | 13 | 14 | let help (_state: AppState) : Unit = 15 | [ 16 | "experiments - Launch the experiments TUI to access CLI experiments and demos" 17 | "" 18 | "Usage:" 19 | " experiments" 20 | "" 21 | "Opens an interactive TUI where you can:" 22 | " • Browse available CLI experiments and demos" 23 | " • Use ↑/↓ arrow keys to navigate" 24 | " • Press Enter to launch selected experiment" 25 | " • Press Q to quit" 26 | ] |> Stdlib.printLines 27 | -------------------------------------------------------------------------------- /packages/darklang/modelContextProtocol/serverBuilder/logging.dark: -------------------------------------------------------------------------------- 1 | module Darklang.ModelContextProtocol.ServerBuilder 2 | // Logging functions 3 | let getLogFilePath (serverName: String) : String = 4 | $"rundir/logs/mcp-server-{serverName}.log" 5 | 6 | let logWithPath (logFilePath: String) (message: String) : Unit = 7 | let timestamp = (Stdlib.DateTime.now_v0 ()) |> Stdlib.DateTime.toString 8 | let logMessage = $"[{timestamp}] {message}\n" 9 | Builtin.fileAppendText logFilePath logMessage 10 | () 11 | 12 | let logIncomingRequestWithPath (logFilePath: String) (message: String) : Unit = 13 | logWithPath logFilePath $"Incoming request: {message}" 14 | 15 | let logAndSendToClientWithPath (logFilePath: String) (message: String) : Unit = 16 | logWithPath logFilePath $"Sending to client: {message}" 17 | Builtin.printLine message -------------------------------------------------------------------------------- /packages/darklang/stdlib/bool.dark: -------------------------------------------------------------------------------- 1 | module Darklang.Stdlib.Bool 2 | 3 | 4 | /// Returns the inverse of : {{true}} if 5 | /// is {{false}} and {{false}} if is {{true}} 6 | let not (b: Bool) : Bool = Builtin.boolNot b 7 | 8 | /// Returns {{true}} if both and are {{true}} 9 | let ``and`` (a: Bool) (b: Bool) : Bool = a && b 10 | 11 | /// Returns {{true}} if either is true or is {{true}} 12 | let ``or`` (a: Bool) (b: Bool) : Bool = a || b 13 | 14 | /// Returns {{true}} if exactly one of and is {{true}}. Returns {{false}} if both are {{true}} or neither is {{true}} 15 | let xor (a: Bool) (b: Bool) : Bool = if a then Stdlib.Bool.not b else b 16 | 17 | /// Return {\"true\"} or {\"false\"} 18 | let toString (b: Bool) : String = if b then "true" else "false" -------------------------------------------------------------------------------- /backend/src/LibHttpMiddleware/LibHttpMiddleware.fsproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Library 5 | net8.0 6 | 8.0 7 | false 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /scripts/installers/install-exe-file: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Script to install single files from tar.gz files, checking the sha 4 | 5 | set -euo pipefail 6 | 7 | for i in "$@" ; do 8 | case "${i}" in 9 | --arm64-sha256=*) 10 | ARM64_SHA256=${1/--arm64-sha256=/''} 11 | shift 12 | ;; 13 | --amd64-sha256=*) 14 | AMD64_SHA256=${1/--amd64-sha256=/''} 15 | shift 16 | ;; 17 | --url=*) 18 | URL=${1/--url=/''} 19 | shift 20 | ;; 21 | --target=*) 22 | TARGET=${1/--target=/''} 23 | shift 24 | ;; 25 | esac 26 | done 27 | 28 | case $(dpkg --print-architecture) in 29 | arm64) CHECKSUM=$ARM64_SHA256;; 30 | amd64) CHECKSUM=$AMD64_SHA256;; 31 | *) exit 1;; 32 | esac 33 | sudo curl -SL --output ${TARGET} $URL 34 | echo "$CHECKSUM ${TARGET}" | sha256sum -c - 35 | sudo chmod +x ${TARGET} 36 | -------------------------------------------------------------------------------- /backend/testfiles/execution/cloud/_events.dark: -------------------------------------------------------------------------------- 1 | // TODO: figure out why these tests were flaky, 2 | // and uncomment the file (remove the `_` in `_events.dark`). 3 | 4 | type FruitRecord = { fruits: List } 5 | 6 | // getQueue works 7 | Builtin.testGetQueue_v0 "TestWorker" = [] 8 | 9 | // emit works 10 | (let _ = Builtin.emit "value" "TestWorker" 11 | let queue = Builtin.testGetQueue_v0 "TestWorker" 12 | queue) = [ "\"value\"" ] 13 | 14 | // emit works with mixed values 15 | (let _ = Builtin.emit "value" "TestWorker" 16 | let _ = Builtin.emit 1 "TestWorker" 17 | let _ = Builtin.emit (FruitRecord { fruits = [ "apple"; "banana" ] }) "TestWorker" 18 | let queue = Builtin.testGetQueue_v0 "TestWorker" 19 | Stdlib.List.sort queue) = 20 | [ "\"value\"" 21 | "1" 22 | "FruitRecord {\n fruits: [\n \"apple\", \"banana\"\n ]\n}" ] -------------------------------------------------------------------------------- /backend/testfiles/httphandler/bad-response-just-int.test: -------------------------------------------------------------------------------- 1 | [http-handler POST /] 2 | request.body |> Darklang.Stdlib.List.length 3 | 4 | [request] 5 | POST / HTTP/1.1 6 | Host: HOST 7 | Date: Sun, 08 Nov 2020 15:38:01 GMT 8 | Content-Length: 13 9 | 10 | 13 characters 11 | 12 | [response] 13 | HTTP/1.1 500 Internal Server Error 14 | Date: xxx, xx xxx xxxx xx:xx:xx xxx 15 | x-darklang-execution-id: 0123456789 16 | Content-Type: text/plain; charset=utf-8 17 | Server: darklang 18 | Strict-Transport-Security: max-age=31536000; includeSubDomains; preload 19 | Content-Length: LENGTH 20 | 21 | Application error: expected a HTTP response, got: 22 | type Int64: 23 | 13 24 | 25 | HTTP handlers should return results in the form: 26 | Darklang.Stdlib.Http.Response { 27 | statusCode : Int64 28 | headers : List 29 | body : Bytes 30 | } -------------------------------------------------------------------------------- /backend/src/LibPackageManager/Stats.fs: -------------------------------------------------------------------------------- 1 | module LibPackageManager.Stats 2 | 3 | open System.Threading.Tasks 4 | open FSharp.Control.Tasks 5 | 6 | open Prelude 7 | 8 | open Microsoft.Data.Sqlite 9 | open Fumble 10 | open LibDB.Db 11 | 12 | 13 | type Stats = { types : int64; values : int64; fns : int64 } 14 | 15 | // Stats count total unique content items, not branch-specific views 16 | let get () : Ply = 17 | uply { 18 | let countQuery table = 19 | Sql.query $"SELECT COUNT(DISTINCT id) as count FROM {table}" 20 | |> Sql.executeRowAsync (fun read -> read.int64 "count") 21 | 22 | let! typesCount = countQuery "package_types" 23 | let! valuesCount = countQuery "package_values" 24 | let! fnsCount = countQuery "package_functions" 25 | 26 | return { types = typesCount; values = valuesCount; fns = fnsCount } 27 | } 28 | -------------------------------------------------------------------------------- /backend/testfiles/httpclient/v0/_response-redirect-to-same-place-absolute.test: -------------------------------------------------------------------------------- 1 | [expected-request] 2 | GET PATH HTTP/1.1 3 | Host: HOST 4 | Content-Length: 0 5 | 6 | 7 | [response] 8 | HTTP/1.1 302L Found 9 | Date: xxx, xx xxx xxxx xx:xx:xx xxx 10 | Location: http://URL 11 | 12 | [test] 13 | (let response = (Darklang.Stdlib.HttpClient.request "get" "http://URL" [] []) |> Builtin.unwrap 14 | let respHeaders = response.headers |> Darklang.Stdlib.List.filter (fun h -> Darklang.Stdlib.Tuple2.first h != "date") 15 | {response with headers = respHeaders}) == 16 | Darklang.Stdlib.HttpClient.Response 17 | { statusCode = 302L 18 | headers = 19 | [ 20 | ("server", "kestrel") 21 | ("transfer-encoding", "chunked") 22 | ("location", "not sure why we aren't getting this") 23 | ] 24 | body = [] } 25 | 26 | -------------------------------------------------------------------------------- /backend/testfiles/httpclient/v0/response-redirect-302.test: -------------------------------------------------------------------------------- 1 | [expected-request] 2 | GET PATH HTTP/1.1 3 | Host: HOST 4 | Content-Length: 0 5 | 6 | [response] 7 | HTTP/1.1 302 Found 8 | Date: xxx, xx xxx xxxx xx:xx:xx xxx 9 | Location: /v0/response-redirect-destination 10 | 11 | 12 | [test] 13 | (let response = (Darklang.Stdlib.HttpClient.request "get" "http://URL" [] [] |> Builtin.unwrap) 14 | let respHeaders = (response.headers |> Darklang.Stdlib.List.filter (fun h -> (Darklang.Stdlib.Tuple2.first h) != "date")) 15 | {response with headers = respHeaders}) == 16 | Darklang.Stdlib.HttpClient.Response 17 | { statusCode = 302L 18 | headers = 19 | [ 20 | ("server", "kestrel") 21 | ("location", "/v0/response-redirect-destination") 22 | ("transfer-encoding", "chunked") 23 | ] 24 | body = [] } 25 | 26 | -------------------------------------------------------------------------------- /backend/testfiles/httpclient/v0/response-redirect-306.test: -------------------------------------------------------------------------------- 1 | [expected-request] 2 | GET PATH HTTP/1.1 3 | Host: HOST 4 | Content-Length: 0 5 | 6 | [response] 7 | HTTP/1.1 306 Swtich Proxy 8 | Date: xxx, xx xxx xxxx xx:xx:xx xxx 9 | Location: /v0/response-redirect-destination 10 | 11 | 12 | [test] 13 | (let response = (Darklang.Stdlib.HttpClient.request "get" "http://URL" [] [] |> Builtin.unwrap) 14 | let respHeaders = (response.headers |> Darklang.Stdlib.List.filter (fun h -> (Darklang.Stdlib.Tuple2.first h) != "date")) 15 | {response with headers = respHeaders}) == 16 | Darklang.Stdlib.HttpClient.Response 17 | { statusCode = 306L 18 | headers = 19 | [ 20 | ("server", "kestrel") 21 | ("location", "/v0/response-redirect-destination") 22 | ("transfer-encoding", "chunked") 23 | ] 24 | body = [] } 25 | -------------------------------------------------------------------------------- /backend/testfiles/httpclient/v0/_response-redirect-304.test: -------------------------------------------------------------------------------- 1 | [expected-request] 2 | GET PATH HTTP/1.1 3 | Host: HOST 4 | Content-Length: 0 5 | 6 | [response] 7 | HTTP/1.1 304L Not Modified 8 | Date: xxx, xx xxx xxxx xx:xx:xx xxx 9 | Location: /v0/response-redirect-destination 10 | 11 | 12 | [test] 13 | (let response = (Darklang.Stdlib.HttpClient.request "get" "http://URL" [] []) |> Builtin.unwrap 14 | let respHeaders = response.headers |> Darklang.Stdlib.List.filter (fun h -> Darklang.Stdlib.Tuple2.first h != "date") 15 | {response with headers = respHeaders}) == 16 | Darklang.Stdlib.HttpClient.Response 17 | { statusCode = 304L 18 | headers = 19 | [ 20 | ("server", "kestrel") 21 | ("location", "/v0/response-redirect-destination") 22 | ("transfer-encoding", "chunked") 23 | ] 24 | body = [] } 25 | 26 | -------------------------------------------------------------------------------- /backend/testfiles/httpclient/v0/response-redirect-303.test: -------------------------------------------------------------------------------- 1 | [expected-request] 2 | GET PATH HTTP/1.1 3 | Host: HOST 4 | Content-Length: 0 5 | 6 | [response] 7 | HTTP/1.1 303 See Other 8 | Date: xxx, xx xxx xxxx xx:xx:xx xxx 9 | Location: /v0/response-redirect-destination 10 | 11 | 12 | [test] 13 | (let response = (Darklang.Stdlib.HttpClient.request "get" "http://URL" [] [] |> Builtin.unwrap) 14 | let respHeaders = (response.headers |> Darklang.Stdlib.List.filter (fun h -> (Darklang.Stdlib.Tuple2.first h) != "date")) 15 | {response with headers = respHeaders}) == 16 | Darklang.Stdlib.HttpClient.Response 17 | { statusCode = 303L 18 | headers = 19 | [ 20 | ("server", "kestrel") 21 | ("location", "/v0/response-redirect-destination") 22 | ("transfer-encoding", "chunked") 23 | ] 24 | body = [] } 25 | 26 | -------------------------------------------------------------------------------- /backend/testfiles/httpclient/v0/response-redirect-305.test: -------------------------------------------------------------------------------- 1 | [expected-request] 2 | GET PATH HTTP/1.1 3 | Host: HOST 4 | Content-Length: 0 5 | 6 | [response] 7 | HTTP/1.1 305 Use Proxy 8 | Date: xxx, xx xxx xxxx xx:xx:xx xxx 9 | Location: /v0/response-redirect-destination 10 | 11 | 12 | [test] 13 | (let response = (Darklang.Stdlib.HttpClient.request "get" "http://URL" [] [] |> Builtin.unwrap) 14 | let respHeaders = (response.headers |> Darklang.Stdlib.List.filter (fun h -> (Darklang.Stdlib.Tuple2.first h) != "date")) 15 | {response with headers = respHeaders}) == 16 | Darklang.Stdlib.HttpClient.Response 17 | { statusCode = 305L 18 | headers = 19 | [ 20 | ("server", "kestrel") 21 | ("location", "/v0/response-redirect-destination") 22 | ("transfer-encoding", "chunked") 23 | ] 24 | body = [] } 25 | 26 | -------------------------------------------------------------------------------- /backend/testfiles/httpclient/v0/response-redirect-308.test: -------------------------------------------------------------------------------- 1 | [expected-request] 2 | GET PATH HTTP/1.1 3 | Host: HOST 4 | Content-Length: 0 5 | 6 | [response] 7 | HTTP/1.1 308 Permanent Redirect 8 | Date: xxx, xx xxx xxxx xx:xx:xx xxx 9 | Location: /v0/response-redirect-destination 10 | 11 | 12 | [test] 13 | (let response = (Darklang.Stdlib.HttpClient.request "get" "http://URL" [] [] |> Builtin.unwrap) 14 | let respHeaders = (response.headers |> Darklang.Stdlib.List.filter (fun h -> (Darklang.Stdlib.Tuple2.first h) != "date")) 15 | {response with headers = respHeaders}) == 16 | Darklang.Stdlib.HttpClient.Response 17 | { statusCode = 308L 18 | headers = 19 | [ 20 | ("server", "kestrel") 21 | ("location", "/v0/response-redirect-destination") 22 | ("transfer-encoding", "chunked") 23 | ] 24 | body = [] } 25 | -------------------------------------------------------------------------------- /backend/testfiles/httpclient/v0/response-redirect-300.test: -------------------------------------------------------------------------------- 1 | [expected-request] 2 | GET PATH HTTP/1.1 3 | Host: HOST 4 | Content-Length: 0 5 | 6 | [response] 7 | HTTP/1.1 300 Multiple Choice 8 | Date: xxx, xx xxx xxxx xx:xx:xx xxx 9 | Location: /v0/response-redirect-destination 10 | 11 | 12 | [test] 13 | (let response = (Darklang.Stdlib.HttpClient.request "get" "http://URL" [] [] |> Builtin.unwrap) 14 | let respHeaders = (response.headers |> Darklang.Stdlib.List.filter (fun h -> (Darklang.Stdlib.Tuple2.first h) != "date")) 15 | {response with headers = respHeaders}) == 16 | Darklang.Stdlib.HttpClient.Response 17 | { statusCode = 300L 18 | headers = 19 | [ 20 | ("server", "kestrel") 21 | ("location", "/v0/response-redirect-destination") 22 | ("transfer-encoding", "chunked") 23 | ] 24 | body = [] } 25 | 26 | -------------------------------------------------------------------------------- /backend/testfiles/httpclient/v0/response-redirect-301.test: -------------------------------------------------------------------------------- 1 | [expected-request] 2 | GET PATH HTTP/1.1 3 | Host: HOST 4 | Content-Length: 0 5 | 6 | [response] 7 | HTTP/1.1 301 Moved Permanently 8 | Date: xxx, xx xxx xxxx xx:xx:xx xxx 9 | Location: /v0/response-redirect-destination 10 | 11 | 12 | [test] 13 | (let response = (Darklang.Stdlib.HttpClient.request "get" "http://URL" [] [] |> Builtin.unwrap) 14 | let respHeaders = (response.headers |> Darklang.Stdlib.List.filter (fun h -> (Darklang.Stdlib.Tuple2.first h) != "date")) 15 | {response with headers = respHeaders}) == 16 | Darklang.Stdlib.HttpClient.Response 17 | { statusCode = 301L 18 | headers = 19 | [ 20 | ("server", "kestrel") 21 | ("location", "/v0/response-redirect-destination") 22 | ("transfer-encoding", "chunked") 23 | ] 24 | body = [] } 25 | 26 | -------------------------------------------------------------------------------- /backend/testfiles/httpclient/v0/response-redirect-307.test: -------------------------------------------------------------------------------- 1 | [expected-request] 2 | GET PATH HTTP/1.1 3 | Host: HOST 4 | Content-Length: 0 5 | 6 | [response] 7 | HTTP/1.1 307 Temporary Redirect 8 | Date: xxx, xx xxx xxxx xx:xx:xx xxx 9 | Location: /v0/response-redirect-destination 10 | 11 | 12 | [test] 13 | (let response = (Darklang.Stdlib.HttpClient.request "get" "http://URL" [] [] |> Builtin.unwrap) 14 | let respHeaders = (response.headers |> Darklang.Stdlib.List.filter (fun h -> (Darklang.Stdlib.Tuple2.first h) != "date")) 15 | {response with headers = respHeaders}) == 16 | Darklang.Stdlib.HttpClient.Response 17 | { statusCode = 307L 18 | headers = 19 | [ 20 | ("server", "kestrel") 21 | ("location", "/v0/response-redirect-destination") 22 | ("transfer-encoding", "chunked") 23 | ] 24 | body = [] } 25 | 26 | -------------------------------------------------------------------------------- /scripts/deployment/new-build-containers.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | . ./scripts/devcontainer/_assert-in-container "$0" "$@" 3 | 4 | set -euo pipefail 5 | 6 | set -x 7 | 8 | # We specify `docker build –-pull=false` because otherwise it looks for the 9 | # containers we just built in the repository, even if they're available locally. As a 10 | # result, we first need to pull the actual remote containers we need from docker. 11 | docker pull amd64/buildpack-deps:jammy-curl 12 | 13 | docker build . -t darkbaseservice:latest -f containers/base-service-Dockerfile --load --pull=false 14 | docker build . -t darkfsharpservice:latest -f containers/fsharp-service-Dockerfile --load --pull=false 15 | docker build . -t bwdserver:latest -f containers/bwdserver/Dockerfile --load --pull=false 16 | docker build . -t prodexec:latest -f containers/prodexec/Dockerfile --load --pull=false 17 | -------------------------------------------------------------------------------- /packages/darklang/languageTools/lsp-server/sync.dark: -------------------------------------------------------------------------------- 1 | module Darklang.LanguageTools.LspServer.Sync 2 | 3 | /// Handles `dark/listInstances` requests from VS Code 4 | let handleListInstancesRequest 5 | (state: LspState) 6 | (requestID: JsonRPC.RequestId) 7 | : LspState = 8 | let instances = SCM.Instances.list () 9 | 10 | let instancesJson = 11 | instances 12 | |> Stdlib.List.map (fun instance -> 13 | Json.Object 14 | [ ("id", Json.String (Stdlib.Uuid.toString instance.id)) 15 | ("name", Json.String instance.name) 16 | ("url", Json.String instance.url) ]) 17 | |> Json.Array 18 | 19 | let responseJson = 20 | let requestID = Stdlib.Option.Option.Some requestID 21 | (JsonRPC.Response.Ok.make requestID instancesJson) 22 | |> Stdlib.AltJson.format 23 | 24 | logAndSendToClient responseJson 25 | 26 | state 27 | -------------------------------------------------------------------------------- /packages/darklang/languageTools/lsp-server/switchBranch.dark: -------------------------------------------------------------------------------- 1 | module Darklang.LanguageTools.LspServer.SwitchBranch 2 | 3 | let handleSwitchBranchRequest 4 | (state: LspState) 5 | (requestId: JsonRPC.RequestId) 6 | (branchIdStr: String) 7 | : LspState = 8 | let branchID = 9 | if branchIdStr == "main" then 10 | Stdlib.Option.Option.None 11 | else 12 | match Stdlib.Uuid.parse branchIdStr with 13 | | Ok uuid -> Stdlib.Option.Option.Some uuid 14 | | Error _ -> Stdlib.Option.Option.None 15 | 16 | let responseJson = 17 | let requestID = Stdlib.Option.Option.Some(requestId) 18 | let body = Json.Object [ ("success", Json.Bool true) ] 19 | (JsonRPC.Response.Ok.make requestID body) 20 | |> Stdlib.AltJson.format 21 | 22 | logAndSendToClient responseJson 23 | 24 | // the actual change requested 25 | { state with branchID = branchID } 26 | -------------------------------------------------------------------------------- /backend/src/DvalReprDeveloper/DvalReprDeveloper.fsproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Library 5 | net8.0 6 | 8.0 7 | 8 | false 9 | true 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /backend/testfiles/httpclient/v0/basic-get-helper-function.test: -------------------------------------------------------------------------------- 1 | [expected-request] 2 | GET PATH HTTP/1.1 3 | Host: HOST 4 | Content-Length: 0 5 | 6 | [response] 7 | HTTP/1.1 200 OK 8 | Date: xxx, xx xxx xxxx xx:xx:xx xxx 9 | Content-type: text/plain; charset=utf-8 10 | Content-Length: LENGTH 11 | 12 | Hello back 13 | 14 | [test] 15 | (let response = (Darklang.Stdlib.HttpClient.get "http://URL" [] |> Builtin.unwrap) 16 | let respHeaders = (response.headers |> Darklang.Stdlib.List.filter (fun h -> (Darklang.Stdlib.Tuple2.first h) != "date")) 17 | { response with headers = respHeaders}) == 18 | Darklang.Stdlib.HttpClient.Response 19 | { statusCode = 200L 20 | headers = [ 21 | ("server", "kestrel") 22 | ("content-length", "LENGTH") 23 | ("content-type", "text/plain; charset=utf-8") 24 | ] 25 | body = ("Hello back" |> Darklang.Stdlib.String.toBytes) } 26 | -------------------------------------------------------------------------------- /backend/testfiles/httpclient/v0/basic-get.test: -------------------------------------------------------------------------------- 1 | [expected-request] 2 | GET PATH HTTP/1.1 3 | Host: HOST 4 | Content-Length: 0 5 | 6 | [response] 7 | HTTP/1.1 200 OK 8 | Date: xxx, xx xxx xxxx xx:xx:xx xxx 9 | Content-type: text/plain; charset=utf-8 10 | Content-Length: LENGTH 11 | 12 | Hello back 13 | 14 | [test] 15 | (let response = (Darklang.Stdlib.HttpClient.request "get" "http://URL" [] [] |> Builtin.unwrap) 16 | let respHeaders = (response.headers |> Darklang.Stdlib.List.filter (fun h -> (Darklang.Stdlib.Tuple2.first h) != "date")) 17 | {response with headers = respHeaders}) == 18 | Darklang.Stdlib.HttpClient.Response 19 | { statusCode = 200L 20 | headers = [ 21 | ("server", "kestrel") 22 | ("content-length", "LENGTH") 23 | ("content-type", "text/plain; charset=utf-8") 24 | ] 25 | body = ("Hello back" |> Darklang.Stdlib.String.toBytes) } 26 | -------------------------------------------------------------------------------- /backend/testfiles/httpclient/v0/basic-options-helper-function.test: -------------------------------------------------------------------------------- 1 | [expected-request] 2 | OPTIONS PATH HTTP/1.1 3 | Host: HOST 4 | Content-Length: 0 5 | 6 | [response] 7 | HTTP/1.1 200 OK 8 | Date: xxx, xx xxx xxxx xx:xx:xx xxx 9 | Content-type: text/plain; charset=utf-8 10 | Content-Length: LENGTH 11 | 12 | Hello back 13 | 14 | [test] 15 | (let response = (Darklang.Stdlib.HttpClient.options "http://URL" |> Builtin.unwrap) 16 | let respHeaders = (response.headers |> Darklang.Stdlib.List.filter (fun h -> (Darklang.Stdlib.Tuple2.first h) != "date")) 17 | {response with headers = respHeaders}) == 18 | Darklang.Stdlib.HttpClient.Response 19 | { statusCode = 200L 20 | headers = [ 21 | ("server", "kestrel") 22 | ("content-length", "LENGTH") 23 | ("content-type", "text/plain; charset=utf-8") 24 | ] 25 | body = ("Hello back" |> Darklang.Stdlib.String.toBytes) } -------------------------------------------------------------------------------- /backend/testfiles/httpclient/v0/basic-options.test: -------------------------------------------------------------------------------- 1 | [expected-request] 2 | OPTIONS PATH HTTP/1.1 3 | Host: HOST 4 | Content-Length: 0 5 | 6 | [response] 7 | HTTP/1.1 200 OK 8 | Date: xxx, xx xxx xxxx xx:xx:xx xxx 9 | Content-type: text/plain; charset=utf-8 10 | Content-Length: LENGTH 11 | 12 | Hello back 13 | 14 | [test] 15 | (let response = (Darklang.Stdlib.HttpClient.request "options" "http://URL" [] [] |> Builtin.unwrap) 16 | let respHeaders = (response.headers |> Darklang.Stdlib.List.filter (fun h -> (Darklang.Stdlib.Tuple2.first h) != "date")) 17 | {response with headers = respHeaders}) == 18 | Darklang.Stdlib.HttpClient.Response 19 | { statusCode = 200L 20 | headers = [ 21 | ("server", "kestrel") 22 | ("content-length", "LENGTH") 23 | ("content-type", "text/plain; charset=utf-8") 24 | ] 25 | body = ("Hello back" |> Darklang.Stdlib.String.toBytes) } -------------------------------------------------------------------------------- /scripts/installers/install-gz-file: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Script to install binary files, checking the sha 4 | 5 | set -euo pipefail 6 | 7 | for i in "$@" ; do 8 | case "${i}" in 9 | --arm64-sha256=*) 10 | ARM64_SHA256=${1/--arm64-sha256=/''} 11 | shift 12 | ;; 13 | --amd64-sha256=*) 14 | AMD64_SHA256=${1/--amd64-sha256=/''} 15 | shift 16 | ;; 17 | --url=*) 18 | URL=${1/--url=/''} 19 | shift 20 | ;; 21 | --target=*) 22 | TARGET=${1/--target=/''} 23 | shift 24 | ;; 25 | esac 26 | done 27 | FILENAME=$(basename $URL) 28 | case $(dpkg --print-architecture) in 29 | arm64) CHECKSUM=$ARM64_SHA256;; 30 | amd64) CHECKSUM=$AMD64_SHA256;; 31 | *) exit 1;; 32 | esac 33 | wget $URL 34 | echo "$CHECKSUM $FILENAME" | sha256sum -c - 35 | gunzip $FILENAME 36 | EXTRACT_FILE="${FILENAME%.*}" 37 | sudo mv ${EXTRACT_FILE} ${TARGET} 38 | sudo chmod +x ${TARGET} 39 | -------------------------------------------------------------------------------- /backend/testfiles/httpclient/v0/basic-delete-helper-function.test: -------------------------------------------------------------------------------- 1 | [expected-request] 2 | DELETE PATH HTTP/1.1 3 | Host: HOST 4 | Content-Length: 0 5 | 6 | [response] 7 | HTTP/1.1 200 OK 8 | Date: xxx, xx xxx xxxx xx:xx:xx xxx 9 | Content-type: text/plain; charset=utf-8 10 | Content-Length: LENGTH 11 | 12 | Hello back 13 | 14 | [test] 15 | (let response = (Darklang.Stdlib.HttpClient.delete "http://URL" |> Builtin.unwrap) 16 | let respHeaders = (response.headers |> Darklang.Stdlib.List.filter (fun h -> (Darklang.Stdlib.Tuple2.first h) != "date")) 17 | {response with headers = respHeaders}) == 18 | Darklang.Stdlib.HttpClient.Response 19 | { statusCode = 200L 20 | headers = [ 21 | ("server", "kestrel") 22 | ("content-length", "LENGTH") 23 | ("content-type", "text/plain; charset=utf-8") 24 | ] 25 | body = ("Hello back" |> Darklang.Stdlib.String.toBytes) } 26 | 27 | -------------------------------------------------------------------------------- /backend/testfiles/httpclient/v0/basic-delete.test: -------------------------------------------------------------------------------- 1 | [expected-request] 2 | DELETE PATH HTTP/1.1 3 | Host: HOST 4 | Content-Length: 0 5 | 6 | [response] 7 | HTTP/1.1 200 OK 8 | Date: xxx, xx xxx xxxx xx:xx:xx xxx 9 | Content-type: text/plain; charset=utf-8 10 | Content-Length: LENGTH 11 | 12 | Hello back 13 | 14 | [test] 15 | (let response = (Darklang.Stdlib.HttpClient.request "delete" "http://URL" [] [] |> Builtin.unwrap) 16 | let respHeaders = (response.headers |> Darklang.Stdlib.List.filter (fun h -> (Darklang.Stdlib.Tuple2.first h) != "date")) 17 | {response with headers = respHeaders}) == 18 | Darklang.Stdlib.HttpClient.Response 19 | { statusCode = 200L 20 | headers = [ 21 | ("server", "kestrel") 22 | ("content-length", "LENGTH") 23 | ("content-type", "text/plain; charset=utf-8") 24 | ] 25 | body = ("Hello back" |> Darklang.Stdlib.String.toBytes) } 26 | 27 | -------------------------------------------------------------------------------- /backend/src/BuiltinExecution/Libs/Bool.fs: -------------------------------------------------------------------------------- 1 | module BuiltinExecution.Libs.Bool 2 | 3 | open System.Threading.Tasks 4 | open FSharp.Control.Tasks 5 | open Prelude 6 | open LibExecution.RuntimeTypes 7 | open LibExecution.Builtin.Shortcuts 8 | 9 | 10 | let fns : List = 11 | // TODO: Maybe Expose ENot 12 | [ { name = fn "boolNot" 0 13 | typeParams = [] 14 | parameters = [ Param.make "b" TBool "" ] 15 | returnType = TBool 16 | description = 17 | "Returns the inverse of : 18 | {{true}} if is {{false}} 19 | and {{false}} if is {{true}}" 20 | fn = 21 | (function 22 | | _, _, _, [ DBool b ] -> Ply(DBool(not b)) 23 | | _ -> incorrectArgs ()) 24 | sqlSpec = SqlFunction "not" 25 | previewable = Pure 26 | deprecated = NotDeprecated } ] 27 | 28 | 29 | let builtins = LibExecution.Builtin.make [] fns 30 | -------------------------------------------------------------------------------- /backend/src/LibClientTypesToCloudTypes/LibClientTypesToCloudTypes.fsproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Library 5 | net8.0 6 | 8.0 7 | 8 | false 9 | false 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /containers/prodexec/run.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -euo pipefail 4 | 5 | # Set up chisel 6 | chisel --version 7 | cat < /home/dark/auth.json 8 | { 9 | "$DARK_CONFIG_PRODEXEC_CHISEL_USERNAME:$DARK_CONFIG_PRODEXEC_CHISEL_PASSWORD": ["localhost:22"] 10 | } 11 | EOF 12 | nohup chisel server --authfile /home/dark/auth.json --port "$DARK_CONFIG_PRODEXEC_PORT" -v & 13 | 14 | 15 | # Set up ssh auth 16 | echo "dark:$DARK_CONFIG_PRODEXEC_SSH_PASSWORD" | sudo chpasswd 17 | sudo service ssh start 18 | 19 | 20 | # Add config vars to env for users logging in. This loops through env vars, then 21 | # adds export and saves them. This handles newlines correctly, needed for certs and 22 | # other auth. 23 | env_vars=$(compgen -v | grep ^DARK_CONFIG) 24 | for var in $env_vars 25 | do 26 | typeset -p $var | sed 's/^declare -x/export/' >> /home/dark/.bashrc 27 | done 28 | 29 | 30 | while true; do 31 | sleep 1 32 | done 33 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # OSes 2 | .DS_Store 3 | 4 | # Config 5 | config/local 6 | 7 | # IDEs 8 | .idea 9 | .ionide/ 10 | 11 | # Build system 12 | rundir/ 13 | 14 | # F# / dotnet 15 | backend/packages/ 16 | backend/paket-files/ 17 | backend/.paket/load/ 18 | backend/Build/ 19 | backend/src/**/obj 20 | backend/tests/**/obj 21 | backend/static/dark_wasm/ 22 | backend/static/tree-sitter/ 23 | 24 | # Parser 25 | tree-sitter-darklang/build/ 26 | tree-sitter-darklang/package-lock.json 27 | tree-sitter-darklang/node_modules 28 | tree-sitter-darklang/bindings 29 | tree-sitter-darklang/tree-sitter-darklang.so 30 | tree-sitter-darklang/xplat-builds 31 | tree-sitter-darklang/src/parser.c 32 | 33 | # Extension 34 | .vsix/* 35 | 36 | # Terraform 37 | **/.terraform/* 38 | *.tfstate 39 | *.tfstate.* 40 | 41 | ############### 42 | # Deployment 43 | ############### 44 | 45 | deploy-lock-manual-deploy 46 | .secrets 47 | 48 | clis/ 49 | 50 | .mono 51 | .fake -------------------------------------------------------------------------------- /scripts/deployment/deploy-lock-wait-and-acquire: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | . ./scripts/devcontainer/_assert-in-container "$0" "$@" 3 | 4 | # Acquire the deploy lock. Required argument is the name for the lock, which should 5 | # be a number (the build number) or "manual". 6 | 7 | set -euo pipefail 8 | 9 | LOCKFILE_NAME=$(./scripts/deployment/deploy-lock-one-get-name) 10 | 11 | echo "Lock file: ${LOCKFILE_NAME}" 12 | 13 | while true; do 14 | # Deploy locks start with a timestamp, so they can be sorted directly 15 | next=$( ./scripts/deployment/deploy-lock-all-list | sort -n | head -n 1) 16 | # smallest timestamp goes first 17 | if [[ "${next}" == "${LOCKFILE_NAME}" ]]; then 18 | echo "We're up!" 19 | exit 0 20 | elif [[ "${next}" == "" ]]; then 21 | echo "No locks found, we're up!" 22 | exit 0 23 | else 24 | echo "Waiting for other deploy to finish (${next}), sleeping" 25 | sleep 5 26 | fi 27 | done 28 | -------------------------------------------------------------------------------- /backend/src/CronChecker/CronChecker.fsproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net8.0 6 | 8.0 7 | 8 | true 9 | false 10 | false 11 | false 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /backend/src/LibService/FireAndForget.fs: -------------------------------------------------------------------------------- 1 | module LibService.FireAndForget 2 | 3 | open System.Threading.Tasks 4 | open System.Threading 5 | open FSharp.Control.Tasks 6 | 7 | open Prelude 8 | 9 | /// Execute a function in the backgorund, 10 | /// ignoring any results and forwarding exceptions to Rollbar 11 | let fireAndForgetTask (name : string) (f : unit -> Task<'b>) : unit = 12 | // this should be a backgroundTask, but that doesn't work due to 13 | // https://github.com/dotnet/fsharp/issues/12761 14 | task { 15 | use _ = Telemetry.child $"fireAndForget: {name}" [ "task_name", name ] 16 | try 17 | // Resolve to make sure we catch the exception 18 | let! (_ : 'b) = f () 19 | Telemetry.addTag "success" true 20 | return () 21 | with e -> 22 | Telemetry.addTag "success" false 23 | Rollbar.sendException None [ "fire-and-forget", name ] e 24 | return () 25 | } 26 | |> ignore> 27 | -------------------------------------------------------------------------------- /backend/src/LocalExec/Utils.fs: -------------------------------------------------------------------------------- 1 | module LocalExec.Utils 2 | 3 | open Prelude 4 | 5 | module RT = LibExecution.RuntimeTypes 6 | module PT = LibExecution.ProgramTypes 7 | module PT2RT = LibExecution.ProgramTypesToRuntimeTypes 8 | 9 | 10 | let isNormalFile (path : string) : bool = 11 | try 12 | let attrs = System.IO.File.GetAttributes(path) 13 | let isDir = attrs.HasFlag(System.IO.FileAttributes.Directory) 14 | let exists = System.IO.File.Exists(path) || System.IO.Directory.Exists(path) 15 | exists && not isDir 16 | with e -> 17 | false 18 | 19 | 20 | let rec listDirectoryRecursive (dir : string) : List = 21 | let contents = System.IO.Directory.EnumerateFileSystemEntries dir |> Seq.toList 22 | let (files, dirs) = contents |> List.partition (fun x -> isNormalFile x) 23 | let nested = dirs |> List.map (fun d -> listDirectoryRecursive d) |> List.flatten 24 | dirs |> List.append files |> List.append nested 25 | -------------------------------------------------------------------------------- /backend/testfiles/httpclient/v0/basic-post.test: -------------------------------------------------------------------------------- 1 | [expected-request] 2 | POST PATH HTTP/1.1 3 | Host: HOST 4 | Content-Length: 2 5 | 6 | -1 7 | [response] 8 | HTTP/1.1 200 OK 9 | Date: xxx, xx xxx xxxx xx:xx:xx xxx 10 | Content-type: text/plain; charset=utf-8 11 | Content-Length: LENGTH 12 | 13 | Hello back 14 | 15 | [test] 16 | (let response = (Darklang.Stdlib.HttpClient.request "post" "http://URL" [] ("-1" |> Darklang.Stdlib.String.toBytes) |> Builtin.unwrap) 17 | let respHeaders = (response.headers |> Darklang.Stdlib.List.filter (fun h -> (Darklang.Stdlib.Tuple2.first h) != "date")) 18 | {response with headers = respHeaders}) == 19 | Darklang.Stdlib.HttpClient.Response 20 | { statusCode = 200L 21 | headers = [ 22 | ("server", "kestrel") 23 | ("content-length", "LENGTH") 24 | ("content-type", "text/plain; charset=utf-8") 25 | ] 26 | body = ("Hello back" |> Darklang.Stdlib.String.toBytes) } -------------------------------------------------------------------------------- /backend/testfiles/httpclient/v0/basic-post-helper-function.test: -------------------------------------------------------------------------------- 1 | [expected-request] 2 | POST PATH HTTP/1.1 3 | Host: HOST 4 | Content-Length: 2 5 | 6 | -1 7 | [response] 8 | HTTP/1.1 200 OK 9 | Date: xxx, xx xxx xxxx xx:xx:xx xxx 10 | Content-type: text/plain; charset=utf-8 11 | Content-Length: LENGTH 12 | 13 | Hello back 14 | 15 | [test] 16 | (let response = (Darklang.Stdlib.HttpClient.post "http://URL" [] ("-1" |> Darklang.Stdlib.String.toBytes) |> Builtin.unwrap) 17 | let respHeaders = (response.headers |> Darklang.Stdlib.List.filter (fun h -> (Darklang.Stdlib.Tuple2.first h) != "date")) 18 | {response with headers = respHeaders}) == 19 | Darklang.Stdlib.HttpClient.Response 20 | { statusCode = 200L 21 | headers = [ 22 | ("server", "kestrel") 23 | ("content-length", "LENGTH") 24 | ("content-type", "text/plain; charset=utf-8") 25 | ] 26 | body = ("Hello back" |> Darklang.Stdlib.String.toBytes) } -------------------------------------------------------------------------------- /backend/testfiles/httpclient/v0/_uri-with-path-fragment.test: -------------------------------------------------------------------------------- 1 | [expected-request] 2 | GET PATH?q=5 HTTP/1.1 3 | Host: HOST 4 | Content-Length: 0 5 | 6 | 7 | [response] 8 | HTTP/1.1 200 OK 9 | Date: xxx, xx xxx xxxx xx:xx:xx xxx 10 | Content-type: text/plain; charset=utf-8 11 | Content-Length: LENGTH 12 | 13 | Hello back 14 | 15 | [test] 16 | (let response = (Darklang.Stdlib.HttpClient.request "get" "http://URL?q=5#row=5-👨‍👩‍👧‍👦" [] []) |> Builtin.unwrap 17 | let respHeaders = response.headers |> Darklang.Stdlib.List.filter (fun h -> Darklang.Stdlib.Tuple2.first h != "date") 18 | {response with headers = respHeaders}) == 19 | Darklang.Stdlib.HttpClient.Response 20 | { statusCode = 200L 21 | headers = 22 | [ 23 | ("content-length", "LENGTH") 24 | ("content-type", "text/plain; charset=utf-8") 25 | ("server", "kestrel") 26 | ] 27 | body = "Hello back" |> Darklang.Stdlib.String.toBytes} 28 | -------------------------------------------------------------------------------- /backend/testfiles/httpclient/v0/request-query-param-int.test: -------------------------------------------------------------------------------- 1 | [expected-request] 2 | GET PATH?i=5&n=-1 HTTP/1.1 3 | Host: HOST 4 | Content-Length: 0 5 | 6 | 7 | [response] 8 | HTTP/1.1 200 OK 9 | Date: xxx, xx xxx xxxx xx:xx:xx xxx 10 | Content-type: text/plain; charset=utf-8 11 | Content-Length: LENGTH 12 | 13 | Hello back 14 | 15 | [test] 16 | (let response = (Darklang.Stdlib.HttpClient.request "get" "http://URL?i=5&n=-1" [] [] |> Builtin.unwrap) 17 | let respHeaders = (response.headers |> Darklang.Stdlib.List.filter (fun h -> (Darklang.Stdlib.Tuple2.first h) != "date")) 18 | {response with headers = respHeaders}) == 19 | Darklang.Stdlib.HttpClient.Response 20 | { statusCode = 200L 21 | headers = 22 | [ 23 | ("server", "kestrel") 24 | ("content-length", "LENGTH") 25 | ("content-type", "text/plain; charset=utf-8") 26 | ] 27 | body = ("Hello back" |> Darklang.Stdlib.String.toBytes) } 28 | -------------------------------------------------------------------------------- /backend/testfiles/httpclient/v0/uri-with-path-slash.test: -------------------------------------------------------------------------------- 1 | [expected-request] 2 | GET /v0/uri-with-path-slash/ HTTP/1.1 3 | Host: HOST 4 | Content-Length: 0 5 | 6 | 7 | [response] 8 | HTTP/1.1 200 OK 9 | Date: xxx, xx xxx xxxx xx:xx:xx xxx 10 | Content-type: text/plain; charset=utf-8 11 | Content-Length: LENGTH 12 | 13 | Hello back 14 | 15 | [test] 16 | (let response = (Darklang.Stdlib.HttpClient.request "get" "http://URL/" [] [] |> Builtin.unwrap) 17 | let respHeaders = (response.headers |> Darklang.Stdlib.List.filter (fun h -> (Darklang.Stdlib.Tuple2.first h) != "date")) 18 | {response with headers = respHeaders}) == 19 | Darklang.Stdlib.HttpClient.Response 20 | { statusCode = 200L 21 | headers = 22 | [ 23 | ("server", "kestrel") 24 | ("content-length", "LENGTH") 25 | ("content-type", "text/plain; charset=utf-8") 26 | ] 27 | body = ("Hello back" |> Darklang.Stdlib.String.toBytes) } 28 | -------------------------------------------------------------------------------- /backend/testfiles/httpclient/v0/todo/response-body-invalid-string.test: -------------------------------------------------------------------------------- 1 | [expected-request] 2 | GET PATH HTTP/1.1 3 | Accept: */* 4 | Accept-Encoding: deflate, gzip, br 5 | Content-Type: text/plain; charset=utf-8 6 | Host: HOST 7 | 8 | [response] 9 | HTTP/1.1 200 OK 10 | Date: xxx, xx xxx xxxx xx:xx:xx xxx 11 | Content-Length: LENGTH 12 | 13 | RANDOM_BYTES 14 | 15 | [test] 16 | // RANDOM_BYTES filled in by the test suite 17 | (let response = (Darklang.Stdlib.HttpClient.request "get" "http://URL" [] []) |> Builtin.unwrap 18 | let respHeaders = response.headers |> Darklang.Stdlib.List.filter (fun h -> Darklang.Stdlib.Tuple2.first h != "date") 19 | {response with headers = respHeaders}) = 20 | Darklang.Stdlib.HttpClient.Response 21 | { body = "utf-8 decoding error" 22 | statusCode = 200L 23 | headers = 24 | [ 25 | ("content-length", "LENGTH") 26 | ("server", "kestrel") 27 | ] 28 | raw = "utf-8 decoding error"} 29 | -------------------------------------------------------------------------------- /packages/darklang/cli/old_notes/_msg.dark: -------------------------------------------------------------------------------- 1 | module Darklang.Cli 2 | 3 | type Msg = 4 | // -- Account, Auth. 5 | // | ShowAccountStatus 6 | // | StartLogin 7 | // `dark login --username=stachu --password=yolo` 8 | // | Login of username: String * password: String 9 | // | StartSignup 10 | //- email and username is required 11 | //- full name is optional 12 | //- contact links etc are optional 13 | //- (email verification) 14 | // | SubmitSignup of username: String * password: String 15 | // TODO: change password 16 | // TODO: auth should eventually include oauth, etc 17 | // | LogOut 18 | 19 | // -- Matter 20 | // | AddFunction of ... 21 | // | AddType of ... 22 | 23 | // -- App 24 | //| AddHandler of ... 25 | //| AddDB of ... 26 | //| EditDefinition of ... 27 | //| EditHandler of ... 28 | //| UpdateExpr of old: Expr * new: Expr 29 | //| UpdateDBSchema of ... 30 | //| EditSecret of ... 31 | //| DeleteSecret of ... -------------------------------------------------------------------------------- /packages/darklang/modelContextProtocol/serverBuilder/state.dark: -------------------------------------------------------------------------------- 1 | module Darklang.ModelContextProtocol.ServerBuilder.State 2 | 3 | // 4 | type Json = Stdlib.AltJson.Json 5 | type TraceLevel = Darklang.ModelContextProtocol.TraceLevel 6 | type LoggingLevel = Darklang.ModelContextProtocol.LoggingLevel 7 | // 8 | 9 | 10 | // Main builder type that accumulates server configuration 11 | type McpServerBuilder = 12 | { name: String 13 | tools: Dict 14 | resources: Dict 15 | prompts: Dict } 16 | 17 | 18 | // Server state for the builder-based server 19 | type BuilderServerState = 20 | { initialized: Bool 21 | shouldShutdown: Bool 22 | server: McpServerBuilder 23 | traceLevel: TraceLevel 24 | loggingLevel: LoggingLevel 25 | logFilePath: String 26 | resourceTemplates: Dict 27 | roots: List } -------------------------------------------------------------------------------- /containers/prodexec/Dockerfile: -------------------------------------------------------------------------------- 1 | # Dockerfile for the the prodexec container 2 | 3 | FROM darkfsharpservice:latest 4 | 5 | WORKDIR /home/dark 6 | 7 | RUN sudo apt update && sudo apt install openssh-server -y \ 8 | --no-install-recommends \ 9 | && sudo rm -rf /var/lib/apt/lists/* 10 | 11 | COPY --chown=dark:dark scripts scripts 12 | COPY --chown=dark:dark backend/migrations migrations 13 | 14 | RUN mkdir app 15 | 16 | # Setting this now means we can set the filesystem to readonly 17 | ENV DARK_CONFIG_RUNDIR=/home/dark/gcp-rundir 18 | RUN ./scripts/devcontainer/_create-app-directories 19 | 20 | COPY --chown=dark:dark ./backend/Build/out/ProdExec/Release/net8.0/linux-x64/publish/* app/ 21 | RUN ./scripts/linting/_check-linked-libs app/ProdExec 22 | 23 | # Add chisel. This allows us to tunnel SSH over HTTP 24 | COPY --from=jpillora/chisel /app/bin /usr/bin/chisel 25 | 26 | COPY --chmod=755 --chown=dark:dark containers/prodexec/run.sh . 27 | 28 | CMD "./run.sh" 29 | -------------------------------------------------------------------------------- /backend/testfiles/httpclient/v0/request-query-param-float.test: -------------------------------------------------------------------------------- 1 | [expected-request] 2 | GET PATH?f=5.7&n=-0L.0L&l=1231325345345345.34534534634634634 HTTP/1.1 3 | Host: HOST 4 | Content-Length: 0 5 | 6 | 7 | [response] 8 | HTTP/1.1 200 OK 9 | Date: xxx, xx xxx xxxx xx:xx:xx xxx 10 | Content-type: text/plain; charset=utf-8 11 | Content-Length: LENGTH 12 | 13 | 14 | [test] 15 | (let response = (Darklang.Stdlib.HttpClient.request "get" "http://URL?f=5.7&n=-0L.0L&l=1231325345345345.34534534634634634" [] [] |> Builtin.unwrap) 16 | let respHeaders = (response.headers |> Darklang.Stdlib.List.filter (fun h -> (Darklang.Stdlib.Tuple2.first h) != "date")) 17 | {response with headers = respHeaders}) == 18 | Darklang.Stdlib.HttpClient.Response 19 | { statusCode = 200L 20 | headers = 21 | [ 22 | ("server", "kestrel") 23 | ("content-length", "LENGTH") 24 | ("content-type", "text/plain; charset=utf-8") 25 | ] 26 | body = [] } 27 | -------------------------------------------------------------------------------- /backend/testfiles/httpclient/v0/_uri-with-auth-both.test: -------------------------------------------------------------------------------- 1 | [expected-request] 2 | GET PATH HTTP/1.1 3 | Authorization: Basic dXNlcjpwYXNzd29yZA== 4 | Host: HOST 5 | Content-Length: 0 6 | 7 | 8 | [response] 9 | HTTP/1.1 200 OK 10 | Date: xxx, xx xxx xxxx xx:xx:xx xxx 11 | Content-type: text/plain; charset=utf-8 12 | Content-Length: LENGTH 13 | 14 | Hello back 15 | 16 | [test] 17 | (let response = (Darklang.Stdlib.HttpClient.request "get" "http://user:password@URL" [] []) |> Builtin.unwrap 18 | let respHeaders = response.headers |> Darklang.Stdlib.List.filter (fun h -> Darklang.Stdlib.Tuple2.first h != "date") 19 | {response with headers = respHeaders}) == 20 | Darklang.Stdlib.HttpClient.Response 21 | { statusCode = 200L 22 | headers = 23 | [ 24 | ("server", "kestrel") 25 | ("content-length", "LENGTH") 26 | ("content-type", "text/plain; charset=utf-8") 27 | ] 28 | body = "Hello back" |> Darklang.Stdlib.String.toBytes} 29 | -------------------------------------------------------------------------------- /backend/src/LibBinarySerialization/Serializers/PT/PackageValue.fs: -------------------------------------------------------------------------------- 1 | module LibBinarySerialization.Serializers.PT.PackageValue 2 | 3 | open System 4 | open System.IO 5 | open Prelude 6 | 7 | open LibExecution.ProgramTypes 8 | 9 | open LibBinarySerialization.BinaryFormat 10 | open LibBinarySerialization.Serializers.Common 11 | open LibBinarySerialization.Serializers.PT.Common 12 | 13 | let write (w : BinaryWriter) (v : PackageValue.PackageValue) : unit = 14 | Guid.write w v.id 15 | LibBinarySerialization.Serializers.PT.Expr.Expr.write w v.body 16 | String.write w v.description 17 | Deprecation.write w FQValueName.write v.deprecated 18 | 19 | let read (r : BinaryReader) : PackageValue.PackageValue = 20 | let id = Guid.read r 21 | let body = LibBinarySerialization.Serializers.PT.Expr.Expr.read r 22 | let description = String.read r 23 | let deprecated = Deprecation.read r FQValueName.read 24 | { id = id; body = body; description = description; deprecated = deprecated } 25 | -------------------------------------------------------------------------------- /backend/testfiles/httpclient/v0/_request-content-type-empty.test: -------------------------------------------------------------------------------- 1 | [expected-request] 2 | GET PATH HTTP/1.1 3 | Host: HOST 4 | Content-Length: 0 5 | 6 | [response] 7 | HTTP/1.1 200 OK 8 | Date: xxx, xx xxx xxxx xx:xx:xx xxx 9 | Content-type: text/plain; charset=utf-8 10 | Content-Length: LENGTH 11 | 12 | Hello back 13 | 14 | [test] 15 | (let reqHeaders = [("Content-Type", "")] 16 | let response = (Darklang.Stdlib.HttpClient.request "get" "http://URL" reqHeaders []) |> Builtin.unwrap 17 | let respHeaders = response.headers |> Darklang.Stdlib.List.filter (fun h -> Darklang.Stdlib.Tuple2.first h != "date") 18 | {response with headers = respHeaders}) == 19 | Darklang.Stdlib.HttpClient.Response 20 | { 21 | statusCode = 200L 22 | headers = 23 | [ 24 | ("content-length", "LENGTH") 25 | ("content-type", "text/plain; charset=utf-8") 26 | ("Server", "Kestrel") 27 | ] 28 | body = "Hello back" |> Darklang.Stdlib.String.toBytes} 29 | -------------------------------------------------------------------------------- /.claude/settings.local.json: -------------------------------------------------------------------------------- 1 | { 2 | "permissions": { 3 | "allow": [ 4 | "Bash(mkdir:*)", 5 | "Bash(grep:*)", 6 | "Bash(rg:*)", 7 | "WebFetch(domain:github.com)", 8 | "Bash(find:*)", 9 | "Bash(ls:*)", 10 | "Bash(./scripts/build/build-release-cli-exes.sh:*)", 11 | "Bash(./scripts/run-cli:*)", 12 | "Bash(./scripts/run-backend-tests:*)", 13 | "Bash(cat:*)", 14 | "Bash(dotnet-illink-analyze:*)", 15 | "Bash(./scripts/build/_dotnet-wrapper:*)", 16 | "Bash(sqlite3:*)", 17 | "Bash(/dev/null)", 18 | "Bash(done)", 19 | "Bash(/home/stachu/code/dark/scripts/build/build-release-cli-exes.sh:*)", 20 | "Bash(scripts/run-backend-tests:*)", 21 | "Bash(timeout:*)", 22 | "mcp__darklang-push-notifications__send_ntfy_notification", 23 | "mcp__push-notifications__send_pushover_notification", 24 | "mcp__push-notifications__send_ntfy_notification" 25 | ], 26 | "deny": [] 27 | } 28 | } -------------------------------------------------------------------------------- /packages/feriel/modelContextProtocol/push-notification/README.md: -------------------------------------------------------------------------------- 1 | # Push Notification MCP Server 2 | 3 | This is an MCP server that provides push notification capabilities through multiple services. 4 | 5 | ## Features 6 | 7 | - **Pushover notifications**: Send notifications via the Pushover 8 | - **ntfy.sh notifications**: Send notifications via the ntfy.sh 9 | 10 | ## Setup 11 | 12 | ### Adding to Claude Desktop 13 | 14 | To add this MCP server run: 15 | 16 | ```bash 17 | claude mcp add push-notifications ./scripts/run-cli run @Feriel.ModelContextProtocol.PushNotification.Server.main --env PUSHOVER_USER_KEY=your_pushover_user_key --env PUSHOVER_APP_TOKEN=your_pushover_app_token 18 | ``` 19 | 20 | ### Environment Variables 21 | 22 | For Pushover notifications, you'll need: 23 | 24 | - `PUSHOVER_USER_KEY`: Your Pushover user key 25 | - `PUSHOVER_APP_TOKEN`: Your Pushover application token 26 | 27 | For ntfy.sh notifications, no authentication is required - just specify a topic name. 28 | -------------------------------------------------------------------------------- /scripts/package-extension.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | . ./scripts/devcontainer/_assert-in-container "$0" "$@" 3 | set -euo pipefail 4 | 5 | 6 | # Script to package VS Code extension 7 | 8 | EXTENSION_DIR="./vscode-extension" 9 | VSIX_DIR="./.vsix" 10 | 11 | # Create VSIX directory if it doesn't exist 12 | mkdir -p "$VSIX_DIR" 13 | 14 | # Package the extension 15 | echo "Packaging extension..." 16 | cd "$EXTENSION_DIR" 17 | npm i 18 | vsce package --skip-license --pre-release 19 | 20 | # Move the VSIX file to .vsix directory 21 | echo "Moving VSIX file to $VSIX_DIR..." 22 | mv -- *.vsix "../$VSIX_DIR/" 23 | 24 | # Get the name of the VSIX file 25 | cd .. 26 | VSIX_FILE=$(find "$VSIX_DIR" -name "*.vsix" -type f -print0 | xargs -0 ls -t | head -1) 27 | VSIX_FILENAME=$(basename "$VSIX_FILE") 28 | 29 | echo "✅ Extension packaged successfully: $VSIX_FILENAME" 30 | echo 31 | echo "To use the darklang extension in the devcontainer, run:" 32 | echo "code --install-extension $VSIX_DIR/$VSIX_FILENAME" -------------------------------------------------------------------------------- /backend/testfiles/httpclient/v0/basic-head-returns-body-helper-function.test: -------------------------------------------------------------------------------- 1 | [expected-request] 2 | HEAD PATH HTTP/1.1 3 | Host: HOST 4 | Content-Length: 0 5 | 6 | [response] 7 | HTTP/1.1 200 OK 8 | Date: xxx, xx xxx xxxx xx:xx:xx xxx 9 | Content-type: text/plain; charset=utf-8 10 | Content-Length: LENGTH 11 | 12 | Here's a body! 13 | 14 | 15 | [test] 16 | // HEAD requests aren't supposed to actually return a body. But it seems like the 17 | // clients successfully ignore it. 18 | (let response = (Darklang.Stdlib.HttpClient.head "http://URL" |> Builtin.unwrap) 19 | let respHeaders = (response.headers |> Darklang.Stdlib.List.filter (fun h -> (Darklang.Stdlib.Tuple2.first h) != "date")) 20 | {response with headers = respHeaders}) == 21 | Darklang.Stdlib.HttpClient.Response 22 | { statusCode = 200L 23 | headers = [ 24 | ("server", "kestrel") 25 | ("content-length", "15") 26 | ("content-type", "text/plain; charset=utf-8") 27 | ] 28 | body = [] } 29 | -------------------------------------------------------------------------------- /backend/testfiles/httpclient/v0/basic-head-returns-body.test: -------------------------------------------------------------------------------- 1 | [expected-request] 2 | HEAD PATH HTTP/1.1 3 | Host: HOST 4 | Content-Length: 0 5 | 6 | [response] 7 | HTTP/1.1 200 OK 8 | Date: xxx, xx xxx xxxx xx:xx:xx xxx 9 | Content-type: text/plain; charset=utf-8 10 | Content-Length: LENGTH 11 | 12 | Here's a body! 13 | 14 | 15 | [test] 16 | // HEAD requests aren't supposed to actually return a body. But it seems like the 17 | // clients successfully ignore it. 18 | (let response = (Darklang.Stdlib.HttpClient.request "head" "http://URL" [] [] |> Builtin.unwrap) 19 | let respHeaders = (response.headers |> Darklang.Stdlib.List.filter (fun h -> (Darklang.Stdlib.Tuple2.first h) != "date")) 20 | {response with headers = respHeaders}) == 21 | Darklang.Stdlib.HttpClient.Response 22 | { statusCode = 200L 23 | headers = [ 24 | ("server", "kestrel") 25 | ("content-length", "15") 26 | ("content-type", "text/plain; charset=utf-8") 27 | ] 28 | body = [] } 29 | -------------------------------------------------------------------------------- /backend/testfiles/httpclient/v0/todo/response-body-unicode.test: -------------------------------------------------------------------------------- 1 | [expected-request] 2 | GET PATH HTTP/1.1 3 | Accept: */* 4 | Accept-Encoding: deflate, gzip, br 5 | Content-Type: text/plain; charset=utf-8 6 | Host: HOST 7 | 8 | [response] 9 | HTTP/1.1 200 OK 10 | Date: xxx, xx xxx xxxx xx:xx:xx xxx 11 | Content-Length: LENGTH 12 | 13 | 👱👱🏻👱🏼👱🏽👱🏾👱🏿", "👨‍❤️‍💋‍👨", "﷽﷽﷽ 14 | 15 | [test] 16 | (let response = (Darklang.Stdlib.HttpClient.request "get" "http://URL" [] []) |> Builtin.unwrap 17 | let respHeaders = response.headers |> Darklang.Stdlib.List.filter (fun h -> Darklang.Stdlib.Tuple2.first h != "date") 18 | {response with headers = respHeaders}) = 19 | Darklang.Stdlib.HttpClient.Response 20 | { body = """👱👱🏻👱🏼👱🏽👱🏾👱🏿", "👨‍❤️‍💋‍👨", "﷽﷽﷽""" 21 | statusCode = 200L 22 | headers = 23 | [ 24 | ("content-length", "LENGTH") 25 | ("server", "kestrel") 26 | ] 27 | raw = """👱👱🏻👱🏼👱🏽👱🏾👱🏿", "👨‍❤️‍💋‍👨", "﷽﷽﷽"""} 28 | -------------------------------------------------------------------------------- /backend/testfiles/httpclient/v0/uri-with-path-basic.test: -------------------------------------------------------------------------------- 1 | [expected-request] 2 | GET /v0/uri-with-path-basic/some/2path/hyphen-ated/under_scored HTTP/1.1 3 | Host: HOST 4 | Content-Length: 0 5 | 6 | 7 | [response] 8 | HTTP/1.1 200 OK 9 | Date: xxx, xx xxx xxxx xx:xx:xx xxx 10 | Content-type: text/plain; charset=utf-8 11 | Content-Length: LENGTH 12 | 13 | Hello back 14 | 15 | [test] 16 | (let response = (Darklang.Stdlib.HttpClient.request "get" "http://URL/some/2path/hyphen-ated/under_scored" [] [] |> Builtin.unwrap) 17 | let respHeaders = (response.headers |> Darklang.Stdlib.List.filter (fun h -> (Darklang.Stdlib.Tuple2.first h) != "date")) 18 | {response with headers = respHeaders}) == 19 | Darklang.Stdlib.HttpClient.Response 20 | { statusCode = 200L 21 | headers = [ 22 | ("server", "kestrel") 23 | ("content-length", "LENGTH") 24 | ("content-type", "text/plain; charset=utf-8") 25 | ] 26 | body = ("Hello back" |> Darklang.Stdlib.String.toBytes) } -------------------------------------------------------------------------------- /packages/darklang/scm/packageOps.dark: -------------------------------------------------------------------------------- 1 | module Darklang.SCM.PackageOps 2 | 3 | /// Add a list of package ops to the database, applying them immediately 4 | /// 5 | /// Returns the number of inserted ops on success (duplicates are skipped), 6 | /// or an error message on failure 7 | let add 8 | (instanceID: Stdlib.Option.Option) 9 | (branchID: Stdlib.Option.Option) 10 | (ops: List) 11 | : Stdlib.Result.Result = 12 | Builtin.scmAddOps instanceID branchID ops 13 | 14 | 15 | /// Get recent package ops from the database 16 | let getRecent 17 | (branchID: Stdlib.Option.Option) 18 | (limit: Int64) 19 | : List = 20 | Builtin.scmGetRecentOps branchID limit 21 | 22 | 23 | /// Get recent package ops from ALL branches (no branch filter) 24 | let getRecentAllBranches 25 | (limit: Int64) 26 | : List = 27 | Builtin.scmGetRecentOpsAllBranches limit 28 | -------------------------------------------------------------------------------- /backend/src/Prelude/Dictionary.fs: -------------------------------------------------------------------------------- 1 | module Dictionary 2 | 3 | type T<'k, 'v> = System.Collections.Generic.Dictionary<'k, 'v> 4 | 5 | let get (k : 'k) (t : T<'k, 'v>) : Option<'v> = FSharpPlus.Dictionary.tryGetValue k t 6 | 7 | let containsKey (k : 'k) (t : T<'k, 'v>) : bool = t.ContainsKey k 8 | 9 | let add (k : 'k) (v : 'v) (d : T<'k, 'v>) : unit = 10 | d[k] <- v 11 | () 12 | 13 | let empty () : T<'k, 'v> = System.Collections.Generic.Dictionary<'k, 'v>() 14 | 15 | let keys = FSharpPlus.Dictionary.keys 16 | let values = FSharpPlus.Dictionary.values 17 | 18 | let toList (d : T<'k, 'v>) : List<'k * 'v> = 19 | seq { 20 | let mutable e = d.GetEnumerator() 21 | 22 | while e.MoveNext() do 23 | yield (e.Current.Key, e.Current.Value) 24 | } 25 | |> Seq.toList 26 | 27 | let fromList (l : List<'k * 'v>) : T<'k, 'v> = 28 | let result = empty () 29 | List.iter (fun (k, v) -> result[k] <- v) l 30 | result 31 | 32 | let toMap (d : T<'k, 'v>) : Map<'k, 'v> = d |> toList |> Map.ofList 33 | -------------------------------------------------------------------------------- /backend/testfiles/httpclient/v0/todo/response-redirect-to-same-place-relative.test: -------------------------------------------------------------------------------- 1 | [expected-request] 2 | GET PATH HTTP/1.1 3 | Accept: */* 4 | Accept-Encoding: deflate, gzip, br 5 | Content-Type: text/plain; charset=utf-8 6 | Host: HOST 7 | 8 | [response] 9 | HTTP/1.1 302 Found 10 | Date: xxx, xx xxx xxxx xx:xx:xx xxx 11 | Location: /v0/response-redirect-to-same-place-relative 12 | 13 | [test] 14 | (match Darklang.Stdlib.HttpClient.request "get" "http://URL" [] [] with 15 | | Ok -> "fail" 16 | | Error response -> 17 | let respHeaders = response.headers |> Darklang.Stdlib.List.filter (fun h -> Darklang.Stdlib.Tuple2.first h != "date") 18 | {response with headers = respHeaders}) = 19 | Darklang.Stdlib.HttpClient.Response 20 | { body = "" 21 | statusCode = 302 22 | error = "" 23 | headers = 24 | [ 25 | "HTTP/1.1 302 Found" = "" 26 | "Transfer-Encoding" = "chunked" 27 | ("server", "kestrel") 28 | ] 29 | raw = "" 30 | } 31 | 32 | -------------------------------------------------------------------------------- /tree-sitter-darklang/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tree-sitter-darklang", 3 | "version": "0.0.1", 4 | "description": "A tree-sitter parser to parse Darklang source code", 5 | "main": "bindings/node", 6 | "scripts": { 7 | "build-parser": "tree-sitter generate", 8 | "test": "tree-sitter test", 9 | "parse": "tree-sitter parse" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "git+https://github.com/darklang/dark.git" 14 | }, 15 | "author": "", 16 | "license": "SEE LICENSE IN LICENSE.md", 17 | "bugs": { 18 | "url": "https://github.com/darklang/dark/issues" 19 | }, 20 | "homepage": "https://github.com/darklang/dark#readme", 21 | "dependencies": { 22 | "nan": "^2.17.0" 23 | }, 24 | "devDependencies": { 25 | "tree-sitter-cli": "^0.20.8", 26 | "tree-sitter-javascript": "^0.19.0", 27 | "web-tree-sitter": "^0.20.8" 28 | }, 29 | "tree-sitter": [ 30 | { 31 | "file-types": [ 32 | ".dark" 33 | ] 34 | } 35 | ] 36 | } -------------------------------------------------------------------------------- /backend/src/ProdExec/ProdExec.fsproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net8.0 6 | 8.0 7 | 8 | true 9 | false 10 | false 11 | false 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /backend/testfiles/httpclient/v0/todo/response-content-type-invalid.test: -------------------------------------------------------------------------------- 1 | [expected-request] 2 | GET PATH HTTP/1.1 3 | Accept: */* 4 | Accept-Encoding: deflate, gzip, br 5 | Content-Type: text/plain; charset=utf-8 6 | Host: HOST 7 | 8 | [response] 9 | HTTP/1.1 200 OK 10 | Date: xxx, xx xxx xxxx xx:xx:xx xxx 11 | Content-type: invalid content type header 12 | Content-Length: LENGTH 13 | 14 | "Hello back" 15 | 16 | [test] 17 | (let reqHeaders = {} 18 | let response = (Darklang.Stdlib.HttpClient.request "get" "http://URL" {} reqHeaders) |> Builtin.unwrap 19 | let respHeaders = response.headers |> Darklang.Stdlib.List.filter (fun h -> Darklang.Stdlib.Tuple2.first h != "date") 20 | {response with headers = respHeaders}) = 21 | Darklang.Stdlib.HttpClient.Response 22 | { body = "\"Hello back\"" 23 | statusCode = 200L 24 | headers = 25 | [ 26 | ("content-length", "LENGTH") 27 | "Content-Type" = "invalid content type header" 28 | ("server", "kestrel") 29 | ]} 30 | -------------------------------------------------------------------------------- /backend/testfiles/httpclient/v0/uri-with-path-dots.test: -------------------------------------------------------------------------------- 1 | [expected-request] 2 | GET /v0/uri-with-path-dots/2path/hyphen-ated/under_scored/ HTTP/1.1 3 | Host: HOST 4 | Content-Length: 0 5 | 6 | 7 | [response] 8 | HTTP/1.1 200 OK 9 | Date: xxx, xx xxx xxxx xx:xx:xx xxx 10 | Content-type: text/plain; charset=utf-8 11 | Content-Length: LENGTH 12 | 13 | Hello back 14 | 15 | [test] 16 | (let response = (Darklang.Stdlib.HttpClient.request "get" "http://URL/some/../2path/hyphen-ated/under_scored/" [] [] |> Builtin.unwrap) 17 | let respHeaders = (response.headers |> Darklang.Stdlib.List.filter (fun h -> (Darklang.Stdlib.Tuple2.first h) != "date")) 18 | {response with headers = respHeaders}) == 19 | Darklang.Stdlib.HttpClient.Response 20 | { statusCode = 200L 21 | headers = 22 | [ 23 | ("server", "kestrel") 24 | ("content-length", "LENGTH") 25 | ("content-type", "text/plain; charset=utf-8") 26 | ] 27 | body = ("Hello back" |> Darklang.Stdlib.String.toBytes) } 28 | -------------------------------------------------------------------------------- /backend/testfiles/httpclient/v0/todo/request-query-param-null.test: -------------------------------------------------------------------------------- 1 | [expected-request] 2 | GET PATH?x HTTP/1.1 3 | Accept: */* 4 | Accept-Encoding: deflate, gzip, br 5 | Content-Type: application/json; charset=utf-8 6 | Host: HOST 7 | 8 | [response] 9 | HTTP/1.1 200 OK 10 | Date: xxx, xx xxx xxxx xx:xx:xx xxx 11 | Content-type: text/plain; charset=utf-8 12 | Content-Length: LENGTH 13 | 14 | "Hello back" 15 | 16 | [test] 17 | (let response = (Darklang.Stdlib.HttpClient.request "get" "http://URL" [HttpClient.ContentType.json_v0] { x = null }) |> Builtin.unwrap 18 | let respHeaders = response.headers |> Darklang.Stdlib.List.filter (fun h -> Darklang.Stdlib.Tuple2.first h != "date") 19 | {response with headers = respHeaders}) = 20 | Darklang.Stdlib.HttpClient.Response 21 | { body = "\"Hello back\"" 22 | statusCode = 200L 23 | headers = 24 | [ 25 | ("content-length", "LENGTH") 26 | ("content-type", "text/plain; charset=utf-8") 27 | ("server", "kestrel") 28 | ]} 29 | -------------------------------------------------------------------------------- /scripts/deployment/new-push-containers.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | . ./scripts/devcontainer/_assert-in-container "$0" "$@" 3 | 4 | set -euo pipefail 5 | 6 | set -x 7 | 8 | registry="us-central1-docker.pkg.dev" 9 | gcloud auth configure-docker "$registry" 10 | 11 | repo="$registry/darklang-next/production-containers" 12 | 13 | docker tag bwdserver:latest "$repo/bwdserver:latest" 14 | docker image push "$repo/bwdserver:latest" 15 | 16 | docker tag prodexec:latest "$repo/prodexec:latest" 17 | docker image push "$repo/prodexec:latest" 18 | 19 | # Digests are a property of the container in the registry, which is why we do 20 | # this here 21 | bwdserver_digest=$(docker inspect --format='{{index .RepoDigests 0}}' "$repo/bwdserver:latest") 22 | prodexec_digest=$(docker inspect --format='{{index .RepoDigests 0}}' "$repo/prodexec:latest") 23 | 24 | cat << EOF > image-digests.json 25 | [ 26 | { "name": "bwdserver", "digest": "$bwdserver_digest" }, 27 | { "name": "prodexec", "digest": "$prodexec_digest" } 28 | ] 29 | EOF 30 | -------------------------------------------------------------------------------- /backend/testfiles/execution/stdlib/bool.dark: -------------------------------------------------------------------------------- 1 | Stdlib.Bool.and_v0 false false = false 2 | Stdlib.Bool.and_v0 false true = false 3 | Stdlib.Bool.and_v0 true false = false 4 | Stdlib.Bool.and_v0 true true = true 5 | Stdlib.Bool.and_v0 (8L >= 5L) (6L <= 8L) = true 6 | 7 | (false && false) = false 8 | (false && true) = false 9 | (true && false) = false 10 | (true && true) = true 11 | ((8L >= 5L) && (6L <= 8L)) = true 12 | 13 | Stdlib.Bool.not_v0 false = true 14 | Stdlib.Bool.not_v0 true = false 15 | 16 | Stdlib.Bool.or_v0 false false = false 17 | Stdlib.Bool.or_v0 true false = true 18 | Stdlib.Bool.or_v0 false true = true 19 | Stdlib.Bool.or_v0 true true = true 20 | 21 | (false || false) = false 22 | (true || false) = true 23 | (false || true) = true 24 | (true || true) = true 25 | 26 | Stdlib.Bool.xor_v0 false false = false 27 | Stdlib.Bool.xor_v0 false true = true 28 | Stdlib.Bool.xor_v0 true false = true 29 | Stdlib.Bool.xor_v0 true true = false 30 | 31 | Stdlib.Bool.toString true = "true" 32 | Stdlib.Bool.toString false = "false" -------------------------------------------------------------------------------- /backend/testfiles/httpclient/v0/todo/response-header-empty.test: -------------------------------------------------------------------------------- 1 | [expected-request] 2 | GET PATH HTTP/1.1 3 | Accept: */* 4 | Accept-Encoding: deflate, gzip, br 5 | Content-Type: text/plain; charset=utf-8 6 | Host: HOST 7 | 8 | [response] 9 | HTTP/1.1 200 OK 10 | Date: xxx, xx xxx xxxx xx:xx:xx xxx 11 | Content-type: text/plain; charset=utf-8 12 | Content-Length: LENGTH 13 | Some-Header: 14 | 15 | "Hello back" 16 | 17 | [test] 18 | (let response = (Darklang.Stdlib.HttpClient.request "get" "http://URL" [] []) |> Builtin.unwrap 19 | let respHeaders = response.headers |> Darklang.Stdlib.List.filter (fun h -> Darklang.Stdlib.Tuple2.first h != "date") 20 | {response with headers = respHeaders}) = 21 | Darklang.Stdlib.HttpClient.Response 22 | { body = "\"Hello back\"" 23 | statusCode = 200L 24 | headers = 25 | [ 26 | "Some-Header" = "" 27 | ("content-length", "LENGTH") 28 | ("content-type", "text/plain; charset=utf-8") 29 | ("server", "kestrel") 30 | ]} 31 | -------------------------------------------------------------------------------- /backend/testfiles/httpclient/v0/todo/response-header-int.test: -------------------------------------------------------------------------------- 1 | [expected-request] 2 | GET PATH HTTP/1.1 3 | Accept: */* 4 | Accept-Encoding: deflate, gzip, br 5 | Content-Type: text/plain; charset=utf-8 6 | Host: HOST 7 | 8 | [response] 9 | HTTP/1.1 200 OK 10 | Date: xxx, xx xxx xxxx xx:xx:xx xxx 11 | Content-type: text/plain; charset=utf-8 12 | Content-Length: LENGTH 13 | Some-Header: 5 14 | 15 | "Hello back" 16 | 17 | [test] 18 | (let response = (Darklang.Stdlib.HttpClient.request "get" "http://URL" [] []) |> Builtin.unwrap 19 | let respHeaders = response.headers |> Darklang.Stdlib.List.filter (fun h -> Darklang.Stdlib.Tuple2.first h != "date") 20 | {response with headers = respHeaders}) = 21 | Darklang.Stdlib.HttpClient.Response 22 | { body = "\"Hello back\"" 23 | statusCode = 200L 24 | headers = 25 | [ 26 | "Some-Header" = "5" 27 | ("content-length", "LENGTH") 28 | ("content-type", "text/plain; charset=utf-8") 29 | ("server", "kestrel") 30 | ]} 31 | -------------------------------------------------------------------------------- /backend/testfiles/httpclient/v0/todo/response-redirect-delete.test: -------------------------------------------------------------------------------- 1 | [expected-request] 2 | DELETE PATH HTTP/1.1 3 | Accept: */* 4 | Accept-Encoding: deflate, gzip, br 5 | Content-Type: text/plain; charset=utf-8 6 | Host: HOST 7 | 8 | [response] 9 | HTTP/1.1 302 Found 10 | Date: xxx, xx xxx xxxx xx:xx:xx xxx 11 | Location: /v0/response-redirect-destination 12 | 13 | 14 | [test] 15 | (match Darklang.Stdlib.HttpClient.request "delete" "http://URL" [] [] with 16 | | Ok _ -> "fail" 17 | | Error response -> 18 | let respHeaders = response.headers |> Darklang.Stdlib.List.filter (fun h -> Darklang.Stdlib.Tuple2.first h != "date") 19 | {response with headers = respHeaders}) = 20 | Darklang.Stdlib.HttpClient.Response 21 | { body = "" 22 | statusCode = 302 23 | headers = 24 | [ 25 | "HTTP/1.1 302 Found" = "" 26 | "Location" = "/v0/response-redirect-destination" 27 | ("server", "kestrel") 28 | "Transfer-Encoding" = "chunked" 29 | ] 30 | raw = ""} 31 | -------------------------------------------------------------------------------- /backend/testfiles/httpclient/v0/todo/uri-with-auth-just-username.test: -------------------------------------------------------------------------------- 1 | [expected-request] 2 | GET PATH HTTP/1.1 3 | Accept: */* 4 | Accept-Encoding: deflate, gzip, br 5 | Authorization: Basic dXNlcjo= 6 | Content-Type: text/plain; charset=utf-8 7 | Host: HOST 8 | 9 | 10 | [response] 11 | HTTP/1.1 200 OK 12 | Date: xxx, xx xxx xxxx xx:xx:xx xxx 13 | Content-type: text/plain; charset=utf-8 14 | Content-Length: LENGTH 15 | 16 | "Hello back" 17 | 18 | [test] 19 | (let response = (Darklang.Stdlib.HttpClient.request "get" "http://user@URL" [] []) |> Builtin.unwrap 20 | let respHeaders = response.headers |> Darklang.Stdlib.List.filter (fun h -> Darklang.Stdlib.Tuple2.first h != "date") 21 | {response with headers = respHeaders}) = 22 | Darklang.Stdlib.HttpClient.Response 23 | { body = "\"Hello back\"" 24 | statusCode = 200L 25 | headers = 26 | [ 27 | ("content-length", "LENGTH") 28 | ("content-type", "text/plain; charset=utf-8") 29 | ("server", "kestrel") 30 | ]} 31 | -------------------------------------------------------------------------------- /backend/testfiles/httpclient/v0/response-header-duplicate.test: -------------------------------------------------------------------------------- 1 | [expected-request] 2 | GET PATH HTTP/1.1 3 | Host: HOST 4 | Content-Length: 0 5 | 6 | [response] 7 | HTTP/1.1 200 OK 8 | Date: xxx, xx xxx xxxx xx:xx:xx xxx 9 | Content-type: text/plain; charset=utf-8 10 | Content-Length: LENGTH 11 | some-header: string 12 | some-header: other string 13 | 14 | Hello back 15 | 16 | [test] 17 | (let response = (Darklang.Stdlib.HttpClient.request "get" "http://URL" [] [] |> Builtin.unwrap) 18 | let respHeaders = (response.headers |> Darklang.Stdlib.List.filter (fun h -> (Darklang.Stdlib.Tuple2.first h) != "date")) 19 | {response with headers = respHeaders}) == 20 | Darklang.Stdlib.HttpClient.Response 21 | { statusCode = 200L 22 | headers = 23 | [ 24 | ("server", "kestrel") 25 | ("some-header", "other string") 26 | ("content-length", "LENGTH") 27 | ("content-type", "text/plain; charset=utf-8") 28 | ] 29 | body = ("Hello back" |> Darklang.Stdlib.String.toBytes) } 30 | -------------------------------------------------------------------------------- /backend/testfiles/httpclient/v0/todo/response-content-encoding-brotli.test: -------------------------------------------------------------------------------- 1 | [expected-request] 2 | GET PATH HTTP/1.1 3 | Accept: */* 4 | Accept-Encoding: deflate, gzip, br 5 | Content-Type: text/plain; charset=utf-8 6 | Host: HOST 7 | 8 | 9 | [response] 10 | HTTP/1.1 200 OK 11 | Date: xxx, xx xxx xxxx xx:xx:xx xxx 12 | Content-type: text/plain; charset=utf-8 13 | Content-Encoding: br 14 | 15 | "Hello back" 16 | 17 | [test] 18 | (let response = (Darklang.Stdlib.HttpClient.request "get" "http://URL/" [] []) |> Builtin.unwrap 19 | let respHeaders = response.headers |> Darklang.Stdlib.List.filter (fun h -> Darklang.Stdlib.Tuple2.first h != "date") 20 | {response with headers = respHeaders}) = 21 | Darklang.Stdlib.HttpClient.Response 22 | { body = "\"Hello back\"" 23 | statusCode = 200L 24 | headers = 25 | [ 26 | "Transfer-Encoding" = "chunked" 27 | "Content-Encoding" = "br" 28 | ("content-type", "text/plain; charset=utf-8") 29 | ("server", "kestrel") 30 | ]} 31 | -------------------------------------------------------------------------------- /backend/testfiles/httpclient/v0/todo/response-header-object.test: -------------------------------------------------------------------------------- 1 | [expected-request] 2 | GET PATH HTTP/1.1 3 | Accept: */* 4 | Accept-Encoding: deflate, gzip, br 5 | Content-Type: text/plain; charset=utf-8 6 | Host: HOST 7 | 8 | [response] 9 | HTTP/1.1 200 OK 10 | Date: xxx, xx xxx xxxx xx:xx:xx xxx 11 | Content-type: text/plain; charset=utf-8 12 | Content-Length: LENGTH 13 | Some-Header: {} 14 | 15 | "Hello back" 16 | 17 | [test] 18 | (let response = (Darklang.Stdlib.HttpClient.request "get" "http://URL" [] []) |> Builtin.unwrap 19 | let respHeaders = response.headers |> Darklang.Stdlib.List.filter (fun h -> Darklang.Stdlib.Tuple2.first h != "date") 20 | {response with headers = respHeaders}) = 21 | Darklang.Stdlib.HttpClient.Response 22 | { body = "\"Hello back\"" 23 | statusCode = 200L 24 | headers = 25 | [ 26 | "Some-Header" = "{}" 27 | ("content-length", "LENGTH") 28 | ("content-type", "text/plain; charset=utf-8") 29 | ("server", "kestrel") 30 | ]} 31 | -------------------------------------------------------------------------------- /backend/testfiles/httpclient/v0/todo/uri-with-auth-just-password.test: -------------------------------------------------------------------------------- 1 | [expected-request] 2 | GET PATH HTTP/1.1 3 | Accept: */* 4 | Accept-Encoding: deflate, gzip, br 5 | Authorization: Basic OnBhc3N3b3Jk 6 | Content-Type: text/plain; charset=utf-8 7 | Host: HOST 8 | 9 | 10 | [response] 11 | HTTP/1.1 200 OK 12 | Date: xxx, xx xxx xxxx xx:xx:xx xxx 13 | Content-type: text/plain; charset=utf-8 14 | Content-Length: LENGTH 15 | 16 | "Hello back" 17 | 18 | [test] 19 | (let response = (Darklang.Stdlib.HttpClient.request "get" "http://:password@URL" [] []) |> Builtin.unwrap 20 | let respHeaders = response.headers |> Darklang.Stdlib.List.filter (fun h -> Darklang.Stdlib.Tuple2.first h != "date") 21 | {response with headers = respHeaders}) = 22 | Darklang.Stdlib.HttpClient.Response 23 | { body = "\"Hello back\"" 24 | statusCode = 200L 25 | headers = 26 | [ 27 | ("content-length", "LENGTH") 28 | ("content-type", "text/plain; charset=utf-8") 29 | ("server", "kestrel") 30 | ]} 31 | -------------------------------------------------------------------------------- /docs/production/emergency-login.md: -------------------------------------------------------------------------------- 1 | # Emergency Login 2 | 3 | If login is broken because auth0 is down or the ops-login canvas (which backs 4 | login.darklang.com) is down, on-call will still need a way to log in. 5 | 6 | To do this, run: 7 | 8 | - `scripts/production/connect-to-exec host` 9 | - `./app/ProdExec emergency-login ` 10 | 11 | This will: 12 | 13 | - inserts a row into the `session` table in postgres and 14 | - prints a session key, along with instructions for use 15 | 16 | To use: 17 | 18 | - you will need the cookie-inspector extension installed: 19 | https://chrome.google.com/webstore/detail/cookie-inspector/jgbbilmfbammlbbhmmgaagdkbkepnijn 20 | - You'll need to be on darklang.com (or darklang.localhost, for local env login) for the following steps 21 | - open the dev console, go to the Cookies tab, right click the table and select 22 | Add Cookie 23 | - Name = `__session`, Value and Domain are printed by the script above 24 | - Click Submit 25 | 26 | With said cookie set, you are now logged in. 27 | -------------------------------------------------------------------------------- /backend/testfiles/httpclient/v0/todo/response-content-encoding-gzipped.test: -------------------------------------------------------------------------------- 1 | [expected-request] 2 | GET PATH HTTP/1.1 3 | Accept: */* 4 | Accept-Encoding: deflate, gzip, br 5 | Content-Type: text/plain; charset=utf-8 6 | Host: HOST 7 | 8 | 9 | [response] 10 | HTTP/1.1 200 OK 11 | Date: xxx, xx xxx xxxx xx:xx:xx xxx 12 | Content-type: text/plain; charset=utf-8 13 | Content-Encoding: gzip 14 | 15 | "Hello back" 16 | 17 | [test] 18 | (let response = (Darklang.Stdlib.HttpClient.request "get" "http://URL/" [] []) |> Builtin.unwrap 19 | let respHeaders = response.headers |> Darklang.Stdlib.List.filter (fun h -> Darklang.Stdlib.Tuple2.first h != "date") 20 | {response with headers = respHeaders}) = 21 | Darklang.Stdlib.HttpClient.Response 22 | { body = "\"Hello back\"" 23 | statusCode = 200L 24 | headers = 25 | [ 26 | "Transfer-Encoding" = "chunked" 27 | "Content-Encoding" = "gzip" 28 | ("content-type", "text/plain; charset=utf-8") 29 | ("server", "kestrel") 30 | ]} 31 | -------------------------------------------------------------------------------- /backend/testfiles/httpclient/v0/todo/response-header-string.test: -------------------------------------------------------------------------------- 1 | [expected-request] 2 | GET PATH HTTP/1.1 3 | Accept: */* 4 | Accept-Encoding: deflate, gzip, br 5 | Content-Type: text/plain; charset=utf-8 6 | Host: HOST 7 | 8 | [response] 9 | HTTP/1.1 200 OK 10 | Date: xxx, xx xxx xxxx xx:xx:xx xxx 11 | Content-type: text/plain; charset=utf-8 12 | Content-Length: LENGTH 13 | Some-Header: string 14 | 15 | "Hello back" 16 | 17 | [test] 18 | (let response = (Darklang.Stdlib.HttpClient.request "get" "http://URL" [] []) |> Builtin.unwrap 19 | let respHeaders = response.headers |> Darklang.Stdlib.List.filter (fun h -> Darklang.Stdlib.Tuple2.first h != "date") 20 | {response with headers = respHeaders}) = 21 | Darklang.Stdlib.HttpClient.Response 22 | { body = "\"Hello back\"" 23 | statusCode = 200L 24 | headers = 25 | [ 26 | "Some-Header" = "string" 27 | ("content-length", "LENGTH") 28 | ("content-type", "text/plain; charset=utf-8") 29 | ("server", "kestrel") 30 | ]} 31 | -------------------------------------------------------------------------------- /backend/testfiles/httpclient/v0/todo/uri-with-auth-just-username-with-colon.test: -------------------------------------------------------------------------------- 1 | [expected-request] 2 | GET PATH HTTP/1.1 3 | Accept: */* 4 | Accept-Encoding: deflate, gzip, br 5 | Authorization: Basic dXNlcjo= 6 | Content-Type: text/plain; charset=utf-8 7 | Host: HOST 8 | 9 | 10 | [response] 11 | HTTP/1.1 200 OK 12 | Date: xxx, xx xxx xxxx xx:xx:xx xxx 13 | Content-type: text/plain; charset=utf-8 14 | Content-Length: LENGTH 15 | 16 | "Hello back" 17 | 18 | [test] 19 | (let response = (Darklang.Stdlib.HttpClient.request "get" "http://user:@URL" [] []) |> Builtin.unwrap 20 | let respHeaders = response.headers |> Darklang.Stdlib.List.filter (fun h -> Darklang.Stdlib.Tuple2.first h != "date") 21 | {response with headers = respHeaders}) = 22 | Darklang.Stdlib.HttpClient.Response 23 | { body = "\"Hello back\"" 24 | statusCode = 200L 25 | headers = 26 | [ 27 | ("content-length", "LENGTH") 28 | ("content-type", "text/plain; charset=utf-8") 29 | ("server", "kestrel") 30 | ]} 31 | -------------------------------------------------------------------------------- /backend/testfiles/execution/stdlib/uuid.dark: -------------------------------------------------------------------------------- 1 | // parse OK 2 | (Stdlib.Uuid.parse_v0 "3700adbc-7a46-4ff4-81d3-45afb03f6e2d") 3 | |> Builtin.unwrap 4 | |> Stdlib.Uuid.toString = "3700adbc-7a46-4ff4-81d3-45afb03f6e2d" 5 | 6 | (Stdlib.Uuid.parse_v0 "11111111-2222-3333-4444-555555555555") 7 | |> Builtin.unwrap 8 | |> Stdlib.Uuid.toString = "11111111-2222-3333-4444-555555555555" 9 | 10 | // bad format 11 | Stdlib.Uuid.parse_v0 "👱🏼" = Stdlib.Result.Result.Error Stdlib.Uuid.ParseError.BadFormat 12 | Stdlib.Uuid.parse_v0 "1111111🍇-2222-3333-4444-555555555555" = Stdlib.Result.Result.Error Stdlib.Uuid.ParseError.BadFormat 13 | Stdlib.Uuid.parse_v0 "psp-soslsls==" = Stdlib.Result.Result.Error Stdlib.Uuid.ParseError.BadFormat 14 | Stdlib.Uuid.parse_v0 "123456" = Stdlib.Result.Result.Error Stdlib.Uuid.ParseError.BadFormat 15 | Stdlib.Uuid.parse_v0 "d388ff30-667f-11eb-ae93" = Stdlib.Result.Result.Error Stdlib.Uuid.ParseError.BadFormat 16 | Stdlib.Uuid.parse_v0 "d388ff30-667f-11eb-ae93-0242ac13000" = Stdlib.Result.Result.Error Stdlib.Uuid.ParseError.BadFormat -------------------------------------------------------------------------------- /backend/testfiles/httpclient/v0/basic-patch.test: -------------------------------------------------------------------------------- 1 | [expected-request] 2 | PATCH PATH HTTP/1.1 3 | Host: HOST 4 | Content-Type: application/json; charset=utf-8 5 | Content-Length: 1 6 | 7 | 5 8 | [response] 9 | HTTP/1.1 200 OK 10 | Date: xxx, xx xxx xxxx xx:xx:xx xxx 11 | Content-type: text/plain; charset=utf-8 12 | Content-Length: LENGTH 13 | 14 | Hello back 15 | 16 | [test] 17 | (let response = (Darklang.Stdlib.HttpClient.request "patch" "http://URL" [("Content-Type", "application/json; charset=utf-8")] ("5" |> Darklang.Stdlib.String.toBytes) |> Builtin.unwrap) 18 | let respHeaders = (response.headers |> Darklang.Stdlib.List.filter (fun h -> (Darklang.Stdlib.Tuple2.first h) != "date")) 19 | {response with headers = respHeaders}) == 20 | Darklang.Stdlib.HttpClient.Response 21 | { statusCode = 200L 22 | headers = [ 23 | ("server", "kestrel") 24 | ("content-length", "LENGTH") 25 | ("content-type", "text/plain; charset=utf-8") 26 | ] 27 | body = ("Hello back" |> Darklang.Stdlib.String.toBytes) } -------------------------------------------------------------------------------- /backend/testfiles/httpclient/v0/todo/response-content-encoding-deflate.test: -------------------------------------------------------------------------------- 1 | [expected-request] 2 | GET PATH HTTP/1.1 3 | Accept: */* 4 | Accept-Encoding: deflate, gzip, br 5 | Content-Type: text/plain; charset=utf-8 6 | Host: HOST 7 | 8 | 9 | [response] 10 | HTTP/1.1 200 OK 11 | Date: xxx, xx xxx xxxx xx:xx:xx xxx 12 | Content-type: text/plain; charset=utf-8 13 | Content-Encoding: deflate 14 | 15 | "Hello back" 16 | 17 | [test] 18 | (let response = (Darklang.Stdlib.HttpClient.request "get" "http://URL/" [] []) |> Builtin.unwrap 19 | let respHeaders = response.headers |> Darklang.Stdlib.List.filter (fun h -> Darklang.Stdlib.Tuple2.first h != "date") 20 | {response with headers = respHeaders}) = 21 | Darklang.Stdlib.HttpClient.Response 22 | { body = "\"Hello back\"" 23 | statusCode = 200L 24 | headers = 25 | [ 26 | "Transfer-Encoding" = "chunked" 27 | "Content-Encoding" = "deflate" 28 | ("content-type", "text/plain; charset=utf-8") 29 | ("server", "kestrel") 30 | ]} 31 | -------------------------------------------------------------------------------- /docs/release.md: -------------------------------------------------------------------------------- 1 | # Releasing a new version of Darklang 2 | 3 | Releasing a version of darklang is relatively simple. 4 | 5 | ## Tag the last commit of the month 6 | 7 | - Find the commit: `git log | grep -B 5 "Merge pull request" | less` 8 | - Tag the commit: 9 | 10 | ``` 11 | V=RELEASE_NUMBER && git tag -a release$V -m "Release $V - https://blog.darklang.com/darklang-release-$V/" COMMIT_SHA && git push origin release$V && git push origin release$V 12 | ``` 13 | 14 | ## Create the changelog 15 | 16 | In the docs repo, update changelog.md. 17 | 18 | You can automatically generate changelog entries from PRs using: 19 | 20 | ``` 21 | pip install github-changelog 22 | brew install gnu-sed 23 | changelog darklang dark releasePREV_RELEASE_NUMBER releaseRELEASE_NUMBER | sed 's!#\([[:digit:]]\+\)![#\1]\(https://github.com/darklang/dark/pull/\1\)!g' 24 | ``` 25 | 26 | ## Release materials 27 | 28 | - Blog post 29 | - Twitter @darklang 30 | - Reflection recording on youtube 31 | - Announcement channel on Discord 32 | - Send email via mailchimp 33 | -------------------------------------------------------------------------------- /backend/testfiles/httpclient/v0/request-form-simple.test: -------------------------------------------------------------------------------- 1 | [expected-request] 2 | GET PATH HTTP/1.1 3 | Host: HOST 4 | Content-Type: application/x-www-form-urlencoded 5 | Content-Length: 0 6 | 7 | 8 | [response] 9 | HTTP/1.1 200 OK 10 | Date: xxx, xx xxx xxxx xx:xx:xx xxx 11 | Content-type: application/x-www-form-urlencoded; charset=utf-8 12 | Content-Length: 0 13 | 14 | [test] 15 | (let reqHeaders = [("Content-type", "application/x-www-form-urlencoded")] 16 | let response = (Darklang.Stdlib.HttpClient.request "get" "http://URL" reqHeaders [] |> Builtin.unwrap) 17 | let respHeaders = (response.headers |> Darklang.Stdlib.List.filter (fun h -> (Darklang.Stdlib.Tuple2.first h) != "date")) 18 | {response with headers = respHeaders}) == 19 | Darklang.Stdlib.HttpClient.Response 20 | { statusCode = 200L 21 | headers = 22 | [ 23 | ("server", "kestrel") 24 | ("transfer-encoding", "chunked") 25 | ("content-type", "application/x-www-form-urlencoded; charset=utf-8") 26 | ] 27 | body = [] } 28 | -------------------------------------------------------------------------------- /backend/testfiles/httpclient/v0/todo/request-header-override-content-length-get.test: -------------------------------------------------------------------------------- 1 | [expected-request] 2 | GET PATH HTTP/1.1 3 | Accept: */* 4 | Accept-Encoding: deflate, gzip, br 5 | Content-Type: text/plain; charset=utf-8 6 | Host: HOST 7 | 8 | [response] 9 | HTTP/1.1 200 OK 10 | Date: xxx, xx xxx xxxx xx:xx:xx xxx 11 | Content-type: text/plain; charset=utf-8 12 | Content-Length: LENGTH 13 | 14 | "Hello back" 15 | 16 | [test] 17 | (let reqHeaders = { "Content-length" = "6"; } 18 | let response = (Darklang.Stdlib.HttpClient.request "get" "http://URL" {} reqHeaders) |> Builtin.unwrap 19 | let respHeaders = response.headers |> Darklang.Stdlib.List.filter (fun h -> Darklang.Stdlib.Tuple2.first h != "date") 20 | {response with headers = respHeaders}) = 21 | Darklang.Stdlib.HttpClient.Response 22 | { body = "\"Hello back\"" 23 | statusCode = 200L 24 | headers = 25 | [ 26 | ("content-length", "LENGTH") 27 | ("content-type", "text/plain; charset=utf-8") 28 | ("server", "kestrel") 29 | ]} 30 | --------------------------------------------------------------------------------