@editframe/elements 0.20.4-beta.0 → 0.23.6-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (183) hide show
  1. package/dist/DelayedLoadingState.js +0 -27
  2. package/dist/EF_FRAMEGEN.d.ts +5 -3
  3. package/dist/EF_FRAMEGEN.js +49 -11
  4. package/dist/_virtual/_@oxc-project_runtime@0.94.0/helpers/decorate.js +7 -0
  5. package/dist/attachContextRoot.d.ts +1 -0
  6. package/dist/attachContextRoot.js +9 -0
  7. package/dist/elements/ContextProxiesController.d.ts +1 -2
  8. package/dist/elements/EFAudio.js +5 -9
  9. package/dist/elements/EFCaptions.d.ts +1 -3
  10. package/dist/elements/EFCaptions.js +112 -129
  11. package/dist/elements/EFImage.js +6 -7
  12. package/dist/elements/EFMedia/AssetIdMediaEngine.js +2 -5
  13. package/dist/elements/EFMedia/AssetMediaEngine.js +36 -33
  14. package/dist/elements/EFMedia/BaseMediaEngine.js +57 -73
  15. package/dist/elements/EFMedia/BufferedSeekingInput.d.ts +1 -1
  16. package/dist/elements/EFMedia/BufferedSeekingInput.js +134 -78
  17. package/dist/elements/EFMedia/JitMediaEngine.js +9 -19
  18. package/dist/elements/EFMedia/audioTasks/makeAudioBufferTask.js +7 -13
  19. package/dist/elements/EFMedia/audioTasks/makeAudioFrequencyAnalysisTask.js +2 -3
  20. package/dist/elements/EFMedia/audioTasks/makeAudioInitSegmentFetchTask.js +1 -1
  21. package/dist/elements/EFMedia/audioTasks/makeAudioInputTask.js +6 -5
  22. package/dist/elements/EFMedia/audioTasks/makeAudioSeekTask.js +1 -3
  23. package/dist/elements/EFMedia/audioTasks/makeAudioSegmentFetchTask.js +1 -1
  24. package/dist/elements/EFMedia/audioTasks/makeAudioSegmentIdTask.js +1 -1
  25. package/dist/elements/EFMedia/audioTasks/makeAudioTimeDomainAnalysisTask.js +1 -1
  26. package/dist/elements/EFMedia/shared/AudioSpanUtils.js +9 -25
  27. package/dist/elements/EFMedia/shared/BufferUtils.js +2 -17
  28. package/dist/elements/EFMedia/shared/GlobalInputCache.js +0 -24
  29. package/dist/elements/EFMedia/shared/PrecisionUtils.js +0 -21
  30. package/dist/elements/EFMedia/shared/ThumbnailExtractor.js +0 -17
  31. package/dist/elements/EFMedia/tasks/makeMediaEngineTask.js +1 -10
  32. package/dist/elements/EFMedia/videoTasks/MainVideoInputCache.d.ts +29 -0
  33. package/dist/elements/EFMedia/videoTasks/MainVideoInputCache.js +32 -0
  34. package/dist/elements/EFMedia/videoTasks/ScrubInputCache.js +1 -15
  35. package/dist/elements/EFMedia/videoTasks/makeScrubVideoBufferTask.js +1 -7
  36. package/dist/elements/EFMedia/videoTasks/makeScrubVideoInputTask.js +8 -5
  37. package/dist/elements/EFMedia/videoTasks/makeScrubVideoSeekTask.js +12 -13
  38. package/dist/elements/EFMedia/videoTasks/makeScrubVideoSegmentIdTask.js +1 -1
  39. package/dist/elements/EFMedia/videoTasks/makeUnifiedVideoSeekTask.js +134 -70
  40. package/dist/elements/EFMedia/videoTasks/makeVideoBufferTask.js +11 -18
  41. package/dist/elements/EFMedia.d.ts +19 -0
  42. package/dist/elements/EFMedia.js +44 -25
  43. package/dist/elements/EFSourceMixin.js +5 -7
  44. package/dist/elements/EFSurface.js +6 -9
  45. package/dist/elements/EFTemporal.browsertest.d.ts +11 -0
  46. package/dist/elements/EFTemporal.d.ts +10 -0
  47. package/dist/elements/EFTemporal.js +100 -41
  48. package/dist/elements/EFThumbnailStrip.js +23 -73
  49. package/dist/elements/EFTimegroup.browsertest.d.ts +3 -3
  50. package/dist/elements/EFTimegroup.d.ts +35 -14
  51. package/dist/elements/EFTimegroup.js +138 -181
  52. package/dist/elements/EFVideo.d.ts +16 -2
  53. package/dist/elements/EFVideo.js +156 -108
  54. package/dist/elements/EFWaveform.js +23 -40
  55. package/dist/elements/SampleBuffer.js +3 -7
  56. package/dist/elements/TargetController.js +5 -5
  57. package/dist/elements/durationConverter.js +4 -4
  58. package/dist/elements/renderTemporalAudio.d.ts +10 -0
  59. package/dist/elements/renderTemporalAudio.js +35 -0
  60. package/dist/elements/updateAnimations.js +19 -43
  61. package/dist/gui/ContextMixin.d.ts +5 -5
  62. package/dist/gui/ContextMixin.js +167 -162
  63. package/dist/gui/Controllable.browsertest.d.ts +0 -0
  64. package/dist/gui/Controllable.d.ts +15 -0
  65. package/dist/gui/Controllable.js +9 -0
  66. package/dist/gui/EFConfiguration.js +7 -7
  67. package/dist/gui/EFControls.browsertest.d.ts +11 -0
  68. package/dist/gui/EFControls.d.ts +18 -4
  69. package/dist/gui/EFControls.js +70 -28
  70. package/dist/gui/EFDial.browsertest.d.ts +0 -0
  71. package/dist/gui/EFDial.d.ts +18 -0
  72. package/dist/gui/EFDial.js +141 -0
  73. package/dist/gui/EFFilmstrip.browsertest.d.ts +11 -0
  74. package/dist/gui/EFFilmstrip.d.ts +12 -2
  75. package/dist/gui/EFFilmstrip.js +214 -129
  76. package/dist/gui/EFFitScale.js +5 -8
  77. package/dist/gui/EFFocusOverlay.js +4 -4
  78. package/dist/gui/EFPause.browsertest.d.ts +0 -0
  79. package/dist/gui/EFPause.d.ts +23 -0
  80. package/dist/gui/EFPause.js +59 -0
  81. package/dist/gui/EFPlay.browsertest.d.ts +0 -0
  82. package/dist/gui/EFPlay.d.ts +23 -0
  83. package/dist/gui/EFPlay.js +59 -0
  84. package/dist/gui/EFPreview.d.ts +4 -0
  85. package/dist/gui/EFPreview.js +18 -9
  86. package/dist/gui/EFResizableBox.browsertest.d.ts +0 -0
  87. package/dist/gui/EFResizableBox.d.ts +34 -0
  88. package/dist/gui/EFResizableBox.js +547 -0
  89. package/dist/gui/EFScrubber.d.ts +9 -3
  90. package/dist/gui/EFScrubber.js +13 -13
  91. package/dist/gui/EFTimeDisplay.d.ts +7 -1
  92. package/dist/gui/EFTimeDisplay.js +8 -8
  93. package/dist/gui/EFToggleLoop.d.ts +9 -3
  94. package/dist/gui/EFToggleLoop.js +7 -5
  95. package/dist/gui/EFTogglePlay.d.ts +12 -4
  96. package/dist/gui/EFTogglePlay.js +26 -21
  97. package/dist/gui/EFWorkbench.js +5 -5
  98. package/dist/gui/PlaybackController.d.ts +67 -0
  99. package/dist/gui/PlaybackController.js +310 -0
  100. package/dist/gui/TWMixin.js +1 -1
  101. package/dist/gui/TWMixin2.js +1 -1
  102. package/dist/gui/TargetOrContextMixin.d.ts +10 -0
  103. package/dist/gui/TargetOrContextMixin.js +98 -0
  104. package/dist/gui/efContext.d.ts +2 -2
  105. package/dist/index.d.ts +5 -0
  106. package/dist/index.js +5 -1
  107. package/dist/otel/BridgeSpanExporter.d.ts +13 -0
  108. package/dist/otel/BridgeSpanExporter.js +87 -0
  109. package/dist/otel/setupBrowserTracing.d.ts +12 -0
  110. package/dist/otel/setupBrowserTracing.js +32 -0
  111. package/dist/otel/tracingHelpers.d.ts +34 -0
  112. package/dist/otel/tracingHelpers.js +112 -0
  113. package/dist/style.css +1 -1
  114. package/dist/transcoding/cache/RequestDeduplicator.js +0 -21
  115. package/dist/transcoding/cache/URLTokenDeduplicator.js +1 -21
  116. package/dist/transcoding/utils/UrlGenerator.js +2 -19
  117. package/dist/utils/LRUCache.js +6 -53
  118. package/package.json +13 -5
  119. package/src/elements/ContextProxiesController.ts +10 -10
  120. package/src/elements/EFAudio.ts +1 -0
  121. package/src/elements/EFCaptions.browsertest.ts +128 -56
  122. package/src/elements/EFCaptions.ts +60 -34
  123. package/src/elements/EFImage.browsertest.ts +1 -2
  124. package/src/elements/EFMedia/AssetMediaEngine.ts +65 -37
  125. package/src/elements/EFMedia/BaseMediaEngine.ts +110 -52
  126. package/src/elements/EFMedia/BufferedSeekingInput.ts +218 -101
  127. package/src/elements/EFMedia/JitMediaEngine.browsertest.ts +3 -0
  128. package/src/elements/EFMedia/audioTasks/makeAudioInputTask.ts +7 -3
  129. package/src/elements/EFMedia/audioTasks/makeAudioSeekTask.chunkboundary.regression.browsertest.ts +1 -1
  130. package/src/elements/EFMedia/videoTasks/MainVideoInputCache.ts +76 -0
  131. package/src/elements/EFMedia/videoTasks/makeScrubVideoInputTask.ts +16 -10
  132. package/src/elements/EFMedia/videoTasks/makeScrubVideoSeekTask.ts +7 -1
  133. package/src/elements/EFMedia/videoTasks/makeUnifiedVideoSeekTask.ts +222 -116
  134. package/src/elements/EFMedia.browsertest.ts +8 -15
  135. package/src/elements/EFMedia.ts +54 -8
  136. package/src/elements/EFSurface.browsertest.ts +2 -6
  137. package/src/elements/EFSurface.ts +1 -0
  138. package/src/elements/EFTemporal.browsertest.ts +58 -1
  139. package/src/elements/EFTemporal.ts +140 -4
  140. package/src/elements/EFThumbnailStrip.browsertest.ts +2 -8
  141. package/src/elements/EFThumbnailStrip.ts +1 -0
  142. package/src/elements/EFTimegroup.browsertest.ts +16 -15
  143. package/src/elements/EFTimegroup.ts +281 -275
  144. package/src/elements/EFVideo.browsertest.ts +162 -74
  145. package/src/elements/EFVideo.ts +229 -101
  146. package/src/elements/FetchContext.browsertest.ts +7 -2
  147. package/src/elements/TargetController.browsertest.ts +1 -0
  148. package/src/elements/TargetController.ts +1 -0
  149. package/src/elements/renderTemporalAudio.ts +108 -0
  150. package/src/elements/updateAnimations.browsertest.ts +181 -6
  151. package/src/elements/updateAnimations.ts +6 -6
  152. package/src/gui/ContextMixin.browsertest.ts +274 -27
  153. package/src/gui/ContextMixin.ts +230 -175
  154. package/src/gui/Controllable.browsertest.ts +258 -0
  155. package/src/gui/Controllable.ts +41 -0
  156. package/src/gui/EFControls.browsertest.ts +294 -80
  157. package/src/gui/EFControls.ts +139 -28
  158. package/src/gui/EFDial.browsertest.ts +84 -0
  159. package/src/gui/EFDial.ts +172 -0
  160. package/src/gui/EFFilmstrip.browsertest.ts +712 -0
  161. package/src/gui/EFFilmstrip.ts +213 -23
  162. package/src/gui/EFPause.browsertest.ts +202 -0
  163. package/src/gui/EFPause.ts +73 -0
  164. package/src/gui/EFPlay.browsertest.ts +202 -0
  165. package/src/gui/EFPlay.ts +73 -0
  166. package/src/gui/EFPreview.ts +20 -5
  167. package/src/gui/EFResizableBox.browsertest.ts +79 -0
  168. package/src/gui/EFResizableBox.ts +898 -0
  169. package/src/gui/EFScrubber.ts +7 -5
  170. package/src/gui/EFTimeDisplay.browsertest.ts +19 -19
  171. package/src/gui/EFTimeDisplay.ts +3 -1
  172. package/src/gui/EFToggleLoop.ts +6 -5
  173. package/src/gui/EFTogglePlay.ts +30 -23
  174. package/src/gui/PlaybackController.ts +522 -0
  175. package/src/gui/TWMixin.css +3 -0
  176. package/src/gui/TargetOrContextMixin.ts +185 -0
  177. package/src/gui/efContext.ts +2 -2
  178. package/src/otel/BridgeSpanExporter.ts +150 -0
  179. package/src/otel/setupBrowserTracing.ts +73 -0
  180. package/src/otel/tracingHelpers.ts +251 -0
  181. package/test/cache-integration-verification.browsertest.ts +1 -1
  182. package/types.json +1 -1
  183. package/dist/elements/ContextProxiesController.js +0 -69
