Compare commits

..

3 Commits

Author SHA1 Message Date
pinpox
7c3b3ffd8e Update flake.lock 2025-10-23 12:05:12 +02:00
pinpox
ae5712229c Fix python linter errors 2025-10-23 12:04:56 +02:00
pinpox
0ea561f998 Regenerate vars 2025-10-23 12:04:08 +02:00
143 changed files with 3199 additions and 1711 deletions

View File

@@ -76,6 +76,7 @@ in
cmd = "su - text-user -c 'pytest -s -n0 -m service_runner -p no:cacheprovider -o addopts="" ${cli.passthru.sourceWithTests}/clan_lib/llm'"
print("Running tests with command: " + cmd)
# Run tests as text-user (environment variables are set automatically)
peer1.succeed(cmd)
'';

View File

@@ -1,8 +1,17 @@
diff --git a/src/main.rs b/src/main.rs
index 8baf5924a7db..1234567890ab 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1295,6 +1295,12 @@ won't take effect until you reboot the system.
From 46ec73cdeac58807a49b738c44d2e8d5dfbc5fc8 Mon Sep 17 00:00:00 2001
From: pinpox <git@pablo.tools>
Date: Mon, 20 Oct 2025 16:30:22 +0200
Subject: [PATCH] patch switch-to-configuration-ng for container tests
---
pkgs/by-name/sw/switch-to-configuration-ng/src/src/main.rs | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/pkgs/by-name/sw/switch-to-configuration-ng/src/src/main.rs b/pkgs/by-name/sw/switch-to-configuration-ng/src/src/main.rs
index f4d339ccf60e..c50863e15e32 100644
--- a/pkgs/by-name/sw/switch-to-configuration-ng/src/src/main.rs
+++ b/pkgs/by-name/sw/switch-to-configuration-ng/src/src/main.rs
@@ -1320,6 +1320,12 @@ won't take effect until you reboot the system.
for (mountpoint, current_filesystem) in current_filesystems {
// Use current version of systemctl binary before daemon is reexeced.
@@ -15,3 +24,6 @@ index 8baf5924a7db..1234567890ab 100644
let unit = path_to_unit_name(&current_system_bin, &mountpoint);
if let Some(new_filesystem) = new_filesystems.get(&mountpoint) {
if current_filesystem.fs_type != new_filesystem.fs_type
--
2.51.0

View File

@@ -16,7 +16,6 @@
options = {
host = lib.mkOption {
type = lib.types.str;
default = "";
description = ''
ip address or hostname (domain) of the machine
'';

View File

@@ -1,6 +1,6 @@
[
{
"publickey": "age1ntpf7lqqw4zrk8swjvwtyfak7f2wg04uf7ggu6vk2yyt9qt74qkswn25ck",
"publickey": "age1yr5dfj6sx0lrcyegc5jentmwsf779wsc24lkd65dhrpkney0m59q5j060j",
"type": "age"
}
]

View File

@@ -1,15 +1,14 @@
{
"data": "ENC[AES256_GCM,data:ACFpRJRDIgVPurZwHYW0J1MnvyuiRGnXMeQj1nb9rDAIqHbZzZk8+E0Nu1+EdXwk78ziP6tHR1GQP2ILTtpLME4lXXRVjouW5Eo=,iv:ctR1HENO3XGIq1/gzYi47nateYzsSK317EKn92ptqDI=,tag:q1yuk/ZMx3nuORkiT/XXqg==,type:str]",
"data": "ENC[AES256_GCM,data:ldQaNVkxK3vRIMIT28bnVL7Ls6XQowltSQAECmHC7/c9RzSPcIlHyPelZBfhgWWZpKVQiUG8juedn6W/cQ+zDZK6ViY1AWr/RYY=,iv:ZqOfwCl0YGctS/m/BGCz2XL9BBCt2IkpIaSqgxpuLaI=,tag:jcfqLJsAuH0ijlOtAyePBw==,type:str]",
"sops": {
"age": [
{
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAvMUtabnp3V0dzNFFYRzk0\nd0ZJbUtDMXRPRGxpRjhYR1MyQzdJYWdJTUFrCjBNV0pPTTlIOHBBbzlEQkFzVy92\ndENxcDdIZlNDSm1oZTNveUtIeVc3MXcKLS0tIGtocENjMFNYT0s1LzhYNy92QU5G\nREVEdjErb0xPSE1yb0g5bGlackh6bEUKwxBoDteD7+JfnlFF71CHx4oEdV/TFYcF\n3JPYUbTWAIyMtUu/CLbX+Pn9Mv+McrEIqhwT7TWL/YbELKVadX/k5Q==\n-----END AGE ENCRYPTED FILE-----\n"
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBGUWZrL294aTRBUjY1NkdV\nUHFRWW5VdTZkVThRQmdnb1hHS3M1U1VncXpBCkM1ejMxNUh1MGNFeUdRdWpMbVE5\nRk1TcUJuaFVBc01POXlRT3BaS0w1UUkKLS0tIG5mQ2xYN0Z5ZWVWOE04YmhHUmFO\neWxMSTNOOWR5UlBiYi95ZU5TK2RTdEEK6b0uvQ9TrLNw6x41hFMBM4hynl1H1MBu\nnMTxPjQbOP7iFHd5RFbexPHfOitcHowpt3+6wIwIS1sdTFOpqTc5cw==\n-----END AGE ENCRYPTED FILE-----\n"
}
],
"lastmodified": "2025-09-18T14:33:37Z",
"mac": "ENC[AES256_GCM,data:4631iJmioJ2vZ2PTFbdEJu7UqtyQbp43XBlgEbFAviGZdugb3weVI24rJ8m1Rdnxq8uciEeiX6YHBhURdWQY4JNm2wTGnjz7e2PwQ8FCwOmxCcIQPpdKKsziq/M4HArgD66eUxIWfTt1yJfHgBcUuuANbrbH8MirllT+hJTBhqE=,iv:rM8a/MpKbK7DlqjuR4BG77XDHLK11Q+E2rzZLDJalhk=,tag:bbGMn4anXrLHg4eLA0/CXA==,type:str]",
"unencrypted_suffix": "_unencrypted",
"version": "3.10.2"
"lastmodified": "2025-10-23T09:40:31Z",
"mac": "ENC[AES256_GCM,data:rjoogj7WVVxOJYMGvZpFkrYcttme3YEZFdHiBy6FY/wbW6vJhxtMVsMlmtg3Ak/fQwxFQOKlXRVSxo9qGk1b05iYxZdZrzvdpH4wVD3xjzDWhIurDsuSHFqHyrx6TqfZzpKKijPTZESe6C/CCvV3uWo/BgBlOM0CrH3QHig4spw=,iv:mKKxmzQPcw0m3qPOTvyjFNO84phyjnALpnqhZFf/vQ0=,tag:yXSrmSLipwDrUQQs/lS+0A==,type:str]",
"version": "3.11.0"
}
}

View File

@@ -1,33 +1,33 @@
-----BEGIN CERTIFICATE-----
MIIFuzCCA6OgAwIBAgIUNV3+MOkEcQinHmoFprxZfyR6TF4wDQYJKoZIhvcNAQEL
MIIFuzCCA6OgAwIBAgIUO6okUptnp+SX9kPy+Jk/LNQoIGowDQYJKoZIhvcNAQEL
BQAwbDELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMRYwFAYDVQQHDA1TYW4gRnJh
bmNpc2NvMRUwEwYDVQQKDAxFeGFtcGxlIENvcnAxCzAJBgNVBAsMAklUMRQwEgYD
VQQDDAtleGFtcGxlLmNvbTAgFw0yNTEwMjExMzE3MTZaGA8yMTI1MDkyNzEzMTcx
NlowbDELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMRYwFAYDVQQHDA1TYW4gRnJh
VQQDDAtleGFtcGxlLmNvbTAgFw0yNTEwMjMwOTQwMjdaGA8yMTI1MDkyOTA5NDAy
N1owbDELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMRYwFAYDVQQHDA1TYW4gRnJh
bmNpc2NvMRUwEwYDVQQKDAxFeGFtcGxlIENvcnAxCzAJBgNVBAsMAklUMRQwEgYD
VQQDDAtleGFtcGxlLmNvbTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB
AMbUCTs38JdEFlz+fiEwsEb9OV+6u4P5pkKkRFIJ04sTW9/NIeUJx5xOcAPn6B8K
mi+d6vHln2WDCNJHqthGHQDS250x8Qs+JrmtIvDPko+oDOlbWMPiT4Lv6p134+lV
obkiEMKSKz1gHuhlnHXFjkU+xTjxvEtGuq1+JPem4oJ9HUhSk1F6cftigzrYqUuk
JRROiUrbKiFp/TLedmAqQg/7wOrJKSKX91pQwNZhjB2/1REt0HP92W8uZIrzvLqq
JkrGfK9Y6e87DwXoTT0lvMAT7jbMsMWdGoCw/BQV8CwciUUG4ggI/jb+2TTktB3f
kMN/qRTKZ3zv/rn68RJfecAXYCQ2VfvO/Mr9nml2/cM7nrUBcs12YAHcm3766VWJ
pq6qBLcz/pHzMdt+/23nbO7bH2PL6r69VCSYvsDDnqpVL+LnYhgYUE0lPjuWuGmp
oKjggS6p4p1PXEQMOcj9UWdOyjefSzJsOp+25Of9SQzxHkBsVw0iArRFUYP6G15k
kNjYpuinFTw1XVDCFGPRIAhySnERlkv6WNyQQC87QTVJITKkz3R5cv4gwFG0kjAi
Va4nIJs2CctcizuEaPlwnEFrZ99gcB7RYPSUQVGAbfkqt2bhy/xGr+Jlp4kqPfS5
iPomwfcDwEnDbmcM8S2adPWtZ+oHskxZQmJ6+jhGgM73AgMBAAGjUzBRMB0GA1Ud
DgQWBBRHz2QAo1z8r9BewZro+HYv18AxTzAfBgNVHSMEGDAWgBRHz2QAo1z8r9Be
wZro+HYv18AxTzAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4ICAQCz
BTuZI7VymDWerWLfHMWyogoJWOkFB2yEpQe7J+LjS8yZmJg4CYpA4JJ+uM2sBm2Q
yL6M57ZmSY6EFoYeYw3gRfwGC32qJHirhsWvrjUpRC5+4YT9P6fNmgm5aD27JZao
bjyNA9Vy9SCL4JMeWET2w9VGNDaYQCs0x57HZioxYRMSD5vMVbirvCtqX7H3F/X+
r/VHEqEae7tVtuAB2D2GdcFzslCRb9uomuVfLJNqR6Nz1Tw+2adyySijRMCDdpRl
Pg9MBv4sevL6F4C1vUqUG1LXzcfHLFtrV1oUIEpJ0frxAgpdhSbnHiQa64cKX3N0
CsS6VALipGFmxj01+jD0Vhhf4rjjTT5C3Ag4WTqI98Fu4RMW35eBstnt6UUWyJQO
Q1skk+hg0ynfb3lO8OIZ4sDkmxDqAOQXeMMo1tU2YMgNA5Lv1FyO9Silc0VlkOiO
ft1RC8UbECqYyTvz7SNrv8aQP6EUoNSpxQHyBHOQy65dyOLOdP4S+PccUwsdxv/N
O5eN9ndMWqNvnyPKyQ3M+MLVvkCR1vDb6ABgPhH17BLkj8fWQgy5lhjJy5a8VHlO
1VDzV1Xeezy/MYCpS+TamaWTXscbhLMzWWiiAiDT8dltKw4G6U+g7DiF80kM59L5
D1hOs4gOQ853+83L/Ej4ESTj0B04NLVMlzMGtl3qcA==
ANRRvIKNM9wFr5xkuGlkXM6TbZvsu1T2qeLUL+vVv4KXsyqJjld2qhAp4RsjtnWW
APn4jSb63JS0WkKTS//KBJKDdR2XKDWA0wE+Hyp7oZU3zHmuOeG+hRMJhX9AQQI1
qQS7E1XJ00tjqH5365ZAcsce7KXIn0gchZkhTXw8+cxWVLBDR5z93tUXsPrFXvE1
Hd5j5dXTjI/WY3MM5OJ5w3tzRBO9iXmAIC7mbDaGNdUF+Aj2aPGZewe/8yX50tT6
eoEPpGio0gIOvzoW5laspgrQmYmS7LX902nFTr0Z40cJfea4Ck20Ov+VhkL4kGhC
advUsdVC0DePp24RgFkcQcrRP8dkujklSdqbOthEY2n8hRHHRLI5GsbTNuRQ/iGt
UEhlFLev+h7va3pT/SVkmbdyUpCi6dzAbmCDzqzJ4meg+BwFmIq6Uk5+zps7D8Ur
4d+tJX+XtaIAmuMJDgHWsdBCCQEHa8MvvGidz0kMQTkthtNMHm417YL7Zeb3J2z/
6k0wVTZceNTu0+dYE+y4zPUcMK4+MFDFQydfNZhQZBlQw0WqetZE0TJbPKOwmZpe
VoP22dN0Zbqa1JfnBQUhB47h646vZlboBHcRq4q5MD7e08AVZflA8pCg2yacSRNF
yilC5nheam/fa5yaHFsmbBYIHdXehEMaq77Lz+MMbXj9AgMBAAGjUzBRMB0GA1Ud
DgQWBBSbmxZwUQvvrjXbo/7Ko9VWEDf0WzAfBgNVHSMEGDAWgBSbmxZwUQvvrjXb
o/7Ko9VWEDf0WzAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4ICAQA8
YMD6+tfp3A4GrovoxduSg5Qy1gPu/P8nlAaUPdG6DSHY4SJHtcaQ5rJliEzzlg8O
VnQBycrIUZHeVzeGNGdX3o915SvC5nwRkRkf7bb5vFEfOYVELDjbM51GlNMm6b0Y
JcQjT/TNfNglbcTL4UabD/kyWy8tkwSp2NH9C1+nnsjRoN1k65Qin0bzmpVKgGaG
eQTIfJlOjtk31/kamjG+ACheia+HmqM63g9vPugV2PbRLrFwXzqDjDKm+H6UTzLU
UTrkGlM9btE3PBLir1+mWLTTXm2olOqaS6/WOVKhMsQe5Ypyla77vFVd7i3whejK
sH9192T83AkqItOxGpxENWcDDgKQ6CH1pXKBori9ejflrzrFkXSonMZH//bFLGyh
yaFoEBcLTnlYKBnL2b1jL3hdC8KelXbIJEo++fBD77er/9kwWhwzYPi6uGUH5IV3
QmexM0vS/iVku0glTSBXkqTBWdsFMJ0a9X7tpLBFvIk4JqOdbPx5h920OAPWC+H/
YnuAdSPOzBgmW9o3UZ6O2w7tlz8SC12+Qh/HDx9YJb6XIEHVuo5FTkr/T+zwy3DN
kiRj0X5tFeiX6MM1LtDYKTlZ2+vFNR6SFhTV+Vg5bAiy00YSgqXA2l6uo6LyqV4T
qOLThtAMCgzwF0/KpNuP35tW8CPSQ+ORD2spDWvLbw==
-----END CERTIFICATE-----

View File

@@ -1,18 +1,18 @@
{
"data": "ENC[AES256_GCM,data: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,iv:dGSmyDNBdVyF54bYS/Zxm2NNXZyGtLjkyYlrI9/nKvc=,tag:ip2fy76NjObWbW20HyuZUA==,type:str]",
"data": "ENC[AES256_GCM,data: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,iv:cZnbdgqnDSWvbOkzjKKc/vC8xjiVSySrkpzOP+uxWKE=,tag:OA8KjOoyYJMpb2tAR8Y6OA==,type:str]",
"sops": {
"age": [
{
"recipient": "age1ntpf7lqqw4zrk8swjvwtyfak7f2wg04uf7ggu6vk2yyt9qt74qkswn25ck",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBNQS96MUFubVdOR2VCc2xO\ncTc5QnNHNTFpdURnSnF3dVhBQXQ3bnBuRW1RCngzSVlhSW9rNUxoSWdKcEtKVXc3\nQitLZ2NDUXBSUmxtVWpYRUlvOHVXcW8KLS0tIGZaWlRVak9NYmt2elpwYStYenRE\nanlkT3BET1FjQ2lFZkp3SXFMSkJSaVkKKkr+MNNqs6Ve3K5OrZfBEGlnc7OAthqf\nOZrP9NYOTMgkvhFsZTVpUS0zskry0iwmTNt+KeluYf0Tko8K53Kx2A==\n-----END AGE ENCRYPTED FILE-----\n"
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBMMndqMFM2b3JxZDlSMUdM\nejJyWFZLU0lHN1ZYdFJJMmp2NExuY2tnYUZFCkVLbVNjQ0hYb2tld2xvQU4rNE1J\nRzFQMjV2eHhMc3AyUzlvSGV6cUFOQ1UKLS0tIEZpWVN3WHBEZ0Vjc2lkQXdJRVRB\nd296b2VZNmVQbUt6WENWNFYzb1p0NVkK/idbBu9CRAEmY2fIx3X9GJqOkpdkmdWP\nUVeNB+SAOxNK2a6ys6d1iGsnVvRmQuZ5V4GZN6xDvnpvibWdKCGKgQ==\n-----END AGE ENCRYPTED FILE-----\n"
},
{
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBXd2dVYmJIbUVVeXk5Nk1E\nekFiUldVVUhRTmE4dHRiTHNDdEMyS1pRV1RrCkNScGdXVSs4UU5id29DV0pZWDQr\nenV1QmpnOFk5aFpTTUxmb0hDVHZDdFkKLS0tIHpmalJtRC94bEhaUStmeUlHT21w\nd3o3UzJHZklxK0RCYUUxc2c3aG1XclkKEPq1ZgyGiAK/Hy4zT7wfdDfPEE3vMHpR\nzwQV5y3M3DmlnKQEvJu0DpQ334CyAcubZC7cswQdUrM8TPqJhb/TuA==\n-----END AGE ENCRYPTED FILE-----\n"
"recipient": "age1yr5dfj6sx0lrcyegc5jentmwsf779wsc24lkd65dhrpkney0m59q5j060j",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBkQkprZTU1dGNONnQ3eTIr\ncVlheXlpUUlZM2wyL3l3NlY1azNkZTI2c0RvCm0rYXBkOXBMS1ZaQVVtU2gyRXMr\nd0JuclBDVEJabld6K0VVMTJ4SCtOTVkKLS0tIC9vbTdNRlVoNHZNWlB5enUyREhj\nc21XQ1U4bU1RdzMwMithMEpWSGx4UTQKxfPIPxh3yxmzCU50l+JGzj9YKlUmeuaA\nEpphjkaUokTZh2dXE7i7XOVNylB+o2f71+mlv1ak6ftL18i/VcgADQ==\n-----END AGE ENCRYPTED FILE-----\n"
}
],
"lastmodified": "2025-10-21T13:17:17Z",
"mac": "ENC[AES256_GCM,data:wdAFURkJZvclbz3UFPSPV9fma7zrZVEhMhsRqylGQMLepX/WohEAr8nJgeHl05be1Q8M8biPXCCoL0vfwg4BRZOkhD8PusJh8iBI3+STNQe/S1qoIK1ByfBFhJD+tIsVsgduLp6G32e6SRNvkuX3UpJqyViuRUavfQd3b8LRU4I=,iv:S3sMNTz5Kg4TxHj1tnk/ayiFuO74dR4aPnnomtkGByo=,tag:uive2bYe42s6VtPd03jTMw==,type:str]",
"lastmodified": "2025-10-23T09:40:31Z",
"mac": "ENC[AES256_GCM,data:tY8mchq1LuM5j1jghjyvLeROgXEu/k+jkSf4CH3U+tnElyXYzcPt/UzbcuXVsL9xyr+n+uJz5kjtu+5sfNf2zeUgrMxaiThLk1ZnpNPq7FQKbcvysBMW4eId2hDWhi7/1T7/k9bYaGejPI9u6OFywbTW2Ic4Dw5kflw2LmXpe+E=,iv:c3bSj7TD63OtnDKaWjX/vjc0ffL8Hdxx4T5XNLa3YuI=,tag:IBXfB3ze1sSmbbkU35svxw==,type:str]",
"version": "3.11.0"
}
}

View File

@@ -1,19 +1,18 @@
{
"data": "ENC[AES256_GCM,data:Q0Vn7J0nERccBYT8HZxHF0Zi5qxmMu40n0H1c+L2SCRF6vRLdURxXKDwvh8xtTU=,iv:ucExjoYDFYy19GsBbNNldJRPBSpT+L+x4PrwTG+m2K8=,tag:/Quupyy/nnUNZsDudEMmNA==,type:str]",
"data": "ENC[AES256_GCM,data:UAkF87pKrzkDHZIfBX4I3F7/qCvP40wC+73jGTrznhM97qG507rkNqc=,iv:U35X9C62qmFHRHFYO1UkKRR49jxxMrVKrbxORe1SZmg=,tag:hVRTMY+l+Fhszq2J9yGJhQ==,type:str]",
"sops": {
"age": [
{
"recipient": "age1ntpf7lqqw4zrk8swjvwtyfak7f2wg04uf7ggu6vk2yyt9qt74qkswn25ck",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBQWWo5OEJ5N1RTR0xMaDhL\nQnlUV2RrRXIzM01OemhQWjVkd3FNZjRhR2dzCi9IeE56b3VZTkNkdW9DMzVia3Zx\nbklxWmFpenRjdEIrc0ZDTGdmSTAxRTQKLS0tIHZJdjdYUzhhY0YzQjRqS0psZmpI\nVHJpUjNZNHRpc2ZWSml1TVNNejhiT28K8TTP/J+XspXZ7TVYj9YaBhEodPIXjojB\nRLqAIgJXRaK4NCLukC6l0IMii6w5J/512RnO2ZBTGhKfbdLfyLOFqg==\n-----END AGE ENCRYPTED FILE-----\n"
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBBR2pTN1Z2czJOelM1SjQ4\nQ1BxK09iT0JlNjBEMThoRnRXTXBRejk3ZVVRClJMeVlHTklwTzRubldvdmdmUzRJ\ndDR1M3pEMHROUktyckxoODBxTXozMUUKLS0tIHBUTFFseTJSaSsyWU5PdThNQ2hG\nTXpjZlhIaG1CM2E3R2JmdWlndTZzZHcKYbFqPfQ5s157FBj2Xs8Q5lXgi+FUX9aZ\ne6nOnOHvmgq6MdDK2z6WjtqP4HM/WU9iFSrGSQCvhPFweY1ILmU9tQ==\n-----END AGE ENCRYPTED FILE-----\n"
},
{
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBrZVc5b0FhbzNXcG1zUDlD\neEVWcWpSRkRCMkxBTHdBM3dCbjVpR3FBa0VjCitlTmx4eUJOMHlaU0dFZEhpK3ZD\nZzlMQXVuZWpnaUNmQW9kOGtOaGVDMU0KLS0tIFNlUi9LSzF0UEJCSVBiRlRSNFQz\nNHhMbmNlRXd4ZEJQWVcvTWdCRWEzMUkKls7RbmNOdPDx8z15F+7qay9qIWx6jNsN\nTahT+GgbG29t1aGQCb0yEzKuUyAp39maxxSWToPsfCgJSYJ8RYiUng==\n-----END AGE ENCRYPTED FILE-----\n"
"recipient": "age1yr5dfj6sx0lrcyegc5jentmwsf779wsc24lkd65dhrpkney0m59q5j060j",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB2dGVGd1A5dkt6QzAyL2Ja\nSUNoSEc0K0NQc04zREZSN3JORUdMRlpCdkI4CktSSS9NTjhKTUt1YUhTZERmK1Fi\nSjhmTG1CckNTYVNnL1MrR3d1VnRLMkkKLS0tIFNBS2Z0N3BFWjRUcGRDMm5mM2FR\nNlp3VkRYN1dtMGdRSFVjNXVZb2pjbXcKYaWNCnqIe85ldUWh5RWcbxA28k/1EJV9\ntoD5y6Qu0qZqm7vhgHPSZ8fCmuiafQTUj77XhjJbfw1exac1wP0m8w==\n-----END AGE ENCRYPTED FILE-----\n"
}
],
"lastmodified": "2025-09-18T14:33:39Z",
"mac": "ENC[AES256_GCM,data:g+9/fRiqom2+W28ZpiF+oBj9V6ieq5Xz3sRz3GyzvHoLr6yw51JvpG2QuYNYANW0WCiUjFDkU0qPj/9gLHcuX52nc+gNaTzznb1QGPg7WCGSQI7xaMzyYsPxHpg/BOdj5CL8GyLiOWstD1ch0kc3bJmyu68sJUs04uGtHAADzsE=,iv:oASrYaZarEPDu0R3hd/jMazLgwG5r//hIdMyU/tN15o=,tag:o1fgf5oy+rlWXg88FN5Nfw==,type:str]",
"unencrypted_suffix": "_unencrypted",
"version": "3.10.2"
"lastmodified": "2025-10-23T09:40:34Z",
"mac": "ENC[AES256_GCM,data:y4bNxCWif4JL/AfkfV2wQb22xooHQUgr6//ajzgyBJ5Z9xB8WboVhyTn0nKnA6G+1PpJmoaXUWFQcv0kq6RPFw3teATw5pdKK29NS9rxqnIht5v7mrqP0CnVoFQVDz1yGtY0rotGd7hUNAXsrjb6Ewfs6N3k8QPwpTX4V7STT48=,iv:+BMrr/Vjnd8DT7aC1/ckp5LZ7UuQkffsMeGbBFxjhGA=,tag:vuUWd2UjJaVY/20QrIzC+g==,type:str]",
"version": "3.11.0"
}
}

View File

@@ -1,19 +1,18 @@
{
"data": "ENC[AES256_GCM,data:4NIUEK05kEQAKjR8F9mU3M/XvtZXw+X6CejVI0usMcb4WzagNz7XTVDhLWXZ9St5Ev0Y,iv:bD2+rDLMoWSqUAIZRJof0wRrJVya1xwZUTIJBdCs98I=,tag:g2s4byFHTzwU3ikcBGMElA==,type:str]",
"data": "ENC[AES256_GCM,data:6iT6HrBnOhKWfDKRzI73kobk2a1XsVUZhQpBK8kK9BQa8mv+tIc45gP5GwcD,iv:K5tWZHyLznul21de/RgXloJ/7g87N/bq3EGKSOOe1HY=,tag:q9Xm92SpgKcCI8W95E509A==,type:str]",
"sops": {
"age": [
{
"recipient": "age1ntpf7lqqw4zrk8swjvwtyfak7f2wg04uf7ggu6vk2yyt9qt74qkswn25ck",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBQeVh2M2tqSGlOVkpzNlhU\nd0pMd1R0c0tQWnZzdXViWmtxcjl1Wk1Ka0FNCnBUUWJVbjlyR1hSNGpXNWlPRHJB\nNnMzN3BMQ2NDamFBMlhHbVdJUEZ6cjQKLS0tIEJjWmI0ZDl1NXgrSW9uc0R0LzAr\neEwwOC9DdDg2RTJHQ0M3QTFlcVBaSE0K2Du4NguefdEyY1gS6OuVdO3gHga4omcR\n8B+K1wUfIQbArxZLawPxrj7WNDoW5d4mF9fA3MeV1DFyc4KwtYZmUw==\n-----END AGE ENCRYPTED FILE-----\n"
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBQb3hNVW5BK05tRllUTFRR\nUzM3aFJYamVzZEZELzRieDMwNzBtMTY2ZnpBCjdFU25uSllRYjloTjV2Zjc3RjAz\nTTl4M1pRL0g0QklhUlNjK2oveHZQWVUKLS0tIGlLdHFWMHZ6QXltdEF1YmlicXgy\ndzZVUGRmQ1JKUFN6WEhIbEMvZy9xUU0KurnoOMO9YEbsqg8zYw7vqL1hjKGnq65B\nxI+uV0aGVS79zu6Sqtm62XmgRNdySdz+seYxtISZviGRvhKsvHWi0g==\n-----END AGE ENCRYPTED FILE-----\n"
},
{
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAvWkdBakVrMVR4RU8xdDlF\nRDkvL0Mrb3ltazhIMjRLZDVlSTVlaFY2ODBBCnlQM2s0SGEvZjFDN3dGWDhIN0dK\nenhQbjZ1ZS9QZzg5SE5XazZXS3dFSkkKLS0tIHJhKzhadGpjTXd4L3hOQkhpR0Fy\nYzhTN2dxVSt3OE5uZFpuWmVlYW4vd1kKwHOxP0C5mLcm4oIT/sGQtUsdsmu3LSN0\nSola5+N+IrAZ+HKnuZlDLZ5JmJSc5j/YhGNn7KR1xhkhfGSS1e3UZw==\n-----END AGE ENCRYPTED FILE-----\n"
"recipient": "age1yr5dfj6sx0lrcyegc5jentmwsf779wsc24lkd65dhrpkney0m59q5j060j",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBPSEJWanVSNEdkTDd1T3ho\nUFE2UklxRmp4L0M4MHlnM3o3Q0FkR00rRjNBCkk2SEVpQjdCeUpxN21mbG5LdUZh\nUXhwRkpwVDZuTzhUb2g3WmFnR3c4NWMKLS0tIGcrbkIrYVYyVVlBN0NyYlI4cGx1\nTitVR083K3pwL1Zlc1I3VVdVakJCVkUKCcDSfeu4Md5iPcZOcCktDvwXh0G3wdFb\nGr73xyIY9lcP2+NupMBgKTBxck4beRcJxxyFhYf9ZSkmIK3e4pFQNg==\n-----END AGE ENCRYPTED FILE-----\n"
}
],
"lastmodified": "2025-09-18T14:33:39Z",
"mac": "ENC[AES256_GCM,data:ehbrYqTJcsBKGHUB25JHFnKXrJ6z3LkcElZ89xVr4XxLet+odbhsjIoP2FCcxex7PlXcegMduhHBpXwNGUbX+IUNAXTxlWA9CLDmYhWuS2WLiEVXrS11NE03/zUyHdVx/C38dbIPrWD9iaYSrAiuOyfqDTh9k/Bn7vehLTtadoE=,iv:Nk2WVuJydi5tfsb1Mib4A6NocBCDp9QoIbSadq3bIDI=,tag:IaoyfCv3SkmtemXMR9XnkA==,type:str]",
"unencrypted_suffix": "_unencrypted",
"version": "3.10.2"
"lastmodified": "2025-10-23T09:40:34Z",
"mac": "ENC[AES256_GCM,data:Qh2VQ43UqUhQtMBEhjBfky2hUjTNxJcKrIm6zGteJ/X+z0FxZ9LoUew48ml+HPHvPN+vLRtgVeOkP2d3x0kq/ag4RDNFuK7IgkzrVt8vzT0IpDHJmyw1ehGYy/MhMaDR5zv0ADZV/GrXrn9O+Wj74cgGqYg15TQJ3I/a68ViYhk=,iv:aJZCcd25xaOhmrfkJZuGUAdAGb7lFrP3D5kZtZnP/v4=,tag:se43YOxOEZHawE1SnnvTzA==,type:str]",
"version": "3.11.0"
}
}

View File

@@ -1,19 +1,18 @@
{
"data": "ENC[AES256_GCM,data:0BmP+NwG/NGe6R5yU55/MdPEQ8E5u+VXWtvstHc4GpDtmBY=,iv:vo8XBcN7KcYjiyKvvp+XDOdP9yR9B7wJi0XlaiCdVbk=,tag:brK9ntAPSuOvw/C+oDo51g==,type:str]",
"data": "ENC[AES256_GCM,data:BxHELvbSQkvKnKLFXTAuRFYSO0lD2WuZLFah9S0=,iv:Lt8kE85YZcwfD5t5bEQWTOHoD66D77u/1CqimEI7DW0=,tag:LD8oeZ+imsh9pV75+3YzkQ==,type:str]",
"sops": {
"age": [
{
"recipient": "age1ntpf7lqqw4zrk8swjvwtyfak7f2wg04uf7ggu6vk2yyt9qt74qkswn25ck",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA4Tk1INGtybUVlejlNNlZE\nVms3TkdRVVF1T0E4TmV3NmxvYWVEL2U3WVhNCjJIaHhBcWVlMEYxRjg5bzJpTWdJ\neUhaRTNRTmtlTW0zUXQxTVZEMkQ2MFEKLS0tIFNGWDI4b2FXTE8xQ2xqb0cyK3FI\ncktHWnE5c1ZSVFpmQU1HZmU2VVB1QmcK/s1fVmwpMMg4BYkkAJzSY7hVQWae1F7g\nmfH8EGlr74mifWUNEbd49/K13nl8atQx6bcau83JIEQR+yyihuY4Jw==\n-----END AGE ENCRYPTED FILE-----\n"
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBDRmExdUNwWGRnQ2FLWXpU\nWW40V09mNTJwMEk1d0xGRjRLZ2NGRk9uQVg4CmE5cWpZVE81TzJxZm04ZWowVW1y\naUVldkc4MHJsbW1vOTh6KzFNM3lGUncKLS0tIDlDdW90dzlJTVRwN2JlYWFTZHpC\nV1pnOStuanV3MmJwWXJVbitqV0dUK28KEp0VUW7F1kRB2VqINUu1yXLbDwvvLzJn\noFd9WnA6HXxfyZvwk45DSwnb6VskuQryy9cqNYgvfiVOAaPuK0rsHg==\n-----END AGE ENCRYPTED FILE-----\n"
},
{
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBsL2FXVytUUVZnVU90bG5L\nYURiYjgwN3RuTldWMGl4clpUWmxkeUsrVzM0CkhKZFgwWHl4dWhNSWRQRXVPNDR6\na3hHNmp2RG9YNDhNM2MyV2FuOGY2UlUKLS0tIFpNU2tNOHdhRDRTdHhYWVh2NGZa\nU3J3S0hpclZzWGIwTlFyczdNZkZSZTAKXCZrLaIOVq90ejoKMaRiK0xNw8WOPcnm\nz2uxProEYvQhY8k29mhCFX5HCN0tGn1XTtHeDL7uHuKuFsnSG/fgYQ==\n-----END AGE ENCRYPTED FILE-----\n"
"recipient": "age1yr5dfj6sx0lrcyegc5jentmwsf779wsc24lkd65dhrpkney0m59q5j060j",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBiWnhRMTkxd3l5VWVLMCty\nQ1JYRGlYZ25LeXVNL0Z1bHl1UzVpeEdFYm1RCit4ZFc3YmdGeC9FTENWOGVJaUJo\nOGFPZWZHSWhlcU54WGJ6U3RNWE9PUlEKLS0tIGd0cWpBSzBWcXZWS2R3Y254bFdC\nVnRMeGJCbHNNWE5JVWVXTVZFTDgxSVUKavBd6tPVqlkZGMo1dlYOZ3U3hkRa7IuP\ni/MoRY2J6IdDVoOq1bYJzZ30apG/ADXYUNCVnqDftyD9t5Ws04SkMg==\n-----END AGE ENCRYPTED FILE-----\n"
}
],
"lastmodified": "2025-09-18T14:33:39Z",
"mac": "ENC[AES256_GCM,data:QkGJKj/H+MI9Mr9Up5NDUToSddY5eTz47egc2+IatfxR8RebKJ2/mYaeLV26vPdmY60bIac4N/nZkoa6IVBhkHHMvsEHsx3nD6Lro9Wf/pWP8Zddzr90LF5p2+wusq25JutKQiPKOb2gmrcagmSsH/7V/UqI/my3PMeKmw6irhw=,iv:hOtHF/cDFdNfvqCKRhJsOwAHEiQmCPjENzsg23sKG+Q=,tag:K7qG9b4fQD0VbAV8OYp3vw==,type:str]",
"unencrypted_suffix": "_unencrypted",
"version": "3.10.2"
"lastmodified": "2025-10-23T09:40:34Z",
"mac": "ENC[AES256_GCM,data:k60YIgs+HdcrGkaW4XI3iu2O6JMwlX0ToV8o/Eix27M1xJ2ipcnJI7gghWGBG3GWlzuVHAl9QlkFPu4SRv6KtP62iGzbnzXlbIe+Z1eQgkn+GGaM39SZpsMlW8T1OtV3mW6oe+NRCP7zmPgc2Yr3U08wYSooDOn4XOze5qZNbGk=,iv:q8siSK4a8CHkXcJxO4QTsX12zZxKTwHdw6qirF8j/v0=,tag:aWrxb1x+7BJQ9vIrttc+Hw==,type:str]",
"version": "3.11.0"
}
}

View File

@@ -39,6 +39,7 @@
...
}:
let
uniqueStrings = list: builtins.attrNames (builtins.groupBy lib.id list);
# Collect searchDomains from all servers in this instance
allServerSearchDomains = lib.flatten (
lib.mapAttrsToList (_name: machineConfig: machineConfig.settings.certificate.searchDomains or [ ]) (
@@ -46,7 +47,7 @@
)
);
# Merge client's searchDomains with all servers' searchDomains
searchDomains = lib.uniqueStrings (settings.certificate.searchDomains ++ allServerSearchDomains);
searchDomains = uniqueStrings (settings.certificate.searchDomains ++ allServerSearchDomains);
in
{
clan.core.vars.generators.openssh-ca = lib.mkIf (searchDomains != [ ]) {

View File

@@ -1,23 +1,7 @@
🚧🚧🚧 Experimental 🚧🚧🚧
Use at your own risk.
We are still refining its interfaces, instability and breakages are expected.
---
This module sets up [yggdrasil](https://yggdrasil-network.github.io/) across your clan.
Yggdrasil is designed to be a future-proof and decentralised alternative to the
structured routing protocols commonly used today on the internet. Inside your
clan, it will allow you to reach all of your machines.
If you have other services in your inventory which export peers (e.g. the
`internet` or the services) as [service
exports](https://docs.clan.lol/reference/options/clan_service/#exports), they
will be added as yggdrasil peers automatically. This allows using the stable
yggdrasil IPv6 address to refer to other hosts and letting yggdrasil decide on
the best routing based on available connections.
Yggdrasil is designed to be a future-proof and decentralised alternative to
the structured routing protocols commonly used today on the internet. Inside your clan, it will allow you to reach all of your machines.
## Example Usage

View File

@@ -29,13 +29,12 @@
];
};
options.extraPeers = lib.mkOption {
options.peers = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ ];
description = ''
Additional static peers to configure for this host. If you use a
VPN clan service, it will automatically be added as peers to other hosts.
Local peers are also auto-discovered and don't need to be added.
Static peers to configure for this host.
If not set, local peers will be auto-discovered
'';
example = [
"tcp://192.168.1.1:6443"
@@ -46,67 +45,16 @@
};
};
perInstance =
{
settings,
roles,
exports,
...
}:
{ settings, ... }:
{
nixosModule =
{
config,
pkgs,
lib,
clan-core,
...
}:
let
mkPeers = ip: [
# "tcp://${ip}:6443"
"quic://${ip}:6443"
"ws://${ip}:6443"
"tls://${ip}:6443"
];
select' = clan-core.inputs.nix-select.lib.select;
# TODO make it nicer @lassulus, @picnoir wants microlens
# Get a list of all exported IPs from all VPN modules
exportedPeerIPs = builtins.foldl' (
acc: e:
if e == { } then
acc
else
acc ++ (lib.flatten (builtins.filter (s: s != "") (lib.attrValues (select' "peers.*.plain" e))))
) [ ] (lib.attrValues (select' "instances.*.networking.?peers.*.host.?plain" exports));
# Construct a list of peers in yggdrasil format
exportedPeers = lib.flatten (map mkPeers exportedPeerIPs);
in
{
# Set <yggdrasil ip> <hostname>.<tld> for all hosts.
# Networking modules will then add themselves as peers, so we can
# always use this to resolve a host via the best possible route,
# doing fail-over if needed.
networking.extraHosts = lib.strings.concatStringsSep "\n" (
lib.filter (n: n != "") (
map (
name:
let
ipPath = "${config.clan.core.settings.directory}/vars/per-machine/${name}/yggdrasil/address/value";
in
if builtins.pathExists ipPath then
"${builtins.readFile ipPath} ${name}.${config.clan.core.settings.tld}"
else
""
) (lib.attrNames roles.default.machines)
)
);
clan.core.vars.generators.yggdrasil = {
files.privateKey = { };
@@ -151,7 +99,7 @@
settings = {
PrivateKeyPath = "/key";
IfName = "ygg";
Peers = lib.lists.unique (exportedPeers ++ settings.extraPeers);
Peers = settings.peers;
MulticastInterfaces = [
# Ethernet is preferred over WIFI
{

View File

@@ -17,13 +17,6 @@
roles.default.machines.peer1 = { };
roles.default.machines.peer2 = { };
};
# Peers are set form exports of the internet service
instances."internet" = {
module.name = "internet";
roles.default.machines.peer1.settings.host = "peer1";
roles.default.machines.peer2.settings.host = "peer2";
};
};
};

View File

@@ -140,6 +140,9 @@
pkgs,
...
}:
let
uniqueStrings = list: builtins.attrNames (builtins.groupBy lib.id list);
in
{
imports = [
(import ./shared.nix {
@@ -156,7 +159,7 @@
config = {
systemd.services.zerotier-inventory-autoaccept =
let
machines = lib.uniqueStrings (
machines = uniqueStrings (
(lib.optionals (roles ? moon) (lib.attrNames roles.moon.machines))
++ (lib.optionals (roles ? controller) (lib.attrNames roles.controller.machines))
++ (lib.optionals (roles ? peer) (lib.attrNames roles.peer.machines))

18
devFlake/flake.lock generated
View File

@@ -3,10 +3,10 @@
"clan-core-for-checks": {
"flake": false,
"locked": {
"lastModified": 1761204206,
"narHash": "sha256-A4KDudGblln1yh8c95OVow2NRlHtbGZXr/pgNenyrNc=",
"lastModified": 1760361585,
"narHash": "sha256-v4PnSmt1hXW4dSgVWxcd1ZeEBlhO7NksNRC5cX7L5iw=",
"ref": "main",
"rev": "aabbe0dfac47b7cfbe2210bcb27fb7ecce93350f",
"rev": "7e7e58eb64ef61beb0a938a6622ec0122382131b",
"shallow": true,
"type": "git",
"url": "https://git.clan.lol/clan/clan-core"
@@ -105,11 +105,11 @@
},
"nixpkgs-dev": {
"locked": {
"lastModified": 1761544814,
"narHash": "sha256-t5f0A+2MtSWTfA6hzMNiotpIMGLlSQF2JnK9m6nkzIY=",
"lastModified": 1761164809,
"narHash": "sha256-3uM91Lx9WZomE6MMEBorJyEyBNiHWRIxza/GganDxew=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "e5aa45ed6c45058ec109658b2b7352a9a062cdf3",
"rev": "3d2db9755e7815937fb7b8f089fad9b44bc416d8",
"type": "github"
},
"original": {
@@ -208,11 +208,11 @@
"nixpkgs": []
},
"locked": {
"lastModified": 1761311587,
"narHash": "sha256-Msq86cR5SjozQGCnC6H8C+0cD4rnx91BPltZ9KK613Y=",
"lastModified": 1760945191,
"narHash": "sha256-ZRVs8UqikBa4Ki3X4KCnMBtBW0ux1DaT35tgsnB1jM4=",
"owner": "numtide",
"repo": "treefmt-nix",
"rev": "2eddae033e4e74bf581c2d1dfa101f9033dbd2dc",
"rev": "f56b1934f5f8fcab8deb5d38d42fd692632b47c2",
"type": "github"
},
"original": {

View File

@@ -150,61 +150,10 @@ Those are very similar to NixOS VM tests, as in they run virtualized nixos machi
As of now the container test driver is a downstream development in clan-core.
Basically everything stated under the NixOS VM tests sections applies here, except some limitations.
### Using Container Tests vs VM Tests
Limitations:
Container tests are **enabled by default** for all tests using the clan testing framework.
They offer significant performance advantages over VM tests:
- **Faster startup**
- **Lower resource usage**: No full kernel boot or hardware emulation overhead
To control whether a test uses containers or VMs, use the `clan.test.useContainers` option:
```nix
{
clan = {
directory = ./.;
test.useContainers = true; # Use containers (default)
# test.useContainers = false; # Use VMs instead
};
}
```
**When to use VM tests instead of container tests:**
- Testing kernel features, modules, or boot processes
- Testing hardware-specific features
- When you need full system isolation
### System Requirements for Container Tests
Container tests require the **`uid-range`** system feature** in the Nix sandbox.
This feature allows Nix to allocate a range of UIDs for containers to use, enabling `systemd-nspawn` containers to run properly inside the Nix build sandbox.
**Configuration:**
The `uid-range` feature requires the `auto-allocate-uids` setting to be enabled in your Nix configuration.
To verify or enable it, add to your `/etc/nix/nix.conf` or NixOS configuration:
```nix
settings.experimental-features = [
"auto-allocate-uids"
];
nix.settings.auto-allocate-uids = true;
nix.settings.system-features = [ "uid-range" ];
```
**Technical details:**
- Container tests set `requiredSystemFeatures = [ "uid-range" ];` in their derivation (see `lib/test/container-test-driver/driver-module.nix:98`)
- Without this feature, containers cannot properly manage user namespaces and will fail to start
### Limitations
- Cannot run in interactive mode, however while the container test runs, it logs a nsenter command that can be used to log into each of the containers.
- Early implementation and limited by features.
- Cannot run in interactive mode, however while the container test runs, it logs a nsenter command that can be used to log into each of the container.
- setuid binaries don't work
### Where to find examples for NixOS container tests

18
flake.lock generated
View File

@@ -71,11 +71,11 @@
]
},
"locked": {
"lastModified": 1761339987,
"narHash": "sha256-IUaawVwItZKi64IA6kF6wQCLCzpXbk2R46dHn8sHkig=",
"lastModified": 1760721282,
"narHash": "sha256-aAHphQbU9t/b2RRy2Eb8oMv+I08isXv2KUGFAFn7nCo=",
"owner": "nix-darwin",
"repo": "nix-darwin",
"rev": "7cd9aac79ee2924a85c211d21fafd394b06a38de",
"rev": "c3211fcd0c56c11ff110d346d4487b18f7365168",
"type": "github"
},
"original": {
@@ -115,10 +115,10 @@
"nixpkgs": {
"locked": {
"lastModified": 315532800,
"narHash": "sha256-yDxtm0PESdgNetiJN5+MFxgubBcLDTiuSjjrJiyvsvM=",
"rev": "d7f52a7a640bc54c7bb414cca603835bf8dd4b10",
"narHash": "sha256-Zk4ebRwxwsWjR20i15LsNk13uVhgubF44pJQddcCt4w=",
"rev": "cb82756ecc37fa623f8cf3e88854f9bf7f64af93",
"type": "tarball",
"url": "https://releases.nixos.org/nixpkgs/nixpkgs-25.11pre871443.d7f52a7a640b/nixexprs.tar.xz"
"url": "https://releases.nixos.org/nixpkgs/nixpkgs-25.11pre880602.cb82756ecc37/nixexprs.tar.xz"
},
"original": {
"type": "tarball",
@@ -181,11 +181,11 @@
]
},
"locked": {
"lastModified": 1761311587,
"narHash": "sha256-Msq86cR5SjozQGCnC6H8C+0cD4rnx91BPltZ9KK613Y=",
"lastModified": 1760945191,
"narHash": "sha256-ZRVs8UqikBa4Ki3X4KCnMBtBW0ux1DaT35tgsnB1jM4=",
"owner": "numtide",
"repo": "treefmt-nix",
"rev": "2eddae033e4e74bf581c2d1dfa101f9033dbd2dc",
"rev": "f56b1934f5f8fcab8deb5d38d42fd692632b47c2",
"type": "github"
},
"original": {

View File

@@ -137,12 +137,6 @@ in
default = { };
type = types.submoduleWith {
specialArgs = {
self = throw ''
'self' is banned in the use of clan.services
Use 'exports' instead: https://docs.clan.lol/reference/options/clan_service/#exports
---
If you really need to used 'self' here, that makes the module less portable
'';
inherit (config.clanSettings)
clan-core
nixpkgs

View File

@@ -7,10 +7,14 @@
...
}:
let
inherit (lib) mkOption types uniqueStrings;
inherit (lib) mkOption types;
inherit (types) attrsWith submoduleWith;
errorContext = "Error context: ${lib.concatStringsSep "." _ctx}";
# TODO:
# Remove once this gets merged upstream; performs in O(n*log(n) instead of O(n^2))
# https://github.com/NixOS/nixpkgs/pull/355616/files
uniqueStrings = list: builtins.attrNames (builtins.groupBy lib.id list);
/**
Merges the role- and machine-settings using the role interface
@@ -77,7 +81,6 @@ let
applySettings =
instanceName: instance:
lib.mapAttrs (roleName: role: {
settings = config.instances.${instanceName}.roles.${roleName}.finalSettings.config;
machines = lib.mapAttrs (machineName: _v: {
settings =
config.instances.${instanceName}.roles.${roleName}.machines.${machineName}.finalSettings.config;
@@ -155,29 +158,6 @@ in
(
{ name, ... }@role:
{
options.finalSettings = mkOption {
default = evalMachineSettings instance.name role.name null role.config.settings { };
type = types.raw;
description = ''
Final evaluated settings of the curent-machine
This contains the merged and evaluated settings of the role interface,
the role settings and the machine settings.
Type: 'configuration' as returned by 'lib.evalModules'
'';
apply = lib.warn ''
=== WANRING ===
'roles.<roleName>.settings' do not contain machine specific settings.
Prefer `machines.<machineName>.settings` instead. (i.e `perInstance: roles.<roleName>.machines.<machineName>.settings`)
If you have a use-case that requires access to the original role settings without machine overrides.
Contact us via matrix (https://matrix.to/#/#clan:clan.lol) or file an issue: https://git.clan.lol
This feature will be removed in the next release
'';
};
# instances.{instanceName}.roles.{roleName}.machines
options.machines = mkOption {
description = ''
@@ -236,7 +216,7 @@ in
options.extraModules = lib.mkOption {
default = [ ];
type = types.listOf types.deferredModule;
type = types.listOf (types.either types.deferredModule types.str);
};
}
)
@@ -503,9 +483,6 @@ in
type = types.deferredModule;
default = { };
description = ''
!!! Danger "Experimental Feature"
This feature is experimental and will change in the future.
export modules defined in 'perInstance'
mapped to their instance name
@@ -634,9 +611,6 @@ in
type = types.deferredModule;
default = { };
description = ''
!!! Danger "Experimental Feature"
This feature is experimental and will change in the future.
export modules defined in 'perMachine'
mapped to their machine name
@@ -738,9 +712,6 @@ in
exports = mkOption {
description = ''
!!! Danger "Experimental Feature"
This feature is experimental and will change in the future.
This services exports.
Gets merged with all other services exports.
@@ -879,11 +850,7 @@ in
instanceRes.nixosModule
]
++ (map (
s:
if builtins.typeOf s == "string" then
lib.warn "String types for 'extraModules' will be deprecated - ${s}" "${directory}/${s}"
else
lib.setDefaultModuleLocation "via inventory.instances.${instanceName}.roles.${roleName}" s
s: if builtins.typeOf s == "string" then "${directory}/${s}" else s
) instanceCfg.roles.${roleName}.extraModules);
};
}

View File

@@ -137,7 +137,6 @@ in
settings = { };
};
};
settings = { };
};
peer = {
machines = {
@@ -147,9 +146,6 @@ in
};
};
};
settings = {
timeout = "foo-peer";
};
};
};
settings = {

View File

@@ -102,23 +102,18 @@ in
specificRoleSettings =
res.importedModulesEvaluated.self-A.result.allMachines.jon.passthru.instances.instance_foo.roles.peer;
};
expected = {
expected = rec {
hasMachineSettings = true;
hasRoleSettings = true;
hasRoleSettings = false;
specificMachineSettings = {
timeout = "foo-peer-jon";
};
specificRoleSettings = {
machines = {
jon = {
settings = {
timeout = "foo-peer-jon";
};
settings = specificMachineSettings;
};
};
settings = {
timeout = "foo-peer";
};
};
};
};

View File

@@ -501,7 +501,7 @@ def setup_filesystems(container: ContainerInfo) -> None:
if file.is_symlink():
target = file.readlink()
sym = container.nix_store_dir / file.name
os.symlink(target, sym)
sym.symlink_to(target)
# Read /proc/mounts and replicate every bind mount
with Path("/proc/self/mounts").open() as f:

View File

@@ -1,26 +1,4 @@
{ self, lib, ... }:
let
clanModule = lib.modules.importApply ./default.nix { clan-core = self; };
in
{
flake.modules.clan.default = clanModule;
perSystem =
{
pkgs,
lib,
...
}:
let
jsonDocs = import ./eval-docs.nix {
inherit
pkgs
lib
clanModule
;
clanLib = self.clanLib;
};
in
{
legacyPackages.clan-options = jsonDocs.optionsJSON;
};
flake.modules.clan.default = lib.modules.importApply ./default.nix { clan-core = self; };
}

View File

@@ -288,7 +288,7 @@ in
Global information about the clan.
'';
type = types.deferredModuleWith {
staticModules = [ ../inventoryClass/meta.nix ];
staticModules = [ ../inventoryClass/meta-interface.nix ];
};
default = { };
};

View File

@@ -222,18 +222,6 @@ in
inventoryClass =
let
flakeInputs = config.self.inputs;
# Compute the relative directory path
selfStr = toString config.self;
dirStr = toString directory;
relativeDirectory =
if selfStr == dirStr then
""
else if lib.hasPrefix selfStr dirStr then
lib.removePrefix (selfStr + "/") dirStr
else
# This shouldn't happen in normal usage, but can occur when
# the flake is copied (e.g., in tests). Fall back to empty string.
"";
in
{
_module.args = {
@@ -242,12 +230,7 @@ in
imports = [
../inventoryClass/default.nix
{
inherit
inventory
directory
flakeInputs
relativeDirectory
;
inherit inventory directory flakeInputs;
exportsModule = config.exportsModule;
}
(

View File

@@ -1,20 +1,19 @@
{
pkgs,
lib,
clanModule,
clanLib,
clan-core,
}:
let
eval = lib.evalModules {
modules = [
clanModule
clan-core.modules.clan.default
];
};
evalDocs = pkgs.nixosOptionsDoc {
options = eval.options;
warningsAreErrors = false;
transformOptions = clanLib.docs.stripStorePathsFromDeclarations;
transformOptions = clan-core.clanLib.docs.stripStorePathsFromDeclarations;
};
in
{

View File

@@ -1,5 +1,24 @@
{
self,
...
}:
{
imports = [
./clan/flake-module.nix
];
perSystem =
{
pkgs,
lib,
...
}:
let
jsonDocs = import ./eval-docs.nix {
inherit pkgs lib;
clan-core = self;
};
in
{
legacyPackages.clan-options = jsonDocs.optionsJSON;
};
}

View File

@@ -40,11 +40,12 @@ let
name:
let
v = set.${name};
loc = path ++ [ name ];
in
if pred loc v then
if pred path v then
[
(lib.nameValuePair name (if lib.isAttrs v then filterAttrsRecursive' loc pred v else v))
(lib.nameValuePair name (
if lib.isAttrs v then filterAttrsRecursive' (path ++ [ name ]) pred v else v
))
]
else
[ ]
@@ -55,7 +56,8 @@ let
# Remove extraModules from serialization,
# identified by: prefix + pathLength + name
# inventory.instances.*.roles.*.extraModules
path: _value: !(lib.length path == 5 && ((lib.last path)) == "extraModules")
path: _value:
lib.length path <= 5 || lib.head path != "instances" || (lib.elemAt path 5) != "extraModules"
) exposedInventory;
in
{
@@ -81,14 +83,6 @@ in
directory = mkOption {
type = types.path;
};
relativeDirectory = mkOption {
type = types.str;
internal = true;
description = ''
The relative directory path from the flake root to the clan directory.
Empty string if directory equals the flake root.
'';
};
machines = mkOption {
type = types.attrsOf (submodule ({
options = {

View File

@@ -115,7 +115,7 @@ in
meta = lib.mkOption {
type = lib.types.submoduleWith {
modules = [
./meta.nix
./meta-interface.nix
];
};
};
@@ -359,7 +359,7 @@ in
inherit clanLib;
};
}
(import ./role.nix { })
(import ./roles-interface.nix { })
];
}
);

View File

@@ -44,6 +44,12 @@ in
description = ''
List of additionally imported `.nix` expressions.
Supported types:
- **Strings**: Interpreted relative to the 'directory' passed to `lib.clan`.
- **Paths**: should be relative to the current file.
- **Any**: Nix expression must be serializable to JSON.
!!! Note
**The import only happens if the machine is part of the service or role.**
@@ -67,8 +73,15 @@ in
}
```
'';
apply = value: if lib.isString value then value else builtins.seq (builtins.toJSON value) value;
default = [ ];
type = types.listOf types.raw;
type = types.listOf (
types.oneOf [
types.str
types.path
(types.attrsOf types.anything)
]
);
};
};
}

View File

@@ -0,0 +1,70 @@
{
flakeInputs,
clanLib,
}:
{ lib, config, ... }:
let
inspectModule =
inputName: moduleName: module:
let
eval = clanLib.evalService {
modules = [ module ];
prefix = [
inputName
"clan"
"modules"
moduleName
];
};
in
{
manifest = eval.config.manifest;
roles = lib.mapAttrs (_n: v: { inherit (v) description; }) eval.config.roles;
};
in
{
options.staticModules = lib.mkOption {
readOnly = true;
type = lib.types.raw;
apply = moduleSet: lib.mapAttrs (inspectModule "<clan-core>") moduleSet;
};
options.modulesPerSource = lib.mkOption {
# { sourceName :: { moduleName :: {} }}
readOnly = true;
type = lib.types.raw;
default =
let
inputsWithModules = lib.filterAttrs (_inputName: v: v ? clan.modules) flakeInputs;
in
lib.mapAttrs (
inputName: v: lib.mapAttrs (inspectModule inputName) v.clan.modules
) inputsWithModules;
};
options.moduleSchemas = lib.mkOption {
# { sourceName :: { moduleName :: { roleName :: Schema }}}
readOnly = true;
type = lib.types.raw;
default = lib.mapAttrs (
_inputName: moduleSet:
lib.mapAttrs (
_moduleName: module:
(clanLib.evalService {
modules = [ module ];
prefix = [ ];
}).config.result.api.schema
) moduleSet
) config.modulesPerSource;
};
options.templatesPerSource = lib.mkOption {
# { sourceName :: { moduleName :: {} }}
readOnly = true;
type = lib.types.raw;
default =
let
inputsWithTemplates = lib.filterAttrs (_inputName: v: v ? clan.templates) flakeInputs;
in
lib.mapAttrs (_inputName: v: lib.mapAttrs (_n: t: t) v.clan.templates) inputsWithTemplates;
};
}

View File

@@ -107,7 +107,8 @@ in
readOnly = true;
};
tld = lib.mkOption {
type = types.strMatching "[a-z]+";
default = "clan";
type = lib.types.str;
description = ''
the TLD for the clan
'';

View File

@@ -94,7 +94,7 @@ class TestHttpBridge:
def test_http_bridge_middleware_setup(self, http_bridge: tuple) -> None:
"""Test that middleware is properly set up."""
api, middleware_chain = http_bridge
_api, middleware_chain = http_bridge
# Test that we can create the bridge with middleware
# The actual HTTP handling will be tested through the server integration tests

View File

@@ -2,4 +2,5 @@ app/api
app/.fonts
.vite
storybook-static
*.css.d.ts

View File

@@ -1,20 +1,31 @@
import type { StorybookConfig } from "storybook-solidjs-vite";
import { mergeConfig } from "vite";
import type { StorybookConfig } from "@kachurun/storybook-solid-vite";
export default {
framework: "storybook-solidjs-vite",
const config: StorybookConfig = {
framework: "@kachurun/storybook-solid-vite",
stories: ["../src/**/*.mdx", "../src/**/*.stories.tsx"],
addons: [
"@storybook/addon-links",
"@storybook/addon-docs",
"@storybook/addon-a11y",
{
name: "@storybook/addon-vitest",
options: {
cli: false,
},
},
],
async viteFinal(config) {
return mergeConfig(config, {
define: { "process.env": {} },
});
},
core: {
disableTelemetry: true,
},
} satisfies StorybookConfig;
typescript: {
reactDocgen: "react-docgen-typescript",
reactDocgenTypescriptOptions: {
shouldExtractLiteralValuesFromEnum: true,
// 👇 Default prop filter, which excludes props from node_modules
propFilter: (prop: any) =>
prop.parent ? !/node_modules/.test(prop.parent.fileName) : true,
},
},
};
export default config;

View File

@@ -1,4 +1,4 @@
import type { Preview } from "storybook-solidjs-vite";
import type { Preview } from "@kachurun/storybook-solid-vite";
import "../src/index.css";
import "./preview.css";

View File

@@ -1,4 +1,4 @@
import { setProjectAnnotations } from "storybook-solidjs-vite";
import { setProjectAnnotations } from "@kachurun/storybook-solid-vite";
import * as a11yAddonAnnotations from "@storybook/addon-a11y/preview";
import * as projectAnnotations from "./preview";

View File

@@ -1,9 +1,19 @@
{
"ignore": [
"gtk.webview.js",
"src/api/clan/client-fetch.ts",
"stylelint.config.js",
"util.ts",
"src/components/v2/**",
"api/**",
"tailwind/**"
],
"ignoreDependencies": [
"@babel/plugin-syntax-import-attributes",
"@storybook/addon-viewport",
"@typescript-eslint/parser",
"@vitest/coverage-v8",
"http-server",
"playwright",
"wait-on"
]
}

File diff suppressed because it is too large Load Diff

View File

@@ -12,35 +12,48 @@
"check": "tsc --noEmit --skipLibCheck && eslint ./src --fix",
"test": "vitest run --project unit --typecheck",
"vite": "vite",
"storybook": "storybook",
"knip": "knip --fix",
"storybook": "storybook dev -p 6006",
"storybook-dev": "storybook dev -p 6006",
"test-storybook": "vitest run --project storybook",
"test-storybook-update-snapshots": "vitest run --project storybook --update"
},
"license": "MIT",
"devDependencies": {
"@babel/plugin-syntax-import-attributes": "^7.27.1",
"@eslint/js": "^9.3.0",
"@kachurun/storybook-solid-vite": "^9.0.11",
"@linaria/core": "^6.3.0",
"@storybook/addon-a11y": "^9.1.13",
"@storybook/addon-docs": "^9.1.13",
"@storybook/addon-links": "^9.1.13",
"@storybook/addon-vitest": "^9.1.13",
"@sinonjs/fake-timers": "^14.0.0",
"@storybook/addon-a11y": "^9.0.8",
"@storybook/addon-docs": "^9.0.8",
"@storybook/addon-links": "^9.0.8",
"@storybook/addon-viewport": "^9.0.8",
"@storybook/addon-vitest": "^9.0.8",
"@types/node": "^22.15.19",
"@types/sinonjs__fake-timers": "^8.1.5",
"@types/three": "^0.176.0",
"@typescript-eslint/parser": "^8.32.1",
"@vitest/browser": "^3.2.3",
"@vitest/coverage-v8": "^3.2.3",
"@wyw-in-js/vite": "^0.7.0",
"autoprefixer": "^10.4.19",
"classnames": "^2.5.1",
"concurrently": "^9.1.2",
"eslint": "^9.27.0",
"eslint-plugin-tailwindcss": "^3.17.0",
"eslint-plugin-unused-imports": "^4.1.4",
"extend": "^3.0.2",
"http-server": "^14.1.1",
"jsdom": "^26.1.0",
"knip": "^5.61.2",
"markdown-to-jsx": "^7.7.10",
"playwright": "1.54.1",
"postcss": "^8.4.38",
"postcss-url": "^10.1.3",
"prettier": "^3.2.5",
"storybook": "^9.1.13",
"storybook-solidjs-vite": "^9.0.3",
"storybook": "^9.0.8",
"swagger-ui-dist": "^5.26.2",
"tailwindcss": "^3.4.3",
"typescript": "^5.4.5",
"typescript-eslint": "^8.32.1",
@@ -49,9 +62,11 @@
"vite-css-modules": "^1.10.0",
"vite-plugin-solid": "^2.8.2",
"vite-plugin-solid-svg": "^0.8.1",
"vitest": "^3.2.4"
"vitest": "^3.2.3",
"wait-on": "^8.0.3"
},
"dependencies": {
"@floating-ui/dom": "^1.6.8",
"@kobalte/core": "^0.13.10",
"@kobalte/tailwindcss": "^0.9.0",
"@modular-forms/solid": "^0.25.1",
@@ -65,6 +80,22 @@
"solid-js": "^1.9.7",
"solid-toast": "^0.5.0",
"three": "^0.176.0",
"troika-three-text": "^0.52.4",
"valibot": "^1.1.0"
},
"optionalDependencies": {
"@esbuild/darwin-arm64": "^0.25.4",
"@esbuild/darwin-x64": "^0.25.4",
"@esbuild/linux-arm64": "^0.25.4",
"@esbuild/linux-x64": "^0.25.4"
},
"overrides": {
"vite": {
"rollup": "npm:@rollup/wasm-node@^4.34.9"
},
"@rollup/rollup-darwin-x64": "npm:@rollup/wasm-node@^4.34.9",
"@rollup/rollup-linux-x64": "npm:@rollup/wasm-node@^4.34.9",
"@rollup/rollup-darwin-arm64": "npm:@rollup/wasm-node@^4.34.9",
"@rollup/rollup-linux-arm64": "npm:@rollup/wasm-node@^4.34.9"
}
}

View File

@@ -1,6 +1,7 @@
import type { Meta, StoryObj } from "storybook-solidjs-vite";
import type { Meta, StoryObj } from "@kachurun/storybook-solid";
import { Alert, AlertProps } from "@/src/components/Alert/Alert";
import { expect, fn } from "storybook/test";
import { StoryContext } from "@kachurun/storybook-solid-vite";
const AlertExamples = (props: AlertProps) => (
<div class="grid w-fit grid-cols-2 gap-8">
@@ -19,14 +20,14 @@ const AlertExamples = (props: AlertProps) => (
</div>
);
const meta: Meta<typeof AlertExamples> = {
const meta: Meta<AlertProps> = {
title: "Components/Alert",
component: AlertExamples,
};
export default meta;
type Story = StoryObj<typeof meta>;
type Story = StoryObj<AlertProps>;
export const Info: Story = {
args: {
@@ -91,13 +92,10 @@ export const InfoDismiss: Story = {
args: {
...Info.args,
onDismiss: fn(),
},
render(args) {
return <Alert {...args} />;
},
play: async ({ canvas, userEvent, args }) => {
await userEvent.click(canvas.getByRole("button"));
await expect(args.onDismiss).toHaveBeenCalled();
play: async ({ canvas, step, userEvent, args }: StoryContext) => {
await userEvent.click(canvas.getByRole("button"));
await expect(args.onDismiss).toHaveBeenCalled();
},
},
};

View File

@@ -1,7 +1,8 @@
import type { Meta, StoryObj } from "storybook-solidjs-vite";
import type { Meta, StoryObj } from "@kachurun/storybook-solid";
import { Button, ButtonProps } from "./Button";
import { Component } from "solid-js";
import { expect, fn, within } from "storybook/test";
import { StoryContext } from "@kachurun/storybook-solid-vite";
const getCursorStyle = (el: Element) => window.getComputedStyle(el).cursor;
@@ -201,7 +202,7 @@ const ButtonExamples: Component<ButtonProps> = (props) => (
</>
);
const meta: Meta<typeof ButtonExamples> = {
const meta: Meta<ButtonProps> = {
title: "Components/Button",
component: ButtonExamples,
};
@@ -210,13 +211,15 @@ export default meta;
type Story = StoryObj<ButtonProps>;
const timeout = process.env.NODE_ENV === "test" ? 500 : 2000;
export const Primary: Story = {
args: {
hierarchy: "primary",
onClick: fn(),
},
play: async ({ canvasElement, step, userEvent, args }) => {
play: async ({ canvasElement, step, userEvent, args }: StoryContext) => {
const canvas = within(canvasElement);
const buttons = await canvas.findAllByRole("button");
@@ -261,7 +264,7 @@ export const GhostPrimary: Story = {
},
play: Primary.play,
decorators: [
(Story) => (
(Story: StoryObj) => (
<div class="p-10 bg-def-3">
<Story />
</div>

View File

@@ -8,11 +8,11 @@ import Icon, { IconVariant } from "@/src/components/Icon/Icon";
import { Loader } from "@/src/components/Loader/Loader";
import { getInClasses, joinByDash, keepTruthy } from "@/src/util";
type Size = "default" | "s" | "xs";
type Hierarchy = "primary" | "secondary";
type Elasticity = "default" | "fit";
export type Size = "default" | "s" | "xs";
export type Hierarchy = "primary" | "secondary";
export type Elasticity = "default" | "fit";
type Action = () => Promise<void>;
export type Action = () => Promise<void>;
export interface ButtonProps
extends JSX.ButtonHTMLAttributes<HTMLButtonElement> {

View File

@@ -1,7 +1,7 @@
import { CubeConstruction } from "./CubeConstruction";
import { Meta, StoryObj } from "storybook-solidjs-vite";
import { Meta, StoryObj } from "@kachurun/storybook-solid";
const meta: Meta<typeof CubeConstruction> = {
const meta: Meta = {
title: "Components/CubeConstruction",
component: CubeConstruction,
globals: {
@@ -12,7 +12,7 @@ const meta: Meta<typeof CubeConstruction> = {
export default meta;
type Story = StoryObj<typeof meta>;
type Story = StoryObj;
export const Default: Story = {
args: {},

View File

@@ -1,14 +1,14 @@
import { Meta, StoryObj } from "storybook-solidjs-vite";
import { Divider } from "@/src/components/Divider/Divider";
import { Meta, StoryObj } from "@kachurun/storybook-solid";
import { Divider, DividerProps } from "@/src/components/Divider/Divider";
const meta: Meta<typeof Divider> = {
const meta: Meta<DividerProps> = {
title: "Components/Divider",
component: Divider,
};
export default meta;
type Story = StoryObj<typeof meta>;
type Story = StoryObj<DividerProps>;
export const Default: Story = {};
@@ -30,7 +30,7 @@ export const Vertical: Story = {
orientation: "vertical",
},
decorators: [
(Story) => (
(Story: Story) => (
<div class="h-32 w-full">
<Story />
</div>
@@ -43,5 +43,5 @@ export const VerticalInverted: Story = {
inverted: true,
...Vertical.args,
},
decorators: Vertical.decorators,
decorators: [...Vertical.decorators],
};

View File

@@ -1,4 +1,4 @@
import type { Meta, StoryObj } from "storybook-solidjs-vite";
import type { Meta, StoryContext, StoryObj } from "@kachurun/storybook-solid";
import cx from "classnames";
import { Checkbox, CheckboxProps } from "@/src/components/Form/Checkbox";
@@ -23,17 +23,17 @@ const Examples = (props: CheckboxProps) => (
</div>
);
const meta: Meta<typeof Examples> = {
const meta = {
title: "Components/Form/Checkbox",
component: Examples,
decorators: [
(Story, { args }) => {
(Story: StoryObj, context: StoryContext<CheckboxProps>) => {
return (
<div
class={cx({
"w-[600px]": (args.orientation || "vertical") == "vertical",
"w-[1024px]": args.orientation == "horizontal",
"bg-inv-acc-3": args.inverted,
"w-[600px]": (context.args.orientation || "vertical") == "vertical",
"w-[1024px]": context.args.orientation == "horizontal",
"bg-inv-acc-3": context.args.inverted,
})}
>
<Story />
@@ -41,7 +41,7 @@ const meta: Meta<typeof Examples> = {
);
},
],
};
} satisfies Meta<CheckboxProps>;
export default meta;

View File

@@ -1,4 +1,4 @@
import type { Meta, StoryObj } from "storybook-solidjs-vite";
import type { Meta, StoryContext, StoryObj } from "@kachurun/storybook-solid";
import {
Fieldset,
FieldsetFieldProps,
@@ -18,17 +18,17 @@ const FieldsetExamples = (props: FieldsetProps) => (
</div>
);
const meta: Meta<typeof FieldsetExamples> = {
const meta = {
title: "Components/Form/Fieldset",
component: FieldsetExamples,
decorators: [
(Story, { args }) => {
(Story: StoryObj, context: StoryContext<FieldsetProps>) => {
return (
<div
class={cx({
"w-[600px]": (args.orientation || "vertical") == "vertical",
"w-[512px]": args.orientation == "horizontal",
"bg-inv-acc-3": args.inverted,
"w-[600px]": (context.args.orientation || "vertical") == "vertical",
"w-[512px]": context.args.orientation == "horizontal",
"bg-inv-acc-3": context.args.inverted,
})}
>
<Story />
@@ -36,7 +36,7 @@ const meta: Meta<typeof FieldsetExamples> = {
);
},
],
};
} satisfies Meta<FieldsetProps>;
export default meta;

View File

@@ -1,4 +1,4 @@
import type { Meta, StoryObj } from "storybook-solidjs-vite";
import type { Meta, StoryContext, StoryObj } from "@kachurun/storybook-solid";
import cx from "classnames";
import {
HostFileInput,
@@ -31,17 +31,17 @@ const Examples = (props: HostFileInputProps) => (
</div>
);
const meta: Meta<typeof Examples> = {
const meta = {
title: "Components/Form/HostFileInput",
component: Examples,
decorators: [
(Story, { args }) => {
(Story: StoryObj, context: StoryContext<HostFileInputProps>) => {
return (
<div
class={cx({
"w-[600px]": (args.orientation || "vertical") == "vertical",
"w-[1024px]": args.orientation == "horizontal",
"bg-inv-acc-3": args.inverted,
"w-[600px]": (context.args.orientation || "vertical") == "vertical",
"w-[1024px]": context.args.orientation == "horizontal",
"bg-inv-acc-3": context.args.inverted,
})}
>
<Story />
@@ -49,7 +49,7 @@ const meta: Meta<typeof Examples> = {
);
},
],
};
} satisfies Meta<HostFileInputProps>;
export default meta;

View File

@@ -10,15 +10,15 @@ import styles from "./Label.module.css";
import cx from "classnames";
import { getInClasses } from "@/src/util";
type Size = "default" | "s";
export type Size = "default" | "s";
type LabelComponent =
export type LabelComponent =
| typeof TextField.Label
| typeof Checkbox.Label
| typeof Combobox.Label
| typeof Select.Label;
type DescriptionComponent =
export type DescriptionComponent =
| typeof TextField.Description
| typeof Checkbox.Description
| typeof Combobox.Description

View File

@@ -1,12 +1,12 @@
import type { Meta, StoryObj } from "storybook-solidjs-vite";
import { MachineTags } from "./MachineTags";
import type { Meta, StoryObj } from "@kachurun/storybook-solid";
import { MachineTags, MachineTagsProps } from "./MachineTags";
import { createForm, setValue } from "@modular-forms/solid";
import { Button } from "../Button/Button";
const meta: Meta<typeof MachineTags> = {
const meta = {
title: "Components/MachineTags",
component: MachineTags,
};
} satisfies Meta<MachineTagsProps>;
export default meta;

View File

@@ -19,7 +19,7 @@ import { CollectionNode } from "@kobalte/core";
import styles from "./MachineTags.module.css";
import { keepTruthy } from "@/src/util";
interface MachineTag {
export interface MachineTag {
value: string;
disabled?: boolean;
}

View File

@@ -3,7 +3,7 @@ import { JSX, mergeProps } from "solid-js";
import styles from "./Orienter.module.css";
interface OrienterProps {
export interface OrienterProps {
orientation?: "vertical" | "horizontal";
align?: "center" | "start";
children: JSX.Element;

View File

@@ -1,4 +1,4 @@
import type { Meta, StoryObj } from "storybook-solidjs-vite";
import type { Meta, StoryContext, StoryObj } from "@kachurun/storybook-solid";
import cx from "classnames";
import { TextArea, TextAreaProps } from "./TextArea";
@@ -23,17 +23,17 @@ const Examples = (props: TextAreaProps) => (
</div>
);
const meta: Meta<typeof Examples> = {
const meta = {
title: "Components/Form/TextArea",
component: Examples,
decorators: [
(Story, { args }) => {
(Story: StoryObj, context: StoryContext<TextAreaProps>) => {
return (
<div
class={cx({
"w-[600px]": (args.orientation || "vertical") == "vertical",
"w-[1024px]": args.orientation == "horizontal",
"bg-inv-acc-3": args.inverted,
"w-[600px]": (context.args.orientation || "vertical") == "vertical",
"w-[1024px]": context.args.orientation == "horizontal",
"bg-inv-acc-3": context.args.inverted,
})}
>
<Story />
@@ -41,7 +41,7 @@ const meta: Meta<typeof Examples> = {
);
},
],
};
} satisfies Meta<TextAreaProps>;
export default meta;

View File

@@ -1,4 +1,4 @@
import type { Meta, StoryObj } from "storybook-solidjs-vite";
import type { Meta, StoryContext, StoryObj } from "@kachurun/storybook-solid";
import cx from "classnames";
import { TextInput, TextInputProps } from "@/src/components/Form/TextInput";
import Icon from "../Icon/Icon";
@@ -25,17 +25,17 @@ const Examples = (props: TextInputProps) => (
</div>
);
const meta: Meta<typeof Examples> = {
const meta = {
title: "Components/Form/TextInput",
component: Examples,
decorators: [
(Story, { args }) => {
(Story: StoryObj, context: StoryContext<TextInputProps>) => {
return (
<div
class={cx({
"w-[600px]": (args.orientation || "vertical") == "vertical",
"w-[1024px]": args.orientation == "horizontal",
"bg-inv-acc-3": args.inverted,
"w-[600px]": (context.args.orientation || "vertical") == "vertical",
"w-[1024px]": context.args.orientation == "horizontal",
"bg-inv-acc-3": context.args.inverted,
})}
>
<Story />
@@ -43,7 +43,7 @@ const meta: Meta<typeof Examples> = {
);
},
],
};
} satisfies Meta<TextInputProps>;
export default meta;

View File

@@ -1,4 +1,4 @@
import type { Meta, StoryObj } from "storybook-solidjs-vite";
import type { Meta, StoryObj, StoryContext } from "@kachurun/storybook-solid";
import { Component, For } from "solid-js";
import Icon, { IconProps, IconVariant } from "./Icon";
import cx from "classnames";
@@ -57,12 +57,12 @@ const IconExamples: Component<IconProps> = (props) => (
</div>
);
const meta: Meta<typeof IconExamples> = {
const meta: Meta<IconProps> = {
title: "Components/Icon",
component: IconExamples,
decorators: [
(Story, { args }) => (
<div class={cx(args.inverted || false ? "bg-inv-acc-3" : "")}>
(Story: StoryObj, context: StoryContext<IconProps>) => (
<div class={cx(context.args.inverted || false ? "bg-inv-acc-3" : "")}>
<Story />
</div>
),
@@ -71,7 +71,7 @@ const meta: Meta<typeof IconExamples> = {
export default meta;
type Story = StoryObj<typeof meta>;
type Story = StoryObj<IconProps>;
export const Default: Story = {};

View File

@@ -1,4 +1,4 @@
import type { Meta, StoryObj } from "storybook-solidjs-vite";
import type { Meta, StoryObj } from "@kachurun/storybook-solid";
import { Loader, LoaderProps } from "@/src/components/Loader/Loader";
const LoaderExamples = (props: LoaderProps) => (
@@ -9,14 +9,14 @@ const LoaderExamples = (props: LoaderProps) => (
</div>
);
const meta: Meta<typeof LoaderExamples> = {
const meta: Meta<LoaderProps> = {
title: "Components/Loader",
component: LoaderExamples,
};
export default meta;
type Story = StoryObj<typeof meta>;
type Story = StoryObj<LoaderProps>;
export const Primary: Story = {
args: {

View File

@@ -3,7 +3,7 @@ import { mergeProps } from "solid-js";
import styles from "./Loader.module.css";
import cx from "classnames";
type Hierarchy = "primary" | "secondary";
export type Hierarchy = "primary" | "secondary";
export interface LoaderProps {
hierarchy?: Hierarchy;

View File

@@ -1,11 +1,11 @@
import type { Meta, StoryObj } from "storybook-solidjs-vite";
import type { Meta, StoryContext, StoryObj } from "@kachurun/storybook-solid";
import { LoadingBar } from "./LoadingBar";
const meta: Meta<typeof LoadingBar> = {
const meta: Meta = {
title: "Components/LoadingBar",
component: LoadingBar,
decorators: [
(Story) => {
(Story: StoryObj, context: StoryContext<unknown>) => {
return (
<div class={"flex w-fit items-center justify-center bg-slate-500 p-10"}>
<Story />
@@ -17,6 +17,6 @@ const meta: Meta<typeof LoadingBar> = {
export default meta;
type Story = StoryObj<typeof meta>;
type Story = StoryObj;
export const Default: Story = {};

View File

@@ -2,7 +2,7 @@ import { JSX } from "solid-js";
import styles from "./LoadingBar.module.css";
import cx from "classnames";
type LoadingBarProps = JSX.HTMLAttributes<HTMLDivElement> & {};
export type LoadingBarProps = JSX.HTMLAttributes<HTMLDivElement> & {};
export const LoadingBar = (props: LoadingBarProps) => (
<div {...props} class={cx(styles.loading_bar, props.class)} />
);

View File

@@ -1,4 +1,4 @@
import type { Meta, StoryObj } from "storybook-solidjs-vite";
import type { Meta, StoryContext, StoryObj } from "@kachurun/storybook-solid";
import { Component, For } from "solid-js";
import { Logo, LogoProps, LogoVariant } from "./Logo";
import cx from "classnames";
@@ -11,12 +11,12 @@ const LogoExamples: Component<LogoProps> = (props) => (
</div>
);
const meta: Meta<typeof LogoExamples> = {
const meta: Meta<LogoProps> = {
title: "Components/Logo",
component: LogoExamples,
decorators: [
(Story, { args }) => (
<div class={cx(args.inverted || false ? "bg-inv-acc-3" : "")}>
(Story: StoryObj, context: StoryContext<LogoProps>) => (
<div class={cx(context.args.inverted || false ? "bg-inv-acc-3" : "")}>
<Story />
</div>
),
@@ -25,7 +25,7 @@ const meta: Meta<typeof LogoExamples> = {
export default meta;
type Story = StoryObj<typeof meta>;
type Story = StoryObj<LogoProps>;
export const Default: Story = {};

View File

@@ -1,11 +1,14 @@
import { MachineStatus } from "@/src/components/MachineStatus/MachineStatus";
import { Meta, StoryObj } from "storybook-solidjs-vite";
import {
MachineStatus,
MachineStatusProps,
} from "@/src/components/MachineStatus/MachineStatus";
import { Meta, StoryObj } from "@kachurun/storybook-solid";
const meta: Meta<typeof MachineStatus> = {
const meta: Meta<MachineStatusProps> = {
title: "Components/MachineStatus",
component: MachineStatus,
decorators: [
(Story) => (
(Story: StoryObj) => (
<div class="p-5 bg-inv-1">
<Story />
</div>
@@ -15,7 +18,7 @@ const meta: Meta<typeof MachineStatus> = {
export default meta;
type Story = StoryObj<typeof meta>;
type Story = StoryObj<MachineStatusProps>;
export const Loading: Story = {
args: {},

View File

@@ -1,16 +1,17 @@
import { Meta, StoryObj } from "storybook-solidjs-vite";
import { TagProps } from "@/src/components/Tag/Tag";
import { Meta, StoryObj } from "@kachurun/storybook-solid";
import { fn } from "storybook/test";
import { Modal } from "@/src/components/Modal/Modal";
import { Modal, ModalProps } from "@/src/components/Modal/Modal";
import { Fieldset, FieldsetFieldProps } from "@/src/components/Form/Fieldset";
import { TextInput } from "@/src/components/Form/TextInput";
import { TextArea } from "@/src/components/Form/TextArea";
import { Checkbox } from "@/src/components/Form/Checkbox";
import { Button } from "../Button/Button";
const meta: Meta<typeof Modal> = {
const meta: Meta<ModalProps> = {
title: "Components/Modal",
component: Modal,
render: (args) => (
render: (args: ModalProps) => (
<Modal
{...args}
children={
@@ -67,7 +68,7 @@ const meta: Meta<typeof Modal> = {
export default meta;
type Story = StoryObj<typeof meta>;
type Story = StoryObj<TagProps>;
export const Default: Story = {
args: {

View File

@@ -14,7 +14,7 @@ import Icon from "../Icon/Icon";
import cx from "classnames";
import { Dynamic } from "solid-js/web";
interface ModalContextType {
export interface ModalContextType {
portalRef: HTMLDivElement;
}

View File

@@ -1,11 +1,14 @@
import type { Meta, StoryObj } from "storybook-solidjs-vite";
import { NavSection } from "@/src/components/NavSection/NavSection";
import type { Meta, StoryObj } from "@kachurun/storybook-solid";
import {
NavSection,
NavSectionProps,
} from "@/src/components/NavSection/NavSection";
const meta: Meta<typeof NavSection> = {
const meta: Meta<NavSectionProps> = {
title: "Components/NavSection",
component: NavSection,
decorators: [
(Story) => (
(Story: StoryObj) => (
<div class="w-96">
<Story />
</div>
@@ -15,7 +18,7 @@ const meta: Meta<typeof NavSection> = {
export default meta;
type Story = StoryObj<typeof NavSection>;
type Story = StoryObj<NavSectionProps>;
export const Default: Story = {
args: {

View File

@@ -16,7 +16,7 @@ import { CollectionNode } from "@kobalte/core/*";
import cx from "classnames";
import { Loader } from "../Loader/Loader";
interface Option {
export interface Option {
value: string;
label: string;
disabled?: boolean;

View File

@@ -1,20 +1,24 @@
import { Meta, StoryObj } from "storybook-solidjs-vite";
import { Meta, StoryObj } from "@kachurun/storybook-solid";
import { Search } from "./Search";
import { Search, SearchProps } from "./Search";
import Icon from "../Icon/Icon";
import { Combobox } from "@kobalte/core/combobox";
import { Typography } from "../Typography/Typography";
import { ItemRenderOptions, SearchMultiple } from "./MultipleSearch";
import {
ItemRenderOptions,
SearchMultiple,
SearchMultipleProps,
} from "./MultipleSearch";
import { Show } from "solid-js";
const meta: Meta<typeof Search> = {
const meta = {
title: "Components/Search",
component: Search,
};
} satisfies Meta<SearchProps<unknown>>;
export default meta;
type Story = StoryObj<typeof meta>;
type Story = StoryObj<SearchProps<unknown>>;
// To test the virtualizer, we can generate a list of modules
function generateModules(count: number): Module[] {
@@ -103,7 +107,7 @@ export const Default: Story = {
);
},
},
render: (args) => {
render: (args: SearchProps<Module>) => {
return (
<div class="fixed bottom-10 left-1/2 mb-2 w-[30rem] -translate-x-1/2">
<Search<Module>
@@ -126,7 +130,7 @@ export const Loading: Story = {
options: [],
renderItem: () => <span></span>,
},
render: (args) => {
render: (args: SearchProps<Module>) => {
return (
<div class="absolute bottom-1/3 w-3/4 px-3">
<Search<Module>
@@ -231,7 +235,7 @@ export const Multiple: Story = {
);
},
},
render: (args) => {
render: (args: SearchMultipleProps<MachineOrTag>) => {
return (
<div class="absolute bottom-1/3 w-3/4 px-3">
<SearchMultiple<MachineOrTag>

View File

@@ -8,7 +8,7 @@ import { CollectionNode } from "@kobalte/core/*";
import { Loader } from "../Loader/Loader";
import cx from "classnames";
interface Option {
export interface Option {
value: string;
label: string;
disabled?: boolean;

View File

@@ -1,14 +1,14 @@
import { Meta, StoryObj } from "storybook-solidjs-vite";
import { Meta, StoryObj } from "@kachurun/storybook-solid";
import { TagSelect } from "./TagSelect";
import { TagSelect, TagSelectProps } from "./TagSelect";
import { Tag } from "../Tag/Tag";
import Icon from "../Icon/Icon";
import { createSignal } from "solid-js";
const meta: Meta<typeof TagSelect> = {
const meta = {
title: "Components/Custom/SelectStepper",
component: TagSelect,
};
} satisfies Meta<TagSelectProps<string>>;
export default meta;
@@ -17,7 +17,7 @@ interface Item {
label: string;
}
type Story = StoryObj<typeof meta>;
type Story = StoryObj<TagSelectProps<Item>>;
const Item = (item: Item) => (
<Tag
@@ -42,8 +42,8 @@ export const Default: Story = {
{ value: "corge", label: "Corge" },
{ value: "grault", label: "Grault" },
],
},
render: (args) => {
} satisfies Partial<TagSelectProps<Item>>,
render: (args: TagSelectProps<Item>) => {
const [state, setState] = createSignal<Item[]>([]);
return (
<TagSelect<Item>

View File

@@ -1,13 +1,18 @@
import { Meta, StoryObj } from "storybook-solidjs-vite";
import { TagProps } from "@/src/components/Tag/Tag";
import { Meta, StoryContext, StoryObj } from "@kachurun/storybook-solid";
import { Select } from "./Select";
import { Select, SelectProps } from "./Select";
import { Fieldset } from "../Form/Fieldset";
const meta: Meta<typeof Select> = {
// const meta: Meta<SelectProps> = {
// title: "Components/Select",
// component: Select,
// };
const meta = {
title: "Components/Form/Select",
component: Select,
decorators: [
(Story) => {
(Story: StoryObj, context: StoryContext<SelectProps>) => {
return (
<div class={`w-[600px]`}>
<Fieldset>
@@ -17,11 +22,11 @@ const meta: Meta<typeof Select> = {
);
},
],
};
} satisfies Meta<SelectProps>;
export default meta;
type Story = StoryObj<typeof meta>;
type Story = StoryObj<TagProps>;
export const Default: Story = {
args: {

View File

@@ -16,7 +16,7 @@ import cx from "classnames";
import { useModalContext } from "../Modal/Modal";
import { keepTruthy } from "@/src/util";
interface Option {
export interface Option {
value: string;
label: string;
disabled?: boolean;

View File

@@ -1,5 +1,10 @@
import type { Meta, StoryObj } from "storybook-solidjs-vite";
import { createMemoryHistory, MemoryRouter, Route } from "@solidjs/router";
import type { Meta, StoryObj } from "@kachurun/storybook-solid";
import {
createMemoryHistory,
MemoryRouter,
Route,
RouteSectionProps,
} from "@solidjs/router";
import { Sidebar } from "@/src/components/Sidebar/Sidebar";
import { Suspense } from "solid-js";
import { addClanURI, resetStore } from "@/src/stores/clan";
@@ -101,7 +106,7 @@ const staticSections = [
},
];
const meta: Meta<typeof Sidebar> = {
const meta: Meta<RouteSectionProps> = {
title: "Components/Sidebar",
component: Sidebar,
render: () => {
@@ -139,7 +144,7 @@ const meta: Meta<typeof Sidebar> = {
export default meta;
type Story = StoryObj<typeof meta>;
type Story = StoryObj<RouteSectionProps>;
const mockFetcher = <K extends OperationNames>(
method: K,

View File

@@ -4,12 +4,12 @@ import { SidebarBody } from "@/src/components/Sidebar/SidebarBody";
import cx from "classnames";
import { splitProps } from "solid-js";
interface LinkProps {
export interface LinkProps {
path: string;
label?: string;
}
interface SectionProps {
export interface SectionProps {
title: string;
links: LinkProps[];
}

View File

@@ -1,5 +1,8 @@
import type { Meta, StoryObj } from "storybook-solidjs-vite";
import { SidebarPane } from "@/src/components/Sidebar/SidebarPane";
import type { Meta, StoryObj } from "@kachurun/storybook-solid";
import {
SidebarPane,
SidebarPaneProps,
} from "@/src/components/Sidebar/SidebarPane";
import { SidebarSection } from "./SidebarSection";
import { Divider } from "@/src/components/Divider/Divider";
import { TextInput } from "@/src/components/Form/TextInput";
@@ -11,6 +14,9 @@ import { splitProps } from "solid-js";
import { Typography } from "@/src/components/Typography/Typography";
import { MachineTags } from "@/src/components/Form/MachineTags";
import { setValue } from "@modular-forms/solid";
import { StoryContext } from "@kachurun/storybook-solid-vite";
type Story = StoryObj<SidebarPaneProps>;
const profiles = {
ron: {
@@ -22,13 +28,18 @@ const profiles = {
},
};
const meta: Meta<typeof SidebarPane> = {
const meta: Meta<SidebarPaneProps> = {
title: "Components/SidebarPane",
component: SidebarPane,
decorators: [
(
Story: StoryObj<SidebarPaneProps>,
context: StoryContext<SidebarPaneProps>,
) =>
() => <Story {...context.args} />,
],
};
type Story = StoryObj<typeof meta>;
export default meta;
export const Default: Story = {
@@ -40,7 +51,7 @@ export const Default: Story = {
},
// We have to provide children within a custom render function to ensure we aren't creating any reactivity outside the
// solid-js scope.
render: (args) => (
render: (args: SidebarPaneProps) => (
<SidebarPane
{...args}
children={

View File

@@ -2,7 +2,7 @@ import { JSX, Show } from "solid-js";
import styles from "./SidebarSection.module.css";
import { Typography } from "@/src/components/Typography/Typography";
interface SidebarSectionProps {
export interface SidebarSectionProps {
title: string;
controls?: JSX.Element;
children: JSX.Element;

View File

@@ -18,7 +18,7 @@ import { Button } from "@/src/components/Button/Button";
import { Loader } from "../../components/Loader/Loader";
import { SidebarSection } from "./SidebarSection";
interface SidebarSectionFormProps<FormValues extends FieldValues> {
export interface SidebarSectionFormProps<FormValues extends FieldValues> {
title: string;
schema: GenericSchema<FormValues> | GenericSchemaAsync<FormValues>;
initialValues: PartialValues<FormValues>;

View File

@@ -7,7 +7,7 @@ import styles from "./SidebarSectionInstall.module.css";
import { Alert } from "../Alert/Alert";
import { useClanContext } from "@/src/routes/Clan/Clan";
interface SidebarSectionInstallProps {
export interface SidebarSectionInstallProps {
clanURI: string;
machineName: string;
}

View File

@@ -6,7 +6,7 @@ import styles from "./SidebarSectionInstall.module.css";
import { UpdateModal } from "@/src/workflows/InstallMachine/UpdateMachine";
import { useClanContext } from "@/src/routes/Clan/Clan";
interface SidebarSectionUpdateProps {
export interface SidebarSectionUpdateProps {
clanURI: string;
machineName: string;
}

View File

@@ -1,16 +1,16 @@
import { Tag } from "@/src/components/Tag/Tag";
import { Meta, StoryObj } from "storybook-solidjs-vite";
import { Tag, TagProps } from "@/src/components/Tag/Tag";
import { Meta, type StoryContext, StoryObj } from "@kachurun/storybook-solid";
import { fn } from "storybook/test";
import Icon from "../Icon/Icon";
const meta: Meta<typeof Tag> = {
const meta: Meta<TagProps> = {
title: "Components/Tag",
component: Tag,
};
export default meta;
type Story = StoryObj<typeof meta>;
type Story = StoryObj<TagProps>;
export const Default: Story = {
args: {
@@ -43,7 +43,7 @@ export const WithAction: Story = {
icon: IconAction,
interactive: true,
},
play: async ({ canvas, step, userEvent, args }) => {
play: async ({ canvas, step, userEvent, args }: StoryContext) => {
await userEvent.click(canvas.getByRole("button"));
// await expect(args.icon.onClick).toHaveBeenCalled();
},

View File

@@ -1,11 +1,11 @@
import { TagGroup } from "@/src/components/TagGroup/TagGroup";
import { Meta, StoryObj } from "storybook-solidjs-vite";
import { TagGroup, TagGroupProps } from "@/src/components/TagGroup/TagGroup";
import { Meta, StoryObj } from "@kachurun/storybook-solid";
const meta: Meta<typeof TagGroup> = {
const meta: Meta<TagGroupProps> = {
title: "Components/TagGroup",
component: TagGroup,
decorators: [
(Story) => (
(Story: StoryObj) => (
/* for some reason w-x from tailwind was not working */
<div style="width: 196px">
<Story />
@@ -16,7 +16,7 @@ const meta: Meta<typeof TagGroup> = {
export default meta;
type Story = StoryObj<typeof meta>;
type Story = StoryObj<TagGroupProps>;
export const Default: Story = {
args: {

View File

@@ -1,18 +1,19 @@
import { Meta, StoryObj } from "storybook-solidjs-vite";
import { Toolbar } from "@/src/components/Toolbar/Toolbar";
import { Meta, StoryObj } from "@kachurun/storybook-solid";
import { Toolbar, ToolbarProps } from "@/src/components/Toolbar/Toolbar";
import { ToolbarButton } from "./ToolbarButton";
const meta: Meta<typeof Toolbar> = {
const meta: Meta<ToolbarProps> = {
title: "Components/Toolbar",
component: Toolbar,
};
export default meta;
type Story = StoryObj<typeof meta>;
type Story = StoryObj<ToolbarProps>;
export const Default: Story = {
// We have to specify children inside a render function to avoid issues with reactivity outside a solid-js context.
// @ts-expect-error: args in storybook is not typed correctly. This is a storybook issue.
render: (args) => (
<div class="flex h-[80vh]">
<div class="mt-auto">

View File

@@ -5,7 +5,7 @@ import Icon, { IconVariant } from "@/src/components/Icon/Icon";
import type { JSX } from "solid-js";
import { Tooltip } from "../Tooltip/Tooltip";
interface ToolbarButtonProps
export interface ToolbarButtonProps
extends JSX.ButtonHTMLAttributes<HTMLButtonElement> {
icon: IconVariant;
description: JSX.Element;

View File

@@ -1,18 +1,18 @@
import { Meta, StoryObj } from "storybook-solidjs-vite";
import { Tooltip } from "@/src/components/Tooltip/Tooltip";
import { Meta, StoryObj } from "@kachurun/storybook-solid";
import { Tooltip, TooltipProps } from "@/src/components/Tooltip/Tooltip";
import { Typography } from "@/src/components/Typography/Typography";
const meta: Meta<typeof Tooltip> = {
const meta: Meta<TooltipProps> = {
title: "Components/Tooltip",
component: Tooltip,
decorators: [
(Story) => (
(Story: StoryObj<TooltipProps>) => (
<div class="p-16">
<Story />
</div>
),
],
render: (args) => (
render: (args: TooltipProps) => (
<div class="p-16">
<Tooltip
{...args}
@@ -33,7 +33,7 @@ const meta: Meta<typeof Tooltip> = {
export default meta;
type Story = StoryObj<typeof meta>;
type Story = StoryObj<TooltipProps>;
export const Default: Story = {
args: {

View File

@@ -1,4 +1,4 @@
import type { Meta, StoryObj } from "storybook-solidjs-vite";
import type { Meta, StoryObj } from "@kachurun/storybook-solid";
import { Family, Hierarchy, Typography, Weight } from "./Typography";
import { Component, For, Show } from "solid-js";
@@ -73,14 +73,14 @@ const TypographyExamples: Component<TypographyExamplesProps> = (props) => (
</table>
);
const meta: Meta<typeof TypographyExamples> = {
const meta: Meta<TypographyExamplesProps> = {
title: "Components/Typography",
component: TypographyExamples,
};
export default meta;
type Story = StoryObj<typeof meta>;
type Story = StoryObj<TypographyExamplesProps>;
export const BodyCondensed: Story = {
args: {

View File

@@ -9,15 +9,15 @@ import { getInClasses } from "@/src/util";
export type Hierarchy = "body" | "title" | "headline" | "label" | "teaser";
export type Weight = "normal" | "medium" | "bold";
export type Family = "regular" | "mono";
type Transform = "uppercase" | "lowercase" | "capitalize";
interface SizeForHierarchy {
export type Transform = "uppercase" | "lowercase" | "capitalize";
export interface SizeForHierarchy {
body: "default" | "s" | "xs" | "xxs";
headline: "default" | "m" | "l" | "xl" | "xxl";
title: "default" | "m" | "l";
label: "default" | "s" | "xs" | "xxs";
teaser: "default";
}
interface TagForHierarchy {
export interface TagForHierarchy {
body: "span" | "p" | "div";
headline: "h1" | "h2" | "h3" | "h4";
title: "h1" | "h2" | "h3" | "h4";
@@ -40,7 +40,7 @@ const defaultTagMap = {
label: "span",
teaser: "h3",
} as const;
interface TypographyProps<H extends Hierarchy> {
export interface TypographyProps<H extends Hierarchy> {
hierarchy: H;
children: JSX.Element;
size?: SizeForHierarchy[H];

View File

@@ -1,7 +1,7 @@
import { createContext, JSX, useContext } from "solid-js";
import { ApiCall, OperationArgs, OperationNames } from "./api";
interface ApiClient {
export interface ApiClient {
fetch: Fetcher;
}

View File

@@ -3,7 +3,7 @@ import { addClanURI, setActiveClanURI } from "@/src/stores/clan";
import { Params, Navigator, useParams, useSearchParams } from "@solidjs/router";
export const encodeBase64 = (value: string) => window.btoa(value);
const decodeBase64 = (value: string) => window.atob(value);
export const decodeBase64 = (value: string) => window.atob(value);
export const selectClanFolder = async () => {
const req = callApi("get_clan_folder", {});
@@ -80,7 +80,7 @@ export const navigateToClan = (navigate: Navigator, clanURI: string) => {
export const navigateToOnboarding = (navigate: Navigator, addClan: boolean) =>
navigate(`/${addClan ? "?addClan=true" : ""}`);
const navigateToMachine = (
export const navigateToMachine = (
navigate: Navigator,
clanURI: string,
name: string,
@@ -90,7 +90,7 @@ const navigateToMachine = (
navigate(path);
};
const clanURIParam = (params: Params) => {
export const clanURIParam = (params: Params) => {
try {
return decodeBase64(params.clanURI);
} catch (e) {
@@ -101,19 +101,19 @@ const clanURIParam = (params: Params) => {
export const useClanURI = () => clanURIParam(useParams());
const machineNameParam = (params: Params) => {
export const machineNameParam = (params: Params) => {
return params.machineName;
};
const inputParam = (params: Params) => params.input;
const nameParam = (params: Params) => params.name;
const idParam = (params: Params) => params.id;
export const inputParam = (params: Params) => params.input;
export const nameParam = (params: Params) => params.name;
export const idParam = (params: Params) => params.id;
export const useMachineName = (): string => machineNameParam(useParams());
const useInputParam = (): string => inputParam(useParams());
const useNameParam = (): string => nameParam(useParams());
export const useInputParam = (): string => inputParam(useParams());
export const useNameParam = (): string => nameParam(useParams());
const maybeUseIdParam = (): string | null => {
export const maybeUseIdParam = (): string | null => {
const params = useParams();
if (params.id === undefined) {
return null;

View File

@@ -0,0 +1,25 @@
import { useMutation, useQueryClient } from "@tanstack/solid-query";
import { callApi, OperationArgs } from "@/src/hooks/api";
import { encodeBase64 } from "@/src/hooks/clan";
const queryClient = useQueryClient();
export const updateMachine = useMutation(() => ({
mutationFn: async (args: OperationArgs<"set_machine">) => {
const call = callApi("set_machine", args);
return {
args,
...call,
};
},
onSuccess: async ({ args }) => {
const {
name,
flake: { identifier },
} = args.machine;
await queryClient.invalidateQueries({
queryKey: ["clans", encodeBase64(identifier), "machine", name],
});
},
}));

View File

@@ -44,7 +44,7 @@ window.notifyBus = (msg: ProcessMessage) => {
*
* consider using useNotify for reactive usage on solidjs
*/
function _subscribeNotify<T extends ProcessMessage>(
export function _subscribeNotify<T extends ProcessMessage>(
filter: (msg: T) => boolean,
callback: (msg: T) => void,
) {
@@ -65,7 +65,7 @@ function _subscribeNotify<T extends ProcessMessage>(
* The signal has the value of the last message where filter was true
* null in case no message was recieved yet
*/
function useNotify<T extends ProcessMessage = ProcessMessage>(
export function useNotify<T extends ProcessMessage = ProcessMessage>(
filter: (msg: T) => boolean = () => true as boolean,
) {
const [message, setMessage] = createSignal<T | null>(null);

View File

@@ -16,16 +16,16 @@ export interface ClanDetails {
fieldsSchema: SuccessData<"get_clan_details_schema">;
}
type Tags = SuccessData<"list_tags">;
export type Tags = SuccessData<"list_tags">;
export type Machine = SuccessData<"get_machine">;
type MachineState = SuccessData<"get_machine_state">;
export type MachineState = SuccessData<"get_machine_state">;
export type MachineStatus = MachineState["status"];
type ListMachines = SuccessData<"list_machines">;
type MachineDetails = SuccessData<"get_machine_details">;
export type ListMachines = SuccessData<"list_machines">;
export type MachineDetails = SuccessData<"get_machine_details">;
type ListServiceModules = SuccessData<"list_service_modules">;
export type ListServiceModules = SuccessData<"list_service_modules">;
export type ListServiceInstances = SuccessData<"list_service_instances">;
export interface MachineDetail {
@@ -35,7 +35,7 @@ export interface MachineDetail {
}
export type MachinesQueryResult = UseQueryResult<ListMachines>;
type ClanListQueryResult = UseQueryResult<ClanDetails>[];
export type ClanListQueryResult = UseQueryResult<ClanDetails>[];
export const DefaultQueryClient = new QueryClient({
defaultOptions: {
@@ -67,7 +67,7 @@ export const useMachinesQuery = (clanURI: string) => {
}));
};
const machineKey = (clanUri: string, machineName: string) => [
export const machineKey = (clanUri: string, machineName: string) => [
...clanKey(clanUri),
"machine",
encodeBase64(machineName),
@@ -174,7 +174,7 @@ export const useMachineStateQuery = (clanURI: string, machineName: string) => {
}));
};
const useServiceModulesQuery = (clanURI: string) => {
export const useServiceModulesQuery = (clanURI: string) => {
const client = useApiClient();
return useQuery<ListServiceModules>(() => ({
@@ -222,7 +222,10 @@ export const useServiceInstancesQuery = (clanURI: string) => {
}));
};
const useMachineDetailsQuery = (clanURI: string, machineName: string) => {
export const useMachineDetailsQuery = (
clanURI: string,
machineName: string,
) => {
const client = useApiClient();
return useQuery<MachineDetails>(() => ({
queryKey: [machineKey(clanURI, machineName), "details"],
@@ -248,7 +251,7 @@ const useMachineDetailsQuery = (clanURI: string, machineName: string) => {
}));
};
const ClanDetailsPersister = experimental_createQueryPersister({
export const ClanDetailsPersister = experimental_createQueryPersister({
storage: ClanDetailsStore,
});
@@ -370,10 +373,10 @@ export const useClanListQuery = (
}));
};
type MachineFlashOptions = SuccessData<"get_machine_flash_options">;
type MachineFlashOptionsQuery = UseQueryResult<MachineFlashOptions>;
export type MachineFlashOptions = SuccessData<"get_machine_flash_options">;
export type MachineFlashOptionsQuery = UseQueryResult<MachineFlashOptions>;
const useMachineFlashOptions = (): MachineFlashOptionsQuery => {
export const useMachineFlashOptions = (): MachineFlashOptionsQuery => {
const client = useApiClient();
return useQuery<MachineFlashOptions>(() => ({
queryKey: ["flash_options"],
@@ -392,8 +395,8 @@ const useMachineFlashOptions = (): MachineFlashOptionsQuery => {
}));
};
type SystemStorageOptions = SuccessData<"list_system_storage_devices">;
type SystemStorageOptionsQuery = UseQueryResult<SystemStorageOptions>;
export type SystemStorageOptions = SuccessData<"list_system_storage_devices">;
export type SystemStorageOptionsQuery = UseQueryResult<SystemStorageOptions>;
export const useSystemStorageOptions = (): SystemStorageOptionsQuery => {
const client = useApiClient();
@@ -414,8 +417,10 @@ export const useSystemStorageOptions = (): SystemStorageOptionsQuery => {
}));
};
type MachineHardwareSummary = SuccessData<"get_machine_hardware_summary">;
type MachineHardwareSummaryQuery = UseQueryResult<MachineHardwareSummary>;
export type MachineHardwareSummary =
SuccessData<"get_machine_hardware_summary">;
export type MachineHardwareSummaryQuery =
UseQueryResult<MachineHardwareSummary>;
export const useMachineHardwareSummary = (
clanUri: string,
@@ -452,8 +457,8 @@ export const useMachineHardwareSummary = (
}));
};
type MachineDiskSchema = SuccessData<"get_machine_disk_schemas">;
type MachineDiskSchemaQuery = UseQueryResult<MachineDiskSchema>;
export type MachineDiskSchema = SuccessData<"get_machine_disk_schemas">;
export type MachineDiskSchemaQuery = UseQueryResult<MachineDiskSchema>;
export const useMachineDiskSchemas = (
clanUri: string,
@@ -491,7 +496,7 @@ export const useMachineDiskSchemas = (
};
export type MachineGenerators = SuccessData<"get_generators">;
type MachineGeneratorsQuery = UseQueryResult<MachineGenerators>;
export type MachineGeneratorsQuery = UseQueryResult<MachineGenerators>;
export const useMachineGenerators = (
clanUri: string,
@@ -533,7 +538,7 @@ export const useMachineGenerators = (
}));
};
type ServiceModulesQuery = ReturnType<typeof useServiceModules>;
export type ServiceModulesQuery = ReturnType<typeof useServiceModules>;
export type ServiceModules = SuccessData<"list_service_modules">;
export const useServiceModules = (clanUri: string) => {
const client = useApiClient();
@@ -561,7 +566,7 @@ export const useServiceModules = (clanUri: string) => {
export const clanKey = (clanUri: string) => ["clans", encodeBase64(clanUri)];
export type ServiceInstancesQuery = ReturnType<typeof useServiceInstances>;
type ServiceInstances = SuccessData<"list_service_instances">;
export type ServiceInstances = SuccessData<"list_service_instances">;
export const useServiceInstances = (clanUri: string) => {
const client = useApiClient();
return useQuery(() => ({

View File

@@ -7,13 +7,13 @@ import {
} from "solid-js";
import { createStore, SetStoreFunction, Store } from "solid-js/store";
interface StepBase {
export interface StepBase {
id: string;
}
type Step<ExtraFields = unknown> = StepBase & ExtraFields;
export type Step<ExtraFields = unknown> = StepBase & ExtraFields;
interface StepOptions<Id, StoreType> {
export interface StepOptions<Id, StoreType> {
initialStep: Id;
initialStoreData?: StoreType;
}
@@ -95,7 +95,10 @@ export function createStepper<
}
type StoreTuple<T> = [get: Store<T>, set: SetStoreFunction<T>];
interface StepperReturn<T extends readonly Step[], StepId = T[number]["id"]> {
export interface StepperReturn<
T extends readonly Step[],
StepId = T[number]["id"],
> {
_store: never;
activeStep: Accessor<StepId>;
setActiveStep: (id: StepId) => void;

View File

@@ -1,43 +1,45 @@
import { fn } from "storybook/test";
import type { Meta, StoryObj } from "storybook-solidjs-vite";
import { ClanSettingsModal } from "./ClanSettingsModal";
import type { Meta, StoryObj } from "@kachurun/storybook-solid";
import { ClanSettingsModal, ClanSettingsModalProps } from "./ClanSettingsModal";
const meta: Meta<typeof ClanSettingsModal> = {
const meta: Meta<ClanSettingsModalProps> = {
title: "Modals/ClanSettings",
component: ClanSettingsModal,
};
export default meta;
type Story = StoryObj<typeof meta>;
type Story = StoryObj<ClanSettingsModalProps>;
export const Default: Story = {
args: {
onClose: fn(),
model: {
uri: "/home/foo/my-clan",
details: {
name: "Sol",
description: null,
icon: null,
const props: ClanSettingsModalProps = {
onClose: fn(),
model: {
uri: "/home/foo/my-clan",
details: {
name: "Sol",
description: null,
icon: null,
},
fieldsSchema: {
name: {
readonly: true,
reason: null,
readonly_members: [],
},
fieldsSchema: {
name: {
readonly: true,
reason: null,
readonly_members: [],
},
description: {
readonly: false,
reason: null,
readonly_members: [],
},
icon: {
readonly: false,
reason: null,
readonly_members: [],
},
description: {
readonly: false,
reason: null,
readonly_members: [],
},
icon: {
readonly: false,
reason: null,
readonly_members: [],
},
},
},
};
export const Default: Story = {
args: props,
};

View File

@@ -8,7 +8,7 @@ import { useClanListQuery } from "@/src/hooks/queries";
import { Alert } from "@/src/components/Alert/Alert";
import { NavSection } from "@/src/components/NavSection/NavSection";
interface ListClansModalProps {
export interface ListClansModalProps {
onClose?: () => void;
error?: {
title: string;

View File

@@ -39,7 +39,7 @@ import { ListClansModal } from "@/src/modals/ListClansModal/ListClansModal";
import { AddMachine } from "@/src/workflows/AddMachine/AddMachine";
import { SelectService } from "@/src/workflows/Service/SelectServiceFlyout";
type WorldMode = "default" | "select" | "service" | "create" | "move";
export type WorldMode = "default" | "select" | "service" | "create" | "move";
function createClanContext(
clanURI: string,

View File

@@ -22,7 +22,7 @@ type FieldNames = "name" | "description" | "machineClass";
type FormValues = v.InferInput<typeof schema>;
interface SectionGeneralProps {
export interface SectionGeneralProps {
clanURI: string;
machineName: string;
onSubmit: (values: FormValues) => Promise<void>;

View File

@@ -52,7 +52,7 @@ export function createMachineMesh() {
};
}
function createCubeBase(
export function createCubeBase(
color: THREE.ColorRepresentation,
emissive: THREE.ColorRepresentation,
geometry: THREE.BoxGeometry,
@@ -70,7 +70,7 @@ function createCubeBase(
}
// Function to build rounded rect shape
function roundedRectShape(w: number, h: number, r: number) {
export function roundedRectShape(w: number, h: number, r: number) {
const shape = new THREE.Shape();
const x = -w / 2;
const y = -h / 2;

View File

@@ -80,7 +80,7 @@ const [lastClickedMachine, setLastClickedMachine] = createSignal<string | null>(
// Exported so others could also emit the signal if needed
// And for testing purposes
function emitMachineClick(id: string | null) {
export function emitMachineClick(id: string | null) {
setLastClickedMachine(id);
if (id) {
// Clear after a short delay to allow re-clicking the same machine

View File

@@ -7,7 +7,7 @@ const [highlightGroups, setHighlightGroups] = createStore<
>({});
// Add highlight
function highlight(group: string, nodeId: string) {
export function highlight(group: string, nodeId: string) {
setHighlightGroups(group, (prev = new Set()) => {
const next = new Set(prev);
next.add(nodeId);
@@ -16,7 +16,7 @@ function highlight(group: string, nodeId: string) {
}
// Remove highlight
function unhighlight(group: string, nodeId: string) {
export function unhighlight(group: string, nodeId: string) {
setHighlightGroups(group, (prev = new Set()) => {
const next = new Set(prev);
next.delete(nodeId);

View File

@@ -1,4 +1,4 @@
import { Meta, StoryObj } from "storybook-solidjs-vite";
import { Meta, StoryObj } from "@kachurun/storybook-solid";
import { Splash } from "./splash";
const meta: Meta = {

Some files were not shown because too many files have changed in this diff Show More