Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
4d389f7
spike: Feasibility of generating Signify types from Python type hints…
Sotatek-Patrick-Vu Jun 27, 2025
56a35f5
add and use generated TS types
Sotatek-Patrick-Vu Jul 7, 2025
c003c1b
demo generated types for credentials().get()
Sotatek-Patrick-Vu Jul 8, 2025
8016535
update types for CredentialState
Sotatek-Patrick-Vu Jul 9, 2025
1679dc6
resolve review comments
Sotatek-Patrick-Vu Jul 10, 2025
98b637f
fix credentials types + add registries's types
Sotatek-Patrick-Vu Jul 11, 2025
508b1eb
Add more types for credentialing
Sotatek-Patrick-Vu Jul 14, 2025
0663bbe
resolve review comments
Sotatek-Patrick-Vu Jul 16, 2025
025ed27
fix errors/warning for swagger validator
Sotatek-Patrick-Vu Jul 16, 2025
63ed799
re-generate types
Sotatek-Patrick-Vu Jul 17, 2025
8b4c712
re-generate types
Sotatek-Patrick-Vu Jul 18, 2025
911318d
remove debug logs and add multiple overload signatures
Sotatek-Patrick-Vu Jul 18, 2025
84d5c3d
aiding.py type hints and auto-generated OpenAPI specs
Sotatek-Patrick-Vu Aug 7, 2025
82d077e
Generate types for Group Member
Sotatek-Patrick-Vu Aug 8, 2025
7c9bcd7
resolve review comments
Sotatek-Patrick-Vu Aug 14, 2025
9acdffe
fix mock data for aiding test
Sotatek-Patrick-Vu Aug 19, 2025
2756a67
feat: agenting.py type hints and OpenAPI specs #377
Sotatek-Patrick-Vu Aug 14, 2025
7a0e185
chore: remove any type for decrypter, encrypter and utils
Aug 6, 2025
cfd9227
feat(ui): replace any type
Aug 8, 2025
d74ba14
chore: remove cast string
Aug 8, 2025
50384d4
chore: update type
Aug 11, 2025
7656c87
chore: update review comments
Sotatek-DucVu Aug 26, 2025
05ad972
chore: remove any type in app directory
Aug 14, 2025
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 .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ dist
# IntelliJ Project Files
.idea
coverage/*

6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,12 @@ Typescript source files needs to be transpiled before running scripts or integra
npm run build
```

To build and generate TypeScript types from KERIA OpenAPI docs dynamically

```
SPEC_URL=http://localhost:3902/spec.yaml npm run build
```

- ready() must be called before library is useable. Example minimum viable client code.

```javascript
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@
],
"scripts": {
"start": "npm run build -- --watch",
"build": "tsc -p tsconfig.build.json && tsc -p tsconfig.json --noEmit",
"generate:types": "node scripts/generate-types.js",
"build": "npm run generate:types && tsc -p tsconfig.build.json && tsc -p tsconfig.json --noEmit",
"test": "vitest",
"prepare": "tsc -p tsconfig.build.json",
"test:integration": "vitest -c vitest.integration.ts",
Expand Down
36 changes: 36 additions & 0 deletions scripts/generate-types.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { execSync } from 'child_process';
import fs from 'fs';
import path from 'path';

const specUrl = process.env.SPEC_URL;
const outputFile = path.resolve('src/types/keria-api-schema.ts');

if (!specUrl) {
console.log('⚠️ Skipping OpenAPI type generation: SPEC_URL is not set.');
process.exit(0);
}

console.log(`📦 Generating types from ${specUrl}`);
execSync(`npx openapi-typescript "${specUrl}" --output ${outputFile} --enum`, {
stdio: 'inherit',
});

// Read the full file
const fullContent = fs.readFileSync(outputFile, 'utf8');

// Extract the `export interface components { ... }` block
const componentsMatch = fullContent.match(/export interface components \{[\s\S]+?\n\}/);

// Extract all `export enum ... { ... }` blocks
const enumMatches = [...fullContent.matchAll(/export enum [\w\d_]+ \{[\s\S]+?\n\}/g)];

if (!componentsMatch) {
console.error("❌ Could not find 'export interface components' block.");
process.exit(1);
}

// Combine the interface and enums
const enumsText = enumMatches.map(m => m[0]).join('\n\n');
const cleaned = `// AUTO-GENERATED: Only components and enums retained from OpenAPI schema\n\n${enumsText}\n\n${componentsMatch[0]}\n`;

fs.writeFileSync(outputFile, cleaned, 'utf8');
78 changes: 52 additions & 26 deletions src/keri/app/aiding.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,22 @@
import { Tier } from '../core/salter.ts';
import { Algos } from '../core/manager.ts';
import { incept, interact, reply, rotate } from '../core/eventing.ts';
import { components } from '../../types/keria-api-schema.ts';
import { b, Ilks, Serials, Vrsn_1_0 } from '../core/core.ts';
import { Tholder } from '../core/tholder.ts';
import {
EndRoleAddAttributes,
incept,
interact,
InteractEventSAD,
reply,
rotate,
SealSourceTriple,
} from '../core/eventing.ts';
import { parseRangeHeaders } from '../core/httping.ts';
import { IdentifierManagerFactory, SignResult } from '../core/keeping.ts';
import { HabState, KeyState } from '../core/keyState.ts';
import { Algos } from '../core/manager.ts';
import { MtrDex } from '../core/matter.ts';
import { Tier } from '../core/salter.ts';
import { Serder } from '../core/serder.ts';
import { parseRangeHeaders } from '../core/httping.ts';
import { IdentifierManagerFactory } from '../core/keeping.ts';
import { HabState } from '../core/keyState.ts';
import { Tholder } from '../core/tholder.ts';

/** Arguments required to create an identfier */
export interface CreateIdentiferArgs {
Expand All @@ -19,13 +28,13 @@ export interface CreateIdentiferArgs {
proxy?: string;
delpre?: string;
dcode?: string;
data?: any;
data?: Record<string, unknown>[];
algo?: Algos;
pre?: string;
states?: any[];
rstates?: any[];
prxs?: any[];
nxts?: any[];
states?: KeyState[];
rstates?: KeyState[];
prxs?: string[];
nxts?: string[];
mhab?: HabState;
keys?: string[];
ndigs?: string[];
Expand All @@ -34,7 +43,7 @@ export interface CreateIdentiferArgs {
ncount?: number;
tier?: Tier;
extern_type?: string;
extern?: any;
extern?: Record<string, unknown>;
}

/** Arguments required to rotate an identfier */
Expand All @@ -48,8 +57,8 @@ export interface RotateIdentifierArgs {
ncode?: string;
ncount?: number;
ncodes?: string[];
states?: any[];
rstates?: any[];
states?: KeyState[];
rstates?: KeyState[];
}

/**
Expand All @@ -73,6 +82,8 @@ export interface IdentifierInfo {
name: string;
}

export type GroupMember = components['schemas']['GroupMember'];

/** Identifier */
export class Identifier {
public client: IdentifierDeps;
Expand All @@ -92,7 +103,15 @@ export class Identifier {
* @param {number} [end=24] End index of list of notifications, defaults to 24
* @returns {Promise<any>} A promise to the list of managed identifiers
*/
async list(start: number = 0, end: number = 24): Promise<any> {
async list(
start: number = 0,
end: number = 24
): Promise<{
start: number;
end: number;
total: number;
aids: any;
}> {
const extraHeaders = new Headers();
extraHeaders.append('Range', `aids=${start}-${end}`);

Expand Down Expand Up @@ -249,7 +268,7 @@ export class Identifier {
}

const sigs = await keeper!.sign(b(serder.raw));
const jsondata: any = {
const jsondata: Record<string, unknown> = {
name: name,
icp: serder.sad,
sigs: sigs,
Expand Down Expand Up @@ -277,7 +296,10 @@ export class Identifier {
* @param {any} [data] Option data to be anchored in the interaction event
* @returns {Promise<EventResult>} A promise to the interaction event result
*/
async interact(name: string, data?: any): Promise<EventResult> {
async interact(
name: string,
data?: SealSourceTriple[] | SealSourceTriple
): Promise<EventResult> {
const { serder, sigs, jsondata } = await this.createInteract(
name,
data
Expand All @@ -293,16 +315,20 @@ export class Identifier {

async createInteract(
name: string,
data?: any
): Promise<{ serder: any; sigs: any; jsondata: any }> {
data?: SealSourceTriple[] | SealSourceTriple
): Promise<{
serder: Serder<InteractEventSAD>;
sigs: SignResult;
jsondata: Record<string, unknown>;
}> {
const hab = await this.get(name);
const pre: string = hab.prefix;

const state = hab.state;
const sn = parseInt(state.s, 16);
const dig = state.d;

data = Array.isArray(data) ? data : [data];
data = Array.isArray(data) ? data : ([data] as SealSourceTriple[]);

const serder = interact({
pre: pre,
Expand All @@ -315,7 +341,7 @@ export class Identifier {
const keeper = this.client!.manager!.get(hab);
const sigs = await keeper.sign(b(serder.raw));

const jsondata: any = {
const jsondata: Record<string, unknown> = {
ixn: serder.sad,
sigs: sigs,
};
Expand Down Expand Up @@ -399,7 +425,7 @@ export class Identifier {

const sigs = await keeper.sign(b(serder.raw));

const jsondata: any = {
const jsondata: Record<string, unknown> = {
rot: serder.sad,
sigs: sigs,
smids:
Expand Down Expand Up @@ -472,7 +498,7 @@ export class Identifier {
eid?: string,
stamp?: string
): Serder {
const data: any = {
const data: EndRoleAddAttributes = {
cid: pre,
role: role,
};
Expand All @@ -487,9 +513,9 @@ export class Identifier {
* Get the members of a group identifier
* @async
* @param {string} name - Name or alias of the identifier
* @returns {Promise<any>} - A promise to the list of members
* @returns {Promise<GroupMember>} - A promise to the list of members
*/
async members(name: string): Promise<any> {
async members(name: string): Promise<GroupMember> {
const res = await this.client.fetch(
'/identifiers/' + name + '/members',
'GET',
Expand Down
29 changes: 18 additions & 11 deletions src/keri/app/clienting.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import { components } from '../../types/keria-api-schema.ts';
import { Authenticater } from '../core/authing.ts';
import { HEADER_SIG_TIME } from '../core/httping.ts';
import { ExternalModule, IdentifierManagerFactory } from '../core/keeping.ts';
import { KeyState } from '../core/keyState.ts';
import { Tier } from '../core/salter.ts';

import { Identifier } from './aiding.ts';
import { Contacts, Challenges } from './contacting.ts';
import { Agent, Controller } from './controller.ts';
import { Oobis, Operations, KeyEvents, KeyStates, Config } from './coring.ts';
import { Challenges, Contacts } from './contacting.ts';
import { Agent, Controller, RotateAID } from './controller.ts';
import { Config, KeyEvents, KeyStates, Oobis, Operations } from './coring.ts';
import { Credentials, Ipex, Registries, Schemas } from './credentialing.ts';
import { Delegations } from './delegating.ts';
import { Escrows } from './escrowing.ts';
Expand All @@ -16,9 +18,14 @@ import { Notifications } from './notifying.ts';

const DEFAULT_BOOT_URL = 'http://localhost:3903';

class State {
agent: any | null;
controller: any | null;
// Export type outside the class
export type AgentResourceResult = components["schemas"]["AgentResourceResult"];

export type StateController = components["schemas"]["Controller"];

export class State {
agent: KeyState | null;
controller: StateController | null;
ridx: number;
pidx: number;

Expand Down Expand Up @@ -117,7 +124,7 @@ export class SignifyClient {
throw new Error(`agent does not exist for controller ${caid}`);
}

const data = await res.json();
const data = await res.json() as AgentResourceResult;
const state = new State();
state.agent = data.agent ?? {};
state.controller = data.controller ?? {};
Expand All @@ -141,13 +148,13 @@ export class SignifyClient {
);
this.controller.ridx = state.ridx !== undefined ? state.ridx : 0;
// Create agent representing the AID of KERIA cloud agent
this.agent = new Agent(state.agent);
this.agent = new Agent(state.agent!);
if (this.agent.anchor != this.controller.pre) {
throw Error(
'commitment to controller AID missing in agent inception event'
);
}
if (this.controller.serder.sad.s == 0) {
if (Number(this.controller.serder.sad.s) == 0) {
await this.approveDelegation();
}
this.manager = new IdentifierManagerFactory(
Expand All @@ -172,7 +179,7 @@ export class SignifyClient {
async fetch(
path: string,
method: string,
data: any,
data: unknown | null | undefined,
extraHeaders?: Headers
): Promise<Response> {
const headers = new Headers();
Expand Down Expand Up @@ -343,7 +350,7 @@ export class SignifyClient {
* @param {Array<string>} aids List of managed AIDs to be rotated
* @returns {Promise<Response>} A promise to the result of the rotation
*/
async rotate(nbran: string, aids: string[]): Promise<Response> {
async rotate(nbran: string, aids: RotateAID[]): Promise<Response> {
const data = this.controller.rotate(nbran, aids);
return await fetch(this.url + '/agent/' + this.controller.pre, {
method: 'PUT',
Expand Down
19 changes: 7 additions & 12 deletions src/keri/app/contacting.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
import { SignifyClient } from './clienting.ts';
import { Operation } from './coring.ts';
import { components } from '../../types/keria-api-schema.ts';

export interface Contact {
alias: string;
oobi: string;
id: string;
[key: string]: unknown;
}

export type Contact = components['schemas']['Contact'];

export interface ContactInfo {
[key: string]: unknown;
Expand Down Expand Up @@ -111,9 +108,7 @@ export class Contacts {
}
}

export interface Challenge {
words: string[];
}
export type Challenge = components['schemas']['Challenge'];

/**
* Challenges
Expand Down Expand Up @@ -189,15 +184,15 @@ export class Challenges {
* Mark challenge response as signed and accepted
* @param source Prefix of the identifier that was challenged
* @param said qb64 AID of exn message representing the signed response
* @returns {Promise<Response>} A promise to the result
* @returns {Promise<Operation<any>>} A promise to the result
*/
async responded(source: string, said: string): Promise<Response> {
async responded(source: string, said: string): Promise<Operation<any>> {
const path = `/challenges_verify/${source}`;
const method = 'PUT';
const data = {
said: said,
};
const res = await this.client.fetch(path, method, data);
return res;
return await res.json() as Operation<any>;
}
}
Loading