@unispechq/unispec-core 0.2.9 → 0.2.10

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/dist/cjs/index.js CHANGED
@@ -17,6 +17,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
17
17
  __exportStar(require("./types/index.js"), exports);
18
18
  __exportStar(require("./loader/index.js"), exports);
19
19
  __exportStar(require("./validator/index.js"), exports);
20
+ __exportStar(require("./schemas/index.js"), exports);
20
21
  __exportStar(require("./normalizer/index.js"), exports);
21
22
  __exportStar(require("./diff/index.js"), exports);
22
23
  __exportStar(require("./converters/index.js"), exports);
@@ -0,0 +1,131 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.resolveSchemaRef = resolveSchemaRef;
4
+ exports.registerSchema = registerSchema;
5
+ exports.dedupeSchemas = dedupeSchemas;
6
+ function normalizeSchemaRef(ref) {
7
+ const trimmed = ref.trim();
8
+ if (trimmed.length === 0)
9
+ return "";
10
+ const withoutHash = trimmed.startsWith("#") ? trimmed.slice(1) : trimmed;
11
+ const parts = withoutHash.split("/").filter(Boolean);
12
+ return parts.length > 0 ? parts[parts.length - 1] : withoutHash;
13
+ }
14
+ function stableStringify(value) {
15
+ if (value === null)
16
+ return "null";
17
+ const t = typeof value;
18
+ if (t === "number" || t === "boolean")
19
+ return JSON.stringify(value);
20
+ if (t === "string")
21
+ return JSON.stringify(value);
22
+ if (Array.isArray(value))
23
+ return `[${value.map(stableStringify).join(",")}]`;
24
+ if (t !== "object")
25
+ return JSON.stringify(value);
26
+ const obj = value;
27
+ const keys = Object.keys(obj).sort();
28
+ const entries = keys.map((k) => `${JSON.stringify(k)}:${stableStringify(obj[k])}`);
29
+ return `{${entries.join(",")}}`;
30
+ }
31
+ function resolveSchemaRef(doc, ref) {
32
+ const name = normalizeSchemaRef(ref);
33
+ if (!name)
34
+ return undefined;
35
+ return doc.schemas?.[name];
36
+ }
37
+ function registerSchema(doc, name, jsonSchema) {
38
+ if (!doc.schemas) {
39
+ doc.schemas = {};
40
+ }
41
+ const definition = {
42
+ jsonSchema,
43
+ };
44
+ doc.schemas[name] = definition;
45
+ return definition;
46
+ }
47
+ function updateSchemaRefs(doc, mapping) {
48
+ const rest = doc.protocols?.rest;
49
+ const websocket = doc.protocols?.websocket;
50
+ if (rest?.routes) {
51
+ for (const route of rest.routes) {
52
+ for (const params of [route.pathParams, route.queryParams, route.headers]) {
53
+ if (!params)
54
+ continue;
55
+ for (const p of params) {
56
+ if (!p.schemaRef)
57
+ continue;
58
+ const key = normalizeSchemaRef(p.schemaRef);
59
+ const next = mapping[key];
60
+ if (next && next !== key)
61
+ p.schemaRef = next;
62
+ }
63
+ }
64
+ const requestContent = route.requestBody?.content;
65
+ if (requestContent) {
66
+ for (const media of Object.values(requestContent)) {
67
+ if (!media.schemaRef)
68
+ continue;
69
+ const key = normalizeSchemaRef(media.schemaRef);
70
+ const next = mapping[key];
71
+ if (next && next !== key)
72
+ media.schemaRef = next;
73
+ }
74
+ }
75
+ const responses = route.responses;
76
+ if (responses) {
77
+ for (const resp of Object.values(responses)) {
78
+ const respContent = resp.content;
79
+ if (!respContent)
80
+ continue;
81
+ for (const media of Object.values(respContent)) {
82
+ if (!media.schemaRef)
83
+ continue;
84
+ const key = normalizeSchemaRef(media.schemaRef);
85
+ const next = mapping[key];
86
+ if (next && next !== key)
87
+ media.schemaRef = next;
88
+ }
89
+ }
90
+ }
91
+ }
92
+ }
93
+ if (websocket?.channels) {
94
+ for (const channel of websocket.channels) {
95
+ if (!channel.messages)
96
+ continue;
97
+ for (const message of channel.messages) {
98
+ if (!message.schemaRef)
99
+ continue;
100
+ const key = normalizeSchemaRef(message.schemaRef);
101
+ const next = mapping[key];
102
+ if (next && next !== key)
103
+ message.schemaRef = next;
104
+ }
105
+ }
106
+ }
107
+ }
108
+ function dedupeSchemas(doc) {
109
+ if (!doc.schemas)
110
+ return;
111
+ const names = Object.keys(doc.schemas);
112
+ const hashToName = new Map();
113
+ const renameMap = {};
114
+ for (const name of names) {
115
+ const schema = doc.schemas[name];
116
+ const hash = stableStringify(schema?.jsonSchema ?? null);
117
+ const existing = hashToName.get(hash);
118
+ if (!existing) {
119
+ hashToName.set(hash, name);
120
+ continue;
121
+ }
122
+ renameMap[name] = existing;
123
+ }
124
+ const duplicates = Object.keys(renameMap);
125
+ if (duplicates.length === 0)
126
+ return;
127
+ updateSchemaRefs(doc, renameMap);
128
+ for (const dup of duplicates) {
129
+ delete doc.schemas[dup];
130
+ }
131
+ }
package/dist/index.cjs CHANGED
@@ -17,6 +17,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
17
17
  __exportStar(require("./types/index.js"), exports);
