micro-contracts 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (99) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +351 -0
  3. package/dist/cli/templates.d.ts +16 -0
  4. package/dist/cli/templates.d.ts.map +1 -0
  5. package/dist/cli/templates.js +377 -0
  6. package/dist/cli/templates.js.map +1 -0
  7. package/dist/cli.d.ts +9 -0
  8. package/dist/cli.d.ts.map +1 -0
  9. package/dist/cli.js +978 -0
  10. package/dist/cli.js.map +1 -0
  11. package/dist/generator/dependencyGenerator.d.ts +43 -0
  12. package/dist/generator/dependencyGenerator.d.ts.map +1 -0
  13. package/dist/generator/dependencyGenerator.js +159 -0
  14. package/dist/generator/dependencyGenerator.js.map +1 -0
  15. package/dist/generator/domainGenerator.d.ts +16 -0
  16. package/dist/generator/domainGenerator.d.ts.map +1 -0
  17. package/dist/generator/domainGenerator.js +212 -0
  18. package/dist/generator/domainGenerator.js.map +1 -0
  19. package/dist/generator/index.d.ts +37 -0
  20. package/dist/generator/index.d.ts.map +1 -0
  21. package/dist/generator/index.js +747 -0
  22. package/dist/generator/index.js.map +1 -0
  23. package/dist/generator/linter.d.ts +24 -0
  24. package/dist/generator/linter.d.ts.map +1 -0
  25. package/dist/generator/linter.js +202 -0
  26. package/dist/generator/linter.js.map +1 -0
  27. package/dist/generator/overlayProcessor.d.ts +90 -0
  28. package/dist/generator/overlayProcessor.d.ts.map +1 -0
  29. package/dist/generator/overlayProcessor.js +532 -0
  30. package/dist/generator/overlayProcessor.js.map +1 -0
  31. package/dist/generator/schemaGenerator.d.ts +10 -0
  32. package/dist/generator/schemaGenerator.d.ts.map +1 -0
  33. package/dist/generator/schemaGenerator.js +299 -0
  34. package/dist/generator/schemaGenerator.js.map +1 -0
  35. package/dist/generator/templateProcessor.d.ts +178 -0
  36. package/dist/generator/templateProcessor.d.ts.map +1 -0
  37. package/dist/generator/templateProcessor.js +607 -0
  38. package/dist/generator/templateProcessor.js.map +1 -0
  39. package/dist/generator/typeGenerator.d.ts +9 -0
  40. package/dist/generator/typeGenerator.d.ts.map +1 -0
  41. package/dist/generator/typeGenerator.js +395 -0
  42. package/dist/generator/typeGenerator.js.map +1 -0
  43. package/dist/guardrails/allowlist.d.ts +45 -0
  44. package/dist/guardrails/allowlist.d.ts.map +1 -0
  45. package/dist/guardrails/allowlist.js +261 -0
  46. package/dist/guardrails/allowlist.js.map +1 -0
  47. package/dist/guardrails/config.d.ts +40 -0
  48. package/dist/guardrails/config.d.ts.map +1 -0
  49. package/dist/guardrails/config.js +174 -0
  50. package/dist/guardrails/config.js.map +1 -0
  51. package/dist/guardrails/docs.d.ts +24 -0
  52. package/dist/guardrails/docs.d.ts.map +1 -0
  53. package/dist/guardrails/docs.js +138 -0
  54. package/dist/guardrails/docs.js.map +1 -0
  55. package/dist/guardrails/drift.d.ts +23 -0
  56. package/dist/guardrails/drift.d.ts.map +1 -0
  57. package/dist/guardrails/drift.js +127 -0
  58. package/dist/guardrails/drift.js.map +1 -0
  59. package/dist/guardrails/index.d.ts +19 -0
  60. package/dist/guardrails/index.d.ts.map +1 -0
  61. package/dist/guardrails/index.js +25 -0
  62. package/dist/guardrails/index.js.map +1 -0
  63. package/dist/guardrails/lint.d.ts +20 -0
  64. package/dist/guardrails/lint.d.ts.map +1 -0
  65. package/dist/guardrails/lint.js +274 -0
  66. package/dist/guardrails/lint.js.map +1 -0
  67. package/dist/guardrails/manifest.d.ts +43 -0
  68. package/dist/guardrails/manifest.d.ts.map +1 -0
  69. package/dist/guardrails/manifest.js +231 -0
  70. package/dist/guardrails/manifest.js.map +1 -0
  71. package/dist/guardrails/runner.d.ts +31 -0
  72. package/dist/guardrails/runner.d.ts.map +1 -0
  73. package/dist/guardrails/runner.js +268 -0
  74. package/dist/guardrails/runner.js.map +1 -0
  75. package/dist/guardrails/security.d.ts +31 -0
  76. package/dist/guardrails/security.d.ts.map +1 -0
  77. package/dist/guardrails/security.js +181 -0
  78. package/dist/guardrails/security.js.map +1 -0
  79. package/dist/guardrails/typecheck.d.ts +15 -0
  80. package/dist/guardrails/typecheck.d.ts.map +1 -0
  81. package/dist/guardrails/typecheck.js +104 -0
  82. package/dist/guardrails/typecheck.js.map +1 -0
  83. package/dist/guardrails/types.d.ts +196 -0
  84. package/dist/guardrails/types.d.ts.map +1 -0
  85. package/dist/guardrails/types.js +8 -0
  86. package/dist/guardrails/types.js.map +1 -0
  87. package/dist/index.d.ts +7 -0
  88. package/dist/index.d.ts.map +1 -0
  89. package/dist/index.js +7 -0
  90. package/dist/index.js.map +1 -0
  91. package/dist/types.d.ts +489 -0
  92. package/dist/types.d.ts.map +1 -0
  93. package/dist/types.js +297 -0
  94. package/dist/types.js.map +1 -0
  95. package/docs/architecture.svg +226 -0
  96. package/docs/development-guardrails.md +541 -0
  97. package/docs/guardrails-concept.svg +252 -0
  98. package/docs/overlays-deep-dive.md +298 -0
  99. package/package.json +66 -0
