@afora/core 0.0.8 → 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 +149 -23
- package/dist/adapters.d.ts +25 -0
- package/dist/adapters.js +32 -0
- package/dist/core/application/application.d.ts +30 -0
- package/dist/core/application/application.js +1291 -0
- package/dist/core/application/exceptions.d.ts +17 -0
- package/dist/core/application/exceptions.js +85 -0
- package/dist/core/application/migrate.d.ts +19 -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.d.ts +30 -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 +60 -3
- package/dist/core/auth/jwt.d.ts +30 -0
- package/dist/core/auth/jwt.js +200 -0
- package/dist/core/auth/types/jwt.interface.d.ts +74 -0
- 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.d.ts +5 -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.d.ts +3 -1
- 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 +95 -5
- package/dist/core/blueprint/variant.d.ts +32 -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 +24 -0
- package/dist/core/entity/abstract-entity.js +181 -0
- package/dist/core/entity/entity.d.ts +1 -1
- 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.d.ts +9 -0
- package/dist/core/entity/exceptions.js +27 -0
- package/dist/core/entity/trust.d.ts +24 -0
- package/dist/core/entity/trust.js +7 -0
- package/dist/core/entity/types/entity.interface.d.ts +30 -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.d.ts +16 -0
- package/dist/core/output/access.js +66 -0
- package/dist/core/output/coerce.js +38 -0
- package/dist/core/output/exceptions.d.ts +16 -0
- package/dist/core/output/exceptions.js +22 -0
- package/dist/core/output/http.d.ts +36 -0
- package/dist/core/output/http.js +351 -0
- package/dist/core/output/openapi-sink.d.ts +33 -4
- package/dist/core/output/openapi-sink.js +571 -0
- 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.d.ts +7 -2
- package/dist/core/output/stage.js +111 -0
- package/dist/core/output/status.d.ts +1 -1
- package/dist/core/output/status.js +52 -0
- package/dist/core/output/types/http.interface.d.ts +134 -0
- package/dist/core/output/types/output.interface.d.ts +49 -5
- package/dist/core/parse/issue.d.ts +28 -3
- package/dist/core/parse/issue.js +31 -0
- package/dist/core/parse/parse.d.ts +16 -0
- package/dist/core/parse/parse.js +323 -0
- package/dist/core/parse/strip.js +48 -0
- package/dist/core/property/abstract-property.d.ts +9 -0
- package/dist/core/property/abstract-property.js +37 -0
- package/dist/core/property/define-property.d.ts +34 -6
- 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/property/types/property-options.interface.d.ts +10 -0
- package/dist/core/property/types/property-type.interface.d.ts +4 -0
- package/dist/core/record/abstract-record.d.ts +8 -0
- 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 +17 -1
- package/dist/core/repository/cursor.js +83 -0
- package/dist/core/repository/exceptions.d.ts +13 -0
- package/dist/core/repository/exceptions.js +72 -0
- package/dist/core/repository/in-memory.d.ts +2 -2
- package/dist/core/repository/in-memory.js +196 -0
- package/dist/core/repository/migrate.d.ts +20 -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.d.ts +2 -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 +50 -5
- package/dist/core/repository/types/schema.interface.d.ts +132 -0
- package/dist/core/repository/upcast.d.ts +64 -0
- package/dist/core/repository/upcast.js +179 -0
- package/dist/core/schema/schema.d.ts +37 -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.d.ts +1 -1
- 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.d.ts +12 -0
- package/dist/core/use-case/access.js +56 -0
- package/dist/core/use-case/decorators.d.ts +40 -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.d.ts +18 -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 +233 -0
- 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/access.interface.d.ts +39 -0
- package/dist/core/use-case/types/route.interface.d.ts +86 -0
- package/dist/core/use-case/use-case.d.ts +127 -2
- package/dist/core/use-case/use-case.js +493 -0
- package/dist/experimental.d.ts +18 -1
- package/dist/experimental.js +18 -123
- package/dist/index.d.ts +49 -51
- package/dist/index.js +213 -443
- 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.d.ts +0 -2
- 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.d.ts +2 -2
- 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.d.ts +31 -0
- package/dist/std/resource.js +39 -0
- package/dist/std/runtime.d.ts +6 -0
- package/dist/std/runtime.js +193 -0
- package/dist/std/stages.js +4 -0
- package/dist/std/use-cases/auth.d.ts +5 -1
- package/dist/std/use-cases/auth.js +193 -0
- package/dist/std/use-cases/crud.js +261 -0
- package/dist/std/use-cases/index.d.ts +1 -1
- package/dist/std/use-cases/index.js +12 -0
- package/dist/std/use-cases/lifecycle.js +97 -0
- package/dist/std/use-cases/shared.d.ts +2 -2
- package/dist/std/use-cases/shared.js +232 -0
- package/dist/std/use-cases/types.d.ts +102 -2
- package/dist/std/use-cases/types.js +0 -0
- package/dist/testing/http-provider-contract.d.ts +32 -0
- package/dist/testing/http-provider-contract.js +344 -0
- package/dist/testing/index.d.ts +2 -0
- package/dist/testing/index.js +3 -944
- package/dist/testing/memory-publisher.js +12 -0
- package/dist/testing/provider-contract.d.ts +26 -1
- package/dist/testing/provider-contract.js +1071 -0
- package/package.json +23 -3
- package/dist/index-qxfkyd6k.js +0 -1721
- package/dist/index-t5qwtyj5.js +0 -8194
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import type { ExceptionLayers } from "../exception/enums/exception-layers.js";
|
|
2
|
+
/**
|
|
3
|
+
* What a span carries: names and outcomes only, never an input, an argument,
|
|
4
|
+
* a response or any value of a record.
|
|
5
|
+
*/
|
|
6
|
+
export type TraceAttributes = Readonly<Record<string, string | number | boolean>>;
|
|
7
|
+
/** How a span ended. */
|
|
8
|
+
export type TraceStatus = "ok" | "error";
|
|
9
|
+
/** A span afora started; it ends it once, with the attributes of the outcome. */
|
|
10
|
+
export interface ITraceSpan {
|
|
11
|
+
end(status: TraceStatus, attributes: TraceAttributes): void;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* What traces an application: a span per command operation, use case and
|
|
15
|
+
* repository call made through a use case. `@afora/otel` adapts an
|
|
16
|
+
* OpenTelemetry `Tracer` to it; `memoryTracer()` keeps the spans for tests.
|
|
17
|
+
*/
|
|
18
|
+
export interface ITracer {
|
|
19
|
+
/**
|
|
20
|
+
* Starts a span. `parent` is the span of the call that made this one (the
|
|
21
|
+
* command of a use case, the use case of a repository call), `undefined`
|
|
22
|
+
* for a call that starts a trace.
|
|
23
|
+
*/
|
|
24
|
+
startSpan(name: string, attributes: TraceAttributes, parent: ITraceSpan | undefined): ITraceSpan;
|
|
25
|
+
/**
|
|
26
|
+
* Runs `run` with `span` as the current span, when the tracer has a notion
|
|
27
|
+
* of one (an OpenTelemetry context): what your code and the instrumented
|
|
28
|
+
* drivers start inside the call is nested in its span.
|
|
29
|
+
*/
|
|
30
|
+
withSpan?<Result>(span: ITraceSpan, run: () => Result): Result;
|
|
31
|
+
}
|
|
32
|
+
/** Throws unless `value` can trace an application. */
|
|
33
|
+
export declare function checkTracer(value: unknown): ITracer;
|
|
34
|
+
/**
|
|
35
|
+
* Carries the tracer of an application, when it has a `trace`: on the
|
|
36
|
+
* application and on the use case classes it binds.
|
|
37
|
+
*/
|
|
38
|
+
export declare const ApplicationTracerKey: unique symbol;
|
|
39
|
+
/**
|
|
40
|
+
* The tracer of a call: the one its scope carries, else the one of the
|
|
41
|
+
* application its class was bound by, else the started application's.
|
|
42
|
+
*/
|
|
43
|
+
export declare function resolveTracer(scoped: ITracer | undefined, owner: unknown): ITracer | undefined;
|
|
44
|
+
/** Starts a span; a tracer that throws never breaks the call it traces. */
|
|
45
|
+
export declare function startSpan(tracer: ITracer, name: string, attributes: TraceAttributes, parent: ITraceSpan | undefined): ITraceSpan | undefined;
|
|
46
|
+
/** Runs `run` inside `span`, if the tracer can make it current. */
|
|
47
|
+
export declare function runInSpan<Result>(tracer: ITracer, span: ITraceSpan, run: () => Result): Result;
|
|
48
|
+
/** Ends a span that succeeded. */
|
|
49
|
+
export declare function endSpan(span: ITraceSpan | undefined): void;
|
|
50
|
+
/**
|
|
51
|
+
* Ends a span that failed, with the layer and name of the exception where it
|
|
52
|
+
* was first raised (`layer` and `UnexpectedError` for anything else thrown).
|
|
53
|
+
* Never its message, details or cause: they can hold values.
|
|
54
|
+
*/
|
|
55
|
+
export declare function failSpan(span: ITraceSpan | undefined, error: unknown, layer: ExceptionLayers): void;
|
|
56
|
+
/** A span as `memoryTracer` keeps it. */
|
|
57
|
+
export interface MemorySpan {
|
|
58
|
+
readonly name: string;
|
|
59
|
+
/** Those it started with, then those it ended with. */
|
|
60
|
+
readonly attributes: TraceAttributes;
|
|
61
|
+
readonly parent: MemorySpan | undefined;
|
|
62
|
+
/** `undefined` until it ends. */
|
|
63
|
+
readonly status: TraceStatus | undefined;
|
|
64
|
+
}
|
|
65
|
+
/** A tracer that keeps the spans, for tests. */
|
|
66
|
+
export interface MemoryTracer extends ITracer {
|
|
67
|
+
/** Every span, in the order they started. */
|
|
68
|
+
readonly spans: readonly MemorySpan[];
|
|
69
|
+
clear(): void;
|
|
70
|
+
}
|
|
71
|
+
/** Keeps every span in `spans`, with its parent, for tests. */
|
|
72
|
+
export declare function memoryTracer(): MemoryTracer;
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { startedApplication } from "../application/registry.js";
|
|
2
|
+
import { Exception, UNEXPECTED_ERROR } from "../exception/exception.js";
|
|
3
|
+
import { InvalidTraceDeclaration } from "./exceptions.js";
|
|
4
|
+
export function checkTracer(value) {
|
|
5
|
+
if (typeof value !== "object" || value === null || typeof value.startSpan !== "function" || value.withSpan !== undefined && typeof value.withSpan !== "function") {
|
|
6
|
+
throw new InvalidTraceDeclaration({
|
|
7
|
+
source: "trace",
|
|
8
|
+
details: {
|
|
9
|
+
reason: "Expected `trace` to be a tracer: { startSpan(name, attributes, parent), withSpan? }."
|
|
10
|
+
}
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
return value;
|
|
14
|
+
}
|
|
15
|
+
export const ApplicationTracerKey = Symbol("afora.application.tracer");
|
|
16
|
+
export function resolveTracer(scoped, owner) {
|
|
17
|
+
return scoped ?? owner?.[ApplicationTracerKey] ?? startedApplication()?.[ApplicationTracerKey];
|
|
18
|
+
}
|
|
19
|
+
export function startSpan(tracer, name, attributes, parent) {
|
|
20
|
+
try {
|
|
21
|
+
return tracer.startSpan(name, attributes, parent);
|
|
22
|
+
} catch {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
export function runInSpan(tracer, span, run) {
|
|
27
|
+
if (tracer.withSpan === undefined)
|
|
28
|
+
return run();
|
|
29
|
+
let ran = "no";
|
|
30
|
+
let result;
|
|
31
|
+
let thrown;
|
|
32
|
+
const once = () => {
|
|
33
|
+
try {
|
|
34
|
+
result = run();
|
|
35
|
+
ran = "returned";
|
|
36
|
+
return result;
|
|
37
|
+
} catch (error) {
|
|
38
|
+
ran = "threw";
|
|
39
|
+
thrown = error;
|
|
40
|
+
throw error;
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
try {
|
|
44
|
+
return tracer.withSpan(span, once);
|
|
45
|
+
} catch {
|
|
46
|
+
if (ran === "threw")
|
|
47
|
+
throw thrown;
|
|
48
|
+
return ran === "returned" ? result : run();
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
const OK = Object.freeze({ "afora.outcome": "ok" });
|
|
52
|
+
export function endSpan(span) {
|
|
53
|
+
if (span === undefined)
|
|
54
|
+
return;
|
|
55
|
+
try {
|
|
56
|
+
span.end("ok", OK);
|
|
57
|
+
} catch {}
|
|
58
|
+
}
|
|
59
|
+
export function failSpan(span, error, layer) {
|
|
60
|
+
if (span === undefined)
|
|
61
|
+
return;
|
|
62
|
+
const origin = error instanceof Exception ? error.origin : undefined;
|
|
63
|
+
const name = origin?.name ?? UNEXPECTED_ERROR;
|
|
64
|
+
try {
|
|
65
|
+
span.end("error", {
|
|
66
|
+
"afora.outcome": "error",
|
|
67
|
+
"afora.exception.layer": origin?.layer ?? layer,
|
|
68
|
+
"afora.exception.name": name,
|
|
69
|
+
"afora.exception.unexpected": name === UNEXPECTED_ERROR
|
|
70
|
+
});
|
|
71
|
+
} catch {}
|
|
72
|
+
}
|
|
73
|
+
export function memoryTracer() {
|
|
74
|
+
const spans = [];
|
|
75
|
+
const recorded = new WeakMap;
|
|
76
|
+
return Object.freeze({
|
|
77
|
+
spans,
|
|
78
|
+
clear: () => {
|
|
79
|
+
spans.length = 0;
|
|
80
|
+
},
|
|
81
|
+
startSpan(name, attributes, parent) {
|
|
82
|
+
const span = {
|
|
83
|
+
name,
|
|
84
|
+
attributes: { ...attributes },
|
|
85
|
+
parent: parent === undefined ? undefined : recorded.get(parent),
|
|
86
|
+
status: undefined
|
|
87
|
+
};
|
|
88
|
+
spans.push(span);
|
|
89
|
+
const handle = {
|
|
90
|
+
end(status, ended) {
|
|
91
|
+
if (span.status !== undefined)
|
|
92
|
+
return;
|
|
93
|
+
span.status = status;
|
|
94
|
+
span.attributes = { ...span.attributes, ...ended };
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
recorded.set(handle, span);
|
|
98
|
+
return handle;
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { AnyBlueprint } from "../blueprint/types/blueprint.interface.js";
|
|
2
|
+
import type { AccessDeclaration } from "./types/access.interface.js";
|
|
3
|
+
/** Whether `access` lets anyone in. */
|
|
4
|
+
export declare function isPublicAccess(access: AccessDeclaration): access is {
|
|
5
|
+
readonly public: true;
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* The frozen declaration of `access` for a use case whose input is
|
|
9
|
+
* `blueprint`, or the reason it is not valid. The keys of `context` are
|
|
10
|
+
* checked where the context is known: when the use case becomes an operation.
|
|
11
|
+
*/
|
|
12
|
+
export declare function accessDeclarationOf(access: unknown, blueprint: AnyBlueprint): AccessDeclaration | string;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { isScalarKey } from "./route.js";
|
|
2
|
+
function isObject(value) {
|
|
3
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
4
|
+
}
|
|
5
|
+
const ACCESS_KEYS = ["public", "scopes", "owner", "context"];
|
|
6
|
+
const PRINCIPAL_VALUE = /^(subject|scopes|claims\.[^.]+)$/;
|
|
7
|
+
export function isPublicAccess(access) {
|
|
8
|
+
return access.public === true;
|
|
9
|
+
}
|
|
10
|
+
export function accessDeclarationOf(access, blueprint) {
|
|
11
|
+
if (!isObject(access)) {
|
|
12
|
+
return "Expected access to be { public: true } or { scopes?, owner?, context? }.";
|
|
13
|
+
}
|
|
14
|
+
for (const key of Object.keys(access)) {
|
|
15
|
+
if (!ACCESS_KEYS.includes(key))
|
|
16
|
+
return `access has no option "${key}".`;
|
|
17
|
+
}
|
|
18
|
+
const { scopes, owner, context } = access;
|
|
19
|
+
if (access.public !== undefined && typeof access.public !== "boolean") {
|
|
20
|
+
return "Expected access.public to be a boolean.";
|
|
21
|
+
}
|
|
22
|
+
if (access.public === true) {
|
|
23
|
+
if (Object.keys(access).length > 1) {
|
|
24
|
+
return "access.public leaves no room for scopes, owner or context: anyone may call.";
|
|
25
|
+
}
|
|
26
|
+
return Object.freeze({ public: true });
|
|
27
|
+
}
|
|
28
|
+
if (scopes !== undefined) {
|
|
29
|
+
if (!Array.isArray(scopes) || scopes.some((scope) => typeof scope !== "string" || scope === "") || new Set(scopes).size !== scopes.length) {
|
|
30
|
+
return "Expected access.scopes to be distinct, non-empty strings.";
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
if (owner !== undefined) {
|
|
34
|
+
if (!isObject(owner) || Object.keys(owner).some((key) => key !== "input" && key !== "claim") || typeof owner.input !== "string" || owner.claim !== undefined && (typeof owner.claim !== "string" || owner.claim === "")) {
|
|
35
|
+
return "Expected access.owner to be { input, claim? }.";
|
|
36
|
+
}
|
|
37
|
+
if (!isScalarKey(blueprint, owner.input)) {
|
|
38
|
+
return `access.owner compares "${owner.input}", which is not a scalar key of the input.`;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
if (context !== undefined) {
|
|
42
|
+
if (!isObject(context)) {
|
|
43
|
+
return "Expected access.context to map context keys to values of the caller.";
|
|
44
|
+
}
|
|
45
|
+
for (const [key, value] of Object.entries(context)) {
|
|
46
|
+
if (typeof value !== "string" || !PRINCIPAL_VALUE.test(value)) {
|
|
47
|
+
return `Expected access.context["${key}"] to be "subject", "scopes" or "claims.<name>".`;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return Object.freeze({
|
|
52
|
+
...scopes === undefined ? {} : { scopes: Object.freeze([...scopes]) },
|
|
53
|
+
...owner === undefined ? {} : { owner: Object.freeze({ ...owner }) },
|
|
54
|
+
...context === undefined ? {} : { context: Object.freeze({ ...context }) }
|
|
55
|
+
});
|
|
56
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { AccessDeclaration } from "./types/access.interface.js";
|
|
2
|
+
import type { RouteSettings } from "./types/route.interface.js";
|
|
3
|
+
/** What a decorator of this module is given besides the class: nothing it reads. */
|
|
4
|
+
type ClassContext = {
|
|
5
|
+
readonly kind: string;
|
|
6
|
+
};
|
|
7
|
+
/** A class decorator for use cases, in the standard form and in the legacy one of TypeScript. */
|
|
8
|
+
export type UseCaseDecorator = <UseCase extends abstract new (...args: any[]) => unknown>(value: UseCase, context?: ClassContext) => void;
|
|
9
|
+
/**
|
|
10
|
+
* Says where a use case is served over HTTP, on the class itself:
|
|
11
|
+
*
|
|
12
|
+
* ```ts
|
|
13
|
+
* @Route("GET", "/posts/{slug}", { query: ["page"], cookie: { session: "sid" } })
|
|
14
|
+
* class ListPosts extends useCaseOf({ name: "ListPosts", blueprint, response }) {}
|
|
15
|
+
* ```
|
|
16
|
+
*
|
|
17
|
+
* The same as the `route` setting of `useCaseOf` (see `RouteDeclaration`), so
|
|
18
|
+
* `describe()`, the sinks and `afora.json` see one declaration. It names no
|
|
19
|
+
* server: the sink of an output does. Throws `InvalidUseCaseDeclaration` on
|
|
20
|
+
* a class that is not a use case, on one that already has another route
|
|
21
|
+
* (the same one again is accepted), and for a route that does not fit the
|
|
22
|
+
* input of the use case.
|
|
23
|
+
*/
|
|
24
|
+
export declare function Route(method: RouteSettings["method"], path?: string, options?: Omit<RouteSettings, "method" | "path">): UseCaseDecorator;
|
|
25
|
+
export declare function Route(method: RouteSettings["method"], options?: Omit<RouteSettings, "method" | "path">): UseCaseDecorator;
|
|
26
|
+
/**
|
|
27
|
+
* Says who may call a use case, on the class itself:
|
|
28
|
+
*
|
|
29
|
+
* ```ts
|
|
30
|
+
* @Access({ scopes: ["posts:write"], context: { userId: "subject" } })
|
|
31
|
+
* class PublishPost extends useCaseOf({ name: "PublishPost", blueprint, response, context }) {}
|
|
32
|
+
* ```
|
|
33
|
+
*
|
|
34
|
+
* The same as the `access` setting of `useCaseOf` (see `AccessDeclaration`).
|
|
35
|
+
* Throws `InvalidUseCaseDeclaration` on a class that is not a use case, on
|
|
36
|
+
* one that already declares another access (the same one again is accepted),
|
|
37
|
+
* and for a declaration that does not fit the input of the use case.
|
|
38
|
+
*/
|
|
39
|
+
export declare function Access(access: AccessDeclaration): UseCaseDecorator;
|
|
40
|
+
export {};
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { Kind } from "../symbols/kind.js";
|
|
2
|
+
import { accessDeclarationOf } from "./access.js";
|
|
3
|
+
import { routeDeclarationOf } from "./route.js";
|
|
4
|
+
import { InvalidUseCaseDeclaration } from "./use-case.js";
|
|
5
|
+
function sameData(a, b) {
|
|
6
|
+
if (a === b)
|
|
7
|
+
return true;
|
|
8
|
+
if (typeof a !== "object" || typeof b !== "object" || a === null || b === null || Array.isArray(a) !== Array.isArray(b)) {
|
|
9
|
+
return false;
|
|
10
|
+
}
|
|
11
|
+
const left = Object.keys(a);
|
|
12
|
+
return left.length === Object.keys(b).length && left.every((key) => Object.hasOwn(b, key) && sameData(a[key], b[key]));
|
|
13
|
+
}
|
|
14
|
+
function decoratedUseCase(value, decorator) {
|
|
15
|
+
if (typeof value !== "function" || value[Kind] !== "use-case") {
|
|
16
|
+
throw new InvalidUseCaseDeclaration({
|
|
17
|
+
source: decorator,
|
|
18
|
+
details: {
|
|
19
|
+
reason: `${decorator} decorates a class that extends useCaseOf(...).`
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
return value;
|
|
24
|
+
}
|
|
25
|
+
export function Route(method, pathOrOptions, options) {
|
|
26
|
+
const settings = typeof pathOrOptions === "string" ? { ...options, method, path: pathOrOptions } : { ...pathOrOptions, method };
|
|
27
|
+
return (value) => {
|
|
28
|
+
const UseCase = decoratedUseCase(value, "@Route");
|
|
29
|
+
const invalid = (reason) => {
|
|
30
|
+
throw new InvalidUseCaseDeclaration({
|
|
31
|
+
source: UseCase.useCaseName,
|
|
32
|
+
details: { reason }
|
|
33
|
+
});
|
|
34
|
+
};
|
|
35
|
+
const declared = routeDeclarationOf(settings, UseCase.blueprint, UseCase.response);
|
|
36
|
+
if (typeof declared === "string")
|
|
37
|
+
return invalid(declared);
|
|
38
|
+
if (UseCase.route !== undefined) {
|
|
39
|
+
if (sameData(UseCase.route, declared))
|
|
40
|
+
return;
|
|
41
|
+
invalid("The use case already has another route: declare it once, with the route setting or with @Route.");
|
|
42
|
+
}
|
|
43
|
+
Object.defineProperty(UseCase, "route", { value: declared });
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
export function Access(access) {
|
|
47
|
+
return (value) => {
|
|
48
|
+
const UseCase = decoratedUseCase(value, "@Access");
|
|
49
|
+
const invalid = (reason) => {
|
|
50
|
+
throw new InvalidUseCaseDeclaration({
|
|
51
|
+
source: UseCase.useCaseName,
|
|
52
|
+
details: { reason }
|
|
53
|
+
});
|
|
54
|
+
};
|
|
55
|
+
const declared = accessDeclarationOf(access, UseCase.blueprint);
|
|
56
|
+
if (typeof declared === "string")
|
|
57
|
+
return invalid(declared);
|
|
58
|
+
if (UseCase.access !== undefined) {
|
|
59
|
+
if (sameData(UseCase.access, declared))
|
|
60
|
+
return;
|
|
61
|
+
invalid("The use case already declares another access: declare it once, with the access setting or with @Access.");
|
|
62
|
+
}
|
|
63
|
+
Object.defineProperty(UseCase, "access", { value: declared });
|
|
64
|
+
};
|
|
65
|
+
}
|
|
@@ -1,12 +1,16 @@
|
|
|
1
|
+
import type { Issue } from "../parse/types/parse.interface.js";
|
|
1
2
|
/**
|
|
2
3
|
* A step of a use case is invalid: at declaration (`useCaseOf({ steps })`),
|
|
3
4
|
* or while the steps run (a `change` or `invoke` on something that is not a
|
|
4
|
-
* record or has no such member
|
|
5
|
+
* record or has no such member, or `args` that a `run`'s `params` reject).
|
|
6
|
+
* `details.index` is the position of the step.
|
|
5
7
|
*/
|
|
6
8
|
export declare const InvalidUseCaseStep: import("../../index.js").DeclaredExceptionClass<{
|
|
7
9
|
readonly index: number;
|
|
8
10
|
readonly reason: string;
|
|
9
11
|
readonly code?: string;
|
|
12
|
+
/** The issues of the `args` a `run` gave its `params` (`code: "invalid_args"`). */
|
|
13
|
+
readonly issues?: readonly Issue[];
|
|
10
14
|
}>;
|
|
11
15
|
/**
|
|
12
16
|
* `useCasePluginOf` or `useCaseCatalogOf` was given what cannot be a plugin
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { ExceptionLayers } from "../exception/enums/exception-layers.js";
|
|
2
|
+
import { exceptionOf } from "../exception/exception.js";
|
|
3
|
+
export const InvalidUseCaseStep = exceptionOf({
|
|
4
|
+
layer: ExceptionLayers.UseCase,
|
|
5
|
+
name: "InvalidUseCaseStep",
|
|
6
|
+
message: ({
|
|
7
|
+
index,
|
|
8
|
+
reason
|
|
9
|
+
}) => `Invalid step ${index}: ${reason}`
|
|
10
|
+
});
|
|
11
|
+
export const InvalidUseCasePlugin = exceptionOf({
|
|
12
|
+
layer: ExceptionLayers.UseCase,
|
|
13
|
+
name: "InvalidUseCasePlugin",
|
|
14
|
+
message: ({ reason }, source) => `Invalid use case plugin "${source}": ${reason}.`
|
|
15
|
+
});
|
|
16
|
+
export const UnknownUseCasePlugin = exceptionOf({
|
|
17
|
+
layer: ExceptionLayers.UseCase,
|
|
18
|
+
name: "UnknownUseCasePlugin",
|
|
19
|
+
message: ({ name }) => `No use case plugin "${name}" in the catalog.`
|
|
20
|
+
});
|
|
21
|
+
export const IncompatibleUseCasePlugin = exceptionOf({
|
|
22
|
+
layer: ExceptionLayers.UseCase,
|
|
23
|
+
name: "IncompatibleUseCasePlugin",
|
|
24
|
+
message: ({ reason }, source) => `Incompatible use case plugin "${source}": ${reason}.`
|
|
25
|
+
});
|
|
26
|
+
export const UseCaseUnavailable = exceptionOf({
|
|
27
|
+
layer: ExceptionLayers.UseCase,
|
|
28
|
+
name: "UseCaseUnavailable",
|
|
29
|
+
message: ({
|
|
30
|
+
repository,
|
|
31
|
+
reason
|
|
32
|
+
}, source) => `The use case "${source}" is not available for "${repository}": ${reason}.`
|
|
33
|
+
});
|
|
34
|
+
export const UseCaseEntityNotFound = exceptionOf({
|
|
35
|
+
layer: ExceptionLayers.UseCase,
|
|
36
|
+
name: "UseCaseEntityNotFound",
|
|
37
|
+
message: "The entity was not found."
|
|
38
|
+
});
|
|
39
|
+
export const UseCaseEntityConflict = exceptionOf({
|
|
40
|
+
layer: ExceptionLayers.UseCase,
|
|
41
|
+
name: "UseCaseEntityConflict",
|
|
42
|
+
message: "The entity already exists."
|
|
43
|
+
});
|
|
44
|
+
export const UseCaseInvalidCredentials = exceptionOf({
|
|
45
|
+
layer: ExceptionLayers.UseCase,
|
|
46
|
+
name: "UseCaseInvalidCredentials",
|
|
47
|
+
message: "Invalid credentials."
|
|
48
|
+
});
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { isVersion, satisfies } from "../record/plugin.js";
|
|
2
|
+
import { Kind } from "../symbols/kind.js";
|
|
3
|
+
import {
|
|
4
|
+
IncompatibleUseCasePlugin,
|
|
5
|
+
InvalidUseCasePlugin,
|
|
6
|
+
UnknownUseCasePlugin
|
|
7
|
+
} from "./exceptions.js";
|
|
8
|
+
import { isUseCasePlugin } from "./plugin.js";
|
|
9
|
+
export function isUseCaseCatalog(value) {
|
|
10
|
+
return typeof value === "object" && value !== null && value[Kind] === "use-case-catalog";
|
|
11
|
+
}
|
|
12
|
+
export function useCaseCatalogOf(...plugins) {
|
|
13
|
+
const byName = {};
|
|
14
|
+
const context = {};
|
|
15
|
+
for (const plugin of plugins) {
|
|
16
|
+
if (!isUseCasePlugin(plugin)) {
|
|
17
|
+
throw new InvalidUseCasePlugin({
|
|
18
|
+
source: "catalog",
|
|
19
|
+
details: {
|
|
20
|
+
reason: "a catalog takes plugins made by `useCasePluginOf`"
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
if (Object.hasOwn(byName, plugin.name)) {
|
|
25
|
+
throw new InvalidUseCasePlugin({
|
|
26
|
+
source: plugin.name,
|
|
27
|
+
details: { reason: "the catalog has another plugin with its name" }
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
byName[plugin.name] = plugin;
|
|
31
|
+
context[plugin.name] = plugin.make;
|
|
32
|
+
}
|
|
33
|
+
const find = (name) => {
|
|
34
|
+
const plugin = Object.hasOwn(byName, name) ? byName[name] : undefined;
|
|
35
|
+
if (!plugin) {
|
|
36
|
+
throw new UnknownUseCasePlugin({ source: "catalog", details: { name } });
|
|
37
|
+
}
|
|
38
|
+
return plugin;
|
|
39
|
+
};
|
|
40
|
+
return Object.freeze({
|
|
41
|
+
[Kind]: "use-case-catalog",
|
|
42
|
+
plugins: Object.freeze(byName),
|
|
43
|
+
list: Object.freeze([...plugins]),
|
|
44
|
+
context: Object.freeze(context),
|
|
45
|
+
resolve(reference) {
|
|
46
|
+
const at = typeof reference === "string" ? reference.lastIndexOf("@") : -1;
|
|
47
|
+
if (at <= 0)
|
|
48
|
+
return find(String(reference));
|
|
49
|
+
const plugin = find(reference.slice(0, at));
|
|
50
|
+
const version = reference.slice(at + 1);
|
|
51
|
+
if (!isVersion(version)) {
|
|
52
|
+
throw new IncompatibleUseCasePlugin({
|
|
53
|
+
source: plugin.name,
|
|
54
|
+
details: { reason: `"${version}" is not a semantic version` }
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
if (!satisfies(plugin.version, version)) {
|
|
58
|
+
throw new IncompatibleUseCasePlugin({
|
|
59
|
+
source: plugin.name,
|
|
60
|
+
details: {
|
|
61
|
+
reason: `version ${plugin.version} does not satisfy ${version}`
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
return plugin;
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { hasPlugin, isVersion } from "../record/plugin.js";
|
|
2
|
+
import { isRepository } from "../repository/repository.js";
|
|
3
|
+
import { inspectRepository } from "../repository/state.js";
|
|
4
|
+
import { Kind } from "../symbols/kind.js";
|
|
5
|
+
import { InvalidUseCasePlugin, UseCaseUnavailable } from "./exceptions.js";
|
|
6
|
+
import { isPlain } from "./steps/references.js";
|
|
7
|
+
import { useCaseTargetOf } from "./target.js";
|
|
8
|
+
import { useCaseOf } from "./use-case.js";
|
|
9
|
+
const UseCaseOrigin = Symbol("useCaseOrigin");
|
|
10
|
+
const NAME = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
11
|
+
const identities = new WeakMap;
|
|
12
|
+
let nextIdentity = 0;
|
|
13
|
+
function keyOf(value) {
|
|
14
|
+
if (typeof value === "function" || typeof value === "object" && value) {
|
|
15
|
+
if (Array.isArray(value))
|
|
16
|
+
return `[${value.map(keyOf).join(",")}]`;
|
|
17
|
+
if (isPlain(value)) {
|
|
18
|
+
return `{${Object.keys(value).sort().filter((key) => value[key] !== undefined).map((key) => `${JSON.stringify(key)}:${keyOf(value[key])}`).join(",")}}`;
|
|
19
|
+
}
|
|
20
|
+
let identity = identities.get(value);
|
|
21
|
+
if (identity === undefined) {
|
|
22
|
+
identity = nextIdentity++;
|
|
23
|
+
identities.set(value, identity);
|
|
24
|
+
}
|
|
25
|
+
return `#${identity}`;
|
|
26
|
+
}
|
|
27
|
+
return value === undefined ? "undefined" : JSON.stringify(value);
|
|
28
|
+
}
|
|
29
|
+
export function isUseCasePlugin(value) {
|
|
30
|
+
return typeof value === "object" && value !== null && value[Kind] === "use-case-plugin";
|
|
31
|
+
}
|
|
32
|
+
export function useCaseOriginOf(UseCase) {
|
|
33
|
+
return typeof UseCase === "function" ? UseCase[UseCaseOrigin] : undefined;
|
|
34
|
+
}
|
|
35
|
+
export function useCasePluginOf(name, settings) {
|
|
36
|
+
const source = typeof name === "string" ? name : "useCasePluginOf";
|
|
37
|
+
const invalid = (reason) => {
|
|
38
|
+
throw new InvalidUseCasePlugin({ source, details: { reason } });
|
|
39
|
+
};
|
|
40
|
+
if (typeof name !== "string" || !NAME.test(name)) {
|
|
41
|
+
invalid("its name must be an identifier");
|
|
42
|
+
}
|
|
43
|
+
if (!isPlain(settings) || typeof settings.compile !== "function") {
|
|
44
|
+
invalid("it needs a compile function");
|
|
45
|
+
}
|
|
46
|
+
const { version = "1.0.0", description, compile } = settings;
|
|
47
|
+
if (!isVersion(version)) {
|
|
48
|
+
invalid(`"${String(version)}" is not a semantic version`);
|
|
49
|
+
}
|
|
50
|
+
if (description !== undefined && typeof description !== "string") {
|
|
51
|
+
invalid("its description must be a string");
|
|
52
|
+
}
|
|
53
|
+
const names = (list, what) => {
|
|
54
|
+
if (list === undefined)
|
|
55
|
+
return Object.freeze([]);
|
|
56
|
+
if (!Array.isArray(list) || list.some((item) => typeof item !== "string" || item === "")) {
|
|
57
|
+
invalid(`requires.${what} must be a list of names`);
|
|
58
|
+
}
|
|
59
|
+
return Object.freeze([...new Set(list)]);
|
|
60
|
+
};
|
|
61
|
+
if (settings.requires !== undefined && !isPlain(settings.requires)) {
|
|
62
|
+
invalid("requires must be an object of actions and plugins");
|
|
63
|
+
}
|
|
64
|
+
const requires = Object.freeze({
|
|
65
|
+
actions: names(settings.requires?.actions, "actions"),
|
|
66
|
+
plugins: names(settings.requires?.plugins, "plugins")
|
|
67
|
+
});
|
|
68
|
+
const made = new WeakMap;
|
|
69
|
+
const make = (repository, given) => {
|
|
70
|
+
if (!isRepository(repository)) {
|
|
71
|
+
return invalid("it makes a use case for a repository of repositoryOf");
|
|
72
|
+
}
|
|
73
|
+
if (given !== undefined && !isPlain(given)) {
|
|
74
|
+
return invalid("its options must be a plain object");
|
|
75
|
+
}
|
|
76
|
+
const options = Object.freeze({ ...given });
|
|
77
|
+
const info = inspectRepository(repository);
|
|
78
|
+
let classes = made.get(info.state);
|
|
79
|
+
if (!classes) {
|
|
80
|
+
classes = new Map;
|
|
81
|
+
made.set(info.state, classes);
|
|
82
|
+
}
|
|
83
|
+
const key = keyOf(options);
|
|
84
|
+
const known = classes.get(key);
|
|
85
|
+
if (known)
|
|
86
|
+
return known;
|
|
87
|
+
const target = useCaseTargetOf(repository, name);
|
|
88
|
+
const missing = {
|
|
89
|
+
actions: requires.actions.filter((action) => !info.actions.includes(action)),
|
|
90
|
+
plugins: requires.plugins.filter((plugin) => !hasPlugin(info.entity, plugin))
|
|
91
|
+
};
|
|
92
|
+
if (missing.actions.length > 0 || missing.plugins.length > 0) {
|
|
93
|
+
const quoted = (list) => list.map((item) => `"${item}"`).join(", ");
|
|
94
|
+
throw new UseCaseUnavailable({
|
|
95
|
+
source: name,
|
|
96
|
+
details: {
|
|
97
|
+
repository: target.repositoryName,
|
|
98
|
+
reason: [
|
|
99
|
+
missing.actions.length > 0 ? `the repository lacks the action ${quoted(missing.actions)}` : undefined,
|
|
100
|
+
missing.plugins.length > 0 ? `the entity lacks the plugin ${quoted(missing.plugins)}` : undefined
|
|
101
|
+
].filter(Boolean).join(" and "),
|
|
102
|
+
missing
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
const UseCase = useCaseOf(compile(target, options));
|
|
107
|
+
Object.defineProperty(UseCase, UseCaseOrigin, {
|
|
108
|
+
value: Object.freeze({
|
|
109
|
+
plugin,
|
|
110
|
+
repository,
|
|
111
|
+
options
|
|
112
|
+
})
|
|
113
|
+
});
|
|
114
|
+
classes.set(key, UseCase);
|
|
115
|
+
return UseCase;
|
|
116
|
+
};
|
|
117
|
+
const plugin = Object.freeze({
|
|
118
|
+
[Kind]: "use-case-plugin",
|
|
119
|
+
name,
|
|
120
|
+
version,
|
|
121
|
+
...description === undefined ? {} : { description },
|
|
122
|
+
requires,
|
|
123
|
+
make,
|
|
124
|
+
toJSON: () => ({
|
|
125
|
+
kind: "use-case-plugin",
|
|
126
|
+
name,
|
|
127
|
+
version,
|
|
128
|
+
...description === undefined ? {} : { description },
|
|
129
|
+
requires
|
|
130
|
+
})
|
|
131
|
+
});
|
|
132
|
+
return plugin;
|
|
133
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { AnyBlueprint } from "../blueprint/types/blueprint.interface.js";
|
|
2
|
+
import type { OpenAPISchema } from "../openapi/types/openapi.interface.js";
|
|
3
|
+
import type { RouteDeclaration } from "./types/route.interface.js";
|
|
4
|
+
/** A header name (an HTTP token), which a cookie name is too. */
|
|
5
|
+
export declare const HEADER_NAME: RegExp;
|
|
6
|
+
/** A path such as `/entries/{slug}`: `{name}` only as a whole segment, no `:`, `*`, query or spaces. */
|
|
7
|
+
export declare function isRoutePath(value: unknown, parameters?: boolean): value is string;
|
|
8
|
+
/** The names of the `{name}` segments of `path`, in order. */
|
|
9
|
+
export declare function parametersOfPath(path: string): string[];
|
|
10
|
+
/** Whether a JSON Schema describes a value that is neither an object nor a list. */
|
|
11
|
+
export declare function isScalarSchema(schema: OpenAPISchema): boolean;
|
|
12
|
+
/** Whether `name` is a key of `blueprint` that is a scalar property (no object, no list). */
|
|
13
|
+
export declare function isScalarKey(blueprint: AnyBlueprint, name: string): boolean;
|
|
14
|
+
/**
|
|
15
|
+
* The frozen, normalized declaration of `route` for a use case whose input is
|
|
16
|
+
* `blueprint` (the method in lower case), or the reason it is not valid.
|
|
17
|
+
*/
|
|
18
|
+
export declare function routeDeclarationOf(route: unknown, blueprint: AnyBlueprint, response: AnyBlueprint): RouteDeclaration | string;
|