@powerhousedao/connect 6.2.2-dev.9 → 6.2.2-staging.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.
Files changed (108) hide show
  1. package/assets/document-icon-192x192.png +0 -0
  2. package/assets/document-icon-512x512.png +0 -0
  3. package/assets/pwa-192x192.png +0 -0
  4. package/assets/pwa-512x512-maskable.png +0 -0
  5. package/assets/pwa-512x512.png +0 -0
  6. package/dist/{AddDriveModal-MMHlkJBJ.js → AddDriveModal-Q-Ffh5XA.js} +5 -5
  7. package/dist/AddDriveModal-Q-Ffh5XA.js.map +1 -0
  8. package/dist/{ClearStorageModal-BrqS8I-J.js → ClearStorageModal-DXV7eveG.js} +4 -4
  9. package/dist/{ClearStorageModal-BrqS8I-J.js.map → ClearStorageModal-DXV7eveG.js.map} +1 -1
  10. package/dist/ConfirmDocumentUpgradeModal-FUOhZsk7.js +114 -0
  11. package/dist/ConfirmDocumentUpgradeModal-FUOhZsk7.js.map +1 -0
  12. package/dist/{CookiesPolicyModal-CkcUKPLb.js → CookiesPolicyModal-BpyPPyxS.js} +3 -3
  13. package/dist/{CookiesPolicyModal-CkcUKPLb.js.map → CookiesPolicyModal-BpyPPyxS.js.map} +1 -1
  14. package/dist/{CreateDocumentModal-CvsTh0HM.js → CreateDocumentModal-C45W8J0V.js} +7 -5
  15. package/dist/CreateDocumentModal-C45W8J0V.js.map +1 -0
  16. package/dist/{DebugSettingsModal-CCZi5Gmx.js → DebugSettingsModal-D-8XfrLE.js} +6 -6
  17. package/dist/{DebugSettingsModal-CCZi5Gmx.js.map → DebugSettingsModal-D-8XfrLE.js.map} +1 -1
  18. package/dist/{DeleteDriveModal-DSTK8FYk.js → DeleteDriveModal-Dug84t1j.js} +4 -4
  19. package/dist/{DeleteDriveModal-DSTK8FYk.js.map → DeleteDriveModal-Dug84t1j.js.map} +1 -1
  20. package/dist/{DeleteItemModal-B2nJmjsP.js → DeleteItemModal-mMzy3V8o.js} +4 -4
  21. package/dist/{DeleteItemModal-B2nJmjsP.js.map → DeleteItemModal-mMzy3V8o.js.map} +1 -1
  22. package/dist/{DisclaimerModal-D5FO34u9.js → DisclaimerModal-DvpD5dPM.js} +3 -3
  23. package/dist/{DisclaimerModal-D5FO34u9.js.map → DisclaimerModal-DvpD5dPM.js.map} +1 -1
  24. package/dist/DocumentVersionUnsupportedModal-C3Q0V75l.js +32 -0
  25. package/dist/DocumentVersionUnsupportedModal-C3Q0V75l.js.map +1 -0
  26. package/dist/{DownloadDocumentWithErrorsModal-tEr3bbtN.js → DownloadDocumentWithErrorsModal-IKl0ljQb.js} +3 -3
  27. package/dist/{DownloadDocumentWithErrorsModal-tEr3bbtN.js.map → DownloadDocumentWithErrorsModal-IKl0ljQb.js.map} +1 -1
  28. package/dist/DriveAuthRequiredModal-Cw9eCAQ_.js +36 -0
  29. package/dist/DriveAuthRequiredModal-Cw9eCAQ_.js.map +1 -0
  30. package/dist/{DriveSettingsModal-kR3hQnh4.js → DriveSettingsModal-C6-mRQiv.js} +3 -3
  31. package/dist/{DriveSettingsModal-kR3hQnh4.js.map → DriveSettingsModal-C6-mRQiv.js.map} +1 -1
  32. package/dist/{InspectorModal-BfwRytsl.js → InspectorModal-Fnp57gG_.js} +6 -6
  33. package/dist/{InspectorModal-BfwRytsl.js.map → InspectorModal-Fnp57gG_.js.map} +1 -1
  34. package/dist/LoginModal-CybbOm3N.js +75 -0
  35. package/dist/LoginModal-CybbOm3N.js.map +1 -0
  36. package/dist/{MissingPackageModal-BQcCQbOn.js → MissingPackageModal-C7usWVHQ.js} +18 -6
  37. package/dist/MissingPackageModal-C7usWVHQ.js.map +1 -0
  38. package/dist/OpenFileDocumentsModal-Bp2SFfGY.js +311 -0
  39. package/dist/OpenFileDocumentsModal-Bp2SFfGY.js.map +1 -0
  40. package/dist/SettingsModal-BKyrRL4W.js +568 -0
  41. package/dist/SettingsModal-BKyrRL4W.js.map +1 -0
  42. package/dist/{UpgradeDriveModal-BZGSE9_r.js → UpgradeDriveModal-_8t9slxQ.js} +3 -3
  43. package/dist/{UpgradeDriveModal-BZGSE9_r.js.map → UpgradeDriveModal-_8t9slxQ.js.map} +1 -1
  44. package/dist/app-loader-1ACi7O7G.js +4 -0
  45. package/dist/{app-loader-BqH5S4gP.js → app-loader-9oxJxBSC.js} +839 -560
  46. package/dist/app-loader-9oxJxBSC.js.map +1 -0
  47. package/dist/build-info-Ck0vFY36.js +28 -0
  48. package/dist/build-info-Ck0vFY36.js.map +1 -0
  49. package/dist/components-C5E86_ow.js +4 -0
  50. package/dist/{connect.config-vgUeFoNp.js → connect.config-DaHDEUrw.js} +13 -7
  51. package/dist/connect.config-DaHDEUrw.js.map +1 -0
  52. package/dist/{connection-state-DO_2lAEJ.js → connection-state-3w4liC1X.js} +4 -4
  53. package/dist/{connection-state-DO_2lAEJ.js.map → connection-state-3w4liC1X.js.map} +1 -1
  54. package/dist/cookie-banner-BH6bIq24.js +70 -0
  55. package/dist/cookie-banner-BH6bIq24.js.map +1 -0
  56. package/dist/{reload-connect-toast-D1Iqw9E_.js → i18n-CWO0vYVd.js} +23 -30
  57. package/dist/i18n-CWO0vYVd.js.map +1 -0
  58. package/dist/load-CvwtlhbX.js +131 -0
  59. package/dist/load-CvwtlhbX.js.map +1 -0
  60. package/dist/main.js +1 -1
  61. package/dist/{pglite-seed-z3B1JA38.js → pglite-seed-BqAUvNFw.js} +4 -4
  62. package/dist/{pglite-seed-z3B1JA38.js.map → pglite-seed-BqAUvNFw.js.map} +1 -1
  63. package/dist/preferences-store-_5Cw4_YV.js +104 -0
  64. package/dist/preferences-store-_5Cw4_YV.js.map +1 -0
  65. package/dist/pwa-manifest-link-DIgOSo2Q.js +34 -0
  66. package/dist/pwa-manifest-link-DIgOSo2Q.js.map +1 -0
  67. package/dist/{reactor-ynguOpe7.js → reactor-CrKlyk9q.js} +240 -67
  68. package/dist/reactor-CrKlyk9q.js.map +1 -0
  69. package/dist/reactor.worker.js +11 -6
  70. package/dist/reactor.worker.js.map +1 -1
  71. package/dist/{registerServiceWorker-nJIc0yEY.js → registerServiceWorker-BSzbNfvz.js} +6 -4
  72. package/dist/registerServiceWorker-BSzbNfvz.js.map +1 -0
  73. package/dist/sentry-QXKgQmr2.js +15 -0
  74. package/dist/sentry-QXKgQmr2.js.map +1 -0
  75. package/dist/start-connect.js +1 -1
  76. package/dist/style.css +243 -7
  77. package/dist/{toast-4t-VimvD.js → toast-BXIGd4Eb.js} +8 -5
  78. package/dist/toast-BXIGd4Eb.js.map +1 -0
  79. package/dist/use-renown-login-Cf3va6u_.js +52 -0
  80. package/dist/use-renown-login-Cf3va6u_.js.map +1 -0
  81. package/dist/useRegistryPackages-DMhCvTWq.js +314 -0
  82. package/dist/useRegistryPackages-DMhCvTWq.js.map +1 -0
  83. package/package.json +19 -13
  84. package/public/powerhouse.config.json +3 -0
  85. package/style.css +1 -0
  86. package/dist/AddDriveModal-MMHlkJBJ.js.map +0 -1
  87. package/dist/CreateDocumentModal-CvsTh0HM.js.map +0 -1
  88. package/dist/DriveAuthRequiredModal-BA2vS3oF.js +0 -40
  89. package/dist/DriveAuthRequiredModal-BA2vS3oF.js.map +0 -1
  90. package/dist/MissingPackageModal-BQcCQbOn.js.map +0 -1
  91. package/dist/SettingsModal-CdoFxyJJ.js +0 -289
  92. package/dist/SettingsModal-CdoFxyJJ.js.map +0 -1
  93. package/dist/app-loader-BqH5S4gP.js.map +0 -1
  94. package/dist/app-loader-J8Z4QAD5.js +0 -4
  95. package/dist/build-info-AUWI7jOw.js +0 -18
  96. package/dist/build-info-AUWI7jOw.js.map +0 -1
  97. package/dist/components-B6-PFL8z.js +0 -5
  98. package/dist/connect.config-vgUeFoNp.js.map +0 -1
  99. package/dist/load-BucYuvxH.js +0 -100
  100. package/dist/load-BucYuvxH.js.map +0 -1
  101. package/dist/package-BcKQ9BGX.js +0 -127
  102. package/dist/package-BcKQ9BGX.js.map +0 -1
  103. package/dist/reactor-ynguOpe7.js.map +0 -1
  104. package/dist/registerServiceWorker-nJIc0yEY.js.map +0 -1
  105. package/dist/reload-connect-toast-D1Iqw9E_.js.map +0 -1
  106. package/dist/toast-4t-VimvD.js.map +0 -1
  107. package/dist/useRegistryPackages-BaYtekjQ.js +0 -179
  108. package/dist/useRegistryPackages-BaYtekjQ.js.map +0 -1
