zen-fs-config 0.5.40 → 0.5.41

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.
@@ -9,7 +9,7 @@ import {
9
9
  registerBackend,
10
10
  unregisterBackend,
11
11
  wrapZenFSFileSystem
12
- } from "./chunk-NSEXSKVW.mjs";
12
+ } from "./chunk-S2S3XBK2.mjs";
13
13
  export {
14
14
  createBackend,
15
15
  getAccountFields,
@@ -277,9 +277,9 @@ registerBackend("GitHub", async (options) => {
277
277
  registerBackend("Gitee", async (options) => {
278
278
  const _gt = await importOptionalBackend("zen-fs-gitee");
279
279
  const Gitee = _gt?.Gitee ?? _gt?.default;
280
- if (typeof Gitee !== "function") {
280
+ if (!Gitee || typeof Gitee !== "function" && typeof Gitee.create !== "function") {
281
281
  throw new Error(
282
- `Backend package "zen-fs-gitee" loaded, but its Gitee export is not a constructor (typeof=${typeof Gitee}). Load its UMD bundle so it exposes the global "ZenFSGitee".`
282
+ `Backend package "zen-fs-gitee" loaded, but its Gitee export is not a usable backend (typeof=${typeof Gitee}). Load its UMD bundle so it exposes the global "ZenFSGitee".`
283
283
  );
284
284
  }
285
285
  return wrapZenFSFileSystem({
@@ -306,6 +306,39 @@ registerBackend("Gitee", async (options) => {
306
306
  // identity = the Git repo location
307
307
  identityFields: ["owner", "repo", "branch", "baseUrl"]
308
308
  });
309
+ async function resolveRemoteStorageHref(href) {
310
+ const raw = String(href ?? "").trim();
311
+ if (!raw) throw new Error("RemoteStorage href is required.");
312
+ if (/^[a-z][a-z0-9+.-]*:\/\//i.test(raw)) return raw.replace(/\/+$/, "");
313
+ const match = raw.match(/^(?:acct:)?([^@/]+)@([^@/]+)$/);
314
+ if (!match) {
315
+ throw new Error(
316
+ `RemoteStorage href "${raw}" is neither an absolute URL nor a user address (user@domain).`
317
+ );
318
+ }
319
+ const user = match[1];
320
+ const domain = match[2];
321
+ const webfingerUrl = `https://${domain}/.well-known/webfinger?resource=acct:${encodeURIComponent(user)}@${encodeURIComponent(domain)}`;
322
+ let response;
323
+ try {
324
+ response = await globalThis.fetch(webfingerUrl, {
325
+ headers: { Accept: "application/jrd+json, application/json" }
326
+ });
327
+ } catch (err) {
328
+ throw new Error(
329
+ `RemoteStorage WebFinger request failed (${webfingerUrl}): ${err instanceof Error ? err.message : String(err)}`
330
+ );
331
+ }
332
+ if (!response.ok) {
333
+ throw new Error(`RemoteStorage WebFinger lookup failed (HTTP ${response.status}): ${webfingerUrl}`);
334
+ }
335
+ const jrd = await response.json();
336
+ const link = (jrd?.links ?? []).find(
337
+ (item) => item && item.rel === "http://tools.ietf.org/id/draft-dejong-remotestorage" && item.href
338
+ );
339
+ if (!link) throw new Error(`WebFinger response has no RemoteStorage link: ${webfingerUrl}`);
340
+ return String(link.href).replace(/\/+$/, "");
341
+ }
309
342
  registerBackend("RemoteStorage", async (options) => {
310
343
  const _rs = await importOptionalBackend("zen-fs-remotestoragejs");
311
344
  const RemoteStorageFileSystem = _rs?.RemoteStorageFileSystem ?? _rs?.default ?? (typeof _rs === "function" ? _rs : void 0);
@@ -315,7 +348,7 @@ registerBackend("RemoteStorage", async (options) => {
315
348
  );
316
349
  }
317
350
  return new RemoteStorageFileSystem({
318
- href: options.href,
351
+ href: await resolveRemoteStorageHref(options.href),
319
352
  token: options.token,
320
353
  basePath: options.basePath || void 0,
321
354
  preciseMtime: true,
@@ -326,7 +359,7 @@ registerBackend("RemoteStorage", async (options) => {
326
359
  label: "RemoteStorage",
327
360
  icon: "\u{1F4E1}",
328
361
  fields: [
329
- { key: "href", label: "User Address (href)", type: "text", placeholder: "user@5apps.com", required: true },
362
+ { key: "href", label: "User Address / Storage URL (href)", type: "text", placeholder: "user@5apps.com \u6216 https://storage.5apps.com/user", required: true },
330
363
  { key: "token", label: "Bearer Token", type: "password", placeholder: "rs-xxxxxxxx", required: true },
331
364
  { key: "basePath", label: "Base Path", type: "text", placeholder: "/zen-fs-config/" }
332
365
  ],
package/dist/index.js CHANGED
@@ -215,6 +215,39 @@ async function importOptionalBackend(pkg) {
215
215
  );
216
216
  }
217
217
  }
218
+ async function resolveRemoteStorageHref(href) {
219
+ const raw = String(href ?? "").trim();
220
+ if (!raw) throw new Error("RemoteStorage href is required.");
221
+ if (/^[a-z][a-z0-9+.-]*:\/\//i.test(raw)) return raw.replace(/\/+$/, "");
222
+ const match = raw.match(/^(?:acct:)?([^@/]+)@([^@/]+)$/);
223
+ if (!match) {
224
+ throw new Error(
225
+ `RemoteStorage href "${raw}" is neither an absolute URL nor a user address (user@domain).`
226
+ );
227
+ }
228
+ const user = match[1];
229
+ const domain = match[2];
230
+ const webfingerUrl = `https://${domain}/.well-known/webfinger?resource=acct:${encodeURIComponent(user)}@${encodeURIComponent(domain)}`;
231
+ let response;
232
+ try {
233
+ response = await globalThis.fetch(webfingerUrl, {
234
+ headers: { Accept: "application/jrd+json, application/json" }
235
+ });
236
+ } catch (err) {
237
+ throw new Error(
238
+ `RemoteStorage WebFinger request failed (${webfingerUrl}): ${err instanceof Error ? err.message : String(err)}`
239
+ );
240
+ }
241
+ if (!response.ok) {
242
+ throw new Error(`RemoteStorage WebFinger lookup failed (HTTP ${response.status}): ${webfingerUrl}`);
243
+ }
244
+ const jrd = await response.json();
245
+ const link = (jrd?.links ?? []).find(
246
+ (item) => item && item.rel === "http://tools.ietf.org/id/draft-dejong-remotestorage" && item.href
247
+ );
248
+ if (!link) throw new Error(`WebFinger response has no RemoteStorage link: ${webfingerUrl}`);
249
+ return String(link.href).replace(/\/+$/, "");
250
+ }
218
251
  var registry, metadataRegistry, inMemoryCounter, _memCache, OPTIONAL_BACKEND_GLOBALS;
219
252
  var init_backend_registry = __esm({
220
253
  "src/backend-registry.ts"() {
@@ -326,9 +359,9 @@ var init_backend_registry = __esm({
326
359
  registerBackend("Gitee", async (options) => {
327
360
  const _gt = await importOptionalBackend("zen-fs-gitee");
328
361
  const Gitee = _gt?.Gitee ?? _gt?.default;
329
- if (typeof Gitee !== "function") {
362
+ if (!Gitee || typeof Gitee !== "function" && typeof Gitee.create !== "function") {
330
363
  throw new Error(
331
- `Backend package "zen-fs-gitee" loaded, but its Gitee export is not a constructor (typeof=${typeof Gitee}). Load its UMD bundle so it exposes the global "ZenFSGitee".`
364
+ `Backend package "zen-fs-gitee" loaded, but its Gitee export is not a usable backend (typeof=${typeof Gitee}). Load its UMD bundle so it exposes the global "ZenFSGitee".`
332
365
  );
333
366
  }
334
367
  return wrapZenFSFileSystem({
@@ -364,7 +397,7 @@ var init_backend_registry = __esm({
364
397
  );
365
398
  }
366
399
  return new RemoteStorageFileSystem({
367
- href: options.href,
400
+ href: await resolveRemoteStorageHref(options.href),
368
401
  token: options.token,
369
402
  basePath: options.basePath || void 0,
370
403
  preciseMtime: true,
@@ -375,7 +408,7 @@ var init_backend_registry = __esm({
375
408
  label: "RemoteStorage",
376
409
  icon: "\u{1F4E1}",
377
410
  fields: [
378
- { key: "href", label: "User Address (href)", type: "text", placeholder: "user@5apps.com", required: true },
411
+ { key: "href", label: "User Address / Storage URL (href)", type: "text", placeholder: "user@5apps.com \u6216 https://storage.5apps.com/user", required: true },
379
412
  { key: "token", label: "Bearer Token", type: "password", placeholder: "rs-xxxxxxxx", required: true },
380
413
  { key: "basePath", label: "Base Path", type: "text", placeholder: "/zen-fs-config/" }
381
414
  ],
@@ -1418,6 +1451,12 @@ function identityOptions(type, options) {
1418
1451
  for (const f of fields) {
1419
1452
  if (options && options[f] !== void 0) subset[f] = options[f];
1420
1453
  }
1454
+ if (type === "Gitee" || type === "GitHub") {
1455
+ const defaultEndpoint = type === "Gitee" ? "https://gitee.com/api/v5" : "https://api.github.com";
1456
+ const raw = options && options.baseUrl;
1457
+ const value = typeof raw === "string" && raw.trim() !== "" ? raw : defaultEndpoint;
1458
+ subset["baseUrl"] = value.replace(/\/+$/, "");
1459
+ }
1421
1460
  return subset;
1422
1461
  }
1423
1462
  function backendDedupKey(desc) {
@@ -2808,10 +2847,15 @@ var ConfigRepo = class {
2808
2847
  }
2809
2848
  const descPath = this.appDataGroupFilePath(id);
2810
2849
  try {
2811
- await this.cachedFS.unlink(descPath);
2812
- } catch {
2850
+ await this.deleteFile(descPath);
2851
+ } catch (err) {
2852
+ log2.log(`[ConfigRepo] removeAppDataGroup: deleteFile failed for "${id}":`, err);
2853
+ try {
2854
+ await this.cachedFS.unlink(descPath);
2855
+ } catch {
2856
+ }
2857
+ await this.unlinkVersionSidecar(this.cachedFS, descPath);
2813
2858
  }
2814
- await this.unlinkVersionSidecar(this.cachedFS, descPath);
2815
2859
  log2.log(`[ConfigRepo] removeAppDataGroup: "${id}" removed`);
2816
2860
  }
2817
2861
  async listAccountBackends() {
@@ -3194,12 +3238,25 @@ async function connect(appId, options = {}) {
3194
3238
  onConflict: options.onConflict,
3195
3239
  syncPollIntervalMs: options.syncPollIntervalMs
3196
3240
  });
3197
- const dataGroup2 = await repo2.createAppDataGroup("default", []);
3241
+ const appDataGroups2 = [];
3242
+ const descriptors2 = await repo2.listAppDataGroups();
3243
+ for (const d of descriptors2) {
3244
+ try {
3245
+ appDataGroups2.push(await repo2.getAppDataGroup(d.id));
3246
+ } catch (err) {
3247
+ log3(`connect: failed to load app data group "${d.id}":`, err);
3248
+ }
3249
+ }
3250
+ let dataGroup2 = appDataGroups2[0];
3251
+ if (!dataGroup2) {
3252
+ dataGroup2 = await repo2.createAppDataGroup("default", []);
3253
+ appDataGroups2.push(dataGroup2);
3254
+ }
3198
3255
  return {
3199
3256
  groupType: options.groupType ?? "config-sync",
3200
3257
  repo: repo2,
3201
3258
  dataGroup: dataGroup2,
3202
- appDataGroups: [dataGroup2]
3259
+ appDataGroups: appDataGroups2
3203
3260
  };
3204
3261
  }
3205
3262
  const { type, options: backendOptions } = options.backendInfo;
package/dist/index.mjs CHANGED
@@ -9,7 +9,7 @@ import {
9
9
  registerBackend,
10
10
  unregisterBackend,
11
11
  wrapZenFSFileSystem
12
- } from "./chunk-NSEXSKVW.mjs";
12
+ } from "./chunk-S2S3XBK2.mjs";
13
13
 
14
14
  // src/config-repo.ts
15
15
  import {
@@ -351,7 +351,7 @@ function registerFolderBackend() {
351
351
  const pathMod = await import("path");
352
352
  const dir = options.path;
353
353
  const store = new FolderStore({ path: dir, label: options.label, fs, pathMod });
354
- return (await import("./backend-registry-ZFTXH537.mjs")).wrapZenFSFileSystem({ backend: FolderBackend, path: dir, label: options.label });
354
+ return (await import("./backend-registry-RBKWNJZI.mjs")).wrapZenFSFileSystem({ backend: FolderBackend, path: dir, label: options.label });
355
355
  },
356
356
  {
357
357
  type: "Folder",
@@ -911,6 +911,12 @@ function identityOptions(type, options) {
911
911
  for (const f of fields) {
912
912
  if (options && options[f] !== void 0) subset[f] = options[f];
913
913
  }
914
+ if (type === "Gitee" || type === "GitHub") {
915
+ const defaultEndpoint = type === "Gitee" ? "https://gitee.com/api/v5" : "https://api.github.com";
916
+ const raw = options && options.baseUrl;
917
+ const value = typeof raw === "string" && raw.trim() !== "" ? raw : defaultEndpoint;
918
+ subset["baseUrl"] = value.replace(/\/+$/, "");
919
+ }
914
920
  return subset;
915
921
  }
916
922
  function backendDedupKey(desc) {
@@ -2301,10 +2307,15 @@ var ConfigRepo = class {
2301
2307
  }
2302
2308
  const descPath = this.appDataGroupFilePath(id);
2303
2309
  try {
2304
- await this.cachedFS.unlink(descPath);
2305
- } catch {
2310
+ await this.deleteFile(descPath);
2311
+ } catch (err) {
2312
+ log2.log(`[ConfigRepo] removeAppDataGroup: deleteFile failed for "${id}":`, err);
2313
+ try {
2314
+ await this.cachedFS.unlink(descPath);
2315
+ } catch {
2316
+ }
2317
+ await this.unlinkVersionSidecar(this.cachedFS, descPath);
2306
2318
  }
2307
- await this.unlinkVersionSidecar(this.cachedFS, descPath);
2308
2319
  log2.log(`[ConfigRepo] removeAppDataGroup: "${id}" removed`);
2309
2320
  }
2310
2321
  async listAccountBackends() {
@@ -2686,12 +2697,25 @@ async function connect(appId, options = {}) {
2686
2697
  onConflict: options.onConflict,
2687
2698
  syncPollIntervalMs: options.syncPollIntervalMs
2688
2699
  });
2689
- const dataGroup2 = await repo2.createAppDataGroup("default", []);
2700
+ const appDataGroups2 = [];
2701
+ const descriptors2 = await repo2.listAppDataGroups();
2702
+ for (const d of descriptors2) {
2703
+ try {
2704
+ appDataGroups2.push(await repo2.getAppDataGroup(d.id));
2705
+ } catch (err) {
2706
+ log3(`connect: failed to load app data group "${d.id}":`, err);
2707
+ }
2708
+ }
2709
+ let dataGroup2 = appDataGroups2[0];
2710
+ if (!dataGroup2) {
2711
+ dataGroup2 = await repo2.createAppDataGroup("default", []);
2712
+ appDataGroups2.push(dataGroup2);
2713
+ }
2690
2714
  return {
2691
2715
  groupType: options.groupType ?? "config-sync",
2692
2716
  repo: repo2,
2693
2717
  dataGroup: dataGroup2,
2694
- appDataGroups: [dataGroup2]
2718
+ appDataGroups: appDataGroups2
2695
2719
  };
2696
2720
  }
2697
2721
  const { type, options: backendOptions } = options.backendInfo;
@@ -24528,6 +24528,39 @@ var ZenFSConfig = (() => {
24528
24528
  );
24529
24529
  }
24530
24530
  }
24531
+ async function resolveRemoteStorageHref(href) {
24532
+ const raw = String(href ?? "").trim();
24533
+ if (!raw) throw new Error("RemoteStorage href is required.");
24534
+ if (/^[a-z][a-z0-9+.-]*:\/\//i.test(raw)) return raw.replace(/\/+$/, "");
24535
+ const match = raw.match(/^(?:acct:)?([^@/]+)@([^@/]+)$/);
24536
+ if (!match) {
24537
+ throw new Error(
24538
+ `RemoteStorage href "${raw}" is neither an absolute URL nor a user address (user@domain).`
24539
+ );
24540
+ }
24541
+ const user = match[1];
24542
+ const domain = match[2];
24543
+ const webfingerUrl = `https://${domain}/.well-known/webfinger?resource=acct:${encodeURIComponent(user)}@${encodeURIComponent(domain)}`;
24544
+ let response;
24545
+ try {
24546
+ response = await globalThis.fetch(webfingerUrl, {
24547
+ headers: { Accept: "application/jrd+json, application/json" }
24548
+ });
24549
+ } catch (err2) {
24550
+ throw new Error(
24551
+ `RemoteStorage WebFinger request failed (${webfingerUrl}): ${err2 instanceof Error ? err2.message : String(err2)}`
24552
+ );
24553
+ }
24554
+ if (!response.ok) {
24555
+ throw new Error(`RemoteStorage WebFinger lookup failed (HTTP ${response.status}): ${webfingerUrl}`);
24556
+ }
24557
+ const jrd = await response.json();
24558
+ const link5 = (jrd?.links ?? []).find(
24559
+ (item) => item && item.rel === "http://tools.ietf.org/id/draft-dejong-remotestorage" && item.href
24560
+ );
24561
+ if (!link5) throw new Error(`WebFinger response has no RemoteStorage link: ${webfingerUrl}`);
24562
+ return String(link5.href).replace(/\/+$/, "");
24563
+ }
24531
24564
  var registry, metadataRegistry, inMemoryCounter, _memCache, OPTIONAL_BACKEND_GLOBALS;
24532
24565
  var init_backend_registry = __esm({
24533
24566
  "src/backend-registry.ts"() {
@@ -24639,9 +24672,9 @@ var ZenFSConfig = (() => {
24639
24672
  registerBackend("Gitee", async (options) => {
24640
24673
  const _gt = await importOptionalBackend("zen-fs-gitee");
24641
24674
  const Gitee = _gt?.Gitee ?? _gt?.default;
24642
- if (typeof Gitee !== "function") {
24675
+ if (!Gitee || typeof Gitee !== "function" && typeof Gitee.create !== "function") {
24643
24676
  throw new Error(
24644
- `Backend package "zen-fs-gitee" loaded, but its Gitee export is not a constructor (typeof=${typeof Gitee}). Load its UMD bundle so it exposes the global "ZenFSGitee".`
24677
+ `Backend package "zen-fs-gitee" loaded, but its Gitee export is not a usable backend (typeof=${typeof Gitee}). Load its UMD bundle so it exposes the global "ZenFSGitee".`
24645
24678
  );
24646
24679
  }
24647
24680
  return wrapZenFSFileSystem({
@@ -24677,7 +24710,7 @@ var ZenFSConfig = (() => {
24677
24710
  );
24678
24711
  }
24679
24712
  return new RemoteStorageFileSystem({
24680
- href: options.href,
24713
+ href: await resolveRemoteStorageHref(options.href),
24681
24714
  token: options.token,
24682
24715
  basePath: options.basePath || void 0,
24683
24716
  preciseMtime: true,
@@ -24688,7 +24721,7 @@ var ZenFSConfig = (() => {
24688
24721
  label: "RemoteStorage",
24689
24722
  icon: "\u{1F4E1}",
24690
24723
  fields: [
24691
- { key: "href", label: "User Address (href)", type: "text", placeholder: "user@5apps.com", required: true },
24724
+ { key: "href", label: "User Address / Storage URL (href)", type: "text", placeholder: "user@5apps.com \u6216 https://storage.5apps.com/user", required: true },
24692
24725
  { key: "token", label: "Bearer Token", type: "password", placeholder: "rs-xxxxxxxx", required: true },
24693
24726
  { key: "basePath", label: "Base Path", type: "text", placeholder: "/zen-fs-config/" }
24694
24727
  ],
@@ -27351,6 +27384,12 @@ var ZenFSConfig = (() => {
27351
27384
  for (const f of fields) {
27352
27385
  if (options && options[f] !== void 0) subset[f] = options[f];
27353
27386
  }
27387
+ if (type === "Gitee" || type === "GitHub") {
27388
+ const defaultEndpoint = type === "Gitee" ? "https://gitee.com/api/v5" : "https://api.github.com";
27389
+ const raw = options && options.baseUrl;
27390
+ const value = typeof raw === "string" && raw.trim() !== "" ? raw : defaultEndpoint;
27391
+ subset["baseUrl"] = value.replace(/\/+$/, "");
27392
+ }
27354
27393
  return subset;
27355
27394
  }
27356
27395
  function backendDedupKey(desc) {
@@ -28741,10 +28780,15 @@ var ZenFSConfig = (() => {
28741
28780
  }
28742
28781
  const descPath = this.appDataGroupFilePath(id);
28743
28782
  try {
28744
- await this.cachedFS.unlink(descPath);
28745
- } catch {
28783
+ await this.deleteFile(descPath);
28784
+ } catch (err2) {
28785
+ log6.log(`[ConfigRepo] removeAppDataGroup: deleteFile failed for "${id}":`, err2);
28786
+ try {
28787
+ await this.cachedFS.unlink(descPath);
28788
+ } catch {
28789
+ }
28790
+ await this.unlinkVersionSidecar(this.cachedFS, descPath);
28746
28791
  }
28747
- await this.unlinkVersionSidecar(this.cachedFS, descPath);
28748
28792
  log6.log(`[ConfigRepo] removeAppDataGroup: "${id}" removed`);
28749
28793
  }
28750
28794
  async listAccountBackends() {
@@ -29127,12 +29171,25 @@ var ZenFSConfig = (() => {
29127
29171
  onConflict: options.onConflict,
29128
29172
  syncPollIntervalMs: options.syncPollIntervalMs
29129
29173
  });
29130
- const dataGroup2 = await repo2.createAppDataGroup("default", []);
29174
+ const appDataGroups2 = [];
29175
+ const descriptors2 = await repo2.listAppDataGroups();
29176
+ for (const d of descriptors2) {
29177
+ try {
29178
+ appDataGroups2.push(await repo2.getAppDataGroup(d.id));
29179
+ } catch (err2) {
29180
+ log7(`connect: failed to load app data group "${d.id}":`, err2);
29181
+ }
29182
+ }
29183
+ let dataGroup2 = appDataGroups2[0];
29184
+ if (!dataGroup2) {
29185
+ dataGroup2 = await repo2.createAppDataGroup("default", []);
29186
+ appDataGroups2.push(dataGroup2);
29187
+ }
29131
29188
  return {
29132
29189
  groupType: options.groupType ?? "config-sync",
29133
29190
  repo: repo2,
29134
29191
  dataGroup: dataGroup2,
29135
- appDataGroups: [dataGroup2]
29192
+ appDataGroups: appDataGroups2
29136
29193
  };
29137
29194
  }
29138
29195
  const { type, options: backendOptions } = options.backendInfo;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zen-fs-config",
3
- "version": "0.5.40",
3
+ "version": "0.5.41",
4
4
  "description": "Distributed config management library built on ZenFS, zen-fs-cache, and zen-fs-sync",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",