signalk-backup 0.5.0 → 0.6.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.
@@ -0,0 +1,26 @@
1
+ import type { IRouter } from 'express';
2
+ export interface RestoreHostWriteOptions {
3
+ getUpstreamBase: () => string | null;
4
+ log?: (msg: string) => void;
5
+ }
6
+ export interface HostRestoreStatus {
7
+ state: 'idle' | 'preparing' | 'streaming' | 'extracting' | 'completed' | 'failed' | 'rolling_back' | 'rolled_back';
8
+ progress: number;
9
+ statusMessage: string;
10
+ error?: string;
11
+ backupId?: string;
12
+ sourcePath?: string;
13
+ targetPath?: string;
14
+ }
15
+ export declare function registerHostRestoreRoutes(router: IRouter, opts: RestoreHostWriteOptions): void;
16
+ interface ResolvedHostTarget {
17
+ /** Absolute path the bytes land at on disk. */
18
+ absoluteTarget: string;
19
+ /** Sibling path that holds the existing target during the rename
20
+ * swap; cleaned up on success, restored on failure. */
21
+ safetyPath: string;
22
+ }
23
+ export declare function resolveHostTarget(customPath: string, _isDir: boolean, sourcePath: string): ResolvedHostTarget;
24
+ export declare function resolveZipEntryPath(entryPath: string, targetDir: string): string | null;
25
+ export {};
26
+ //# sourceMappingURL=restore-host-write.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"restore-host-write.d.ts","sourceRoot":"","sources":["../src/restore-host-write.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,OAAO,EAAqB,MAAM,SAAS,CAAA;AAGzD,MAAM,WAAW,uBAAuB;IAEtC,eAAe,EAAE,MAAM,MAAM,GAAG,IAAI,CAAA;IACpC,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAA;CAC5B;AAGD,MAAM,WAAW,iBAAiB;IAChC,KAAK,EACD,MAAM,GACN,WAAW,GACX,WAAW,GACX,YAAY,GACZ,WAAW,GACX,QAAQ,GACR,cAAc,GACd,aAAa,CAAA;IACjB,QAAQ,EAAE,MAAM,CAAA;IAChB,aAAa,EAAE,MAAM,CAAA;IACrB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AA6BD,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,uBAAuB,GAAG,IAAI,CAqL9F;AAED,UAAU,kBAAkB;IAC1B,+CAA+C;IAC/C,cAAc,EAAE,MAAM,CAAA;IACtB;4DACwD;IACxD,UAAU,EAAE,MAAM,CAAA;CACnB;AAKD,wBAAgB,iBAAiB,CAC/B,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,OAAO,EACf,UAAU,EAAE,MAAM,GACjB,kBAAkB,CAWpB;AAqID,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CASvF"}
@@ -0,0 +1,363 @@
1
+ // Plugin-side host-write restore — bypasses the container so the user can target paths it can't see.
2
+ import { createWriteStream } from 'node:fs';
3
+ import { mkdir, rename, rm, stat } from 'node:fs/promises';
4
+ import path from 'node:path';
5
+ import { pipeline } from 'node:stream/promises';
6
+ import { Readable } from 'node:stream';
7
+ import unzipper from 'unzipper';
8
+ class HostRestoreState {
9
+ status = {
10
+ state: 'idle',
11
+ progress: 0,
12
+ statusMessage: ''
13
+ };
14
+ reset() {
15
+ this.status = { state: 'idle', progress: 0, statusMessage: '' };
16
+ }
17
+ isRunning() {
18
+ return ['preparing', 'streaming', 'extracting', 'rolling_back'].includes(this.status.state);
19
+ }
20
+ }
21
+ const state = new HostRestoreState();
22
+ export function registerHostRestoreRoutes(router, opts) {
23
+ router.get('/api/restore-partial-host/status', (_req, res) => {
24
+ res.json({
25
+ success: true,
26
+ data: state.status,
27
+ timestamp: new Date().toISOString()
28
+ });
29
+ });
30
+ router.post('/api/restore-partial-host/reset', (_req, res) => {
31
+ // Refuse to reset mid-flight — would clear the single-flight slot
32
+ // and let a second restore start while the first still has files
33
+ // open. Caller has to wait for the active restore to terminate.
34
+ if (state.isRunning()) {
35
+ res.status(409).json({
36
+ success: false,
37
+ error: {
38
+ code: 'RESTORE_IN_PROGRESS',
39
+ message: 'Cannot reset while a host-restore is in progress'
40
+ },
41
+ timestamp: new Date().toISOString()
42
+ });
43
+ return;
44
+ }
45
+ state.reset();
46
+ res.json({
47
+ success: true,
48
+ data: { message: 'Host-restore state reset' },
49
+ timestamp: new Date().toISOString()
50
+ });
51
+ });
52
+ router.post('/api/restore-partial-host', async (req, res) => {
53
+ const body = req.body;
54
+ if (typeof body.backupId !== 'string' ||
55
+ typeof body.sourcePath !== 'string' ||
56
+ typeof body.customPath !== 'string' ||
57
+ typeof body.isDir !== 'boolean') {
58
+ res.status(400).json({
59
+ success: false,
60
+ error: {
61
+ code: 'INVALID_INPUT',
62
+ message: 'backupId, sourcePath, customPath, isDir are required'
63
+ },
64
+ timestamp: new Date().toISOString()
65
+ });
66
+ return;
67
+ }
68
+ // Reject empty customPath up front — an empty string would silently
69
+ // resolve to process.cwd() and write somewhere the user didn't ask.
70
+ if (body.customPath.trim().length === 0) {
71
+ res.status(400).json({
72
+ success: false,
73
+ error: { code: 'INVALID_INPUT', message: 'customPath must not be empty' },
74
+ timestamp: new Date().toISOString()
75
+ });
76
+ return;
77
+ }
78
+ // Type-check confirmOverwrite if present: a non-boolean truthy
79
+ // value (e.g. the string "false" from a misbuilt form) would
80
+ // otherwise bypass the conflict probe and silently overwrite.
81
+ if ('confirmOverwrite' in body && typeof body.confirmOverwrite !== 'boolean') {
82
+ res.status(400).json({
83
+ success: false,
84
+ error: {
85
+ code: 'INVALID_INPUT',
86
+ message: 'confirmOverwrite must be a boolean when provided'
87
+ },
88
+ timestamp: new Date().toISOString()
89
+ });
90
+ return;
91
+ }
92
+ const { backupId, sourcePath, customPath, isDir, confirmOverwrite } = body;
93
+ // sourcePath safety: reject `..` segments and NUL bytes. No reject-list
94
+ // (package.json etc.) — the explicit point of host-restore is "copy
95
+ // anywhere I can write", so the in-container side-effect concerns
96
+ // don't apply.
97
+ if (sourcePath.includes('\0') || customPath.includes('\0')) {
98
+ res.status(400).json({
99
+ success: false,
100
+ error: { code: 'INVALID_INPUT', message: 'paths must not contain NUL bytes' },
101
+ timestamp: new Date().toISOString()
102
+ });
103
+ return;
104
+ }
105
+ if (sourcePath.split('/').some((p) => p === '..')) {
106
+ res.status(400).json({
107
+ success: false,
108
+ error: { code: 'INVALID_INPUT', message: 'sourcePath must not contain ".." segments' },
109
+ timestamp: new Date().toISOString()
110
+ });
111
+ return;
112
+ }
113
+ if (state.isRunning()) {
114
+ res.status(409).json({
115
+ success: false,
116
+ error: {
117
+ code: 'RESTORE_IN_PROGRESS',
118
+ message: 'A host-restore is already in progress'
119
+ },
120
+ timestamp: new Date().toISOString()
121
+ });
122
+ return;
123
+ }
124
+ const target = resolveHostTarget(customPath, isDir, sourcePath);
125
+ // Reserve the single-flight slot synchronously so a concurrent
126
+ // POST sees state.isRunning() === true before either request hits
127
+ // its first await. Any early-return path below clears the slot
128
+ // via state.reset() so the user can retry.
129
+ state.status = {
130
+ state: 'preparing',
131
+ progress: 0,
132
+ statusMessage: 'Preparing host-restore…',
133
+ backupId,
134
+ sourcePath,
135
+ targetPath: target.absoluteTarget
136
+ };
137
+ // Conflict probe — surface the existing entry so the UI shows a diff.
138
+ if (!confirmOverwrite) {
139
+ const existing = await safeStat(target.absoluteTarget);
140
+ if (existing) {
141
+ state.reset();
142
+ res.status(409).json({
143
+ success: false,
144
+ error: {
145
+ code: 'TARGET_EXISTS',
146
+ message: `Target '${target.absoluteTarget}' already exists; resubmit with confirmOverwrite=true to proceed`
147
+ },
148
+ data: {
149
+ conflict: {
150
+ targetPath: target.absoluteTarget,
151
+ mtime: existing.mtime.toISOString(),
152
+ size: existing.size
153
+ }
154
+ },
155
+ timestamp: new Date().toISOString()
156
+ });
157
+ return;
158
+ }
159
+ }
160
+ const base = opts.getUpstreamBase();
161
+ if (!base) {
162
+ state.reset();
163
+ res.status(503).json({
164
+ success: false,
165
+ error: { code: 'UPSTREAM_NOT_READY', message: 'backup-server not ready' },
166
+ timestamp: new Date().toISOString()
167
+ });
168
+ return;
169
+ }
170
+ res.status(202).json({
171
+ success: true,
172
+ data: { started: true },
173
+ timestamp: new Date().toISOString()
174
+ });
175
+ runRestore({
176
+ backupId,
177
+ sourcePath,
178
+ isDir,
179
+ target,
180
+ upstreamBase: base,
181
+ log: opts.log
182
+ }).catch((err) => {
183
+ opts.log?.(`host-restore failed: ${errMsg(err)}`);
184
+ state.status = {
185
+ ...state.status,
186
+ state: 'failed',
187
+ error: errMsg(err),
188
+ statusMessage: 'Host-restore failed'
189
+ };
190
+ });
191
+ });
192
+ }
193
+ // Mirror of the server's trailing-slash interpretation: if the user
194
+ // types `tmp/` the source basename is appended so a file restore lands
195
+ // at `tmp/<filename>` instead of overwriting `tmp` as a file.
196
+ export function resolveHostTarget(customPath, _isDir, sourcePath) {
197
+ const explicitDir = /[/\\]$/.test(customPath);
198
+ const stripped = explicitDir ? customPath.replace(/[/\\]+$/, '') : customPath;
199
+ const baseCustom = explicitDir && stripped === '' && path.isAbsolute(customPath) ? customPath : stripped;
200
+ const joined = path.isAbsolute(baseCustom) ? baseCustom : path.resolve(baseCustom);
201
+ const absoluteTarget = explicitDir ? path.join(joined, path.basename(sourcePath)) : joined;
202
+ return {
203
+ absoluteTarget,
204
+ safetyPath: makeSafetyPath(absoluteTarget)
205
+ };
206
+ }
207
+ function makeSafetyPath(targetAbs) {
208
+ const stamp = new Date().toISOString().replace(/[:.]/g, '-');
209
+ return `${targetAbs}.partial-restore-backup-${stamp}`;
210
+ }
211
+ async function runRestore(ctx) {
212
+ let safetyStashed = null;
213
+ try {
214
+ // Stash any existing target so a failure mid-write can be rolled
215
+ // back. Same sibling-rename pattern as the server's partial-restore-
216
+ // service — atomic on the same filesystem.
217
+ const existing = await safeStat(ctx.target.absoluteTarget);
218
+ if (existing) {
219
+ await rename(ctx.target.absoluteTarget, ctx.target.safetyPath);
220
+ safetyStashed = ctx.target.safetyPath;
221
+ }
222
+ state.status = {
223
+ ...state.status,
224
+ state: 'streaming',
225
+ progress: 25,
226
+ statusMessage: 'Downloading from backup-server…'
227
+ };
228
+ const url = ctx.upstreamBase.replace(/\/$/, '') +
229
+ `/api/backups/${encodeURIComponent(ctx.backupId)}/download-subtree?path=${encodeURIComponent(ctx.sourcePath)}`;
230
+ const upstream = await fetch(url);
231
+ if (!upstream.ok || !upstream.body) {
232
+ const text = await upstream.text().catch(() => '');
233
+ throw new Error(`upstream ${upstream.status}: ${text.slice(0, 200)}`);
234
+ }
235
+ const nodeStream = Readable.fromWeb(upstream.body);
236
+ if (ctx.isDir) {
237
+ state.status = {
238
+ ...state.status,
239
+ state: 'extracting',
240
+ progress: 60,
241
+ statusMessage: 'Extracting ZIP…'
242
+ };
243
+ await mkdir(ctx.target.absoluteTarget, { recursive: true });
244
+ // Per-entry path-traversal guard: unzipper.Extract uses a string
245
+ // prefix check that doesn't catch ../ sequences inside an entry
246
+ // name. Parse the ZIP and resolve each entry against the target;
247
+ // reject anything that escapes via path.relative starting with '..'.
248
+ await extractZipSafely(nodeStream, ctx.target.absoluteTarget);
249
+ }
250
+ else {
251
+ state.status = {
252
+ ...state.status,
253
+ state: 'streaming',
254
+ progress: 60,
255
+ statusMessage: 'Writing file…'
256
+ };
257
+ // Single-file source. Make sure the parent exists; absolute or
258
+ // relative the user gave us, the dirname may not.
259
+ await mkdir(path.dirname(ctx.target.absoluteTarget), { recursive: true });
260
+ await pipeline(nodeStream, createWriteStream(ctx.target.absoluteTarget));
261
+ }
262
+ state.status = {
263
+ ...state.status,
264
+ state: 'completed',
265
+ progress: 100,
266
+ statusMessage: 'Host-restore complete'
267
+ };
268
+ // Clean up the safety stash on success — best-effort.
269
+ if (safetyStashed) {
270
+ await rm(safetyStashed, { recursive: true, force: true }).catch(() => undefined);
271
+ }
272
+ }
273
+ catch (err) {
274
+ const message = errMsg(err);
275
+ ctx.log?.(`host-restore error: ${message}`);
276
+ // Scrub partial output unconditionally — a failed fresh-target restore would otherwise leak bytes.
277
+ await rm(ctx.target.absoluteTarget, { recursive: true, force: true }).catch(() => undefined);
278
+ if (safetyStashed) {
279
+ state.status = {
280
+ ...state.status,
281
+ state: 'rolling_back',
282
+ statusMessage: 'Rolling back…'
283
+ };
284
+ try {
285
+ await rename(safetyStashed, ctx.target.absoluteTarget);
286
+ state.status = {
287
+ ...state.status,
288
+ state: 'rolled_back',
289
+ error: message,
290
+ statusMessage: 'Rolled back to previous target'
291
+ };
292
+ return;
293
+ }
294
+ catch (rollbackErr) {
295
+ ctx.log?.(`host-restore rollback failed: ${errMsg(rollbackErr)}`);
296
+ }
297
+ }
298
+ state.status = {
299
+ ...state.status,
300
+ state: 'failed',
301
+ error: message,
302
+ statusMessage: 'Host-restore failed'
303
+ };
304
+ }
305
+ }
306
+ async function safeStat(p) {
307
+ try {
308
+ const s = await stat(p);
309
+ return { mtime: s.mtime, size: s.size };
310
+ }
311
+ catch {
312
+ return null;
313
+ }
314
+ }
315
+ // Resolve a ZIP entry path against the target directory. Returns the
316
+ // absolute destination when the entry is contained inside targetDir,
317
+ // or null when the entry name is empty, contains '..' segments, or
318
+ // the resolved path otherwise escapes via path.relative. We reject
319
+ // '..' outright (rather than silently dropping it) so a malicious
320
+ // snapshot can't smuggle data into unintended locations under the
321
+ // target — even if the resolved path stays inside the root, the
322
+ // shape of the entry path is suspicious enough to refuse.
323
+ export function resolveZipEntryPath(entryPath, targetDir) {
324
+ const resolvedTarget = path.resolve(targetDir);
325
+ const parts = entryPath.split(/[/\\]/).filter((p) => p.length > 0);
326
+ if (parts.length === 0)
327
+ return null;
328
+ if (parts.some((p) => p === '..'))
329
+ return null;
330
+ const dest = path.resolve(resolvedTarget, parts.join('/'));
331
+ const rel = path.relative(resolvedTarget, dest);
332
+ if (rel.startsWith('..') || path.isAbsolute(rel))
333
+ return null;
334
+ return dest;
335
+ }
336
+ // Per-entry path validation while extracting the server's directory
337
+ // ZIP. unzipper.Extract uses a string-prefix check that doesn't catch
338
+ // `../` inside entry names; consume entries through Parse so we can
339
+ // resolve each path ourselves and reject anything that escapes.
340
+ async function extractZipSafely(input, targetDir) {
341
+ const resolvedTarget = path.resolve(targetDir);
342
+ await pipeline(input, unzipper.Parse(), async (parsed) => {
343
+ for await (const entry of parsed) {
344
+ const dest = resolveZipEntryPath(entry.path, resolvedTarget);
345
+ if (!dest) {
346
+ entry.autodrain();
347
+ throw new Error(`ZIP entry "${entry.path}" escapes the target directory`);
348
+ }
349
+ if (entry.type === 'Directory') {
350
+ await mkdir(dest, { recursive: true });
351
+ entry.autodrain();
352
+ }
353
+ else {
354
+ await mkdir(path.dirname(dest), { recursive: true });
355
+ await pipeline(entry, createWriteStream(dest));
356
+ }
357
+ }
358
+ });
359
+ }
360
+ function errMsg(err) {
361
+ return err instanceof Error ? err.message : String(err);
362
+ }
363
+ //# sourceMappingURL=restore-host-write.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"restore-host-write.js","sourceRoot":"","sources":["../src/restore-host-write.ts"],"names":[],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- (function(){let e=document.createElement(`link`).relList;if(e&&e.supports&&e.supports(`modulepreload`))return;for(let e of document.querySelectorAll(`link[rel="modulepreload"]`))n(e);new MutationObserver(e=>{for(let t of e)if(t.type===`childList`)for(let e of t.addedNodes)e.tagName===`LINK`&&e.rel===`modulepreload`&&n(e)}).observe(document,{childList:!0,subtree:!0});function t(e){let t={};return e.integrity&&(t.integrity=e.integrity),e.referrerPolicy&&(t.referrerPolicy=e.referrerPolicy),e.crossOrigin===`use-credentials`?t.credentials=`include`:e.crossOrigin===`anonymous`?t.credentials=`omit`:t.credentials=`same-origin`,t}function n(e){if(e.ep)return;e.ep=!0;let n=t(e);fetch(e.href,n)}})();var e=`modulepreload`,t=function(e){return`/signalk-backup/`+e},n={},r=function(r,i,a){let o=Promise.resolve();if(i&&i.length>0){let r=document.getElementsByTagName(`link`),s=document.querySelector(`meta[property=csp-nonce]`),c=s?.nonce||s?.getAttribute(`nonce`);function l(e){return Promise.all(e.map(e=>Promise.resolve(e).then(e=>({status:`fulfilled`,value:e}),e=>({status:`rejected`,reason:e}))))}o=l(i.map(i=>{if(i=t(i,a),i in n)return;n[i]=!0;let o=i.endsWith(`.css`),s=o?`[rel="stylesheet"]`:``;if(a)for(let e=r.length-1;e>=0;e--){let t=r[e];if(t.href===i&&(!o||t.rel===`stylesheet`))return}else if(document.querySelector(`link[href="${i}"]${s}`))return;let l=document.createElement(`link`);if(l.rel=o?`stylesheet`:e,o||(l.as=`script`),l.crossOrigin=``,l.href=i,c&&l.setAttribute(`nonce`,c),document.head.appendChild(l),o)return new Promise((e,t)=>{l.addEventListener(`load`,e),l.addEventListener(`error`,()=>t(Error(`Unable to preload CSS for ${i}`)))})}))}function s(e){let t=new Event(`vite:preloadError`,{cancelable:!0});if(t.payload=e,window.dispatchEvent(t),!t.defaultPrevented)throw e}return o.then(e=>{for(let t of e||[])t.status===`rejected`&&s(t.reason);return r().catch(s)})};async function i(){let e=await fetch(`/admin/.vite/manifest.json`);if(!e.ok)return;let t=await e.json(),n=new Set;for(let e of Object.values(t))if(e){for(let t of e.css??[])n.add(t);typeof e.file==`string`&&e.file.endsWith(`.css`)&&n.add(e.file)}await Promise.all([...n].map(e=>new Promise(t=>{let n=document.createElement(`link`);n.rel=`stylesheet`,n.href=`/admin/`+e,n.onload=t,n.onerror=t,document.head.appendChild(n)})))}try{await i()}catch(e){console.warn(`signalk-backup: admin CSS not available`,e)}finally{await r(()=>import(`./main-Bb6YhYnz.js`),[])}
2
- //# sourceMappingURL=index-BbZnGesz.js.map
1
+ (function(){let e=document.createElement(`link`).relList;if(e&&e.supports&&e.supports(`modulepreload`))return;for(let e of document.querySelectorAll(`link[rel="modulepreload"]`))n(e);new MutationObserver(e=>{for(let t of e)if(t.type===`childList`)for(let e of t.addedNodes)e.tagName===`LINK`&&e.rel===`modulepreload`&&n(e)}).observe(document,{childList:!0,subtree:!0});function t(e){let t={};return e.integrity&&(t.integrity=e.integrity),e.referrerPolicy&&(t.referrerPolicy=e.referrerPolicy),e.crossOrigin===`use-credentials`?t.credentials=`include`:e.crossOrigin===`anonymous`?t.credentials=`omit`:t.credentials=`same-origin`,t}function n(e){if(e.ep)return;e.ep=!0;let n=t(e);fetch(e.href,n)}})();var e=`modulepreload`,t=function(e){return`/signalk-backup/`+e},n={},r=function(r,i,a){let o=Promise.resolve();if(i&&i.length>0){let r=document.getElementsByTagName(`link`),s=document.querySelector(`meta[property=csp-nonce]`),c=s?.nonce||s?.getAttribute(`nonce`);function l(e){return Promise.all(e.map(e=>Promise.resolve(e).then(e=>({status:`fulfilled`,value:e}),e=>({status:`rejected`,reason:e}))))}o=l(i.map(i=>{if(i=t(i,a),i in n)return;n[i]=!0;let o=i.endsWith(`.css`),s=o?`[rel="stylesheet"]`:``;if(a)for(let e=r.length-1;e>=0;e--){let t=r[e];if(t.href===i&&(!o||t.rel===`stylesheet`))return}else if(document.querySelector(`link[href="${i}"]${s}`))return;let l=document.createElement(`link`);if(l.rel=o?`stylesheet`:e,o||(l.as=`script`),l.crossOrigin=``,l.href=i,c&&l.setAttribute(`nonce`,c),document.head.appendChild(l),o)return new Promise((e,t)=>{l.addEventListener(`load`,e),l.addEventListener(`error`,()=>t(Error(`Unable to preload CSS for ${i}`)))})}))}function s(e){let t=new Event(`vite:preloadError`,{cancelable:!0});if(t.payload=e,window.dispatchEvent(t),!t.defaultPrevented)throw e}return o.then(e=>{for(let t of e||[])t.status===`rejected`&&s(t.reason);return r().catch(s)})};async function i(){let e=await fetch(`/admin/.vite/manifest.json`);if(!e.ok)return;let t=await e.json(),n=new Set;for(let e of Object.values(t))if(e){for(let t of e.css??[])n.add(t);typeof e.file==`string`&&e.file.endsWith(`.css`)&&n.add(e.file)}await Promise.all([...n].map(e=>new Promise(t=>{let n=document.createElement(`link`);n.rel=`stylesheet`,n.href=`/admin/`+e,n.onload=t,n.onerror=t,document.head.appendChild(n)})))}try{await i()}catch(e){console.warn(`signalk-backup: admin CSS not available`,e)}finally{await r(()=>import(`./main-B53J3gmZ.js`),[])}
2
+ //# sourceMappingURL=index-C8j20cZ-.js.map
@@ -1 +1 @@
1
- {"version":3,"mappings":"m2DACM,eAAe,GAAiB,CAC9B,IAAM,EAAM,MAAM,MAAM,4BAA4B,EACpD,GAAI,CAAC,EAAI,GAAI,OACb,IAAM,EAAW,MAAM,EAAI,KAAK,EAC1B,EAAW,IAAI,IAMrB,IAAK,IAAM,KAAS,OAAO,OAAO,CAAQ,EACnC,KACL,KAAK,IAAM,KAAO,EAAM,KAAO,CAAC,EAAG,EAAS,IAAI,CAAG,EAC/C,OAAO,EAAM,MAAS,UAAY,EAAM,KAAK,SAAS,MAAM,GAC9D,EAAS,IAAI,EAAM,IAAI,CAF0B,CAKrD,MAAM,QAAQ,IACZ,CAAC,GAAG,CAAQ,EAAE,IACX,GACC,IAAI,QAAS,GAAY,CACvB,IAAM,EAAO,SAAS,cAAc,MAAM,EAC1C,EAAK,IAAM,aACX,EAAK,KAAO,UAAY,EACxB,EAAK,OAAS,EACd,EAAK,QAAU,EACf,SAAS,KAAK,YAAY,CAAI,CAChC,CAAC,CACL,CACF,CACF,CAEA,GAAI,CACF,MAAM,EAAe,CACvB,OAAS,EAAK,CACZ,QAAQ,KAAK,0CAA2C,CAAG,CAC7D,QAAU,CACR,YAAM,OAAO,yBACf","names":[],"ignoreList":[],"sources":["../../webapp/index.html?html-proxy&index=0.js"],"sourcesContent":["\n async function injectAdminCss() {\n const res = await fetch('/admin/.vite/manifest.json')\n if (!res.ok) return\n const manifest = await res.json()\n const cssFiles = new Set()\n // Two shapes are common: an entry whose `css` array lists its\n // stylesheet imports, OR a top-level entry whose `file` itself\n // ends in `.css` (when Vite extracts a global stylesheet). The\n // current SignalK admin uses the second shape; older builds and\n // the docs example use the first. Cover both.\n for (const entry of Object.values(manifest)) {\n if (!entry) continue\n for (const css of entry.css ?? []) cssFiles.add(css)\n if (typeof entry.file === 'string' && entry.file.endsWith('.css')) {\n cssFiles.add(entry.file)\n }\n }\n await Promise.all(\n [...cssFiles].map(\n (css) =>\n new Promise((resolve) => {\n const link = document.createElement('link')\n link.rel = 'stylesheet'\n link.href = '/admin/' + css\n link.onload = resolve\n link.onerror = resolve\n document.head.appendChild(link)\n })\n )\n )\n }\n\n try {\n await injectAdminCss()\n } catch (err) {\n console.warn('signalk-backup: admin CSS not available', err)\n } finally {\n await import('./src/main.tsx')\n }\n "],"file":"index-BbZnGesz.js"}
1
+ {"version":3,"mappings":"m2DACM,eAAe,GAAiB,CAC9B,IAAM,EAAM,MAAM,MAAM,4BAA4B,EACpD,GAAI,CAAC,EAAI,GAAI,OACb,IAAM,EAAW,MAAM,EAAI,KAAK,EAC1B,EAAW,IAAI,IAMrB,IAAK,IAAM,KAAS,OAAO,OAAO,CAAQ,EACnC,KACL,KAAK,IAAM,KAAO,EAAM,KAAO,CAAC,EAAG,EAAS,IAAI,CAAG,EAC/C,OAAO,EAAM,MAAS,UAAY,EAAM,KAAK,SAAS,MAAM,GAC9D,EAAS,IAAI,EAAM,IAAI,CAF0B,CAKrD,MAAM,QAAQ,IACZ,CAAC,GAAG,CAAQ,EAAE,IACX,GACC,IAAI,QAAS,GAAY,CACvB,IAAM,EAAO,SAAS,cAAc,MAAM,EAC1C,EAAK,IAAM,aACX,EAAK,KAAO,UAAY,EACxB,EAAK,OAAS,EACd,EAAK,QAAU,EACf,SAAS,KAAK,YAAY,CAAI,CAChC,CAAC,CACL,CACF,CACF,CAEA,GAAI,CACF,MAAM,EAAe,CACvB,OAAS,EAAK,CACZ,QAAQ,KAAK,0CAA2C,CAAG,CAC7D,QAAU,CACR,YAAM,OAAO,yBACf","names":[],"ignoreList":[],"sources":["../../webapp/index.html?html-proxy&index=0.js"],"sourcesContent":["\n async function injectAdminCss() {\n const res = await fetch('/admin/.vite/manifest.json')\n if (!res.ok) return\n const manifest = await res.json()\n const cssFiles = new Set()\n // Two shapes are common: an entry whose `css` array lists its\n // stylesheet imports, OR a top-level entry whose `file` itself\n // ends in `.css` (when Vite extracts a global stylesheet). The\n // current SignalK admin uses the second shape; older builds and\n // the docs example use the first. Cover both.\n for (const entry of Object.values(manifest)) {\n if (!entry) continue\n for (const css of entry.css ?? []) cssFiles.add(css)\n if (typeof entry.file === 'string' && entry.file.endsWith('.css')) {\n cssFiles.add(entry.file)\n }\n }\n await Promise.all(\n [...cssFiles].map(\n (css) =>\n new Promise((resolve) => {\n const link = document.createElement('link')\n link.rel = 'stylesheet'\n link.href = '/admin/' + css\n link.onload = resolve\n link.onerror = resolve\n document.head.appendChild(link)\n })\n )\n )\n }\n\n try {\n await injectAdminCss()\n } catch (err) {\n console.warn('signalk-backup: admin CSS not available', err)\n } finally {\n await import('./src/main.tsx')\n }\n "],"file":"index-C8j20cZ-.js"}