@editframe/elements 0.20.4-beta.0 → 0.21.0-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 (92) hide show
  1. package/dist/DelayedLoadingState.js +0 -27
  2. package/dist/EF_FRAMEGEN.d.ts +5 -3
  3. package/dist/EF_FRAMEGEN.js +50 -11
  4. package/dist/_virtual/_@oxc-project_runtime@0.93.0/helpers/decorate.js +7 -0
  5. package/dist/elements/ContextProxiesController.js +2 -22
  6. package/dist/elements/EFAudio.js +4 -8
  7. package/dist/elements/EFCaptions.js +59 -84
  8. package/dist/elements/EFImage.js +5 -6
  9. package/dist/elements/EFMedia/AssetIdMediaEngine.js +2 -4
  10. package/dist/elements/EFMedia/AssetMediaEngine.js +35 -30
  11. package/dist/elements/EFMedia/BaseMediaEngine.js +57 -73
  12. package/dist/elements/EFMedia/BufferedSeekingInput.js +134 -76
  13. package/dist/elements/EFMedia/JitMediaEngine.js +9 -19
  14. package/dist/elements/EFMedia/audioTasks/makeAudioBufferTask.js +3 -6
  15. package/dist/elements/EFMedia/audioTasks/makeAudioFrequencyAnalysisTask.js +1 -1
  16. package/dist/elements/EFMedia/audioTasks/makeAudioInitSegmentFetchTask.js +1 -1
  17. package/dist/elements/EFMedia/audioTasks/makeAudioInputTask.js +6 -5
  18. package/dist/elements/EFMedia/audioTasks/makeAudioSeekTask.js +1 -3
  19. package/dist/elements/EFMedia/audioTasks/makeAudioSegmentFetchTask.js +1 -1
  20. package/dist/elements/EFMedia/audioTasks/makeAudioSegmentIdTask.js +1 -1
  21. package/dist/elements/EFMedia/audioTasks/makeAudioTimeDomainAnalysisTask.js +1 -1
  22. package/dist/elements/EFMedia/shared/AudioSpanUtils.js +4 -16
  23. package/dist/elements/EFMedia/shared/BufferUtils.js +2 -15
  24. package/dist/elements/EFMedia/shared/GlobalInputCache.js +0 -24
  25. package/dist/elements/EFMedia/shared/PrecisionUtils.js +0 -21
  26. package/dist/elements/EFMedia/shared/ThumbnailExtractor.js +0 -17
  27. package/dist/elements/EFMedia/tasks/makeMediaEngineTask.js +1 -10
  28. package/dist/elements/EFMedia/videoTasks/MainVideoInputCache.d.ts +29 -0
  29. package/dist/elements/EFMedia/videoTasks/MainVideoInputCache.js +32 -0
  30. package/dist/elements/EFMedia/videoTasks/ScrubInputCache.js +1 -15
  31. package/dist/elements/EFMedia/videoTasks/makeScrubVideoBufferTask.js +1 -7
  32. package/dist/elements/EFMedia/videoTasks/makeScrubVideoInputTask.js +8 -5
  33. package/dist/elements/EFMedia/videoTasks/makeScrubVideoSeekTask.js +12 -13
  34. package/dist/elements/EFMedia/videoTasks/makeScrubVideoSegmentIdTask.js +1 -1
  35. package/dist/elements/EFMedia/videoTasks/makeUnifiedVideoSeekTask.js +134 -70
  36. package/dist/elements/EFMedia/videoTasks/makeVideoBufferTask.js +7 -11
  37. package/dist/elements/EFMedia.js +26 -24
  38. package/dist/elements/EFSourceMixin.js +5 -7
  39. package/dist/elements/EFSurface.js +6 -9
  40. package/dist/elements/EFTemporal.js +19 -37
  41. package/dist/elements/EFThumbnailStrip.js +16 -59
  42. package/dist/elements/EFTimegroup.js +95 -90
  43. package/dist/elements/EFVideo.d.ts +6 -2
  44. package/dist/elements/EFVideo.js +142 -107
  45. package/dist/elements/EFWaveform.js +18 -27
  46. package/dist/elements/SampleBuffer.js +2 -5
  47. package/dist/elements/TargetController.js +3 -3
  48. package/dist/elements/durationConverter.js +4 -4
  49. package/dist/elements/updateAnimations.js +14 -35
  50. package/dist/gui/ContextMixin.js +23 -52
  51. package/dist/gui/EFConfiguration.js +7 -7
  52. package/dist/gui/EFControls.js +5 -5
  53. package/dist/gui/EFFilmstrip.js +77 -98
  54. package/dist/gui/EFFitScale.js +5 -6
  55. package/dist/gui/EFFocusOverlay.js +4 -4
  56. package/dist/gui/EFPreview.js +4 -4
  57. package/dist/gui/EFScrubber.js +9 -9
  58. package/dist/gui/EFTimeDisplay.js +5 -5
  59. package/dist/gui/EFToggleLoop.js +4 -4
  60. package/dist/gui/EFTogglePlay.js +5 -5
  61. package/dist/gui/EFWorkbench.js +5 -5
  62. package/dist/gui/TWMixin2.js +1 -1
  63. package/dist/index.d.ts +1 -0
  64. package/dist/otel/BridgeSpanExporter.d.ts +13 -0
  65. package/dist/otel/BridgeSpanExporter.js +87 -0
  66. package/dist/otel/setupBrowserTracing.d.ts +12 -0
  67. package/dist/otel/setupBrowserTracing.js +30 -0
  68. package/dist/otel/tracingHelpers.d.ts +34 -0
  69. package/dist/otel/tracingHelpers.js +113 -0
  70. package/dist/transcoding/cache/RequestDeduplicator.js +0 -21
  71. package/dist/transcoding/cache/URLTokenDeduplicator.js +1 -21
  72. package/dist/transcoding/utils/UrlGenerator.js +2 -19
  73. package/dist/utils/LRUCache.js +6 -53
  74. package/package.json +10 -2
  75. package/src/elements/EFCaptions.browsertest.ts +2 -0
  76. package/src/elements/EFMedia/AssetMediaEngine.ts +65 -37
  77. package/src/elements/EFMedia/BaseMediaEngine.ts +110 -52
  78. package/src/elements/EFMedia/BufferedSeekingInput.ts +218 -101
  79. package/src/elements/EFMedia/audioTasks/makeAudioInputTask.ts +7 -3
  80. package/src/elements/EFMedia/videoTasks/MainVideoInputCache.ts +76 -0
  81. package/src/elements/EFMedia/videoTasks/makeScrubVideoInputTask.ts +16 -10
  82. package/src/elements/EFMedia/videoTasks/makeScrubVideoSeekTask.ts +7 -1
  83. package/src/elements/EFMedia/videoTasks/makeUnifiedVideoSeekTask.ts +222 -116
  84. package/src/elements/EFMedia.ts +16 -1
  85. package/src/elements/EFTimegroup.browsertest.ts +10 -8
  86. package/src/elements/EFTimegroup.ts +164 -76
  87. package/src/elements/EFVideo.browsertest.ts +19 -27
  88. package/src/elements/EFVideo.ts +203 -101
  89. package/src/otel/BridgeSpanExporter.ts +150 -0
  90. package/src/otel/setupBrowserTracing.ts +68 -0
  91. package/src/otel/tracingHelpers.ts +251 -0
  92. package/types.json +1 -1
