diff --git a/.changeset/fix-lexicon-fields.md b/.changeset/fix-lexicon-fields.md new file mode 100644 index 0000000..0af1c50 --- /dev/null +++ b/.changeset/fix-lexicon-fields.md @@ -0,0 +1,11 @@ +--- +"@hypercerts-org/sdk-core": patch +"@hypercerts-org/sdk-react": patch +--- + +fix(lexicon): correct field names and types to match lexicon schema + +- Fix `workTimeframeFrom/To` -> `workTimeFrameFrom/To` (capital 'F' in Frame) +- Make `shortDescription` required for hypercert claims per lexicon schema +- Update all interfaces, implementations, and tests to use correct field names +- Add comprehensive lexicon documentation to README diff --git a/packages/lexicon/package.json b/packages/lexicon/package.json index fca65e7..1a1c8f7 100644 --- a/packages/lexicon/package.json +++ b/packages/lexicon/package.json @@ -1,6 +1,6 @@ { "name": "@hypercerts-org/lexicon", - "version": "0.7.0", + "version": "0.8.0", "description": "ATProto lexicon definitions and TypeScript types for the Hypercerts protocol", "type": "module", "main": "dist/index.cjs", diff --git a/packages/sdk-core/README.md b/packages/sdk-core/README.md index bf49413..5febb27 100644 --- a/packages/sdk-core/README.md +++ b/packages/sdk-core/README.md @@ -33,11 +33,15 @@ const session = await sdk.callback(callbackParams); const repo = sdk.getRepository(session); const claim = await repo.hypercerts.create({ title: "Tree Planting Initiative 2025", - description: "Planted 1000 trees in the rainforest", - impact: { - scope: ["Environmental Conservation"], - work: { from: "2025-01-01", to: "2025-12-31" }, - contributors: ["did:plc:contributor1"], + shortDescription: "1000 trees planted in rainforest", + description: "Planted 1000 trees in the Amazon rainforest region", + workScope: "Environmental Conservation", + workTimeFrameFrom: "2025-01-01T00:00:00Z", + workTimeFrameTo: "2025-12-31T23:59:59Z", + rights: { + name: "Attribution", + type: "license", + description: "CC-BY-4.0", }, }); ``` @@ -607,21 +611,134 @@ await mockStore.set(mockSession); ### Working with Lexicons +The SDK exports lexicon types and validation utilities from the `@hypercerts-org/lexicon` package for direct record manipulation and validation. + +#### Lexicon Types + +All lexicon types are available with proper TypeScript support: + +```typescript +import type { + HypercertClaim, + HypercertRights, + HypercertContribution, + HypercertCollection, + HypercertMeasurement, + HypercertEvaluation, + HypercertLocation, + StrongRef, +} from "@hypercerts-org/sdk-core"; + +// Create a properly typed hypercert claim +const claim: HypercertClaim = { + $type: "org.hypercerts.claim", + title: "Community Garden Project", + shortDescription: "Urban garden serving 50 families", // REQUIRED + description: "Detailed description...", + workScope: "Food Security", + workTimeFrameFrom: "2024-01-01T00:00:00Z", // Note: Capital 'F' + workTimeFrameTo: "2024-12-31T00:00:00Z", // Note: Capital 'F' + rights: { uri: "at://...", cid: "..." }, + createdAt: new Date().toISOString(), +}; +``` + +#### Validation + +Validate records before creating them: + +```typescript +import { + validate, + OrgHypercertsClaim, + HYPERCERT_COLLECTIONS, +} from "@hypercerts-org/sdk-core"; + +// Validate using the lexicon package +const validation = validate( + HYPERCERT_COLLECTIONS.CLAIM, // "org.hypercerts.claim" + claim +); + +if (!validation.valid) { + console.error("Validation failed:", validation.error); +} + +// Or use type-specific validators +const isValid = OrgHypercertsClaim.isMain(claim); +const validationResult = OrgHypercertsClaim.validateMain(claim); +``` + +#### Using LexiconRegistry + +For repository-level validation: + ```typescript import { LexiconRegistry, HYPERCERT_LEXICONS, HYPERCERT_COLLECTIONS, -} from "@hypercerts-org/sdk-core/lexicons"; +} from "@hypercerts-org/sdk-core"; const registry = new LexiconRegistry(); registry.registerLexicons(HYPERCERT_LEXICONS); // Validate a record -const isValid = registry.validate( - "org.hypercerts.claim", +const result = registry.validate( + HYPERCERT_COLLECTIONS.CLAIM, claimData ); + +if (!result.valid) { + console.error("Invalid record:", result.error); +} +``` + +#### Creating Records with Proper Types + +```typescript +import type { + HypercertContribution, + StrongRef, +} from "@hypercerts-org/sdk-core"; +import { HYPERCERT_COLLECTIONS } from "@hypercerts-org/sdk-core"; + +// Create a contribution record +const contribution: HypercertContribution = { + $type: HYPERCERT_COLLECTIONS.CONTRIBUTION, + hypercert: { + uri: "at://did:plc:abc/org.hypercerts.claim/xyz", + cid: "bafyrei...", + } as StrongRef, + contributors: ["did:plc:contributor1", "did:plc:contributor2"], + role: "implementer", + description: "On-ground implementation team", + workTimeframeFrom: "2024-01-01T00:00:00Z", // Note: lowercase 'f' for contributions + workTimeframeTo: "2024-06-30T00:00:00Z", // Note: lowercase 'f' for contributions + createdAt: new Date().toISOString(), +}; + +// Use with repository operations +await repo.records.create({ + collection: HYPERCERT_COLLECTIONS.CONTRIBUTION, + record: contribution, +}); +``` + +#### Available Lexicon Collections + +```typescript +import { HYPERCERT_COLLECTIONS } from "@hypercerts-org/sdk-core"; + +// Collection NSIDs +HYPERCERT_COLLECTIONS.CLAIM // "org.hypercerts.claim" +HYPERCERT_COLLECTIONS.RIGHTS // "org.hypercerts.claim.rights" +HYPERCERT_COLLECTIONS.CONTRIBUTION // "org.hypercerts.claim.contribution" +HYPERCERT_COLLECTIONS.MEASUREMENT // "org.hypercerts.claim.measurement" +HYPERCERT_COLLECTIONS.EVALUATION // "org.hypercerts.claim.evaluation" +HYPERCERT_COLLECTIONS.EVIDENCE // "org.hypercerts.claim.evidence" +HYPERCERT_COLLECTIONS.COLLECTION // "org.hypercerts.collection" +HYPERCERT_COLLECTIONS.LOCATION // "app.certified.location" ``` ## Development diff --git a/packages/sdk-core/package.json b/packages/sdk-core/package.json index ad3ace5..8d93a68 100644 --- a/packages/sdk-core/package.json +++ b/packages/sdk-core/package.json @@ -1,6 +1,6 @@ { "name": "@hypercerts-org/sdk-core", - "version": "0.7.0", + "version": "0.8.0", "description": "Framework-agnostic ATProto SDK core for authentication, repository operations, and lexicon management", "main": "dist/index.cjs", "repository": { diff --git a/packages/sdk-core/src/repository/HypercertOperationsImpl.ts b/packages/sdk-core/src/repository/HypercertOperationsImpl.ts index 68d8b96..eeaa3e1 100644 --- a/packages/sdk-core/src/repository/HypercertOperationsImpl.ts +++ b/packages/sdk-core/src/repository/HypercertOperationsImpl.ts @@ -266,18 +266,15 @@ export class HypercertOperationsImpl extends EventEmitter imple const hypercertRecord: Record = { $type: HYPERCERT_COLLECTIONS.CLAIM, title: params.title, + shortDescription: params.shortDescription, description: params.description, workScope: params.workScope, - workTimeframeFrom: params.workTimeframeFrom, - workTimeframeTo: params.workTimeframeTo, + workTimeFrameFrom: params.workTimeFrameFrom, + workTimeFrameTo: params.workTimeFrameTo, rights: { uri: result.rightsUri, cid: result.rightsCid }, createdAt, }; - if (params.shortDescription) { - hypercertRecord.shortDescription = params.shortDescription; - } - if (imageBlobRef) { hypercertRecord.image = imageBlobRef; } diff --git a/packages/sdk-core/src/repository/interfaces.ts b/packages/sdk-core/src/repository/interfaces.ts index 4d00692..46fc274 100644 --- a/packages/sdk-core/src/repository/interfaces.ts +++ b/packages/sdk-core/src/repository/interfaces.ts @@ -117,14 +117,14 @@ export interface CreateHypercertParams { * * ISO 8601 date format (YYYY-MM-DD). */ - workTimeframeFrom: string; + workTimeFrameFrom: string; /** * End date of the work period. * * ISO 8601 date format (YYYY-MM-DD). */ - workTimeframeTo: string; + workTimeFrameTo: string; /** * Rights associated with the hypercert. @@ -153,11 +153,11 @@ export interface CreateHypercertParams { }; /** - * Optional short description for display in lists/cards. + * Short description for display in lists/cards. * - * Should be under 200 characters. + * Required field. Should be under 300 characters. */ - shortDescription?: string; + shortDescription: string; /** * Optional cover image for the hypercert. diff --git a/packages/sdk-core/tests/repository/HypercertOperationsImpl.test.ts b/packages/sdk-core/tests/repository/HypercertOperationsImpl.test.ts index a74e2af..2d602eb 100644 --- a/packages/sdk-core/tests/repository/HypercertOperationsImpl.test.ts +++ b/packages/sdk-core/tests/repository/HypercertOperationsImpl.test.ts @@ -38,10 +38,11 @@ describe("HypercertOperationsImpl", () => { describe("create", () => { const validParams = { title: "Test Hypercert", + shortDescription: "A test hypercert", description: "A test hypercert for unit testing", workScope: "Testing", - workTimeframeFrom: "2024-01-01T00:00:00Z", - workTimeframeTo: "2024-12-31T23:59:59Z", + workTimeFrameFrom: "2024-01-01T00:00:00Z", + workTimeFrameTo: "2024-12-31T23:59:59Z", rights: { name: "Attribution", type: "CC-BY-4.0", @@ -138,8 +139,8 @@ describe("HypercertOperationsImpl", () => { title: "Test", description: "Test", workScope: "Test", - workTimeframeFrom: "2024-01-01", - workTimeframeTo: "2024-12-31", + workTimeFrameFrom: "2024-01-01", + workTimeFrameTo: "2024-12-31", createdAt: "2024-01-01", }, }, @@ -178,8 +179,8 @@ describe("HypercertOperationsImpl", () => { title: "Test", description: "Test", workScope: "Test", - workTimeframeFrom: "2024-01-01", - workTimeframeTo: "2024-12-31", + workTimeFrameFrom: "2024-01-01", + workTimeFrameTo: "2024-12-31", createdAt: "2024-01-01", }, }, @@ -337,8 +338,8 @@ describe("HypercertOperationsImpl", () => { title: "Old Title", description: "Old description", workScope: "Old scope", - workTimeframeFrom: "2024-01-01", - workTimeframeTo: "2024-12-31", + workTimeFrameFrom: "2024-01-01", + workTimeFrameTo: "2024-12-31", createdAt: "2024-01-01T00:00:00Z", rights: { uri: "at://rights", cid: "rights-cid" }, }, @@ -403,8 +404,8 @@ describe("HypercertOperationsImpl", () => { title: "Title", description: "Desc", workScope: "Scope", - workTimeframeFrom: "2024-01-01", - workTimeframeTo: "2024-12-31", + workTimeFrameFrom: "2024-01-01", + workTimeFrameTo: "2024-12-31", createdAt: "2024-01-01", rights: { uri: "at://rights", cid: "cid" }, image: { ref: { $link: "old-image" } }, @@ -462,8 +463,8 @@ describe("HypercertOperationsImpl", () => { title: "Test", description: "Test", workScope: "Test", - workTimeframeFrom: "2024-01-01", - workTimeframeTo: "2024-12-31", + workTimeFrameFrom: "2024-01-01", + workTimeFrameTo: "2024-12-31", createdAt: "2024-01-01", }, }, @@ -519,8 +520,8 @@ describe("HypercertOperationsImpl", () => { title: "Test", description: "Test", workScope: "Test", - workTimeframeFrom: "2024-01-01", - workTimeframeTo: "2024-12-31", + workTimeFrameFrom: "2024-01-01", + workTimeFrameTo: "2024-12-31", createdAt: "2024-01-01", }, }, @@ -555,8 +556,8 @@ describe("HypercertOperationsImpl", () => { title: "Test", description: "Test", workScope: "Test", - workTimeframeFrom: "2024-01-01", - workTimeframeTo: "2024-12-31", + workTimeFrameFrom: "2024-01-01", + workTimeFrameTo: "2024-12-31", createdAt: "2024-01-01", }, }, diff --git a/packages/sdk-react/package.json b/packages/sdk-react/package.json index ce0c66c..852f59a 100644 --- a/packages/sdk-react/package.json +++ b/packages/sdk-react/package.json @@ -1,6 +1,6 @@ { "name": "@hypercerts-org/sdk-react", - "version": "0.7.0", + "version": "0.8.0", "description": "React hooks and components for the Hypercerts ATProto SDK", "type": "module", "main": "dist/index.cjs", diff --git a/packages/sdk-react/src/hooks/useHypercerts.ts b/packages/sdk-react/src/hooks/useHypercerts.ts index f06e11a..f7c6c12 100644 --- a/packages/sdk-react/src/hooks/useHypercerts.ts +++ b/packages/sdk-react/src/hooks/useHypercerts.ts @@ -53,10 +53,11 @@ const DEFAULT_LIMIT = 50; * const handleSubmit = async (data) => { * const result = await create({ * title: data.title, + * shortDescription: data.shortDesc, * description: data.description, * workScope: data.workScope, - * workTimeframeFrom: data.startDate, - * workTimeframeTo: data.endDate, + * workTimeFrameFrom: data.startDate, + * workTimeFrameTo: data.endDate, * rights: { * name: "CC-BY-4.0", * type: "license", diff --git a/packages/sdk-react/src/types.ts b/packages/sdk-react/src/types.ts index 1c45f74..6548857 100644 --- a/packages/sdk-react/src/types.ts +++ b/packages/sdk-react/src/types.ts @@ -5,17 +5,17 @@ */ import type { - ATProtoSDK, - ATProtoSDKConfig, - Collaborator, - CollaboratorPermissions, - CreateHypercertParams, - CreateHypercertResult, - HypercertClaim, - OrganizationInfo, - Repository, - RepositoryRole, - Session, + ATProtoSDK, + ATProtoSDKConfig, + Collaborator, + CollaboratorPermissions, + CreateHypercertParams, + CreateHypercertResult, + HypercertClaim, + OrganizationInfo, + Repository, + RepositoryRole, + Session, } from "@hypercerts-org/sdk-core"; import type { QueryClient } from "@tanstack/react-query"; import type { ReactNode } from "react"; @@ -311,8 +311,8 @@ export interface UpdateHypercertParams { description?: string; workScope?: string; impactScope?: string; - workTimeframeFrom?: string; - workTimeframeTo?: string; + workTimeFrameFrom?: string; + workTimeFrameTo?: string; impactTimeframeFrom?: string; impactTimeframeTo?: string; contributors?: string[]; @@ -412,16 +412,15 @@ export interface SyncMessage { // ───────────────────────────────────────────── export type { - ATProtoSDK, - ATProtoSDKConfig, - Collaborator, - CollaboratorPermissions, - CreateHypercertParams, - CreateHypercertResult, - HypercertClaim, - OrganizationInfo, - Repository, - RepositoryRole, - Session + ATProtoSDK, + ATProtoSDKConfig, + Collaborator, + CollaboratorPermissions, + CreateHypercertParams, + CreateHypercertResult, + HypercertClaim, + OrganizationInfo, + Repository, + RepositoryRole, + Session, }; - diff --git a/packages/sdk-react/tests/hooks/useHypercerts.test.tsx b/packages/sdk-react/tests/hooks/useHypercerts.test.tsx index 2550c25..2bf8da3 100644 --- a/packages/sdk-react/tests/hooks/useHypercerts.test.tsx +++ b/packages/sdk-react/tests/hooks/useHypercerts.test.tsx @@ -218,10 +218,11 @@ describe("useHypercerts", () => { const createParams = { title: "New Hypercert", - description: "A new hypercert", + shortDescription: "A new hypercert", + description: "A new hypercert for testing", workScope: "Testing", - workTimeframeFrom: "2024-01-01", - workTimeframeTo: "2024-12-31", + workTimeFrameFrom: "2024-01-01", + workTimeFrameTo: "2024-12-31", rights: { name: "CC-BY-4.0", type: "license" as const, description: "Attribution" }, }; @@ -271,10 +272,11 @@ describe("useHypercerts", () => { act(() => { result.current.create({ title: "New HC", + shortDescription: "Test HC", description: "Test description", workScope: "Test", - workTimeframeFrom: "2024-01-01", - workTimeframeTo: "2024-12-31", + workTimeFrameFrom: "2024-01-01", + workTimeFrameTo: "2024-12-31", rights: { name: "CC-BY-4.0", type: "license" as const, description: "Attribution" }, }); });