docorbit 0.1.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 (144) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +660 -0
  3. package/apps/cli/bin/docorbit.js +8 -0
  4. package/apps/cli/src/commands/add.ts +44 -0
  5. package/apps/cli/src/commands/api.ts +38 -0
  6. package/apps/cli/src/commands/context.ts +47 -0
  7. package/apps/cli/src/commands/dashboard.ts +55 -0
  8. package/apps/cli/src/commands/diff.ts +30 -0
  9. package/apps/cli/src/commands/evaluate.ts +133 -0
  10. package/apps/cli/src/commands/examples.ts +39 -0
  11. package/apps/cli/src/commands/export.ts +89 -0
  12. package/apps/cli/src/commands/impact.ts +31 -0
  13. package/apps/cli/src/commands/init.ts +69 -0
  14. package/apps/cli/src/commands/inspect.ts +30 -0
  15. package/apps/cli/src/commands/mcp.ts +72 -0
  16. package/apps/cli/src/commands/pitfalls.ts +38 -0
  17. package/apps/cli/src/commands/recipes.ts +35 -0
  18. package/apps/cli/src/commands/search.ts +48 -0
  19. package/apps/cli/src/commands/update.ts +73 -0
  20. package/apps/cli/src/commands/verify.ts +48 -0
  21. package/apps/cli/src/formatters/colors.ts +23 -0
  22. package/apps/cli/src/formatters/inspection.ts +102 -0
  23. package/apps/cli/src/formatters/knowledge.ts +272 -0
  24. package/apps/cli/src/formatters/retrieval.ts +74 -0
  25. package/apps/cli/src/formatters/terminal.ts +6 -0
  26. package/apps/cli/src/formatters/verification.ts +126 -0
  27. package/apps/cli/src/index.ts +409 -0
  28. package/bin/docorbit.js +8 -0
  29. package/package.json +46 -0
  30. package/packages/core/src/dashboard/server.ts +314 -0
  31. package/packages/core/src/dashboard/ui.ts +586 -0
  32. package/packages/core/src/implementation-service.ts +451 -0
  33. package/packages/core/src/index.ts +7 -0
  34. package/packages/core/src/inspector.ts +71 -0
  35. package/packages/core/src/pipeline.ts +331 -0
  36. package/packages/crawler/src/config.ts +12 -0
  37. package/packages/crawler/src/fetcher.ts +185 -0
  38. package/packages/crawler/src/index.ts +2 -0
  39. package/packages/discovery/src/index.ts +31 -0
  40. package/packages/discovery/src/provider.ts +47 -0
  41. package/packages/discovery/src/providers/generic.ts +98 -0
  42. package/packages/discovery/src/providers/github.ts +61 -0
  43. package/packages/discovery/src/providers/llms-txt.ts +73 -0
  44. package/packages/discovery/src/providers/markdown.ts +48 -0
  45. package/packages/discovery/src/providers/openapi.ts +91 -0
  46. package/packages/discovery/src/providers/sitemap.ts +62 -0
  47. package/packages/discovery/src/providers/skill.ts +54 -0
  48. package/packages/discovery/src/ranker.ts +123 -0
  49. package/packages/evaluation/src/dataset.ts +963 -0
  50. package/packages/evaluation/src/index.ts +8 -0
  51. package/packages/evaluation/src/runner.ts +241 -0
  52. package/packages/evaluation/src/strategies/context7-runner.ts +269 -0
  53. package/packages/evaluation/src/strategies/docorbit-runner.ts +228 -0
  54. package/packages/evaluation/src/strategies/firecrawl-runner.ts +172 -0
  55. package/packages/evaluation/src/strategies/web-search-runner.ts +194 -0
  56. package/packages/evaluation/src/types.ts +34 -0
  57. package/packages/evaluation/src/version-matcher.ts +73 -0
  58. package/packages/export/src/agents-md.ts +200 -0
  59. package/packages/export/src/claude-md.ts +141 -0
  60. package/packages/export/src/docs-map.ts +150 -0
  61. package/packages/export/src/index.ts +6 -0
  62. package/packages/export/src/llms-txt.ts +96 -0
  63. package/packages/export/src/service.ts +250 -0
  64. package/packages/export/src/skill-md.ts +128 -0
  65. package/packages/mcp/src/index.ts +46 -0
  66. package/packages/mcp/src/resources/index.ts +189 -0
  67. package/packages/mcp/src/server.ts +278 -0
  68. package/packages/mcp/src/tools/analyze-impact.ts +74 -0
  69. package/packages/mcp/src/tools/check-api.ts +86 -0
  70. package/packages/mcp/src/tools/diff-docs.ts +68 -0
  71. package/packages/mcp/src/tools/export-context.ts +73 -0
  72. package/packages/mcp/src/tools/find-api.ts +99 -0
  73. package/packages/mcp/src/tools/find-example.ts +100 -0
  74. package/packages/mcp/src/tools/find-pitfall.ts +94 -0
  75. package/packages/mcp/src/tools/find-recipe.ts +98 -0
  76. package/packages/mcp/src/tools/get-doc.ts +130 -0
  77. package/packages/mcp/src/tools/get-docs-map.ts +64 -0
  78. package/packages/mcp/src/tools/get-version.ts +118 -0
  79. package/packages/mcp/src/tools/implementation-context.ts +88 -0
  80. package/packages/mcp/src/tools/index.ts +59 -0
  81. package/packages/mcp/src/tools/list-sources.ts +85 -0
  82. package/packages/mcp/src/tools/search-docs.ts +123 -0
  83. package/packages/mcp/src/tools/types.ts +28 -0
  84. package/packages/mcp/src/transports/http.ts +256 -0
  85. package/packages/mcp/src/transports/stdio.ts +105 -0
  86. package/packages/mcp/src/transports/types.ts +6 -0
  87. package/packages/mcp/src/types.ts +102 -0
  88. package/packages/normalizer/src/example-indexer.ts +240 -0
  89. package/packages/normalizer/src/html.ts +253 -0
  90. package/packages/normalizer/src/index.ts +8 -0
  91. package/packages/normalizer/src/llms.ts +83 -0
  92. package/packages/normalizer/src/openapi/endpoint-parser.ts +406 -0
  93. package/packages/normalizer/src/openapi/schema-resolver.ts +111 -0
  94. package/packages/normalizer/src/openapi.ts +2 -0
  95. package/packages/normalizer/src/page.ts +184 -0
  96. package/packages/normalizer/src/pitfall-extractor.ts +190 -0
  97. package/packages/normalizer/src/slicer.ts +455 -0
  98. package/packages/retrieval/src/engine.ts +120 -0
  99. package/packages/retrieval/src/index.ts +7 -0
  100. package/packages/retrieval/src/intent.ts +43 -0
  101. package/packages/retrieval/src/packer.ts +145 -0
  102. package/packages/retrieval/src/recipe-engine.ts +313 -0
  103. package/packages/retrieval/src/scorer.ts +139 -0
  104. package/packages/retrieval/src/weights.ts +31 -0
  105. package/packages/security/src/annotations.ts +112 -0
  106. package/packages/security/src/index.ts +2 -0
  107. package/packages/security/src/ssrf.ts +153 -0
  108. package/packages/shared/src/errors.ts +53 -0
  109. package/packages/shared/src/hashing.ts +23 -0
  110. package/packages/shared/src/index.ts +3 -0
  111. package/packages/shared/src/types.ts +881 -0
  112. package/packages/storage/src/db.ts +72 -0
  113. package/packages/storage/src/index.ts +11 -0
  114. package/packages/storage/src/interfaces.ts +115 -0
  115. package/packages/storage/src/repositories/api-repository.ts +219 -0
  116. package/packages/storage/src/repositories/chunk-repository.ts +316 -0
  117. package/packages/storage/src/repositories/example-repository.ts +206 -0
  118. package/packages/storage/src/repositories/page-repository.ts +205 -0
  119. package/packages/storage/src/repositories/pitfall-repository.ts +188 -0
  120. package/packages/storage/src/repositories/source-repository.ts +205 -0
  121. package/packages/storage/src/repository.ts +256 -0
  122. package/packages/storage/src/schema.ts +269 -0
  123. package/packages/storage/src/search-tokens.ts +28 -0
  124. package/packages/verification/src/diff-engine.ts +258 -0
  125. package/packages/verification/src/extractor.ts +339 -0
  126. package/packages/verification/src/impact-scanner.ts +203 -0
  127. package/packages/verification/src/index.ts +5 -0
  128. package/packages/verification/src/services.ts +238 -0
  129. package/packages/verification/src/verifier.ts +375 -0
  130. package/packages/workspace/src/detector.ts +143 -0
  131. package/packages/workspace/src/ecosystems/cargo.ts +84 -0
  132. package/packages/workspace/src/ecosystems/composer.ts +42 -0
  133. package/packages/workspace/src/ecosystems/go.ts +54 -0
  134. package/packages/workspace/src/ecosystems/index.ts +34 -0
  135. package/packages/workspace/src/ecosystems/maven.ts +34 -0
  136. package/packages/workspace/src/ecosystems/npm.ts +83 -0
  137. package/packages/workspace/src/ecosystems/pub.ts +40 -0
  138. package/packages/workspace/src/ecosystems/pypi.ts +100 -0
  139. package/packages/workspace/src/ecosystems/rubygems.ts +30 -0
  140. package/packages/workspace/src/ecosystems/types.ts +18 -0
  141. package/packages/workspace/src/index.ts +5 -0
  142. package/packages/workspace/src/lockfile.ts +194 -0
  143. package/packages/workspace/src/resolver.ts +234 -0
  144. package/packages/workspace/src/semver.ts +259 -0