@@ -1,24 +1,104 @@
1
1
 
2
- !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="e6d789cd-51cc-5a5a-b3c5-95dea7e29db0")}catch(e){}}();
2
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="bb3bf75e-9d72-5474-a859-7426e7ff2735")}catch(e){}}();
3
3
  import { n as getRuntimeConfig } from "./runtime-config-DL3QjcfM.js";
4
4
  import { a as resolvePgMajorForRuntime, r as loadPGliteModule } from "./pglite-major-B39aOEiE.js";
5
- import { r as getVersion, t as getGitSha } from "./build-info-AUWI7jOw.js";
6
- import { n as buildPHGlobalConfig, o as phGlobalConfig } from "./connect.config-vgUeFoNp.js";
7
- import { a as detectReactorPgMajor, h as RELATIONAL_PGLITE_NAME, m as REACTOR_PGLITE_NAME, n as setWorkerConnectionStatus, o as detectRelationalPgMajor, p as REACTOR_INSTANCE_NAMESPACE } from "./connection-state-DO_2lAEJ.js";
8
- import { n as toast } from "./toast-4t-VimvD.js";
5
+ import { i as getVersion, n as getGitSha } from "./build-info-Ck0vFY36.js";
6
+ import { n as buildPHGlobalConfig, o as phGlobalConfig } from "./connect.config-DaHDEUrw.js";
7
+ import { t as refreshPwaManifestLink } from "./pwa-manifest-link-DIgOSo2Q.js";
8
+ import { a as detectReactorPgMajor, h as RELATIONAL_PGLITE_NAME, m as REACTOR_PGLITE_NAME, n as setWorkerConnectionStatus, o as detectRelationalPgMajor, p as REACTOR_INSTANCE_NAMESPACE } from "./connection-state-3w4liC1X.js";
9
+ import { r as toast } from "./toast-BXIGd4Eb.js";
10
+ import { PWA_IDB_KEY, PWA_IDB_NAME, PWA_IDB_STORE, PWA_IDB_VERSION, mergePwaConfig, withInferredCategory } from "@powerhousedao/shared/connect";
9
11
  import { createRelationalDb } from "@powerhousedao/shared/processors";
10
- import { DocumentModelRegistry, DocumentModelResolver, ReactorEventTypes } from "@powerhousedao/reactor";
12
+ import { DocumentModelRegistry, DocumentModelResolver, ReactorEventTypes, createReactorHostModuleBase } from "@powerhousedao/reactor";
11
13
  import { MessageRouter, RPC_PROTOCOL_VERSION, SyncManagerProxy, connectReactorClient, createInspectorProxy, createPortTransport, createReactorEventBusProxy, createRelationalPgliteProxy, createWorkerAdminClient, postReactorIdentity } from "@powerhousedao/reactor-browser/rpc";
12
14
  import { Kysely } from "kysely";
13
15
  import { PGliteDialect } from "kysely-pglite-dialect";
14
- import { BrowserLocalStorage, ChannelScheme as ChannelScheme$1, DRIVE_DOCUMENT_TYPES, DocumentCache, DocumentChangeType, ReactorBuilder as ReactorBuilder$1, ReactorClientBuilder as ReactorClientBuilder$1, RegistryClient, addPHEventHandlers, addRemoteDrive, createAnalyticsStore, extractDriveSlugFromPath, extractNodeSlugFromPath, getDrives, isDriveAuthError, login, refreshReactorDataClient, setAttachmentService, setDefaultPHGlobalConfig, setDocumentCache, setDriveMetadata, setDrives, setFeatures, setPGliteDB, setPHToast, setPackageDiscoveryService, setReactorClient, setReactorClientModule, setRenown, setSelectedDrive, setSelectedNode, setVetraPackageManager, waitForDocumentReady } from "@powerhousedao/reactor-browser";
16
+ import { BrowserLocalStorage, ChannelScheme as ChannelScheme$1, DRIVE_DOCUMENT_TYPES, DocumentCache, DocumentChangeType, ReactorBuilder as ReactorBuilder$1, ReactorClientBuilder as ReactorClientBuilder$1, RegistryClient, addDrive, addPHEventHandlers, addRemoteDrive, createAnalyticsStore, extractDriveSlugFromPath, extractNodeSlugFromPath, getDrives, isDriveAuthError, login, refreshReactorDataClient, setAttachmentService, setDefaultPHGlobalConfig, setDocumentCache, setDriveMetadata, setDrives, setFeatures, setPGliteDB, setPHToast, setPackageDiscoveryService, setReactorClient, setReactorClientModule, setRenown, setSelectedDrive, setSelectedNode, setVetraPackageManager, waitForDocumentReady } from "@powerhousedao/reactor-browser";
15
17
  import { ConsoleLogger, logger, setLogLevel as setLogLevel$1 } from "document-model";
