feat: Phase 1 — extract the client (web SPA + desktop) to dtoro/oikos-web
Some checks failed
ci / build-test (push) Has been cancelled
ci / docker-build (push) Has been cancelled

Problem: the hexagonal refactor churns the backend tree for nine more
phases; the UI delivery stack (web/ SPA, cmd/desktop Wails wrapper,
compose/web image) must move to its own repo first so doc/layout
rewrites land once on a backend-only tree.

Change:
- New repo git.hubris.network/dtoro/oikos-web (v0.33.0): web/, desktop/
  (updateURL repointed to oikos-web releases), compose/, own CI (web +
  desktop jobs), own deploy script (CI-green gate, TOCTOU guard,
  version-tagged images, prune-to-3), own webhook receiver on :9798 +
  launchd unit, own compose project publishing the same 8091:80.
- Cutover executed on mac-mini in order: oikos stack's web service
  stopped+removed, oikos-web project brought up on 8091; outer Caddy
  untouched (targets the published port) — serving + Authentik flow +
  /wails 404 quirk verified post-cutover.
- Stripped from oikos: web/, cmd/desktop/, compose/web/, desktop CI
  workflow, ci.yml web job, Makefile ui/desktop/desktop-package/install
  targets, the compose web service, oikos-web from deploy.sh's fallback
  prune list; wails + go-keyring dropped from go.mod, vendor synced.
- README / CONTRIBUTING / AGENTS.md / .agents dev+operations docs now
  point at the new repo; mbse + mascot design docs carry a path note.

Risk: production SPA serving depends on the new pipeline now; rollback
is versioned-image re-up of the old web service from a pre-split
checkout (port 8091). Desktop builds installed before the split still
check dtoro/oikos releases — one manual reinstall, noted in the
oikos-web release notes.

Verification: go vet, make test (race), make generate-check, golangci
(no new findings; baseline down 400→365); post-cutover curls —
localhost:8091 200, /wails/runtime.js 404, outer Caddy 302 Authentik.
This commit is contained in:
2026-08-15 22:27:52 +02:00
parent e074f04bdf
commit d4d99a7473
18854 changed files with 2615729 additions and 173735 deletions

View File

@@ -0,0 +1 @@
export * from "./watch.svelte.js";

1
web/node_modules/runed/dist/utilities/watch/index.js generated vendored Normal file
View File

@@ -0,0 +1 @@
export * from "./watch.svelte.js";

View File

@@ -0,0 +1,36 @@
import type { Getter } from "../../internal/types.js";
export type WatchOptions = {
/**
* If `true`, the effect doesn't run until one of the `sources` changes.
*
* @default false
*/
lazy?: boolean;
};
export declare function watch<T extends Array<unknown>>(sources: {
[K in keyof T]: Getter<T[K]>;
}, effect: (values: T, previousValues: {
[K in keyof T]: T[K] | undefined;
}) => void | VoidFunction, options?: WatchOptions): void;
export declare function watch<T>(source: Getter<T>, effect: (value: T, previousValue: T | undefined) => void | VoidFunction, options?: WatchOptions): void;
export declare namespace watch {
var pre: typeof watchPre;
}
declare function watchPre<T extends Array<unknown>>(sources: {
[K in keyof T]: Getter<T[K]>;
}, effect: (values: T, previousValues: {
[K in keyof T]: T[K] | undefined;
}) => void | VoidFunction, options?: WatchOptions): void;
declare function watchPre<T>(source: Getter<T>, effect: (value: T, previousValue: T | undefined) => void | VoidFunction, options?: WatchOptions): void;
export declare function watchOnce<T extends Array<unknown>>(sources: {
[K in keyof T]: Getter<T[K]>;
}, effect: (values: T, previousValues: T) => void | VoidFunction): void;
export declare function watchOnce<T>(source: Getter<T>, effect: (value: T, previousValue: T) => void | VoidFunction): void;
export declare namespace watchOnce {
var pre: typeof watchOncePre;
}
declare function watchOncePre<T extends Array<unknown>>(sources: {
[K in keyof T]: Getter<T[K]>;
}, effect: (values: T, previousValues: T) => void | VoidFunction): void;
declare function watchOncePre<T>(source: Getter<T>, effect: (value: T, previousValue: T) => void | VoidFunction): void;
export {};

View File

@@ -0,0 +1,69 @@
import { untrack } from "svelte";
function runEffect(flush, effect) {
switch (flush) {
case "post":
$effect(effect);
break;
case "pre":
$effect.pre(effect);
break;
}
}
function runWatcher(sources, flush, effect, options = {}) {
const { lazy = false } = options;
// Run the effect immediately if `lazy` is `false`.
let active = !lazy;
// On the first run, if the dependencies are an array, pass an empty array
// to the previous value instead of `undefined` to allow destructuring.
//
// watch(() => [a, b], ([a, b], [prevA, prevB]) => { ... });
let previousValues = Array.isArray(sources)
? []
: undefined;
runEffect(flush, () => {
const values = Array.isArray(sources) ? sources.map((source) => source()) : sources();
if (!active) {
active = true;
previousValues = values;
return;
}
const cleanup = untrack(() => effect(values, previousValues));
previousValues = values;
return cleanup;
});
}
function runWatcherOnce(sources, flush, effect) {
const cleanupRoot = $effect.root(() => {
let stop = false;
runWatcher(sources, flush, (values, previousValues) => {
if (stop) {
cleanupRoot();
return;
}
// Since `lazy` is `true`, `previousValues` is always defined.
const cleanup = effect(values, previousValues);
stop = true;
return cleanup;
},
// Running the effect immediately just once makes no sense at all.
// That's just `onMount` with extra steps.
{ lazy: true });
});
$effect(() => {
return cleanupRoot;
});
}
export function watch(sources, effect, options) {
runWatcher(sources, "post", effect, options);
}
function watchPre(sources, effect, options) {
runWatcher(sources, "pre", effect, options);
}
watch.pre = watchPre;
export function watchOnce(source, effect) {
runWatcherOnce(source, "post", effect);
}
function watchOncePre(source, effect) {
runWatcherOnce(source, "pre", effect);
}
watchOnce.pre = watchOncePre;