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

@atproto/common-web

Package Overview
Dependencies
Maintainers
4
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.3.0 to 0.3.1

10

CHANGELOG.md
# @atproto/common-web
## 0.3.1
### Patch Changes
- [#2770](https://github.com/bluesky-social/atproto/pull/2770) [`a07b21151`](https://github.com/bluesky-social/atproto/commit/a07b21151f1850340c4b7797ebb11521b1a6cdf3) Thanks [@matthieusieben](https://github.com/matthieusieben)! - Add omit() utility
- [#2770](https://github.com/bluesky-social/atproto/pull/2770) [`a07b21151`](https://github.com/bluesky-social/atproto/commit/a07b21151f1850340c4b7797ebb11521b1a6cdf3) Thanks [@matthieusieben](https://github.com/matthieusieben)! - DID document parsing optimization
- [#2835](https://github.com/bluesky-social/atproto/pull/2835) [`eb20ff64a`](https://github.com/bluesky-social/atproto/commit/eb20ff64a2d8e3061c652e1e247bf9b0fe3c41a6) Thanks [@matthieusieben](https://github.com/matthieusieben)! - ponyfill URL.canParse
## 0.3.0

@@ -4,0 +14,0 @@

2

dist/async.d.ts

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

export declare const readFromGenerator: <T>(gen: AsyncGenerator<T, any, unknown>, isDone: (last?: T | undefined) => Promise<boolean> | boolean, waitFor?: Promise<unknown>, maxLength?: number) => Promise<T[]>;
export declare const readFromGenerator: <T>(gen: AsyncGenerator<T>, isDone: (last?: T) => Promise<boolean> | boolean, waitFor?: Promise<unknown>, maxLength?: number) => Promise<T[]>;
export type Deferrable = {

@@ -3,0 +3,0 @@ resolve: () => void;

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

import { ZodError } from 'zod';
export interface Checkable<T> {

@@ -9,3 +8,3 @@ parse: (obj: unknown) => T;

success: false;
error: ZodError;
error: Error;
};

@@ -18,4 +17,5 @@ }

export declare const is: <T>(obj: unknown, def: Checkable<T>) => obj is T;
export declare const create: <T>(def: Checkable<T>) => (v: unknown) => v is T;
export declare const assure: <T>(def: Checkable<T>, obj: unknown) => T;
export declare const isObject: (obj: unknown) => obj is Record<string, unknown>;
//# sourceMappingURL=check.d.ts.map
"use strict";
// Explicitly not using "zod" types here to avoid mismatching types due to
// version differences.
Object.defineProperty(exports, "__esModule", { value: true });
exports.isObject = exports.assure = exports.is = void 0;
exports.isObject = exports.assure = exports.create = exports.is = void 0;
const is = (obj, def) => {

@@ -8,2 +10,4 @@ return def.safeParse(obj).success;

exports.is = is;
const create = (def) => (v) => def.safeParse(v).success;
exports.create = create;
const assure = (def, obj) => {

@@ -10,0 +14,0 @@ return def.parse(obj);

@@ -14,3 +14,3 @@ import { z } from 'zod';

type: string;
serviceEndpoint: (string | Record<string, unknown>) & (string | Record<string, unknown> | undefined);
serviceEndpoint: string | Record<string, unknown>;
}[] | undefined;

@@ -62,7 +62,7 @@ };

type: string;
serviceEndpoint: (string | Record<string, unknown>) & (string | Record<string, unknown> | undefined);
serviceEndpoint: string | Record<string, unknown>;
}, {
id: string;
type: string;
serviceEndpoint: (string | Record<string, unknown>) & (string | Record<string, unknown> | undefined);
serviceEndpoint: string | Record<string, unknown>;
}>, "many">>;

@@ -81,3 +81,3 @@ }, "strip", z.ZodTypeAny, {

type: string;
serviceEndpoint: (string | Record<string, unknown>) & (string | Record<string, unknown> | undefined);
serviceEndpoint: string | Record<string, unknown>;
}[] | undefined;

@@ -96,3 +96,3 @@ }, {

type: string;
serviceEndpoint: (string | Record<string, unknown>) & (string | Record<string, unknown> | undefined);
serviceEndpoint: string | Record<string, unknown>;
}[] | undefined;

@@ -99,0 +99,0 @@ }>;

@@ -21,9 +21,12 @@ "use strict";

const aka = doc.alsoKnownAs;
if (!aka)
return undefined;
const found = aka.find((name) => name.startsWith('at://'));
if (!found)
return undefined;
// strip off at:// prefix
return found.slice(5);
if (aka) {
for (let i = 0; i < aka.length; i++) {
const alias = aka[i];
if (alias.startsWith('at://')) {
// strip off "at://" prefix
return alias.slice(5);
}
}
}
return undefined;
};

@@ -37,17 +40,13 @@ exports.getHandle = getHandle;