16
18
  import { BrowserKeyStorage, RenownBuilder, RenownCryptoBuilder, createSignatureVerifier } from "@renown/sdk";
17
- import { createAttachmentClient, createRemoteAttachmentService } from "@powerhousedao/reactor-attachments/client";
19
+ import { PwaConfigSchema } from "@powerhousedao/shared/document-model";
20
+ import { createAttachmentClient, createRemoteAttachmentService, createXhrUploadTransport } from "@powerhousedao/reactor-attachments/client";
18
21
  import { OpenFeature } from "@openfeature/web-sdk";
19
22
  import * as common from "@powerhousedao/powerhouse-vetra-packages";
20
23
  import commonPkg from "@powerhousedao/powerhouse-vetra-packages/package.json" with { type: "json" };
24
+ import { toCdnUrl } from "@powerhousedao/shared/registry/urls";
21
25
  import vetraPkg from "@powerhousedao/vetra/package.json" with { type: "json" };
26
+ //#region src/utils/pwa-idb.ts
27
+ function openPwaIdb() {
28
+ return new Promise((resolve, reject) => {
29
+ const req = indexedDB.open(PWA_IDB_NAME, PWA_IDB_VERSION);
30
+ req.onupgradeneeded = () => {
31
+ if (!req.result.objectStoreNames.contains(PWA_IDB_STORE)) req.result.createObjectStore(PWA_IDB_STORE);
32
+ };
33
+ req.onerror = () => reject(req.error ?? /* @__PURE__ */ new Error("ph-pwa open failed"));
34
+ req.onblocked = () => reject(/* @__PURE__ */ new Error("ph-pwa open blocked"));
35
+ req.onsuccess = () => resolve(req.result);
36
+ });
37
+ }
38
+ /** Write (replace) the single merged fragment record. Best-effort: a failure to
39
+ * mirror the manifest must never break package installation, so callers ignore
40
+ * rejections. No-op when IndexedDB is unavailable (SSR/tests). */
41
+ async function writeMergedPwaFragment(fragment) {
42
+ if (typeof indexedDB === "undefined") return;
43
+ const db = await openPwaIdb();
44
+ try {
45
+ await new Promise((resolve, reject) => {
46
+ const tx = db.transaction(PWA_IDB_STORE, "readwrite");
47
+ tx.oncomplete = () => resolve();
48
+ tx.onerror = () => reject(tx.error ?? /* @__PURE__ */ new Error("PWA fragment write failed"));
49
+ tx.objectStore(PWA_IDB_STORE).put({
50
+ version: 1,
51
+ fragment,
52
+ updatedAt: Date.now()
53
+ }, PWA_IDB_KEY);
54
+ });
55
+ } finally {
56
+ db.close();
57
+ }
58
+ }
59
+ /** Resolve `src` against the package `baseUrl`, absolute srcs passing through.
60
+ * `baseUrl` may itself be relative (e.g. a same-origin-proxied registry, or the
61
+ * dev `/node_modules/…` path), so it is first made absolute against the app
62
+ * origin — `new URL(src, relativeBase)` would otherwise throw and silently
63
+ * leave the src unresolved. A malformed src is left untouched. */
64
+ function toAbsoluteAssetUrl(src, baseUrl) {
65
+ try {
66
+ const origin = typeof location !== "undefined" ? location.href : "http://localhost/";
67
+ return new URL(src, new URL(baseUrl, origin)).toString();
68
+ } catch {
69
+ return src;
70
+ }
71
+ }
72
+ function resolveIconSrc(icon, baseUrl) {
73
+ return {
74
+ ...icon,
75
+ src: toAbsoluteAssetUrl(icon.src, baseUrl)
76
+ };
77
+ }
78
+ /**
79
+ * Rewrite a package fragment's relative asset srcs (manifest icons and
80
+ * file-handler icons) to absolute URLs against the package's base — a runtime
81
+ * package's assets live on the registry CDN, not the app origin, so the served
82
+ * manifest must point there. `baseUrl` null (a local/bundled package whose
83
+ * assets are app-origin-relative) leaves srcs untouched.
84
+ */
85
+ function resolveFragmentAssetUrls(config, baseUrl) {
86
+ if (!baseUrl || !config.manifest) return config;
87
+ const m = config.manifest;
88
+ const resolveIcons = (icons) => icons?.map((icon) => resolveIconSrc(icon, baseUrl));
89
+ return {
90
+ ...config,
91
+ manifest: {
92
+ ...m,
93
+ icons: resolveIcons(m.icons),
94
+ file_handlers: m.file_handlers?.map((handler) => ({
95
+ ...handler,
96
+ icons: resolveIcons(handler.icons)
97
+ }))
98
+ }
99
+ };
100
+ }
101
+ //#endregion
22
102
  //#region src/pglite.db.ts
