selfstore 1.0.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/LICENSE +21 -0
- package/README.md +364 -0
- package/dist/advanced.d.ts +90 -0
- package/dist/advanced.js +41 -0
- package/dist/advanced.js.map +1 -0
- package/dist/backups.d.ts +182 -0
- package/dist/backups.js +360 -0
- package/dist/backups.js.map +1 -0
- package/dist/cache-De5_sALA.d.ts +129 -0
- package/dist/cache-lock-LZILZNLP.js +23 -0
- package/dist/cache-lock-LZILZNLP.js.map +1 -0
- package/dist/chunk-ATUPHVLG.js +441 -0
- package/dist/chunk-ATUPHVLG.js.map +1 -0
- package/dist/chunk-BJ3RUMGI.js +45 -0
- package/dist/chunk-BJ3RUMGI.js.map +1 -0
- package/dist/chunk-CUWB5EWF.js +732 -0
- package/dist/chunk-CUWB5EWF.js.map +1 -0
- package/dist/chunk-FDPAKHQS.js +230 -0
- package/dist/chunk-FDPAKHQS.js.map +1 -0
- package/dist/chunk-IBIEKK36.js +331 -0
- package/dist/chunk-IBIEKK36.js.map +1 -0
- package/dist/chunk-JB4JHM7V.js +474 -0
- package/dist/chunk-JB4JHM7V.js.map +1 -0
- package/dist/chunk-N4RPMX3Y.js +1908 -0
- package/dist/chunk-N4RPMX3Y.js.map +1 -0
- package/dist/chunk-OWLX6PNT.js +231 -0
- package/dist/chunk-OWLX6PNT.js.map +1 -0
- package/dist/chunk-QBCU7OOP.js +20 -0
- package/dist/chunk-QBCU7OOP.js.map +1 -0
- package/dist/chunk-SGISYMT5.js +46 -0
- package/dist/chunk-SGISYMT5.js.map +1 -0
- package/dist/chunk-VBFMMR7Q.js +29 -0
- package/dist/chunk-VBFMMR7Q.js.map +1 -0
- package/dist/drive-Bv94UM8b.d.ts +222 -0
- package/dist/flows.d.ts +306 -0
- package/dist/flows.js +153 -0
- package/dist/flows.js.map +1 -0
- package/dist/groups.d.ts +39 -0
- package/dist/groups.js +103 -0
- package/dist/groups.js.map +1 -0
- package/dist/households.d.ts +214 -0
- package/dist/households.js +410 -0
- package/dist/households.js.map +1 -0
- package/dist/index.d.ts +205 -0
- package/dist/index.js +249 -0
- package/dist/index.js.map +1 -0
- package/dist/kdf-worker.js +2395 -0
- package/dist/kdf-worker.js.map +1 -0
- package/dist/local-2py_vGGy.d.ts +47 -0
- package/dist/local-AM22RWRR.js +7 -0
- package/dist/local-AM22RWRR.js.map +1 -0
- package/dist/machine-ALMkT3mk.d.ts +17 -0
- package/dist/store-ChIGEOph.d.ts +577 -0
- package/dist/sync.d.ts +107 -0
- package/dist/sync.js +3 -0
- package/dist/sync.js.map +1 -0
- package/dist/widgets.d.ts +426 -0
- package/dist/widgets.js +2579 -0
- package/dist/widgets.js.map +1 -0
- package/llms.txt +405 -0
- package/package.json +99 -0
package/dist/sync.d.ts
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hybrid Logical Clock: wall time + a counter + a node id, giving a
|
|
3
|
+
* deterministic total order that tolerates clock skew. Encoded as a fixed-width
|
|
4
|
+
* string so a plain string comparison equals causal order.
|
|
5
|
+
*/
|
|
6
|
+
type Hlc = string;
|
|
7
|
+
/** A fresh random replica id (one per device). */
|
|
8
|
+
declare function createNode(): string;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Schema-agnostic convergent merge for plain JSON. It keeps a small sidecar of
|
|
12
|
+
* metadata (one logical clock per record) and reconciles two replica states with
|
|
13
|
+
* well-known set semantics. Documents stay plain objects: identity is a
|
|
14
|
+
* configured id field, change detection is by content hash, nothing is injected.
|
|
15
|
+
*
|
|
16
|
+
* Per-collection strategy:
|
|
17
|
+
* 'lww-set' records keyed by id; later write per id wins; deletes tombstoned.
|
|
18
|
+
* 'lww-map' records keyed by id, merged field BY field: concurrent edits
|
|
19
|
+
* to different fields of one record both survive; the same field
|
|
20
|
+
* goes to the later clock. Deletes stay record-level.
|
|
21
|
+
* 'grow-set' append-only; entries immutable; union, never a conflict.
|
|
22
|
+
* 'lww-register' a single value; later write wins.
|
|
23
|
+
* 'manual' surface concurrent edits to the same id instead of resolving.
|
|
24
|
+
*
|
|
25
|
+
* For collaborative rich text, store an Automerge/Yjs document as a binary file
|
|
26
|
+
* in the snapshot instead; this engine stops short of sequence CRDTs.
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
type Id = string;
|
|
30
|
+
type MergeStrategy = 'lww-set' | 'lww-map' | 'grow-set' | 'lww-register' | 'manual';
|
|
31
|
+
interface SyncConfig {
|
|
32
|
+
/** Field holding each record's stable id (default 'id'). Dot-notation allowed. */
|
|
33
|
+
idField?: string;
|
|
34
|
+
/** Per-collection id path override for records whose id is not at the top level. */
|
|
35
|
+
ids?: Record<string, string>;
|
|
36
|
+
/** Strategy per collection name; anything unlisted uses `fallback`. */
|
|
37
|
+
strategies?: Record<string, MergeStrategy>;
|
|
38
|
+
/** Strategy for unlisted collections (default 'lww-set'). */
|
|
39
|
+
fallback?: MergeStrategy;
|
|
40
|
+
}
|
|
41
|
+
/** Sidecar metadata for one collection, keyed by record id. */
|
|
42
|
+
interface ColMeta {
|
|
43
|
+
clocks: Record<Id, Hlc>;
|
|
44
|
+
deleted: Record<Id, Hlc>;
|
|
45
|
+
hashes: Record<Id, number>;
|
|
46
|
+
/** 'lww-map' only: per-field [clock, contentHash], so concurrent edits to
|
|
47
|
+
* different fields of one record merge instead of one side losing. */
|
|
48
|
+
fields?: Record<Id, Record<string, [Hlc, number]>>;
|
|
49
|
+
}
|
|
50
|
+
/** Everything one replica tracks. Travels with the data so the other side can merge. */
|
|
51
|
+
interface SyncMeta {
|
|
52
|
+
node: string;
|
|
53
|
+
clock: Hlc | null;
|
|
54
|
+
cols: Record<string, ColMeta>;
|
|
55
|
+
}
|
|
56
|
+
/** A same-id concurrent edit the 'manual' strategy refused to auto-resolve.
|
|
57
|
+
* `local`/`remote` carry the two full versions during the session; consumers
|
|
58
|
+
* persisting conflicts (e.g. the store's journal) redact them, so they are
|
|
59
|
+
* absent after a reload - never write user records to unencrypted storage. */
|
|
60
|
+
interface Conflict {
|
|
61
|
+
collection: string;
|
|
62
|
+
id: Id;
|
|
63
|
+
local?: unknown;
|
|
64
|
+
remote?: unknown;
|
|
65
|
+
/** The side kept as the provisional value (so it still converges). */
|
|
66
|
+
kept: 'local' | 'remote';
|
|
67
|
+
}
|
|
68
|
+
interface MergeResult {
|
|
69
|
+
collections: Record<string, unknown[]>;
|
|
70
|
+
meta: SyncMeta;
|
|
71
|
+
conflicts: Conflict[];
|
|
72
|
+
}
|
|
73
|
+
declare function createMeta(node?: string): SyncMeta;
|
|
74
|
+
declare function stamp(meta: SyncMeta, collections: Record<string, unknown[]>, config: SyncConfig, wallNow?: number): SyncMeta;
|
|
75
|
+
/** One replica's full state: the collections plus their sync metadata. What
|
|
76
|
+
* `merge` and `detectConflicts` take on each side. */
|
|
77
|
+
interface ReplicaState {
|
|
78
|
+
collections: Record<string, unknown[]>;
|
|
79
|
+
meta: SyncMeta;
|
|
80
|
+
}
|
|
81
|
+
type State = ReplicaState;
|
|
82
|
+
/**
|
|
83
|
+
* Merge replica `b` into `a` (commutative for the convergent strategies). The
|
|
84
|
+
* optional `base` lets 'manual' tell a true concurrent conflict from a one-sided
|
|
85
|
+
* change; without it, 'manual' errs toward surfacing.
|
|
86
|
+
*/
|
|
87
|
+
declare function merge(a: State, b: State, config: SyncConfig, base?: SyncMeta): MergeResult;
|
|
88
|
+
/**
|
|
89
|
+
* Concurrent same-id edits that a convergent strategy resolves silently by keeping
|
|
90
|
+
* the later clock. Informational only: it never changes what `merge` keeps, it
|
|
91
|
+
* reports which records both replicas changed since `base` (the loser's version is
|
|
92
|
+
* the one merge dropped). Empty without a base, since two-way state alone cannot
|
|
93
|
+
* tell a concurrent edit from a one-sided change.
|
|
94
|
+
*/
|
|
95
|
+
declare function detectConflicts(a: State, b: State, config: SyncConfig, base?: SyncMeta): Conflict[];
|
|
96
|
+
/** What a merge did to one collection, counted by record. */
|
|
97
|
+
interface CollectionChanges {
|
|
98
|
+
added: number;
|
|
99
|
+
updated: number;
|
|
100
|
+
removed: number;
|
|
101
|
+
}
|
|
102
|
+
/** Per-collection record counts of what changed between two states (same id
|
|
103
|
+
* resolution as the merge). Collections with no change are omitted, so an empty
|
|
104
|
+
* result means the states are equivalent. Feeds the sync journal. */
|
|
105
|
+
declare function changes(before: Record<string, unknown[]>, after: Record<string, unknown[]>, config: SyncConfig): Record<string, CollectionChanges>;
|
|
106
|
+
|
|
107
|
+
export { type ColMeta, type CollectionChanges, type Conflict, type Hlc, type Id, type MergeResult, type MergeStrategy, type ReplicaState, type SyncConfig, type SyncMeta, changes, createMeta, createNode, detectConflicts, merge, stamp };
|
package/dist/sync.js
ADDED
package/dist/sync.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"sync.js"}
|
|
@@ -0,0 +1,426 @@
|
|
|
1
|
+
import { FlowHost, ConnectTargets, ConnectFlowOptions, ConnectKind, ConnectFlow, ShareEngine, ShareLevel, ShareFlow, JoinEngine, JoinFlow, ReplicaFlow } from './flows.js';
|
|
2
|
+
import { BackupsManager } from './backups.js';
|
|
3
|
+
import './store-ChIGEOph.js';
|
|
4
|
+
import './cache-De5_sALA.js';
|
|
5
|
+
import './sync.js';
|
|
6
|
+
import './drive-Bv94UM8b.js';
|
|
7
|
+
import './machine-ALMkT3mk.js';
|
|
8
|
+
|
|
9
|
+
/** Flat key -> string map; widgets merge a partial override over EN defaults. */
|
|
10
|
+
type WidgetLabels = Record<string, string>;
|
|
11
|
+
/** The common shell: shadow root + styles, label merging, focus-preserving
|
|
12
|
+
* re-render, event emission, subscription cleanup. */
|
|
13
|
+
declare abstract class FlowWidget extends HTMLElement {
|
|
14
|
+
protected root: ShadowRoot;
|
|
15
|
+
private styleEl;
|
|
16
|
+
private body;
|
|
17
|
+
private overrides;
|
|
18
|
+
protected unsub: (() => void) | null;
|
|
19
|
+
/** The widget's EN default strings; subclasses provide theirs. */
|
|
20
|
+
protected abstract defaults(): WidgetLabels;
|
|
21
|
+
/** Build the current view into the given container. */
|
|
22
|
+
protected abstract view(into: HTMLElement): void;
|
|
23
|
+
constructor();
|
|
24
|
+
/** Partial override map, merged over the EN defaults (also the i18n hook). */
|
|
25
|
+
get labels(): WidgetLabels;
|
|
26
|
+
set labels(map: WidgetLabels);
|
|
27
|
+
protected t(key: string): string;
|
|
28
|
+
/** A heading an empty-string label removes: labels = { 'share.title': '' }
|
|
29
|
+
* hides that heading (the host page already says it) without a fork.
|
|
30
|
+
* Append through put(), which skips the null. */
|
|
31
|
+
protected heading(part: string, key: string): HTMLElement | null;
|
|
32
|
+
/** Map a flow error to copy: labels can override per labelKey
|
|
33
|
+
* ('error.targetUnavailable', ...), with a generic fallback. */
|
|
34
|
+
protected errorText(labelKey: string | undefined): string;
|
|
35
|
+
protected emit(name: string, detail?: unknown): void;
|
|
36
|
+
/** Rebuild the body, keeping the focused input's value and focus across the
|
|
37
|
+
* rebuild (inputs carry data-keep so typing survives a snapshot change). */
|
|
38
|
+
protected rerender(): void;
|
|
39
|
+
disconnectedCallback(): void;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* <selfstore-connect>: the "where does my data live" journey, ready to drop
|
|
44
|
+
* in. A thin skin over connectFlow - every ordering and failure rule lives in
|
|
45
|
+
* the flow (and its tests); this element only renders the snapshot and calls
|
|
46
|
+
* the actions. Theme it with --selfstore-* custom properties and ::part()
|
|
47
|
+
* selectors; reword or localize it through the `labels` property.
|
|
48
|
+
*
|
|
49
|
+
* import { defineSelfstoreWidgets } from 'selfstore/widgets';
|
|
50
|
+
* defineSelfstoreWidgets();
|
|
51
|
+
*
|
|
52
|
+
* const el = document.querySelector('selfstore-connect');
|
|
53
|
+
* el.store = store; // simple store (or a FlowHost)
|
|
54
|
+
* el.targets = { drive: gisDriveAuth({ clientId }), file: true };
|
|
55
|
+
* el.addEventListener('selfstore-connected', (e) => ...e.detail.outcome...);
|
|
56
|
+
*/
|
|
57
|
+
|
|
58
|
+
/** A named WebDAV provider offered in the connect form's quick-pick row, so a
|
|
59
|
+
* user can start from their host instead of a blank URL field. selfstore ships
|
|
60
|
+
* no list - the app supplies whichever providers it wants to surface (its own
|
|
61
|
+
* server, a sovereign host, ...); the widget just renders them and pre-fills the
|
|
62
|
+
* URL field on pick. Pure UI sugar over the same WebDAV form. */
|
|
63
|
+
interface WebdavPreset {
|
|
64
|
+
/** Stable id (also the `data-preset` attribute, for theming/tests). */
|
|
65
|
+
id: string;
|
|
66
|
+
/** Button text: the provider or host name. */
|
|
67
|
+
label: string;
|
|
68
|
+
/** Base server URL that pre-fills the WebDAV URL field when picked. Omit for a
|
|
69
|
+
* label-only entry (the user then types the full URL themselves). */
|
|
70
|
+
url?: string;
|
|
71
|
+
/** Optional guidance, shown as a line under the form once this preset is
|
|
72
|
+
* picked (e.g. where to find the app password for this host) and as the
|
|
73
|
+
* quick-pick button's tooltip. */
|
|
74
|
+
help?: string;
|
|
75
|
+
/** Optional link appended to the `help` line (e.g. a how-to page). */
|
|
76
|
+
helpUrl?: string;
|
|
77
|
+
/** Optional "create an account" link shown once this preset is picked, for a
|
|
78
|
+
* user who has no account with the host yet. The app owns the URL; selfstore
|
|
79
|
+
* hard-codes no provider. */
|
|
80
|
+
signupUrl?: string;
|
|
81
|
+
}
|
|
82
|
+
declare class SelfstoreConnectElement extends FlowWidget {
|
|
83
|
+
#private;
|
|
84
|
+
static get observedAttributes(): string[];
|
|
85
|
+
protected defaults(): WidgetLabels;
|
|
86
|
+
/** The simple store (anything exposing `flowHost`), or a hand-built FlowHost. */
|
|
87
|
+
get store(): FlowHost | {
|
|
88
|
+
flowHost: FlowHost;
|
|
89
|
+
} | null;
|
|
90
|
+
set store(v: FlowHost | {
|
|
91
|
+
flowHost: FlowHost;
|
|
92
|
+
} | null);
|
|
93
|
+
get targets(): ConnectTargets | null;
|
|
94
|
+
set targets(v: ConnectTargets | null);
|
|
95
|
+
get options(): ConnectFlowOptions;
|
|
96
|
+
set options(v: ConnectFlowOptions);
|
|
97
|
+
/** Badge one destination as the one to pick (part="tag", label
|
|
98
|
+
* 'connect.recommended'). Purely a highlight: the order still follows the
|
|
99
|
+
* targets object. Also the `recommended` attribute. Default: none. */
|
|
100
|
+
get recommended(): ConnectKind | null;
|
|
101
|
+
set recommended(v: ConnectKind | null);
|
|
102
|
+
/** An optional icon per destination - an image URL or data URI, rendered as
|
|
103
|
+
* part="icon" at the head of that card. Purely decorative and per-instance;
|
|
104
|
+
* omit a kind for no icon. Size it with --selfstore-icon-size. */
|
|
105
|
+
get icons(): Partial<Record<ConnectKind, string>>;
|
|
106
|
+
set icons(v: Partial<Record<ConnectKind, string>> | null);
|
|
107
|
+
/** Destinations to tuck behind a discreet link under the cards instead of a
|
|
108
|
+
* full card - for power-user options (say, WebDAV) that must stay reachable
|
|
109
|
+
* without weighing the everyday choice. Also the `advanced` attribute
|
|
110
|
+
* (comma-separated). The journey after the click is exactly the same. */
|
|
111
|
+
get advanced(): ConnectKind[];
|
|
112
|
+
set advanced(v: ConnectKind[] | null);
|
|
113
|
+
/** Named WebDAV providers offered as a quick-pick row above the WebDAV form,
|
|
114
|
+
* pre-filling the URL field. The app owns the list (its own host, sovereign
|
|
115
|
+
* providers, ...); selfstore ships none. Purely additive - the blank form
|
|
116
|
+
* still works. */
|
|
117
|
+
get webdavPresets(): WebdavPreset[];
|
|
118
|
+
set webdavPresets(v: WebdavPreset[] | null);
|
|
119
|
+
/** The underlying flow, for programmatic control (may be null until wired). */
|
|
120
|
+
get flow(): ConnectFlow | null;
|
|
121
|
+
attributeChangedCallback(name: string, _old: string | null, value: string | null): void;
|
|
122
|
+
connectedCallback(): void;
|
|
123
|
+
private wire;
|
|
124
|
+
protected view(into: HTMLElement): void;
|
|
125
|
+
private destCard;
|
|
126
|
+
/** The grouped "your own server" card shown when both WebDAV and S3 are
|
|
127
|
+
* offered: one entry into the tabbed form, opening on the first server kind. */
|
|
128
|
+
private serverCard;
|
|
129
|
+
/** WebDAV/S3 segmented toggle. Switching leaves the form and re-enters it on
|
|
130
|
+
* the other kind - no flow surgery - and the typed fields live on the element,
|
|
131
|
+
* so nothing the user wrote is lost across the switch. */
|
|
132
|
+
private serverTabs;
|
|
133
|
+
private webdavFormChildren;
|
|
134
|
+
private s3FormChildren;
|
|
135
|
+
private submitS3;
|
|
136
|
+
private cancel;
|
|
137
|
+
private submitPassword;
|
|
138
|
+
/** Flip the password field between hidden and shown IN PLACE - swap the
|
|
139
|
+
* input type and the eye glyph without a re-render, so what the user has
|
|
140
|
+
* already typed survives the toggle. */
|
|
141
|
+
private togglePassword;
|
|
142
|
+
/** Enter or leave the forgotten-password confirmation sub-state. */
|
|
143
|
+
private setConfirmOverwrite;
|
|
144
|
+
/** Confirmed: abandon the forgotten password and start a fresh backup over
|
|
145
|
+
* the protected one (the flow makes this device win). */
|
|
146
|
+
private doOverwrite;
|
|
147
|
+
/** Fill the WebDAV URL field from a picked provider preset and move focus on to
|
|
148
|
+
* the username, so the pick just seeds the form the user then completes. The
|
|
149
|
+
* pick is remembered so its guidance / sign-up link render and its chip flags
|
|
150
|
+
* as active; a re-render keeps the already-typed username and password. */
|
|
151
|
+
private pickWebdavPreset;
|
|
152
|
+
private submitWebdav;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/** A destructive action, offered to confirmAction before it runs: a row's
|
|
156
|
+
* revoke/remove, or 'stop' - ending the whole share. */
|
|
157
|
+
type ShareAction = {
|
|
158
|
+
type: 'revoke' | 'remove';
|
|
159
|
+
id: string;
|
|
160
|
+
} | {
|
|
161
|
+
type: 'stop';
|
|
162
|
+
};
|
|
163
|
+
declare class SelfstoreShareElement extends FlowWidget {
|
|
164
|
+
#private;
|
|
165
|
+
static get observedAttributes(): string[];
|
|
166
|
+
protected defaults(): WidgetLabels;
|
|
167
|
+
/** The app-side transport of links and memberships (see ShareEngine). */
|
|
168
|
+
get engine(): ShareEngine | null;
|
|
169
|
+
set engine(v: ShareEngine | null);
|
|
170
|
+
/** Flow options (deadlineMs), for engines whose legs outgrow the default
|
|
171
|
+
* 30s guard (a large first upload, a slow relay). */
|
|
172
|
+
get options(): {
|
|
173
|
+
deadlineMs?: number;
|
|
174
|
+
};
|
|
175
|
+
set options(v: {
|
|
176
|
+
deadlineMs?: number;
|
|
177
|
+
} | null);
|
|
178
|
+
/** Which levels the panel OFFERS to create (['read', 'write'] by default) -
|
|
179
|
+
* one create button per level without a live link. Also the `levels`
|
|
180
|
+
* attribute, comma-separated: levels="read". Links the engine lists
|
|
181
|
+
* render whatever this says. */
|
|
182
|
+
get levels(): ShareLevel[];
|
|
183
|
+
set levels(v: ShareLevel[]);
|
|
184
|
+
/** Render the create buttons (default). `with-create="off"` hides them: the
|
|
185
|
+
* panel becomes list-and-revoke only. */
|
|
186
|
+
get withCreate(): boolean;
|
|
187
|
+
set withCreate(v: boolean);
|
|
188
|
+
/** Render the members section (default). `with-members="off"` hides it:
|
|
189
|
+
* a links-only panel. */
|
|
190
|
+
get withMembers(): boolean;
|
|
191
|
+
set withMembers(v: boolean);
|
|
192
|
+
/** Host veto over the destructive actions: called before revoke, remove or
|
|
193
|
+
* stop runs; answer (or resolve) false to keep things as they are -
|
|
194
|
+
* window.confirm fits as-is. Default: everything proceeds. */
|
|
195
|
+
get confirmAction(): ((action: ShareAction) => boolean | Promise<boolean>) | null;
|
|
196
|
+
set confirmAction(v: ((action: ShareAction) => boolean | Promise<boolean>) | null);
|
|
197
|
+
/** Turn a link URL into an image source (a data URL fits): when set, each
|
|
198
|
+
* link card renders it as an img part="qr". Injected so the element ships
|
|
199
|
+
* no QR dependency. */
|
|
200
|
+
get qrProvider(): ((url: string) => Promise<string>) | null;
|
|
201
|
+
set qrProvider(v: ((url: string) => Promise<string>) | null);
|
|
202
|
+
get flow(): ShareFlow | null;
|
|
203
|
+
attributeChangedCallback(name: string, _old: string | null, value: string | null): void;
|
|
204
|
+
connectedCallback(): void;
|
|
205
|
+
private wire;
|
|
206
|
+
protected view(into: HTMLElement): void;
|
|
207
|
+
private create;
|
|
208
|
+
/** Run a destructive action through the host's veto (default: proceed).
|
|
209
|
+
* A throwing hook reads as "no": destruction needs a clear yes. */
|
|
210
|
+
private guarded;
|
|
211
|
+
private revoke;
|
|
212
|
+
private removeRow;
|
|
213
|
+
private stop;
|
|
214
|
+
private qrFor;
|
|
215
|
+
private copy;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* <selfstore-join>: opening an invitation link, ready to drop in. A skin over
|
|
220
|
+
* joinFlow: preview first, join on an explicit yes, named outcomes (already
|
|
221
|
+
* following another share / invite spent) instead of generic errors, and the
|
|
222
|
+
* account switch as part of the journey when the engine offers one.
|
|
223
|
+
*
|
|
224
|
+
* el.link = location.href; // or the link attribute
|
|
225
|
+
* el.engine = myJoinEngine;
|
|
226
|
+
* el.addEventListener('selfstore-joined', () => goHome());
|
|
227
|
+
*
|
|
228
|
+
* variant="banner" collapses the invitation card to a one-line row (label,
|
|
229
|
+
* join, account switch) for pages that render their own preview around it.
|
|
230
|
+
*/
|
|
231
|
+
|
|
232
|
+
declare class SelfstoreJoinElement extends FlowWidget {
|
|
233
|
+
#private;
|
|
234
|
+
static get observedAttributes(): string[];
|
|
235
|
+
protected defaults(): WidgetLabels;
|
|
236
|
+
get engine(): JoinEngine | null;
|
|
237
|
+
set engine(v: JoinEngine | null);
|
|
238
|
+
/** The invitation. Also settable as the `link` attribute. */
|
|
239
|
+
get link(): string | null;
|
|
240
|
+
set link(v: string | null);
|
|
241
|
+
/** Flow options (deadlineMs). Set it when the engine's join opens a popup:
|
|
242
|
+
* a generous deadline keeps the network guard without timing out a human
|
|
243
|
+
* who is reading an account chooser. */
|
|
244
|
+
get options(): {
|
|
245
|
+
deadlineMs?: number;
|
|
246
|
+
};
|
|
247
|
+
set options(v: {
|
|
248
|
+
deadlineMs?: number;
|
|
249
|
+
} | null);
|
|
250
|
+
/** 'card' (default): the full invitation card - who shared, what level.
|
|
251
|
+
* 'banner': one line - label, join, account switch - for a host page that
|
|
252
|
+
* renders its own preview around the widget. Also the `variant` attribute. */
|
|
253
|
+
get variant(): 'card' | 'banner';
|
|
254
|
+
set variant(v: 'card' | 'banner');
|
|
255
|
+
get flow(): JoinFlow | null;
|
|
256
|
+
attributeChangedCallback(name: string, _old: string | null, value: string | null): void;
|
|
257
|
+
connectedCallback(): void;
|
|
258
|
+
private wire;
|
|
259
|
+
protected view(into: HTMLElement): void;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
declare class SelfstoreStatusElement extends FlowWidget {
|
|
263
|
+
#private;
|
|
264
|
+
static get observedAttributes(): string[];
|
|
265
|
+
protected defaults(): WidgetLabels;
|
|
266
|
+
/** The simple store (anything exposing `flowHost`), or a hand-built FlowHost. */
|
|
267
|
+
get store(): FlowHost | {
|
|
268
|
+
flowHost: FlowHost;
|
|
269
|
+
} | null;
|
|
270
|
+
set store(v: FlowHost | {
|
|
271
|
+
flowHost: FlowHost;
|
|
272
|
+
} | null);
|
|
273
|
+
/** 'row' (dot + text + action) or 'dot' (the dot alone). Also the attribute. */
|
|
274
|
+
get variant(): 'row' | 'dot';
|
|
275
|
+
set variant(v: 'row' | 'dot' | null);
|
|
276
|
+
/** Optional icon per target kind (e.g. { drive: url, file: url }), shown
|
|
277
|
+
* before the text in the row variant. Same contract as the connect cards. */
|
|
278
|
+
get icons(): Record<string, string>;
|
|
279
|
+
set icons(v: Record<string, string> | null);
|
|
280
|
+
attributeChangedCallback(name: string, _old: string | null, value: string | null): void;
|
|
281
|
+
connectedCallback(): void;
|
|
282
|
+
private host;
|
|
283
|
+
private wire;
|
|
284
|
+
protected view(into: HTMLElement): void;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/** A destructive gesture, offered to confirmAction before it runs. `delete`
|
|
288
|
+
* and `forget` carry the row; `leave` is a membership's removal (fileId =
|
|
289
|
+
* its wallet, null for a legacy unbound one) - the host performs the actual
|
|
290
|
+
* leave on the 'selfstore-backups-leave' event, whose detail repeats it. */
|
|
291
|
+
type BackupsAction = {
|
|
292
|
+
type: 'delete';
|
|
293
|
+
fileId: string;
|
|
294
|
+
name: string;
|
|
295
|
+
active: boolean;
|
|
296
|
+
} | {
|
|
297
|
+
type: 'forget';
|
|
298
|
+
fileId: string;
|
|
299
|
+
who: string;
|
|
300
|
+
} | {
|
|
301
|
+
type: 'leave';
|
|
302
|
+
fileId: string | null;
|
|
303
|
+
who: string;
|
|
304
|
+
} | {
|
|
305
|
+
type: 'replica-remove';
|
|
306
|
+
label: string;
|
|
307
|
+
};
|
|
308
|
+
declare class SelfstoreBackupsElement extends FlowWidget {
|
|
309
|
+
#private;
|
|
310
|
+
static get observedAttributes(): string[];
|
|
311
|
+
protected defaults(): WidgetLabels;
|
|
312
|
+
/** The headless manager this panel drives (see 'selfstore/backups'). */
|
|
313
|
+
get manager(): BackupsManager | null;
|
|
314
|
+
set manager(v: BackupsManager | null);
|
|
315
|
+
/** The households this device is a member of (one entry per share, or a
|
|
316
|
+
* single object for one): each renders in "Shared with me" labeled by who
|
|
317
|
+
* shares it, removal = leaving (host performs it on
|
|
318
|
+
* 'selfstore-backups-leave', whose detail names the entry's fileId). With
|
|
319
|
+
* `fileId` (the share's dedicated wallet), that registry row IS the
|
|
320
|
+
* member row: active it wears the badge, inactive it opens like any
|
|
321
|
+
* shared silo - and its one menu action is leaving, never a mere forget. */
|
|
322
|
+
get member(): {
|
|
323
|
+
by: string;
|
|
324
|
+
fileId?: string | null;
|
|
325
|
+
}[] | null;
|
|
326
|
+
set member(v: {
|
|
327
|
+
by: string;
|
|
328
|
+
fileId?: string | null;
|
|
329
|
+
}[] | {
|
|
330
|
+
by: string;
|
|
331
|
+
fileId?: string | null;
|
|
332
|
+
} | null);
|
|
333
|
+
/** Live state of the ATTACHED backup, from the host's store: the active
|
|
334
|
+
* row's pills prefer these over the learned memories (which may lag). */
|
|
335
|
+
get activeEncrypted(): boolean | null;
|
|
336
|
+
set activeEncrypted(v: boolean | null);
|
|
337
|
+
get activeShared(): boolean | null;
|
|
338
|
+
set activeShared(v: boolean | null);
|
|
339
|
+
/** Host veto over the destructive gestures (delete / forget / leave):
|
|
340
|
+
* answer (or resolve) false to keep things as they are - window.confirm
|
|
341
|
+
* fits as-is. Default: everything proceeds. */
|
|
342
|
+
get confirmAction(): ((action: BackupsAction) => boolean | Promise<boolean>) | null;
|
|
343
|
+
set confirmAction(v: ((action: BackupsAction) => boolean | Promise<boolean>) | null);
|
|
344
|
+
get withCreate(): boolean;
|
|
345
|
+
set withCreate(v: boolean);
|
|
346
|
+
get withRename(): boolean;
|
|
347
|
+
set withRename(v: boolean);
|
|
348
|
+
get withDelete(): boolean;
|
|
349
|
+
set withDelete(v: boolean);
|
|
350
|
+
get withOpen(): boolean;
|
|
351
|
+
set withOpen(v: boolean);
|
|
352
|
+
get withEncrypt(): boolean;
|
|
353
|
+
set withEncrypt(v: boolean);
|
|
354
|
+
get withShare(): boolean;
|
|
355
|
+
set withShare(v: boolean);
|
|
356
|
+
get withShared(): boolean;
|
|
357
|
+
set withShared(v: boolean);
|
|
358
|
+
attributeChangedCallback(name: string, _old: string | null, value: string | null): void;
|
|
359
|
+
connectedCallback(): void;
|
|
360
|
+
/** Re-list the rows from the destination (the host calls this after a
|
|
361
|
+
* gesture of its own moved things - a leave, an external change). */
|
|
362
|
+
refresh(): void;
|
|
363
|
+
private wire;
|
|
364
|
+
/** The backup-copy journey (see 'selfstore/flows' replicaFlow). Setting it
|
|
365
|
+
* adds a menu entry on the active backup's card and renders the copy
|
|
366
|
+
* cards under it; without it the panel is byte-identical to before. The
|
|
367
|
+
* widget restores a recorded copy by itself. */
|
|
368
|
+
get replica(): ReplicaFlow | null;
|
|
369
|
+
set replica(v: ReplicaFlow | null);
|
|
370
|
+
/** One destination listing, then the lazy encryption probes (each learned
|
|
371
|
+
* bit repaints its row). A transient failure keeps the last good list. */
|
|
372
|
+
private loadRows;
|
|
373
|
+
/** t() with {placeholders}. */
|
|
374
|
+
private ts;
|
|
375
|
+
private label;
|
|
376
|
+
protected view(into: HTMLElement): void;
|
|
377
|
+
private pills;
|
|
378
|
+
private rowCard;
|
|
379
|
+
private passwordCard;
|
|
380
|
+
private renameCard;
|
|
381
|
+
private createCard;
|
|
382
|
+
/** The transient setup UI under the active card: the destination choice or a
|
|
383
|
+
* config form. The ATTACHED copy is not here - it rides inside the active
|
|
384
|
+
* card (replicaAttachedLine). Idle renders nothing - the entry point lives
|
|
385
|
+
* in the active card's menu. */
|
|
386
|
+
private replicaSetup;
|
|
387
|
+
/** The attached copy is not a backup of its own: it renders as ONE line
|
|
388
|
+
* INSIDE the active backup's card, under its pills (label, freshness, an
|
|
389
|
+
* inline "remove") - never a card or a block of its own. Null when no copy
|
|
390
|
+
* is attached. */
|
|
391
|
+
private replicaAttachedLine;
|
|
392
|
+
private replicaPicker;
|
|
393
|
+
/** A config field: mutates state on input, no repaint (validated on submit). */
|
|
394
|
+
private replicaField;
|
|
395
|
+
private replicaWebdavForm;
|
|
396
|
+
private replicaS3Form;
|
|
397
|
+
private menuButton;
|
|
398
|
+
/** The open overflow menu: a backdrop closes it on any outside click or
|
|
399
|
+
* tap, Escape closes it and hands focus back to its trigger. The panel is
|
|
400
|
+
* a dropdown over the trigger's corner on wide screens and a bottom sheet
|
|
401
|
+
* on phones - both styled in the base sheet. */
|
|
402
|
+
private menu;
|
|
403
|
+
private menuItem;
|
|
404
|
+
private eye;
|
|
405
|
+
private nameTaken;
|
|
406
|
+
/** Run a destructive action through the host's veto (default: proceed).
|
|
407
|
+
* A throwing hook reads as "no": destruction needs a clear yes. */
|
|
408
|
+
private guarded;
|
|
409
|
+
/** Open a row programmatically - the same gesture as tapping it, password
|
|
410
|
+
* card included. Lets a host chain "open this backup, then act on it"
|
|
411
|
+
* (its own sub-views) without re-implementing the passphrase step. */
|
|
412
|
+
open(fileId: string, passphrase?: string): Promise<void>;
|
|
413
|
+
private create;
|
|
414
|
+
private rename;
|
|
415
|
+
private deleteRow;
|
|
416
|
+
private forget;
|
|
417
|
+
private closePw;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
/** Register the elements as <PREFIX-connect>, <PREFIX-share>, <PREFIX-join>,
|
|
421
|
+
* <PREFIX-status> and <PREFIX-backups> (default prefix 'selfstore'). Safe to
|
|
422
|
+
* call twice; throws in environments without custom elements (browser code
|
|
423
|
+
* only). */
|
|
424
|
+
declare function defineSelfstoreWidgets(prefix?: string): void;
|
|
425
|
+
|
|
426
|
+
export { type BackupsAction, FlowWidget, SelfstoreBackupsElement, SelfstoreConnectElement, SelfstoreJoinElement, SelfstoreShareElement, SelfstoreStatusElement, type ShareAction, type WebdavPreset, type WidgetLabels, defineSelfstoreWidgets };
|