@afora/core 0.0.9 → 0.0.12

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.
Files changed (186) hide show
  1. package/README.md +62 -189
  2. package/dist/adapters.js +31 -55
  3. package/dist/core/application/application.d.ts +30 -0
  4. package/dist/core/application/application.js +1291 -0
  5. package/dist/core/application/exceptions.js +85 -0
  6. package/dist/core/application/migrate.js +67 -0
  7. package/dist/core/application/providers.js +108 -0
  8. package/dist/core/application/registry.js +23 -0
  9. package/dist/core/application/schema.js +398 -0
  10. package/dist/core/application/start.js +162 -0
  11. package/dist/core/application/types/application.interface.d.ts +23 -3
  12. package/dist/core/auth/jwt.js +200 -0
  13. package/dist/core/auth/types/jwt.interface.d.ts +10 -2
  14. package/dist/core/blueprint/blueprint-plugin.js +7 -0
  15. package/dist/core/blueprint/blueprint.js +146 -0
  16. package/dist/core/blueprint/callable.js +13 -0
  17. package/dist/core/blueprint/exceptions.js +20 -0
  18. package/dist/core/blueprint/is-blueprint.js +4 -0
  19. package/dist/core/blueprint/list.js +19 -0
  20. package/dist/core/blueprint/optional.js +69 -0
  21. package/dist/core/blueprint/record-field.js +14 -0
  22. package/dist/core/blueprint/slice.js +127 -0
  23. package/dist/core/blueprint/types/blueprint.interface.d.ts +16 -0
  24. package/dist/core/blueprint/variant.js +72 -0
  25. package/dist/core/command/command.js +428 -0
  26. package/dist/core/command/exceptions.js +30 -0
  27. package/dist/core/command/types/command.interface.d.ts +10 -1
  28. package/dist/core/entity/abstract-entity.d.ts +19 -0
  29. package/dist/core/entity/abstract-entity.js +181 -0
  30. package/dist/core/entity/entity.js +209 -0
  31. package/dist/core/entity/event.d.ts +39 -0
  32. package/dist/core/entity/event.js +80 -0
  33. package/dist/core/entity/exceptions.js +27 -0
  34. package/dist/core/entity/trust.js +7 -0
  35. package/dist/core/entity/version.js +13 -0
  36. package/dist/core/environment/environment.d.ts +26 -3
  37. package/dist/core/environment/environment.js +71 -0
  38. package/dist/core/event-policy/event-policy.js +76 -0
  39. package/dist/core/event-policy/exceptions.js +12 -0
  40. package/dist/core/event-policy/publish.js +98 -0
  41. package/dist/core/event-policy/publishers.js +18 -0
  42. package/dist/core/event-policy/types/event-policy.interface.d.ts +8 -2
  43. package/dist/core/exception/enums/exception-layers.js +15 -0
  44. package/dist/core/exception/exception.d.ts +15 -0
  45. package/dist/core/exception/exception.js +128 -0
  46. package/dist/core/exception/exported.js +49 -0
  47. package/dist/core/exception/guard.d.ts +20 -1
  48. package/dist/core/exception/guard.js +16 -0
  49. package/dist/core/exception/types/exception.interface.d.ts +13 -0
  50. package/dist/core/json.js +19 -0
  51. package/dist/core/logger/exceptions.js +7 -0
  52. package/dist/core/logger/logger.d.ts +14 -1
  53. package/dist/core/logger/logger.js +178 -0
  54. package/dist/core/openapi/exceptions.js +7 -0
  55. package/dist/core/openapi/openapi.js +132 -0
  56. package/dist/core/openapi/types/openapi.interface.d.ts +7 -1
  57. package/dist/core/output/access.js +66 -0
  58. package/dist/core/output/coerce.js +38 -0
  59. package/dist/core/output/exceptions.js +22 -0
  60. package/dist/{index-y280ew0k.js → core/output/http.js} +35 -80
  61. package/dist/core/output/openapi-sink.d.ts +10 -2
  62. package/dist/{index-hg1z2s2v.js → core/output/openapi-sink.js} +60 -544
  63. package/dist/core/output/output.js +192 -0
  64. package/dist/core/output/problem.js +30 -0
  65. package/dist/core/output/select.js +28 -0
  66. package/dist/core/output/stage.js +111 -0
  67. package/dist/core/output/status.js +52 -0
  68. package/dist/core/output/types/http.interface.d.ts +5 -1
  69. package/dist/core/output/types/output.interface.d.ts +24 -0
  70. package/dist/core/parse/issue.js +31 -0
  71. package/dist/core/parse/parse.d.ts +8 -1
  72. package/dist/core/parse/parse.js +323 -0
  73. package/dist/core/parse/strip.js +48 -0
  74. package/dist/core/property/abstract-property.js +37 -0
  75. package/dist/core/property/define-property.js +209 -0
  76. package/dist/core/property/enums/property-states.js +8 -0
  77. package/dist/core/property/exceptions.js +12 -0
  78. package/dist/core/property/input.js +10 -0
  79. package/dist/core/property/is-property.js +3 -0
  80. package/dist/core/property/lineage.js +8 -0
  81. package/dist/core/property/property.d.ts +27 -0
  82. package/dist/core/property/property.js +46 -0
  83. package/dist/core/property/refine.js +103 -0
  84. package/dist/core/record/abstract-record.d.ts +2 -1
  85. package/dist/core/record/abstract-record.js +307 -0
  86. package/dist/core/record/exceptions.js +49 -0
  87. package/dist/core/record/marks.js +7 -0
  88. package/dist/core/record/mask.js +60 -0
  89. package/dist/core/record/name.js +23 -0
  90. package/dist/core/record/plugin-catalog.js +115 -0
  91. package/dist/core/record/plugin.js +198 -0
  92. package/dist/core/record/record.js +117 -0
  93. package/dist/core/record/types/record.interface.d.ts +5 -0
  94. package/dist/core/repository/cursor.js +83 -0
  95. package/dist/core/repository/exceptions.js +72 -0
  96. package/dist/core/repository/in-memory.js +196 -0
  97. package/dist/core/repository/migrate.js +254 -0
  98. package/dist/core/repository/plan.js +27 -0
  99. package/dist/core/repository/provider.js +29 -0
  100. package/dist/core/repository/query.js +153 -0
  101. package/dist/core/repository/references.js +196 -0
  102. package/dist/core/repository/repository.d.ts +22 -2
  103. package/dist/core/repository/repository.js +720 -0
  104. package/dist/core/repository/state.js +138 -0
  105. package/dist/core/repository/types/query.interface.d.ts +13 -0
  106. package/dist/core/repository/types/repository.interface.d.ts +20 -8
  107. package/dist/core/repository/types/schema.interface.d.ts +8 -2
  108. package/dist/core/repository/upcast.js +179 -0
  109. package/dist/core/schema/schema.d.ts +9 -0
  110. package/dist/core/schema/schema.js +542 -0
  111. package/dist/core/standard/standard-schema.js +1 -0
  112. package/dist/core/standard/standard.js +23 -0
  113. package/dist/core/symbols/demo.d.ts +4 -0
  114. package/dist/core/symbols/demo.js +1 -0
  115. package/dist/core/symbols/kind.js +1 -0
  116. package/dist/core/symbols/meta.js +1 -0
  117. package/dist/core/symbols/state.d.ts +5 -0
  118. package/dist/core/symbols/state.js +1 -0
  119. package/dist/core/tracing/exceptions.d.ts +4 -0
  120. package/dist/core/tracing/exceptions.js +7 -0
  121. package/dist/core/tracing/tracing.d.ts +72 -0
  122. package/dist/core/tracing/tracing.js +101 -0
  123. package/dist/core/use-case/access.js +56 -0
  124. package/dist/core/use-case/decorators.js +65 -0
  125. package/dist/core/use-case/exceptions.d.ts +5 -1
  126. package/dist/core/use-case/exceptions.js +48 -0
  127. package/dist/core/use-case/plugin-catalog.js +68 -0
  128. package/dist/core/use-case/plugin.js +133 -0
  129. package/dist/core/use-case/route.js +200 -0
  130. package/dist/core/use-case/scope.d.ts +5 -0
  131. package/dist/core/use-case/scope.js +4 -0
  132. package/dist/core/use-case/steps/describe.d.ts +5 -1
  133. package/dist/core/use-case/steps/describe.js +24 -0
  134. package/dist/core/use-case/steps/references.js +105 -0
  135. package/dist/core/use-case/steps/run.js +139 -0
  136. package/dist/core/use-case/steps/types/check.interface.d.ts +27 -8
  137. package/dist/core/use-case/steps/types/steps.interface.d.ts +8 -1
  138. package/dist/core/use-case/steps/validate.js +368 -0
  139. package/dist/core/use-case/target.js +100 -0
  140. package/dist/core/use-case/types/route.interface.d.ts +25 -5
  141. package/dist/core/use-case/use-case.d.ts +77 -2
  142. package/dist/core/use-case/use-case.js +493 -0
  143. package/dist/experimental.d.ts +5 -0
  144. package/dist/experimental.js +18 -161
  145. package/dist/index.d.ts +7 -5
  146. package/dist/index.js +213 -367
  147. package/dist/std/blueprint-plugin.js +42 -0
  148. package/dist/std/blueprint.d.ts +21 -0
  149. package/dist/std/blueprint.js +3 -0
  150. package/dist/std/command.js +3 -0
  151. package/dist/std/entity.d.ts +20 -1
  152. package/dist/std/entity.js +30 -0
  153. package/dist/std/event-policies.js +3 -0
  154. package/dist/std/properties/contact.d.ts +3 -0
  155. package/dist/std/properties/contact.js +29 -0
  156. package/dist/std/properties/enum.js +26 -0
  157. package/dist/std/properties/exceptions.js +14 -0
  158. package/dist/std/properties/headless.js +45 -0
  159. package/dist/std/properties/identity.js +28 -0
  160. package/dist/std/properties/json.js +161 -0
  161. package/dist/std/properties/password.js +79 -0
  162. package/dist/std/properties/primitives.d.ts +4 -0
  163. package/dist/std/properties/primitives.js +28 -0
  164. package/dist/std/properties/time.js +55 -0
  165. package/dist/std/record-plugins.js +73 -0
  166. package/dist/std/record.d.ts +20 -1
  167. package/dist/std/record.js +3 -0
  168. package/dist/std/resource.js +39 -0
  169. package/dist/std/runtime.js +193 -0
  170. package/dist/std/stages.js +4 -0
  171. package/dist/std/use-cases/auth.js +193 -0
  172. package/dist/std/use-cases/crud.js +261 -0
  173. package/dist/std/use-cases/index.js +12 -0
  174. package/dist/std/use-cases/lifecycle.js +97 -0
  175. package/dist/std/use-cases/shared.js +232 -0
  176. package/dist/std/use-cases/types.d.ts +5 -1
  177. package/dist/std/use-cases/types.js +0 -0
  178. package/dist/testing/http-provider-contract.js +344 -0
  179. package/dist/testing/index.js +3 -1435
  180. package/dist/testing/memory-publisher.js +12 -0
  181. package/dist/testing/provider-contract.d.ts +12 -3
  182. package/dist/testing/provider-contract.js +1071 -0
  183. package/package.json +17 -5
  184. package/dist/index-3aae5jfq.js +0 -2997
  185. package/dist/index-ny3as79r.js +0 -4567
  186. package/dist/index-xegv6a1p.js +0 -4291
