@nostrify/react 0.2.13 → 0.2.15

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.
Files changed (60) hide show
  1. package/.turbo/turbo-build.log +14 -20
  2. package/CHANGELOG.md +18 -0
  3. package/dist/NostrContext.d.ts +13 -0
  4. package/dist/NostrContext.d.ts.map +1 -0
  5. package/dist/NostrContext.js +7 -0
  6. package/dist/NostrContext.ts +17 -0
  7. package/dist/example/test-helpers.js +33 -0
  8. package/dist/example/test-helpers.ts +33 -0
  9. package/dist/example/useAuthor.js +31 -0
  10. package/dist/example/useAuthor.ts +38 -0
  11. package/dist/example/useCurrentUser.js +42 -0
  12. package/dist/example/useCurrentUser.ts +48 -0
  13. package/dist/example/useLoginActions.js +23 -0
  14. package/dist/example/useLoginActions.ts +22 -0
  15. package/dist/example/useSocialFeed.js +15 -0
  16. package/dist/example/useSocialFeed.ts +18 -0
  17. package/dist/example/vite-env.d.js +0 -0
  18. package/dist/example/vite-env.d.ts +1 -0
  19. package/dist/login/NLogin.d.ts +46 -0
  20. package/dist/login/NLogin.d.ts.map +1 -0
  21. package/dist/login/NLogin.js +73 -0
  22. package/dist/login/NLogin.ts +122 -0
  23. package/dist/login/NUser.d.ts +23 -0
  24. package/dist/login/NUser.d.ts.map +1 -0
  25. package/dist/login/NUser.js +41 -0
  26. package/dist/login/NUser.ts +58 -0
  27. package/dist/login/NostrLoginContext.d.ts +24 -0
  28. package/dist/login/NostrLoginContext.d.ts.map +1 -0
  29. package/dist/login/NostrLoginContext.js +5 -0
  30. package/dist/login/NostrLoginContext.ts +28 -0
  31. package/dist/login/NostrLoginProvider.d.ts +15 -0
  32. package/dist/login/NostrLoginProvider.d.ts.map +1 -0
  33. package/dist/login/NostrLoginProvider.js +17 -0
  34. package/dist/login/NostrLoginProvider.ts +34 -0
  35. package/dist/login/mod.d.ts +5 -0
  36. package/dist/login/mod.d.ts.map +1 -0
  37. package/dist/login/mod.js +10 -0
  38. package/dist/login/mod.ts +4 -0
  39. package/dist/login/nostrLoginReducer.d.ts +16 -0
  40. package/dist/login/nostrLoginReducer.d.ts.map +1 -0
  41. package/dist/login/nostrLoginReducer.js +28 -0
  42. package/dist/login/nostrLoginReducer.ts +42 -0
  43. package/dist/login/useNostrLogin.d.ts +3 -0
  44. package/dist/login/useNostrLogin.d.ts.map +1 -0
  45. package/dist/login/useNostrLogin.js +12 -0
  46. package/dist/login/useNostrLogin.ts +13 -0
  47. package/dist/login/useNostrLoginReducer.d.ts +4 -0
  48. package/dist/login/useNostrLoginReducer.d.ts.map +1 -0
  49. package/dist/login/useNostrLoginReducer.js +15 -0
  50. package/dist/login/useNostrLoginReducer.ts +20 -0
  51. package/dist/mod.d.ts +4 -0
  52. package/dist/mod.d.ts.map +1 -0
  53. package/dist/mod.js +7 -0
  54. package/dist/mod.ts +3 -0
  55. package/dist/tsconfig.tsbuildinfo +1 -0
  56. package/dist/useNostr.d.ts +3 -0
  57. package/dist/useNostr.d.ts.map +1 -0
  58. package/dist/useNostr.js +12 -0
  59. package/dist/useNostr.ts +13 -0
  60. package/package.json +5 -6
@@ -1,25 +1,19 @@
1
1
 
2
2
 
