@hyperframes/core 0.8.214 → 0.8.217
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/audioRhythm.d.ts +6 -0
- package/dist/audioRhythm.js +24 -0
- package/dist/colorGroups.d.ts +4 -2
- package/dist/colorGroups.js +11 -3
- package/dist/envProxy.d.ts +4 -0
- package/dist/envProxy.js +9 -0
- package/dist/generated/runtime-inline.js +1 -1
- package/dist/hyperframe.runtime.iife.js +71 -71
- package/dist/runtime/compositionLength.d.ts +25 -5
- package/dist/runtime/compositionLength.js +36 -20
- package/dist/runtime/mediaClock.d.ts +1 -0
- package/dist/runtime/mediaClock.js +4 -0
- package/dist/telemetryRedaction.js +12 -11
- package/package.json +8 -4
|
@@ -11,11 +11,16 @@ export declare function readCompositionSize(root: Element): {
|
|
|
11
11
|
width: number | null;
|
|
12
12
|
height: number | null;
|
|
13
13
|
};
|
|
14
|
-
|
|
15
|
-
export declare function resolveMediaWindowDurationSeconds(doc: ParentNode, media: {
|
|
14
|
+
type MediaTiming = {
|
|
16
15
|
mediaStart: (el: HTMLMediaElement) => number;
|
|
17
16
|
mediaDuration: (el: HTMLMediaElement) => number | null;
|
|
18
|
-
}
|
|
17
|
+
};
|
|
18
|
+
/** The timed `<video>`/`<audio>` whose window ends last, and where it ends. */
|
|
19
|
+
export declare function longestMediaWindow(doc: ParentNode, media: MediaTiming): {
|
|
20
|
+
seconds: number;
|
|
21
|
+
element: HTMLMediaElement;
|
|
22
|
+
} | null;
|
|
23
|
+
export declare function resolveMediaWindowDurationSeconds(doc: ParentNode, media: MediaTiming): number | null;
|
|
19
24
|
/** The latest of the root's declared length and its direct sub-compositions' ends. */
|
|
20
25
|
export declare function resolveAuthoredCompositionFloorSeconds(root: Element, startResolver: StartResolver): number | null;
|
|
21
26
|
/** The last-resort length: the latest end among the root's timed clips. Until every clip's length
|
|
@@ -31,9 +36,24 @@ export type CompositionLengthInputs = {
|
|
|
31
36
|
fallback: number;
|
|
32
37
|
derived: () => number;
|
|
33
38
|
};
|
|
34
|
-
|
|
35
|
-
|
|
39
|
+
export type CompositionLengthFrom = "declared" | "timeline" | "floor" | "fallback" | "derived";
|
|
40
|
+
export type CompositionLengthSource = {
|
|
41
|
+
from: CompositionLengthFrom;
|
|
42
|
+
} | {
|
|
43
|
+
from: "media";
|
|
44
|
+
tag: string;
|
|
45
|
+
src: string;
|
|
46
|
+
trimmed: boolean;
|
|
47
|
+
};
|
|
36
48
|
export declare function resolveCompositionLengthSeconds(input: CompositionLengthInputs): number;
|
|
49
|
+
/** A declared root length is the length; else the longest of timeline, floors and fallback (a
|
|
50
|
+
* loop-inflated timeline yields to a floor or fallback); else the length derived from the clips.
|
|
51
|
+
* `from` names the input that set it, and `floorIndex` which floor when a floor did. */
|
|
52
|
+
export declare function resolveCompositionLength(input: CompositionLengthInputs): {
|
|
53
|
+
seconds: number;
|
|
54
|
+
from: CompositionLengthFrom;
|
|
55
|
+
floorIndex: number | null;
|
|
56
|
+
};
|
|
37
57
|
/** Size, frame rate and length of a composition nothing is running (a server-parsed or detached
|
|
38
58
|
* document): no timelines, no adapters, and media without a known source length is pending. */
|
|
39
59
|
export declare function readStaticCompositionMeta(doc: Document): {
|
|
@@ -16,9 +16,9 @@ export function readCompositionSize(root) {
|
|
|
16
16
|
height: parseCompositionDimension(root.getAttribute("data-height")),
|
|
17
17
|
};
|
|
18
18
|
}
|
|
19
|
-
/** The
|
|
20
|
-
export function
|
|
21
|
-
|
|
19
|
+
/** The timed `<video>`/`<audio>` whose window ends last, and where it ends. */
|
|
20
|
+
export function longestMediaWindow(doc, media) {
|
|
21
|
+
let longest = null;
|
|
22
22
|
for (const node of doc.querySelectorAll("video[data-start], audio[data-start]")) {
|
|
23
23
|
const start = media.mediaStart(node);
|
|
24
24
|
if (!Number.isFinite(start))
|
|
@@ -26,10 +26,14 @@ export function resolveMediaWindowDurationSeconds(doc, media) {
|
|
|
26
26
|
const duration = media.mediaDuration(node);
|
|
27
27
|
if (duration == null || duration <= MIN_VALID_TIMELINE_DURATION_SECONDS)
|
|
28
28
|
continue;
|
|
29
|
-
|
|
29
|
+
const end = Math.max(0, start) + duration;
|
|
30
|
+
if (!longest || end > longest.seconds)
|
|
31
|
+
longest = { seconds: end, element: node };
|
|
30
32
|
}
|
|
31
|
-
return aboveOneFrame(
|
|
32
|
-
|
|
33
|
+
return longest && aboveOneFrame(longest.seconds) !== null ? longest : null;
|
|
34
|
+
}
|
|
35
|
+
export function resolveMediaWindowDurationSeconds(doc, media) {
|
|
36
|
+
return longestMediaWindow(doc, media)?.seconds ?? null;
|
|
33
37
|
}
|
|
34
38
|
/** The latest of the root's declared length and its direct sub-compositions' ends. */
|
|
35
39
|
export function resolveAuthoredCompositionFloorSeconds(root, startResolver) {
|
|
@@ -75,14 +79,19 @@ export function resolveContentDerivedDuration(root, startResolver, { unregistere
|
|
|
75
79
|
? { ...result, seconds: null, source: "unresolved", reason: "a clip's length is pending" }
|
|
76
80
|
: result;
|
|
77
81
|
}
|
|
78
|
-
/** A declared root length is the length; else the longest of timeline, floors and fallback (a
|
|
79
|
-
* loop-inflated timeline yields to a floor or fallback); else the length derived from the clips. */
|
|
80
82
|
export function resolveCompositionLengthSeconds(input) {
|
|
83
|
+
return resolveCompositionLength(input).seconds;
|
|
84
|
+
}
|
|
85
|
+
/** A declared root length is the length; else the longest of timeline, floors and fallback (a
|
|
86
|
+
* loop-inflated timeline yields to a floor or fallback); else the length derived from the clips.
|
|
87
|
+
* `from` names the input that set it, and `floorIndex` which floor when a floor did. */
|
|
88
|
+
export function resolveCompositionLength(input) {
|
|
81
89
|
if (input.declared !== null && Number.isFinite(input.declared) && input.declared > 0) {
|
|
82
|
-
return input.declared;
|
|
90
|
+
return { seconds: input.declared, from: "declared", floorIndex: null };
|
|
83
91
|
}
|
|
84
92
|
const rawTimeline = aboveOneFrame(input.timeline());
|
|
85
|
-
const
|
|
93
|
+
const floors = input.floors().map((seconds) => seconds ?? 0);
|
|
94
|
+
const floor = Math.max(0, ...floors);
|
|
86
95
|
const fallback = Number.isFinite(input.fallback) && input.fallback > MIN_VALID_TIMELINE_DURATION_SECONDS
|
|
87
96
|
? input.fallback
|
|
88
97
|
: 0;
|
|
@@ -91,16 +100,23 @@ export function resolveCompositionLengthSeconds(input) {
|
|
|
91
100
|
rawTimeline >= LOOP_INFLATED_TIMELINE_SECONDS &&
|
|
92
101
|
aboveOneFrame(floorOrFallback) !== null;
|
|
93
102
|
const timeline = loopInflated ? null : rawTimeline;
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
seconds
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
103
|
+
const floorWins = floor >= fallback;
|
|
104
|
+
const fromFloorOrFallback = (seconds) => ({
|
|
105
|
+
seconds,
|
|
106
|
+
from: floorWins ? "floor" : "fallback",
|
|
107
|
+
floorIndex: floorWins ? floors.indexOf(floor) : null,
|
|
108
|
+
});
|
|
109
|
+
if (timeline !== null) {
|
|
110
|
+
return timeline >= floorOrFallback
|
|
111
|
+
? { seconds: timeline, from: "timeline", floorIndex: null }
|
|
112
|
+
: fromFloorOrFallback(floorOrFallback);
|
|
113
|
+
}
|
|
114
|
+
if (aboveOneFrame(floor) !== null)
|
|
115
|
+
return fromFloorOrFallback(floorOrFallback);
|
|
116
|
+
if (fallback > 0)
|
|
117
|
+
return { seconds: fallback, from: "fallback", floorIndex: null };
|
|
118
|
+
const derived = input.derived();
|
|
119
|
+
return { seconds: derived > 0 ? derived : 0, from: "derived", floorIndex: null };
|
|
104
120
|
}
|
|
105
121
|
/** Size, frame rate and length of a composition nothing is running (a server-parsed or detached
|
|
106
122
|
* document): no timelines, no adapters, and media without a known source length is pending. */
|
|
@@ -2,3 +2,7 @@ export const MEDIA_ELEMENT_LEAD_SECONDS = 1e-5;
|
|
|
2
2
|
export function mediaElementTime(sourceSeconds) {
|
|
3
3
|
return sourceSeconds + MEDIA_ELEMENT_LEAD_SECONDS;
|
|
4
4
|
}
|
|
5
|
+
export function startsSoundOnlyInsideATap() {
|
|
6
|
+
const { userAgent, maxTouchPoints } = navigator;
|
|
7
|
+
return /iP(hone|ad|od)/.test(userAgent) || (/Macintosh/.test(userAgent) && maxTouchPoints > 1);
|
|
8
|
+
}
|
|
@@ -10,12 +10,6 @@ export function redactUrlQueryStrings(value) {
|
|
|
10
10
|
.replace(/\b(https?:\/\/[^\s?]+)\?[^\s]*/g, "$1?…")
|
|
11
11
|
.replace(/((?:X-(?:Amz|Goog)-[\w-]*|Signature)=)[^&\s]*/gi, "$1…");
|
|
12
12
|
}
|
|
13
|
-
/**
|
|
14
|
-
* Path characters we treat as part of a single segment. Space is deliberately
|
|
15
|
-
* excluded: including it would let a match run past the path and swallow the
|
|
16
|
-
* prose after it, and a path with a space still gets its remaining segments
|
|
17
|
-
* redacted, which is the part that carries the identifying information.
|
|
18
|
-
*/
|
|
19
13
|
/**
|
|
20
14
|
* A path segment is defined by what ENDS it, not by an alphabet.
|
|
21
15
|
*
|
|
@@ -31,6 +25,9 @@ export function redactUrlQueryStrings(value) {
|
|
|
31
25
|
* run past the path and swallow the prose after it.
|
|
32
26
|
*/
|
|
33
27
|
const SEGMENT = String.raw `[^\s/\\'"]+`;
|
|
28
|
+
/** A folder that another separator follows may hold spaces (`My Drive`, `내 드라이브`, `OneDrive - Contoso`); only
|
|
29
|
+
* the last segment stops at a space, so prose after a path survives. Line breaks still end it. */
|
|
30
|
+
const FOLDER = String.raw `${SEGMENT}(?:[^\S\r\n]+(?![A-Za-z]:[\\/])${SEGMENT})*(?=[\\/])`;
|
|
34
31
|
/** Same, minus the dot, so a trailing `.ext` can be matched separately. */
|
|
35
32
|
const SEGMENT_NODOT = String.raw `[^\s/\\'".]+`;
|
|
36
33
|
/**
|
|
@@ -54,10 +51,12 @@ const TOKEN_TAIL = String.raw `[^\s'")]*`;
|
|
|
54
51
|
* position inside a URL can start a match. URLs are handled above, where the
|
|
55
52
|
* host is kept and only the query is dropped.
|
|
56
53
|
*/
|
|
57
|
-
const ABSOLUTE_PATH = new RegExp(
|
|
54
|
+
const ABSOLUTE_PATH = new RegExp(
|
|
55
|
+
// A Windows share (`\\server\share`) starts with two separators; the first is taken as its root.
|
|
56
|
+
String.raw `(?<![:\w\p{L}\p{N}\p{M}/\\])(?:[A-Za-z]:|\\(?=\\[^\s\\/]))?(?:[\\/]${FOLDER})+[\\/]${SEGMENT}${TOKEN_TAIL}`, "gu");
|
|
58
57
|
/** `./assets/bgm.mp3`, `../out.wav`, `.\tmp\x` — relative paths leak the same
|
|
59
58
|
* project structure absolute ones do, and were previously untouched. */
|
|
60
|
-
const RELATIVE_PATH = new RegExp(String.raw `(?<![\w/\\.])\.{1,2}(?:[\\/]${
|
|
59
|
+
const RELATIVE_PATH = new RegExp(String.raw `(?<![\w/\\.])\.{1,2}(?:[\\/]${FOLDER})*[\\/]${SEGMENT}${TOKEN_TAIL}`, "g");
|
|
61
60
|
/**
|
|
62
61
|
* A bare basename with an asset extension. ffprobe reports the input by the
|
|
63
62
|
* name it was given, so a caller that passes a basename (or a path this
|
|
@@ -82,6 +81,8 @@ const BARE_RELATIVE_PATH = new RegExp([
|
|
|
82
81
|
// Two or more separators: `customer/acme/video.mp4`. No extension needed —
|
|
83
82
|
// that much structure is already a path.
|
|
84
83
|
String.raw `(?<![^\s'\"(=,\[])(?:${SEGMENT}[\\/]){2,}${SEGMENT}${TOKEN_TAIL}`,
|
|
84
|
+
// Spaced folders only before a file name with an extension, so ffprobe's `N/A, … bitrate: N/A` is not a path.
|
|
85
|
+
String.raw `(?<![^\s'\"(=,\[])${SEGMENT}[\\/](?:${FOLDER}[\\/]){1,8}${SEGMENT_NODOT}\.\w{1,8}\b${TOKEN_TAIL}`,
|
|
85
86
|
// One separator, but the last segment carries a file extension:
|
|
86
87
|
// `assets/bgm.mp3`. That segment is dot-free on purpose — SEGMENT includes
|
|
87
88
|
// `.`, so a greedy one swallows the extension this rule needs.
|
|
@@ -122,10 +123,10 @@ function redactFilePaths(value) {
|
|
|
122
123
|
// (`/assets/x.mp3`), so the absolute rule would consume it and leave the
|
|
123
124
|
// leading `.` stranded outside the redaction.
|
|
124
125
|
.replace(RELATIVE_PATH, "[path]")
|
|
125
|
-
//
|
|
126
|
-
//
|
|
127
|
-
.replace(BARE_RELATIVE_PATH, "[path]")
|
|
126
|
+
// Absolute BEFORE bare-relative: the folders after a space in `/Users/kim/My Drive/x.mp4` look like a bare path
|
|
127
|
+
// on their own, and claiming them first would strand the start of the absolute one.
|
|
128
128
|
.replace(ABSOLUTE_PATH, "[path]")
|
|
129
|
+
.replace(BARE_RELATIVE_PATH, "[path]")
|
|
129
130
|
.replace(ASSET_BASENAME, "[file]"));
|
|
130
131
|
}
|
|
131
132
|
export function redactTelemetryString(value, maxLength = MAX_TELEMETRY_STRING_LENGTH) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hyperframes/core",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.217",
|
|
4
4
|
"description": "",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -64,6 +64,10 @@
|
|
|
64
64
|
"import": "./dist/removeTree.js",
|
|
65
65
|
"types": "./dist/removeTree.d.ts"
|
|
66
66
|
},
|
|
67
|
+
"./env-proxy": {
|
|
68
|
+
"import": "./dist/envProxy.js",
|
|
69
|
+
"types": "./dist/envProxy.d.ts"
|
|
70
|
+
},
|
|
67
71
|
"./rename-retry": {
|
|
68
72
|
"import": "./dist/renameWithRetry.js",
|
|
69
73
|
"types": "./dist/renameWithRetry.d.ts"
|
|
@@ -425,9 +429,9 @@
|
|
|
425
429
|
"linkedom": "^0.18.12",
|
|
426
430
|
"postcss": "^8.5.8",
|
|
427
431
|
"wawoff2": "^2.0.1",
|
|
428
|
-
"@hyperframes/
|
|
429
|
-
"@hyperframes/
|
|
430
|
-
"@hyperframes/studio-server": "0.8.
|
|
432
|
+
"@hyperframes/parsers": "0.8.217",
|
|
433
|
+
"@hyperframes/lint": "0.8.217",
|
|
434
|
+
"@hyperframes/studio-server": "0.8.217"
|
|
431
435
|
},
|
|
432
436
|
"devDependencies": {
|
|
433
437
|
"@fontsource/archivo-black": "^5.2.8",
|