23
103
  async function createMainThreadPGlite(major, dbName) {
24
104
  const { PGlite } = await loadPGliteModule(major);
@@ -73,7 +153,7 @@ async function getDb() {
73
153
  * Creates a Reactor that plugs into legacy storage but syncs through the new
74
154
  * Reactor GQL API.
75
155
  */
76
- async function createBrowserReactor(documentModelModules, upgradeManifests, renown, documentModelLoader) {
156
+ async function createBrowserReactor(documentModelModules, upgradeManifests, renown, featureFlags, documentModelLoader) {
77
157
  const signerConfig = {
78
158
  signer: renown.signer,
79
159
  verifier: createSignatureVerifier()
@@ -84,7 +164,7 @@ async function createBrowserReactor(documentModelModules, upgradeManifests, reno
84
164
  };
85
165
  const pg = await getReactorPGlite();
86
166
  const logger = new ConsoleLogger(["reactor-client"]);
87
- const builder = new ReactorClientBuilder$1().withLogger(logger).withSigner(signerConfig).withReactorBuilder(new ReactorBuilder$1().withDocumentModelSources(documentModelModules).withUpgradeManifests(upgradeManifests).withChannelScheme(ChannelScheme$1.CONNECT).withJwtHandler(jwtHandler).withKysely(new Kysely({ dialect: new PGliteDialect(pg) })));
167
+ const builder = new ReactorClientBuilder$1().withLogger(logger).withSigner(signerConfig).withReactorBuilder(new ReactorBuilder$1().withDocumentModelSources(documentModelModules).withUpgradeManifests(upgradeManifests).withChannelScheme(ChannelScheme$1.CONNECT).withExecutorConfig({ featureFlags }).withJwtHandler(jwtHandler).withKysely(new Kysely({ dialect: new PGliteDialect(pg) })));
88
168
  if (documentModelLoader) builder.withDocumentModelLoader(documentModelLoader);
89
169
  const module = await builder.buildModule();
90
170
  return {
@@ -103,40 +183,74 @@ function getDefaultDrives(runtimeConfig) {
103
183
  * Add default drives for the new reactor via sync manager.
104
184
  *
105
185
  * Drives register concurrently so a slow or unreachable drive can't delay the
106
- * others — in particular the one the URL slug resolves to. Retries with linear
107
- * backoff to handle the common race where Connect's dev server is ready before
108
- * the switchboard has finished binding its port.
186
+ * others — in particular the one the URL slug resolves to. Remote drives
187
+ * retry with linear backoff to handle the common race where Connect's dev
188
+ * server is ready before the switchboard has finished binding its port.
109
189
  *
110
- * @param drives - Array of drive objects with url, optional name and icon
190
+ * Each entry is either a remote drive (a URL registered with the sync
191
+ * manager, waited on for initial backfill) or a local drive (created once in
192
+ * this browser's local reactor; its fixed id makes later boots skip it).
193
+ *
194
+ * @param drives - Array of default-drive entries
111
195
  */
112
196
  async function addDefaultDrivesForNewReactor(drives) {
197
+ await Promise.all(drives.map((drive) => "url" in drive ? addRemoteDefaultDrive(drive) : addLocalDefaultDrive(drive)));
198
+ }
199
+ async function addRemoteDefaultDrive(drive) {
113
200
  const MAX_ATTEMPTS = 3;
114
201
  const BACKOFF_MS = 2e3;
115
- await Promise.all(drives.map(async (drive) => {
116
- let driveId;
117
- for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) try {
118
- driveId = await addRemoteDrive(drive.url);
119
- break;
120
- } catch (error) {
121
- if (isDriveAuthError(error)) break;
122
- if (attempt === MAX_ATTEMPTS) console.error(`Failed to add default drive ${drive.url} after ${MAX_ATTEMPTS} attempts:`, error);
123
- else {
124
- const delay = BACKOFF_MS * attempt;
125
- console.warn(`Default drive ${drive.url} not reachable (attempt ${attempt}/${MAX_ATTEMPTS}), retrying in ${delay}ms...`);
126
- await new Promise((resolve) => setTimeout(resolve, delay));
127
- }
202
+ let driveId;
203
+ for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) try {
204
+ driveId = await addRemoteDrive(drive.url);
205
+ break;
206
+ } catch (error) {
207
+ if (isDriveAuthError(error)) break;
208
+ if (attempt === MAX_ATTEMPTS) console.error(`Failed to add default drive ${drive.url} after ${MAX_ATTEMPTS} attempts:`, error);
209
+ else {
210
+ const delay = BACKOFF_MS * attempt;
211
+ console.warn(`Default drive ${drive.url} not reachable (attempt ${attempt}/${MAX_ATTEMPTS}), retrying in ${delay}ms...`);
212
+ await new Promise((resolve) => setTimeout(resolve, delay));
128
213
  }
129
- if (driveId && (drive.name || drive.icon)) try {
130
- const reactorClient = window.ph?.reactorClient;
131
- if (reactorClient) await waitForDocumentReady(reactorClient, driveId, { timeoutMs: 15e3 });
132
- await setDriveMetadata(driveId, {
133
- name: drive.name,
134
- icon: drive.icon
135
- });
136
- } catch (error) {
137
- console.warn(`Default drive ${drive.url} was added but metadata update failed:`, error);
214
+ }
215
+ if (driveId && (drive.name || drive.icon)) try {
216
+ const reactorClient = window.ph?.reactorClientModule?.client;
217
+ if (reactorClient) await waitForDocumentReady(reactorClient, driveId, { timeoutMs: 15e3 });
218
+ await setDriveMetadata(driveId, {
219
+ name: drive.name,
220
+ icon: drive.icon
221
+ });
222
+ } catch (error) {
223
+ console.warn(`Default drive ${drive.url} was added but metadata update failed:`, error);
224
+ }
225
+ }
226
+ /**
227
+ * Create a configured local default drive in this browser's local reactor.
228
+ * The drive is only created when the configured id is not already taken, so
229
+ * repeated boots are idempotent and a drive the user deleted stays deleted.
230
+ * Name and icon are written straight into the created document's global state
231
+ * (no setDriveMetadata round-trip), and `app` maps to the drive's preferred
232
+ * editor.
233
+ */
234
+ async function addLocalDefaultDrive(drive) {
235
+ if (!drive.id) {
236
+ console.error("Ignoring local default drive with no id:", JSON.stringify(drive));
237
+ return;
238
+ }
239
+ try {
240
+ const reactorClient = window.ph?.reactorClientModule?.client;
241
+ if (reactorClient) {
242
+ if (await reactorClient.isDocumentIdTaken(drive.id)) return;
138
243
  }
139
- }));
244
+ await addDrive({
245
+ id: drive.id,
246
+ global: {
247
+ name: drive.name ?? "",
248
+ icon: drive.icon ?? null
249
+ }
250
+ }, drive.app);
251
+ } catch (error) {
252
+ console.error(`Failed to create local default drive ${drive.id}:`, error);
253
+ }
140
254
  }
141
255
  //#endregion
142
256
  //#region src/feature-flags.ts
@@ -601,14 +715,14 @@ var BrowserPackageManager = class {
601
715
  #cdnUrl;
602
716
  #localPackageVersion;
603
717
  #localPackageNames = new Set([LOCAL_PACKAGE_NAME]);
718
+ #pwaSyncTimer;
719
+ /** Serialized last-mirrored PWA fragment; lets a sync skip the IndexedDB
720
+ * write + manifest re-attach when nothing PWA-relevant changed. */
721
+ #lastSyncedFragmentJson;
604
722
  constructor(namespace, registryUrl) {
605
723
  this.#storage = new BrowserLocalStorage(namespace + ":PH_PACKAGES");
606
724
  this.registryUrl = registryUrl;
607
- this.#cdnUrl = registryUrl !== null ? this.#toCdnUrl(registryUrl) : null;
608
- }
609
- #toCdnUrl(baseUrl) {
610
- if (baseUrl.includes("/-/cdn")) return baseUrl;
611
- return `${baseUrl.endsWith("/") ? baseUrl.slice(0, -1) : baseUrl}/-/cdn`;
725
+ this.#cdnUrl = registryUrl !== null ? toCdnUrl(registryUrl) : null;
612
726
  }
613
727
  async init(localPackage, localPackageVersion, studioMode) {
614
728
  this.addLocalPackage(common.manifest.name, common, commonPkg.version);
@@ -829,6 +943,53 @@ var BrowserPackageManager = class {
829
943
  this.#subscribers.forEach((handler) => {
830
944
  handler({ packages });
831
945
  });
946
+ this.#schedulePwaSync();
947
+ }
948
+ /**
949
+ * Debounced mirror of every loaded package's PWA fragment into IndexedDB, so
950
+ * the service worker can serve a web-app manifest that reflects packages
951
+ * installed AT RUNTIME (a SW can't read localStorage, where the package list
952
+ * lives). Debounced because `init()` rehydrates many packages in a burst;
953
+ * coalescing collapses that into a single write.
954
+ */
955
+ #schedulePwaSync() {
956
+ if (typeof indexedDB === "undefined") return;
957
+ clearTimeout(this.#pwaSyncTimer);
958
+ this.#pwaSyncTimer = setTimeout(() => {
959
+ this.#syncPwaFragments();
960
+ }, 50);
961
+ }
962
+ async #syncPwaFragments() {
963
+ const contributions = [];
964
+ for (const [name, loadedPackage] of this.#packages) {
965
+ const manifest = loadedPackage.manifest;
966
+ let config = {};
967
+ if (manifest.pwa) {
968
+ const parsed = PwaConfigSchema.safeParse(manifest.pwa);
969
+ if (parsed.success) {
970
+ const importUrl = this.#storage.get(name)?.importUrl ?? null;
971
+ const baseUrl = importUrl ? importUrl.replace(/browser\/index\.js$/, "") : null;
972
+ config = resolveFragmentAssetUrls(parsed.data, baseUrl);
973
+ } else console.warn(`[Connect][PWA] "${name}" ships an invalid pwa fragment; skipped.`, parsed.error);
974
+ }
975
+ config = withInferredCategory(config, manifest.category);
976
+ if (Object.keys(config).length === 0) continue;
977
+ contributions.push({
978
+ source: name,
979
+ config
980
+ });
981
+ }
982
+ const merged = mergePwaConfig(contributions);
983
+ const mergedJson = JSON.stringify(merged);
984
+ if (mergedJson === this.#lastSyncedFragmentJson) return;
985
+ try {
986
+ await writeMergedPwaFragment(merged);
987
+ } catch (error) {
988
+ console.debug("[Connect][PWA] fragment mirror to IndexedDB failed:", error);
989
+ return;
990
+ }
991
+ this.#lastSyncedFragmentJson = mergedJson;
992
+ refreshPwaManifestLink();
832
993
  }
833
994
  };
834
995
  //#endregion
@@ -855,6 +1016,10 @@ function bumpWorkerGen(namespace, gen) {
855
1016
  const PING_INTERVAL_MS = 2e3;
856
1017
  const PING_DEADLINE_MS = 3e3;
857
1018
  const MAX_MISSED_PINGS = 2;
1019
+ /** Sorted so the same set always produces the same fingerprint. */
1020
+ function enabledFlagList(flags) {
1021
+ return Object.entries(flags).filter(([, enabled]) => enabled).map(([name]) => name).sort().join(",");
1022
+ }
858
1023
  function toReactorIdentity(user) {
859
1024
  if (!user) return null;
860
1025
  return {
@@ -890,14 +1055,17 @@ function createWorkerReactorClientModule(args) {
890
1055
  models: args.documentModelModules.map((m) => ({
891
1056
  id: m.documentModel.global.id,
892
1057
  version: m.version ?? 1
893
- }))
1058
+ })),
1059
+ featureFlags: enabledFlagList(args.featureFlags)
894
1060
  },
895
1061
  construct: {
896
1062
  namespace: args.namespace,
897
1063
  relationalNamespace: args.relationalNamespace,
898
1064
  cdnUrl: args.cdnUrl,
899
1065
  packageSpecs: args.packageSpecs,
900
- studioMode: args.studioMode
1066
+ studioMode: args.studioMode,
1067
+ renownChainId: args.renownChainId,
1068
+ featureFlags: args.featureFlags
901
1069
  },
902
1070
  packages: args.packageSpecs
903
1071
  }, args.onReload, documentModelRegistry);
@@ -1004,29 +1172,23 @@ var NullAttachmentService = class {
1004
1172
  get() {
1005
1173
  return Promise.reject(/* @__PURE__ */ new Error("NullAttachmentService: no attachment service configured"));
1006
1174
  }
1175
+ getDownloadTarget() {
1176
+ return Promise.reject(/* @__PURE__ */ new Error("NullAttachmentService: no attachment service configured"));
1177
+ }
1007
1178
  };
1008
1179
  async function createProcessorHostModule(reactorClient, readModels, attachmentService) {
1009
1180
  try {
1010
1181
  const { pgLite, relationalDb } = await getDb();
1011
1182
  const { store: analyticsStore } = await createAnalyticsStore({ pgLite });
1012
1183
  return {
1013
- relationalDb,
1014
- analyticsStore,
1015
- processorApp: "connect",
1016
- client: reactorClient,
1017
- attachments: createAttachmentClient(attachmentService ?? new NullAttachmentService()),
1018
- dispatch: { async execute(docId, branch, actions, signal) {
1019
- const jobInfo = await reactorClient.executeAsync(docId, branch, actions, signal);
1020
- return {
1021
- id: jobInfo.id,
1022
- status: jobInfo.status
1023
- };
1024
- } },
1025
- getReadModel(name) {
1026
- const model = readModels.find((m) => m.name === name);
1027
- if (!model) throw new Error(`Read model "${name}" not found`);
1028
- return model;
1029
- }
1184
+ ...createReactorHostModuleBase({
1185
+ client: reactorClient,
1186
+ readModels,
1187
+ relationalDb,
1188
+ analyticsStore,
1189
+ processorApp: "connect"
1190
+ }),
1191
+ attachments: createAttachmentClient(attachmentService ?? new NullAttachmentService())
1030
1192
  };
1031
1193
  } catch (error) {
1032
1194
  console.error(`Failed to initialize processor host module:`);
@@ -1120,11 +1282,16 @@ async function createReactor(localPackage) {
1120
1282
  setPHToast(toast);
1121
1283
  const features = await initFeatureFlags();
1122
1284
  logger.info("Features: @features", JSON.stringify(Object.fromEntries(features), null, 2));
1285
+ const renownChainId = getRuntimeConfig().connect.renown?.chainId;
1123
1286
  const keyPairStorage = await BrowserKeyStorage.create();
1124
- const renownCrypto = await new RenownCryptoBuilder().withKeyPairStorage(keyPairStorage).build();
1287
+ const cryptoBuilder = new RenownCryptoBuilder().withKeyPairStorage(keyPairStorage);
1288
+ if (renownChainId !== void 0) cryptoBuilder.withChainId(renownChainId);
1289
+ const renownCrypto = await cryptoBuilder.build();
1125
1290
  const renown = await new RenownBuilder("connect", {
1126
1291
  basename: getRuntimeConfig().connect.renown?.namespace ?? phGlobalConfig.routerBasename,
1127
- baseUrl: phGlobalConfig.renownUrl
1292
+ baseUrl: phGlobalConfig.renownUrl,
1293
+ switchboardUrl: phGlobalConfig.switchboardUrl,
1294
+ chainId: renownChainId
1128
1295
  }).withCrypto(renownCrypto).build();
1129
1296
  const runtimeConfig = getRuntimeConfig();
1130
1297
  const packageManager = new BrowserPackageManager(phGlobalConfig.routerBasename ?? "", runtimeConfig.packageRegistryUrl ?? null);
@@ -1146,6 +1313,9 @@ async function createReactor(localPackage) {
1146
1313
  storageKey: phGlobalConfig.routerBasename ?? ""
1147
1314
  }) : new NoRegistryDiscoveryService(packageManager);
1148
1315
  setPackageDiscoveryService(discoveryService);
1316
+ const reactorFeatureFlags = runtimeConfig.connect?.reactor?.featureFlags ?? {};
1317
+ const enabledReactorFlags = Object.entries(reactorFeatureFlags).filter(([, enabled]) => enabled).map(([name]) => name);
1318
+ if (enabledReactorFlags.length > 0) logger.info(`Reactor feature flags: ${enabledReactorFlags.join(", ")}`);
1149
1319
  let reactorClientModule;
1150
1320
  if (isReactorWorkerEnabled()) {
1151
1321
  const packageSpecs = packageManager.getRegistryPackages().map((p) => p.version ? `${p.name}@${p.version}` : p.name);
@@ -1156,6 +1326,8 @@ async function createReactor(localPackage) {
1156
1326
  cdnUrl: packageManager.cdnUrl ?? "",
1157
1327
  packageSpecs,
1158
1328
  studioMode: phGlobalConfig.studioMode,
1329
+ renownChainId,
1330
+ featureFlags: reactorFeatureFlags,
1159
1331
  documentModelModules,
1160
1332
  upgradeManifests,
1161
1333
  documentModelLoader: { async load(documentType) {
@@ -1183,7 +1355,7 @@ async function createReactor(localPackage) {
1183
1355
  logger.error("Reactor worker failed to start: @error", error);
1184
1356
  throw error;
1185
1357
  }
1186
- } else reactorClientModule = await createBrowserReactor(documentModelModules, upgradeManifests, renown, discoveryService);
1358
+ } else reactorClientModule = await createBrowserReactor(documentModelModules, upgradeManifests, renown, reactorFeatureFlags, discoveryService);
1187
1359
  const drives = await getDrives(reactorClientModule.client);
1188
1360
  await login(getDidFromUrl(), renown);
1189
1361
  const documentCache = new DocumentCache(reactorClientModule.client);
@@ -1206,7 +1378,8 @@ async function createReactor(localPackage) {
1206
1378
  };
1207
1379
  setAttachmentService(createRemoteAttachmentService({
1208
1380
  remoteUrl: switchboardOrigin,
1209
- jwtHandler: attachmentJwtHandler
1381
+ jwtHandler: attachmentJwtHandler,
1382
+ uploadTransport: createXhrUploadTransport()
1210
1383
  }));
1211
1384
  }
1212
1385
  }
@@ -1278,5 +1451,5 @@ function getDriveUrl() {
1278
1451
  //#endregion
1279
1452
  export { createReactor as n, clearReactorStorage as t };
1280
1453
 
1281
- //# sourceMappingURL=reactor-ynguOpe7.js.map
1282
- //# debugId=e6d789cd-51cc-5a5a-b3c5-95dea7e29db0
1454
+ //# sourceMappingURL=reactor-CrKlyk9q.js.map
1455
+ //# debugId=bb3bf75e-9d72-5474-a859-7426e7ff2735