Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ logic lives now.
- `errors` — `VeroError` with stable, switchable `VeroErrorCode`s
- `network` — network config and HTTPS-enforcing endpoint validation
- `rpc` — RPC client with failover, health tracking, and origin-safe URL building
- `account` — Horizon account loader, data-entry helpers, and stroop-safe balances
- `contract` — typed read/write wrappers for the Vero core contract entrypoints

Wallet adapters and higher-level transaction builders are tracked as open
Expand Down
10 changes: 8 additions & 2 deletions scripts/smoke-test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,25 @@ const REQUIRED_EXPORTS = [
'MAINNET',
'retry',
'defaultIsRetryable',
'AccountLoader',
'AccountDataKey',
];

const DECLARATION_CHECKS = [
['cjs', 'index.d.ts', /export \* from '\.\/(types|errors|network|rpc|nonce)\/index\.js'/],
['cjs', 'index.d.ts', /export \* from '\.\/(types|errors|network|rpc|nonce|account)\/index\.js'/],
['cjs', 'index.d.ts', /export \* from '\.\/resilience\/backoff\.js'/],
['cjs', 'index.d.ts', "export * from './account/index.js'"],
['cjs', 'errors/index.d.ts', 'VeroError'],
['cjs', 'rpc/index.d.ts', 'RpcClient'],
['cjs', 'nonce/index.d.ts', 'NonceManager'],
['esm', 'index.d.ts', /export \* from '\.\/(types|errors|network|rpc|nonce)\/index\.js'/],
['cjs', 'account/index.d.ts', 'AccountLoader'],
['esm', 'index.d.ts', /export \* from '\.\/(types|errors|network|rpc|nonce|account)\/index\.js'/],
['esm', 'index.d.ts', /export \* from '\.\/resilience\/backoff\.js'/],
['esm', 'index.d.ts', "export * from './account/index.js'"],
['esm', 'errors/index.d.ts', 'VeroError'],
['esm', 'rpc/index.d.ts', 'RpcClient'],
['esm', 'nonce/index.d.ts', 'NonceManager'],
['esm', 'account/index.d.ts', 'AccountLoader'],
];

function assert(condition, message) {
Expand Down
77 changes: 77 additions & 0 deletions src/__tests__/index.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/**
* Root barrel regression (#67).
*
* Account tests imported `../index` (the account-local barrel), so the
* module could ship in `dist` and still be unreachable from
* `@vero-protocol/sdk`. This file imports the package root and asserts
* every module directory is re-exported, so a new module cannot be
* omitted the same way.
*/

import { readdirSync, readFileSync, statSync } from 'node:fs';
import { join } from 'node:path';
import {
AccountDataKey,
AccountLoader,
DataKey,
EventCursor,
NonceManager,
retry,
Role,
RpcClient,
validateUrl,
VeroError,
} from '../index';

/**
* `contract` re-exports `Task`, `Vote`, and `SubmitResult`, so
* `export * from './contract'` collides with `./types`. Resolving that
* is outside #67.
*/
const ROOT_BARREL_EXCEPTIONS = new Set(['contract']);

describe('package root barrel', () => {
it('exposes AccountLoader for import { AccountLoader } from "@vero-protocol/sdk"', () => {
expect(AccountLoader).toBeDefined();
expect(typeof AccountLoader).toBe('function');
});

it('keeps protocol DataKey distinct from AccountDataKey', () => {
expect(DataKey.reputation).toBe('vero_reputation');
expect(AccountDataKey.Reputation).toBe('reputation');
expect(DataKey).not.toBe(AccountDataKey);
});

it('re-exports every module directory', () => {
const srcDir = join(__dirname, '..');
const barrel = readFileSync(join(srcDir, 'index.ts'), 'utf8');
const moduleDirs = readdirSync(srcDir).filter((name) => {
const full = join(srcDir, name);
return statSync(full).isDirectory() && name !== '__tests__';
});

for (const name of ROOT_BARREL_EXCEPTIONS) {
expect(moduleDirs).toContain(name);
}

const missing = moduleDirs.filter((name) => {
if (ROOT_BARREL_EXCEPTIONS.has(name)) {
return false;
}
return !barrel.includes(`from './${name}`);
});

expect(missing).toEqual([]);
});

it('resolves a representative symbol from each public module', () => {
expect(Role).toBeDefined();
expect(VeroError).toBeDefined();
expect(validateUrl).toBeDefined();
expect(RpcClient).toBeDefined();
expect(NonceManager).toBeDefined();
expect(retry).toBeDefined();
expect(EventCursor).toBeDefined();
expect(AccountLoader).toBeDefined();
});
});
22 changes: 11 additions & 11 deletions src/account/__tests__/data.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
*/

import {
DataKey,
AccountDataKey,
readDataEntry,
getReputation,
getMetadata,
Expand Down Expand Up @@ -80,15 +80,15 @@ describe('Account Data', () => {
});

it('getReputation returns null for an entry failing the validity check (#70)', () => {
const data = { [DataKey.Reputation]: '!!!not base64!!!' };
const data = { [AccountDataKey.Reputation]: '!!!not base64!!!' };
expect(getReputation(data)).toBeNull();
});
});

