@zenera/faker 1.1.20 → 1.1.22
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 +32 -13
- package/dist/cache.d.ts +14 -1
- package/dist/cache.js +57 -1
- package/dist/command.js +83 -9
- package/dist/generate.d.ts +11 -1
- package/dist/generate.js +60 -12
- package/dist/logger.d.ts +62 -0
- package/dist/logger.js +273 -0
- package/dist/page.d.ts +3 -0
- package/dist/page.js +499 -0
- package/dist/paging.d.ts +31 -3
- package/dist/paging.js +315 -6
- package/dist/probe.js +11 -0
- package/dist/prompt.d.ts +15 -1
- package/dist/prompt.js +40 -6
- package/dist/router.d.ts +8 -2
- package/dist/router.js +23 -5
- package/dist/server.d.ts +8 -0
- package/dist/server.js +346 -41
- package/dist/setup.d.ts +18 -0
- package/dist/setup.js +57 -15
- package/dist/spec.d.ts +9 -0
- package/dist/spec.js +45 -7
- package/package.json +3 -3
package/dist/spec.js
CHANGED
|
@@ -3,6 +3,11 @@ import { CliError, EXIT } from '@zenera/cli/lib';
|
|
|
3
3
|
import { createHash } from 'node:crypto';
|
|
4
4
|
import { pagingOf } from "./paging.js";
|
|
5
5
|
import { normalize } from "./schema.js";
|
|
6
|
+
/**
|
|
7
|
+
* Version of generator prompt rules and environment contracts.
|
|
8
|
+
* Bumping this invalidates cached Python generators and triggers regeneration.
|
|
9
|
+
*/
|
|
10
|
+
export const GENERATOR_RULES_VERSION = 2;
|
|
6
11
|
// ---------------------------------------------------------------------------
|
|
7
12
|
// Documents, flattened
|
|
8
13
|
//
|
|
@@ -57,13 +62,14 @@ export async function loadSpec(file) {
|
|
|
57
62
|
if (!isObject(op)) {
|
|
58
63
|
continue;
|
|
59
64
|
}
|
|
60
|
-
const path = join(prefix, template);
|
|
61
|
-
const merged = override(shared, params(op.parameters, dialect));
|
|
65
|
+
const { path, fixed } = splitQuery(join(prefix, template));
|
|
66
|
+
const merged = override(override(shared, params(op.parameters, dialect)), pinned(fixed));
|
|
62
67
|
out.push(build({
|
|
63
68
|
source: file,
|
|
64
69
|
dialect,
|
|
65
70
|
method,
|
|
66
71
|
path,
|
|
72
|
+
fixed,
|
|
67
73
|
op,
|
|
68
74
|
params: merged,
|
|
69
75
|
body: requestBody(op, dialect),
|
|
@@ -88,11 +94,42 @@ function join(prefix, template) {
|
|
|
88
94
|
const base = prefix.replace(/\/+$/, '');
|
|
89
95
|
return `${base}${template.startsWith('/') ? '' : '/'}${template}` || '/';
|
|
90
96
|
}
|
|
97
|
+
/** The path as it has to be called — what a person needs to see. */
|
|
98
|
+
export const called = (op) => op.fixed ? `${op.path}?${new URLSearchParams(op.fixed).toString()}` : op.path;
|
|
99
|
+
/**
|
|
100
|
+
* A path template has no query string, but VMware, Oracle and others write one
|
|
101
|
+
* anyway — `/x/{id}?action=retry` — because they need two `post`s under one
|
|
102
|
+
* path and the object model gives them one. Taken verbatim the key matches
|
|
103
|
+
* nothing, since a request path never contains a `?`. So it is read for what
|
|
104
|
+
* it means: the path is the part before the `?`, and each pair after it is a
|
|
105
|
+
* query value this operation insists on.
|
|
106
|
+
*/
|
|
107
|
+
function splitQuery(template) {
|
|
108
|
+
const cut = template.indexOf('?');
|
|
109
|
+
if (cut < 0) {
|
|
110
|
+
return { path: template };
|
|
111
|
+
}
|
|
112
|
+
const fixed = {};
|
|
113
|
+
for (const [name, value] of new URLSearchParams(template.slice(cut + 1))) {
|
|
114
|
+
fixed[name] = value;
|
|
115
|
+
}
|
|
116
|
+
const path = template.slice(0, cut) || '/';
|
|
117
|
+
return Object.keys(fixed).length > 0 ? { path, fixed } : { path };
|
|
118
|
+
}
|
|
119
|
+
/** A one-value enum, so the check, the page and the generator all see the rule. */
|
|
120
|
+
function pinned(fixed) {
|
|
121
|
+
return Object.entries(fixed ?? {}).map(([name, value]) => ({
|
|
122
|
+
name,
|
|
123
|
+
in: 'query',
|
|
124
|
+
required: true,
|
|
125
|
+
schema: { type: 'string', enum: [value] },
|
|
126
|
+
}));
|
|
127
|
+
}
|
|
91
128
|
function build(b) {
|
|
92
129
|
const success = successOf(b.op, b.dialect);
|
|
93
130
|
const operationId = typeof b.op.operationId === 'string' && b.op.operationId
|
|
94
131
|
? b.op.operationId
|
|
95
|
-
: synthesizeId(b.method, b.path);
|
|
132
|
+
: synthesizeId(b.method, b.path, b.fixed);
|
|
96
133
|
const operation = {
|
|
97
134
|
key: '',
|
|
98
135
|
source: b.source,
|
|
@@ -102,9 +139,10 @@ function build(b) {
|
|
|
102
139
|
summary: typeof b.op.summary === 'string' ? b.op.summary : undefined,
|
|
103
140
|
description: typeof b.op.description === 'string' ? b.op.description : undefined,
|
|
104
141
|
params: b.params,
|
|
142
|
+
fixed: b.fixed,
|
|
105
143
|
requestBody: b.body,
|
|
106
144
|
success,
|
|
107
|
-
paging: pagingOf(b.params, success.schema),
|
|
145
|
+
paging: pagingOf(b.params, success.schema, b.body?.schema),
|
|
108
146
|
};
|
|
109
147
|
return { ...operation, key: keyOf(operation) };
|
|
110
148
|
}
|
|
@@ -117,6 +155,7 @@ function build(b) {
|
|
|
117
155
|
*/
|
|
118
156
|
function keyOf(op) {
|
|
119
157
|
const shape = {
|
|
158
|
+
version: GENERATOR_RULES_VERSION,
|
|
120
159
|
method: op.method,
|
|
121
160
|
path: op.path,
|
|
122
161
|
operationId: op.operationId,
|
|
@@ -146,9 +185,8 @@ function canonical(value) {
|
|
|
146
185
|
}
|
|
147
186
|
return value;
|
|
148
187
|
}
|
|
149
|
-
function synthesizeId(method, path) {
|
|
150
|
-
const words = path
|
|
151
|
-
.split('/')
|
|
188
|
+
function synthesizeId(method, path, fixed) {
|
|
189
|
+
const words = [...path.split('/'), ...Object.values(fixed ?? {})]
|
|
152
190
|
.filter(Boolean)
|
|
153
191
|
.map((s) => s.replace(/[{}]/g, '').replace(/[^A-Za-z0-9]+/g, '_'));
|
|
154
192
|
return [method, ...words].join('_');
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zenera/faker",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.22",
|
|
4
4
|
"description": "Mock HTTP server for swagger/OpenAPI documents, with response bodies generated by a model.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"openapi",
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"@apidevtools/swagger-parser": "^12.0.0",
|
|
46
46
|
"ajv": "^8.17.1",
|
|
47
47
|
"ajv-formats": "^3.0.1",
|
|
48
|
-
"@zenera/cli": "^1.1.
|
|
49
|
-
"@zenera/neo": "^1.1.
|
|
48
|
+
"@zenera/cli": "^1.1.22",
|
|
49
|
+
"@zenera/neo": "^1.1.22"
|
|
50
50
|
}
|
|
51
51
|
}
|