@oaspect/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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 oaspect contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,14 @@
1
+ # @oaspect/core
2
+
3
+ Framework-free OpenAPI processing behind [oaspect](https://github.com/oaspect/oaspect):
4
+
5
+ - `loadSpecText()`, `parseSpec()`, `normalizeSpec()`: JSON or YAML, Swagger 2.0 → OpenAPI 3.0
6
+ - `buildModel()`: tags → operations with resolved parameters, bodies and responses
7
+ - `resolveSchema()`, `childrenOf()`, `typeLabel()`: `$ref`/`allOf` resolution for schema trees
8
+ - `exampleFromSchema()`, `mediaExample()`: example values
9
+ - `buildRequest()`, `defaultValues()`, `defaultBody()`: concrete HTTP requests
10
+ - `SNIPPET_LANGUAGES`, `resolveSnippet()`: code samples for 30 clients
11
+ - `parseMarkdown()`, `selectLocale()`, `localize()`: Markdown with `:::lang` blocks and `x-i18n`
12
+ - `tokenize()`: syntax highlighting tokens
13
+
14
+ Written in TypeScript; ships ESM with type definitions. MIT licensed.
@@ -0,0 +1,358 @@
1
+ /** Any OpenAPI object (schema, parameter, response…), vendor extensions included. */
2
+ type OpenAPIObject = Record<string, any>;
3
+ interface OpenAPIDocument extends OpenAPIObject {
4
+ openapi?: string;
5
+ swagger?: string;
6
+ info?: OpenAPIObject;
7
+ servers?: ServerObject[];
8
+ tags?: OpenAPIObject[];
9
+ paths?: Record<string, OpenAPIObject>;
10
+ components?: OpenAPIObject;
11
+ }
12
+ interface ServerObject extends OpenAPIObject {
13
+ url: string;
14
+ description?: string;
15
+ variables?: Record<string, {
16
+ default?: string;
17
+ enum?: string[];
18
+ description?: string;
19
+ }>;
20
+ }
21
+ type HttpMethod = "get" | "put" | "post" | "delete" | "options" | "head" | "patch" | "trace";
22
+ interface ResolvedSchema {
23
+ schema: OpenAPIObject;
24
+ /** Component name when the schema came from a $ref. */
25
+ name?: string;
26
+ }
27
+ type SchemaChildren = {
28
+ kind: "properties";
29
+ } | ({
30
+ kind: "items" | "map";
31
+ } & ResolvedSchema) | {
32
+ kind: "oneOf" | "anyOf";
33
+ variants: ResolvedSchema[];
34
+ } | null;
35
+ interface LinkInfo {
36
+ name: string;
37
+ operationId?: string;
38
+ operationRef?: string;
39
+ /** Target parameter name → runtime expression or value. */
40
+ parameters: Record<string, unknown>;
41
+ requestBody?: unknown;
42
+ description: string;
43
+ }
44
+ interface ResponseInfo {
45
+ status: string;
46
+ description: string;
47
+ "x-i18n"?: OpenAPIObject;
48
+ content: Record<string, OpenAPIObject>;
49
+ headers: Record<string, OpenAPIObject>;
50
+ links: LinkInfo[];
51
+ }
52
+ interface CallbackInfo {
53
+ name: string;
54
+ /** Operations keyed by runtime expression ("{$request.body#/callbackUrl}") in `path`. */
55
+ operations: Operation[];
56
+ }
57
+ interface Operation {
58
+ /** Stable id for links: "operation/<operationId>" or "operation/<method>-<path>". */
59
+ anchor: string;
60
+ /** "webhook": sent by the API (3.1 webhooks); "callback": nested under an operation. */
61
+ kind: "operation" | "webhook" | "callback";
62
+ method: HttpMethod;
63
+ path: string;
64
+ summary: string;
65
+ description: string;
66
+ "x-i18n"?: OpenAPIObject;
67
+ operationId?: string;
68
+ deprecated: boolean;
69
+ tags: string[];
70
+ parameters: OpenAPIObject[];
71
+ requestBody: OpenAPIObject | null;
72
+ responses: ResponseInfo[];
73
+ callbacks: CallbackInfo[];
74
+ security: OpenAPIObject[];
75
+ servers: ServerObject[];
76
+ }
77
+ interface TagGroup {
78
+ name: string;
79
+ anchor: string;
80
+ description: string;
81
+ "x-i18n"?: OpenAPIObject;
82
+ operations: Operation[];
83
+ }
84
+ interface SchemaEntry {
85
+ name: string;
86
+ anchor: string;
87
+ schema: OpenAPIObject;
88
+ }
89
+ interface ApiModel {
90
+ openapi: string;
91
+ info: OpenAPIObject;
92
+ servers: ServerObject[];
93
+ securitySchemes: Record<string, OpenAPIObject>;
94
+ tags: TagGroup[];
95
+ operations: Operation[];
96
+ /** OpenAPI 3.1 webhooks; `path` holds the webhook name. */
97
+ webhooks: Operation[];
98
+ schemas: SchemaEntry[];
99
+ }
100
+ /** Values entered for an operation's parameters, by location. */
101
+ interface ParameterValues {
102
+ path: Record<string, string>;
103
+ query: Record<string, string>;
104
+ header: Record<string, string>;
105
+ cookie: Record<string, string>;
106
+ }
107
+ /** One part of a multipart/form-data body: a text value or a file. */
108
+ interface FormField {
109
+ name: string;
110
+ /** Text value (ignored for files). */
111
+ value?: string;
112
+ /** Present for file parts; `name` is the file name shown in samples. */
113
+ file?: {
114
+ name: string;
115
+ type?: string;
116
+ };
117
+ }
118
+ /** A concrete HTTP request, as produced by buildRequest(). */
119
+ interface HttpRequest {
120
+ method: string;
121
+ url: string;
122
+ headers: Record<string, string>;
123
+ /** Text body (JSON, form-urlencoded…). */
124
+ body?: string;
125
+ /**
126
+ * multipart/form-data parts. Set instead of `body`; there is no
127
+ * Content-Type header because clients add it with the boundary.
128
+ */
129
+ form?: FormField[];
130
+ }
131
+ interface SnippetClient {
132
+ key: string;
133
+ label: string;
134
+ build: (request: HttpRequest) => string;
135
+ }
136
+ interface SnippetLanguage {
137
+ key: string;
138
+ label: string;
139
+ /** Tokenizer language for tokenize(). */
140
+ highlight: string;
141
+ clients: SnippetClient[];
142
+ }
143
+ interface Token {
144
+ /** Token class ("string", "keyword"…), or null for plain text. */
145
+ type: string | null;
146
+ text: string;
147
+ }
148
+ type InlineNode = {
149
+ type: "text";
150
+ value: string;
151
+ } | {
152
+ type: "br";
153
+ } | {
154
+ type: "code";
155
+ value: string;
156
+ } | {
157
+ type: "strong" | "em" | "del";
158
+ children: InlineNode[];
159
+ } | {
160
+ type: "link";
161
+ href: string;
162
+ title?: string;
163
+ children: InlineNode[];
164
+ } | {
165
+ type: "image";
166
+ src: string;
167
+ alt: string;
168
+ title?: string;
169
+ };
170
+ interface ListItem {
171
+ checked: boolean | null;
172
+ children: MarkdownBlock[];
173
+ }
174
+ type MarkdownBlock = {
175
+ type: "paragraph";
176
+ children: InlineNode[];
177
+ } | {
178
+ type: "heading";
179
+ level: number;
180
+ children: InlineNode[];
181
+ } | {
182
+ type: "code";
183
+ language: string;
184
+ text: string;
185
+ } | {
186
+ type: "blockquote";
187
+ children: MarkdownBlock[];
188
+ } | {
189
+ type: "hr";
190
+ } | {
191
+ type: "list";
192
+ ordered: boolean;
193
+ start: number | null;
194
+ loose: boolean;
195
+ items: ListItem[];
196
+ } | {
197
+ type: "table";
198
+ align: ("left" | "right" | "center" | null)[];
199
+ header: InlineNode[][];
200
+ rows: InlineNode[][][];
201
+ } | {
202
+ type: "lang";
203
+ locales: string[];
204
+ children: MarkdownBlock[];
205
+ } | {
206
+ type: "lang-selected";
207
+ locale: string;
208
+ children: MarkdownBlock[];
209
+ };
210
+
211
+ declare function resolvePointer(spec: OpenAPIObject, ref: unknown): any;
212
+ declare function refName(ref: string): string;
213
+ declare function deref(spec: OpenAPIObject, node: any): {
214
+ node: OpenAPIObject;
215
+ ref?: string;
216
+ };
217
+
218
+ declare function resolveSchema(spec: OpenAPIObject, input: unknown): ResolvedSchema;
219
+ declare function inferType(schema: OpenAPIObject): string;
220
+ declare function typeLabel(spec: OpenAPIObject, schema: OpenAPIObject, name?: string): string;
221
+ declare function constraintsOf(schema: OpenAPIObject): string[];
222
+ declare function childrenOf(spec: OpenAPIObject, schema: OpenAPIObject): SchemaChildren;
223
+
224
+ declare function exampleFromSchema(spec: OpenAPIObject, input: unknown, seen?: Set<string>, depth?: number): any;
225
+ declare function mediaExample(spec: OpenAPIObject, media: OpenAPIObject | undefined): any;
226
+ declare function mediaExamples(spec: OpenAPIObject, media: OpenAPIObject | undefined): {
227
+ key: string;
228
+ summary: string;
229
+ value: unknown;
230
+ }[];
231
+
232
+ declare const HTTP_METHODS: HttpMethod[];
233
+ declare function slugify(value: unknown): string;
234
+ declare const tagAnchor: (name: string) => string;
235
+ declare const modelAnchor: (name: string) => string;
236
+ declare function buildModel(spec: OpenAPIDocument): ApiModel;
237
+ declare function serverUrl(server: ServerObject, values?: Record<string, string>): string;
238
+ /** Finds the operation a link points to (operationId, or a local operationRef). */
239
+ declare function linkTarget(model: ApiModel, link: LinkInfo): Operation | undefined;
240
+
241
+ declare function jsonMediaType(content?: Record<string, unknown>): string | undefined;
242
+ declare function parameterExample(spec: OpenAPIObject, param: OpenAPIObject): any;
243
+ declare function defaultValues(spec: OpenAPIObject, operation: Operation): ParameterValues;
244
+ interface DefaultBody {
245
+ contentType: string | null;
246
+ /** Text body for JSON and other text media types. */
247
+ body: string;
248
+ /** Parts for multipart/form-data. */
249
+ form?: FormField[];
250
+ }
251
+ declare const isMultipart: (contentType?: string | null) => boolean;
252
+ /**
253
+ * Parts for a multipart/form-data body from its schema: binary properties
254
+ * become file parts, everything else a text part with an example value.
255
+ */
256
+ declare function defaultForm(spec: OpenAPIObject, media: OpenAPIObject | undefined): FormField[];
257
+ declare function defaultBody(spec: OpenAPIObject, operation: Operation): DefaultBody;
258
+ declare function formEncode(value: unknown): string;
259
+ interface BuildRequestOptions {
260
+ server?: ServerObject;
261
+ values: Partial<ParameterValues>;
262
+ body?: string;
263
+ /** multipart/form-data parts; used instead of `body` when given. */
264
+ form?: FormField[];
265
+ contentType?: string | null;
266
+ headers?: Record<string, string>;
267
+ }
268
+ declare function buildRequest(operation: Pick<Operation, "method" | "path">, { server, values, body, form, contentType, headers }: BuildRequestOptions): HttpRequest;
269
+
270
+ declare const SNIPPET_LANGUAGES: SnippetLanguage[];
271
+ declare function resolveSnippet(selection: unknown): {
272
+ language: SnippetLanguage;
273
+ client: SnippetLanguage["clients"][number];
274
+ selection: string;
275
+ };
276
+
277
+ declare function localize(node: OpenAPIObject | null | undefined, field: string, locale?: string): any;
278
+
279
+ declare function parseBlocks(lines: string[]): MarkdownBlock[];
280
+ declare function parseInline(text: string, { links }?: {
281
+ links?: boolean;
282
+ }): InlineNode[];
283
+ declare function selectLocale(blocks: MarkdownBlock[], locale: string, fallbacks?: string[]): MarkdownBlock[];
284
+ declare function parseMarkdown(source: unknown): MarkdownBlock[];
285
+ declare function safeUrl(url: unknown, { image }?: {
286
+ image?: boolean;
287
+ }): string | null;
288
+
289
+ declare function tokenize(code: string, language: string): Token[];
290
+
291
+ interface Credential {
292
+ /** Bearer / http token, API key or OAuth2 / OpenID Connect access token. */
293
+ token?: string;
294
+ username?: string;
295
+ password?: string;
296
+ clientId?: string;
297
+ clientSecret?: string;
298
+ /** Space-separated OAuth2 scopes for token requests. */
299
+ scopes?: string;
300
+ }
301
+ type Credentials = Record<string, Credential>;
302
+ interface AuthParts {
303
+ headers: Record<string, string>;
304
+ query: Record<string, string>;
305
+ cookies: Record<string, string>;
306
+ }
307
+ interface SecurityRequirement {
308
+ /** Scheme names that must all be present. Empty: auth optional. */
309
+ schemes: string[];
310
+ scopes: Record<string, string[]>;
311
+ }
312
+ /** The operation's requirements (alternatives), from its own or the global `security`. */
313
+ declare function securityRequirements(operation: Pick<Operation, "security">): SecurityRequirement[];
314
+ declare function hasCredential(scheme: OpenAPIObject | undefined, credential: Credential | undefined): boolean;
315
+ /**
316
+ * Headers, query parameters and cookies for an operation: the first
317
+ * requirement whose schemes all have credentials wins. Nothing is applied
318
+ * when no requirement can be met.
319
+ */
320
+ declare function applySecurity(schemes: Record<string, OpenAPIObject>, requirements: SecurityRequirement[], credentials: Credentials): AuthParts;
321
+ /** Short description of a scheme for labels: "Bearer", "API key (header: X-Key)"… */
322
+ declare function describeScheme(scheme: OpenAPIObject): string;
323
+ /** OAuth2 flows a viewer can complete itself (no browser redirect). */
324
+ declare const TOKEN_FLOWS: readonly ["clientCredentials", "password"];
325
+ /** Token request (RFC 6749 §4.3 / §4.4) for a clientCredentials or password flow. */
326
+ declare function buildTokenRequest(flowName: (typeof TOKEN_FLOWS)[number], flow: OpenAPIObject, credential: Credential): HttpRequest;
327
+
328
+ declare function convertSchema(schema: any): any;
329
+ /** Converts a Swagger 2.0 document to OpenAPI 3.0.3. */
330
+ declare function convertSwagger2(doc: OpenAPIObject): OpenAPIDocument;
331
+
332
+ declare const EXTERNAL_KEY = "x-oaspect-external";
333
+ interface ExternalRefOptions {
334
+ /** URL of the document itself; relative refs resolve against it. */
335
+ baseUrl: string;
336
+ /** Loads a referenced document's text (fetch, fs…). */
337
+ read: (url: string) => Promise<string>;
338
+ /** Upper bound on loaded documents. Default 100. */
339
+ maxDocuments?: number;
340
+ }
341
+ /** True when the document contains a $ref to another file or URL. */
342
+ declare function hasExternalRefs(node: unknown): boolean;
343
+ declare function resolveExternalRefs(doc: OpenAPIDocument, { baseUrl, read, maxDocuments }: ExternalRefOptions): Promise<OpenAPIDocument>;
344
+
345
+ /** Parses JSON, falling back to YAML. Throws with a readable message. */
346
+ declare function parseSpec(text: string): unknown;
347
+ declare function isOpenApiDocument(value: unknown): value is OpenAPIDocument;
348
+ /** OpenAPI 3.x documents pass through; Swagger 2.0 documents are converted. */
349
+ declare function normalizeSpec(doc: OpenAPIDocument): OpenAPIDocument;
350
+ /** parseSpec + validation + normalizeSpec. */
351
+ declare function loadSpecText(text: string): OpenAPIDocument;
352
+ /**
353
+ * loadSpecText + external $ref resolution. Without `read`, external refs are
354
+ * left as they are (the viewer shows them as unresolved).
355
+ */
356
+ declare function loadSpec(text: string, options?: Partial<ExternalRefOptions>): Promise<OpenAPIDocument>;
357
+
358
+ export { type ApiModel, type AuthParts, type BuildRequestOptions, type CallbackInfo, type Credential, type Credentials, type DefaultBody, EXTERNAL_KEY, type ExternalRefOptions, type FormField, HTTP_METHODS, type HttpMethod, type HttpRequest, type InlineNode, type LinkInfo, type ListItem, type MarkdownBlock, type OpenAPIDocument, type OpenAPIObject, type Operation, type ParameterValues, type ResolvedSchema, type ResponseInfo, SNIPPET_LANGUAGES, type SchemaChildren, type SchemaEntry, type SecurityRequirement, type ServerObject, type SnippetClient, type SnippetLanguage, TOKEN_FLOWS, type TagGroup, type Token, applySecurity, buildModel, buildRequest, buildTokenRequest, childrenOf, constraintsOf, convertSchema, convertSwagger2, defaultBody, defaultForm, defaultValues, deref, describeScheme, exampleFromSchema, formEncode, hasCredential, hasExternalRefs, inferType, isMultipart, isOpenApiDocument, jsonMediaType, linkTarget, loadSpec, loadSpecText, localize, mediaExample, mediaExamples, modelAnchor, normalizeSpec, parameterExample, parseBlocks, parseInline, parseMarkdown, parseSpec, refName, resolveExternalRefs, resolvePointer, resolveSchema, resolveSnippet, safeUrl, securityRequirements, selectLocale, serverUrl, slugify, tagAnchor, tokenize, typeLabel };