diff options
author | polwex <polwex@sortug.com> | 2025-06-14 23:30:34 +0700 |
---|---|---|
committer | polwex <polwex@sortug.com> | 2025-06-14 23:30:34 +0700 |
commit | d8b3e15bec60f58defad13e961f80354d250235d (patch) | |
tree | 6a6f9158141bf40ea452a5913d72160362e0c472 |
aaaaaa
-rwxr-xr-x | bs5/.gitignore | 73 | ||||
-rw-r--r-- | bs5/.ocamlformat | 79 | ||||
-rw-r--r-- | bs5/bs5.opam | 50 | ||||
-rw-r--r-- | bs5/dune | 36 | ||||
-rw-r--r-- | bs5/dune-project | 45 | ||||
-rw-r--r-- | bs5/js/package-lock.json | 1152 | ||||
-rw-r--r-- | bs5/js/package.json | 9 | ||||
-rw-r--r-- | bs5/js/styles.css | 20 | ||||
-rw-r--r-- | bs5/js/tailwind.config.js | 7 | ||||
-rw-r--r-- | bs5/server/Claude.md | 69 | ||||
-rw-r--r-- | bs5/server/DreamRSC.ml | 1 | ||||
-rw-r--r-- | bs5/server/api/dune | 5 | ||||
-rw-r--r-- | bs5/server/api/json.ml | 13 | ||||
-rw-r--r-- | bs5/server/api/stream.ml | 18 | ||||
-rw-r--r-- | bs5/server/api/upload.ml | 37 | ||||
-rw-r--r-- | bs5/server/db/notes.json | 26 | ||||
-rw-r--r-- | bs5/server/dune | 31 | ||||
-rw-r--r-- | bs5/server/middleware/counter.ml | 5 | ||||
-rw-r--r-- | bs5/server/middleware/dune | 5 | ||||
-rw-r--r-- | bs5/server/middleware/logs.ml | 12 | ||||
-rw-r--r-- | bs5/server/middleware/promise.ml | 12 | ||||
-rw-r--r-- | bs5/server/pages/Index.re | 18 | ||||
-rw-r--r-- | bs5/server/pages/dune | 12 | ||||
-rw-r--r-- | bs5/server/server.ml | 24 | ||||
-rw-r--r-- | bs5/test/dune | 2 | ||||
-rw-r--r-- | bs5/test/test_bs5.ml | 0 | ||||
-rw-r--r-- | devenv.nix | 166 | ||||
-rw-r--r-- | devenv.yaml | 21 |
28 files changed, 1948 insertions, 0 deletions
diff --git a/bs5/.gitignore b/bs5/.gitignore new file mode 100755 index 0000000..83eac39 --- /dev/null +++ b/bs5/.gitignore @@ -0,0 +1,73 @@ +### OS ### +.DS_Store + +### Node ### +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Dependency directories +node_modules/ + +# Optional npm cache directory +.npm + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env + +*.annot +*.cmo +*.cma +*.cmi +*.a +*.o +*.cmx +*.cmxs +*.cmxa + +# dune working directory +_build/ + +# odoc (odoc-driver) html artifacts +_html/ + +# ocamlbuild targets +*.byte +*.native + +# Merlin configuring file for Vim and Emacs +.merlin + +# Dune generated files +*.install + +# Local OPAM switch +_opam/ + +# vscode +.vscode + +*.dump +_build +_opam +_esy +.bsb.lock +.merlin + +.direnv/ +demo/.running/* diff --git a/bs5/.ocamlformat b/bs5/.ocamlformat new file mode 100644 index 0000000..bdeaab5 --- /dev/null +++ b/bs5/.ocamlformat @@ -0,0 +1,79 @@ +profile=default +margin=120 +version=0.27.0 +break-cases=toplevel +break-collection-expressions=wrap +if-then-else=k-r +function-indent=2 +extension-indent=2 +indent-after-in=2 +let-binding-indent=2 +match-indent=2 +match-indent-nested=always +cases-exp-indent=2 +margin=60 + +max-iters=10 +margin-check=false +disable=false +debug=false +comment-check=true +wrap-fun-args=true +wrap-comments=false +type-decl-indent=2 +type-decl=compact +stritem-extension-indent=0 +space-around-variants=true +space-around-records=true +space-around-lists=true +space-around-arrays=true +single-case=compact +sequence-style=terminator +sequence-blank-line=preserve-one +parse-toplevel-phrases=false +parse-docstrings=false +parens-tuple-patterns=multi-line-only +parens-tuple=always +parens-ite=false +ocp-indent-compat=false +nested-match=wrap +module-item-spacing=compact +max-indent=68 +match-indent-nested=never +match-indent=0 +margin=80 +line-endings=lf +let-module=compact +let-binding-spacing=compact +let-binding-indent=2 +let-and=compact +leading-nested-match-parens=false +infix-precedence=indent +indicate-nested-or-patterns=unsafe-no +indicate-multiline-delimiters=no +indent-after-in=0 +if-then-else=compact +function-indent-nested=never +function-indent=2 +field-space=loose +extension-indent=2 +exp-grouping=parens +dock-collection-brackets=true +doc-comments-tag-only=default +doc-comments-padding=2 +doc-comments=after-when-possible +disambiguate-non-breaking-match=false +cases-matching-exp-indent=normal +cases-exp-indent=4 +break-struct=force +break-string-literals=auto +break-sequences=true +break-separators=after +break-infix-before-func=false +break-infix=wrap +break-fun-sig=wrap +break-fun-decl=wrap +break-collection-expressions=fit-or-vertical +break-cases=fit +break-before-in=fit-or-vertical +assignment-operator=end-line diff --git a/bs5/bs5.opam b/bs5/bs5.opam new file mode 100644 index 0000000..6347e75 --- /dev/null +++ b/bs5/bs5.opam @@ -0,0 +1,50 @@ +# This file is generated by dune, edit dune-project instead +opam-version: "2.0" +synopsis: "The best blog in the universe" +description: "Nomo nos salvabit" +maintainer: ["Spandrell <s@spandrell.ch>"] +authors: ["Spandrell <s@spandrell.ch>"] +license: "LICENSE" +tags: ["add topics" "to describe" "your" "project"] +homepage: "https://github.com/username/reponame" +doc: "https://spandrell.ch" +bug-reports: "https://github.com/username/reponame/issues" +depends: [ + "dune" {>= "3.19"} + "server-reason-react" + "ocaml" + "reason" + "sqlite3" + "uri" + "uucp" + "yojson" + "ppx_yojson_conv" + "lwt" + "lwt_ppx" + "melange" + "melange-json" + "melange-json-native" + "ocamlformat" + "melange-webapi" {with-dev-setup} + "melange-fetch" {with-dev-setup} + "dream" {with-dev-setup} + "melange-webapi" {with-dev-setup} + "melange-fetch" {with-dev-setup} + "odoc" {with-doc} +] +build: [ + ["dune" "subst"] {dev} + [ + "dune" + "build" + "-p" + name + "-j" + jobs + "@install" + "@runtest" {with-test} + "@doc" {with-doc} + ] +] +dev-repo: "git+https://github.com/username/reponame.git" +x-maintenance-intent: ["(latest)"] diff --git a/bs5/dune b/bs5/dune new file mode 100644 index 0000000..1538fad --- /dev/null +++ b/bs5/dune @@ -0,0 +1,36 @@ +(rule + (alias demo) + (enabled_if + (= %{profile} "dev")) + (deps + server/server.exe + ; (alias_rec client) + ) + (action + (progn + ; we want dune to write the file but not attach any fsevents listeners to it, + ; so that watchexec can read from it without issues. + ; this means no (target), no (with-stdout-to), just a bash command with stdout + ; redirect inside a string + (bash "date > %{project_root}/.running/built_at.txt")))) + +(install + (section bin) + (enabled_if + (= %{profile} dev)) + (files + ("./js/node_modules/@tailwindcss/cli/dist/index.mjs" as tailwind))) + +; (rule +; (target output.css) +; (enabled_if +; (= %{profile} dev)) +; (alias client) +; (deps +; (source_tree ./) +; (source_tree ./../server) +; (:config tailwind.config.js) +; (:input styles.css)) +; (action +; (progn +; (run tailwind --config=%{config} --input=%{input} --output=%{target})))) diff --git a/bs5/dune-project b/bs5/dune-project new file mode 100644 index 0000000..e5f8d85 --- /dev/null +++ b/bs5/dune-project @@ -0,0 +1,45 @@ +(lang dune 3.19) + +(name bs5) + +(generate_opam_files true) + +(source + (github username/reponame)) + +(authors "Spandrell <s@spandrell.ch>") + +(maintainers "Spandrell <s@spandrell.ch>") + +(license LICENSE) + +(documentation https://spandrell.ch) + +(package + (name bs5) + (synopsis "The best blog in the universe") + (description "Nomo nos salvabit") + (depends + server-reason-react + ocaml + reason + sqlite3 + uri + uucp + yojson + ppx_yojson_conv + lwt + lwt_ppx + melange + melange-json + melange-json-native + ocamlformat + (melange-webapi :with-dev-setup) + (melange-fetch :with-dev-setup) + (dream :with-dev-setup) + (melange-webapi :with-dev-setup) + (melange-fetch :with-dev-setup)) + (tags + ("add topics" "to describe" your project))) + +; See the complete stanza docs at https://dune.readthedocs.io/en/stable/reference/dune-project/index.html diff --git a/bs5/js/package-lock.json b/bs5/js/package-lock.json new file mode 100644 index 0000000..f1c8099 --- /dev/null +++ b/bs5/js/package-lock.json @@ -0,0 +1,1152 @@ +{ + "name": "bs5-js", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "bs5-js", + "version": "0.0.0", + "dependencies": { + "@tailwindcss/cli": "^4.1.4", + "tailwindcss": "^4.1.4" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "license": "Apache-2.0", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@isaacs/fs-minipass": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", + "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", + "license": "ISC", + "dependencies": { + "minipass": "^7.0.4" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", + "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", + "license": "MIT", + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@parcel/watcher": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.1.tgz", + "integrity": "sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "detect-libc": "^1.0.3", + "is-glob": "^4.0.3", + "micromatch": "^4.0.5", + "node-addon-api": "^7.0.0" + }, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "@parcel/watcher-android-arm64": "2.5.1", + "@parcel/watcher-darwin-arm64": "2.5.1", + "@parcel/watcher-darwin-x64": "2.5.1", + "@parcel/watcher-freebsd-x64": "2.5.1", + "@parcel/watcher-linux-arm-glibc": "2.5.1", + "@parcel/watcher-linux-arm-musl": "2.5.1", + "@parcel/watcher-linux-arm64-glibc": "2.5.1", + "@parcel/watcher-linux-arm64-musl": "2.5.1", + "@parcel/watcher-linux-x64-glibc": "2.5.1", + "@parcel/watcher-linux-x64-musl": "2.5.1", + "@parcel/watcher-win32-arm64": "2.5.1", + "@parcel/watcher-win32-ia32": "2.5.1", + "@parcel/watcher-win32-x64": "2.5.1" + } + }, + "node_modules/@parcel/watcher-android-arm64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.1.tgz", + "integrity": "sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-arm64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.1.tgz", + "integrity": "sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-x64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.1.tgz", + "integrity": "sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-freebsd-x64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.1.tgz", + "integrity": "sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-glibc": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.1.tgz", + "integrity": "sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-musl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.1.tgz", + "integrity": "sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-glibc": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.1.tgz", + "integrity": "sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-musl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.1.tgz", + "integrity": "sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-glibc": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.1.tgz", + "integrity": "sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-musl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.1.tgz", + "integrity": "sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-arm64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.1.tgz", + "integrity": "sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-ia32": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.1.tgz", + "integrity": "sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-x64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.1.tgz", + "integrity": "sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@tailwindcss/cli": { + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@tailwindcss/cli/-/cli-4.1.10.tgz", + "integrity": "sha512-TuO7IOUpTG1JeqtMQbQXjR4RIhfZ43mor/vpCp3S5X9h0WxUom5NYgxfNO0PiFoLMJ6/eYCelC7KGvUOmqqK6A==", + "license": "MIT", + "dependencies": { + "@parcel/watcher": "^2.5.1", + "@tailwindcss/node": "4.1.10", + "@tailwindcss/oxide": "4.1.10", + "enhanced-resolve": "^5.18.1", + "mri": "^1.2.0", + "picocolors": "^1.1.1", + "tailwindcss": "4.1.10" + }, + "bin": { + "tailwindcss": "dist/index.mjs" + } + }, + "node_modules/@tailwindcss/node": { + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.10.tgz", + "integrity": "sha512-2ACf1znY5fpRBwRhMgj9ZXvb2XZW8qs+oTfotJ2C5xR0/WNL7UHZ7zXl6s+rUqedL1mNi+0O+WQr5awGowS3PQ==", + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "^2.3.0", + "enhanced-resolve": "^5.18.1", + "jiti": "^2.4.2", + "lightningcss": "1.30.1", + "magic-string": "^0.30.17", + "source-map-js": "^1.2.1", + "tailwindcss": "4.1.10" + } + }, + "node_modules/@tailwindcss/oxide": { + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.10.tgz", + "integrity": "sha512-v0C43s7Pjw+B9w21htrQwuFObSkio2aV/qPx/mhrRldbqxbWJK6KizM+q7BF1/1CmuLqZqX3CeYF7s7P9fbA8Q==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "detect-libc": "^2.0.4", + "tar": "^7.4.3" + }, + "engines": { + "node": ">= 10" + }, + "optionalDependencies": { + "@tailwindcss/oxide-android-arm64": "4.1.10", + "@tailwindcss/oxide-darwin-arm64": "4.1.10", + "@tailwindcss/oxide-darwin-x64": "4.1.10", + "@tailwindcss/oxide-freebsd-x64": "4.1.10", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.10", + "@tailwindcss/oxide-linux-arm64-gnu": "4.1.10", + "@tailwindcss/oxide-linux-arm64-musl": "4.1.10", + "@tailwindcss/oxide-linux-x64-gnu": "4.1.10", + "@tailwindcss/oxide-linux-x64-musl": "4.1.10", + "@tailwindcss/oxide-wasm32-wasi": "4.1.10", + "@tailwindcss/oxide-win32-arm64-msvc": "4.1.10", + "@tailwindcss/oxide-win32-x64-msvc": "4.1.10" + } + }, + "node_modules/@tailwindcss/oxide-android-arm64": { + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.10.tgz", + "integrity": "sha512-VGLazCoRQ7rtsCzThaI1UyDu/XRYVyH4/EWiaSX6tFglE+xZB5cvtC5Omt0OQ+FfiIVP98su16jDVHDEIuH4iQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-darwin-arm64": { + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.10.tgz", + "integrity": "sha512-ZIFqvR1irX2yNjWJzKCqTCcHZbgkSkSkZKbRM3BPzhDL/18idA8uWCoopYA2CSDdSGFlDAxYdU2yBHwAwx8euQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-darwin-x64": { + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.10.tgz", + "integrity": "sha512-eCA4zbIhWUFDXoamNztmS0MjXHSEJYlvATzWnRiTqJkcUteSjO94PoRHJy1Xbwp9bptjeIxxBHh+zBWFhttbrQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-freebsd-x64": { + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.10.tgz", + "integrity": "sha512-8/392Xu12R0cc93DpiJvNpJ4wYVSiciUlkiOHOSOQNH3adq9Gi/dtySK7dVQjXIOzlpSHjeCL89RUUI8/GTI6g==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.10.tgz", + "integrity": "sha512-t9rhmLT6EqeuPT+MXhWhlRYIMSfh5LZ6kBrC4FS6/+M1yXwfCtp24UumgCWOAJVyjQwG+lYva6wWZxrfvB+NhQ==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.10.tgz", + "integrity": "sha512-3oWrlNlxLRxXejQ8zImzrVLuZ/9Z2SeKoLhtCu0hpo38hTO2iL86eFOu4sVR8cZc6n3z7eRXXqtHJECa6mFOvA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-musl": { + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.10.tgz", + "integrity": "sha512-saScU0cmWvg/Ez4gUmQWr9pvY9Kssxt+Xenfx1LG7LmqjcrvBnw4r9VjkFcqmbBb7GCBwYNcZi9X3/oMda9sqQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-gnu": { + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.10.tgz", + "integrity": "sha512-/G3ao/ybV9YEEgAXeEg28dyH6gs1QG8tvdN9c2MNZdUXYBaIY/Gx0N6RlJzfLy/7Nkdok4kaxKPHKJUlAaoTdA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-musl": { + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.10.tgz", + "integrity": "sha512-LNr7X8fTiKGRtQGOerSayc2pWJp/9ptRYAa4G+U+cjw9kJZvkopav1AQc5HHD+U364f71tZv6XamaHKgrIoVzA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi": { + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.10.tgz", + "integrity": "sha512-d6ekQpopFQJAcIK2i7ZzWOYGZ+A6NzzvQ3ozBvWFdeyqfOZdYHU66g5yr+/HC4ipP1ZgWsqa80+ISNILk+ae/Q==", + "bundleDependencies": [ + "@napi-rs/wasm-runtime", + "@emnapi/core", + "@emnapi/runtime", + "@tybys/wasm-util", + "@emnapi/wasi-threads", + "tslib" + ], + "cpu": [ + "wasm32" + ], + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.4.3", + "@emnapi/runtime": "^1.4.3", + "@emnapi/wasi-threads": "^1.0.2", + "@napi-rs/wasm-runtime": "^0.2.10", + "@tybys/wasm-util": "^0.9.0", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.10.tgz", + "integrity": "sha512-i1Iwg9gRbwNVOCYmnigWCCgow8nDWSFmeTUU5nbNx3rqbe4p0kRbEqLwLJbYZKmSSp23g4N6rCDmm7OuPBXhDA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-win32-x64-msvc": { + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.10.tgz", + "integrity": "sha512-sGiJTjcBSfGq2DVRtaSljq5ZgZS2SDHSIfhOylkBvHVjwOsodBhnb3HdmiKkVuUGKD0I7G63abMOVaskj1KpOA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide/node_modules/detect-libc": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz", + "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/chownr": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", + "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", + "license": "Apache-2.0", + "bin": { + "detect-libc": "bin/detect-libc.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/enhanced-resolve": { + "version": "5.18.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.1.tgz", + "integrity": "sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC" + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/jiti": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.4.2.tgz", + "integrity": "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==", + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/lightningcss": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.1.tgz", + "integrity": "sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==", + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-darwin-arm64": "1.30.1", + "lightningcss-darwin-x64": "1.30.1", + "lightningcss-freebsd-x64": "1.30.1", + "lightningcss-linux-arm-gnueabihf": "1.30.1", + "lightningcss-linux-arm64-gnu": "1.30.1", + "lightningcss-linux-arm64-musl": "1.30.1", + "lightningcss-linux-x64-gnu": "1.30.1", + "lightningcss-linux-x64-musl": "1.30.1", + "lightningcss-win32-arm64-msvc": "1.30.1", + "lightningcss-win32-x64-msvc": "1.30.1" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.30.1.tgz", + "integrity": "sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.30.1.tgz", + "integrity": "sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.30.1.tgz", + "integrity": "sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.30.1.tgz", + "integrity": "sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q==", + "cpu": [ + "arm" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.30.1.tgz", + "integrity": "sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.30.1.tgz", + "integrity": "sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.30.1.tgz", + "integrity": "sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.30.1.tgz", + "integrity": "sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.30.1.tgz", + "integrity": "sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.30.1.tgz", + "integrity": "sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss/node_modules/detect-libc": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz", + "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/magic-string": { + "version": "0.30.17", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", + "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/minizlib": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.0.2.tgz", + "integrity": "sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==", + "license": "MIT", + "dependencies": { + "minipass": "^7.1.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/mkdirp": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", + "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", + "license": "MIT", + "bin": { + "mkdirp": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mri": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", + "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/node-addon-api": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tailwindcss": { + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.10.tgz", + "integrity": "sha512-P3nr6WkvKV/ONsTzj6Gb57sWPMX29EPNPopo7+FcpkQaNsrNpZ1pv8QmrYI2RqEKD7mlGqLnGovlcYnBK0IqUA==", + "license": "MIT" + }, + "node_modules/tapable": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.2.tgz", + "integrity": "sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/tar": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.4.3.tgz", + "integrity": "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==", + "license": "ISC", + "dependencies": { + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.0.1", + "mkdirp": "^3.0.1", + "yallist": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/yallist": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", + "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + } + } +} diff --git a/bs5/js/package.json b/bs5/js/package.json new file mode 100644 index 0000000..460acfa --- /dev/null +++ b/bs5/js/package.json @@ -0,0 +1,9 @@ +{ + "name": "bs5-js", + "version": "0.0.0", + "description": "", + "dependencies": { + "@tailwindcss/cli": "^4.1.4", + "tailwindcss": "^4.1.4" + } +} diff --git a/bs5/js/styles.css b/bs5/js/styles.css new file mode 100644 index 0000000..58d0cc9 --- /dev/null +++ b/bs5/js/styles.css @@ -0,0 +1,20 @@ +@import "tailwindcss"; + +/* Since we use dynamic classNames on Theme.re, we need to inline the colors here. This can cause tailwind to not generate some classes if there's a missing variant here or we add new colors. */ +@source inline("{hover:,}{text,bg,border}-[#FFC53D]"); +@source inline("{hover:,}{text,bg,border}-[#080808]"); +@source inline("{hover:,}{text,bg,border}-[#0F0F0F]"); +@source inline("{hover:,}{text,bg,border}-[#151515]"); +@source inline("{hover:,}{text,bg,border}-[#191919]"); +@source inline("{hover:,}{text,bg,border}-[#1E1E1E]"); +@source inline("{hover:,}{text,bg,border}-[#252525]"); +@source inline("{hover:,}{text,bg,border}-[#2A2A2A]"); +@source inline("{hover:,}{text,bg,border}-[#313131]"); +@source inline("{hover:,}{text,bg,border}-[#3A3A3A]"); +@source inline("{hover:,}{text,bg,border}-[#484848]"); +@source inline("{hover:,}{text,bg,border}-[#6E6E6E]"); +@source inline("{hover:,}{text,bg,border}-[#B4B4B4]"); +@source inline("{hover:,}{text,bg,border}-[#EEEEEE]"); +@source inline("{hover:,}{text,bg,border}-[#F5F5F5]"); +@source inline("{hover:,}{text,bg,border}-[#FFFFFF]"); +@source inline("{hover:,focus:,active:,disabled:,}{text,bg,border}-primary"); diff --git a/bs5/js/tailwind.config.js b/bs5/js/tailwind.config.js new file mode 100644 index 0000000..eaa66bb --- /dev/null +++ b/bs5/js/tailwind.config.js @@ -0,0 +1,7 @@ +/** @type {import('tailwindcss').Config} */ +export default { + content: { + files: ["./client/*.re","./server/*.re", "./universal/*.re"], + }, + plugins: {}, +} diff --git a/bs5/server/Claude.md b/bs5/server/Claude.md new file mode 100644 index 0000000..cbf70d7 --- /dev/null +++ b/bs5/server/Claude.md @@ -0,0 +1,69 @@ + +Core Architecture + + server.re - Main entry point: + - Uses Dream web framework (OCaml's web server) + - Sets up routing with getAndPost helper for progressive enhancement + - Serves static assets (CSS, JS) and handles React rendering + + Key Libraries (from dune file): + + - dream - Web framework + - react/reactDOM - Native React implementation + - demo_shared_native - Shared components + - lwt.unix - Async I/O + - yojson - JSON handling + + Rendering Pipeline + + Three rendering modes: + 1. renderToString - Basic SSR (line 28-31) + 2. renderToStaticMarkup - No hydration attributes (line 34-38) + 3. renderToStream - Streaming SSR with React Server Components + + React Server Components (RSC) + + DreamRSC.re handles the magic: + + Content negotiation: + switch (Dream.header(request, "Accept")) { + | Some(accept) when is_react_component_header(accept) => + stream_model(~location=Dream.target(request), app) (* RSC payload *) + | _ => + stream_html(~bootstrapScripts, app) (* Full HTML *) + + Server Actions (lines 50-87): + - Reads ACTION_ID header + - Decodes form data or request body + - Calls registered server function via FunctionReferences.get + - Streams response back as application/react.action + + Request Flow + + 1. Client request → Dream router + 2. Accept header check → RSC payload vs HTML + 3. Server function lookup → FunctionReferences registry + 4. Component rendering → ReactServerDOM.render_model/render_html + 5. Streaming response → Chunks sent via Dream.write + + File Structure in OCaml syntax: + + (* Main server setup *) + let server = + Dream.logger ( + Dream.router [ + get_and_post "/" Pages.Home.handler; + Dream.get "/static/**" (Dream.static "./build/client"); + (* ... more routes *) + ] + ) + + (* Page handler example *) + let handler _request = + let app = Document.make [ + Html.h1 [React.string "Hello"] + ] in + Dream.html (ReactDOM.renderToStaticMarkup app) + + The beauty is it's a standard Dream web server that happens to render React components natively, with RSC streaming as an enhancement + layer on top! diff --git a/bs5/server/DreamRSC.ml b/bs5/server/DreamRSC.ml new file mode 100644 index 0000000..0b35071 --- /dev/null +++ b/bs5/server/DreamRSC.ml @@ -0,0 +1 @@ +let debug = Sys.getenv_opt "DEMO_ENV" == Some "development" diff --git a/bs5/server/api/dune b/bs5/server/api/dune new file mode 100644 index 0000000..9dc8d84 --- /dev/null +++ b/bs5/server/api/dune @@ -0,0 +1,5 @@ +(library + (name api) + (libraries dream lwt yojson) + (preprocess + (pps lwt_ppx ppx_yojson_conv))) diff --git a/bs5/server/api/json.ml b/bs5/server/api/json.ml new file mode 100644 index 0000000..3e4c68c --- /dev/null +++ b/bs5/server/api/json.ml @@ -0,0 +1,13 @@ +open Ppx_yojson_conv_lib.Yojson_conv.Primitives + +type message_object = { message : string } [@@deriving yojson] + +let routes = + [ + Dream.post "/json" (fun req -> + let%lwt body = Dream.body req in + let message_object = + body |> Yojson.Safe.from_string |> message_object_of_yojson + in + `String message_object.message |> Yojson.Safe.to_string |> Dream.json); + ] diff --git a/bs5/server/api/stream.ml b/bs5/server/api/stream.ml new file mode 100644 index 0000000..8bfaa86 --- /dev/null +++ b/bs5/server/api/stream.ml @@ -0,0 +1,18 @@ +let streams = + [ + Dream.post "/stream" (fun request -> + let request_stream = Dream.body_stream request in + + Dream.stream + ~headers:[ ("Content-Type", "application/octet-stream") ] + (fun response_stream -> + let rec loop () = + match%lwt Dream.read request_stream with + | None -> Dream.close response_stream + | Some chunk -> + let%lwt () = Dream.write response_stream chunk in + let%lwt () = Dream.flush response_stream in + loop () + in + loop ())); + ] diff --git a/bs5/server/api/upload.ml b/bs5/server/api/upload.ml new file mode 100644 index 0000000..4ba5fb7 --- /dev/null +++ b/bs5/server/api/upload.ml @@ -0,0 +1,37 @@ +(* let home request = *) +(* <html> *) +(* <body> *) +(* <form method="POST" action="/" enctype="multipart/form-data"> *) +(* <%s! Dream.csrf_tag request %> *) +(* <input name="files" type="file" multiple> *) +(* <button>Submit!</button> *) +(* </form> *) +(* </body> *) +(* </html> *) + +(* let report files = *) +(* <html> *) +(* <body> *) +(* % files |> List.iter begin fun (name, content) -> *) +(* % let name = *) +(* % match name with *) +(* % | None -> "None" *) +(* % | Some name -> name *) +(* % in *) +(* <p><%s name %>, <%i String.length content %> bytes</p> *) +(* % end; *) +(* </body> *) +(* </html> *) + +(* let uploads = *) +(* [ *) + +(* Dream.get "/" (fun request -> *) +(* Dream.html (home request)); *) + +(* Dream.post "/" (fun request -> *) +(* match%lwt Dream.multipart request with *) +(* | `Ok ["files", files] -> Dream.html (report files) *) +(* | _ -> Dream.empty `Bad_Request); *) + +(* ] *) diff --git a/bs5/server/db/notes.json b/bs5/server/db/notes.json new file mode 100644 index 0000000..8529c93 --- /dev/null +++ b/bs5/server/db/notes.json @@ -0,0 +1,26 @@ +[ + { + "id": 0, + "title": "Lorem ipsum for markdown, exists", + "content": "# Aethere conterminus nec est damno\n\nLorem markdownum patiente clade retenta, domos facta cacumine nostris coniunx aspergine intraverat. Petit **et**, est est recens invitaque refert asper vigoris undis sacerdos.\n\nEt undis laetos Caystros intellege est auras corpus, montes ambit tum formae pellitis [et inque](#lenta-argo). Sit dicentum nondum, Dorceus debita attonitum nulla cornua vestem si auras.\n\n## Dummodo in veretur argenti plenissima quoque damnare\n\nSic quae, aula fortibus fratribus longoque abiit mea cava commune spectant uno telis hiemem. Quibus vestigia pugnat prolisque Caesareos bracchia caesae, victoria citi colubris totos penates usum hirta. Perituraque inest promittat Procris mille famaque ursa, hamadryadas rapuere moxque amorem. Domui comites adspicit tabellae euhoe matri duxere dei Dianae Aegyptia celebrare. Veniat gestasset levavit: oras cursus arcebatque quid, herba caput tum praecepta.\n\nUrget quod dixi idemque. Timuitque hortis dubiaque meo per cantu admissum manibus lapis minimamque simulacraque currere licet, Fortuna reliquit massa. Positus tenet.\n\nLusisse Hymenaeon terrore referebat mortale in Pelopeia, facienda positoque bibisset. Per totum, virginibus dumque cornua modumque domus arma ecquid hoc meo [tertius hic](#mater). Coniugis laudis, *fertur*, postquam nostro, mihi mortale fessam illa quater autumni per sapiens, albentes hippomene, et.\n\n## Non sacra tibi superare circa\n\nEst per viro est, nec in trunca causa. Viso placet, cadunt quaque ignorant verbenis loquor exceptas in, summe iuga nectare. Mihi domus segetes, ferro, in quodque litoraque dixit, mediis bacis, egit, qua meae iram Boeotaque.\n\nCuspide accipit poterat, spreto haec quoque? Turpe quae, iacentia esset fissa vivum, an lacertis ire; spumas. Quis quod concidit Alcmene. Attollo mollia metalla adest terris **cultosque prompsit celsum** minima, saepe. Et mi est laverat totis, videndo dedimus capientur: iamque.\n\nHumanas nec, adest hanc iaculum; Phrygiae vae deinde quae neque quodque Nesse caelum chlamydem tamen generosos? Genuit puer placabilis tamen, invito et nervos tuam hoc.\n\nOpus nec. Motu omne vates negate fluere, nec sic membra Hennaeis pleno, arcana toto non. Quicquid se opta saepe! Tibi litis sunt saliente herba. Stamina huius sceptra iuvenes turbasti et mihi votique qua tanta, uno super ero vacuus fluminis tepida.", + "updated_at": 1716604800 + }, + { + "id": 1, + "title": "Our markdown parser is poor, don't stress it plz", + "content": "\n# Murra acta una cretum refert\n## Undas pati incola cognoscit Arethusa\nPatrium utentem: illa tempora; reddit seque, ab fuga notas Charaxi! Mater bibulas o mollia elisi veribus,\n[virtute](http: //www.de.com/iuncta-iactanti.html) tutus sub nam strictumque gens animasse,\n[anni inde](http: //aera.org/) illuc tellus. Munus generosos militis quoque sit.\n## Semper plura tempora tantae effodit cervix subito\nUllam vero: duris mea bellis pulvere! Cervice placidi, ignes, Laelapa pectore languentique fugitque; utroque Medusaeo. Pereat des, argento gemmas praestantes Amore referre. Tamen lanugine novercae frigora miser cum.\nPallas iam solet salix transit; causa fugio animalibus currant verba aversata, faciam tenus, unda. Opem tereti lecto ferventibus pater: Festa fictus nihil: mea quidem quem quodque leonis ad urbes: deus? Tantae corpus; o audit vox animam peteret presso sua quatere Venus.\n## Quem et recondidit puer conlapsa currus\nCeycis mallem bracchia. Minimas si invitae et catulo in detestatur fuit, dea pares, viscera flebant Elin solet annis frondescere sordidus. Laborem ut Troezena grandia at certans posses et Minyeides **nobis** tracti natae.\n## Posse ulla templo Iove aliter\nPictae aeraque sceptro, stupet, levi nec amans hoc breve inplet, gravidamque, locus. Foribusque simul, caput amplexa, silvis titulis: e removit aderat: orbae frenis, ingenium ardua gradere **esse**. Sine et *pessima percusso* est tener aduncis funibus claro: *sumptis* hostibus et, ore venti iamdudum. Laniare novis scopulis, tu priora veniens, nec quodque se novorum tribuam nomine?\n\n## Quodque cervicibus luces\nDedit socios esset, exarsit et movere Saturnia pudici, herede. Nec optima, non hanc spisso, sum gladii qua descendunt **noceat altoque me**. Patrium utentem: illa tempora; reddit seque, ab fuga notas Charaxi.\nPede tota, ligati: subduci succedit animas recessit inde aut, salva, ista. Artes carebat nutrix, arte primus sceptra accipis subit manibusque.\n> Rudentes quaque nec error tecta aves sic obsistere, ignis non nisi expalluit quater harpen; domus. Sine exilibus caerula quo modico et imagine, cana cognoscenda pars torvum cupidine membra: Achilli negabamus manu nec? Tenebant et rubigine tremuere deorum, ora. [Quae agmine patriaeque](http: //incurvatasortemque.io/) fuerit obverterat quoque; sum reticere; huic quaque **adspergine exsangues**! Protegat **verso** fama limite [ligno dextera](http: //intendens-in.net/lentinavis.html), lusisse at haeserat > pro exarsit deae: magni hamo altior.\n", + "updated_at": 1716604800 + }, + { + "id": 2, + "title": "Another important note", + "content": "# Ad Latous\n\n## Verba nostra\n\nRemovere vicimus quid nisi fluctibus Dictys. Tutus ictu amborum iniere inque, quod, omnes, neu pariter Andraemone nequiquam quod suo; luctantia. Feris ara fusum reliquit spirat longique alitibus, ab capillis movi persequitur.\n\n## Crimina Fames\n\nErat qui quodque decusque te tibi nil volucrem [in audaci](#in-et) obliquis **rebus tacuit**. Virtute est annis arma aequora, tenet vellem Eurydices dixerunt supplice animal.\n\n## Ab ego saxum ab tecta tympana mentita\n\nMei mutabit lacerata. Voti aguntur teloque, adest *vocabant*, unde defecto habet.\n\n- Ait pondere\n- Flamma putares cursu genitore plagas conabar manibus\n- Guttis recepta\n- Dixit electus\n- Exaudi tremulo\n- Natique duroque intrat sperat\n\n## Damnarat velox acerris mihi invitus celebrantur mali\n\nRemovere vicimus quid nisi. Est sed in neque patietur foramen exi haec ait. Ter laverat sociis quasque potitur si [est columnis](#dominoque) tempora dum audito et omnia *Pharosque est*.\n\n```cgiKeyboard(myspace_blacklist_streaming.ebook.browserUatBcc(5 / 45, ofRwSecondary), ccd);```\n\nFunda **Gorgoneum tenera ardet** condar viros cannae sequiturque *claro* quicumque. Serpens innumeraeque Cereris foret agitat socios gravem aquae nescisse, deus acie. Demissaque unum dubitabile erepta sanguinea surgis scindere illic meae credidit **dummodo maius** dat aures Illyricos coercet concipe roganti repetitaque.\n", + "updated_at": 1716804800 + }, + { + "id": 3, + "title": "RSC with navigation, yuhu! 🎉", + "content": "# Scrobibus luctu sunt cognoscenda erat iuvenis\n\n## Ciboque exuit quoque toris portae sed equos\n\nStygiam colle porrigit et stipite\ncuraque muneris. Aram labens admonitu prensam status, vox undis et\n**percussit**, quoque; nec mando ripae!\n\n## Per nec rudentes auras\n\nIxionis talia, nam de quaerere limine, illa non neu flevit! Suis cui nec esset\nquid crura. Quae fore uterum summa.\n\n## Populo refluitque deprensa\n\nPergama et fuerunt signa commemorare ecce, non ferit, impetus ab sustinui resto,\npiscem *inductus*, quem. Manum cruentior obruit. Alis Epiros; tum alti aurum\nvideri *et siqua tecta*, vitamque vellera quam superatus per matris mollescat,\n*si*.\n\nMei [signa](#satis-in-illo) evitata Elin flumina; divum\n[puer](#figuram-tot-vocari), reppulit ira arcton. Epulis ut incepti quod. Ter\naliis acta *ira*: obstitit!\n\n## Ipse forte ille remittat\n\nIpse que, nexu vana sequar fui opus perstant! Post hospes.\n\nUt quae illi vidit et in me, *sonumque coniunx* gravitate montis legum pars? Ait\ne addidit guttur, **habitantque** saxum Mopsopium innuba et Peragit sedisti et\nglaebis, ambitae quo currere. Ante per ignem; infantem inpositus tu enim qui:\nhostis mihi mirum euntem, quid? Spicis et frontem repressit deinde, ut residens\nbella vocatum [plumbum](#leto-per-his). Voce documenta stant, inhonorati viaeque\nvidet iterum sanguine, aras veste futuri, argumenta arcus milite non, non?\n\nMaenades Turnusque consulat morsu, sive *mille tenuere ossibus*. Amor duo, ecce\nimperio muneraque contemptus quodcumque quam tetigere tibi, petit, ubi aurumque\n**rogant**. Loca nubes colla ademit: cognoscenda atque. Funereum habebit dixit\nest gemitum viroque Megareus quibus: bracchia signa meus, filia; lucem decent\ntacito?\n", + "updated_at": 1716904800 + } +] diff --git a/bs5/server/dune b/bs5/server/dune new file mode 100644 index 0000000..f1e713e --- /dev/null +++ b/bs5/server/dune @@ -0,0 +1,31 @@ +; (include_subdirs qualified) + +(executable + (enabled_if + (= %{profile} "dev")) + (name server) + (libraries + ; local + middleware + api + ; + dream + server-reason-react.belt + server-reason-react.js + server-reason-react.react + server-reason-react.reactDom + server-reason-react.html + lwt.unix + str + unix + ; belt + yojson) + (flags + (:standard -I +server-reason-react)) + (preprocess + (pps + lwt_ppx + ppx_yojson_conv + server-reason-react.ppx + melange.ppx + melange-json-native.ppx))) diff --git a/bs5/server/middleware/counter.ml b/bs5/server/middleware/counter.ml new file mode 100644 index 0000000..85aa26f --- /dev/null +++ b/bs5/server/middleware/counter.ml @@ -0,0 +1,5 @@ +let count = ref 0 + +let count_requests inner_handler request = + count := !count + 1; + inner_handler request diff --git a/bs5/server/middleware/dune b/bs5/server/middleware/dune new file mode 100644 index 0000000..56d764f --- /dev/null +++ b/bs5/server/middleware/dune @@ -0,0 +1,5 @@ +(library + (name middleware) + (libraries dream lwt) + (preprocess + (pps lwt_ppx))) diff --git a/bs5/server/middleware/logs.ml b/bs5/server/middleware/logs.ml new file mode 100644 index 0000000..6925ca4 --- /dev/null +++ b/bs5/server/middleware/logs.ml @@ -0,0 +1,12 @@ +let detailed_logger inner_handler request = + let method_str = Dream.method_to_string (Dream.method_ request) in + let path = Dream.target request in + let user_agent = Dream.header request "User-Agent" |> Option.value ~default:"unknown" in + let client_ip = Dream.client request in + let%lwt () = + Lwt_io.printf "%s %s %s - %s - %s\n" (Ptime_clock.now () |> Ptime.to_rfc3339) method_str path client_ip user_agent + in + let%lwt response = inner_handler request in + let status = Dream.status response |> Dream.status_to_int in + let%lwt () = Lwt_io.printf " -> %d\n" status in + Lwt.return response diff --git a/bs5/server/middleware/promise.ml b/bs5/server/middleware/promise.ml new file mode 100644 index 0000000..cbb81ec --- /dev/null +++ b/bs5/server/middleware/promise.ml @@ -0,0 +1,12 @@ +let successful = ref 0 +let failed = ref 0 + +let count_requests inner_handler request = + try%lwt + let%lwt response = inner_handler request in + successful := !successful +1; + Lwt.return response + + with exn -> + failed := !failed + 1; + raise exn diff --git a/bs5/server/pages/Index.re b/bs5/server/pages/Index.re new file mode 100644 index 0000000..20dfbff --- /dev/null +++ b/bs5/server/pages/Index.re @@ -0,0 +1,18 @@ +module Page = { + [@react.async.component] + let make = () => { + // let promiseIn2 = + // Lwt.bind(Lwt_unix.sleep(2.0), _ => + // Lwt.return("Solusionao in 2 seconds!") + // ); + // let promiseIn4 = + // Lwt.bind(Lwt_unix.sleep(4.0), _ => + // Lwt.return("Solusionao in 4 seconds!") + // ); + Lwt.return( + <div> {React.string("Well hi")} </div>, + ); + }; +}; + +// let handler = request => DreamRSC.create; diff --git a/bs5/server/pages/dune b/bs5/server/pages/dune new file mode 100644 index 0000000..4d54676 --- /dev/null +++ b/bs5/server/pages/dune @@ -0,0 +1,12 @@ +(library + (name pages) + (libraries + dream + lwt.unix + server-reason-react.belt + server-reason-react.js + server-reason-react.react + server-reason-react.reactDom + server-reason-react.html) + (preprocess + (pps melange.ppx lwt_ppx server-reason-react.ppx melange-json-native.ppx))) diff --git a/bs5/server/server.ml b/bs5/server/server.ml new file mode 100644 index 0000000..40834b9 --- /dev/null +++ b/bs5/server/server.ml @@ -0,0 +1,24 @@ +(* TODO *) +(* Dream.origin_referrer_check *) +let router = + Dream.router + ([ + Dream.get "/root" (fun _ -> Dream.html "Roooot"); + Dream.get "/login" (fun _ -> Dream.html "Welcome"); + Dream.get "/blog/:poast" (fun req -> + Dream.html (Dream.param req "poast")); + Dream.get "/react" (fun _ -> Dream.html "Welcome"); + Dream.get "/fail" (fun _ -> raise (Failure "Oh noes")); + Dream.post "/echo" (fun req -> + let%lwt body = Dream.body req in + Dream.respond ~headers:[ ("Content-Type", "application/json") ] body); + (* Dream.get "/" Pages. *) + ] + @ Api.Json.routes @ Api.Stream.streams) + +let server = + Dream.logger @@ Middleware.Logs.detailed_logger + @@ Middleware.Promise.count_requests + @@ Middleware.Counter.count_requests router + +let () = Dream.run server diff --git a/bs5/test/dune b/bs5/test/dune new file mode 100644 index 0000000..612ec02 --- /dev/null +++ b/bs5/test/dune @@ -0,0 +1,2 @@ +(test + (name test_bs5)) diff --git a/bs5/test/test_bs5.ml b/bs5/test/test_bs5.ml new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/bs5/test/test_bs5.ml diff --git a/devenv.nix b/devenv.nix new file mode 100644 index 0000000..dc5f298 --- /dev/null +++ b/devenv.nix @@ -0,0 +1,166 @@ +{ + pkgs, + lib, + config, + inputs, + ... +}: let + oopkgs = import inputs.nix-ocaml {system = pkgs.system;}; + opkgs = oopkgs.ocamlPackages; + + ocaml-quickjs = opkgs.buildDunePackage { + name = "quickjs"; + pname = "quickjs"; + version = "0.1.1"; + # src = pkgs.fetchFromGitHub { + # owner = "dhcmrlchtdj"; + # repo = "ocaml-quickjs"; + # rev = "master"; + # sha256 = "sha256-CIMxkElHW6GARt8WKe+mr+G/MNkMfxvP/Q6yI1nFG+M="; + # }; + # src = inputs.quickjs-ml; + # src = pkgs.fetchFromGitHub { + # fetchSubmodules = true; + # owner = "ml-in-barcelona"; + # repo = "quickjs.ml"; + # rev = "master"; + # sha256 = "zS1kBkpg6mipGr8IGS5tRP//Hws5EhDhT1Izc2DuwT0="; + # }; + src = ./quickjs.ml; + # src = builtins.fetchurl { + # url = "https://github.com/ml-in-barcelona/quickjs.ml?submodules=1"; + # sha256 = "0szmgj980l3jah5lsz63fpniz34sr5rabhriv0srv58wp8qh858h"; + # }; + doCheck = true; + buildInputs = [pkgs.git]; + propagatedBuildInputs = [ + pkgs.git + opkgs.alcotest + opkgs.integers + opkgs.ctypes + ]; + }; + melange-json-native = opkgs.buildDunePackage { + pname = "melange-json-native"; + version = "1.3.0"; + src = builtins.fetchurl { + url = "https://github.com/melange-community/melange-json/releases/download/2.0.0/melange-json-2.0.0.tbz"; + sha256 = "1n1avcplidrigkch4y8lnh136g5q06d0xhgzvgips3y399lw2jah"; + }; + propagatedBuildInputs = with opkgs; [ppxlib yojson]; + }; + + server-reason-react = opkgs.buildDunePackage { + pname = "server-reason-react"; + version = "n/a"; + doCheck = true; + + src = ./srr; + # src = pkgs.fetchFromGitHub { + # owner = "ml-in-barcelona"; + # repo = "server-reason-react"; + # rev = "d5dd436b0a447ff0a82f9a8d7a02f102139299a9"; + # sha256 = "sha256-CIMxkElHW6GARt8WKe+mr+G/MNkMfxvP/Q6yI1nFG+M="; + # }; + nativeBuildInputs = with opkgs; [ + ocamlformat + reason + melange + reason-native.refmterr + ]; + propagatedBuildInputs = + (with opkgs; [ + reason-react + reason-react-ppx + melange-json + melange-json-native + alcotest-lwt + ocamlformat + uucp + ocaml-quickjs + yojson + uri + melange + ocaml_pcre + lwt + lwt_ppx + ]) + ++ [pkgs.nodejs]; + }; + # nix-ocaml = inputs.nix-ocaml.legacyPackages.${pkgs.system}; +in { + # https://devenv.sh/packages/ + env.WTF = pkgs.lib.makeLibraryPath config.packages; + packages = + (with pkgs; [ + git + watchexec + sqlite + pkg-config + coreutils-full + ]) + ++ (with oopkgs; [ocaml opam dune_3]) + ++ [ + # server-reason-react + melange-json-native + ] + ++ (with opkgs; [ + base + core + utop + ocamlformat + ocaml_sqlite3 + eio + integers + uri + yojson + melange + dream + melange-webapi + melange-fetch + melange-json + reason + reason-react + ocaml-lsp + ppx_yojson_conv + ]); + + # https://devenv.sh/languages/ + languages.javascript = { + enable = true; + npm.enable = true; + }; + + # https://devenv.sh/processes/ + # processes.cargo-watch.exec = "cargo-watch"; + + # https://devenv.sh/services/ + # services.postgres.enable = true; + + # https://devenv.sh/scripts/ + scripts.hello.exec = '' + echo hello from $GREET + ''; + + enterShell = '' + hello + git --version + ''; + + # https://devenv.sh/tasks/ + # tasks = { + # "myproj:setup".exec = "mytool build"; + # "devenv:enterShell".after = [ "myproj:setup" ]; + # }; + + # https://devenv.sh/tests/ + enterTest = '' + echo "Running tests" + git --version | grep --color=auto "${pkgs.git.version}" + ''; + + # https://devenv.sh/git-hooks/ + # git-hooks.hooks.shellcheck.enable = true; + + # See full reference at https://devenv.sh/reference/options/ +} diff --git a/devenv.yaml b/devenv.yaml new file mode 100644 index 0000000..ca6a9e1 --- /dev/null +++ b/devenv.yaml @@ -0,0 +1,21 @@ +# yaml-language-server: $schema=https://devenv.sh/devenv.schema.json +inputs: + nixpkgs: + # url: github:cachix/devenv-nixpkgs/rolling + url: github:nixos/nixpkgs/nixpkgs-unstable + nix-ocaml: + url: github:nix-ocaml/nix-overlays + inputs: + nixpkgs: + follows: nixpkgs + +# If you're using non-OSS software, you can set allowUnfree to true. +# allowUnfree: true + +# If you're willing to use a package that's vulnerable +# permittedInsecurePackages: +# - "openssl-1.1.1w" + +# If you have more than one devenv you can merge them +#imports: +# - ./backend |