@powerhousedao/ph-clint-app 0.1.0-dev.89 → 0.1.0-dev.90
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/dist/browser/{editor-BuHwuzRa.js → editor-Bp2wUo8R.js} +748 -522
- package/dist/browser/editor-Bp2wUo8R.js.map +1 -0
- package/dist/browser/editors/ph-clint-project-editor/module.js +1 -1
- package/dist/connect/assets/{AddDriveModal-BoKLJyUY-DwlVwt2n.js → AddDriveModal-BoKLJyUY-cGJwd_2F.js} +2 -2
- package/dist/connect/assets/{AddDriveModal-BoKLJyUY-DwlVwt2n.js.map → AddDriveModal-BoKLJyUY-cGJwd_2F.js.map} +1 -1
- package/dist/connect/assets/{ClearStorageModal-CXoNWmRe-lBEW3sSH.js → ClearStorageModal-CXoNWmRe-D3ifKRm-.js} +2 -2
- package/dist/connect/assets/{ClearStorageModal-CXoNWmRe-lBEW3sSH.js.map → ClearStorageModal-CXoNWmRe-D3ifKRm-.js.map} +1 -1
- package/dist/connect/assets/{CookiesPolicyModal-1nOh6oN3-BYSDp-5J.js → CookiesPolicyModal-1nOh6oN3-kK_sx5mz.js} +2 -2
- package/dist/connect/assets/{CookiesPolicyModal-1nOh6oN3-BYSDp-5J.js.map → CookiesPolicyModal-1nOh6oN3-kK_sx5mz.js.map} +1 -1
- package/dist/connect/assets/{CreateDocumentModal-nQbLVDq3-DJbJhlYp.js → CreateDocumentModal-nQbLVDq3-DFAye2CX.js} +2 -2
- package/dist/connect/assets/{CreateDocumentModal-nQbLVDq3-DJbJhlYp.js.map → CreateDocumentModal-nQbLVDq3-DFAye2CX.js.map} +1 -1
- package/dist/connect/assets/{DebugSettingsModal-5SDhy5gt-Du3PKhbh.js → DebugSettingsModal-5SDhy5gt-DLMPdDQB.js} +2 -2
- package/dist/connect/assets/{DebugSettingsModal-5SDhy5gt-Du3PKhbh.js.map → DebugSettingsModal-5SDhy5gt-DLMPdDQB.js.map} +1 -1
- package/dist/connect/assets/{DeleteDriveModal-6P0T1QgD-CaEKmseP.js → DeleteDriveModal-6P0T1QgD-CK6cPXbT.js} +2 -2
- package/dist/connect/assets/{DeleteDriveModal-6P0T1QgD-CaEKmseP.js.map → DeleteDriveModal-6P0T1QgD-CK6cPXbT.js.map} +1 -1
- package/dist/connect/assets/{DeleteItemModal-BmV1aQyZ-B01dssV-.js → DeleteItemModal-BmV1aQyZ-Ld9lHNhl.js} +2 -2
- package/dist/connect/assets/{DeleteItemModal-BmV1aQyZ-B01dssV-.js.map → DeleteItemModal-BmV1aQyZ-Ld9lHNhl.js.map} +1 -1
- package/dist/connect/assets/{DisclaimerModal-CVTFfN0_-CfPGYzP1.js → DisclaimerModal-CVTFfN0_-D7N9TDI4.js} +2 -2
- package/dist/connect/assets/{DisclaimerModal-CVTFfN0_-CfPGYzP1.js.map → DisclaimerModal-CVTFfN0_-D7N9TDI4.js.map} +1 -1
- package/dist/connect/assets/{DownloadDocumentWithErrorsModal-CXeFUtaz-UexzRqQT.js → DownloadDocumentWithErrorsModal-CXeFUtaz-CYIIl42M.js} +2 -2
- package/dist/connect/assets/{DownloadDocumentWithErrorsModal-CXeFUtaz-UexzRqQT.js.map → DownloadDocumentWithErrorsModal-CXeFUtaz-CYIIl42M.js.map} +1 -1
- package/dist/connect/assets/{DriveAuthRequiredModal-B3VdLwSH-C5FZCMC5.js → DriveAuthRequiredModal-B3VdLwSH-fqmI_4sF.js} +2 -2
- package/dist/connect/assets/{DriveAuthRequiredModal-B3VdLwSH-C5FZCMC5.js.map → DriveAuthRequiredModal-B3VdLwSH-fqmI_4sF.js.map} +1 -1
- package/dist/connect/assets/{DriveSettingsModal-CsNFEJ9E-BoXSbW1c.js → DriveSettingsModal-CsNFEJ9E-BR2vyeU8.js} +2 -2
- package/dist/connect/assets/{DriveSettingsModal-CsNFEJ9E-BoXSbW1c.js.map → DriveSettingsModal-CsNFEJ9E-BR2vyeU8.js.map} +1 -1
- package/dist/connect/assets/{InspectorModal-BsSihzuc-hukmcBoP.js → InspectorModal-BsSihzuc-B0n5gbGB.js} +2 -2
- package/dist/connect/assets/{InspectorModal-BsSihzuc-hukmcBoP.js.map → InspectorModal-BsSihzuc-B0n5gbGB.js.map} +1 -1
- package/dist/connect/assets/{MissingPackageModal-Bn036mWQ-BOXHWmFa.js → MissingPackageModal-Bn036mWQ-C021G1nV.js} +2 -2
- package/dist/connect/assets/{MissingPackageModal-Bn036mWQ-BOXHWmFa.js.map → MissingPackageModal-Bn036mWQ-C021G1nV.js.map} +1 -1
- package/dist/connect/assets/{SettingsModal-S_A5qf95-Fz0f2CXX.js → SettingsModal-S_A5qf95-uzigz0nk.js} +2 -2
- package/dist/connect/assets/{SettingsModal-S_A5qf95-Fz0f2CXX.js.map → SettingsModal-S_A5qf95-uzigz0nk.js.map} +1 -1
- package/dist/connect/assets/{UpgradeDriveModal-C7lV4rAX-BkeBWpDi.js → UpgradeDriveModal-C7lV4rAX-CTIf4duw.js} +2 -2
- package/dist/connect/assets/{UpgradeDriveModal-C7lV4rAX-BkeBWpDi.js.map → UpgradeDriveModal-C7lV4rAX-CTIf4duw.js.map} +1 -1
- package/dist/connect/assets/build-info-cc1PjSvF-DtAGzh0W.js.map +1 -1
- package/dist/connect/assets/chunk-pbuEa-1d-KEQyMDN_.js.map +1 -1
- package/dist/connect/assets/{components-ByEUkO4U-Mc23VVA-.js → components-ByEUkO4U-xPZUVkzr.js} +4 -4
- package/dist/connect/assets/{components-ByEUkO4U-Mc23VVA-.js.map → components-ByEUkO4U-xPZUVkzr.js.map} +1 -1
- package/dist/connect/assets/{components-HCKyLh9m.js → components-Djrujufg.js} +2 -2
- package/dist/connect/assets/{components-HCKyLh9m.js.map → components-Djrujufg.js.map} +1 -1
- package/dist/connect/assets/{connect-pRAhId57.js → connect-CGvtDtZh.js} +2 -2
- package/dist/connect/assets/{connect-pRAhId57.js.map → connect-CGvtDtZh.js.map} +1 -1
- package/dist/connect/assets/{connect.config-Bt4pvvWV-CAyer5pZ.js → connect.config-Bt4pvvWV-Dj3FkjaZ.js} +2 -2
- package/dist/connect/assets/{connect.config-Bt4pvvWV-CAyer5pZ.js.map → connect.config-Bt4pvvWV-Dj3FkjaZ.js.map} +1 -1
- package/dist/connect/assets/{dist-DmhUSVtT.js → dist-t-ezfpxv.js} +3 -3
- package/dist/connect/assets/{dist-DmhUSVtT.js.map → dist-t-ezfpxv.js.map} +1 -1
- package/dist/connect/assets/{editor-B560eld_-CIE3_3II.js → editor-B560eld_-DLpKHubk.js} +2 -2
- package/dist/connect/assets/{editor-B560eld_-CIE3_3II.js.map → editor-B560eld_-DLpKHubk.js.map} +1 -1
- package/dist/connect/assets/editor-BaQ62r3i.js +2 -0
- package/dist/connect/assets/editor-BaQ62r3i.js.map +1 -0
- package/dist/connect/assets/{editor-CAVHtR-v-ClzvSfK5.js → editor-CAVHtR-v-D33T9vW0.js} +2 -2
- package/dist/connect/assets/{editor-CAVHtR-v-ClzvSfK5.js.map → editor-CAVHtR-v-D33T9vW0.js.map} +1 -1
- package/dist/connect/assets/{editor-CVCLxUhe-C6nP7EPN.js → editor-CVCLxUhe-C9Ftgu1S.js} +2 -2
- package/dist/connect/assets/{editor-CVCLxUhe-C6nP7EPN.js.map → editor-CVCLxUhe-C9Ftgu1S.js.map} +1 -1
- package/dist/connect/assets/{editor-DnG2Zzet-CRhirYbH.js → editor-DnG2Zzet-CQ38uSti.js} +2 -2
- package/dist/connect/assets/{editor-DnG2Zzet-CRhirYbH.js.map → editor-DnG2Zzet-CQ38uSti.js.map} +1 -1
- package/dist/connect/assets/{editor-mor7dV6n-DDigdbLM.js → editor-mor7dV6n-NTU9k1uj.js} +2 -2
- package/dist/connect/assets/{editor-mor7dV6n-DDigdbLM.js.map → editor-mor7dV6n-NTU9k1uj.js.map} +1 -1
- package/dist/connect/assets/{editor-xCnDas6c-CyHuSL7M.js → editor-xCnDas6c-DdGhx--5.js} +2 -2
- package/dist/connect/assets/{editor-xCnDas6c-CyHuSL7M.js.map → editor-xCnDas6c-DdGhx--5.js.map} +1 -1
- package/dist/connect/assets/esm-dD38MHAT.js +10 -0
- package/dist/connect/assets/esm-dD38MHAT.js.map +1 -0
- package/dist/connect/assets/{hooks-Btogj1f0-CMXQZV6d.js → hooks-Btogj1f0-BKAAmyt-.js} +2 -2
- package/dist/connect/assets/{hooks-Btogj1f0-CMXQZV6d.js.map → hooks-Btogj1f0-BKAAmyt-.js.map} +1 -1
- package/dist/connect/assets/index-NB86TKkT.js +3 -0
- package/dist/connect/assets/{index-DS7vbdaR.js.map → index-NB86TKkT.js.map} +1 -1
- package/dist/connect/assets/{load-BWibkoFV-BVilFVpD.js → load-BWibkoFV-CDRjWwYQ.js} +2 -2
- package/dist/connect/assets/{load-BWibkoFV-BVilFVpD.js.map → load-BWibkoFV-CDRjWwYQ.js.map} +1 -1
- package/dist/connect/assets/module-1hr_ne9w-CWAVsMzZ.js.map +1 -1
- package/dist/connect/assets/{module-DP3LMNU7.js → module-7zCQE1MC.js} +2 -2
- package/dist/connect/assets/{module-DP3LMNU7.js.map → module-7zCQE1MC.js.map} +1 -1
- package/dist/connect/assets/module-CRq_lUkG-D61oeRlC.js.map +1 -1
- package/dist/connect/assets/module-DP3bVbak-VbUPIBZP.js.map +1 -1
- package/dist/connect/assets/package-BZLLR0Ma-B7vswMvI.js.map +1 -1
- package/dist/connect/assets/{pglite-seed-BWk7yOwr-T-NGWlGh.js → pglite-seed-BWk7yOwr-b2F0oJiP.js} +2 -2
- package/dist/connect/assets/{pglite-seed-BWk7yOwr-T-NGWlGh.js.map → pglite-seed-BWk7yOwr-b2F0oJiP.js.map} +1 -1
- package/dist/connect/assets/pglite.worker-BuyZ0d7u.js.map +1 -1
- package/dist/connect/assets/pglite.worker.legacy-D5InEf0i.js.map +1 -1
- package/dist/connect/assets/{ph-clint-project-BZ5X7dVQ.js → ph-clint-project-p1zY6e17.js} +2 -2
- package/dist/connect/assets/{ph-clint-project-BZ5X7dVQ.js.map → ph-clint-project-p1zY6e17.js.map} +1 -1
- package/dist/connect/assets/{reactor-BxNvHOKC-DmVBqCt_.js → reactor-BxNvHOKC-CeMF1M2u.js} +4 -4
- package/dist/connect/assets/{reactor-BxNvHOKC-DmVBqCt_.js.map → reactor-BxNvHOKC-CeMF1M2u.js.map} +1 -1
- package/dist/connect/assets/{registerServiceWorker-D9l1kja7-oC4nicB2.js → registerServiceWorker-D9l1kja7-DG3DqQna.js} +2 -2
- package/dist/connect/assets/{registerServiceWorker-D9l1kja7-oC4nicB2.js.map → registerServiceWorker-D9l1kja7-DG3DqQna.js.map} +1 -1
- package/dist/connect/assets/reload-connect-toast-CwvSc6F--BQcqGaN_.js.map +1 -1
- package/dist/connect/assets/runtime-config-DL3QjcfM-D9g4VFtY.js.map +1 -1
- package/dist/connect/assets/toast-BEy42aYx-Dg_jZf6v.js.map +1 -1
- package/dist/connect/assets/{useRegistryPackages-B-gFECmd-CXOBKziY.js → useRegistryPackages-B-gFECmd-C8h2M5qM.js} +2 -2
- package/dist/connect/assets/{useRegistryPackages-B-gFECmd-CXOBKziY.js.map → useRegistryPackages-B-gFECmd-C8h2M5qM.js.map} +1 -1
- package/dist/connect/assets/{useVetraDocument-tpjMF571-DT5T1mNT.js → useVetraDocument-tpjMF571-DxT9GgCx.js} +2 -2
- package/dist/connect/assets/{useVetraDocument-tpjMF571-DT5T1mNT.js.map → useVetraDocument-tpjMF571-DxT9GgCx.js.map} +1 -1
- package/dist/connect/assets/utils-BiV3cs9Y-B7i9WJ1b.js.map +1 -1
- package/dist/connect/assets/{utils-CkWB3DRk-CxqNKFWP.js → utils-CkWB3DRk-BURJYofr.js} +2 -2
- package/dist/connect/assets/{utils-CkWB3DRk-CxqNKFWP.js.map → utils-CkWB3DRk-BURJYofr.js.map} +1 -1
- package/dist/connect/index.html +3 -3
- package/dist/connect/powerhouse.config.json +1 -1
- package/dist/node/{editor-Bev0bZuJ.mjs → editor-BC9t7D3T.mjs} +748 -517
- package/dist/node/editor-BC9t7D3T.mjs.map +1 -0
- package/dist/node/editors/ph-clint-project-editor/module.mjs +1 -1
- package/package.json +2 -2
- package/dist/browser/editor-BuHwuzRa.js.map +0 -1
- package/dist/connect/assets/editor-BrPMvNFl.js +0 -2
- package/dist/connect/assets/editor-BrPMvNFl.js.map +0 -1
- package/dist/connect/assets/esm-mbrYcWED.js +0 -10
- package/dist/connect/assets/esm-mbrYcWED.js.map +0 -1
- package/dist/connect/assets/index-DS7vbdaR.js +0 -3
- package/dist/node/editor-Bev0bZuJ.mjs.map +0 -1
|
@@ -2,11 +2,6 @@ import { Ar as generateId, _r as undo, g as actions, jr as __commonJSMin, mr as
|
|
|
2
2
|
import { C as useDocumentById, S as t$7, _ as usePHToast, b as e$5, c as DEFAULT_SWITCHBOARD_URL, d as showRevisionHistory, f as useDownloadDocument, g as useNodeParentFolderById, h as useNodeById, l as getSwitchboardGatewayUrlFromDriveUrl, m as useNodeActions, p as useGetSwitchboardLink, s as useSelectedPhClintProjectDocument, u as setSelectedNode, v as useSelectedDocumentSafe, x as t$8, y as useSelectedDrive } from "./ph-clint-project-CHq59BKL.js";
|
|
3
3
|
import { Fragment, Suspense, createContext, createElement, forwardRef, useCallback, useContext, useEffect, useLayoutEffect, useMemo, useReducer, useRef, useState } from "react";
|
|
4
4
|
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
5
|
-
import "node:crypto";
|
|
6
|
-
import "node:path";
|
|
7
|
-
import "node:fs/promises";
|
|
8
|
-
import "node:fs";
|
|
9
|
-
import "node:stream";
|
|
10
5
|
//#region ../../node_modules/.pnpm/remeda@2.33.7/node_modules/remeda/dist/defaultTo.js
|
|
11
6
|
function t$6(...t) {
|
|
12
7
|
return t$7(n$4, t);
|
|
@@ -128,6 +123,521 @@ function t(...t) {
|
|
|
128
123
|
return t$7(Object.values, t);
|
|
129
124
|
}
|
|
130
125
|
//#endregion
|
|
126
|
+
//#region ../../node_modules/.pnpm/@powerhousedao+reactor-attachments@6.2.0-dev.23_@sentry+node-core@10.58.0_@opentelemetr_52094b0a5e5faf97617f2bc1ad7b3eb6/node_modules/@powerhousedao/reactor-attachments/dist/null-attachment-transport-Drx03s02.js
|
|
127
|
+
/**
|
|
128
|
+
* Thrown when an attachment ref or hash is not known to the store.
|
|
129
|
+
*/
|
|
130
|
+
var AttachmentNotFound = class extends Error {
|
|
131
|
+
constructor(identifier) {
|
|
132
|
+
super(`Attachment not found: ${identifier}`);
|
|
133
|
+
this.name = "AttachmentNotFound";
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
/**
|
|
137
|
+
* Thrown when a reservation ID is not found in the reservation store.
|
|
138
|
+
*/
|
|
139
|
+
var ReservationNotFound = class extends Error {
|
|
140
|
+
constructor(reservationId) {
|
|
141
|
+
super(`Reservation not found: ${reservationId}`);
|
|
142
|
+
this.name = "ReservationNotFound";
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
/**
|
|
146
|
+
* Thrown when an attachment ref string does not match the expected format.
|
|
147
|
+
*/
|
|
148
|
+
var InvalidAttachmentRef = class extends Error {
|
|
149
|
+
constructor(ref) {
|
|
150
|
+
super(`Invalid attachment ref: ${ref}`);
|
|
151
|
+
this.name = "InvalidAttachmentRef";
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
/**
|
|
155
|
+
* Thrown by reserve() when the claimed hash is already available in the store.
|
|
156
|
+
* The caller should use err.ref directly and upload nothing -- this is the
|
|
157
|
+
* dedup fast path: duplicate content never leaves the client.
|
|
158
|
+
*/
|
|
159
|
+
var AttachmentAlreadyExists = class extends Error {
|
|
160
|
+
hash;
|
|
161
|
+
ref;
|
|
162
|
+
constructor(hash, ref) {
|
|
163
|
+
super(`Attachment already exists for hash: ${hash}`);
|
|
164
|
+
this.name = "AttachmentAlreadyExists";
|
|
165
|
+
this.hash = hash;
|
|
166
|
+
this.ref = ref;
|
|
167
|
+
}
|
|
168
|
+
};
|
|
169
|
+
/**
|
|
170
|
+
* Thrown by send() when the server-computed hash of the uploaded bytes
|
|
171
|
+
* does not match the hash claimed at reservation time. Nothing is committed;
|
|
172
|
+
* the reservation is retained so the client can retry with correct bytes.
|
|
173
|
+
*/
|
|
174
|
+
var HashMismatch = class extends Error {
|
|
175
|
+
claimed;
|
|
176
|
+
actual;
|
|
177
|
+
constructor(claimed, actual) {
|
|
178
|
+
super(`Hash mismatch: claimed ${claimed} but computed ${actual}`);
|
|
179
|
+
this.name = "HashMismatch";
|
|
180
|
+
this.claimed = claimed;
|
|
181
|
+
this.actual = actual;
|
|
182
|
+
}
|
|
183
|
+
};
|
|
184
|
+
/**
|
|
185
|
+
* Thrown by send() when the uploaded byte count does not equal the
|
|
186
|
+
* sizeBytes declared at reservation time. The handle may reject
|
|
187
|
+
* mid-stream as soon as the count exceeds the declaration.
|
|
188
|
+
* Nothing is committed; the reservation is retained for retry.
|
|
189
|
+
*
|
|
190
|
+
* "actual" is the byte count received from the stream before aborting --
|
|
191
|
+
* it includes the chunk that crossed the declaration and can exceed bytes
|
|
192
|
+
* persisted. On mid-stream aborts the true total is unknown; at least
|
|
193
|
+
* "actual" bytes were sent.
|
|
194
|
+
*/
|
|
195
|
+
var SizeMismatch = class extends Error {
|
|
196
|
+
declared;
|
|
197
|
+
actual;
|
|
198
|
+
constructor(declared, actual) {
|
|
199
|
+
super(`Size mismatch: declared ${declared} bytes but received ${actual}`);
|
|
200
|
+
this.name = "SizeMismatch";
|
|
201
|
+
this.declared = declared;
|
|
202
|
+
this.actual = actual;
|
|
203
|
+
}
|
|
204
|
+
};
|
|
205
|
+
/**
|
|
206
|
+
* Thrown by get() when the hash is reserved by an in-flight upload and
|
|
207
|
+
* bytes are not yet available anywhere. Deliberately NOT a subclass of
|
|
208
|
+
* AttachmentNotFound -- callers must distinguish "retry later" from "unknown".
|
|
209
|
+
* After expiresAtUtc has passed the hash reads as not found.
|
|
210
|
+
*
|
|
211
|
+
* metadata is populated when the reservation is local and its fields are
|
|
212
|
+
* known (mimeType, fileName, sizeBytes). It is undefined when the pending
|
|
213
|
+
* state is learned from a remote transport that did not supply the full
|
|
214
|
+
* Attachment-Pending header (transport-pending / degraded wire case).
|
|
215
|
+
*/
|
|
216
|
+
var AttachmentPending = class extends Error {
|
|
217
|
+
hash;
|
|
218
|
+
expiresAtUtc;
|
|
219
|
+
metadata;
|
|
220
|
+
constructor(hash, expiresAtUtc, meta) {
|
|
221
|
+
super(`Attachment pending upload for hash: ${hash}, expires: ${expiresAtUtc}`);
|
|
222
|
+
this.name = "AttachmentPending";
|
|
223
|
+
this.hash = hash;
|
|
224
|
+
this.expiresAtUtc = expiresAtUtc;
|
|
225
|
+
this.metadata = meta;
|
|
226
|
+
}
|
|
227
|
+
};
|
|
228
|
+
const REF_PATTERN = /^attachment:\/\/v(\d+):(.+)$/;
|
|
229
|
+
const DEFAULT_VERSION = 1;
|
|
230
|
+
function parseRef(ref) {
|
|
231
|
+
const match = REF_PATTERN.exec(ref);
|
|
232
|
+
if (!match) throw new InvalidAttachmentRef(ref);
|
|
233
|
+
return {
|
|
234
|
+
version: Number(match[1]),
|
|
235
|
+
hash: match[2]
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
function createRef(hash, version = DEFAULT_VERSION) {
|
|
239
|
+
return `attachment://v${version}:${hash}`;
|
|
240
|
+
}
|
|
241
|
+
const CLIENT_HASH_PATTERN = /^[a-f0-9]{64}$/;
|
|
242
|
+
var AttachmentService = class {
|
|
243
|
+
constructor(store, reservations, uploadFactory) {
|
|
244
|
+
this.store = store;
|
|
245
|
+
this.reservations = reservations;
|
|
246
|
+
this.uploadFactory = uploadFactory;
|
|
247
|
+
}
|
|
248
|
+
async reserve(options) {
|
|
249
|
+
if (options.clientHash !== void 0) return this.reserveHashFirst(options);
|
|
250
|
+
const reservation = await this.reservations.create(options);
|
|
251
|
+
return this.uploadFactory.createUpload(reservation);
|
|
252
|
+
}
|
|
253
|
+
async stat(ref) {
|
|
254
|
+
const { hash } = parseRef(ref);
|
|
255
|
+
return this.store.stat(hash);
|
|
256
|
+
}
|
|
257
|
+
async get(ref, signal) {
|
|
258
|
+
const { hash } = parseRef(ref);
|
|
259
|
+
return this.store.get(hash, signal);
|
|
260
|
+
}
|
|
261
|
+
async reserveHashFirst(options) {
|
|
262
|
+
const normalized = options.clientHash.toLowerCase();
|
|
263
|
+
if (!CLIENT_HASH_PATTERN.test(normalized)) throw new Error(`clientHash must be a 64-character lowercase hex string, got: ${options.clientHash}`);
|
|
264
|
+
if (options.sizeBytes === void 0 || !Number.isInteger(options.sizeBytes) || options.sizeBytes <= 0 || !Number.isSafeInteger(options.sizeBytes)) throw new Error("sizeBytes must be a positive safe integer when clientHash is provided");
|
|
265
|
+
const normalizedOptions = {
|
|
266
|
+
...options,
|
|
267
|
+
clientHash: normalized
|
|
268
|
+
};
|
|
269
|
+
let existingHeader = null;
|
|
270
|
+
try {
|
|
271
|
+
existingHeader = await this.store.stat(normalized);
|
|
272
|
+
} catch (err) {
|
|
273
|
+
if (!(err instanceof AttachmentNotFound) && !(err instanceof AttachmentPending)) throw err;
|
|
274
|
+
}
|
|
275
|
+
if (existingHeader !== null && existingHeader.status === "available") throw new AttachmentAlreadyExists(normalized, createRef(normalized));
|
|
276
|
+
const reservation = await this.reservations.create(normalizedOptions);
|
|
277
|
+
return this.uploadFactory.createUpload(reservation);
|
|
278
|
+
}
|
|
279
|
+
};
|
|
280
|
+
async function buildAuthHeaders(url, jwtHandler) {
|
|
281
|
+
const headers = {};
|
|
282
|
+
if (jwtHandler) {
|
|
283
|
+
const token = await jwtHandler(url);
|
|
284
|
+
if (token) headers["Authorization"] = `Bearer ${token}`;
|
|
285
|
+
}
|
|
286
|
+
return headers;
|
|
287
|
+
}
|
|
288
|
+
function isRecord$2(value) {
|
|
289
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
290
|
+
}
|
|
291
|
+
function deriveExtension(fileName) {
|
|
292
|
+
const idx = fileName.lastIndexOf(".");
|
|
293
|
+
if (idx <= 0 || idx === fileName.length - 1) return null;
|
|
294
|
+
return fileName.slice(idx + 1).toLowerCase();
|
|
295
|
+
}
|
|
296
|
+
function isReservationBase(value) {
|
|
297
|
+
if (!isRecord$2(value)) return false;
|
|
298
|
+
if (typeof value.reservationId !== "string") return false;
|
|
299
|
+
if (typeof value.mimeType !== "string") return false;
|
|
300
|
+
if (typeof value.fileName !== "string") return false;
|
|
301
|
+
if (value.extension !== null && typeof value.extension !== "string") return false;
|
|
302
|
+
if (typeof value.createdAtUtc !== "string") return false;
|
|
303
|
+
if (typeof value.expiresAtUtc !== "string") return false;
|
|
304
|
+
return true;
|
|
305
|
+
}
|
|
306
|
+
function isReservation(value) {
|
|
307
|
+
if (!isReservationBase(value)) return false;
|
|
308
|
+
if (value.clientHash !== void 0 && value.clientHash !== null && typeof value.clientHash !== "string") return false;
|
|
309
|
+
if (value.sizeBytes !== void 0 && value.sizeBytes !== null && typeof value.sizeBytes !== "number") return false;
|
|
310
|
+
return true;
|
|
311
|
+
}
|
|
312
|
+
var RemoteReservationStore = class {
|
|
313
|
+
remoteUrl;
|
|
314
|
+
jwtHandler;
|
|
315
|
+
fetchFn;
|
|
316
|
+
constructor(config) {
|
|
317
|
+
this.remoteUrl = config.remoteUrl;
|
|
318
|
+
this.jwtHandler = config.jwtHandler;
|
|
319
|
+
this.fetchFn = (config.fetchFn ?? globalThis.fetch).bind(globalThis);
|
|
320
|
+
}
|
|
321
|
+
async create(options) {
|
|
322
|
+
const url = `${this.remoteUrl}/attachments/reservations`;
|
|
323
|
+
const authHeaders = await buildAuthHeaders(url, this.jwtHandler);
|
|
324
|
+
const extension = options.extension ?? deriveExtension(options.fileName);
|
|
325
|
+
const bodyObj = {
|
|
326
|
+
mimeType: options.mimeType,
|
|
327
|
+
fileName: options.fileName,
|
|
328
|
+
extension
|
|
329
|
+
};
|
|
330
|
+
if (options.clientHash !== void 0) bodyObj.clientHash = options.clientHash;
|
|
331
|
+
if (options.sizeBytes !== void 0) bodyObj.sizeBytes = options.sizeBytes;
|
|
332
|
+
const response = await this.fetchFn(url, {
|
|
333
|
+
method: "POST",
|
|
334
|
+
headers: {
|
|
335
|
+
...authHeaders,
|
|
336
|
+
"Content-Type": "application/json"
|
|
337
|
+
},
|
|
338
|
+
body: JSON.stringify(bodyObj)
|
|
339
|
+
});
|
|
340
|
+
if (response.status === 409) {
|
|
341
|
+
let body;
|
|
342
|
+
try {
|
|
343
|
+
body = await response.json();
|
|
344
|
+
} catch {
|
|
345
|
+
throw new Error(`Reservation create failed: ${response.status} ${response.statusText}`);
|
|
346
|
+
}
|
|
347
|
+
if (isRecord$2(body) && body.error === "already_exists" && options.clientHash !== void 0) {
|
|
348
|
+
let ref;
|
|
349
|
+
if (typeof body.ref === "string") try {
|
|
350
|
+
parseRef(body.ref);
|
|
351
|
+
ref = body.ref;
|
|
352
|
+
} catch {
|
|
353
|
+
ref = createRef(options.clientHash);
|
|
354
|
+
}
|
|
355
|
+
else ref = createRef(options.clientHash);
|
|
356
|
+
throw new AttachmentAlreadyExists(options.clientHash, ref);
|
|
357
|
+
}
|
|
358
|
+
throw new Error(`Reservation create failed: ${response.status} ${response.statusText}`);
|
|
359
|
+
}
|
|
360
|
+
if (!response.ok) throw new Error(`Reservation create failed: ${response.status} ${response.statusText}`);
|
|
361
|
+
let json;
|
|
362
|
+
try {
|
|
363
|
+
json = await response.json();
|
|
364
|
+
} catch {
|
|
365
|
+
throw new Error("Reservation create returned non-JSON response");
|
|
366
|
+
}
|
|
367
|
+
if (typeof json !== "object" || json === null || typeof json.reservationId !== "string" || json.reservationId.length === 0) throw new Error("Reservation create returned a payload missing a non-empty reservationId string");
|
|
368
|
+
const body = json;
|
|
369
|
+
const now = /* @__PURE__ */ new Date();
|
|
370
|
+
return {
|
|
371
|
+
reservationId: body.reservationId,
|
|
372
|
+
mimeType: options.mimeType,
|
|
373
|
+
fileName: options.fileName,
|
|
374
|
+
extension,
|
|
375
|
+
createdAtUtc: body.createdAtUtc ?? now.toISOString(),
|
|
376
|
+
expiresAtUtc: body.expiresAtUtc ?? new Date(now.getTime() + 1440 * 60 * 1e3).toISOString(),
|
|
377
|
+
clientHash: options.clientHash ?? null,
|
|
378
|
+
sizeBytes: options.sizeBytes ?? null
|
|
379
|
+
};
|
|
380
|
+
}
|
|
381
|
+
async get(reservationId) {
|
|
382
|
+
const url = `${this.remoteUrl}/attachments/reservations/${encodeURIComponent(reservationId)}`;
|
|
383
|
+
const authHeaders = await buildAuthHeaders(url, this.jwtHandler);
|
|
384
|
+
const response = await this.fetchFn(url, { headers: authHeaders });
|
|
385
|
+
if (response.status === 404) throw new ReservationNotFound(reservationId);
|
|
386
|
+
if (!response.ok) throw new Error(`Reservation get failed: ${response.status} ${response.statusText}`);
|
|
387
|
+
let parsed;
|
|
388
|
+
try {
|
|
389
|
+
parsed = await response.json();
|
|
390
|
+
} catch {
|
|
391
|
+
throw new Error("Reservation get returned non-JSON response");
|
|
392
|
+
}
|
|
393
|
+
if (!isReservation(parsed)) throw new Error("Reservation get returned a payload that does not match the Reservation shape");
|
|
394
|
+
return {
|
|
395
|
+
reservationId: parsed.reservationId,
|
|
396
|
+
mimeType: parsed.mimeType,
|
|
397
|
+
fileName: parsed.fileName,
|
|
398
|
+
extension: parsed.extension,
|
|
399
|
+
createdAtUtc: parsed.createdAtUtc,
|
|
400
|
+
expiresAtUtc: parsed.expiresAtUtc,
|
|
401
|
+
clientHash: parsed.clientHash ?? null,
|
|
402
|
+
sizeBytes: parsed.sizeBytes ?? null
|
|
403
|
+
};
|
|
404
|
+
}
|
|
405
|
+
async delete(reservationId) {
|
|
406
|
+
const url = `${this.remoteUrl}/attachments/reservations/${encodeURIComponent(reservationId)}`;
|
|
407
|
+
const authHeaders = await buildAuthHeaders(url, this.jwtHandler);
|
|
408
|
+
const response = await this.fetchFn(url, {
|
|
409
|
+
method: "DELETE",
|
|
410
|
+
headers: authHeaders
|
|
411
|
+
});
|
|
412
|
+
if (!response.ok && response.status !== 404 && response.status !== 410) throw new Error(`Reservation delete failed: ${response.status} ${response.statusText}`);
|
|
413
|
+
}
|
|
414
|
+
deleteExpired() {
|
|
415
|
+
return Promise.reject(/* @__PURE__ */ new Error("RemoteReservationStore.deleteExpired is not supported"));
|
|
416
|
+
}
|
|
417
|
+
};
|
|
418
|
+
function isRecord$1(value) {
|
|
419
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
420
|
+
}
|
|
421
|
+
var RemoteAttachmentUpload = class {
|
|
422
|
+
reservationId;
|
|
423
|
+
ref;
|
|
424
|
+
expiresAtUtc;
|
|
425
|
+
remoteUrl;
|
|
426
|
+
jwtHandler;
|
|
427
|
+
fetchFn;
|
|
428
|
+
constructor(reservation, config) {
|
|
429
|
+
this.reservationId = reservation.reservationId;
|
|
430
|
+
this.ref = reservation.clientHash !== null ? createRef(reservation.clientHash) : null;
|
|
431
|
+
this.expiresAtUtc = reservation.expiresAtUtc;
|
|
432
|
+
this.remoteUrl = config.remoteUrl;
|
|
433
|
+
this.jwtHandler = config.jwtHandler;
|
|
434
|
+
this.fetchFn = (config.fetchFn ?? globalThis.fetch).bind(globalThis);
|
|
435
|
+
}
|
|
436
|
+
async send(data) {
|
|
437
|
+
const url = `${this.remoteUrl}/attachments/reservations/${this.reservationId}`;
|
|
438
|
+
const authHeaders = await buildAuthHeaders(url, this.jwtHandler);
|
|
439
|
+
const body = await new Response(data).blob();
|
|
440
|
+
const response = await this.fetchFn(url, {
|
|
441
|
+
method: "PUT",
|
|
442
|
+
headers: {
|
|
443
|
+
...authHeaders,
|
|
444
|
+
"Content-Type": "application/octet-stream"
|
|
445
|
+
},
|
|
446
|
+
body
|
|
447
|
+
});
|
|
448
|
+
if (response.status === 422) {
|
|
449
|
+
let errorBody;
|
|
450
|
+
try {
|
|
451
|
+
errorBody = await response.json();
|
|
452
|
+
} catch {
|
|
453
|
+
throw new Error(`Attachment upload failed: ${response.status} ${response.statusText}`);
|
|
454
|
+
}
|
|
455
|
+
if (isRecord$1(errorBody)) {
|
|
456
|
+
if (errorBody.error === "hash_mismatch" && typeof errorBody.claimed === "string" && typeof errorBody.actual === "string") throw new HashMismatch(errorBody.claimed, errorBody.actual);
|
|
457
|
+
if (errorBody.error === "size_mismatch" && typeof errorBody.declared === "number" && typeof errorBody.actual === "number") throw new SizeMismatch(errorBody.declared, errorBody.actual);
|
|
458
|
+
}
|
|
459
|
+
throw new Error(`Attachment upload failed: ${response.status} ${response.statusText}`);
|
|
460
|
+
}
|
|
461
|
+
if (!response.ok) throw new Error(`Attachment upload failed: ${response.status} ${response.statusText}`);
|
|
462
|
+
return await response.json();
|
|
463
|
+
}
|
|
464
|
+
};
|
|
465
|
+
var RemoteAttachmentUploadFactory = class {
|
|
466
|
+
constructor(config) {
|
|
467
|
+
this.config = config;
|
|
468
|
+
}
|
|
469
|
+
createUpload(reservation) {
|
|
470
|
+
return new RemoteAttachmentUpload(reservation, this.config);
|
|
471
|
+
}
|
|
472
|
+
};
|
|
473
|
+
function isRecord(value) {
|
|
474
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
475
|
+
}
|
|
476
|
+
function isAttachmentMetadata(value) {
|
|
477
|
+
if (!isRecord(value)) return false;
|
|
478
|
+
if (typeof value.mimeType !== "string") return false;
|
|
479
|
+
if (typeof value.fileName !== "string") return false;
|
|
480
|
+
if (typeof value.sizeBytes !== "number" || !Number.isFinite(value.sizeBytes) || value.sizeBytes < 0) return false;
|
|
481
|
+
if (value.extension !== null && typeof value.extension !== "string") return false;
|
|
482
|
+
if (typeof value.createdAtUtc !== "string") return false;
|
|
483
|
+
if (value.lastAccessedAtUtc !== void 0 && typeof value.lastAccessedAtUtc !== "string") return false;
|
|
484
|
+
return true;
|
|
485
|
+
}
|
|
486
|
+
function contentTypeFallback(response) {
|
|
487
|
+
const contentLength = response.headers.get("Content-Length");
|
|
488
|
+
if (contentLength === null) throw new Error("Switchboard response missing both Attachment-Metadata and Content-Length headers");
|
|
489
|
+
const sizeBytes = Number(contentLength);
|
|
490
|
+
if (!Number.isInteger(sizeBytes) || sizeBytes < 0) throw new Error(`Switchboard response has invalid Content-Length header: ${JSON.stringify(contentLength)}`);
|
|
491
|
+
const lastModified = response.headers.get("Last-Modified");
|
|
492
|
+
const dateHeader = response.headers.get("Date");
|
|
493
|
+
const createdAtUtc = lastModified ? new Date(lastModified).toISOString() : dateHeader ? new Date(dateHeader).toISOString() : (/* @__PURE__ */ new Date()).toISOString();
|
|
494
|
+
return {
|
|
495
|
+
mimeType: response.headers.get("Content-Type") ?? "application/octet-stream",
|
|
496
|
+
fileName: "unknown",
|
|
497
|
+
sizeBytes,
|
|
498
|
+
extension: null,
|
|
499
|
+
createdAtUtc,
|
|
500
|
+
lastAccessedAtUtc: createdAtUtc
|
|
501
|
+
};
|
|
502
|
+
}
|
|
503
|
+
function parseMetadata(response) {
|
|
504
|
+
let fallbackCache;
|
|
505
|
+
const fallback = () => {
|
|
506
|
+
if (fallbackCache === void 0) fallbackCache = contentTypeFallback(response);
|
|
507
|
+
return fallbackCache;
|
|
508
|
+
};
|
|
509
|
+
const metaHeader = response.headers.get("Attachment-Metadata");
|
|
510
|
+
if (metaHeader) try {
|
|
511
|
+
const parsed = JSON.parse(metaHeader);
|
|
512
|
+
if (isRecord(parsed)) {
|
|
513
|
+
if (parsed.extension === void 0) parsed.extension = null;
|
|
514
|
+
if (parsed.createdAtUtc === void 0) parsed.createdAtUtc = fallback().createdAtUtc;
|
|
515
|
+
if (parsed.lastAccessedAtUtc === void 0) parsed.lastAccessedAtUtc = fallback().lastAccessedAtUtc;
|
|
516
|
+
}
|
|
517
|
+
if (isAttachmentMetadata(parsed)) return parsed;
|
|
518
|
+
} catch {}
|
|
519
|
+
return fallback();
|
|
520
|
+
}
|
|
521
|
+
function parsePendingExpiry(response) {
|
|
522
|
+
const header = response.headers.get("Attachment-Pending");
|
|
523
|
+
if (!header) return null;
|
|
524
|
+
try {
|
|
525
|
+
const parsed = JSON.parse(header);
|
|
526
|
+
if (!isRecord(parsed)) return null;
|
|
527
|
+
if (typeof parsed.expiresAtUtc !== "string") return null;
|
|
528
|
+
const result = { expiresAtUtc: parsed.expiresAtUtc };
|
|
529
|
+
if (typeof parsed.mimeType === "string") result.mimeType = parsed.mimeType;
|
|
530
|
+
if (typeof parsed.fileName === "string") result.fileName = parsed.fileName;
|
|
531
|
+
if (typeof parsed.sizeBytes === "number" && Number.isFinite(parsed.sizeBytes) && parsed.sizeBytes >= 0) result.sizeBytes = parsed.sizeBytes;
|
|
532
|
+
return result;
|
|
533
|
+
} catch {
|
|
534
|
+
return null;
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
function parsePendingHeader(response) {
|
|
538
|
+
const partial = parsePendingExpiry(response);
|
|
539
|
+
if (!partial) return null;
|
|
540
|
+
if (typeof partial.mimeType !== "string" || typeof partial.fileName !== "string" || partial.sizeBytes === void 0) return null;
|
|
541
|
+
return {
|
|
542
|
+
expiresAtUtc: partial.expiresAtUtc,
|
|
543
|
+
mimeType: partial.mimeType,
|
|
544
|
+
fileName: partial.fileName,
|
|
545
|
+
sizeBytes: partial.sizeBytes
|
|
546
|
+
};
|
|
547
|
+
}
|
|
548
|
+
var RemoteAttachmentStore = class {
|
|
549
|
+
remoteUrl;
|
|
550
|
+
jwtHandler;
|
|
551
|
+
fetchFn;
|
|
552
|
+
constructor(config) {
|
|
553
|
+
this.remoteUrl = config.remoteUrl;
|
|
554
|
+
this.jwtHandler = config.jwtHandler;
|
|
555
|
+
this.fetchFn = (config.fetchFn ?? globalThis.fetch).bind(globalThis);
|
|
556
|
+
}
|
|
557
|
+
/**
|
|
558
|
+
* Get attachment metadata. Normally returns a pending AttachmentHeader
|
|
559
|
+
* (status: 'pending') when the server responds 202 with a full
|
|
560
|
+
* Attachment-Pending header. When only expiresAtUtc is present in the
|
|
561
|
+
* header (degraded wire), throws AttachmentPending instead -- the
|
|
562
|
+
* AttachmentPending throw is the degraded-wire case.
|
|
563
|
+
*/
|
|
564
|
+
async stat(hash) {
|
|
565
|
+
const url = `${this.remoteUrl}/attachments/${hash}`;
|
|
566
|
+
const authHeaders = await buildAuthHeaders(url, this.jwtHandler);
|
|
567
|
+
const response = await this.fetchFn(url, {
|
|
568
|
+
method: "HEAD",
|
|
569
|
+
headers: authHeaders
|
|
570
|
+
});
|
|
571
|
+
if (response.status === 202) {
|
|
572
|
+
const fullPending = parsePendingHeader(response);
|
|
573
|
+
if (fullPending) return buildPendingHeader(hash, fullPending);
|
|
574
|
+
const partial = parsePendingExpiry(response);
|
|
575
|
+
if (partial) throw new AttachmentPending(hash, partial.expiresAtUtc);
|
|
576
|
+
throw new Error("Attachment stat returned 202 with missing or malformed Attachment-Pending header");
|
|
577
|
+
}
|
|
578
|
+
if (response.status === 404) throw new AttachmentNotFound(hash);
|
|
579
|
+
if (!response.ok) throw new Error(`Attachment stat failed: ${response.status} ${response.statusText}`);
|
|
580
|
+
return buildHeader(hash, parseMetadata(response));
|
|
581
|
+
}
|
|
582
|
+
async get(hash, signal) {
|
|
583
|
+
return this.fetchAttachment(hash, signal);
|
|
584
|
+
}
|
|
585
|
+
async fetchAttachment(hash, signal) {
|
|
586
|
+
const url = `${this.remoteUrl}/attachments/${hash}`;
|
|
587
|
+
const headers = await buildAuthHeaders(url, this.jwtHandler);
|
|
588
|
+
const response = await this.fetchFn(url, {
|
|
589
|
+
signal,
|
|
590
|
+
headers
|
|
591
|
+
});
|
|
592
|
+
if (response.status === 202) {
|
|
593
|
+
const pending = parsePendingExpiry(response);
|
|
594
|
+
if (!pending) throw new Error("Attachment fetch returned 202 with missing or malformed Attachment-Pending header");
|
|
595
|
+
throw new AttachmentPending(hash, pending.expiresAtUtc);
|
|
596
|
+
}
|
|
597
|
+
if (response.status === 404) throw new AttachmentNotFound(hash);
|
|
598
|
+
if (!response.ok) throw new Error(`Attachment fetch failed: ${response.status} ${response.statusText}`);
|
|
599
|
+
if (!response.body) throw new Error("Response body is null");
|
|
600
|
+
return {
|
|
601
|
+
header: buildHeader(hash, parseMetadata(response)),
|
|
602
|
+
body: response.body
|
|
603
|
+
};
|
|
604
|
+
}
|
|
605
|
+
};
|
|
606
|
+
function buildHeader(hash, metadata) {
|
|
607
|
+
return {
|
|
608
|
+
hash,
|
|
609
|
+
mimeType: metadata.mimeType,
|
|
610
|
+
fileName: metadata.fileName,
|
|
611
|
+
sizeBytes: metadata.sizeBytes,
|
|
612
|
+
extension: metadata.extension,
|
|
613
|
+
status: "available",
|
|
614
|
+
source: "sync",
|
|
615
|
+
createdAtUtc: metadata.createdAtUtc,
|
|
616
|
+
lastAccessedAtUtc: metadata.lastAccessedAtUtc ?? metadata.createdAtUtc,
|
|
617
|
+
expiresAtUtc: null
|
|
618
|
+
};
|
|
619
|
+
}
|
|
620
|
+
function buildPendingHeader(hash, pending) {
|
|
621
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
622
|
+
return {
|
|
623
|
+
hash,
|
|
624
|
+
mimeType: pending.mimeType,
|
|
625
|
+
fileName: pending.fileName,
|
|
626
|
+
sizeBytes: pending.sizeBytes,
|
|
627
|
+
extension: null,
|
|
628
|
+
status: "pending",
|
|
629
|
+
source: "sync",
|
|
630
|
+
createdAtUtc: now,
|
|
631
|
+
lastAccessedAtUtc: now,
|
|
632
|
+
expiresAtUtc: pending.expiresAtUtc
|
|
633
|
+
};
|
|
634
|
+
}
|
|
635
|
+
function createRemoteAttachmentService(config) {
|
|
636
|
+
const reservations = new RemoteReservationStore(config);
|
|
637
|
+
const uploadFactory = new RemoteAttachmentUploadFactory(config);
|
|
638
|
+
return new AttachmentService(new RemoteAttachmentStore(config), reservations, uploadFactory);
|
|
639
|
+
}
|
|
640
|
+
//#endregion
|
|
131
641
|
//#region ../../node_modules/.pnpm/@powerhousedao+design-system@6.2.0-dev.23_@babel+runtime@7.29.2_@electric-sql+pglite@0._b451e295ed096f1dc466b71d0cbd40f8/node_modules/@powerhousedao/design-system/dist/Removed-CO_5C1ce.js
|
|
132
642
|
function Created(props) {
|
|
133
643
|
return /* @__PURE__ */ jsxs("svg", {
|
|
@@ -7838,542 +8348,258 @@ function ToolbarCloseButton(props) {
|
|
|
7838
8348
|
});
|
|
7839
8349
|
}
|
|
7840
8350
|
/**
|
|
7841
|
-
* Creates a toolbar button click handler.
|
|
7842
|
-
*
|
|
7843
|
-
* If an override is provided, it is called with the current document. Otherwise,
|
|
7844
|
-
* the built-in handler is called with the current document.
|
|
7845
|
-
*/
|
|
7846
|
-
function makeOnClick(document, onClickOverride, defaultOnClick) {
|
|
7847
|
-
if (e$5(onClickOverride)) return () => onClickOverride(document);
|
|
7848
|
-
return () => defaultOnClick(document);
|
|
7849
|
-
}
|
|
7850
|
-
/**
|
|
7851
|
-
* Text input styled for use inside a toolbar.
|
|
7852
|
-
*
|
|
7853
|
-
* This wraps `NodeInput` with toolbar-specific text styling. Use it for inline
|
|
7854
|
-
* toolbar editing flows where the user can submit a value or cancel editing.
|
|
7855
|
-
*/
|
|
7856
|
-
function ToolbarInput(props) {
|
|
7857
|
-
const { defaultValue, className, onSubmit, onCancel, "aria-label": ariaLabel } = props;
|
|
7858
|
-
return /* @__PURE__ */ jsx(NodeInput, {
|
|
7859
|
-
defaultValue,
|
|
7860
|
-
className: twMerge("text-center text-sm font-medium text-muted-foreground", className),
|
|
7861
|
-
"aria-label": ariaLabel,
|
|
7862
|
-
onCancel,
|
|
7863
|
-
onSubmit
|
|
7864
|
-
});
|
|
7865
|
-
}
|
|
7866
|
-
/**
|
|
7867
|
-
* Toolbar control for displaying and renaming the current document.
|
|
7868
|
-
*
|
|
7869
|
-
* By default, the component renders the document name as a clickable heading.
|
|
7870
|
-
* When clicked, it switches to an inline input. Submitting the input renames
|
|
7871
|
-
* both the node and the corresponding drive node entry.
|
|
7872
|
-
*/
|
|
7873
|
-
function ToolbarName(props) {
|
|
7874
|
-
const { document, inputClassName, titleClassName } = props;
|
|
7875
|
-
const [isEditing, setIsEditing] = useState(false);
|
|
7876
|
-
const node = useNodeById(document?.header.id);
|
|
7877
|
-
const { onRenameNode, onRenameDriveNodes } = useNodeActions();
|
|
7878
|
-
const documentName = document?.header.name;
|
|
7879
|
-
const documentId = document?.header.id;
|
|
7880
|
-
const activateEditing = () => setIsEditing(true);
|
|
7881
|
-
const cancelEditing = () => setIsEditing(false);
|
|
7882
|
-
const onSubmit = (newName) => {
|
|
7883
|
-
cancelEditing();
|
|
7884
|
-
if (!documentId || !node) return;
|
|
7885
|
-
Promise.all([onRenameNode(newName, node), onRenameDriveNodes(newName, documentId)]).catch(console.error);
|
|
7886
|
-
};
|
|
7887
|
-
if (!documentName) return null;
|
|
7888
|
-
if (isEditing) return /* @__PURE__ */ jsx(ToolbarInput, {
|
|
7889
|
-
className: inputClassName,
|
|
7890
|
-
onSubmit,
|
|
7891
|
-
onCancel: cancelEditing,
|
|
7892
|
-
defaultValue: documentName,
|
|
7893
|
-
"aria-label": "Document name"
|
|
7894
|
-
});
|
|
7895
|
-
return /* @__PURE__ */ jsx("h1", {
|
|
7896
|
-
className: twMerge("cursor-pointer text-sm font-medium text-muted-foreground hover:hover-effect", titleClassName),
|
|
7897
|
-
onClick: activateEditing,
|
|
7898
|
-
title: "Click to edit",
|
|
7899
|
-
children: documentName
|
|
7900
|
-
});
|
|
7901
|
-
}
|
|
7902
|
-
/**
|
|
7903
|
-
* Default slot layout for the built-in document toolbar controls.
|
|
7904
|
-
*
|
|
7905
|
-
* The toolbar is divided into three control groups:
|
|
7906
|
-
*
|
|
7907
|
-
* - `first`: primary document actions.
|
|
7908
|
-
* - `second`: document identity/display controls.
|
|
7909
|
-
* - `third`: secondary document actions.
|
|
7910
|
-
*/
|
|
7911
|
-
const defaultControlSlots = {
|
|
7912
|
-
first: [
|
|
7913
|
-
"undo",
|
|
7914
|
-
"redo",
|
|
7915
|
-
"download"
|
|
7916
|
-
],
|
|
7917
|
-
second: ["name"],
|
|
7918
|
-
third: [
|
|
7919
|
-
"history",
|
|
7920
|
-
"switchboard",
|
|
7921
|
-
"close"
|
|
7922
|
-
]
|
|
7923
|
-
};
|
|
7924
|
-
/**
|
|
7925
|
-
* Ordered list of toolbar slot names.
|
|
7926
|
-
*/
|
|
7927
|
-
const controlSlots = t$3(defaultControlSlots);
|
|
7928
|
-
/**
|
|
7929
|
-
* Ordered list of all built-in document toolbar control names.
|
|
7930
|
-
*
|
|
7931
|
-
* The order is derived from `defaultControlSlots`.
|
|
7932
|
-
*/
|
|
7933
|
-
const documentToolbarControls = [
|
|
7934
|
-
...defaultControlSlots.first,
|
|
7935
|
-
...defaultControlSlots.second,
|
|
7936
|
-
...defaultControlSlots.third
|
|
7937
|
-
];
|
|
7938
|
-
/**
|
|
7939
|
-
* Default component implementation for each built-in toolbar control.
|
|
7940
|
-
*
|
|
7941
|
-
* These components are used unless a matching entry is provided through
|
|
7942
|
-
* `componentOverrides`.
|
|
7943
|
-
*/
|
|
7944
|
-
const defaultControlComponents = {
|
|
7945
|
-
undo: ToolbarUndoButton,
|
|
7946
|
-
redo: ToolbarRedoButton,
|
|
7947
|
-
download: ToolbarDownloadButton,
|
|
7948
|
-
name: ToolbarName,
|
|
7949
|
-
switchboard: ToolbarSwitchboardButton,
|
|
7950
|
-
history: ToolbarHistoryButton,
|
|
7951
|
-
close: ToolbarCloseButton
|
|
7952
|
-
};
|
|
7953
|
-
/**
|
|
7954
|
-
* Creates a predicate for checking whether a built-in toolbar control should render.
|
|
7955
|
-
*
|
|
7956
|
-
* A control renders when it is included in `enabledControls` and absent from
|
|
7957
|
-
* `disabledControls`. When `enabledControls` is omitted, all built-in controls
|
|
7958
|
-
* are considered enabled. When a control appears in both lists,
|
|
7959
|
-
* `disabledControls` takes precedence.
|
|
7960
|
-
*/
|
|
7961
|
-
function makeIsEnabledChecker(args) {
|
|
7962
|
-
const { enabledControls = documentToolbarControls, disabledControls = [] } = args;
|
|
7963
|
-
return (control) => e$2(control, enabledControls) && !e$2(control, disabledControls);
|
|
7964
|
-
}
|
|
7965
|
-
/**
|
|
7966
|
-
* Creates a getter for rendering the built-in toolbar controls in a slot.
|
|
7967
|
-
*
|
|
7968
|
-
* The returned function resolves the controls assigned to a slot, filters them
|
|
7969
|
-
* through the enabled/disabled control lists, applies any component overrides,
|
|
7970
|
-
* and renders each control with the current document.
|
|
7971
|
-
*/
|
|
7972
|
-
function makeToolbarControlsRenderer(args) {
|
|
7973
|
-
const { document, enabledControls, disabledControls, componentOverrides } = args;
|
|
7974
|
-
const checkIsEnabled = makeIsEnabledChecker({
|
|
7975
|
-
enabledControls,
|
|
7976
|
-
disabledControls
|
|
7977
|
-
});
|
|
7978
|
-
const renderComponent = (control) => t$4(e(componentOverrides, control), t$6(e(defaultControlComponents, control)), (Component) => /* @__PURE__ */ jsx(Component, { document }, control));
|
|
7979
|
-
return (slot) => t$4(e(defaultControlSlots, slot), n$1(checkIsEnabled), t$2(renderComponent));
|
|
7980
|
-
}
|
|
7981
|
-
/**
|
|
7982
|
-
* Checks whether a custom control should render in the requested position.
|
|
7983
|
-
*
|
|
7984
|
-
* Controls without an explicit position are treated as `"start"`.
|
|
7985
|
-
*/
|
|
7986
|
-
function isControlInPosition(control, position) {
|
|
7987
|
-
return t$6(control.position, "start") === position;
|
|
7988
|
-
}
|
|
7989
|
-
/**
|
|
7990
|
-
* Creates a getter for rendering custom controls in a slot and position.
|
|
7991
|
-
*
|
|
7992
|
-
* The returned function resolves the custom control or controls assigned to a
|
|
7993
|
-
* slot, then renders only the controls that belong in the requested position.
|
|
7994
|
-
*/
|
|
7995
|
-
function makeCustomControlsRenderer(args) {
|
|
7996
|
-
const { document, customControls = {} } = args;
|
|
7997
|
-
return (slot, pos) => {
|
|
7998
|
-
const controlOrControlList = e(customControls, slot);
|
|
7999
|
-
if (!e$5(controlOrControlList)) return null;
|
|
8000
|
-
if (e$3(controlOrControlList)) return renderCustomControlList(controlOrControlList, pos, document);
|
|
8001
|
-
return renderCustomControl(controlOrControlList, pos, document);
|
|
8002
|
-
};
|
|
8003
|
-
}
|
|
8004
|
-
/**
|
|
8005
|
-
* Renders a single custom control when it belongs in the requested position.
|
|
8006
|
-
*/
|
|
8007
|
-
function renderCustomControl(control, pos, document) {
|
|
8008
|
-
if (!isControlInPosition(control, pos)) return null;
|
|
8009
|
-
const Component = control.component;
|
|
8010
|
-
return /* @__PURE__ */ jsx(Component, { document });
|
|
8011
|
-
}
|
|
8012
|
-
/**
|
|
8013
|
-
* Renders a list of custom controls for the requested position.
|
|
8014
|
-
*
|
|
8015
|
-
* Returns `null` when no controls in the list belong in that position.
|
|
8016
|
-
*/
|
|
8017
|
-
function renderCustomControlList(controls, pos, document) {
|
|
8018
|
-
const controlsInPosition = n$1(controls, (control) => isControlInPosition(control, pos));
|
|
8019
|
-
if (!t$8(controlsInPosition, 1)) return null;
|
|
8020
|
-
return /* @__PURE__ */ jsx(Fragment$1, { children: t$2(controlsInPosition, ({ component: Component, key }) => /* @__PURE__ */ jsx(Component, { document }, key)) });
|
|
8021
|
-
}
|
|
8022
|
-
/**
|
|
8023
|
-
* Renders a document toolbar.
|
|
8024
|
-
*
|
|
8025
|
-
* By default, the toolbar renders the built-in document controls grouped into
|
|
8026
|
-
* toolbar slots. The controls operate on the provided `document`, or on the
|
|
8027
|
-
* currently selected document when no document is provided.
|
|
8028
|
-
*
|
|
8029
|
-
* Use `enabledControls` and `disabledControls` to control which built-in
|
|
8030
|
-
* controls are shown. Use `componentOverrides` to replace individual built-in
|
|
8031
|
-
* controls while keeping the default toolbar layout. Use `customControls` to
|
|
8032
|
-
* insert additional controls before or after the built-in controls in a slot.
|
|
8033
|
-
*
|
|
8034
|
-
* To take over the toolbar contents completely, pass `children`.
|
|
8035
|
-
*/
|
|
8036
|
-
function DocumentToolbar(props) {
|
|
8037
|
-
const [selectedDocument] = useSelectedDocumentSafe();
|
|
8038
|
-
const { toolbarClassName, document = selectedDocument, toolbarContainer: Container = ToolbarContainer } = props;
|
|
8039
|
-
if ("children" in props) return /* @__PURE__ */ jsx(Container, {
|
|
8040
|
-
className: toolbarClassName,
|
|
8041
|
-
children: props.children
|
|
8042
|
-
});
|
|
8043
|
-
return /* @__PURE__ */ jsx(Container, {
|
|
8044
|
-
className: toolbarClassName,
|
|
8045
|
-
children: t$2(controlSlots, (slot) => /* @__PURE__ */ createElement(ControlsContainerSlot, {
|
|
8046
|
-
...props,
|
|
8047
|
-
document,
|
|
8048
|
-
slot,
|
|
8049
|
-
key: slot
|
|
8050
|
-
}))
|
|
8051
|
-
});
|
|
8351
|
+
* Creates a toolbar button click handler.
|
|
8352
|
+
*
|
|
8353
|
+
* If an override is provided, it is called with the current document. Otherwise,
|
|
8354
|
+
* the built-in handler is called with the current document.
|
|
8355
|
+
*/
|
|
8356
|
+
function makeOnClick(document, onClickOverride, defaultOnClick) {
|
|
8357
|
+
if (e$5(onClickOverride)) return () => onClickOverride(document);
|
|
8358
|
+
return () => defaultOnClick(document);
|
|
8052
8359
|
}
|
|
8053
8360
|
/**
|
|
8054
|
-
*
|
|
8361
|
+
* Text input styled for use inside a toolbar.
|
|
8055
8362
|
*
|
|
8056
|
-
*
|
|
8057
|
-
*
|
|
8058
|
-
* after them.
|
|
8363
|
+
* This wraps `NodeInput` with toolbar-specific text styling. Use it for inline
|
|
8364
|
+
* toolbar editing flows where the user can submit a value or cancel editing.
|
|
8059
8365
|
*/
|
|
8060
|
-
function
|
|
8061
|
-
const {
|
|
8062
|
-
|
|
8063
|
-
|
|
8064
|
-
|
|
8065
|
-
|
|
8066
|
-
|
|
8067
|
-
|
|
8068
|
-
const renderCustomControls = makeCustomControlsRenderer({
|
|
8069
|
-
document,
|
|
8070
|
-
customControls
|
|
8071
|
-
});
|
|
8072
|
-
return /* @__PURE__ */ jsxs(ControlsContainer, {
|
|
8073
|
-
className: controlsContainerClassName,
|
|
8074
|
-
children: [
|
|
8075
|
-
renderCustomControls(slot, "start"),
|
|
8076
|
-
renderToolbarControls(slot),
|
|
8077
|
-
renderCustomControls(slot, "end")
|
|
8078
|
-
]
|
|
8366
|
+
function ToolbarInput(props) {
|
|
8367
|
+
const { defaultValue, className, onSubmit, onCancel, "aria-label": ariaLabel } = props;
|
|
8368
|
+
return /* @__PURE__ */ jsx(NodeInput, {
|
|
8369
|
+
defaultValue,
|
|
8370
|
+
className: twMerge("text-center text-sm font-medium text-muted-foreground", className),
|
|
8371
|
+
"aria-label": ariaLabel,
|
|
8372
|
+
onCancel,
|
|
8373
|
+
onSubmit
|
|
8079
8374
|
});
|
|
8080
8375
|
}
|
|
8081
|
-
const normalNodeOptions = [
|
|
8082
|
-
"DUPLICATE",
|
|
8083
|
-
"RENAME",
|
|
8084
|
-
"DELETE",
|
|
8085
|
-
"SETTINGS"
|
|
8086
|
-
];
|
|
8087
|
-
const debugNodeOptions = [
|
|
8088
|
-
"ADD_TRIGGER",
|
|
8089
|
-
"REMOVE_TRIGGER",
|
|
8090
|
-
"ADD_INVALID_TRIGGER"
|
|
8091
|
-
];
|
|
8092
|
-
[...normalNodeOptions, ...debugNodeOptions];
|
|
8093
|
-
//#endregion
|
|
8094
|
-
//#region ../../node_modules/.pnpm/@powerhousedao+reactor-attachments@6.0.0-dev.257_@emnapi+core@1.10.0_@emnapi+runtime@1._ac8ae047337833ca71dd21474a1e3e5f/node_modules/@powerhousedao/reactor-attachments/dist/index.js
|
|
8095
8376
|
/**
|
|
8096
|
-
*
|
|
8377
|
+
* Toolbar control for displaying and renaming the current document.
|
|
8378
|
+
*
|
|
8379
|
+
* By default, the component renders the document name as a clickable heading.
|
|
8380
|
+
* When clicked, it switches to an inline input. Submitting the input renames
|
|
8381
|
+
* both the node and the corresponding drive node entry.
|
|
8097
8382
|
*/
|
|
8098
|
-
|
|
8099
|
-
|
|
8100
|
-
|
|
8101
|
-
|
|
8102
|
-
}
|
|
8103
|
-
|
|
8383
|
+
function ToolbarName(props) {
|
|
8384
|
+
const { document, inputClassName, titleClassName } = props;
|
|
8385
|
+
const [isEditing, setIsEditing] = useState(false);
|
|
8386
|
+
const node = useNodeById(document?.header.id);
|
|
8387
|
+
const { onRenameNode, onRenameDriveNodes } = useNodeActions();
|
|
8388
|
+
const documentName = document?.header.name;
|
|
8389
|
+
const documentId = document?.header.id;
|
|
8390
|
+
const activateEditing = () => setIsEditing(true);
|
|
8391
|
+
const cancelEditing = () => setIsEditing(false);
|
|
8392
|
+
const onSubmit = (newName) => {
|
|
8393
|
+
cancelEditing();
|
|
8394
|
+
if (!documentId || !node) return;
|
|
8395
|
+
Promise.all([onRenameNode(newName, node), onRenameDriveNodes(newName, documentId)]).catch(console.error);
|
|
8396
|
+
};
|
|
8397
|
+
if (!documentName) return null;
|
|
8398
|
+
if (isEditing) return /* @__PURE__ */ jsx(ToolbarInput, {
|
|
8399
|
+
className: inputClassName,
|
|
8400
|
+
onSubmit,
|
|
8401
|
+
onCancel: cancelEditing,
|
|
8402
|
+
defaultValue: documentName,
|
|
8403
|
+
"aria-label": "Document name"
|
|
8404
|
+
});
|
|
8405
|
+
return /* @__PURE__ */ jsx("h1", {
|
|
8406
|
+
className: twMerge("cursor-pointer text-sm font-medium text-muted-foreground hover:hover-effect", titleClassName),
|
|
8407
|
+
onClick: activateEditing,
|
|
8408
|
+
title: "Click to edit",
|
|
8409
|
+
children: documentName
|
|
8410
|
+
});
|
|
8411
|
+
}
|
|
8104
8412
|
/**
|
|
8105
|
-
*
|
|
8413
|
+
* Default slot layout for the built-in document toolbar controls.
|
|
8414
|
+
*
|
|
8415
|
+
* The toolbar is divided into three control groups:
|
|
8416
|
+
*
|
|
8417
|
+
* - `first`: primary document actions.
|
|
8418
|
+
* - `second`: document identity/display controls.
|
|
8419
|
+
* - `third`: secondary document actions.
|
|
8106
8420
|
*/
|
|
8107
|
-
|
|
8108
|
-
|
|
8109
|
-
|
|
8110
|
-
|
|
8111
|
-
|
|
8421
|
+
const defaultControlSlots = {
|
|
8422
|
+
first: [
|
|
8423
|
+
"undo",
|
|
8424
|
+
"redo",
|
|
8425
|
+
"download"
|
|
8426
|
+
],
|
|
8427
|
+
second: ["name"],
|
|
8428
|
+
third: [
|
|
8429
|
+
"history",
|
|
8430
|
+
"switchboard",
|
|
8431
|
+
"close"
|
|
8432
|
+
]
|
|
8112
8433
|
};
|
|
8113
8434
|
/**
|
|
8114
|
-
*
|
|
8115
|
-
*/
|
|
8116
|
-
|
|
8117
|
-
|
|
8118
|
-
|
|
8119
|
-
|
|
8120
|
-
|
|
8121
|
-
|
|
8122
|
-
const
|
|
8123
|
-
|
|
8124
|
-
|
|
8125
|
-
|
|
8126
|
-
|
|
8127
|
-
|
|
8128
|
-
|
|
8129
|
-
|
|
8130
|
-
|
|
8131
|
-
|
|
8132
|
-
|
|
8133
|
-
|
|
8134
|
-
|
|
8135
|
-
|
|
8136
|
-
|
|
8137
|
-
|
|
8138
|
-
|
|
8139
|
-
|
|
8140
|
-
|
|
8141
|
-
async stat(ref) {
|
|
8142
|
-
const { hash } = parseRef(ref);
|
|
8143
|
-
return this.store.stat(hash);
|
|
8144
|
-
}
|
|
8145
|
-
async get(ref, signal) {
|
|
8146
|
-
const { hash } = parseRef(ref);
|
|
8147
|
-
return this.store.get(hash, signal);
|
|
8148
|
-
}
|
|
8149
|
-
};
|
|
8150
|
-
async function buildAuthHeaders(url, jwtHandler) {
|
|
8151
|
-
const headers = {};
|
|
8152
|
-
if (jwtHandler) {
|
|
8153
|
-
const token = await jwtHandler(url);
|
|
8154
|
-
if (token) headers["Authorization"] = `Bearer ${token}`;
|
|
8155
|
-
}
|
|
8156
|
-
return headers;
|
|
8157
|
-
}
|
|
8158
|
-
function isRecord$1(value) {
|
|
8159
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
8160
|
-
}
|
|
8161
|
-
function isReservation(value) {
|
|
8162
|
-
if (!isRecord$1(value)) return false;
|
|
8163
|
-
if (typeof value.reservationId !== "string") return false;
|
|
8164
|
-
if (typeof value.mimeType !== "string") return false;
|
|
8165
|
-
if (typeof value.fileName !== "string") return false;
|
|
8166
|
-
if (value.extension !== null && typeof value.extension !== "string") return false;
|
|
8167
|
-
if (typeof value.createdAtUtc !== "string") return false;
|
|
8168
|
-
if (typeof value.expiresAtUtc !== "string") return false;
|
|
8169
|
-
return true;
|
|
8170
|
-
}
|
|
8171
|
-
var RemoteReservationStore = class {
|
|
8172
|
-
remoteUrl;
|
|
8173
|
-
jwtHandler;
|
|
8174
|
-
fetchFn;
|
|
8175
|
-
constructor(config) {
|
|
8176
|
-
this.remoteUrl = config.remoteUrl;
|
|
8177
|
-
this.jwtHandler = config.jwtHandler;
|
|
8178
|
-
this.fetchFn = config.fetchFn ?? globalThis.fetch;
|
|
8179
|
-
}
|
|
8180
|
-
async create(options) {
|
|
8181
|
-
const url = `${this.remoteUrl}/attachments/reservations`;
|
|
8182
|
-
const authHeaders = await buildAuthHeaders(url, this.jwtHandler);
|
|
8183
|
-
const response = await this.fetchFn(url, {
|
|
8184
|
-
method: "POST",
|
|
8185
|
-
headers: {
|
|
8186
|
-
...authHeaders,
|
|
8187
|
-
"Content-Type": "application/json"
|
|
8188
|
-
},
|
|
8189
|
-
body: JSON.stringify({
|
|
8190
|
-
mimeType: options.mimeType,
|
|
8191
|
-
fileName: options.fileName,
|
|
8192
|
-
extension: options.extension ?? null
|
|
8193
|
-
})
|
|
8194
|
-
});
|
|
8195
|
-
if (!response.ok) throw new Error(`Reservation create failed: ${response.status} ${response.statusText}`);
|
|
8196
|
-
const json = await response.json();
|
|
8197
|
-
const now = /* @__PURE__ */ new Date();
|
|
8198
|
-
return {
|
|
8199
|
-
reservationId: json.reservationId,
|
|
8200
|
-
mimeType: options.mimeType,
|
|
8201
|
-
fileName: options.fileName,
|
|
8202
|
-
extension: options.extension ?? null,
|
|
8203
|
-
createdAtUtc: json.createdAtUtc ?? now.toISOString(),
|
|
8204
|
-
expiresAtUtc: json.expiresAtUtc ?? new Date(now.getTime() + 1440 * 60 * 1e3).toISOString()
|
|
8205
|
-
};
|
|
8206
|
-
}
|
|
8207
|
-
async get(reservationId) {
|
|
8208
|
-
const url = `${this.remoteUrl}/attachments/reservations/${encodeURIComponent(reservationId)}`;
|
|
8209
|
-
const authHeaders = await buildAuthHeaders(url, this.jwtHandler);
|
|
8210
|
-
const response = await this.fetchFn(url, { headers: authHeaders });
|
|
8211
|
-
if (response.status === 404) throw new ReservationNotFound(reservationId);
|
|
8212
|
-
if (!response.ok) throw new Error(`Reservation get failed: ${response.status} ${response.statusText}`);
|
|
8213
|
-
let parsed;
|
|
8214
|
-
try {
|
|
8215
|
-
parsed = await response.json();
|
|
8216
|
-
} catch {
|
|
8217
|
-
throw new Error("Reservation get returned non-JSON response");
|
|
8218
|
-
}
|
|
8219
|
-
if (!isReservation(parsed)) throw new Error("Reservation get returned a payload that does not match the Reservation shape");
|
|
8220
|
-
return parsed;
|
|
8221
|
-
}
|
|
8222
|
-
async delete(reservationId) {
|
|
8223
|
-
const url = `${this.remoteUrl}/attachments/reservations/${encodeURIComponent(reservationId)}`;
|
|
8224
|
-
const authHeaders = await buildAuthHeaders(url, this.jwtHandler);
|
|
8225
|
-
const response = await this.fetchFn(url, {
|
|
8226
|
-
method: "DELETE",
|
|
8227
|
-
headers: authHeaders
|
|
8228
|
-
});
|
|
8229
|
-
if (!response.ok && response.status !== 404 && response.status !== 410) throw new Error(`Reservation delete failed: ${response.status} ${response.statusText}`);
|
|
8230
|
-
}
|
|
8231
|
-
deleteExpired() {
|
|
8232
|
-
return Promise.reject(/* @__PURE__ */ new Error("RemoteReservationStore.deleteExpired is not supported"));
|
|
8233
|
-
}
|
|
8234
|
-
};
|
|
8235
|
-
var RemoteAttachmentUpload = class {
|
|
8236
|
-
reservationId;
|
|
8237
|
-
remoteUrl;
|
|
8238
|
-
jwtHandler;
|
|
8239
|
-
fetchFn;
|
|
8240
|
-
options;
|
|
8241
|
-
constructor(reservationId, options, config) {
|
|
8242
|
-
this.reservationId = reservationId;
|
|
8243
|
-
this.options = options;
|
|
8244
|
-
this.remoteUrl = config.remoteUrl;
|
|
8245
|
-
this.jwtHandler = config.jwtHandler;
|
|
8246
|
-
this.fetchFn = config.fetchFn ?? globalThis.fetch;
|
|
8247
|
-
}
|
|
8248
|
-
async send(data) {
|
|
8249
|
-
const url = `${this.remoteUrl}/attachments/reservations/${this.reservationId}`;
|
|
8250
|
-
const authHeaders = await buildAuthHeaders(url, this.jwtHandler);
|
|
8251
|
-
const response = await this.fetchFn(url, {
|
|
8252
|
-
method: "PUT",
|
|
8253
|
-
headers: {
|
|
8254
|
-
...authHeaders,
|
|
8255
|
-
"Content-Type": "application/octet-stream"
|
|
8256
|
-
},
|
|
8257
|
-
body: data,
|
|
8258
|
-
duplex: "half"
|
|
8259
|
-
});
|
|
8260
|
-
if (!response.ok) throw new Error(`Attachment upload failed: ${response.status} ${response.statusText}`);
|
|
8261
|
-
return await response.json();
|
|
8262
|
-
}
|
|
8263
|
-
};
|
|
8264
|
-
var RemoteAttachmentUploadFactory = class {
|
|
8265
|
-
constructor(config) {
|
|
8266
|
-
this.config = config;
|
|
8267
|
-
}
|
|
8268
|
-
createUpload(reservationId, options) {
|
|
8269
|
-
return new RemoteAttachmentUpload(reservationId, options, this.config);
|
|
8270
|
-
}
|
|
8435
|
+
* Ordered list of toolbar slot names.
|
|
8436
|
+
*/
|
|
8437
|
+
const controlSlots = t$3(defaultControlSlots);
|
|
8438
|
+
/**
|
|
8439
|
+
* Ordered list of all built-in document toolbar control names.
|
|
8440
|
+
*
|
|
8441
|
+
* The order is derived from `defaultControlSlots`.
|
|
8442
|
+
*/
|
|
8443
|
+
const documentToolbarControls = [
|
|
8444
|
+
...defaultControlSlots.first,
|
|
8445
|
+
...defaultControlSlots.second,
|
|
8446
|
+
...defaultControlSlots.third
|
|
8447
|
+
];
|
|
8448
|
+
/**
|
|
8449
|
+
* Default component implementation for each built-in toolbar control.
|
|
8450
|
+
*
|
|
8451
|
+
* These components are used unless a matching entry is provided through
|
|
8452
|
+
* `componentOverrides`.
|
|
8453
|
+
*/
|
|
8454
|
+
const defaultControlComponents = {
|
|
8455
|
+
undo: ToolbarUndoButton,
|
|
8456
|
+
redo: ToolbarRedoButton,
|
|
8457
|
+
download: ToolbarDownloadButton,
|
|
8458
|
+
name: ToolbarName,
|
|
8459
|
+
switchboard: ToolbarSwitchboardButton,
|
|
8460
|
+
history: ToolbarHistoryButton,
|
|
8461
|
+
close: ToolbarCloseButton
|
|
8271
8462
|
};
|
|
8272
|
-
|
|
8273
|
-
|
|
8463
|
+
/**
|
|
8464
|
+
* Creates a predicate for checking whether a built-in toolbar control should render.
|
|
8465
|
+
*
|
|
8466
|
+
* A control renders when it is included in `enabledControls` and absent from
|
|
8467
|
+
* `disabledControls`. When `enabledControls` is omitted, all built-in controls
|
|
8468
|
+
* are considered enabled. When a control appears in both lists,
|
|
8469
|
+
* `disabledControls` takes precedence.
|
|
8470
|
+
*/
|
|
8471
|
+
function makeIsEnabledChecker(args) {
|
|
8472
|
+
const { enabledControls = documentToolbarControls, disabledControls = [] } = args;
|
|
8473
|
+
return (control) => e$2(control, enabledControls) && !e$2(control, disabledControls);
|
|
8274
8474
|
}
|
|
8275
|
-
|
|
8276
|
-
|
|
8277
|
-
|
|
8278
|
-
|
|
8279
|
-
|
|
8280
|
-
|
|
8281
|
-
|
|
8282
|
-
|
|
8283
|
-
|
|
8475
|
+
/**
|
|
8476
|
+
* Creates a getter for rendering the built-in toolbar controls in a slot.
|
|
8477
|
+
*
|
|
8478
|
+
* The returned function resolves the controls assigned to a slot, filters them
|
|
8479
|
+
* through the enabled/disabled control lists, applies any component overrides,
|
|
8480
|
+
* and renders each control with the current document.
|
|
8481
|
+
*/
|
|
8482
|
+
function makeToolbarControlsRenderer(args) {
|
|
8483
|
+
const { document, enabledControls, disabledControls, componentOverrides } = args;
|
|
8484
|
+
const checkIsEnabled = makeIsEnabledChecker({
|
|
8485
|
+
enabledControls,
|
|
8486
|
+
disabledControls
|
|
8487
|
+
});
|
|
8488
|
+
const renderComponent = (control) => t$4(e(componentOverrides, control), t$6(e(defaultControlComponents, control)), (Component) => /* @__PURE__ */ jsx(Component, { document }, control));
|
|
8489
|
+
return (slot) => t$4(e(defaultControlSlots, slot), n$1(checkIsEnabled), t$2(renderComponent));
|
|
8284
8490
|
}
|
|
8285
|
-
|
|
8286
|
-
|
|
8287
|
-
|
|
8288
|
-
|
|
8289
|
-
|
|
8290
|
-
|
|
8291
|
-
|
|
8292
|
-
const createdAtUtc = lastModified ? new Date(lastModified).toISOString() : dateHeader ? new Date(dateHeader).toISOString() : (/* @__PURE__ */ new Date()).toISOString();
|
|
8293
|
-
return {
|
|
8294
|
-
mimeType: response.headers.get("Content-Type") ?? "application/octet-stream",
|
|
8295
|
-
fileName: "unknown",
|
|
8296
|
-
sizeBytes,
|
|
8297
|
-
extension: null,
|
|
8298
|
-
createdAtUtc,
|
|
8299
|
-
lastAccessedAtUtc: createdAtUtc
|
|
8300
|
-
};
|
|
8491
|
+
/**
|
|
8492
|
+
* Checks whether a custom control should render in the requested position.
|
|
8493
|
+
*
|
|
8494
|
+
* Controls without an explicit position are treated as `"start"`.
|
|
8495
|
+
*/
|
|
8496
|
+
function isControlInPosition(control, position) {
|
|
8497
|
+
return t$6(control.position, "start") === position;
|
|
8301
8498
|
}
|
|
8302
|
-
|
|
8303
|
-
|
|
8304
|
-
|
|
8305
|
-
|
|
8306
|
-
|
|
8499
|
+
/**
|
|
8500
|
+
* Creates a getter for rendering custom controls in a slot and position.
|
|
8501
|
+
*
|
|
8502
|
+
* The returned function resolves the custom control or controls assigned to a
|
|
8503
|
+
* slot, then renders only the controls that belong in the requested position.
|
|
8504
|
+
*/
|
|
8505
|
+
function makeCustomControlsRenderer(args) {
|
|
8506
|
+
const { document, customControls = {} } = args;
|
|
8507
|
+
return (slot, pos) => {
|
|
8508
|
+
const controlOrControlList = e(customControls, slot);
|
|
8509
|
+
if (!e$5(controlOrControlList)) return null;
|
|
8510
|
+
if (e$3(controlOrControlList)) return renderCustomControlList(controlOrControlList, pos, document);
|
|
8511
|
+
return renderCustomControl(controlOrControlList, pos, document);
|
|
8307
8512
|
};
|
|
8308
|
-
const metaHeader = response.headers.get("Attachment-Metadata");
|
|
8309
|
-
if (metaHeader) try {
|
|
8310
|
-
const parsed = JSON.parse(metaHeader);
|
|
8311
|
-
if (isRecord(parsed)) {
|
|
8312
|
-
if (parsed.extension === void 0) parsed.extension = null;
|
|
8313
|
-
if (parsed.createdAtUtc === void 0) parsed.createdAtUtc = fallback().createdAtUtc;
|
|
8314
|
-
if (parsed.lastAccessedAtUtc === void 0) parsed.lastAccessedAtUtc = fallback().lastAccessedAtUtc;
|
|
8315
|
-
}
|
|
8316
|
-
if (isAttachmentMetadata(parsed)) return parsed;
|
|
8317
|
-
} catch {}
|
|
8318
|
-
return fallback();
|
|
8319
8513
|
}
|
|
8320
|
-
|
|
8321
|
-
|
|
8322
|
-
|
|
8323
|
-
|
|
8324
|
-
|
|
8325
|
-
|
|
8326
|
-
|
|
8327
|
-
this.fetchFn = config.fetchFn ?? globalThis.fetch;
|
|
8328
|
-
}
|
|
8329
|
-
async stat(hash) {
|
|
8330
|
-
const url = `${this.remoteUrl}/attachments/${hash}`;
|
|
8331
|
-
const authHeaders = await buildAuthHeaders(url, this.jwtHandler);
|
|
8332
|
-
const response = await this.fetchFn(url, {
|
|
8333
|
-
method: "HEAD",
|
|
8334
|
-
headers: authHeaders
|
|
8335
|
-
});
|
|
8336
|
-
if (response.status === 404) throw new AttachmentNotFound(hash);
|
|
8337
|
-
if (!response.ok) throw new Error(`Attachment stat failed: ${response.status} ${response.statusText}`);
|
|
8338
|
-
return buildHeader(hash, parseMetadata(response));
|
|
8339
|
-
}
|
|
8340
|
-
async get(hash, signal) {
|
|
8341
|
-
return this.fetchAttachment(hash, signal);
|
|
8342
|
-
}
|
|
8343
|
-
async fetchAttachment(hash, signal) {
|
|
8344
|
-
const url = `${this.remoteUrl}/attachments/${hash}`;
|
|
8345
|
-
const headers = await buildAuthHeaders(url, this.jwtHandler);
|
|
8346
|
-
const response = await this.fetchFn(url, {
|
|
8347
|
-
signal,
|
|
8348
|
-
headers
|
|
8349
|
-
});
|
|
8350
|
-
if (response.status === 404) throw new AttachmentNotFound(hash);
|
|
8351
|
-
if (!response.ok) throw new Error(`Attachment fetch failed: ${response.status} ${response.statusText}`);
|
|
8352
|
-
if (!response.body) throw new Error("Response body is null");
|
|
8353
|
-
return {
|
|
8354
|
-
header: buildHeader(hash, parseMetadata(response)),
|
|
8355
|
-
body: response.body
|
|
8356
|
-
};
|
|
8357
|
-
}
|
|
8358
|
-
};
|
|
8359
|
-
function buildHeader(hash, metadata) {
|
|
8360
|
-
return {
|
|
8361
|
-
hash,
|
|
8362
|
-
mimeType: metadata.mimeType,
|
|
8363
|
-
fileName: metadata.fileName,
|
|
8364
|
-
sizeBytes: metadata.sizeBytes,
|
|
8365
|
-
extension: metadata.extension,
|
|
8366
|
-
status: "available",
|
|
8367
|
-
source: "sync",
|
|
8368
|
-
createdAtUtc: metadata.createdAtUtc,
|
|
8369
|
-
lastAccessedAtUtc: metadata.lastAccessedAtUtc ?? metadata.createdAtUtc
|
|
8370
|
-
};
|
|
8514
|
+
/**
|
|
8515
|
+
* Renders a single custom control when it belongs in the requested position.
|
|
8516
|
+
*/
|
|
8517
|
+
function renderCustomControl(control, pos, document) {
|
|
8518
|
+
if (!isControlInPosition(control, pos)) return null;
|
|
8519
|
+
const Component = control.component;
|
|
8520
|
+
return /* @__PURE__ */ jsx(Component, { document });
|
|
8371
8521
|
}
|
|
8372
|
-
|
|
8373
|
-
|
|
8374
|
-
|
|
8375
|
-
|
|
8522
|
+
/**
|
|
8523
|
+
* Renders a list of custom controls for the requested position.
|
|
8524
|
+
*
|
|
8525
|
+
* Returns `null` when no controls in the list belong in that position.
|
|
8526
|
+
*/
|
|
8527
|
+
function renderCustomControlList(controls, pos, document) {
|
|
8528
|
+
const controlsInPosition = n$1(controls, (control) => isControlInPosition(control, pos));
|
|
8529
|
+
if (!t$8(controlsInPosition, 1)) return null;
|
|
8530
|
+
return /* @__PURE__ */ jsx(Fragment$1, { children: t$2(controlsInPosition, ({ component: Component, key }) => /* @__PURE__ */ jsx(Component, { document }, key)) });
|
|
8531
|
+
}
|
|
8532
|
+
/**
|
|
8533
|
+
* Renders a document toolbar.
|
|
8534
|
+
*
|
|
8535
|
+
* By default, the toolbar renders the built-in document controls grouped into
|
|
8536
|
+
* toolbar slots. The controls operate on the provided `document`, or on the
|
|
8537
|
+
* currently selected document when no document is provided.
|
|
8538
|
+
*
|
|
8539
|
+
* Use `enabledControls` and `disabledControls` to control which built-in
|
|
8540
|
+
* controls are shown. Use `componentOverrides` to replace individual built-in
|
|
8541
|
+
* controls while keeping the default toolbar layout. Use `customControls` to
|
|
8542
|
+
* insert additional controls before or after the built-in controls in a slot.
|
|
8543
|
+
*
|
|
8544
|
+
* To take over the toolbar contents completely, pass `children`.
|
|
8545
|
+
*/
|
|
8546
|
+
function DocumentToolbar(props) {
|
|
8547
|
+
const [selectedDocument] = useSelectedDocumentSafe();
|
|
8548
|
+
const { toolbarClassName, document = selectedDocument, toolbarContainer: Container = ToolbarContainer } = props;
|
|
8549
|
+
if ("children" in props) return /* @__PURE__ */ jsx(Container, {
|
|
8550
|
+
className: toolbarClassName,
|
|
8551
|
+
children: props.children
|
|
8552
|
+
});
|
|
8553
|
+
return /* @__PURE__ */ jsx(Container, {
|
|
8554
|
+
className: toolbarClassName,
|
|
8555
|
+
children: t$2(controlSlots, (slot) => /* @__PURE__ */ createElement(ControlsContainerSlot, {
|
|
8556
|
+
...props,
|
|
8557
|
+
document,
|
|
8558
|
+
slot,
|
|
8559
|
+
key: slot
|
|
8560
|
+
}))
|
|
8561
|
+
});
|
|
8562
|
+
}
|
|
8563
|
+
/**
|
|
8564
|
+
* Renders one toolbar controls slot.
|
|
8565
|
+
*
|
|
8566
|
+
* Custom controls with position `"start"` are rendered before the built-in
|
|
8567
|
+
* controls for the slot. Custom controls with position `"end"` are rendered
|
|
8568
|
+
* after them.
|
|
8569
|
+
*/
|
|
8570
|
+
function ControlsContainerSlot(props) {
|
|
8571
|
+
const { slot, document, controlsContainerClassName, enabledControls, disabledControls, componentOverrides, customControls, controlsContainer: ControlsContainer = ToolbarControlsContainer } = props;
|
|
8572
|
+
const renderToolbarControls = makeToolbarControlsRenderer({
|
|
8573
|
+
document,
|
|
8574
|
+
enabledControls,
|
|
8575
|
+
disabledControls,
|
|
8576
|
+
componentOverrides
|
|
8577
|
+
});
|
|
8578
|
+
const renderCustomControls = makeCustomControlsRenderer({
|
|
8579
|
+
document,
|
|
8580
|
+
customControls
|
|
8581
|
+
});
|
|
8582
|
+
return /* @__PURE__ */ jsxs(ControlsContainer, {
|
|
8583
|
+
className: controlsContainerClassName,
|
|
8584
|
+
children: [
|
|
8585
|
+
renderCustomControls(slot, "start"),
|
|
8586
|
+
renderToolbarControls(slot),
|
|
8587
|
+
renderCustomControls(slot, "end")
|
|
8588
|
+
]
|
|
8589
|
+
});
|
|
8376
8590
|
}
|
|
8591
|
+
const normalNodeOptions = [
|
|
8592
|
+
"DUPLICATE",
|
|
8593
|
+
"RENAME",
|
|
8594
|
+
"DELETE",
|
|
8595
|
+
"SETTINGS"
|
|
8596
|
+
];
|
|
8597
|
+
const debugNodeOptions = [
|
|
8598
|
+
"ADD_TRIGGER",
|
|
8599
|
+
"REMOVE_TRIGGER",
|
|
8600
|
+
"ADD_INVALID_TRIGGER"
|
|
8601
|
+
];
|
|
8602
|
+
[...normalNodeOptions, ...debugNodeOptions];
|
|
8377
8603
|
//#endregion
|
|
8378
8604
|
//#region editors/ph-clint-project-editor/editor.tsx
|
|
8379
8605
|
const POWERHOUSE_LEVELS = [
|
|
@@ -10198,4 +10424,4 @@ function StatusBadge(props) {
|
|
|
10198
10424
|
//#endregion
|
|
10199
10425
|
export { Editor as default };
|
|
10200
10426
|
|
|
10201
|
-
//# sourceMappingURL=editor-
|
|
10427
|
+
//# sourceMappingURL=editor-Bp2wUo8R.js.map
|