Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@atproto/common-web

Package Overview
Dependencies
Maintainers
3
Versions
9
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@atproto/common-web - npm Package Compare versions

Comparing version 0.1.0 to 0.2.0

tests/check.test.ts

1

dist/ipld.d.ts

@@ -10,1 +10,2 @@ import { CID } from 'multiformats/cid';

export declare const ipldToJson: (val: IpldValue) => JsonValue;
export declare const ipldEquals: (a: IpldValue, b: IpldValue) => boolean;
export declare const utf8Len: (str: string) => number;
export declare const graphemeLen: (str: string) => number;
export declare const utf8ToB64Url: (utf8: string) => string;
export declare const b64UrlToUtf8: (b64: string) => string;
export declare const parseLanguage: (langTag: string) => LanguageTag | null;
export declare const validateLanguage: (langTag: string) => boolean;
export declare type LanguageTag = {
grandfathered?: string;
language?: string;
extlang?: string;
script?: string;
region?: string;
variant?: string;
extension?: string;
privateUse?: string;
};

2

dist/tid.d.ts

@@ -8,4 +8,4 @@ export declare class TID {

static fromStr(str: string): TID;
static oldestFirst(a: TID, b: TID): number;
static newestFirst(a: TID, b: TID): number;
static oldestFirst(a: TID, b: TID): number;
static is(str: string): boolean;

@@ -12,0 +12,0 @@ timestamp(): number;

@@ -20,1 +20,2 @@ import { CID } from 'multiformats/cid';

export declare type ArrayEl<A> = A extends readonly (infer T)[] ? T : never;
export declare type NotEmptyArray<T> = [T, ...T[]];

@@ -1,3 +0,3 @@

/// <reference types="node" />
export declare const noUndefinedVals: <T>(obj: Record<string, T>) => Record<string, T>;
export declare const jitter: (maxMs: number) => number;
export declare const wait: (ms: number) => Promise<unknown>;

@@ -9,3 +9,3 @@ export declare const bailableWait: (ms: number) => {

export declare const flattenUint8Arrays: (arrs: Uint8Array[]) => Uint8Array;
export declare const streamToArray: (stream: AsyncIterable<Uint8Array>) => Promise<Uint8Array>;
export declare const streamToBuffer: (stream: AsyncIterable<Uint8Array>) => Promise<Uint8Array>;
export declare const s32encode: (i: number) => string;

@@ -18,1 +18,3 @@ export declare const s32decode: (s: string) => number;

export declare const range: (num: number) => number[];
export declare const dedupeStrs: (strs: string[]) => string[];
export declare const parseIntWithFallback: <T>(value: string | undefined, fallback: T) => number | T;
{
"name": "@atproto/common-web",
"version": "0.1.0",
"version": "0.2.0",
"main": "dist/index.js",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/bluesky-social/atproto.git",
"directory": "packages/common-web"
},
"scripts": {
"test": "jest",
"prettier": "prettier --check src/",
"prettier:fix": "prettier --write src/",
"prettier": "prettier --check src/ tests/",
"prettier:fix": "prettier --write src/ tests/",
"lint": "eslint . --ext .ts,.tsx",

@@ -22,6 +27,7 @@ "lint:fix": "yarn lint --fix",

"dependencies": {
"graphemer": "^1.4.0",
"multiformats": "^9.6.4",
"uint8arrays": "3.0.0",
"zod": "^3.14.2"
"zod": "^3.21.4"
}
}

@@ -78,1 +78,30 @@ import { CID } from 'multiformats/cid'

}
export const ipldEquals = (a: IpldValue, b: IpldValue): boolean => {
// walk arrays
if (Array.isArray(a) && Array.isArray(b)) {
if (a.length !== b.length) return false
for (let i = 0; i < a.length; i++) {
if (!ipldEquals(a[i], b[i])) return false
}
return true
}
// objects
if (a && b && typeof a === 'object' && typeof b === 'object') {
// check bytes
if (a instanceof Uint8Array && b instanceof Uint8Array) {
return ui8.equals(a, b)
}
// check cids
if (CID.asCID(a) && CID.asCID(b)) {
return CID.asCID(a)?.equals(CID.asCID(b))
}
// walk plain objects
if (Object.keys(a).length !== Object.keys(b).length) return false
for (const key of Object.keys(a)) {
if (!ipldEquals(a[key], b[key])) return false
}
return true
}
return a === b
}

@@ -0,1 +1,4 @@

import Graphemer from 'graphemer'
import * as ui8 from 'uint8arrays'
// counts the number of bytes in a utf8 string

@@ -8,3 +11,47 @@ export const utf8Len = (str: string): number => {

export const graphemeLen = (str: string): number => {
return [...new Intl.Segmenter().segment(str)].length
const splitter = new Graphemer()
return splitter.countGraphemes(str)
}
export const utf8ToB64Url = (utf8: string): string => {
return ui8.toString(ui8.fromString(utf8, 'utf8'), 'base64url')
}
export const b64UrlToUtf8 = (b64: string): string => {
return ui8.toString(ui8.fromString(b64, 'base64url'), 'utf8')
}
export const parseLanguage = (langTag: string): LanguageTag | null => {
const parsed = langTag.match(bcp47Regexp)
if (!parsed?.groups) return null
const parts = parsed.groups
return {
grandfathered: parts.grandfathered,
language: parts.language,
extlang: parts.extlang,
script: parts.script,
region: parts.region,
variant: parts.variant,
extension: parts.extension,
privateUse: parts.privateUseA || parts.privateUseB,
}
}
export const validateLanguage = (langTag: string): boolean => {
return bcp47Regexp.test(langTag)
}
export type LanguageTag = {
grandfathered?: string
language?: string
extlang?: string
script?: string
region?: string
variant?: string
extension?: string
privateUse?: string
}
// Validates well-formed BCP 47 syntax: https://www.rfc-editor.org/rfc/rfc5646.html#section-2.1
const bcp47Regexp =
/^((?<grandfathered>(en-GB-oed|i-ami|i-bnn|i-default|i-enochian|i-hak|i-klingon|i-lux|i-mingo|i-navajo|i-pwn|i-tao|i-tay|i-tsu|sgn-BE-FR|sgn-BE-NL|sgn-CH-DE)|(art-lojban|cel-gaulish|no-bok|no-nyn|zh-guoyu|zh-hakka|zh-min|zh-min-nan|zh-xiang))|((?<language>([A-Za-z]{2,3}(-(?<extlang>[A-Za-z]{3}(-[A-Za-z]{3}){0,2}))?)|[A-Za-z]{4}|[A-Za-z]{5,8})(-(?<script>[A-Za-z]{4}))?(-(?<region>[A-Za-z]{2}|[0-9]{3}))?(-(?<variant>[A-Za-z0-9]{5,8}|[0-9][A-Za-z0-9]{3}))*(-(?<extension>[0-9A-WY-Za-wy-z](-[A-Za-z0-9]{2,8})+))*(-(?<privateUseA>x(-[A-Za-z0-9]{1,8})+))?)|(?<privateUseB>x(-[A-Za-z0-9]{1,8})+))$/
import { s32encode, s32decode } from './util'
const TID_LEN = 13
let lastTimestamp = 0

@@ -6,2 +9,6 @@ let timestampCount = 0

function dedash(str: string): string {
return str.replaceAll('-', '')
}
export class TID {

@@ -11,4 +18,4 @@ str: string

constructor(str: string) {
const noDashes = str.replace(/-/g, '')
if (noDashes.length !== 13) {
const noDashes = dedash(str)
if (noDashes.length !== TID_LEN) {
throw new Error(`Poorly formatted TID: ${noDashes.length} length`)

@@ -51,6 +58,2 @@ }

static newestFirst(a: TID, b: TID): number {
return a.compareTo(b) * -1
}
static oldestFirst(a: TID, b: TID): number {

@@ -60,19 +63,16 @@ return a.compareTo(b)

static newestFirst(a: TID, b: TID): number {
return b.compareTo(a)
}
static is(str: string): boolean {
try {
TID.fromStr(str)
return true
} catch (err) {
return false
}
return dedash(str).length === TID_LEN
}
timestamp(): number {
const substr = this.str.slice(0, 11)
return s32decode(substr)
return s32decode(this.str.slice(0, 11))
}
clockid(): number {
const substr = this.str.slice(11, 13)
return s32decode(substr)
return s32decode(this.str.slice(11, 13))
}

@@ -100,3 +100,3 @@

equals(other: TID): boolean {
return this.compareTo(other) === 0
return this.str === other.str
}

@@ -103,0 +103,0 @@

@@ -45,1 +45,3 @@ import { CID } from 'multiformats/cid'

export type ArrayEl<A> = A extends readonly (infer T)[] ? T : never
export type NotEmptyArray<T> = [T, ...T[]]

@@ -12,2 +12,6 @@ export const noUndefinedVals = <T>(

export const jitter = (maxMs: number) => {
return Math.round((Math.random() - 0.5) * maxMs * 2)
}
export const wait = (ms: number) => {

@@ -44,3 +48,3 @@ return new Promise((res) => setTimeout(res, ms))

export const streamToArray = async (
export const streamToBuffer = async (
stream: AsyncIterable<Uint8Array>,

@@ -111,1 +115,13 @@ ): Promise<Uint8Array> => {

}
export const dedupeStrs = (strs: string[]): string[] => {
return [...new Set(strs)]
}
export const parseIntWithFallback = <T>(
value: string | undefined,
fallback: T,
): number | T => {
const parsed = parseInt(value || '', 10)
return isNaN(parsed) ? fallback : parsed
}

@@ -1,2 +0,2 @@

import { graphemeLen, utf8Len } from '../src'
import { graphemeLen, parseLanguage, utf8Len, validateLanguage } from '../src'

@@ -17,3 +17,3 @@ describe('string', () => {

it('caluclates grapheme length', () => {
it('calculates grapheme length', () => {
expect(graphemeLen('a')).toBe(1)

@@ -31,2 +31,86 @@ expect(graphemeLen('~')).toBe(1)

})
describe('languages', () => {
it('validates BCP 47', () => {
// valid
expect(validateLanguage('de')).toEqual(true)
expect(validateLanguage('de-CH')).toEqual(true)
expect(validateLanguage('de-DE-1901')).toEqual(true)
expect(validateLanguage('es-419')).toEqual(true)
expect(validateLanguage('sl-IT-nedis')).toEqual(true)
expect(validateLanguage('mn-Cyrl-MN')).toEqual(true)
expect(validateLanguage('x-fr-CH')).toEqual(true)
expect(
validateLanguage('en-GB-boont-r-extended-sequence-x-private'),
).toEqual(true)
expect(validateLanguage('sr-Cyrl')).toEqual(true)
expect(validateLanguage('hy-Latn-IT-arevela')).toEqual(true)
expect(validateLanguage('i-klingon')).toEqual(true)
// invalid
expect(validateLanguage('')).toEqual(false)
expect(validateLanguage('x')).toEqual(false)
expect(validateLanguage('de-CH-')).toEqual(false)
expect(validateLanguage('i-bad-grandfathered')).toEqual(false)
})
it('parses BCP 47', () => {
// valid
expect(parseLanguage('de')).toEqual({
language: 'de',
})
expect(parseLanguage('de-CH')).toEqual({
language: 'de',
region: 'CH',
})
expect(parseLanguage('de-DE-1901')).toEqual({
language: 'de',
region: 'DE',
variant: '1901',
})
expect(parseLanguage('es-419')).toEqual({
language: 'es',
region: '419',
})
expect(parseLanguage('sl-IT-nedis')).toEqual({
language: 'sl',
region: 'IT',
variant: 'nedis',
})
expect(parseLanguage('mn-Cyrl-MN')).toEqual({
language: 'mn',
script: 'Cyrl',
region: 'MN',
})
expect(parseLanguage('x-fr-CH')).toEqual({
privateUse: 'x-fr-CH',
})
expect(
parseLanguage('en-GB-boont-r-extended-sequence-x-private'),
).toEqual({
language: 'en',
region: 'GB',
variant: 'boont',
extension: 'r-extended-sequence',
privateUse: 'x-private',
})
expect(parseLanguage('sr-Cyrl')).toEqual({
language: 'sr',
script: 'Cyrl',
})
expect(parseLanguage('hy-Latn-IT-arevela')).toEqual({
language: 'hy',
script: 'Latn',
region: 'IT',
variant: 'arevela',
})
expect(parseLanguage('i-klingon')).toEqual({
grandfathered: 'i-klingon',
})
// invalid
expect(parseLanguage('')).toEqual(null)
expect(parseLanguage('x')).toEqual(null)
expect(parseLanguage('de-CH-')).toEqual(null)
expect(parseLanguage('i-bad-grandfathered')).toEqual(null)
})
})
})

@@ -18,2 +18,120 @@ import TID from '../src/tid'

})
it('throws if invalid tid passed', () => {
expect(() => new TID('')).toThrow('Poorly formatted TID: 0 length')
})
describe('nextStr', () => {
it('returns next tid as a string', () => {
const str = TID.nextStr()
expect(typeof str).toEqual('string')
expect(str.length).toEqual(13)
})
})
describe('newestFirst', () => {
it('sorts tids newest first', () => {
const oldest = TID.next()
const newest = TID.next()
const tids = [oldest, newest]
tids.sort(TID.newestFirst)
expect(tids).toEqual([newest, oldest])
})
})
describe('oldestFirst', () => {
it('sorts tids oldest first', () => {
const oldest = TID.next()
const newest = TID.next()
const tids = [newest, oldest]
tids.sort(TID.oldestFirst)
expect(tids).toEqual([oldest, newest])
})
})
describe('is', () => {
it('true for valid tids', () => {
const tid = TID.next()
const asStr = tid.toString()
expect(TID.is(asStr)).toBe(true)
})
it('false for invalid tids', () => {
expect(TID.is('')).toBe(false)
})
})
describe('equals', () => {
it('true when same tid', () => {
const tid = TID.next()
expect(tid.equals(tid)).toBe(true)
})
it('true when different instance, same tid', () => {
const tid0 = TID.next()
const tid1 = new TID(tid0.toString())
expect(tid0.equals(tid1)).toBe(true)
})
it('false when different tid', () => {
const tid0 = TID.next()
const tid1 = TID.next()
expect(tid0.equals(tid1)).toBe(false)
})
})
describe('newerThan', () => {
it('true for newer tid', () => {
const tid0 = TID.next()
const tid1 = TID.next()
expect(tid1.newerThan(tid0)).toBe(true)
})
it('false for older tid', () => {
const tid0 = TID.next()
const tid1 = TID.next()
expect(tid0.newerThan(tid1)).toBe(false)
})
it('false for identical tids', () => {
const tid0 = TID.next()
const tid1 = new TID(tid0.toString())
expect(tid0.newerThan(tid1)).toBe(false)
})
})
describe('olderThan', () => {
it('true for older tid', () => {
const tid0 = TID.next()
const tid1 = TID.next()
expect(tid0.olderThan(tid1)).toBe(true)
})
it('false for newer tid', () => {
const tid0 = TID.next()
const tid1 = TID.next()
expect(tid1.olderThan(tid0)).toBe(false)
})
it('false for identical tids', () => {
const tid0 = TID.next()
const tid1 = new TID(tid0.toString())
expect(tid0.olderThan(tid1)).toBe(false)
})
})
})

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc