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 .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ defaults: &defaults
working_directory: ~/schema-utils-js
docker:
- image: cimg/node:20.12.1
resource_class: large

jobs:
test:
Expand Down
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ build
docs
.DS_Store
dist
.vscode
.vscode
LLMLOG.MD
5 changes: 4 additions & 1 deletion jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,8 @@ module.exports = {
resetMocks: true,
restoreMocks: true,
rootDir: "./src",
preset: "ts-jest",
transformIgnorePatterns: ["node_modules/(?!((@open-rpc/spec-types|@open-rpc/spec))/)"],
transform: {
"^.+\\.[tj]sx?$": ["ts-jest", { tsconfig: { allowJs: true } }],
},
};
1,330 changes: 811 additions & 519 deletions package-lock.json

Large diffs are not rendered by default.

5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
"@json-schema-tools/dereferencer": "^1.6.3",
"@json-schema-tools/meta-schema": "^1.7.5",
"@json-schema-tools/reference-resolver": "^1.2.6",
"@open-rpc/meta-schema": "^1.14.9",
"@open-rpc/spec-types": "^0.0.1",
"@open-rpc/specification-extension-spec": "^1.0.2",
"ajv": "^6.10.0",
"detect-node": "^2.0.4",
Expand All @@ -44,6 +44,7 @@
"isomorphic-fetch": "^3.0.0"
},
"devDependencies": {
"@babel/core": "^7.29.0",
"@types/detect-node": "^2.0.0",
"@types/fs-extra": "^9.0.1",
"@types/is-url": "^1.2.28",
Expand All @@ -61,7 +62,7 @@
"json-schema": "^0.4.0",
"prettier": "^2.0.0",
"rimraf": "^3.0.0",
"ts-jest": "^29.1.2",
"ts-jest": "29.4.6",
"typedoc": "^0.25.13",
"typescript": "^4.9.5",
"webpack": "^5.1.3",
Expand Down
12 changes: 6 additions & 6 deletions src/apply-extension-spec.test.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import applyExtensionSpec from "./apply-extension-spec";
import { OpenrpcDocument } from "@open-rpc/meta-schema";
import { OpenrpcDocument } from "./types";
import goodSchema from "./extension-good-schema.json";
import getExtendedMetaSchema from "./get-extended-metaschema";

describe("applyExtensionSpec", () => {
it("should successfully apply extension spec to meta schema", () => {
const result = applyExtensionSpec(goodSchema as OpenrpcDocument, getExtendedMetaSchema());
const result = applyExtensionSpec(goodSchema as OpenrpcDocument, getExtendedMetaSchema("1.3"));

// Check if extension was applied to methodObject
const methodObjectDef = result.definitions.methodObject;
Expand Down Expand Up @@ -47,7 +47,7 @@ describe("applyExtensionSpec", () => {
"x-extensions": [],
};

const schema = getExtendedMetaSchema();
const schema = getExtendedMetaSchema("1.3");
const result = applyExtensionSpec(emptyExtensionsDoc as OpenrpcDocument, schema);
expect(result).toEqual(schema);
});
Expand All @@ -64,7 +64,7 @@ describe("applyExtensionSpec", () => {
};

expect(() => {
applyExtensionSpec(badDoc as OpenrpcDocument, getExtendedMetaSchema());
applyExtensionSpec(badDoc as OpenrpcDocument, getExtendedMetaSchema("1.3"));
}).toThrow("nonExistentDefinition does not exist, cannot apply extension x-notification");
});

Expand Down Expand Up @@ -96,7 +96,7 @@ describe("applyExtensionSpec", () => {
],
};

const result = applyExtensionSpec(doc as OpenrpcDocument, getExtendedMetaSchema());
const result = applyExtensionSpec(doc as OpenrpcDocument, getExtendedMetaSchema("1.3"));
expect(result.definitions["x-test-potate"]).toBeDefined();
expect(result.definitions["x-test-potate"].properties["x-next"]).toBeDefined();
expect(result.definitions["x-test-potate"].properties["x-next"].type).toBe("boolean");
Expand All @@ -119,7 +119,7 @@ describe("applyExtensionSpec", () => {
});

it("should throw error when extension property already exists", () => {
const schema = getExtendedMetaSchema();
const schema = getExtendedMetaSchema("1.3");
const modifiedSchema = {
...schema,
definitions: {
Expand Down
2 changes: 1 addition & 1 deletion src/apply-extension-spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { OpenrpcDocument as OpenRPC } from "@open-rpc/meta-schema";
import { OpenrpcDocument as OpenRPC } from "./types";

/**
* Applies extension specifications to an OpenRPC document by modifying the meta schema
Expand Down
7 changes: 1 addition & 6 deletions src/dereference-document.test.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,6 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import dereferenceDocument, { OpenRPCDocumentDereferencingError } from "./dereference-document";
import {
OpenrpcDocument,
ContentDescriptorObject,
JSONSchema,
MethodObject,
} from "@open-rpc/meta-schema";
import { OpenrpcDocument, ContentDescriptorObject, JSONSchema, MethodObject } from "./types";
import { JSONSchemaObject } from "@json-schema-tools/meta-schema";

const workingDocument: OpenrpcDocument = {
Expand Down
6 changes: 4 additions & 2 deletions src/dereference-document.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import Dereferencer from "@json-schema-tools/dereferencer";
import metaSchema, {
import getMetaSchemaForVersion from "./get-meta-schema-for-version";
import {
OpenrpcDocument as OpenRPC,
ReferenceObject,
ExamplePairingObject,
Expand All @@ -10,7 +11,7 @@ import metaSchema, {
OpenrpcDocument,
MethodObject,
MethodOrReference,
} from "@open-rpc/meta-schema";
} from "./types";
import referenceResolver from "@json-schema-tools/reference-resolver";
import safeStringify from "fast-safe-stringify";

Expand Down Expand Up @@ -475,6 +476,7 @@ export default async function dereferenceDocument(
derefDoc = await handleSchemaComponents(derefDoc);
derefDoc = await handleSchemasInsideContentDescriptorComponents(derefDoc);

const metaSchema = getMetaSchemaForVersion(openrpcDocument.openrpc);
const definitionsMap = createDefinitionsMap(metaSchema);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const extensions = [] as any;
Expand Down
2 changes: 1 addition & 1 deletion src/generate-method-id.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { generateMethodParamId, generateMethodResultId } from "./generate-method-id";
import { MethodObject } from "@open-rpc/meta-schema";
import { MethodObject } from "./types";

describe("methodParamId", () => {
describe("default paramStructure: either", () => {
Expand Down
2 changes: 1 addition & 1 deletion src/generate-method-id.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { MethodObject, ContentDescriptorObject } from "@open-rpc/meta-schema";
import { MethodObject, ContentDescriptorObject } from "./types";
import { findIndex } from "./helper-functions";

/**
Expand Down
2 changes: 1 addition & 1 deletion src/get-document-extended-metaschema.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import getDocumentExtendedMetaSchema from "./get-document-extended-metaschema";
import goodSchema from "./extension-good-schema.json";
import { OpenrpcDocument } from "@open-rpc/meta-schema";
import { OpenrpcDocument } from "./types";

describe("getDocumentExtendedMetaSchema", () => {
it("should successfully apply extension spec to meta schema", () => {
Expand Down
4 changes: 2 additions & 2 deletions src/get-document-extended-metaschema.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { OpenrpcDocument } from "@open-rpc/meta-schema";
import { OpenrpcDocument } from "./types";
import applyExtensionSpec from "./apply-extension-spec";
import getExtendedMetaSchema from "./get-extended-metaschema";

// eslint-disable-next-line @typescript-eslint/no-explicit-any
export default function getDocumentExtendedMetaSchema(document: OpenrpcDocument): any {
const extendedMetaSchema = getExtendedMetaSchema();
const extendedMetaSchema = getExtendedMetaSchema(document.openrpc);
const extendedDocument = applyExtensionSpec(document, extendedMetaSchema);
return extendedDocument;
}
6 changes: 3 additions & 3 deletions src/get-extended-metaschema.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import metaSchema from "@open-rpc/meta-schema";
import getMetaSchemaForVersion from "./get-meta-schema-for-version";
import { metaSchema as extensionMetaSchema } from "@open-rpc/specification-extension-spec";

// eslint-disable-next-line @typescript-eslint/no-explicit-any
function getExtendedMetaSchema(): any {
function getExtendedMetaSchema(version = "1.3"): any {
// NOTE: this is to make sure we don't mutate the original meta schema
const metaSchemaCopy = JSON.parse(JSON.stringify(metaSchema));
const metaSchemaCopy = JSON.parse(JSON.stringify(getMetaSchemaForVersion(version)));
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const extensionMetaSchemaCopy = { ...extensionMetaSchema } as any;

Expand Down
27 changes: 27 additions & 0 deletions src/get-meta-schema-for-version.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import getMetaSchemaForVersion from "./get-meta-schema-for-version";

describe("getMetaSchemaForVersion", () => {
it("should successfully apply extension spec to meta schema", () => {
const result = getMetaSchemaForVersion("1.3");
expect(result).toBeDefined();
if ("enum" in result.properties["openrpc"]) {
expect(result.properties["openrpc"].enum).toContain("1.3.2");
} else {
throw new Error("OpenRPC schema version 1.3 is not supported");
}

const result1_4 = getMetaSchemaForVersion("1.4");
expect(result1_4).toBeDefined();
if ("regex" in result1_4.properties["openrpc"]) {
expect(result1_4.properties["openrpc"].regex).toContain("4");
} else {
throw new Error("OpenRPC schema version 1.4 is not supported");
}
});

it("should throw an error for an unsupported version", () => {
expect(() => getMetaSchemaForVersion("99.9")).toThrow(
"Unsupported OpenRPC schema version: 99.9"
);
});
});
9 changes: 9 additions & 0 deletions src/get-meta-schema-for-version.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { spec } from "@open-rpc/spec-types";

const getMetaSchemaForVersion = (version: string) => {
if (/^1\.4(\..*)?$/.test(version)) return spec.OpenRPCSpecificationSchema1_4;
if (/^1\./.test(version)) return spec.OpenRPCSpecificationSchema1_3;
throw new Error(`Unsupported OpenRPC schema version: ${version}`);
};

export default getMetaSchemaForVersion;
2 changes: 1 addition & 1 deletion src/get-open-rpc-document-from-file.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { OpenrpcDocument as OpenRPC } from "@open-rpc/meta-schema";
import { OpenrpcDocument as OpenRPC } from "./types";
import { readJson } from "fs-extra";
import { TGetOpenRPCDocument } from "./get-open-rpc-document";

Expand Down
2 changes: 1 addition & 1 deletion src/get-open-rpc-document-from-url.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { OpenrpcDocument as OpenRPC } from "@open-rpc/meta-schema";
import { OpenrpcDocument as OpenRPC } from "./types";
import fetch from "isomorphic-fetch";
import { TGetOpenRPCDocument } from "./get-open-rpc-document";

Expand Down
2 changes: 1 addition & 1 deletion src/get-open-rpc-document.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
import { OpenrpcDocument as OpenRPC } from "@open-rpc/meta-schema";
import { OpenrpcDocument as OpenRPC } from "./types";
export type TGetOpenRPCDocument = (schema: string) => Promise<OpenRPC>;
2 changes: 1 addition & 1 deletion src/helper-functions.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import * as help from "./helper-functions";
import { OpenrpcDocument as OpenRPC } from "@open-rpc/meta-schema";
import { OpenrpcDocument as OpenRPC } from "./types";

describe("helper functions", () => {
describe("find", () => {
Expand Down
2 changes: 1 addition & 1 deletion src/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import fs from "fs-extra";
import { OpenrpcDocument as OpenRPC } from "@open-rpc/meta-schema";
import { OpenrpcDocument as OpenRPC } from "./types";
import { parseOpenRPCDocument } from "./";
import rimraf from "rimraf";
import { promisify } from "util";
Expand Down
2 changes: 1 addition & 1 deletion src/method-call-validator/method-call-validator.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import MethodCallValidator from "./method-call-validator";
import { MethodObject, OpenrpcDocument as OpenRPC, OpenrpcDocument } from "@open-rpc/meta-schema";
import { MethodObject, OpenrpcDocument as OpenRPC, OpenrpcDocument } from "../types";
import MethodCallParameterValidationError from "./parameter-validation-error";
import MethodCallMethodNotFoundError from "./method-not-found-error";
import MethodNotFoundError from "./method-not-found-error";
Expand Down
2 changes: 1 addition & 1 deletion src/method-call-validator/method-call-validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
MethodObject,
ContentDescriptorObject,
MethodOrReference,
} from "@open-rpc/meta-schema";
} from "../types";
import MethodNotFoundError from "./method-not-found-error";
import { find, compact } from "../helper-functions";
import MethodRefUnexpectedError from "./method-ref-unexpected-error";
Expand Down
2 changes: 1 addition & 1 deletion src/method-call-validator/method-not-found-error.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import MethodNotFoundError from "./method-not-found-error";
import { OpenrpcDocument as OpenRPC } from "@open-rpc/meta-schema";
import { OpenrpcDocument as OpenRPC } from "../types";

const exampleDoc = {
info: {
Expand Down
2 changes: 1 addition & 1 deletion src/method-call-validator/method-not-found-error.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { MethodObject, OpenrpcDocument as OpenRPC } from "@open-rpc/meta-schema";
import { MethodObject, OpenrpcDocument as OpenRPC } from "../types";

/**
* Provides an error interface for handling when a method is trying to be called but does not exist.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import MethodRefUnexpectedError from "./method-ref-unexpected-error";
import { OpenrpcDocument as OpenRPC } from "@open-rpc/meta-schema";
import { OpenrpcDocument as OpenRPC } from "../types";

const exampleDoc = {
info: {
Expand Down
2 changes: 1 addition & 1 deletion src/method-call-validator/method-ref-unexpected-error.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { OpenrpcDocument as OpenRPC } from "@open-rpc/meta-schema";
import { OpenrpcDocument as OpenRPC } from "../types";

/**
* Provides an error interface for handling when a method is trying to be called but does not exist.
Expand Down
2 changes: 1 addition & 1 deletion src/method-call-validator/parameter-validation-error.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ErrorObject } from "ajv";
import { JSONSchema } from "@open-rpc/meta-schema";
import { JSONSchema } from "../types";

/**
* Provides an error interface for handling when a function call has invalid parameters.
Expand Down
2 changes: 1 addition & 1 deletion src/parse-open-rpc-document.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ jest.mock("fs-extra", () => ({

import * as _fs from "fs-extra";
import makeParseOpenRPCDocument, { makeCustomResolver } from "./parse-open-rpc-document";
import { OpenrpcDocument as OpenRPC, OpenrpcDocument } from "@open-rpc/meta-schema";
import { OpenrpcDocument as OpenRPC, OpenrpcDocument } from "./types";
import { OpenRPCDocumentValidationError } from "./validate-open-rpc-document";
import fetchUrlSchema from "./get-open-rpc-document-from-url";
import readSchemaFromFile from "./get-open-rpc-document-from-file";
Expand Down
2 changes: 1 addition & 1 deletion src/parse-open-rpc-document.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import validateOpenRPCDocument, {
} from "./validate-open-rpc-document";
import defaultResolver from "@json-schema-tools/reference-resolver";
import isUrl = require("is-url");
import { OpenrpcDocument } from "@open-rpc/meta-schema";
import { OpenrpcDocument } from "./types";
import { TGetOpenRPCDocument } from "./get-open-rpc-document";
import ReferenceResolver, {
ProtocolHandlerMap,
Expand Down
31 changes: 31 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import type { V1_4, V1_3 } from "@open-rpc/spec-types";

export type { V1_4, V1_3 };

// Union types for version-agnostic usage
export type OpenrpcDocument = V1_4.OpenrpcDocument | V1_3.OpenrpcDocument;
export type MethodObject = V1_4.MethodObject | V1_3.MethodObject;
export type ContentDescriptorObject = V1_4.ContentDescriptorObject | V1_3.ContentDescriptorObject;
export type JSONSchema = V1_4.JSONSchema | V1_3.JSONSchema;
export type ReferenceObject = V1_4.ReferenceObject | V1_3.ReferenceObject;
export type MethodOrReference = V1_4.MethodOrReference | V1_3.MethodOrReference;
export type ExamplePairingObject = V1_4.ExamplePairingObject | V1_3.ExamplePairingObject;
export type SchemaComponents = V1_4.SchemaComponents | V1_3.SchemaComponents;
export type ContentDescriptorComponents =
| V1_4.ContentDescriptorComponents
| V1_3.ContentDescriptorComponents;

export type RefNode = { $ref: string };

export type NoRefs<T> =
// If T itself is a ref → remove it
T extends RefNode
? never
: // If T is an array → apply recursively to element type
T extends (infer U)[]
? NoRefs<U>[]
: // If T is an object → map over its properties
T extends object
? { [K in keyof T]: NoRefs<T[K]> }
: // Primitives (string, number, etc.) are left as-is
T;
2 changes: 1 addition & 1 deletion src/validate-open-rpc-document.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import validateOpenRPCDocument, {
OpenRPCDocumentValidationError,
} from "./validate-open-rpc-document";
import { OpenrpcDocument } from "@open-rpc/meta-schema";
import { OpenrpcDocument } from "./types";
import goodExtensionSchema from "./extension-good-schema.json";
import badExtensionSchema from "./extension-bad-schema.json";

Expand Down
6 changes: 4 additions & 2 deletions src/validate-open-rpc-document.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { OpenrpcDocument as OpenRPC } from "@open-rpc/meta-schema";
import { OpenrpcDocument as OpenRPC } from "./types";
import Ajv, { ErrorObject } from "ajv";
import JsonSchemaMetaSchema from "@json-schema-tools/meta-schema";
import applyExtensionSpec from "./apply-extension-spec";
Expand Down Expand Up @@ -51,9 +51,11 @@ export class OpenRPCDocumentValidationError implements Error {
export default function validateOpenRPCDocument(
document: OpenRPC
): OpenRPCDocumentValidationError | true {
if (!document) throw new Error("schema-utils-js: Internal Error - document is undefined");

const ajv = new Ajv();
ajv.addSchema(JsonSchemaMetaSchema, "https://meta.json-schema.tools");
let extMetaSchema = getExtendedMetaSchema();
let extMetaSchema = getExtendedMetaSchema(document.openrpc);
try {
extMetaSchema = applyExtensionSpec(document, extMetaSchema);
ajv.validate(extMetaSchema, document);
Expand Down
Loading