@@ -0,0 +1,132 @@
1
+ import { isBlueprint } from "../blueprint/is-blueprint.js";
2
+ import { isBlueprintList } from "../blueprint/list.js";
3
+ import { isRecordClass, recordFieldOf } from "../blueprint/record-field.js";
4
+ import { isBlueprintVariant } from "../blueprint/variant.js";
5
+ import { identityKeysOf } from "../entity/abstract-entity.js";
6
+ import { isEntityClass } from "../entity/entity.js";
7
+ import { isProperty } from "../property/is-property.js";
8
+ import { rawTypeOf } from "../schema/schema.js";
9
+ import { InvalidOpenAPIModel } from "./exceptions.js";
10
+ const NO_MARKS = { writeOnly: [], readOnly: [] };
11
+ function isField(value) {
12
+ return isProperty(value) || isBlueprint(value) || isBlueprintList(value) || isBlueprintVariant(value) || recordFieldOf(value) !== undefined;
13
+ }
14
+ function isOptional(field) {
15
+ if (isProperty(field))
16
+ return field.type.optional === true;
17
+ if (isBlueprint(field) || isBlueprintList(field) || isBlueprintVariant(field)) {
18
+ return field.optional;
19
+ }
20
+ return recordFieldOf(field)?.optional ?? false;
21
+ }
22
+ export function plainOf(value, nested = false) {
23
+ if (Array.isArray(value))
24
+ return value.map((item) => plainOf(item, true));
25
+ if (typeof value !== "object" || value === null)
26
+ return value;
27
+ const id = value.$id;
28
+ if (nested && typeof id === "string")
29
+ return { $ref: id };
30
+ const plain = {};
31
+ for (const [key, item] of Object.entries(value)) {
32
+ if (!key.startsWith("~"))
33
+ plain[key] = plainOf(item, true);
34
+ }
35
+ return plain;
36
+ }
37
+ function objectOf(properties, scope, marks) {
38
+ const shape = {};
39
+ const required = [];
40
+ for (const [key, field] of Object.entries(properties)) {
41
+ const schema = schemaOf(field, scope);
42
+ if (marks.readOnly.includes(key))
43
+ schema.readOnly = true;
44
+ if (marks.writeOnly.includes(key))
45
+ schema.writeOnly = true;
46
+ shape[key] = schema;
47
+ if (!isOptional(field))
48
+ required.push(key);
49
+ }
50
+ return {
51
+ type: "object",
52
+ properties: shape,
53
+ ...required.length > 0 ? { required } : {},
54
+ additionalProperties: false
55
+ };
56
+ }
57
+ function recordSchemaOf(record, scope) {
58
+ const { sensitive = [] } = record.options ?? {};
59
+ return objectOf(record.blueprint.properties, scope, {
60
+ writeOnly: sensitive,
61
+ readOnly: isEntityClass(record) ? identityKeysOf(record) : []
62
+ });
63
+ }
64
+ function schemaOf(field, scope) {
65
+ if (isBlueprintList(field)) {
66
+ const { item, minItems, maxItems } = field;
67
+ return {
68
+ type: "array",
69
+ items: schemaOf(item, scope),
70
+ ...minItems === undefined ? {} : { minItems },
71
+ ...maxItems === undefined ? {} : { maxItems }
72
+ };
73
+ }
74
+ if (isBlueprint(field))
75
+ return objectOf(field.properties, scope, NO_MARKS);
76
+ if (isBlueprintVariant(field)) {
77
+ const variants = field.variants;
78
+ return {
79
+ oneOf: Object.entries(variants).map(([tag, variant]) => {
80
+ const object = isRecordClass(variant) ? recordSchemaOf(variant, scope) : objectOf(variant.properties, scope, NO_MARKS);
81
+ return {
82
+ ...object,
83
+ properties: { [field.key]: { const: tag }, ...object.properties },
84
+ required: [field.key, ...object.required ?? []]
85
+ };
86
+ }),
87
+ discriminator: { propertyName: field.key }
88
+ };
89
+ }
90
+ const record = recordFieldOf(field);
91
+ if (record) {
92
+ const name = scope.names.get(record.record);
93
+ return name === undefined ? recordSchemaOf(record.record, scope) : { $ref: `${scope.prefix}${name}` };
94
+ }
95
+ if (!isProperty(field)) {
96
+ throw new InvalidOpenAPIModel({
97
+ source: "openapi",
98
+ details: { name: String(field) }
99
+ });
100
+ }
101
+ const schema = plainOf(rawTypeOf(field.type.schema));
102
+ if (field.type.sensitive)
103
+ schema.writeOnly = true;
104
+ return schema;
105
+ }
106
+ export function toOpenAPI(modelOrModels, { refPrefix = "#/components/schemas/" } = {}) {
107
+ if (isField(modelOrModels)) {
108
+ return schemaOf(modelOrModels, { names: new Map, prefix: refPrefix });
109
+ }
110
+ if (typeof modelOrModels !== "object" || modelOrModels === null) {
111
+ throw new InvalidOpenAPIModel({
112
+ source: "openapi",
113
+ details: { name: String(modelOrModels) }
114
+ });
115
+ }
116
+ const names = new Map;
117
+ for (const [name, model] of Object.entries(modelOrModels)) {
118
+ if (!isField(model)) {
119
+ throw new InvalidOpenAPIModel({ source: "openapi", details: { name } });
120
+ }
121
+ const record = recordFieldOf(model);
122
+ if (record && !names.has(record.record))
123
+ names.set(record.record, name);
124
+ }
125
+ const scope = { names, prefix: refPrefix };
126
+ const schemas = {};
127
+ for (const [name, model] of Object.entries(modelOrModels)) {
128
+ const record = recordFieldOf(model);
129
+ schemas[name] = record && names.get(record.record) === name ? recordSchemaOf(record.record, scope) : schemaOf(model, scope);
130
+ }
131
+ return { schemas };
132
+ }
@@ -28,10 +28,16 @@ export interface OpenAPIComponents<Name extends PropertyKey = string> {
28
28
  readonly [Key in Name]: OpenAPISchema;
29
29
  };
