From 658091f2ec553677cd44d23467696f6316a03096 Mon Sep 17 00:00:00 2001 From: Jack Works Date: Fri, 15 Feb 2019 22:34:02 +0800 Subject: [PATCH] Add package: gun --- types/gun/gun-tests.ts | 56 +++++++ types/gun/index.d.ts | 318 ++++++++++++++++++++++++++++++++++++++++ types/gun/tsconfig.json | 22 +++ types/gun/tslint.json | 1 + 4 files changed, 397 insertions(+) create mode 100644 types/gun/gun-tests.ts create mode 100644 types/gun/index.d.ts create mode 100644 types/gun/tsconfig.json create mode 100644 types/gun/tslint.json diff --git a/types/gun/gun-tests.ts b/types/gun/gun-tests.ts new file mode 100644 index 0000000000..2f65781bac --- /dev/null +++ b/types/gun/gun-tests.ts @@ -0,0 +1,56 @@ +import GunServer = require('gun'); +import Gun = require('gun/gun'); +import 'gun/lib/path.js'; +import 'gun/lib/not.js'; +import 'gun/lib/open.js'; +import 'gun/lib/load.js'; +import 'gun/lib/then.js'; +import 'gun/lib/bye.js'; +import 'gun/lib/later.js'; +import 'gun/lib/unset.js'; +import 'gun/lib/time.js'; + +Gun('http://yourdomain.com/gun'); +Gun(['http://server1.com/gun', 'http://server2.com/gun']); +Gun({ + s3: { + key: '', + secret: '', + bucket: '' + }, + file: 'file/path.json', + uuid() { + return 'xxxxxx'; + } +}); + +interface AppState { + object: { + num: number; + str: string; + bool: boolean; + obj: { + arr2: Array<{ foo: number; bar: string; }> + } + }; + chatRoom: Array<{ by: string; message: string; }>; +} + +const app = new Gun(); +app.get('object').get('bool').put(true); +app.get('object').get('num').put(1); +app.get('object').get('obj').get('arr2').set({ foo: 1, bar: '2' }); + +app.get('object').on((data) => { + data.bool; +}); +app.get('object').off(); +app.get('object').once((data) => { + if (data) data.bool; +}); +async function name() { + const data = await app.get('object').promise!(); + data.put.bool; +} +app.get('chatRoom').time!({ by: 'A', message: 'Hello' }); +app.get('chatRoom').time!((msg) => { msg.by; }, 20); diff --git a/types/gun/index.d.ts b/types/gun/index.d.ts new file mode 100644 index 0000000000..0c9063b4ed --- /dev/null +++ b/types/gun/index.d.ts @@ -0,0 +1,318 @@ +// Type definitions for gun 0.9 +// Project: https://github.com/amark/gun#readme +// Definitions by: Jack Works +// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped + +declare module 'gun' { + const Gun: Gun.Constructor + export = Gun +} + +declare module 'gun/gun' { + const cons: typeof import('gun') + export = cons +} + +declare namespace Gun { + type ArrayOf = T extends Array ? U : never + /** 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 + type ArrayAsRecord = ArrayOf extends never ? DataType : Record + /** + * options['module name'] allows you to pass options to a 3rd party module. + * Their project README will likely list the exposed options + * https://github.com/amark/gun/wiki/Modules + */ + type ConstructorOptions = Partial<{ + /** Undocumented but mentioned. Write data to a JSON. */ + file: string + /** Undocumented but mentioned. Create a websocket server */ + web: any + /** Undocumented but mentioned. Amazon S3 */ + s3: { + key: any + secret: any + bucket: any + } + /** the URLs are properties, and the value is an empty object. */ + peers: Record + /** default: true, creates and persists local (nodejs) data using Radisk. */ + radisk: boolean + /** default: true, persists local (browser) data to localStorage. */ + localStorage: boolean + /** uuid allows you to override the default 24 random alphanumeric soul generator with your own function. */ + uuid(): string + /** + * allows you to pass options to a 3rd party module. Their project README will likely list the exposed options + * @see https://github.com/amark/gun/wiki/Modules + */ + [key: string]: any + }> + type Saveable = Partial | string | number | boolean | null | ChainReference + type AckCallback = (ack: { err: Error; ok: any } | { err: undefined; ok: string }) => void + interface ChainReference { + /** + * Save data into gun, syncing it with your connected peers. + * + * * You cannot save primitive values at the root level. + * + * @param data You do not need to re-save the entire object every time, + * gun will automatically merge your data into what already exists as a "partial" update. + * + * * `undefined`, `NaN`, `Infinity`, `array`, will be rejected. + * * Traditional arrays are dangerous in real-time apps. Use `gun.set` instead. + * + * @param callback invoked on each acknowledgment + */ + put(data: DisallowArray, 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 + * (or that will be saved later). + * * Note that if you use .put at any depth after a get it first reads the data and then writes, merging the data as a partial update. + * @param callback You will usually be using gun.on or gun.once to actually retrieve your data, + * not this callback (it is intended for more low level control, for module and extensions). + * + * **Avoid use callback. The type in the document may be wrong.** + * + * **Here the type of callback respect to the actual behavior** + */ + get( + key: ArrayOf extends never ? K : never, + callback?: ( + /** the raw data. Internal node of gun. Will not typed here. */ + paramA: Record< + 'gun' | '$' | 'root' | 'id' | 'back' | 'on' | 'tag' | 'get' | 'soul' | 'ack' | 'put', + any + >, + /** the key, ID, or property name of the data. */ + paramB: Record<'off' | 'to' | 'next' | 'the' | 'on' | 'as' | 'back' | 'rid' | 'id', any> + ) => void + ): ChainReference + /** + * Change the configuration of the gun database instance. + * @param options The options argument is the same object you pass to the constructor. + * + * The options's properties replace those in the instance's configuration but options.peers are **added** to peers known to the gun instance. + * @returns No mention in the document, behavior as `ChainReference` + */ + opt(options: ConstructorOptions): ChainReference + /** + * Move up to the parent context on the chain. + * + * Every time a new chain is created, a reference to the old context is kept to go back to. + * @param amount The number of times you want to go back up the chain. + * `-1` or `Infinity` will take you to the root. + * @returns Impossible to determinate final type. You must cast it by yourself. + */ + back(amount?: number): ChainReference + + // Main API + /** + * Subscribe to updates and changes on a node or property in realtime. + * @param option Currently, the only option is to filter out old data, and just be given the changes. + * If you're listening to a node with 100 fields, and just one changes, + * you'll instead be passed a node with a single property representing that change rather than the full node every time. + * @param callback + * Once initially and whenever the property or node you're focused on changes, this callback is immediately fired with the data as it is at that point in time. + * + * Since gun streams data, the callback will probably be called multiple times as new chunk comes in. + * To remove a listener call .off() on the same property or node. + */ + on( + callback: (data: ArrayAsRecord, key: ReferenceKey) => void, + option?: { change: boolean } | boolean + ): ChainReference + /** + * Get the current data without subscribing to updates. Or `undefined` if it cannot be found. + * @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, + option?: { wait: number } + ): ChainReference + /** + * **.set does not means 'set data', it means a Mathematical Set** + * + * Add a unique item to an unordered list. + * `gun.set` works like a mathematical set, where each item in the list is unique. + * If the item is added twice, it will be merged. + * + * **This means only objects, for now, are supported.** + */ + set( + data: DataType extends Array + ? U extends { [key: string]: any; [key: number]: any } + ? ArrayOf + : never + : never, + callback?: AckCallback + ): ChainReference> + /** + * Map iterates over each property and item on a node, passing it down the chain, + * behaving like a forEach on your data. + * It also subscribes to every item as well and listens for newly inserted items. + */ + map( + callback?: (value: ArrayOf, key: DataType) => ArrayOf | undefined + ): ChainReference, ReferenceKey> + /** + * Undocumented, but extremely useful and mentioned in the document + * + * Remove **all** listener on this node. + */ + off(): void + + // Extended API + /** + * + * Path does the same thing as `.get` but has some conveniences built in. + * @deprecated This is not friendly with type system. + * + * **Warning**: This extension was removed from core, you probably shouldn't be using it! + * + * **Warning**: Not included by default! You must include it yourself via `require('gun/lib/path.js')` or + * ``! + */ + path?(path: string | string[]): ChainReference + /** + * Handle cases where data can't be found. + * + * **Warning**: Not included by default! You must include it yourself via `require('gun/lib/not.js')` or + * ``! + */ + not?(callback: (key: ReferenceKey) => void): ChainReference + /** + * Open behaves very similarly to gun.on, except it gives you the **full depth of a document** on every update. + * It also works with graphs, tables, or other data structures. Think of it as opening up a live connection to a document. + * + * **Warning**: Not included by default! You must include it yourself via `require('gun/lib/open.js')` or + * ``! + */ + open?(callback: (data: ArrayAsRecord) => void): ChainReference + /** + * Loads the full object once. It is the same as `open` but with the behavior of `once`. + * + * **Warning**: Not included by default! You must include it yourself via `require('gun/lib/load.js')` or + * ``! + */ + load?(callback: (data: ArrayAsRecord) => void): ChainReference + /** + * Returns a promise for you to use. + * + * **Warning**: Not included by default! You must include it yourself via `require('gun/lib/then.js')` or + * ``! + */ + then?, TResult2 = never>( + onfulfilled?: ((value: TResult1) => TResult1 | PromiseLike) | undefined | null + ): Promise + /** + * Returns a promise for you to use. + * + * **Warning**: Not included by default! You must include it yourself via `require('gun/lib/then.js')` or + * ``! + */ + promise?< + TResult1 = { put: ArrayAsRecord; key: ReferenceKey; gun: ChainReference }, + TResult2 = never + >( + onfulfilled?: ((value: TResult1) => TResult1 | PromiseLike) | undefined | null + ): Promise + /** + * bye lets you change data after that browser peer disconnects. + * This is useful for games and status messages, + * that if a player leaves you can remove them from the game or set a user's status to "away". + * + * **Warning**: Not included by default! You must include it yourself via `require('gun/lib/bye.js')` or + * ``! + */ + bye?(): { + put(data: DisallowArray>): void + } + /** + * Say you save some data, but want to do something with it later, like expire it or refresh it. + * Well, then `later` is for you! You could use this to easily implement a TTL or similar behavior. + * + * **Warning**: Not included by default! You must include it yourself via `require('gun/lib/later.js')` or + * ``! + */ + later?( + callback: ( + this: ChainReference, + data: ArrayAsRecord, + key: ReferenceKey + ) => void, + seconds: number + ): ChainReference + /** + * After you save some data in an unordered list, you may need to remove it. + * + * **Warning**: Not included by default! You must include it yourself via `require('gun/lib/unset.js')` or + * ``! + */ + unset?(data: ArrayOf): ChainReference + /** + * Subscribes to all future events that occur on the Timegraph and retrieve a specified number of old events + * + * **Warning**: The Timegraph extension isn't required by default, you would need to include at "gun/lib/time.js" + */ + time?( + callback: (data: ArrayOf, key: ReferenceKey, time: number) => void, + alsoReceiveNOldEvents?: number + ): ChainReference + /** Pushes data to a Timegraph with it's time set to Gun.state()'s time */ + time?(data: ArrayOf): void + } + + interface GunSEA { + // There is no the only content in the api document. + user: { + create(alias: string, passphrase: string, callback: (...args: any[]) => void): any + } + } + + interface Constructor { + /** + * @description + * no parameters creates a local datastore using the default persistence layer, either localStorage or Radisk. + */ + (): ChainReference & GunSEA + new (): ChainReference & GunSEA + + /** + * @param url + * passing a URL creates the above local datastore that also tries to sync with the URL. + * + * or you can pass in an array of URLs to sync with multiple peers. + */ + (url: string | string[]): ChainReference & GunSEA + new (url: string | string[]): ChainReference & GunSEA + (option: ConstructorOptions): ChainReference & GunSEA + new (option: ConstructorOptions): ChainReference & GunSEA + node: { + /** Returns true if data is a gun node, otherwise false. */ + is(anything: any): anything is ChainReference + /** Returns data's gun ID (instead of manually grabbing its metadata i.e. data["_"]["#"], which is faster but could change in the future) + * + * Returns undefined if data is not correct gun data. */ + soul(data: ChainReference): string + /** Returns a "gun-ified" variant of the json input by injecting a new gun ID into the metadata field. */ + ify(json: any): any + } + /** @see https://gun.eco/docs/SEA */ + SEA: any + } +} + +// Following modules does not export anything, but extends the gun prototype +declare module 'gun/lib/path.js' +declare module 'gun/lib/not.js' +declare module 'gun/lib/open.js' +declare module 'gun/lib/load.js' +declare module 'gun/lib/then.js' +declare module 'gun/lib/bye.js' +declare module 'gun/lib/later.js' +declare module 'gun/lib/unset.js' +declare module 'gun/lib/time.js' +declare const Gun: typeof import('gun') diff --git a/types/gun/tsconfig.json b/types/gun/tsconfig.json new file mode 100644 index 0000000000..7b7490d14c --- /dev/null +++ b/types/gun/tsconfig.json @@ -0,0 +1,22 @@ +{ + "compilerOptions": { + "module": "commonjs", + "lib": [ + "es6" + ], + "noImplicitAny": true, + "noImplicitThis": true, + "strictNullChecks": true, + "baseUrl": "../", + "typeRoots": [ + "../" + ], + "types": [], + "noEmit": true, + "forceConsistentCasingInFileNames": true + }, + "files": [ + "index.d.ts", + "gun-tests.ts" + ] +} diff --git a/types/gun/tslint.json b/types/gun/tslint.json new file mode 100644 index 0000000000..3db14f85ea --- /dev/null +++ b/types/gun/tslint.json @@ -0,0 +1 @@ +{ "extends": "dtslint/dt.json" }