Skip to content

Commit

Permalink
feat(providers): add utils for announcing
Browse files Browse the repository at this point in the history
  • Loading branch information
mrcnk committed Aug 2, 2024
1 parent d3549d0 commit 9618882
Show file tree
Hide file tree
Showing 26 changed files with 317 additions and 30 deletions.
Binary file modified bun.lockb
Binary file not shown.
34 changes: 7 additions & 27 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,40 +1,20 @@
{
"name": "mina-js",
"version": "0.0.1",
"private": true,
"scripts": {
"build": "tsup",
"test": "bun test",
"build": "bun run --filter '*' build",
"test": "bun run --filter '*' test",
"lint": "bunx biome check .",
"format": "bunx biome check . --write",
"format:unsafe": "bunx biome check . --write --unsafe"
},
"devDependencies": {
"@biomejs/biome": "1.8.3",
"@happy-dom/global-registrator": "^14.12.3",
"@tsconfig/bun": "1.0.7",
"@types/bun": "1.1.6",
"tsup": "8.2.3"
"tsup": "8.2.3",
"typescript": "5.5.4"
},
"dependencies": {
"@noble/curves": "1.4.2",
"@noble/hashes": "1.4.0",
"@scure/bip32": "1.4.0",
"@scure/bip39": "1.3.0",
"mina-signer": "3.0.7",
"zod": "3.23.8"
},
"peerDependencies": {
"typescript": "^5.0.0"
},
"exports": {
"./accounts": {
"types": "./dist/accounts/index.d.ts",
"import": "./dist/accounts/index.mjs",
"default": "./dist/accounts/index.js"
},
"./providers": {
"types": "./dist/providers/index.d.ts",
"import": "./dist/providers/index.mjs",
"default": "./dist/providers/index.js"
}
}
"workspaces": ["packages/*"]
}
27 changes: 27 additions & 0 deletions packages/accounts/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"name": "@mina-js/accounts",
"version": "0.0.1",
"module": "dist/index.js",
"types": "dist/index.d.ts",
"exports": {
".": {
"types": "./dist/index.d.ts",
"default": "./dist/index.cjs",
"import": "./dist/index.js"
}
},
"scripts": {
"build": "tsup",
"test": "bun test"
},
"dependencies": {
"@noble/curves": "1.4.2",
"@noble/hashes": "1.4.0",
"@scure/bip32": "1.4.0",
"@scure/bip39": "1.3.0",
"mina-signer": "3.0.7"
},
"peerDependencies": {
"typescript": "^5.0.0"
}
}
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
3 changes: 3 additions & 0 deletions packages/accounts/tsup.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import sharedConfig from "../shared/tsup.config";

export default sharedConfig;
2 changes: 2 additions & 0 deletions packages/connect/bunfig.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[test]
preload = "./happy-dom.ts"
3 changes: 3 additions & 0 deletions packages/connect/happy-dom.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { GlobalRegistrator } from "@happy-dom/global-registrator";

GlobalRegistrator.register();
24 changes: 24 additions & 0 deletions packages/connect/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"name": "@mina-js/connect",
"version": "0.0.1",
"module": "dist/index.js",
"types": "dist/index.d.ts",
"exports": {
".": {
"types": "./dist/index.d.ts",
"default": "./dist/index.cjs",
"import": "./dist/index.js"
}
},
"scripts": {
"build": "tsup",
"test": "bun test"
},
"dependencies": {
"@mina-js/providers": "workspace:*",
"zod": "3.23.8"
},
"peerDependencies": {
"typescript": "^5.0.0"
}
}
16 changes: 16 additions & 0 deletions packages/connect/src/__mocks__/provider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { mock } from "bun:test";
import type { MinaProviderDetail } from "@mina-js/providers";

export const mockedProvider: MinaProviderDetail = {
info: {
name: "Pallad",
icon: "data:image/",
rdns: "co.pallad",
slug: "pallad",
},
provider: {
request: mock(),
addListener: mock(),
removeListener: mock(),
},
};
28 changes: 28 additions & 0 deletions packages/connect/src/events.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { expect, it, mock } from "bun:test";
import type { MinaAnnounceProviderEvent } from "@mina-js/providers";
import { mockedProvider } from "./__mocks__/provider";
import { announceProvider, requestProviders } from "./events";

type Resolver = (value: unknown) => void;

it("announcec Mina Provider with window event", async () => {
const listener =
(resolve: Resolver) =>
({ detail }: MinaAnnounceProviderEvent) => {
expect(detail.info.slug).toEqual(mockedProvider.info.slug);
resolve(true);
};
await new Promise((resolve) => {
window.addEventListener("mina:announceProvider", listener(resolve));
announceProvider(mockedProvider);
window.removeEventListener("mina:announceProvider", listener(resolve));
});
});

it("requests Mina Provider with window event", () => {
const listener = mock();
window.addEventListener("mina:requestProvider", listener);
requestProviders(() => {});
expect(listener).toHaveBeenCalled();
window.removeEventListener("mina:requestProvider", listener);
});
51 changes: 51 additions & 0 deletions packages/connect/src/events.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import type {
MinaAnnounceProviderEvent,
MinaProviderDetail,
} from "@mina-js/providers";

export type AnnounceProviderReturnType = () => void;

/**
* Creates an event to announce a Mina provider and registers a handler to respond to subsequent requests.
*
* @param {MinaProviderDetail} detail - The details of the provider to announce.
* @returns {AnnounceProviderReturnType} A function to remove the event listener when it is no longer needed.
*/
export function announceProvider(
detail: MinaProviderDetail,
): AnnounceProviderReturnType {
const event: CustomEvent<MinaProviderDetail> = new CustomEvent(
"mina:announceProvider",
{ detail: Object.freeze(detail) },
);
window.dispatchEvent(event);
const handler = () => window.dispatchEvent(event);
window.addEventListener("mina:requestProvider", handler);
return () => window.removeEventListener("mina:requestProvider", handler);
}