30
30
  }
31
+ /**
32
+ * What `toOpenAPI` takes after the models: how a record named among them is
33
+ * referenced.
34
+ */
31
35
  export interface OpenAPIOptions {
32
36
  /**
33
37
  * Where a record named among the models is referenced from: `$ref` is this
34
- * prefix and its name. `"#/components/schemas/"` by default.
38
+ * prefix and its name.
39
+ *
40
+ * @default "#/components/schemas/"
35
41
  */
36
42
  readonly refPrefix?: string;
37
43
  }
@@ -0,0 +1,66 @@
1
+ import { isPublicAccess } from "../use-case/access.js";
2
+ import { Forbidden, InvalidOutputStage, Unauthenticated } from "./exceptions.js";
3
+ function readerOf(path) {
4
+ if (path === "subject")
5
+ return (principal) => principal.subject;
6
+ if (path === "scopes")
7
+ return (principal) => [...principal.scopes];
8
+ const claim = path.slice("claims.".length);
9
+ return (principal) => principal.claims && Object.hasOwn(principal.claims, claim) ? principal.claims[claim] : undefined;
10
+ }
11
+ export function accessGuardOf(id, access, context) {
12
+ if (isPublicAccess(access)) {
13
+ return (_input, options) => options === undefined ? undefined : { context: options.context };
14
+ }
15
+ const scopes = access.scopes ?? [];
16
+ const owner = access.owner;
17
+ const ownerOf = owner ? readerOf(owner.claim === undefined ? "subject" : `claims.${owner.claim}`) : undefined;
18
+ const fills = Object.entries(access.context ?? {}).map(([key, path]) => [key, readerOf(path)]);
19
+ for (const [key] of fills) {
20
+ if (!context || !Object.hasOwn(context.properties, key)) {
21
+ throw new InvalidOutputStage({
22
+ source: id,
23
+ details: {
24
+ reason: `The access of ${id} fills the context key "${key}", which the context of the operation does not declare.`
25
+ }
26
+ });
27
+ }
28
+ }
29
+ return (input, options) => {
30
+ const principal = options?.principal;
31
+ if (principal === undefined || principal === null || typeof principal.subject !== "string" || !Array.isArray(principal.scopes)) {
32
+ throw new Unauthenticated({ source: id });
33
+ }
34
+ if (scopes.length > 0) {
35
+ const missing = scopes.filter((scope) => !principal.scopes.includes(scope));
36
+ if (missing.length > 0) {
37
+ throw new Forbidden({
38
+ source: id,
39
+ details: { reason: "scope", scopes: missing }
40
+ });
41
+ }
42
+ }
43
+ if (owner && ownerOf) {
44
+ const expected = ownerOf(principal);
45
+ const given = typeof input === "object" && input !== null ? input[owner.input] : undefined;
46
+ if (given === undefined || given === null || expected === undefined || expected === null || String(given) !== String(expected)) {
47
+ throw new Forbidden({ source: id, details: { reason: "owner" } });
48
+ }
49
+ }
50
+ if (fills.length === 0) {
51
+ return options?.context === undefined ? undefined : { context: options.context };
52
+ }
53
+ const given = options?.context;
54
+ const filled = {
55
+ ...typeof given === "object" && given !== null ? given : {}
56
+ };
57
+ for (const [key, read] of fills) {
58
+ const value = read(principal);
59
+ if (value === undefined)
60
+ delete filled[key];
61
+ else
62
+ filled[key] = value;
63
+ }
64
+ return { context: filled };
65
+ };
66
+ }
@@ -0,0 +1,38 @@
1
+ import { toOpenAPI } from "../openapi/openapi.js";
2
+ const scalarTypes = new WeakMap;
3
+ function scalarTypesOf(blueprint) {
4
+ let types = scalarTypes.get(blueprint);
5
+ if (!types) {
6
+ const schema = toOpenAPI(blueprint);
7
+ const found = {};
8
+ for (const [key, property] of Object.entries(schema.properties ?? {})) {
9
+ found[key] = [property.type ?? []].flat();
10
+ }
11
+ types = Object.freeze(found);
12
+ scalarTypes.set(blueprint, types);
13
+ }
14
+ return types;
15
+ }
16
+ function coerce(text, types) {
17
+ if (!types || types.includes("string"))
18
+ return text;
19
+ const trimmed = text.trim();
20
+ if (types.includes("boolean") && (trimmed === "true" || trimmed === "false")) {
21
+ return trimmed === "true";
22
+ }
23
+ if (types.includes("integer") || types.includes("number")) {
24
+ const number = trimmed === "" ? Number.NaN : Number(trimmed);
25
+ if (Number.isFinite(number))
26
+ return number;
27
+ }
28
+ return text;
29
+ }
30
+ export function coerceTextValues(blueprint, texts) {
31
+ const types = scalarTypesOf(blueprint);
32
+ const out = {};
33
+ for (const [key, value] of Object.entries(texts)) {
34
+ if (value !== undefined)
35
+ out[key] = coerce(value, types[key]);
36
+ }
37
+ return out;
38
+ }
@@ -0,0 +1,22 @@
1
+ import { ExceptionLayers } from "../exception/enums/exception-layers.js";
2
+ import { exceptionOf } from "../exception/exception.js";
3
+ export const InvalidOutputStage = exceptionOf({
4
+ layer: ExceptionLayers.Output,
5
+ name: "InvalidOutputStage",
6
+ message: ({ reason }) => reason
7
+ });
8
+ export const DuplicateOperation = exceptionOf({
9
+ layer: ExceptionLayers.Output,
10
+ name: "DuplicateOperation",
11
+ message: ({ id }) => `The operation "${id}" is declared twice.`
12
+ });
13
+ export const Unauthenticated = exceptionOf({
14
+ layer: ExceptionLayers.Output,
15
+ name: "Unauthenticated",
16
+ message: "Authentication is required."
17
+ });
18
+ export const Forbidden = exceptionOf({
19
+ layer: ExceptionLayers.Output,
20
+ name: "Forbidden",
21
+ message: (_details) => "The caller may not do this."
22
+ });
@@ -1,61 +1,17 @@
1
+ import { jwtOf, jwtOptionsIssue } from "../auth/jwt.js";
2
+ import { Exception } from "../exception/exception.js";
3
+ import { HEADER_NAME } from "../use-case/route.js";
4
+ import { coerceTextValues } from "./coerce.js";
5
+ import { InvalidOutputStage } from "./exceptions.js";
1
6
  import {
2
- statusOf2,
3
- PROBLEM_MEDIA_TYPE2,
4
- problemOf2,
5
- outputValueOf2,
6
- ROUTE_OPTION_KEYS2,
7
- routeOptionsIssue2,
8
- requestIdHeaderOf2,
9
- operationRoutes2,
10
- openapi2
11
- } from "./index-hg1z2s2v.js";
12
- import {
13
- Exception2,
14
- toOpenAPI2,
15
- HEADER_NAME,
16
- InvalidOutputStage2,
17
- jwtOptionsIssue,
18
- jwtOf2
19
- } from "./index-xegv6a1p.js";
20
- // src/core/output/coerce.ts
21
- var scalarTypes = new WeakMap;
22
- function scalarTypesOf(blueprint) {
23
- let types = scalarTypes.get(blueprint);
24
- if (!types) {
25
- const schema = toOpenAPI2(blueprint);
26
- const found = {};
27
- for (const [key, property] of Object.entries(schema.properties ?? {})) {
28
- found[key] = [property.type ?? []].flat();
29
- }
30
- types = Object.freeze(found);
31
- scalarTypes.set(blueprint, types);
32
- }
33
- return types;
34
- }
35
- function coerce(text, types) {
36
- if (!types || types.includes("string"))
37
- return text;
38
- const trimmed = text.trim();
39
- if (types.includes("boolean") && (trimmed === "true" || trimmed === "false")) {
40
- return trimmed === "true";
41
- }
42
- if (types.includes("integer") || types.includes("number")) {
43
- const number = trimmed === "" ? Number.NaN : Number(trimmed);
44
- if (Number.isFinite(number))
45
- return number;
46
- }
47
- return text;
48
- }
49
- function coerceTextValues2(blueprint, texts) {
50
- const types = scalarTypesOf(blueprint);
51
- const out = {};
52
- for (const [key, value] of Object.entries(texts)) {
53
- if (value !== undefined)
54
- out[key] = coerce(value, types[key]);
55
- }
56
- return out;
57
- }
58
- // src/core/output/http.ts
7
+ openapi,
8
+ operationRoutes,
9
+ ROUTE_OPTION_KEYS,
10
+ requestIdHeaderOf,
11
+ routeOptionsIssue
12
+ } from "./openapi-sink.js";
13
+ import { outputValueOf, PROBLEM_MEDIA_TYPE, problemOf } from "./problem.js";
14
+ import { statusOf } from "./status.js";
59
15
  function isObject(value) {
60
16
  return typeof value === "object" && value !== null && !Array.isArray(value);
61
17
  }