18
18
  __exportStar(require("./loader/index.js"), exports);
19
19
  __exportStar(require("./validator/index.js"), exports);
20
+ __exportStar(require("./schemas/index.js"), exports);
20
21
  __exportStar(require("./normalizer/index.js"), exports);
21
22
  __exportStar(require("./diff/index.js"), exports);
22
23
  __exportStar(require("./converters/index.js"), exports);
package/dist/index.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  export * from "./types/index.js";
2
2
  export * from "./loader/index.js";
3
3
  export * from "./validator/index.js";
4
+ export * from "./schemas/index.js";
4
5
  export * from "./normalizer/index.js";
5
6
  export * from "./diff/index.js";
6
7
  export * from "./converters/index.js";
package/dist/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  export * from "./types/index.js";
2
2
  export * from "./loader/index.js";
3
3
  export * from "./validator/index.js";
4
+ export * from "./schemas/index.js";
4
5
  export * from "./normalizer/index.js";
5
6
  export * from "./diff/index.js";
6
7
  export * from "./converters/index.js";
@@ -0,0 +1,4 @@
1
+ import { UniSpecDocument, UniSpecSchemaDefinition } from "../types/index.js";
2
+ export declare function resolveSchemaRef(doc: UniSpecDocument, ref: string): UniSpecSchemaDefinition | undefined;
3
+ export declare function registerSchema(doc: UniSpecDocument, name: string, jsonSchema: Record<string, unknown>): UniSpecSchemaDefinition;
4
+ export declare function dedupeSchemas(doc: UniSpecDocument): void;
@@ -0,0 +1,126 @@
1
+ function normalizeSchemaRef(ref) {
2
+ const trimmed = ref.trim();
3
+ if (trimmed.length === 0)
4
+ return "";
5
+ const withoutHash = trimmed.startsWith("#") ? trimmed.slice(1) : trimmed;
6
+ const parts = withoutHash.split("/").filter(Boolean);
7
+ return parts.length > 0 ? parts[parts.length - 1] : withoutHash;
8
+ }
9
+ function stableStringify(value) {
10
+ if (value === null)
11
+ return "null";
12
+ const t = typeof value;
13
+ if (t === "number" || t === "boolean")
14
+ return JSON.stringify(value);
15
+ if (t === "string")
16
+ return JSON.stringify(value);
17
+ if (Array.isArray(value))
18
+ return `[${value.map(stableStringify).join(",")}]`;
19
+ if (t !== "object")
20
+ return JSON.stringify(value);
21
+ const obj = value;
22
+ const keys = Object.keys(obj).sort();
23
+ const entries = keys.map((k) => `${JSON.stringify(k)}:${stableStringify(obj[k])}`);
24
+ return `{${entries.join(",")}}`;
25
+ }
26
+ export function resolveSchemaRef(doc, ref) {
27
+ const name = normalizeSchemaRef(ref);
28
+ if (!name)
29
+ return undefined;
30
+ return doc.schemas?.[name];
31
+ }
32
+ export function registerSchema(doc, name, jsonSchema) {
33
+ if (!doc.schemas) {
34
+ doc.schemas = {};
35
+ }
36
+ const definition = {
37
+ jsonSchema,
38
+ };
39
+ doc.schemas[name] = definition;
40
+ return definition;
41
+ }
42
+ function updateSchemaRefs(doc, mapping) {
43
+ const rest = doc.protocols?.rest;
44
+ const websocket = doc.protocols?.websocket;
45
+ if (rest?.routes) {
46
+ for (const route of rest.routes) {
47
+ for (const params of [route.pathParams, route.queryParams, route.headers]) {
48
+ if (!params)
49
+ continue;
50
+ for (const p of params) {
51
+ if (!p.schemaRef)
52
+ continue;
53
+ const key = normalizeSchemaRef(p.schemaRef);
54
+ const next = mapping[key];
55
+ if (next && next !== key)
56
+ p.schemaRef = next;
57
+ }
58
+ }
59
+ const requestContent = route.requestBody?.content;
60
+ if (requestContent) {
61
+ for (const media of Object.values(requestContent)) {
62
+ if (!media.schemaRef)
63
+ continue;
64
+ const key = normalizeSchemaRef(media.schemaRef);
65
+ const next = mapping[key];
66
+ if (next && next !== key)
67
+ media.schemaRef = next;
68
+ }
69
+ }
70
+ const responses = route.responses;
71
+ if (responses) {
72
+ for (const resp of Object.values(responses)) {
73
+ const respContent = resp.content;
74
+ if (!respContent)
75
+ continue;
76
+ for (const media of Object.values(respContent)) {
77
+ if (!media.schemaRef)
78
+ continue;
79
+ const key = normalizeSchemaRef(media.schemaRef);
80
+ const next = mapping[key];
81
+ if (next && next !== key)
82
+ media.schemaRef = next;
83
+ }
84
+ }
85
+ }
86
+ }
87
+ }
88
+ if (websocket?.channels) {
89
+ for (const channel of websocket.channels) {
90
+ if (!channel.messages)
91
+ continue;
92
+ for (const message of channel.messages) {
93
+ if (!message.schemaRef)
94
+ continue;
95
+ const key = normalizeSchemaRef(message.schemaRef);
96
+ const next = mapping[key];
97
+ if (next && next !== key)
98
+ message.schemaRef = next;
99
+ }
100
+ }
101
+ }
102
+ }
103
+ export function dedupeSchemas(doc) {
104
+ if (!doc.schemas)
105
+ return;
106
+ const names = Object.keys(doc.schemas);
107
+ const hashToName = new Map();
108
+ const renameMap = {};
109
+ for (const name of names) {
110
+ const schema = doc.schemas[name];
111
+ const hash = stableStringify(schema?.jsonSchema ?? null);
112
+ const existing = hashToName.get(hash);
113
+ if (!existing) {
114
+ hashToName.set(hash, name);
115
+ continue;
116
+ }
117
+ renameMap[name] = existing;
118
+ }
119
+ const duplicates = Object.keys(renameMap);
120
+ if (duplicates.length === 0)
121
+ return;
122
+ updateSchemaRefs(doc, renameMap);
123
+ for (const dup of duplicates) {
124
+ delete doc.schemas[dup];
125
+ }
126
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unispechq/unispec-core",
3
- "version": "0.2.9",
3
+ "version": "0.2.10",
4
4
  "description": "Central UniSpec Core Engine providing parsing, validation, normalization, diffing, and conversion of UniSpec specs.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -48,7 +48,7 @@
48
48
  "release:major": "node scripts/release.js major"
49
49
  },
50
50
  "dependencies": {
51
- "@unispechq/unispec-schema": "^0.3.3",
51
+ "@unispechq/unispec-schema": "^0.3.4",
52
52
  "ajv": "^8.12.0"
53
53
  },
54
54
  "devDependencies": {