3
- > @nostrify/react@0.2.12 build /home/sid/repos/nostrify/packages/react
4
- > tsc -p tsconfig.json && node ../../esbuild.config.js --package ./
3
+ > @nostrify/react@0.2.13 build /home/sid/repos/nostrify/packages/react
4
+ > npx tsc -p tsconfig.json && node ../../esbuild.config.js --package ./
5
5
 
6
+ npm warn Unknown env config "verify-deps-before-run". This will stop working in the next major version of npm.
7
+ npm warn Unknown env config "_jsr-registry". This will stop working in the next major version of npm.
8
+ ⠙⠙Building with esbuild...
6
9
 
7
- dist/login/NLogin.js 2.0kb
8
- dist/example/test-helpers.js 1.2kb
9
- dist/example/useCurrentUser.js 1.1kb
10
- dist/login/NUser.js 978b 
11
- dist/example/useAuthor.js 829b 
12
- dist/login/nostrLoginReducer.js 743b 
13
- dist/login/NostrLoginProvider.js 669b 
14
- dist/example/useLoginActions.js 560b 
15
- dist/login/useNostrLoginReducer.js 486b 
16
- dist/example/useSocialFeed.js 370b 
17
- dist/login/useNostrLogin.js 321b 
18
- dist/useNostr.js 286b 
19
- dist/login/mod.js 257b 
20
- dist/mod.js 163b 
21
- dist/login/NostrLoginContext.js 120b 
22
- dist/NostrContext.js 114b 
23
- dist/example/vite-env.d.js 0b 
10
+ dist/login/NLogin.js 2.0kb
11
+ dist/example/test-helpers.js 1.2kb
12
+ dist/example/useCurrentUser.js 1.1kb
13
+ dist/login/NUser.js 978b 
14
+ dist/example/useAuthor.js 829b 
15
+ ...and 12 more output files...
24
16
 
25
- ⚡ Done in 39ms
17
+ ⚡ Done in 24ms
18
+ Copying source files...
19
+ Done!
package/CHANGELOG.md CHANGED
@@ -1,5 +1,23 @@
1
1
  # @nostrify/react
2
2
 
3
+ ## 0.2.15
4
+
5
+ ### Patch Changes
6
+
7
+ - export js files instead of TS files
8
+ - Updated dependencies
9
+ - @nostrify/nostrify@0.46.11
10
+ - @nostrify/types@0.36.7
11
+
12
+ ## 0.2.14
13
+
14
+ ### Patch Changes
15
+
16
+ - distribute ts files
17
+ - Updated dependencies
18
+ - @nostrify/nostrify@0.46.10
19
+ - @nostrify/types@0.36.6
20
+
3
21
  ## 0.2.13
4
22
 
5
23
  ### Patch Changes