export type RequestProvidersParameters = (
providerDetail: MinaProviderDetail,
) => void;
export type RequestProvidersReturnType = (() => void) | undefined;

/**
* Requests providers to be announced.
*
* This function adds an event listener for "mina:announceProvider" events and
* dispatches a custom event named "mina:requestProvider" to trigger the announce.
*
* @param listener A callback function that will be called when a provider is announced.
*/
export function requestProviders(
listener: RequestProvidersParameters,
): RequestProvidersReturnType {
if (typeof window === "undefined") return;
const handler = (event: MinaAnnounceProviderEvent) => listener(event.detail);

window.addEventListener("mina:announceProvider", handler);

window.dispatchEvent(new CustomEvent("mina:requestProvider"));

return () => window.removeEventListener("mina:announceProvider", handler);
}
18 changes: 18 additions & 0 deletions packages/connect/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import type {
MinaAnnounceProviderEvent,
MinaProviderClient,
MinaRequestProviderEvent,
} from "@mina-js/providers";

declare global {
interface WindowEventMap {
"mina:announceProvider": MinaAnnounceProviderEvent;
"mina:requestProvider": MinaRequestProviderEvent;
}
interface Window {
mina?: MinaProviderClient | undefined;
}
}

export * from "./store";
export * from "./events";
96 changes: 96 additions & 0 deletions packages/connect/src/store.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import type { MinaProviderDetail } from "@mina-js/providers";
import { requestProviders } from "./events";

export type Listener = (providerDetails: readonly MinaProviderDetail[]) => void;

export type Store = {
/**
* Clears the store, including all provider details.
*/
clear(): void;
/**
* Destroys the store, including all provider details and listeners.
*/
destroy(): void;
/**
* Finds a provider detail by its slug.
*/
findProvider(args: { slug: string }): MinaProviderDetail | undefined;
/**
* Returns all provider details that have been emitted.
*/
getProviders(): readonly MinaProviderDetail[];
/**
* Resets the store, and emits an event to request provider details.
*/
reset(): void;
/**
* Subscribes to emitted provider details.
*/
subscribe(
listener: Listener,
args?: { emitImmediately?: boolean | undefined } | undefined,
): () => void;

/**
* @internal
* Current state of listening listeners.
*/
_listeners(): Set<Listener>;
};

export function createStore(): Store {
const listeners: Set<Listener> = new Set();
let providerDetails: readonly MinaProviderDetail[] = [];

const request = () =>
requestProviders((providerDetail) => {
if (
providerDetails.some(
({ info }) => info.slug === providerDetail.info.slug,
)
)
return;

providerDetails = [...providerDetails, providerDetail];
for (const listener of listeners) {
listener(providerDetails);
}
});
let unwatch = request();

return {
_listeners() {
return listeners;
},
clear() {
for (const listener of listeners) {
listener([]);
}
providerDetails = [];
},
destroy() {
this.clear();
listeners.clear();
unwatch?.();
},
findProvider({ slug }) {
return providerDetails.find(
(providerDetail) => providerDetail.info.slug === slug,
);
},
getProviders() {
return providerDetails;
},
reset() {
this.clear();
unwatch?.();
unwatch = request();
},
subscribe(listener, { emitImmediately } = {}) {
listeners.add(listener);
if (emitImmediately) listener(providerDetails);
return () => listeners.delete(listener);
},
};
}
3 changes: 3 additions & 0 deletions packages/connect/tsup.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import sharedConfig from "../shared/tsup.config";

export default sharedConfig;
24 changes: 24 additions & 0 deletions packages/providers/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"name": "@mina-js/providers",
"version": "0.0.1",
"type": "module",
"module": "dist/index.js",
"types": "dist/index.d.ts",
"exports": {
".": {
"types": "./dist/index.d.ts",
"default": "./dist/index.cjs",
"import": "./dist/index.js"
}
},
"scripts": {
"build": "tsup",
"test": "bun test"
},
"dependencies": {
"zod": "3.23.8"
},
"peerDependencies": {
"typescript": "^5.0.0"
}
}
File renamed without changes.
2 changes: 1 addition & 1 deletion src/providers/types.ts → packages/providers/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export interface MinaAnnounceProviderEvent
type: "mina:announceProvider";
}

export interface EIP6963RequestProviderEvent extends Event {
export interface MinaRequestProviderEvent extends Event {
type: "mina:requestProvider";
}

Expand Down
File renamed without changes.
3 changes: 3 additions & 0 deletions packages/providers/tsup.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import sharedConfig from "../shared/tsup.config";

export default sharedConfig;
11 changes: 11 additions & 0 deletions packages/shared/tsup.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { defineConfig } from "tsup";

export default defineConfig({
entry: ["src/index.ts"],
outDir: "./dist",
format: ["esm", "cjs"],
sourcemap: true,
clean: true,
bundle: true,
dts: true,
});
1 change: 0 additions & 1 deletion src/index.ts

This file was deleted.

1 change: 0 additions & 1 deletion tsup.config.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { defineConfig } from "tsup";

export default defineConfig({
entry: ["src/accounts/index.ts", "src/providers/index.ts"],
outDir: "./dist",
format: ["esm", "cjs"],
sourcemap: true,
Expand Down

0 comments on commit 9618882

Please sign in to comment.