describe('getReputation', () => {
it('should parse valid reputation data', () => {
const data = {
[DataKey.Reputation]: Buffer.from(
[AccountDataKey.Reputation]: Buffer.from(
JSON.stringify({ score: 250, metadata: { contributions: 5 } })
).toString('base64'),
};
Expand All @@ -106,15 +106,15 @@ describe('Account Data', () => {

it('should handle invalid reputation JSON gracefully', () => {
const data = {
[DataKey.Reputation]: Buffer.from('invalid json').toString('base64'),
[AccountDataKey.Reputation]: Buffer.from('invalid json').toString('base64'),
};
const result = getReputation(data);
expect(result).toBeNull();
});

it('should handle reputation with missing score', () => {
const data = {
[DataKey.Reputation]: Buffer.from(
[AccountDataKey.Reputation]: Buffer.from(
JSON.stringify({ metadata: { contributions: 5 } })
).toString('base64'),
};
Expand All @@ -136,7 +136,7 @@ describe('Account Data', () => {

for (const { score, tier } of testCases) {
const data = {
[DataKey.Reputation]: Buffer.from(
[AccountDataKey.Reputation]: Buffer.from(
JSON.stringify({ score })
).toString('base64'),
};
Expand All @@ -149,7 +149,7 @@ describe('Account Data', () => {
describe('getMetadata', () => {
it('should parse valid metadata', () => {
const data = {
[DataKey.Metadata]: Buffer.from(
[AccountDataKey.Metadata]: Buffer.from(
JSON.stringify({ name: 'test', version: 1 })
).toString('base64'),
};
Expand All @@ -167,15 +167,15 @@ describe('Account Data', () => {

it('should handle invalid metadata JSON', () => {
const data = {
[DataKey.Metadata]: Buffer.from('invalid json').toString('base64'),
[AccountDataKey.Metadata]: Buffer.from('invalid json').toString('base64'),
};
const result = getMetadata(data);
expect(result).toBeNull();
});

it('should handle malformed metadata base64', () => {
const data = {
[DataKey.Metadata]: 'not-valid-base64!',
[AccountDataKey.Metadata]: 'not-valid-base64!',
};
const result = getMetadata(data);
expect(result).toBeNull();
Expand All @@ -185,7 +185,7 @@ describe('Account Data', () => {
describe('getProfile', () => {
it('should parse valid profile', () => {
const data = {
[DataKey.Profile]: Buffer.from(
[AccountDataKey.Profile]: Buffer.from(
JSON.stringify({ name: 'testuser', email: 'test@example.com' })
).toString('base64'),
};
Expand All @@ -203,7 +203,7 @@ describe('Account Data', () => {

it('should handle invalid profile JSON', () => {
const data = {
[DataKey.Profile]: Buffer.from('invalid json').toString('base64'),
[AccountDataKey.Profile]: Buffer.from('invalid json').toString('base64'),
};
const result = getProfile(data);
expect(result).toBeNull();
Expand Down
2 changes: 1 addition & 1 deletion src/account/__tests__/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ describe('Account module index', () => {
// Check that all expected exports exist
expect(account.AccountLoader).toBeDefined();
expect(account.accountLoader).toBeDefined();
expect(account.DataKey).toBeDefined();
expect(account.AccountDataKey).toBeDefined();
expect(account.readDataEntry).toBeDefined();
expect(account.getReputation).toBeDefined();
expect(account.getMetadata).toBeDefined();
Expand Down
15 changes: 9 additions & 6 deletions src/account/data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,18 @@
import { AccountDataEntry, ReputationData } from './types.js';

/**
* Data key constants
* Horizon account data-entry names.
*
* Named `AccountDataKey` so it does not collide with the contract storage
* `DataKey` in `src/types` when both are re-exported from the package root.
*/
export const DataKey = {
export const AccountDataKey = {
Reputation: 'reputation',
Metadata: 'metadata',
Profile: 'profile',
} as const;

export type DataKey = typeof DataKey[keyof typeof DataKey];
export type AccountDataKey = typeof AccountDataKey[keyof typeof AccountDataKey];

/**
* Strip trailing base64 padding so two equivalent encodings compare equal.
Expand Down Expand Up @@ -70,7 +73,7 @@ export function readDataEntry(
* @returns The reputation data, or null if not found
*/
export function getReputation(data: Record<string, string>): ReputationData | null {
const entry = readDataEntry(data, DataKey.Reputation);
const entry = readDataEntry(data, AccountDataKey.Reputation);
if (!entry || !entry.isValid) {
return null;
}
Expand Down Expand Up @@ -105,7 +108,7 @@ function getReputationTier(score: number): 'bronze' | 'silver' | 'gold' | 'plati
* Read metadata from account data.
*/
export function getMetadata(data: Record<string, string>): Record<string, unknown> | null {
const entry = readDataEntry(data, DataKey.Metadata);
const entry = readDataEntry(data, AccountDataKey.Metadata);
if (!entry || !entry.isValid) {
return null;
}
Expand All @@ -121,7 +124,7 @@ export function getMetadata(data: Record<string, string>): Record<string, unknow
* Read profile data from account data.
*/
export function getProfile(data: Record<string, string>): Record<string, unknown> | null {
const entry = readDataEntry(data, DataKey.Profile);
const entry = readDataEntry(data, AccountDataKey.Profile);
if (!entry || !entry.isValid) {
return null;
}
Expand Down
2 changes: 1 addition & 1 deletion src/account/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export { AccountLoader, accountLoader } from './loader.js';

// Data
export {
DataKey,
AccountDataKey,
readDataEntry,
getReputation,
getMetadata,
Expand Down
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,4 @@ export * from './rpc/index.js';
export * from './nonce/index.js';
export * from './resilience/backoff.js';
export * from './events/index.js';
export * from './account/index.js';
Loading