@effect-app/infra 0.71.2 → 0.72.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/CHANGELOG.md +6 -0
- package/_cjs/api/express/makeJsonSchema.cjs +81 -54
- package/_cjs/api/express/makeJsonSchema.cjs.map +1 -1
- package/_cjs/api/express/makeJsonSchema.test.cjs +28 -4
- package/_cjs/api/express/makeJsonSchema.test.cjs.map +1 -1
- package/_src/api/express/makeJsonSchema.test.ts +34 -8
- package/_src/api/express/makeJsonSchema.ts +101 -45
- package/dist/api/express/makeJsonSchema.d.ts +2 -3
- package/dist/api/express/makeJsonSchema.d.ts.map +1 -1
- package/dist/api/express/makeJsonSchema.js +73 -42
- package/dist/api/express/makeOpenApiSpecs.d.ts +1 -1
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.
|
|
6
|
+
exports.checkPaths = checkPaths;
|
|
7
7
|
exports.makeJsonSchema = makeJsonSchema;
|
|
8
8
|
var tsplus_module_1 = _interopRequireWildcard(require("@effect/io/Effect"));
|
|
9
|
-
var tsplus_module_2 = _interopRequireWildcard(require("@effect/
|
|
10
|
-
var tsplus_module_3 = _interopRequireWildcard(require("@effect
|
|
9
|
+
var tsplus_module_2 = _interopRequireWildcard(require("@effect-app/core/_ext/Iterable"));
|
|
10
|
+
var tsplus_module_3 = _interopRequireWildcard(require("@effect/data/Chunk"));
|
|
11
11
|
var _errors = require("../../errors.cjs");
|
|
12
12
|
var RS = _interopRequireWildcard(require("./schema/routing.cjs"));
|
|
13
13
|
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
|
|
@@ -15,68 +15,95 @@ function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj &&
|
|
|
15
15
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
16
16
|
|
|
17
17
|
const rx = /:(\w+)/g;
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
const match = matches[i];
|
|
35
|
-
switch (match._tag) {
|
|
36
|
-
case "resource":
|
|
37
|
-
{
|
|
38
|
-
if (!(match.value in pathNavigator)) {
|
|
39
|
-
;
|
|
40
|
-
pathNavigator[match.value] = {
|
|
41
|
-
[methods]: []
|
|
42
|
-
};
|
|
43
|
-
}
|
|
44
|
-
pathNavigator = pathNavigator[match.value];
|
|
45
|
-
i++;
|
|
46
|
-
break;
|
|
18
|
+
class PathsTree {
|
|
19
|
+
constructor(path) {
|
|
20
|
+
this.methods = [];
|
|
21
|
+
this.params = {};
|
|
22
|
+
this.subpaths = {};
|
|
23
|
+
this.path = path;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
function checkShadowing(pathNavigator, match) {
|
|
27
|
+
return tsplus_module_1.gen(function* ($) {
|
|
28
|
+
switch (match._tag) {
|
|
29
|
+
case "resource":
|
|
30
|
+
{
|
|
31
|
+
// check shadowing: if 'a/:param' comes before 'a/b', then 'a/b' is shadowed
|
|
32
|
+
if (pathNavigator.params[1]) {
|
|
33
|
+
return yield* $(tsplus_module_1.fail(new _errors.InvalidStateError(`Path ${pathNavigator.path}${match.value}/ is shadowed by ${pathNavigator.params[1].path}`)));
|
|
47
34
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
35
|
+
break;
|
|
36
|
+
}
|
|
37
|
+
case "param":
|
|
38
|
+
{
|
|
39
|
+
// check shadowing: if 'a/b' comes before 'a/:param', then 'a/"param' is partially shadowed
|
|
40
|
+
const subpaths = Object.getOwnPropertyNames(pathNavigator.subpaths);
|
|
41
|
+
if (subpaths.length > 0) {
|
|
42
|
+
yield* $(tsplus_module_2.ext_forEach(subpaths, s => tsplus_module_1.logInfo(`Path ${pathNavigator.path}:${match.value}/ is partially shadowed by ${pathNavigator.subpaths[s].path}`)));
|
|
43
|
+
}
|
|
44
|
+
break;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
function checkPaths(paths) {
|
|
50
|
+
return tsplus_module_1.gen(function* ($) {
|
|
51
|
+
const pathMethods = new PathsTree("/");
|
|
52
|
+
const regex = /(?:^|\/)([^/:\n]+)|:(\w+)/g;
|
|
53
|
+
for (const path of paths) {
|
|
54
|
+
const matches = [...path.path.matchAll(regex)].map(match => match[1] ? {
|
|
55
|
+
_tag: "resource",
|
|
56
|
+
value: match[1]
|
|
57
|
+
} : {
|
|
58
|
+
_tag: "param",
|
|
59
|
+
value: match[2]
|
|
60
|
+
});
|
|
61
|
+
let pathNavigator = pathMethods;
|
|
62
|
+
for (let i = 0; i < matches.length;) {
|
|
63
|
+
const match = matches[i];
|
|
64
|
+
yield* $(checkShadowing(pathNavigator, match));
|
|
65
|
+
switch (match._tag) {
|
|
66
|
+
case "resource":
|
|
67
|
+
{
|
|
68
|
+
if (!(match.value in pathNavigator.subpaths)) {
|
|
69
|
+
pathNavigator.subpaths[match.value] = new PathsTree(`${pathNavigator.path}${match.value}/`);
|
|
70
|
+
}
|
|
71
|
+
pathNavigator = pathNavigator.subpaths[match.value];
|
|
53
72
|
i++;
|
|
73
|
+
break;
|
|
54
74
|
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
75
|
+
case "param":
|
|
76
|
+
{
|
|
77
|
+
const paramsNames = [match.value];
|
|
78
|
+
let numberOfParams = 1;
|
|
79
|
+
while (i < matches.length && matches[i + 1]?._tag === "param") {
|
|
80
|
+
numberOfParams++;
|
|
81
|
+
i++;
|
|
82
|
+
paramsNames.push(matches[i].value);
|
|
83
|
+
}
|
|
84
|
+
if (!(numberOfParams in pathNavigator.params)) {
|
|
85
|
+
pathNavigator.params[numberOfParams] = new PathsTree(`${pathNavigator.path}:${paramsNames.join("/:")}/`);
|
|
86
|
+
}
|
|
87
|
+
pathNavigator = pathNavigator.params[numberOfParams];
|
|
88
|
+
i++;
|
|
89
|
+
break;
|
|
60
90
|
}
|
|
61
|
-
|
|
62
|
-
i++;
|
|
63
|
-
break;
|
|
64
|
-
}
|
|
91
|
+
}
|
|
65
92
|
}
|
|
93
|
+
if (pathNavigator.methods.includes(path.method)) {
|
|
94
|
+
// throw duplicate path-method error
|
|
95
|
+
return yield* $(tsplus_module_1.fail(new _errors.InvalidStateError(`Duplicate method ${path.method} for path ${path.path}`)));
|
|
96
|
+
}
|
|
97
|
+
pathNavigator.methods.push(path.method);
|
|
66
98
|
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
return tsplus_module_1.fail(new _errors.InvalidStateError(`Duplicate method ${path.method} for path ${path.path}`));
|
|
70
|
-
}
|
|
71
|
-
pathNavigator[methods].push(path.method);
|
|
72
|
-
}
|
|
73
|
-
return tsplus_module_1.succeed(paths);
|
|
99
|
+
return paths;
|
|
100
|
+
});
|
|
74
101
|
}
|
|
75
102
|
/**
|
|
76
103
|
* Work in progress JSONSchema generator.
|
|
77
104
|
*/
|
|
78
105
|
function makeJsonSchema(r) {
|
|
79
|
-
return tsplus_module_1.map(tsplus_module_1.flatMap(
|
|
106
|
+
return tsplus_module_1.map(tsplus_module_1.flatMap(tsplus_module_2.ext_forEach(tsplus_module_3.fromIterable(r), RS.makeFromSchema), checkPaths), e => {
|
|
80
107
|
const map = ({
|
|
81
108
|
method,
|
|
82
109
|
path,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"makeJsonSchema.cjs","names":["_errors","require","RS","_interopRequireWildcard","_getRequireWildcardCache","nodeInterop","WeakMap","cacheBabelInterop","cacheNodeInterop","obj","__esModule","default","cache","has","get","newObj","hasPropertyDescriptor","Object","defineProperty","getOwnPropertyDescriptor","key","prototype","hasOwnProperty","call","desc","set","rx","
|
|
1
|
+
{"version":3,"file":"makeJsonSchema.cjs","names":["_errors","require","RS","_interopRequireWildcard","_getRequireWildcardCache","nodeInterop","WeakMap","cacheBabelInterop","cacheNodeInterop","obj","__esModule","default","cache","has","get","newObj","hasPropertyDescriptor","Object","defineProperty","getOwnPropertyDescriptor","key","prototype","hasOwnProperty","call","desc","set","rx","PathsTree","constructor","path","methods","params","subpaths","checkShadowing","pathNavigator","match","tsplus_module_1","gen","$","_tag","fail","InvalidStateError","value","getOwnPropertyNames","length","tsplus_module_2","ext_forEach","s","logInfo","checkPaths","paths","pathMethods","regex","matches","matchAll","map","i","paramsNames","numberOfParams","push","join","includes","method","makeJsonSchema","r","flatMap","tsplus_module_3","fromIterable","makeFromSchema","e","responses","rest","reduce","prev","cur","statusCode","type","split","replace","_a","b","Response"],"sources":["../../../_src/api/express/makeJsonSchema.ts"],"sourcesContent":[null],"mappings":"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"}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
3
|
var tsplus_module_1 = _interopRequireWildcard(require("@effect/io/Effect"));
|
|
4
|
+
var tsplus_module_2 = _interopRequireWildcard(require("@effect/data/Either"));
|
|
5
|
+
var tsplus_module_3 = _interopRequireWildcard(require("@effect/io/Logger"));
|
|
4
6
|
var _errors = require("../../errors.cjs");
|
|
5
7
|
var _makeJsonSchema = require("./makeJsonSchema.cjs");
|
|
6
8
|
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
|
|
@@ -13,15 +15,37 @@ const equalPaths = ["/securities/:idt/search/:id2", "/securities/:id/search/:id2
|
|
|
13
15
|
path,
|
|
14
16
|
method: "get"
|
|
15
17
|
}));
|
|
16
|
-
const differentPaths = ["/securities/:id/search/", "/securities/search/:id2", "/securities/:id/:id2/search/:id3"].map(path => ({
|
|
18
|
+
const differentPaths = ["/securities/search/:id2", "/securities/:id/search/", "/securities/:id/search/:id2", "/securities/:id/:id2/search/:id3"].map(path => ({
|
|
17
19
|
path,
|
|
18
20
|
method: "get"
|
|
19
21
|
})).concat(["/securities/:idt/search/:id2"].map(path => ({
|
|
20
22
|
path,
|
|
21
23
|
method: "post"
|
|
22
24
|
})));
|
|
23
|
-
test("works", () => {
|
|
24
|
-
expect((0, _makeJsonSchema.
|
|
25
|
-
|
|
25
|
+
test("works", async () => {
|
|
26
|
+
expect(await tsplus_module_1.runPromise((0, _makeJsonSchema.checkPaths)(differentPaths))).toStrictEqual(differentPaths);
|
|
27
|
+
for (const p of equalPaths) {
|
|
28
|
+
expect(await tsplus_module_1.runPromiseEither((0, _makeJsonSchema.checkPaths)([path, p]))).toStrictEqual(tsplus_module_2.left(new _errors.InvalidStateError(`Duplicate method ${p.method} for path ${p.path}`)));
|
|
29
|
+
}
|
|
30
|
+
expect(await tsplus_module_1.runPromiseEither((0, _makeJsonSchema.checkPaths)(["a/:p1/c/:p2", "a/b"].map(path => ({
|
|
31
|
+
path,
|
|
32
|
+
method: "get"
|
|
33
|
+
}))))).toStrictEqual(tsplus_module_2.left(new _errors.InvalidStateError(`Path /a/b/ is shadowed by /a/:p1/`)));
|
|
34
|
+
expect(await tsplus_module_1.runPromiseEither((0, _makeJsonSchema.checkPaths)(["a/:p1/c/:p2", "a/:p111/c/d"].map(path => ({
|
|
35
|
+
path,
|
|
36
|
+
method: "get"
|
|
37
|
+
}))))).toStrictEqual(tsplus_module_2.left(new _errors.InvalidStateError(`Path /a/:p1/c/d/ is shadowed by /a/:p1/c/:p2/`)));
|
|
38
|
+
});
|
|
39
|
+
test("log shadowing", async () => {
|
|
40
|
+
const arr = [];
|
|
41
|
+
const paths = ["a/b", "a/:p"].map(path => ({
|
|
42
|
+
path,
|
|
43
|
+
method: "get"
|
|
44
|
+
}));
|
|
45
|
+
const CustomLogger = tsplus_module_3.make((_, __, message) => {
|
|
46
|
+
arr.push(message);
|
|
47
|
+
});
|
|
48
|
+
await tsplus_module_1.runPromise(tsplus_module_1.provideLayer((0, _makeJsonSchema.checkPaths)(paths), tsplus_module_3.replace(tsplus_module_3.defaultLogger, CustomLogger)));
|
|
49
|
+
expect(arr).toStrictEqual([`Path /a/:p/ is partially shadowed by /a/b/`]);
|
|
26
50
|
});
|
|
27
51
|
//# sourceMappingURL=makeJsonSchema.test.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"makeJsonSchema.test.cjs","names":["_errors","require","_makeJsonSchema","_getRequireWildcardCache","nodeInterop","WeakMap","cacheBabelInterop","cacheNodeInterop","_interopRequireWildcard","obj","__esModule","default","cache","has","get","newObj","hasPropertyDescriptor","Object","defineProperty","getOwnPropertyDescriptor","key","prototype","hasOwnProperty","call","desc","set","path","method","equalPaths","map","differentPaths","concat","test","expect","
|
|
1
|
+
{"version":3,"file":"makeJsonSchema.test.cjs","names":["_errors","require","_makeJsonSchema","_getRequireWildcardCache","nodeInterop","WeakMap","cacheBabelInterop","cacheNodeInterop","_interopRequireWildcard","obj","__esModule","default","cache","has","get","newObj","hasPropertyDescriptor","Object","defineProperty","getOwnPropertyDescriptor","key","prototype","hasOwnProperty","call","desc","set","path","method","equalPaths","map","differentPaths","concat","test","expect","tsplus_module_1","runPromise","checkPaths","toStrictEqual","p","runPromiseEither","tsplus_module_2","left","InvalidStateError","arr","paths","CustomLogger","tsplus_module_3","make","_","__","message","push","provideLayer","replace","defaultLogger"],"sources":["../../../_src/api/express/makeJsonSchema.test.ts"],"sourcesContent":[null],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { InvalidStateError } from "../../errors.js"
|
|
2
|
-
import {
|
|
2
|
+
import { checkPaths } from "./makeJsonSchema.js"
|
|
3
3
|
|
|
4
4
|
const path = { path: "/securities/:id/search/:id2", method: "get" }
|
|
5
5
|
|
|
@@ -14,8 +14,9 @@ const differentPaths: {
|
|
|
14
14
|
path: string
|
|
15
15
|
method: string
|
|
16
16
|
}[] = [
|
|
17
|
-
"/securities/:id/search/",
|
|
18
17
|
"/securities/search/:id2",
|
|
18
|
+
"/securities/:id/search/",
|
|
19
|
+
"/securities/:id/search/:id2",
|
|
19
20
|
"/securities/:id/:id2/search/:id3"
|
|
20
21
|
]
|
|
21
22
|
.map((path) => ({ path, method: "get" }))
|
|
@@ -24,12 +25,37 @@ const differentPaths: {
|
|
|
24
25
|
]
|
|
25
26
|
.map((path) => ({ path, method: "post" })))
|
|
26
27
|
|
|
27
|
-
test("works", () => {
|
|
28
|
-
expect(
|
|
28
|
+
test("works", async () => {
|
|
29
|
+
expect(await (checkPaths(differentPaths)).runPromise).toStrictEqual(differentPaths)
|
|
30
|
+
|
|
31
|
+
for (const p of equalPaths) {
|
|
32
|
+
expect(await checkPaths([path, p]).runPromiseEither).toStrictEqual(
|
|
33
|
+
Either.left(new InvalidStateError(`Duplicate method ${p.method} for path ${p.path}`))
|
|
34
|
+
)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
expect(await checkPaths(["a/:p1/c/:p2", "a/b"].map((path) => ({ path, method: "get" }))).runPromiseEither)
|
|
38
|
+
.toStrictEqual(
|
|
39
|
+
Either.left(new InvalidStateError(`Path /a/b/ is shadowed by /a/:p1/`))
|
|
40
|
+
)
|
|
29
41
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
42
|
+
expect(await checkPaths(["a/:p1/c/:p2", "a/:p111/c/d"].map((path) => ({ path, method: "get" }))).runPromiseEither)
|
|
43
|
+
.toStrictEqual(
|
|
44
|
+
Either.left(new InvalidStateError(`Path /a/:p1/c/d/ is shadowed by /a/:p1/c/:p2/`))
|
|
33
45
|
)
|
|
34
|
-
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
test("log shadowing", async () => {
|
|
49
|
+
const arr: string[] = []
|
|
50
|
+
const paths = ["a/b", "a/:p"].map((path) => ({ path, method: "get" }))
|
|
51
|
+
|
|
52
|
+
const CustomLogger = Logger.make<string, void>((_, __, message) => {
|
|
53
|
+
arr.push(message)
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
await checkPaths(paths)
|
|
57
|
+
.provideLayer(Logger.replace(Logger.defaultLogger, CustomLogger))
|
|
58
|
+
.runPromise
|
|
59
|
+
|
|
60
|
+
expect(arr).toStrictEqual([`Path /a/:p/ is partially shadowed by /a/b/`])
|
|
35
61
|
})
|
|
@@ -9,62 +9,118 @@ const rx = /:(\w+)/g
|
|
|
9
9
|
|
|
10
10
|
type _A<C> = C extends ReadonlyArray<infer A> ? A : never
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
12
|
+
class PathsTree {
|
|
13
|
+
public readonly methods: string[]
|
|
14
|
+
public readonly params: { [key: number]: PathsTree | undefined }
|
|
15
|
+
public readonly subpaths: { [key: string]: PathsTree | undefined }
|
|
16
|
+
public readonly path: string
|
|
17
|
+
|
|
18
|
+
constructor(path: string) {
|
|
19
|
+
this.methods = []
|
|
20
|
+
this.params = {}
|
|
21
|
+
this.subpaths = {}
|
|
22
|
+
this.path = path
|
|
19
23
|
}
|
|
20
|
-
|
|
21
|
-
const regex = /(?:^|\/)([^/:\n]+)|:(\w+)/g
|
|
22
|
-
|
|
23
|
-
for (const path of paths) {
|
|
24
|
-
const matches = [...path.path.matchAll(regex)]
|
|
25
|
-
.map((match) =>
|
|
26
|
-
match[1]
|
|
27
|
-
? { _tag: "resource", value: match[1] } as const
|
|
28
|
-
: { _tag: "param", value: match[2]! } as const
|
|
29
|
-
)
|
|
24
|
+
}
|
|
30
25
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
26
|
+
interface Match {
|
|
27
|
+
_tag: "resource" | "param"
|
|
28
|
+
value: string
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function checkShadowing(
|
|
32
|
+
pathNavigator: PathsTree,
|
|
33
|
+
match: Match
|
|
34
|
+
) {
|
|
35
|
+
return Effect.gen(function*($) {
|
|
36
|
+
switch (match._tag) {
|
|
37
|
+
case "resource": {
|
|
38
|
+
// check shadowing: if 'a/:param' comes before 'a/b', then 'a/b' is shadowed
|
|
39
|
+
if (pathNavigator.params[1]) {
|
|
40
|
+
return yield* $(Effect.fail(
|
|
41
|
+
new InvalidStateError(
|
|
42
|
+
`Path ${pathNavigator.path}${match.value}/ is shadowed by ${pathNavigator.params[1].path}`
|
|
43
|
+
)
|
|
44
|
+
))
|
|
45
|
+
}
|
|
46
|
+
break
|
|
47
|
+
}
|
|
48
|
+
case "param": {
|
|
49
|
+
// check shadowing: if 'a/b' comes before 'a/:param', then 'a/"param' is partially shadowed
|
|
50
|
+
const subpaths = Object.getOwnPropertyNames(pathNavigator.subpaths)
|
|
51
|
+
if (subpaths.length > 0) {
|
|
52
|
+
yield* $(subpaths.forEachEffect((s) =>
|
|
53
|
+
Effect.logInfo(
|
|
54
|
+
`Path ${pathNavigator.path}:${match.value}/ is partially shadowed by ${pathNavigator.subpaths[s]!.path}`
|
|
55
|
+
)
|
|
56
|
+
))
|
|
42
57
|
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
58
|
+
break
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
})
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function checkPaths<T extends { path: string; method: string }>(
|
|
65
|
+
paths: readonly T[]
|
|
66
|
+
) {
|
|
67
|
+
return Effect.gen(function*($) {
|
|
68
|
+
const pathMethods: PathsTree = new PathsTree("/")
|
|
69
|
+
const regex = /(?:^|\/)([^/:\n]+)|:(\w+)/g
|
|
70
|
+
for (const path of paths) {
|
|
71
|
+
const matches = [...path.path.matchAll(regex)]
|
|
72
|
+
.map((match) =>
|
|
73
|
+
match[1]
|
|
74
|
+
? { _tag: "resource", value: match[1] } as const
|
|
75
|
+
: { _tag: "param", value: match[2]! } as const
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
let pathNavigator = pathMethods
|
|
79
|
+
for (let i = 0; i < matches.length;) {
|
|
80
|
+
const match = matches[i]!
|
|
81
|
+
|
|
82
|
+
yield* $(checkShadowing(pathNavigator, match))
|
|
83
|
+
|
|
84
|
+
switch (match._tag) {
|
|
85
|
+
case "resource": {
|
|
86
|
+
if (!(match.value in pathNavigator.subpaths)) {
|
|
87
|
+
pathNavigator.subpaths[match.value] = new PathsTree(`${pathNavigator.path}${match.value}/`)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
pathNavigator = pathNavigator.subpaths[match.value]!
|
|
47
91
|
i++
|
|
92
|
+
break
|
|
48
93
|
}
|
|
94
|
+
case "param": {
|
|
95
|
+
const paramsNames = [match.value]
|
|
96
|
+
let numberOfParams = 1
|
|
97
|
+
while (i < matches.length && matches[i + 1]?._tag === "param") {
|
|
98
|
+
numberOfParams++
|
|
99
|
+
i++
|
|
100
|
+
paramsNames.push(matches[i]!.value)
|
|
101
|
+
}
|
|
49
102
|
|
|
50
|
-
|
|
51
|
-
|
|
103
|
+
if (!(numberOfParams in pathNavigator.params)) {
|
|
104
|
+
pathNavigator.params[numberOfParams] = new PathsTree(
|
|
105
|
+
`${pathNavigator.path}:${paramsNames.join("/:")}/`
|
|
106
|
+
)
|
|
107
|
+
}
|
|
108
|
+
pathNavigator = pathNavigator.params[numberOfParams]!
|
|
109
|
+
i++
|
|
110
|
+
break
|
|
52
111
|
}
|
|
53
|
-
pathNavigator = (pathNavigator)[numberOfParams]!
|
|
54
|
-
i++
|
|
55
|
-
break
|
|
56
112
|
}
|
|
57
113
|
}
|
|
58
|
-
}
|
|
59
114
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
115
|
+
if (pathNavigator.methods.includes(path.method)) {
|
|
116
|
+
// throw duplicate path-method error
|
|
117
|
+
return yield* $(Effect.fail(new InvalidStateError(`Duplicate method ${path.method} for path ${path.path}`)))
|
|
118
|
+
}
|
|
119
|
+
pathNavigator.methods.push(path.method)
|
|
63
120
|
}
|
|
64
|
-
pathNavigator[methods].push(path.method)
|
|
65
|
-
}
|
|
66
121
|
|
|
67
|
-
|
|
122
|
+
return paths
|
|
123
|
+
})
|
|
68
124
|
}
|
|
69
125
|
|
|
70
126
|
/**
|
|
@@ -74,7 +130,7 @@ export function makeJsonSchema(r: Iterable<RS.RouteDescriptorAny>) {
|
|
|
74
130
|
return Chunk
|
|
75
131
|
.fromIterable(r)
|
|
76
132
|
.forEachEffect(RS.makeFromSchema)
|
|
77
|
-
.flatMap(
|
|
133
|
+
.flatMap(checkPaths)
|
|
78
134
|
.map((e) => {
|
|
79
135
|
const map = ({ method, path, responses, ...rest }: _A<typeof e>) => ({
|
|
80
136
|
[method]: {
|
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
import { Effect } from "@effect/io/Effect";
|
|
2
1
|
import { InvalidStateError } from "../../errors.js";
|
|
3
2
|
import * as RS from "./schema/routing.js";
|
|
4
3
|
type Methods = "GET" | "PUT" | "POST" | "PATCH" | "DELETE";
|
|
5
|
-
export declare function
|
|
4
|
+
export declare function checkPaths<T extends {
|
|
6
5
|
path: string;
|
|
7
6
|
method: string;
|
|
8
|
-
}>(paths: readonly T[]): Effect<never, InvalidStateError, readonly T[]>;
|
|
7
|
+
}>(paths: readonly T[]): import("@effect/io/Effect").Effect<never, InvalidStateError, readonly T[]>;
|
|
9
8
|
/**
|
|
10
9
|
* Work in progress JSONSchema generator.
|
|
11
10
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"makeJsonSchema.d.ts","sourceRoot":"","sources":["../../../_src/api/express/makeJsonSchema.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"makeJsonSchema.d.ts","sourceRoot":"","sources":["../../../_src/api/express/makeJsonSchema.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAA;AACnD,OAAO,KAAK,EAAE,MAAM,qBAAqB,CAAA;AAEzC,KAAK,OAAO,GAAG,KAAK,GAAG,KAAK,GAAG,MAAM,GAAG,OAAO,GAAG,QAAQ,CAAA;AA0D1D,wBAAgB,UAAU,CAAC,CAAC,SAAS;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,EACnE,KAAK,EAAE,SAAS,CAAC,EAAE,8EA2DpB;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAAE,QAAQ,CAAC,EAAE,CAAC,kBAAkB,CAAC;;;;;;;;;;;;;;;;;;;;;KA8BhE"}
|
|
@@ -1,61 +1,92 @@
|
|
|
1
1
|
import * as tsplus_module_1 from "@effect/io/Effect";
|
|
2
|
-
import * as tsplus_module_2 from "@effect/
|
|
3
|
-
import * as tsplus_module_3 from "@effect
|
|
2
|
+
import * as tsplus_module_2 from "@effect-app/core/_ext/Iterable";
|
|
3
|
+
import * as tsplus_module_3 from "@effect/data/Chunk";
|
|
4
4
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
5
5
|
import { InvalidStateError } from "../../errors.js";
|
|
6
6
|
import * as RS from "./schema/routing.js";
|
|
7
7
|
const rx = /:(\w+)/g;
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
;
|
|
24
|
-
(pathNavigator)[match.value] = { [methods]: [] };
|
|
25
|
-
}
|
|
26
|
-
pathNavigator = (pathNavigator)[match.value];
|
|
27
|
-
i++;
|
|
28
|
-
break;
|
|
8
|
+
class PathsTree {
|
|
9
|
+
constructor(path) {
|
|
10
|
+
this.methods = [];
|
|
11
|
+
this.params = {};
|
|
12
|
+
this.subpaths = {};
|
|
13
|
+
this.path = path;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
function checkShadowing(pathNavigator, match) {
|
|
17
|
+
return tsplus_module_1.gen(function* ($) {
|
|
18
|
+
switch (match._tag) {
|
|
19
|
+
case "resource": {
|
|
20
|
+
// check shadowing: if 'a/:param' comes before 'a/b', then 'a/b' is shadowed
|
|
21
|
+
if (pathNavigator.params[1]) {
|
|
22
|
+
return yield* $(tsplus_module_1.fail(new InvalidStateError(`Path ${pathNavigator.path}${match.value}/ is shadowed by ${pathNavigator.params[1].path}`)));
|
|
29
23
|
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
24
|
+
break;
|
|
25
|
+
}
|
|
26
|
+
case "param": {
|
|
27
|
+
// check shadowing: if 'a/b' comes before 'a/:param', then 'a/"param' is partially shadowed
|
|
28
|
+
const subpaths = Object.getOwnPropertyNames(pathNavigator.subpaths);
|
|
29
|
+
if (subpaths.length > 0) {
|
|
30
|
+
yield* $(tsplus_module_2.ext_forEach(subpaths, (s) => tsplus_module_1.logInfo(`Path ${pathNavigator.path}:${match.value}/ is partially shadowed by ${pathNavigator.subpaths[s].path}`)));
|
|
31
|
+
}
|
|
32
|
+
break;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
export function checkPaths(paths) {
|
|
38
|
+
return tsplus_module_1.gen(function* ($) {
|
|
39
|
+
const pathMethods = new PathsTree("/");
|
|
40
|
+
const regex = /(?:^|\/)([^/:\n]+)|:(\w+)/g;
|
|
41
|
+
for (const path of paths) {
|
|
42
|
+
const matches = [...path.path.matchAll(regex)]
|
|
43
|
+
.map((match) => match[1]
|
|
44
|
+
? { _tag: "resource", value: match[1] }
|
|
45
|
+
: { _tag: "param", value: match[2] });
|
|
46
|
+
let pathNavigator = pathMethods;
|
|
47
|
+
for (let i = 0; i < matches.length;) {
|
|
48
|
+
const match = matches[i];
|
|
49
|
+
yield* $(checkShadowing(pathNavigator, match));
|
|
50
|
+
switch (match._tag) {
|
|
51
|
+
case "resource": {
|
|
52
|
+
if (!(match.value in pathNavigator.subpaths)) {
|
|
53
|
+
pathNavigator.subpaths[match.value] = new PathsTree(`${pathNavigator.path}${match.value}/`);
|
|
54
|
+
}
|
|
55
|
+
pathNavigator = pathNavigator.subpaths[match.value];
|
|
34
56
|
i++;
|
|
57
|
+
break;
|
|
35
58
|
}
|
|
36
|
-
|
|
37
|
-
;
|
|
38
|
-
|
|
59
|
+
case "param": {
|
|
60
|
+
const paramsNames = [match.value];
|
|
61
|
+
let numberOfParams = 1;
|
|
62
|
+
while (i < matches.length && matches[i + 1]?._tag === "param") {
|
|
63
|
+
numberOfParams++;
|
|
64
|
+
i++;
|
|
65
|
+
paramsNames.push(matches[i].value);
|
|
66
|
+
}
|
|
67
|
+
if (!(numberOfParams in pathNavigator.params)) {
|
|
68
|
+
pathNavigator.params[numberOfParams] = new PathsTree(`${pathNavigator.path}:${paramsNames.join("/:")}/`);
|
|
69
|
+
}
|
|
70
|
+
pathNavigator = pathNavigator.params[numberOfParams];
|
|
71
|
+
i++;
|
|
72
|
+
break;
|
|
39
73
|
}
|
|
40
|
-
pathNavigator = (pathNavigator)[numberOfParams];
|
|
41
|
-
i++;
|
|
42
|
-
break;
|
|
43
74
|
}
|
|
44
75
|
}
|
|
76
|
+
if (pathNavigator.methods.includes(path.method)) {
|
|
77
|
+
// throw duplicate path-method error
|
|
78
|
+
return yield* $(tsplus_module_1.fail(new InvalidStateError(`Duplicate method ${path.method} for path ${path.path}`)));
|
|
79
|
+
}
|
|
80
|
+
pathNavigator.methods.push(path.method);
|
|
45
81
|
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
return tsplus_module_1.fail(new InvalidStateError(`Duplicate method ${path.method} for path ${path.path}`));
|
|
49
|
-
}
|
|
50
|
-
pathNavigator[methods].push(path.method);
|
|
51
|
-
}
|
|
52
|
-
return tsplus_module_1.succeed(paths);
|
|
82
|
+
return paths;
|
|
83
|
+
});
|
|
53
84
|
}
|
|
54
85
|
/**
|
|
55
86
|
* Work in progress JSONSchema generator.
|
|
56
87
|
*/
|
|
57
88
|
export function makeJsonSchema(r) {
|
|
58
|
-
return tsplus_module_1.map(tsplus_module_1.flatMap(
|
|
89
|
+
return tsplus_module_1.map(tsplus_module_1.flatMap(tsplus_module_2.ext_forEach(tsplus_module_3.fromIterable(r), RS.makeFromSchema), checkPaths), (e) => {
|
|
59
90
|
const map = ({ method, path, responses, ...rest }) => ({
|
|
60
91
|
[method]: {
|
|
61
92
|
...rest,
|
|
@@ -82,4 +113,4 @@ class Response {
|
|
|
82
113
|
this.type = type;
|
|
83
114
|
}
|
|
84
115
|
}
|
|
85
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
116
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWFrZUpzb25TY2hlbWEuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9fc3JjL2FwaS9leHByZXNzL21ha2VKc29uU2NoZW1hLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUFBLHVEQUF1RDtBQUV2RCxPQUFPLEVBQUUsaUJBQWlCLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQTtBQUNuRCxPQUFPLEtBQUssRUFBRSxNQUFNLHFCQUFxQixDQUFBO0FBSXpDLE1BQU0sRUFBRSxHQUFHLFNBQVMsQ0FBQTtBQUlwQixNQUFNLFNBQVM7SUFNYixZQUFZLElBQVk7UUFDdEIsSUFBSSxDQUFDLE9BQU8sR0FBRyxFQUFFLENBQUE7UUFDakIsSUFBSSxDQUFDLE1BQU0sR0FBRyxFQUFFLENBQUE7UUFDaEIsSUFBSSxDQUFDLFFBQVEsR0FBRyxFQUFFLENBQUE7UUFDbEIsSUFBSSxDQUFDLElBQUksR0FBRyxJQUFJLENBQUE7SUFDbEIsQ0FBQztDQUNGO0FBT0QsU0FBUyxjQUFjLENBQ3JCLGFBQXdCLEVBQ3hCLEtBQVk7SUFFWixPQUFPLG9CQUFXLFFBQVEsQ0FBQyxFQUFDLENBQUM7UUFDM0IsUUFBUSxLQUFLLENBQUMsSUFBSSxFQUFFO1lBQ2xCLEtBQUssVUFBVSxDQUFDLENBQUM7Z0JBQ2YsNEVBQTRFO2dCQUM1RSxJQUFJLGFBQWEsQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLEVBQUU7b0JBQzNCLE9BQU8sS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDLHFCQUNkLElBQUksaUJBQWlCLENBQ25CLFFBQVEsYUFBYSxDQUFDLElBQUksR0FBRyxLQUFLLENBQUMsS0FBSyxvQkFBb0IsYUFBYSxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsQ0FBQyxJQUFJLEVBQUUsQ0FDM0YsQ0FDRixDQUFDLENBQUE7aUJBQ0g7Z0JBQ0QsTUFBSzthQUNOO1lBQ0QsS0FBSyxPQUFPLENBQUMsQ0FBQztnQkFDWiwyRkFBMkY7Z0JBQzNGLE1BQU0sUUFBUSxH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
|
|
@@ -18,7 +18,7 @@ export declare function makeOpenApiSpecs(rdescs: Iterable<RouteDescriptorAny>, i
|
|
|
18
18
|
version: string;
|
|
19
19
|
};
|
|
20
20
|
tags: never[];
|
|
21
|
-
paths: Record<string, Record<"
|
|
21
|
+
paths: Record<string, Record<"GET" | "PUT" | "POST" | "PATCH" | "DELETE", {
|
|
22
22
|
[x: string]: {
|
|
23
23
|
responses: any;
|
|
24
24
|
tags: readonly string[] | undefined;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@effect-app/infra",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.72.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"dependencies": {
|
|
@@ -21,8 +21,8 @@
|
|
|
21
21
|
"redlock": "^4.2.0",
|
|
22
22
|
"@effect-app/core": "0.56.1",
|
|
23
23
|
"@effect-app/infra-adapters": "0.49.2",
|
|
24
|
-
"@effect-app/
|
|
25
|
-
"@effect-app/
|
|
24
|
+
"@effect-app/prelude": "0.58.2",
|
|
25
|
+
"@effect-app/schema": "0.50.1"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
28
|
"@babel/cli": "^7.22.5",
|