@deveye/types 0.15.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 +23 -0
- package/package.json +68 -0
- package/src/domain/audience.ts +549 -0
- package/src/domain/backup.ts +355 -0
- package/src/domain/credential.ts +55 -0
- package/src/domain/database.ts +467 -0
- package/src/domain/deploy.ts +231 -0
- package/src/domain/device.ts +172 -0
- package/src/domain/deviceFiles.ts +84 -0
- package/src/domain/deviceLogs.ts +82 -0
- package/src/domain/featureRegistry.ts +392 -0
- package/src/domain/finance.ts +477 -0
- package/src/domain/git.ts +419 -0
- package/src/domain/home.ts +314 -0
- package/src/domain/live.ts +272 -0
- package/src/domain/logs.ts +117 -0
- package/src/domain/mail.ts +394 -0
- package/src/domain/metrics.ts +127 -0
- package/src/domain/note.ts +202 -0
- package/src/domain/notifications.ts +268 -0
- package/src/domain/packages.ts +35 -0
- package/src/domain/password.ts +36 -0
- package/src/domain/presence.ts +21 -0
- package/src/domain/project.ts +168 -0
- package/src/domain/projectBoard.ts +130 -0
- package/src/domain/projectChat.ts +46 -0
- package/src/domain/projectHistory.ts +82 -0
- package/src/domain/projectLink.ts +87 -0
- package/src/domain/projectPlan.ts +68 -0
- package/src/domain/report.ts +492 -0
- package/src/domain/role.ts +8 -0
- package/src/domain/secrecy.ts +66 -0
- package/src/domain/sentinel.ts +623 -0
- package/src/domain/sharing.ts +186 -0
- package/src/domain/syncProtocol.ts +116 -0
- package/src/domain/twoFactor.ts +40 -0
- package/src/domain/uptime.ts +216 -0
- package/src/domain/user.ts +141 -0
- package/src/domain/workspace.ts +56 -0
- package/src/domain/workspaceRole.ts +251 -0
- package/src/features/admin.ts +112 -0
- package/src/features/audience.ts +275 -0
- package/src/features/backup.ts +230 -0
- package/src/features/database.ts +461 -0
- package/src/features/deploy.ts +245 -0
- package/src/features/device.ts +292 -0
- package/src/features/deviceFiles.ts +83 -0
- package/src/features/deviceLogs.ts +36 -0
- package/src/features/deviceTerminal.ts +57 -0
- package/src/features/finance.ts +360 -0
- package/src/features/git.ts +368 -0
- package/src/features/home.ts +32 -0
- package/src/features/live.ts +113 -0
- package/src/features/logs.ts +86 -0
- package/src/features/mail.ts +374 -0
- package/src/features/metrics.ts +185 -0
- package/src/features/note.ts +189 -0
- package/src/features/notify.ts +164 -0
- package/src/features/password.ts +67 -0
- package/src/features/project.ts +709 -0
- package/src/features/registry.ts +103 -0
- package/src/features/secrecy.ts +120 -0
- package/src/features/sentinel.ts +233 -0
- package/src/features/sharing.ts +79 -0
- package/src/features/twoFactor.ts +47 -0
- package/src/features/uptime.ts +186 -0
- package/src/features/user.ts +91 -0
- package/src/features/workspace.ts +200 -0
- package/src/http/auth.ts +94 -0
- package/src/http/device.ts +222 -0
- package/src/http/status.ts +45 -0
- package/src/index.ts +1700 -0
- package/src/protocol/agent.ts +1171 -0
- package/src/protocol/envelope.ts +46 -0
- package/src/protocol/error.ts +27 -0
- package/src/protocol/result.ts +17 -0
- package/src/protocol/version.ts +6 -0
- package/src/sdk/client-ambient.d.ts +238 -0
- package/src/sdk/client.ts +66 -0
- package/src/sdk/ids.ts +25 -0
- package/src/sdk/index.ts +11 -0
- package/src/sdk/manifest.ts +326 -0
- package/src/sdk/providers.ts +48 -0
- package/src/sdk/server.ts +378 -0
- package/src/sdk/testing.ts +179 -0
- package/src/utils/version.ts +28 -0
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
import type { ZodType } from 'zod';
|
|
2
|
+
import {
|
|
3
|
+
EXTERNAL_FEATURE_ID_PATTERN,
|
|
4
|
+
isExternalFeatureId,
|
|
5
|
+
type FeatureId
|
|
6
|
+
} from '../domain/workspaceRole';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* The feature manifest: everything DevEye needs to know about a feature,
|
|
10
|
+
* declared once.
|
|
11
|
+
*
|
|
12
|
+
* Historically this knowledge was scattered across ~14 registration points
|
|
13
|
+
* (descriptor, catalog entry, settings wiring tables, invalidation keys,
|
|
14
|
+
* teleport segments...). A module declares all of it here; the app's generated
|
|
15
|
+
* glue reads the manifest and wires every screen from it. Native features are
|
|
16
|
+
* being migrated onto the same contract (Weather first).
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* One WebSocket command contract. Identical in shape to the descriptors native
|
|
21
|
+
* features declare in `deveye-types/src/features/*`: the server validates the
|
|
22
|
+
* input before your handler runs and the output after it returns, both ways,
|
|
23
|
+
* so a payload that does not match a schema never crosses the wire.
|
|
24
|
+
*/
|
|
25
|
+
export interface ManifestCommand<Cmd extends string = string> {
|
|
26
|
+
/** Must start with `<manifest.id>.` (enforced by {@link validateManifest}). */
|
|
27
|
+
command: Cmd;
|
|
28
|
+
input: ZodType;
|
|
29
|
+
output: ZodType;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* A permission your feature defines beyond the built-in none/read/write axis.
|
|
34
|
+
* It appears in every workspace role editor, under your feature's row, and is
|
|
35
|
+
* stored in the role's grant (`extras`).
|
|
36
|
+
*
|
|
37
|
+
* Two bounded types only:
|
|
38
|
+
* - `toggle`: a boolean. Absent from a grant = **false** (fail-closed).
|
|
39
|
+
* The workspace owner always gets `true`.
|
|
40
|
+
* - `choice`: one value among 2 to 5 options. Absent = `default`, which MUST
|
|
41
|
+
* be the least-privileged option. The owner gets `ownerValue`.
|
|
42
|
+
*/
|
|
43
|
+
export type ExtraPermissionSpec =
|
|
44
|
+
| {
|
|
45
|
+
/** Unique within the feature. `/^[a-z][a-zA-Z0-9]{1,23}$/`. */
|
|
46
|
+
key: string;
|
|
47
|
+
/** Short label shown next to the control in the role editor. */
|
|
48
|
+
label: string;
|
|
49
|
+
/** One sentence: what granting this allows. */
|
|
50
|
+
description: string;
|
|
51
|
+
type: 'toggle';
|
|
52
|
+
}
|
|
53
|
+
| {
|
|
54
|
+
key: string;
|
|
55
|
+
label: string;
|
|
56
|
+
description: string;
|
|
57
|
+
type: 'choice';
|
|
58
|
+
options: readonly { value: string; label: string }[];
|
|
59
|
+
/** Value when the grant is silent. MUST be the least-privileged option. */
|
|
60
|
+
default: string;
|
|
61
|
+
/** Value the workspace owner (who has everything) receives. */
|
|
62
|
+
ownerValue: string;
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
/** Hard cap on `extraPermissions`: keeps role editors legible. */
|
|
66
|
+
export const MAX_EXTRA_PERMISSIONS = 4;
|
|
67
|
+
|
|
68
|
+
const EXTRA_KEY_PATTERN = /^[a-z][a-zA-Z0-9]{1,23}$/;
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Native capabilities your server code may call through `ctx.deveye`.
|
|
72
|
+
* Calling a facade you did not declare throws `forbidden`: the declaration
|
|
73
|
+
* is also what an administrator reviews before installing your module.
|
|
74
|
+
*/
|
|
75
|
+
export type NativeCapability =
|
|
76
|
+
| 'notify'
|
|
77
|
+
| 'mail.accounts'
|
|
78
|
+
| 'members.read'
|
|
79
|
+
/** Read/authorize the workspace's devices. */
|
|
80
|
+
| 'devices.read'
|
|
81
|
+
/**
|
|
82
|
+
* The full agent-fleet sync transport (outbound requests, browser fan-out,
|
|
83
|
+
* per-socket subscriptions). Reserved for repatriated NATIVE features: the
|
|
84
|
+
* agent protocol is app infrastructure, not a third-party surface.
|
|
85
|
+
*/
|
|
86
|
+
| 'agents';
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Settings tabs the shell can render for you.
|
|
90
|
+
* - `'notifications'` and `'permissions'` are fully generic: DevEye renders
|
|
91
|
+
* them from the manifest alone, you write no component.
|
|
92
|
+
* - `'general'` and `'sources'` need a panel component, provided by your
|
|
93
|
+
* client entry (`settingsPanels`), keyed by the tab id.
|
|
94
|
+
*/
|
|
95
|
+
export type SettingsTab = 'general' | 'sources' | 'notifications' | 'permissions';
|
|
96
|
+
|
|
97
|
+
/** A custom settings tab. Needs a matching panel in `settingsPanels`. */
|
|
98
|
+
export interface CustomTabRef {
|
|
99
|
+
/** `/^[a-z][a-z0-9]{1,23}$/`, distinct from the built-in tab ids. */
|
|
100
|
+
id: string;
|
|
101
|
+
label: string;
|
|
102
|
+
/** Icon class suffix; defaults to `settings`. */
|
|
103
|
+
icon?: string;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const CUSTOM_TAB_PATTERN = /^[a-z][a-z0-9]{1,23}$/;
|
|
107
|
+
const BUILTIN_TABS: readonly string[] = [
|
|
108
|
+
'general',
|
|
109
|
+
'sources',
|
|
110
|
+
'notifications',
|
|
111
|
+
'permissions',
|
|
112
|
+
'sync',
|
|
113
|
+
'encryption',
|
|
114
|
+
'sharing'
|
|
115
|
+
];
|
|
116
|
+
|
|
117
|
+
/** See {@link FeatureManifest.alsoInvalidatedBy}. */
|
|
118
|
+
export interface CrossTopicInvalidation {
|
|
119
|
+
/** A NATIVE live topic that is not this feature's own. */
|
|
120
|
+
topic: string;
|
|
121
|
+
/** Subset of `resources` to re-fetch when it beats. */
|
|
122
|
+
keys: readonly string[];
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/** Where a feature's card can live on the home grid. */
|
|
126
|
+
export type FeatureCategory = 'supervision' | 'security' | 'dev' | 'work' | 'daily' | 'analysis';
|
|
127
|
+
|
|
128
|
+
export interface FeatureManifest<Id extends FeatureId = FeatureId> {
|
|
129
|
+
/** External modules: `x-<slug>`. Native features keep their enum id. */
|
|
130
|
+
id: Id;
|
|
131
|
+
/** UI title. English for external modules, French for natives. */
|
|
132
|
+
label: string;
|
|
133
|
+
/** One sentence: what the feature does. Feeds the catalog and the roles screen. */
|
|
134
|
+
description: string;
|
|
135
|
+
/**
|
|
136
|
+
* Icon class suffix (`icon-<icon>`). External modules ship the SVG in
|
|
137
|
+
* `assets/icons/`; the app's codegen wires the class as
|
|
138
|
+
* `x-<slug>-<name>` and rewrites this field accordingly.
|
|
139
|
+
*/
|
|
140
|
+
icon: string;
|
|
141
|
+
category: FeatureCategory;
|
|
142
|
+
|
|
143
|
+
/** The feature can send notifications. Opens the Notifications tab. */
|
|
144
|
+
notifies: boolean;
|
|
145
|
+
/** The feature owns addressable items with per-item settings. */
|
|
146
|
+
hasItems: boolean;
|
|
147
|
+
/** Singular noun for item-scope screen titles. Required when `hasItems`. */
|
|
148
|
+
itemNoun?: string;
|
|
149
|
+
/** Feature-scope reusable settings (API keys, destinations). Opens the Sources tab. */
|
|
150
|
+
sources?: { hint: string };
|
|
151
|
+
/**
|
|
152
|
+
* Whether an item can be projected into another workspace. Decided by
|
|
153
|
+
* encryption, not preference. External modules: `'never'` only for now
|
|
154
|
+
* (cross-workspace listing requires repo-level wiring not yet in the SDK).
|
|
155
|
+
*/
|
|
156
|
+
shareTier: 'open' | 'perItem' | 'never';
|
|
157
|
+
|
|
158
|
+
/** Card rendering on the home grid. `compact` halves the minimum height. */
|
|
159
|
+
tile?: { compact?: boolean };
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* The feature offers a compact TOPBAR widget (pinned top-right of the
|
|
163
|
+
* navbar). Declares only the DATA the picker shows; the component itself
|
|
164
|
+
* comes from the client entry (`FeatureClient.TopbarWidget`). Offered and
|
|
165
|
+
* rendered only to members whose role grants the feature: absence of the
|
|
166
|
+
* grant means absence of the widget, same rule as the grid card.
|
|
167
|
+
*/
|
|
168
|
+
topbarWidget?: { description: string };
|
|
169
|
+
|
|
170
|
+
/** See {@link ExtraPermissionSpec}. At most {@link MAX_EXTRA_PERMISSIONS}. */
|
|
171
|
+
extraPermissions?: readonly ExtraPermissionSpec[];
|
|
172
|
+
/** See {@link NativeCapability}. */
|
|
173
|
+
nativeCapabilities?: readonly NativeCapability[];
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Resource keys this feature owns, `<id>.<name>` (by convention, the
|
|
177
|
+
* command whose result the key caches). The client invalidation bus and
|
|
178
|
+
* `useResource` are keyed on them.
|
|
179
|
+
*/
|
|
180
|
+
resources: readonly string[];
|
|
181
|
+
/**
|
|
182
|
+
* Keys re-fetched when the feature's live topic fires (a mutation by any
|
|
183
|
+
* member). Defaults to all of `resources`.
|
|
184
|
+
*/
|
|
185
|
+
invalidatedByTopic?: readonly string[];
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Keys ALSO re-fetched when another feature's topic fires. The escape
|
|
189
|
+
* hatch for real data coupling (CloudSync's share rows carry device names:
|
|
190
|
+
* a device rename must refresh the share list). Native topics only, and
|
|
191
|
+
* never your own.
|
|
192
|
+
*/
|
|
193
|
+
alsoInvalidatedBy?: readonly CrossTopicInvalidation[];
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Prefix override for commands and resources. Native-id modules only, and
|
|
197
|
+
* only when the historical command casing differs from the id (cloudsync
|
|
198
|
+
* owns `cloudSync.*` commands). Must equal `<id>.` case-insensitively.
|
|
199
|
+
* Defaults to `<id>.`.
|
|
200
|
+
*/
|
|
201
|
+
commandPrefix?: string;
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Builds the live/teleport segment for one item, e.g. `(id) => `job:${id}``.
|
|
205
|
+
* MUST return byte-for-byte what your view declares via `useLiveSegment('l1', ...)`:
|
|
206
|
+
* it is a rendezvous, not a convention. Requires `hasItems`.
|
|
207
|
+
*/
|
|
208
|
+
itemSegment?: (itemId: number | string) => string;
|
|
209
|
+
|
|
210
|
+
/** Settings tabs, per scope. Omit a scope to render no settings there. */
|
|
211
|
+
settings?: {
|
|
212
|
+
feature?: readonly (SettingsTab | CustomTabRef)[];
|
|
213
|
+
item?: readonly (SettingsTab | CustomTabRef)[];
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
commands: readonly ManifestCommand[];
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function fail(id: string, message: string): never {
|
|
220
|
+
throw new Error(`FeatureManifest « ${id} »: ${message}`);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Validates the data half of a manifest (shapes, prefixes, coherence).
|
|
225
|
+
* Called by the template's standalone CI and again by the app at registration,
|
|
226
|
+
* so a violation surfaces before the module ever meets a runtime.
|
|
227
|
+
*/
|
|
228
|
+
export function validateManifest(m: FeatureManifest): void {
|
|
229
|
+
const external = isExternalFeatureId(m.id);
|
|
230
|
+
if (external && !EXTERNAL_FEATURE_ID_PATTERN.test(m.id)) fail(m.id, 'invalid external id');
|
|
231
|
+
if (!m.label.trim()) fail(m.id, 'empty label');
|
|
232
|
+
if (!m.description.trim()) fail(m.id, 'empty description');
|
|
233
|
+
if (m.hasItems && !m.itemNoun?.trim()) fail(m.id, 'hasItems requires itemNoun');
|
|
234
|
+
if (!m.hasItems && m.itemSegment) fail(m.id, 'itemSegment requires hasItems');
|
|
235
|
+
if (external && m.shareTier !== 'never') {
|
|
236
|
+
fail(m.id, "external modules must declare shareTier 'never' for now");
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
if (m.commandPrefix !== undefined) {
|
|
240
|
+
if (external) fail(m.id, 'commandPrefix is reserved for native-id modules');
|
|
241
|
+
if (m.commandPrefix.toLowerCase() !== `${m.id}.`.toLowerCase()) {
|
|
242
|
+
fail(
|
|
243
|
+
m.id,
|
|
244
|
+
`commandPrefix « ${m.commandPrefix} » must equal « ${m.id}. » case-insensitively`
|
|
245
|
+
);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
const prefix = m.commandPrefix ?? `${m.id}.`;
|
|
249
|
+
for (const c of m.commands) {
|
|
250
|
+
if (!c.command.startsWith(prefix)) {
|
|
251
|
+
fail(m.id, `command « ${c.command} » must start with « ${prefix} »`);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
for (const key of m.resources) {
|
|
255
|
+
if (!key.startsWith(prefix)) {
|
|
256
|
+
fail(m.id, `resource « ${key} » must start with « ${prefix} »`);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
for (const key of m.invalidatedByTopic ?? []) {
|
|
260
|
+
if (!m.resources.includes(key)) {
|
|
261
|
+
fail(m.id, `invalidatedByTopic « ${key} » is not in resources`);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
const cross = m.alsoInvalidatedBy ?? [];
|
|
266
|
+
if (cross.length > 4) fail(m.id, 'more than 4 alsoInvalidatedBy entries');
|
|
267
|
+
for (const entry of cross) {
|
|
268
|
+
if (entry.topic === m.id) fail(m.id, "alsoInvalidatedBy must name ANOTHER feature's topic");
|
|
269
|
+
for (const key of entry.keys) {
|
|
270
|
+
if (!m.resources.includes(key)) {
|
|
271
|
+
fail(m.id, `alsoInvalidatedBy key « ${key} » is not in resources`);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
if ((m.nativeCapabilities ?? []).includes('agents') && external) {
|
|
277
|
+
fail(m.id, "capability 'agents' is reserved for native-id modules");
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
const extras = m.extraPermissions ?? [];
|
|
281
|
+
if (extras.length > MAX_EXTRA_PERMISSIONS) {
|
|
282
|
+
fail(m.id, `more than ${MAX_EXTRA_PERMISSIONS} extra permissions`);
|
|
283
|
+
}
|
|
284
|
+
const seen = new Set<string>();
|
|
285
|
+
for (const spec of extras) {
|
|
286
|
+
if (!EXTRA_KEY_PATTERN.test(spec.key)) fail(m.id, `invalid extra key « ${spec.key} »`);
|
|
287
|
+
if (seen.has(spec.key)) fail(m.id, `duplicate extra key « ${spec.key} »`);
|
|
288
|
+
seen.add(spec.key);
|
|
289
|
+
if (spec.type === 'choice') {
|
|
290
|
+
if (spec.options.length < 2 || spec.options.length > 5) {
|
|
291
|
+
fail(m.id, `extra « ${spec.key} »: 2 to 5 options required`);
|
|
292
|
+
}
|
|
293
|
+
const values = spec.options.map((o) => o.value);
|
|
294
|
+
if (new Set(values).size !== values.length) {
|
|
295
|
+
fail(m.id, `extra « ${spec.key} »: duplicate option values`);
|
|
296
|
+
}
|
|
297
|
+
if (!values.includes(spec.default)) {
|
|
298
|
+
fail(m.id, `extra « ${spec.key} »: default is not an option`);
|
|
299
|
+
}
|
|
300
|
+
if (!values.includes(spec.ownerValue)) {
|
|
301
|
+
fail(m.id, `extra « ${spec.key} »: ownerValue is not an option`);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
for (const scope of ['feature', 'item'] as const) {
|
|
307
|
+
for (const tab of m.settings?.[scope] ?? []) {
|
|
308
|
+
if (typeof tab === 'string') {
|
|
309
|
+
if (tab === 'notifications' && !m.notifies) {
|
|
310
|
+
fail(m.id, 'notifications tab requires notifies');
|
|
311
|
+
}
|
|
312
|
+
if (tab === 'sources' && !m.sources) fail(m.id, 'sources tab requires sources');
|
|
313
|
+
if (tab === 'sources' && scope === 'item') {
|
|
314
|
+
fail(m.id, 'sources is a feature-scope tab');
|
|
315
|
+
}
|
|
316
|
+
} else {
|
|
317
|
+
if (!CUSTOM_TAB_PATTERN.test(tab.id) || BUILTIN_TABS.includes(tab.id)) {
|
|
318
|
+
fail(m.id, `invalid custom tab id « ${tab.id} »`);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
if ((m.settings?.[scope]?.length ?? 0) > 0 && scope === 'item' && !m.hasItems) {
|
|
323
|
+
fail(m.id, 'item settings require hasItems');
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Provider contracts: the inversion for PUBLIC app code that needs a MODULE's
|
|
3
|
+
* data.
|
|
4
|
+
*
|
|
5
|
+
* A module exposes named contracts on its service (`FeatureService.providers`);
|
|
6
|
+
* the app looks them up at call time (`moduleProvider(key)` in its SDK
|
|
7
|
+
* assembly) and degrades cleanly when the module is absent. The contract types
|
|
8
|
+
* live here, in the published package, because both sides must agree on them
|
|
9
|
+
* while neither may import the other.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/** Key under `FeatureService.providers` for the CloudSync backup source. */
|
|
13
|
+
export const CLOUDSYNC_BACKUP_PROVIDER = 'cloudsync.backup' as const;
|
|
14
|
+
|
|
15
|
+
export interface SyncBackupShare {
|
|
16
|
+
id: number;
|
|
17
|
+
name: string;
|
|
18
|
+
workspaceId: number;
|
|
19
|
+
userId: number;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface SyncBackupStats {
|
|
23
|
+
fileCount: number;
|
|
24
|
+
liveBytes: number;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface SyncBackupFile {
|
|
28
|
+
relPath: string;
|
|
29
|
+
kind: 'file' | 'dir';
|
|
30
|
+
hash: string;
|
|
31
|
+
size: number;
|
|
32
|
+
mtime: number;
|
|
33
|
+
mode: number | null;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* What the Backup feature needs from CloudSync, inverted: the CloudSync module
|
|
38
|
+
* registers this; Backup consumes it. Absent provider = the backup run fails
|
|
39
|
+
* with a clean "module not installed" error and the UI hides the source kind.
|
|
40
|
+
*/
|
|
41
|
+
export interface CloudSyncBackupProvider {
|
|
42
|
+
findShare(shareId: number): Promise<SyncBackupShare | null>;
|
|
43
|
+
listShares(workspaceId: number): Promise<readonly SyncBackupShare[]>;
|
|
44
|
+
statsByShare(shareId: number): Promise<SyncBackupStats>;
|
|
45
|
+
listPresentFiles(shareId: number): Promise<readonly SyncBackupFile[]>;
|
|
46
|
+
/** Decrypted plaintext stream of one blob. */
|
|
47
|
+
openBlob(shareId: number, hash: string): Promise<AsyncIterable<Uint8Array>>;
|
|
48
|
+
}
|