const getVerificationMaterial = (doc, keyId) => {
const did = (0, exports.getDid)(doc);
let keys = doc.verificationMethod;
if (!keys)
// /!\ Hot path
const key = findItemById(doc, 'verificationMethod', `#${keyId}`);
if (!key) {
return undefined;
if (typeof keys !== 'object')
}
if (!key.publicKeyMultibase) {
return undefined;
if (!Array.isArray(keys)) {
keys = [keys];
}
const found = keys.find((key) => key.id === `#${keyId}` || key.id === `${did}#${keyId}`);
if (!found?.publicKeyMultibase)
return undefined;
return {
type: found.type,
publicKeyMultibase: found.publicKeyMultibase,
type: key.type,
publicKeyMultibase: key.publicKeyMultibase,
};

@@ -85,42 +84,58 @@ };

const getServiceEndpoint = (doc, opts) => {
const did = (0, exports.getDid)(doc);
let services = doc.service;
if (!services)
// /!\ Hot path
const service = findItemById(doc, 'service', opts.id);
if (!service) {
return undefined;
if (typeof services !== 'object')
return undefined;
if (!Array.isArray(services)) {
services = [services];
}
const found = services.find((service) => service.id === opts.id || service.id === `${did}${opts.id}`);
if (!found)
if (opts.type && service.type !== opts.type) {
return undefined;
if (opts.type && found.type !== opts.type) {
return undefined;
}
if (typeof found.serviceEndpoint !== 'string') {
if (typeof service.serviceEndpoint !== 'string') {
return undefined;
}
return validateUrl(found.serviceEndpoint);
return validateUrl(service.serviceEndpoint);
};
exports.getServiceEndpoint = getServiceEndpoint;
function findItemById(doc, type, id) {
// /!\ Hot path
const items = doc[type];
if (items) {
for (let i = 0; i < items.length; i++) {
const item = items[i];
const itemId = item.id;
if (itemId[0] === '#'
? itemId === id
: // Optimized version of: itemId === `${doc.id}${id}`
itemId.length === doc.id.length + id.length &&
itemId[doc.id.length] === '#' &&
itemId.endsWith(id) &&
itemId.startsWith(doc.id) // <== We could probably skip this check
) {
return item;
}
}
}
return undefined;
}
// Check protocol and hostname to prevent potential SSRF
const validateUrl = (urlStr) => {
let url;
try {
url = new URL(urlStr);
}
catch {
if (!urlStr.startsWith('http://') && !urlStr.startsWith('https://')) {
return undefined;
}
if (!['http:', 'https:'].includes(url.protocol)) {
if (!canParseUrl(urlStr)) {
return undefined;
}
else if (!url.hostname) {
return undefined;
}
else {
return urlStr;
}
return urlStr;
};
const canParseUrl = URL.canParse ??
// URL.canParse is not available in Node.js < 18.17.0
((urlStr) => {
try {
new URL(urlStr);
return true;
}
catch {
return false;
}
});
// Types

@@ -127,0 +142,0 @@ // --------

/// <reference types="node" />
export declare const noUndefinedVals: <T>(obj: Record<string, T | undefined>) => Record<string, T>;
export declare function omit<T extends undefined | Record<string, unknown>, K extends keyof NonNullable<T>>(obj: T, keys: readonly K[]): T extends undefined ? undefined : Omit<T, K>;
export declare const jitter: (maxMs: number) => number;

@@ -4,0 +5,0 @@ export declare const wait: (ms: number) => Promise<unknown>;

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.parseIntWithFallback = exports.dedupeStrs = exports.range = exports.chunkArray = exports.errHasMsg = exports.isErrnoException = exports.asyncFilter = exports.s32decode = exports.s32encode = exports.streamToBuffer = exports.flattenUint8Arrays = exports.bailableWait = exports.wait = exports.jitter = exports.noUndefinedVals = void 0;
exports.parseIntWithFallback = exports.dedupeStrs = exports.range = exports.chunkArray = exports.errHasMsg = exports.isErrnoException = exports.asyncFilter = exports.s32decode = exports.s32encode = exports.streamToBuffer = exports.flattenUint8Arrays = exports.bailableWait = exports.wait = exports.jitter = exports.omit = exports.noUndefinedVals = void 0;
const noUndefinedVals = (obj) => {

@@ -13,2 +13,8 @@ Object.keys(obj).forEach((k) => {

exports.noUndefinedVals = noUndefinedVals;
function omit(obj, keys) {
if (!obj)
return obj;
return Object.fromEntries(Object.entries(obj).filter((entry) => !keys.includes(entry[0])));
}
exports.omit = omit;
const jitter = (maxMs) => {

@@ -15,0 +21,0 @@ return Math.round((Math.random() - 0.5) * maxMs * 2);

{
"name": "@atproto/common-web",
"version": "0.3.0",
"version": "0.3.1",
"license": "MIT",

@@ -21,3 +21,3 @@ "description": "Shared web-platform-friendly code for atproto libraries",

"uint8arrays": "3.0.0",
"zod": "^3.21.4"
"zod": "^3.23.8"
},

@@ -24,0 +24,0 @@ "devDependencies": {

export const keyBy = <T>(arr: T[], key: string): Record<string, T> => {
return arr.reduce((acc, cur) => {
acc[cur[key]] = cur
return acc
}, {} as Record<string, T>)
return arr.reduce(
(acc, cur) => {
acc[cur[key]] = cur
return acc
},
{} as Record<string, T>,
)
}

@@ -7,0 +10,0 @@

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

import { ZodError } from 'zod'
// Explicitly not using "zod" types here to avoid mismatching types due to
// version differences.

@@ -7,3 +8,3 @@ export interface Checkable<T> {

obj: unknown,
) => { success: true; data: T } | { success: false; error: ZodError }
) => { success: true; data: T } | { success: false; error: Error }
}

@@ -20,2 +21,7 @@

export const create =
<T>(def: Checkable<T>) =>
(v: unknown): v is T =>
def.safeParse(v).success
export const assure = <T>(def: Checkable<T>, obj: unknown): T => {

@@ -22,0 +28,0 @@ return def.parse(obj)

@@ -20,7 +20,12 @@ import { z } from 'zod'

const aka = doc.alsoKnownAs
if (!aka) return undefined
const found = aka.find((name) => name.startsWith('at://'))
if (!found) return undefined
// strip off at:// prefix
return found.slice(5)
if (aka) {
for (let i = 0; i < aka.length; i++) {
const alias = aka[i]
if (alias.startsWith('at://')) {
// strip off "at://" prefix
return alias.slice(5)
}
}
}
return undefined
}

@@ -39,16 +44,16 @@

): { type: string; publicKeyMultibase: string } | undefined => {
const did = getDid(doc)
let keys = doc.verificationMethod
if (!keys) return undefined
if (typeof keys !== 'object') return undefined
if (!Array.isArray(keys)) {
keys = [keys]
// /!\ Hot path
const key = findItemById(doc, 'verificationMethod', `#${keyId}`)
if (!key) {
return undefined
}
const found = keys.find(
(key) => key.id === `#${keyId}` || key.id === `${did}#${keyId}`,
)
if (!found?.publicKeyMultibase) return undefined
if (!key.publicKeyMultibase) {
return undefined
}
return {
type: found.type,
publicKeyMultibase: found.publicKeyMultibase,
type: key.type,
publicKeyMultibase: key.publicKeyMultibase,
}

@@ -88,39 +93,78 @@ }

) => {
const did = getDid(doc)
let services = doc.service
if (!services) return undefined
if (typeof services !== 'object') return undefined
if (!Array.isArray(services)) {
services = [services]
// /!\ Hot path
const service = findItemById(doc, 'service', opts.id)
if (!service) {
return undefined
}
const found = services.find(
(service) => service.id === opts.id || service.id === `${did}${opts.id}`,
)
if (!found) return undefined
if (opts.type && found.type !== opts.type) {
if (opts.type && service.type !== opts.type) {
return undefined
}
if (typeof found.serviceEndpoint !== 'string') {
if (typeof service.serviceEndpoint !== 'string') {
return undefined
}
return validateUrl(found.serviceEndpoint)
return validateUrl(service.serviceEndpoint)
}
function findItemById<
D extends DidDocument,
T extends 'verificationMethod' | 'service',
>(doc: D, type: T, id: string): NonNullable<D[T]>[number] | undefined
function findItemById(
doc: DidDocument,
type: 'verificationMethod' | 'service',
id: string,
) {
// /!\ Hot path
const items = doc[type]
if (items) {
for (let i = 0; i < items.length; i++) {
const item = items[i]
const itemId = item.id
if (
itemId[0] === '#'
? itemId === id
: // Optimized version of: itemId === `${doc.id}${id}`
itemId.length === doc.id.length + id.length &&
itemId[doc.id.length] === '#' &&
itemId.endsWith(id) &&
itemId.startsWith(doc.id) // <== We could probably skip this check
) {
return item
}
}
}
return undefined
}
// Check protocol and hostname to prevent potential SSRF
const validateUrl = (urlStr: string): string | undefined => {
let url
try {
url = new URL(urlStr)
} catch {
if (!urlStr.startsWith('http://') && !urlStr.startsWith('https://')) {
return undefined
}
if (!['http:', 'https:'].includes(url.protocol)) {
if (!canParseUrl(urlStr)) {
return undefined
} else if (!url.hostname) {
return undefined
} else {
return urlStr
}
return urlStr
}
const canParseUrl =
URL.canParse ??
// URL.canParse is not available in Node.js < 18.17.0
((urlStr: string): boolean => {
try {
new URL(urlStr)
return true
} catch {
return false
}
})
// Types

@@ -127,0 +171,0 @@ // --------

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

export function omit<
T extends undefined | Record<string, unknown>,
K extends keyof NonNullable<T>,
>(obj: T, keys: readonly K[]): T extends undefined ? undefined : Omit<T, K>
export function omit(
obj: Record<string, unknown>,
keys: readonly string[],
): undefined | Record<string, unknown> {
if (!obj) return obj
return Object.fromEntries(
Object.entries(obj).filter((entry) => !keys.includes(entry[0])),
)
}
export const jitter = (maxMs: number) => {

@@ -14,0 +29,0 @@ return Math.round((Math.random() - 0.5) * maxMs * 2)

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

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