@domicile-desktop/react 0.0.0-alpha-2edc3c14d846 → 0.0.0-alpha-b85d9bb6d4af

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -31,6 +31,17 @@ export const Shell = mount(Focused);
31
31
  - `./session`: `useIdle`, `useLocked`
32
32
  - `./portals`: `usePortalRequests`, `useCapturing`, `useBoundShortcuts`,
33
33
  `usePortalWallpaper`
34
+ - Shared watches, for state a library watches (UPower, BlueZ, `pactl`):
35
+ - `./shared-watch`: `sharedWatch(start)` runs one watch for every
36
+ subscriber, from the first to the last.
37
+ - `./per-desktop`: `perDesktop(make)`, one value per desktop.
38
+ - `./use-shared-watch`: `useSharedWatch(watch)`, and `useDeskWatch(desk)`
39
+ for a `perDesktop` watch of the desktop above.
40
+ - `./watch-presence`: `watchPresence(read, onPresent)` looks for hardware
41
+ in `/sys` every 10 seconds; `isAbsent` says whether to hide for want of
42
+ it.
43
+ - The `system-*` packages' `/react` use these: `useBattery`,
44
+ `useBluetooth`, `useNetwork`, `useAudio` and more.
34
45
  - `./fake-domicile`: `<FakeDomicile fake>` for tests. `fake` is a
35
46
  `FakeDomicileHost` (`@domicile-desktop/sdk/fake-host`) the test drives;
36
47
  without it, a desktop no test drives.
@@ -38,6 +49,7 @@ export const Shell = mount(Focused);
38
49
  ## Dependencies
39
50
 
40
51
  - `@domicile-desktop/sdk` for the desktop's types and `FakeDomicileHost`.
52
+ - `@cprussin/option-result` for `watchPresence`'s `Result`.
41
53
  - `react` and `react-dom` are peers: the shell's own copies.
42
54
 
43
55
  ## Test
