Skip to content

Commit b1a4e38

Browse files
committed
feat(lexicon): lexicon package in react sdk and readme
1 parent 9456cdf commit b1a4e38

File tree

9 files changed

+193
-43
lines changed

9 files changed

+193
-43
lines changed

README.md

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ A monorepo containing SDK packages for the Hypercerts protocol on ATProto.
66

77
| Package | Description | Status |
88
|---------|-------------|--------|
9+
| [`@hypercerts-org/lexicon`](./packages/lexicon) | ATProto lexicon definitions and generated TypeScript types for Hypercerts | ✅ Complete |
910
| [`@hypercerts-org/sdk-core`](./packages/sdk-core) | Framework-agnostic core SDK for ATProto authentication, repository operations, and lexicon management | ✅ Complete |
1011
| [`@hypercerts-org/sdk-react`](./packages/sdk-react) | React hooks and components for ATProto integration | ✅ Complete |
1112

@@ -44,9 +45,34 @@ The SDK provides a unified `Repository` that works with both server types, with
4445
│ - Repository (records, blobs, profiles) │
4546
│ - Domain services (hypercerts, organizations) │
4647
│ - Lexicon registry and validation │
48+
│ - Re-exports types from @hypercerts-org/lexicon │
49+
├─────────────────────────────────────────────────────────┤
50+
│ @hypercerts-org/lexicon │
51+
│ - ATProto lexicon JSON definitions │
52+
│ - Generated TypeScript types (via @atproto/lex-cli) │
53+
│ - Runtime validation (isRecord, validateRecord) │
4754
└─────────────────────────────────────────────────────────┘
4855
```
4956

57+
### Type Inheritance
58+
59+
Types flow from the lexicon package through sdk-core to sdk-react:
60+
61+
```
62+
@hypercerts-org/lexicon → Generated types with $type field
63+
↓ (OrgHypercertsClaim.Main, etc.)
64+
@hypercerts-org/sdk-core → Re-exports with friendly aliases
65+
↓ (HypercertClaim, HypercertRights, etc.)
66+
@hypercerts-org/sdk-react → Consumes types from sdk-core
67+
(Hypercert extends HypercertClaim)
68+
```
69+
70+
This architecture ensures:
71+
- **Single source of truth**: Lexicon definitions generate all types
72+
- **Consistent validation**: Runtime validation using `@atproto/lexicon`
73+
- **Clean API surface**: sdk-core provides user-friendly type aliases
74+
- **Simplified dependencies**: sdk-react only depends on sdk-core
75+
5076
## Getting Started
5177

5278
### Installation
@@ -215,13 +241,18 @@ Available tasks defined in `turbo.json`:
215241
```
216242
hypercerts-sdk/
217243
├── packages/
244+
│ ├── lexicon/ # Lexicon definitions package
245+
│ │ ├── lexicons/ # ATProto lexicon JSON files
246+
│ │ └── src/
247+
│ │ ├── types/ # Generated TypeScript types
248+
│ │ ├── lexicons.ts # Lexicon registry
249+
│ │ └── index.ts # Package exports
218250
│ ├── sdk-core/ # Core SDK package
219251
│ │ ├── src/
220252
│ │ │ ├── auth/ # OAuth client
221253
│ │ │ ├── core/ # SDK class, types, errors
222254
│ │ │ ├── repository/ # Repository operations
223-
│ │ │ ├── services/ # Domain services
224-
│ │ │ ├── lexicons/ # Hypercert lexicons
255+
│ │ │ ├── services/ # Domain services (re-exports lexicon types)
225256
│ │ │ └── storage/ # In-memory stores
226257
│ │ └── tests/
227258
│ └── sdk-react/ # React package

packages/lexicon/README.md

Lines changed: 87 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,110 @@
1-
# Hypercerts Lexicon Documentation
1+
# @hypercerts-org/lexicon
22

3-
This repository contains ATProto lexicon definitions for the
4-
Hypercerts protocol. Each lexicon defines a record type that can be
5-
stored on the ATProto network.
3+
ATProto lexicon definitions and generated TypeScript types for the Hypercerts protocol.
64

75
## Installation
86

