@atproto/common 0.0.1 → 0.1.0

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/ipld.d.ts ADDED
@@ -0,0 +1,10 @@
1
+ import { CID } from 'multiformats/cid';
2
+ import * as Block from 'multiformats/block';
3
+ import * as cborCodec from '@ipld/dag-cbor';
4
+ export declare const dataToCborBlock: (data: unknown) => Promise<Block.Block<unknown>>;
5
+ export declare const verifyCidForBytes: (cid: CID, bytes: Uint8Array) => Promise<void>;
6
+ export declare const sha256RawToCid: (hash: Uint8Array) => CID;
7
+ export declare const cidForCbor: (data: unknown) => Promise<CID>;
8
+ export declare const cborEncode: typeof cborCodec.encode;
9
+ export declare const cborDecode: typeof cborCodec.decode;
10
+ export declare const cborBytesToRecord: (bytes: Uint8Array) => Record<string, unknown>;
@@ -0,0 +1,9 @@
1
+ export interface Def<T> {
2
+ parse: (obj: unknown) => T;
3
+ safeParse: (obj: unknown) => {
4
+ success: boolean;
5
+ };
6
+ }
7
+ export declare const is: <T>(obj: unknown, def: Def<T>) => obj is T;
8
+ export declare const assure: <T>(def: Def<T>, obj: unknown) => T;
9
+ export declare const isObject: (obj: unknown) => obj is Record<string, unknown>;
@@ -0,0 +1,2 @@
1
+ import { CID } from 'multiformats/cid';
2
+ export declare const cidForData: (data: unknown) => Promise<CID>;
@@ -0,0 +1,8 @@
1
+ export * as check from './check';
2
+ export * as util from './util';
3
+ export * from './util';
4
+ export * from './tid';
5
+ export * from './blocks';
6
+ export * from './logger';
7
+ export * from './types';
8
+ export * from './streams';
@@ -0,0 +1,10 @@
1
+ import { CID } from 'multiformats/cid';
2
+ import * as Block from 'multiformats/block';
3
+ import * as cborCodec from '@ipld/dag-cbor';
4
+ export declare const dataToCborBlock: (data: unknown) => Promise<Block.Block<unknown>>;
5
+ export declare const verifyCidForBytes: (cid: CID, bytes: Uint8Array) => Promise<void>;
6
+ export declare const sha256RawToCid: (hash: Uint8Array) => CID;
7
+ export declare const cidForCbor: (data: unknown) => Promise<CID>;
8
+ export declare const cborEncode: typeof cborCodec.encode;
9
+ export declare const cborDecode: typeof cborCodec.decode;
10
+ export declare const cborBytesToRecord: (bytes: Uint8Array) => Record<string, unknown>;
@@ -0,0 +1,2 @@
1
+ import pino from 'pino';
2
+ export declare const subsystemLogger: (name: string) => pino.Logger;
@@ -0,0 +1,6 @@
1
+ export declare class NameResolveError extends Error {
2
+ responseStatus: number;
3
+ responseBody: any;
4
+ constructor(username: string, responseStatus: number, responseBody: any);
5
+ }
6
+ export declare function resolveName(name: string): Promise<string>;
@@ -0,0 +1,20 @@
1
+ export declare const ADX_URI_REGEX: RegExp;
2
+ export declare class AdxUri {
3
+ hash: string;
4
+ host: string;
5
+ pathname: string;
6
+ searchParams: URLSearchParams;
7
+ constructor(uri: string, base?: string);
8
+ get protocol(): string;
9
+ get origin(): string;
10
+ get hostname(): string;
11
+ set hostname(v: string);
12
+ get search(): string;
13
+ set search(v: string);
14
+ get collection(): string;
15
+ set collection(v: string);
16
+ get recordKey(): string;
17
+ set recordKey(v: string);
18
+ get href(): string;
19
+ toString(): string;
20
+ }
@@ -0,0 +1,9 @@
1
+ import { AxiosError } from 'axios';
2
+ export declare const assureAxiosError: (err: unknown) => AxiosError;
3
+ export declare const parseAxiosError: (e: unknown) => {
4
+ code: number;
5
+ msg: string;
6
+ err: AxiosError;
7
+ };
8
+ export declare const didNetworkUrl: () => string;
9
+ export declare const cleanHostUrl: (url: string) => string;
@@ -0,0 +1,14 @@
1
+ /// <reference types="node" />
2
+ /// <reference types="node" />
3
+ import { Stream, Readable, Transform, TransformCallback } from 'stream';
4
+ export declare const forwardStreamErrors: (...streams: Stream[]) => void;
5
+ export declare const cloneStream: (stream: Readable) => Readable;
6
+ export declare const streamSize: (stream: Readable) => Promise<number>;
7
+ export declare const bytesToStream: (bytes: Uint8Array) => Readable;
8
+ export declare class MaxSizeChecker extends Transform {
9
+ maxSize: number;
10
+ createError: () => Error;
11
+ totalSize: number;
12
+ constructor(maxSize: number, createError: () => Error);
13
+ _transform(chunk: Uint8Array, _enc: BufferEncoding, cb: TransformCallback): void | this;
14
+ }
@@ -0,0 +1,20 @@
1
+ export declare class TID {
2
+ str: string;
3
+ constructor(str: string);
4
+ static next(): TID;
5
+ static nextStr(): string;
6
+ static fromTime(timestamp: number, clockid: number): TID;
7
+ static fromStr(str: string): TID;
8
+ static newestFirst(a: TID, b: TID): number;
9
+ static oldestFirst(a: TID, b: TID): number;
10
+ static is(str: string): boolean;
11
+ timestamp(): number;
12
+ clockid(): number;
13
+ formatted(): string;
14
+ toString(): string;
15
+ compareTo(other: TID): number;
16
+ equals(other: TID): boolean;
17
+ newerThan(other: TID): boolean;
18
+ olderThan(other: TID): boolean;
19
+ }
20
+ export default TID;
@@ -0,0 +1,14 @@
1
+ import * as mf from 'multiformats/cid';
2
+ import { z } from 'zod';
3
+ export declare const isCid: (str: string) => boolean;
4
+ declare const bytes: z.ZodType<Uint8Array, z.ZodTypeDef, Uint8Array>;
5
+ export declare type Bytes = z.infer<typeof bytes>;
6
+ export declare const def: {
7
+ string: z.ZodString;
8
+ cid: z.ZodEffects<z.ZodEffects<z.ZodAny, any, any>, mf.CID, any>;
9
+ strToCid: z.ZodEffects<z.ZodEffects<z.ZodString, string, string>, mf.CID, string>;
10
+ bytes: z.ZodType<Uint8Array, z.ZodTypeDef, Uint8Array>;
11
+ strToInt: z.ZodEffects<z.ZodEffects<z.ZodString, string, string>, number, string>;
12
+ strToBool: z.ZodEffects<z.ZodString, boolean, string>;
13
+ };
14
+ export {};
@@ -0,0 +1,9 @@
1
+ export declare const noUndefinedVals: <T>(obj: Record<string, T>) => Record<string, T>;
2
+ export declare const wait: (ms: number) => Promise<unknown>;
3
+ export declare const flattenUint8Arrays: (arrs: Uint8Array[]) => Uint8Array;
4
+ export declare const streamToArray: (stream: AsyncIterable<Uint8Array>) => Promise<Uint8Array>;
5
+ export declare const s32encode: (i: number) => string;
6
+ export declare const s32decode: (s: string) => number;
7
+ export declare const asyncFilter: <T>(arr: T[], fn: (t: T) => Promise<boolean>) => Promise<T[]>;
8
+ export declare const isErrnoException: (err: unknown) => err is NodeJS.ErrnoException;
9
+ export declare const errHasMsg: (err: unknown, msg: string) => boolean;
@@ -0,0 +1,4 @@
1
+ export declare const SECOND = 1000;
2
+ export declare const MINUTE: number;
3
+ export declare const HOUR: number;
4
+ export declare const DAY: number;
package/dist/types.d.ts CHANGED
@@ -1,14 +1,18 @@
1
1
  import * as mf from 'multiformats/cid';