@@ -0,0 +1,13 @@
1
+ import { type Context } from 'react';
2
+ import type { NPool } from '@nostrify/nostrify';
3
+ /** The shape of the Nostr context provided by NostrProvider. */
4
+ export interface NostrContextType {
5
+ /** The Nostr relay pool for querying and publishing events */
6
+ nostr: NPool;
7
+ }
8
+ /**
9
+ * React context for Nostr functionality.
10
+ * Use this with useContext or the useNostr hook to access Nostr features.
11
+ */
12
+ export declare const NostrContext: Context<NostrContextType | undefined>;
13
+ //# sourceMappingURL=NostrContext.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NostrContext.d.ts","sourceRoot":"","sources":["../NostrContext.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,OAAO,EAAiB,MAAM,OAAO,CAAC;AAEpD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAEhD,gEAAgE;AAChE,MAAM,WAAW,gBAAgB;IAC/B,8DAA8D;IAC9D,KAAK,EAAE,KAAK,CAAC;CACd;AAED;;;GAGG;AACH,eAAO,MAAM,YAAY,EAAE,OAAO,CAAC,gBAAgB,GAAG,SAAS,CAE9D,CAAC"}
@@ -0,0 +1,7 @@
1
+ import { createContext } from "react";
2
+ const NostrContext = createContext(
3
+ void 0
4
+ );
5
+ export {
6
+ NostrContext
7
+ };
@@ -0,0 +1,17 @@
1
+ import { type Context, createContext } from 'react';
2
+
3
+ import type { NPool } from '@nostrify/nostrify';
4
+
5
+ /** The shape of the Nostr context provided by NostrProvider. */
6
+ export interface NostrContextType {
7
+ /** The Nostr relay pool for querying and publishing events */
8
+ nostr: NPool;
9
+ }
10
+
11
+ /**
12
+ * React context for Nostr functionality.
13
+ * Use this with useContext or the useNostr hook to access Nostr features.
14
+ */
15
+ export const NostrContext: Context<NostrContextType | undefined> = createContext<NostrContextType | undefined>(
16
+ undefined,
17
+ );
@@ -0,0 +1,33 @@
1
+ import { PropertySymbol, Window } from "happy-dom";
2
+ function polyfillDOM() {
3
+ const window = new Window();
4
+ const document = window.document;
5
+ const browserWindow = document[PropertySymbol.window];
6
+ const setInnerHTML = (html) => document.documentElement.innerHTML = html;
7
+ const cancelAsync = () => window.happyDOM.abort();
8
+ Object.assign(globalThis, {
9
+ window,
10
+ document,
11
+ HTMLElement: browserWindow.HTMLElement,
12
+ Element: browserWindow.Element,
13
+ Node: browserWindow.Node,
14
+ navigator: browserWindow.navigator,
15
+ DocumentFragment: browserWindow.DocumentFragment,
16
+ DocumentType: browserWindow.DocumentType,
17
+ SVGElement: browserWindow.SVGElement,
18
+ Text: browserWindow.Text,
19
+ requestAnimationFrame: browserWindow.requestAnimationFrame,
20
+ cancelAnimationFrame: browserWindow.cancelAnimationFrame,
21
+ setTimeout: browserWindow.setTimeout,
22
+ clearTimeout: browserWindow.clearTimeout,
23
+ setInterval: browserWindow.setInterval,
24
+ clearInterval: browserWindow.clearInterval,
25
+ queueMicrotask: browserWindow.queueMicrotask,
26
+ AbortController: browserWindow.AbortController,
27
+ cancelAsync,
28
+ setInnerHTML
29
+ });
30
+ }
31
+ export {
32
+ polyfillDOM
33
+ };
@@ -0,0 +1,33 @@
1
+ import { PropertySymbol, Window } from 'happy-dom';
2
+
3
+ export function polyfillDOM(): void {
4
+ const window = new Window();
5
+ const document = window.document;
6
+ const browserWindow = document[PropertySymbol.window];
7
+
8
+ const setInnerHTML = (html: string) => document.documentElement.innerHTML = html;
9
+ const cancelAsync = () => window.happyDOM.abort();
10
+
11
+ Object.assign(globalThis, {
12
+ window,
13
+ document,
14
+ HTMLElement: browserWindow.HTMLElement,
15
+ Element: browserWindow.Element,
16
+ Node: browserWindow.Node,
17
+ navigator: browserWindow.navigator,
18
+ DocumentFragment: browserWindow.DocumentFragment,
19
+ DocumentType: browserWindow.DocumentType,
20
+ SVGElement: browserWindow.SVGElement,
21
+ Text: browserWindow.Text,
22
+ requestAnimationFrame: browserWindow.requestAnimationFrame,
23
+ cancelAnimationFrame: browserWindow.cancelAnimationFrame,
24
+ setTimeout: browserWindow.setTimeout,
25
+ clearTimeout: browserWindow.clearTimeout,
26
+ setInterval: browserWindow.setInterval,
27
+ clearInterval: browserWindow.clearInterval,
28
+ queueMicrotask: browserWindow.queueMicrotask,
29
+ AbortController: browserWindow.AbortController,
30
+ cancelAsync,
31
+ setInnerHTML,
32
+ });
33
+ }
@@ -0,0 +1,31 @@
1
+ import { NSchema as n } from "@nostrify/nostrify";
2
+ import { useSuspenseQuery } from "@tanstack/react-query";
3
+ import { useNostr } from "../useNostr.js";
4
+ function useAuthor(pubkey) {
5
+ const { nostr } = useNostr();
6
+ const { data } = useSuspenseQuery({
7
+ queryKey: ["author", pubkey ?? ""],
8
+ queryFn: async ({ signal }) => {
9
+ if (!pubkey) {
10
+ return {};
11
+ }
12
+ const [event] = await nostr.query(
13
+ [{ kinds: [0], authors: [pubkey], limit: 1 }],
14
+ { signal: AbortSignal.any([signal, AbortSignal.timeout(500)]) }
15
+ );
16
+ if (!event) {
17
+ return {};
18
+ }
19
+ try {
20
+ const metadata = n.json().pipe(n.metadata()).parse(event.content);
21
+ return { ...metadata, event };
22
+ } catch {
23
+ return { event };
24
+ }
25
+ }
26
+ });
27
+ return data;
28
+ }
29
+ export {
30
+ useAuthor
31
+ };
@@ -0,0 +1,38 @@
1
+ import { NSchema as n } from '@nostrify/nostrify';
2
+ import type { NostrEvent, NostrMetadata } from '@nostrify/types';
3
+ import { useSuspenseQuery } from '@tanstack/react-query';
4
+
5
+ import { useNostr } from '../useNostr.ts';
6
+
7
+ export function useAuthor(
8
+ pubkey: string | undefined,
9
+ ): NostrMetadata & { event?: NostrEvent } {
10
+ const { nostr } = useNostr();
11
+
12
+ const { data } = useSuspenseQuery<NostrMetadata & { event?: NostrEvent }>({
13
+ queryKey: ['author', pubkey ?? ''],
14
+ queryFn: async ({ signal }) => {
15
+ if (!pubkey) {
16
+ return {};
17
+ }
18
+
19
+ const [event] = await nostr.query(
20
+ [{ kinds: [0], authors: [pubkey!], limit: 1 }],
21
+ { signal: AbortSignal.any([signal, AbortSignal.timeout(500)]) },
22
+ );
23
+
24
+ if (!event) {
25
+ return {};
26
+ }
27
+
28
+ try {
29
+ const metadata = n.json().pipe(n.metadata()).parse(event.content);
30
+ return { ...metadata, event };
31
+ } catch {
32
+ return { event };
33
+ }
34
+ },
35
+ });
36
+
37
+ return data;
38
+ }
@@ -0,0 +1,42 @@
1
+ import { NUser, useNostrLogin } from "@nostrify/react/login";
2
+ import { useMemo } from "react";
3
+ import { useAuthor } from "./useAuthor.js";
4
+ import { useNostr } from "../useNostr.js";
5
+ function useCurrentUser() {
6
+ const { nostr } = useNostr();
7
+ const { logins } = useNostrLogin();
8
+ function loginToUser(login) {
9
+ switch (login.type) {
10
+ case "nsec":
11
+ return NUser.fromNsecLogin(login);
12
+ case "bunker":
13
+ return NUser.fromBunkerLogin(login, nostr);
14
+ case "extension":
15
+ return NUser.fromExtensionLogin(login);
16
+ default:
17
+ throw new Error(`Unsupported login type: ${login.type}`);
18
+ }
19
+ }
20
+ const users = useMemo(() => {
21
+ const users2 = [];
22
+ for (const login of logins) {
23
+ try {
24
+ const user2 = loginToUser(login);
25
+ users2.push(user2);
26
+ } catch (error) {
27
+ console.warn("Skipped invalid login", login.id, error);
28
+ }
29
+ }
30
+ return users2;
31
+ }, [logins, nostr]);
32
+ const user = users[0];
33
+ const metadata = useAuthor(user?.pubkey);
34
+ return {
35
+ user,
36
+ users,
37
+ metadata
38
+ };
39
+ }
40
+ export {
41
+ useCurrentUser
42
+ };
@@ -0,0 +1,48 @@
1
+ import { type NLoginType, NUser, useNostrLogin } from '@nostrify/react/login';
2
+ import { useMemo } from 'react';
3
+
4
+ import { useAuthor } from './useAuthor.ts';
5
+ import { useNostr } from '../useNostr.ts';
6
+
7
+ export function useCurrentUser() {
8
+ const { nostr } = useNostr();
9
+ const { logins } = useNostrLogin();
10
+
11
+ function loginToUser(login: NLoginType): NUser {
12
+ switch (login.type) {
13
+ case 'nsec':
14
+ return NUser.fromNsecLogin(login);
15
+ case 'bunker':
16
+ return NUser.fromBunkerLogin(login, nostr);
17
+ case 'extension':
18
+ return NUser.fromExtensionLogin(login);
19
+ default:
20
+ // Learn how to define other login types: https://nostrify.dev/react/logins#custom-login-types
21
+ throw new Error(`Unsupported login type: ${login.type}`);
22
+ }
23
+ }
24
+
25
+ const users = useMemo(() => {
26
+ const users: NUser[] = [];
27
+
28
+ for (const login of logins) {
29
+ try {
30
+ const user = loginToUser(login);
31
+ users.push(user);
32
+ } catch (error) {
33
+ console.warn('Skipped invalid login', login.id, error);
34
+ }
35
+ }
36
+
37
+ return users;
38
+ }, [logins, nostr]);
39
+
40
+ const user: NUser | undefined = users[0];
41
+ const metadata = useAuthor(user?.pubkey);
42
+
43
+ return {
44
+ user,
45
+ users,
46
+ metadata,
47
+ };
48
+ }
@@ -0,0 +1,23 @@
1
+ import { useNostr } from "@nostrify/react";
2
+ import { NLogin, useNostrLogin } from "@nostrify/react/login";
3
+ function useLoginActions() {
4
+ const { nostr } = useNostr();
5
+ const { addLogin } = useNostrLogin();
6
+ return {
7
+ nsec(nsec) {
8
+ const login = NLogin.fromNsec(nsec);
9
+ addLogin(login);
10
+ },
11
+ async bunker(uri) {
12
+ const login = await NLogin.fromBunker(uri, nostr);
13
+ addLogin(login);
14
+ },
15
+ async extension() {
16
+ const login = await NLogin.fromExtension();
17
+ addLogin(login);
18
+ }
19
+ };
20
+ }
21
+ export {
22
+ useLoginActions
23
+ };
@@ -0,0 +1,22 @@
1
+ import { useNostr } from '@nostrify/react';
2
+ import { NLogin, useNostrLogin } from '@nostrify/react/login';
3
+
4
+ export function useLoginActions() {
5
+ const { nostr } = useNostr();
6
+ const { addLogin } = useNostrLogin();
7
+
8
+ return {
9
+ nsec(nsec: string): void {
10
+ const login = NLogin.fromNsec(nsec);
11
+ addLogin(login);
12
+ },
13
+ async bunker(uri: string): Promise<void> {
14
+ const login = await NLogin.fromBunker(uri, nostr);
15
+ addLogin(login);
16
+ },
17
+ async extension(): Promise<void> {
18
+ const login = await NLogin.fromExtension();
19
+ addLogin(login);
20
+ },
21
+ };
22
+ }
@@ -0,0 +1,15 @@
1
+ import { useSuspenseQuery } from "@tanstack/react-query";
2
+ import { useNostr } from "../useNostr.js";
3
+ function useSocialFeed() {
4
+ const { nostr } = useNostr();
5
+ return useSuspenseQuery({
6
+ queryKey: ["social-feed"],
7
+ queryFn: () => nostr.query(
8
+ [{ kinds: [1], limit: 5 }],
9
+ { signal: AbortSignal.timeout(5e3) }
10
+ )
11
+ });
12
+ }
13
+ export {
14
+ useSocialFeed
15
+ };
@@ -0,0 +1,18 @@
1
+ import { useSuspenseQuery, type UseSuspenseQueryResult } from '@tanstack/react-query';
2
+
3
+ import { useNostr } from '../useNostr.ts';
4
+
5
+ import type { NostrEvent } from '@nostrify/types';
6
+
7
+ export function useSocialFeed(): UseSuspenseQueryResult<NostrEvent[]> {
8
+ const { nostr } = useNostr();
9
+
10
+ return useSuspenseQuery({
11
+ queryKey: ['social-feed'],
12
+ queryFn: () =>
13
+ nostr.query(
14
+ [{ kinds: [1], limit: 5 }],
15
+ { signal: AbortSignal.timeout(5000) },
16
+ ),
17
+ });
18
+ }
File without changes
@@ -0,0 +1 @@
1
+ /// <reference types="vite/client" />
@@ -0,0 +1,46 @@
1
+ import type { NPool } from '@nostrify/nostrify';
2
+ /** An object represeting any supported Nostr login credentials. */
3
+ export type NLoginType = NLoginNsec | NLoginBunker | NLoginExtension | NLoginOther;
4
+ /** Nostr login with nsec. */
5
+ export type NLoginNsec = NLoginBase<'nsec', {
6
+ nsec: `nsec1${string}`;
7
+ }>;
8
+ /** NIP-46 (aka remote signer) login. */
9
+ export type NLoginBunker = NLoginBase<'bunker', {
10
+ bunkerPubkey: string;
11
+ clientNsec: `nsec1${string}`;
12
+ relays: string[];
13
+ }>;
14
+ /** NIP-07 (browser extension) login. */
15
+ export type NLoginExtension = NLoginBase<'extension', null>;
16
+ /** Additional login types created by the library user. */
17
+ export type NLoginOther = NLoginBase<`x-${string}`, {
18
+ [key: string]: unknown;
19
+ }>;
20
+ /** Base properties shared by Nostr login objects. */
21
+ interface NLoginBase<T extends string, D> {
22
+ id: string;
23
+ type: T;
24
+ pubkey: string;
25
+ createdAt: string;
26
+ data: D;
27
+ }
28
+ /** Class representing Nostr login credentials. */
29
+ export declare class NLogin<T extends string, D> implements NLoginBase<T, D> {
30
+ id: string;
31
+ type: T;
32
+ pubkey: string;
33
+ createdAt: string;
34
+ data: D;
35
+ constructor(type: T, pubkey: string, data: D);
36
+ /** Create a login object from an nsec. */
37
+ static fromNsec(nsec: string): NLoginNsec;
38
+ /** Create a login object from a bunker URI. */
39
+ static fromBunker(uri: string, pool: NPool): Promise<NLoginBunker>;
40
+ /** Create a login object from a browser extension. */
41
+ static fromExtension(): Promise<NLoginExtension>;
42
+ /** Convert to a JSON-serializable object. */
43
+ toJSON(): NLoginBase<T, D>;
44
+ }
45
+ export {};
46
+ //# sourceMappingURL=NLogin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NLogin.d.ts","sourceRoot":"","sources":["../../login/NLogin.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAIhD,mEAAmE;AACnE,MAAM,MAAM,UAAU,GAAG,UAAU,GAAG,YAAY,GAAG,eAAe,GAAG,WAAW,CAAC;AAEnF,6BAA6B;AAC7B,MAAM,MAAM,UAAU,GAAG,UAAU,CAAC,MAAM,EAAE;IAC1C,IAAI,EAAE,QAAQ,MAAM,EAAE,CAAC;CACxB,CAAC,CAAC;AAEH,wCAAwC;AACxC,MAAM,MAAM,YAAY,GAAG,UAAU,CAAC,QAAQ,EAAE;IAC9C,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,QAAQ,MAAM,EAAE,CAAC;IAC7B,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB,CAAC,CAAC;AAEH,wCAAwC;AACxC,MAAM,MAAM,eAAe,GAAG,UAAU,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC;AAE5D,0DAA0D;AAC1D,MAAM,MAAM,WAAW,GAAG,UAAU,CAAC,KAAK,MAAM,EAAE,EAAE;IAClD,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB,CAAC,CAAC;AAEH,qDAAqD;AACrD,UAAU,UAAU,CAAC,CAAC,SAAS,MAAM,EAAE,CAAC;IACtC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,CAAC,CAAC;IACR,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,CAAC,CAAC;CACT;AAED,kDAAkD;AAClD,qBAAa,MAAM,CAAC,CAAC,SAAS,MAAM,EAAE,CAAC,CAAE,YAAW,UAAU,CAAC,CAAC,EAAE,CAAC,CAAC;IAC3D,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,CAAC,CAAC;IACR,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,CAAC,CAAC;gBAEH,IAAI,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;IAQ5C,0CAA0C;IAC1C,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU;IAezC,+CAA+C;WAClC,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,GAAG,OAAO,CAAC,YAAY,CAAC;IA4BxE,sDAAsD;WACzC,aAAa,IAAI,OAAO,CAAC,eAAe,CAAC;IAYtD,6CAA6C;IAC7C,MAAM,IAAI,UAAU,CAAC,CAAC,EAAE,CAAC,CAAC;CAS3B"}
@@ -0,0 +1,73 @@
1
+ import { BunkerURI, NConnectSigner, NSecSigner } from "@nostrify/nostrify";
2
+ import { generateSecretKey, getPublicKey, nip19 } from "nostr-tools";
3
+ class NLogin {
4
+ id;
5
+ type;
6
+ pubkey;
7
+ createdAt;
8
+ data;
9
+ constructor(type, pubkey, data) {
10
+ this.id = `${type}:${pubkey}`;
11
+ this.type = type;
12
+ this.pubkey = pubkey;
13
+ this.createdAt = (/* @__PURE__ */ new Date()).toISOString();
14
+ this.data = data;
15
+ }
16
+ /** Create a login object from an nsec. */
17
+ static fromNsec(nsec) {
18
+ const decoded = nip19.decode(nsec);
19
+ if (decoded.type !== "nsec") {
20
+ throw new Error("Invalid nsec");
21
+ }
22
+ const sk = decoded.data;
23
+ const pubkey = getPublicKey(sk);
24
+ return new NLogin("nsec", pubkey, {
25
+ nsec: nip19.nsecEncode(sk)
26
+ });
27
+ }
28
+ /** Create a login object from a bunker URI. */
29
+ static async fromBunker(uri, pool) {
30
+ const { pubkey: bunkerPubkey, secret, relays } = new BunkerURI(uri);
31
+ if (!relays.length) {
32
+ throw new Error("No relay provided");
33
+ }
34
+ const sk = generateSecretKey();
35
+ const nsec = nip19.nsecEncode(sk);
36
+ const clientSigner = new NSecSigner(sk);
37
+ const signer = new NConnectSigner({
38
+ relay: pool.group(relays),
39
+ pubkey: bunkerPubkey,
40
+ signer: clientSigner,
41
+ timeout: 6e4
42
+ });
43
+ await signer.connect(secret);
44
+ const pubkey = await signer.getPublicKey();
45
+ return new NLogin("bunker", pubkey, {
46
+ bunkerPubkey,
47
+ clientNsec: nsec,
48
+ relays
49
+ });
50
+ }
51
+ /** Create a login object from a browser extension. */
52
+ static async fromExtension() {
53
+ const windowSigner = globalThis.nostr;
54
+ if (!windowSigner) {
55
+ throw new Error("Nostr extension is not available");
56
+ }
57
+ const pubkey = await windowSigner.getPublicKey();
58
+ return new NLogin("extension", pubkey, null);
59
+ }
60
+ /** Convert to a JSON-serializable object. */
61
+ toJSON() {
62
+ return {
63
+ id: this.id,
64
+ type: this.type,
65
+ pubkey: this.pubkey,
66
+ createdAt: this.createdAt,
67
+ data: this.data
68
+ };
69
+ }
70
+ }
71
+ export {
72
+ NLogin
73
+ };
@@ -0,0 +1,122 @@
1
+ import { BunkerURI, NConnectSigner, NSecSigner } from '@nostrify/nostrify';
2
+ import type { NPool } from '@nostrify/nostrify';
3
+ import { generateSecretKey, getPublicKey, nip19 } from 'nostr-tools';
4
+ import type { NostrSigner } from '@nostrify/types';
5
+
6
+ /** An object represeting any supported Nostr login credentials. */
7
+ export type NLoginType = NLoginNsec | NLoginBunker | NLoginExtension | NLoginOther;
8
+
9
+ /** Nostr login with nsec. */
10
+ export type NLoginNsec = NLoginBase<'nsec', {
11
+ nsec: `nsec1${string}`;
12
+ }>;
13
+
14
+ /** NIP-46 (aka remote signer) login. */
15
+ export type NLoginBunker = NLoginBase<'bunker', {
16
+ bunkerPubkey: string;
17
+ clientNsec: `nsec1${string}`;
18
+ relays: string[];
19
+ }>;
20
+
21
+ /** NIP-07 (browser extension) login. */
22
+ export type NLoginExtension = NLoginBase<'extension', null>;
23
+
24
+ /** Additional login types created by the library user. */
25
+ export type NLoginOther = NLoginBase<`x-${string}`, {
26
+ [key: string]: unknown;
27
+ }>;
28
+
29
+ /** Base properties shared by Nostr login objects. */
30
+ interface NLoginBase<T extends string, D> {
31
+ id: string;
32
+ type: T;
33
+ pubkey: string;
34
+ createdAt: string;
35
+ data: D;
36
+ }
37
+
38
+ /** Class representing Nostr login credentials. */
39
+ export class NLogin<T extends string, D> implements NLoginBase<T, D> {
40
+ public id: string;
41
+ public type: T;
42
+ public pubkey: string;
43
+ public createdAt: string;
44
+ public data: D;
45
+
46
+ constructor(type: T, pubkey: string, data: D) {
47
+ this.id = `${type}:${pubkey}`;
48
+ this.type = type;
49
+ this.pubkey = pubkey;
50
+ this.createdAt = new Date().toISOString();
51
+ this.data = data;
52
+ }
53
+
54
+ /** Create a login object from an nsec. */
55
+ static fromNsec(nsec: string): NLoginNsec {
56
+ const decoded = nip19.decode(nsec);
57
+
58
+ if (decoded.type !== 'nsec') {
59
+ throw new Error('Invalid nsec');
60
+ }
61
+
62
+ const sk = decoded.data;
63
+ const pubkey = getPublicKey(sk);
64
+
65
+ return new NLogin('nsec', pubkey, {
66
+ nsec: nip19.nsecEncode(sk),
67
+ });
68
+ }
69
+
70
+ /** Create a login object from a bunker URI. */
71
+ static async fromBunker(uri: string, pool: NPool): Promise<NLoginBunker> {
72
+ const { pubkey: bunkerPubkey, secret, relays } = new BunkerURI(uri);
73
+
74
+ if (!relays.length) {
75
+ throw new Error('No relay provided');
76
+ }
77
+
78
+ const sk = generateSecretKey();
79
+ const nsec = nip19.nsecEncode(sk);
80
+ const clientSigner = new NSecSigner(sk);
81
+
82
+ const signer = new NConnectSigner({
83
+ relay: pool.group(relays),
84
+ pubkey: bunkerPubkey,
85
+ signer: clientSigner,
86
+ timeout: 60_000,
87
+ });
88
+
89
+ await signer.connect(secret);
90
+ const pubkey = await signer.getPublicKey();
91
+
92
+ return new NLogin('bunker', pubkey, {
93
+ bunkerPubkey,
94
+ clientNsec: nsec,
95
+ relays,
96
+ });
97
+ }
98
+
99
+ /** Create a login object from a browser extension. */
100
+ static async fromExtension(): Promise<NLoginExtension> {
101
+ const windowSigner = (globalThis as unknown as { nostr?: NostrSigner }).nostr;
102
+
103
+ if (!windowSigner) {
104
+ throw new Error('Nostr extension is not available');
105
+ }
106
+
107
+ const pubkey = await windowSigner.getPublicKey();
108
+
109
+ return new NLogin('extension', pubkey, null);
110
+ }
111
+
112
+ /** Convert to a JSON-serializable object. */
113
+ toJSON(): NLoginBase<T, D> {
114
+ return {
115
+ id: this.id,
116
+ type: this.type,
117
+ pubkey: this.pubkey,
118
+ createdAt: this.createdAt,
119
+ data: this.data,
120
+ };
121
+ }
122
+ }