mirror of
https://github.com/profullstack/logicsrc.git
synced 2026-10-02 20:57:03 +00:00
Merge master; OpenFleet keeps its name, PR 177's rental descriptor becomes OpenRental
PR 177 shipped a second OpenFleet under the same slug: a published listing of OpenAgent profiles and ipfile swarms with CoinPay rental offers. Anthony ruled that OpenFleet means the human-controlled fleet and the record of who spawned whom, so the rental contract is renamed OpenRental (slug openrental, catalogs family, group noun listing): docs/openrental.md, logicsrc-openrental.schema.json with type logicsrc.openrental and scope kind listing, fixtures/openrental, createOpenRental and OpenRental* types in the SDK, the openrental validator kind. Minor bumps because an export moved: @logicsrc/schemas 0.2.0, @logicsrc/validators 0.2.0, @logicsrc/sdk 0.2.0. The discovery contract test now covers openfleet, openrental and openwall, one per family, and accepts a landing-page link in llms.txt. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RZV4zJ2pDZLNN3kE5jFCmV
This commit is contained in:
commit
68f3151699
35 changed files with 2068 additions and 14 deletions
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@logicsrc/validators",
|
||||
"version": "0.1.0",
|
||||
"version": "0.2.0",
|
||||
"description": "LogicSRC schema validation helpers.",
|
||||
"type": "module",
|
||||
"main": "./dist/index.js",
|
||||
|
|
@ -11,10 +11,10 @@
|
|||
"scripts": {
|
||||
"build": "tsc -p tsconfig.json",
|
||||
"test": "vitest run src",
|
||||
"validate:fixtures": "node dist/cli.js task ../schemas/fixtures/task.yaml && node dist/cli.js agent ../schemas/fixtures/agent.yaml && node dist/cli.js agentad-ad ../schemas/fixtures/agentad-ad.yaml && node dist/cli.js agentad-placement ../schemas/fixtures/agentad-placement.yaml && node dist/cli.js repo ../schemas/fixtures/repo.yaml && node dist/cli.js pull-request ../schemas/fixtures/pull-request.yaml && node dist/cli.js openontology-manifest ../schemas/fixtures/openontology/valid/manifest.json && node dist/cli.js openontology-claim ../schemas/fixtures/openontology/valid/claim-relationship.json && node dist/cli.js openontology-changeset ../schemas/fixtures/openontology/valid/changeset.json && node dist/cli.js opencontext-manifest ../schemas/fixtures/opencontext/valid/manifest.json && node dist/cli.js opencontext-object ../schemas/fixtures/opencontext/valid/object-policy.json && node dist/cli.js opencontext-bundle ../schemas/fixtures/opencontext/valid/bundle.json && node dist/cli.js opencontext-decision ../schemas/fixtures/opencontext/valid/decision.json && node dist/cli.js opencontext-role ../schemas/fixtures/opencontext/valid/role.json && node dist/cli.js opencontext-provenance ../schemas/fixtures/opencontext/valid/provenance.json && node dist/cli.js opencontext-diagnostic ../schemas/fixtures/opencontext/valid/diagnostic.json && node dist/cli.js opencontext-audit-event ../schemas/fixtures/opencontext/valid/audit-event.json"
|
||||
"validate:fixtures": "node dist/cli.js task ../schemas/fixtures/task.yaml && node dist/cli.js agent ../schemas/fixtures/agent.yaml && node dist/cli.js agentad-ad ../schemas/fixtures/agentad-ad.yaml && node dist/cli.js agentad-placement ../schemas/fixtures/agentad-placement.yaml && node dist/cli.js repo ../schemas/fixtures/repo.yaml && node dist/cli.js pull-request ../schemas/fixtures/pull-request.yaml && node dist/cli.js openontology-manifest ../schemas/fixtures/openontology/valid/manifest.json && node dist/cli.js openontology-claim ../schemas/fixtures/openontology/valid/claim-relationship.json && node dist/cli.js openontology-changeset ../schemas/fixtures/openontology/valid/changeset.json && node dist/cli.js opencontext-manifest ../schemas/fixtures/opencontext/valid/manifest.json && node dist/cli.js opencontext-object ../schemas/fixtures/opencontext/valid/object-policy.json && node dist/cli.js opencontext-bundle ../schemas/fixtures/opencontext/valid/bundle.json && node dist/cli.js opencontext-decision ../schemas/fixtures/opencontext/valid/decision.json && node dist/cli.js opencontext-role ../schemas/fixtures/opencontext/valid/role.json && node dist/cli.js opencontext-provenance ../schemas/fixtures/opencontext/valid/provenance.json && node dist/cli.js opencontext-diagnostic ../schemas/fixtures/opencontext/valid/diagnostic.json && node dist/cli.js opencontext-audit-event ../schemas/fixtures/opencontext/valid/audit-event.json && node dist/cli.js openrental ../schemas/fixtures/openrental/mixed.json && node dist/cli.js openwall-message ../schemas/fixtures/openwall/broadcast.json && node dist/cli.js openwall-message ../schemas/fixtures/openwall/direct.json && node dist/cli.js openwall-message ../schemas/fixtures/openwall/announcement.json && node dist/cli.js openwall-receipt ../schemas/fixtures/openwall/receipt-accepted.json && node dist/cli.js openwall-receipt ../schemas/fixtures/openwall/receipt-retrying.json"
|
||||
},
|
||||
"dependencies": {
|
||||
"@logicsrc/schemas": "^0.1.0",
|
||||
"@logicsrc/schemas": "^0.2.0",
|
||||
"ajv": "^8.17.1",
|
||||
"ajv-formats": "^3.0.1",
|
||||
"yaml": "^2.8.1"
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import * as addFormatsModule from "ajv-formats";
|
|||
import type { ErrorObject } from "ajv";
|
||||
import { parse } from "yaml";
|
||||
import { isSchemaKind, schemas, type SchemaKind } from "./schemas.js";
|
||||
import { validateOpenRentalReferences } from "./openrental.js";
|
||||
|
||||
type CompiledSchema = { (data: unknown): boolean; errors?: ErrorObject[] | null };
|
||||
|
||||
|
|
@ -61,6 +62,10 @@ export function validate(kind: SchemaKind, data: unknown): ValidationResult {
|
|||
const ok = validateDocument(data);
|
||||
|
||||
if (ok) {
|
||||
if (kind === "openrental") {
|
||||
const errors = validateOpenRentalReferences(data);
|
||||
if (errors.length) return { ok: false, kind, errors };
|
||||
}
|
||||
return { ok: true, kind, data };
|
||||
}
|
||||
|
||||
|
|
|
|||
141
packages/validators/src/openrental.test.ts
Normal file
141
packages/validators/src/openrental.test.ts
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
import { readFileSync } from "node:fs";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { assertSchemaKind, createValidator, schemas, validate } from "./index.js";
|
||||
|
||||
function fixture() {
|
||||
return JSON.parse(readFileSync(new URL("../../schemas/fixtures/openrental/mixed.json", import.meta.url), "utf8"));
|
||||
}
|
||||
|
||||
function errorAt(data: unknown, keyword: string, path: string) {
|
||||
const result = validate("openrental", data);
|
||||
expect(result.ok).toBe(false);
|
||||
if (!result.ok) expect(result.errors).toContainEqual(expect.objectContaining({ keyword, instancePath: path }));
|
||||
}
|
||||
|
||||
describe("OpenRental", () => {
|
||||
it("registers a publicly exported schema and validates the mixed fixture", () => {
|
||||
expect(assertSchemaKind("openrental")).toBe("openrental");
|
||||
expect(validate("openrental", fixture()).ok).toBe(true);
|
||||
const ajv = createValidator();
|
||||
ajv.addSchema(schemas.agent);
|
||||
expect(ajv.compile(schemas.openrental)(fixture())).toBe(true);
|
||||
});
|
||||
|
||||
it("validates the complete descriptor in the public specification", () => {
|
||||
const doc = readFileSync(new URL("../../../docs/openrental.md", import.meta.url), "utf8");
|
||||
const example = doc.match(/```json\n([\s\S]*?)\n```/);
|
||||
expect(example).not.toBeNull();
|
||||
expect(validate("openrental", JSON.parse(example![1])).ok).toBe(true);
|
||||
});
|
||||
|
||||
it.each(["openagent", "openswarm"])("accepts a listing containing only %s members", (kind) => {
|
||||
const listing = fixture();
|
||||
listing.members = listing.members.filter((member: { kind: string }) => member.kind === kind);
|
||||
// A whole-listing rate applies to either single-kind listing without expansion.
|
||||
listing.rentals = [listing.rentals[0]];
|
||||
expect(validate("openrental", listing).ok).toBe(true);
|
||||
});
|
||||
|
||||
it("accepts membership without rental offers and preserves extension metadata", () => {
|
||||
const listing = fixture();
|
||||
delete listing.rentals;
|
||||
listing.metadata = { custom: { nested: [null, true, 42, "value"] } };
|
||||
const snapshot = structuredClone(listing);
|
||||
expect(validate("openrental", listing).ok).toBe(true);
|
||||
expect(listing).toEqual(snapshot);
|
||||
});
|
||||
|
||||
it.each([
|
||||
["no members", (f: ReturnType<typeof fixture>) => { f.members = []; }],
|
||||
["unsupported kind", (f: ReturnType<typeof fixture>) => { f.members[0].kind = "openrental"; }],
|
||||
["agent without identity", (f: ReturnType<typeof fixture>) => { delete f.members[0].id; }],
|
||||
["swarm without identity", (f: ReturnType<typeof fixture>) => { delete f.members[1].id; }],
|
||||
["swarm with an agent DID", (f: ReturnType<typeof fixture>) => { f.members[1].id = "analyst.coinpay"; }],
|
||||
["agent with a swarm key", (f: ReturnType<typeof fixture>) => { f.members[0].id = f.members[1].id; }],
|
||||
["short swarm key", (f: ReturnType<typeof fixture>) => { f.members[1].id = "ed25519:abcd"; }],
|
||||
["non-HTTPS member URL", (f: ReturnType<typeof fixture>) => { f.members[0].url = "http://example.com/agent"; }],
|
||||
["unsupported version", (f: ReturnType<typeof fixture>) => { f.version = "9.0"; }],
|
||||
["unknown contract field", (f: ReturnType<typeof fixture>) => { f.rental = []; }]
|
||||
])("rejects %s", (_, mutate) => {
|
||||
const listing = fixture();
|
||||
mutate(listing);
|
||||
expect(validate("openrental", listing).ok).toBe(false);
|
||||
});
|
||||
|
||||
it("rejects duplicate member identities even when URL and metadata differ", () => {
|
||||
const listing = fixture();
|
||||
listing.members.push({ ...listing.members[0], url: "https://elsewhere.example/agent", metadata: { alias: true } });
|
||||
errorAt(listing, "uniqueMember", "/members/2");
|
||||
});
|
||||
|
||||
it("rejects duplicate rental IDs", () => {
|
||||
const listing = fixture();
|
||||
listing.rentals[1].id = listing.rentals[0].id;
|
||||
errorAt(listing, "uniqueRental", "/rentals/1/id");
|
||||
});
|
||||
|
||||
it("rejects dangling references after a member is removed", () => {
|
||||
const listing = fixture();
|
||||
listing.members.shift();
|
||||
errorAt(listing, "memberReference", "/rentals/1/scope/members/0");
|
||||
});
|
||||
|
||||
it("accepts a rental scoped to both kinds without counting a member twice", () => {
|
||||
const listing = fixture();
|
||||
listing.rentals[1].scope.members.push({ kind: "openswarm", id: listing.members[1].id });
|
||||
expect(validate("openrental", listing).ok).toBe(true);
|
||||
listing.rentals[1].scope.members.push({ ...listing.rentals[1].scope.members[0] });
|
||||
errorAt(listing, "uniqueMember", "/rentals/1/scope/members/2");
|
||||
});
|
||||
|
||||
it.each([25, -1, "-1.000000", "01.000000", "1e3", "NaN", "Infinity", "1.00", "0.0000001"])(
|
||||
"rejects an inexact or invalid amount %s", (amount) => {
|
||||
const listing = fixture();
|
||||
listing.rentals[0].rate.amount = amount;
|
||||
expect(validate("openrental", listing).ok).toBe(false);
|
||||
}
|
||||
);
|
||||
|
||||
it.each(["0.000000", "0.000001", "9007199254740993.123456"])("preserves exact amount %s", (amount) => {
|
||||
const listing = fixture();
|
||||
listing.rentals[0].rate.amount = amount;
|
||||
expect(validate("openrental", listing).ok).toBe(true);
|
||||
expect(listing.rentals[0].rate.amount).toBe(amount);
|
||||
});
|
||||
|
||||
it.each([
|
||||
["non-CoinPay provider", (r: ReturnType<typeof fixture>) => { r.payment.provider = "other"; }],
|
||||
["missing CoinPay metadata", (r: ReturnType<typeof fixture>) => { delete r.payment; }],
|
||||
["missing payee", (r: ReturnType<typeof fixture>) => { delete r.payment.payee_did; }],
|
||||
["missing checkout", (r: ReturnType<typeof fixture>) => { delete r.payment.checkout_url; }],
|
||||
["insecure checkout", (r: ReturnType<typeof fixture>) => { r.payment.checkout_url = "http://example.com/checkout"; }],
|
||||
["unsupported currency", (r: ReturnType<typeof fixture>) => { r.rate.currency = "EUR"; }],
|
||||
["missing unit", (r: ReturnType<typeof fixture>) => { delete r.rate.unit; }],
|
||||
["unsupported unit", (r: ReturnType<typeof fixture>) => { r.rate.unit = "second"; }],
|
||||
["fractional billing units", (r: ReturnType<typeof fixture>) => { r.minimum_units = 0.5; }],
|
||||
["unsafe integer units", (r: ReturnType<typeof fixture>) => { r.maximum_units = 9007199254740992; }],
|
||||
["empty member scope", (r: ReturnType<typeof fixture>) => { r.scope = { kind: "members", members: [] }; }],
|
||||
["ambiguous listing scope", (r: ReturnType<typeof fixture>) => { r.scope.members = []; }]
|
||||
])("rejects a rental with %s", (_, mutate) => {
|
||||
const listing = fixture();
|
||||
mutate(listing.rentals[0]);
|
||||
expect(validate("openrental", listing).ok).toBe(false);
|
||||
});
|
||||
|
||||
it("rejects maximum units below the minimum", () => {
|
||||
const listing = fixture();
|
||||
listing.rentals[0].minimum_units = 25;
|
||||
errorAt(listing, "rentalUnits", "/rentals/0/maximum_units");
|
||||
});
|
||||
|
||||
it("checks offer windows by instant, including timezone offsets", () => {
|
||||
const listing = fixture();
|
||||
listing.rentals[0].valid_from = "2026-09-13T12:00:00Z";
|
||||
listing.rentals[0].valid_until = "2026-09-13T14:00:00+02:00";
|
||||
errorAt(listing, "rentalPeriod", "/rentals/0/valid_until");
|
||||
listing.rentals[0].valid_until = "2026-09-13T13:00:00+02:00";
|
||||
errorAt(listing, "rentalPeriod", "/rentals/0/valid_until");
|
||||
listing.rentals[0].valid_until = "2026-09-13T15:00:00+02:00";
|
||||
expect(validate("openrental", listing).ok).toBe(true);
|
||||
});
|
||||
});
|
||||
72
packages/validators/src/openrental.ts
Normal file
72
packages/validators/src/openrental.ts
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
import type { ErrorObject } from "ajv";
|
||||
|
||||
type MemberReference = { kind: "openagent" | "openswarm"; id: string };
|
||||
|
||||
// Called only after the JSON Schema has checked the shape. Keep the checks
|
||||
// which need sibling values here; uniqueItems cannot enforce identity when
|
||||
// two entries have different URLs or metadata.
|
||||
type FleetReferences = {
|
||||
members: MemberReference[];
|
||||
rentals?: Array<{
|
||||
id: string;
|
||||
scope: { kind: "listing" } | { kind: "members"; members: MemberReference[] };
|
||||
minimum_units?: number;
|
||||
maximum_units?: number;
|
||||
valid_from?: string;
|
||||
valid_until?: string;
|
||||
}>;
|
||||
};
|
||||
|
||||
function memberKey(member: MemberReference): string {
|
||||
return JSON.stringify([member.kind, member.id]);
|
||||
}
|
||||
|
||||
export function validateOpenRentalReferences(data: unknown): ErrorObject[] {
|
||||
const listing = data as FleetReferences;
|
||||
const errors: ErrorObject[] = [];
|
||||
function report(keyword: string, instancePath: string, message: string) {
|
||||
errors.push({ keyword, instancePath, schemaPath: "#/openrental-semantics", params: {}, message });
|
||||
}
|
||||
|
||||
const members = new Set<string>();
|
||||
listing.members.forEach((member, i) => {
|
||||
const key = memberKey(member);
|
||||
if (members.has(key)) {
|
||||
report("uniqueMember", `/members/${i}`, "duplicates an existing member kind and id");
|
||||
}
|
||||
members.add(key);
|
||||
});
|
||||
|
||||
const rentals = new Set<string>();
|
||||
listing.rentals?.forEach((rental, i) => {
|
||||
const path = `/rentals/${i}`;
|
||||
if (rentals.has(rental.id)) {
|
||||
report("uniqueRental", `${path}/id`, "duplicates an existing rental id");
|
||||
}
|
||||
rentals.add(rental.id);
|
||||
|
||||
if (rental.scope.kind === "members") {
|
||||
const scopedMembers = new Set<string>();
|
||||
rental.scope.members.forEach((member, j) => {
|
||||
const key = memberKey(member);
|
||||
const memberPath = `${path}/scope/members/${j}`;
|
||||
if (!members.has(key)) {
|
||||
report("memberReference", memberPath, "must reference a member of this listing by kind and id");
|
||||
}
|
||||
if (scopedMembers.has(key)) {
|
||||
report("uniqueMember", memberPath, "duplicates a member in this rental scope");
|
||||
}
|
||||
scopedMembers.add(key);
|
||||
});
|
||||
}
|
||||
|
||||
if (rental.maximum_units !== undefined && rental.maximum_units < (rental.minimum_units ?? 1)) {
|
||||
report("rentalUnits", `${path}/maximum_units`, "must be at least minimum_units");
|
||||
}
|
||||
if (rental.valid_from && rental.valid_until && Date.parse(rental.valid_from) >= Date.parse(rental.valid_until)) {
|
||||
report("rentalPeriod", `${path}/valid_until`, "must be later than valid_from");
|
||||
}
|
||||
});
|
||||
|
||||
return errors;
|
||||
}
|
||||
120
packages/validators/src/openwall.test.ts
Normal file
120
packages/validators/src/openwall.test.ts
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
import { readFileSync } from "node:fs";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { validate } from "./index.js";
|
||||
|
||||
function fixture(name: string) {
|
||||
return JSON.parse(readFileSync(new URL(`../../schemas/fixtures/openwall/${name}.json`, import.meta.url), "utf8"));
|
||||
}
|
||||
|
||||
describe("OpenWall draft structural contracts", () => {
|
||||
it.each(["broadcast", "direct", "announcement"])("accepts the %s message fixture", (name) => {
|
||||
expect(validate("openwall-message", fixture(name))).toMatchObject({ ok: true });
|
||||
});
|
||||
|
||||
it.each(["receipt-accepted", "receipt-retrying"])("accepts the %s fixture", (name) => {
|
||||
expect(validate("openwall-receipt", fixture(name))).toMatchObject({ ok: true });
|
||||
});
|
||||
|
||||
it("requires a service scope for every users selector", () => {
|
||||
const message = fixture("broadcast");
|
||||
delete message.audience.selectors[0].scope;
|
||||
expect(validate("openwall-message", message).ok).toBe(false);
|
||||
});
|
||||
|
||||
it("permits a union of relationship groups and sources", () => {
|
||||
const message = fixture("broadcast");
|
||||
message.audience.selectors.push({
|
||||
source: "https://social.example/graph",
|
||||
actor: "did:web:ops.example",
|
||||
groups: ["connections", "followers", "following"]
|
||||
});
|
||||
expect(validate("openwall-message", message).ok).toBe(true);
|
||||
});
|
||||
|
||||
it.each(["broadcast", "direct"])("rejects changing a %s to public while retaining recipients", (name) => {
|
||||
const message = fixture(name);
|
||||
message.visibility = "public";
|
||||
expect(validate("openwall-message", message).ok).toBe(false);
|
||||
});
|
||||
|
||||
it("rejects a private message with an unrestricted public audience", () => {
|
||||
const message = fixture("announcement");
|
||||
message.visibility = "private";
|
||||
expect(validate("openwall-message", message).ok).toBe(false);
|
||||
});
|
||||
|
||||
it("rejects recipient metadata in a public audience", () => {
|
||||
const message = fixture("announcement");
|
||||
message.audience.recipient = "did:web:ada.example";
|
||||
expect(validate("openwall-message", message).ok).toBe(false);
|
||||
});
|
||||
|
||||
it("requires explicit groups and rejects an implicit all audience", () => {
|
||||
const message = fixture("broadcast");
|
||||
message.audience.selectors[0].groups = [];
|
||||
expect(validate("openwall-message", message).ok).toBe(false);
|
||||
message.audience.selectors[0].groups = ["all"];
|
||||
expect(validate("openwall-message", message).ok).toBe(false);
|
||||
});
|
||||
|
||||
it("requires a resolved recipient identity rather than a display handle", () => {
|
||||
const message = fixture("direct");
|
||||
message.audience.recipient = "ada.example";
|
||||
expect(validate("openwall-message", message).ok).toBe(false);
|
||||
});
|
||||
|
||||
it("rejects absent expiry, malformed timestamps and oversized text", () => {
|
||||
const message = fixture("direct");
|
||||
delete message.expiresAt;
|
||||
expect(validate("openwall-message", message).ok).toBe(false);
|
||||
message.expiresAt = "tomorrow";
|
||||
expect(validate("openwall-message", message).ok).toBe(false);
|
||||
message.expiresAt = "2026-09-13T13:00:00Z";
|
||||
message.content.text = "x".repeat(4001);
|
||||
expect(validate("openwall-message", message).ok).toBe(false);
|
||||
});
|
||||
|
||||
it("requires receipt delivery correlation and disallows audience disclosure", () => {
|
||||
const receipt = fixture("receipt-accepted");
|
||||
delete receipt.deliveryId;
|
||||
expect(validate("openwall-receipt", receipt).ok).toBe(false);
|
||||
receipt.deliveryId = "unscoped-message-id";
|
||||
expect(validate("openwall-receipt", receipt).ok).toBe(false);
|
||||
receipt.deliveryId = "urn:uuid:db925d64-4e1a-4e5a-83c1-e37b68aa5eef";
|
||||
receipt.recipients = ["did:web:other.example"];
|
||||
expect(validate("openwall-receipt", receipt).ok).toBe(false);
|
||||
});
|
||||
|
||||
it("requires retry timing and a reason and disallows retry timing on acceptance", () => {
|
||||
const receipt = fixture("receipt-retrying");
|
||||
delete receipt.nextAttemptAt;
|
||||
expect(validate("openwall-receipt", receipt).ok).toBe(false);
|
||||
receipt.nextAttemptAt = "2026-09-13T12:01:00Z";
|
||||
delete receipt.reason;
|
||||
expect(validate("openwall-receipt", receipt).ok).toBe(false);
|
||||
receipt.reason = "rate-limited";
|
||||
receipt.state = "accepted";
|
||||
expect(validate("openwall-receipt", receipt).ok).toBe(false);
|
||||
});
|
||||
|
||||
it("represents ambiguous provider acceptance separately from a known failure", () => {
|
||||
const receipt = fixture("receipt-accepted");
|
||||
receipt.state = "unknown";
|
||||
receipt.reason = "acceptance-unknown";
|
||||
expect(validate("openwall-receipt", receipt).ok).toBe(true);
|
||||
receipt.reason = "rate-limited";
|
||||
expect(validate("openwall-receipt", receipt).ok).toBe(false);
|
||||
delete receipt.reason;
|
||||
receipt.state = "failed";
|
||||
expect(validate("openwall-receipt", receipt).ok).toBe(false);
|
||||
});
|
||||
|
||||
it("requires a real route and attempt for provider acceptance", () => {
|
||||
const receipt = fixture("receipt-accepted");
|
||||
receipt.attempt = 0;
|
||||
expect(validate("openwall-receipt", receipt).ok).toBe(false);
|
||||
receipt.attempt = 1;
|
||||
delete receipt.route;
|
||||
expect(validate("openwall-receipt", receipt).ok).toBe(false);
|
||||
});
|
||||
});
|
||||
|
|
@ -8,6 +8,7 @@
|
|||
* could not load a single schema.
|
||||
*/
|
||||
import agentSchema from "@logicsrc/schemas/agent" with { type: "json" };
|
||||
import openrentalSchema from "@logicsrc/schemas/openrental" with { type: "json" };
|
||||
import accountAuditEventSchema from "@logicsrc/schemas/account-audit-event" with { type: "json" };
|
||||
import accountGrantSchema from "@logicsrc/schemas/account-grant" with { type: "json" };
|
||||
import accountProviderSchema from "@logicsrc/schemas/account-provider" with { type: "json" };
|
||||
|
|
@ -68,8 +69,14 @@ import credsManifestSchema from "@logicsrc/schemas/opencreds-manifest" with { ty
|
|||
import credsDatabaseSchema from "@logicsrc/schemas/opencreds-database" with { type: "json" };
|
||||
import credsAuditEventSchema from "@logicsrc/schemas/opencreds-audit-event" with { type: "json" };
|
||||
|
||||
import openwallMessageSchema from "@logicsrc/schemas/openwall-message" with { type: "json" };
|
||||
import openwallReceiptSchema from "@logicsrc/schemas/openwall-receipt" with { type: "json" };
|
||||
|
||||
export const schemas = {
|
||||
"openwall-message": openwallMessageSchema,
|
||||
"openwall-receipt": openwallReceiptSchema,
|
||||
agent: agentSchema,
|
||||
openrental: openrentalSchema,
|
||||
"account-audit-event": accountAuditEventSchema,
|
||||
"account-grant": accountGrantSchema,
|
||||
"account-provider": accountProviderSchema,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue