@justin06lee/subaru 0.1.0
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/dist/card-BFhpWbA5.d.ts +32 -0
- package/dist/chunk-CPOOJ2JD.js +213 -0
- package/dist/chunk-CPOOJ2JD.js.map +1 -0
- package/dist/chunk-K7IY5EW6.js +138 -0
- package/dist/chunk-K7IY5EW6.js.map +1 -0
- package/dist/chunk-MI26ZEPP.js +92 -0
- package/dist/chunk-MI26ZEPP.js.map +1 -0
- package/dist/chunk-POOH3Z7G.js +178 -0
- package/dist/chunk-POOH3Z7G.js.map +1 -0
- package/dist/chunk-SRQ3JWJK.js +261 -0
- package/dist/chunk-SRQ3JWJK.js.map +1 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +59 -0
- package/dist/cli.js.map +1 -0
- package/dist/core-DmEjLKLp.d.ts +104 -0
- package/dist/electron-renderer.d.ts +34 -0
- package/dist/electron-renderer.js +81 -0
- package/dist/electron-renderer.js.map +1 -0
- package/dist/electron.d.ts +239 -0
- package/dist/electron.js +428 -0
- package/dist/electron.js.map +1 -0
- package/dist/github-Ds4DWDq9.d.ts +37 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.js +49 -0
- package/dist/index.js.map +1 -0
- package/dist/react.d.ts +42 -0
- package/dist/react.js +109 -0
- package/dist/react.js.map +1 -0
- package/dist/source.d.ts +57 -0
- package/dist/source.js +11 -0
- package/dist/source.js.map +1 -0
- package/dist/styles-zfa0Pc0l.d.ts +47 -0
- package/dist/tauri.d.ts +71 -0
- package/dist/tauri.js +109 -0
- package/dist/tauri.js.map +1 -0
- package/dist/web.d.ts +59 -0
- package/dist/web.js +79 -0
- package/dist/web.js.map +1 -0
- package/package.json +120 -0
package/dist/react.d.ts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { ReactNode, CSSProperties } from 'react';
|
|
3
|
+
import { a as UpdaterLike, U as UpdaterState } from './core-DmEjLKLp.js';
|
|
4
|
+
import { d as CardStyleOptions } from './styles-zfa0Pc0l.js';
|
|
5
|
+
|
|
6
|
+
interface UseUpdater extends UpdaterState {
|
|
7
|
+
check(): Promise<void>;
|
|
8
|
+
install(): Promise<void>;
|
|
9
|
+
restart(): Promise<void>;
|
|
10
|
+
skip(): Promise<void>;
|
|
11
|
+
dismiss(): void;
|
|
12
|
+
}
|
|
13
|
+
/** Subscribe a component to an updater. Works with the core updater and the Electron renderer proxy alike. */
|
|
14
|
+
declare function useUpdater(updater: UpdaterLike): UseUpdater;
|
|
15
|
+
interface UpdatePromptLabels {
|
|
16
|
+
available: (version: string, name: string) => ReactNode;
|
|
17
|
+
installing: (progress: number | null) => ReactNode;
|
|
18
|
+
ready: (kind: UpdaterState['kind']) => ReactNode;
|
|
19
|
+
error: (message: string) => ReactNode;
|
|
20
|
+
update: ReactNode;
|
|
21
|
+
restart: ReactNode;
|
|
22
|
+
skip: ReactNode;
|
|
23
|
+
later: ReactNode;
|
|
24
|
+
retry: ReactNode;
|
|
25
|
+
notes: ReactNode;
|
|
26
|
+
}
|
|
27
|
+
interface UpdatePromptProps extends CardStyleOptions {
|
|
28
|
+
updater: UpdaterLike;
|
|
29
|
+
/** Program name shown in the notice. */
|
|
30
|
+
name?: string;
|
|
31
|
+
labels?: Partial<UpdatePromptLabels>;
|
|
32
|
+
style?: CSSProperties;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* The "a new version is available" card. Renders nothing until there is
|
|
36
|
+
* something to say. Under the prompt policy it offers Update / Skip / Later;
|
|
37
|
+
* under notify (or for a skipped release) it only tells; while installing it
|
|
38
|
+
* shows progress; when an update is ready it offers a restart.
|
|
39
|
+
*/
|
|
40
|
+
declare function UpdatePrompt({ updater, name, labels: custom, unstyled, className, style, theme, position, accent, vars }: UpdatePromptProps): react.JSX.Element | null;
|
|
41
|
+
|
|
42
|
+
export { UpdatePrompt, type UpdatePromptLabels, type UpdatePromptProps, type UseUpdater, useUpdater };
|
package/dist/react.js
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import {
|
|
2
|
+
cardVars,
|
|
3
|
+
injectStyles
|
|
4
|
+
} from "./chunk-POOH3Z7G.js";
|
|
5
|
+
|
|
6
|
+
// src/react.tsx
|
|
7
|
+
import { useEffect, useState, useSyncExternalStore } from "react";
|
|
8
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
9
|
+
function useUpdater(updater) {
|
|
10
|
+
const state = useSyncExternalStore(updater.subscribe, updater.getState, updater.getState);
|
|
11
|
+
return {
|
|
12
|
+
...state,
|
|
13
|
+
check: () => updater.check(),
|
|
14
|
+
install: () => updater.install(),
|
|
15
|
+
restart: () => updater.restart(),
|
|
16
|
+
skip: () => updater.skip(),
|
|
17
|
+
dismiss: () => updater.dismiss()
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
var defaultLabels = {
|
|
21
|
+
available: (version, name) => /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
22
|
+
name ? `${name} ` : "",
|
|
23
|
+
version,
|
|
24
|
+
" is available."
|
|
25
|
+
] }),
|
|
26
|
+
installing: (progress) => progress === null ? "Updating\u2026" : `Updating\u2026 ${Math.round(progress * 100)}%`,
|
|
27
|
+
ready: (kind) => kind === "web" ? "New version ready. It loads the next time you come back." : "Update ready. Restart to finish.",
|
|
28
|
+
error: (message) => `Update failed: ${message}`,
|
|
29
|
+
update: "Update now",
|
|
30
|
+
restart: "Restart now",
|
|
31
|
+
skip: "Skip this version",
|
|
32
|
+
later: "Later",
|
|
33
|
+
retry: "Try again",
|
|
34
|
+
notes: "What changed"
|
|
35
|
+
};
|
|
36
|
+
function UpdatePrompt({ updater, name = "", labels: custom, unstyled, className, style, theme = "auto", position = "bottom-right", accent, vars }) {
|
|
37
|
+
const u = useUpdater(updater);
|
|
38
|
+
const labels = { ...defaultLabels, ...custom };
|
|
39
|
+
const [showNotes, setShowNotes] = useState(false);
|
|
40
|
+
useEffect(() => {
|
|
41
|
+
if (!unstyled) injectStyles();
|
|
42
|
+
}, [unstyled]);
|
|
43
|
+
if (u.dismissed) return null;
|
|
44
|
+
const version = u.release?.version ?? u.release?.tag ?? "";
|
|
45
|
+
const cls = ["subaru", `subaru-${u.status}`, className].filter(Boolean).join(" ");
|
|
46
|
+
const busy = u.status === "installing";
|
|
47
|
+
let body = null;
|
|
48
|
+
let actions = null;
|
|
49
|
+
switch (u.status) {
|
|
50
|
+
case "available": {
|
|
51
|
+
body = labels.available(version, name);
|
|
52
|
+
const passive = u.policy === "notify" || u.skipped;
|
|
53
|
+
actions = passive ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
54
|
+
u.release?.url && /* @__PURE__ */ jsx("a", { className: "subaru-link", href: u.release.url, target: "_blank", rel: "noreferrer", children: labels.notes }),
|
|
55
|
+
/* @__PURE__ */ jsx("button", { type: "button", className: "subaru-button subaru-secondary", onClick: u.dismiss, children: labels.later })
|
|
56
|
+
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
57
|
+
/* @__PURE__ */ jsx("button", { type: "button", className: "subaru-button subaru-primary", onClick: () => void u.install(), children: labels.update }),
|
|
58
|
+
/* @__PURE__ */ jsx("button", { type: "button", className: "subaru-button subaru-secondary", onClick: () => void u.skip(), children: labels.skip }),
|
|
59
|
+
/* @__PURE__ */ jsx("button", { type: "button", className: "subaru-button subaru-secondary", onClick: u.dismiss, children: labels.later })
|
|
60
|
+
] });
|
|
61
|
+
break;
|
|
62
|
+
}
|
|
63
|
+
case "installing":
|
|
64
|
+
body = labels.installing(u.progress);
|
|
65
|
+
break;
|
|
66
|
+
case "ready":
|
|
67
|
+
body = labels.ready(u.kind);
|
|
68
|
+
actions = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
69
|
+
/* @__PURE__ */ jsx("button", { type: "button", className: "subaru-button subaru-primary", onClick: () => void u.restart(), children: labels.restart }),
|
|
70
|
+
/* @__PURE__ */ jsx("button", { type: "button", className: "subaru-button subaru-secondary", onClick: u.dismiss, children: labels.later })
|
|
71
|
+
] });
|
|
72
|
+
break;
|
|
73
|
+
case "error":
|
|
74
|
+
body = labels.error(u.error ?? "");
|
|
75
|
+
actions = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
76
|
+
/* @__PURE__ */ jsx("button", { type: "button", className: "subaru-button subaru-primary", onClick: () => void u.install(), children: labels.retry }),
|
|
77
|
+
/* @__PURE__ */ jsx("button", { type: "button", className: "subaru-button subaru-secondary", onClick: u.dismiss, children: labels.later })
|
|
78
|
+
] });
|
|
79
|
+
break;
|
|
80
|
+
default:
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
return /* @__PURE__ */ jsxs(
|
|
84
|
+
"div",
|
|
85
|
+
{
|
|
86
|
+
className: cls,
|
|
87
|
+
style: { ...cardVars({ accent, vars }), ...style },
|
|
88
|
+
"data-subaru-theme": theme,
|
|
89
|
+
"data-subaru-position": position,
|
|
90
|
+
role: "status",
|
|
91
|
+
"aria-live": "polite",
|
|
92
|
+
"aria-busy": busy,
|
|
93
|
+
children: [
|
|
94
|
+
/* @__PURE__ */ jsxs("div", { className: "subaru-body", children: [
|
|
95
|
+
/* @__PURE__ */ jsx("span", { className: "subaru-text", children: body }),
|
|
96
|
+
u.status === "available" && u.release?.notes && /* @__PURE__ */ jsx("button", { type: "button", className: "subaru-notes-toggle", onClick: () => setShowNotes((s) => !s), "aria-expanded": showNotes, children: labels.notes }),
|
|
97
|
+
showNotes && u.release?.notes && /* @__PURE__ */ jsx("pre", { className: "subaru-notes", children: u.release.notes }),
|
|
98
|
+
busy && /* @__PURE__ */ jsx("div", { className: u.progress === null ? "subaru-bar subaru-bar-indeterminate" : "subaru-bar", "aria-hidden": "true", children: /* @__PURE__ */ jsx("div", { className: "subaru-bar-fill", style: u.progress === null ? void 0 : { width: `${Math.round(u.progress * 100)}%` } }) })
|
|
99
|
+
] }),
|
|
100
|
+
actions && /* @__PURE__ */ jsx("div", { className: "subaru-actions", children: actions })
|
|
101
|
+
]
|
|
102
|
+
}
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
export {
|
|
106
|
+
UpdatePrompt,
|
|
107
|
+
useUpdater
|
|
108
|
+
};
|
|
109
|
+
//# sourceMappingURL=react.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/react.tsx"],"sourcesContent":["import { useEffect, useState, useSyncExternalStore, type CSSProperties, type ReactNode } from 'react';\nimport type { UpdaterLike, UpdaterState } from './core';\nimport { cardVars, injectStyles, type CardStyleOptions } from './styles';\n\nexport interface UseUpdater extends UpdaterState {\n check(): Promise<void>;\n install(): Promise<void>;\n restart(): Promise<void>;\n skip(): Promise<void>;\n dismiss(): void;\n}\n\n/** Subscribe a component to an updater. Works with the core updater and the Electron renderer proxy alike. */\nexport function useUpdater(updater: UpdaterLike): UseUpdater {\n const state = useSyncExternalStore(updater.subscribe, updater.getState, updater.getState);\n return {\n ...state,\n check: () => updater.check(),\n install: () => updater.install(),\n restart: () => updater.restart(),\n skip: () => updater.skip(),\n dismiss: () => updater.dismiss(),\n };\n}\n\nexport interface UpdatePromptLabels {\n available: (version: string, name: string) => ReactNode;\n installing: (progress: number | null) => ReactNode;\n ready: (kind: UpdaterState['kind']) => ReactNode;\n error: (message: string) => ReactNode;\n update: ReactNode;\n restart: ReactNode;\n skip: ReactNode;\n later: ReactNode;\n retry: ReactNode;\n notes: ReactNode;\n}\n\nconst defaultLabels: UpdatePromptLabels = {\n available: (version, name) => (\n <>\n {name ? `${name} ` : ''}\n {version} is available.\n </>\n ),\n installing: (progress) => (progress === null ? 'Updating…' : `Updating… ${Math.round(progress * 100)}%`),\n ready: (kind) => (kind === 'web' ? 'New version ready. It loads the next time you come back.' : 'Update ready. Restart to finish.'),\n error: (message) => `Update failed: ${message}`,\n update: 'Update now',\n restart: 'Restart now',\n skip: 'Skip this version',\n later: 'Later',\n retry: 'Try again',\n notes: 'What changed',\n};\n\nexport interface UpdatePromptProps extends CardStyleOptions {\n updater: UpdaterLike;\n /** Program name shown in the notice. */\n name?: string;\n labels?: Partial<UpdatePromptLabels>;\n style?: CSSProperties;\n}\n\n/**\n * The \"a new version is available\" card. Renders nothing until there is\n * something to say. Under the prompt policy it offers Update / Skip / Later;\n * under notify (or for a skipped release) it only tells; while installing it\n * shows progress; when an update is ready it offers a restart.\n */\nexport function UpdatePrompt({ updater, name = '', labels: custom, unstyled, className, style, theme = 'auto', position = 'bottom-right', accent, vars }: UpdatePromptProps) {\n const u = useUpdater(updater);\n const labels = { ...defaultLabels, ...custom };\n const [showNotes, setShowNotes] = useState(false);\n useEffect(() => {\n if (!unstyled) injectStyles();\n }, [unstyled]);\n\n if (u.dismissed) return null;\n const version = u.release?.version ?? u.release?.tag ?? '';\n const cls = ['subaru', `subaru-${u.status}`, className].filter(Boolean).join(' ');\n const busy = u.status === 'installing';\n\n let body: ReactNode = null;\n let actions: ReactNode = null;\n switch (u.status) {\n case 'available': {\n body = labels.available(version, name);\n const passive = u.policy === 'notify' || u.skipped;\n actions = passive ? (\n <>\n {u.release?.url && (\n <a className=\"subaru-link\" href={u.release.url} target=\"_blank\" rel=\"noreferrer\">\n {labels.notes}\n </a>\n )}\n <button type=\"button\" className=\"subaru-button subaru-secondary\" onClick={u.dismiss}>\n {labels.later}\n </button>\n </>\n ) : (\n <>\n <button type=\"button\" className=\"subaru-button subaru-primary\" onClick={() => void u.install()}>\n {labels.update}\n </button>\n <button type=\"button\" className=\"subaru-button subaru-secondary\" onClick={() => void u.skip()}>\n {labels.skip}\n </button>\n <button type=\"button\" className=\"subaru-button subaru-secondary\" onClick={u.dismiss}>\n {labels.later}\n </button>\n </>\n );\n break;\n }\n case 'installing':\n body = labels.installing(u.progress);\n break;\n case 'ready':\n body = labels.ready(u.kind);\n actions = (\n <>\n <button type=\"button\" className=\"subaru-button subaru-primary\" onClick={() => void u.restart()}>\n {labels.restart}\n </button>\n <button type=\"button\" className=\"subaru-button subaru-secondary\" onClick={u.dismiss}>\n {labels.later}\n </button>\n </>\n );\n break;\n case 'error':\n body = labels.error(u.error ?? '');\n actions = (\n <>\n <button type=\"button\" className=\"subaru-button subaru-primary\" onClick={() => void u.install()}>\n {labels.retry}\n </button>\n <button type=\"button\" className=\"subaru-button subaru-secondary\" onClick={u.dismiss}>\n {labels.later}\n </button>\n </>\n );\n break;\n default:\n return null;\n }\n\n return (\n <div\n className={cls}\n style={{ ...cardVars({ accent, vars }), ...style } as CSSProperties}\n data-subaru-theme={theme}\n data-subaru-position={position}\n role=\"status\"\n aria-live=\"polite\"\n aria-busy={busy}\n >\n <div className=\"subaru-body\">\n <span className=\"subaru-text\">{body}</span>\n {u.status === 'available' && u.release?.notes && (\n <button type=\"button\" className=\"subaru-notes-toggle\" onClick={() => setShowNotes((s) => !s)} aria-expanded={showNotes}>\n {labels.notes}\n </button>\n )}\n {showNotes && u.release?.notes && <pre className=\"subaru-notes\">{u.release.notes}</pre>}\n {busy && (\n <div className={u.progress === null ? 'subaru-bar subaru-bar-indeterminate' : 'subaru-bar'} aria-hidden=\"true\">\n <div className=\"subaru-bar-fill\" style={u.progress === null ? undefined : { width: `${Math.round(u.progress * 100)}%` }} />\n </div>\n )}\n </div>\n {actions && <div className=\"subaru-actions\">{actions}</div>}\n </div>\n );\n}\n"],"mappings":"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","names":[]}
|
package/dist/source.d.ts
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { A as Adapter } from './core-DmEjLKLp.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Source mode: the program updates from its own git checkout instead of from
|
|
5
|
+
* published bundles. For unsigned personal apps that a Makefile builds and
|
|
6
|
+
* copies into /Applications, this is the update path that actually works:
|
|
7
|
+
* fetch, fast-forward, `make update`, and the Makefile does what it already
|
|
8
|
+
* does (stop, delete, build, install, reset permissions, relaunch).
|
|
9
|
+
*
|
|
10
|
+
* The adapter runs no commands itself; it hands argv to a Runner. Electron's
|
|
11
|
+
* main process gets one from "@justin06lee/subaru/electron" (nodeRunner) and
|
|
12
|
+
* a Tauri window from "@justin06lee/subaru/tauri" (shellRunner).
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
interface RunRequest {
|
|
16
|
+
argv: string[];
|
|
17
|
+
cwd: string;
|
|
18
|
+
/** Start it and return immediately: the command is going to kill this program. */
|
|
19
|
+
detach?: boolean;
|
|
20
|
+
}
|
|
21
|
+
interface RunResult {
|
|
22
|
+
code: number;
|
|
23
|
+
output: string;
|
|
24
|
+
}
|
|
25
|
+
type Runner = (request: RunRequest) => Promise<RunResult>;
|
|
26
|
+
interface SourceAdapterOptions {
|
|
27
|
+
/** Absolute path of the git checkout the installed program was built from. */
|
|
28
|
+
checkout: string;
|
|
29
|
+
run: Runner;
|
|
30
|
+
/**
|
|
31
|
+
* "owner/name" or a full git URL. Only used to clone when `checkout` does
|
|
32
|
+
* not exist yet (a fresh machine). The checkout's own origin is what gets
|
|
33
|
+
* fetched otherwise.
|
|
34
|
+
*/
|
|
35
|
+
repo?: string;
|
|
36
|
+
/**
|
|
37
|
+
* What counts as an update. "branch": any new commit on the tracked
|
|
38
|
+
* upstream branch (every push is an update). "releases": only a newer tag.
|
|
39
|
+
* Default "branch".
|
|
40
|
+
*/
|
|
41
|
+
track?: 'branch' | 'releases';
|
|
42
|
+
/** Builds without installing; run in the background under the auto policy. Default ["make", "build"]. */
|
|
43
|
+
build?: string[];
|
|
44
|
+
/** Stops, rebuilds, reinstalls and relaunches the program. Default ["make", "update"]. */
|
|
45
|
+
update?: string[];
|
|
46
|
+
/** State key. Default: the checkout path. */
|
|
47
|
+
name?: string;
|
|
48
|
+
}
|
|
49
|
+
declare function sourceAdapter(options: SourceAdapterOptions): Adapter;
|
|
50
|
+
/**
|
|
51
|
+
* Directories a GUI app launched from Finder does not have on PATH but a
|
|
52
|
+
* Makefile almost always needs.
|
|
53
|
+
*/
|
|
54
|
+
declare const TOOL_DIRS: string[];
|
|
55
|
+
declare function pathWithTools(current: string | undefined, home: string | undefined): string;
|
|
56
|
+
|
|
57
|
+
export { type RunRequest, type RunResult, type Runner, type SourceAdapterOptions, TOOL_DIRS, pathWithTools, sourceAdapter };
|
package/dist/source.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The update card's default look, shared by the React and the vanilla card.
|
|
3
|
+
*
|
|
4
|
+
* Three things make it editable without forking. Every value is a CSS custom
|
|
5
|
+
* property on `.subaru`, so `--subaru-accent: rebeccapurple` is the whole
|
|
6
|
+
* edit. This stylesheet is inserted first in <head>, and its rules are plain
|
|
7
|
+
* one-class selectors, so a `.subaru { ... }` rule of your own comes later,
|
|
8
|
+
* wins on the tie, and needs no `!important` — while an app's bare
|
|
9
|
+
* `button { ... }` styles, being weaker than a class, cannot reach inside the
|
|
10
|
+
* card. And the theme and position hooks sit in `:where(...)`, keeping them
|
|
11
|
+
* at one class too, so overriding a token on `.subaru` beats them.
|
|
12
|
+
* `unstyled` drops the stylesheet altogether and leaves the class names.
|
|
13
|
+
*/
|
|
14
|
+
/** The tokens the card reads, without the `--subaru-` prefix. */
|
|
15
|
+
declare const CARD_TOKENS: readonly ["accent", "accent-text", "bg", "text", "muted", "border", "shadow", "fill", "fill-hover", "font", "radius", "button-radius", "padding", "gap", "width", "offset", "z"];
|
|
16
|
+
type CardToken = (typeof CARD_TOKENS)[number];
|
|
17
|
+
/** Where the card sits. */
|
|
18
|
+
declare const CARD_POSITIONS: readonly ["bottom-right", "bottom-left", "bottom-center", "top-right", "top-left", "top-center"];
|
|
19
|
+
type CardPosition = (typeof CARD_POSITIONS)[number];
|
|
20
|
+
/** 'auto' follows prefers-color-scheme. */
|
|
21
|
+
type CardTheme = 'auto' | 'dark' | 'light';
|
|
22
|
+
declare const CARD_CSS = "\n.subaru{\n\n --subaru-accent:#f5c542;\n --subaru-accent-text:#1b1400;\n --subaru-bg:#15171d;\n --subaru-text:#eef1f7;\n --subaru-muted:#a4abba;\n --subaru-border:rgba(255,255,255,.09);\n --subaru-shadow:0 14px 40px rgba(0,0,0,.5);\n --subaru-fill:rgba(255,255,255,.08);\n --subaru-fill-hover:rgba(255,255,255,.15);\n\n --subaru-font:500 14px/1.45 system-ui,-apple-system,\"Segoe UI\",sans-serif;\n --subaru-radius:14px;\n --subaru-button-radius:9px;\n --subaru-padding:14px;\n --subaru-gap:12px;\n --subaru-width:23rem;\n --subaru-offset:16px;\n --subaru-z:2147483000;\n --subaru-tx:0;\n --subaru-ty:8px;\n position:fixed;\n right:var(--subaru-offset);\n bottom:var(--subaru-offset);\n z-index:var(--subaru-z);\n box-sizing:border-box;\n width:min(var(--subaru-width),calc(100vw - var(--subaru-offset) * 2));\n padding:var(--subaru-padding);\n border:1px solid var(--subaru-border);\n border-radius:var(--subaru-radius);\n background:var(--subaru-bg);\n color:var(--subaru-text);\n font:var(--subaru-font);\n box-shadow:var(--subaru-shadow);\n display:flex;\n flex-direction:column;\n gap:var(--subaru-gap);\n /* Runs when the element enters the document. Both cards keep one element\n across state changes, so a progress tick never replays or cuts it. */\n animation:subaru-in .22s cubic-bezier(.2,.8,.3,1);\n}\n@media (prefers-color-scheme: light){.subaru:where([data-subaru-theme=\"auto\"]){\n --subaru-accent:#f5c542;\n --subaru-accent-text:#231a00;\n --subaru-bg:#fff;\n --subaru-text:#15171d;\n --subaru-muted:#61697a;\n --subaru-border:rgba(0,0,0,.08);\n --subaru-shadow:0 14px 40px rgba(0,0,0,.16);\n --subaru-fill:rgba(0,0,0,.055);\n --subaru-fill-hover:rgba(0,0,0,.1);\n}}\n.subaru:where([data-subaru-theme=\"light\"]){\n --subaru-accent:#f5c542;\n --subaru-accent-text:#231a00;\n --subaru-bg:#fff;\n --subaru-text:#15171d;\n --subaru-muted:#61697a;\n --subaru-border:rgba(0,0,0,.08);\n --subaru-shadow:0 14px 40px rgba(0,0,0,.16);\n --subaru-fill:rgba(0,0,0,.055);\n --subaru-fill-hover:rgba(0,0,0,.1);\n}\n.subaru:where([data-subaru-theme=\"dark\"]){\n --subaru-accent:#f5c542;\n --subaru-accent-text:#1b1400;\n --subaru-bg:#15171d;\n --subaru-text:#eef1f7;\n --subaru-muted:#a4abba;\n --subaru-border:rgba(255,255,255,.09);\n --subaru-shadow:0 14px 40px rgba(0,0,0,.5);\n --subaru-fill:rgba(255,255,255,.08);\n --subaru-fill-hover:rgba(255,255,255,.15);\n}\n.subaru:where([data-subaru-position^=\"top\"]){top:var(--subaru-offset);bottom:auto;--subaru-ty:-8px}\n.subaru:where([data-subaru-position$=\"left\"]){left:var(--subaru-offset);right:auto}\n.subaru:where([data-subaru-position$=\"center\"]){left:50%;right:auto;translate:-50% 0;--subaru-tx:-50%}\n@keyframes subaru-in{from{opacity:0;translate:var(--subaru-tx) var(--subaru-ty)}to{opacity:1;translate:var(--subaru-tx) 0}}\n\n.subaru-body{display:flex;flex-direction:column;gap:8px;min-width:0}\n.subaru-text{font:inherit;text-wrap:pretty;overflow-wrap:anywhere}\n/* An error can be a whole tool's output; keep the card a card. */\n.subaru-error .subaru-text{max-height:7.5rem;overflow:auto;overscroll-behavior:contain;white-space:pre-wrap}\n.subaru-actions{display:flex;flex-wrap:wrap;align-items:center;gap:8px}\n\n.subaru-button{\n appearance:none;\n margin:0;\n border:0;\n border-radius:var(--subaru-button-radius);\n padding:8px 13px;\n font:inherit;\n font-weight:600;\n line-height:1.2;\n color:inherit;\n background:var(--subaru-fill);\n cursor:pointer;\n transition:background .12s ease,filter .12s ease;\n}\n.subaru-button:hover{background:var(--subaru-fill-hover)}\n.subaru-button:active{filter:brightness(.94)}\n.subaru-primary{background:var(--subaru-accent);color:var(--subaru-accent-text)}\n.subaru-primary:hover{background:var(--subaru-accent);filter:brightness(1.07)}\n.subaru :focus-visible{outline:2px solid var(--subaru-accent);outline-offset:2px}\n\n.subaru-link{color:var(--subaru-muted);margin-right:auto;text-underline-offset:2px}\n.subaru-link:hover{color:var(--subaru-text)}\n.subaru-notes-toggle{\n appearance:none;\n align-self:flex-start;\n margin:0;\n border:0;\n padding:0;\n background:none;\n font:inherit;\n font-size:13px;\n font-weight:500;\n color:var(--subaru-muted);\n text-align:left;\n text-decoration:underline;\n text-underline-offset:2px;\n cursor:pointer;\n}\n.subaru-notes-toggle:hover{color:var(--subaru-text)}\n.subaru-notes{\n margin:0;\n max-height:9.5rem;\n overflow:auto;\n overscroll-behavior:contain;\n padding:9px 11px;\n border-radius:calc(var(--subaru-button-radius) - 1px);\n background:var(--subaru-fill);\n color:var(--subaru-muted);\n font:12px/1.5 ui-monospace,SFMono-Regular,Menlo,monospace;\n white-space:pre-wrap;\n}\n\n.subaru-bar{height:5px;border-radius:999px;background:var(--subaru-fill);overflow:hidden}\n.subaru-bar-fill{height:100%;border-radius:inherit;background:var(--subaru-accent);transition:width .2s ease}\n.subaru-bar-indeterminate .subaru-bar-fill{width:40%;animation:subaru-slide 1.3s ease-in-out infinite}\n@keyframes subaru-slide{from{translate:-105% 0}to{translate:265% 0}}\n\n@media (prefers-reduced-motion: reduce){\n .subaru,.subaru-bar-fill,.subaru-bar-indeterminate .subaru-bar-fill{animation:none;transition:none}\n}\n";
|
|
23
|
+
/** Add the default stylesheet to the document once. */
|
|
24
|
+
declare function injectStyles(): void;
|
|
25
|
+
/**
|
|
26
|
+
* Token overrides for one card: `{ accent: '#7c5cff', radius: '4px' }`, or
|
|
27
|
+
* any CSS custom property written out in full.
|
|
28
|
+
*/
|
|
29
|
+
type CardVars = Partial<Record<CardToken, string>> & Record<string, string>;
|
|
30
|
+
/** How the card looks, shared by <UpdatePrompt> and mountCard. */
|
|
31
|
+
interface CardStyleOptions {
|
|
32
|
+
/** Default 'auto' (follows prefers-color-scheme). */
|
|
33
|
+
theme?: CardTheme;
|
|
34
|
+
/** Default 'bottom-right'. */
|
|
35
|
+
position?: CardPosition;
|
|
36
|
+
/** Shorthand for vars: { accent }. */
|
|
37
|
+
accent?: string;
|
|
38
|
+
/** Token overrides: { radius: '4px', width: '26rem' }. */
|
|
39
|
+
vars?: CardVars;
|
|
40
|
+
/** Drop the built-in look and style the class names yourself. */
|
|
41
|
+
unstyled?: boolean;
|
|
42
|
+
className?: string;
|
|
43
|
+
}
|
|
44
|
+
/** The inline custom properties a card carries, from accent and vars. */
|
|
45
|
+
declare function cardVars(options: Pick<CardStyleOptions, 'accent' | 'vars'>): Record<string, string>;
|
|
46
|
+
|
|
47
|
+
export { CARD_CSS as C, CARD_POSITIONS as a, CARD_TOKENS as b, type CardPosition as c, type CardStyleOptions as d, type CardTheme as e, type CardToken as f, type CardVars as g, cardVars as h, injectStyles as i };
|
package/dist/tauri.d.ts
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { check } from '@tauri-apps/plugin-updater';
|
|
2
|
+
import { relaunch } from '@tauri-apps/plugin-process';
|
|
3
|
+
import { P as Policy, b as Updater, A as Adapter } from './core-DmEjLKLp.js';
|
|
4
|
+
import { M as MountCardOptions } from './card-BFhpWbA5.js';
|
|
5
|
+
import { SourceAdapterOptions, Runner } from './source.js';
|
|
6
|
+
import './styles-zfa0Pc0l.js';
|
|
7
|
+
|
|
8
|
+
interface TauriAdapterOptions {
|
|
9
|
+
/** Passed through to the updater plugin's check(). */
|
|
10
|
+
timeout?: number;
|
|
11
|
+
headers?: HeadersInit;
|
|
12
|
+
/** Injection points for tests. */
|
|
13
|
+
check?: typeof check;
|
|
14
|
+
relaunch?: typeof relaunch;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Updates for a Tauri 2 app, on top of the official updater plugin: the plugin
|
|
18
|
+
* finds, verifies (minisign) and installs the bundle; subaru adds the policy,
|
|
19
|
+
* the throttle, the memory of skipped releases and the UI.
|
|
20
|
+
*
|
|
21
|
+
* Needs `tauri-plugin-updater` registered on the Rust side and a
|
|
22
|
+
* `plugins.updater` block in tauri.conf.json. The release workflow in
|
|
23
|
+
* example/release-tauri.yml publishes the latest.json it points at.
|
|
24
|
+
*/
|
|
25
|
+
declare function tauriAdapter(options?: TauriAdapterOptions): Adapter;
|
|
26
|
+
interface ShellRunnerOptions {
|
|
27
|
+
/** Where a detached `make update` writes its output. Default /tmp/subaru-update.log. */
|
|
28
|
+
log?: string;
|
|
29
|
+
/** The name of the allowed program in the app's shell capability. Default "sh". */
|
|
30
|
+
program?: string;
|
|
31
|
+
/** Injection point for tests. */
|
|
32
|
+
execute?: (program: string, args: string[], cwd: string) => Promise<{
|
|
33
|
+
code: number | null;
|
|
34
|
+
stdout: string;
|
|
35
|
+
stderr: string;
|
|
36
|
+
}>;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* A Runner on the Tauri shell plugin for sourceAdapter (source mode). Every
|
|
40
|
+
* request becomes one `sh -c` invocation, so the capability only has to allow
|
|
41
|
+
* that:
|
|
42
|
+
*
|
|
43
|
+
* { "identifier": "shell:allow-execute",
|
|
44
|
+
* "allow": [{ "name": "sh", "cmd": "sh", "args": ["-c", { "validator": "[\\s\\S]+" }] }] }
|
|
45
|
+
*
|
|
46
|
+
* Detached requests are backgrounded with nohup and logged, so `make update`
|
|
47
|
+
* survives the app it is about to quit.
|
|
48
|
+
*/
|
|
49
|
+
declare function shellRunner(options?: ShellRunnerOptions): Runner;
|
|
50
|
+
interface TauriAutoOptions extends TauriAdapterOptions {
|
|
51
|
+
policy?: Policy;
|
|
52
|
+
name?: string;
|
|
53
|
+
ui?: boolean | MountCardOptions;
|
|
54
|
+
debug?: (message: string) => void;
|
|
55
|
+
/** Source mode instead of the updater plugin: the app's git checkout, fast-forwarded and rebuilt with make. */
|
|
56
|
+
checkout?: string;
|
|
57
|
+
source?: Omit<SourceAdapterOptions, 'checkout' | 'run'> & {
|
|
58
|
+
run?: Runner;
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* The whole integration in one call. Release mode (default) uses the
|
|
63
|
+
* updater plugin; passing `checkout` switches to source mode via the shell
|
|
64
|
+
* plugin. Mounts the card and starts checking.
|
|
65
|
+
*
|
|
66
|
+
* import { subaru } from '@justin06lee/subaru/tauri';
|
|
67
|
+
* subaru({ name: 'reze' });
|
|
68
|
+
*/
|
|
69
|
+
declare function subaru(options?: TauriAutoOptions): Updater;
|
|
70
|
+
|
|
71
|
+
export { type ShellRunnerOptions, type TauriAdapterOptions, type TauriAutoOptions, shellRunner, subaru, tauriAdapter };
|
package/dist/tauri.js
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import {
|
|
2
|
+
mountCard
|
|
3
|
+
} from "./chunk-K7IY5EW6.js";
|
|
4
|
+
import {
|
|
5
|
+
createUpdater,
|
|
6
|
+
webStorage
|
|
7
|
+
} from "./chunk-SRQ3JWJK.js";
|
|
8
|
+
import {
|
|
9
|
+
TOOL_DIRS,
|
|
10
|
+
sourceAdapter
|
|
11
|
+
} from "./chunk-MI26ZEPP.js";
|
|
12
|
+
import "./chunk-POOH3Z7G.js";
|
|
13
|
+
|
|
14
|
+
// src/tauri.ts
|
|
15
|
+
import { check as pluginCheck } from "@tauri-apps/plugin-updater";
|
|
16
|
+
import { relaunch as pluginRelaunch } from "@tauri-apps/plugin-process";
|
|
17
|
+
import { Command } from "@tauri-apps/plugin-shell";
|
|
18
|
+
function tauriAdapter(options = {}) {
|
|
19
|
+
const check = options.check ?? pluginCheck;
|
|
20
|
+
const relaunch = options.relaunch ?? pluginRelaunch;
|
|
21
|
+
let held = null;
|
|
22
|
+
async function hold(update) {
|
|
23
|
+
if (held && held !== update) await held.close().catch(() => {
|
|
24
|
+
});
|
|
25
|
+
held = update;
|
|
26
|
+
}
|
|
27
|
+
return {
|
|
28
|
+
kind: "tauri",
|
|
29
|
+
name: "tauri",
|
|
30
|
+
storage: webStorage(),
|
|
31
|
+
async check(signal) {
|
|
32
|
+
const timeout = options.timeout ?? (signal ? void 0 : void 0);
|
|
33
|
+
const update = await check({ timeout, headers: options.headers });
|
|
34
|
+
if (!update || !update.available) {
|
|
35
|
+
await hold(null);
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
await hold(update);
|
|
39
|
+
return {
|
|
40
|
+
tag: update.version,
|
|
41
|
+
notes: update.body,
|
|
42
|
+
date: update.date
|
|
43
|
+
};
|
|
44
|
+
},
|
|
45
|
+
async install(release, { relaunch: doRelaunch, onProgress }) {
|
|
46
|
+
let update = held;
|
|
47
|
+
if (!update || update.version !== release.tag) {
|
|
48
|
+
update = await check({ timeout: options.timeout, headers: options.headers });
|
|
49
|
+
if (!update || !update.available) throw new Error("release is no longer available");
|
|
50
|
+
await hold(update);
|
|
51
|
+
}
|
|
52
|
+
let total;
|
|
53
|
+
let got = 0;
|
|
54
|
+
await update.download((event) => {
|
|
55
|
+
if (event.event === "Started") {
|
|
56
|
+
total = event.data.contentLength;
|
|
57
|
+
onProgress(total ? 0 : null);
|
|
58
|
+
} else if (event.event === "Progress") {
|
|
59
|
+
got += event.data.chunkLength;
|
|
60
|
+
onProgress(total ? Math.min(got / total, 1) : null);
|
|
61
|
+
} else {
|
|
62
|
+
onProgress(1);
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
await update.install();
|
|
66
|
+
if (doRelaunch) {
|
|
67
|
+
await relaunch();
|
|
68
|
+
return "restarting";
|
|
69
|
+
}
|
|
70
|
+
return "ready";
|
|
71
|
+
},
|
|
72
|
+
async restart() {
|
|
73
|
+
await relaunch();
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
function shellRunner(options = {}) {
|
|
78
|
+
const program = options.program ?? "sh";
|
|
79
|
+
const log = options.log ?? "/tmp/subaru-update.log";
|
|
80
|
+
const execute = options.execute ?? (async (prog, args, cwd) => {
|
|
81
|
+
const out = await Command.create(prog, args, { cwd }).execute();
|
|
82
|
+
return { code: out.code, stdout: out.stdout, stderr: out.stderr };
|
|
83
|
+
});
|
|
84
|
+
const pathLine = `export PATH="${TOOL_DIRS.map((d) => d.replace(/^~/, "$HOME")).join(":")}:$PATH"`;
|
|
85
|
+
return async ({ argv, cwd, detach }) => {
|
|
86
|
+
const command = argv.map(shellQuote).join(" ");
|
|
87
|
+
const script = detach ? `${pathLine}; cd ${shellQuote(cwd)} && { printf '\\n=== %s %s ===\\n' "$(date)" ${shellQuote(command)} >> ${shellQuote(log)}; nohup sh -c ${shellQuote(command)} >> ${shellQuote(log)} 2>&1 & } && echo "started; output in ${log}"` : `${pathLine}; cd ${shellQuote(cwd)} && ${command} 2>&1`;
|
|
88
|
+
const out = await execute(program, ["-c", script], cwd);
|
|
89
|
+
return { code: out.code ?? 1, output: out.stdout + out.stderr };
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
function shellQuote(s) {
|
|
93
|
+
return `'${s.replace(/'/g, `'\\''`)}'`;
|
|
94
|
+
}
|
|
95
|
+
function subaru(options = {}) {
|
|
96
|
+
const adapter = options.checkout ? sourceAdapter({ checkout: options.checkout, run: options.source?.run ?? shellRunner(), ...options.source }) : tauriAdapter(options);
|
|
97
|
+
const updater = createUpdater({ adapter, policy: options.policy, debug: options.debug, storage: webStorage() });
|
|
98
|
+
if (options.ui !== false && typeof document !== "undefined") {
|
|
99
|
+
mountCard(updater, { name: options.name, ...typeof options.ui === "object" ? options.ui : {} });
|
|
100
|
+
}
|
|
101
|
+
updater.start();
|
|
102
|
+
return updater;
|
|
103
|
+
}
|
|
104
|
+
export {
|
|
105
|
+
shellRunner,
|
|
106
|
+
subaru,
|
|
107
|
+
tauriAdapter
|
|
108
|
+
};
|
|
109
|
+
//# sourceMappingURL=tauri.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/tauri.ts"],"sourcesContent":["import { check as pluginCheck, type Update } from '@tauri-apps/plugin-updater';\nimport { relaunch as pluginRelaunch } from '@tauri-apps/plugin-process';\nimport { Command } from '@tauri-apps/plugin-shell';\nimport { createUpdater, webStorage, type Adapter, type Policy, type Updater } from './core';\nimport { mountCard, type MountCardOptions } from './card';\nimport { sourceAdapter, TOOL_DIRS, type Runner, type SourceAdapterOptions } from './source';\n\nexport interface TauriAdapterOptions {\n /** Passed through to the updater plugin's check(). */\n timeout?: number;\n headers?: HeadersInit;\n /** Injection points for tests. */\n check?: typeof pluginCheck;\n relaunch?: typeof pluginRelaunch;\n}\n\n/**\n * Updates for a Tauri 2 app, on top of the official updater plugin: the plugin\n * finds, verifies (minisign) and installs the bundle; subaru adds the policy,\n * the throttle, the memory of skipped releases and the UI.\n *\n * Needs `tauri-plugin-updater` registered on the Rust side and a\n * `plugins.updater` block in tauri.conf.json. The release workflow in\n * example/release-tauri.yml publishes the latest.json it points at.\n */\nexport function tauriAdapter(options: TauriAdapterOptions = {}): Adapter {\n const check = options.check ?? pluginCheck;\n const relaunch = options.relaunch ?? pluginRelaunch;\n let held: Update | null = null;\n\n async function hold(update: Update | null) {\n if (held && held !== update) await held.close().catch(() => {});\n held = update;\n }\n\n return {\n kind: 'tauri',\n name: 'tauri',\n storage: webStorage(),\n async check(signal) {\n const timeout = options.timeout ?? (signal ? undefined : undefined);\n const update = await check({ timeout, headers: options.headers });\n if (!update || !update.available) {\n await hold(null);\n return null;\n }\n await hold(update);\n return {\n tag: update.version,\n notes: update.body,\n date: update.date,\n };\n },\n async install(release, { relaunch: doRelaunch, onProgress }) {\n let update = held;\n if (!update || update.version !== release.tag) {\n update = await check({ timeout: options.timeout, headers: options.headers });\n if (!update || !update.available) throw new Error('release is no longer available');\n await hold(update);\n }\n let total: number | undefined;\n let got = 0;\n await update.download((event) => {\n if (event.event === 'Started') {\n total = event.data.contentLength;\n onProgress(total ? 0 : null);\n } else if (event.event === 'Progress') {\n got += event.data.chunkLength;\n onProgress(total ? Math.min(got / total, 1) : null);\n } else {\n onProgress(1);\n }\n });\n await update.install();\n if (doRelaunch) {\n await relaunch();\n return 'restarting';\n }\n return 'ready';\n },\n async restart() {\n await relaunch();\n },\n };\n}\n\nexport interface ShellRunnerOptions {\n /** Where a detached `make update` writes its output. Default /tmp/subaru-update.log. */\n log?: string;\n /** The name of the allowed program in the app's shell capability. Default \"sh\". */\n program?: string;\n /** Injection point for tests. */\n execute?: (program: string, args: string[], cwd: string) => Promise<{ code: number | null; stdout: string; stderr: string }>;\n}\n\n/**\n * A Runner on the Tauri shell plugin for sourceAdapter (source mode). Every\n * request becomes one `sh -c` invocation, so the capability only has to allow\n * that:\n *\n * { \"identifier\": \"shell:allow-execute\",\n * \"allow\": [{ \"name\": \"sh\", \"cmd\": \"sh\", \"args\": [\"-c\", { \"validator\": \"[\\\\s\\\\S]+\" }] }] }\n *\n * Detached requests are backgrounded with nohup and logged, so `make update`\n * survives the app it is about to quit.\n */\nexport function shellRunner(options: ShellRunnerOptions = {}): Runner {\n const program = options.program ?? 'sh';\n const log = options.log ?? '/tmp/subaru-update.log';\n const execute =\n options.execute ??\n (async (prog: string, args: string[], cwd: string) => {\n const out = await Command.create(prog, args, { cwd }).execute();\n return { code: out.code, stdout: out.stdout, stderr: out.stderr };\n });\n const pathLine = `export PATH=\"${TOOL_DIRS.map((d) => d.replace(/^~/, '$HOME')).join(':')}:$PATH\"`;\n return async ({ argv, cwd, detach }) => {\n const command = argv.map(shellQuote).join(' ');\n const script = detach\n ? `${pathLine}; cd ${shellQuote(cwd)} && { printf '\\\\n=== %s %s ===\\\\n' \"$(date)\" ${shellQuote(command)} >> ${shellQuote(log)}; nohup sh -c ${shellQuote(command)} >> ${shellQuote(log)} 2>&1 & } && echo \"started; output in ${log}\"`\n : `${pathLine}; cd ${shellQuote(cwd)} && ${command} 2>&1`;\n const out = await execute(program, ['-c', script], cwd);\n return { code: out.code ?? 1, output: out.stdout + out.stderr };\n };\n}\n\nfunction shellQuote(s: string): string {\n return `'${s.replace(/'/g, `'\\\\''`)}'`;\n}\n\nexport interface TauriAutoOptions extends TauriAdapterOptions {\n policy?: Policy;\n name?: string;\n ui?: boolean | MountCardOptions;\n debug?: (message: string) => void;\n /** Source mode instead of the updater plugin: the app's git checkout, fast-forwarded and rebuilt with make. */\n checkout?: string;\n source?: Omit<SourceAdapterOptions, 'checkout' | 'run'> & { run?: Runner };\n}\n\n/**\n * The whole integration in one call. Release mode (default) uses the\n * updater plugin; passing `checkout` switches to source mode via the shell\n * plugin. Mounts the card and starts checking.\n *\n * import { subaru } from '@justin06lee/subaru/tauri';\n * subaru({ name: 'reze' });\n */\nexport function subaru(options: TauriAutoOptions = {}): Updater {\n const adapter = options.checkout\n ? sourceAdapter({ checkout: options.checkout, run: options.source?.run ?? shellRunner(), ...options.source })\n : tauriAdapter(options);\n const updater = createUpdater({ adapter, policy: options.policy, debug: options.debug, storage: webStorage() });\n if (options.ui !== false && typeof document !== 'undefined') {\n mountCard(updater, { name: options.name, ...(typeof options.ui === 'object' ? options.ui : {}) });\n }\n updater.start();\n return updater;\n}\n"],"mappings":"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","names":[]}
|
package/dist/web.d.ts
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { P as Policy, b as Updater, A as Adapter } from './core-DmEjLKLp.js';
|
|
2
|
+
import { M as MountCardOptions } from './card-BFhpWbA5.js';
|
|
3
|
+
import './styles-zfa0Pc0l.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* The stamp file `subaru stamp` writes at build time and this adapter polls.
|
|
7
|
+
* Any change to `id` is a new deploy.
|
|
8
|
+
*/
|
|
9
|
+
interface Stamp {
|
|
10
|
+
id: string;
|
|
11
|
+
version?: string;
|
|
12
|
+
commit?: string;
|
|
13
|
+
builtAt?: string;
|
|
14
|
+
notes?: string;
|
|
15
|
+
url?: string;
|
|
16
|
+
}
|
|
17
|
+
interface WebAdapterOptions {
|
|
18
|
+
/** Where the stamp is served. Default "/subaru.json". */
|
|
19
|
+
url?: string;
|
|
20
|
+
/** Minimum time between polls. Default 5 minutes. */
|
|
21
|
+
interval?: number;
|
|
22
|
+
/**
|
|
23
|
+
* The id of the build the page was loaded with. Defaults to a
|
|
24
|
+
* <meta name="subaru-version" content="..."> tag when present, otherwise to
|
|
25
|
+
* whatever the first poll returns.
|
|
26
|
+
*/
|
|
27
|
+
current?: string;
|
|
28
|
+
/** Injection points for tests. */
|
|
29
|
+
fetch?: (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
|
|
30
|
+
reload?: () => void;
|
|
31
|
+
document?: Document;
|
|
32
|
+
window?: Window;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Updates for a web app: a new deploy is detected by polling a tiny JSON stamp
|
|
36
|
+
* and applied by reloading the page. "Install and restart" reloads now;
|
|
37
|
+
* "apply, take effect later" reloads the moment the tab is hidden, so a
|
|
38
|
+
* person mid-typing never loses the page under their hands.
|
|
39
|
+
*/
|
|
40
|
+
declare function webAdapter(options?: WebAdapterOptions): Adapter;
|
|
41
|
+
interface WebAutoOptions extends WebAdapterOptions {
|
|
42
|
+
policy?: Policy;
|
|
43
|
+
/** Program name shown on the card. */
|
|
44
|
+
name?: string;
|
|
45
|
+
/** false: no card; bring your own UI with updater.subscribe or useUpdater. */
|
|
46
|
+
ui?: boolean | MountCardOptions;
|
|
47
|
+
debug?: (message: string) => void;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* The whole integration in one call: poll the deploy stamp, show the card
|
|
51
|
+
* when a new deploy lands, reload on "Update now". Returns the updater for
|
|
52
|
+
* anything more.
|
|
53
|
+
*
|
|
54
|
+
* import { subaru } from '@justin06lee/subaru/web';
|
|
55
|
+
* subaru({ name: 'hours' });
|
|
56
|
+
*/
|
|
57
|
+
declare function subaru(options?: WebAutoOptions): Updater;
|
|
58
|
+
|
|
59
|
+
export { type Stamp, type WebAdapterOptions, type WebAutoOptions, subaru, webAdapter };
|
package/dist/web.js
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import {
|
|
2
|
+
mountCard
|
|
3
|
+
} from "./chunk-K7IY5EW6.js";
|
|
4
|
+
import {
|
|
5
|
+
createUpdater,
|
|
6
|
+
webStorage
|
|
7
|
+
} from "./chunk-SRQ3JWJK.js";
|
|
8
|
+
import "./chunk-POOH3Z7G.js";
|
|
9
|
+
|
|
10
|
+
// src/web.ts
|
|
11
|
+
function webAdapter(options = {}) {
|
|
12
|
+
const url = options.url ?? "/subaru.json";
|
|
13
|
+
const doFetch = options.fetch ?? ((input, init) => fetch(input, init));
|
|
14
|
+
const doc = options.document ?? (typeof document !== "undefined" ? document : void 0);
|
|
15
|
+
const win = options.window ?? (typeof window !== "undefined" ? window : void 0);
|
|
16
|
+
const reload = options.reload ?? (() => win?.location.reload());
|
|
17
|
+
let current = options.current ?? metaVersion(doc);
|
|
18
|
+
let pendingReload = false;
|
|
19
|
+
return {
|
|
20
|
+
kind: "web",
|
|
21
|
+
name: `web:${typeof location !== "undefined" ? location.host : ""}${url}`,
|
|
22
|
+
interval: options.interval ?? 5 * 60 * 1e3,
|
|
23
|
+
storage: webStorage(),
|
|
24
|
+
async check(signal) {
|
|
25
|
+
const res = await doFetch(url, { cache: "no-store", signal, headers: { accept: "application/json" } });
|
|
26
|
+
if (!res.ok) throw new Error(`${url}: ${res.status}`);
|
|
27
|
+
const stamp = await res.json();
|
|
28
|
+
if (!stamp || typeof stamp.id !== "string" || !stamp.id) throw new Error(`${url}: no id`);
|
|
29
|
+
if (current === void 0) {
|
|
30
|
+
current = stamp.id;
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
if (stamp.id === current) return null;
|
|
34
|
+
const release = { tag: stamp.id };
|
|
35
|
+
if (stamp.version) release.version = stamp.version;
|
|
36
|
+
if (stamp.notes) release.notes = stamp.notes;
|
|
37
|
+
if (stamp.url) release.url = stamp.url;
|
|
38
|
+
if (stamp.builtAt) release.date = stamp.builtAt;
|
|
39
|
+
return release;
|
|
40
|
+
},
|
|
41
|
+
async install(_release, { relaunch }) {
|
|
42
|
+
if (relaunch || !doc || doc.visibilityState === "hidden") {
|
|
43
|
+
reload();
|
|
44
|
+
return "restarting";
|
|
45
|
+
}
|
|
46
|
+
if (!pendingReload) {
|
|
47
|
+
pendingReload = true;
|
|
48
|
+
const onHide = () => {
|
|
49
|
+
if (doc.visibilityState === "hidden") {
|
|
50
|
+
doc.removeEventListener("visibilitychange", onHide);
|
|
51
|
+
reload();
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
doc.addEventListener("visibilitychange", onHide);
|
|
55
|
+
}
|
|
56
|
+
return "ready";
|
|
57
|
+
},
|
|
58
|
+
async restart() {
|
|
59
|
+
reload();
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
function metaVersion(doc) {
|
|
64
|
+
const content = doc?.querySelector('meta[name="subaru-version"]')?.getAttribute("content");
|
|
65
|
+
return content || void 0;
|
|
66
|
+
}
|
|
67
|
+
function subaru(options = {}) {
|
|
68
|
+
const updater = createUpdater({ adapter: webAdapter(options), policy: options.policy, debug: options.debug });
|
|
69
|
+
if (options.ui !== false && typeof document !== "undefined") {
|
|
70
|
+
mountCard(updater, { name: options.name, ...typeof options.ui === "object" ? options.ui : {} });
|
|
71
|
+
}
|
|
72
|
+
updater.start();
|
|
73
|
+
return updater;
|
|
74
|
+
}
|
|
75
|
+
export {
|
|
76
|
+
subaru,
|
|
77
|
+
webAdapter
|
|
78
|
+
};
|
|
79
|
+
//# sourceMappingURL=web.js.map
|