@darksheep/json-schema 0.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +63 -0
- package/package.json +31 -0
- package/src/errors.js +46 -0
- package/src/index.js +32 -0
- package/src/validator.js +122 -0
- package/types/errors.d.ts +35 -0
- package/types/index.d.ts +24 -0
- package/types/validator.d.ts +32 -0
package/README.md
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# @darksheep/package.json
|
|
2
|
+
Fetch the package.json of either; the executing program, or a given package by name.
|
|
3
|
+
This is primarily useful when you're making a package, and you need to find the package.json for the service that is executing your package.
|
|
4
|
+
When using pnpm you need to be cautious to set the root directory correctly.
|
|
5
|
+
|
|
6
|
+
## Examples
|
|
7
|
+
### Common JS
|
|
8
|
+
```js
|
|
9
|
+
const { getPackageJson } = require('@darksheep/package.json');
|
|
10
|
+
(async () => {
|
|
11
|
+
const { name, version } = await getPackageJson();
|
|
12
|
+
|
|
13
|
+
console.info({ name, version });
|
|
14
|
+
})()
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
### Module JS
|
|
18
|
+
```js
|
|
19
|
+
import { getPackageJson } from '@darksheep/package.json';
|
|
20
|
+
const { name, version } = await getPackageJson();
|
|
21
|
+
|
|
22
|
+
console.info({ name, version });
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
### Sync API
|
|
26
|
+
This api is only useful for commonjs where top level await is not available.
|
|
27
|
+
If you intend on using this inside a module file, please reconsider!
|
|
28
|
+
|
|
29
|
+
```js
|
|
30
|
+
const { getPackageJson } = require('@darksheep/package.json/sync');
|
|
31
|
+
const { name, version } = getPackageJson();
|
|
32
|
+
|
|
33
|
+
console.info({ name, version });
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Detailed Docs
|
|
37
|
+
### Async (`@darksheep/package.json`)
|
|
38
|
+
```js
|
|
39
|
+
/** @typedef {import('@darksheep/package.json').PackageJson} PackageJson */
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Attempt to find the package.json for {packageName}
|
|
43
|
+
*
|
|
44
|
+
* @param {string} [packageName] The name of the npm package (the main package if blank)
|
|
45
|
+
* @param {string | URL} [root] an optional root url to start from
|
|
46
|
+
* @returns {Promise<PackageJson | null>}
|
|
47
|
+
*/
|
|
48
|
+
export function getPackageJson(packageName, root);
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Sync (`@darksheep/package.json/sync`)
|
|
52
|
+
```js
|
|
53
|
+
/** @typedef {import('@darksheep/package.json').PackageJson} PackageJson */
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Attempt to find the package.json for {packageName} synchronously
|
|
57
|
+
*
|
|
58
|
+
* @param {string} [packageName] The name of the npm package (the main package if blank)
|
|
59
|
+
* @param {string | URL} [root] an optional root file url to start from
|
|
60
|
+
* @returns {PackageJson | null}
|
|
61
|
+
*/
|
|
62
|
+
export function getPackageJson(packageName, root);
|
|
63
|
+
```
|
package/package.json
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@darksheep/json-schema",
|
|
3
|
+
"version": "0.0.0",
|
|
4
|
+
"description": "JSON schema validation (with types)",
|
|
5
|
+
"license": "UNLICENCED",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./types/index.d.ts",
|
|
10
|
+
"default": "./src/index.js"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"src",
|
|
15
|
+
"types",
|
|
16
|
+
"!*.test.*"
|
|
17
|
+
],
|
|
18
|
+
"scripts": {
|
|
19
|
+
"prepack": "yarn dlx -q -p typescript tsc",
|
|
20
|
+
"test": "node --test src/**/*.test.js",
|
|
21
|
+
"test:watch": "node --watch --test src/**/*.test.js"
|
|
22
|
+
},
|
|
23
|
+
"dependencies": {
|
|
24
|
+
"@darksheep/errors": "",
|
|
25
|
+
"ajv": "8.17.1",
|
|
26
|
+
"json-schema-to-ts": "3.1.1"
|
|
27
|
+
},
|
|
28
|
+
"engines": {
|
|
29
|
+
"node": "^20.13.1 || ^22.2.0"
|
|
30
|
+
}
|
|
31
|
+
}
|
package/src/errors.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { BadRequest, InternalServerError } from '@darksheep/errors';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @typedef {(
|
|
5
|
+
* & import('@darksheep/errors').ErrorOptions
|
|
6
|
+
* & { errors: import('ajv').DefinedError[] }
|
|
7
|
+
* )} ErrorOptions
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* The server will not process the request body not matching the expected request schema.
|
|
12
|
+
*/
|
|
13
|
+
export class RequestSchemaError extends BadRequest {
|
|
14
|
+
/** @type {import('ajv').DefinedError[]} */
|
|
15
|
+
errors = [];
|
|
16
|
+
name = 'RequestSchemaError';
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* @param {string} message - The custom message for the http error.
|
|
20
|
+
* @param {ErrorOptions} options - The custom message for the http error.
|
|
21
|
+
*/
|
|
22
|
+
constructor(message, { errors, ...options }) {
|
|
23
|
+
super(message, options);
|
|
24
|
+
|
|
25
|
+
this.errors = errors;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* The server tried sending a response that did not match the expected response schema.
|
|
31
|
+
*/
|
|
32
|
+
export class ResponseSchemaError extends InternalServerError {
|
|
33
|
+
/** @type {import('ajv').DefinedError[]} */
|
|
34
|
+
errors = [];
|
|
35
|
+
name = 'ResponseSchemaError';
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* @param {string} message - The custom message for the http error.
|
|
39
|
+
* @param {ErrorOptions} options - The custom message for the http error.
|
|
40
|
+
*/
|
|
41
|
+
constructor(message, { errors, ...options }) {
|
|
42
|
+
super(message, options);
|
|
43
|
+
|
|
44
|
+
this.errors = errors;
|
|
45
|
+
}
|
|
46
|
+
}
|
package/src/index.js
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @typedef {import('ajv').DefinedError} DefinedError
|
|
3
|
+
* @typedef {Exclude<import('json-schema-to-ts').JSONSchema, boolean>} JSONSchema
|
|
4
|
+
* @typedef {import('json-schema-to-ts').FromSchemaOptions} FromSchemaOptions
|
|
5
|
+
* @typedef {import('json-schema-to-ts').FromSchemaDefaultOptions} FromSchemaDefaultOptions
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @template {JSONSchema} Schema
|
|
10
|
+
* @template {FromSchemaOptions} [Options=FromSchemaDefaultOptions]
|
|
11
|
+
* @typedef {import('json-schema-to-ts').FromSchema<Schema, Options>} FromSchema
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @template {JSONSchema} const Schema - The raw schema type.
|
|
16
|
+
* @param {Schema} schema - The raw JSON schema.
|
|
17
|
+
* @returns {Schema}
|
|
18
|
+
*/
|
|
19
|
+
export function defineSchema(schema) {
|
|
20
|
+
return schema;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export {
|
|
24
|
+
RequestSchemaError,
|
|
25
|
+
ResponseSchemaError,
|
|
26
|
+
} from './errors.js';
|
|
27
|
+
|
|
28
|
+
export {
|
|
29
|
+
validateData,
|
|
30
|
+
validateRequestBody,
|
|
31
|
+
validateResponseBody,
|
|
32
|
+
} from './validator.js';
|
package/src/validator.js
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { Ajv } from 'ajv';
|
|
2
|
+
import { RequestSchemaError, ResponseSchemaError } from './errors.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @import { DefinedError, AsyncSchema, AsyncValidateFunction, ValidationError } from 'ajv'
|
|
6
|
+
* @import { FromSchema, JSONSchema } from 'json-schema-to-ts'
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @typedef {(
|
|
11
|
+
* & { schema: JSONSchema }
|
|
12
|
+
* )} ToJsonOptions
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
const ajv = new Ajv({
|
|
16
|
+
allErrors: true,
|
|
17
|
+
verbose: false,
|
|
18
|
+
allowUnionTypes: false,
|
|
19
|
+
multipleOfPrecision: 5,
|
|
20
|
+
ownProperties: true,
|
|
21
|
+
strict: true,
|
|
22
|
+
useDefaults: false,
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* @type {WeakMap<Exclude<JSONSchema, boolean>, AsyncValidateFunction>}
|
|
27
|
+
*/
|
|
28
|
+
const compiled = new WeakMap();
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* @param {unknown} input - The value to check.
|
|
32
|
+
* @returns {input is ValidationError & { errors: DefinedError[] }}
|
|
33
|
+
*/
|
|
34
|
+
function isAjvError(input) {
|
|
35
|
+
return (
|
|
36
|
+
input instanceof Error &&
|
|
37
|
+
'errors' in input &&
|
|
38
|
+
Array.isArray(input.errors)
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* @param {Exclude<JSONSchema, boolean>} schema - The original JSONSchema.
|
|
44
|
+
* @returns {AsyncValidateFunction}
|
|
45
|
+
*/
|
|
46
|
+
function getValidator(schema) {
|
|
47
|
+
const Compiled = compiled.get(schema);
|
|
48
|
+
if (Compiled != null) {
|
|
49
|
+
return Compiled;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const validator = ajv.compile(
|
|
53
|
+
/** @type {AsyncSchema} */ ({
|
|
54
|
+
...schema,
|
|
55
|
+
$async: true,
|
|
56
|
+
}),
|
|
57
|
+
);
|
|
58
|
+
compiled.set(schema, validator);
|
|
59
|
+
|
|
60
|
+
return validator;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* @template {Exclude<JSONSchema, boolean>} Schema
|
|
65
|
+
* @template [Data=FromSchema<Schema>]
|
|
66
|
+
* @param {Schema} schema - The request body json schema.
|
|
67
|
+
* @param {unknown} data - The data to validate.
|
|
68
|
+
* @returns {Promise<Data>}
|
|
69
|
+
* @throws {ValidationError & { errors: DefinedError[] }}
|
|
70
|
+
*/
|
|
71
|
+
export async function validateData(schema, data) {
|
|
72
|
+
/** @type {AsyncValidateFunction<Data>} */
|
|
73
|
+
const validator = getValidator(schema);
|
|
74
|
+
|
|
75
|
+
return await validator(data);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* @template {Exclude<JSONSchema, boolean>} Schema
|
|
80
|
+
* @template [Data=FromSchema<Schema>]
|
|
81
|
+
* @param {Schema} schema - The request body json schema.
|
|
82
|
+
* @param {unknown} data - The data to validate.
|
|
83
|
+
* @returns {Promise<Data>}
|
|
84
|
+
* @throws {RequestSchemaError}
|
|
85
|
+
*/
|
|
86
|
+
export async function validateRequestBody(schema, data) {
|
|
87
|
+
try {
|
|
88
|
+
return await validateData(schema, data);
|
|
89
|
+
} catch (error) {
|
|
90
|
+
if (isAjvError(error)) {
|
|
91
|
+
throw new RequestSchemaError(
|
|
92
|
+
'Request.body does not match Schema',
|
|
93
|
+
{ cause: error, errors: error.errors },
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
throw error;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* @template {Exclude<JSONSchema, boolean>} Schema
|
|
103
|
+
* @template [Data=FromSchema<Schema>]
|
|
104
|
+
* @param {Schema} schema - The response body json schema.
|
|
105
|
+
* @param {unknown} data - The data to validate.
|
|
106
|
+
* @returns {Promise<Data>}
|
|
107
|
+
* @throws {ResponseSchemaError}
|
|
108
|
+
*/
|
|
109
|
+
export async function validateResponseBody(schema, data) {
|
|
110
|
+
try {
|
|
111
|
+
return await validateData(schema, data);
|
|
112
|
+
} catch (error) {
|
|
113
|
+
if (isAjvError(error)) {
|
|
114
|
+
throw new ResponseSchemaError(
|
|
115
|
+
'Response.body does not match Schema',
|
|
116
|
+
{ cause: error, errors: error.errors },
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
throw error;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @typedef {(
|
|
3
|
+
* & import('@darksheep/errors').ErrorOptions
|
|
4
|
+
* & { errors: import('ajv').DefinedError[] }
|
|
5
|
+
* )} ErrorOptions
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* The server will not process the request body not matching the expected request schema.
|
|
9
|
+
*/
|
|
10
|
+
export class RequestSchemaError extends BadRequest {
|
|
11
|
+
/**
|
|
12
|
+
* @param {string} message - The custom message for the http error.
|
|
13
|
+
* @param {ErrorOptions} options - The custom message for the http error.
|
|
14
|
+
*/
|
|
15
|
+
constructor(message: string, { errors, ...options }: ErrorOptions);
|
|
16
|
+
/** @type {import('ajv').DefinedError[]} */
|
|
17
|
+
errors: import("ajv").DefinedError[];
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* The server tried sending a response that did not match the expected response schema.
|
|
21
|
+
*/
|
|
22
|
+
export class ResponseSchemaError extends InternalServerError {
|
|
23
|
+
/**
|
|
24
|
+
* @param {string} message - The custom message for the http error.
|
|
25
|
+
* @param {ErrorOptions} options - The custom message for the http error.
|
|
26
|
+
*/
|
|
27
|
+
constructor(message: string, { errors, ...options }: ErrorOptions);
|
|
28
|
+
/** @type {import('ajv').DefinedError[]} */
|
|
29
|
+
errors: import("ajv").DefinedError[];
|
|
30
|
+
}
|
|
31
|
+
export type ErrorOptions = (import("@darksheep/errors").ErrorOptions & {
|
|
32
|
+
errors: import("ajv").DefinedError[];
|
|
33
|
+
});
|
|
34
|
+
import { BadRequest } from '@darksheep/errors';
|
|
35
|
+
import { InternalServerError } from '@darksheep/errors';
|
package/types/index.d.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @typedef {import('ajv').DefinedError} DefinedError
|
|
3
|
+
* @typedef {Exclude<import('json-schema-to-ts').JSONSchema, boolean>} JSONSchema
|
|
4
|
+
* @typedef {import('json-schema-to-ts').FromSchemaOptions} FromSchemaOptions
|
|
5
|
+
* @typedef {import('json-schema-to-ts').FromSchemaDefaultOptions} FromSchemaDefaultOptions
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* @template {JSONSchema} Schema
|
|
9
|
+
* @template {FromSchemaOptions} [Options=FromSchemaDefaultOptions]
|
|
10
|
+
* @typedef {import('json-schema-to-ts').FromSchema<Schema, Options>} FromSchema
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* @template {JSONSchema} const Schema - The raw schema type.
|
|
14
|
+
* @param {Schema} schema - The raw JSON schema.
|
|
15
|
+
* @returns {Schema}
|
|
16
|
+
*/
|
|
17
|
+
export function defineSchema<const Schema extends JSONSchema>(schema: Schema): Schema;
|
|
18
|
+
export type DefinedError = import("ajv").DefinedError;
|
|
19
|
+
export type JSONSchema = Exclude<import("json-schema-to-ts").JSONSchema, boolean>;
|
|
20
|
+
export type FromSchemaOptions = import("json-schema-to-ts").FromSchemaOptions;
|
|
21
|
+
export type FromSchemaDefaultOptions = import("json-schema-to-ts").FromSchemaDefaultOptions;
|
|
22
|
+
export type FromSchema<Schema extends JSONSchema, Options extends FromSchemaOptions = import("json-schema-to-ts").FromSchemaDefaultOptions> = import("json-schema-to-ts").FromSchema<Schema, Options>;
|
|
23
|
+
export { RequestSchemaError, ResponseSchemaError } from "./errors.js";
|
|
24
|
+
export { validateData, validateRequestBody, validateResponseBody } from "./validator.js";
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @template {Exclude<JSONSchema, boolean>} Schema
|
|
3
|
+
* @template [Data=FromSchema<Schema>]
|
|
4
|
+
* @param {Schema} schema - The request body json schema.
|
|
5
|
+
* @param {unknown} data - The data to validate.
|
|
6
|
+
* @returns {Promise<Data>}
|
|
7
|
+
* @throws {ValidationError & { errors: DefinedError[] }}
|
|
8
|
+
*/
|
|
9
|
+
export function validateData<Schema extends Exclude<JSONSchema, boolean>, Data = FromSchema<Schema>>(schema: Schema, data: unknown): Promise<Data>;
|
|
10
|
+
/**
|
|
11
|
+
* @template {Exclude<JSONSchema, boolean>} Schema
|
|
12
|
+
* @template [Data=FromSchema<Schema>]
|
|
13
|
+
* @param {Schema} schema - The request body json schema.
|
|
14
|
+
* @param {unknown} data - The data to validate.
|
|
15
|
+
* @returns {Promise<Data>}
|
|
16
|
+
* @throws {RequestSchemaError}
|
|
17
|
+
*/
|
|
18
|
+
export function validateRequestBody<Schema extends Exclude<JSONSchema, boolean>, Data = FromSchema<Schema>>(schema: Schema, data: unknown): Promise<Data>;
|
|
19
|
+
/**
|
|
20
|
+
* @template {Exclude<JSONSchema, boolean>} Schema
|
|
21
|
+
* @template [Data=FromSchema<Schema>]
|
|
22
|
+
* @param {Schema} schema - The response body json schema.
|
|
23
|
+
* @param {unknown} data - The data to validate.
|
|
24
|
+
* @returns {Promise<Data>}
|
|
25
|
+
* @throws {ResponseSchemaError}
|
|
26
|
+
*/
|
|
27
|
+
export function validateResponseBody<Schema extends Exclude<JSONSchema, boolean>, Data = FromSchema<Schema>>(schema: Schema, data: unknown): Promise<Data>;
|
|
28
|
+
export type ToJsonOptions = ({
|
|
29
|
+
schema: JSONSchema;
|
|
30
|
+
});
|
|
31
|
+
import type { JSONSchema } from 'json-schema-to-ts';
|
|
32
|
+
import type { FromSchema } from 'json-schema-to-ts';
|