@forgesworn/fold-kit 0.0.0-seed.0 → 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/README.md +154 -4
- package/dist/access.d.ts +34 -0
- package/dist/access.js +102 -0
- package/dist/channel.d.ts +27 -0
- package/dist/channel.js +38 -0
- package/dist/credential.d.ts +58 -0
- package/dist/credential.js +145 -0
- package/dist/display-name.d.ts +38 -0
- package/dist/display-name.js +78 -0
- package/dist/epoch.d.ts +387 -0
- package/dist/epoch.js +665 -0
- package/dist/hex.d.ts +28 -0
- package/dist/hex.js +32 -0
- package/dist/identity.d.ts +49 -0
- package/dist/identity.js +17 -0
- package/dist/index.d.ts +25 -0
- package/dist/index.js +17 -0
- package/dist/invitation.d.ts +217 -0
- package/dist/invitation.js +484 -0
- package/dist/kinds.d.ts +64 -0
- package/dist/kinds.js +64 -0
- package/dist/lane.d.ts +41 -0
- package/dist/lane.js +63 -0
- package/dist/link.d.ts +52 -0
- package/dist/link.js +107 -0
- package/dist/network-hints.d.ts +16 -0
- package/dist/network-hints.js +65 -0
- package/dist/persistent-invitation.d.ts +35 -0
- package/dist/persistent-invitation.js +118 -0
- package/dist/room.d.ts +53 -0
- package/dist/room.js +153 -0
- package/dist/transport.d.ts +24 -0
- package/dist/transport.js +1 -0
- package/dist/types.d.ts +62 -0
- package/dist/types.js +1 -0
- package/dist/verify.d.ts +26 -0
- package/dist/verify.js +46 -0
- package/llms.txt +100 -0
- package/package.json +76 -6
- package/vectors/circle-vectors.json +2173 -0
- package/vectors/kithmoot-vectors.json +1550 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { Event } from 'nostr-tools/pure';
|
|
2
|
+
import type { Filter } from 'nostr-tools/filter';
|
|
3
|
+
/**
|
|
4
|
+
* The relay transport seam the kit's codecs need: publish, subscribe, close.
|
|
5
|
+
*
|
|
6
|
+
* This is the kit's own declaration, not an import of KithMoot's
|
|
7
|
+
* `RelayTransport` (KithMoot's `src/relay-pool.ts`). The relay pool itself
|
|
8
|
+
* has not moved to the kit yet (see EXTRACTION.md): epoch, invitation and
|
|
9
|
+
* persistent-invitation only ever call `publish`/`subscribe`/`close` on the
|
|
10
|
+
* transport they are given, so the kit needs only that much of the shape,
|
|
11
|
+
* not KithMoot's richer `describe()`/`rekey()` extensions.
|
|
12
|
+
*
|
|
13
|
+
* A caller can pass KithMoot's `NostrRelayPool` (which implements a superset
|
|
14
|
+
* of this interface) straight through, or the kit's own `SimTransport`
|
|
15
|
+
* (`test/sim-relay.ts`), or any other implementation with the same three
|
|
16
|
+
* methods.
|
|
17
|
+
*/
|
|
18
|
+
export interface RelayTransport {
|
|
19
|
+
publish(event: Event): Promise<void>;
|
|
20
|
+
/** `via` is the relay URL that delivered the event, when the transport
|
|
21
|
+
* knows it. */
|
|
22
|
+
subscribe(filters: Filter[], onEvent: (event: Event, via?: string) => void, onEose?: () => void): () => void;
|
|
23
|
+
close(): void;
|
|
24
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type { Event } from 'nostr-tools/pure';
|
|
2
|
+
/** Circle-layer wire types: a subset of KithMoot's `src/types.ts`, copied
|
|
3
|
+
* byte-identical per declaration from the pinned source commit (see
|
|
4
|
+
* EXTRACTION.md). KithMoot's `types.ts` also defines roster, media and
|
|
5
|
+
* descriptor types that stay in KithMoot; those import `Reachability`
|
|
6
|
+
* (KithMoot's `src/reachability.ts`), which does not move here (see
|
|
7
|
+
* EXTRACTION.md's "What did not move" section). None of the types below
|
|
8
|
+
* need it. */
|
|
9
|
+
/** A device credential is an ordinary signed Nostr event, never published bare. */
|
|
10
|
+
export type DeviceCredential = Event;
|
|
11
|
+
/** Kindred tiers, closest first: family, mutual verified bond, one-way
|
|
12
|
+
* recognition, no requirement at all. */
|
|
13
|
+
export type AccessTier = 'open' | 'ken' | 'kith' | 'kin';
|
|
14
|
+
/** What a room requires of its agents. `owned-by-members`: an agent is
|
|
15
|
+
* admitted to the roster only with a verified ownership proof from a
|
|
16
|
+
* participant who is in the room. See `AgentOwnership`. */
|
|
17
|
+
export type AgentRule = 'owned-by-members';
|
|
18
|
+
/** A room's admission rule. `admitted` lists the issuer pubkeys the room
|
|
19
|
+
* trusts to vouch for guests; irrelevant when `tier` is `open`. `agents`
|
|
20
|
+
* is a separate rule about what an agent has to show; absent means
|
|
21
|
+
* nothing, which is how every room worked before it existed. */
|
|
22
|
+
export interface RoomPolicy {
|
|
23
|
+
tier: AccessTier;
|
|
24
|
+
admitted?: string[];
|
|
25
|
+
agents?: AgentRule;
|
|
26
|
+
/** When present, the only participants admitted, whatever the tier
|
|
27
|
+
* says. A direct message is a room whose policy lists two. See
|
|
28
|
+
* `docs/messages.md`. */
|
|
29
|
+
members?: string[];
|
|
30
|
+
/** A quiet room: its chat rides the kind 1059 firehose as dead drops to
|
|
31
|
+
* keys derived from the epoch key, on a cadence, so a relay cannot tell
|
|
32
|
+
* whether anything was said, by whom, or when. Only with `members`,
|
|
33
|
+
* because everybody derives every member's keys. Rides here so that
|
|
34
|
+
* everyone who joins agrees on how the room talks, exactly as they
|
|
35
|
+
* agree on who may enter. See `quiet.ts`. */
|
|
36
|
+
quiet?: true;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* A signed claim that `issuer` recognises `participant` at `tier` in `room`,
|
|
40
|
+
* until `expiresAt`. Never issued for `open`, since open needs no proof.
|
|
41
|
+
*
|
|
42
|
+
* `room` is what stops a proof being a bearer token: without it, one proof
|
|
43
|
+
* admits its holder to every room that happens to trust the same issuer, and
|
|
44
|
+
* an issuer who vouched for a guest at one moot has not vouched for them at
|
|
45
|
+
* all of them. The cost of that binding is stated plainly: a kindred proof is
|
|
46
|
+
* a room grant here, not a portable statement about a relationship, so an
|
|
47
|
+
* issuer mints one per room. In this protocol the party who vouches is the
|
|
48
|
+
* party who sent the join link, so it already knows the room id.
|
|
49
|
+
*/
|
|
50
|
+
export type KindredProof = {
|
|
51
|
+
tier: Exclude<AccessTier, 'open'>;
|
|
52
|
+
participant: string;
|
|
53
|
+
issuer: string;
|
|
54
|
+
/** The room id this proof is valid in. */
|
|
55
|
+
room: string;
|
|
56
|
+
/** 32 random bytes, hex, unique to this proof. Signed over, so two proofs
|
|
57
|
+
* on identical terms are still distinguishable - which is what a revocation
|
|
58
|
+
* list, or an audit, needs to name one of them. */
|
|
59
|
+
nonce: string;
|
|
60
|
+
sig: string;
|
|
61
|
+
expiresAt: number;
|
|
62
|
+
};
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/verify.d.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { type Event } from 'nostr-tools/pure';
|
|
2
|
+
/**
|
|
3
|
+
* Verify an event's signature, ignoring any cached verdict it arrives with.
|
|
4
|
+
*
|
|
5
|
+
* `verifyEvent` caches its result on the event object under `verifiedSymbol`
|
|
6
|
+
* and returns that cached value on any later call, without re-checking
|
|
7
|
+
* anything. The property is an own enumerable symbol, so it survives a
|
|
8
|
+
* shallow copy (`{ ...event }`, which is exactly how a caller might rebuild
|
|
9
|
+
* an event after editing a field) - and an attacker who hands us an object
|
|
10
|
+
* rather than a JSON string can simply set it themselves. Either way the
|
|
11
|
+
* signature check silently becomes a no-op.
|
|
12
|
+
*
|
|
13
|
+
* Every inbound event in this library goes through here rather than calling
|
|
14
|
+
* `verifyEvent` directly, so the defence cannot be forgotten by the next
|
|
15
|
+
* decoder somebody writes. The copy also keeps the caller's object
|
|
16
|
+
* unmodified: we never stamp a verdict onto something we did not sign.
|
|
17
|
+
*/
|
|
18
|
+
export declare function verifyEventUncached(event: Event): boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Keep relay fan-out from verifying the same signed event once per relay and
|
|
21
|
+
* replay. The cached value includes every signed field, so an object that
|
|
22
|
+
* reuses a known id while changing its body or signature is rejected. The
|
|
23
|
+
* fixed-size FIFO also prevents an untrusted relay from
|
|
24
|
+
* growing keeper memory without bound.
|
|
25
|
+
*/
|
|
26
|
+
export declare function boundedEventVerifier(limit?: number, verify?: (event: Event) => boolean): (event: Event) => boolean;
|
package/dist/verify.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { verifyEvent, verifiedSymbol } from 'nostr-tools/pure';
|
|
2
|
+
/**
|
|
3
|
+
* Verify an event's signature, ignoring any cached verdict it arrives with.
|
|
4
|
+
*
|
|
5
|
+
* `verifyEvent` caches its result on the event object under `verifiedSymbol`
|
|
6
|
+
* and returns that cached value on any later call, without re-checking
|
|
7
|
+
* anything. The property is an own enumerable symbol, so it survives a
|
|
8
|
+
* shallow copy (`{ ...event }`, which is exactly how a caller might rebuild
|
|
9
|
+
* an event after editing a field) - and an attacker who hands us an object
|
|
10
|
+
* rather than a JSON string can simply set it themselves. Either way the
|
|
11
|
+
* signature check silently becomes a no-op.
|
|
12
|
+
*
|
|
13
|
+
* Every inbound event in this library goes through here rather than calling
|
|
14
|
+
* `verifyEvent` directly, so the defence cannot be forgotten by the next
|
|
15
|
+
* decoder somebody writes. The copy also keeps the caller's object
|
|
16
|
+
* unmodified: we never stamp a verdict onto something we did not sign.
|
|
17
|
+
*/
|
|
18
|
+
export function verifyEventUncached(event) {
|
|
19
|
+
const unverified = { ...event };
|
|
20
|
+
delete unverified[verifiedSymbol];
|
|
21
|
+
return verifyEvent(unverified);
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Keep relay fan-out from verifying the same signed event once per relay and
|
|
25
|
+
* replay. The cached value includes every signed field, so an object that
|
|
26
|
+
* reuses a known id while changing its body or signature is rejected. The
|
|
27
|
+
* fixed-size FIFO also prevents an untrusted relay from
|
|
28
|
+
* growing keeper memory without bound.
|
|
29
|
+
*/
|
|
30
|
+
export function boundedEventVerifier(limit = 8_192, verify = verifyEventUncached) {
|
|
31
|
+
if (!Number.isSafeInteger(limit) || limit < 1)
|
|
32
|
+
throw new Error('Event verification cache size must be a positive integer');
|
|
33
|
+
const verified = new Map();
|
|
34
|
+
return event => {
|
|
35
|
+
const cached = verified.get(event.id);
|
|
36
|
+
if (cached !== undefined) {
|
|
37
|
+
return cached === JSON.stringify([event.id, event.pubkey, event.created_at, event.kind, event.tags, event.content, event.sig]);
|
|
38
|
+
}
|
|
39
|
+
if (!verify(event))
|
|
40
|
+
return false;
|
|
41
|
+
verified.set(event.id, JSON.stringify([event.id, event.pubkey, event.created_at, event.kind, event.tags, event.content, event.sig]));
|
|
42
|
+
if (verified.size > limit)
|
|
43
|
+
verified.delete(verified.keys().next().value);
|
|
44
|
+
return true;
|
|
45
|
+
};
|
|
46
|
+
}
|
package/llms.txt
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# @forgesworn/fold-kit
|
|
2
|
+
|
|
3
|
+
> Circle primitives for private Nostr groups nobody operates: a room key per epoch, sealed to each device that stays; link invitations as capabilities; member removal by rekey; device credentials so a person approves a device once and it signs from then on. Extracted from KithMoot, byte-identical on the wire. Framework-free: owns no storage, UI, relay defaults or identity keys, and does no relay I/O of its own.
|
|
4
|
+
|
|
5
|
+
ESM-only, Node.js 22.13+. Two entry points: `@forgesworn/fold-kit` (everything) and `@forgesworn/fold-kit/lane` (relay lane classification only).
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @forgesworn/fold-kit
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Not yet published to npm under this API. Until it is, pin an immutable Git commit:
|
|
14
|
+
|
|
15
|
+
```json
|
|
16
|
+
{
|
|
17
|
+
"dependencies": {
|
|
18
|
+
"@forgesworn/fold-kit": "github:forgesworn/fold-kit#<commit>"
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
### Peer dependencies
|
|
24
|
+
|
|
25
|
+
`nostr-tools` (`>=2.24.2 <3`), `@noble/hashes` (`^1.8.0`), `@noble/curves` (`^2.0.1`) - peers, not dependencies, so a consumer keeps a single copy of each. `@noble/hashes` is 1.x only: this kit's imports have no `.js` suffix and its `hkdf` calls pass a string `info`, both of which 2.x rejects.
|
|
26
|
+
|
|
27
|
+
## Exports
|
|
28
|
+
|
|
29
|
+
### Identity, verification, hex
|
|
30
|
+
|
|
31
|
+
- `hexEquals(a, b)` → `boolean` - constant-shape hex comparison
|
|
32
|
+
- `normaliseHex(hex)` → `string` - lower-cases and validates hex
|
|
33
|
+
- `verifyEventUncached(event)` → `boolean`
|
|
34
|
+
- `boundedEventVerifier(limit?, verify?)` → `(event) => boolean` - an LRU-bounded verifier
|
|
35
|
+
- `localIdentity(participantSk)` → `ParticipantIdentity` - a local signer, plus the `ParticipantIdentity`/`UnsignedEvent` seam every codec below takes as a dependency
|
|
36
|
+
|
|
37
|
+
### Wire kinds and types
|
|
38
|
+
|
|
39
|
+
- `KINDS` → `{ CREDENTIAL: 20460, CHAT: 1460, INVITATION_REQUEST: 20466, INVITATION_GRANT: 20467, INVITATION_RETIREMENT: 1461, GROUP_INVITATION: 1463, ROOM_REKEY: 1462, EPOCH_REQUEST: 20468, EPOCH_GRANT: 20469 }`
|
|
40
|
+
- `DeviceCredential`, `AccessTier` (`'open' | 'ken' | 'kith' | 'kin'`), `AgentRule`, `RoomPolicy`, `KindredProof` - link-layer wire types
|
|
41
|
+
- `RelayTransport` → `{ publish(event), subscribe(filters, onEvent, onEose?), close() }` - the transport seam every relay-facing codec takes; this kit declares its own interface rather than importing a relay pool implementation
|
|
42
|
+
|
|
43
|
+
### Rooms, credentials, access
|
|
44
|
+
|
|
45
|
+
- `generateRoomSecret()` → `Uint8Array` (32 bytes)
|
|
46
|
+
- `deriveRoom(secret)` → `{ roomId, roomKey }`
|
|
47
|
+
- `encodeJoinUrl(base, secret, relays, policy?)` / `decodeJoinUrl(url)` - legacy v1 join URL
|
|
48
|
+
- `parseRoomPolicy(raw)` → `RoomPolicy | undefined`
|
|
49
|
+
- `createDeviceCredential(opts)` → `Promise<DeviceCredential>` - room-scope or person-scope (`scope: 'person'`)
|
|
50
|
+
- `verifyDeviceCredential(event, opts)` → `VerifyResult` - pass `acceptPerson: true` to accept a person-scope credential where a room credential is expected
|
|
51
|
+
- `PERSON_CREDENTIAL_MAX_SECONDS` - 30-day cap on a person-scope credential
|
|
52
|
+
- `issueKindredProof(opts)` → `KindredProof`
|
|
53
|
+
- `evaluateAccess(policy, participant, proof, now, roomId)` → `{ admitted, reason }`
|
|
54
|
+
- `sanitiseDisplayName(raw)` → `string | undefined`, `MAX_DISPLAY_NAME_LENGTH`
|
|
55
|
+
- `safeRelayUrls(raw)`, `safeIceUrls(raw)`, `assertNetworkHintBounds(relays, ice)`, `MAX_RELAY_HINTS`, `MAX_ICE_HINTS`, `MAX_NETWORK_HINT_LENGTH`
|
|
56
|
+
|
|
57
|
+
### Links and invitations
|
|
58
|
+
|
|
59
|
+
- `parseRoomLink(url)` / `encodeRoomLink(base, link)` → `RoomLink` - the v1/v2/v3 link envelope
|
|
60
|
+
- `createRoomInvitation(persistent?)`, `roomInvitation(bearer, inviter, persistent?)`, `deriveInvitationId(invitation)`
|
|
61
|
+
- `encodeInvitationRequest(opts)` / `decodeInvitationRequest(event, opts)` - v2 live rendezvous request
|
|
62
|
+
- `encodeInvitationGrant(opts)` / `decodeRoomAdmissionGrant(event, opts)` - v2 grant, with a delegation chain
|
|
63
|
+
- `verifyInvitationDelegation(invitation, chain, now)`
|
|
64
|
+
- `encodeInvitationRetirement(opts)` / `decodeInvitationRetirementNotice(event, invitation)` - durable tombstone (kind 1461)
|
|
65
|
+
- `hostRoomInvitation(opts)` / `requestRoomAdmission(opts)` - the live desk, both sides
|
|
66
|
+
- `encodePersistentInvitation(opts)` / `decodePersistentInvitation(event, invitation)` - v3 stored group invitation (kind 1463)
|
|
67
|
+
- `requestPersistentRoomAdmission(opts)` - joins from stored admission alone, no live desk needed
|
|
68
|
+
|
|
69
|
+
### Epochs (removal by rekey)
|
|
70
|
+
|
|
71
|
+
- `deriveEpoch({ epoch, secret })` → `{ epoch, id, key }`, `generateEpochSecret()`
|
|
72
|
+
- `encodeRekeyEvent(opts)` / `peekRekeyEvent(event, opts)` / `decodeRekeyEvent(event, opts)` - the durable rekey notice (kind 1462), sealed per remaining device, naming who was removed
|
|
73
|
+
- `encodeEpochRequest(opts)` / `decodeEpochRequest(event, opts)` - a device asking the authority for the current epoch
|
|
74
|
+
- `encodeEpochGrant(opts)` / `decodeEpochGrant(event, opts)` - the authority's answer (secret, epoch-0, or a refusal `'removed' | 'closed'`)
|
|
75
|
+
- `epochRequestAdmission(opts)`, `deriveEpochRequestKey(roomKey)`
|
|
76
|
+
- `hostRoomEpoch(opts)` / `requestRoomEpoch(opts)` / `EpochRefusedError` - the live request/grant desk, both sides
|
|
77
|
+
- `canonicalAdmins(admins)` / `signAdmins(opts)` / `verifyAdmins(opts)` - the authority's signed admin list
|
|
78
|
+
- `canonicalChannels(channels)` / `signChannels(opts)` / `verifyChannels(opts)` - the authority's signed channel list; `CHANNEL_NAME`, `RESERVED_CHANNELS`
|
|
79
|
+
|
|
80
|
+
### Channel derivation
|
|
81
|
+
|
|
82
|
+
- `deriveChannel(roomId, roomKey, channel?)` → `{ id, key }` - a named channel's id/key, derived from the room key; `CHANNEL_ID_INFO`, `CHANNEL_KEY_INFO`, `MAX_CHANNEL_NAME_LENGTH`. The chat log and event codecs themselves stay in the consuming app.
|
|
83
|
+
|
|
84
|
+
### Lane (`@forgesworn/fold-kit/lane`)
|
|
85
|
+
|
|
86
|
+
- `laneOfRelayUrl(url, circle?)` / `laneOfRelays(urls, circle?)` → `Lane | undefined` (`'public' | 'sheltered' | 'direct'`)
|
|
87
|
+
- `isLane(value)`, `weakestLane(lanes)`, `isDowngrade(requested, actual)`
|
|
88
|
+
- `LANES`, `LANE_MEANING`, `LANE_LABEL`, `LANE_GLYPH`
|
|
89
|
+
|
|
90
|
+
## Design notes
|
|
91
|
+
|
|
92
|
+
- Every `kithmoot/v*` derivation label and message prefix is byte-identical to the source KithMoot commit this kit was extracted from (see EXTRACTION.md). These are protocol names now, not branding.
|
|
93
|
+
- No baked-in relay defaults, no storage, no DOM access. A caller supplies a `RelayTransport` implementation (a relay pool, or a test double).
|
|
94
|
+
- A room's authority - the party that may rekey and remove members - is the root inviter key pinned in the room's link, not a separate role system.
|
|
95
|
+
|
|
96
|
+
## Docs
|
|
97
|
+
|
|
98
|
+
- README.md - full API overview, peer dependency rationale, how this kit relates to `covey-kit` and `roost-kit`
|
|
99
|
+
- EXTRACTION.md - the source commit, exactly which modules and functions moved, and the two splits (`access.ts`, `chat.ts`) performed on the way
|
|
100
|
+
- AGENTS.md - build/test commands and repository conventions for an agent working in this repo
|
package/package.json
CHANGED
|
@@ -1,19 +1,89 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@forgesworn/fold-kit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.1.0",
|
|
4
4
|
"description": "Circle primitives for private Nostr groups nobody operates: epoch keys, link invites, removal by rekey, device credentials.",
|
|
5
5
|
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"default": "./dist/index.js"
|
|
12
|
+
},
|
|
13
|
+
"./lane": {
|
|
14
|
+
"types": "./dist/lane.d.ts",
|
|
15
|
+
"default": "./dist/lane.js"
|
|
16
|
+
},
|
|
17
|
+
"./package.json": "./package.json"
|
|
18
|
+
},
|
|
6
19
|
"files": [
|
|
20
|
+
"dist",
|
|
21
|
+
"vectors/kithmoot-vectors.json",
|
|
22
|
+
"vectors/circle-vectors.json",
|
|
7
23
|
"LICENSE",
|
|
8
|
-
"README.md"
|
|
24
|
+
"README.md",
|
|
25
|
+
"llms.txt"
|
|
9
26
|
],
|
|
10
|
-
"
|
|
11
|
-
"
|
|
12
|
-
"
|
|
27
|
+
"scripts": {
|
|
28
|
+
"build": "tsc -p tsconfig.json",
|
|
29
|
+
"test": "vitest run",
|
|
30
|
+
"test:watch": "vitest",
|
|
31
|
+
"typecheck": "tsc -p tsconfig.typecheck.json",
|
|
32
|
+
"diff-source": "node scripts/diff-source.mjs",
|
|
33
|
+
"bundle-check": "node scripts/bundle-check.mjs",
|
|
34
|
+
"tarball-smoke": "node scripts/tarball-smoke.mjs",
|
|
35
|
+
"vectors": "vitest run vectors/",
|
|
36
|
+
"check": "npm run typecheck && npm test && npm run diff-source",
|
|
37
|
+
"prepare": "npm run build",
|
|
38
|
+
"prepublishOnly": "npm run check && npm run bundle-check && npm run tarball-smoke"
|
|
13
39
|
},
|
|
40
|
+
"keywords": [
|
|
41
|
+
"nostr",
|
|
42
|
+
"e2ee",
|
|
43
|
+
"circle",
|
|
44
|
+
"group-chat",
|
|
45
|
+
"epoch-key",
|
|
46
|
+
"invite-link",
|
|
47
|
+
"device-credential"
|
|
48
|
+
],
|
|
14
49
|
"author": "forgesworn",
|
|
15
50
|
"license": "MIT",
|
|
51
|
+
"sideEffects": false,
|
|
52
|
+
"engines": {
|
|
53
|
+
"node": ">=22.13"
|
|
54
|
+
},
|
|
55
|
+
"peerDependencies": {
|
|
56
|
+
"nostr-tools": ">=2.24.2 <3",
|
|
57
|
+
"@noble/hashes": "^1.8.0",
|
|
58
|
+
"@noble/curves": "^2.0.1"
|
|
59
|
+
},
|
|
60
|
+
"dependencies": {
|
|
61
|
+
"@scure/base": "^2.0.0"
|
|
62
|
+
},
|
|
63
|
+
"devDependencies": {
|
|
64
|
+
"@noble/curves": "2.0.1",
|
|
65
|
+
"@noble/hashes": "^1.8.0",
|
|
66
|
+
"@types/node": "^22",
|
|
67
|
+
"esbuild": "^0.25.0",
|
|
68
|
+
"nostr-tools": "2.25.0",
|
|
69
|
+
"signet-login": "0.17.1",
|
|
70
|
+
"typescript": "^5.6.0",
|
|
71
|
+
"vitest": "^4.1.11"
|
|
72
|
+
},
|
|
16
73
|
"publishConfig": {
|
|
17
|
-
"access": "public"
|
|
74
|
+
"access": "public",
|
|
75
|
+
"provenance": true
|
|
76
|
+
},
|
|
77
|
+
"funding": {
|
|
78
|
+
"type": "lightning",
|
|
79
|
+
"url": "lightning:profusemeat89@walletofsatoshi.com"
|
|
80
|
+
},
|
|
81
|
+
"homepage": "https://github.com/forgesworn/fold-kit#readme",
|
|
82
|
+
"bugs": {
|
|
83
|
+
"url": "https://github.com/forgesworn/fold-kit/issues"
|
|
84
|
+
},
|
|
85
|
+
"repository": {
|
|
86
|
+
"type": "git",
|
|
87
|
+
"url": "git+https://github.com/forgesworn/fold-kit.git"
|
|
18
88
|
}
|
|
19
89
|
}
|