@@ -0,0 +1,98 @@
1
+ import { currentTimeContext } from "./currentTimeContext.js";
2
+ import { durationContext } from "./durationContext.js";
3
+ import { loopContext, playingContext } from "./playingContext.js";
4
+ import { __decorate } from "../_virtual/_@oxc-project_runtime@0.94.0/helpers/decorate.js";
5
+ import { isEFTemporal } from "../elements/EFTemporal.js";
6
+ import { TargetController } from "../elements/TargetController.js";
7
+ import { isControllable } from "./Controllable.js";
8
+ import { consume } from "@lit/context";
9
+ import { property, state } from "lit/decorators.js";
10
+ var ContextRequestEvent = class extends Event {
11
+ constructor(context, contextTarget, callback, subscribe) {
12
+ super("context-request", {
13
+ bubbles: true,
14
+ composed: true
15
+ });
16
+ this.context = context;
17
+ this.contextTarget = contextTarget;
18
+ this.callback = callback;
19
+ this.subscribe = subscribe ?? false;
20
+ }
21
+ };
22
+ function TargetOrContextMixin(superClass, contextToProxy) {
23
+ class TargetOrContextClass extends superClass {
24
+ constructor(..._args) {
25
+ super(..._args);
26
+ this.target = "";
27
+ this.targetElement = null;
28
+ this.contextFromParent = null;
29
+ }
30
+ #targetController;
31
+ #contextUnsubscribe;
32
+ #contextRequestHandler;
33
+ #additionalContextUnsubscribes = /* @__PURE__ */ new Map();
34
+ get effectiveContext() {
35
+ return this.targetElement ?? this.contextFromParent;
36
+ }
37
+ connectedCallback() {
38
+ super.connectedCallback();
39
+ if (this.target) this.#targetController = new TargetController(this);
40
+ this.#contextRequestHandler = (event) => {
41
+ if (this.targetElement && event.type === "context-request") {
42
+ event.stopPropagation();
43
+ this.targetElement.dispatchEvent(new event.constructor(event.type, event));
44
+ }
45
+ };
46
+ this.addEventListener("context-request", this.#contextRequestHandler, true);
47
+ }
48
+ #subscribeToTargetContext() {
49
+ if (!this.targetElement) return;
50
+ this.#contextUnsubscribe?.();
51
+ for (const unsubscribe of this.#additionalContextUnsubscribes.values()) unsubscribe();
52
+ this.#additionalContextUnsubscribes.clear();
53
+ const event = new ContextRequestEvent(contextToProxy, this, (value, unsubscribe) => {
54
+ this.contextFromParent = value;
55
+ this.#contextUnsubscribe = unsubscribe;
56
+ }, true);
57
+ this.targetElement.dispatchEvent(event);
58
+ const additionalContexts = [
59
+ [playingContext, "playing"],
60
+ [loopContext, "loop"],
61
+ [currentTimeContext, "currentTimeMs"],
62
+ [durationContext, "durationMs"]
63
+ ];
64
+ for (const [context, propertyName] of additionalContexts) {
65
+ const contextEvent = new ContextRequestEvent(context, this, (value, unsubscribe) => {
66
+ if (propertyName in this) this[propertyName] = value;
67
+ this.#additionalContextUnsubscribes.set(context, unsubscribe);
68
+ }, true);
69
+ this.targetElement.dispatchEvent(contextEvent);
70
+ }
71
+ }
72
+ updated(changedProperties) {
73
+ super.updated?.(changedProperties);
74
+ if (changedProperties.has("targetElement") && this.targetElement) {
75
+ if (isEFTemporal(this.targetElement) && !isControllable(this.targetElement)) console.warn("Control element is targeting a non-root temporal element without playbackController. Controls can only target root temporal elements (not nested within a timegroup). Target element:", this.targetElement);
76
+ this.#subscribeToTargetContext();
77
+ }
78
+ if (changedProperties.has("target")) {
79
+ if (this.target && !this.#targetController) this.#targetController = new TargetController(this);
80
+ }
81
+ }
82
+ disconnectedCallback() {
83
+ super.disconnectedCallback();
84
+ this.#contextUnsubscribe?.();
85
+ for (const unsubscribe of this.#additionalContextUnsubscribes.values()) unsubscribe();
86
+ this.#additionalContextUnsubscribes.clear();
87
+ if (this.#contextRequestHandler) this.removeEventListener("context-request", this.#contextRequestHandler, true);
88
+ }
89
+ }
90
+ __decorate([property({ type: String })], TargetOrContextClass.prototype, "target", void 0);
91
+ __decorate([state()], TargetOrContextClass.prototype, "targetElement", void 0);
92
+ __decorate([consume({
93
+ context: contextToProxy,
94
+ subscribe: true
95
+ })], TargetOrContextClass.prototype, "contextFromParent", void 0);
96
+ return TargetOrContextClass;
97
+ }
98
+ export { TargetOrContextMixin };
@@ -1,4 +1,4 @@
1
- import { ContextMixinInterface } from './ContextMixin.js';
1
+ import { ControllableInterface } from './Controllable.js';
2
2
  export declare const efContext: {
3
- __context__: ContextMixinInterface | null;
3
+ __context__: ControllableInterface | null;
4
4
  };
package/dist/index.d.ts CHANGED
@@ -10,12 +10,17 @@ export { EFWorkbench } from './gui/EFWorkbench.js';
10
10
  export { EFPreview } from './gui/EFPreview.js';
11
11
  export { EFFilmstrip } from './gui/EFFilmstrip.js';
12
12
  export { EFTogglePlay } from './gui/EFTogglePlay.js';
13
+ export { EFPlay } from './gui/EFPlay.js';
14
+ export { EFPause } from './gui/EFPause.js';
13
15
  export { EFToggleLoop } from './gui/EFToggleLoop.js';
14
16
  export { EFScrubber } from './gui/EFScrubber.js';
15
17
  export { EFTimeDisplay } from './gui/EFTimeDisplay.js';
18
+ export { type DialChangeDetail, EFDial } from './gui/EFDial.js';
16
19
  export { EFControls } from './gui/EFControls.js';
17
20
  export { EFFocusOverlay } from './gui/EFFocusOverlay.js';
21
+ export { type BoxBounds, EFResizableBox } from './gui/EFResizableBox.ts';
18
22
  export { EFFitScale } from './gui/EFFitScale.js';
19
23
  export { EFSurface } from './elements/EFSurface.ts';
20
24
  export { EFThumbnailStrip } from './elements/EFThumbnailStrip.ts';
21
25
  export { getRenderInfo, RenderInfo } from './getRenderInfo.js';
26
+ export type { TraceContext } from './otel/tracingHelpers.js';
package/dist/index.js CHANGED
@@ -11,15 +11,19 @@ import { EFWorkbench } from "./gui/EFWorkbench.js";
11
11
  import { EFPreview } from "./gui/EFPreview.js";
12
12
  import { EFFilmstrip } from "./gui/EFFilmstrip.js";
13
13
  import { EFTogglePlay } from "./gui/EFTogglePlay.js";
14
+ import { EFPlay } from "./gui/EFPlay.js";
15
+ import { EFPause } from "./gui/EFPause.js";
14
16
  import { EFToggleLoop } from "./gui/EFToggleLoop.js";
15
17
  import { EFScrubber } from "./gui/EFScrubber.js";
16
18
  import { EFTimeDisplay } from "./gui/EFTimeDisplay.js";
19
+ import { EFDial } from "./gui/EFDial.js";
17
20
  import { EFControls } from "./gui/EFControls.js";
18
21
  import { EFFocusOverlay } from "./gui/EFFocusOverlay.js";
22
+ import { EFResizableBox } from "./gui/EFResizableBox.js";
19
23
  import { EFFitScale } from "./gui/EFFitScale.js";
20
24
  import { EFSurface } from "./elements/EFSurface.js";
21
25
  import { EFThumbnailStrip } from "./elements/EFThumbnailStrip.js";
22
26
  import "./EF_FRAMEGEN.js";
23
27
  import { RenderInfo, getRenderInfo } from "./getRenderInfo.js";
24
28
  if (typeof window !== "undefined") window.EF_REGISTERED = true;
25
- export { EFAudio, EFCaptions, EFCaptionsActiveWord, EFCaptionsAfterActiveWord, EFCaptionsBeforeActiveWord, EFCaptionsSegment, EFConfiguration, EFControls, EFFilmstrip, EFFitScale, EFFocusOverlay, EFImage, EFPreview, EFScrubber, EFSurface, EFThumbnailStrip, EFTimeDisplay, EFTimegroup, EFToggleLoop, EFTogglePlay, EFVideo, EFWaveform, EFWorkbench, RenderInfo, getRenderInfo };
29
+ export { EFAudio, EFCaptions, EFCaptionsActiveWord, EFCaptionsAfterActiveWord, EFCaptionsBeforeActiveWord, EFCaptionsSegment, EFConfiguration, EFControls, EFDial, EFFilmstrip, EFFitScale, EFFocusOverlay, EFImage, EFPause, EFPlay, EFPreview, EFResizableBox, EFScrubber, EFSurface, EFThumbnailStrip, EFTimeDisplay, EFTimegroup, EFToggleLoop, EFTogglePlay, EFVideo, EFWaveform, EFWorkbench, RenderInfo, getRenderInfo };
@@ -0,0 +1,13 @@
1
+ import { ExportResult } from '@opentelemetry/core';
2
+ import { ReadableSpan, SpanExporter } from '@opentelemetry/sdk-trace-base';
3
+ interface BridgeWithSpanExport {
4
+ exportSpans?: (endpoint: string, payload: string) => void;
5
+ }
6
+ export declare class BridgeSpanExporter implements SpanExporter {
7
+ private bridge;
8
+ private endpoint;
9
+ constructor(bridge: BridgeWithSpanExport, endpoint: string);
10
+ export(spans: ReadableSpan[], resultCallback: (result: ExportResult) => void): void;
11
+ shutdown(): Promise<void>;
12
+ }
13
+ export {};
@@ -0,0 +1,87 @@
1
+ import { ExportResultCode } from "@opentelemetry/core";
2
+ function toHex(value) {
3
+ if (typeof value === "string") return value;
4
+ if (Array.isArray(value)) return value.map((b) => {
5
+ return (typeof b === "number" ? b : 0).toString(16).padStart(2, "0");
6
+ }).join("");
7
+ if (ArrayBuffer.isView(value)) return Array.from(value).map((b) => b.toString(16).padStart(2, "0")).join("");
8
+ return String(value);
9
+ }
10
+ function convertAttribute(value) {
11
+ if (typeof value === "string") return { stringValue: value };
12
+ if (typeof value === "number") return Number.isInteger(value) ? { intValue: value } : { doubleValue: value };
13
+ if (typeof value === "boolean") return { boolValue: value };
14
+ if (Array.isArray(value)) return { arrayValue: { values: value.map(convertAttribute) } };
15
+ return { stringValue: String(value) };
16
+ }
17
+ var BridgeSpanExporter = class {
18
+ constructor(bridge, endpoint) {
19
+ this.bridge = bridge;
20
+ this.endpoint = endpoint;
21
+ }
22
+ export(spans, resultCallback) {
23
+ if (!this.bridge?.exportSpans) {
24
+ resultCallback({ code: ExportResultCode.FAILED });
25
+ return;
26
+ }
27
+ try {
28
+ const otlpPayload = { resourceSpans: [{
29
+ resource: { attributes: Object.entries(spans[0]?.resource?.attributes || {}).map(([key, value]) => ({
30
+ key,
31
+ value: convertAttribute(value)
32
+ })) },
33
+ scopeSpans: [{
34
+ scope: {
35
+ name: "telecine-browser",
36
+ version: "1.0.0"
37
+ },
38
+ spans: spans.map((span) => {
39
+ const ctx = span.spanContext();
40
+ return {
41
+ traceId: toHex(ctx.traceId),
42
+ spanId: toHex(ctx.spanId),
43
+ parentSpanId: span.parentSpanId ? toHex(span.parentSpanId) : void 0,
44
+ name: span.name,
45
+ kind: span.kind,
46
+ startTimeUnixNano: String(span.startTime[0] * 1e9 + span.startTime[1]),
47
+ endTimeUnixNano: String(span.endTime[0] * 1e9 + span.endTime[1]),
48
+ attributes: Object.entries(span.attributes).map(([key, value]) => ({
49
+ key,
50
+ value: convertAttribute(value)
51
+ })),
52
+ status: span.status,
53
+ events: span.events.map((event) => ({
54
+ timeUnixNano: String(event.time[0] * 1e9 + event.time[1]),
55
+ name: event.name,
56
+ attributes: Object.entries(event.attributes || {}).map(([key, value]) => ({
57
+ key,
58
+ value: convertAttribute(value)
59
+ }))
60
+ })),
61
+ links: span.links.map((link) => ({
62
+ traceId: toHex(link.context.traceId),
63
+ spanId: toHex(link.context.spanId),
64
+ attributes: Object.entries(link.attributes || {}).map(([key, value]) => ({
65
+ key,
66
+ value: convertAttribute(value)
67
+ }))
68
+ }))
69
+ };
70
+ })
71
+ }]
72
+ }] };
73
+ const serializedPayload = JSON.stringify(otlpPayload);
74
+ this.bridge.exportSpans(this.endpoint, serializedPayload);
75
+ resultCallback({ code: ExportResultCode.SUCCESS });
76
+ } catch (error) {
77
+ resultCallback({
78
+ code: ExportResultCode.FAILED,
79
+ error: error instanceof Error ? error : new Error(String(error))
80
+ });
81
+ }
82
+ }
83
+ shutdown() {
84
+ return Promise.resolve();
85
+ }
86
+ };
87
+ export { BridgeSpanExporter };
@@ -0,0 +1,12 @@
1
+ interface BridgeWithSpanExport {
2
+ exportSpans?: (endpoint: string, payload: string) => void;
3
+ }
4
+ export interface BrowserTracingConfig {
5
+ otelEndpoint: string;
6
+ serviceName?: string;
7
+ bridge?: BridgeWithSpanExport;
8
+ useBatching?: boolean;
9
+ }
10
+ export declare function setupBrowserTracing(config: BrowserTracingConfig): Promise<void>;
11
+ export declare function isBrowserTracingInitialized(): boolean;
12
+ export {};
@@ -0,0 +1,32 @@
1
+ import { BridgeSpanExporter } from "./BridgeSpanExporter.js";
2
+ import { Resource } from "@opentelemetry/resources";
3
+ import { BatchSpanProcessor, SimpleSpanProcessor } from "@opentelemetry/sdk-trace-base";
4
+ import { WebTracerProvider } from "@opentelemetry/sdk-trace-web";
5
+ import { ATTR_SERVICE_NAME } from "@opentelemetry/semantic-conventions";
6
+ var isInitialized = false;
7
+ var provider = null;
8
+ async function setupBrowserTracing(config) {
9
+ if (isInitialized) return;
10
+ try {
11
+ if (!config.bridge) throw new Error("Bridge is required for browser tracing");
12
+ const exporter = new BridgeSpanExporter(config.bridge, config.otelEndpoint);
13
+ let spanProcessor;
14
+ if (config.useBatching) spanProcessor = new BatchSpanProcessor(exporter, {
15
+ maxQueueSize: 100,
16
+ maxExportBatchSize: 10,
17
+ scheduledDelayMillis: 500
18
+ });
19
+ else spanProcessor = new SimpleSpanProcessor(exporter);
20
+ provider = new WebTracerProvider({
21
+ resource: new Resource({ [ATTR_SERVICE_NAME]: config.serviceName || "telecine-browser" }),
22
+ spanProcessors: [spanProcessor]
23
+ });
24
+ const { ZoneContextManager } = await import("@opentelemetry/context-zone");
25
+ provider.register({ contextManager: new ZoneContextManager() });
26
+ isInitialized = true;
27
+ } catch (error) {
28
+ console.error("Failed to initialize browser tracing:", error);
29
+ throw error;
30
+ }
31
+ }
32
+ export { setupBrowserTracing };
@@ -0,0 +1,34 @@
1
+ import { Context, Span } from '@opentelemetry/api';
2
+ export type TraceContext = Record<string, string>;
3
+ /**
4
+ * Enable tracing globally. Call this during initialization if tracing is requested.
5
+ */
6
+ export declare function enableTracing(): void;
7
+ /**
8
+ * Check if tracing is currently enabled.
9
+ */
10
+ export declare function isTracingEnabled(): boolean;
11
+ /**
12
+ * Set the current frame's span. Call this when starting a frame render.
13
+ * All spans created during this frame will use this as parent if
14
+ * Zone.js doesn't provide one via context.active()
15
+ */
16
+ export declare function setCurrentFrameSpan(span: Span): void;
17
+ /**
18
+ * Clear the current frame span. Call this when a frame completes.
19
+ */
20
+ export declare function clearCurrentFrameSpan(): void;
21
+ export declare function extractParentContext(traceContext?: TraceContext): Context;
22
+ /**
23
+ * Get the active span's context to pass to child operations
24
+ * Use this when calling functions that create child spans
25
+ */
26
+ export declare function getActiveContext(): Context;
27
+ /**
28
+ * Wrapper that passes span context explicitly to the function
29
+ * Use this for operations that need to store or propagate context across boundaries
30
+ */
31
+ export declare function withSpanAndContext<T>(name: string, attributes: Record<string, string | number | boolean> | undefined, parentContext: Context | undefined, fn: (span: Span, activeContext: Context) => Promise<T>): Promise<T>;
32
+ export declare function createSpan(name: string, attributes?: Record<string, string | number | boolean>, parentContext?: Context): Span;
33
+ export declare function withSpan<T>(name: string, attributes: Record<string, string | number | boolean> | undefined, parentContext: Context | undefined, fn: (span: Span) => Promise<T>): Promise<T>;
34
+ export declare function withSpanSync<T>(name: string, attributes: Record<string, string | number | boolean> | undefined, parentContext: Context | undefined, fn: (span: Span) => T): T;
@@ -0,0 +1,112 @@
1
+ import { context, propagation, trace } from "@opentelemetry/api";
2
+ var tracingEnabled = false;
3
+ function enableTracing() {
4
+ tracingEnabled = true;
5
+ }
6
+ function isTracingEnabled() {
7
+ return tracingEnabled;
8
+ }
9
+ var currentFrameSpan;
10
+ function setCurrentFrameSpan(span) {
11
+ currentFrameSpan = span;
12
+ }
13
+ function clearCurrentFrameSpan() {
14
+ currentFrameSpan = void 0;
15
+ }
16
+ function extractParentContext(traceContext) {
17
+ if (!traceContext) return context.active();
18
+ try {
19
+ return propagation.extract(context.active(), traceContext);
20
+ } catch (_error) {
21
+ return context.active();
22
+ }
23
+ }
24
+ async function withSpanAndContext(name, attributes, parentContext, fn) {
25
+ if (!tracingEnabled) {
26
+ const noopSpan = trace.getTracer("telecine-browser").startSpan(name);
27
+ const result = await fn(noopSpan, parentContext || context.active());
28
+ noopSpan.end();
29
+ return result;
30
+ }
31
+ const tracer = trace.getTracer("telecine-browser");
32
+ let ctx;
33
+ if (parentContext) ctx = parentContext;
34
+ else {
35
+ const activeContext = context.active();
36
+ if (trace.getSpan(activeContext)?.isRecording?.()) ctx = activeContext;
37
+ else if (currentFrameSpan) ctx = trace.setSpan(context.active(), currentFrameSpan);
38
+ else ctx = activeContext;
39
+ }
40
+ const span = tracer.startSpan(name, { attributes }, ctx);
41
+ const spanContext = trace.setSpan(ctx, span);
42
+ try {
43
+ const result = await context.with(spanContext, async () => {
44
+ return fn(span, spanContext);
45
+ });
46
+ span.end();
47
+ return result;
48
+ } catch (error) {
49
+ span.recordException(error);
50
+ span.end();
51
+ throw error;
52
+ }
53
+ }
54
+ function createSpan(name, attributes, parentContext) {
55
+ const tracer = trace.getTracer("telecine-browser");
56
+ const ctx = parentContext || context.active();
57
+ return context.with(ctx, () => {
58
+ const span = tracer.startSpan(name);
59
+ if (attributes) span.setAttributes(attributes);
60
+ return span;
61
+ });
62
+ }
63
+ async function withSpan(name, attributes, parentContext, fn) {
64
+ if (!tracingEnabled) {
65
+ const noopSpan = trace.getTracer("telecine-browser").startSpan(name);
66
+ const result = await fn(noopSpan);
67
+ noopSpan.end();
68
+ return result;
69
+ }
70
+ const tracer = trace.getTracer("telecine-browser");
71
+ let ctx;
72
+ if (parentContext) ctx = parentContext;
73
+ else {
74
+ const activeContext = context.active();
75
+ if (trace.getSpan(activeContext)?.isRecording?.()) ctx = activeContext;
76
+ else if (currentFrameSpan) ctx = trace.setSpan(context.active(), currentFrameSpan);
77
+ else ctx = activeContext;
78
+ }
79
+ const span = tracer.startSpan(name, { attributes }, ctx);
80
+ const spanContext = trace.setSpan(ctx, span);
81
+ try {
82
+ const result = await context.with(spanContext, async () => {
83
+ return fn(span);
84
+ });
85
+ span.end();
86
+ return result;
87
+ } catch (error) {
88
+ span.recordException(error);
89
+ span.end();
90
+ throw error;
91
+ }
92
+ }
93
+ function withSpanSync(name, attributes, parentContext, fn) {
94
+ if (!tracingEnabled) {
95
+ const noopSpan = trace.getTracer("telecine-browser").startSpan(name);
96
+ const result = fn(noopSpan);
97
+ noopSpan.end();
98
+ return result;
99
+ }
100
+ const span = createSpan(name, attributes, parentContext);
101
+ const ctx = parentContext || context.active();
102
+ try {
103
+ const result = context.with(trace.setSpan(ctx, span), () => fn(span));
104
+ span.end();
105
+ return result;
106
+ } catch (error) {
107
+ span.recordException(error);
108
+ span.end();
109
+ throw error;
110
+ }
111
+ }
112
+ export { clearCurrentFrameSpan, enableTracing, extractParentContext, isTracingEnabled, setCurrentFrameSpan, withSpan, withSpanAndContext, withSpanSync };
package/dist/style.css CHANGED
@@ -1,2 +1,2 @@
1
- *,:before,:after{box-sizing:border-box;border:0 solid #e5e7eb}:before,:after{--tw-content:""}html,:host{-webkit-text-size-adjust:100%;tab-size:4;font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent;font-family:ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;line-height:1.5}body{line-height:inherit;margin:0}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-feature-settings:normal;font-variation-settings:normal;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-feature-settings:inherit;font-variation-settings:inherit;font-family:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:#0000;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{margin:0;padding:0;list-style:none}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder{opacity:1;color:#9ca3af}textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}[hidden]{display:none}*,:before,:after{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:#3b82f680;--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:#3b82f680;--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }.container{width:100%}@media (min-width:640px){.container{max-width:640px}}@media (min-width:768px){.container{max-width:768px}}@media (min-width:1024px){.container{max-width:1024px}}@media (min-width:1280px){.container{max-width:1280px}}@media (min-width:1536px){.container{max-width:1536px}}.pointer-events-none{pointer-events:none}.visible{visibility:visible}.invisible{visibility:hidden}.static{position:static}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.inset-0{inset:0}.left-0{left:0}.top-0{top:0}.isolate{isolation:isolate}.z-10{z-index:10}.z-20,.z-\[20\]{z-index:20}.z-\[5\]{z-index:5}.col-span-2{grid-column:span 2/span 2}.mx-2{margin-left:.5rem;margin-right:.5rem}.mb-\[1px\]{margin-bottom:1px}.block{display:block}.inline-block{display:inline-block}.inline{display:inline}.flex{display:flex}.grid{display:grid}.contents{display:contents}.hidden{display:none}.size-full{width:100%;height:100%}.h-\[1\.1rem\]{height:1.1rem}.h-\[200px\]{height:200px}.h-\[270px\]{height:270px}.h-\[300px\]{height:300px}.h-\[360px\]{height:360px}.h-\[500px\]{height:500px}.h-\[5px\]{height:5px}.h-full{height:100%}.w-1{width:.25rem}.w-\[1000px\]{width:1000px}.w-\[2px\]{width:2px}.w-\[480px\]{width:480px}.w-\[600px\]{width:600px}.w-\[640px\]{width:640px}.w-full{width:100%}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y))rotate(var(--tw-rotate))skewX(var(--tw-skew-x))skewY(var(--tw-skew-y))scaleX(var(--tw-scale-x))scaleY(var(--tw-scale-y))}.cursor-crosshair{cursor:crosshair}.resize{resize:both}.place-content-center{place-content:center}.items-center{align-items:center}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-visible{overflow:visible}.whitespace-nowrap{white-space:nowrap}.text-nowrap{text-wrap:nowrap}.rounded{border-radius:.25rem}.border{border-width:1px}.border-r-2{border-right-width:2px}.border-blue-200{--tw-border-opacity:1;border-color:rgb(191 219 254/var(--tw-border-opacity))}.border-blue-500{--tw-border-opacity:1;border-color:rgb(59 130 246/var(--tw-border-opacity))}.border-green-200{--tw-border-opacity:1;border-color:rgb(187 247 208/var(--tw-border-opacity))}.border-green-500{--tw-border-opacity:1;border-color:rgb(34 197 94/var(--tw-border-opacity))}.border-purple-200{--tw-border-opacity:1;border-color:rgb(233 213 255/var(--tw-border-opacity))}.border-red-700{--tw-border-opacity:1;border-color:rgb(185 28 28/var(--tw-border-opacity))}.border-slate-500{--tw-border-opacity:1;border-color:rgb(100 116 139/var(--tw-border-opacity))}.border-slate-600{--tw-border-opacity:1;border-color:rgb(71 85 105/var(--tw-border-opacity))}.border-yellow-500{--tw-border-opacity:1;border-color:rgb(234 179 8/var(--tw-border-opacity))}.border-b-slate-600{--tw-border-opacity:1;border-bottom-color:rgb(71 85 105/var(--tw-border-opacity))}.bg-black{--tw-bg-opacity:1;background-color:rgb(0 0 0/var(--tw-bg-opacity))}.bg-blue-200{--tw-bg-opacity:1;background-color:rgb(191 219 254/var(--tw-bg-opacity))}.bg-blue-50{--tw-bg-opacity:1;background-color:rgb(239 246 255/var(--tw-bg-opacity))}.bg-blue-500{--tw-bg-opacity:1;background-color:rgb(59 130 246/var(--tw-bg-opacity))}.bg-green-200{--tw-bg-opacity:1;background-color:rgb(187 247 208/var(--tw-bg-opacity))}.bg-green-50{--tw-bg-opacity:1;background-color:rgb(240 253 244/var(--tw-bg-opacity))}.bg-lime-400{--tw-bg-opacity:1;background-color:rgb(163 230 53/var(--tw-bg-opacity))}.bg-purple-50{--tw-bg-opacity:1;background-color:rgb(250 245 255/var(--tw-bg-opacity))}.bg-red-500{--tw-bg-opacity:1;background-color:rgb(239 68 68/var(--tw-bg-opacity))}.bg-slate-100{--tw-bg-opacity:1;background-color:rgb(241 245 249/var(--tw-bg-opacity))}.bg-slate-200{--tw-bg-opacity:1;background-color:rgb(226 232 240/var(--tw-bg-opacity))}.bg-slate-300{--tw-bg-opacity:1;background-color:rgb(203 213 225/var(--tw-bg-opacity))}.bg-slate-500{--tw-bg-opacity:1;background-color:rgb(100 116 139/var(--tw-bg-opacity))}.bg-slate-800{--tw-bg-opacity:1;background-color:rgb(30 41 59/var(--tw-bg-opacity))}.bg-yellow-200{--tw-bg-opacity:1;background-color:rgb(254 240 138/var(--tw-bg-opacity))}.bg-yellow-400{--tw-bg-opacity:1;background-color:rgb(250 204 21/var(--tw-bg-opacity))}.bg-opacity-20{--tw-bg-opacity:.2}.object-contain{-o-object-fit:contain;object-fit:contain}.p-4{padding:1rem}.p-\[1px\]{padding:1px}.px-0{padding-left:0;padding-right:0}.px-0\.5{padding-left:.125rem;padding-right:.125rem}.pb-0{padding-bottom:0}.pl-1{padding-left:.25rem}.pl-2{padding-left:.5rem}.pr-0{padding-right:0}.pr-1{padding-right:.25rem}.pt-\[8px\]{padding-top:8px}.text-center{text-align:center}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.text-\[8px\]{font-size:8px}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xs{font-size:.75rem;line-height:1rem}.font-bold{font-weight:700}.ordinal{--tw-ordinal:ordinal;font-variant-numeric:var(--tw-ordinal)var(--tw-slashed-zero)var(--tw-numeric-figure)var(--tw-numeric-spacing)var(--tw-numeric-fraction)}.text-black{--tw-text-opacity:1;color:rgb(0 0 0/var(--tw-text-opacity))}.text-green-200{--tw-text-opacity:1;color:rgb(187 247 208/var(--tw-text-opacity))}.text-green-900{--tw-text-opacity:1;color:rgb(20 83 45/var(--tw-text-opacity))}.line-through{text-decoration-line:line-through}.opacity-50{opacity:.5}.shadow{--tw-shadow:0 1px 3px 0 #0000001a,0 1px 2px -1px #0000001a;--tw-shadow-colored:0 1px 3px 0 var(--tw-shadow-color),0 1px 2px -1px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-slate-300{--tw-shadow-color:#cbd5e1;--tw-shadow:var(--tw-shadow-colored)}.shadow-slate-600{--tw-shadow-color:#475569;--tw-shadow:var(--tw-shadow-colored)}.outline{outline-style:solid}.blur{--tw-blur:blur(8px);filter:var(--tw-blur)var(--tw-brightness)var(--tw-contrast)var(--tw-grayscale)var(--tw-hue-rotate)var(--tw-invert)var(--tw-saturate)var(--tw-sepia)var(--tw-drop-shadow)}.filter{filter:var(--tw-blur)var(--tw-brightness)var(--tw-contrast)var(--tw-grayscale)var(--tw-hue-rotate)var(--tw-invert)var(--tw-saturate)var(--tw-sepia)var(--tw-drop-shadow)}.backdrop-filter{-webkit-backdrop-filter:var(--tw-backdrop-blur)var(--tw-backdrop-brightness)var(--tw-backdrop-contrast)var(--tw-backdrop-grayscale)var(--tw-backdrop-hue-rotate)var(--tw-backdrop-invert)var(--tw-backdrop-opacity)var(--tw-backdrop-saturate)var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur)var(--tw-backdrop-brightness)var(--tw-backdrop-contrast)var(--tw-backdrop-grayscale)var(--tw-backdrop-hue-rotate)var(--tw-backdrop-invert)var(--tw-backdrop-opacity)var(--tw-backdrop-saturate)var(--tw-backdrop-sepia)}.transition{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,-webkit-backdrop-filter,backdrop-filter;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1)}.ease-out{transition-timing-function:cubic-bezier(0,0,.2,1)}.hover\:bg-slate-400:hover{--tw-bg-opacity:1;background-color:rgb(148 163 184/var(--tw-bg-opacity))}.peer:hover~.peer-hover\:border-slate-400{--tw-border-opacity:1;border-color:rgb(148 163 184/var(--tw-border-opacity))}.peer:hover~.peer-hover\:bg-slate-300{--tw-bg-opacity:1;background-color:rgb(203 213 225/var(--tw-bg-opacity))}.data-\[focused\]\:bg-slate-400[data-focused]{--tw-bg-opacity:1;background-color:rgb(148 163 184/var(--tw-bg-opacity))}.peer[data-focused]~.peer-data-\[focused\]\:border-slate-400{--tw-border-opacity:1;border-color:rgb(148 163 184/var(--tw-border-opacity))}.peer[data-focused]~.peer-data-\[focused\]\:bg-slate-300{--tw-bg-opacity:1;background-color:rgb(203 213 225/var(--tw-bg-opacity))}
1
+ *,:before,:after{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:#3b82f680;--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:#3b82f680;--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }*,:before,:after{box-sizing:border-box;border:0 solid #e5e7eb}:before,:after{--tw-content:""}html,:host{-webkit-text-size-adjust:100%;tab-size:4;font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent;font-family:ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;line-height:1.5}body{line-height:inherit;margin:0}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-feature-settings:normal;font-variation-settings:normal;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-feature-settings:inherit;font-variation-settings:inherit;font-family:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:#0000;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{margin:0;padding:0;list-style:none}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder{opacity:1;color:#9ca3af}textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}.container{width:100%}@media (min-width:640px){.container{max-width:640px}}@media (min-width:768px){.container{max-width:768px}}@media (min-width:1024px){.container{max-width:1024px}}@media (min-width:1280px){.container{max-width:1280px}}@media (min-width:1536px){.container{max-width:1536px}}.pointer-events-none{pointer-events:none}.visible{visibility:visible}.invisible{visibility:hidden}.static{position:static}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.inset-0{inset:0}.left-0{left:0}.top-0{top:0}.isolate{isolation:isolate}.z-10{z-index:10}.z-20,.z-\[20\]{z-index:20}.z-\[5\]{z-index:5}.col-span-2{grid-column:span 2/span 2}.mx-2{margin-left:.5rem;margin-right:.5rem}.mb-\[1px\]{margin-bottom:1px}.block{display:block}.inline-block{display:inline-block}.inline{display:inline}.flex{display:flex}.grid{display:grid}.contents{display:contents}.hidden{display:none}.size-full{width:100%;height:100%}.h-\[1\.1rem\]{height:1.1rem}.h-\[200px\]{height:200px}.h-\[270px\]{height:270px}.h-\[300px\]{height:300px}.h-\[360px\]{height:360px}.h-\[500px\]{height:500px}.h-\[5px\]{height:5px}.h-full{height:100%}.w-1{width:.25rem}.w-\[1000px\]{width:1000px}.w-\[2px\]{width:2px}.w-\[480px\]{width:480px}.w-\[600px\]{width:600px}.w-\[640px\]{width:640px}.w-full{width:100%}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y))rotate(var(--tw-rotate))skewX(var(--tw-skew-x))skewY(var(--tw-skew-y))scaleX(var(--tw-scale-x))scaleY(var(--tw-scale-y))}.cursor-crosshair{cursor:crosshair}.resize{resize:both}.place-content-center{place-content:center}.items-center{align-items:center}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-visible{overflow:visible}.whitespace-nowrap{white-space:nowrap}.text-nowrap{text-wrap:nowrap}.rounded{border-radius:.25rem}.border{border-width:1px}.border-r-2{border-right-width:2px}.border-blue-200{--tw-border-opacity:1;border-color:rgb(191 219 254/var(--tw-border-opacity,1))}.border-blue-500{--tw-border-opacity:1;border-color:rgb(59 130 246/var(--tw-border-opacity,1))}.border-green-200{--tw-border-opacity:1;border-color:rgb(187 247 208/var(--tw-border-opacity,1))}.border-green-500{--tw-border-opacity:1;border-color:rgb(34 197 94/var(--tw-border-opacity,1))}.border-purple-200{--tw-border-opacity:1;border-color:rgb(233 213 255/var(--tw-border-opacity,1))}.border-red-700{--tw-border-opacity:1;border-color:rgb(185 28 28/var(--tw-border-opacity,1))}.border-slate-500{--tw-border-opacity:1;border-color:rgb(100 116 139/var(--tw-border-opacity,1))}.border-slate-600{--tw-border-opacity:1;border-color:rgb(71 85 105/var(--tw-border-opacity,1))}.border-yellow-500{--tw-border-opacity:1;border-color:rgb(234 179 8/var(--tw-border-opacity,1))}.border-b-slate-600{--tw-border-opacity:1;border-bottom-color:rgb(71 85 105/var(--tw-border-opacity,1))}.bg-black{--tw-bg-opacity:1;background-color:rgb(0 0 0/var(--tw-bg-opacity,1))}.bg-blue-200{--tw-bg-opacity:1;background-color:rgb(191 219 254/var(--tw-bg-opacity,1))}.bg-blue-50{--tw-bg-opacity:1;background-color:rgb(239 246 255/var(--tw-bg-opacity,1))}.bg-blue-500{--tw-bg-opacity:1;background-color:rgb(59 130 246/var(--tw-bg-opacity,1))}.bg-green-200{--tw-bg-opacity:1;background-color:rgb(187 247 208/var(--tw-bg-opacity,1))}.bg-green-50{--tw-bg-opacity:1;background-color:rgb(240 253 244/var(--tw-bg-opacity,1))}.bg-lime-400{--tw-bg-opacity:1;background-color:rgb(163 230 53/var(--tw-bg-opacity,1))}.bg-purple-50{--tw-bg-opacity:1;background-color:rgb(250 245 255/var(--tw-bg-opacity,1))}.bg-red-500{--tw-bg-opacity:1;background-color:rgb(239 68 68/var(--tw-bg-opacity,1))}.bg-slate-100{--tw-bg-opacity:1;background-color:rgb(241 245 249/var(--tw-bg-opacity,1))}.bg-slate-200{--tw-bg-opacity:1;background-color:rgb(226 232 240/var(--tw-bg-opacity,1))}.bg-slate-300{--tw-bg-opacity:1;background-color:rgb(203 213 225/var(--tw-bg-opacity,1))}.bg-slate-500{--tw-bg-opacity:1;background-color:rgb(100 116 139/var(--tw-bg-opacity,1))}.bg-slate-800{--tw-bg-opacity:1;background-color:rgb(30 41 59/var(--tw-bg-opacity,1))}.bg-yellow-200{--tw-bg-opacity:1;background-color:rgb(254 240 138/var(--tw-bg-opacity,1))}.bg-yellow-400{--tw-bg-opacity:1;background-color:rgb(250 204 21/var(--tw-bg-opacity,1))}.bg-opacity-20{--tw-bg-opacity:.2}.object-contain{-o-object-fit:contain;object-fit:contain}.p-4{padding:1rem}.p-\[1px\]{padding:1px}.px-0\.5{padding-left:.125rem;padding-right:.125rem}.pb-0{padding-bottom:0}.pl-1{padding-left:.25rem}.pl-2{padding-left:.5rem}.pr-0{padding-right:0}.pr-1{padding-right:.25rem}.pt-\[8px\]{padding-top:8px}.text-center{text-align:center}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.text-\[8px\]{font-size:8px}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xs{font-size:.75rem;line-height:1rem}.font-bold{font-weight:700}.ordinal{--tw-ordinal:ordinal;font-variant-numeric:var(--tw-ordinal)var(--tw-slashed-zero)var(--tw-numeric-figure)var(--tw-numeric-spacing)var(--tw-numeric-fraction)}.text-black{--tw-text-opacity:1;color:rgb(0 0 0/var(--tw-text-opacity,1))}.text-green-200{--tw-text-opacity:1;color:rgb(187 247 208/var(--tw-text-opacity,1))}.text-green-900{--tw-text-opacity:1;color:rgb(20 83 45/var(--tw-text-opacity,1))}.line-through{text-decoration-line:line-through}.opacity-50{opacity:.5}.shadow{--tw-shadow:0 1px 3px 0 #0000001a,0 1px 2px -1px #0000001a;--tw-shadow-colored:0 1px 3px 0 var(--tw-shadow-color),0 1px 2px -1px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-slate-300{--tw-shadow-color:#cbd5e1;--tw-shadow:var(--tw-shadow-colored)}.shadow-slate-600{--tw-shadow-color:#475569;--tw-shadow:var(--tw-shadow-colored)}.outline{outline-style:solid}.blur{--tw-blur:blur(8px);filter:var(--tw-blur)var(--tw-brightness)var(--tw-contrast)var(--tw-grayscale)var(--tw-hue-rotate)var(--tw-invert)var(--tw-saturate)var(--tw-sepia)var(--tw-drop-shadow)}.filter{filter:var(--tw-blur)var(--tw-brightness)var(--tw-contrast)var(--tw-grayscale)var(--tw-hue-rotate)var(--tw-invert)var(--tw-saturate)var(--tw-sepia)var(--tw-drop-shadow)}.backdrop-filter{-webkit-backdrop-filter:var(--tw-backdrop-blur)var(--tw-backdrop-brightness)var(--tw-backdrop-contrast)var(--tw-backdrop-grayscale)var(--tw-backdrop-hue-rotate)var(--tw-backdrop-invert)var(--tw-backdrop-opacity)var(--tw-backdrop-saturate)var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur)var(--tw-backdrop-brightness)var(--tw-backdrop-contrast)var(--tw-backdrop-grayscale)var(--tw-backdrop-hue-rotate)var(--tw-backdrop-invert)var(--tw-backdrop-opacity)var(--tw-backdrop-saturate)var(--tw-backdrop-sepia)}.transition{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,-webkit-backdrop-filter,backdrop-filter;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1)}.ease-out{transition-timing-function:cubic-bezier(0,0,.2,1)}.hover\:bg-slate-400:hover{--tw-bg-opacity:1;background-color:rgb(148 163 184/var(--tw-bg-opacity,1))}.peer:hover~.peer-hover\:border-slate-400{--tw-border-opacity:1;border-color:rgb(148 163 184/var(--tw-border-opacity,1))}.peer:hover~.peer-hover\:bg-slate-300{--tw-bg-opacity:1;background-color:rgb(203 213 225/var(--tw-bg-opacity,1))}.data-\[focused\]\:bg-slate-400[data-focused]{--tw-bg-opacity:1;background-color:rgb(148 163 184/var(--tw-bg-opacity,1))}.peer[data-focused]~.peer-data-\[focused\]\:border-slate-400{--tw-border-opacity:1;border-color:rgb(148 163 184/var(--tw-border-opacity,1))}.peer[data-focused]~.peer-data-\[focused\]\:bg-slate-300{--tw-bg-opacity:1;background-color:rgb(203 213 225/var(--tw-bg-opacity,1))}
2
2
  /*$vite$:1*/
@@ -1,16 +1,7 @@
1
- /**
2
- * Request deduplication utility
3
- * Manages pending requests to prevent concurrent duplicate requests
4
- */
5
1
  var RequestDeduplicator = class {
6
2
  constructor() {
7
3
  this.pendingRequests = /* @__PURE__ */ new Map();
8
4
  }
9
- /**
10
- * Execute a request with deduplication
11
- * If a request with the same key is already pending, return the existing promise
12
- * Otherwise, execute the request factory and track the promise
13
- */
14
5
  async executeRequest(key, requestFactory) {
15
6
  const existingRequest = this.pendingRequests.get(key);
16
7
  if (existingRequest) return existingRequest;
@@ -25,27 +16,15 @@ var RequestDeduplicator = class {
25
16
  throw error;
26
17
  }
27
18
  }
28
- /**
29
- * Clear all pending requests (used in cache clearing)
30
- */
31
19
  clear() {
32
20
  this.pendingRequests.clear();
33
21
  }
34
- /**
35
- * Get number of pending requests
36
- */
37
22
  getPendingCount() {
38
23
  return this.pendingRequests.size;
39
24
  }
40
- /**
41
- * Check if a request is pending
42
- */
43
25
  isPending(key) {
44
26
  return this.pendingRequests.has(key);
45
27
  }
46
- /**
47
- * Get all pending request keys
48
- */
49
28
  getPendingKeys() {
50
29
  return Array.from(this.pendingRequests.keys());
51
30
  }
@@ -2,10 +2,6 @@ var URLTokenDeduplicator = class {
2
2
  constructor() {
3
3
  this.tokenCache = /* @__PURE__ */ new Map();
4
4
  }
5
- /**
6
- * Get or create a URL token with global deduplication
7
- * Multiple requests for the same cache key will share the same token promise
8
- */
9
5
  async getToken(cacheKey, tokenFactory, parseExpiration) {
10
6
  const now = Date.now();
11
7
  const cached = this.tokenCache.get(cacheKey);
@@ -27,36 +23,20 @@ var URLTokenDeduplicator = class {
27
23
  });
28
24
  return tokenPromise;
29
25
  }
30
- /**
31
- * Clear all cached tokens (used in testing)
32
- */
33
26
  clear() {
34
27
  this.tokenCache.clear();
35
28
  }
36
- /**
37
- * Get number of cached tokens
38
- */
39
29
  getCachedCount() {
40
30
  return this.tokenCache.size;
41
31
  }
42
- /**
43
- * Check if a token is cached and valid
44
- */
45
32
  hasValidToken(cacheKey) {
46
33
  const cached = this.tokenCache.get(cacheKey);
47
34
  if (!cached) return false;
48
- const now = Date.now();
49
- return now < cached.expiration;
35
+ return Date.now() < cached.expiration;
50
36
  }
51
- /**
52
- * Get all cached token keys
53
- */
54
37
  getCachedKeys() {
55
38
  return Array.from(this.tokenCache.keys());
56
39
  }
57
- /**
58
- * Remove expired tokens from cache
59
- */
60
40
  cleanup() {
61
41
  const now = Date.now();
62
42
  for (const [key, entry] of this.tokenCache.entries()) if (now >= entry.expiration) this.tokenCache.delete(key);
@@ -2,9 +2,6 @@ var UrlGenerator = class {
2
2
  constructor(baseUrl) {
3
3
  this.baseUrl = baseUrl;
4
4
  }
5
- /**
6
- * Generate video segment URL
7
- */
8
5
  generateSegmentUrl(segmentId, renditionId, metadata) {
9
6
  const audioRendition = metadata.audioRendition;
10
7
  const videoRendition = metadata.videoRendition;
@@ -13,31 +10,17 @@ var UrlGenerator = class {
13
10
  console.error("Rendition not found", metadata);
14
11
  throw new Error(`Rendition ${renditionId} not found`);
15
12
  }
16
- const template = segmentId === "init" ? metadata.templates.initSegment : metadata.templates.mediaSegment;
17
- return template.replace("{rendition}", renditionId).replace("{segmentId}", segmentId.toString()).replace("{src}", metadata.src).replace("{trackId}", rendition.trackId?.toString() ?? "");
13
+ return (segmentId === "init" ? metadata.templates.initSegment : metadata.templates.mediaSegment).replace("{rendition}", renditionId).replace("{segmentId}", segmentId.toString()).replace("{src}", metadata.src).replace("{trackId}", rendition.trackId?.toString() ?? "");
18
14
  }
19
- /**
20
- * Generate init segment URL
21
- */
22
15
  generateInitSegmentUrl(mediaUrl, rendition) {
23
16
  return `${this.baseUrl()}/api/v1/transcode/${rendition}/init.m4s?url=${encodeURIComponent(mediaUrl)}`;
24
17
  }
25
- /**
26
- * Generate manifest URL
27
- */
28
18
  generateManifestUrl(mediaUrl) {
29
19
  return `${this.baseUrl()}/api/v1/transcode/manifest.json?url=${encodeURIComponent(mediaUrl)}`;
30
20
  }
31
- /**
32
- * Generate track fragment index URL
33
- */
34
21
  generateTrackFragmentIndexUrl(mediaUrl) {
35
- const normalizedSrc = mediaUrl.startsWith("/") ? mediaUrl.slice(1) : mediaUrl;
36
- return `/@ef-track-fragment-index/${normalizedSrc}`;
22
+ return `/@ef-track-fragment-index/${mediaUrl.startsWith("/") ? mediaUrl.slice(1) : mediaUrl}`;
37
23
  }
38
- /**
39
- * Generate quality presets URL
40
- */
41
24
  generatePresetsUrl() {
42
25
  return `${this.baseUrl()}/api/v1/transcode/presets`;
43
26
  }