shokupan 0.13.0 → 0.14.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/dist/{analyzer-BOtveWL-.cjs → analyzer-BZSVGTmP.cjs} +5 -4
- package/dist/analyzer-BZSVGTmP.cjs.map +1 -0
- package/dist/{analyzer-B0fMzeIo.js → analyzer-Faojwm7c.js} +5 -4
- package/dist/analyzer-Faojwm7c.js.map +1 -0
- package/dist/{analyzer.impl-CUDO6vpn.cjs → analyzer.impl-5aCqtook.cjs} +28 -11
- package/dist/analyzer.impl-5aCqtook.cjs.map +1 -0
- package/dist/{analyzer.impl-DmHe92Oi.js → analyzer.impl-COdN69gL.js} +28 -11
- package/dist/analyzer.impl-COdN69gL.js.map +1 -0
- package/dist/ast-analyzer-worker-C3jrQ8VR.js +184 -0
- package/dist/ast-analyzer-worker-C3jrQ8VR.js.map +1 -0
- package/dist/ast-analyzer-worker-D_uYkqmY.cjs +184 -0
- package/dist/ast-analyzer-worker-D_uYkqmY.cjs.map +1 -0
- package/dist/cli.cjs +1 -1
- package/dist/cli.js +1 -1
- package/dist/context.d.ts +39 -4
- package/dist/decorators/di.d.ts +31 -0
- package/dist/decorators/hooks.d.ts +28 -0
- package/dist/decorators/http.d.ts +60 -0
- package/dist/decorators/index.d.ts +8 -0
- package/dist/decorators/mcp.d.ts +48 -0
- package/dist/decorators/util/container.d.ts +36 -0
- package/dist/decorators/websocket.d.ts +172 -0
- package/dist/index-BP7v0Hiv.cjs +12216 -0
- package/dist/index-BP7v0Hiv.cjs.map +1 -0
- package/dist/index-CUNBeZKj.js +12176 -0
- package/dist/index-CUNBeZKj.js.map +1 -0
- package/dist/index.cjs +137 -10518
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -2
- package/dist/index.js +137 -10477
- package/dist/index.js.map +1 -1
- package/dist/{json-parser-COdZ0fqY.cjs → json-parser-BA0mUgMF.cjs} +3 -3
- package/dist/json-parser-BA0mUgMF.cjs.map +1 -0
- package/dist/{json-parser-B3dnQmCC.js → json-parser-BFM-SnBR.js} +3 -3
- package/dist/json-parser-BFM-SnBR.js.map +1 -0
- package/dist/knex-DDPXR-sQ.js +218 -0
- package/dist/knex-DDPXR-sQ.js.map +1 -0
- package/dist/knex-DghF-jjm.cjs +240 -0
- package/dist/knex-DghF-jjm.cjs.map +1 -0
- package/dist/level-BU87Jbus.js +184 -0
- package/dist/level-BU87Jbus.js.map +1 -0
- package/dist/level-DNFl2n-m.cjs +184 -0
- package/dist/level-DNFl2n-m.cjs.map +1 -0
- package/dist/plugins/application/api-explorer/static/explorer-client.mjs +54 -28
- package/dist/plugins/application/asyncapi/plugin.d.ts +1 -0
- package/dist/plugins/application/asyncapi/static/asyncapi-client.mjs +22 -11
- package/dist/plugins/application/dashboard/fetch-interceptor.d.ts +3 -1
- package/dist/plugins/application/dashboard/metrics-collector.d.ts +5 -3
- package/dist/plugins/application/dashboard/plugin.d.ts +36 -3
- package/dist/plugins/application/dashboard/static/requests.js +517 -53
- package/dist/plugins/application/dashboard/static/tabs.js +2 -2
- package/dist/plugins/application/error-view/index.d.ts +25 -0
- package/dist/plugins/application/error-view/reason-phrases.d.ts +1 -0
- package/dist/plugins/application/openapi/analyzer.d.ts +3 -1
- package/dist/plugins/application/openapi/analyzer.impl.d.ts +4 -2
- package/dist/router.d.ts +56 -21
- package/dist/shokupan.d.ts +25 -11
- package/dist/sqlite-CLrcTkti.js +180 -0
- package/dist/sqlite-CLrcTkti.js.map +1 -0
- package/dist/sqlite-n7FQ6Ja6.cjs +180 -0
- package/dist/sqlite-n7FQ6Ja6.cjs.map +1 -0
- package/dist/surreal-6QONU6xa.cjs +210 -0
- package/dist/surreal-6QONU6xa.cjs.map +1 -0
- package/dist/surreal-w7DeGVI-.js +188 -0
- package/dist/surreal-w7DeGVI-.js.map +1 -0
- package/dist/util/adapter/datastore/knex.d.ts +29 -0
- package/dist/util/adapter/datastore/level.d.ts +26 -0
- package/dist/util/adapter/datastore/sqlite.d.ts +24 -0
- package/dist/util/adapter/datastore/surreal.d.ts +29 -0
- package/dist/util/adapter/datastore.d.ts +59 -0
- package/dist/util/adapter/h3.d.ts +8 -0
- package/dist/util/adapter/index.d.ts +1 -0
- package/dist/util/ast-analyzer-worker.d.ts +77 -0
- package/dist/util/ast-worker-thread.d.ts +1 -0
- package/dist/util/cookie-parser.d.ts +6 -0
- package/dist/util/env-loader.d.ts +7 -0
- package/dist/util/html.d.ts +15 -0
- package/dist/util/ide.d.ts +9 -0
- package/dist/util/logger.d.ts +25 -0
- package/dist/util/query-string.d.ts +8 -0
- package/dist/util/response-transformer.d.ts +87 -0
- package/dist/util/symbol.d.ts +1 -0
- package/dist/util/types.d.ts +116 -42
- package/dist/websocket.d.ts +163 -0
- package/package.json +27 -1
- package/dist/analyzer-B0fMzeIo.js.map +0 -1
- package/dist/analyzer-BOtveWL-.cjs.map +0 -1
- package/dist/analyzer.impl-CUDO6vpn.cjs.map +0 -1
- package/dist/analyzer.impl-DmHe92Oi.js.map +0 -1
- package/dist/json-parser-B3dnQmCC.js.map +0 -1
- package/dist/json-parser-COdZ0fqY.cjs.map +0 -1
- package/dist/plugins/application/error-view/views/error.d.ts +0 -2
- package/dist/plugins/application/error-view/views/status.d.ts +0 -2
- package/dist/util/decorators.d.ts +0 -134
- package/dist/util/di.d.ts +0 -13
- /package/dist/{util → decorators/util}/metadata.d.ts +0 -0
- /package/dist/{util → decorators/util}/stack.d.ts +0 -0
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import { c as createLogger } from "./index-CUNBeZKj.js";
|
|
2
|
+
class LevelAdapter {
|
|
3
|
+
constructor(options = {}) {
|
|
4
|
+
this.options = options;
|
|
5
|
+
if (options.db) {
|
|
6
|
+
this.db = options.db;
|
|
7
|
+
} else {
|
|
8
|
+
throw new Error("LevelAdapter requires an initialized AbstractLevel instance in options.db for now, or ensure classic-level is installed.");
|
|
9
|
+
}
|
|
10
|
+
process.on("exit", async () => {
|
|
11
|
+
await this.disconnect();
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
name = "leveldb";
|
|
15
|
+
db;
|
|
16
|
+
logger = createLogger("level-adapter");
|
|
17
|
+
async connect() {
|
|
18
|
+
if (this.db.status === "opening" || this.db.status === "open") return;
|
|
19
|
+
await this.db.open();
|
|
20
|
+
}
|
|
21
|
+
async disconnect() {
|
|
22
|
+
await this.db.close();
|
|
23
|
+
}
|
|
24
|
+
async setupSchema() {
|
|
25
|
+
if (process.env.NODE_ENV !== "test") this.logger.debug("LevelAdapter", "Clearing DB");
|
|
26
|
+
await this.db.clear();
|
|
27
|
+
if (process.env.NODE_ENV !== "test") this.logger.debug("LevelAdapter", "DB cleared");
|
|
28
|
+
}
|
|
29
|
+
getKey(table, id) {
|
|
30
|
+
return `${table}:${id}`;
|
|
31
|
+
}
|
|
32
|
+
async get(table, id) {
|
|
33
|
+
try {
|
|
34
|
+
const raw = await this.db.get(this.getKey(table, id));
|
|
35
|
+
if (raw === void 0) return null;
|
|
36
|
+
return JSON.parse(raw);
|
|
37
|
+
} catch (e) {
|
|
38
|
+
if (e.code === "LEVEL_NOT_FOUND" || e.notFound) return null;
|
|
39
|
+
throw e;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
async create(table, id, data) {
|
|
43
|
+
const key = this.getKey(table, id);
|
|
44
|
+
try {
|
|
45
|
+
const val = await this.db.get(key);
|
|
46
|
+
if (val !== void 0) {
|
|
47
|
+
this.logger.error("LevelAdapter", `Record ${key} found unexpectedly`);
|
|
48
|
+
throw new Error(`Record ${id} already exists`);
|
|
49
|
+
}
|
|
50
|
+
} catch (e) {
|
|
51
|
+
const isNotFound = e.code === "LEVEL_NOT_FOUND" || e.notFound;
|
|
52
|
+
if (!isNotFound) {
|
|
53
|
+
this.logger.error("LevelAdapter", `Create error for ${key}:`, e);
|
|
54
|
+
throw e;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
await this.db.put(key, JSON.stringify(data));
|
|
58
|
+
return data;
|
|
59
|
+
}
|
|
60
|
+
async update(table, id, data) {
|
|
61
|
+
const key = this.getKey(table, id);
|
|
62
|
+
let currentRaw;
|
|
63
|
+
try {
|
|
64
|
+
currentRaw = await this.db.get(key);
|
|
65
|
+
} catch (e) {
|
|
66
|
+
if (e.code === "LEVEL_NOT_FOUND" || e.notFound) {
|
|
67
|
+
throw new Error(`Record ${id} does not exist`);
|
|
68
|
+
}
|
|
69
|
+
throw e;
|
|
70
|
+
}
|
|
71
|
+
if (currentRaw === void 0) throw new Error(`Record ${id} does not exist`);
|
|
72
|
+
const current = JSON.parse(currentRaw);
|
|
73
|
+
const updated = { ...current, ...data };
|
|
74
|
+
await this.db.put(key, JSON.stringify(updated));
|
|
75
|
+
return updated;
|
|
76
|
+
}
|
|
77
|
+
async upsert(table, id, data) {
|
|
78
|
+
const key = this.getKey(table, id);
|
|
79
|
+
await this.db.put(key, JSON.stringify(data));
|
|
80
|
+
return data;
|
|
81
|
+
}
|
|
82
|
+
async delete(table, id) {
|
|
83
|
+
await this.db.del(this.getKey(table, id));
|
|
84
|
+
}
|
|
85
|
+
async count(table, query) {
|
|
86
|
+
const items = await this.scan(table, query);
|
|
87
|
+
return items.length;
|
|
88
|
+
}
|
|
89
|
+
async deleteMany(table, query) {
|
|
90
|
+
const items = await this.scan(table, query);
|
|
91
|
+
items.map((item) => ({ type: "del", key: this.getKey(table, item.id || item._id) }));
|
|
92
|
+
for (const item of items) {
|
|
93
|
+
}
|
|
94
|
+
const keysToDelete = [];
|
|
95
|
+
for await (const [key, value] of this.db.iterator({ gte: table + ":", lte: table + ":ÿ" })) {
|
|
96
|
+
try {
|
|
97
|
+
const data = JSON.parse(value);
|
|
98
|
+
if (this.matches(data, query)) {
|
|
99
|
+
keysToDelete.push(key);
|
|
100
|
+
}
|
|
101
|
+
} catch {
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
if (query?.sort || query?.limit) {
|
|
105
|
+
for (const key of keysToDelete) {
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
let matches = [];
|
|
109
|
+
for await (const [key, value] of this.db.iterator({ gte: table + ":", lte: table + ":ÿ" })) {
|
|
110
|
+
try {
|
|
111
|
+
const data = JSON.parse(value);
|
|
112
|
+
if (this.matches(data, query)) {
|
|
113
|
+
matches.push({ key, data });
|
|
114
|
+
}
|
|
115
|
+
} catch {
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
matches = this.applySortLimit(matches, query);
|
|
119
|
+
if (matches.length) {
|
|
120
|
+
await this.db.batch(matches.map((m) => ({ type: "del", key: m.key })));
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
async findMany(table, query) {
|
|
124
|
+
let matches = [];
|
|
125
|
+
for await (const [key, value] of this.db.iterator({ gte: table + ":", lte: table + ":ÿ" })) {
|
|
126
|
+
try {
|
|
127
|
+
const data = JSON.parse(value);
|
|
128
|
+
if (this.matches(data, query)) {
|
|
129
|
+
matches.push({ key, data });
|
|
130
|
+
}
|
|
131
|
+
} catch {
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
matches = this.applySortLimit(matches, query);
|
|
135
|
+
return matches.map((m) => m.data);
|
|
136
|
+
}
|
|
137
|
+
async scan(table, query) {
|
|
138
|
+
return this.findMany(table, query);
|
|
139
|
+
}
|
|
140
|
+
matches(data, query) {
|
|
141
|
+
if (!query) return true;
|
|
142
|
+
if (query.where) {
|
|
143
|
+
for (const [k, v] of Object.entries(query.where)) {
|
|
144
|
+
if (data[k] !== v) return false;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
if (query.lt) {
|
|
148
|
+
for (const [k, v] of Object.entries(query.lt)) {
|
|
149
|
+
if (!(data[k] < v)) return false;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
if (query.gt) {
|
|
153
|
+
for (const [k, v] of Object.entries(query.gt)) {
|
|
154
|
+
if (!(data[k] > v)) return false;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
return true;
|
|
158
|
+
}
|
|
159
|
+
applySortLimit(items, query) {
|
|
160
|
+
if (!query) return items;
|
|
161
|
+
if (query.sort) {
|
|
162
|
+
items.sort((a, b) => {
|
|
163
|
+
for (const [k, dir] of Object.entries(query.sort)) {
|
|
164
|
+
const av = a.data[k];
|
|
165
|
+
const bv = b.data[k];
|
|
166
|
+
if (av < bv) return dir === "asc" ? -1 : 1;
|
|
167
|
+
if (av > bv) return dir === "asc" ? 1 : -1;
|
|
168
|
+
}
|
|
169
|
+
return 0;
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
if (query.offset) {
|
|
173
|
+
items = items.slice(query.offset);
|
|
174
|
+
}
|
|
175
|
+
if (query.limit) {
|
|
176
|
+
items = items.slice(0, query.limit);
|
|
177
|
+
}
|
|
178
|
+
return items;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
export {
|
|
182
|
+
LevelAdapter
|
|
183
|
+
};
|
|
184
|
+
//# sourceMappingURL=level-BU87Jbus.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"level-BU87Jbus.js","sources":["../src/util/adapter/datastore/level.ts"],"sourcesContent":["import type { AbstractLevel } from 'abstract-level';\nimport { createLogger } from '../../logger';\nimport type { DatastoreAdapter, QueryOptions } from '../datastore';\n\nexport class LevelAdapter implements DatastoreAdapter {\n name = 'leveldb';\n private db: AbstractLevel<any, string, string>;\n private logger = createLogger('level-adapter');\n\n constructor(\n private options: { location?: string, db?: any; } = {}\n ) {\n if (options.db) {\n this.db = options.db;\n } else {\n // Dynamic import to avoid hard dependency if not used\n // This expects user to have installed classic-level\n // We can't easily dynamically new it without knowing the module name or having it passed.\n // For now, we assume options.db is passed OR we try to require 'classic-level'\n // But usually adapters inject the instance or factory.\n // We'll try to import classic-level if location is provided.\n throw new Error(\"LevelAdapter requires an initialized AbstractLevel instance in options.db for now, or ensure classic-level is installed.\");\n }\n\n process.on(\"exit\", async () => {\n await this.disconnect();\n });\n }\n\n async connect(): Promise<void> {\n if (this.db.status === 'opening' || this.db.status === 'open') return;\n await this.db.open();\n }\n\n async disconnect(): Promise<void> {\n await this.db.close();\n }\n\n async setupSchema(): Promise<void> {\n // Ensure fresh state for tests that might reuse instances (though we try not to)\n // Ensure fresh state for tests that might reuse instances (though we try not to)\n if (process.env.NODE_ENV !== 'test') this.logger.debug('LevelAdapter', \"Clearing DB\");\n await this.db.clear();\n if (process.env.NODE_ENV !== 'test') this.logger.debug('LevelAdapter', \"DB cleared\");\n }\n\n private getKey(table: string, id: string) {\n return `${table}:${id}`;\n }\n\n async get<T>(table: string, id: string): Promise<T | null> {\n try {\n const raw = await this.db.get(this.getKey(table, id));\n if (raw === undefined) return null;\n return JSON.parse(raw);\n } catch (e: any) {\n if (e.code === 'LEVEL_NOT_FOUND' || e.notFound) return null;\n throw e;\n }\n }\n\n async create<T>(table: string, id: string, data: T): Promise<T> {\n const key = this.getKey(table, id);\n try {\n const val = await this.db.get(key);\n if (val !== undefined) {\n this.logger.error('LevelAdapter', `Record ${key} found unexpectedly`);\n throw new Error(`Record ${id} already exists`);\n }\n } catch (e: any) {\n // Check for various not found error codes/flags\n const isNotFound = e.code === 'LEVEL_NOT_FOUND' || e.notFound;\n if (!isNotFound) {\n this.logger.error('LevelAdapter', `Create error for ${key}:`, e);\n throw e;\n }\n }\n\n await this.db.put(key, JSON.stringify(data));\n return data;\n }\n\n async update<T>(table: string, id: string, data: Partial<T>): Promise<T> {\n const key = this.getKey(table, id);\n let currentRaw: string | undefined;\n try {\n currentRaw = await this.db.get(key);\n } catch (e: any) {\n if (e.code === 'LEVEL_NOT_FOUND' || e.notFound) {\n throw new Error(`Record ${id} does not exist`);\n }\n throw e;\n }\n\n if (currentRaw === undefined) throw new Error(`Record ${id} does not exist`);\n\n const current = JSON.parse(currentRaw);\n const updated = { ...current, ...data };\n await this.db.put(key, JSON.stringify(updated));\n return updated;\n }\n\n async upsert<T>(table: string, id: string, data: T): Promise<T> {\n const key = this.getKey(table, id);\n // LevelDB put IS upsert basically (overwrite)\n // But if we want merge, we need read first. \n // Surreal upsert replaces content? Yes.\n await this.db.put(key, JSON.stringify(data));\n return data;\n }\n\n async delete(table: string, id: string): Promise<void> {\n await this.db.del(this.getKey(table, id));\n }\n\n async count(table: string, query?: QueryOptions): Promise<number> {\n // Full scan required if internal implementation\n // Optimization: Maintain counters? Too complex for generic adapter.\n // Scan keys for table prefix\n const items = await this.scan(table, query);\n return items.length;\n }\n\n async deleteMany(table: string, query?: QueryOptions): Promise<void> {\n const items = await this.scan(table, query);\n // Batch delete\n const ops = items.map(item => ({ type: 'del' as const, key: this.getKey(table, (item as any).id || (item as any)._id) }));\n // Wait, we need the ID. \n // If stored data has ID, good. If not, we need to return keys from scan.\n // Let's make scan return keys?\n\n // Re-implement scan to return keys+values or just support delete loop.\n // Since we don't have atomic batch delete by query in Level without keys.\n\n // Let's assume scan returns objects and we rely on ID being in the object (Shokupan convention).\n // Or we parse key.\n // Key format: table:id.\n\n // Impl:\n for (const item of items) {\n // Extract ID from object or re-scan keys? \n // Ideally scan should return { key, value }.\n // But existing findMany returns T[].\n\n // If we really need robust deleteMany, we need scan to return keys.\n // We can iterate generic iterator.\n }\n\n // Simple generic delete:\n // This is inefficient but functional for small datasets (like failed requests buffer).\n // Better leveldb usage would be separate indexes.\n\n // Re-scan finding keys\n const keysToDelete: string[] = [];\n for await (const [key, value] of this.db.iterator({ gte: table + ':', lte: table + ':\\xFF' })) {\n try {\n const data = JSON.parse(value);\n if (this.matches(data, query)) {\n keysToDelete.push(key);\n }\n } catch { }\n }\n\n // Apply limit/sort? \n // Applying limit/sort on deletions matches logic in other adapters.\n // If sort is needed, we must fetch all matching, sort, slice, then delete.\n\n if (query?.sort || query?.limit) {\n // We need full objects to sort\n const candidates: { key: string, data: any; }[] = [];\n for (const key of keysToDelete) {\n // We already parsed it above but didn't keep it.\n // Rescan properly:\n }\n // Let's combine logic.\n }\n\n // Proper implementation:\n let matches: { key: string, data: any; }[] = [];\n for await (const [key, value] of this.db.iterator({ gte: table + ':', lte: table + ':\\xFF' })) {\n try {\n const data = JSON.parse(value);\n if (this.matches(data, query)) {\n matches.push({ key, data });\n }\n } catch { }\n }\n\n matches = this.applySortLimit(matches, query);\n\n if (matches.length) {\n await this.db.batch(matches.map(m => ({ type: 'del', key: m.key })));\n }\n }\n\n async findMany<T>(table: string, query?: QueryOptions): Promise<T[]> {\n let matches: { key: string, data: T; }[] = [];\n for await (const [key, value] of this.db.iterator({ gte: table + ':', lte: table + ':\\xFF' })) {\n try {\n const data = JSON.parse(value);\n if (this.matches(data, query)) {\n matches.push({ key, data });\n }\n } catch { }\n }\n\n matches = this.applySortLimit(matches, query);\n return matches.map(m => m.data);\n }\n\n private async scan<T>(table: string, query?: QueryOptions): Promise<T[]> {\n return this.findMany(table, query);\n }\n\n private matches(data: any, query?: QueryOptions): boolean {\n if (!query) return true;\n if (query.where) {\n for (const [k, v] of Object.entries(query.where)) {\n if (data[k] !== v) return false;\n }\n }\n if (query.lt) {\n for (const [k, v] of Object.entries(query.lt)) {\n if (!(data[k] < (v as any))) return false;\n }\n }\n if (query.gt) {\n for (const [k, v] of Object.entries(query.gt)) {\n if (!(data[k] > (v as any))) return false;\n }\n }\n return true;\n }\n\n private applySortLimit<T>(items: { key: string, data: T; }[], query?: QueryOptions): { key: string, data: T; }[] {\n if (!query) return items;\n\n if (query.sort) {\n items.sort((a, b) => {\n for (const [k, dir] of Object.entries(query.sort!)) {\n const av = (a.data as any)[k];\n const bv = (b.data as any)[k];\n if (av < bv) return dir === 'asc' ? -1 : 1;\n if (av > bv) return dir === 'asc' ? 1 : -1;\n }\n return 0;\n });\n }\n\n if (query.offset) {\n items = items.slice(query.offset);\n }\n\n if (query.limit) {\n items = items.slice(0, query.limit);\n }\n\n return items;\n }\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const index = require("./index-BP7v0Hiv.cjs");
|
|
4
|
+
class LevelAdapter {
|
|
5
|
+
constructor(options = {}) {
|
|
6
|
+
this.options = options;
|
|
7
|
+
if (options.db) {
|
|
8
|
+
this.db = options.db;
|
|
9
|
+
} else {
|
|
10
|
+
throw new Error("LevelAdapter requires an initialized AbstractLevel instance in options.db for now, or ensure classic-level is installed.");
|
|
11
|
+
}
|
|
12
|
+
process.on("exit", async () => {
|
|
13
|
+
await this.disconnect();
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
name = "leveldb";
|
|
17
|
+
db;
|
|
18
|
+
logger = index.createLogger("level-adapter");
|
|
19
|
+
async connect() {
|
|
20
|
+
if (this.db.status === "opening" || this.db.status === "open") return;
|
|
21
|
+
await this.db.open();
|
|
22
|
+
}
|
|
23
|
+
async disconnect() {
|
|
24
|
+
await this.db.close();
|
|
25
|
+
}
|
|
26
|
+
async setupSchema() {
|
|
27
|
+
if (process.env.NODE_ENV !== "test") this.logger.debug("LevelAdapter", "Clearing DB");
|
|
28
|
+
await this.db.clear();
|
|
29
|
+
if (process.env.NODE_ENV !== "test") this.logger.debug("LevelAdapter", "DB cleared");
|
|
30
|
+
}
|
|
31
|
+
getKey(table, id) {
|
|
32
|
+
return `${table}:${id}`;
|
|
33
|
+
}
|
|
34
|
+
async get(table, id) {
|
|
35
|
+
try {
|
|
36
|
+
const raw = await this.db.get(this.getKey(table, id));
|
|
37
|
+
if (raw === void 0) return null;
|
|
38
|
+
return JSON.parse(raw);
|
|
39
|
+
} catch (e) {
|
|
40
|
+
if (e.code === "LEVEL_NOT_FOUND" || e.notFound) return null;
|
|
41
|
+
throw e;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
async create(table, id, data) {
|
|
45
|
+
const key = this.getKey(table, id);
|
|
46
|
+
try {
|
|
47
|
+
const val = await this.db.get(key);
|
|
48
|
+
if (val !== void 0) {
|
|
49
|
+
this.logger.error("LevelAdapter", `Record ${key} found unexpectedly`);
|
|
50
|
+
throw new Error(`Record ${id} already exists`);
|
|
51
|
+
}
|
|
52
|
+
} catch (e) {
|
|
53
|
+
const isNotFound = e.code === "LEVEL_NOT_FOUND" || e.notFound;
|
|
54
|
+
if (!isNotFound) {
|
|
55
|
+
this.logger.error("LevelAdapter", `Create error for ${key}:`, e);
|
|
56
|
+
throw e;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
await this.db.put(key, JSON.stringify(data));
|
|
60
|
+
return data;
|
|
61
|
+
}
|
|
62
|
+
async update(table, id, data) {
|
|
63
|
+
const key = this.getKey(table, id);
|
|
64
|
+
let currentRaw;
|
|
65
|
+
try {
|
|
66
|
+
currentRaw = await this.db.get(key);
|
|
67
|
+
} catch (e) {
|
|
68
|
+
if (e.code === "LEVEL_NOT_FOUND" || e.notFound) {
|
|
69
|
+
throw new Error(`Record ${id} does not exist`);
|
|
70
|
+
}
|
|
71
|
+
throw e;
|
|
72
|
+
}
|
|
73
|
+
if (currentRaw === void 0) throw new Error(`Record ${id} does not exist`);
|
|
74
|
+
const current = JSON.parse(currentRaw);
|
|
75
|
+
const updated = { ...current, ...data };
|
|
76
|
+
await this.db.put(key, JSON.stringify(updated));
|
|
77
|
+
return updated;
|
|
78
|
+
}
|
|
79
|
+
async upsert(table, id, data) {
|
|
80
|
+
const key = this.getKey(table, id);
|
|
81
|
+
await this.db.put(key, JSON.stringify(data));
|
|
82
|
+
return data;
|
|
83
|
+
}
|
|
84
|
+
async delete(table, id) {
|
|
85
|
+
await this.db.del(this.getKey(table, id));
|
|
86
|
+
}
|
|
87
|
+
async count(table, query) {
|
|
88
|
+
const items = await this.scan(table, query);
|
|
89
|
+
return items.length;
|
|
90
|
+
}
|
|
91
|
+
async deleteMany(table, query) {
|
|
92
|
+
const items = await this.scan(table, query);
|
|
93
|
+
items.map((item) => ({ type: "del", key: this.getKey(table, item.id || item._id) }));
|
|
94
|
+
for (const item of items) {
|
|
95
|
+
}
|
|
96
|
+
const keysToDelete = [];
|
|
97
|
+
for await (const [key, value] of this.db.iterator({ gte: table + ":", lte: table + ":ÿ" })) {
|
|
98
|
+
try {
|
|
99
|
+
const data = JSON.parse(value);
|
|
100
|
+
if (this.matches(data, query)) {
|
|
101
|
+
keysToDelete.push(key);
|
|
102
|
+
}
|
|
103
|
+
} catch {
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
if (query?.sort || query?.limit) {
|
|
107
|
+
for (const key of keysToDelete) {
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
let matches = [];
|
|
111
|
+
for await (const [key, value] of this.db.iterator({ gte: table + ":", lte: table + ":ÿ" })) {
|
|
112
|
+
try {
|
|
113
|
+
const data = JSON.parse(value);
|
|
114
|
+
if (this.matches(data, query)) {
|
|
115
|
+
matches.push({ key, data });
|
|
116
|
+
}
|
|
117
|
+
} catch {
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
matches = this.applySortLimit(matches, query);
|
|
121
|
+
if (matches.length) {
|
|
122
|
+
await this.db.batch(matches.map((m) => ({ type: "del", key: m.key })));
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
async findMany(table, query) {
|
|
126
|
+
let matches = [];
|
|
127
|
+
for await (const [key, value] of this.db.iterator({ gte: table + ":", lte: table + ":ÿ" })) {
|
|
128
|
+
try {
|
|
129
|
+
const data = JSON.parse(value);
|
|
130
|
+
if (this.matches(data, query)) {
|
|
131
|
+
matches.push({ key, data });
|
|
132
|
+
}
|
|
133
|
+
} catch {
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
matches = this.applySortLimit(matches, query);
|
|
137
|
+
return matches.map((m) => m.data);
|
|
138
|
+
}
|
|
139
|
+
async scan(table, query) {
|
|
140
|
+
return this.findMany(table, query);
|
|
141
|
+
}
|
|
142
|
+
matches(data, query) {
|
|
143
|
+
if (!query) return true;
|
|
144
|
+
if (query.where) {
|
|
145
|
+
for (const [k, v] of Object.entries(query.where)) {
|
|
146
|
+
if (data[k] !== v) return false;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
if (query.lt) {
|
|
150
|
+
for (const [k, v] of Object.entries(query.lt)) {
|
|
151
|
+
if (!(data[k] < v)) return false;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
if (query.gt) {
|
|
155
|
+
for (const [k, v] of Object.entries(query.gt)) {
|
|
156
|
+
if (!(data[k] > v)) return false;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return true;
|
|
160
|
+
}
|
|
161
|
+
applySortLimit(items, query) {
|
|
162
|
+
if (!query) return items;
|
|
163
|
+
if (query.sort) {
|
|
164
|
+
items.sort((a, b) => {
|
|
165
|
+
for (const [k, dir] of Object.entries(query.sort)) {
|
|
166
|
+
const av = a.data[k];
|
|
167
|
+
const bv = b.data[k];
|
|
168
|
+
if (av < bv) return dir === "asc" ? -1 : 1;
|
|
169
|
+
if (av > bv) return dir === "asc" ? 1 : -1;
|
|
170
|
+
}
|
|
171
|
+
return 0;
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
if (query.offset) {
|
|
175
|
+
items = items.slice(query.offset);
|
|
176
|
+
}
|
|
177
|
+
if (query.limit) {
|
|
178
|
+
items = items.slice(0, query.limit);
|
|
179
|
+
}
|
|
180
|
+
return items;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
exports.LevelAdapter = LevelAdapter;
|
|
184
|
+
//# sourceMappingURL=level-DNFl2n-m.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"level-DNFl2n-m.cjs","sources":["../src/util/adapter/datastore/level.ts"],"sourcesContent":["import type { AbstractLevel } from 'abstract-level';\nimport { createLogger } from '../../logger';\nimport type { DatastoreAdapter, QueryOptions } from '../datastore';\n\nexport class LevelAdapter implements DatastoreAdapter {\n name = 'leveldb';\n private db: AbstractLevel<any, string, string>;\n private logger = createLogger('level-adapter');\n\n constructor(\n private options: { location?: string, db?: any; } = {}\n ) {\n if (options.db) {\n this.db = options.db;\n } else {\n // Dynamic import to avoid hard dependency if not used\n // This expects user to have installed classic-level\n // We can't easily dynamically new it without knowing the module name or having it passed.\n // For now, we assume options.db is passed OR we try to require 'classic-level'\n // But usually adapters inject the instance or factory.\n // We'll try to import classic-level if location is provided.\n throw new Error(\"LevelAdapter requires an initialized AbstractLevel instance in options.db for now, or ensure classic-level is installed.\");\n }\n\n process.on(\"exit\", async () => {\n await this.disconnect();\n });\n }\n\n async connect(): Promise<void> {\n if (this.db.status === 'opening' || this.db.status === 'open') return;\n await this.db.open();\n }\n\n async disconnect(): Promise<void> {\n await this.db.close();\n }\n\n async setupSchema(): Promise<void> {\n // Ensure fresh state for tests that might reuse instances (though we try not to)\n // Ensure fresh state for tests that might reuse instances (though we try not to)\n if (process.env.NODE_ENV !== 'test') this.logger.debug('LevelAdapter', \"Clearing DB\");\n await this.db.clear();\n if (process.env.NODE_ENV !== 'test') this.logger.debug('LevelAdapter', \"DB cleared\");\n }\n\n private getKey(table: string, id: string) {\n return `${table}:${id}`;\n }\n\n async get<T>(table: string, id: string): Promise<T | null> {\n try {\n const raw = await this.db.get(this.getKey(table, id));\n if (raw === undefined) return null;\n return JSON.parse(raw);\n } catch (e: any) {\n if (e.code === 'LEVEL_NOT_FOUND' || e.notFound) return null;\n throw e;\n }\n }\n\n async create<T>(table: string, id: string, data: T): Promise<T> {\n const key = this.getKey(table, id);\n try {\n const val = await this.db.get(key);\n if (val !== undefined) {\n this.logger.error('LevelAdapter', `Record ${key} found unexpectedly`);\n throw new Error(`Record ${id} already exists`);\n }\n } catch (e: any) {\n // Check for various not found error codes/flags\n const isNotFound = e.code === 'LEVEL_NOT_FOUND' || e.notFound;\n if (!isNotFound) {\n this.logger.error('LevelAdapter', `Create error for ${key}:`, e);\n throw e;\n }\n }\n\n await this.db.put(key, JSON.stringify(data));\n return data;\n }\n\n async update<T>(table: string, id: string, data: Partial<T>): Promise<T> {\n const key = this.getKey(table, id);\n let currentRaw: string | undefined;\n try {\n currentRaw = await this.db.get(key);\n } catch (e: any) {\n if (e.code === 'LEVEL_NOT_FOUND' || e.notFound) {\n throw new Error(`Record ${id} does not exist`);\n }\n throw e;\n }\n\n if (currentRaw === undefined) throw new Error(`Record ${id} does not exist`);\n\n const current = JSON.parse(currentRaw);\n const updated = { ...current, ...data };\n await this.db.put(key, JSON.stringify(updated));\n return updated;\n }\n\n async upsert<T>(table: string, id: string, data: T): Promise<T> {\n const key = this.getKey(table, id);\n // LevelDB put IS upsert basically (overwrite)\n // But if we want merge, we need read first. \n // Surreal upsert replaces content? Yes.\n await this.db.put(key, JSON.stringify(data));\n return data;\n }\n\n async delete(table: string, id: string): Promise<void> {\n await this.db.del(this.getKey(table, id));\n }\n\n async count(table: string, query?: QueryOptions): Promise<number> {\n // Full scan required if internal implementation\n // Optimization: Maintain counters? Too complex for generic adapter.\n // Scan keys for table prefix\n const items = await this.scan(table, query);\n return items.length;\n }\n\n async deleteMany(table: string, query?: QueryOptions): Promise<void> {\n const items = await this.scan(table, query);\n // Batch delete\n const ops = items.map(item => ({ type: 'del' as const, key: this.getKey(table, (item as any).id || (item as any)._id) }));\n // Wait, we need the ID. \n // If stored data has ID, good. If not, we need to return keys from scan.\n // Let's make scan return keys?\n\n // Re-implement scan to return keys+values or just support delete loop.\n // Since we don't have atomic batch delete by query in Level without keys.\n\n // Let's assume scan returns objects and we rely on ID being in the object (Shokupan convention).\n // Or we parse key.\n // Key format: table:id.\n\n // Impl:\n for (const item of items) {\n // Extract ID from object or re-scan keys? \n // Ideally scan should return { key, value }.\n // But existing findMany returns T[].\n\n // If we really need robust deleteMany, we need scan to return keys.\n // We can iterate generic iterator.\n }\n\n // Simple generic delete:\n // This is inefficient but functional for small datasets (like failed requests buffer).\n // Better leveldb usage would be separate indexes.\n\n // Re-scan finding keys\n const keysToDelete: string[] = [];\n for await (const [key, value] of this.db.iterator({ gte: table + ':', lte: table + ':\\xFF' })) {\n try {\n const data = JSON.parse(value);\n if (this.matches(data, query)) {\n keysToDelete.push(key);\n }\n } catch { }\n }\n\n // Apply limit/sort? \n // Applying limit/sort on deletions matches logic in other adapters.\n // If sort is needed, we must fetch all matching, sort, slice, then delete.\n\n if (query?.sort || query?.limit) {\n // We need full objects to sort\n const candidates: { key: string, data: any; }[] = [];\n for (const key of keysToDelete) {\n // We already parsed it above but didn't keep it.\n // Rescan properly:\n }\n // Let's combine logic.\n }\n\n // Proper implementation:\n let matches: { key: string, data: any; }[] = [];\n for await (const [key, value] of this.db.iterator({ gte: table + ':', lte: table + ':\\xFF' })) {\n try {\n const data = JSON.parse(value);\n if (this.matches(data, query)) {\n matches.push({ key, data });\n }\n } catch { }\n }\n\n matches = this.applySortLimit(matches, query);\n\n if (matches.length) {\n await this.db.batch(matches.map(m => ({ type: 'del', key: m.key })));\n }\n }\n\n async findMany<T>(table: string, query?: QueryOptions): Promise<T[]> {\n let matches: { key: string, data: T; }[] = [];\n for await (const [key, value] of this.db.iterator({ gte: table + ':', lte: table + ':\\xFF' })) {\n try {\n const data = JSON.parse(value);\n if (this.matches(data, query)) {\n matches.push({ key, data });\n }\n } catch { }\n }\n\n matches = this.applySortLimit(matches, query);\n return matches.map(m => m.data);\n }\n\n private async scan<T>(table: string, query?: QueryOptions): Promise<T[]> {\n return this.findMany(table, query);\n }\n\n private matches(data: any, query?: QueryOptions): boolean {\n if (!query) return true;\n if (query.where) {\n for (const [k, v] of Object.entries(query.where)) {\n if (data[k] !== v) return false;\n }\n }\n if (query.lt) {\n for (const [k, v] of Object.entries(query.lt)) {\n if (!(data[k] < (v as any))) return false;\n }\n }\n if (query.gt) {\n for (const [k, v] of Object.entries(query.gt)) {\n if (!(data[k] > (v as any))) return false;\n }\n }\n return true;\n }\n\n private applySortLimit<T>(items: { key: string, data: T; }[], query?: QueryOptions): { key: string, data: T; }[] {\n if (!query) return items;\n\n if (query.sort) {\n items.sort((a, b) => {\n for (const [k, dir] of Object.entries(query.sort!)) {\n const av = (a.data as any)[k];\n const bv = (b.data as any)[k];\n if (av < bv) return dir === 'asc' ? -1 : 1;\n if (av > bv) return dir === 'asc' ? 1 : -1;\n }\n return 0;\n });\n }\n\n if (query.offset) {\n items = items.slice(query.offset);\n }\n\n if (query.limit) {\n items = items.slice(0, query.limit);\n }\n\n return items;\n }\n}\n"],"names":["createLogger"],"mappings":"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"}
|
|
@@ -64,7 +64,7 @@ function renderInfoSection(info) {
|
|
|
64
64
|
const { title, description } = info;
|
|
65
65
|
return `
|
|
66
66
|
<div class="info-section">
|
|
67
|
-
<h1>${title || 'API Explorer'}</h1>
|
|
67
|
+
<h1>${escapeHtml(title || 'API Explorer')}</h1>
|
|
68
68
|
${description ? `<div class="markdown-content" data-markdown="true">${parseMarkdown(description)}</div>` : ''}
|
|
69
69
|
</div>
|
|
70
70
|
`;
|
|
@@ -74,9 +74,9 @@ function renderMiddlewareView(middleware, container) {
|
|
|
74
74
|
const html = `
|
|
75
75
|
<div class="middleware-detail-view">
|
|
76
76
|
<div class="middleware-header">
|
|
77
|
-
<h1>${middleware.name}</h1>
|
|
77
|
+
<h1>${escapeHtml(middleware.name)}</h1>
|
|
78
78
|
<div class="middleware-meta">
|
|
79
|
-
${middleware.scope ? `<span class="badge">${middleware.scope}</span>` : ''}
|
|
79
|
+
${middleware.scope ? `<span class="badge">${escapeHtml(middleware.scope)}</span>` : ''}
|
|
80
80
|
${middleware.file ? `
|
|
81
81
|
<a href="vscode://file/${middleware.file}:${middleware.startLine || 1}" class="doc-source-link">
|
|
82
82
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
@@ -139,10 +139,10 @@ function renderMiddlewareView(middleware, container) {
|
|
|
139
139
|
const route = explorerData.routes.find(r => r.path === routePath);
|
|
140
140
|
if (route) {
|
|
141
141
|
return `
|
|
142
|
-
|
|
143
|
-
<td class="col-method"><span class="badge badge-${route.method.toUpperCase()}">${route.method.toUpperCase()}</span></td>
|
|
144
|
-
<td class="col-path"><a href="#${route.op.operationId}" class="route-link">${routePath}</a></td>
|
|
145
|
-
<td class="col-desc">${route.op.summary || route.op.description || '-'}</td>
|
|
142
|
+
<tr>
|
|
143
|
+
<td class="col-method"><span class="badge badge-${escapeHtml(route.method.toUpperCase())}">${escapeHtml(route.method.toUpperCase())}</span></td>
|
|
144
|
+
<td class="col-path"><a href="#${escapeHtml(route.op.operationId)}" class="route-link">${highlightPath(routePath)}</a></td>
|
|
145
|
+
<td class="col-desc">${escapeHtml(route.op.summary || route.op.description || '-')}</td>
|
|
146
146
|
</tr>
|
|
147
147
|
`;
|
|
148
148
|
}
|
|
@@ -270,14 +270,14 @@ function renderSchema(schema, depth = 0, isResponse = false) {
|
|
|
270
270
|
return `
|
|
271
271
|
<div style="margin-left: ${indent}px;">
|
|
272
272
|
<div class="property-heading" style="display: flex; align-items: center; gap: 8px; padding: 6px 0;">
|
|
273
|
-
<div class="property-name" style="font-family: monospace; font-weight: 500; color: var(--text-primary);">${key}</div>
|
|
273
|
+
<div class="property-name" style="font-family: monospace; font-weight: 500; color: var(--text-primary);">${escapeHtml(key)}</div>
|
|
274
274
|
<span class="property-detail" style="color: var(--text-secondary); font-size: 0.85rem;">
|
|
275
|
-
<span class="property-detail-value">${propType}</span>
|
|
275
|
+
<span class="property-detail-value">${escapeHtml(propType)}</span>
|
|
276
276
|
${isUnknown ? '<span class="unknown-marker" title="Type could not be determined statically" style="color: #ff9800; margin-left: 4px;">⚠️</span>' : ''}
|
|
277
277
|
</span>
|
|
278
278
|
${badgeHtml}
|
|
279
279
|
</div>
|
|
280
|
-
${prop.description ? `<div style="color: var(--text-secondary); font-size: 0.85rem; margin-left: 0; margin-top: -4px; margin-bottom: 4px;">${prop.description}</div>` : ''}
|
|
280
|
+
${prop.description ? `<div style="color: var(--text-secondary); font-size: 0.85rem; margin-left: 0; margin-top: -4px; margin-bottom: 4px;">${escapeHtml(prop.description)}</div>` : ''}
|
|
281
281
|
${hasNested ? renderSchema(propType === 'array' ? prop.items : prop, depth + 1, isResponse) : ''}
|
|
282
282
|
</div>
|
|
283
283
|
`;
|
|
@@ -296,18 +296,37 @@ function renderSchema(schema, depth = 0, isResponse = false) {
|
|
|
296
296
|
|
|
297
297
|
return `
|
|
298
298
|
<div style="margin-left: ${indent}px; padding: 4px 0;">
|
|
299
|
-
<span class="property-detail-value" style="color: var(--text-secondary); font-family: monospace;">${type}</span>
|
|
300
|
-
${schema.format ? `<span style="color: var(--text-secondary); font-size: 0.85rem; margin-left: 6px;">(${schema.format})</span>` : ''}
|
|
301
|
-
${schema.description ? `<div style="color: var(--text-secondary); font-size: 0.85rem; margin-top: 4px;">${schema.description}</div>` : ''}
|
|
299
|
+
<span class="property-detail-value" style="color: var(--text-secondary); font-family: monospace;">${escapeHtml(type)}</span>
|
|
300
|
+
${schema.format ? `<span style="color: var(--text-secondary); font-size: 0.85rem; margin-left: 6px;">(${escapeHtml(schema.format)})</span>` : ''}
|
|
301
|
+
${schema.description ? `<div style="color: var(--text-secondary); font-size: 0.85rem; margin-top: 4px;">${escapeHtml(schema.description)}</div>` : ''}
|
|
302
302
|
</div>
|
|
303
303
|
`;
|
|
304
304
|
}
|
|
305
305
|
|
|
306
|
+
// Helper to escape HTML
|
|
307
|
+
function escapeHtml(text) {
|
|
308
|
+
if (!text) return '';
|
|
309
|
+
return String(text)
|
|
310
|
+
.replace(/&/g, "&")
|
|
311
|
+
.replace(/</g, "<")
|
|
312
|
+
.replace(/>/g, ">")
|
|
313
|
+
.replace(/"/g, """)
|
|
314
|
+
.replace(/'/g, "'");
|
|
315
|
+
}
|
|
316
|
+
|
|
306
317
|
// Helper to highlight path operators
|
|
307
318
|
function highlightPath(path) {
|
|
308
319
|
if (!path) return '';
|
|
309
320
|
|
|
310
|
-
|
|
321
|
+
// specific pattern replacements on ESCAPED string
|
|
322
|
+
// {{...}} -> <span...>...</span>
|
|
323
|
+
// :param -> <span...>...</span>
|
|
324
|
+
// * -> <span...>...</span>
|
|
325
|
+
|
|
326
|
+
// We must escape the path first to prevent XSS
|
|
327
|
+
let safePath = escapeHtml(path);
|
|
328
|
+
|
|
329
|
+
return safePath
|
|
311
330
|
// Highlight {{substitution}} patterns
|
|
312
331
|
.replace(/\{\{([^}]+)\}\}/g, '<span style="color: #4caf50;">{{$1}}</span>')
|
|
313
332
|
// Highlight :parameter patterns
|
|
@@ -321,10 +340,11 @@ function highlightPath(path) {
|
|
|
321
340
|
function renderRequestView(route, container) {
|
|
322
341
|
const { method, path, op } = route;
|
|
323
342
|
|
|
343
|
+
// Extract metadata
|
|
324
344
|
// Extract metadata
|
|
325
345
|
const source = op['x-shokupan-source'];
|
|
326
346
|
const middlewares = op['x-shokupan-middleware'] || [];
|
|
327
|
-
const summary = op.summary
|
|
347
|
+
const summary = op.summary ? escapeHtml(op.summary) : highlightPath(route.path);
|
|
328
348
|
|
|
329
349
|
// Build tabs for Request Body, Params, Auth, etc.
|
|
330
350
|
const uniqueParams = getUniqueParams(op);
|
|
@@ -337,7 +357,7 @@ function renderRequestView(route, container) {
|
|
|
337
357
|
<div class="request-panel-header">
|
|
338
358
|
<div class="request-header-main">
|
|
339
359
|
<div class="request-url-bar">
|
|
340
|
-
<span class="url-method badge-${method}">${method.toUpperCase()}</span>
|
|
360
|
+
<span class="url-method badge-${escapeHtml(method)}">${escapeHtml(method.toUpperCase())}</span>
|
|
341
361
|
<div class="url-input" style="display: flex; align-items: center; font-family: monospace; white-space: nowrap; overflow-x: auto;">${highlightPath(path)}</div>
|
|
342
362
|
</div>
|
|
343
363
|
<button class="send-btn" id="btn-send">Send</button>
|
|
@@ -422,7 +442,7 @@ function renderRequestView(route, container) {
|
|
|
422
442
|
<div class="middleware-list" style="display: flex; flex-direction: column; gap: 4px;">
|
|
423
443
|
${middlewares.map((mw, idx) => `<div style="display: flex; align-items: center; gap: 8px;">
|
|
424
444
|
<span style="font-family: monospace; color: var(--text-secondary); min-width: 20px;">${idx + 1}.</span>
|
|
425
|
-
<span class="middleware-badge" title="${mw.metadata ? JSON.stringify(mw.metadata)
|
|
445
|
+
<span class="middleware-badge" title="${mw.metadata ? escapeHtml(JSON.stringify(mw.metadata)) : ''}">${escapeHtml(mw.name)}</span>
|
|
426
446
|
</div>`).join('')}
|
|
427
447
|
</div>
|
|
428
448
|
</div>
|
|
@@ -432,8 +452,8 @@ function renderRequestView(route, container) {
|
|
|
432
452
|
<h3 style="margin-top: 0; margin-bottom: 12px; font-size: 1rem;">Request</h3>
|
|
433
453
|
<div style="display: grid; gap: 8px; font-size: 0.9rem;">
|
|
434
454
|
<div style="display: flex; gap: 8px;">
|
|
435
|
-
<span class="badge badge-${method.toUpperCase()}">${method.toUpperCase()}</span>
|
|
436
|
-
<code style="background: var(--bg-primary); padding: 2px 6px; border-radius: 4px;">${path}</code>
|
|
455
|
+
<span class="badge badge-${escapeHtml(method.toUpperCase())}">${escapeHtml(method.toUpperCase())}</span>
|
|
456
|
+
<code style="background: var(--bg-primary); padding: 2px 6px; border-radius: 4px;">${escapeHtml(path)}</code>
|
|
437
457
|
</div>
|
|
438
458
|
${op.parameters && op.parameters.length > 0 ? `
|
|
439
459
|
<div style="display: grid; grid-template-columns: 120px 1fr; gap: 8px;">
|
|
@@ -441,12 +461,12 @@ function renderRequestView(route, container) {
|
|
|
441
461
|
<div style="display: flex; flex-wrap: wrap; gap: 4px;">
|
|
442
462
|
${op.parameters.filter(p => p.in === 'query').map(p =>
|
|
443
463
|
`<span style="background: var(--bg-primary); padding: 2px 6px; border-radius: 4px; font-size: 0.85rem;">
|
|
444
|
-
<code>${p.name}</code>${p.required ? '*' : ''}
|
|
464
|
+
<code>${escapeHtml(p.name)}</code>${p.required ? '*' : ''}
|
|
445
465
|
</span>`
|
|
446
466
|
).join('')}
|
|
447
467
|
${op.parameters.filter(p => p.in === 'path').map(p =>
|
|
448
468
|
`<span style="background: var(--bg-primary); padding: 2px 6px; border-radius: 4px; font-size: 0.85rem;">
|
|
449
|
-
<code>{${p.name}}</code>
|
|
469
|
+
<code>{${escapeHtml(p.name)}}</code>
|
|
450
470
|
</span>`
|
|
451
471
|
).join('')}
|
|
452
472
|
</div>
|
|
@@ -457,7 +477,7 @@ function renderRequestView(route, container) {
|
|
|
457
477
|
<span style="color: var(--text-secondary);">Body:</span>
|
|
458
478
|
<div style="display: flex; flex-wrap: wrap; gap: 4px;">
|
|
459
479
|
${Object.keys(op.requestBody.content || {}).map(ct =>
|
|
460
|
-
`<code style="background: var(--bg-primary); padding: 2px 6px; border-radius: 4px; font-size: 0.85rem;">${ct}</code>`
|
|
480
|
+
`<code style="background: var(--bg-primary); padding: 2px 6px; border-radius: 4px; font-size: 0.85rem;">${escapeHtml(ct)}</code>`
|
|
461
481
|
).join('')}
|
|
462
482
|
</div>
|
|
463
483
|
</div>
|
|
@@ -474,13 +494,13 @@ function renderRequestView(route, container) {
|
|
|
474
494
|
return `
|
|
475
495
|
<div style="border-left: 2px solid var(--text-secondary); padding-left: 12px;">
|
|
476
496
|
<div style="display: flex; align-items: center; gap: 8px; margin-bottom: 8px;">
|
|
477
|
-
<code style="background: var(--bg-primary); padding: 2px 8px; border-radius: 4px; font-weight: bold;">${code}</code>
|
|
478
|
-
<span style="color: var(--text-secondary);">${resp.description || 'Response'}</span>
|
|
497
|
+
<code style="background: var(--bg-primary); padding: 2px 8px; border-radius: 4px; font-weight: bold;">${escapeHtml(code)}</code>
|
|
498
|
+
<span style="color: var(--text-secondary);">${escapeHtml(resp.description || 'Response')}</span>
|
|
479
499
|
</div>
|
|
480
500
|
${contentTypes.length > 0 ? `
|
|
481
501
|
<div style="display: flex; flex-wrap: wrap; gap: 4px; margin-bottom: 8px;">
|
|
482
502
|
${contentTypes.map(ct =>
|
|
483
|
-
`<code style="background: var(--bg-primary); padding: 2px 6px; border-radius: 4px; font-size: 0.8rem;">${ct}</code>`
|
|
503
|
+
`<code style="background: var(--bg-primary); padding: 2px 6px; border-radius: 4px; font-size: 0.8rem;">${escapeHtml(ct)}</code>`
|
|
484
504
|
).join('')}
|
|
485
505
|
</div>
|
|
486
506
|
` : ''}
|
|
@@ -894,9 +914,9 @@ function renderParamsTable(params) {
|
|
|
894
914
|
<div class="params-table">
|
|
895
915
|
${params.map(p => `
|
|
896
916
|
<div class="param-row">
|
|
897
|
-
<div class="param-key">${p.name}${p.required ? '*' : ''}</div>
|
|
917
|
+
<div class="param-key">${escapeHtml(p.name)}${p.required ? '*' : ''}</div>
|
|
898
918
|
<div class="param-value">
|
|
899
|
-
<input type="text" name="param-${p.name}" data-in="${p.in}" placeholder="${p.description || ''}" />
|
|
919
|
+
<input type="text" name="param-${escapeHtml(p.name)}" data-in="${p.in}" placeholder="${escapeHtml(p.description || '')}" />
|
|
900
920
|
</div>
|
|
901
921
|
</div>
|
|
902
922
|
`).join('')}
|
|
@@ -1267,7 +1287,13 @@ function parseMarkdown(text) {
|
|
|
1267
1287
|
return originalBlockquote(quote);
|
|
1268
1288
|
};
|
|
1269
1289
|
|
|
1270
|
-
|
|
1290
|
+
const html = marked.parse(text, { renderer });
|
|
1291
|
+
|
|
1292
|
+
if (typeof DOMPurify !== 'undefined') {
|
|
1293
|
+
return DOMPurify.sanitize(html);
|
|
1294
|
+
}
|
|
1295
|
+
|
|
1296
|
+
return html;
|
|
1271
1297
|
}
|
|
1272
1298
|
|
|
1273
1299
|
|
|
@@ -3,6 +3,7 @@ import { Shokupan } from '../../../shokupan';
|
|
|
3
3
|
import { DeepPartial, ShokupanPlugin, ShokupanPluginOptions } from '../../../util/types';
|
|
4
4
|
export interface AsyncApiPluginOptions {
|
|
5
5
|
path?: string;
|
|
6
|
+
serverUrl?: string;
|
|
6
7
|
spec?: DeepPartial<any>;
|
|
7
8
|
disableSourceView?: boolean;
|
|
8
9
|
}
|