@skmtc/core 0.27.0 → 0.28.1

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 (111) hide show
  1. package/esm/context/CoreContext.d.ts.map +1 -1
  2. package/esm/context/CoreContext.js +4 -2
  3. package/esm/context/GenerateContext.d.ts +16 -2
  4. package/esm/context/GenerateContext.d.ts.map +1 -1
  5. package/esm/context/GenerateContext.js +94 -15
  6. package/esm/context/generateTypes.d.ts +33 -2
  7. package/esm/context/generateTypes.d.ts.map +1 -1
  8. package/esm/dsl/model/ModelDriver.d.ts.map +1 -1
  9. package/esm/dsl/model/ModelDriver.js +1 -2
  10. package/esm/dsl/model/toModelProjectionBase.d.ts +1 -1
  11. package/esm/dsl/model/toModelProjectionBase.d.ts.map +1 -1
  12. package/esm/dsl/model/toModelProjectionBase.js +9 -9
  13. package/esm/dsl/operation/gql/GqlOperationDriver.d.ts.map +1 -1
  14. package/esm/dsl/operation/gql/GqlOperationDriver.js +1 -3
  15. package/esm/dsl/operation/gql/toGqlOperationProjectionBase.d.ts +1 -1
  16. package/esm/dsl/operation/gql/toGqlOperationProjectionBase.d.ts.map +1 -1
  17. package/esm/dsl/operation/gql/toGqlOperationProjectionBase.js +9 -15
  18. package/esm/dsl/operation/oas/OasOperationDriver.d.ts.map +1 -1
  19. package/esm/dsl/operation/oas/OasOperationDriver.js +1 -3
  20. package/esm/dsl/operation/oas/toOasOperationProjectionBase.d.ts +1 -1
  21. package/esm/dsl/operation/oas/toOasOperationProjectionBase.d.ts.map +1 -1
  22. package/esm/dsl/operation/oas/toOasOperationProjectionBase.js +9 -15
  23. package/esm/dsl/webhook/WebhookDriver.d.ts.map +1 -1
  24. package/esm/dsl/webhook/WebhookDriver.js +1 -3
  25. package/esm/dsl/webhook/toWebhookProjectionBase.d.ts +1 -1
  26. package/esm/dsl/webhook/toWebhookProjectionBase.d.ts.map +1 -1
  27. package/esm/dsl/webhook/toWebhookProjectionBase.js +9 -15
  28. package/esm/enrichments/EnrichmentAudit.d.ts +79 -0
  29. package/esm/enrichments/EnrichmentAudit.d.ts.map +1 -0
  30. package/esm/enrichments/EnrichmentAudit.js +210 -0
  31. package/esm/enrichments/EnrichmentWarning.d.ts +72 -0
  32. package/esm/enrichments/EnrichmentWarning.d.ts.map +1 -0
  33. package/esm/enrichments/EnrichmentWarning.js +50 -0
  34. package/esm/enrichments/findUnknownKeys.d.ts +40 -0
  35. package/esm/enrichments/findUnknownKeys.d.ts.map +1 -0
  36. package/esm/enrichments/findUnknownKeys.js +80 -0
  37. package/esm/enrichments/parseEnrichmentUmbrella.d.ts +63 -0
  38. package/esm/enrichments/parseEnrichmentUmbrella.d.ts.map +1 -0
  39. package/esm/enrichments/parseEnrichmentUmbrella.js +66 -0
  40. package/esm/enrichments/toEnrichmentDescriptor.d.ts.map +1 -1
  41. package/esm/enrichments/toEnrichmentDescriptor.js +1 -20
  42. package/esm/enrichments/valibotShape.d.ts +51 -0
  43. package/esm/enrichments/valibotShape.d.ts.map +1 -0
  44. package/esm/enrichments/valibotShape.js +37 -0
  45. package/esm/helpers/nearestKey.d.ts +18 -0
  46. package/esm/helpers/nearestKey.d.ts.map +1 -0
  47. package/esm/helpers/nearestKey.js +60 -0
  48. package/esm/mod.d.ts +2 -0
  49. package/esm/mod.d.ts.map +1 -1
  50. package/esm/mod.js +2 -0
  51. package/esm/run/toArtifacts.d.ts.map +1 -1
  52. package/esm/run/toArtifacts.js +2 -1
  53. package/esm/types/Manifest.d.ts +9 -0
  54. package/esm/types/Manifest.d.ts.map +1 -1
  55. package/esm/types/Manifest.js +2 -0
  56. package/package.json +1 -1
  57. package/script/context/CoreContext.d.ts.map +1 -1
  58. package/script/context/CoreContext.js +4 -2
  59. package/script/context/GenerateContext.d.ts +16 -2
  60. package/script/context/GenerateContext.d.ts.map +1 -1
  61. package/script/context/GenerateContext.js +94 -15
  62. package/script/context/generateTypes.d.ts +33 -2
  63. package/script/context/generateTypes.d.ts.map +1 -1
  64. package/script/dsl/model/ModelDriver.d.ts.map +1 -1
  65. package/script/dsl/model/ModelDriver.js +1 -5
  66. package/script/dsl/model/toModelProjectionBase.d.ts +1 -1
  67. package/script/dsl/model/toModelProjectionBase.d.ts.map +1 -1
  68. package/script/dsl/model/toModelProjectionBase.js +9 -45
  69. package/script/dsl/operation/gql/GqlOperationDriver.d.ts.map +1 -1
  70. package/script/dsl/operation/gql/GqlOperationDriver.js +1 -6
  71. package/script/dsl/operation/gql/toGqlOperationProjectionBase.d.ts +1 -1
  72. package/script/dsl/operation/gql/toGqlOperationProjectionBase.d.ts.map +1 -1
  73. package/script/dsl/operation/gql/toGqlOperationProjectionBase.js +9 -51
  74. package/script/dsl/operation/oas/OasOperationDriver.d.ts.map +1 -1
  75. package/script/dsl/operation/oas/OasOperationDriver.js +1 -6
  76. package/script/dsl/operation/oas/toOasOperationProjectionBase.d.ts +1 -1
  77. package/script/dsl/operation/oas/toOasOperationProjectionBase.d.ts.map +1 -1
  78. package/script/dsl/operation/oas/toOasOperationProjectionBase.js +9 -51
  79. package/script/dsl/webhook/WebhookDriver.d.ts.map +1 -1
  80. package/script/dsl/webhook/WebhookDriver.js +1 -6
  81. package/script/dsl/webhook/toWebhookProjectionBase.d.ts +1 -1
  82. package/script/dsl/webhook/toWebhookProjectionBase.d.ts.map +1 -1
  83. package/script/dsl/webhook/toWebhookProjectionBase.js +9 -51
  84. package/script/enrichments/EnrichmentAudit.d.ts +79 -0
  85. package/script/enrichments/EnrichmentAudit.d.ts.map +1 -0
  86. package/script/enrichments/EnrichmentAudit.js +215 -0
  87. package/script/enrichments/EnrichmentWarning.d.ts +72 -0
  88. package/script/enrichments/EnrichmentWarning.d.ts.map +1 -0
  89. package/script/enrichments/EnrichmentWarning.js +87 -0
  90. package/script/enrichments/findUnknownKeys.d.ts +40 -0
  91. package/script/enrichments/findUnknownKeys.d.ts.map +1 -0
  92. package/script/enrichments/findUnknownKeys.js +84 -0
  93. package/script/enrichments/parseEnrichmentUmbrella.d.ts +63 -0
  94. package/script/enrichments/parseEnrichmentUmbrella.d.ts.map +1 -0
  95. package/script/enrichments/parseEnrichmentUmbrella.js +103 -0
  96. package/script/enrichments/toEnrichmentDescriptor.d.ts.map +1 -1
  97. package/script/enrichments/toEnrichmentDescriptor.js +10 -29
  98. package/script/enrichments/valibotShape.d.ts +51 -0
  99. package/script/enrichments/valibotShape.d.ts.map +1 -0
  100. package/script/enrichments/valibotShape.js +44 -0
  101. package/script/helpers/nearestKey.d.ts +18 -0
  102. package/script/helpers/nearestKey.d.ts.map +1 -0
  103. package/script/helpers/nearestKey.js +64 -0
  104. package/script/mod.d.ts +2 -0
  105. package/script/mod.d.ts.map +1 -1
  106. package/script/mod.js +2 -0
  107. package/script/run/toArtifacts.d.ts.map +1 -1
  108. package/script/run/toArtifacts.js +2 -1
  109. package/script/types/Manifest.d.ts +9 -0
  110. package/script/types/Manifest.d.ts.map +1 -1
  111. package/script/types/Manifest.js +2 -0