9-
```
10-
npm i @hypercerts-org/lexicon
7+
```bash
8+
pnpm add @hypercerts-org/lexicon
119
```
1210

13-
## Usage
11+
## Features
1412

15-
```typescript
16-
import { AtpBaseClient } from '@hypercerts-org/lexicon'
17-
import type { HypercertClaim } from '@hypercerts-org/lexicon'
13+
- **Lexicon JSON Definitions**: ATProto-compliant lexicon schemas for all Hypercerts record types
14+
- **Generated TypeScript Types**: Strongly-typed interfaces generated via `@atproto/lex-cli`
15+
- **Runtime Validation**: `isRecord` and `validateRecord` functions for each type
16+
- **Lexicon Registry**: Pre-configured registry for schema validation
17+
18+
## Usage
1819

19-
const client = new AtpBaseClient({
20-
service: 'https://bsky.social',
21-
headers: { Authorization: `Bearer ${token}` }
22-
})
20+
### Using Types
2321

24-
const hypercert: HypercertClaim = {
22+
```typescript
23+
import type {
24+
OrgHypercertsClaim,
25+
OrgHypercertsCollection,
26+
OrgHypercertsClaimRights
27+
} from '@hypercerts-org/lexicon';
28+
29+
// Use the Main type for full records (includes $type)
30+
const claim: OrgHypercertsClaim.Main = {
2531
$type: 'org.hypercerts.claim',
2632
title: 'My Impact Work',
2733
shortDescription: 'Description here',
2834
workScope: 'Scope of work',
2935
workTimeFrameFrom: '2023-01-01T00:00:00Z',
3036
workTimeFrameTo: '2023-12-31T23:59:59Z',
3137
createdAt: new Date().toISOString()
38+
};
39+
```
40+
41+
### Runtime Validation
42+
43+
```typescript
44+
import { OrgHypercertsClaim } from '@hypercerts-org/lexicon';
45+
46+
// Check if a value matches the type
47+
if (OrgHypercertsClaim.isRecord(unknownValue)) {
48+
// unknownValue is now typed as OrgHypercertsClaim.Main
49+
console.log(unknownValue.title);
3250
}
3351

34-
await client.org.hypercerts.claim.create(
35-
{ repo: 'did:plc:example' },
36-
hypercert
37-
)
52+
// Validate with detailed error information
53+
const result = OrgHypercertsClaim.validateRecord(data);
54+
if (result.success) {
55+
// data is valid
56+
} else {
57+
console.error(result.error);
58+
}
3859
```
3960

61+
### Using Lexicon Constants
62+
63+
```typescript
64+
import { HYPERCERT_LEXICONS, HYPERCERT_COLLECTIONS } from '@hypercerts-org/lexicon';
65+
66+
// Collection names for ATProto operations
67+
console.log(HYPERCERT_COLLECTIONS.CLAIM); // 'org.hypercerts.claim'
68+
console.log(HYPERCERT_COLLECTIONS.COLLECTION); // 'org.hypercerts.collection'
69+
console.log(HYPERCERT_COLLECTIONS.RIGHTS); // 'org.hypercerts.claim.rights'
70+
71+
// Full lexicon documents for registry
72+
import { Lexicons } from '@atproto/lexicon';
73+
const registry = new Lexicons(HYPERCERT_LEXICONS);
74+
```
75+
76+
## Type Inheritance
77+
78+
This package is the source of truth for Hypercerts types. The SDK packages re-export these types with friendly aliases:
79+
80+
```
81+
@hypercerts-org/lexicon
82+
├── OrgHypercertsClaim.Main → sdk-core: HypercertClaim
83+
├── OrgHypercertsClaimRights.Main → sdk-core: HypercertRights
84+
├── OrgHypercertsCollection.Main → sdk-core: HypercertCollection
85+
└── ...etc
86+
```
87+
88+
**Recommendation**: Import types from `@hypercerts-org/sdk-core` for cleaner imports, unless you need direct access to validation functions.
89+
90+
## Development
91+
92+
### Regenerating Types
93+
94+
Types are generated from the lexicon JSON files using `@atproto/lex-cli`:
95+
96+
```bash
97+
pnpm generate # Regenerates src/types/ from lexicons/
98+
pnpm build # Build the package
99+
```
100+
101+
### Adding New Lexicons
102+
103+
1. Add lexicon JSON file to `lexicons/` directory
104+
2. Run `pnpm generate` to regenerate types
105+
3. Export new types from `src/index.ts`
106+
4. Update `HYPERCERT_COLLECTIONS` if adding a new collection
107+
40108
## Certified Lexicons
41109

42110
Certified lexicons are common/shared lexicons that can be used across multiple protocols.

packages/sdk-core/README.md

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,38 @@ pnpm add @hypercerts-org/sdk-core
1111
```
1212
@hypercerts-org/sdk-core
1313
├── / → Full SDK (createATProtoSDK, Repository, types, errors)
14-
├── /types → TypeScript types & Zod schemas
14+
├── /types → TypeScript types (re-exported from @hypercerts-org/lexicon)
1515
├── /errors → Error classes
16-
├── /lexicons → LexiconRegistry, HYPERCERT_LEXICONS
16+
├── /lexicons → LexiconRegistry, HYPERCERT_LEXICONS, HYPERCERT_COLLECTIONS
1717
├── /storage → InMemorySessionStore, InMemoryStateStore
1818
└── /testing → createMockSession, MockSessionStore
1919
```
2020

21+
## Type System
22+
23+
Types are generated from ATProto lexicon definitions in `@hypercerts-org/lexicon` and re-exported with friendly aliases:
24+
25+
| Lexicon Type | SDK Alias |
26+
|--------------|-----------|
27+
| `OrgHypercertsClaim.Main` | `HypercertClaim` |
28+
| `OrgHypercertsClaimRights.Main` | `HypercertRights` |
29+
| `OrgHypercertsClaimContribution.Main` | `HypercertContribution` |
30+
| `OrgHypercertsClaimMeasurement.Main` | `HypercertMeasurement` |
31+
| `OrgHypercertsClaimEvaluation.Main` | `HypercertEvaluation` |
32+
| `OrgHypercertsCollection.Main` | `HypercertCollection` |
33+
| `AppCertifiedLocation.Main` | `HypercertLocation` |
34+
35+
```typescript
36+
import type { HypercertClaim, HypercertRights } from "@hypercerts-org/sdk-core";
37+
38+
// For validation functions, import the namespaced types
39+
import { OrgHypercertsClaim } from "@hypercerts-org/sdk-core";
40+
41+
if (OrgHypercertsClaim.isRecord(data)) {
42+
// data is typed as HypercertClaim
43+
}
44+
```
45+
2146
## Usage
2247

2348
```typescript

packages/sdk-react/README.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,23 @@ pnpm add @hypercerts-org/sdk-react @hypercerts-org/sdk-core @tanstack/react-quer
1414
└── /testing → TestProvider, mocks, createMockATProtoReact
1515
```
1616

17+
## Type System
18+
19+
Types are inherited from `@hypercerts-org/sdk-core` (which re-exports from `@hypercerts-org/lexicon`):
20+
21+
```typescript
22+
import type {
23+
HypercertClaim, // Base claim type from lexicon
24+
HypercertCollection,
25+
CreateHypercertParams,
26+
Session
27+
} from "@hypercerts-org/sdk-react";
28+
29+
// The Hypercert type extends HypercertClaim with ATProto metadata
30+
import type { Hypercert } from "@hypercerts-org/sdk-react";
31+
// Hypercert = HypercertClaim & { uri: string; cid: string }
32+
```
33+
1734
## Usage
1835

1936
```typescript

packages/sdk-react/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ export type {
146146
Repository,
147147
Collaborator,
148148
CollaboratorPermissions,
149-
HypercertRecord,
149+
HypercertClaim,
150150
CreateHypercertParams,
151151
CreateHypercertResult,
152152
OrganizationInfo,

packages/sdk-react/src/testing/mocks.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -200,14 +200,15 @@ export function createMockHypercert(overrides: Partial<Hypercert> = {}): Hyperce
200200
const yearAgo = new Date(now.getFullYear() - 1, now.getMonth(), now.getDate());
201201

202202
return {
203-
uri: "at://did:plc:mock123/org.hypercerts.hypercert/mock-rkey",
203+
uri: "at://did:plc:mock123/org.hypercerts.claim/mock-rkey",
204204
cid: "bafyreimockhypercertcid123456789",
205-
$type: "org.hypercerts.hypercert",
205+
$type: "org.hypercerts.claim",
206206
title: "Mock Hypercert",
207+
shortDescription: "A mock hypercert",
207208
description: "A mock hypercert for testing purposes",
208209
workScope: "Testing",
209-
workTimeframeFrom: yearAgo.toISOString().split("T")[0],
210-
workTimeframeTo: now.toISOString().split("T")[0],
210+
workTimeFrameFrom: yearAgo.toISOString().split("T")[0],
211+
workTimeFrameTo: now.toISOString().split("T")[0],
211212
createdAt: now.toISOString(),
212213
...overrides,
213214
};

packages/sdk-react/src/types.ts

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import type {
1111
CollaboratorPermissions,
1212
CreateHypercertParams,
1313
CreateHypercertResult,
14-
HypercertRecord,
14+
HypercertClaim,
1515
OrganizationInfo,
1616
Repository,
1717
RepositoryRole,
@@ -298,7 +298,7 @@ export interface UseCollaboratorsResult {
298298
/**
299299
* Hypercert with metadata for display.
300300
*/
301-
export interface Hypercert extends HypercertRecord {
301+
export interface Hypercert extends HypercertClaim {
302302
uri: string;
303303
cid: string;
304304
}
@@ -413,8 +413,15 @@ export interface SyncMessage {
413413

414414
export type {
415415
ATProtoSDK,
416-
ATProtoSDKConfig, Collaborator,
417-
CollaboratorPermissions, CreateHypercertParams,
418-
CreateHypercertResult, HypercertRecord, OrganizationInfo, Repository, RepositoryRole, Session
416+
ATProtoSDKConfig,
417+
Collaborator,
418+
CollaboratorPermissions,
419+
CreateHypercertParams,
420+
CreateHypercertResult,
421+
HypercertClaim,
422+
OrganizationInfo,
423+
Repository,
424+
RepositoryRole,
425+
Session
419426
};
420427

packages/sdk-react/tests/testing/mocks.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ describe("createMockHypercert", () => {
166166

167167
expect(hc.title).toBe("Mock Hypercert");
168168
expect(hc.workScope).toBe("Testing");
169-
expect(hc.$type).toBe("org.hypercerts.hypercert");
169+
expect(hc.$type).toBe("org.hypercerts.claim");
170170
expect(hc.uri).toContain("at://");
171171
expect(hc.cid).toBeTruthy();
172172
});
@@ -184,8 +184,8 @@ describe("createMockHypercert", () => {
184184
it("should set valid date ranges", () => {
185185
const hc = createMockHypercert();
186186

187-
const from = new Date(hc.workTimeframeFrom!);
188-
const to = new Date(hc.workTimeframeTo!);
187+
const from = new Date(hc.workTimeFrameFrom);
188+
const to = new Date(hc.workTimeFrameTo);
189189

190190
expect(from).toBeInstanceOf(Date);
191191
expect(to).toBeInstanceOf(Date);

packages/sdk-react/tests/utils/fixtures.ts

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ export function createMockSession(overrides: Partial<Session & { did?: string; h
2525
* Create mock collaborator permissions.
2626
*/
2727
export function createMockPermissions(
28-
role: "viewer" | "editor" | "admin" | "owner" = "viewer"
28+
role: "viewer" | "editor" | "admin" | "owner" = "viewer",
2929
): CollaboratorPermissions {
3030
switch (role) {
3131
case "viewer":
@@ -81,14 +81,15 @@ export function createMockHypercert(overrides: Partial<Hypercert> = {}): Hyperce
8181
const yearAgo = new Date(now.getFullYear() - 1, now.getMonth(), now.getDate());
8282

8383
return {
84-
uri: "at://did:plc:test123/org.hypercerts.hypercert/test-rkey",
84+
uri: "at://did:plc:test123/org.hypercerts.claim/test-rkey",
8585
cid: "bafyreitesthypercertcid123456789",
86-
$type: "org.hypercerts.hypercert",
86+
$type: "org.hypercerts.claim",
8787
title: "Test Hypercert",
88+
shortDescription: "A test hypercert",
8889
description: "A test hypercert for testing purposes",
8990
workScope: "Testing",
90-
workTimeframeFrom: yearAgo.toISOString().split("T")[0],
91-
workTimeframeTo: now.toISOString().split("T")[0],
91+
workTimeFrameFrom: yearAgo.toISOString().split("T")[0],
92+
workTimeFrameTo: now.toISOString().split("T")[0],
9293
createdAt: now.toISOString(),
9394
...overrides,
9495
};
@@ -100,10 +101,10 @@ export function createMockHypercert(overrides: Partial<Hypercert> = {}): Hyperce
100101
export function createMockHypercertList(count: number = 5): Hypercert[] {
101102
return Array.from({ length: count }, (_, i) =>
102103
createMockHypercert({
103-
uri: `at://did:plc:test123/org.hypercerts.hypercert/test-rkey-${i}`,
104+
uri: `at://did:plc:test123/org.hypercerts.claim/test-rkey-${i}`,
104105
cid: `bafyreitesthypercertcid${i}`,
105106
title: `Test Hypercert ${i + 1}`,
106-
})
107+
}),
107108
);
108109
}
109110

0 commit comments

Comments
 (0)