@esposter/shared 4.3.3 → 4.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -105,6 +105,36 @@ let ObjectMethod = /* @__PURE__ */ function(ObjectMethod) {
105
105
  ObjectMethod["ValueOf"] = "valueOf";
106
106
  return ObjectMethod;
107
107
  }({});
108
+ // src/models/panzoom/PanzoomContain.ts
109
+ let PanzoomContain = /* @__PURE__ */ function(PanzoomContain) {
110
+ PanzoomContain["Automatic"] = "automatic";
111
+ PanzoomContain["Inside"] = "inside";
112
+ PanzoomContain["Outside"] = "outside";
113
+ return PanzoomContain;
114
+ }({});
115
+ // src/models/panzoom/PanzoomDeltaMode.ts
116
+ let PanzoomDeltaMode = /* @__PURE__ */ function(PanzoomDeltaMode) {
117
+ PanzoomDeltaMode[PanzoomDeltaMode["Pixel"] = 0] = "Pixel";
118
+ PanzoomDeltaMode[PanzoomDeltaMode["Line"] = 1] = "Line";
119
+ PanzoomDeltaMode[PanzoomDeltaMode["Page"] = 2] = "Page";
120
+ return PanzoomDeltaMode;
121
+ }({});
122
+ // src/models/panzoom/PanzoomEventName.ts
123
+ let PanzoomEventName = /* @__PURE__ */ function(PanzoomEventName) {
124
+ PanzoomEventName["Change"] = "panzoomchange";
125
+ PanzoomEventName["End"] = "panzoomend";
126
+ PanzoomEventName["Pan"] = "panzoompan";
127
+ PanzoomEventName["Reset"] = "panzoomreset";
128
+ PanzoomEventName["Start"] = "panzoomstart";
129
+ PanzoomEventName["Zoom"] = "panzoomzoom";
130
+ return PanzoomEventName;
131
+ }({});
132
+ // src/models/panzoom/PanzoomZoomDirection.ts
133
+ let PanzoomZoomDirection = /* @__PURE__ */ function(PanzoomZoomDirection) {
134
+ PanzoomZoomDirection["In"] = "in";
135
+ PanzoomZoomDirection["Out"] = "out";
136
+ return PanzoomZoomDirection;
137
+ }({});
108
138
  // src/services/app/constants.ts
109
139
  const MAX_READ_LIMIT = 1e3;
110
140
  const SITE_NAME = "Esposter";
@@ -157,6 +187,10 @@ var ItemMetadata = class {
157
187
  deletedAt = null;
158
188
  updatedAt = /* @__PURE__ */ new Date();
159
189
  };
190
+ // src/util/object/getPropertyNames.ts
191
+ const getPropertyNames = () => new Proxy({}, { get: (_target, property) => property });
192
+ // src/models/shared/ItemMetadataPropertyNames.ts
193
+ const ItemMetadataPropertyNames = getPropertyNames();
160
194
  // src/util/reactivity/getRawData.ts
