BOC v1.0: RS256 auth, Ledger integration, Prometheus metrics, CI/CD, backup

This commit is contained in:
Bernt
2026-07-28 23:08:32 +00:00
parent 0b4f160af1
commit af874040ca
11541 changed files with 1654104 additions and 1103 deletions
+308
View File
@@ -0,0 +1,308 @@
import { isMotionValue, defaultOffset, isGenerator, createGeneratorEasing, fillOffset } from 'motion-dom';
import { progress, secondsToMilliseconds, warning, reverseEasing, getEasingForSegment } from 'motion-utils';
import { resolveSubjects } from '../animate/resolve-subjects.mjs';
import { calculateRepeatDuration } from './utils/calc-repeat-duration.mjs';
import { calcNextTime } from './utils/calc-time.mjs';
import { addKeyframes } from './utils/edit.mjs';
import { normalizeTimes } from './utils/normalize-times.mjs';
import { compareByTime } from './utils/sort.mjs';
const defaultSegmentEasing = "easeInOut";
const MAX_REPEAT = 20;
function createAnimationsFromSequence(sequence, { defaultTransition = {}, ...sequenceTransition } = {}, scope, generators) {
const defaultDuration = defaultTransition.duration || 0.3;
const animationDefinitions = new Map();
const sequences = new Map();
const elementCache = {};
const timeLabels = new Map();
let prevTime = 0;
let currentTime = 0;
let totalDuration = 0;
/**
* Build the timeline by mapping over the sequence array and converting
* the definitions into keyframes and offsets with absolute time values.
* These will later get converted into relative offsets in a second pass.
*/
for (let i = 0; i < sequence.length; i++) {
const segment = sequence[i];
/**
* If this is a timeline label, mark it and skip the rest of this iteration.
*/
if (typeof segment === "string") {
timeLabels.set(segment, currentTime);
continue;
}
else if (!Array.isArray(segment)) {
timeLabels.set(segment.name, calcNextTime(currentTime, segment.at, prevTime, timeLabels));
continue;
}
let [subject, keyframes, transition = {}] = segment;
/**
* If a relative or absolute time value has been specified we need to resolve
* it in relation to the currentTime.
*/
if (transition.at !== undefined) {
currentTime = calcNextTime(currentTime, transition.at, prevTime, timeLabels);
}
/**
* Keep track of the maximum duration in this definition. This will be
* applied to currentTime once the definition has been parsed.
*/
let maxDuration = 0;
const resolveValueSequence = (valueKeyframes, valueTransition, valueSequence, elementIndex = 0, numSubjects = 0) => {
const valueKeyframesAsList = keyframesAsList(valueKeyframes);
const { delay = 0, times = defaultOffset(valueKeyframesAsList), type = defaultTransition.type || "keyframes", repeat, repeatType, repeatDelay = 0, ...remainingTransition } = valueTransition;
let { ease = defaultTransition.ease || "easeOut", duration } = valueTransition;
/**
* Resolve stagger() if defined.
*/
const calculatedDelay = typeof delay === "function"
? delay(elementIndex, numSubjects)
: delay;
/**
* If this animation should and can use a spring, generate a spring easing function.
*/
const numKeyframes = valueKeyframesAsList.length;
const createGenerator = isGenerator(type)
? type
: generators?.[type || "keyframes"];
if (numKeyframes <= 2 && createGenerator) {
/**
* As we're creating an easing function from a spring,
* ideally we want to generate it using the real distance
* between the two keyframes. However this isn't always
* possible - in these situations we use 0-100.
*/
let absoluteDelta = 100;
if (numKeyframes === 2 &&
isNumberKeyframesArray(valueKeyframesAsList)) {
const delta = valueKeyframesAsList[1] - valueKeyframesAsList[0];
absoluteDelta = Math.abs(delta);
}
const springTransition = {
...defaultTransition,
...remainingTransition,
};
if (duration !== undefined) {
springTransition.duration = secondsToMilliseconds(duration);
}
const springEasing = createGeneratorEasing(springTransition, absoluteDelta, createGenerator);
ease = springEasing.ease;
duration = springEasing.duration;
}
duration ?? (duration = defaultDuration);
const startTime = currentTime + calculatedDelay;
/**
* If there's only one time offset of 0, fill in a second with length 1
*/
if (times.length === 1 && times[0] === 0) {
times[1] = 1;
}
/**
* Fill out if offset if fewer offsets than keyframes
*/
const remainder = times.length - valueKeyframesAsList.length;
remainder > 0 && fillOffset(times, remainder);
/**
* If only one value has been set, ie [1], push a null to the start of
* the keyframe array. This will let us mark a keyframe at this point
* that will later be hydrated with the previous value.
*/
valueKeyframesAsList.length === 1 &&
valueKeyframesAsList.unshift(null);
/**
* Segments can't express `repeat: Infinity` or very large
* counts — they'd leave dead time after the segment or
* explode the keyframe array. Ignore with a warning.
*/
if (repeat) {
warning(repeat < MAX_REPEAT, `Sequence segments can't repeat ${repeat} times — ignoring repeat option. Use a value below ${MAX_REPEAT} or apply repeat at the sequence level instead.`);
}
if (repeat && repeat < MAX_REPEAT) {
/**
* Express repeatDelay in units of a single iteration's duration
* so it can be added to the per-iteration time offsets below
* before they're normalized to 0-1.
*/
const repeatDelayUnits = duration > 0 ? repeatDelay / duration : 0;
duration = calculateRepeatDuration(duration, repeat, repeatDelay);
const originalKeyframes = [...valueKeyframesAsList];
const originalTimes = [...times];
ease = Array.isArray(ease) ? [...ease] : [ease];
const originalEase = [...ease];
/**
* For reverse/mirror, alternate iterations play the segment
* backwards. mirror matches JSAnimation's mirroredGenerator:
* reversed keyframes, easings unchanged. reverse matches
* JSAnimation's iterationProgress = 1 - p: reversed
* keyframes, easing array reversed AND each function easing
* mapped through reverseEasing (string easings unchanged —
* they're resolved later by the keyframes engine).
*/
const isFlipping = repeatType === "reverse" || repeatType === "mirror";
let flippedKeyframes = originalKeyframes;
let flippedEases = originalEase;
if (isFlipping) {
flippedKeyframes = [...originalKeyframes].reverse();
if (repeatType === "reverse") {
flippedEases = [...originalEase]
.reverse()
.map((e) => typeof e === "function"
? reverseEasing(e)
: e);
}
}
for (let repeatIndex = 0; repeatIndex < repeat; repeatIndex++) {
const isFlipped = isFlipping && repeatIndex % 2 === 0;
const iterKeyframes = isFlipped
? flippedKeyframes
: originalKeyframes;
const iterEase = isFlipped ? flippedEases : originalEase;
const iterStartOffset = (repeatIndex + 1) * (1 + repeatDelayUnits);
/**
* If repeatDelay is set, hold the previous iteration's
* final value through the delay by inserting a keyframe
* at the moment the next iteration begins.
*/
if (repeatDelayUnits > 0) {
valueKeyframesAsList.push(valueKeyframesAsList[valueKeyframesAsList.length - 1]);
times.push(iterStartOffset);
ease.push("linear");
}
valueKeyframesAsList.push(...iterKeyframes);
for (let keyframeIndex = 0; keyframeIndex < iterKeyframes.length; keyframeIndex++) {
times.push(originalTimes[keyframeIndex] + iterStartOffset);
ease.push(keyframeIndex === 0
? "linear"
: getEasingForSegment(iterEase, keyframeIndex - 1));
}
}
normalizeTimes(times, repeat, repeatDelayUnits);
}
const targetTime = startTime + duration;
/**
* Add keyframes, mapping offsets to absolute time.
*/
addKeyframes(valueSequence, valueKeyframesAsList, ease, times, startTime, targetTime);
maxDuration = Math.max(calculatedDelay + duration, maxDuration);
totalDuration = Math.max(targetTime, totalDuration);
};
if (isMotionValue(subject)) {
const subjectSequence = getSubjectSequence(subject, sequences);
resolveValueSequence(keyframes, transition, getValueSequence("default", subjectSequence));
}
else {
const subjects = resolveSubjects(subject, keyframes, scope, elementCache);
const numSubjects = subjects.length;
/**
* For every element in this segment, process the defined values.
*/
for (let subjectIndex = 0; subjectIndex < numSubjects; subjectIndex++) {
/**
* Cast necessary, but we know these are of this type
*/
keyframes = keyframes;
transition = transition;
const thisSubject = subjects[subjectIndex];
const subjectSequence = getSubjectSequence(thisSubject, sequences);
for (const key in keyframes) {
resolveValueSequence(keyframes[key], getValueTransition(transition, key), getValueSequence(key, subjectSequence), subjectIndex, numSubjects);
}
}
}
prevTime = currentTime;
currentTime += maxDuration;
}
/**
* For every element and value combination create a new animation.
*/
sequences.forEach((valueSequences, element) => {
for (const key in valueSequences) {
const valueSequence = valueSequences[key];
/**
* Arrange all the keyframes in ascending time order.
*/
valueSequence.sort(compareByTime);
const keyframes = [];
const valueOffset = [];
const valueEasing = [];
/**
* For each keyframe, translate absolute times into
* relative offsets based on the total duration of the timeline.
*/
for (let i = 0; i < valueSequence.length; i++) {
const { at, value, easing } = valueSequence[i];
keyframes.push(value);
valueOffset.push(progress(0, totalDuration, at));
valueEasing.push(easing || "easeOut");
}
/**
* If the first keyframe doesn't land on offset: 0
* provide one by duplicating the initial keyframe. This ensures
* it snaps to the first keyframe when the animation starts.
*/
if (valueOffset[0] !== 0) {
valueOffset.unshift(0);
keyframes.unshift(keyframes[0]);
valueEasing.unshift(defaultSegmentEasing);
}
/**
* If the last keyframe doesn't land on offset: 1
* provide one with a null wildcard value. This will ensure it
* stays static until the end of the animation.
*/
if (valueOffset[valueOffset.length - 1] !== 1) {
valueOffset.push(1);
keyframes.push(null);
}
if (!animationDefinitions.has(element)) {
animationDefinitions.set(element, {
keyframes: {},
transition: {},
});
}
const definition = animationDefinitions.get(element);
definition.keyframes[key] = keyframes;
/**
* Exclude `type` from defaultTransition since springs have been
* converted to duration-based easing functions in resolveValueSequence.
* Including `type: "spring"` would cause JSAnimation to error when
* the merged keyframes array has more than 2 keyframes.
*/
const { type: _type, ...remainingDefaultTransition } = defaultTransition;
definition.transition[key] = {
...remainingDefaultTransition,
duration: totalDuration,
ease: valueEasing,
times: valueOffset,
...sequenceTransition,
};
}
});
return animationDefinitions;
}
function getSubjectSequence(subject, sequences) {
!sequences.has(subject) && sequences.set(subject, {});
return sequences.get(subject);
}
function getValueSequence(name, sequences) {
if (!sequences[name])
sequences[name] = [];
return sequences[name];
}
function keyframesAsList(keyframes) {
return Array.isArray(keyframes) ? keyframes : [keyframes];
}
function getValueTransition(transition, key) {
return transition && transition[key]
? {
...transition,
...transition[key],
}
: { ...transition };
}
const isNumber = (keyframe) => typeof keyframe === "number";
const isNumberKeyframesArray = (keyframes) => keyframes.every(isNumber);
export { createAnimationsFromSequence, getValueTransition };
//# sourceMappingURL=create.mjs.map
File diff suppressed because one or more lines are too long
@@ -0,0 +1,6 @@
function calculateRepeatDuration(duration, repeat, repeatDelay) {
return duration * (repeat + 1) + repeatDelay * repeat;
}
export { calculateRepeatDuration };
//# sourceMappingURL=calc-repeat-duration.mjs.map
@@ -0,0 +1 @@
{"version":3,"file":"calc-repeat-duration.mjs","sources":["../../../../../src/animation/sequence/utils/calc-repeat-duration.ts"],"sourcesContent":["export function calculateRepeatDuration(\n duration: number,\n repeat: number,\n repeatDelay: number\n): number {\n return duration * (repeat + 1) + repeatDelay * repeat\n}\n"],"names":[],"mappings":"SAAgB,uBAAuB,CACnC,QAAgB,EAChB,MAAc,EACd,WAAmB,EAAA;IAEnB,OAAO,QAAQ,IAAI,MAAM,GAAG,CAAC,CAAC,GAAG,WAAW,GAAG,MAAM;AACzD;;;;"}
@@ -0,0 +1,24 @@
/**
* Given a absolute or relative time definition and current/prev time state of the sequence,
* calculate an absolute time for the next keyframes.
*/
function calcNextTime(current, next, prev, labels) {
if (typeof next === "number") {
return next;
}
else if (next.startsWith("-") || next.startsWith("+")) {
return Math.max(0, current + parseFloat(next));
}
else if (next === "<") {
return prev;
}
else if (next.startsWith("<")) {
return Math.max(0, prev + parseFloat(next.slice(1)));
}
else {
return labels.get(next) ?? current;
}
}
export { calcNextTime };
//# sourceMappingURL=calc-time.mjs.map
@@ -0,0 +1 @@
{"version":3,"file":"calc-time.mjs","sources":["../../../../../src/animation/sequence/utils/calc-time.ts"],"sourcesContent":["import { SequenceTime } from \"../types\"\n\n/**\n * Given a absolute or relative time definition and current/prev time state of the sequence,\n * calculate an absolute time for the next keyframes.\n */\nexport function calcNextTime(\n current: number,\n next: SequenceTime,\n prev: number,\n labels: Map<string, number>\n): number {\n if (typeof next === \"number\") {\n return next\n } else if (next.startsWith(\"-\") || next.startsWith(\"+\")) {\n return Math.max(0, current + parseFloat(next))\n } else if (next === \"<\") {\n return prev\n } else if (next.startsWith(\"<\")) {\n return Math.max(0, prev + parseFloat(next.slice(1)))\n } else {\n return labels.get(next) ?? current\n }\n}\n"],"names":[],"mappings":"AAEA;;;AAGG;AACG,SAAU,YAAY,CACxB,OAAe,EACf,IAAkB,EAClB,IAAY,EACZ,MAA2B,EAAA;AAE3B,IAAA,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE;AAC1B,QAAA,OAAO,IAAI;IACf;AAAO,SAAA,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE;AACrD,QAAA,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC;IAClD;AAAO,SAAA,IAAI,IAAI,KAAK,GAAG,EAAE;AACrB,QAAA,OAAO,IAAI;IACf;AAAO,SAAA,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE;AAC7B,QAAA,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,GAAG,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IACxD;SAAO;QACH,OAAO,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,OAAO;IACtC;AACJ;;;;"}
@@ -0,0 +1,31 @@
import { mixNumber } from 'motion-dom';
import { getEasingForSegment, removeItem } from 'motion-utils';
function eraseKeyframes(sequence, startTime, endTime) {
for (let i = 0; i < sequence.length; i++) {
const keyframe = sequence[i];
if (keyframe.at > startTime && keyframe.at < endTime) {
removeItem(sequence, keyframe);
// If we remove this item we have to push the pointer back one
i--;
}
}
}
function addKeyframes(sequence, keyframes, easing, offset, startTime, endTime) {
/**
* Erase every existing value between currentTime and targetTime,
* this will essentially splice this timeline into any currently
* defined ones.
*/
eraseKeyframes(sequence, startTime, endTime);
for (let i = 0; i < keyframes.length; i++) {
sequence.push({
value: keyframes[i],
at: mixNumber(startTime, endTime, offset[i]),
easing: getEasingForSegment(easing, i),
});
}
}
export { addKeyframes, eraseKeyframes };
//# sourceMappingURL=edit.mjs.map
@@ -0,0 +1 @@
{"version":3,"file":"edit.mjs","sources":["../../../../../src/animation/sequence/utils/edit.ts"],"sourcesContent":["import { mixNumber, UnresolvedValueKeyframe } from \"motion-dom\"\nimport { Easing, getEasingForSegment, removeItem } from \"motion-utils\"\nimport type { ValueSequence } from \"../types\"\n\nexport function eraseKeyframes(\n sequence: ValueSequence,\n startTime: number,\n endTime: number\n): void {\n for (let i = 0; i < sequence.length; i++) {\n const keyframe = sequence[i]\n\n if (keyframe.at > startTime && keyframe.at < endTime) {\n removeItem(sequence, keyframe)\n\n // If we remove this item we have to push the pointer back one\n i--\n }\n }\n}\n\nexport function addKeyframes(\n sequence: ValueSequence,\n keyframes: UnresolvedValueKeyframe[],\n easing: Easing | Easing[],\n offset: number[],\n startTime: number,\n endTime: number\n): void {\n /**\n * Erase every existing value between currentTime and targetTime,\n * this will essentially splice this timeline into any currently\n * defined ones.\n */\n eraseKeyframes(sequence, startTime, endTime)\n\n for (let i = 0; i < keyframes.length; i++) {\n sequence.push({\n value: keyframes[i],\n at: mixNumber(startTime, endTime, offset[i]),\n easing: getEasingForSegment(easing, i),\n })\n }\n}\n"],"names":[],"mappings":";;;SAIgB,cAAc,CAC1B,QAAuB,EACvB,SAAiB,EACjB,OAAe,EAAA;AAEf,IAAA,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,QAAQ,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;AACtC,QAAA,MAAM,QAAQ,GAAG,QAAQ,CAAC,CAAC,CAAC;AAE5B,QAAA,IAAI,QAAQ,CAAC,EAAE,GAAG,SAAS,IAAI,QAAQ,CAAC,EAAE,GAAG,OAAO,EAAE;AAClD,YAAA,UAAU,CAAC,QAAQ,EAAE,QAAQ,CAAC;;AAG9B,YAAA,CAAC,EAAE;QACP;IACJ;AACJ;AAEM,SAAU,YAAY,CACxB,QAAuB,EACvB,SAAoC,EACpC,MAAyB,EACzB,MAAgB,EAChB,SAAiB,EACjB,OAAe,EAAA;AAEf;;;;AAIG;AACH,IAAA,cAAc,CAAC,QAAQ,EAAE,SAAS,EAAE,OAAO,CAAC;AAE5C,IAAA,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,SAAS,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;QACvC,QAAQ,CAAC,IAAI,CAAC;AACV,YAAA,KAAK,EAAE,SAAS,CAAC,CAAC,CAAC;YACnB,EAAE,EAAE,SAAS,CAAC,SAAS,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC;AAC5C,YAAA,MAAM,EAAE,mBAAmB,CAAC,MAAM,EAAE,CAAC,CAAC;AACzC,SAAA,CAAC;IACN;AACJ;;;;"}
@@ -0,0 +1,18 @@
/**
* Take an array of times that represent repeated keyframes. For instance
* if we have original times of [0, 0.5, 1] then our repeated times will
* be [0, 0.5, 1, 1, 1.5, 2]. Loop over the times and scale them back
* down to a 0-1 scale.
*
* `repeatDelayUnits` is the repeatDelay expressed in units of a single
* iteration's duration, so the total span equals `(repeat + 1) + repeat * repeatDelayUnits`.
*/
function normalizeTimes(times, repeat, repeatDelayUnits = 0) {
const totalUnits = repeat + 1 + repeat * repeatDelayUnits;
for (let i = 0; i < times.length; i++) {
times[i] = times[i] / totalUnits;
}
}
export { normalizeTimes };
//# sourceMappingURL=normalize-times.mjs.map
@@ -0,0 +1 @@
{"version":3,"file":"normalize-times.mjs","sources":["../../../../../src/animation/sequence/utils/normalize-times.ts"],"sourcesContent":["/**\n * Take an array of times that represent repeated keyframes. For instance\n * if we have original times of [0, 0.5, 1] then our repeated times will\n * be [0, 0.5, 1, 1, 1.5, 2]. Loop over the times and scale them back\n * down to a 0-1 scale.\n *\n * `repeatDelayUnits` is the repeatDelay expressed in units of a single\n * iteration's duration, so the total span equals `(repeat + 1) + repeat * repeatDelayUnits`.\n */\nexport function normalizeTimes(\n times: number[],\n repeat: number,\n repeatDelayUnits = 0\n): void {\n const totalUnits = repeat + 1 + repeat * repeatDelayUnits\n for (let i = 0; i < times.length; i++) {\n times[i] = times[i] / totalUnits\n }\n}\n"],"names":[],"mappings":"AAAA;;;;;;;;AAQG;AACG,SAAU,cAAc,CAC1B,KAAe,EACf,MAAc,EACd,gBAAgB,GAAG,CAAC,EAAA;IAEpB,MAAM,UAAU,GAAG,MAAM,GAAG,CAAC,GAAG,MAAM,GAAG,gBAAgB;AACzD,IAAA,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;QACnC,KAAK,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,GAAG,UAAU;IACpC;AACJ;;;;"}
@@ -0,0 +1,15 @@
function compareByTime(a, b) {
if (a.at === b.at) {
if (a.value === null)
return 1;
if (b.value === null)
return -1;
return 0;
}
else {
return a.at - b.at;
}
}
export { compareByTime };
//# sourceMappingURL=sort.mjs.map
@@ -0,0 +1 @@
{"version":3,"file":"sort.mjs","sources":["../../../../../src/animation/sequence/utils/sort.ts"],"sourcesContent":["import { AbsoluteKeyframe } from \"../types\"\n\nexport function compareByTime(\n a: AbsoluteKeyframe,\n b: AbsoluteKeyframe\n): number {\n if (a.at === b.at) {\n if (a.value === null) return 1\n if (b.value === null) return -1\n return 0\n } else {\n return a.at - b.at\n }\n}\n"],"names":[],"mappings":"AAEM,SAAU,aAAa,CACzB,CAAmB,EACnB,CAAmB,EAAA;IAEnB,IAAI,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,EAAE,EAAE;AACf,QAAA,IAAI,CAAC,CAAC,KAAK,KAAK,IAAI;AAAE,YAAA,OAAO,CAAC;AAC9B,QAAA,IAAI,CAAC,CAAC,KAAK,KAAK,IAAI;YAAE,OAAO,EAAE;AAC/B,QAAA,OAAO,CAAC;IACZ;SAAO;AACH,QAAA,OAAO,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,EAAE;IACtB;AACJ;;;;"}