diff --git a/types/gun/gun-tests.ts b/types/gun/gun-tests.ts index d196fba695..3c9eb0e55e 100644 --- a/types/gun/gun-tests.ts +++ b/types/gun/gun-tests.ts @@ -28,7 +28,9 @@ interface AppState { object: { num: number; str: string; + /** Comment test */ bool: boolean; + specstr: 'a' | 'b'; obj: { arr2: Array<{ foo: number; bar: string }>; }; @@ -47,6 +49,9 @@ app.get('object') .get('obj') .get('arr2') .set({ foo: 1, bar: '2' }); +app.get('object').put({ + bool: true +}); app.get('object') .get('bool') @@ -70,3 +75,46 @@ app.get('chatRoom').time!(msg => { }, 20); // $ExpectError app.get('object').time!({ a: 1 }); + +class X { + val: string; + b() {} +} +interface BadState { + // Top level primitives + a: 1; + b: { + // Ban functions + c: () => void; + // Ban class + d: typeof X; + // Recursive check for banned types + e: { + f: () => void; + }; + }; + // Filter, remove functions on prototype. + c: X; +} +const bad = new Gun(); +// $ExpectError +bad.get('a').put(1); +bad.get('b') + .get('c') + // $ExpectError + .put(() => {}); +bad.get('b') + .get('d') + // $ExpectError + .put(X); + +bad.get('b').put({ + // $ExpectError + c: () => {}, + // $ExpectError + d: X, + // $ExpectError + e: { + f: () => {} + } +}); diff --git a/types/gun/index.d.ts b/types/gun/index.d.ts index 51d02b68d5..176521acaa 100644 --- a/types/gun/index.d.ts +++ b/types/gun/index.d.ts @@ -1,4 +1,4 @@ -// Type definitions for gun 0.9 +// Type definitions for gun 0.9.9999991 // Project: https://github.com/amark/gun#readme // Definitions by: Jack Works // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped @@ -12,6 +12,30 @@ declare namespace Gun { /** Gun does not accept Array value, so we need extract to make types correct */ type AllowArray = ArrayOf extends never ? T : ArrayOf; type DisallowArray = ArrayOf extends never ? T : never; + /** These types cannot be stored on Gun */ + + type AlwaysDisallowedType = T extends (...args: any[]) => void + ? never + : T extends { new (...args: any[]): any } + ? never + : AccessObject; + type AccessObject = T extends object + ? { [key in keyof T]: (AlwaysDisallowedType extends never ? never : AccessObject) } + : T; + /** These types cannot be stored on Gun's root level */ + type DisallowPrimitives = Open extends false + ? T + : T extends string + ? never + : T extends number + ? never + : T extends boolean + ? never + : T extends null + ? never + : T extends undefined + ? never + : T; type ArrayAsRecord = ArrayOf extends never ? DataType : Record; /** * options['module name'] allows you to pass options to a 3rd party module. @@ -45,7 +69,8 @@ declare namespace Gun { }>; type Saveable = Partial | string | number | boolean | null | ChainReference; type AckCallback = (ack: { err: Error; ok: any } | { err: undefined; ok: string }) => void; - interface ChainReference { + interface ChainReference { + //#region API /** * Save data into gun, syncing it with your connected peers. * @@ -59,7 +84,10 @@ declare namespace Gun { * * @param callback invoked on each acknowledgment */ - put(data: DisallowArray, callback?: AckCallback): ChainReference; + put( + data: Partial>>>, + callback?: AckCallback + ): ChainReference; /** * Where to read data from. * @param key The key is the ID or property name of the data that you saved from earlier @@ -83,7 +111,7 @@ declare namespace Gun { /** the key, ID, or property name of the data. */ paramB: Record<'off' | 'to' | 'next' | 'the' | 'on' | 'as' | 'back' | 'rid' | 'id', any> ) => void - ): ChainReference; + ): ChainReference; /** * Change the configuration of the gun database instance. * @param options The options argument is the same object you pass to the constructor. @@ -115,7 +143,10 @@ declare namespace Gun { * To remove a listener call .off() on the same property or node. */ on( - callback: (data: ArrayAsRecord, key: ReferenceKey) => void, + callback: ( + data: DisallowPrimitives>>, + key: ReferenceKey + ) => void, option?: { change: boolean } | boolean ): ChainReference; /** @@ -123,7 +154,10 @@ declare namespace Gun { * @returns In the document, it said the return value may change in the future. Don't rely on it. */ once( - callback?: (data: (ArrayAsRecord) | undefined, key: ReferenceKey) => void, + callback?: ( + data: (DisallowPrimitives>>) | undefined, + key: ReferenceKey + ) => void, option?: { wait: number } ): ChainReference; /** @@ -136,11 +170,13 @@ declare namespace Gun { * **This means only objects, for now, are supported.** */ set( - data: DataType extends Array - ? U extends { [key: string]: any; [key: number]: any } - ? ArrayOf + data: AlwaysDisallowedType< + DataType extends Array + ? U extends { [key: string]: any; [key: number]: any } + ? ArrayOf + : never : never - : never, + >, callback?: AckCallback ): ChainReference>; /** @@ -157,8 +193,8 @@ declare namespace Gun { * Remove **all** listener on this node. */ off(): void; - - // Extended API + //#endregion + //#region Extended API /** * * Path does the same thing as `.get` but has some conveniences built in. @@ -256,15 +292,76 @@ declare namespace Gun { ): ChainReference; /** Pushes data to a Timegraph with it's time set to Gun.state()'s time */ time?(data: ArrayOf): void; + //#endregion + //#region User + /** + * Creates a new user and calls callback upon completion. + * @param alias Username or Alias which can be used to find a user. + * @param pass Passphrase that will be extended with PBKDF2 to make it a secure way to login. + * @param cb Callback that is to be called upon creation of the user. + * @param opt Option Object containing options for creation. (In gun options are added at end of syntax. opt is rarely used, hence is added at the end.) + */ + create( + alias: string, + pass: string, + cb?: (ack: { ok: 0; pub: string } | { err: string }) => void, + opt?: {} + ): ChainReference; + /** + * Authenticates a user, previously created via User.create. + * @param alias Username or Alias which can be used to find a user. + * @param pass Passphrase for the user + * @param cb Callback that is to be called upon authentication of the user. + * @param opt Option Object containing options for authentiaction. (In gun options are added at end of syntax. opt is rarely used, hence is added at the end.) + */ + auth( + alias: string, + pass: string, + cb?: ( + ack: + | { + ack: 2; + get: string; + on: (...args: [unknown, unknown, unknown]) => unknown; + put: { alias: string; auth: any; epub: string; pub: string }; + sea: CryptoKeyPair; + soul: string; + } + | { err: string } + ) => void, + opt?: {} + ): ChainReference; + /** + * Returns the key pair in the form of an object as below. + */ + pair(): CryptoKeyPair; + /** + * Log out currently authenticated user. Parameters are unused in the current implementation. + * @param opt unused in current implementation. + * @param cb unused in current implementation. + */ + leave(opt?: never, cb?: never): ChainReference; + /** + * Deletes a user from the current gun instance and propagates the delete to other peers. + * @param alias Username or alias. + * @param pass Passphrase for the user. + * @param cb Callback that is called when the user was successfully deleted. + */ + delete(alias: string, pass: string, cb?: (ack: { ok: 0 }) => void): Promise; + /** + * Recall saves a users credentials in sessionStorage of the browser. As long as the tab of your app is not closed the user stays logged in, even through page refreshes and reloads. + * @param opt option object If you want to use browser sessionStorage to allow users to stay logged in as long as the session is open, set opt.sessionStorage to true + * @param cb internally the callback is passed on to the user.auth function to logged the user back in. Refer to user.auth for callback documentation. + */ + recall(opt?: { sessionStorage: boolean }, cb?: Parameters[2]): ChainReference; + /** + * @param publicKey If you know a users publicKey you can get his user graph and see any unencrypted data he may have stored there. + */ + user(publicKey?: string): ChainReference; + //#endregion } - interface GunSEA { - // There is no the only content in the api document. - user: { - create(alias: string, passphrase: string, callback: (...args: any[]) => void): any; - }; - } - + type CryptoKeyPair = Record<'pub' | 'priv' | 'epub' | 'epriv', string>; interface Constructor { /** * @description @@ -274,8 +371,12 @@ declare namespace Gun { * * or you can pass in an array of URLs to sync with multiple peers. */ - (options?: string | string[] | ConstructorOptions): ChainReference & GunSEA; - new (options?: string | string[] | ConstructorOptions): ChainReference & GunSEA; + (options?: string | string[] | ConstructorOptions): ChainReference; + new (options?: string | string[] | ConstructorOptions): ChainReference< + DataType, + any, + 'pre_root' + >; node: { /** Returns true if data is a gun node, otherwise false. */ is(anything: any): anything is ChainReference; @@ -289,7 +390,66 @@ declare namespace Gun { ify(json: any): any; }; /** @see https://gun.eco/docs/SEA */ - SEA: any; + SEA: { + /** If you want SEA to throw while in development, turn SEA.throw = true on, but please do not use this in production. */ + throw?: boolean; + /** Last known error */ + err?: Error; + /** + * This gives you a Proof of Work (POW) / Hashing of Data + * @param data The data to be hashed, work to be performed on. + * @param pair (salt) You can pass pair of keys to use as salt. Salt will prevent others to pre-compute the work, + * so using your public key is not a good idea. If it is not specified, it will be random, + * which ruins your chance of ever being able to re-derive the work deterministically + * @param callback function to executed upon execution of proof + * @param opt default: {name: 'PBKDF2', encode: 'base64'} + */ + work( + data: any, + pair?: any, + callback?: (data: string | void) => void, + opt?: Partial<{ + name: 'SHA-256' | 'PBKDF2'; + encode: 'base64' | 'base32' | 'base16'; + /** iterations to use on subtle.deriveBits */ + iterations: number; + salt: any; + hash: string; + length: any; + }> + ): Promise; + /** + * This generates a cryptographically secure public/private key pair - be careful not to leak the private keys! + * Note: API subject to change we may change the parameters to accept data and work, in addition to generation. + * You will need this for most of SEA's API, see those method's examples. + * The default cryptographic primitives for the asymmetric keys are ECDSA for signing and ECDH for encryption. + */ + pair(cb: (data: CryptoKeyPair) => void, opt?: {}): Promise; + /** + * Adds a signature to a message, for data that you want to prevent attackers tampering with. + * @param data is the content that you want to prove is authorized. + * @param pair is from .pair. + */ + sign(data: any, pair: CryptoKeyPair): Promise; + /** + * Gets the data if and only if the message can be verified as coming from the person you expect. + * @param message is what comes from .sign. + * @param pair from .pair or its public key text (pair.pub). + */ + verify(message: any, pair: CryptoKeyPair | string): Promise; + /** + * Takes some data that you want to keep secret and encrypts it so nobody else can read it. + * @param data is the content that you want to encrypt. + * @param pair from .pair or a passphrase you want to use as a cypher to encrypt with. + */ + encrypt(data: any, pair: CryptoKeyPair | string): Promise; + /** + * Read the secret data, if and only if you are allowed to. + * @param message is what comes from .encrypt. + * @param pair from .pair or the passphrase to decypher the message. + */ + decrypt(message: any, pair: CryptoKeyPair | string): Promise; + }; } } declare const Gun: Gun.Constructor;