@@ -0,0 +1,963 @@
1
+ import type { BenchmarkTaskDef } from './types.ts';
2
+
3
+ export const BENCHMARK_DATASET: BenchmarkTaskDef[] = [
4
+ // ==========================================
5
+ // TRAIN / DEVELOPMENT SPLIT (4 tasks)
6
+ // ==========================================
7
+ {
8
+ id: 'train_nextjs_14_route_sync',
9
+ split: 'train',
10
+ title: 'Next.js 14 Synchronous Dynamic Route Parameters',
11
+ ecosystem: 'typescript',
12
+ library: 'next',
13
+ targetVersion: '14.2.5',
14
+ taskPrompt: 'Implement dynamic route parameter handling in Next.js 14 App Router GET handler',
15
+ docsUrl: 'https://nextjs.org/docs/14/app/api-reference/file-conventions/route',
16
+ workspaceFiles: {
17
+ 'package.json': JSON.stringify({
18
+ name: 'my-next-app',
19
+ dependencies: {
20
+ next: '^14.2.0',
21
+ react: '^18.2.0',
22
+ },
23
+ }, null, 2),
24
+ },
25
+ docs: [
26
+ {
27
+ url: 'https://nextjs.org/docs/14/routing',
28
+ title: 'Next.js 14 App Router: Route Handlers and Params',
29
+ version: '14.2.5',
30
+ content: `# Next.js 14 Route Handlers
31
+ In Next.js 14, Route Handlers receive context with synchronous parameters:
32
+ \`export async function GET(request: Request, { params }: { params: { id: string } })\`.
33
+ The params object is a plain synchronous object: \`const id = params.id;\`.
34
+ Do not await params in Next.js 14.`,
35
+ },
36
+ {
37
+ url: 'https://nextjs.org/docs/15/routing',
38
+ title: 'Next.js 15 Asynchronous Route Parameters Breaking Change',
39
+ version: '15.0.0',
40
+ content: `# Next.js 15 Async Dynamic Route Parameters
41
+ Breaking Change: In Next.js 15, route handler parameters are asynchronous Promises.
42
+ Must use: \`const { id } = await params;\`. Accessing properties synchronously causes a runtime warning/error.`,
43
+ },
44
+ ],
45
+ groundTruth: {
46
+ expectedVersion: '14.2.5',
47
+ expectedApi: {
48
+ symbol: 'GET',
49
+ path: '/api/users/[id]',
50
+ },
51
+ fatalPitfalls: ['await params in Next.js 14 is unnecessary and params is synchronous'],
52
+ validCode: `export async function GET(request: Request, { params }: { params: { id: string } }) {
53
+ const userId = params.id;
54
+ return Response.json({ id: userId });
55
+ }`,
56
+ invalidCode: {
57
+ snippet: `export async function GET(request: Request, { params }: { params: Promise<{ id: string }> }) {
58
+ const { id } = await params;
59
+ return Response.json({ id });
60
+ }`,
61
+ expectedRule: 'version_mismatch',
62
+ },
63
+ dynamicCode: `export async function GET(req: Request, ctx: any) {
64
+ const field = eval("ctx.params.id");
65
+ return Response.json({ field });
66
+ }`,
67
+ },
68
+ },
69
+
70
+ {
71
+ id: 'train_stripe_v1_charges',
72
+ split: 'train',
73
+ title: 'Stripe Legacy Charges Direct Payment',
74
+ ecosystem: 'javascript',
75
+ library: 'stripe',
76
+ targetVersion: '12.18.0',
77
+ taskPrompt: 'Create a direct credit card charge with Stripe API legacy charges endpoint',
78
+ docsUrl: 'https://stripe.com/docs/api/charges/create',
79
+ workspaceFiles: {
80
+ 'package.json': JSON.stringify({
81
+ name: 'legacy-store',
82
+ dependencies: {
83
+ stripe: '^12.0.0',
84
+ },
85
+ }, null, 2),
86
+ },
87
+ docs: [
88
+ {
89
+ url: 'https://stripe.com/docs/api/charges',
90
+ title: 'Stripe API: Charges Resource',
91
+ version: '12.18.0',
92
+ content: `# Charges API
93
+ POST /v1/charges
94
+ Creates a new charge object.
95
+ Required parameters:
96
+ - amount: integer (required)
97
+ - currency: string (required)
98
+ - source: string (required token or card ID)`,
99
+ },
100
+ ],
101
+ groundTruth: {
102
+ expectedVersion: '12.18.0',
103
+ expectedApi: {
104
+ method: 'POST',
105
+ path: '/v1/charges',
106
+ requiredParams: ['amount', 'currency'],
107
+ },
108
+ fatalPitfalls: ['Do not omit amount or currency when creating a charge'],
109
+ validCode: `const charge = await stripe.charges.create({
110
+ amount: 2000,
111
+ currency: 'usd',
112
+ source: 'tok_visa'
113
+ });`,
114
+ invalidCode: {
115
+ snippet: `const charge = await stripe.charges.create({
116
+ currency: 'usd',
117
+ source: 'tok_visa'
118
+ });`,
119
+ expectedRule: 'required_parameters',
120
+ },
121
+ dynamicCode: `const fn = stripe['charges']['create'];
122
+ await fn({ amount: 1000, currency: 'usd' });`,
123
+ },
124
+ },
125
+
126
+ {
127
+ id: 'train_pydantic_v1_validator',
128
+ split: 'train',
129
+ title: 'Pydantic v1 Custom Field Validator',
130
+ ecosystem: 'python',
131
+ library: 'pydantic',
132
+ targetVersion: '1.10.12',
133
+ taskPrompt: 'Validate age field using Pydantic v1 validator decorator',
134
+ docsUrl: 'https://docs.pydantic.dev/1.10/usage/validators/',
135
+ workspaceFiles: {
136
+ 'pyproject.toml': `[tool.poetry.dependencies]\npython = "^3.10"\npydantic = "^1.10.0"\n`,
137
+ },
138
+ docs: [
139
+ {
140
+ url: 'https://docs.pydantic.dev/1.10/usage/validators/',
141
+ title: 'Pydantic 1.10 Validators',
142
+ version: '1.10.12',
143
+ content: `# Pydantic 1.x Validators
144
+ Use @validator decorator with pre=True/False.
145
+ Example:
146
+ \`\`\`python
147
+ from pydantic import BaseModel, validator
148
+ class User(BaseModel):
149
+ age: int
150
+ @validator('age')
151
+ def check_age(cls, v):
152
+ if v < 0: raise ValueError('Age cannot be negative')
153
+ return v
154
+ \`\`\``,
155
+ },
156
+ ],
157
+ groundTruth: {
158
+ expectedVersion: '1.10.12',
159
+ expectedApi: {
160
+ symbol: 'validator',
161
+ },
162
+ fatalPitfalls: ['@field_validator is not supported in Pydantic 1.x; use @validator'],
163
+ validCode: `from pydantic import BaseModel, validator
164
+ class User(BaseModel):
165
+ age: int
166
+ @validator('age')
167
+ def validate_age(cls, v):
168
+ if v < 0: raise ValueError('invalid age')
169
+ return v`,
170
+ invalidCode: {
171
+ snippet: `from pydantic import BaseModel, field_validator
172
+ class User(BaseModel):
173
+ age: int
174
+ @field_validator('age')
175
+ @classmethod
176
+ def validate_age(cls, v): return v`,
177
+ expectedRule: 'version_mismatch',
178
+ },
179
+ dynamicCode: `def make_validator():
180
+ return getattr(__import__('pydantic'), 'validator')('age')`,
181
+ },
182
+ },
183
+
184
+ {
185
+ id: 'train_fastapi_095_sync_dep',
186
+ split: 'train',
187
+ title: 'FastAPI 0.95 Database Dependency Injection',
188
+ ecosystem: 'python',
189
+ library: 'fastapi',
190
+ targetVersion: '0.95.2',
191
+ taskPrompt: 'Inject database session in FastAPI 0.95 endpoint using Depends',
192
+ docsUrl: 'https://fastapi.tiangolo.com/tutorial/dependencies/',
193
+ workspaceFiles: {
194
+ 'pyproject.toml': `[tool.poetry.dependencies]\npython = "^3.10"\nfastapi = "^0.95.0"\n`,
195
+ },
196
+ docs: [
197
+ {
198
+ url: 'https://fastapi.tiangolo.com/0.95/dependencies/',
199
+ title: 'FastAPI Dependencies with yield',
200
+ version: '0.95.2',
201
+ content: `# FastAPI Dependencies
202
+ Use \`Depends(get_db)\` where get_db yields a database session and closes it in a finally block.`,
203
+ },
204
+ ],
205
+ groundTruth: {
206
+ expectedVersion: '0.95.2',
207
+ expectedApi: {
208
+ symbol: 'Depends',
209
+ },
210
+ fatalPitfalls: ['Ensure DB connection closes after yield in generator dependency'],
211
+ validCode: `from fastapi import FastAPI, Depends
212
+ app = FastAPI()
213
+ def get_db():
214
+ db = SessionLocal()
215
+ try: yield db
216
+ finally: db.close()
217
+ @app.get('/items')
218
+ def read_items(db = Depends(get_db)):
219
+ return db.query(Item).all()`,
220
+ invalidCode: {
221
+ snippet: `@app.get('/items')
222
+ def read_items(db = get_db()): # Missing Depends wrapper
223
+ return db.query(Item).all()`,
224
+ expectedRule: 'parameter_misconfiguration',
225
+ },
226
+ },
227
+ },
228
+
229
+ // ==========================================
230
+ // HELD-OUT EVALUATION SPLIT (6 tasks)
231
+ // ==========================================
232
+ {
233
+ id: 'eval_nextjs_15_async_params',
234
+ split: 'eval',
235
+ title: 'Next.js 15 Async Dynamic Route Parameters Breaking Change',
236
+ ecosystem: 'typescript',
237
+ library: 'next',
238
+ targetVersion: '15.0.3',
239
+ taskPrompt: 'Implement dynamic route parameter handling in Next.js 15 App Router GET route handler',
240
+ docsUrl: 'https://nextjs.org/docs/app/api-reference/file-conventions/route',
241
+ workspaceFiles: {
242
+ 'package.json': JSON.stringify({
243
+ name: 'modern-next15',
244
+ dependencies: {
245
+ next: '^15.0.0',
246
+ react: '^19.0.0',
247
+ },
248
+ }, null, 2),
249
+ },
250
+ docs: [
251
+ {
252
+ url: 'https://nextjs.org/docs/15/routing',
253
+ title: 'Next.js 15 Asynchronous Route Parameters',
254
+ version: '15.0.3',
255
+ content: `# Next.js 15 Dynamic Route Parameters
256
+ Breaking change in Next.js 15: \`params\` in Page and Route Handlers is now an asynchronous Promise.
257
+ You must use: \`const { id } = await params;\`.
258
+ Accessing \`params.id\` directly without await is deprecated and throws runtime errors in production.`,
259
+ },
260
+ ],
261
+ groundTruth: {
262
+ expectedVersion: '15.0.3',
263
+ expectedApi: {
264
+ symbol: 'GET',
265
+ path: '/api/items/[id]',
266
+ },
267
+ fatalPitfalls: ['Do not access params synchronously in Next.js 15; it is a Promise that must be awaited'],
268
+ validCode: `export async function GET(request: Request, { params }: { params: Promise<{ id: string }> }) {
269
+ const { id } = await params;
270
+ return Response.json({ id });
271
+ }`,
272
+ invalidCode: {
273
+ snippet: `export async function GET(request: Request, { params }: { params: { id: string } }) {
274
+ const id = params.id;
275
+ return Response.json({ id });
276
+ }`,
277
+ expectedRule: 'version_mismatch',
278
+ },
279
+ dynamicCode: `export async function GET(req: Request, ctx: any) {
280
+ const p = await (ctx['params']);
281
+ return Response.json(p);
282
+ }`,
283
+ },
284
+ },
285
+
286
+ {
287
+ id: 'eval_stripe_payment_intents_2024',
288
+ split: 'eval',
289
+ title: 'Stripe Modern Payment Intents Creation',
290
+ ecosystem: 'typescript',
291
+ library: 'stripe',
292
+ targetVersion: '16.12.0',
293
+ taskPrompt: 'Create a Stripe Payment Intent with automatic payment methods enabled',
294
+ docsUrl: 'https://stripe.com/docs/api/payment_intents/create',
295
+ workspaceFiles: {
296
+ 'package.json': JSON.stringify({
297
+ name: 'modern-payments',
298
+ dependencies: {
299
+ stripe: '^16.0.0',
300
+ },
301
+ }, null, 2),
302
+ },
303
+ docs: [
304
+ {
305
+ url: 'https://stripe.com/docs/api/payment_intents',
306
+ title: 'Stripe API: Payment Intents Resource',
307
+ version: '16.12.0',
308
+ content: `# Payment Intents API
309
+ POST /v1/payment_intents
310
+ Creates a PaymentIntent object.
311
+ Required parameters:
312
+ - amount: integer (required)
313
+ - currency: string (required)
314
+ Recommended:
315
+ - automatic_payment_methods: object with enabled: boolean
316
+ Notice: Direct charges via /v1/charges are deprecated for standard checkout.`,
317
+ },
318
+ ],
319
+ groundTruth: {
320
+ expectedVersion: '16.12.0',
321
+ expectedApi: {
322
+ method: 'POST',
323
+ path: '/v1/payment_intents',
324
+ requiredParams: ['amount', 'currency'],
325
+ },
326
+ fatalPitfalls: ['Do not use /v1/charges in modern Stripe integrations; use /v1/payment_intents'],
327
+ validCode: `const intent = await stripe.paymentIntents.create({
328
+ amount: 5000,
329
+ currency: 'usd',
330
+ automatic_payment_methods: { enabled: true }
331
+ });`,
332
+ invalidCode: {
333
+ snippet: `const intent = await stripe.paymentIntents.create({
334
+ amount: 5000
335
+ });`,
336
+ expectedRule: 'required_parameters',
337
+ },
338
+ dynamicCode: `const res = await stripe[Math.random() > 0 ? 'paymentIntents' : 'charges'].create({ amount: 100 });`,
339
+ },
340
+ },
341
+
342
+ {
343
+ id: 'eval_pydantic_v2_field_validator',
344
+ split: 'eval',
345
+ title: 'Pydantic v2 Migration: field_validator Replacement',
346
+ ecosystem: 'python',
347
+ library: 'pydantic',
348
+ targetVersion: '2.6.0',
349
+ taskPrompt: 'Implement custom field validation for email field in Pydantic v2',
350
+ docsUrl: 'https://docs.pydantic.dev/latest/concepts/validators/',
351
+ workspaceFiles: {
352
+ 'pyproject.toml': `[tool.poetry.dependencies]\npython = "^3.11"\npydantic = "^2.6.0"\n`,
353
+ },
354
+ docs: [
355
+ {
356
+ url: 'https://docs.pydantic.dev/2.6/migration/',
357
+ title: 'Pydantic 2.6 Migration Guide: @validator to @field_validator',
358
+ version: '2.6.0',
359
+ content: `# Pydantic V2 Migration
360
+ Breaking change: @validator is deprecated in V2.
361
+ Replace with @field_validator('fieldname', mode='before'|'after') combined with @classmethod.
362
+ Example:
363
+ \`\`\`python
364
+ from pydantic import BaseModel, field_validator
365
+ class User(BaseModel):
366
+ email: str
367
+ @field_validator('email', mode='before')
368
+ @classmethod
369
+ def lower_email(cls, v: str) -> str:
370
+ return v.lower()
371
+ \`\`\``,
372
+ },
373
+ ],
374
+ groundTruth: {
375
+ expectedVersion: '2.6.0',
376
+ expectedApi: {
377
+ symbol: 'field_validator',
378
+ },
379
+ fatalPitfalls: ['@validator is removed/deprecated in Pydantic v2; use @field_validator with @classmethod'],
380
+ validCode: `from pydantic import BaseModel, field_validator
381
+ class User(BaseModel):
382
+ email: str
383
+ @field_validator('email', mode='before')
384
+ @classmethod
385
+ def validate_email(cls, v: str) -> str:
386
+ if '@' not in v: raise ValueError('invalid email')
387
+ return v.lower()`,
388
+ invalidCode: {
389
+ snippet: `from pydantic import BaseModel, validator
390
+ class User(BaseModel):
391
+ email: str
392
+ @validator('email') # Deprecated in v2
393
+ def validate_email(cls, v): return v`,
394
+ expectedRule: 'deprecation',
395
+ },
396
+ dynamicCode: `validator_decorator = getattr(__import__('pydantic'), 'field_validator')`,
397
+ },
398
+ },
399
+
400
+ {
401
+ id: 'eval_fastapi_100_lifespan',
402
+ split: 'eval',
403
+ title: 'FastAPI 0.100+ Lifespan Handler Deprecation of on_event',
404
+ ecosystem: 'python',
405
+ library: 'fastapi',
406
+ targetVersion: '0.110.0',
407
+ taskPrompt: 'Manage application startup and shutdown events using FastAPI modern lifespan context manager',
408
+ docsUrl: 'https://fastapi.tiangolo.com/advanced/events/',
409
+ workspaceFiles: {
410
+ 'pyproject.toml': `[tool.poetry.dependencies]\npython = "^3.11"\nfastapi = "^0.110.0"\n`,
411
+ },
412
+ docs: [
413
+ {
414
+ url: 'https://fastapi.tiangolo.com/advanced/events/',
415
+ title: 'FastAPI Lifespan Events',
416
+ version: '0.110.0',
417
+ content: `# FastAPI Lifespan Events
418
+ In FastAPI 0.100+, \`@app.on_event('startup')\` and \`@app.on_event('shutdown')\` are deprecated.
419
+ Use the \`lifespan\` async context manager parameter on \`FastAPI(lifespan=...)\`.
420
+ Example:
421
+ \`\`\`python
422
+ from contextlib import asynccontextmanager
423
+ from fastapi import FastAPI
424
+ @asynccontextmanager
425
+ async def lifespan(app: FastAPI):
426
+ yield
427
+ app = FastAPI(lifespan=lifespan)
428
+ \`\`\``,
429
+ },
430
+ ],
431
+ groundTruth: {
432
+ expectedVersion: '0.110.0',
433
+ expectedApi: {
434
+ symbol: 'lifespan',
435
+ },
436
+ fatalPitfalls: ['@app.on_event is deprecated; use asynccontextmanager lifespan parameter'],
437
+ validCode: `from contextlib import asynccontextmanager
438
+ from fastapi import FastAPI
439
+
440
+ @asynccontextmanager
441
+ async def lifespan(app: FastAPI):
442
+ # Startup tasks
443
+ yield
444
+ # Shutdown tasks
445
+
446
+ app = FastAPI(lifespan=lifespan)`,
447
+ invalidCode: {
448
+ snippet: `from fastapi import FastAPI
449
+ app = FastAPI()
450
+ @app.on_event('startup') # Deprecated in FastAPI 0.100+
451
+ async def startup_event(): pass`,
452
+ expectedRule: 'deprecation',
453
+ },
454
+ },
455
+ },
456
+
457
+ {
458
+ id: 'eval_tokio_postgres_07',
459
+ split: 'eval',
460
+ title: 'Tokio-Postgres 0.7 Asynchronous Connection and Query Execution',
461
+ ecosystem: 'rust',
462
+ library: 'tokio-postgres',
463
+ targetVersion: '0.7.10',
464
+ taskPrompt: 'Connect asynchronously to Postgres and spawn the connection worker task in Tokio',
465
+ docsUrl: 'https://docs.rs/tokio-postgres/0.7.10/tokio_postgres/',
466
+ workspaceFiles: {
467
+ 'Cargo.toml': `[package]\nname = "rust-pg"\nversion = "0.1.0"\n\n[dependencies]\ntokio = { version = "1.36", features = ["full"] }\ntokio-postgres = "0.7.10"\n`,
468
+ },
469
+ docs: [
470
+ {
471
+ url: 'https://docs.rs/tokio-postgres/0.7.10/tokio_postgres/',
472
+ title: 'tokio_postgres 0.7.10 Documentation',
473
+ version: '0.7.10',
474
+ content: `# tokio_postgres
475
+ The connect function returns a client and a Connection object.
476
+ The connection object must be spawned onto an executor (tokio::spawn) to drive network I/O.
477
+ Example:
478
+ \`\`\`rust
479
+ let (client, connection) = tokio_postgres::connect("host=localhost user=postgres", NoTls).await?;
480
+ tokio::spawn(async move {
481
+ if let Err(e) = connection.await {
482
+ eprintln!("connection error: {}", e);
483
+ }
484
+ });
485
+ \`\`\``,
486
+ },
487
+ ],
488
+ groundTruth: {
489
+ expectedVersion: '0.7.10',
490
+ expectedApi: {
491
+ symbol: 'connect',
492
+ },
493
+ fatalPitfalls: ['Connection object must be spawned onto Tokio runtime or queries will hang indefinitely'],
494
+ validCode: `let (client, connection) = tokio_postgres::connect("host=localhost user=postgres", tokio_postgres::NoTls).await?;
495
+ tokio::spawn(async move {
496
+ if let Err(e) = connection.await {
497
+ eprintln!("connection error: {}", e);
498
+ }
499
+ });
500
+ let rows = client.query("SELECT id, name FROM users", &[]).await?;`,
501
+ invalidCode: {
502
+ snippet: `let (client, connection) = tokio_postgres::connect("host=localhost user=postgres", tokio_postgres::NoTls).await?;
503
+ // Forgot tokio::spawn(connection) - queries will block indefinitely!
504
+ let rows = client.query("SELECT id, name FROM users", &[]).await?;`,
505
+ expectedRule: 'concurrency_deadlock',
506
+ },
507
+ },
508
+ },
509
+
510
+ {
511
+ id: 'eval_gin_gonic_v19',
512
+ split: 'eval',
513
+ title: 'Gin Gonic v1.9 JSON Binding via ShouldBindJSON',
514
+ ecosystem: 'go',
515
+ library: 'github.com/gin-gonic/gin',
516
+ targetVersion: '1.9.1',
517
+ taskPrompt: 'Bind and validate incoming JSON request payload in Gin route handler',
518
+ docsUrl: 'https://gin-gonic.com/docs/examples/binding-and-validation/',
519
+ workspaceFiles: {
520
+ 'go.mod': `module myapi\n\ngo 1.21\n\nrequire github.com/gin-gonic/gin v1.9.1\n`,
521
+ },
522
+ docs: [
523
+ {
524
+ url: 'https://gin-gonic.com/docs/examples/binding-and-validation/',
525
+ title: 'Gin Model Binding and Validation',
526
+ version: '1.9.1',
527
+ content: `# Gin JSON Binding
528
+ In Gin 1.9+, use \`c.ShouldBindJSON(&obj)\` to parse request body into a struct.
529
+ \`c.BindWith\` is deprecated and sets HTTP 400 automatically without programmatic recovery.
530
+ \`ShouldBindJSON\` allows custom error formatting.`,
531
+ },
532
+ ],
533
+ groundTruth: {
534
+ expectedVersion: '1.9.1',
535
+ expectedApi: {
536
+ symbol: 'ShouldBindJSON',
537
+ },
538
+ fatalPitfalls: ['Do not use deprecated BindWith; use ShouldBindJSON for custom error responses'],
539
+ validCode: `func HandleCreate(c *gin.Context) {
540
+ var req CreateRequest
541
+ if err := c.ShouldBindJSON(&req); err != nil {
542
+ c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
543
+ return
544
+ }
545
+ c.JSON(http.StatusOK, gin.H{"status": "ok"})
546
+ }`,
547
+ invalidCode: {
548
+ snippet: `func HandleCreate(c *gin.Context) {
549
+ var req CreateRequest
550
+ c.BindWith(&req, binding.JSON) // Deprecated BindWith
551
+ }`,
552
+ expectedRule: 'deprecation',
553
+ },
554
+ },
555
+ },
556
+
557
+ // ==========================================
558
+ // DEDICATED VERIFICATION SPLIT (7 tasks)
559
+ // Intentionally incorrect generated code testing:
560
+ // - wrong endpoint
561
+ // - wrong HTTP method
562
+ // - missing required parameter
563
+ // - deprecated API
564
+ // - removed API
565
+ // - version mismatch
566
+ // - dynamic/ambiguous code
567
+ // ==========================================
568
+ {
569
+ id: 'verify_wrong_endpoint',
570
+ split: 'verification',
571
+ title: 'Verification: Non-Existent Endpoint Detection',
572
+ ecosystem: 'javascript',
573
+ library: 'stripe',
574
+ targetVersion: 'v14',
575
+ verificationCategory: 'wrong_endpoint',
576
+ taskPrompt: 'Verify detection of non-existent API endpoint',
577
+ docsUrl: 'https://docs.stripe.com/api',
578
+ workspaceFiles: {
579
+ 'package.json': JSON.stringify({ name: 'stripe-test', dependencies: { stripe: '^14.0.0' } }, null, 2),
580
+ },
581
+ docs: [
582
+ {
583
+ url: 'https://docs.stripe.com/api',
584
+ title: 'Stripe API Reference',
585
+ version: 'v14',
586
+ content: '# Stripe API v14\nPOST /v1/webhook_endpoints\nCreate a webhook endpoint.',
587
+ },
588
+ ],
589
+ endpoints: [
590
+ {
591
+ id: 'ep_v1_wh',
592
+ pageId: 'p_wh',
593
+ snapshotId: 'snap_v14',
594
+ method: 'post',
595
+ path: '/v1/webhook_endpoints',
596
+ summary: 'Create webhook endpoint',
597
+ parameters: [{ name: 'url', in: 'query', required: true, type: 'string' }],
598
+ docVersion: 'v14',
599
+ },
600
+ ],
601
+ groundTruth: {
602
+ expectedVersion: 'v14',
603
+ expectedApi: { symbol: 'webhook_endpoints', path: '/v1/webhook_endpoints' },
604
+ fatalPitfalls: ['Endpoint does not exist'],
605
+ validCode: `const res = await fetch('https://api.stripe.com/v1/webhook_endpoints', {
606
+ method: 'POST',
607
+ body: JSON.stringify({ url: 'https://example.com/wh', enabled_events: ['charge.failed'] })
608
+ });`,
609
+ invalidCode: {
610
+ snippet: `fetch('https://api.stripe.com/v1/non_existent_portal_endpoint', { method: 'POST' });`,
611
+ expectedRule: 'endpoint_validity',
612
+ },
613
+ },
614
+ },
615
+
616
+ {
617
+ id: 'verify_wrong_http_method',
618
+ split: 'verification',
619
+ title: 'Verification: Unsupported HTTP Method Detection',
620
+ ecosystem: 'javascript',
621
+ library: 'stripe',
622
+ targetVersion: 'v14',
623
+ verificationCategory: 'wrong_http_method',
624
+ taskPrompt: 'Verify detection of unsupported HTTP method on valid endpoint',
625
+ docsUrl: 'https://docs.stripe.com/api',
626
+ workspaceFiles: {
627
+ 'package.json': JSON.stringify({ name: 'stripe-test', dependencies: { stripe: '^14.0.0' } }, null, 2),
628
+ },
629
+ docs: [
630
+ {
631
+ url: 'https://docs.stripe.com/api',
632
+ title: 'Stripe API Reference',
633
+ version: 'v14',
634
+ content: '# Stripe API v14\nPOST /v1/webhook_endpoints\nCreate a webhook endpoint.',
635
+ },
636
+ ],
637
+ endpoints: [
638
+ {
639
+ id: 'ep_v2_wh',
640
+ pageId: 'p_wh',
641
+ snapshotId: 'snap_v14',
642
+ method: 'post',
643
+ path: '/v1/webhook_endpoints',
644
+ summary: 'Create webhook endpoint',
645
+ parameters: [{ name: 'url', in: 'query', required: true, type: 'string' }],
646
+ docVersion: 'v14',
647
+ },
648
+ ],
649
+ groundTruth: {
650
+ expectedVersion: 'v14',
651
+ expectedApi: { symbol: 'webhook_endpoints', path: '/v1/webhook_endpoints' },
652
+ fatalPitfalls: ['GET is not supported on /v1/webhook_endpoints'],
653
+ validCode: `const res = await fetch('https://api.stripe.com/v1/webhook_endpoints', {
654
+ method: 'POST',
655
+ body: JSON.stringify({ url: 'https://example.com/wh', enabled_events: ['charge.failed'] })
656
+ });`,
657
+ invalidCode: {
658
+ snippet: `fetch('https://api.stripe.com/v1/webhook_endpoints', { method: 'GET' });`,
659
+ expectedRule: 'method_validity',
660
+ },
661
+ },
662
+ },
663
+
664
+ {
665
+ id: 'verify_missing_required_param',
666
+ split: 'verification',
667
+ title: 'Verification: Missing Required Parameter Detection',
668
+ ecosystem: 'javascript',
669
+ library: 'stripe',
670
+ targetVersion: 'v14',
671
+ verificationCategory: 'missing_required_parameter',
672
+ taskPrompt: 'Verify detection of missing required request body parameters',
673
+ docsUrl: 'https://docs.stripe.com/api',
674
+ workspaceFiles: {
675
+ 'package.json': JSON.stringify({ name: 'stripe-test', dependencies: { stripe: '^14.0.0' } }, null, 2),
676
+ },
677
+ docs: [
678
+ {
679
+ url: 'https://docs.stripe.com/api',
680
+ title: 'Stripe API Reference',
681
+ version: 'v14',
682
+ content: '# Stripe API v14\nPOST /v1/webhook_endpoints\nRequires url and enabled_events.',
683
+ },
684
+ ],
685
+ endpoints: [
686
+ {
687
+ id: 'ep_v3_wh',
688
+ pageId: 'p_wh',
689
+ snapshotId: 'snap_v14',
690
+ method: 'post',
691
+ path: '/v1/webhook_endpoints',
692
+ summary: 'Create webhook endpoint',
693
+ parameters: [{ name: 'url', in: 'query', required: true, type: 'string' }],
694
+ requestSchema: {
695
+ type: 'object',
696
+ required: ['enabled_events'],
697
+ properties: {
698
+ url: { type: 'string' },
699
+ enabled_events: { type: 'array' },
700
+ },
701
+ },
702
+ docVersion: 'v14',
703
+ },
704
+ ],
705
+ groundTruth: {
706
+ expectedVersion: 'v14',
707
+ expectedApi: { symbol: 'webhook_endpoints', path: '/v1/webhook_endpoints' },
708
+ fatalPitfalls: ['Missing required body field enabled_events'],
709
+ validCode: `fetch('https://api.stripe.com/v1/webhook_endpoints', {
710
+ method: 'POST',
711
+ body: JSON.stringify({ url: 'https://example.com/wh', enabled_events: ['charge.failed'] })
712
+ });`,
713
+ invalidCode: {
714
+ snippet: `fetch('https://api.stripe.com/v1/webhook_endpoints', {
715
+ method: 'POST',
716
+ body: JSON.stringify({ url: 'https://example.com/wh' })
717
+ });`,
718
+ expectedRule: 'required_parameters',
719
+ },
720
+ },
721
+ },
722
+
723
+ {
724
+ id: 'verify_deprecated_api',
725
+ split: 'verification',
726
+ title: 'Verification: Deprecated API Detection',
727
+ ecosystem: 'javascript',
728
+ library: 'stripe',
729
+ targetVersion: 'v14',
730
+ verificationCategory: 'deprecated_api',
731
+ taskPrompt: 'Verify warning detection on deprecated API endpoints',
732
+ docsUrl: 'https://docs.stripe.com/api',
733
+ workspaceFiles: {
734
+ 'package.json': JSON.stringify({ name: 'stripe-test', dependencies: { stripe: '^14.0.0' } }, null, 2),
735
+ },
736
+ docs: [
737
+ {
738
+ url: 'https://docs.stripe.com/api',
739
+ title: 'Stripe API Reference',
740
+ version: 'v14',
741
+ content: '# Stripe API v14\nPOST /v1/charges\nDeprecated in favor of PaymentIntents.\nPOST /v1/payment_intents\nModern payment intents.',
742
+ },
743
+ ],
744
+ endpoints: [
745
+ {
746
+ id: 'ep_v4_charges',
747
+ pageId: 'p_charges',
748
+ snapshotId: 'snap_v14',
749
+ method: 'post',
750
+ path: '/v1/charges',
751
+ summary: 'Create charge (deprecated)',
752
+ deprecated: true,
753
+ docVersion: 'v14',
754
+ },
755
+ {
756
+ id: 'ep_v4_pi',
757
+ pageId: 'p_pi',
758
+ snapshotId: 'snap_v14',
759
+ method: 'post',
760
+ path: '/v1/payment_intents',
761
+ summary: 'Create payment intent',
762
+ deprecated: false,
763
+ docVersion: 'v14',
764
+ },
765
+ ],
766
+ pitfalls: [
767
+ {
768
+ id: 'pit_dep_charges',
769
+ pageId: 'p_charges',
770
+ snapshotId: 'snap_v14',
771
+ kind: 'deprecated',
772
+ title: 'Charges API Deprecated',
773
+ content: 'Use /v1/payment_intents instead of /v1/charges.',
774
+ relatedApi: '/v1/charges',
775
+ docVersion: 'v14',
776
+ createdAt: '2026-09-07T00:00:00Z',
777
+ },
778
+ ],
779
+ groundTruth: {
780
+ expectedVersion: 'v14',
781
+ expectedApi: { symbol: 'payment_intents', path: '/v1/payment_intents' },
782
+ fatalPitfalls: ['Charges API is deprecated'],
783
+ validCode: `fetch('https://api.stripe.com/v1/payment_intents', {
784
+ method: 'POST',
785
+ body: JSON.stringify({ amount: 2000, currency: 'usd' })
786
+ });`,
787
+ invalidCode: {
788
+ snippet: `fetch('https://api.stripe.com/v1/charges', {
789
+ method: 'POST',
790
+ body: JSON.stringify({ amount: 2000 })
791
+ });`,
792
+ expectedRule: 'deprecation',
793
+ },
794
+ },
795
+ },
796
+
797
+ {
798
+ id: 'verify_removed_api',
799
+ split: 'verification',
800
+ title: 'Verification: Removed API Endpoint Detection',
801
+ ecosystem: 'javascript',
802
+ library: 'stripe',
803
+ targetVersion: 'v14',
804
+ verificationCategory: 'removed_api',
805
+ taskPrompt: 'Verify error detection on removed legacy endpoints',
806
+ docsUrl: 'https://docs.stripe.com/api',
807
+ workspaceFiles: {
808
+ 'package.json': JSON.stringify({ name: 'stripe-test', dependencies: { stripe: '^14.0.0' } }, null, 2),
809
+ },
810
+ docs: [
811
+ {
812
+ url: 'https://docs.stripe.com/api',
813
+ title: 'Stripe API Reference',
814
+ version: 'v14',
815
+ content: '# Stripe API v14\nPOST /v1/payment_methods\nSources API has been removed.',
816
+ },
817
+ ],
818
+ endpoints: [
819
+ {
820
+ id: 'ep_v5_pm',
821
+ pageId: 'p_pm',
822
+ snapshotId: 'snap_v14',
823
+ method: 'post',
824
+ path: '/v1/payment_methods',
825
+ summary: 'Payment methods',
826
+ deprecated: false,
827
+ docVersion: 'v14',
828
+ },
829
+ // Note: /v1/sources exists in older snapshot v12, but was removed in v14
830
+ {
831
+ id: 'ep_v5_sources_legacy',
832
+ pageId: 'p_sources',
833
+ snapshotId: 'snap_v12',
834
+ method: 'post',
835
+ path: '/v1/sources',
836
+ summary: 'Sources (legacy v12)',
837
+ deprecated: true,
838
+ docVersion: 'v12',
839
+ },
840
+ ],
841
+ pitfalls: [
842
+ {
843
+ id: 'pit_rem_sources',
844
+ pageId: 'p_pm',
845
+ snapshotId: 'snap_v14',
846
+ kind: 'removed',
847
+ title: 'Sources API Removed in v14',
848
+ content: 'Endpoint /v1/sources was removed in API version v14; migrate to PaymentMethods.',
849
+ relatedApi: '/v1/sources',
850
+ docVersion: 'v14',
851
+ createdAt: '2026-09-07T00:00:00Z',
852
+ },
853
+ ],
854
+ groundTruth: {
855
+ expectedVersion: 'v14',
856
+ expectedApi: { symbol: 'payment_methods', path: '/v1/payment_methods' },
857
+ fatalPitfalls: ['/v1/sources was removed in v14'],
858
+ validCode: `fetch('https://api.stripe.com/v1/payment_methods', {
859
+ method: 'POST',
860
+ body: JSON.stringify({ type: 'card' })
861
+ });`,
862
+ invalidCode: {
863
+ snippet: `fetch('https://api.stripe.com/v1/sources', {
864
+ method: 'POST',
865
+ body: JSON.stringify({ type: 'card' })
866
+ });`,
867
+ expectedRule: 'removed_api',
868
+ },
869
+ },
870
+ },
871
+
872
+ {
873
+ id: 'verify_version_mismatch',
874
+ split: 'verification',
875
+ title: 'Verification: Version Syntax Conflict Detection (Next.js 14 sync vs 15 async)',
876
+ ecosystem: 'typescript',
877
+ library: 'next',
878
+ targetVersion: '14.2.0',
879
+ verificationCategory: 'version_mismatch',
880
+ taskPrompt: 'Verify detection of Next.js 15 async route parameters in a Next.js 14 project',
881
+ docsUrl: 'https://nextjs.org/docs/14/app/api-reference/file-conventions/route',
882
+ workspaceFiles: {
883
+ 'package.json': JSON.stringify({ name: 'next-app', dependencies: { next: '^14.2.0', react: '^18.2.0' } }, null, 2),
884
+ },
885
+ docs: [
886
+ {
887
+ url: 'https://nextjs.org/docs/14/routing',
888
+ title: 'Next.js 14 Routing',
889
+ version: '14.2.0',
890
+ content: '# Next.js 14 Route Handlers\nNext.js 14 parameters are synchronous.',
891
+ },
892
+ ],
893
+ groundTruth: {
894
+ expectedVersion: '14.2.0',
895
+ expectedApi: { symbol: 'GET', path: '/api/users/[id]' },
896
+ fatalPitfalls: ['Do not use await params in Next.js 14'],
897
+ validCode: `export default function Page({ params }: { params: { slug: string } }) {
898
+ const { slug } = params;
899
+ return <div>{slug}</div>;
900
+ }`,
901
+ invalidCode: {
902
+ snippet: `export default async function Page({ params }: { params: Promise<{ slug: string }> }) {
903
+ const { slug } = await params;
904
+ return <div>{slug}</div>;
905
+ }`,
906
+ expectedRule: 'version_mismatch',
907
+ },
908
+ },
909
+ },
910
+
911
+ {
912
+ id: 'verify_dynamic_ambiguous_code',
913
+ split: 'verification',
914
+ title: 'Verification: Dynamic / Ambiguous Expression Safe Fallback',
915
+ ecosystem: 'javascript',
916
+ library: 'stripe',
917
+ targetVersion: 'v14',
918
+ verificationCategory: 'dynamic_ambiguous_code',
919
+ taskPrompt: 'Verify dynamic expression returns insufficient_evidence without false rejections',
920
+ docsUrl: 'https://docs.stripe.com/api',
921
+ workspaceFiles: {
922
+ 'package.json': JSON.stringify({ name: 'stripe-test', dependencies: { stripe: '^14.0.0' } }, null, 2),
923
+ },
924
+ docs: [
925
+ {
926
+ url: 'https://docs.stripe.com/api',
927
+ title: 'Stripe API Reference',
928
+ version: 'v14',
929
+ content: '# Stripe API v14\nPOST /v1/webhook_endpoints',
930
+ },
931
+ ],
932
+ endpoints: [
933
+ {
934
+ id: 'ep_v7_wh',
935
+ pageId: 'p_wh',
936
+ snapshotId: 'snap_v14',
937
+ method: 'post',
938
+ path: '/v1/webhook_endpoints',
939
+ summary: 'Create webhook endpoint',
940
+ docVersion: 'v14',
941
+ },
942
+ ],
943
+ groundTruth: {
944
+ expectedVersion: 'v14',
945
+ expectedApi: { symbol: 'webhook_endpoints', path: '/v1/webhook_endpoints' },
946
+ fatalPitfalls: ['Dynamic URLs cannot be statically verified'],
947
+ validCode: `fetch('https://api.stripe.com/v1/webhook_endpoints', {
948
+ method: 'POST',
949
+ body: JSON.stringify({ url: 'https://example.com/wh', enabled_events: ['charge.failed'] })
950
+ });`,
951
+ invalidCode: {
952
+ snippet: `async function callDynamic(userSuppliedUrl: string) {
953
+ return fetch(userSuppliedUrl);
954
+ }`,
955
+ expectedRule: 'endpoint_validity',
956
+ },
957
+ dynamicCode: `async function callDynamic(userSuppliedUrl: string) {
958
+ return fetch(userSuppliedUrl);
959
+ }`,
960
+ },
961
+ },
962
+ ];
963
+