@@ -0,0 +1,252 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1100 1195" font-family="'Segoe UI', 'Helvetica Neue', Arial, sans-serif">
2
+ <defs>
3
+ <!-- Gradients -->
4
+ <linearGradient id="headerGrad" x1="0%" y1="0%" x2="100%" y2="0%">
5
+ <stop offset="0%" style="stop-color:#1e3a5f"/>
6
+ <stop offset="100%" style="stop-color:#2d5a87"/>
7
+ </linearGradient>
8
+ <linearGradient id="specLaneGrad" x1="0%" y1="0%" x2="0%" y2="100%">
9
+ <stop offset="0%" style="stop-color:#dbeafe"/>
10
+ <stop offset="100%" style="stop-color:#eff6ff"/>
11
+ </linearGradient>
12
+ <linearGradient id="implLaneGrad" x1="0%" y1="0%" x2="0%" y2="100%">
13
+ <stop offset="0%" style="stop-color:#fee2e2"/>
14
+ <stop offset="100%" style="stop-color:#fef2f2"/>
15
+ </linearGradient>
16
+ <linearGradient id="generatorGrad" x1="0%" y1="0%" x2="100%" y2="0%">
17
+ <stop offset="0%" style="stop-color:#a855f7"/>
18
+ <stop offset="100%" style="stop-color:#7c3aed"/>
19
+ </linearGradient>
20
+ <linearGradient id="mergeGrad" x1="0%" y1="0%" x2="100%" y2="0%">
21
+ <stop offset="0%" style="stop-color:#22c55e"/>
22
+ <stop offset="100%" style="stop-color:#16a34a"/>
23
+ </linearGradient>
24
+ <linearGradient id="taskGrad" x1="0%" y1="0%" x2="0%" y2="100%">
25
+ <stop offset="0%" style="stop-color:#67e8f9"/>
26
+ <stop offset="100%" style="stop-color:#22d3ee"/>
27
+ </linearGradient>
28
+
29
+ <!-- Drop shadow filter -->
30
+ <filter id="shadow" x="-5%" y="-5%" width="110%" height="110%">
31
+ <feDropShadow dx="2" dy="3" stdDeviation="3" flood-opacity="0.15"/>
32
+ </filter>
33
+ <filter id="shadowSmall" x="-5%" y="-5%" width="110%" height="110%">
34
+ <feDropShadow dx="1" dy="2" stdDeviation="2" flood-opacity="0.1"/>
35
+ </filter>
36
+
37
+ <!-- Arrow markers -->
38
+ <marker id="arrowhead" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
39
+ <polygon points="0 0, 10 3.5, 0 7" fill="#64748b"/>
40
+ </marker>
41
+ </defs>
42
+
43
+ <!-- Background -->
44
+ <rect width="1100" height="1195" fill="#f8fafc"/>
45
+
46
+ <!-- Header -->
47
+ <rect x="20" y="15" width="1060" height="70" rx="8" fill="url(#headerGrad)" filter="url(#shadow)"/>
48
+ <text x="550" y="45" text-anchor="middle" fill="white" font-size="22" font-weight="600">AI-Driven Development Guardrails</text>
49
+ <text x="550" y="68" text-anchor="middle" fill="#94a3b8" font-size="13">Contract-first quality gates for TypeScript API development</text>
50
+
51
+ <!-- Two-Lane Headers -->
52
+ <rect x="20" y="100" width="530" height="35" rx="6" fill="#3b82f6"/>
53
+ <text x="285" y="123" text-anchor="middle" fill="white" font-size="14" font-weight="600">Spec Level (Contract Verification)</text>
54
+
55
+ <rect x="550" y="100" width="530" height="35" rx="6" fill="#ef4444"/>
56
+ <text x="815" y="123" text-anchor="middle" fill="white" font-size="14" font-weight="600">Implementation Level (Code Verification)</text>
57
+
58
+ <!-- Lane backgrounds -->
59
+ <rect x="20" y="135" width="530" height="770" rx="0 0 8 8" fill="url(#specLaneGrad)" stroke="#93c5fd" stroke-width="1"/>
60
+ <rect x="550" y="135" width="530" height="770" rx="0 0 8 8" fill="url(#implLaneGrad)" stroke="#fca5a5" stroke-width="1"/>
61
+
62
+ <!-- ==================== TASK: Human writes OpenAPI spec ==================== -->
63
+ <rect x="40" y="150" width="490" height="45" rx="22" fill="url(#taskGrad)" filter="url(#shadowSmall)"/>
64
+ <text x="285" y="178" text-anchor="middle" fill="#0e7490" font-size="12" font-weight="600">👤 Human writes OpenAPI spec</text>
65
+
66
+ <!-- Arrow down -->
67
+ <line x1="285" y1="195" x2="285" y2="215" stroke="#64748b" stroke-width="2" marker-end="url(#arrowhead)"/>
68
+
69
+ <!-- ==================== GATE 1: Change Allowlist ==================== -->
70
+ <rect x="40" y="225" width="490" height="70" rx="6" fill="white" stroke="#3b82f6" stroke-width="2" filter="url(#shadowSmall)"/>
71
+ <rect x="570" y="225" width="490" height="70" rx="6" fill="white" stroke="#ef4444" stroke-width="2" filter="url(#shadowSmall)"/>
72
+
73
+ <text x="285" y="252" text-anchor="middle" fill="#1e40af" font-size="13" font-weight="600">Change Allowlist</text>
74
+ <rect x="60" y="262" width="200" height="24" rx="4" fill="#dbeafe" stroke="#3b82f6" stroke-width="1"/>
75
+ <text x="160" y="278" text-anchor="middle" fill="#1e40af" font-size="10">packages/** edit blocked</text>
76
+ <rect x="270" y="262" width="200" height="24" rx="4" fill="#dbeafe" stroke="#3b82f6" stroke-width="1"/>
77
+ <text x="370" y="278" text-anchor="middle" fill="#1e40af" font-size="10">*.generated.* edit blocked</text>
78
+
79
+ <text x="815" y="252" text-anchor="middle" fill="#991b1b" font-size="13" font-weight="600">Protected Paths</text>
80
+ <rect x="590" y="262" width="220" height="24" rx="4" fill="#fee2e2" stroke="#ef4444" stroke-width="1"/>
81
+ <text x="700" y="278" text-anchor="middle" fill="#991b1b" font-size="10">server/src/_shared/overlays/**</text>
82
+ <rect x="820" y="262" width="220" height="24" rx="4" fill="#fef3c7" stroke="#f59e0b" stroke-width="1"/>
83
+ <text x="930" y="278" text-anchor="middle" fill="#92400e" font-size="10">.github/** (approval req)</text>
84
+
85
+ <!-- Gate 1 badge (after boxes for z-index) -->
86
+ <rect x="515" y="246" width="70" height="28" rx="14" fill="#1e293b"/>
87
+ <text x="550" y="265" text-anchor="middle" fill="white" font-size="14" font-weight="bold">Gate 1</text>
88
+
89
+ <!-- Arrow down -->
90
+ <line x1="550" y1="295" x2="550" y2="315" stroke="#64748b" stroke-width="2" marker-end="url(#arrowhead)"/>
91
+
92
+ <!-- ==================== GATE 2: OpenAPI Spec Validation ==================== -->
93
+ <rect x="40" y="325" width="490" height="70" rx="6" fill="white" stroke="#3b82f6" stroke-width="2" filter="url(#shadowSmall)"/>
94
+ <rect x="570" y="325" width="490" height="70" rx="6" fill="#f8fafc" stroke="#d1d5db" stroke-width="1" stroke-dasharray="4"/>
95
+
96
+ <text x="285" y="352" text-anchor="middle" fill="#1e40af" font-size="13" font-weight="600">OpenAPI Spec Validation</text>
97
+
98
+ <rect x="100" y="362" width="200" height="24" rx="4" fill="#dc2626"/>
99
+ <text x="200" y="378" text-anchor="middle" fill="white" font-size="9" font-weight="600">Spec Lint (Spectral)</text>
100
+
101
+ <rect x="320" y="362" width="180" height="24" rx="4" fill="#f59e0b"/>
102
+ <text x="410" y="378" text-anchor="middle" fill="white" font-size="9" font-weight="600">Breaking Changes (oasdiff)</text>
103
+
104
+ <text x="815" y="365" text-anchor="middle" fill="#9ca3af" font-size="11" font-style="italic">No implementation checks at this gate</text>
105
+
106
+ <!-- Gate 2 badge (after boxes for z-index) -->
107
+ <rect x="515" y="346" width="70" height="28" rx="14" fill="#1e293b"/>
108
+ <text x="550" y="365" text-anchor="middle" fill="white" font-size="14" font-weight="bold">Gate 2</text>
109
+
110
+ <!-- Arrow down -->
111
+ <line x1="550" y1="395" x2="550" y2="415" stroke="#64748b" stroke-width="2" marker-end="url(#arrowhead)"/>
112
+
113
+ <!-- ==================== GENERATOR ==================== -->
114
+ <rect x="200" y="425" width="700" height="50" rx="25" fill="url(#generatorGrad)" filter="url(#shadow)"/>
115
+ <text x="550" y="455" text-anchor="middle" fill="white" font-size="14" font-weight="600">micro-contracts generate: spec/** → packages/**</text>
116
+
117
+ <!-- Arrow down -->
118
+ <line x1="550" y1="475" x2="550" y2="495" stroke="#64748b" stroke-width="2" marker-end="url(#arrowhead)"/>
119
+
120
+ <!-- ==================== GATE 3: Artifact Integrity ==================== -->
121
+ <rect x="40" y="505" width="490" height="115" rx="6" fill="white" stroke="#3b82f6" stroke-width="2" filter="url(#shadowSmall)"/>
122
+ <rect x="570" y="505" width="490" height="115" rx="6" fill="#f8fafc" stroke="#d1d5db" stroke-width="1" stroke-dasharray="4"/>
123
+
124
+ <text x="285" y="532" text-anchor="middle" fill="#1e40af" font-size="13" font-weight="600">Generated Artifact Integrity</text>
125
+
126
+ <rect x="60" y="542" width="140" height="24" rx="4" fill="#dc2626"/>
127
+ <text x="130" y="558" text-anchor="middle" fill="white" font-size="9" font-weight="600">Drift Check (git diff)</text>
128
+
129
+ <rect x="210" y="542" width="140" height="24" rx="4" fill="#dc2626"/>
130
+ <text x="280" y="558" text-anchor="middle" fill="white" font-size="9" font-weight="600">Manifest Verification</text>
131
+
132
+ <rect x="360" y="542" width="140" height="24" rx="4" fill="#dc2626"/>
133
+ <text x="430" y="558" text-anchor="middle" fill="white" font-size="9" font-weight="600">Package Typecheck (tsc)</text>
134
+
135
+ <rect x="60" y="577" width="180" height="24" rx="4" fill="#dc2626"/>
136
+ <text x="150" y="593" text-anchor="middle" fill="white" font-size="9" font-weight="600">Project Rules (Spectral)</text>
137
+
138
+ <rect x="250" y="577" width="250" height="24" rx="4" fill="#dc2626"/>
139
+ <text x="375" y="593" text-anchor="middle" fill="white" font-size="9" font-weight="600">Published Breaking Changes (oasdiff)</text>
140
+
141
+ <text x="815" y="565" text-anchor="middle" fill="#9ca3af" font-size="11" font-style="italic">No implementation checks at this gate</text>
142
+
143
+ <!-- Gate 3 badge (after boxes for z-index) -->
144
+ <rect x="515" y="549" width="70" height="28" rx="14" fill="#1e293b"/>
145
+ <text x="550" y="568" text-anchor="middle" fill="white" font-size="14" font-weight="bold">Gate 3</text>
146
+
147
+ <!-- Arrow down -->
148
+ <line x1="550" y1="620" x2="550" y2="640" stroke="#64748b" stroke-width="2" marker-end="url(#arrowhead)"/>
149
+
150
+ <!-- ==================== TASK: AI/Human implements code ==================== -->
151
+ <rect x="570" y="650" width="490" height="45" rx="22" fill="url(#taskGrad)" filter="url(#shadowSmall)"/>
152
+ <text x="815" y="678" text-anchor="middle" fill="#0e7490" font-size="12" font-weight="600">🤖👤 AI or Human implements code</text>
153
+
154
+ <!-- Arrow down from task -->
155
+ <line x1="815" y1="695" x2="815" y2="715" stroke="#64748b" stroke-width="2" marker-end="url(#arrowhead)"/>
156
+
157
+ <!-- ==================== GATE 4: Code Quality ==================== -->
158
+ <rect x="40" y="735" width="490" height="70" rx="6" fill="#f8fafc" stroke="#d1d5db" stroke-width="1" stroke-dasharray="4"/>
159
+ <rect x="570" y="735" width="490" height="70" rx="6" fill="white" stroke="#ef4444" stroke-width="2" filter="url(#shadowSmall)"/>
160
+
161
+ <text x="285" y="775" text-anchor="middle" fill="#9ca3af" font-size="11" font-style="italic">No spec checks at this gate</text>
162
+
163
+ <text x="815" y="762" text-anchor="middle" fill="#991b1b" font-size="13" font-weight="600">Code Quality</text>
164
+
165
+ <rect x="590" y="772" width="110" height="24" rx="4" fill="#dc2626"/>
166
+ <text x="645" y="788" text-anchor="middle" fill="white" font-size="9" font-weight="600">TypeScript Lint (ESLint)</text>
167
+
168
+ <rect x="710" y="772" width="100" height="24" rx="4" fill="#dc2626"/>
169
+ <text x="760" y="788" text-anchor="middle" fill="white" font-size="9" font-weight="600">Type Check (tsc)</text>
170
+
171
+ <rect x="820" y="772" width="80" height="24" rx="4" fill="#dc2626"/>
172
+ <text x="860" y="788" text-anchor="middle" fill="white" font-size="9" font-weight="600">Unit Tests</text>
173
+
174
+ <rect x="910" y="772" width="120" height="24" rx="4" fill="#f59e0b"/>
175
+ <text x="970" y="788" text-anchor="middle" fill="white" font-size="9" font-weight="600">Format Check (Prettier)</text>
176
+
177
+ <!-- Gate 4 badge (after boxes for z-index) -->
178
+ <rect x="515" y="756" width="70" height="28" rx="14" fill="#1e293b"/>
179
+ <text x="550" y="775" text-anchor="middle" fill="white" font-size="14" font-weight="bold">Gate 4</text>
180
+
181
+ <!-- Arrow down -->
182
+ <line x1="550" y1="805" x2="550" y2="825" stroke="#64748b" stroke-width="2" marker-end="url(#arrowhead)"/>
183
+
184
+ <!-- ==================== GATE 5: Doc Consistency & Static Analysis ==================== -->
185
+ <rect x="40" y="835" width="490" height="70" rx="6" fill="white" stroke="#3b82f6" stroke-width="2" filter="url(#shadowSmall)"/>
186
+ <rect x="570" y="835" width="490" height="70" rx="6" fill="white" stroke="#ef4444" stroke-width="2" filter="url(#shadowSmall)"/>
187
+
188
+ <text x="285" y="862" text-anchor="middle" fill="#1e40af" font-size="13" font-weight="600">Doc Consistency</text>
189
+
190
+ <rect x="60" y="872" width="200" height="24" rx="4" fill="#dc2626"/>
191
+ <text x="160" y="888" text-anchor="middle" fill="white" font-size="9" font-weight="600">Markdown Sync (embedoc)</text>
192
+
193
+ <rect x="270" y="872" width="120" height="24" rx="4" fill="#dc2626"/>
194
+ <text x="330" y="888" text-anchor="middle" fill="white" font-size="9" font-weight="600">Links Validity</text>
195
+
196
+ <text x="815" y="862" text-anchor="middle" fill="#991b1b" font-size="13" font-weight="600">Architectural Integrity</text>
197
+
198
+ <rect x="690" y="872" width="250" height="24" rx="4" fill="#dc2626"/>
199
+ <text x="815" y="888" text-anchor="middle" fill="white" font-size="9" font-weight="600">Static Analysis (CodeQL)</text>
200
+
201
+ <!-- Gate 5 badge (after boxes for z-index) -->
202
+ <rect x="515" y="856" width="70" height="28" rx="14" fill="#1e293b"/>
203
+ <text x="550" y="875" text-anchor="middle" fill="white" font-size="14" font-weight="bold">Gate 5</text>
204
+
205
+ <!-- Arrow down -->
206
+ <line x1="550" y1="905" x2="550" y2="925" stroke="#64748b" stroke-width="2" marker-end="url(#arrowhead)"/>
207
+
208
+ <!-- ==================== TASK: Human reviews docs/code ==================== -->
209
+ <rect x="40" y="935" width="1020" height="45" rx="22" fill="url(#taskGrad)" filter="url(#shadow)"/>
210
+ <text x="550" y="963" text-anchor="middle" fill="#0e7490" font-size="12" font-weight="600">👤 Human reviews docs &amp; code</text>
211
+
212
+ <!-- Arrow down -->
213
+ <line x1="550" y1="980" x2="550" y2="1000" stroke="#64748b" stroke-width="2" marker-end="url(#arrowhead)"/>
214
+
215
+ <!-- ==================== MERGE GATE ==================== -->
216
+ <rect x="40" y="1010" width="1020" height="55" rx="6" fill="url(#mergeGrad)" filter="url(#shadow)"/>
217
+ <text x="550" y="1035" text-anchor="middle" fill="white" font-size="15" font-weight="600">Merge Gate (All Required Status Checks Must Pass)</text>
218
+ <text x="550" y="1055" text-anchor="middle" fill="#bbf7d0" font-size="11">Gate 1 + Gate 2 + Gate 3 + Gate 4 + Gate 5 → PR can merge to main</text>
219
+
220
+ <!-- ==================== LEGEND ==================== -->
221
+ <rect x="40" y="1080" width="1020" height="95" rx="8" fill="white" stroke="#e2e8f0" stroke-width="1"/>
222
+ <text x="550" y="1105" text-anchor="middle" fill="#1e293b" font-size="14" font-weight="600">Legend</text>
223
+
224
+ <!-- Severity legend -->
225
+ <rect x="60" y="1120" width="80" height="22" rx="4" fill="#dc2626"/>
226
+ <text x="100" y="1135" text-anchor="middle" fill="white" font-size="10" font-weight="600">ERROR</text>
227
+ <text x="155" y="1135" fill="#475569" font-size="10">Blocks merge</text>
228
+
229
+ <rect x="230" y="1120" width="80" height="22" rx="4" fill="#f59e0b"/>
230
+ <text x="270" y="1135" text-anchor="middle" fill="white" font-size="10" font-weight="600">WARNING</text>
231
+ <text x="325" y="1135" fill="#475569" font-size="10">Advisory</text>
232
+
233
+ <rect x="400" y="1120" width="80" height="22" rx="4" fill="#f8fafc" stroke="#d1d5db" stroke-width="1" stroke-dasharray="4"/>
234
+ <text x="440" y="1135" text-anchor="middle" fill="#9ca3af" font-size="10">Empty</text>
235
+ <text x="495" y="1135" fill="#475569" font-size="10">No checks</text>
236
+
237
+ <rect x="570" y="1120" width="80" height="22" rx="25" fill="url(#generatorGrad)"/>
238
+ <text x="610" y="1135" text-anchor="middle" fill="white" font-size="10" font-weight="600">Process</text>
239
+ <text x="665" y="1135" fill="#475569" font-size="10">Transformation</text>
240
+
241
+ <rect x="750" y="1120" width="80" height="22" rx="11" fill="url(#taskGrad)"/>
242
+ <text x="790" y="1135" text-anchor="middle" fill="#0e7490" font-size="10" font-weight="600">Task</text>
243
+ <text x="845" y="1135" fill="#475569" font-size="10">Human/AI work</text>
244
+
245
+ <!-- Lane description -->
246
+ <rect x="60" y="1150" width="16" height="12" rx="2" fill="#3b82f6"/>
247
+ <text x="85" y="1160" fill="#475569" font-size="10">Spec Level: OpenAPI contracts, generated artifacts, documentation</text>
248
+
249
+ <rect x="550" y="1150" width="16" height="12" rx="2" fill="#ef4444"/>
250
+ <text x="575" y="1160" fill="#475569" font-size="10">Impl Level: TypeScript code, tests, static analysis (CodeQL)</text>
251
+
252
+ </svg>
@@ -0,0 +1,298 @@
1
+ # OpenAPI Overlays (Deep Dive)
2
+
3
+ > **Navigation**: [← Back to README](../README.md) | [Examples](../examples/)
4
+
5
+ **Overlays** use the standard [OpenAPI Overlay Specification 1.0.0](https://www.openapis.org/blog/2024/10/22/announcing-overlay-specification) to define how `x-*` extensions transform the OpenAPI spec. This enables cross-cutting concerns (middleware, auth, rate limiting) without repeating definitions.
6
+
7
+ **Why Overlays?**
8
+
9
+ - **Standard format**: Uses official OpenAPI Overlay Specification — [learn more](https://learn.openapis.org/overlay/)
10
+ - **Transparent transformation**: Clear what gets injected where
11
+ - **Composable**: Chain multiple overlays (shared + module-specific)
12
+ - **Tool compatibility**: Works with other Overlay-aware tools ([Redocly CLI](https://redocly.com/docs/cli/), etc.)
13
+
14
+ ---
15
+
16
+ ## How Overlays Work
17
+
18
+ 1. **Mark operations** with `x-middleware` (or custom extensions) in OpenAPI
19
+ 2. **Define overlay** that adds params/responses when extension is present
20
+ 3. **Generator applies overlays** and produces `openapi.generated.yaml`
21
+ 4. **Generate code** from the generated spec (types, routes, overlay interfaces)
22
+
23
+ ---
24
+
25
+ ## Overlay Scope
26
+
27
+ | Scope | Overlay Location | Use Case |
28
+ |-------|------------------|----------|
29
+ | **Cross-module** | `spec/_shared/overlays/` | Auth, rate limiting, tenant isolation |
30
+ | **Intra-module** | `spec/{module}/overlays/` | Module-specific validation (e.g., payment validation in billing) |
31
+
32
+ ---
33
+
34
+ ## Writing Overlay Definitions
35
+
36
+ ### 1. Mark Operations in OpenAPI
37
+
38
+ Add extension markers to operations that need cross-cutting behavior:
39
+
40
+ ```yaml
41
+ # spec/core/openapi/core.yaml
42
+ paths:
43
+ /api/tenant/data:
44
+ get:
45
+ operationId: getTenantData
46
+ x-micro-contracts-domain: Tenant
47
+ x-micro-contracts-method: getTenantData
48
+ x-middleware: # ← Extension marker
49
+ - tenantIsolation
50
+ - requireAuth
51
+ - rateLimit
52
+ responses:
53
+ '200':
54
+ description: Success
55
+ content:
56
+ application/json:
57
+ schema:
58
+ $ref: '#/components/schemas/TenantData'
59
+ # Note: 400, 401, 429 are NOT defined here
60
+ # They will be injected by the overlay
61
+ ```
62
+
63
+ ### 2. Define Overlay
64
+
65
+ Create an overlay file that injects parameters and responses based on markers:
66
+
67
+ ```yaml
68
+ # spec/_shared/overlays/middleware.overlay.yaml
69
+ overlay: 1.0.0
70
+ info:
71
+ title: Middleware Extension Overlay
72
+ version: 1.0.0
73
+
74
+ actions:
75
+ # requireAuth: Inject Authorization header + 401 response
76
+ - target: "$.paths[*][*][?(@.x-middleware contains 'requireAuth')]"
77
+ x-micro-contracts-overlay-name: requireAuth # ← Name for type generation
78
+ update:
79
+ parameters:
80
+ - name: Authorization
81
+ in: header
82
+ schema: { type: string }
83
+ description: Bearer token for authentication
84
+ responses:
85
+ '401':
86
+ $ref: '../openapi/problem-details.yaml#/components/responses/Unauthorized'
87
+
88
+ # tenantIsolation: Inject X-Tenant-Id header + 400 response
89
+ - target: "$.paths[*][*][?(@.x-middleware contains 'tenantIsolation')]"
90
+ x-micro-contracts-overlay-name: tenantIsolation
91
+ update:
92
+ parameters:
93
+ - name: X-Tenant-Id
94
+ in: header
95
+ required: true
96
+ schema:
97
+ type: string
98
+ format: uuid
99
+ description: Tenant identifier for multi-tenant isolation
100
+ responses:
101
+ '400':
102
+ description: Missing or invalid X-Tenant-Id header
103
+ content:
104
+ application/problem+json:
105
+ schema:
106
+ $ref: '../openapi/problem-details.yaml#/components/schemas/ProblemDetails'
107
+
108
+ # rateLimit: Inject 429 response with rate limit headers
109
+ - target: "$.paths[*][*][?(@.x-middleware contains 'rateLimit')]"
110
+ x-micro-contracts-overlay-name: rateLimit
111
+ update:
112
+ responses:
113
+ '429':
114
+ description: Too Many Requests
115
+ headers:
116
+ X-RateLimit-Limit:
117
+ schema: { type: integer }
118
+ description: Rate limit ceiling for this endpoint
119
+ Retry-After:
120
+ schema: { type: integer }
121
+ description: Seconds until rate limit resets
122
+ ```
123
+
124
+ > **📦 Full example**: See [`examples/spec/_shared/overlays/middleware.overlay.yaml`](../examples/spec/_shared/overlays/middleware.overlay.yaml)
125
+
126
+ ### $ref Paths in Overlays
127
+
128
+ Specify `$ref` paths **relative to the overlay file's location**. micro-contracts automatically rebases these paths so that the generated `openapi.generated.yaml` can resolve them correctly.
129
+
130
+ ```yaml
131
+ # spec/_shared/overlays/middleware.overlay.yaml
132
+ $ref: '../openapi/problem-details.yaml#/components/responses/Unauthorized'
133
+ # ↑ Resolves to spec/_shared/openapi/problem-details.yaml
134
+ ```
135
+
136
+ **Rebase example:**
137
+
138
+ | File | $ref path |
139
+ |------|-----------|
140
+ | Overlay (source) | `../openapi/problem-details.yaml#/...` |
141
+ | Generated spec (`packages/contract/core/docs/openapi.generated.yaml`) | `../../../../spec/_shared/openapi/problem-details.yaml#/...` |
142
+
143
+ The generated path is relative to `openapi.generated.yaml`'s location, ensuring the reference resolves correctly.
144
+
145
+ ---
146
+
147
+ ## Implementing Overlay Handlers
148
+
149
+ Overlay handlers **do not directly access HTTP request/response objects** (e.g., Fastify's `req`/`reply`). Parameters are extracted by the generated adapter and passed as typed input. However, they are aware of HTTP concepts (header names, status codes) unlike Domain implementations which are purely business logic:
150
+
151
+ ```typescript
152
+ // server/src/core/overlays/requireAuth.ts
153
+ import type {
154
+ RequireAuthOverlayInput,
155
+ OverlayResult
156
+ } from '@project/contract/core/overlays/index.js';
157
+
158
+ export async function requireAuth(input: RequireAuthOverlayInput): Promise<OverlayResult> {
159
+ const authHeader = input['Authorization'];
160
+
161
+ if (!authHeader || !authHeader.startsWith('Bearer ')) {
162
+ return {
163
+ success: false,
164
+ error: { status: 401, message: 'Missing or invalid authorization header' },
165
+ };
166
+ }
167
+
168
+ // In real app: validate JWT token and extract user info
169
+ return {
170
+ success: true,
171
+ context: { userId: 'user-123', role: 'user' },
172
+ };
173
+ }
174
+ ```
175
+
176
+ > **📦 Full examples**: See [`examples/server/src/core/overlays/`](../examples/server/src/core/overlays/)
177
+
178
+ ### Layer Responsibilities
179
+
180
+ | Layer | Responsibility | Touches HTTP objects | Knows HTTP concepts |
181
+ |-------|----------------|---------------------|---------------------|
182
+ | **Generated routes** | Extract params, wire handlers | ✅ Yes | ✅ Yes |
183
+ | **Overlay impl** | Handle cross-cutting concern | ❌ No | ✅ Yes (headers, status codes) |
184
+ | **Domain impl** | Business logic only | ❌ No | ❌ No |
185
+
186
+ ---
187
+
188
+ ## Configuration
189
+
190
+ Configure overlays in `micro-contracts.config.yaml`:
191
+
192
+ ```yaml
193
+ # micro-contracts.config.yaml
194
+ defaults:
195
+ overlays:
196
+ # Shared overlays applied to all modules (in order)
197
+ shared:
198
+ - spec/_shared/overlays/middleware.overlay.yaml
199
+ collision: error # error | warn | last-wins (default: error)
200
+
201
+ modules:
202
+ core:
203
+ openapi: openapi/core.yaml
204
+ overlays: # Module-specific overlays (applied after shared)
205
+ - overlays/cache.overlay.yaml
206
+
207
+ billing:
208
+ openapi: openapi/billing.yaml
209
+ overlays:
210
+ - overlays/payment.overlay.yaml
211
+ ```
212
+
213
+ ---
214
+
215
+ ## Overlay Policies
216
+
217
+ ### 1. Collision Policy
218
+
219
+ When multiple overlays inject the same key (e.g., `401` response):
220
+
221
+ | Scenario | Policy | Rationale |
222
+ |----------|--------|-----------|
223
+ | Same key, **identical content** | ✅ Allow | No ambiguity |
224
+ | Same key, **different content** | ❌ Error | Ambiguous intent |
225
+
226
+ Configure in `defaults.overlays.collision`: `error` (default) | `warn` | `last-wins`
227
+
228
+ ### 2. Application Order
229
+
230
+ Order is **deterministic**:
231
+
232
+ ```
233
+ 1. defaults.overlays.shared (in array order)
234
+ 2. modules.{name}.overlays (in array order)
235
+ ```
236
+
237
+ Generation logs what was injected:
238
+
239
+ ```bash
240
+ npx micro-contracts generate -m core
241
+
242
+ [overlay] Applying spec/_shared/overlays/middleware.overlay.yaml
243
+ → /api/tenant/data GET: +X-Tenant-Id, +400, +401, +429
244
+ [output] packages/contract/core/docs/openapi.generated.yaml
245
+ ```
246
+
247
+ ### 3. JSONPath Patterns
248
+
249
+ micro-contracts uses a restricted JSONPath dialect for reliable parsing:
250
+
251
+ | Pattern | Meaning |
252
+ |---------|---------|
253
+ | `$.paths[*][*]` | All operations |
254
+ | `[?(@.x-ext)]` | Has extension |
255
+ | `[?(@.x-ext contains 'value')]` | Array contains value |
256
+
257
+ **Use `contains` for array membership:**
258
+
259
+ ```yaml
260
+ # ✅ Correct - portable
261
+ - target: "$.paths[*][*][?(@.x-middleware contains 'requireAuth')]"
262
+
263
+ # ❌ Avoid - implementation-dependent
264
+ - target: "$.paths[*][*][?(@.x-middleware[*] == 'requireAuth')]"
265
+ ```
266
+
267
+ ### 4. Marker vs Injection Responsibility
268
+
269
+ | Layer | Responsibility |
270
+ |-------|----------------|
271
+ | **OpenAPI (source)** | Business responses (200, 201, 204) |
272
+ | **OpenAPI (source)** | Extension markers (`x-middleware: [requireAuth]`) |
273
+ | **Overlay** | Cross-cutting responses (400, 401, 403, 429) |
274
+
275
+ **Anti-pattern**: Don't manually add `401` to operations that have `x-middleware: [requireAuth]`. Let the overlay inject it.
276
+
277
+ ---
278
+
279
+ ## Summary
280
+
281
+ | What | Generated | Notes |
282
+ |------|-----------|-------|
283
+ | Overlay application | ✅ Yes | Transform OpenAPI (inject params/responses) |
284
+ | Overlay interfaces | ✅ Yes | HTTP-agnostic (`OverlayRegistry`, `*OverlayInput`) |
285
+ | Contract types | ✅ Yes | Types from OpenAPI schemas |
286
+ | Route wiring | ✅ Via template | Template decides how to wire |
287
+ | Overlay impl | ❌ Human | Implement generated interface |
288
+ | Domain impl | ❌ Human | Business logic |
289
+
290
+ ---
291
+
292
+ ## Related Documentation
293
+
294
+ | Document | Description |
295
+ |----------|-------------|
296
+ | **[Examples](../examples/)** | Complete working project with multiple modules and overlays |
297
+ | **[Guardrails](development-guardrails.md)** | CI integration, security checks |
298
+ | **[README](../README.md)** | Core concepts, configuration |
package/package.json ADDED
@@ -0,0 +1,66 @@
1
+ {
2
+ "name": "micro-contracts",
3
+ "version": "0.9.0",
4
+ "description": "Contract-first OpenAPI toolchain that keeps TypeScript UI and microservices aligned via code generation",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "bin": {
9
+ "micro-contracts": "dist/cli.js"
10
+ },
11
+ "files": [
12
+ "dist",
13
+ "docs",
14
+ "README.md",
15
+ "LICENSE"
16
+ ],
17
+ "scripts": {
18
+ "build": "tsc",
19
+ "dev": "tsx watch src/cli.ts",
20
+ "test": "vitest run",
21
+ "test:watch": "vitest",
22
+ "generate": "tsx src/cli.ts generate",
23
+ "docs:sync": "npx embedoc build",
24
+ "docs:check": "npx embedoc build && git diff --exit-code README.md docs/",
25
+ "prepublishOnly": "npm run build && npm test"
26
+ },
27
+ "keywords": [
28
+ "openapi",
29
+ "contract-first",
30
+ "microservices",
31
+ "code-generation",
32
+ "fastify",
33
+ "typescript",
34
+ "api",
35
+ "guardrails",
36
+ "ai-development"
37
+ ],
38
+ "author": "",
39
+ "license": "MIT",
40
+ "repository": {
41
+ "type": "git",
42
+ "url": "git+https://github.com/foo-ogawa/micro-contracts.git"
43
+ },
44
+ "homepage": "https://github.com/foo-ogawa/micro-contracts#readme",
45
+ "bugs": {
46
+ "url": "https://github.com/foo-ogawa/micro-contracts/issues"
47
+ },
48
+ "dependencies": {
49
+ "commander": "^12.1.0",
50
+ "glob": "^10.3.10",
51
+ "handlebars": "^4.7.8",
52
+ "js-yaml": "^4.1.0",
53
+ "yaml": "^2.8.2"
54
+ },
55
+ "devDependencies": {
56
+ "@types/js-yaml": "^4.0.9",
57
+ "@types/node": "^20.11.0",
58
+ "embedoc": "^0.9.0",
59
+ "tsx": "^4.7.0",
60
+ "typescript": "^5.3.3",
61
+ "vitest": "^1.2.0"
62
+ },
63
+ "engines": {
64
+ "node": ">=18.0.0"
65
+ }
66
+ }