@@ -66,16 +22,16 @@ function onlyKeys(value, allowed, where) {
66
22
  }
67
23
  return;
68
24
  }
69
- var HTTP_OPTION_KEYS2 = [
70
- ...ROUTE_OPTION_KEYS2,
25
+ export const HTTP_OPTION_KEYS = [
26
+ ...ROUTE_OPTION_KEYS,
71
27
  "docs",
72
28
  "debug",
73
29
  "context",
74
30
  "auth"
75
31
  ];
76
- function httpOptionsIssue2(options, where) {
32
+ export function httpOptionsIssue(options, where) {
77
33
  const given = options;
78
- const routing = routeOptionsIssue2(options, where);
34
+ const routing = routeOptionsIssue(options, where);
79
35
  if (routing)
80
36
  return routing;
81
37
  if (given.debug !== undefined && typeof given.debug !== "boolean") {
@@ -109,7 +65,7 @@ function httpOptionsIssue2(options, where) {
109
65
  return `Expected ${where}.docs.path to be a path such as "/openapi.json".`;
110
66
  }
111
67
  try {
112
- openapi2({
68
+ openapi({
113
69
  info: docs.info,
114
70
  ...docs.servers ? { servers: docs.servers } : {}
115
71
  });
@@ -151,9 +107,9 @@ function httpOptionsIssue2(options, where) {
151
107
  }
152
108
  return;
153
109
  }
154
- var REQUEST_ID = /^[\x21-\x7e]{1,200}$/;
155
- var JSON_TYPE = "application/json";
156
- var NO_COOKIES = Object.freeze([]);
110
+ const REQUEST_ID = /^[\x21-\x7e]{1,200}$/;
111
+ const JSON_TYPE = "application/json";
112
+ const NO_COOKIES = Object.freeze([]);
157
113
  function cookiesOf(header) {
158
114
  const cookies = new Map;
159
115
  if (!header)
@@ -190,28 +146,28 @@ function cookieWriterOf(cookie) {
190
146
  const lasting = cookie.maxAge === undefined ? "" : `; Max-Age=${cookie.maxAge}`;
191
147
  return (value) => value === "" ? `${cookie.name}=; ${attributes}; Max-Age=0` : `${cookie.name}=${encodeURIComponent(value)}; ${attributes}${lasting}`;
192
148
  }
193
- function compileRoutes2(operations, options, context, settings) {
149
+ export function compileRoutes(operations, options, context, settings) {
194
150
  const { source } = settings;
195
151
  const { output } = context;
196
152
  let authenticator = settings.authenticator;
197
153
  if (!authenticator && options.auth) {
198
154
  const { header, cookie, ...jwt } = options.auth.jwt;
199
- authenticator = jwtOf2(jwt).authenticator({
155
+ authenticator = jwtOf(jwt).authenticator({
200
156
  ...header === undefined ? {} : { header },
201
157
  ...cookie === undefined ? {} : { cookie }
202
158
  });
203
159
  }
204
- const routes = operationRoutes2(operations, options, source);
160
+ const routes = operationRoutes(operations, options, source);
205
161
  const debug = options.debug === true;
206
162
  const contextHeaders = options.context?.headers ?? {};
207
163
  const contextVariables = options.context?.variables ?? {};
208
- const requestIdHeader = requestIdHeaderOf2(options);
164
+ const requestIdHeader = requestIdHeaderOf(options);
209
165
  const idOf = (request) => {
210
166
  const sent = request?.header(requestIdHeader);
211
167
  return sent !== undefined && REQUEST_ID.test(sent) ? sent : crypto.randomUUID();
212
168
  };
213
169
  const headersOf = (headers, request) => requestIdHeader === undefined ? headers : { ...headers, [requestIdHeader]: idOf(request) };
214
- const problemHeaders = Object.freeze({ "content-type": PROBLEM_MEDIA_TYPE2 });
170
+ const problemHeaders = Object.freeze({ "content-type": PROBLEM_MEDIA_TYPE });
215
171
  const problem = (body, status, request) => ({
216
172
  status,
217
173
  headers: headersOf(problemHeaders, request),
@@ -219,8 +175,8 @@ function compileRoutes2(operations, options, context, settings) {
219
175
  body: JSON.stringify(body)
220
176
  });
221
177
  const fail = (error, operation, request, fallback) => {
222
- const status = fallback !== undefined && !(error instanceof Exception2 && Object.hasOwn(output.statuses, error.name)) ? fallback : statusOf2(error, operation, output.statuses);
223
- return problem(problemOf2(error, { status, debug }), status, request);
178
+ const status = fallback !== undefined && !(error instanceof Exception && Object.hasOwn(output.statuses, error.name)) ? fallback : statusOf(error, operation, output.statuses);
179
+ return problem(problemOf(error, { status, debug }), status, request);
224
180
  };
225
181
  const failure = (error, given) => fail(error, given?.operation, given?.request, given?.status);
226
182
  const compiled = operations.map((operation) => {
@@ -268,7 +224,7 @@ function compileRoutes2(operations, options, context, settings) {
268
224
  }, 400, request);
269
225
  }
270
226
  } else {
271
- input = coerceTextValues2(operation.input, request.query());
227
+ input = coerceTextValues(operation.input, request.query());
272
228
  }
273
229
  if (placed && isObject(input)) {
274
230
  const texts = {};
@@ -287,7 +243,7 @@ function compileRoutes2(operations, options, context, settings) {
287
243
  }
288
244
  for (const name of parameters)
289
245
  texts[name] = request.param(name);
290
- input = { ...input, ...coerceTextValues2(operation.input, texts) };
246
+ input = { ...input, ...coerceTextValues(operation.input, texts) };
291
247
  }
292
248
  let call;
293
249
  if (hasContext) {
@@ -295,7 +251,7 @@ function compileRoutes2(operations, options, context, settings) {
295
251
  for (const [key, name] of contextHeaderKeys) {
296
252
  raw[key] = request.header(name);
297
253
  }
298
- const values = coerceTextValues2(operation.context, raw);
254
+ const values = coerceTextValues(operation.context, raw);
299
255
  for (const [key, name] of contextVariableKeys) {
300
256
  const value = request.variable?.(name);
301
257
  if (value !== undefined)
@@ -313,7 +269,7 @@ function compileRoutes2(operations, options, context, settings) {
313
269
  body: status === 204 ? null : JSON.stringify(response) ?? "null"
314
270
  };
315
271
  }
316
- const value = outputValueOf2(response);
272
+ const value = outputValueOf(response);
317
273
  const sent = [];
318
274
  if (isObject(value)) {
319
275
  for (const [key, write] of setCookies) {
@@ -340,7 +296,7 @@ function compileRoutes2(operations, options, context, settings) {
340
296
  if (options.docs) {
341
297
  const { path, info, servers } = options.docs;
342
298
  if (compiled.some((route) => route.method === "get" && route.path === path)) {
343
- throw new InvalidOutputStage2({
299
+ throw new InvalidOutputStage({
344
300
  source,
345
301
  details: {
346
302
  reason: `${source}.docs.path "${path}" collides with a GET operation route.`
@@ -348,7 +304,7 @@ function compileRoutes2(operations, options, context, settings) {
348
304
  });
349
305
  }
350
306
  const routing = {};
351
- for (const key of ROUTE_OPTION_KEYS2) {
307
+ for (const key of ROUTE_OPTION_KEYS) {
352
308
  if (options[key] !== undefined)
353
309
  routing[key] = options[key];
354
310
  }
@@ -364,7 +320,7 @@ function compileRoutes2(operations, options, context, settings) {
364
320
  }
365
321
  };
366
322
  }
367
- const document = openapi2({
323
+ const document = openapi({
368
324
  info,
369
325
  ...servers ? { servers } : {},
370
326
  ...routing
@@ -393,4 +349,3 @@ function compileRoutes2(operations, options, context, settings) {
393
349
  failure
394
350
  });
395
351
  }
396
- export { coerceTextValues2, HTTP_OPTION_KEYS2, httpOptionsIssue2, compileRoutes2 };
@@ -45,7 +45,11 @@ export interface SecurityRequirement {
45
45
  * sinks that serve HTTP, so the served routes and the document agree.
46
46
  */
47
47
  export interface RouteOptions {
48
- /** Prefix of every path, e.g. `"/api"`. Empty by default. */
48
+ /**
49
+ * Prefix of every path, e.g. `"/api"`.
50
+ *
51
+ * @default ""
52
+ */
49
53
  readonly basePath?: string;
50
54
  /**
51
55
  * Method, path and success status by operation id, e.g.
@@ -56,7 +60,11 @@ export interface RouteOptions {
56
60
  readonly [operationId: string]: {
57
61
  readonly method?: OpenAPIMethod;
58
62
  readonly path?: string;
59
- /** `200` by default. */
63
+ /**
64
+ * The status of a successful response.
65
+ *
66
+ * @default 200
67
+ */
60
68
  readonly status?: RouteStatus;
61
69
  };
62
70
  };