2
2
  import { z } from 'zod';
3
- export declare const isCid: (str: string) => boolean;
4
- declare const bytes: z.ZodType<Uint8Array, z.ZodTypeDef, Uint8Array>;
5
- export declare type Bytes = z.infer<typeof bytes>;
6
- export declare const def: {
7
- string: z.ZodString;
3
+ import { Def } from './check';
4
+ export declare const schema: {
8
5
  cid: z.ZodEffects<z.ZodEffects<z.ZodAny, any, any>, mf.CID, any>;
9
- strToCid: z.ZodEffects<z.ZodEffects<z.ZodString, string, string>, mf.CID, string>;
10
6
  bytes: z.ZodType<Uint8Array, z.ZodTypeDef, Uint8Array>;
11
- strToInt: z.ZodEffects<z.ZodEffects<z.ZodString, string, string>, number, string>;
12
- strToBool: z.ZodEffects<z.ZodString, boolean, string>;
7
+ string: z.ZodString;
8
+ record: z.ZodRecord<z.ZodString, z.ZodUnknown>;
9
+ unknown: z.ZodUnknown;
10
+ };
11
+ export declare const def: {
12
+ cid: Def<mf.CID>;
13
+ bytes: Def<Uint8Array>;
14
+ string: Def<string>;
15
+ record: Def<Record<string, unknown>>;
16
+ unknown: Def<unknown>;
13
17
  };
14
- export {};
18
+ export declare type ArrayEl<A> = A extends readonly (infer T)[] ? T : never;
package/dist/util.d.ts CHANGED
@@ -1,6 +1,9 @@
1
- /// <reference types="node" />
2
1
  export declare const noUndefinedVals: <T>(obj: Record<string, T>) => Record<string, T>;
3
2
  export declare const wait: (ms: number) => Promise<unknown>;
3
+ export declare const bailableWait: (ms: number) => {
4
+ bail: () => void;
5
+ wait: () => Promise<void>;
6
+ };
4
7
  export declare const flattenUint8Arrays: (arrs: Uint8Array[]) => Uint8Array;
5
8
  export declare const streamToArray: (stream: AsyncIterable<Uint8Array>) => Promise<Uint8Array>;
6
9
  export declare const s32encode: (i: number) => string;
@@ -8,3 +11,5 @@ export declare const s32decode: (s: string) => number;
8
11
  export declare const asyncFilter: <T>(arr: T[], fn: (t: T) => Promise<boolean>) => Promise<T[]>;
9
12
  export declare const isErrnoException: (err: unknown) => err is NodeJS.ErrnoException;
10
13
  export declare const errHasMsg: (err: unknown, msg: string) => boolean;
14
+ export declare const chunkArray: <T>(arr: T[], chunkSize: number) => T[][];
15
+ export declare const range: (num: number) => number[];
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@atproto/common",
3
- "version": "0.0.1",
4
- "main": "dist/index.js",
3
+ "version": "0.1.0",
4
+ "main": "src/index.ts",
5
5
  "license": "MIT",
6
6
  "scripts": {
7
7
  "test": "jest",
package/src/async.ts ADDED
@@ -0,0 +1,100 @@
1
+ import { bailableWait } from './util'
2
+
3
+ // reads values from a generator into a list
4
+ // NOTE: does not signal generator to close. it *will* continue to produce values
5
+ export const readFromGenerator = async <T>(
6
+ gen: AsyncGenerator<T>,
7
+ maxLength = Number.MAX_SAFE_INTEGER,
8
+ timeout = 2000,
9
+ ): Promise<T[]> => {
10
+ const evts: T[] = []
11
+ while (evts.length < maxLength) {
12
+ const { bail, wait } = bailableWait(timeout)
13
+ const maybeEvt = await Promise.race([gen.next(), wait()])
14
+ bail()
15
+ if (!maybeEvt) break
16
+ const evt = maybeEvt as IteratorResult<T>
17
+ if (evt.done) break
18
+ evts.push(evt.value)
19
+ }
20
+ return evts
21
+ }
22
+
23
+ export type Deferrable = {
24
+ resolve: () => void
25
+ complete: Promise<void>
26
+ }
27
+
28
+ export const createDeferrable = (): Deferrable => {
29
+ let resolve
30
+ const promise: Promise<void> = new Promise((res) => {
31
+ resolve = () => res()
32
+ })
33
+ return { resolve, complete: promise }
34
+ }
35
+
36
+ export const createDeferrables = (count: number): Deferrable[] => {
37
+ const list: Deferrable[] = []
38
+ for (let i = 0; i < count; i++) {
39
+ list.push(createDeferrable())
40
+ }
41
+ return list
42
+ }
43
+
44
+ export const allComplete = async (deferrables: Deferrable[]): Promise<void> => {
45
+ await Promise.all(deferrables.map((d) => d.complete))
46
+ }
47
+
48
+ export class AsyncBuffer<T> {
49
+ private buffer: T[] = []
50
+ private promise: Promise<void>
51
+ private resolve: () => void
52
+
53
+ constructor(public maxSize?: number) {
54
+ this.resetPromise()
55
+ }
56
+
57
+ get curr(): T[] {
58
+ return this.buffer
59
+ }
60
+
61
+ get size(): number {
62
+ return this.buffer.length
63
+ }
64
+
65
+ resetPromise() {
66
+ this.promise = new Promise<void>((r) => (this.resolve = r))
67
+ }
68
+
69
+ push(item: T) {
70
+ this.buffer.push(item)
71
+ this.resolve()
72
+ }
73
+
74
+ pushMany(items: T[]) {
75
+ items.forEach((i) => this.buffer.push(i))
76
+ this.resolve()
77
+ }
78
+
79
+ async *events(): AsyncGenerator<T> {
80
+ while (true) {
81
+ await this.promise
82
+ if (this.maxSize && this.size > this.maxSize) {
83
+ throw new AsyncBufferFullError(this.maxSize)
84
+ }
85
+ const [first, ...rest] = this.buffer
86
+ if (first) {
87
+ this.buffer = rest
88
+ yield first
89
+ } else {
90
+ this.resetPromise()
91
+ }
92
+ }
93
+ }
94
+ }
95
+
96
+ export class AsyncBufferFullError extends Error {
97
+ constructor(maxSize: number) {
98
+ super(`ReachedMaxBufferSize: ${maxSize}`)
99
+ }
100
+ }
package/src/check.ts CHANGED
@@ -1,13 +1,22 @@
1
- export interface Def<T> {
1
+ import { ZodError } from 'zod'
2
+
3
+ export interface Checkable<T> {
2
4
  parse: (obj: unknown) => T
3
- safeParse: (obj: unknown) => { success: boolean }
5
+ safeParse: (
6
+ obj: unknown,
7
+ ) => { success: true; data: T } | { success: false; error: ZodError }
8
+ }
9
+
10
+ export interface Def<T> {
11
+ name: string
12
+ schema: Checkable<T>
4
13
  }
5
14
 
6
- export const is = <T>(obj: unknown, def: Def<T>): obj is T => {
15
+ export const is = <T>(obj: unknown, def: Checkable<T>): obj is T => {
7
16
  return def.safeParse(obj).success
8
17
  }
9
18
 
10
- export const assure = <T>(def: Def<T>, obj: unknown): T => {
19
+ export const assure = <T>(def: Checkable<T>, obj: unknown): T => {
11
20
  return def.parse(obj)
12
21
  }
13
22
 
package/src/index.ts CHANGED
@@ -1,9 +1,11 @@
1
1
  export * as check from './check'
2
2
  export * as util from './util'
3
3
 
4
+ export * from './async'
4
5
  export * from './util'
5
6
  export * from './tid'
6
- export * from './blocks'
7
+ export * from './ipld'
7
8
  export * from './logger'
8
9
  export * from './types'
9
10
  export * from './streams'
11
+ export * from './times'
package/src/ipld.ts ADDED
@@ -0,0 +1,48 @@
1
+ import { CID } from 'multiformats/cid'
2
+ import * as Block from 'multiformats/block'
3
+ import * as rawCodec from 'multiformats/codecs/raw'
4
+ import { sha256 } from 'multiformats/hashes/sha2'
5
+ import * as mf from 'multiformats'
6
+ import * as cborCodec from '@ipld/dag-cbor'
7
+ import { check, schema } from '.'
8
+
9
+ export const dataToCborBlock = async (data: unknown) => {
10
+ return Block.encode({
11
+ value: data,
12
+ codec: cborCodec,
13
+ hasher: sha256,
14
+ })
15
+ }
16
+
17
+ export const verifyCidForBytes = async (cid: CID, bytes: Uint8Array) => {
18
+ const digest = await sha256.digest(bytes)
19
+ const expected = CID.createV1(cid.code, digest)
20
+ if (!cid.equals(expected)) {
21
+ throw new Error(
22
+ `Not a valid CID for bytes. Expected: ${expected.toString()} Got: ${cid.toString()}`,
23
+ )
24
+ }
25
+ }
26
+
27
+ export const sha256RawToCid = (hash: Uint8Array): CID => {
28
+ const digest = mf.digest.create(sha256.code, hash)
29
+ return CID.createV1(rawCodec.code, digest)
30
+ }
31
+
32
+ export const cidForCbor = async (data: unknown): Promise<CID> => {
33
+ const block = await dataToCborBlock(data)
34
+ return block.cid
35
+ }
36
+
37
+ export const cborEncode = cborCodec.encode
38
+ export const cborDecode = cborCodec.decode
39
+
40
+ export const cborBytesToRecord = (
41
+ bytes: Uint8Array,
42
+ ): Record<string, unknown> => {
43
+ const val = cborDecode(bytes)
44
+ if (!check.is(val, schema.record)) {
45
+ throw new Error(`Expected object, got: ${val}`)
46
+ }
47
+ return val
48
+ }
package/src/times.ts ADDED
@@ -0,0 +1,4 @@
1
+ export const SECOND = 1000
2
+ export const MINUTE = SECOND * 60
3
+ export const HOUR = MINUTE * 60
4
+ export const DAY = HOUR * 24
package/src/types.ts CHANGED
@@ -1,44 +1,43 @@
1
1
  import * as mf from 'multiformats/cid'
2
2
  import { z } from 'zod'
3
+ import { Def } from './check'
3
4
 
4
- const cid = z
5
+ const cidSchema = z
5
6
  .any()
6
7
  .refine((obj: unknown) => mf.CID.asCID(obj) !== null, {
7
8
  message: 'Not a CID',
8
9
  })
9
10
  .transform((obj: unknown) => mf.CID.asCID(obj) as mf.CID)
10
11
 
11
- export const isCid = (str: string): boolean => {
12
- try {
13
- mf.CID.parse(str)
14
- return true
15
- } catch (err) {
16
- return false
17
- }
12
+ export const schema = {
13
+ cid: cidSchema,
14
+ bytes: z.instanceof(Uint8Array),
15
+ string: z.string(),
16
+ record: z.record(z.string(), z.unknown()),
17
+ unknown: z.unknown(),
18
18
  }
19
19
 
20
- const strToCid = z
21
- .string()
22
- .refine(isCid, { message: 'Not a valid CID' })
23
- .transform((str: string) => mf.CID.parse(str))
24
-
25
- const bytes = z.instanceof(Uint8Array)
26
- export type Bytes = z.infer<typeof bytes>
27
-
28
- const strToInt = z
29
- .string()
30
- .refine((str) => !isNaN(parseInt(str)), {
31
- message: 'Cannot parse string to integer',
32
- })
33
- .transform((str) => parseInt(str))
34
-
35
- const strToBool = z.string().transform((str) => str === 'true' || str === 't')
36
-
37
20
  export const def = {
38
- string: z.string(),
39
- cid,
40
- strToCid,
41
- bytes,
42
- strToInt,
43
- strToBool,
21
+ cid: {
22
+ name: 'cid',
23
+ schema: schema.cid,
24
+ } as Def<mf.CID>,
25
+ bytes: {
26
+ name: 'bytes',
27
+ schema: schema.bytes,
28
+ } as Def<Uint8Array>,
29
+ string: {
30
+ name: 'string',
31
+ schema: schema.string,
32
+ } as Def<string>,
33
+ record: {
34
+ name: 'record',
35
+ schema: schema.record,
36
+ } as Def<Record<string, unknown>>,
37
+ unknown: {
38
+ name: 'unknown',
39
+ schema: schema.unknown,
40
+ } as Def<unknown>,
44
41
  }
42
+
43
+ export type ArrayEl<A> = A extends readonly (infer T)[] ? T : never
package/src/util.ts CHANGED
@@ -13,6 +13,20 @@ export const wait = (ms: number) => {
13
13
  return new Promise((res) => setTimeout(res, ms))
14
14
  }
15
15
 
16
+ export const bailableWait = (
17
+ ms: number,
18
+ ): { bail: () => void; wait: () => Promise<void> } => {
19
+ let bail
20
+ const waitPromise = new Promise<void>((res) => {
21
+ const timeout = setTimeout(res, ms)
22
+ bail = () => {
23
+ clearTimeout(timeout)
24
+ res()
25
+ }
26
+ })
27
+ return { bail, wait: () => waitPromise }
28
+ }
29
+
16
30
  export const flattenUint8Arrays = (arrs: Uint8Array[]): Uint8Array => {
17
31
  const length = arrs.reduce((acc, cur) => {
18
32
  return acc + cur.length
@@ -67,9 +81,28 @@ export const asyncFilter = async <T>(
67
81
  export const isErrnoException = (
68
82
  err: unknown,
69
83
  ): err is NodeJS.ErrnoException => {
70
- return !!err && 'code' in err
84
+ return !!err && err['code']
71
85
  }
72
86
 
73
87
  export const errHasMsg = (err: unknown, msg: string): boolean => {
74
88
  return !!err && typeof err === 'object' && err['message'] === msg
75
89
  }
90
+
91
+ export const chunkArray = <T>(arr: T[], chunkSize: number): T[][] => {
92
+ return arr.reduce((acc, cur, i) => {
93
+ const chunkI = Math.floor(i / chunkSize)
94
+ if (!acc[chunkI]) {
95
+ acc[chunkI] = []
96
+ }
97
+ acc[chunkI].push(cur)
98
+ return acc
99
+ }, [] as T[][])
100
+ }
101
+
102
+ export const range = (num: number): number[] => {
103
+ const nums: number[] = []
104
+ for (let i = 0; i < num; i++) {
105
+ nums.push(i)
106
+ }
107
+ return nums
108
+ }
@@ -0,0 +1,22 @@
1
+ import { readFromGenerator, wait } from '../src'
2
+
3
+ describe('async', () => {
4
+ describe('readFromGenerator', () => {
5
+ async function* waitToYield(time: number) {
6
+ for (let i = 0; i < 5; i++) {
7
+ await wait(time)
8
+ yield true
9
+ }
10
+ }
11
+
12
+ it('reads from generator with timeout', async () => {
13
+ const read = await readFromGenerator(waitToYield(100), undefined, 105)
14
+ expect(read).toEqual([true, true, true, true, true])
15
+ })
16
+
17
+ it('stops reading at timeout', async () => {
18
+ const read = await readFromGenerator(waitToYield(100), undefined, 95)
19
+ expect(read).toEqual([])
20
+ })
21
+ })
22
+ })
package/src/blocks.ts DELETED
@@ -1,40 +0,0 @@
1
- import { CID } from 'multiformats/cid'
2
- import * as Block from 'multiformats/block'
3
- import * as rawCodec from 'multiformats/codecs/raw'
4
- import { sha256 as blockHasher } from 'multiformats/hashes/sha2'
5
- import * as mf from 'multiformats'
6
- import * as blockCodec from '@ipld/dag-cbor'
7
-
8
- export const valueToIpldBlock = async (data: unknown) => {
9
- return Block.encode({
10
- value: data,
11
- codec: blockCodec,
12
- hasher: blockHasher,
13
- })
14
- }
15
-
16
- export const sha256RawToCid = (hash: Uint8Array): CID => {
17
- const digest = mf.digest.create(blockHasher.code, hash)
18
- return CID.createV1(rawCodec.code, digest)
19
- }
20
-
21
- export const cidForData = async (data: unknown): Promise<CID> => {
22
- const block = await valueToIpldBlock(data)
23
- return block.cid
24
- }
25
-
26
- export const valueToIpldBytes = (value: unknown): Uint8Array => {
27
- return blockCodec.encode(value)
28
- }
29
-
30
- export const ipldBytesToValue = (bytes: Uint8Array) => {
31
- return blockCodec.decode(bytes)
32
- }
33
-
34
- export const ipldBytesToRecord = (bytes: Uint8Array): object => {
35
- const val = ipldBytesToValue(bytes)
36
- if (typeof val !== 'object' || val === null) {
37
- throw new Error(`Expected object, got: ${val}`)
38
- }
39
- return val
40
- }