@afora/core 0.0.9 → 0.0.11
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 +52 -30
- package/dist/adapters.js +31 -55
- package/dist/core/application/application.d.ts +30 -0
- package/dist/core/application/application.js +1291 -0
- package/dist/core/application/exceptions.js +85 -0
- package/dist/core/application/migrate.js +67 -0
- package/dist/core/application/providers.js +108 -0
- package/dist/core/application/registry.js +23 -0
- package/dist/core/application/schema.js +398 -0
- package/dist/core/application/start.js +162 -0
- package/dist/core/application/types/application.interface.d.ts +23 -3
- package/dist/core/auth/jwt.js +200 -0
- package/dist/core/auth/types/jwt.interface.d.ts +10 -2
- package/dist/core/blueprint/blueprint-plugin.js +7 -0
- package/dist/core/blueprint/blueprint.js +146 -0
- package/dist/core/blueprint/callable.js +13 -0
- package/dist/core/blueprint/exceptions.js +20 -0
- package/dist/core/blueprint/is-blueprint.js +4 -0
- package/dist/core/blueprint/list.js +19 -0
- package/dist/core/blueprint/optional.js +69 -0
- package/dist/core/blueprint/record-field.js +14 -0
- package/dist/core/blueprint/slice.js +127 -0
- package/dist/core/blueprint/types/blueprint.interface.d.ts +16 -0
- package/dist/core/blueprint/variant.js +72 -0
- package/dist/core/command/command.js +428 -0
- package/dist/core/command/exceptions.js +30 -0
- package/dist/core/command/types/command.interface.d.ts +10 -1
- package/dist/core/entity/abstract-entity.d.ts +19 -0
- package/dist/core/entity/abstract-entity.js +181 -0
- package/dist/core/entity/entity.js +209 -0
- package/dist/core/entity/event.d.ts +39 -0
- package/dist/core/entity/event.js +80 -0
- package/dist/core/entity/exceptions.js +27 -0
- package/dist/core/entity/trust.js +7 -0
- package/dist/core/entity/version.js +13 -0
- package/dist/core/environment/environment.d.ts +26 -3
- package/dist/core/environment/environment.js +71 -0
- package/dist/core/event-policy/event-policy.js +76 -0
- package/dist/core/event-policy/exceptions.js +12 -0
- package/dist/core/event-policy/publish.js +98 -0
- package/dist/core/event-policy/publishers.js +18 -0
- package/dist/core/event-policy/types/event-policy.interface.d.ts +8 -2
- package/dist/core/exception/enums/exception-layers.js +15 -0
- package/dist/core/exception/exception.d.ts +15 -0
- package/dist/core/exception/exception.js +128 -0
- package/dist/core/exception/exported.js +49 -0
- package/dist/core/exception/guard.d.ts +20 -1
- package/dist/core/exception/guard.js +16 -0
- package/dist/core/exception/types/exception.interface.d.ts +13 -0
- package/dist/core/json.js +19 -0
- package/dist/core/logger/exceptions.js +7 -0
- package/dist/core/logger/logger.d.ts +14 -1
- package/dist/core/logger/logger.js +178 -0
- package/dist/core/openapi/exceptions.js +7 -0
- package/dist/core/openapi/openapi.js +132 -0
- package/dist/core/openapi/types/openapi.interface.d.ts +7 -1
- package/dist/core/output/access.js +66 -0
- package/dist/core/output/coerce.js +38 -0
- package/dist/core/output/exceptions.js +22 -0
- package/dist/{index-y280ew0k.js → core/output/http.js} +35 -80
- package/dist/core/output/openapi-sink.d.ts +10 -2
- package/dist/{index-hg1z2s2v.js → core/output/openapi-sink.js} +60 -544
- package/dist/core/output/output.js +192 -0
- package/dist/core/output/problem.js +30 -0
- package/dist/core/output/select.js +28 -0
- package/dist/core/output/stage.js +111 -0
- package/dist/core/output/status.js +52 -0
- package/dist/core/output/types/http.interface.d.ts +5 -1
- package/dist/core/output/types/output.interface.d.ts +24 -0
- package/dist/core/parse/issue.js +31 -0
- package/dist/core/parse/parse.d.ts +8 -1
- package/dist/core/parse/parse.js +323 -0
- package/dist/core/parse/strip.js +48 -0
- package/dist/core/property/abstract-property.js +37 -0
- package/dist/core/property/define-property.js +209 -0
- package/dist/core/property/enums/property-states.js +8 -0
- package/dist/core/property/exceptions.js +12 -0
- package/dist/core/property/input.js +10 -0
- package/dist/core/property/is-property.js +3 -0
- package/dist/core/property/lineage.js +8 -0
- package/dist/core/property/property.d.ts +27 -0
- package/dist/core/property/property.js +46 -0
- package/dist/core/property/refine.js +103 -0
- package/dist/core/record/abstract-record.d.ts +2 -1
- package/dist/core/record/abstract-record.js +307 -0
- package/dist/core/record/exceptions.js +49 -0
- package/dist/core/record/marks.js +7 -0
- package/dist/core/record/mask.js +60 -0
- package/dist/core/record/name.js +23 -0
- package/dist/core/record/plugin-catalog.js +115 -0
- package/dist/core/record/plugin.js +198 -0
- package/dist/core/record/record.js +117 -0
- package/dist/core/record/types/record.interface.d.ts +5 -0
- package/dist/core/repository/cursor.js +83 -0
- package/dist/core/repository/exceptions.js +72 -0
- package/dist/core/repository/in-memory.js +196 -0
- package/dist/core/repository/migrate.js +254 -0
- package/dist/core/repository/plan.js +27 -0
- package/dist/core/repository/provider.js +29 -0
- package/dist/core/repository/query.js +153 -0
- package/dist/core/repository/references.js +196 -0
- package/dist/core/repository/repository.d.ts +22 -2
- package/dist/core/repository/repository.js +720 -0
- package/dist/core/repository/state.js +138 -0
- package/dist/core/repository/types/query.interface.d.ts +13 -0
- package/dist/core/repository/types/repository.interface.d.ts +20 -8
- package/dist/core/repository/types/schema.interface.d.ts +8 -2
- package/dist/core/repository/upcast.js +179 -0
- package/dist/core/schema/schema.d.ts +9 -0
- package/dist/core/schema/schema.js +542 -0
- package/dist/core/standard/standard-schema.js +1 -0
- package/dist/core/standard/standard.js +23 -0
- package/dist/core/symbols/demo.d.ts +4 -0
- package/dist/core/symbols/demo.js +1 -0
- package/dist/core/symbols/kind.js +1 -0
- package/dist/core/symbols/meta.js +1 -0
- package/dist/core/symbols/state.d.ts +5 -0
- package/dist/core/symbols/state.js +1 -0
- package/dist/core/tracing/exceptions.d.ts +4 -0
- package/dist/core/tracing/exceptions.js +7 -0
- package/dist/core/tracing/tracing.d.ts +72 -0
- package/dist/core/tracing/tracing.js +101 -0
- package/dist/core/use-case/access.js +56 -0
- package/dist/core/use-case/decorators.js +65 -0
- package/dist/core/use-case/exceptions.d.ts +5 -1
- package/dist/core/use-case/exceptions.js +48 -0
- package/dist/core/use-case/plugin-catalog.js +68 -0
- package/dist/core/use-case/plugin.js +133 -0
- package/dist/core/use-case/route.js +200 -0
- package/dist/core/use-case/scope.d.ts +5 -0
- package/dist/core/use-case/scope.js +4 -0
- package/dist/core/use-case/steps/describe.d.ts +5 -1
- package/dist/core/use-case/steps/describe.js +24 -0
- package/dist/core/use-case/steps/references.js +105 -0
- package/dist/core/use-case/steps/run.js +139 -0
- package/dist/core/use-case/steps/types/check.interface.d.ts +27 -8
- package/dist/core/use-case/steps/types/steps.interface.d.ts +8 -1
- package/dist/core/use-case/steps/validate.js +368 -0
- package/dist/core/use-case/target.js +100 -0
- package/dist/core/use-case/types/route.interface.d.ts +25 -5
- package/dist/core/use-case/use-case.d.ts +77 -2
- package/dist/core/use-case/use-case.js +493 -0
- package/dist/experimental.d.ts +5 -0
- package/dist/experimental.js +18 -161
- package/dist/index.d.ts +7 -5
- package/dist/index.js +213 -367
- package/dist/std/blueprint-plugin.js +42 -0
- package/dist/std/blueprint.d.ts +21 -0
- package/dist/std/blueprint.js +3 -0
- package/dist/std/command.js +3 -0
- package/dist/std/entity.d.ts +20 -1
- package/dist/std/entity.js +30 -0
- package/dist/std/event-policies.js +3 -0
- package/dist/std/properties/contact.d.ts +3 -0
- package/dist/std/properties/contact.js +29 -0
- package/dist/std/properties/enum.js +26 -0
- package/dist/std/properties/exceptions.js +14 -0
- package/dist/std/properties/headless.js +45 -0
- package/dist/std/properties/identity.js +28 -0
- package/dist/std/properties/json.js +161 -0
- package/dist/std/properties/password.js +79 -0
- package/dist/std/properties/primitives.d.ts +4 -0
- package/dist/std/properties/primitives.js +28 -0
- package/dist/std/properties/time.js +55 -0
- package/dist/std/record-plugins.js +73 -0
- package/dist/std/record.d.ts +20 -1
- package/dist/std/record.js +3 -0
- package/dist/std/resource.js +39 -0
- package/dist/std/runtime.js +193 -0
- package/dist/std/stages.js +4 -0
- package/dist/std/use-cases/auth.js +193 -0
- package/dist/std/use-cases/crud.js +261 -0
- package/dist/std/use-cases/index.js +12 -0
- package/dist/std/use-cases/lifecycle.js +97 -0
- package/dist/std/use-cases/shared.js +232 -0
- package/dist/std/use-cases/types.d.ts +5 -1
- package/dist/std/use-cases/types.js +0 -0
- package/dist/testing/http-provider-contract.js +344 -0
- package/dist/testing/index.js +3 -1435
- package/dist/testing/memory-publisher.js +12 -0
- package/dist/testing/provider-contract.d.ts +12 -3
- package/dist/testing/provider-contract.js +1071 -0
- package/package.json +10 -3
- package/dist/index-3aae5jfq.js +0 -2997
- package/dist/index-ny3as79r.js +0 -4567
- package/dist/index-xegv6a1p.js +0 -4291
|
@@ -1,509 +1,27 @@
|
|
|
1
|
+
import { isBlueprint } from "../blueprint/is-blueprint.js";
|
|
2
|
+
import { isBlueprintList } from "../blueprint/list.js";
|
|
3
|
+
import { recordFieldOf } from "../blueprint/record-field.js";
|
|
4
|
+
import { isBlueprintVariant } from "../blueprint/variant.js";
|
|
5
|
+
import { ExceptionLayers } from "../exception/enums/exception-layers.js";
|
|
6
|
+
import { Exception } from "../exception/exception.js";
|
|
7
|
+
import { toOpenAPI } from "../openapi/openapi.js";
|
|
8
|
+
import { isProperty } from "../property/is-property.js";
|
|
9
|
+
import { recordNameOf } from "../record/name.js";
|
|
1
10
|
import {
|
|
2
|
-
isProperty,
|
|
3
|
-
isBlueprint,
|
|
4
|
-
recordFieldOf,
|
|
5
|
-
Exception2,
|
|
6
|
-
isBlueprintVariant,
|
|
7
|
-
recordNameOf2,
|
|
8
|
-
isBlueprintList,
|
|
9
|
-
toOpenAPI2,
|
|
10
11
|
HEADER_NAME,
|
|
11
|
-
isRoutePath,
|
|
12
|
-
|
|
12
|
+
isRoutePath as isPath,
|
|
13
|
+
isScalarKey as isScalarInputKey,
|
|
13
14
|
isScalarSchema,
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
} from "./
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
if (!isObject(value))
|
|
25
|
-
return value;
|
|
26
|
-
return Object.fromEntries(Object.keys(value).filter((key) => !key.startsWith("~") && value[key] !== undefined).sort().map((key) => [key, sorted(value[key])]));
|
|
27
|
-
}
|
|
28
|
-
function evolutionOf(property, description, found = {}, depth = 0) {
|
|
29
|
-
if (property === undefined || depth > 64)
|
|
30
|
-
return found;
|
|
31
|
-
const resolved = "ref" in property ? description.properties[property.ref] : property;
|
|
32
|
-
const lineage = resolved?.lineage;
|
|
33
|
-
if (lineage === undefined || lineage.kind === "factory")
|
|
34
|
-
return found;
|
|
35
|
-
if (lineage.kind === "refined") {
|
|
36
|
-
const { fill, renamedFrom } = lineage.options;
|
|
37
|
-
if (!("fill" in found) && fill !== undefined)
|
|
38
|
-
found.fill = fill;
|
|
39
|
-
if (found.renamedFrom === undefined && typeof renamedFrom === "string") {
|
|
40
|
-
found.renamedFrom = renamedFrom;
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
return evolutionOf(lineage.base, description, found, depth + 1);
|
|
44
|
-
}
|
|
45
|
-
function fieldsOf(fields, description) {
|
|
46
|
-
if (fields === undefined)
|
|
47
|
-
return;
|
|
48
|
-
return Object.fromEntries(Object.keys(fields).sort().map((key) => [
|
|
49
|
-
key,
|
|
50
|
-
lockFieldOf(fields[key], description)
|
|
51
|
-
]));
|
|
52
|
-
}
|
|
53
|
-
function lockFieldOf(field, description) {
|
|
54
|
-
switch (field.kind) {
|
|
55
|
-
case "property": {
|
|
56
|
-
const property = "ref" in field ? description.properties[field.ref] : field;
|
|
57
|
-
const evolution = evolutionOf("ref" in field ? { ref: field.ref } : field, description);
|
|
58
|
-
return {
|
|
59
|
-
schema: sorted(property?.schema ?? {}),
|
|
60
|
-
optional: field.optional,
|
|
61
|
-
...property?.unique ? { unique: true } : {},
|
|
62
|
-
..."fill" in evolution ? { fill: sorted(evolution.fill) } : {},
|
|
63
|
-
...evolution.renamedFrom === undefined ? {} : { renamedFrom: evolution.renamedFrom }
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
|
-
case "list": {
|
|
67
|
-
const item = lockFieldOf(field.item, description);
|
|
68
|
-
return {
|
|
69
|
-
schema: sorted({
|
|
70
|
-
type: "array",
|
|
71
|
-
items: item.schema,
|
|
72
|
-
minItems: field.minItems,
|
|
73
|
-
maxItems: field.maxItems
|
|
74
|
-
}),
|
|
75
|
-
optional: field.optional,
|
|
76
|
-
...item.fields === undefined ? {} : { fields: item.fields }
|
|
77
|
-
};
|
|
78
|
-
}
|
|
79
|
-
case "blueprint": {
|
|
80
|
-
const fields = "ref" in field ? description.blueprints[field.ref]?.fields : field.fields;
|
|
81
|
-
return {
|
|
82
|
-
schema: { type: "object" },
|
|
83
|
-
optional: field.optional,
|
|
84
|
-
fields: fieldsOf(fields, description) ?? {}
|
|
85
|
-
};
|
|
86
|
-
}
|
|
87
|
-
case "record": {
|
|
88
|
-
const fields = description.records[field.ref]?.fields;
|
|
89
|
-
return {
|
|
90
|
-
schema: { type: "object" },
|
|
91
|
-
optional: field.optional,
|
|
92
|
-
fields: fieldsOf(fields, description) ?? {}
|
|
93
|
-
};
|
|
94
|
-
}
|
|
95
|
-
case "variant":
|
|
96
|
-
return {
|
|
97
|
-
schema: { discriminator: field.key, type: "object" },
|
|
98
|
-
optional: field.optional,
|
|
99
|
-
fields: Object.fromEntries(Object.keys(field.variants).sort().map((tag) => [
|
|
100
|
-
tag,
|
|
101
|
-
{
|
|
102
|
-
...lockFieldOf(field.variants[tag], description),
|
|
103
|
-
optional: true
|
|
104
|
-
}
|
|
105
|
-
]))
|
|
106
|
-
};
|
|
107
|
-
case "entity":
|
|
108
|
-
return {
|
|
109
|
-
schema: { entity: field.ref, type: "string" },
|
|
110
|
-
optional: field.optional
|
|
111
|
-
};
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
function lockEntityOf(entity, description) {
|
|
115
|
-
const declared = fieldsOf(entity.fields, description) ?? {};
|
|
116
|
-
const unique = new Set(entity.options.unique.filter((key) => key !== "id"));
|
|
117
|
-
for (const [key, field] of Object.entries(declared)) {
|
|
118
|
-
if (field.unique)
|
|
119
|
-
unique.add(key);
|
|
120
|
-
}
|
|
121
|
-
const fields = Object.fromEntries(Object.entries(declared).map(([key, field]) => [
|
|
122
|
-
key,
|
|
123
|
-
unique.has(key) ? { ...field, unique: true } : field
|
|
124
|
-
]));
|
|
125
|
-
return {
|
|
126
|
-
schemaVersion: entity.schemaVersion ?? 1,
|
|
127
|
-
previousNames: [...entity.previousNames ?? []].sort(),
|
|
128
|
-
unique: [...unique].sort(),
|
|
129
|
-
fields,
|
|
130
|
-
...entity.upgrade === undefined || entity.upgrade.length === 0 ? {} : { upgrade: [...entity.upgrade].sort((a, b) => a - b) }
|
|
131
|
-
};
|
|
132
|
-
}
|
|
133
|
-
function schemaLockOf2(description) {
|
|
134
|
-
return {
|
|
135
|
-
lockVersion: 1,
|
|
136
|
-
entities: sorted(Object.fromEntries(Object.keys(description.entities).map((name) => [
|
|
137
|
-
name,
|
|
138
|
-
lockEntityOf(description.entities[name], description)
|
|
139
|
-
])))
|
|
140
|
-
};
|
|
141
|
-
}
|
|
142
|
-
var METADATA = new Set([
|
|
143
|
-
"title",
|
|
144
|
-
"description",
|
|
145
|
-
"examples",
|
|
146
|
-
"default",
|
|
147
|
-
"deprecated",
|
|
148
|
-
"readOnly",
|
|
149
|
-
"writeOnly",
|
|
150
|
-
"$comment"
|
|
151
|
-
]);
|
|
152
|
-
var UPPER = new Set([
|
|
153
|
-
"maximum",
|
|
154
|
-
"exclusiveMaximum",
|
|
155
|
-
"maxLength",
|
|
156
|
-
"maxItems",
|
|
157
|
-
"maxProperties"
|
|
158
|
-
]);
|
|
159
|
-
var LOWER = new Set([
|
|
160
|
-
"minimum",
|
|
161
|
-
"exclusiveMinimum",
|
|
162
|
-
"minLength",
|
|
163
|
-
"minItems",
|
|
164
|
-
"minProperties"
|
|
165
|
-
]);
|
|
166
|
-
var NARROWING = new Set([
|
|
167
|
-
"pattern",
|
|
168
|
-
"format",
|
|
169
|
-
"multipleOf",
|
|
170
|
-
"allOf",
|
|
171
|
-
"const"
|
|
172
|
-
]);
|
|
173
|
-
var same = (a, b) => JSON.stringify(sorted(a)) === JSON.stringify(sorted(b));
|
|
174
|
-
function literalsOf(schema) {
|
|
175
|
-
if (Array.isArray(schema.enum)) {
|
|
176
|
-
return schema.enum.map((value) => JSON.stringify(value));
|
|
177
|
-
}
|
|
178
|
-
if (Array.isArray(schema.anyOf) && schema.anyOf.every((item) => isObject(item) && ("const" in item))) {
|
|
179
|
-
return schema.anyOf.map((item) => JSON.stringify(item.const));
|
|
180
|
-
}
|
|
181
|
-
return;
|
|
182
|
-
}
|
|
183
|
-
function compareSchemas(previous, next) {
|
|
184
|
-
if (same(previous, next))
|
|
185
|
-
return "same";
|
|
186
|
-
const strip = (schema) => Object.fromEntries(Object.entries(schema).filter(([key]) => !METADATA.has(key)));
|
|
187
|
-
const [a, b] = [strip(previous), strip(next)];
|
|
188
|
-
if (same(a, b))
|
|
189
|
-
return "metadata";
|
|
190
|
-
if (!same(a.type, b.type) || !same(a.entity, b.entity))
|
|
191
|
-
return "type-changed";
|
|
192
|
-
const [before, after] = [literalsOf(a), literalsOf(b)];
|
|
193
|
-
let loosened = false;
|
|
194
|
-
let tightened = false;
|
|
195
|
-
if (before !== undefined || after !== undefined) {
|
|
196
|
-
if (before === undefined || after === undefined)
|
|
197
|
-
return "type-changed";
|
|
198
|
-
const kept = before.every((value) => after.includes(value));
|
|
199
|
-
const gained = after.some((value) => !before.includes(value));
|
|
200
|
-
if (!kept)
|
|
201
|
-
tightened = true;
|
|
202
|
-
if (gained)
|
|
203
|
-
loosened = true;
|
|
204
|
-
}
|
|
205
|
-
const keys = new Set([...Object.keys(a), ...Object.keys(b)]);
|
|
206
|
-
for (const key of keys) {
|
|
207
|
-
if (["type", "entity", "enum", "anyOf"].includes(key))
|
|
208
|
-
continue;
|
|
209
|
-
if (same(a[key], b[key]))
|
|
210
|
-
continue;
|
|
211
|
-
const [x, y] = [a[key], b[key]];
|
|
212
|
-
if (key === "items") {
|
|
213
|
-
const items = compareSchemas(isObject(x) ? x : {}, isObject(y) ? y : {});
|
|
214
|
-
if (items === "type-changed")
|
|
215
|
-
return "type-changed";
|
|
216
|
-
if (items === "tightened")
|
|
217
|
-
tightened = true;
|
|
218
|
-
if (items === "loosened")
|
|
219
|
-
loosened = true;
|
|
220
|
-
} else if (UPPER.has(key) || LOWER.has(key)) {
|
|
221
|
-
if (x === undefined)
|
|
222
|
-
tightened = true;
|
|
223
|
-
else if (y === undefined)
|
|
224
|
-
loosened = true;
|
|
225
|
-
else if (typeof x !== "number" || typeof y !== "number")
|
|
226
|
-
return "type-changed";
|
|
227
|
-
else if (UPPER.has(key) ? y > x : y < x)
|
|
228
|
-
loosened = true;
|
|
229
|
-
else
|
|
230
|
-
tightened = true;
|
|
231
|
-
} else if (NARROWING.has(key)) {
|
|
232
|
-
if (y === undefined)
|
|
233
|
-
loosened = true;
|
|
234
|
-
else
|
|
235
|
-
tightened = true;
|
|
236
|
-
} else {
|
|
237
|
-
return "type-changed";
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
if (tightened)
|
|
241
|
-
return "tightened";
|
|
242
|
-
return loosened ? "loosened" : "metadata";
|
|
243
|
-
}
|
|
244
|
-
function push(context, change, klass, satisfied, extra = {}) {
|
|
245
|
-
context.changes.push({
|
|
246
|
-
entity: context.entity,
|
|
247
|
-
...extra.key === undefined ? {} : { key: extra.key },
|
|
248
|
-
change,
|
|
249
|
-
class: klass,
|
|
250
|
-
satisfied,
|
|
251
|
-
destructive: extra.destructive ?? false,
|
|
252
|
-
...extra.from === undefined ? {} : { from: extra.from },
|
|
253
|
-
...extra.detail === undefined ? {} : { detail: extra.detail }
|
|
254
|
-
});
|
|
255
|
-
}
|
|
256
|
-
var STEP = "an upgrade step (bump schemaVersion and add upgrade[n])";
|
|
257
|
-
function diffFields(context, previous, next, path) {
|
|
258
|
-
const join = (key) => path ? `${path}.${key}` : key;
|
|
259
|
-
const renamedFrom = new Map;
|
|
260
|
-
for (const [key, field] of Object.entries(next)) {
|
|
261
|
-
const from = field.renamedFrom;
|
|
262
|
-
if (from !== undefined && !(key in previous) && from in previous && !(from in next)) {
|
|
263
|
-
renamedFrom.set(key, from);
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
const moved = new Set(renamedFrom.values());
|
|
267
|
-
const keys = [...new Set([...Object.keys(previous), ...Object.keys(next)])].filter((key) => !moved.has(key)).sort();
|
|
268
|
-
for (const key of keys) {
|
|
269
|
-
const was = previous[key];
|
|
270
|
-
const is = next[key];
|
|
271
|
-
if (is === undefined) {
|
|
272
|
-
push(context, "removed", "automatic", true, {
|
|
273
|
-
key: join(key),
|
|
274
|
-
destructive: true,
|
|
275
|
-
detail: "Left out on read; a backfill removes it only with destructive: true. Rename it with renamedFrom to keep the data."
|
|
276
|
-
});
|
|
277
|
-
continue;
|
|
278
|
-
}
|
|
279
|
-
const from = renamedFrom.get(key);
|
|
280
|
-
if (from !== undefined) {
|
|
281
|
-
push(context, "renamed", "declared", true, {
|
|
282
|
-
key: join(key),
|
|
283
|
-
from: join(from)
|
|
284
|
-
});
|
|
285
|
-
diffField(context, previous[from], is, join(key));
|
|
286
|
-
continue;
|
|
287
|
-
}
|
|
288
|
-
if (was === undefined) {
|
|
289
|
-
if (is.optional) {
|
|
290
|
-
push(context, "added-optional", "automatic", true, { key: join(key) });
|
|
291
|
-
} else {
|
|
292
|
-
const satisfied = "fill" in is || context.stepped;
|
|
293
|
-
push(context, "added-required", "declared", satisfied, {
|
|
294
|
-
key: join(key),
|
|
295
|
-
...satisfied ? {} : {
|
|
296
|
-
detail: `Needs a fill (.with({ fill })) or ${STEP} that sets it.`
|
|
297
|
-
}
|
|
298
|
-
});
|
|
299
|
-
}
|
|
300
|
-
continue;
|
|
301
|
-
}
|
|
302
|
-
diffField(context, was, is, join(key));
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
function diffField(context, was, is, key) {
|
|
306
|
-
const compared = compareSchemas(was.schema, is.schema);
|
|
307
|
-
if (compared === "type-changed") {
|
|
308
|
-
push(context, "type-changed", "declared", context.stepped, {
|
|
309
|
-
key,
|
|
310
|
-
...context.stepped ? {} : { detail: `Needs ${STEP} that converts the stored values.` }
|
|
311
|
-
});
|
|
312
|
-
} else if (compared === "tightened") {
|
|
313
|
-
const satisfied = context.stepped || "fill" in is;
|
|
314
|
-
push(context, "tightened", "declared", satisfied, {
|
|
315
|
-
key,
|
|
316
|
-
detail: satisfied ? "Run app.migrate({ mode: 'check' }) to list the rows that violate it." : `Needs ${STEP} or a fill; app.migrate({ mode: 'check' }) lists the rows that violate it.`
|
|
317
|
-
});
|
|
318
|
-
} else if (compared === "loosened") {
|
|
319
|
-
push(context, "loosened", "automatic", true, { key });
|
|
320
|
-
} else if (compared === "metadata") {
|
|
321
|
-
push(context, "metadata", "automatic", true, { key });
|
|
322
|
-
}
|
|
323
|
-
if (was.optional && !is.optional) {
|
|
324
|
-
const satisfied = context.stepped || "fill" in is;
|
|
325
|
-
push(context, "made-required", "declared", satisfied, {
|
|
326
|
-
key,
|
|
327
|
-
...satisfied ? {} : {
|
|
328
|
-
detail: `Needs a fill (.with({ fill })) or ${STEP} for the rows without it.`
|
|
329
|
-
}
|
|
330
|
-
});
|
|
331
|
-
} else if (!was.optional && is.optional) {
|
|
332
|
-
push(context, "made-optional", "automatic", true, { key });
|
|
333
|
-
}
|
|
334
|
-
if (!was.unique && is.unique) {
|
|
335
|
-
push(context, "unique-added", "check", true, {
|
|
336
|
-
key,
|
|
337
|
-
detail: "Run app.migrate({ mode: 'check' }) and look for duplicates before it is enforced."
|
|
338
|
-
});
|
|
339
|
-
} else if (was.unique && !is.unique) {
|
|
340
|
-
push(context, "unique-removed", "automatic", true, { key });
|
|
341
|
-
}
|
|
342
|
-
if (was.fields !== undefined || is.fields !== undefined) {
|
|
343
|
-
diffFields(context, was.fields ?? {}, is.fields ?? {}, key);
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
function diffEntity(entity, from, to, changes) {
|
|
347
|
-
const stepped = to.schemaVersion > from.schemaVersion && (to.upgrade ?? []).some((version) => version > from.schemaVersion && version <= to.schemaVersion);
|
|
348
|
-
const context = { entity, from, to, stepped, changes };
|
|
349
|
-
diffFields(context, from.fields, to.fields, "");
|
|
350
|
-
}
|
|
351
|
-
function diffSchemas2(lock, current) {
|
|
352
|
-
const changes = [];
|
|
353
|
-
const claimed = new Set;
|
|
354
|
-
for (const name of Object.keys(current.entities).sort()) {
|
|
355
|
-
const to = current.entities[name];
|
|
356
|
-
let from = lock.entities[name];
|
|
357
|
-
if (from === undefined) {
|
|
358
|
-
const previous = to.previousNames.find((old) => lock.entities[old] !== undefined && !current.entities[old]);
|
|
359
|
-
if (previous === undefined) {
|
|
360
|
-
changes.push({
|
|
361
|
-
entity: name,
|
|
362
|
-
change: "entity-added",
|
|
363
|
-
class: "automatic",
|
|
364
|
-
satisfied: true,
|
|
365
|
-
destructive: false
|
|
366
|
-
});
|
|
367
|
-
continue;
|
|
368
|
-
}
|
|
369
|
-
claimed.add(previous);
|
|
370
|
-
from = lock.entities[previous];
|
|
371
|
-
changes.push({
|
|
372
|
-
entity: name,
|
|
373
|
-
change: "entity-renamed",
|
|
374
|
-
class: "declared",
|
|
375
|
-
satisfied: true,
|
|
376
|
-
destructive: true,
|
|
377
|
-
from: previous,
|
|
378
|
-
detail: "app.migrate moves its rows; it removes them from the previous name only with destructive: true."
|
|
379
|
-
});
|
|
380
|
-
}
|
|
381
|
-
diffEntity(name, from, to, changes);
|
|
382
|
-
}
|
|
383
|
-
for (const name of Object.keys(lock.entities).sort()) {
|
|
384
|
-
if (current.entities[name] !== undefined || claimed.has(name))
|
|
385
|
-
continue;
|
|
386
|
-
changes.push({
|
|
387
|
-
entity: name,
|
|
388
|
-
change: "entity-removed",
|
|
389
|
-
class: "declared",
|
|
390
|
-
satisfied: false,
|
|
391
|
-
destructive: true,
|
|
392
|
-
detail: `No entity is named "${name}": list it in the previousNames of the entity that replaces it, or remove it from the lock if it is gone.`
|
|
393
|
-
});
|
|
394
|
-
}
|
|
395
|
-
return changes;
|
|
396
|
-
}
|
|
397
|
-
function schemaLockProblemsOf2(lock, current) {
|
|
398
|
-
const problems = [];
|
|
399
|
-
const renamed = new Map;
|
|
400
|
-
for (const [name, entity] of Object.entries(current.entities)) {
|
|
401
|
-
for (const old of entity.previousNames) {
|
|
402
|
-
if (!renamed.has(old))
|
|
403
|
-
renamed.set(old, name);
|
|
404
|
-
}
|
|
405
|
-
}
|
|
406
|
-
for (const name of Object.keys(lock.entities).sort()) {
|
|
407
|
-
const locked = lock.entities[name];
|
|
408
|
-
const now = current.entities[name] === undefined ? renamed.get(name) : name;
|
|
409
|
-
const entity = now === undefined ? undefined : current.entities[now];
|
|
410
|
-
if (entity === undefined) {
|
|
411
|
-
problems.push(`The lock holds the entity "${name}", which no entity is named or lists in previousNames.`);
|
|
412
|
-
} else if (entity.schemaVersion < locked.schemaVersion) {
|
|
413
|
-
problems.push(`The schemaVersion of "${now}" (${entity.schemaVersion}) is lower than the lock's (${locked.schemaVersion}).`);
|
|
414
|
-
}
|
|
415
|
-
}
|
|
416
|
-
return problems;
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
// src/core/output/status.ts
|
|
420
|
-
var INVALID_INPUT = new Set([
|
|
421
|
-
"InvalidUseCaseInput",
|
|
422
|
-
"InvalidUseCaseContext",
|
|
423
|
-
"InvalidCommandContext"
|
|
424
|
-
]);
|
|
425
|
-
var CONFIGURATION = new Set(["InvalidCommandEnvironment"]);
|
|
426
|
-
var NOT_FOUND = new Set([
|
|
427
|
-
"RepositoryEntityNotFound",
|
|
428
|
-
"UnknownCommandOperation",
|
|
429
|
-
"UseCaseEntityNotFound"
|
|
430
|
-
]);
|
|
431
|
-
var CONFLICT = new Set([
|
|
432
|
-
"RepositoryUniqueViolation",
|
|
433
|
-
"RepositoryConcurrencyConflict",
|
|
434
|
-
"UseCaseEntityConflict"
|
|
435
|
-
]);
|
|
436
|
-
function raisedOf(error) {
|
|
437
|
-
let raised = error;
|
|
438
|
-
for (let current = error.cause;current instanceof Exception2; current = current.cause) {
|
|
439
|
-
if (current.name === error.name)
|
|
440
|
-
raised = current;
|
|
441
|
-
}
|
|
442
|
-
return raised;
|
|
443
|
-
}
|
|
444
|
-
function statusOf2(error, operation, statuses) {
|
|
445
|
-
if (!(error instanceof Exception2))
|
|
446
|
-
return 500;
|
|
447
|
-
const { name } = error;
|
|
448
|
-
if (statuses && Object.hasOwn(statuses, name))
|
|
449
|
-
return statuses[name];
|
|
450
|
-
if (INVALID_INPUT.has(name))
|
|
451
|
-
return 400;
|
|
452
|
-
if (CONFIGURATION.has(name))
|
|
453
|
-
return 500;
|
|
454
|
-
if (Array.isArray(raisedOf(error).details?.issues)) {
|
|
455
|
-
return 400;
|
|
456
|
-
}
|
|
457
|
-
if (NOT_FOUND.has(name))
|
|
458
|
-
return 404;
|
|
459
|
-
if (CONFLICT.has(name))
|
|
460
|
-
return 409;
|
|
461
|
-
if (name === "UseCaseInvalidCredentials" || name === "Unauthenticated") {
|
|
462
|
-
return 401;
|
|
463
|
-
}
|
|
464
|
-
if (name === "Forbidden")
|
|
465
|
-
return 403;
|
|
466
|
-
if (operation?.errors.some((Declared) => error.is(Declared) || Declared.name === name)) {
|
|
467
|
-
return 422;
|
|
468
|
-
}
|
|
469
|
-
return 500;
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
// src/core/output/problem.ts
|
|
473
|
-
var PROBLEM_MEDIA_TYPE2 = "application/problem+json";
|
|
474
|
-
var INTERNAL = "Internal error.";
|
|
475
|
-
function problemOf2(error, options) {
|
|
476
|
-
const { status, debug = false } = options;
|
|
477
|
-
const exception = error instanceof Exception2 ? error : undefined;
|
|
478
|
-
const title = exception?.name ?? "UnexpectedError";
|
|
479
|
-
const message = exception?.message ?? (error instanceof Error ? error.message : String(error));
|
|
480
|
-
const raised = exception && raisedOf(exception);
|
|
481
|
-
const rawIssues = raised?.details?.issues;
|
|
482
|
-
const issues = status === 400 && Array.isArray(rawIssues) ? rawIssues.map((issue) => ({
|
|
483
|
-
path: [...issue.path],
|
|
484
|
-
code: issue.code,
|
|
485
|
-
message: issue.message
|
|
486
|
-
})) : undefined;
|
|
487
|
-
return {
|
|
488
|
-
type: `urn:afora:exception:${title}`,
|
|
489
|
-
title,
|
|
490
|
-
status,
|
|
491
|
-
detail: status >= 500 && !debug ? INTERNAL : message,
|
|
492
|
-
...raised ? { layer: raised.layer } : {},
|
|
493
|
-
...issues ? { issues } : {},
|
|
494
|
-
...debug ? { trace: exception ? exception.toString() : String(error) } : {}
|
|
495
|
-
};
|
|
496
|
-
}
|
|
497
|
-
function outputValueOf2(value) {
|
|
498
|
-
const text = JSON.stringify(value);
|
|
499
|
-
return text === undefined ? undefined : JSON.parse(text);
|
|
500
|
-
}
|
|
501
|
-
|
|
502
|
-
// src/core/output/openapi-sink.ts
|
|
503
|
-
var METHODS = ["get", "post", "put", "patch", "delete"];
|
|
504
|
-
var SUCCESS_STATUSES = [200, 201, 202, 204];
|
|
505
|
-
var REQUEST_ID_HEADER = "x-request-id";
|
|
506
|
-
function isObject2(value) {
|
|
15
|
+
parametersOfPath
|
|
16
|
+
} from "../use-case/route.js";
|
|
17
|
+
import { InvalidOutputStage } from "./exceptions.js";
|
|
18
|
+
import { PROBLEM_MEDIA_TYPE } from "./problem.js";
|
|
19
|
+
import { sinkOf } from "./stage.js";
|
|
20
|
+
import { statusOf } from "./status.js";
|
|
21
|
+
const METHODS = ["get", "post", "put", "patch", "delete"];
|
|
22
|
+
const SUCCESS_STATUSES = [200, 201, 202, 204];
|
|
23
|
+
const REQUEST_ID_HEADER = "x-request-id";
|
|
24
|
+
function isObject(value) {
|
|
507
25
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
508
26
|
}
|
|
509
27
|
function onlyKeys(value, allowed, where) {
|
|
@@ -513,7 +31,7 @@ function onlyKeys(value, allowed, where) {
|
|
|
513
31
|
}
|
|
514
32
|
return;
|
|
515
33
|
}
|
|
516
|
-
|
|
34
|
+
const COMPONENT_NAME = /^[A-Za-z0-9._-]+$/;
|
|
517
35
|
function isHeaderValue(value) {
|
|
518
36
|
if (typeof value !== "string")
|
|
519
37
|
return false;
|
|
@@ -527,7 +45,7 @@ function isHeaderValue(value) {
|
|
|
527
45
|
function isScopes(value) {
|
|
528
46
|
return value === undefined || Array.isArray(value) && value.every((scope) => typeof scope === "string" && scope !== "");
|
|
529
47
|
}
|
|
530
|
-
|
|
48
|
+
const SCHEME_KEYS = {
|
|
531
49
|
http: ["scheme", "bearerFormat"],
|
|
532
50
|
apiKey: ["name", "in"],
|
|
533
51
|
oauth2: ["flows"],
|
|
@@ -535,7 +53,7 @@ var SCHEME_KEYS = {
|
|
|
535
53
|
mutualTLS: []
|
|
536
54
|
};
|
|
537
55
|
function schemeIssue(scheme, where) {
|
|
538
|
-
if (!
|
|
56
|
+
if (!isObject(scheme) || typeof scheme.type !== "string") {
|
|
539
57
|
return `Expected ${where} to be a security scheme with a "type".`;
|
|
540
58
|
}
|
|
541
59
|
const keys = Object.hasOwn(SCHEME_KEYS, scheme.type) ? SCHEME_KEYS[scheme.type] : undefined;
|
|
@@ -566,7 +84,7 @@ function schemeIssue(scheme, where) {
|
|
|
566
84
|
}
|
|
567
85
|
return;
|
|
568
86
|
case "oauth2":
|
|
569
|
-
return
|
|
87
|
+
return isObject(scheme.flows) ? undefined : `Expected ${where}.flows to be an object.`;
|
|
570
88
|
case "openIdConnect":
|
|
571
89
|
return typeof scheme.openIdConnectUrl === "string" && scheme.openIdConnectUrl ? undefined : `Expected ${where}.openIdConnectUrl to be a URL.`;
|
|
572
90
|
default:
|
|
@@ -574,7 +92,7 @@ function schemeIssue(scheme, where) {
|
|
|
574
92
|
}
|
|
575
93
|
}
|
|
576
94
|
function requirementIssue(requirement, schemes, where) {
|
|
577
|
-
if (!
|
|
95
|
+
if (!isObject(requirement) || onlyKeys(requirement, ["scheme", "scopes"], where) || typeof requirement.scheme !== "string" || !isScopes(requirement.scopes)) {
|
|
578
96
|
return `Expected ${where} to be { scheme, scopes? }.`;
|
|
579
97
|
}
|
|
580
98
|
if (!Object.hasOwn(schemes, requirement.scheme)) {
|
|
@@ -583,13 +101,13 @@ function requirementIssue(requirement, schemes, where) {
|
|
|
583
101
|
return;
|
|
584
102
|
}
|
|
585
103
|
function securityIssue(security, where) {
|
|
586
|
-
if (!
|
|
104
|
+
if (!isObject(security))
|
|
587
105
|
return `Expected ${where} to be an object.`;
|
|
588
106
|
const bad = onlyKeys(security, ["schemes", "operations", "default"], where);
|
|
589
107
|
if (bad)
|
|
590
108
|
return bad;
|
|
591
109
|
const { schemes, operations } = security;
|
|
592
|
-
if (!
|
|
110
|
+
if (!isObject(schemes))
|
|
593
111
|
return `Expected ${where}.schemes to be an object.`;
|
|
594
112
|
for (const [name, scheme] of Object.entries(schemes)) {
|
|
595
113
|
if (!COMPONENT_NAME.test(name)) {
|
|
@@ -600,7 +118,7 @@ function securityIssue(security, where) {
|
|
|
600
118
|
return schemeBad;
|
|
601
119
|
}
|
|
602
120
|
if (operations !== undefined) {
|
|
603
|
-
if (!
|
|
121
|
+
if (!isObject(operations)) {
|
|
604
122
|
return `Expected ${where}.operations to be an object.`;
|
|
605
123
|
}
|
|
606
124
|
for (const [id, requirement] of Object.entries(operations)) {
|
|
@@ -616,30 +134,30 @@ function securityIssue(security, where) {
|
|
|
616
134
|
}
|
|
617
135
|
return;
|
|
618
136
|
}
|
|
619
|
-
|
|
137
|
+
export const ROUTE_OPTION_KEYS = [
|
|
620
138
|
"basePath",
|
|
621
139
|
"route",
|
|
622
140
|
"responses",
|
|
623
141
|
"requestId",
|
|
624
142
|
"security"
|
|
625
143
|
];
|
|
626
|
-
function
|
|
144
|
+
export function routeOptionsIssue(options, where) {
|
|
627
145
|
const { basePath, route, responses, requestId, security } = options;
|
|
628
|
-
if (basePath !== undefined && (typeof basePath !== "string" || basePath !== "" && !
|
|
146
|
+
if (basePath !== undefined && (typeof basePath !== "string" || basePath !== "" && !isPath(basePath, false))) {
|
|
629
147
|
return `Expected ${where}.basePath to be "" or a path such as "/api".`;
|
|
630
148
|
}
|
|
631
149
|
if (route !== undefined) {
|
|
632
|
-
if (!
|
|
150
|
+
if (!isObject(route))
|
|
633
151
|
return `Expected ${where}.route to be an object.`;
|
|
634
152
|
for (const [id, entry] of Object.entries(route)) {
|
|
635
|
-
if (!
|
|
153
|
+
if (!isObject(entry) || onlyKeys(entry, ["method", "path", "status"], "route")) {
|
|
636
154
|
return `Expected ${where}.route["${id}"] to be { method?, path?, status? }.`;
|
|
637
155
|
}
|
|
638
156
|
if (entry.method !== undefined && !METHODS.includes(entry.method)) {
|
|
639
157
|
return `Expected ${where}.route["${id}"].method to be one of ${METHODS.join(", ")}.`;
|
|
640
158
|
}
|
|
641
159
|
if (entry.path !== undefined) {
|
|
642
|
-
if (!
|
|
160
|
+
if (!isPath(entry.path)) {
|
|
643
161
|
return `Expected ${where}.route["${id}"].path to start with "/", with parameters only as whole "{name}" segments and no query.`;
|
|
644
162
|
}
|
|
645
163
|
const names = parametersOfPath(entry.path);
|
|
@@ -653,12 +171,12 @@ function routeOptionsIssue2(options, where) {
|
|
|
653
171
|
}
|
|
654
172
|
}
|
|
655
173
|
if (responses !== undefined) {
|
|
656
|
-
if (!
|
|
174
|
+
if (!isObject(responses)) {
|
|
657
175
|
return `Expected ${where}.responses to be an object.`;
|
|
658
176
|
}
|
|
659
177
|
for (const [id, entry] of Object.entries(responses)) {
|
|
660
178
|
const at = `${where}.responses["${id}"]`;
|
|
661
|
-
if (!
|
|
179
|
+
if (!isObject(entry) || onlyKeys(entry, ["headers"], at) || entry.headers !== undefined && !isObject(entry.headers)) {
|
|
662
180
|
return `Expected ${at} to be { headers?: { name: value } }.`;
|
|
663
181
|
}
|
|
664
182
|
for (const [name, value] of Object.entries(entry.headers ?? {})) {
|
|
@@ -682,15 +200,15 @@ function routeOptionsIssue2(options, where) {
|
|
|
682
200
|
}
|
|
683
201
|
return;
|
|
684
202
|
}
|
|
685
|
-
function
|
|
203
|
+
export function requestIdHeaderOf(options) {
|
|
686
204
|
return options.requestId === true ? REQUEST_ID_HEADER : options.requestId ?? undefined;
|
|
687
205
|
}
|
|
688
206
|
function validate(options) {
|
|
689
|
-
const bad = onlyKeys(options, ["info", "servers", ...
|
|
207
|
+
const bad = onlyKeys(options, ["info", "servers", ...ROUTE_OPTION_KEYS], "openapi");
|
|
690
208
|
if (bad)
|
|
691
209
|
return bad;
|
|
692
210
|
const { info, servers } = options;
|
|
693
|
-
if (!
|
|
211
|
+
if (!isObject(info))
|
|
694
212
|
return "Expected openapi.info to be an object.";
|
|
695
213
|
const infoBad = onlyKeys(info, ["title", "version", "description"], "info");
|
|
696
214
|
if (infoBad)
|
|
@@ -707,23 +225,23 @@ function validate(options) {
|
|
|
707
225
|
if (!Array.isArray(servers))
|
|
708
226
|
return "Expected openapi.servers to be a list.";
|
|
709
227
|
for (const server of servers) {
|
|
710
|
-
if (!
|
|
228
|
+
if (!isObject(server) || typeof server.url !== "string" || !server.url || server.description !== undefined && typeof server.description !== "string" || onlyKeys(server, ["url", "description"], "server")) {
|
|
711
229
|
return "Expected each openapi.servers entry to be { url, description? }.";
|
|
712
230
|
}
|
|
713
231
|
}
|
|
714
232
|
}
|
|
715
|
-
return
|
|
233
|
+
return routeOptionsIssue(options, "openapi");
|
|
716
234
|
}
|
|
717
235
|
function invalid(reason, source = "openapi") {
|
|
718
|
-
throw new
|
|
236
|
+
throw new InvalidOutputStage({ source, details: { reason } });
|
|
719
237
|
}
|
|
720
238
|
function kebab(text) {
|
|
721
239
|
return text.replace(/([a-z0-9])([A-Z])/g, "$1-$2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1-$2").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
722
240
|
}
|
|
723
|
-
function
|
|
724
|
-
return
|
|
241
|
+
function isScalarKey(operation, name) {
|
|
242
|
+
return isScalarInputKey(operation.input, name);
|
|
725
243
|
}
|
|
726
|
-
function
|
|
244
|
+
export function operationRoutes(operations, options, source = "openapi") {
|
|
727
245
|
const base = options.basePath ?? "";
|
|
728
246
|
const route = options.route ?? {};
|
|
729
247
|
const responses = options.responses ?? {};
|
|
@@ -751,7 +269,7 @@ function operationRoutes2(operations, options, source = "openapi") {
|
|
|
751
269
|
const requestHeaders = declared?.headers ?? {};
|
|
752
270
|
const cookies = declared?.cookie ?? {};
|
|
753
271
|
for (const name of parameters) {
|
|
754
|
-
if (!
|
|
272
|
+
if (!isScalarKey(operation, name)) {
|
|
755
273
|
invalid(`${source}.route["${operation.id}"].path has the parameter "{${name}}", which is not a scalar key of the input of ${operation.id}.`, source);
|
|
756
274
|
}
|
|
757
275
|
if (query.includes(name) || Object.hasOwn(requestHeaders, name) || Object.hasOwn(cookies, name)) {
|
|
@@ -833,11 +351,11 @@ function problemSchema() {
|
|
|
833
351
|
required: ["type", "title", "status", "detail"]
|
|
834
352
|
};
|
|
835
353
|
}
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
[
|
|
354
|
+
const PROBLEM_REF = "#/components/schemas/Problem";
|
|
355
|
+
const problemContent = {
|
|
356
|
+
[PROBLEM_MEDIA_TYPE]: { schema: { $ref: PROBLEM_REF } }
|
|
839
357
|
};
|
|
840
|
-
|
|
358
|
+
const STATUS_TEXT = {
|
|
841
359
|
400: "Invalid input or context.",
|
|
842
360
|
401: "Authentication is required.",
|
|
843
361
|
403: "The caller may not do this.",
|
|
@@ -846,7 +364,7 @@ var STATUS_TEXT = {
|
|
|
846
364
|
422: "Declared error of the operation.",
|
|
847
365
|
500: "Internal error."
|
|
848
366
|
};
|
|
849
|
-
|
|
367
|
+
const SUCCESS_TEXT = {
|
|
850
368
|
200: "Success.",
|
|
851
369
|
201: "Created.",
|
|
852
370
|
202: "Accepted.",
|
|
@@ -864,13 +382,13 @@ function responsesOf(operation, statuses, response, route, requestIdHeader) {
|
|
|
864
382
|
names.set(403, []);
|
|
865
383
|
}
|
|
866
384
|
for (const Declared of operation.errors) {
|
|
867
|
-
const synthetic = new
|
|
868
|
-
layer:
|
|
385
|
+
const synthetic = new Exception({
|
|
386
|
+
layer: ExceptionLayers.UseCase,
|
|
869
387
|
name: Declared.name,
|
|
870
388
|
source: "openapi",
|
|
871
389
|
message: Declared.name
|
|
872
390
|
});
|
|
873
|
-
const status =
|
|
391
|
+
const status = statusOf(synthetic, operation, statuses);
|
|
874
392
|
const list = names.get(status) ?? [];
|
|
875
393
|
if (!list.includes(Declared.name))
|
|
876
394
|
list.push(Declared.name);
|
|
@@ -948,12 +466,12 @@ function withoutKeys(schema, keys) {
|
|
|
948
466
|
function requirementOf(requirement) {
|
|
949
467
|
return { [requirement.scheme]: [...requirement.scopes ?? []] };
|
|
950
468
|
}
|
|
951
|
-
|
|
469
|
+
export const openapi = sinkOf({
|
|
952
470
|
name: "openapi",
|
|
953
471
|
validate,
|
|
954
472
|
build: (operations, options, { output }) => {
|
|
955
|
-
const routes =
|
|
956
|
-
const requestIdHeader =
|
|
473
|
+
const routes = operationRoutes(operations, options);
|
|
474
|
+
const requestIdHeader = requestIdHeaderOf(options);
|
|
957
475
|
const security = options.security;
|
|
958
476
|
const records = new Set;
|
|
959
477
|
for (const operation of operations) {
|
|
@@ -963,7 +481,7 @@ var openapi2 = sinkOf2({
|
|
|
963
481
|
const models = {};
|
|
964
482
|
const classes = new Map;
|
|
965
483
|
for (const record of records) {
|
|
966
|
-
const name =
|
|
484
|
+
const name = recordNameOf(record);
|
|
967
485
|
if (!COMPONENT_NAME.test(name) || name === "Problem") {
|
|
968
486
|
invalid(`The record class "${name}" cannot be a component name; give the class a name that is not "Problem".`);
|
|
969
487
|
}
|
|
@@ -977,7 +495,7 @@ var openapi2 = sinkOf2({
|
|
|
977
495
|
models[`${operation.id}#input`] = operation.input;
|
|
978
496
|
models[`${operation.id}#response`] = operation.response;
|
|
979
497
|
}
|
|
980
|
-
const { schemas } =
|
|
498
|
+
const { schemas } = toOpenAPI(models);
|
|
981
499
|
const paths = {};
|
|
982
500
|
const operationIds = new Set;
|
|
983
501
|
const components = {};
|
|
@@ -1051,5 +569,3 @@ var openapi2 = sinkOf2({
|
|
|
1051
569
|
};
|
|
1052
570
|
}
|
|
1053
571
|
});
|
|
1054
|
-
|
|
1055
|
-
export { schemaLockOf2, diffSchemas2, schemaLockProblemsOf2, statusOf2, PROBLEM_MEDIA_TYPE2, problemOf2, outputValueOf2, ROUTE_OPTION_KEYS2, routeOptionsIssue2, requestIdHeaderOf2, operationRoutes2, openapi2 };
|