Skip to content
Closed
Show file tree
Hide file tree
Changes from 11 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 .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
32 changes: 32 additions & 0 deletions scripts/generate-types.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
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}`, {
stdio: 'inherit',
});

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

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

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

// Add comment header
const cleaned = `// AUTO-GENERATED: Only components retained from OpenAPI schema\n\n${match[0]}\n`;

fs.writeFileSync(outputFile, cleaned, 'utf8');
12 changes: 6 additions & 6 deletions src/keri/app/coring.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ import { SignifyClient } from './clienting.ts';
import libsodium from 'libsodium-wrappers-sumo';
import { Salter } from '../core/salter.ts';
import { Matter, MtrDex } from '../core/matter.ts';
import { components } from '../../types/keria-api-schema.ts';

type OperationBase = components['schemas']['OperationBase'];

export function randomPasscode(): string {
const raw = libsodium.randombytes_buf(16);
Expand Down Expand Up @@ -61,16 +64,13 @@ export class Oobis {
}
}

export interface Operation<T = unknown> {
name: string;
export type Operation<T = unknown> = OperationBase & {
response?: T;
metadata?: {
depends?: Operation;
[property: string]: any;
};
done?: boolean;
error?: any;
response?: T;
}
};

export interface OperationsDeps {
fetch(
Expand Down
59 changes: 25 additions & 34 deletions src/keri/app/credentialing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@ import {
import { Operation } from './coring.ts';
import { HabState } from '../core/keyState.ts';

import { components } from '../../types/keria-api-schema.ts';

export type CredentialResult = components['schemas']['Credential'];
export type Registry = components['schemas']['Registry'];
export type Schema = components['schemas']['Schema'];

/** Types of credentials */
export class CredentialTypes {
static issued = 'issued';
Expand Down Expand Up @@ -229,25 +235,7 @@ export interface IpexAdmitArgs {
datetime?: string;
}

export type CredentialState = {
vn: [number, number];
i: string;
s: string;
d: string;
ri: string;
a: { s: number; d: string };
dt: string;
et: string;
} & (
| {
et: 'iss' | 'rev';
ra: Record<string, never>;
}
| {
et: 'bis' | 'brv';
ra: { i: string; s: string; d: string };
}
);
export type CredentialState = components['schemas']['CredentialState'];

/**
* Credentials
Expand All @@ -266,9 +254,9 @@ export class Credentials {
* List credentials
* @async
* @param {CredentialFilter} [kargs] Optional parameters to filter the credentials
* @returns {Promise<any>} A promise to the list of credentials
* @returns {Promise<CredentialResult[]>} A promise to the list of credentials
*/
async list(kargs: CredentialFilter = {}): Promise<any> {
async list(kargs: CredentialFilter = {}): Promise<CredentialResult[]> {
const path = `/credentials/query`;
const filtr = kargs.filter === undefined ? {} : kargs.filter;
const sort = kargs.sort === undefined ? [] : kargs.sort;
Expand All @@ -292,9 +280,12 @@ export class Credentials {
* @async
* @param {string} said - SAID of the credential
* @param {boolean} [includeCESR=false] - Optional flag export the credential in CESR format
* @returns {Promise<any>} A promise to the credential
* @returns {Promise<CredentialResult | string>} A promise to the credential
*/
async get(said: string, includeCESR: boolean = false): Promise<any> {
async get(
said: string,
includeCESR: boolean = false
): Promise<CredentialResult | string> {
const path = `/credentials/${said}`;
const method = 'GET';
const headers = includeCESR
Expand Down Expand Up @@ -423,7 +414,7 @@ export class Credentials {
* @param {string} name Name or alias of the identifier
* @param {string} said SAID of the credential
* @param {string} datetime date time of revocation
* @returns {Promise<any>} A promise to the long-running operation
* @returns {Promise<RevokeCredentialResult>} A promise to the long-running operation
*/
async revoke(
name: string,
Expand All @@ -437,7 +428,7 @@ export class Credentials {
const dt =
datetime ?? new Date().toISOString().replace('Z', '000+00:00');

const cred = await this.get(said);
const cred = (await this.get(said)) as CredentialResult;

// Create rev
const _rev = {
Expand Down Expand Up @@ -552,7 +543,7 @@ export class RegistryResult {
return this._sigs;
}

async op(): Promise<any> {
async op(): Promise<Registry> {
const res = await this.promise;
return await res.json();
}
Expand All @@ -575,9 +566,9 @@ export class Registries {
* List registries
* @async
* @param {string} name Name or alias of the identifier
* @returns {Promise<any>} A promise to the list of registries
* @returns {Promise<Registry[]>} A promise to the list of registries
*/
async list(name: string): Promise<any> {
async list(name: string): Promise<Registry[]> {
const path = `/identifiers/${name}/registries`;
const method = 'GET';
const res = await this.client.fetch(path, method, null);
Expand Down Expand Up @@ -680,13 +671,13 @@ export class Registries {
* @param {string} name Name or alias of the identifier
* @param {string} registryName Current registry name
* @param {string} newName New registry name
* @returns {Promise<any>} A promise to the registry record
* @returns {Promise<Registry>} A promise to the registry record
*/
async rename(
name: string,
registryName: string,
newName: string
): Promise<any> {
): Promise<Registry> {
const path = `/identifiers/${name}/registries/${registryName}`;
const method = 'PUT';
const data = {
Expand All @@ -713,9 +704,9 @@ export class Schemas {
* Get a schema
* @async
* @param {string} said SAID of the schema
* @returns {Promise<any>} A promise to the schema
* @returns {Promise<Schema>} A promise to the schema
*/
async get(said: string): Promise<any> {
async get(said: string): Promise<Schema> {
const path = `/schema/${said}`;
const method = 'GET';
const res = await this.client.fetch(path, method, null);
Expand All @@ -725,9 +716,9 @@ export class Schemas {
/**
* List schemas
* @async
* @returns {Promise<any>} A promise to the list of schemas
* @returns {Promise<Schema[]>} A promise to the list of schemas
*/
async list(): Promise<any> {
async list(): Promise<Schema[]> {
const path = `/schema`;
const method = 'GET';
const res = await this.client.fetch(path, method, null);
Expand Down
158 changes: 158 additions & 0 deletions src/types/keria-api-schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
// AUTO-GENERATED: Only components retained from OpenAPI schema

export interface components {
schemas: {
ACDCAttributes: {
dt?: string;
i?: string;
u?: string;
} & {
[key: string]: unknown;
};
ACDC: {
v: string;
d: string;
i: string;
s: string;
ri?: string;
a?: components["schemas"]["ACDCAttributes"];
u?: string;
e?: unknown[];
r?: unknown[];
};
IssEvt: {
v: string;
/** @enum {unknown} */
t: "iss" | "bis";
d: string;
i: string;
s: string;
ri: string;
dt: string;
};
Schema: {
$id: string;
$schema: string;
title: string;
description: string;
type: string;
credentialType: string;
version: string;
properties: {
[key: string]: unknown;
};
additionalProperties: boolean;
required: string[];
};
StatusAnchor: {
s: number;
d: string;
};
CredentialStatus: {
vn: number[];
i: string;
s: string;
d: string;
ri: string;
ra: {
[key: string]: unknown;
};
a: components["schemas"]["StatusAnchor"];
dt: string;
et: string;
};
Anchor: {
pre: string;
sn: number;
d: string;
};
Seal: {
s: string;
d: string;
i?: string;
};
ANC: {
v: string;
t: string;
d: string;
i: string;
s: string;
p: string;
di?: string;
a?: components["schemas"]["Seal"][];
};
Credential: {
sad: components["schemas"]["ACDC"];
atc: string;
iss: components["schemas"]["IssEvt"];
issatc: string;
pre: string;
schema: components["schemas"]["Schema"];
chains: {
[key: string]: unknown;
}[];
status: components["schemas"]["CredentialState"];
anchor: components["schemas"]["Anchor"];
anc: components["schemas"]["ANC"];
ancatc: string;
};
OperationStatus: {
code: number;
message: string;
details?: {
[key: string]: unknown;
} | null;
};
OperationBase: {
name: string;
error?: components["schemas"]["OperationStatus"];
done?: boolean;
};
EmptyDict: Record<string, never>;
CredentialStateIssOrRev: {
vn: unknown;
i: string;
s: string;
d: string;
ri: string;
a: components["schemas"]["Seal"];
dt: string;
/** @enum {unknown} */
et: "iss" | "rev";
ra: components["schemas"]["EmptyDict"];
};
RaFields: {
i: string;
s: string;
d: string;
};
CredentialStateBisOrBrv: {
vn: unknown;
i: string;
s: string;
d: string;
ri: string;
a: components["schemas"]["Seal"];
dt: string;
/** @enum {unknown} */
et: "bis" | "brv";
ra: components["schemas"]["RaFields"];
};
CredentialState: components["schemas"]["CredentialStateIssOrRev"] | components["schemas"]["CredentialStateBisOrBrv"];
Operation: components["schemas"]["OperationBase"] & {
metadata?: Record<string, never>;
response?: Record<string, never>;
};
Registry: {
name: string;
regk: string;
pre: string;
state: components["schemas"]["CredentialState"];
};
};
responses: never;
parameters: never;
requestBodies: never;
headers: never;
pathItems: never;
}
Loading
Loading