@@ -0,0 +1,215 @@
1
+ "use strict";
2
+ /**
3
+ * @fileoverview The enrichment consumption audit.
4
+ *
5
+ * The engine already performs every enrichment lookup during the generate
6
+ * walk — the dispatch loops read each `[generatorId][...subject]` block to
7
+ * enumerate variants, the projection statics read each subject leaf and
8
+ * the run-constant scopes, and the Drivers read peer blocks for the
9
+ * variant guard. `EnrichmentAudit` records those lookups (every read is
10
+ * routed through `GenerateContext.readEnrichment`, the single choke
11
+ * point) and, post-walk, diffs them against the paths actually present in
12
+ * `settings.enrichments`. A configured path no lookup touched is dead
13
+ * config — a typo'd generator id / path / method / model name, or an
14
+ * entry orphaned by spec evolution — and becomes an
15
+ * `UNCONSUMED_ENRICHMENT` warning on the manifest.
16
+ *
17
+ * Consumption is recorded prefix-closed: consuming
18
+ * `[id, '/pets', 'post', 'main']` also marks `[id, '/pets', 'post']`,
19
+ * `[id, '/pets']`, and `[id]`. The present-path enumeration walks each
20
+ * generator's slice to the block depth its entry type dictates (model:
21
+ * `[refName]`; operation / webhook / GQL: two segments), so the diff
22
+ * compares like with like. Reserved keys (`_generator`, `_stack`) are
23
+ * engine-owned constants and never flagged.
24
+ *
25
+ * Everything here is warn-level and fail-open — the audit can only add
26
+ * warnings to the manifest, never change generation output.
27
+ *
28
+ * @module EnrichmentAudit
29
+ */
30
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
31
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
32
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
33
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
34
+ };
35
+ var _EnrichmentAudit_instances, _EnrichmentAudit_consumed, _EnrichmentAudit_children, _EnrichmentAudit_warnings, _EnrichmentAudit_reported, _EnrichmentAudit_isConsumed, _EnrichmentAudit_divergenceSuggestion, _EnrichmentAudit_checkBlock;
36
+ Object.defineProperty(exports, "__esModule", { value: true });
37
+ exports.hasVariantEnrichment = exports.EnrichmentAudit = void 0;
38
+ const Enrichments_js_1 = require("../types/Enrichments.js");
39
+ const EnrichmentWarning_js_1 = require("./EnrichmentWarning.js");
40
+ const nearestKey_js_1 = require("../helpers/nearestKey.js");
41
+ const toPathKey = (segments) => JSON.stringify(segments);
42
+ const isRecord = (input) => typeof input === 'object' && input !== null && !Array.isArray(input);
43
+ class EnrichmentAudit {
44
+ constructor() {
45
+ _EnrichmentAudit_instances.add(this);
46
+ /** Prefix-closed set of consumed routing paths, keyed by {@link toPathKey}. */
47
+ _EnrichmentAudit_consumed.set(this, new Set()
48
+ /** Consumed child segments per parent path — the suggestion candidates. */
49
+ );
50
+ /** Consumed child segments per parent path — the suggestion candidates. */
51
+ _EnrichmentAudit_children.set(this, new Map());
52
+ _EnrichmentAudit_warnings.set(this, []
53
+ /** Identity keys of reported warnings — reads repeat, warnings must not. */
54
+ );
55
+ /** Identity keys of reported warnings — reads repeat, warnings must not. */
56
+ _EnrichmentAudit_reported.set(this, new Set()
57
+ /**
58
+ * Record one enrichment lookup. Every prefix of `segments` is marked
59
+ * consumed, so block-level and leaf-level reads land in one namespace.
60
+ */
61
+ );
62
+ }
63
+ /**
64
+ * Record one enrichment lookup. Every prefix of `segments` is marked
65
+ * consumed, so block-level and leaf-level reads land in one namespace.
66
+ */
67
+ consume(segments) {
68
+ let parentKey = toPathKey([]);
69
+ for (let end = 1; end <= segments.length; end++) {
70
+ const key = toPathKey(segments.slice(0, end));
71
+ __classPrivateFieldGet(this, _EnrichmentAudit_consumed, "f").add(key);
72
+ const siblings = __classPrivateFieldGet(this, _EnrichmentAudit_children, "f").get(parentKey);
73
+ if (siblings) {
74
+ siblings.add(segments[end - 1]);
75
+ }
76
+ else {
77
+ __classPrivateFieldGet(this, _EnrichmentAudit_children, "f").set(parentKey, new Set([segments[end - 1]]));
78
+ }
79
+ parentKey = key;
80
+ }
81
+ }
82
+ /**
83
+ * Add a warning, deduplicated on `(type, path)` — the same lookup can
84
+ * repeat many times per run (one per insert call) and must produce one
85
+ * warning.
86
+ */
87
+ report(warning) {
88
+ const key = toPathKey([warning.type, ...warning.path]);
89
+ if (__classPrivateFieldGet(this, _EnrichmentAudit_reported, "f").has(key))
90
+ return;
91
+ __classPrivateFieldGet(this, _EnrichmentAudit_reported, "f").add(key);
92
+ __classPrivateFieldGet(this, _EnrichmentAudit_warnings, "f").push(warning);
93
+ }
94
+ /**
95
+ * Diff the configured enrichment paths against the consumed set and
96
+ * return every warning gathered during the run plus the addressing
97
+ * misses found here. Called once, after the generate walk.
98
+ */
99
+ finalize({ enrichments, generators, skippedGeneratorIds }) {
100
+ if (!isRecord(enrichments))
101
+ return [...__classPrivateFieldGet(this, _EnrichmentAudit_warnings, "f")];
102
+ const generatorsById = new Map(generators.map(generator => [generator.id, generator]));
103
+ const skipped = new Set(skippedGeneratorIds);
104
+ for (const [generatorId, slot] of Object.entries(enrichments)) {
105
+ if (generatorId === Enrichments_js_1.STACK_ENRICHMENT_KEY)
106
+ continue;
107
+ // Other `_`-prefixed keys are structurally invalid — the
108
+ // `generatorEnrichments` schema flags those at config load.
109
+ if ((0, Enrichments_js_1.isReservedEnrichmentKey)(generatorId))
110
+ continue;
111
+ if (skipped.has(generatorId)) {
112
+ this.report({
113
+ level: 'info',
114
+ type: 'SKIPPED_GENERATOR_ENRICHMENT',
115
+ path: [generatorId],
116
+ message: `generator '${generatorId}' is skipped in this run — ` +
117
+ `its enrichments were not applied`
118
+ });
119
+ continue;
120
+ }
121
+ const generator = generatorsById.get(generatorId);
122
+ if (!generator) {
123
+ // An insert-only peer (reached via insertOperation / insertModel
124
+ // without an entry of its own) still consumes its slice through
125
+ // the projection statics — a consumed prefix means the slice is
126
+ // live, and without an entry type its depth is unknowable, so the
127
+ // audit leaves it alone.
128
+ if (__classPrivateFieldGet(this, _EnrichmentAudit_instances, "m", _EnrichmentAudit_isConsumed).call(this, [generatorId]))
129
+ continue;
130
+ const suggestion = (0, nearestKey_js_1.nearestKey)(generatorId, generatorsById.keys());
131
+ this.report({
132
+ level: 'warning',
133
+ type: 'UNKNOWN_GENERATOR_ID',
134
+ path: [generatorId],
135
+ message: `enrichments are configured for '${generatorId}' but no generator ` +
136
+ `with that id is in this run` +
137
+ (suggestion !== undefined ? ` (did you mean '${suggestion}'?)` : ''),
138
+ ...(suggestion !== undefined ? { suggestion } : {})
139
+ });
140
+ continue;
141
+ }
142
+ if (!isRecord(slot))
143
+ continue;
144
+ // The generator is in the run but consumed nothing at all — the
145
+ // document has no subjects of its type (or the wrong protocol).
146
+ // Per-entry warnings would fall back to cross-generator siblings for
147
+ // their "did you mean …?" and could name an unrelated generator, so
148
+ // collapse to one generator-level warning that says what happened.
149
+ const hasSubjectEntries = Object.keys(slot).some(key => !(0, Enrichments_js_1.isReservedEnrichmentKey)(key));
150
+ if (!__classPrivateFieldGet(this, _EnrichmentAudit_instances, "m", _EnrichmentAudit_isConsumed).call(this, [generatorId]) && hasSubjectEntries) {
151
+ this.report({
152
+ level: 'warning',
153
+ type: 'UNCONSUMED_ENRICHMENT',
154
+ path: [generatorId],
155
+ message: `enrichments are configured for '${generatorId}' but it matched ` +
156
+ `no subjects in this run — its enrichments were never read`
157
+ });
158
+ continue;
159
+ }
160
+ // Block depth per entry type: `[refName]` for models,
161
+ // `[path][method]` / `[name][method]` / `[rootKind][fieldName]`
162
+ // for the operation-shaped types.
163
+ const subjectDepth = generator.type === 'model' ? 1 : 2;
164
+ for (const [subjectKey, subjectValue] of Object.entries(slot)) {
165
+ // `_generator` (and any other reserved key) is engine-owned — read
166
+ // through the dedicated helpers by known key, always consumed.
167
+ if ((0, Enrichments_js_1.isReservedEnrichmentKey)(subjectKey))
168
+ continue;
169
+ if (subjectDepth === 1 ||
170
+ !isRecord(subjectValue) ||
171
+ Object.keys(subjectValue).length === 0) {
172
+ __classPrivateFieldGet(this, _EnrichmentAudit_instances, "m", _EnrichmentAudit_checkBlock).call(this, [generatorId, subjectKey]);
173
+ continue;
174
+ }
175
+ for (const methodKey of Object.keys(subjectValue)) {
176
+ __classPrivateFieldGet(this, _EnrichmentAudit_instances, "m", _EnrichmentAudit_checkBlock).call(this, [generatorId, subjectKey, methodKey]);
177
+ }
178
+ }
179
+ }
180
+ return [...__classPrivateFieldGet(this, _EnrichmentAudit_warnings, "f")];
181
+ }
182
+ }
183
+ exports.EnrichmentAudit = EnrichmentAudit;
184
+ _EnrichmentAudit_consumed = new WeakMap(), _EnrichmentAudit_children = new WeakMap(), _EnrichmentAudit_warnings = new WeakMap(), _EnrichmentAudit_reported = new WeakMap(), _EnrichmentAudit_instances = new WeakSet(), _EnrichmentAudit_isConsumed = function _EnrichmentAudit_isConsumed(segments) {
185
+ return __classPrivateFieldGet(this, _EnrichmentAudit_consumed, "f").has(toPathKey(segments));
186
+ }, _EnrichmentAudit_divergenceSuggestion = function _EnrichmentAudit_divergenceSuggestion(path) {
187
+ for (let depth = 0; depth < path.length; depth++) {
188
+ if (__classPrivateFieldGet(this, _EnrichmentAudit_instances, "m", _EnrichmentAudit_isConsumed).call(this, path.slice(0, depth + 1)))
189
+ continue;
190
+ const siblings = __classPrivateFieldGet(this, _EnrichmentAudit_children, "f").get(toPathKey(path.slice(0, depth)));
191
+ return siblings ? (0, nearestKey_js_1.nearestKey)(path[depth], siblings) : undefined;
192
+ }
193
+ return undefined;
194
+ }, _EnrichmentAudit_checkBlock = function _EnrichmentAudit_checkBlock(path) {
195
+ if (__classPrivateFieldGet(this, _EnrichmentAudit_instances, "m", _EnrichmentAudit_isConsumed).call(this, path))
196
+ return;
197
+ const suggestion = __classPrivateFieldGet(this, _EnrichmentAudit_instances, "m", _EnrichmentAudit_divergenceSuggestion).call(this, path);
198
+ this.report({
199
+ level: 'warning',
200
+ type: 'UNCONSUMED_ENRICHMENT',
201
+ path,
202
+ message: `enrichment entry '${(0, EnrichmentWarning_js_1.formatEnrichmentPath)(path)}' was never consumed — ` +
203
+ `no matching generator or subject in this run` +
204
+ (suggestion !== undefined ? ` (did you mean '${suggestion}'?)` : ''),
205
+ ...(suggestion !== undefined ? { suggestion } : {})
206
+ });
207
+ };
208
+ /**
209
+ * Whether an enrichment block declares the given variant — the presence
210
+ * probe behind `SKIPPED_SUBJECT_ENRICHMENT` info lines. Exported for the
211
+ * dispatch loops; a non-record block (absent or malformed) has no
212
+ * variants.
213
+ */
214
+ const hasVariantEnrichment = (block, variant) => isRecord(block) && variant in block;
215
+ exports.hasVariantEnrichment = hasVariantEnrichment;
@@ -0,0 +1,72 @@
1
+ /**
2
+ * @fileoverview The enrichment-warning surface — the manifest's record of
3
+ * enrichment config that did not do what the consumer intended.
4
+ *
5
+ * Three validation layers precede this one: structural validation at CLI
6
+ * load (`generatorEnrichments`), the per-leaf Valibot parse at generate
7
+ * time (a wrongly-typed value in a reached leaf throws), and the
8
+ * missing-`'main'` variant throw (`toVariantList`). All three are loud.
9
+ * What they cannot see is *addressing*: a typo'd generator id, path,
10
+ * method, or model name makes `get()` miss silently — defaults apply and
11
+ * the config entry is dead. Unknown leaf keys are equally silent:
12
+ * `v.object` strips them before the generator sees them.
13
+ *
14
+ * `EnrichmentWarning` is the shared shape for both detectors:
15
+ *
16
+ * - the **consumption audit** (`EnrichmentAudit`) — records every
17
+ * enrichment lookup the engine performs during the generate walk and,
18
+ * post-walk, flags configured entries no lookup ever touched;
19
+ * - **unknown-key detection** (`findUnknownKeys`, run inside
20
+ * `parseEnrichmentUmbrella`) — flags leaf keys the generator's schema
21
+ * does not declare.
22
+ *
23
+ * Both are warn-level and fail-open: generation output is never affected.
24
+ * The warnings ride the manifest (`manifest.enrichmentWarnings`) so
25
+ * post-run diagnostics can re-read them without re-running.
26
+ *
27
+ * @module EnrichmentWarning
28
+ */
29
+ import * as v from 'valibot';
30
+ /**
31
+ * Discriminates what went wrong with an enrichment entry:
32
+ *
33
+ * - `UNCONSUMED_ENRICHMENT` — a configured routing path no engine lookup
34
+ * consumed (typo'd path / method / model name, or an entry orphaned by
35
+ * spec evolution). Warning.
36
+ * - `UNKNOWN_GENERATOR_ID` — a top-level enrichment key that matches no
37
+ * generator in the run (and no insert-only peer consumed it). Warning.
38
+ * - `UNKNOWN_ENRICHMENT_KEY` — a leaf key the generator's enrichment
39
+ * schema does not declare; Valibot strips it silently. Warning.
40
+ * - `SKIPPED_SUBJECT_ENRICHMENT` — the entry is addressed correctly but
41
+ * its subject was excluded by `skip`/`include`. Info: temporary skips
42
+ * are legitimate, so this is not treated as dead config.
43
+ * - `SKIPPED_GENERATOR_ENRICHMENT` — the whole generator is skipped
44
+ * (string entry in `settings.skip`) while enrichments for it exist.
45
+ * Info, for the same reason.
46
+ */
47
+ export type EnrichmentWarningType = 'UNCONSUMED_ENRICHMENT' | 'UNKNOWN_GENERATOR_ID' | 'UNKNOWN_ENRICHMENT_KEY' | 'SKIPPED_SUBJECT_ENRICHMENT' | 'SKIPPED_GENERATOR_ENRICHMENT';
48
+ export declare const enrichmentWarningType: v.GenericSchema<EnrichmentWarningType>;
49
+ /**
50
+ * One enrichment-config warning. `path` is the routing key sequence under
51
+ * `client.json#settings.enrichments` locating the offending entry —
52
+ * `[generatorId, ...subjectSegments]` for addressing warnings, extended
53
+ * down to the leaf key for `UNKNOWN_ENRICHMENT_KEY`. `suggestion` carries
54
+ * the nearest known key when a close match exists (`submitLabl` →
55
+ * `submitLabel`); the human-readable `message` already folds it in.
56
+ */
57
+ export type EnrichmentWarning = {
58
+ level: 'warning' | 'info';
59
+ type: EnrichmentWarningType;
60
+ path: readonly string[];
61
+ message: string;
62
+ suggestion?: string;
63
+ };
64
+ export declare const enrichmentWarning: v.GenericSchema<EnrichmentWarning>;
65
+ /**
66
+ * Render a routing path for warning messages:
67
+ * `['@skmtc/gen-shadcn-form', '/pets', 'post']` →
68
+ * `'@skmtc/gen-shadcn-form → /pets → post'`. The arrow separator keeps
69
+ * segments visually distinct even when they contain dots or slashes.
70
+ */
71
+ export declare const formatEnrichmentPath: (path: readonly string[]) => string;
72
+ //# sourceMappingURL=EnrichmentWarning.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EnrichmentWarning.d.ts","sourceRoot":"","sources":["../../src/enrichments/EnrichmentWarning.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,OAAO,KAAK,CAAC,MAAM,SAAS,CAAA;AAE5B;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,MAAM,qBAAqB,GAC7B,uBAAuB,GACvB,sBAAsB,GACtB,wBAAwB,GACxB,4BAA4B,GAC5B,8BAA8B,CAAA;AAElC,eAAO,MAAM,qBAAqB,EAAE,CAAC,CAAC,aAAa,CAAC,qBAAqB,CAMvE,CAAA;AAEF;;;;;;;GAOG;AACH,MAAM,MAAM,iBAAiB,GAAG;IAC9B,KAAK,EAAE,SAAS,GAAG,MAAM,CAAA;IACzB,IAAI,EAAE,qBAAqB,CAAA;IAC3B,IAAI,EAAE,SAAS,MAAM,EAAE,CAAA;IACvB,OAAO,EAAE,MAAM,CAAA;IACf,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB,CAAA;AAED,eAAO,MAAM,iBAAiB,EAAE,CAAC,CAAC,aAAa,CAAC,iBAAiB,CAM/D,CAAA;AAEF;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB,GAAI,MAAM,SAAS,MAAM,EAAE,KAAG,MAA0B,CAAA"}
@@ -0,0 +1,87 @@
1
+ "use strict";
2
+ /**
3
+ * @fileoverview The enrichment-warning surface — the manifest's record of
4
+ * enrichment config that did not do what the consumer intended.
5
+ *
6
+ * Three validation layers precede this one: structural validation at CLI
7
+ * load (`generatorEnrichments`), the per-leaf Valibot parse at generate
8
+ * time (a wrongly-typed value in a reached leaf throws), and the
9
+ * missing-`'main'` variant throw (`toVariantList`). All three are loud.
10
+ * What they cannot see is *addressing*: a typo'd generator id, path,
11
+ * method, or model name makes `get()` miss silently — defaults apply and
12
+ * the config entry is dead. Unknown leaf keys are equally silent:
13
+ * `v.object` strips them before the generator sees them.
14
+ *
15
+ * `EnrichmentWarning` is the shared shape for both detectors:
16
+ *
17
+ * - the **consumption audit** (`EnrichmentAudit`) — records every
18
+ * enrichment lookup the engine performs during the generate walk and,
19
+ * post-walk, flags configured entries no lookup ever touched;
20
+ * - **unknown-key detection** (`findUnknownKeys`, run inside
21
+ * `parseEnrichmentUmbrella`) — flags leaf keys the generator's schema
22
+ * does not declare.
23
+ *
24
+ * Both are warn-level and fail-open: generation output is never affected.
25
+ * The warnings ride the manifest (`manifest.enrichmentWarnings`) so
26
+ * post-run diagnostics can re-read them without re-running.
27
+ *
28
+ * @module EnrichmentWarning
29
+ */
30
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
31
+ if (k2 === undefined) k2 = k;
32
+ var desc = Object.getOwnPropertyDescriptor(m, k);
33
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
34
+ desc = { enumerable: true, get: function() { return m[k]; } };
35
+ }
36
+ Object.defineProperty(o, k2, desc);
37
+ }) : (function(o, m, k, k2) {
38
+ if (k2 === undefined) k2 = k;
39
+ o[k2] = m[k];
40
+ }));
41
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
42
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
43
+ }) : function(o, v) {
44
+ o["default"] = v;
45
+ });
46
+ var __importStar = (this && this.__importStar) || (function () {
47
+ var ownKeys = function(o) {
48
+ ownKeys = Object.getOwnPropertyNames || function (o) {
49
+ var ar = [];
50
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
51
+ return ar;
52
+ };
53
+ return ownKeys(o);
54
+ };
55
+ return function (mod) {
56
+ if (mod && mod.__esModule) return mod;
57
+ var result = {};
58
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
59
+ __setModuleDefault(result, mod);
60
+ return result;
61
+ };
62
+ })();
63
+ Object.defineProperty(exports, "__esModule", { value: true });
64
+ exports.formatEnrichmentPath = exports.enrichmentWarning = exports.enrichmentWarningType = void 0;
65
+ const v = __importStar(require("valibot"));
66
+ exports.enrichmentWarningType = v.picklist([
67
+ 'UNCONSUMED_ENRICHMENT',
68
+ 'UNKNOWN_GENERATOR_ID',
69
+ 'UNKNOWN_ENRICHMENT_KEY',
70
+ 'SKIPPED_SUBJECT_ENRICHMENT',
71
+ 'SKIPPED_GENERATOR_ENRICHMENT'
72
+ ]);
73
+ exports.enrichmentWarning = v.object({
74
+ level: v.picklist(['warning', 'info']),
75
+ type: exports.enrichmentWarningType,
76
+ path: v.array(v.string()),
77
+ message: v.string(),
78
+ suggestion: v.optional(v.string())
79
+ });
80
+ /**
81
+ * Render a routing path for warning messages:
82
+ * `['@skmtc/gen-shadcn-form', '/pets', 'post']` →
83
+ * `'@skmtc/gen-shadcn-form → /pets → post'`. The arrow separator keeps
84
+ * segments visually distinct even when they contain dots or slashes.
85
+ */
86
+ const formatEnrichmentPath = (path) => path.join(' → ');
87
+ exports.formatEnrichmentPath = formatEnrichmentPath;
@@ -0,0 +1,40 @@
1
+ /**
2
+ * @fileoverview Unknown-key detection for enrichment leaves.
3
+ *
4
+ * Valibot's `v.object` strips keys the schema does not declare — a
5
+ * misspelled optional key (`submitLabl` for `submitLabel`) parses cleanly
6
+ * and the generator silently sees the default. `findUnknownKeys` walks a
7
+ * value against its schema and reports every key the schema would drop,
8
+ * so `parseEnrichmentUmbrella` can surface them as
9
+ * `UNKNOWN_ENRICHMENT_KEY` warnings.
10
+ *
11
+ * Comparing against the *schema* (not the parsed output) keeps the check
12
+ * immune to `v.pipe` transforms that legitimately reshape output, and
13
+ * gives correct "did you mean …?" suggestions — the intended key is in
14
+ * the schema's entries even when it is optional and absent from the
15
+ * parsed value. This is deliberately a warning layer, not
16
+ * `v.strictObject`: a shared client.json paired with an older generator
17
+ * version stays survivable under version skew instead of hard-failing.
18
+ *
19
+ * The walk is fail-open: unions, intersections, loose objects, and any
20
+ * shape it cannot attribute keys to are left alone.
21
+ *
22
+ * @module findUnknownKeys
23
+ */
24
+ /**
25
+ * One key the schema would silently drop. `path` runs from the walked
26
+ * root down to the unknown key (array indices as strings); `suggestion`
27
+ * is the nearest declared key at the same level, when a plausible typo
28
+ * target exists.
29
+ */
30
+ export type UnknownKey = {
31
+ path: string[];
32
+ suggestion?: string;
33
+ };
34
+ /**
35
+ * Every key in `value` that `schema` does not declare and `v.parse` would
36
+ * therefore drop. Wrong-typed values are not this function's concern —
37
+ * the subsequent `v.parse` throws on those as before.
38
+ */
39
+ export declare const findUnknownKeys: (schema: unknown, value: unknown) => UnknownKey[];
40
+ //# sourceMappingURL=findUnknownKeys.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"findUnknownKeys.d.ts","sourceRoot":"","sources":["../../src/enrichments/findUnknownKeys.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAKH;;;;;GAKG;AACH,MAAM,MAAM,UAAU,GAAG;IACvB,IAAI,EAAE,MAAM,EAAE,CAAA;IACd,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB,CAAA;AAgDD;;;;GAIG;AACH,eAAO,MAAM,eAAe,GAAI,QAAQ,OAAO,EAAE,OAAO,OAAO,KAAG,UAAU,EAI3E,CAAA"}
@@ -0,0 +1,84 @@
1
+ "use strict";
2
+ /**
3
+ * @fileoverview Unknown-key detection for enrichment leaves.
4
+ *
5
+ * Valibot's `v.object` strips keys the schema does not declare — a
6
+ * misspelled optional key (`submitLabl` for `submitLabel`) parses cleanly
7
+ * and the generator silently sees the default. `findUnknownKeys` walks a
8
+ * value against its schema and reports every key the schema would drop,
9
+ * so `parseEnrichmentUmbrella` can surface them as
10
+ * `UNKNOWN_ENRICHMENT_KEY` warnings.
11
+ *
12
+ * Comparing against the *schema* (not the parsed output) keeps the check
13
+ * immune to `v.pipe` transforms that legitimately reshape output, and
14
+ * gives correct "did you mean …?" suggestions — the intended key is in
15
+ * the schema's entries even when it is optional and absent from the
16
+ * parsed value. This is deliberately a warning layer, not
17
+ * `v.strictObject`: a shared client.json paired with an older generator
18
+ * version stays survivable under version skew instead of hard-failing.
19
+ *
20
+ * The walk is fail-open: unions, intersections, loose objects, and any
21
+ * shape it cannot attribute keys to are left alone.
22
+ *
23
+ * @module findUnknownKeys
24
+ */
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ exports.findUnknownKeys = void 0;
27
+ const valibotShape_js_1 = require("./valibotShape.js");
28
+ const nearestKey_js_1 = require("../helpers/nearestKey.js");
29
+ const isRecord = (input) => typeof input === 'object' && input !== null && !Array.isArray(input);
30
+ const walk = (rawSchema, value, path, out) => {
31
+ if (value === undefined || value === null)
32
+ return;
33
+ if (!(0, valibotShape_js_1.isValibotSchema)(rawSchema))
34
+ return;
35
+ // A piped schema spreads its base, so `type` / `entries` / `item` are
36
+ // still visible here — no explicit pipe handling needed.
37
+ const { inner } = (0, valibotShape_js_1.unwrap)(rawSchema);
38
+ switch (inner.type) {
39
+ case 'object': {
40
+ if (!isRecord(value) || !(0, valibotShape_js_1.isEntriesRecord)(inner.entries))
41
+ return;
42
+ const entries = inner.entries;
43
+ const declared = Object.keys(entries);
44
+ for (const [key, member] of Object.entries(value)) {
45
+ if (key in entries) {
46
+ walk(entries[key], member, [...path, key], out);
47
+ continue;
48
+ }
49
+ const suggestion = (0, nearestKey_js_1.nearestKey)(key, declared);
50
+ out.push({ path: [...path, key], ...(suggestion !== undefined ? { suggestion } : {}) });
51
+ }
52
+ return;
53
+ }
54
+ case 'record': {
55
+ if (!isRecord(value))
56
+ return;
57
+ for (const [key, member] of Object.entries(value)) {
58
+ walk(inner.value, member, [...path, key], out);
59
+ }
60
+ return;
61
+ }
62
+ case 'array': {
63
+ if (!Array.isArray(value))
64
+ return;
65
+ value.forEach((member, index) => walk(inner.item, member, [...path, String(index)], out));
66
+ return;
67
+ }
68
+ default:
69
+ // Leaves, unions, intersections, loose/strict objects: nothing to
70
+ // attribute, or Valibot itself errors loudly (strict). Fail open.
71
+ return;
72
+ }
73
+ };
74
+ /**
75
+ * Every key in `value` that `schema` does not declare and `v.parse` would
76
+ * therefore drop. Wrong-typed values are not this function's concern —
77
+ * the subsequent `v.parse` throws on those as before.
78
+ */
79
+ const findUnknownKeys = (schema, value) => {
80
+ const out = [];
81
+ walk(schema, value, [], out);
82
+ return out;
83
+ };
84
+ exports.findUnknownKeys = findUnknownKeys;
@@ -0,0 +1,63 @@
1
+ /**
2
+ * @fileoverview The shared umbrella-parse seam for the projection-base
3
+ * factories.
4
+ *
5
+ * Every `static toEnrichments` — OAS operation, webhook, GQL operation,
6
+ * model — assembles the same `{ subject, generator, stack }` umbrella
7
+ * from three storage spots and parses it through the generator's
8
+ * composite schema. This helper centralizes that assembly so both
9
+ * enrichment-validation mechanisms ride one seam:
10
+ *
11
+ * - the reads go through `context.readEnrichment` — the recording
12
+ * accessor feeding the consumption audit (`EnrichmentAudit`);
13
+ * - the raw values are diffed against the schema (`findUnknownKeys`)
14
+ * and dropped keys surface as `UNKNOWN_ENRICHMENT_KEY` warnings.
15
+ *
16
+ * The `stack` scope is exempt from unknown-key detection: it is a single
17
+ * bag shared by every generator in the composition, and each consumer
18
+ * declares only the fields it reads — keys other generators own are
19
+ * expected, not typos.
20
+ *
21
+ * A wrong-typed value still throws exactly as before — the `v.parse` at
22
+ * the end is unchanged, and unknown-key detection runs first so a
23
+ * throwing leaf doesn't hide a dropped key.
24
+ *
25
+ * @module parseEnrichmentUmbrella
26
+ */
27
+ import * as v from 'valibot';
28
+ import { type EnrichmentWarning } from './EnrichmentWarning.js';
29
+ /**
30
+ * Minimal structural view of the context the parse needs. Any
31
+ * `GenerateContext` satisfies it; kept structural so the helper stays
32
+ * decoupled from the full context type and is trivially testable.
33
+ */
34
+ type UmbrellaParseContext = {
35
+ readEnrichment: (segments: readonly string[]) => unknown;
36
+ reportEnrichmentWarning: (warning: EnrichmentWarning) => void;
37
+ };
38
+ type ParseEnrichmentUmbrellaArgs<EnrichmentType> = {
39
+ context: UmbrellaParseContext;
40
+ generatorId: string;
41
+ /**
42
+ * Subject routing segments under `[generatorId]`, including the
43
+ * variant — `[path, method, variant]` for OAS operations,
44
+ * `[name, method, variant]` for webhooks,
45
+ * `[rootKind, fieldName, variant]` for GQL operations,
46
+ * `[refName, variant]` for models.
47
+ */
48
+ subjectSegments: readonly string[];
49
+ /** The generator's composite `{ subject, generator, stack }` schema. */
50
+ schema: v.GenericSchema<EnrichmentType>;
51
+ };
52
+ /**
53
+ * Assemble and parse one `{ subject, generator, stack }` enrichment
54
+ * umbrella, recording the reads for the consumption audit and reporting
55
+ * schema-dropped keys.
56
+ *
57
+ * @throws on a wrong-typed value, exactly like the raw `v.parse` this
58
+ * wraps — the generator's item is then recorded as `error` and the run
59
+ * continues (fail-open).
60
+ */
61
+ export declare const parseEnrichmentUmbrella: <EnrichmentType>({ context, generatorId, subjectSegments, schema }: ParseEnrichmentUmbrellaArgs<EnrichmentType>) => EnrichmentType;
62
+ export {};
63
+ //# sourceMappingURL=parseEnrichmentUmbrella.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parseEnrichmentUmbrella.d.ts","sourceRoot":"","sources":["../../src/enrichments/parseEnrichmentUmbrella.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,KAAK,CAAC,MAAM,SAAS,CAAA;AAG5B,OAAO,EAAwB,KAAK,iBAAiB,EAAE,MAAM,wBAAwB,CAAA;AAErF;;;;GAIG;AACH,KAAK,oBAAoB,GAAG;IAC1B,cAAc,EAAE,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,KAAK,OAAO,CAAA;IACxD,uBAAuB,EAAE,CAAC,OAAO,EAAE,iBAAiB,KAAK,IAAI,CAAA;CAC9D,CAAA;AAED,KAAK,2BAA2B,CAAC,cAAc,IAAI;IACjD,OAAO,EAAE,oBAAoB,CAAA;IAC7B,WAAW,EAAE,MAAM,CAAA;IACnB;;;;;;OAMG;IACH,eAAe,EAAE,SAAS,MAAM,EAAE,CAAA;IAClC,wEAAwE;IACxE,MAAM,EAAE,CAAC,CAAC,aAAa,CAAC,cAAc,CAAC,CAAA;CACxC,CAAA;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,uBAAuB,GAAI,cAAc,EAAE,mDAKrD,2BAA2B,CAAC,cAAc,CAAC,KAAG,cAgChD,CAAA"}