yandex-kit-core 0.1.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/LICENSE +21 -0
- package/README.md +35 -0
- package/dist/client.d.ts +54 -0
- package/dist/client.js +266 -0
- package/dist/errors.d.ts +19 -0
- package/dist/errors.js +30 -0
- package/dist/generated/registry.json +3166 -0
- package/dist/generated/spec.json +1 -0
- package/dist/generated/types.d.ts +12928 -0
- package/dist/generated/types.js +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +4 -0
- package/dist/registry.d.ts +34 -0
- package/dist/registry.js +22 -0
- package/dist/validate.d.ts +8 -0
- package/dist/validate.js +133 -0
- package/package.json +47 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export interface RegistryQueryParam {
|
|
2
|
+
name: string;
|
|
3
|
+
required: boolean;
|
|
4
|
+
type: string;
|
|
5
|
+
enum?: string[] | null;
|
|
6
|
+
minimum?: number | null;
|
|
7
|
+
maximum?: number | null;
|
|
8
|
+
default?: unknown;
|
|
9
|
+
descriptionRu?: string | null;
|
|
10
|
+
}
|
|
11
|
+
export interface RegistryOp {
|
|
12
|
+
id: string;
|
|
13
|
+
method: string;
|
|
14
|
+
path: string;
|
|
15
|
+
tag: string;
|
|
16
|
+
summaryRu: string;
|
|
17
|
+
descriptionRu?: string | null;
|
|
18
|
+
pathParams: string[];
|
|
19
|
+
queryParams: RegistryQueryParam[];
|
|
20
|
+
requestContentType?: string | null;
|
|
21
|
+
requestSchemaRef?: string | null;
|
|
22
|
+
responseSchemaRef?: string | null;
|
|
23
|
+
paginated: boolean;
|
|
24
|
+
itemsProp?: string | null;
|
|
25
|
+
}
|
|
26
|
+
export interface Registry {
|
|
27
|
+
specTitle: string;
|
|
28
|
+
specVersion: string;
|
|
29
|
+
opsCount: number;
|
|
30
|
+
ops: Record<string, RegistryOp>;
|
|
31
|
+
}
|
|
32
|
+
export declare function getRegistry(): Registry;
|
|
33
|
+
export declare function getOp(operationId: string): RegistryOp;
|
|
34
|
+
export declare function loadSpec(): any;
|
package/dist/registry.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Access to the generated operation registry (generated/registry.json) and
|
|
3
|
+
* the bundled OpenAPI spec (generated/spec.json). Both are loaded lazily via
|
|
4
|
+
* readFileSync (no JSON import attributes — node 20 compat) and cached.
|
|
5
|
+
*/
|
|
6
|
+
import { readFileSync } from "node:fs";
|
|
7
|
+
let cachedRegistry;
|
|
8
|
+
let cachedSpec;
|
|
9
|
+
export function getRegistry() {
|
|
10
|
+
cachedRegistry ??= JSON.parse(readFileSync(new URL("./generated/registry.json", import.meta.url), "utf8"));
|
|
11
|
+
return cachedRegistry;
|
|
12
|
+
}
|
|
13
|
+
export function getOp(operationId) {
|
|
14
|
+
const op = getRegistry().ops[operationId];
|
|
15
|
+
if (!op)
|
|
16
|
+
throw new Error(`Unknown operationId: ${operationId}`);
|
|
17
|
+
return op;
|
|
18
|
+
}
|
|
19
|
+
export function loadSpec() {
|
|
20
|
+
cachedSpec ??= JSON.parse(readFileSync(new URL("./generated/spec.json", import.meta.url), "utf8"));
|
|
21
|
+
return cachedSpec;
|
|
22
|
+
}
|
package/dist/validate.js
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Request-body validation (Ajv) and schema resolution helpers driven by the
|
|
3
|
+
* generated operation registry and the bundled OpenAPI spec.
|
|
4
|
+
*/
|
|
5
|
+
import AjvImport, { Ajv as AjvNamed } from "ajv";
|
|
6
|
+
import { getOp, loadSpec } from "./registry.js";
|
|
7
|
+
// ajv v8 is CJS; depending on the loader the class may sit on the named
|
|
8
|
+
// export, the default export, or `.default` of the default export.
|
|
9
|
+
const AjvClass = AjvNamed ??
|
|
10
|
+
AjvImport.default ??
|
|
11
|
+
AjvImport;
|
|
12
|
+
const REF_DEPTH_CAP = 30;
|
|
13
|
+
let ajvInstance;
|
|
14
|
+
let transformedSchemas;
|
|
15
|
+
const validatorCache = new Map();
|
|
16
|
+
/**
|
|
17
|
+
* OpenAPI 3.0 `nullable: true` is not JSON Schema; rewrite it into a type
|
|
18
|
+
* union with "null" (and extend enums) so Ajv validates it correctly.
|
|
19
|
+
*/
|
|
20
|
+
function transformNullable(node) {
|
|
21
|
+
if (Array.isArray(node))
|
|
22
|
+
return node.map(transformNullable);
|
|
23
|
+
if (node === null || typeof node !== "object")
|
|
24
|
+
return node;
|
|
25
|
+
const src = node;
|
|
26
|
+
const out = {};
|
|
27
|
+
for (const [key, value] of Object.entries(src)) {
|
|
28
|
+
// Only drop the OpenAPI keyword; a property literally named "nullable"
|
|
29
|
+
// inside a `properties` map holds an object, not a boolean.
|
|
30
|
+
if (key === "nullable" && typeof value === "boolean")
|
|
31
|
+
continue;
|
|
32
|
+
out[key] = transformNullable(value);
|
|
33
|
+
}
|
|
34
|
+
if (src.nullable === true) {
|
|
35
|
+
if (typeof out.type === "string")
|
|
36
|
+
out.type = [out.type, "null"];
|
|
37
|
+
else if (Array.isArray(out.type)) {
|
|
38
|
+
if (!out.type.includes("null"))
|
|
39
|
+
out.type = [...out.type, "null"];
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
// No sibling `type` (allOf/$ref form, e.g. {allOf:[{$ref:X}],nullable:true}):
|
|
43
|
+
// widening `type` is impossible, so wrap the whole schema instead.
|
|
44
|
+
return { anyOf: [{ type: "null" }, out] };
|
|
45
|
+
}
|
|
46
|
+
if (Array.isArray(out.enum) && !out.enum.includes(null)) {
|
|
47
|
+
out.enum = [...out.enum, null];
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return out;
|
|
51
|
+
}
|
|
52
|
+
function getTransformedSchemas() {
|
|
53
|
+
transformedSchemas ??= transformNullable(loadSpec().components?.schemas ?? {});
|
|
54
|
+
return transformedSchemas;
|
|
55
|
+
}
|
|
56
|
+
function getAjv() {
|
|
57
|
+
ajvInstance ??= new AjvClass({
|
|
58
|
+
strict: false,
|
|
59
|
+
allErrors: true,
|
|
60
|
+
validateFormats: false,
|
|
61
|
+
});
|
|
62
|
+
return ajvInstance;
|
|
63
|
+
}
|
|
64
|
+
function getValidator(operationId, schemaRef) {
|
|
65
|
+
const cached = validatorCache.get(operationId);
|
|
66
|
+
if (cached)
|
|
67
|
+
return cached;
|
|
68
|
+
// Wrap the ref together with all (nullable-transformed) component schemas
|
|
69
|
+
// so internal "#/components/schemas/X" pointers resolve locally.
|
|
70
|
+
const validate = getAjv().compile({
|
|
71
|
+
$ref: schemaRef,
|
|
72
|
+
components: { schemas: getTransformedSchemas() },
|
|
73
|
+
});
|
|
74
|
+
validatorCache.set(operationId, validate);
|
|
75
|
+
return validate;
|
|
76
|
+
}
|
|
77
|
+
export function validateRequestBody(operationId, body) {
|
|
78
|
+
const op = getOp(operationId);
|
|
79
|
+
if (!op.requestSchemaRef)
|
|
80
|
+
return { valid: true, errors: [] };
|
|
81
|
+
const validate = getValidator(operationId, op.requestSchemaRef);
|
|
82
|
+
if (validate(body))
|
|
83
|
+
return { valid: true, errors: [] };
|
|
84
|
+
const errors = (validate.errors ?? []).map((e) => `${e.instancePath || "(root)"}: ${e.message ?? "invalid"}`);
|
|
85
|
+
return { valid: false, errors };
|
|
86
|
+
}
|
|
87
|
+
function resolveRefTarget(ref) {
|
|
88
|
+
if (!ref.startsWith("#/")) {
|
|
89
|
+
throw new Error(`Unsupported external $ref: ${ref}`);
|
|
90
|
+
}
|
|
91
|
+
let node = loadSpec();
|
|
92
|
+
for (const rawSegment of ref.slice(2).split("/")) {
|
|
93
|
+
const segment = rawSegment.replace(/~1/g, "/").replace(/~0/g, "~");
|
|
94
|
+
node = node?.[segment];
|
|
95
|
+
if (node === undefined)
|
|
96
|
+
throw new Error(`Unresolvable $ref: ${ref}`);
|
|
97
|
+
}
|
|
98
|
+
return node;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Inline every $ref. A ref already being expanded on the current branch (or
|
|
102
|
+
* expansion beyond the depth cap) is replaced with {"$circular": "<ref>"}.
|
|
103
|
+
*/
|
|
104
|
+
function deref(node, refStack) {
|
|
105
|
+
if (Array.isArray(node))
|
|
106
|
+
return node.map((item) => deref(item, refStack));
|
|
107
|
+
if (node === null || typeof node !== "object")
|
|
108
|
+
return node;
|
|
109
|
+
const src = node;
|
|
110
|
+
if (typeof src.$ref === "string") {
|
|
111
|
+
const ref = src.$ref;
|
|
112
|
+
if (refStack.includes(ref) || refStack.length >= REF_DEPTH_CAP) {
|
|
113
|
+
return { $circular: ref };
|
|
114
|
+
}
|
|
115
|
+
return deref(resolveRefTarget(ref), [...refStack, ref]);
|
|
116
|
+
}
|
|
117
|
+
const out = {};
|
|
118
|
+
for (const [key, value] of Object.entries(src)) {
|
|
119
|
+
out[key] = deref(value, refStack);
|
|
120
|
+
}
|
|
121
|
+
return out;
|
|
122
|
+
}
|
|
123
|
+
export function resolveOperationSchema(operationId) {
|
|
124
|
+
const op = getOp(operationId);
|
|
125
|
+
const result = {};
|
|
126
|
+
if (op.requestSchemaRef) {
|
|
127
|
+
result.request = deref({ $ref: op.requestSchemaRef }, []);
|
|
128
|
+
}
|
|
129
|
+
if (op.responseSchemaRef) {
|
|
130
|
+
result.response = deref({ $ref: op.responseSchemaRef }, []);
|
|
131
|
+
}
|
|
132
|
+
return result;
|
|
133
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "yandex-kit-core",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Typed client for the Yandex KIT API, driven by the official OpenAPI spec",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "dist/index.js",
|
|
8
|
+
"types": "dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js",
|
|
13
|
+
"default": "./dist/index.js"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist"
|
|
18
|
+
],
|
|
19
|
+
"engines": {
|
|
20
|
+
"node": ">=20"
|
|
21
|
+
},
|
|
22
|
+
"scripts": {
|
|
23
|
+
"build": "rm -rf dist && tsc -p tsconfig.json && mkdir -p dist/generated && cp src/generated/*.json dist/generated/",
|
|
24
|
+
"typecheck": "tsc -p tsconfig.check.json",
|
|
25
|
+
"test": "node --import tsx --test $(find src -name '*.test.ts')",
|
|
26
|
+
"prepare": "npm run build"
|
|
27
|
+
},
|
|
28
|
+
"keywords": [
|
|
29
|
+
"yandex",
|
|
30
|
+
"kit",
|
|
31
|
+
"e-commerce",
|
|
32
|
+
"api-client",
|
|
33
|
+
"openapi"
|
|
34
|
+
],
|
|
35
|
+
"repository": {
|
|
36
|
+
"type": "git",
|
|
37
|
+
"url": "git+https://github.com/ztemerbekov/a1-yandex-kit-skills.git",
|
|
38
|
+
"directory": "packages/core"
|
|
39
|
+
},
|
|
40
|
+
"homepage": "https://github.com/ztemerbekov/a1-yandex-kit-skills#readme",
|
|
41
|
+
"bugs": {
|
|
42
|
+
"url": "https://github.com/ztemerbekov/a1-yandex-kit-skills/issues"
|
|
43
|
+
},
|
|
44
|
+
"dependencies": {
|
|
45
|
+
"ajv": "^8.20.0"
|
|
46
|
+
}
|
|
47
|
+
}
|