@@ -0,0 +1,3 @@
1
+ import type { Appearance } from "@domicile-desktop/sdk/desktop/appearance";
2
+ /** The accent, contrast and motion the config asks for, as they change. */
3
+ export declare const useAppearance: () => Appearance;
@@ -0,0 +1,11 @@
1
+ import { useMemo } from "react";
2
+ import { useDomicile } from "./domicile-provider";
3
+ import { useWatch } from "./use-watch";
4
+ /** The accent, contrast and motion the config asks for, as they change. */
5
+ export const useAppearance = () => {
6
+ const { appearance } = useDomicile();
7
+ const accentColor = useWatch(appearance, "change", () => appearance.accentColor);
8
+ const highContrast = useWatch(appearance, "change", () => appearance.highContrast);
9
+ const reducedMotion = useWatch(appearance, "change", () => appearance.reducedMotion);
10
+ return useMemo(() => ({ accentColor, highContrast, reducedMotion }), [accentColor, highContrast, reducedMotion]);
11
+ };
@@ -0,0 +1,3 @@
1
+ import type { Display } from "@domicile-desktop/sdk/desktop/displays";
2
+ /** The screens, or `undefined` until the compositor describes the desktop. */
3
+ export declare const useDisplays: () => readonly Display[] | undefined;
@@ -0,0 +1,7 @@
1
+ import { useDomicile } from "./domicile-provider";
2
+ import { useWatch } from "./use-watch";
3
+ /** The screens, or `undefined` until the compositor describes the desktop. */
4
+ export const useDisplays = () => {
5
+ const { displays } = useDomicile();
6
+ return useWatch(displays, "change", () => displays.list);
7
+ };
@@ -0,0 +1,12 @@
1
+ import type { AppDomicile } from "@domicile-desktop/sdk/desktop/app-desktop";
2
+ import type { Domicile } from "@domicile-desktop/sdk/desktop/desktop";
3
+ import type { ReactNode } from "react";
4
+ /** Hands `domicile` to every hook below. `mount` renders one for you. */
5
+ export declare const DomicileProvider: ({ children, domicile, }: {
6
+ children: ReactNode;
7
+ domicile: Domicile | AppDomicile;
8
+ }) => import("react").JSX.Element;
9
+ /** A shell's desktop, from the provider above. Throws under an app's. */
10
+ export declare const useDomicile: () => Domicile;
11
+ /** The desktop above as an app has it: a shell's has all of it too. */
12
+ export declare const useAppDomicile: () => AppDomicile;
@@ -0,0 +1,28 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { createContext, useContext } from "react";
3
+ const DomicileContext = createContext(undefined);
4
+ /** Hands `domicile` to every hook below. `mount` renders one for you. */
5
+ export const DomicileProvider = ({ children, domicile, }) => _jsx(DomicileContext, { value: domicile, children: children });
6
+ /** A shell's desktop, from the provider above. Throws under an app's. */
7
+ export const useDomicile = () => {
8
+ const domicile = useProvided("useDomicile");
9
+ if (isShells(domicile)) {
10
+ return domicile;
11
+ }
12
+ else {
13
+ throw new Error("useDomicile: the desktop above is an app's; use useAppDomicile");
14
+ }
15
+ };
16
+ /** The desktop above as an app has it: a shell's has all of it too. */
17
+ export const useAppDomicile = () => useProvided("useAppDomicile");
18
+ const useProvided = (hook) => {
19
+ const domicile = useContext(DomicileContext);
20
+ if (domicile === undefined) {
21
+ throw new Error(`${hook}: no <DomicileProvider> above`);
22
+ }
23
+ else {
24
+ return domicile;
25
+ }
26
+ };
27
+ // Only a shell's desktop manages windows.
28
+ const isShells = (domicile) => "windows" in domicile;
@@ -0,0 +1,15 @@
1
+ import type { ReactNode } from "react";
2
+ import { Component } from "react";
3
+ type Props = {
4
+ children: ReactNode;
5
+ };
6
+ type State = {
7
+ error: unknown;
8
+ };
9
+ /** Shows the error a child throws while drawing, in place of the children. */
10
+ export declare class FailureBoundary extends Component<Props, State> {
11
+ state: State;
12
+ static getDerivedStateFromError(error: unknown): State;
13
+ render(): ReactNode;
14
+ }
15
+ export {};
@@ -0,0 +1,13 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Component } from "react";
3
+ /** Shows the error a child throws while drawing, in place of the children. */
4
+ export class FailureBoundary extends Component {
5
+ state = { error: undefined };
6
+ static getDerivedStateFromError(error) {
7
+ return { error };
8
+ }
9
+ render() {
10
+ const { error } = this.state;
11
+ return error === undefined ? (this.props.children) : (_jsxs("div", { role: "alert", children: [_jsx("p", { children: "Something went wrong, and this can't be shown." }), _jsx("pre", { children: error instanceof Error ? error.message : String(error) })] }));
12
+ }
13
+ }
@@ -0,0 +1,10 @@
1
+ import { FakeDomicileHost } from "@domicile-desktop/sdk/fake-host";
2
+ import type { ReactNode } from "react";
3
+ /**
4
+ * Hands the components below `fake`'s desktop, which the test sets state on
5
+ * and reads calls from. Without `fake`, a desktop no test drives.
6
+ */
7
+ export declare const FakeDomicile: ({ children, fake, }: {
8
+ children: ReactNode;
9
+ fake?: FakeDomicileHost | undefined;
10
+ }) => import("react").JSX.Element;
@@ -0,0 +1,19 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ // The desktop for a component's tests.
3
+ //
4
+ // ```tsx
5
+ // const fake = new FakeDomicileHost();
6
+ // render(<FakeDomicile fake={fake}><Taskbar /></FakeDomicile>);
7
+ // act(() => fake.appear("term", { title: "Terminal" }));
8
+ // ```
9
+ import { FakeDomicileHost } from "@domicile-desktop/sdk/fake-host";
10
+ import { useState } from "react";
11
+ import { DomicileProvider } from "./domicile-provider";
12
+ /**
13
+ * Hands the components below `fake`'s desktop, which the test sets state on
14
+ * and reads calls from. Without `fake`, a desktop no test drives.
15
+ */
16
+ export const FakeDomicile = ({ children, fake, }) => {
17
+ const [own] = useState(() => new FakeDomicileHost());
18
+ return (_jsx(DomicileProvider, { domicile: (fake ?? own).domicile, children: children }));
19
+ };
@@ -0,0 +1,12 @@
1
+ import type { AppDomicile } from "@domicile-desktop/sdk/desktop/app-desktop";
2
+ import type { Domicile } from "@domicile-desktop/sdk/desktop/desktop";
3
+ import type { ComponentType } from "react";
4
+ /**
5
+ * A `Shell` or `main` that draws `App` with the desktop it is handed, in
6
+ * strict mode, showing an error in its place if it throws.
7
+ *
8
+ * ```ts
9
+ * export const Shell: ShellModule = mount(Desktop);
10
+ * ```
11
+ */
12
+ export declare const mount: (App: ComponentType) => (root: HTMLElement, domicile: Domicile | AppDomicile) => void;
package/dist/mount.js ADDED
@@ -0,0 +1,27 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { StrictMode } from "react";
3
+ import { flushSync } from "react-dom";
4
+ import { createRoot } from "react-dom/client";
5
+ import { DomicileProvider } from "./domicile-provider";
6
+ import { FailureBoundary } from "./failure-boundary";
7
+ /**
8
+ * A `Shell` or `main` that draws `App` with the desktop it is handed, in
9
+ * strict mode, showing an error in its place if it throws.
10
+ *
11
+ * ```ts
12
+ * export const Shell: ShellModule = mount(Desktop);
13
+ * ```
14
+ */
15
+ export const mount = (App) => (root, domicile) => {
16
+ // An element of its own: the page reports failures by appending to
17
+ // `root`, and a React root there would erase them. It draws no box, so
18
+ // the app sizes against `root` and nothing is positioned against it.
19
+ const element = root.ownerDocument.createElement("div");
20
+ element.style.display = "contents";
21
+ root.append(element);
22
+ const reactRoot = createRoot(element);
23
+ // Synchronously: the page draws nothing until `Shell` returns.
24
+ flushSync(() => {
25
+ reactRoot.render(_jsx(StrictMode, { children: _jsx(FailureBoundary, { children: _jsx(DomicileProvider, { domicile: domicile, children: _jsx(App, {}) }) }) }));
26
+ });
27
+ };
@@ -0,0 +1,7 @@
1
+ import type { Domicile } from "@domicile-desktop/sdk/desktop/desktop";
2
+ /**
3
+ * `make`, run once per desktop: every caller with the same `domicile` gets the
4
+ * same value. For watches and stores that every bar, panel and the lock
5
+ * screen share without a provider passing them down.
6
+ */
7
+ export declare const perDesktop: <T>(make: (domicile: Domicile) => T) => ((domicile: Domicile) => T);
@@ -0,0 +1,19 @@
1
+ /**
2
+ * `make`, run once per desktop: every caller with the same `domicile` gets the
3
+ * same value. For watches and stores that every bar, panel and the lock
4
+ * screen share without a provider passing them down.
5
+ */
6
+ export const perDesktop = (make) => {
7
+ const made = new WeakMap();
8
+ return (domicile) => {
9
+ const known = made.get(domicile);
10
+ if (known === undefined) {
11
+ const value = make(domicile);
12
+ made.set(domicile, value);
13
+ return value;
14
+ }
15
+ else {
16
+ return known;
17
+ }
18
+ };
19
+ };
@@ -0,0 +1,9 @@
1
+ import type { BoundShortcut, Capturing, PortalRequest, PortalWallpaper } from "@domicile-desktop/sdk/portal";
2
+ /** Every unanswered portal request, oldest first. */
3
+ export declare const usePortalRequests: () => readonly PortalRequest[] | undefined;
4
+ /** Every session that controls or captures input. */
5
+ export declare const useCapturing: () => readonly Capturing[] | undefined;
6
+ /** Every chord applications hold through the GlobalShortcuts portal. */
7
+ export declare const useBoundShortcuts: () => readonly BoundShortcut[] | undefined;
8
+ /** The pictures applications set through the Wallpaper portal. */
9
+ export declare const usePortalWallpaper: () => PortalWallpaper | undefined;
@@ -0,0 +1,23 @@
1
+ import { useDomicile } from "./domicile-provider";
2
+ import { useWatch } from "./use-watch";
3
+ // Each is `undefined` until the compositor's first push.
4
+ /** Every unanswered portal request, oldest first. */
5
+ export const usePortalRequests = () => {
6
+ const { portals } = useDomicile();
7
+ return useWatch(portals, "change", () => portals.requests);
8
+ };
9
+ /** Every session that controls or captures input. */
10
+ export const useCapturing = () => {
11
+ const { portals } = useDomicile();
12
+ return useWatch(portals, "change", () => portals.capturing);
13
+ };
14
+ /** Every chord applications hold through the GlobalShortcuts portal. */
15
+ export const useBoundShortcuts = () => {
16
+ const { portals } = useDomicile();
17
+ return useWatch(portals, "change", () => portals.boundShortcuts);
18
+ };
19
+ /** The pictures applications set through the Wallpaper portal. */
20
+ export const usePortalWallpaper = () => {
21
+ const { portals } = useDomicile();
22
+ return useWatch(portals, "change", () => portals.wallpaper);
23
+ };
@@ -0,0 +1,4 @@
1
+ /** Whether the user stepped away, or `undefined` until the compositor says. */
2
+ export declare const useIdle: () => boolean | undefined;
3
+ /** Whether the desk is locked, or `undefined` until the compositor says. */
4
+ export declare const useLocked: () => boolean | undefined;
@@ -0,0 +1,12 @@
1
+ import { useDomicile } from "./domicile-provider";
2
+ import { useWatch } from "./use-watch";
3
+ /** Whether the user stepped away, or `undefined` until the compositor says. */
4
+ export const useIdle = () => {
5
+ const { session } = useDomicile();
6
+ return useWatch(session, "idlechange", () => session.idle);
7
+ };
8
+ /** Whether the desk is locked, or `undefined` until the compositor says. */
9
+ export const useLocked = () => {
10
+ const { session } = useDomicile();
11
+ return useWatch(session, "lockchange", () => session.locked);
12
+ };
@@ -0,0 +1,15 @@
1
+ /** One watch that many components read, for `useSyncExternalStore`. */
2
+ export type SharedWatch<T> = {
3
+ /** Starts the watch for the first subscriber and stops it after the last. */
4
+ subscribe: (onChange: () => void) => () => void;
5
+ /** The latest value, or `undefined` before the first and while stopped. */
6
+ current: () => T | undefined;
7
+ };
8
+ /**
9
+ * Shares the watch `start` begins among every subscriber, so a library's
10
+ * watch runs once however many bars show it.
11
+ *
12
+ * `start` calls `onValue` with each value and returns what stops it. Failures
13
+ * stay `start`'s to handle, as values or logs.
14
+ */
15
+ export declare const sharedWatch: <T>(start: (onValue: (value: T) => void) => () => void) => SharedWatch<T>;
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Shares the watch `start` begins among every subscriber, so a library's
3
+ * watch runs once however many bars show it.
4
+ *
5
+ * `start` calls `onValue` with each value and returns what stops it. Failures
6
+ * stay `start`'s to handle, as values or logs.
7
+ */
8
+ export const sharedWatch = (start) => {
9
+ const listeners = new Set();
10
+ const state = { running: undefined };
11
+ return {
12
+ current: () => state.running?.value,
13
+ subscribe: (onChange) => {
14
+ listeners.add(onChange);
15
+ if (state.running === undefined) {
16
+ state.running = run(start, listeners);
17
+ }
18
+ return () => {
19
+ listeners.delete(onChange);
20
+ if (listeners.size === 0) {
21
+ state.running?.stop();
22
+ state.running = undefined;
23
+ }
24
+ };
25
+ },
26
+ };
27
+ };
28
+ const run = (start, listeners) => {
29
+ const running = { value: undefined };
30
+ const stop = start((value) => {
31
+ running.value = value;
32
+ for (const onChange of listeners) {
33
+ onChange();
34
+ }
35
+ });
36
+ return Object.assign(running, { stop });
37
+ };
@@ -0,0 +1,9 @@
1
+ import type { Domicile } from "@domicile-desktop/sdk/desktop/desktop";
2
+ import type { SharedWatch } from "./shared-watch";
3
+ /** The latest value of `watch`, or `undefined` before the first. */
4
+ export declare const useSharedWatch: <T>(watch: SharedWatch<T>) => T | undefined;
5
+ /**
6
+ * The latest value of the desktop's `desk` watch, or `undefined` before the
7
+ * first. `desk` is a `perDesktop`, so every caller shares one watch.
8
+ */
9
+ export declare const useDeskWatch: <T>(desk: (domicile: Domicile) => SharedWatch<T>) => T | undefined;
@@ -0,0 +1,9 @@
1
+ import { useSyncExternalStore } from "react";
2
+ import { useDomicile } from "./domicile-provider";
3
+ /** The latest value of `watch`, or `undefined` before the first. */
4
+ export const useSharedWatch = (watch) => useSyncExternalStore(watch.subscribe, watch.current);
5
+ /**
6
+ * The latest value of the desktop's `desk` watch, or `undefined` before the
7
+ * first. `desk` is a `perDesktop`, so every caller shares one watch.
8
+ */
9
+ export const useDeskWatch = (desk) => useSharedWatch(desk(useDomicile()));
@@ -0,0 +1,12 @@
1
+ import type { Unsubscribe } from "@domicile-desktop/sdk/desktop/subscribe";
2
+ /** A namespace of the desktop, or anything with its `on`. */
3
+ export type Watchable<K extends string> = {
4
+ on: (type: K, listener: () => void) => Unsubscribe;
5
+ };
6
+ /**
7
+ * What `read` answers, read again on each `type` event of `source`.
8
+ *
9
+ * `read` must answer the same value until the event: the desktop's lists keep
10
+ * their identity between changes, so reading an attribute does.
11
+ */
12
+ export declare const useWatch: <K extends string, T>(source: Watchable<K>, type: NoInfer<K>, read: () => T) => T;
@@ -0,0 +1,13 @@
1
+ // The one way a hook here follows the desktop: read an attribute again each
2
+ // time its namespace says it changed.
3
+ import { useCallback, useSyncExternalStore } from "react";
4
+ /**
5
+ * What `read` answers, read again on each `type` event of `source`.
6
+ *
7
+ * `read` must answer the same value until the event: the desktop's lists keep
8
+ * their identity between changes, so reading an attribute does.
9
+ */
10
+ export const useWatch = (source, type, read) => {
11
+ const subscribe = useCallback((onChange) => source.on(type, onChange), [source, type]);
12
+ return useSyncExternalStore(subscribe, read);
13
+ };
@@ -0,0 +1,15 @@
1
+ import type { Result } from "@cprussin/option-result";
2
+ import type { SystemError } from "@domicile-desktop/sdk/system";
3
+ /**
4
+ * Calls `onPresent` with `read` now and whenever it changes, and returns a
5
+ * function that stops watching. For whether hardware exists, read
6
+ * from `/sys`, which works while the desktop is locked.
7
+ */
8
+ export declare const watchPresence: (read: () => Promise<Result<boolean, SystemError>>, onPresent: (present: Result<boolean, SystemError>) => void, every?: typeof everyInterval) => (() => void);
9
+ /**
10
+ * Whether hardware is absent: not looked for yet, or
11
+ * not there. A failed look shows the item, since the hardware may be there.
12
+ */
13
+ export declare const isAbsent: (present: Result<boolean, SystemError> | undefined) => boolean;
14
+ declare const everyInterval: (ms: number, tick: () => void) => (() => void);
15
+ export {};
@@ -0,0 +1,48 @@
1
+ /** How often to look again: a device plugged in or out. */
2
+ const EVERY_MS = 10_000;
3
+ /**
4
+ * Calls `onPresent` with `read` now and whenever it changes, and returns a
5
+ * function that stops watching. For whether hardware exists, read
6
+ * from `/sys`, which works while the desktop is locked.
7
+ */
8
+ export const watchPresence = (read, onPresent, every = everyInterval) => {
9
+ const watch = {
10
+ shown: undefined,
11
+ stopped: false,
12
+ };
13
+ const look = () => {
14
+ read()
15
+ .then((present) => {
16
+ const shown = JSON.stringify(present.match({
17
+ Err: (error) => error,
18
+ Ok: (found) => found,
19
+ }));
20
+ if (!watch.stopped && shown !== watch.shown) {
21
+ watch.shown = shown;
22
+ onPresent(present);
23
+ }
24
+ })
25
+ .catch((error) => {
26
+ // biome-ignore lint/suspicious/noConsole: surfacing a background failure; the next look reads again
27
+ console.error("Failed to look for hardware", error);
28
+ });
29
+ };
30
+ look();
31
+ const stopClock = every(EVERY_MS, look);
32
+ return () => {
33
+ watch.stopped = true;
34
+ stopClock();
35
+ };
36
+ };
37
+ /**
38
+ * Whether hardware is absent: not looked for yet, or
39
+ * not there. A failed look shows the item, since the hardware may be there.
40
+ */
41
+ export const isAbsent = (present) => present === undefined ||
42
+ present.match({ Err: () => false, Ok: (has) => !has });
43
+ const everyInterval = (ms, tick) => {
44
+ const id = setInterval(tick, ms);
45
+ return () => {
46
+ clearInterval(id);
47
+ };
48
+ };
@@ -0,0 +1,5 @@
1
+ import type { ClientWindow } from "@domicile-desktop/sdk/desktop/windows";
2
+ /** Every client window, in the order they appeared. */
3
+ export declare const useWindows: () => readonly ClientWindow[];
4
+ /** The window holding the keyboard, or `undefined` while the shell holds it. */
5
+ export declare const useFocusedWindow: () => string | undefined;
@@ -0,0 +1,12 @@
1
+ import { useDomicile } from "./domicile-provider";
2
+ import { useWatch } from "./use-watch";
3
+ /** Every client window, in the order they appeared. */
4
+ export const useWindows = () => {
5
+ const { windows } = useDomicile();
6
+ return useWatch(windows, "change", () => windows.list);
7
+ };
8
+ /** The window holding the keyboard, or `undefined` while the shell holds it. */
9
+ export const useFocusedWindow = () => {
10
+ const { windows } = useDomicile();
11
+ return useWatch(windows, "focuschange", () => windows.focused);
12
+ };
package/package.json CHANGED
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "dependencies": {
3
- "@domicile-desktop/sdk": "0.0.0-alpha-2edc3c14d846"
3
+ "@cprussin/option-result": "^2.0.1",
4
+ "@domicile-desktop/sdk": "0.0.0-alpha-b85d9bb6d4af"
4
5
  },