@@ -1,10 +1,10 @@
1
+ import { __decorate } from "../_virtual/_@oxc-project_runtime@0.93.0/helpers/decorate.js";
1
2
  import { ContextMixin } from "./ContextMixin.js";
2
3
  import { TWMixin } from "./TWMixin2.js";
3
4
  import { LitElement, css, html } from "lit";
4
5
  import { customElement, eventOptions, property } from "lit/decorators.js";
5
- import _decorate from "@oxc-project/runtime/helpers/decorate";
6
6
  import { createRef, ref } from "lit/directives/ref.js";
7
- let EFWorkbench = class EFWorkbench$1 extends ContextMixin(TWMixin(LitElement)) {
7
+ var EFWorkbench = class EFWorkbench$1 extends ContextMixin(TWMixin(LitElement)) {
8
8
  constructor(..._args) {
9
9
  super(..._args);
10
10
  this.rendering = false;
@@ -82,10 +82,10 @@ let EFWorkbench = class EFWorkbench$1 extends ContextMixin(TWMixin(LitElement))
82
82
  `;
83
83
  }
84
84
  };
85
- _decorate([property({ type: Boolean })], EFWorkbench.prototype, "rendering", void 0);
86
- _decorate([eventOptions({
85
+ __decorate([property({ type: Boolean })], EFWorkbench.prototype, "rendering", void 0);
86
+ __decorate([eventOptions({
87
87
  passive: false,
88
88
  capture: true
89
89
  })], EFWorkbench.prototype, "handleStageWheel", null);
90
- EFWorkbench = _decorate([customElement("ef-workbench")], EFWorkbench);
90
+ EFWorkbench = __decorate([customElement("ef-workbench")], EFWorkbench);
91
91
  export { EFWorkbench };
@@ -1,5 +1,5 @@
1
1
  import TWMixin_default from "./TWMixin.js";
2
- let twSheet = null;
2
+ var twSheet = null;
3
3
  if (typeof window !== "undefined" && typeof CSSStyleSheet !== "undefined") try {
4
4
  twSheet = new CSSStyleSheet();
5
5
  if (typeof twSheet.replaceSync === "function") twSheet.replaceSync(TWMixin_default);
package/dist/index.d.ts CHANGED
@@ -19,3 +19,4 @@ export { EFFitScale } from './gui/EFFitScale.js';
19
19
  export { EFSurface } from './elements/EFSurface.ts';
20
20
  export { EFThumbnailStrip } from './elements/EFThumbnailStrip.ts';
21
21
  export { getRenderInfo, RenderInfo } from './getRenderInfo.js';
22
+ export type { TraceContext } from './otel/tracingHelpers.js';
@@ -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): void;
11
+ export declare function isBrowserTracingInitialized(): boolean;
12
+ export {};
@@ -0,0 +1,30 @@
1
+ import { BridgeSpanExporter } from "./BridgeSpanExporter.js";
2
+ import { ZoneContextManager } from "@opentelemetry/context-zone";
3
+ import { Resource } from "@opentelemetry/resources";
4
+ import { BatchSpanProcessor, SimpleSpanProcessor } from "@opentelemetry/sdk-trace-base";
5
+ import { WebTracerProvider } from "@opentelemetry/sdk-trace-web";
6
+ import { ATTR_SERVICE_NAME } from "@opentelemetry/semantic-conventions";
7
+ var isInitialized = false;
8
+ var provider = null;
9
+ function setupBrowserTracing(config) {
10
+ if (isInitialized) return;
11
+ try {
12
+ if (!config.bridge) throw new Error("Bridge is required for browser tracing");
13
+ const exporter = new BridgeSpanExporter(config.bridge, config.otelEndpoint);
14
+ const spanProcessor = config.useBatching ? new BatchSpanProcessor(exporter, {
15
+ maxQueueSize: 100,
16
+ maxExportBatchSize: 10,
17
+ scheduledDelayMillis: 500
18
+ }) : new SimpleSpanProcessor(exporter);
19
+ provider = new WebTracerProvider({
20
+ resource: new Resource({ [ATTR_SERVICE_NAME]: config.serviceName || "telecine-browser" }),
21
+ spanProcessors: [spanProcessor]
22
+ });
23
+ provider.register({ contextManager: new ZoneContextManager() });
24
+ isInitialized = true;
25
+ } catch (error) {
26
+ console.error("Failed to initialize browser tracing:", error);
27
+ throw error;
28
+ }
29
+ }
30
+ 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,113 @@
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 ctx$1 = parentContext || context.active();
28
+ const result = await fn(noopSpan, ctx$1);
29
+ noopSpan.end();
30
+ return result;
31
+ }
32
+ const tracer = trace.getTracer("telecine-browser");
33
+ let ctx;
34
+ if (parentContext) ctx = parentContext;
35
+ else {
36
+ const activeContext = context.active();
37
+ if (trace.getSpan(activeContext)?.isRecording?.()) ctx = activeContext;
38
+ else if (currentFrameSpan) ctx = trace.setSpan(context.active(), currentFrameSpan);
39
+ else ctx = activeContext;
40
+ }
41
+ const span = tracer.startSpan(name, { attributes }, ctx);
42
+ const spanContext = trace.setSpan(ctx, span);
43
+ try {
44
+ const result = await context.with(spanContext, async () => {
45
+ return fn(span, spanContext);
46
+ });
47
+ span.end();
48
+ return result;
49
+ } catch (error) {
50
+ span.recordException(error);
51
+ span.end();
52
+ throw error;
53
+ }
54
+ }
55
+ function createSpan(name, attributes, parentContext) {
56
+ const tracer = trace.getTracer("telecine-browser");
57
+ const ctx = parentContext || context.active();
58
+ return context.with(ctx, () => {
59
+ const span = tracer.startSpan(name);
60
+ if (attributes) span.setAttributes(attributes);
61
+ return span;
62
+ });
63
+ }
64
+ async function withSpan(name, attributes, parentContext, fn) {
65
+ if (!tracingEnabled) {
66
+ const noopSpan = trace.getTracer("telecine-browser").startSpan(name);
67
+ const result = await fn(noopSpan);
68
+ noopSpan.end();
69
+ return result;
70
+ }
71
+ const tracer = trace.getTracer("telecine-browser");
72
+ let ctx;
73
+ if (parentContext) ctx = parentContext;
74
+ else {
75
+ const activeContext = context.active();
76
+ if (trace.getSpan(activeContext)?.isRecording?.()) ctx = activeContext;
77
+ else if (currentFrameSpan) ctx = trace.setSpan(context.active(), currentFrameSpan);
78
+ else ctx = activeContext;
79
+ }
80
+ const span = tracer.startSpan(name, { attributes }, ctx);
81
+ const spanContext = trace.setSpan(ctx, span);
82
+ try {
83
+ const result = await context.with(spanContext, async () => {
84
+ return fn(span);
85
+ });
86
+ span.end();
87
+ return result;
88
+ } catch (error) {
89
+ span.recordException(error);
90
+ span.end();
91
+ throw error;
92
+ }
93
+ }
94
+ function withSpanSync(name, attributes, parentContext, fn) {
95
+ if (!tracingEnabled) {
96
+ const noopSpan = trace.getTracer("telecine-browser").startSpan(name);
97
+ const result = fn(noopSpan);
98
+ noopSpan.end();
99
+ return result;
100
+ }
101
+ const span = createSpan(name, attributes, parentContext);
102
+ const ctx = parentContext || context.active();
103
+ try {
104
+ const result = context.with(trace.setSpan(ctx, span), () => fn(span));
105
+ span.end();
106
+ return result;
107
+ } catch (error) {
108
+ span.recordException(error);
109
+ span.end();
110
+ throw error;
111
+ }
112
+ }
113
+ export { clearCurrentFrameSpan, enableTracing, extractParentContext, isTracingEnabled, setCurrentFrameSpan, withSpan, withSpanAndContext, withSpanSync };
@@ -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
  }
@@ -1,6 +1,3 @@
1
- /**
2
- * A simple LRU (Least Recently Used) cache implementation
3
- */
4
1
  var LRUCache = class {
5
2
  constructor(maxSize) {
6
3
  this.cache = /* @__PURE__ */ new Map();
@@ -35,10 +32,6 @@ var LRUCache = class {
35
32
  return this.cache.size;
36
33
  }
37
34
  };
38
- /**
39
- * Size-aware LRU cache that tracks memory usage in bytes
40
- * Evicts entries when total size exceeds the maximum
41
- */
42
35
  var SizeAwareLRUCache = class {
43
36
  constructor(maxSizeBytes) {
44
37
  this.cache = /* @__PURE__ */ new Map();
@@ -112,17 +105,11 @@ var SizeAwareLRUCache = class {
112
105
  return this.maxSizeBytes;
113
106
  }
114
107
  };
115
- /**
116
- * Red-Black Tree node colors
117
- */
118
108
  var Color = /* @__PURE__ */ function(Color$1) {
119
109
  Color$1["RED"] = "RED";
120
110
  Color$1["BLACK"] = "BLACK";
121
111
  return Color$1;
122
112
  }(Color || {});
123
- /**
124
- * Red-Black Tree node for ordered key storage
125
- */
126
113
  var RBTreeNode = class {
127
114
  constructor(key, color = Color.RED, left = null, right = null, parent = null) {
128
115
  this.key = key;
@@ -132,10 +119,6 @@ var RBTreeNode = class {
132
119
  this.parent = parent;
133
120
  }
134
121
  };
135
- /**
136
- * Red-Black Tree implementation for O(log n) operations
137
- * Supports insert, delete, search, range queries, and nearest neighbor
138
- */
139
122
  var RedBlackTree = class {
140
123
  constructor(compareFn) {
141
124
  this.root = null;
@@ -198,16 +181,12 @@ var RedBlackTree = class {
198
181
  let current = this.root;
199
182
  while (current) {
200
183
  parent = current;
201
- const cmp = this.compareFn(node.key, current.key);
202
- current = cmp < 0 ? current.left : current.right;
184
+ current = this.compareFn(node.key, current.key) < 0 ? current.left : current.right;
203
185
  }
204
186
  node.parent = parent;
205
187
  if (!parent) this.root = node;
206
- else {
207
- const cmp = this.compareFn(node.key, parent.key);
208
- if (cmp < 0) parent.left = node;
209
- else parent.right = node;
210
- }
188
+ else if (this.compareFn(node.key, parent.key) < 0) parent.left = node;
189
+ else parent.right = node;
211
190
  }
212
191
  fixInsert(node) {
213
192
  while (node.parent && node.parent.color === Color.RED) if (node.parent === node.parent.parent?.left) {
@@ -392,10 +371,6 @@ var RedBlackTree = class {
392
371
  if (endCmp < 0) this.inorderRange(node.right, start, end, result);
393
372
  }
394
373
  };
395
- /**
396
- * LRU cache with binary search capabilities using Red-Black tree
397
- * All operations are O(log n) for ordered queries and O(1) for LRU operations
398
- */
399
374
  var OrderedLRUCache = class {
400
375
  constructor(maxSize, compareFn) {
401
376
  this.cache = /* @__PURE__ */ new Map();
@@ -403,9 +378,6 @@ var OrderedLRUCache = class {
403
378
  this.compareFn = compareFn || ((a, b) => a < b ? -1 : a > b ? 1 : 0);
404
379
  this.tree = new RedBlackTree(this.compareFn);
405
380
  }
406
- /**
407
- * Get value by exact key (O(1))
408
- */
409
381
  get(key) {
410
382
  const value = this.cache.get(key);
411
383
  if (value) {
@@ -414,12 +386,8 @@ var OrderedLRUCache = class {
414
386
  }
415
387
  return value;
416
388
  }
417
- /**
418
- * Set key-value pair (O(log n) for tree operations, O(1) for cache)
419
- */
420
389
  set(key, value) {
421
- const isUpdate = this.cache.has(key);
422
- if (isUpdate) this.cache.delete(key);
390
+ if (this.cache.has(key)) this.cache.delete(key);
423
391
  else {
424
392
  if (this.cache.size >= this.maxSize) {
425
393
  const firstKey = this.cache.keys().next().value;
@@ -432,18 +400,9 @@ var OrderedLRUCache = class {
432
400
  }
433
401
  this.cache.set(key, value);
434
402
  }
435
- /**
436
- * Find exact key using tree search (O(log n))
437
- */
438
403
  findExact(key) {
439
- const foundKey = this.tree.find(key);
440
- if (foundKey !== null) return this.get(key);
441
- return void 0;
442
- }
443
- /**
444
- * Find keys within distance of center point (O(log n + k) where k is result count)
445
- * Returns empty array if no keys found in range
446
- */
404
+ if (this.tree.find(key) !== null) return this.get(key);
405
+ }
447
406
  findNearestInRange(center, distance) {
448
407
  const nearestKeys = this.tree.findNearestInRange(center, distance);
449
408
  const result = [];
@@ -456,9 +415,6 @@ var OrderedLRUCache = class {
456
415
  }
457
416
  return result;
458
417
  }
459
- /**
460
- * Find all key-value pairs in range [start, end] (O(log n + k) where k is result count)
461
- */
462
418
  findRange(start, end) {
463
419
  const keys = this.tree.findRange(start, end);
464
420
  const result = [];
@@ -471,9 +427,6 @@ var OrderedLRUCache = class {
471
427
  }
472
428
  return result;
473
429
  }
474
- /**
475
- * Get all keys in sorted order (O(n))
476
- */
477
430
  getSortedKeys() {
478
431
  return this.tree.getAllSorted();
479
432
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@editframe/elements",
3
- "version": "0.20.4-beta.0",
3
+ "version": "0.21.0-beta.0",
4
4
  "description": "",
5
5
  "exports": {
6
6
  ".": {
@@ -27,9 +27,17 @@
27
27
  "license": "UNLICENSED",
28
28
  "dependencies": {
29
29
  "@bramus/style-observer": "^1.3.0",
30
- "@editframe/assets": "0.20.4-beta.0",
30
+ "@editframe/assets": "0.21.0-beta.0",
31
31
  "@lit/context": "^1.1.2",
32
32
  "@lit/task": "^1.0.1",
33
+ "@opentelemetry/api": "^1.9.0",
34
+ "@opentelemetry/context-zone": "^1.26.0",
35
+ "@opentelemetry/core": "^1.26.0",
36
+ "@opentelemetry/exporter-trace-otlp-http": "^0.53.0",
37
+ "@opentelemetry/resources": "^1.26.0",
38
+ "@opentelemetry/sdk-trace-base": "^1.26.0",
39
+ "@opentelemetry/sdk-trace-web": "^1.26.0",
40
+ "@opentelemetry/semantic-conventions": "^1.37.0",
33
41
  "d3": "^7.9.0",
34
42
  "debug": "^4.3.5",
35
43
  "lit": "^3.1.4",
@@ -853,6 +853,7 @@ describe("EFCaptions", () => {
853
853
  // Test at exact boundary 2.6s - should show " test" (the starting word)
854
854
  timegroup.currentTimeMs = 2600;
855
855
  await timegroup.seekTask.taskComplete;
856
+ await captions.updateComplete;
856
857
  await wordContainer.updateComplete;
857
858
 
858
859
  console.log(`At 2600ms: wordText="${wordContainer.wordText}"`);
@@ -861,6 +862,7 @@ describe("EFCaptions", () => {
861
862
  // Test just before boundary 2.599s - should show " timing"
862
863
  timegroup.currentTimeMs = 2599;
863
864
  await timegroup.seekTask.taskComplete;
865
+ await captions.updateComplete;
864
866
  await wordContainer.updateComplete;
865
867
 
866
868
  console.log(`At 2599ms: wordText="${wordContainer.wordText}"`);