mirror of
https://github.com/zoriya/v10.git
synced 2026-08-16 02:45:09 +00:00
feat(packages): add mux media with src parsing, structured source, and storyboards (#1850)
Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1 @@
|
||||
export * from './parse-jwt';
|
||||
@@ -0,0 +1,18 @@
|
||||
/** Decode the payload of a JWT without verifying its signature, `undefined` for malformed tokens. */
|
||||
export function parseJwt<Payload = Record<string, unknown>>(token: string | undefined): Partial<Payload> | undefined {
|
||||
const base64Url = (token ?? '').split('.')[1];
|
||||
if (!base64Url) return undefined;
|
||||
|
||||
try {
|
||||
const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
|
||||
const json = decodeURIComponent(
|
||||
atob(base64)
|
||||
.split('')
|
||||
.map((char) => `%${`00${char.charCodeAt(0).toString(16)}`.slice(-2)}`)
|
||||
.join('')
|
||||
);
|
||||
return JSON.parse(json);
|
||||
} catch {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { parseJwt } from '../parse-jwt';
|
||||
|
||||
// Header `{"alg":"HS256"}`, body is the payload (UTF-8), empty signature.
|
||||
function fakeJwt(payload: Record<string, unknown>): string {
|
||||
const encode = (obj: unknown) =>
|
||||
btoa(String.fromCharCode(...new TextEncoder().encode(JSON.stringify(obj))))
|
||||
.replace(/\+/g, '-')
|
||||
.replace(/\//g, '_');
|
||||
return `${encode({ alg: 'HS256' })}.${encode(payload)}.`;
|
||||
}
|
||||
|
||||
describe('parseJwt', () => {
|
||||
it('decodes a token payload', () => {
|
||||
expect(parseJwt(fakeJwt({ aud: 'v', sub: 'abc' }))).toMatchObject({ aud: 'v', sub: 'abc' });
|
||||
});
|
||||
|
||||
it('decodes unicode payload values', () => {
|
||||
expect(parseJwt(fakeJwt({ sub: 'vidéo' }))).toMatchObject({ sub: 'vidéo' });
|
||||
});
|
||||
|
||||
it('returns undefined for invalid tokens', () => {
|
||||
expect(parseJwt(undefined)).toBeUndefined();
|
||||
expect(parseJwt('')).toBeUndefined();
|
||||
expect(parseJwt('not-a-jwt')).toBeUndefined();
|
||||
expect(parseJwt('a.%%%.c')).toBeUndefined();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,29 @@
|
||||
import { isPlainObject, isUndefined } from '../predicate';
|
||||
|
||||
/**
|
||||
* Deep structural equality for plain objects, arrays, and primitives. Keys
|
||||
* explicitly set to `undefined` are treated as absent (matching JSON
|
||||
* semantics), leaf values are compared with `Object.is`, and non-plain
|
||||
* objects (class instances, Maps, Sets, etc.) are only equal by reference.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* deepEqual({ a: [1, { b: 2 }] }, { a: [1, { b: 2 }] }); // true
|
||||
* deepEqual({ a: 1, b: undefined }, { a: 1 }); // true
|
||||
* ```
|
||||
*/
|
||||
export function deepEqual(a: unknown, b: unknown): boolean {
|
||||
if (Object.is(a, b)) return true;
|
||||
|
||||
if (Array.isArray(a) || Array.isArray(b)) {
|
||||
return (
|
||||
Array.isArray(a) && Array.isArray(b) && a.length === b.length && a.every((value, i) => deepEqual(value, b[i]))
|
||||
);
|
||||
}
|
||||
|
||||
if (!isPlainObject(a) || !isPlainObject(b)) return false;
|
||||
|
||||
const keysA = Object.keys(a).filter((key) => !isUndefined(a[key]));
|
||||
const keysB = Object.keys(b).filter((key) => !isUndefined(b[key]));
|
||||
return keysA.length === keysB.length && keysA.every((key) => deepEqual(a[key], b[key]));
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
export { deepEqual } from './deep-equal';
|
||||
export { defaults } from './defaults';
|
||||
export { omit } from './omit';
|
||||
export { pick } from './pick';
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { deepEqual } from '../deep-equal';
|
||||
|
||||
describe('deepEqual', () => {
|
||||
it('returns true for identical primitives', () => {
|
||||
expect(deepEqual(1, 1)).toBe(true);
|
||||
expect(deepEqual('a', 'a')).toBe(true);
|
||||
expect(deepEqual(true, true)).toBe(true);
|
||||
expect(deepEqual(null, null)).toBe(true);
|
||||
expect(deepEqual(undefined, undefined)).toBe(true);
|
||||
});
|
||||
|
||||
it('returns false for different primitives', () => {
|
||||
expect(deepEqual(1, 2)).toBe(false);
|
||||
expect(deepEqual('a', 'b')).toBe(false);
|
||||
expect(deepEqual(null, undefined)).toBe(false);
|
||||
expect(deepEqual(0, '0')).toBe(false);
|
||||
});
|
||||
|
||||
it('returns true for same reference', () => {
|
||||
const obj = { a: { b: 1 } };
|
||||
expect(deepEqual(obj, obj)).toBe(true);
|
||||
});
|
||||
|
||||
it('compares nested objects structurally', () => {
|
||||
expect(deepEqual({ a: { b: 1 } }, { a: { b: 1 } })).toBe(true);
|
||||
expect(deepEqual({ a: { b: 1 } }, { a: { b: 2 } })).toBe(false);
|
||||
expect(deepEqual({ a: { b: 1 } }, { a: { c: 1 } })).toBe(false);
|
||||
});
|
||||
|
||||
it('compares arrays structurally', () => {
|
||||
expect(deepEqual([1, 2, 3], [1, 2, 3])).toBe(true);
|
||||
expect(deepEqual([1, 2, 3], [1, 2])).toBe(false);
|
||||
expect(deepEqual([1, 2, 3], [3, 2, 1])).toBe(false);
|
||||
expect(deepEqual([{ a: 1 }], [{ a: 1 }])).toBe(true);
|
||||
expect(deepEqual([], {})).toBe(false);
|
||||
});
|
||||
|
||||
it('returns false for objects with different keys', () => {
|
||||
expect(deepEqual({ a: 1 }, { a: 1, b: 2 })).toBe(false);
|
||||
});
|
||||
|
||||
it('treats keys set to undefined as absent', () => {
|
||||
expect(deepEqual({ a: 1, b: undefined }, { a: 1 })).toBe(true);
|
||||
expect(deepEqual({ a: undefined }, {})).toBe(true);
|
||||
expect(deepEqual({ a: null }, {})).toBe(false);
|
||||
});
|
||||
|
||||
it('handles NaN and signed zero like Object.is', () => {
|
||||
expect(deepEqual({ a: NaN }, { a: NaN })).toBe(true);
|
||||
expect(deepEqual({ a: 0 }, { a: -0 })).toBe(false);
|
||||
});
|
||||
|
||||
it('compares non-plain objects by reference only', () => {
|
||||
const date = new Date(0);
|
||||
expect(deepEqual(date, date)).toBe(true);
|
||||
expect(deepEqual(new Date(0), new Date(0))).toBe(false);
|
||||
expect(deepEqual(new Map(), new Map())).toBe(false);
|
||||
});
|
||||
|
||||
it('returns false when comparing object to primitive or null', () => {
|
||||
expect(deepEqual({ a: 1 }, null)).toBe(false);
|
||||
expect(deepEqual({ a: 1 }, 1)).toBe(false);
|
||||
});
|
||||
});
|
||||
@@ -9,3 +9,7 @@ export function camelCase(str: string): string {
|
||||
export function kebabCase(str: string): string {
|
||||
return str.replace(/[A-Z]/g, (m) => `-${m.toLowerCase()}`);
|
||||
}
|
||||
|
||||
export function snakeCase(str: string): string {
|
||||
return str.replace(/[A-Z]/g, (m) => `_${m.toLowerCase()}`);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { camelCase, kebabCase, pascalCase } from '../casing';
|
||||
import { camelCase, kebabCase, pascalCase, snakeCase } from '../casing';
|
||||
|
||||
describe('casing', () => {
|
||||
describe('pascalCase', () => {
|
||||
@@ -42,6 +42,16 @@ describe('casing', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('snakeCase', () => {
|
||||
it('converts camelCase', () => {
|
||||
expect(snakeCase('assetStartTime')).toBe('asset_start_time');
|
||||
});
|
||||
|
||||
it('preserves lowercase', () => {
|
||||
expect(snakeCase('token')).toBe('token');
|
||||
});
|
||||
});
|
||||
|
||||
describe('kebabCase', () => {
|
||||
it('converts camelCase', () => {
|
||||
expect(kebabCase('positionAnchor')).toBe('position-anchor');
|
||||
|
||||
Reference in New Issue
Block a user