161
195
  const getRawData = (data) => {
162
196
  const raw = data && data.__v_raw;
@@ -209,8 +243,12 @@ const withFinalizer = (callback, finalizer) => {
209
243
  };
210
244
  // src/services/error/withFinalizerAsync.ts
211
245
  const withFinalizerAsync = async (callback, finalizer) => {
212
- const callbackResult = await getResultAsync(async () => callback());
213
- if (finalizer) await getResultAsync(async () => finalizer()).match(noop, (error) => {
246
+ const callbackResult = await getResultAsync(async () => {
247
+ return await callback();
248
+ });
249
+ if (finalizer) await getResultAsync(async () => {
250
+ await finalizer();
251
+ }).match(noop, (error) => {
214
252
  reportFinalizerError(callbackResult, error);
215
253
  });
216
254
  return callbackResult.match((value) => value, (error) => {
@@ -336,12 +374,23 @@ const wrapUpgradeDatabase = (database) => ({
336
374
  ...wrapDatabase(database),
337
375
  createObjectStore: (name, options) => wrapObjectStore(database.createObjectStore(name, options))
338
376
  });
377
+ // src/util/function/getSynchronizedFunction.ts
378
+ const pendingPromises = /* @__PURE__ */ new Set();
379
+ const getSynchronizedFunction = (asyncFunction) => (...args) => {
380
+ const promise = withFinalizerAsync(() => asyncFunction(...args), () => {
381
+ pendingPromises.delete(promise);
382
+ });
383
+ pendingPromises.add(promise);
384
+ };
385
+ const waitForSynchronizedFunctions = async () => {
386
+ await Promise.allSettled(pendingPromises);
387
+ };
339
388
  // src/services/indexedDb/wrapUpgradeTransaction.ts
340
389
  const wrapUpgradeTransaction = (transaction) => {
341
390
  const done = getTransactionDone(transaction);
342
- getResultAsync(() => done).match(noop, (error) => {
391
+ getSynchronizedFunction(() => getResultAsync(() => done).match(noop, (error) => {
343
392
  if (transaction.error) console.error(error);
344
- });
393
+ }))();
345
394
  return {
346
395
  get done() {
347
396
  return done;
@@ -610,6 +659,10 @@ const CUSTOM_EMOJI_ID_ATTRIBUTE = "data-custom-emoji-id";
610
659
  const CUSTOM_EMOJI_NAME_ATTRIBUTE = "data-custom-emoji-name";
611
660
  const SPOILER_ATTRIBUTE = "data-spoiler";
612
661
  const SPOILER_SELECTOR = `span[${SPOILER_ATTRIBUTE}]`;
662
+ // src/services/message/emoji/checkIsOnlyReactor.ts
663
+ const checkIsOnlyReactor = (userIds, userId) => userIds.length === 1 && userIds[0] === userId;
664
+ // src/services/message/emoji/getUpdatedUserIds.ts
665
+ const getUpdatedUserIds = (userIds, newUserId) => userIds.includes(newUserId) ? userIds.filter((id) => id !== newUserId) : [...userIds, newUserId];
613
666
  // src/services/message/getMentions.ts
614
667
  const getMentions = (message) => parse(message).querySelectorAll(MENTION_SELECTOR);
615
668
  // src/services/mime/constants.ts
@@ -701,6 +754,643 @@ const contentType = (value) => {
701
754
  };
702
755
  // src/services/mime/extension.ts
703
756
  const extension = (type) => getMimeTypeEntryMap().get(getMediaType(type).toLowerCase())?.extensions?.[0] ?? false;
757
+ // src/services/panzoom/constants.ts
758
+ const PANZOOM_DEFAULT_MAX_SCALE = 4;
759
+ const PANZOOM_DEFAULT_MIN_SCALE = .125;
760
+ const PANZOOM_DEFAULT_START_SCALE = 1;
761
+ const PANZOOM_DEFAULT_STEP = .3;
762
+ const PANZOOM_WHEEL_STEP_DIVISOR = 3;
763
+ const PANZOOM_WHEEL_NOTCH_PIXELS = 100;
764
+ const PANZOOM_PIXELS_PER_LINE = 16;
765
+ const PANZOOM_PRIMARY_BUTTON = 0;
766
+ const PANZOOM_DEFAULT_CURSOR = "move";
767
+ const PANZOOM_DEFAULT_DURATION = 200;
768
+ const PANZOOM_DEFAULT_EASING = "ease-in-out";
769
+ const PANZOOM_DEFAULT_EXCLUDE_CLASS = "panzoom-exclude";
770
+ const PANZOOM_DEFAULT_OVERFLOW = "hidden";
771
+ const PANZOOM_DEFAULT_PAN_THRESHOLD = 0;
772
+ const PANZOOM_DEFAULT_START_PAN = 0;
773
+ const PANZOOM_DEFAULT_TOUCH_ACTION = "none";
774
+ const PANZOOM_TRANSFORM_ORIGIN = "50% 50%";
775
+ const PANZOOM_SVG_NAMESPACE = "http://www.w3.org/2000/svg";
776
+ const PANZOOM_DEFAULT_OPTIONS = Object.freeze({
777
+ animate: false,
778
+ canvas: false,
779
+ cursor: PANZOOM_DEFAULT_CURSOR,
780
+ disablePan: false,
781
+ disableXAxis: false,
782
+ disableYAxis: false,
783
+ disableZoom: false,
784
+ duration: 200,
785
+ easing: PANZOOM_DEFAULT_EASING,
786
+ excludeClass: PANZOOM_DEFAULT_EXCLUDE_CLASS,
787
+ maxScale: 4,
788
+ minScale: PANZOOM_DEFAULT_MIN_SCALE,
789
+ overflow: PANZOOM_DEFAULT_OVERFLOW,
790
+ panOnlyWhenZoomed: false,
791
+ panThreshold: 0,
792
+ pinchAndPan: false,
793
+ relative: false,
794
+ startScale: 1,
795
+ startX: 0,
796
+ startY: 0,
797
+ step: PANZOOM_DEFAULT_STEP,
798
+ touchAction: PANZOOM_DEFAULT_TOUCH_ACTION
799
+ });
800
+ // src/services/panzoom/getPanzoomTarget.ts
801
+ const getPanzoomTarget = ({ element, options, parent }) => options.canvas ? parent : element;
802
+ // src/services/panzoom/restorePanzoomStyles.ts
803
+ const restorePanzoomStyles = ({ styles }, properties) => {
804
+ for (const [element, values] of styles) {
805
+ const restoredProperties = properties ?? [...values.keys()];
806
+ for (const property of restoredProperties.filter((name) => values.has(name))) {
807
+ const previousValue = values.get(property) ?? "";
808
+ if (previousValue) element.style.setProperty(property, previousValue);
809
+ else element.style.removeProperty(property);
810
+ values.delete(property);
811
+ }
812
+ }
813
+ };
814
+ // src/util/map/getOrCreate.ts
815
+ const getOrCreate = (map, key, create) => {
816
+ if (map.has(key)) return map.get(key);
817
+ const value = create();
818
+ map.set(key, value);
819
+ return value;
820
+ };
821
+ // src/services/panzoom/writePanzoomStyle.ts
822
+ const writePanzoomStyle = ({ isDestroyed, styles }, element, property, value) => {
823
+ if (isDestroyed) return;
824
+ const values = getOrCreate(styles, element, () => /* @__PURE__ */ new Map());
825
+ if (!values.has(property)) values.set(property, element.style.getPropertyValue(property));
826
+ element.style.setProperty(property, value);
827
+ };
828
+ // src/services/panzoom/applyPanzoomStyles.ts
829
+ const applyPanzoomStyles = (context) => {
830
+ const { element, options, parent } = context;
831
+ const touchAction = options.touchAction ?? "none";
832
+ writePanzoomStyle(context, parent, "overflow", options.overflow ?? "hidden");
833
+ writePanzoomStyle(context, parent, "touch-action", touchAction);
834
+ writePanzoomStyle(context, element, "touch-action", touchAction);
835
+ if (options.disablePan) restorePanzoomStyles(context, ["cursor"]);
836
+ else writePanzoomStyle(context, getPanzoomTarget(context), "cursor", options.cursor ?? "move");
837
+ };
838
+ // src/services/panzoom/dispatchPanzoomEvent.ts
839
+ const dispatchPanzoomEvent = ({ element, isDestroyed }, name, detail, { silent }) => {
840
+ if (silent || isDestroyed) return;
841
+ element.dispatchEvent(new CustomEvent(name, { detail }));
842
+ };
843
+ // src/services/panzoom/getPanzoomDrag.ts
844
+ const getPanzoomDrag = (state, point) => ({
845
+ anchorPan: {
846
+ x: state.x,
847
+ y: state.y
848
+ },
849
+ anchorPoint: point,
850
+ downPoint: point,
851
+ isDragging: false
852
+ });
853
+ // src/services/panzoom/getPanzoomEventDetail.ts
854
+ const getPanzoomEventDetail = ({ element, state }, originalEvent) => ({
855
+ isSVG: element.namespaceURI === "http://www.w3.org/2000/svg" && element.localName !== "svg",
856
+ originalEvent,
857
+ scale: state.scale,
858
+ x: state.x,
859
+ y: state.y
860
+ });
861
+ // src/util/array/takeOne.ts
862
+ const takeOne = (values, index = 0) => {
863
+ return values[index];
864
+ };
865
+ // src/services/panzoom/endPanzoomGesture.ts
866
+ const endPanzoomGesture = (context, event) => {
867
+ const { options, pointers, state } = context;
868
+ if (!pointers.has(event.pointerId)) return;
869
+ pointers.delete(event.pointerId);
870
+ if (context.drag?.isDragging && event.type === "pointerup" && event.button === 0) context.isClickSuppressed = true;
871
+ context.drag = void 0;
872
+ context.pinch = void 0;
873
+ const remainingPoints = [...pointers.values()];
874
+ if (remainingPoints.length === 1) context.drag = getPanzoomDrag(state, takeOne(remainingPoints, 0));
875
+ if (pointers.size > 0) return;
876
+ restorePanzoomStyles(context, ["user-select"]);
877
+ applyPanzoomStyles(context);
878
+ dispatchPanzoomEvent(context, "panzoomend", getPanzoomEventDetail(context, event), options);
879
+ };
880
+ // src/services/panzoom/checkIsContainableLength.ts
881
+ const checkIsContainableLength = (length) => Number.isFinite(length) && length > 0;
882
+ // src/services/panzoom/constrainPanzoomAxis.ts
883
+ const constrainPanzoomAxis = (value, axis, scale, contain) => {
884
+ if (!contain || !checkIsContainableLength(axis.parentLength)) return value;
885
+ const halfLength = axis.length / 2;
886
+ const leadingPan = (axis.parentStart - axis.center) / scale + halfLength;
887
+ const trailingPan = (axis.parentStart + axis.parentLength - axis.center) / scale - halfLength;
888
+ const isFitting = axis.length * scale <= axis.parentLength;
889
+ const isInside = contain === "inside" || contain === "automatic" && isFitting;
890
+ return Math.max(Math.min(value, isInside ? trailingPan : leadingPan), isInside ? leadingPan : trailingPan);
891
+ };
892
+ // src/services/panzoom/getPanzoomCoverScale.ts
893
+ const getPanzoomCoverScale = (measure) => Math.max(measure.parent.width / measure.size.width, measure.parent.height / measure.size.height);
894
+ // src/services/panzoom/getPanzoomFitScale.ts
895
+ const getPanzoomFitScale = ({ parent, size }) => {
896
+ const fit = Math.min(checkIsContainableLength(parent.width) ? parent.width / size.width : Number.POSITIVE_INFINITY, checkIsContainableLength(parent.height) ? parent.height / size.height : Number.POSITIVE_INFINITY);
897
+ return Number.isFinite(fit) ? fit : 1;
898
+ };
899
+ // src/services/panzoom/constrainPanzoomScale.ts
900
+ const constrainPanzoomScale = (scale, measure, { contain, maxScale = 4, minScale = PANZOOM_DEFAULT_MIN_SCALE }) => {
901
+ const isParentContainable = checkIsContainableLength(measure.parent.width) || checkIsContainableLength(measure.parent.height);
902
+ if (!contain || !isParentContainable || measure.size.width <= 1 || measure.size.height <= 1) return Math.min(Math.max(scale, minScale), maxScale);
903
+ if (contain === "inside") {
904
+ const cap = Math.max(minScale, Math.min(maxScale, getPanzoomFitScale(measure)));
905
+ return Math.min(Math.max(scale, minScale), cap);
906
+ }
907
+ const floor = contain === "outside" ? getPanzoomCoverScale(measure) : getPanzoomFitScale(measure);
908
+ return Math.min(Math.max(scale, Math.max(minScale, floor)), maxScale);
909
+ };
910
+ // src/services/panzoom/getPanzoomStartScale.ts
911
+ const getPanzoomStartScale = (measure, options) => {
912
+ const { startScale = 1 } = options;
913
+ const scale = startScale === "fit" ? getPanzoomFitScale(measure) : startScale;
914
+ return constrainPanzoomScale(scale, measure, options);
915
+ };
916
+ // src/services/panzoom/constrainPanzoomPan.ts
917
+ const constrainPanzoomPan = (current, requested, toScale, measure, options) => {
918
+ const { contain, disablePan, disableXAxis, disableYAxis, force, panOnlyWhenZoomed, relative, roundPixels } = options;
919
+ const isPanDisabled = disablePan || panOnlyWhenZoomed && current.scale === getPanzoomStartScale(measure, options);
920
+ if (!force && isPanDisabled) return {
921
+ x: current.x,
922
+ y: current.y
923
+ };
924
+ const offsetX = relative ? current.x : 0;
925
+ const offsetY = relative ? current.y : 0;
926
+ const unconstrainedX = disableXAxis ? current.x : offsetX + requested.x;
927
+ const unconstrainedY = disableYAxis ? current.y : offsetY + requested.y;
928
+ const roundedX = roundPixels ? Math.round(unconstrainedX) : unconstrainedX;
929
+ const roundedY = roundPixels ? Math.round(unconstrainedY) : unconstrainedY;
930
+ const axisX = {
931
+ center: measure.center.x,
932
+ length: measure.size.width,
933
+ parentLength: measure.parent.width,
934
+ parentStart: measure.parent.left
935
+ };
936
+ const axisY = {
937
+ center: measure.center.y,
938
+ length: measure.size.height,
939
+ parentLength: measure.parent.height,
940
+ parentStart: measure.parent.top
941
+ };
942
+ return {
943
+ x: constrainPanzoomAxis(roundedX, axisX, toScale, contain),
944
+ y: constrainPanzoomAxis(roundedY, axisY, toScale, contain)
945
+ };
946
+ };
947
+ // src/services/panzoom/dispatchPanzoomChange.ts
948
+ const dispatchPanzoomChange = (context, name, detail, options) => {
949
+ dispatchPanzoomEvent(context, name, detail, options);
950
+ dispatchPanzoomEvent(context, "panzoomchange", detail, options);
951
+ };
952
+ // src/services/panzoom/getPanzoomCallOptions.ts
953
+ const getPanzoomCallOptions = ({ options }, callOptions) => ({
954
+ ...options,
955
+ ...callOptions
956
+ });
957
+ // src/services/panzoom/getPanzoomMeasure.ts
958
+ const getInset = (border, padding) => Number.parseFloat(border) + Number.parseFloat(padding);
959
+ const getPanzoomMeasure = (element, parent, state) => {
960
+ const rectangle = element.getBoundingClientRect();
961
+ const parentRectangle = parent.getBoundingClientRect();
962
+ const parentStyle = getComputedStyle(parent);
963
+ const insetLeft = getInset(parentStyle.borderLeftWidth, parentStyle.paddingLeft);
964
+ const insetTop = getInset(parentStyle.borderTopWidth, parentStyle.paddingTop);
965
+ const insetRight = getInset(parentStyle.borderRightWidth, parentStyle.paddingRight);
966
+ const insetBottom = getInset(parentStyle.borderBottomWidth, parentStyle.paddingBottom);
967
+ return {
968
+ center: {
969
+ x: rectangle.left + rectangle.width / 2 - state.scale * state.x,
970
+ y: rectangle.top + rectangle.height / 2 - state.scale * state.y
971
+ },
972
+ parent: {
973
+ height: parentRectangle.height - insetTop - insetBottom,
974
+ left: parentRectangle.left + insetLeft,
975
+ top: parentRectangle.top + insetTop,
976
+ width: parentRectangle.width - insetLeft - insetRight
977
+ },
978
+ size: {
979
+ height: rectangle.height / state.scale,
980
+ width: rectangle.width / state.scale
981
+ }
982
+ };
983
+ };
984
+ // src/services/panzoom/writePanzoomState.ts
985
+ const writePanzoomState = (context, state, { animate, duration = 200, easing = PANZOOM_DEFAULT_EASING, setTransform }) => {
986
+ if (context.isDestroyed) return;
987
+ const { element } = context;
988
+ context.state = state;
989
+ writePanzoomStyle(context, element, "transition", animate ? `transform ${duration}ms ${easing}` : "none");
990
+ if (setTransform) setTransform(element, getPanzoomEventDetail(context));
991
+ else writePanzoomStyle(context, element, "transform", `scale(${state.scale}) translate(${state.x}px, ${state.y}px)`);
992
+ writePanzoomStyle(context, element, "--panzoom-scale", `${state.scale}`);
993
+ };
994
+ // src/services/panzoom/panPanzoom.ts
995
+ const panPanzoom = (context, point, callOptions, originalEvent) => {
996
+ const options = getPanzoomCallOptions(context, callOptions);
997
+ const { element, parent, state } = context;
998
+ const measure = getPanzoomMeasure(element, parent, state);
999
+ const pan = constrainPanzoomPan(state, point, state.scale, measure, options);
1000
+ if (pan.x === state.x && pan.y === state.y) return getPanzoomEventDetail(context, originalEvent);
1001
+ writePanzoomState(context, {
1002
+ scale: state.scale,
1003
+ x: pan.x,
1004
+ y: pan.y
1005
+ }, options);
1006
+ const detail = getPanzoomEventDetail(context, originalEvent);
1007
+ dispatchPanzoomChange(context, "panzoompan", detail, options);
1008
+ return detail;
1009
+ };
1010
+ // src/services/panzoom/movePanzoomDrag.ts
1011
+ const movePanzoomDrag = (context, drag, point, event) => {
1012
+ const { options, state } = context;
1013
+ if (!drag.isDragging) {
1014
+ if (Math.hypot(point.x - drag.downPoint.x, point.y - drag.downPoint.y) <= (options.panThreshold ?? 0)) return;
1015
+ drag.isDragging = true;
1016
+ }
1017
+ panPanzoom(context, {
1018
+ x: drag.anchorPan.x + (point.x - drag.anchorPoint.x) / state.scale,
1019
+ y: drag.anchorPan.y + (point.y - drag.anchorPoint.y) / state.scale
1020
+ }, {
1021
+ animate: false,
1022
+ relative: false
1023
+ }, event);
1024
+ };
1025
+ // src/services/panzoom/getPanzoomPinchFrame.ts
1026
+ const getPanzoomPinchFrame = (pointers) => {
1027
+ const points = [...pointers.values()];
1028
+ const first = takeOne(points, 0);
1029
+ const second = takeOne(points, 1);
1030
+ return {
1031
+ distance: Math.hypot(first.x - second.x, first.y - second.y),
1032
+ midpoint: {
1033
+ x: (first.x + second.x) / 2,
1034
+ y: (first.y + second.y) / 2
1035
+ }
1036
+ };
1037
+ };
1038
+ // src/services/panzoom/getPanzoomPinchScale.ts
1039
+ const getPanzoomPinchScale = (startScale, startDistance, distance) => startDistance > 0 ? startScale * distance / startDistance : startScale;
1040
+ // src/services/panzoom/checkIsZoomRefused.ts
1041
+ const checkIsZoomRefused = ({ disableZoom, force }) => Boolean(disableZoom) && !force;
1042
+ // src/services/panzoom/getPanzoomFocalPan.ts
1043
+ const getPanzoomFocalPan = (current, toScale, point, measure) => {
1044
+ const offsetX = point.x - measure.center.x;
1045
+ const offsetY = point.y - measure.center.y;
1046
+ return {
1047
+ x: offsetX / toScale - offsetX / current.scale + current.x,
1048
+ y: offsetY / toScale - offsetY / current.scale + current.y
1049
+ };
1050
+ };
1051
+ // src/services/panzoom/getPanzoomZoomedState.ts
1052
+ const getPanzoomZoomedState = (state, measure, options, scale, focal) => {
1053
+ const toScale = constrainPanzoomScale(scale, measure, options);
1054
+ const requested = focal ? getPanzoomFocalPan(state, toScale, focal, measure) : {
1055
+ x: state.x,
1056
+ y: state.y
1057
+ };
1058
+ const pan = constrainPanzoomPan(state, requested, toScale, measure, {
1059
+ ...options,
1060
+ force: true,
1061
+ relative: false
1062
+ });
1063
+ return {
1064
+ scale: toScale,
1065
+ x: pan.x,
1066
+ y: pan.y
1067
+ };
1068
+ };
1069
+ // src/services/panzoom/reanchorPanzoomDrag.ts
1070
+ const reanchorPanzoomDrag = ({ drag, pointers, state }) => {
1071
+ if (!drag) return;
1072
+ drag.anchorPoint = takeOne([...pointers.values()], 0);
1073
+ drag.anchorPan = {
1074
+ x: state.x,
1075
+ y: state.y
1076
+ };
1077
+ };
1078
+ // src/services/panzoom/zoomPanzoom.ts
1079
+ const zoomPanzoom = (context, scale, callOptions, originalEvent) => {
1080
+ const options = getPanzoomCallOptions(context, callOptions);
1081
+ if (checkIsZoomRefused(options)) return getPanzoomEventDetail(context, originalEvent);
1082
+ const { element, parent, state } = context;
1083
+ const measure = getPanzoomMeasure(element, parent, state);
1084
+ writePanzoomState(context, getPanzoomZoomedState(state, measure, options, scale, options.focal), options);
1085
+ const detail = getPanzoomEventDetail(context, originalEvent);
1086
+ dispatchPanzoomChange(context, "panzoomzoom", detail, options);
1087
+ reanchorPanzoomDrag(context);
1088
+ return detail;
1089
+ };
1090
+ // src/services/panzoom/movePanzoomPinch.ts
1091
+ const movePanzoomPinch = (context, pinch, event) => {
1092
+ const frame = getPanzoomPinchFrame(context.pointers);
1093
+ const scale = getPanzoomPinchScale(pinch.startScale, pinch.startDistance, frame.distance);
1094
+ zoomPanzoom(context, scale, {
1095
+ animate: false,
1096
+ focal: pinch.midpoint
1097
+ }, event);
1098
+ if (context.options.pinchAndPan) {
1099
+ const { state } = context;
1100
+ panPanzoom(context, {
1101
+ x: state.x + (frame.midpoint.x - pinch.midpoint.x) / state.scale,
1102
+ y: state.y + (frame.midpoint.y - pinch.midpoint.y) / state.scale
1103
+ }, {
1104
+ animate: false,
1105
+ relative: false
1106
+ }, event);
1107
+ }
1108
+ pinch.midpoint = frame.midpoint;
1109
+ };
1110
+ // src/services/panzoom/movePanzoomGesture.ts
1111
+ const movePanzoomGesture = (context, event) => {
1112
+ const { drag, pinch, pointers } = context;
1113
+ if (!pointers.has(event.pointerId)) return;
1114
+ const point = {
1115
+ x: event.clientX,
1116
+ y: event.clientY
1117
+ };
1118
+ pointers.set(event.pointerId, point);
1119
+ if (pinch) movePanzoomPinch(context, pinch, event);
1120
+ else if (drag && event.pointerId === takeOne([...pointers.keys()], 0)) movePanzoomDrag(context, drag, point, event);
1121
+ };
1122
+ // src/services/panzoom/checkIsPanzoomExcluded.ts
1123
+ const checkIsPanzoomExcluded = (target, { exclude = [], excludeClass = PANZOOM_DEFAULT_EXCLUDE_CLASS }) => exclude.some((excluded) => excluded.contains(target)) || target.closest(`.${CSS.escape(excludeClass)}`) !== null;
1124
+ // src/services/panzoom/startPanzoomGesture.ts
1125
+ const startPanzoomGesture = (context, event) => {
1126
+ const { element, options, pointers, state } = context;
1127
+ context.isClickSuppressed = false;
1128
+ if (pointers.size >= 2) return;
1129
+ if (checkIsPanzoomExcluded(event.target, options)) return;
1130
+ const { filter } = options;
1131
+ if (filter && !filter(event)) return;
1132
+ const point = {
1133
+ x: event.clientX,
1134
+ y: event.clientY
1135
+ };
1136
+ pointers.set(event.pointerId, point);
1137
+ getPanzoomTarget(context).setPointerCapture(event.pointerId);
1138
+ if (options.handleStartEvent) options.handleStartEvent(event);
1139
+ else {
1140
+ event.preventDefault();
1141
+ event.stopPropagation();
1142
+ }
1143
+ if (pointers.size === 1) context.drag = getPanzoomDrag(state, point);
1144
+ if (pointers.size === 2) {
1145
+ const frame = getPanzoomPinchFrame(pointers);
1146
+ if (frame.distance > 0) {
1147
+ context.drag = void 0;
1148
+ context.pinch = {
1149
+ midpoint: frame.midpoint,
1150
+ startDistance: frame.distance,
1151
+ startScale: state.scale
1152
+ };
1153
+ }
1154
+ }
1155
+ writePanzoomStyle(context, element, "user-select", "none");
1156
+ if (!options.disablePan && options.panCursor) writePanzoomStyle(context, getPanzoomTarget(context), "cursor", options.panCursor);
1157
+ dispatchPanzoomEvent(context, "panzoomstart", getPanzoomEventDetail(context, event), options);
1158
+ };
1159
+ // src/services/panzoom/bindPanzoom.ts
1160
+ const bindPanzoom = (context, signal) => {
1161
+ const target = getPanzoomTarget(context);
1162
+ const { ownerDocument } = context.element;
1163
+ target.addEventListener("pointerdown", (event) => {
1164
+ startPanzoomGesture(context, event);
1165
+ }, { signal });
1166
+ target.addEventListener("click", (event) => {
1167
+ if (!context.isClickSuppressed) return;
1168
+ context.isClickSuppressed = false;
1169
+ event.preventDefault();
1170
+ event.stopPropagation();
1171
+ }, {
1172
+ capture: true,
1173
+ signal
1174
+ });
1175
+ ownerDocument.addEventListener("pointermove", (event) => {
1176
+ movePanzoomGesture(context, event);
1177
+ }, {
1178
+ passive: true,
1179
+ signal
1180
+ });
1181
+ ownerDocument.addEventListener("pointerup", (event) => {
1182
+ endPanzoomGesture(context, event);
1183
+ }, {
1184
+ passive: true,
1185
+ signal
1186
+ });
1187
+ ownerDocument.addEventListener("pointercancel", (event) => {
1188
+ endPanzoomGesture(context, event);
1189
+ }, {
1190
+ passive: true,
1191
+ signal
1192
+ });
1193
+ };
1194
+ // src/services/panzoom/checkIsPanzoomElement.ts
1195
+ const checkIsPanzoomElement = (value) => "style" in value;
1196
+ // src/services/panzoom/getPanzoomCentredPan.ts
1197
+ const getPanzoomCentredPan = (scale, measure) => {
1198
+ const parentCentreX = measure.parent.left + measure.parent.width / 2;
1199
+ const parentCentreY = measure.parent.top + measure.parent.height / 2;
1200
+ return {
1201
+ x: (parentCentreX - measure.center.x) / scale,
1202
+ y: (parentCentreY - measure.center.y) / scale
1203
+ };
1204
+ };
1205
+ // src/services/panzoom/fitPanzoom.ts
1206
+ const fitPanzoom = (context, callOptions) => {
1207
+ const options = getPanzoomCallOptions(context, {
1208
+ force: true,
1209
+ ...callOptions
1210
+ });
1211
+ const { element, parent, state } = context;
1212
+ const measure = getPanzoomMeasure(element, parent, state);
1213
+ const scale = constrainPanzoomScale(getPanzoomFitScale(measure), measure, options);
1214
+ const pan = constrainPanzoomPan(state, getPanzoomCentredPan(scale, measure), scale, measure, {
1215
+ ...options,
1216
+ relative: false
1217
+ });
1218
+ writePanzoomState(context, {
1219
+ scale,
1220
+ x: pan.x,
1221
+ y: pan.y
1222
+ }, options);
1223
+ const detail = getPanzoomEventDetail(context);
1224
+ dispatchPanzoomChange(context, "panzoomzoom", detail, options);
1225
+ reanchorPanzoomDrag(context);
1226
+ return detail;
1227
+ };
1228
+ // src/services/panzoom/getPanzoomParent.ts
1229
+ const getPanzoomParent = (element) => {
1230
+ const { parentNode } = element;
1231
+ if (parentNode?.nodeType === Node.DOCUMENT_FRAGMENT_NODE && "host" in parentNode) {
1232
+ const { host } = parentNode;
1233
+ return checkIsPanzoomElement(host) ? host : null;
1234
+ }
1235
+ return element.parentElement;
1236
+ };
1237
+ // src/services/panzoom/getPanzoomStartState.ts
1238
+ const getPanzoomStartState = (state, measure, options) => {
1239
+ const { startX = 0, startY = 0 } = options;
1240
+ const scale = getPanzoomStartScale(measure, options);
1241
+ const pan = constrainPanzoomPan(state, {
1242
+ x: startX,
1243
+ y: startY
1244
+ }, scale, measure, {
1245
+ ...options,
1246
+ force: true,
1247
+ relative: false
1248
+ });
1249
+ return {
1250
+ scale,
1251
+ x: pan.x,
1252
+ y: pan.y
1253
+ };
1254
+ };
1255
+ // src/services/panzoom/getPanzoomWheelScale.ts
1256
+ const getPanzoomWheelScale = (scale, { deltaMode, deltaX, deltaY }, step = PANZOOM_DEFAULT_STEP) => {
1257
+ const delta = deltaY === 0 ? deltaX : deltaY;
1258
+ const pixelDelta = deltaMode === 0 ? delta : delta * 16;
1259
+ const notchFraction = Math.min(Math.abs(pixelDelta) / 100, 1);
1260
+ const direction = -Math.sign(pixelDelta);
1261
+ return scale * Math.exp(direction * step * notchFraction / 3);
1262
+ };
1263
+ // src/services/panzoom/resetPanzoom.ts
1264
+ const resetPanzoom = (context, callOptions) => {
1265
+ const options = getPanzoomCallOptions(context, {
1266
+ animate: true,
1267
+ force: true,
1268
+ ...callOptions
1269
+ });
1270
+ const { element, parent, state } = context;
1271
+ const measure = getPanzoomMeasure(element, parent, state);
1272
+ writePanzoomState(context, getPanzoomStartState(state, measure, options), options);
1273
+ const detail = getPanzoomEventDetail(context);
1274
+ dispatchPanzoomChange(context, "panzoomreset", detail, options);
1275
+ reanchorPanzoomDrag(context);
1276
+ return detail;
1277
+ };
1278
+ // src/services/panzoom/getPanzoomStepScale.ts
1279
+ const getPanzoomStepScale = (scale, direction, step = PANZOOM_DEFAULT_STEP) => {
1280
+ const exponent = direction === "in" ? step : -step;
1281
+ return scale * Math.exp(exponent);
1282
+ };
1283
+ // src/services/panzoom/zoomPanzoomStep.ts
1284
+ const zoomPanzoomStep = (context, direction, zoomOptions) => zoomPanzoom(context, getPanzoomStepScale(context.state.scale, direction, context.options.step), {
1285
+ animate: true,
1286
+ ...zoomOptions
1287
+ });
1288
+ // src/services/panzoom/createPanzoom.ts
1289
+ const createPanzoom = (element, options = {}) => {
1290
+ if (element.nodeType !== Node.ELEMENT_NODE) throw new InvalidOperationError("Create", createPanzoom.name, "the element is not an Element");
1291
+ if (!element.isConnected) throw new InvalidOperationError("Create", createPanzoom.name, "the element is not attached to a document");
1292
+ const parent = getPanzoomParent(element);
1293
+ if (!parent) throw new InvalidOperationError("Create", createPanzoom.name, "the element has no parent to contain it");
1294
+ const context = {
1295
+ element,
1296
+ isClickSuppressed: false,
1297
+ isDestroyed: false,
1298
+ options: {
1299
+ ...PANZOOM_DEFAULT_OPTIONS,
1300
+ ...options
1301
+ },
1302
+ parent,
1303
+ pointers: /* @__PURE__ */ new Map(),
1304
+ state: {
1305
+ scale: 1,
1306
+ x: 0,
1307
+ y: 0
1308
+ },
1309
+ styles: /* @__PURE__ */ new Map()
1310
+ };
1311
+ writePanzoomState(context, context.state, { animate: false });
1312
+ const start = getPanzoomStartState(context.state, getPanzoomMeasure(element, parent, context.state), context.options);
1313
+ writePanzoomState(context, start, { animate: false });
1314
+ writePanzoomStyle(context, element, "transform-origin", PANZOOM_TRANSFORM_ORIGIN);
1315
+ applyPanzoomStyles(context);
1316
+ let controller;
1317
+ const bind = () => {
1318
+ if (controller || context.isDestroyed) return;
1319
+ controller = new AbortController();
1320
+ bindPanzoom(context, controller.signal);
1321
+ };
1322
+ const destroy = () => {
1323
+ context.isDestroyed = true;
1324
+ controller?.abort();
1325
+ controller = void 0;
1326
+ context.drag = void 0;
1327
+ context.pinch = void 0;
1328
+ context.pointers.clear();
1329
+ restorePanzoomStyles(context);
1330
+ };
1331
+ const panzoom = {
1332
+ bind,
1333
+ destroy,
1334
+ fit: (fitOptions = {}) => fitPanzoom(context, fitOptions),
1335
+ getOptions: () => ({ ...context.options }),
1336
+ getPan: () => ({
1337
+ x: context.state.x,
1338
+ y: context.state.y
1339
+ }),
1340
+ getScale: () => context.state.scale,
1341
+ pan: (x, y, panOptions = {}) => panPanzoom(context, {
1342
+ x,
1343
+ y
1344
+ }, panOptions),
1345
+ reset: (resetOptions = {}) => resetPanzoom(context, resetOptions),
1346
+ resetStyle: () => {
1347
+ restorePanzoomStyles(context, [
1348
+ "cursor",
1349
+ "overflow",
1350
+ "touch-action",
1351
+ "user-select"
1352
+ ]);
1353
+ },
1354
+ setOptions: (newOptions = {}) => {
1355
+ const previousTarget = getPanzoomTarget(context);
1356
+ context.options = {
1357
+ ...context.options,
1358
+ ...newOptions
1359
+ };
1360
+ if (controller && getPanzoomTarget(context) !== previousTarget) {
1361
+ controller.abort();
1362
+ controller = void 0;
1363
+ restorePanzoomStyles(context, ["cursor"]);
1364
+ bind();
1365
+ }
1366
+ applyPanzoomStyles(context);
1367
+ },
1368
+ zoom: (scale, zoomOptions = {}) => zoomPanzoom(context, scale, zoomOptions),
1369
+ zoomIn: (zoomOptions = {}) => zoomPanzoomStep(context, "in", zoomOptions),
1370
+ zoomOut: (zoomOptions = {}) => zoomPanzoomStep(context, "out", zoomOptions),
1371
+ zoomToPoint: (scale, point, zoomOptions, originalEvent) => zoomPanzoom(context, scale, {
1372
+ ...zoomOptions,
1373
+ focal: {
1374
+ x: point.clientX,
1375
+ y: point.clientY
1376
+ }
1377
+ }, originalEvent),
1378
+ zoomWithWheel: (event, zoomOptions = {}) => {
1379
+ const callOptions = getPanzoomCallOptions(context, zoomOptions);
1380
+ if (!checkIsZoomRefused(callOptions)) event.preventDefault();
1381
+ return zoomPanzoom(context, getPanzoomWheelScale(context.state.scale, event, context.options.step), {
1382
+ ...zoomOptions,
1383
+ animate: false,
1384
+ focal: {
1385
+ x: event.clientX,
1386
+ y: event.clientY
1387
+ }
1388
+ }, event);
1389
+ }
1390
+ };
1391
+ if (!context.options.noBind) bind();
1392
+ return panzoom;
1393
+ };
704
1394
  // src/services/prettier/css.ts
705
1395
  const css = String.raw;
706
1396
  // src/services/prettier/html.ts
@@ -1177,10 +1867,6 @@ const checkIsUnsafeUrlCharacter = (character) => {
1177
1867
  const codePoint = character.codePointAt(0) ?? 0;
1178
1868
  return checkIsWhitespaceCharacter(character) || codePoint < FIRST_PRINTABLE_CODE_POINT || codePoint >= DELETE_CODE_POINT && codePoint <= LAST_C1_CONTROL_CODE_POINT || character === GRAVE_ACCENT;
1179
1869
  };
1180
- // src/util/array/takeOne.ts
1181
- const takeOne = (values, index = 0) => {
1182
- return values[index];
1183
- };
1184
1870
  // src/services/sanitizeHtml/checkIsAllowedUrl.ts
1185
1871
  const PATH_DELIMITER_REGEX = /[/?#]/u;
1186
1872
  const NON_SCHEME_CHARACTERS_REGEX = /[^a-z\d+.-]/giu;
@@ -1193,6 +1879,17 @@ const checkIsAllowedUrl = (url, allowedSchemes) => {
1193
1879
  const scheme = beforePath.slice(0, separatorIndex).replaceAll(NON_SCHEME_CHARACTERS_REGEX, "").toLowerCase();
1194
1880
  return allowedSchemes.includes(scheme);
1195
1881
  };
1882
+ // src/services/sanitizeHtml/getTagPolicy.ts
1883
+ const getTagPolicy = (tagPolicies, tagName) => Object.hasOwn(tagPolicies, tagName) ? tagPolicies[tagName] : void 0;
1884
+ // src/services/sanitizeHtml/checkIsSourceAllowed.ts
1885
+ const checkIsSourceAllowed = (element, allowedSources) => {
1886
+ const sourceRules = getTagPolicy(allowedSources, element.tagName);
1887
+ if (!sourceRules) return true;
1888
+ return Object.entries(sourceRules).every(([name, patterns]) => {
1889
+ const attribute = element.attrs.find((attr) => attr.name === name);
1890
+ return attribute !== void 0 && patterns.some((pattern) => pattern.test(attribute.value));
1891
+ });
1892
+ };
1196
1893
  // src/services/sanitizeHtml/constants.ts
1197
1894
  const HTML_NAMESPACE = html$1.NS.HTML;
1198
1895
  const DEFAULT_ALLOWED_TAGS = [
@@ -1327,7 +2024,7 @@ const MAX_NESTING_DEPTH = 256;
1327
2024
  const SRCSET_DESCRIPTOR_REGEX = /^(?:\d+w|\d*\.?\d+x|\d+h)$/u;
1328
2025
  const ESCAPED_TEXT_CHARACTERS_REGEX = /[&<>]/gu;
1329
2026
  const ESCAPED_ATTRIBUTE_CHARACTERS_REGEX = /[&"<>]/gu;
1330
- const SANITIZER_ADVISORY_WATCH_SINCE = "2026-10-21";
2027
+ const SANITIZER_ADVISORY_WATCH_SINCE = "2026-10-23";
1331
2028
  // src/services/sanitizeHtml/getChildNodes.ts
1332
2029
  const getChildNodes = (element) => "content" in element ? element.content.childNodes : element.childNodes;
1333
2030
  // src/services/sanitizeHtml/collectText.ts
@@ -1420,7 +2117,6 @@ const filterStyle = (value, rules) => {
1420
2117
  return Array.from(declarations, ([property, { important, propertyValue }]) => `${property}${PROPERTY_SEPARATOR}${propertyValue}${important ? IMPORTANT_SUFFIX : ""}`).join(DECLARATION_SEPARATOR);
1421
2118
  };
1422
2119
  // src/services/sanitizeHtml/filterAttributes.ts
1423
- const getTagPolicy = (tagPolicies, tagName) => Object.hasOwn(tagPolicies, tagName) ? tagPolicies[tagName] : void 0;
1424
2120
  const checkIsMetaRefresh = (tagName, attributes) => tagName === "meta" && attributes["http-equiv"]?.trim().toLowerCase() === "refresh";
1425
2121
  const filterAttributes = (element, policy) => {
1426
2122
  const { tagName } = element;
@@ -1451,7 +2147,7 @@ const renderNode = (node, policy, depth) => {
1451
2147
  if (!("tagName" in node) || depth > 256) return "";
1452
2148
  const element = node;
1453
2149
  const children = getChildNodes(element).map((child) => renderNode(child, policy, depth + 1)).join("");
1454
- if (!(element.namespaceURI === HTML_NAMESPACE && !NON_TEXT_TAGS.includes(element.tagName) && policy.allowedTags.includes(element.tagName))) return NON_TEXT_TAGS.includes(element.tagName) ? "" : children;
2150
+ if (!(element.namespaceURI === HTML_NAMESPACE && !NON_TEXT_TAGS.includes(element.tagName) && policy.allowedTags.includes(element.tagName) && checkIsSourceAllowed(element, policy.allowedSources))) return NON_TEXT_TAGS.includes(element.tagName) ? "" : children;
1455
2151
  const attributes = Object.entries(filterAttributes(element, policy)).map(([name, value]) => ` ${name}="${escapeAttribute(value)}"`).join("");
1456
2152
  const opening = `<${element.tagName}${attributes}>`;
1457
2153
  return VOID_TAGS.includes(element.tagName) ? opening : `${opening}${children}</${element.tagName}>`;
@@ -1502,6 +2198,7 @@ const sanitizeHtml = (html, options) => filterHtml(html, {
1502
2198
  },
1503
2199
  allowedClasses: options?.allowedClasses ?? {},
1504
2200
  allowedSchemes: DEFAULT_ALLOWED_SCHEMES,
2201
+ allowedSources: options?.allowedSources ?? {},
1505
2202
  allowedStyles: {
1506
2203
  ...options?.allowedStyles,
1507
2204
  ...TABLE_ALLOWED_STYLES
@@ -1521,47 +2218,54 @@ const COLOR_REGEXES = [
1521
2218
  /^rgba?\(\d{1,3},\s*\d{1,3},\s*\d{1,3}(?:,\s*[\d.]+)?\)$/u,
1522
2219
  /^[a-z]+$/iu
1523
2220
  ];
1524
- const sanitizeTextHtml = (html) => sanitizeHtml(html, {
1525
- allowedAttributes: {
1526
- input: [
1527
- "checked",
1528
- "disabled",
1529
- "type"
1530
- ],
1531
- li: ["data-checked", "data-type"],
1532
- span: [
1533
- CUSTOM_EMOJI_ID_ATTRIBUTE,
1534
- CUSTOM_EMOJI_NAME_ATTRIBUTE,
1535
- MENTION_ID_ATTRIBUTE,
1536
- MENTION_ITEM_TYPE_ATTRIBUTE,
1537
- MENTION_LABEL_ATTRIBUTE,
1538
- MENTION_TYPE_ATTRIBUTE,
1539
- SPOILER_ATTRIBUTE,
1540
- "style"
1541
- ],
1542
- ul: ["data-type"]
1543
- },
1544
- allowedClasses: {
1545
- code: ["language-*"],
1546
- pre: ["language-*"],
1547
- span: ["hljs*"]
1548
- },
1549
- allowedStyles: { span: {
1550
- "background-color": COLOR_REGEXES,
1551
- "border-radius": [/^[\d.]+(?<unit>px|em|rem|%)$/u],
1552
- color: COLOR_REGEXES
1553
- } },
1554
- allowedTags: [
1555
- ...DEFAULT_ALLOWED_TAGS,
1556
- "input",
1557
- "label"
2221
+ const KIT_ALLOWED_ATTRIBUTES = {
2222
+ input: [
2223
+ "checked",
2224
+ "disabled",
2225
+ "type"
1558
2226
  ],
1559
- transformAttributes: { input: ({ checked }) => ({
1560
- ...checked === void 0 ? {} : { checked },
1561
- disabled: "",
1562
- type: "checkbox"
1563
- }) }
1564
- });
2227
+ li: ["data-checked", "data-type"],
2228
+ span: [
2229
+ CUSTOM_EMOJI_ID_ATTRIBUTE,
2230
+ CUSTOM_EMOJI_NAME_ATTRIBUTE,
2231
+ MENTION_ID_ATTRIBUTE,
2232
+ MENTION_ITEM_TYPE_ATTRIBUTE,
2233
+ MENTION_LABEL_ATTRIBUTE,
2234
+ MENTION_TYPE_ATTRIBUTE,
2235
+ SPOILER_ATTRIBUTE,
2236
+ "style"
2237
+ ],
2238
+ ul: ["data-type"]
2239
+ };
2240
+ const sanitizeTextHtml = (html, allowance = {}) => {
2241
+ const allowedAttributes = { ...KIT_ALLOWED_ATTRIBUTES };
2242
+ for (const [tagName, attributes] of Object.entries(allowance.allowedAttributes ?? {})) allowedAttributes[tagName] = [.../* @__PURE__ */ new Set([...allowedAttributes[tagName] ?? [], ...attributes])];
2243
+ return sanitizeHtml(html, {
2244
+ allowedAttributes,
2245
+ allowedClasses: {
2246
+ code: ["language-*"],
2247
+ pre: ["language-*"],
2248
+ span: ["hljs*"]
2249
+ },
2250
+ allowedSources: allowance.allowedSources,
2251
+ allowedStyles: { span: {
2252
+ "background-color": COLOR_REGEXES,
2253
+ "border-radius": [/^[\d.]+(?<unit>px|em|rem|%)$/u],
2254
+ color: COLOR_REGEXES
2255
+ } },
2256
+ allowedTags: [
2257
+ ...DEFAULT_ALLOWED_TAGS,
2258
+ "input",
2259
+ "label",
2260
+ ...allowance.allowedTags ?? []
2261
+ ],
2262
+ transformAttributes: { input: ({ checked }) => ({
2263
+ ...checked === void 0 ? {} : { checked },
2264
+ disabled: "",
2265
+ type: "checkbox"
2266
+ }) }
2267
+ });
2268
+ };
1565
2269
  // src/services/shared/applyItemMetadataMixin.ts
1566
2270
  const applyItemMetadataMixin = (Base) => class ItemWithMetadata extends Base {
1567
2271
  createdAt = /* @__PURE__ */ new Date();
@@ -1572,6 +2276,8 @@ const applyItemMetadataMixin = (Base) => class ItemWithMetadata extends Base {
1572
2276
  const normalizeString = (value) => value?.trim() ?? "";
1573
2277
  // src/services/zod/createNormalizedStringSchema.ts
1574
2278
  const createNormalizedStringSchema = (maxLength, schema = z.string()) => schema.transform(normalizeString).pipe(z.string().max(maxLength));
2279
+ // src/services/zod/createRecordSchema.ts
2280
+ const createRecordSchema = (keySchema, valueSchema, maxEntries) => z.record(keySchema, valueSchema).refine((record) => Object.keys(record).length <= maxEntries, { error: `Record must have at most ${maxEntries} entries` }).meta({ maxProperties: maxEntries });
1575
2281
  // src/util/id/constants.ts
1576
2282
  const ID_SEPARATOR = "|";
1577
2283
  // src/services/zod/createUniqueArraySchema.ts
@@ -1714,26 +2420,8 @@ const downloadFile = (filename, blobPart, type) => {
1714
2420
  };
1715
2421
  // src/util/environment/checkIsServer.ts
1716
2422
  const checkIsServer = () => typeof window === "undefined";
1717
- // src/util/function/getSynchronizedFunction.ts
1718
- const pendingPromises = /* @__PURE__ */ new Set();
1719
- const getSynchronizedFunction = (asyncFunction) => (...args) => {
1720
- const promise = asyncFunction(...args).finally(() => {
1721
- pendingPromises.delete(promise);
1722
- });
1723
- pendingPromises.add(promise);
1724
- };
1725
- const waitForSynchronizedFunctions = async () => {
1726
- await Promise.allSettled(pendingPromises);
1727
- };
1728
2423
  // src/util/id/uuid/constants.ts
1729
2424
  const UUIDV4_REGEX = /^[0-9A-F]{8}-[0-9A-F]{4}-[4][0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/iu;
1730
- // src/util/map/getOrCreate.ts
1731
- const getOrCreate = (map, key, create) => {
1732
- if (map.has(key)) return map.get(key);
1733
- const value = create();
1734
- map.set(key, value);
1735
- return value;
1736
- };
1737
2425
  // src/util/object/clonePlainDeep.ts
1738
2426
  const clone = (data, copies) => {
1739
2427
  if (!Array.isArray(data) && !checkIsPlainObject(data)) return data;
@@ -1751,8 +2439,6 @@ const clone = (data, copies) => {
1751
2439
  }
1752
2440
  };
1753
2441
  const clonePlainDeep = (data) => clone(data, /* @__PURE__ */ new WeakMap());
1754
- // src/util/object/getPropertyNames.ts
1755
- const getPropertyNames = () => new Proxy({}, { get: (_target, property) => property });
1756
2442
  // src/util/object/jsonDateParse.ts
1757
2443
  const ISO_DATE_REGEX = /^(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})T(?<hours>\d{2}):(?<minutes>\d{2}):(?<seconds>\d{2}(?:\.{0,1}\d*))(?:Z|(?<sign>\+|-)(?<offset>[\d|:]*))?$/u;
1758
2444
  const MS_AJAX_DATE_REGEX = /^\/Date\((?<timestamp>-?\d+(?:[-+]\d+)?)\)[/\\]$/u;
@@ -1789,17 +2475,22 @@ const createEvictingPromiseCache = (getKey, load) => {
1789
2475
  });
1790
2476
  };
1791
2477
  };
2478
+ // src/util/promise/rethrowRejections.ts
2479
+ const rethrowRejections = (reasons) => {
2480
+ if (reasons.length === 1) throw takeOne(reasons);
2481
+ else if (reasons.length > 1) throw new AggregateError(reasons, toAppError(takeOne(reasons)).message);
2482
+ };
1792
2483
  // src/util/promise/settleAll.ts
1793
2484
  const settleAll = async (tasks, concurrencyLimit = tasks.length) => {
1794
2485
  if (tasks.length === 0) return [];
2486
+ if (!Number.isInteger(concurrencyLimit) || concurrencyLimit < 1) throw new InvalidOperationError("Read", settleAll.name, "the concurrency limit is not a whole number of at least one");
1795
2487
  const values = [];
1796
2488
  for (const tasksChunk of chunk(tasks, concurrencyLimit)) {
1797
2489
  const results = await Promise.allSettled(tasksChunk.map((task) => Promise.try(task)));
1798
2490
  const reasons = [];
1799
2491
  for (const result of results) if (result.status === "rejected") reasons.push(result.reason);
1800
2492
  else values.push(result.value);
1801
- if (reasons.length === 1) throw takeOne(reasons);
1802
- else if (reasons.length > 1) throw new AggregateError(reasons, toAppError(takeOne(reasons)).message);
2493
+ rethrowRejections(reasons);
1803
2494
  }
1804
2495
  return values;
1805
2496
  };
@@ -2015,6 +2706,8 @@ const EncodedSubDelimiterMap = {
2015
2706
  const encodeUrlSubDelimiters = (value) => Object.entries(EncodedSubDelimiterMap).reduce((encoded, [subDelimiter, encodedSubDelimiter]) => encoded.replaceAll(subDelimiter, encodedSubDelimiter), value);
2016
2707
  // src/util/text/getDecodedUriComponent.ts
2017
2708
  const getDecodedUriComponent = (value, fallback) => getResult(() => decodeURIComponent(value)).unwrapOr(fallback);
2709
+ // src/util/text/padWithZeros.ts
2710
+ const padWithZeros = (value, length) => String(value).padStart(length, "0");
2018
2711
  // src/util/text/streamToText.ts
2019
2712
  const streamToText = async (readable) => {
2020
2713
  readable.setEncoding("utf8");
@@ -2035,33 +2728,33 @@ const truncate = (string, length) => {
2035
2728
  const uncapitalize = (string) => `${string.charAt(0).toLowerCase()}${string.slice(1)}`;
2036
2729
  // src/util/time/constants.ts
2037
2730
  const MILLISECONDS_PER_SECOND = 1e3;
2038
- // src/util/time/hrtime.ts
2039
- const hrtime = (previousHrTime) => {
2040
- if (checkIsServer()) return process.hrtime(previousHrTime);
2041
- const clocktime = performance.now() * .001;
2042
- let seconds = Math.floor(clocktime);
2043
- let nanoseconds = Math.floor(clocktime % 1 * 1e9);
2044
- if (previousHrTime) {
2045
- seconds -= previousHrTime[0];
2046
- nanoseconds -= previousHrTime[1];
2047
- if (nanoseconds < 0) {
2048
- seconds--;
2049
- nanoseconds += 1e9;
2050
- }
2051
- }
2052
- return [seconds, nanoseconds];
2053
- };
2054
- // src/util/time/now.ts
2055
- const loadNanoseconds = hrtime();
2056
- const loadMilliseconds = Date.now();
2057
- const now = () => {
2058
- const [seconds, nanoseconds] = hrtime(loadNanoseconds);
2059
- return (BigInt(loadMilliseconds) * 10n ** 6n + (BigInt(seconds) * 10n ** 9n + BigInt(nanoseconds))).toString();
2060
- };
2731
+ // src/util/time/getExponentialBackoffDelayMs.ts
2732
+ const getExponentialBackoffDelayMs = (attempt, baseDelayMs, maxDelayMs) => Math.min(baseDelayMs * 2 ** attempt, maxDelayMs);
2061
2733
  // src/util/time/sleep.ts
2062
2734
  const sleep = (durationMs) => new Promise((resolve) => {
2063
2735
  setTimeout(() => {
2064
2736
  resolve();
2065
2737
  }, durationMs);
2066
2738
  });
2067
- export { ALL_SPECIAL_VALUES, APPEND_SEGMENT, CHARSET_PARAMETER_REGEX, CUSTOM_EMOJI_ID_ATTRIBUTE, CUSTOM_EMOJI_NAME_ATTRIBUTE, DEFAULT_ALLOWED_ATTRIBUTES, DEFAULT_ALLOWED_SCHEMES, DEFAULT_ALLOWED_TAGS, DEFAULT_COMPRESSION_LEVEL, DEFAULT_FACET_SCORE, DEFAULT_LINK_ATTRIBUTES, DEFAULT_SOURCE_SCORE, DEFAULT_TEXT_CHARSET, DEFAULT_TYPE_SCORE, ESCAPED_ATTRIBUTE_CHARACTERS_REGEX, ESCAPED_TEXT_CHARACTERS_REGEX, FacetScoreMap, ForbiddenError, GIBIBYTE, HTML_NAMESPACE, ID_SEPARATOR, IMPORTANT_REGEX, InvalidOperationError, ItemMetadata, JSON_POINTER_REGEX, JsonPatchOperationType, KIBIBYTE, MAX_NESTING_DEPTH, MAX_READ_LIMIT, MAX_WINDOW_LOG, MEDIA_TYPE_REGEX, MENTION_EVERYONE_ID, MENTION_HERE_ID, MENTION_ID_ATTRIBUTE, MENTION_ITEM_TYPE_ATTRIBUTE, MENTION_LABEL_ATTRIBUTE, MENTION_SELECTOR, MENTION_TYPE, MENTION_TYPE_ATTRIBUTE, MILLISECONDS_PER_SECOND, MIN_WINDOW_LOG, MentionType, NON_TEXT_TAGS, NotFoundError, NotInitializedError, ObjectMethod, Operation, PATH_SEPARATOR_REGEX, RoutePath, SANITIZER_ADVISORY_WATCH_SINCE, SITE_NAME, SPOILER_ATTRIBUTE, SPOILER_SELECTOR, SRCSET_ATTRIBUTE, SRCSET_DESCRIPTOR_REGEX, Serializable, SourceScoreMap, TEXT_MEDIA_TYPE_REGEX, TypeScoreMap, TypedEventEmitter, URL_ATTRIBUTES, UUIDV4_REGEX, VOID_TAGS, WordFilteredError, addJsonValue, applyItemMetadataMixin, applyPatch, applyPatches, capitalize, charset, checkContainsProfanity, checkIsAllowedUrl, checkIsCustomMethod, checkIsDeepEqual, checkIsNestedControl, checkIsNestedInteraction, checkIsPlainObject, checkIsSameBytes, checkIsSameValueZero, checkIsServer, checkIsUnsafeUrlCharacter, checkIsWhitespaceCharacter, chunk, chunkBySerializedSize, clonePlainDeep, collectText, contentType, createEvictingPromiseCache, createHistory, createItemEntityTypeSchema, createNormalizedStringSchema, createUniqueArraySchema, css, dedent, downloadFile, downloadUrl, encodeUrlSubDelimiters, englishProfaneWords, escapeAttribute, escapeCharacters, escapeText, exhaustiveGuard, extension, filterAttributes, filterClasses, filterHtml, filterSrcset, filterStyle, getChildNodes, getDecodedUriComponent, getEndOfDay, getEnumerableKeys, getExtensionMimeTypeMap, getMediaType, getMentions, getMimeScore, getMimeTypeEntryMap, getOrCreate, getPropertyNames, getRawData, getRequestError, getResult, getResultAsync, getSynchronizedFunction, getTransactionDone, getTransactionObjectStore, getWindowLog, getZonedDateTime, hrtime, html, jsonDateParse, lookup, matchesClassPattern, mergeObjectsStrict, noop, normalizeString, now, omitDeep, openDatabase, parseArrayIndex, parseJsonPointer, pushHistory, readJsonValue, redoHistory, removeJsonValue, renderNode, reportFinalizerError, requestToPromise, sanitizeHtml, sanitizeTextHtml, settleAll, sleep, streamToText, takeOne, takeRandom, toAppError, toJsonPointer, toKebabCase, toRawDeep, truncate, uncapitalize, undoHistory, updateJsonValue, waitForSynchronizedFunctions, withFinalizer, withFinalizerAsync, wrapDatabase, wrapIndex, wrapObjectStore, wrapTransaction, wrapUpgradeDatabase, wrapUpgradeTransaction };
2739
+ // src/util/time/createExponentialBackoff.ts
2740
+ const createExponentialBackoff = (baseDelayMs, maxDelayMs) => {
2741
+ let failureCount = 0;
2742
+ return async (operation) => {
2743
+ if (failureCount > 0) await sleep(getExponentialBackoffDelayMs(failureCount - 1, baseDelayMs, maxDelayMs));
2744
+ return getResultAsync(operation).match((value) => {
2745
+ failureCount = 0;
2746
+ return value;
2747
+ }, (error) => {
2748
+ failureCount += 1;
2749
+ throw error;
2750
+ });
2751
+ };
2752
+ };
2753
+ // src/util/time/now.ts
2754
+ let latestNanoseconds = 0n;
2755
+ const now = () => {
2756
+ const nanoseconds = BigInt(Math.round(performance.timeOrigin * 1e3)) * 1000n + BigInt(Math.round(performance.now() * 1e6));
2757
+ latestNanoseconds = nanoseconds > latestNanoseconds ? nanoseconds : latestNanoseconds + 1n;
2758
+ return latestNanoseconds.toString();
2759
+ };
2760
+ export { ALL_SPECIAL_VALUES, APPEND_SEGMENT, CHARSET_PARAMETER_REGEX, CUSTOM_EMOJI_ID_ATTRIBUTE, CUSTOM_EMOJI_NAME_ATTRIBUTE, DEFAULT_ALLOWED_ATTRIBUTES, DEFAULT_ALLOWED_SCHEMES, DEFAULT_ALLOWED_TAGS, DEFAULT_COMPRESSION_LEVEL, DEFAULT_FACET_SCORE, DEFAULT_LINK_ATTRIBUTES, DEFAULT_SOURCE_SCORE, DEFAULT_TEXT_CHARSET, DEFAULT_TYPE_SCORE, ESCAPED_ATTRIBUTE_CHARACTERS_REGEX, ESCAPED_TEXT_CHARACTERS_REGEX, FacetScoreMap, ForbiddenError, GIBIBYTE, HTML_NAMESPACE, ID_SEPARATOR, IMPORTANT_REGEX, InvalidOperationError, ItemMetadata, ItemMetadataPropertyNames, JSON_POINTER_REGEX, JsonPatchOperationType, KIBIBYTE, MAX_NESTING_DEPTH, MAX_READ_LIMIT, MAX_WINDOW_LOG, MEDIA_TYPE_REGEX, MENTION_EVERYONE_ID, MENTION_HERE_ID, MENTION_ID_ATTRIBUTE, MENTION_ITEM_TYPE_ATTRIBUTE, MENTION_LABEL_ATTRIBUTE, MENTION_SELECTOR, MENTION_TYPE, MENTION_TYPE_ATTRIBUTE, MILLISECONDS_PER_SECOND, MIN_WINDOW_LOG, MentionType, NON_TEXT_TAGS, NotFoundError, NotInitializedError, ObjectMethod, Operation, PANZOOM_DEFAULT_CURSOR, PANZOOM_DEFAULT_DURATION, PANZOOM_DEFAULT_EASING, PANZOOM_DEFAULT_EXCLUDE_CLASS, PANZOOM_DEFAULT_MAX_SCALE, PANZOOM_DEFAULT_MIN_SCALE, PANZOOM_DEFAULT_OPTIONS, PANZOOM_DEFAULT_OVERFLOW, PANZOOM_DEFAULT_PAN_THRESHOLD, PANZOOM_DEFAULT_START_PAN, PANZOOM_DEFAULT_START_SCALE, PANZOOM_DEFAULT_STEP, PANZOOM_DEFAULT_TOUCH_ACTION, PANZOOM_PIXELS_PER_LINE, PANZOOM_PRIMARY_BUTTON, PANZOOM_SVG_NAMESPACE, PANZOOM_TRANSFORM_ORIGIN, PANZOOM_WHEEL_NOTCH_PIXELS, PANZOOM_WHEEL_STEP_DIVISOR, PATH_SEPARATOR_REGEX, PanzoomContain, PanzoomDeltaMode, PanzoomEventName, PanzoomZoomDirection, RoutePath, SANITIZER_ADVISORY_WATCH_SINCE, SITE_NAME, SPOILER_ATTRIBUTE, SPOILER_SELECTOR, SRCSET_ATTRIBUTE, SRCSET_DESCRIPTOR_REGEX, Serializable, SourceScoreMap, TEXT_MEDIA_TYPE_REGEX, TypeScoreMap, TypedEventEmitter, URL_ATTRIBUTES, UUIDV4_REGEX, VOID_TAGS, WordFilteredError, addJsonValue, applyItemMetadataMixin, applyPanzoomStyles, applyPatch, applyPatches, bindPanzoom, capitalize, charset, checkContainsProfanity, checkIsAllowedUrl, checkIsContainableLength, checkIsCustomMethod, checkIsDeepEqual, checkIsNestedControl, checkIsNestedInteraction, checkIsOnlyReactor, checkIsPanzoomElement, checkIsPanzoomExcluded, checkIsPlainObject, checkIsSameBytes, checkIsSameValueZero, checkIsServer, checkIsSourceAllowed, checkIsUnsafeUrlCharacter, checkIsWhitespaceCharacter, checkIsZoomRefused, chunk, chunkBySerializedSize, clonePlainDeep, collectText, constrainPanzoomAxis, constrainPanzoomPan, constrainPanzoomScale, contentType, createEvictingPromiseCache, createExponentialBackoff, createHistory, createItemEntityTypeSchema, createNormalizedStringSchema, createPanzoom, createRecordSchema, createUniqueArraySchema, css, dedent, dispatchPanzoomChange, dispatchPanzoomEvent, downloadFile, downloadUrl, encodeUrlSubDelimiters, endPanzoomGesture, englishProfaneWords, escapeAttribute, escapeCharacters, escapeText, exhaustiveGuard, extension, filterAttributes, filterClasses, filterHtml, filterSrcset, filterStyle, fitPanzoom, getChildNodes, getDecodedUriComponent, getEndOfDay, getEnumerableKeys, getExponentialBackoffDelayMs, getExtensionMimeTypeMap, getMediaType, getMentions, getMimeScore, getMimeTypeEntryMap, getOrCreate, getPanzoomCallOptions, getPanzoomCentredPan, getPanzoomCoverScale, getPanzoomDrag, getPanzoomEventDetail, getPanzoomFitScale, getPanzoomFocalPan, getPanzoomMeasure, getPanzoomParent, getPanzoomPinchFrame, getPanzoomPinchScale, getPanzoomStartScale, getPanzoomStartState, getPanzoomStepScale, getPanzoomTarget, getPanzoomWheelScale, getPanzoomZoomedState, getPropertyNames, getRawData, getRequestError, getResult, getResultAsync, getSynchronizedFunction, getTagPolicy, getTransactionDone, getTransactionObjectStore, getUpdatedUserIds, getWindowLog, getZonedDateTime, html, jsonDateParse, lookup, matchesClassPattern, mergeObjectsStrict, movePanzoomDrag, movePanzoomGesture, movePanzoomPinch, noop, normalizeString, now, omitDeep, openDatabase, padWithZeros, panPanzoom, parseArrayIndex, parseJsonPointer, pushHistory, readJsonValue, reanchorPanzoomDrag, redoHistory, removeJsonValue, renderNode, reportFinalizerError, requestToPromise, resetPanzoom, restorePanzoomStyles, rethrowRejections, sanitizeHtml, sanitizeTextHtml, settleAll, sleep, startPanzoomGesture, streamToText, takeOne, takeRandom, toAppError, toJsonPointer, toKebabCase, toRawDeep, truncate, uncapitalize, undoHistory, updateJsonValue, waitForSynchronizedFunctions, withFinalizer, withFinalizerAsync, wrapDatabase, wrapIndex, wrapObjectStore, wrapTransaction, wrapUpgradeDatabase, wrapUpgradeTransaction, writePanzoomState, writePanzoomStyle, zoomPanzoom, zoomPanzoomStep };