5
6
  "description": "React for a Domicile shell or app: mount it on the desktop, read the desktop from context, and follow its state with hooks.",
6
7
  "devDependencies": {
@@ -39,6 +40,10 @@
39
40
  "types": "./dist/mount.d.ts",
40
41
  "default": "./dist/mount.js"
41
42
  },
43
+ "./per-desktop": {
44
+ "types": "./dist/per-desktop.d.ts",
45
+ "default": "./dist/per-desktop.js"
46
+ },
42
47
  "./portals": {
43
48
  "types": "./dist/portals.d.ts",
44
49
  "default": "./dist/portals.js"
@@ -47,10 +52,22 @@
47
52
  "types": "./dist/session.d.ts",
48
53
  "default": "./dist/session.js"
49
54
  },
55
+ "./shared-watch": {
56
+ "types": "./dist/shared-watch.d.ts",
57
+ "default": "./dist/shared-watch.js"
58
+ },
59
+ "./use-shared-watch": {
60
+ "types": "./dist/use-shared-watch.d.ts",
61
+ "default": "./dist/use-shared-watch.js"
62
+ },
50
63
  "./use-watch": {
51
64
  "types": "./dist/use-watch.d.ts",
52
65
  "default": "./dist/use-watch.js"
53
66
  },
67
+ "./watch-presence": {
68
+ "types": "./dist/watch-presence.d.ts",
69
+ "default": "./dist/watch-presence.js"
70
+ },
54
71
  "./windows": {
55
72
  "types": "./dist/windows.d.ts",
56
73
  "default": "./dist/windows.js"
@@ -75,7 +92,7 @@
75
92
  "test:unit": "bun test"
76
93
  },
77
94
  "type": "module",
78
- "version": "0.0.0-alpha-2edc3c14d846",
95
+ "version": "0.0.0-alpha-b85d9bb6d4af",
79
96
  "repository": {
80
97
  "type": "git",
81
98
  "url": "git+https://github.com/cprussin/domicile.git",