@yosr-ai/template-dotnet 0.0.0-stage → 0.2.5

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 (68) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/LICENSE +21 -0
  3. package/README.md +14 -2
  4. package/features/auth-api-key/files/src/App/Core/ApiKeyMiddleware.cs +33 -0
  5. package/features/auth-api-key/files/tests/App.Tests/ApiKeyTests.cs +27 -0
  6. package/features/data-efcore/files/src/App/App.csproj +16 -0
  7. package/features/data-efcore/files/src/App/Core/AppDbContext.cs +22 -0
  8. package/features/data-efcore/files/src/App/Core/AppSettings.cs +30 -0
  9. package/features/data-efcore/files/src/App/Features/Notes/EfNoteStore.cs +46 -0
  10. package/features/data-efcore/files/src/App/Features/Notes/NoteEntity.cs +24 -0
  11. package/features/data-efcore/files/src/App/Program.cs +46 -0
  12. package/features/data-efcore/files/src/App/packages.lock.json +74 -0
  13. package/features/data-efcore/files/tests/App.Tests/ApiFactory.cs +70 -0
  14. package/features/data-efcore/files/tests/App.Tests/packages.lock.json +511 -0
  15. package/features/data-efcore/snippets/program-usings.txt +1 -0
  16. package/features/data-efcore/snippets/readme-db.md +3 -0
  17. package/features/data-efcore/snippets/service-registration.txt +3 -0
  18. package/features/deploy-docker/files/Dockerfile +19 -0
  19. package/features/deploy-docker/files/compose.yaml +12 -0
  20. package/features/deploy-docker/snippets/readme-docker.md +3 -0
  21. package/features/deploy-docker-postgres/snippets/app-config.yaml +3 -0
  22. package/features/deploy-docker-postgres/snippets/app-env.yaml +1 -0
  23. package/features/deploy-docker-postgres/snippets/compose-header.yaml +1 -0
  24. package/features/deploy-docker-postgres/snippets/compose-volumes.yaml +2 -0
  25. package/features/deploy-docker-postgres/snippets/postgres-service.yaml +13 -0
  26. package/features/deploy-docker-postgres/snippets/readme-docker.md +3 -0
  27. package/features/errors-result/files/src/App/Features/Notes/NotesEndpoints.cs +52 -0
  28. package/features/errors-result/files/src/App/Features/Notes/NotesService.cs +56 -0
  29. package/features/errors-result/files/src/App/Shared/Result.cs +8 -0
  30. package/features/errors-result/files/tests/App.Tests/NotesServiceTests.cs +37 -0
  31. package/files/.env.example +7 -0
  32. package/files/App.sln +24 -0
  33. package/files/Directory.Build.props +9 -0
  34. package/files/README.md +20 -0
  35. package/files/gitignore +9 -0
  36. package/files/global.json +6 -0
  37. package/files/src/App/App.csproj +13 -0
  38. package/files/src/App/Core/AppSettings.cs +25 -0
  39. package/files/src/App/Features/Notes/INoteStore.cs +12 -0
  40. package/files/src/App/Features/Notes/MemoryNoteStore.cs +54 -0
  41. package/files/src/App/Features/Notes/Note.cs +24 -0
  42. package/files/src/App/Features/Notes/NotesEndpoints.cs +63 -0
  43. package/files/src/App/Features/Notes/NotesService.cs +38 -0
  44. package/files/src/App/Program.cs +26 -0
  45. package/files/src/App/Properties/launchSettings.json +12 -0
  46. package/files/src/App/Shared/NotFoundException.cs +9 -0
  47. package/files/src/App/appsettings.json +9 -0
  48. package/files/src/App/packages.lock.json +21 -0
  49. package/files/tests/App.Tests/ApiFactory.cs +14 -0
  50. package/files/tests/App.Tests/App.Tests.csproj +28 -0
  51. package/files/tests/App.Tests/GlobalUsings.cs +1 -0
  52. package/files/tests/App.Tests/NotesApiTests.cs +62 -0
  53. package/files/tests/App.Tests/NotesServiceTests.cs +36 -0
  54. package/files/tests/App.Tests/packages.lock.json +428 -0
  55. package/package.json +26 -4
  56. package/rules/architecture.md +5 -0
  57. package/rules/auth-api-key.md +3 -0
  58. package/rules/commands.md +7 -0
  59. package/rules/data-efcore.md +3 -0
  60. package/rules/data-none.md +3 -0
  61. package/rules/errors-result.md +3 -0
  62. package/rules/errors-throw.md +3 -0
  63. package/rules/layout-feature.md +5 -0
  64. package/rules/layout-flat.md +3 -0
  65. package/rules/layout-layer.md +3 -0
  66. package/rules/project-overview.md +5 -0
  67. package/seed.template.json +716 -0
  68. package/style/.gitkeep +0 -0
@@ -0,0 +1,511 @@
1
+ {
2
+ "version": 1,
3
+ "dependencies": {
4
+ "net10.0": {
5
+ "coverlet.collector": {
6
+ "type": "Direct",
7
+ "requested": "[6.0.4, )",
8
+ "resolved": "6.0.4",
9
+ "contentHash": "lkhqpF8Pu2Y7IiN7OntbsTtdbpR1syMsm2F3IgX6ootA4ffRqWL5jF7XipHuZQTdVuWG/gVAAcf8mjk8Tz0xPg=="
10
+ },
11
+ "Microsoft.AspNetCore.Mvc.Testing": {
12
+ "type": "Direct",
13
+ "requested": "[10.0.15, )",
14
+ "resolved": "10.0.15",
15
+ "contentHash": "qwZzLn60Ish1cTZoNDcbS/P8zjUjLGtZUye18KwB8n05eea5WGBgYO/HAxZWLRcFVkSD2ZXY496e1YX7Go4ndQ==",
16
+ "dependencies": {
17
+ "Microsoft.AspNetCore.TestHost": "10.0.15",
18
+ "Microsoft.Extensions.DependencyModel": "10.0.15",
19
+ "Microsoft.Extensions.Hosting": "10.0.15"
20
+ }
21
+ },
22
+ "Microsoft.NET.Test.Sdk": {
23
+ "type": "Direct",
24
+ "requested": "[17.14.1, )",
25
+ "resolved": "17.14.1",
26
+ "contentHash": "HJKqKOE+vshXra2aEHpi2TlxYX7Z9VFYkr+E5rwEvHC8eIXiyO+K9kNm8vmNom3e2rA56WqxU+/N9NJlLGXsJQ==",
27
+ "dependencies": {
28
+ "Microsoft.CodeCoverage": "17.14.1",
29
+ "Microsoft.TestPlatform.TestHost": "17.14.1"
30
+ }
31
+ },
32
+ "xunit": {
33
+ "type": "Direct",
34
+ "requested": "[2.9.3, )",
35
+ "resolved": "2.9.3",
36
+ "contentHash": "TlXQBinK35LpOPKHAqbLY4xlEen9TBafjs0V5KnA4wZsoQLQJiirCR4CbIXvOH8NzkW4YeJKP5P/Bnrodm0h9Q==",
37
+ "dependencies": {
38
+ "xunit.analyzers": "1.18.0",
39
+ "xunit.assert": "2.9.3",
40
+ "xunit.core": "[2.9.3]"
41
+ }
42
+ },
43
+ "xunit.runner.visualstudio": {
44
+ "type": "Direct",
45
+ "requested": "[3.1.4, )",
46
+ "resolved": "3.1.4",
47
+ "contentHash": "5mj99LvCqrq3CNi06xYdyIAXOEh+5b33F2nErCzI5zWiDdLHXiPXEWFSUAF8zlIv0ZWqjZNCwHTQeAPYbF3pCg=="
48
+ },
49
+ "Microsoft.AspNetCore.OpenApi": {
50
+ "type": "Transitive",
51
+ "resolved": "10.0.15",
52
+ "contentHash": "pptFDPMWyPDDtt68s/HsvLYeHJ0WQtntjyve6ztp/5MrLOEzdZvafYIyWSeErtexIGaB8XGYCnnUK8T/Vn4MwQ==",
53
+ "dependencies": {
54
+ "Microsoft.OpenApi": "[2.13.0, 3.0.0)"
55
+ }
56
+ },
57
+ "Microsoft.AspNetCore.TestHost": {
58
+ "type": "Transitive",
59
+ "resolved": "10.0.15",
60
+ "contentHash": "mQWzQqmw2DX3Ofm9S46WkFVRFmKyy6bMwsToAoDdQcK1paSQ6LT8BY3O4VXm1dRTSEIn/4x5A+Q4tzD9jgwJ9Q=="
61
+ },
62
+ "Microsoft.CodeCoverage": {
63
+ "type": "Transitive",
64
+ "resolved": "17.14.1",
65
+ "contentHash": "pmTrhfFIoplzFVbhVwUquT+77CbGH+h4/3mBpdmIlYtBi9nAB+kKI6dN3A/nV4DFi3wLLx/BlHIPK+MkbQ6Tpg=="
66
+ },
67
+ "Microsoft.EntityFrameworkCore": {
68
+ "type": "Transitive",
69
+ "resolved": "10.0.5",
70
+ "contentHash": "9tNBmK3EpYVGRQLiqP+bqK2m+TD0Gv//4vCzR7ZOgl4FWzCFyOpYdIVka13M4kcBdPdSJcs3wbHr3rmzOqbIMA==",
71
+ "dependencies": {
72
+ "Microsoft.EntityFrameworkCore.Abstractions": "10.0.5",
73
+ "Microsoft.EntityFrameworkCore.Analyzers": "10.0.5",
74
+ "Microsoft.Extensions.Caching.Memory": "10.0.5",
75
+ "Microsoft.Extensions.Logging": "10.0.5"
76
+ }
77
+ },
78
+ "Microsoft.EntityFrameworkCore.Abstractions": {
79
+ "type": "Transitive",
80
+ "resolved": "10.0.5",
81
+ "contentHash": "32c58Rnm47Qvhimawf67KO9PytgPz3QoWye7Abapt0Yocw/JnzMiSNj/pRoIKyn8Jxypkv86zxKD4Q/zNTc0Ag=="
82
+ },
83
+ "Microsoft.EntityFrameworkCore.Analyzers": {
84
+ "type": "Transitive",
85
+ "resolved": "10.0.5",
86
+ "contentHash": "ipC4u1VojgEfoIZhtbS2Sx5IluJTP/Jf1hz3yGsxGBgSukYY/CquI6rAjxn5H58CZgVn36qcuPPtNMwZ0AUzMg=="
87
+ },
88
+ "Microsoft.EntityFrameworkCore.InMemory": {
89
+ "type": "Transitive",
90
+ "resolved": "10.0.5",
91
+ "contentHash": "zz4THzlDOrJ7fKU2YUOzQFs2LJ9DgOSr5xFXcDdoD59el73MTQLtQQOAJxQ94F4XDegyL9+2sePSQGdcU25ZxQ==",
92
+ "dependencies": {
93
+ "Microsoft.EntityFrameworkCore": "10.0.5",
94
+ "Microsoft.Extensions.Caching.Memory": "10.0.5",
95
+ "Microsoft.Extensions.Logging": "10.0.5"
96
+ }
97
+ },
98
+ "Microsoft.EntityFrameworkCore.Relational": {
99
+ "type": "Transitive",
100
+ "resolved": "10.0.0",
101
+ "contentHash": "A3MX1ee7RDxWCUdx/KqP+74fbksz0UIhkVZh56YHvbPkEKsffCXgHU3LGkRDwqR/MrBNWLCWC/IVX79tzM30ZA==",
102
+ "dependencies": {
103
+ "Microsoft.EntityFrameworkCore": "10.0.0",
104
+ "Microsoft.Extensions.Caching.Memory": "10.0.0",
105
+ "Microsoft.Extensions.Configuration.Abstractions": "10.0.0",
106
+ "Microsoft.Extensions.Logging": "10.0.0"
107
+ }
108
+ },
109
+ "Microsoft.Extensions.Caching.Abstractions": {
110
+ "type": "Transitive",
111
+ "resolved": "10.0.5",
112
+ "contentHash": "k/QDdQ94/0Shi0KfU+e12m73jfQo+3JpErTtgpZfsCIqkvdEEO0XIx6R+iTbN55rNPaNhOqNY4/sB+jZ8XxVPw==",
113
+ "dependencies": {
114
+ "Microsoft.Extensions.Primitives": "10.0.5"
115
+ }
116
+ },
117
+ "Microsoft.Extensions.Caching.Memory": {
118
+ "type": "Transitive",
119
+ "resolved": "10.0.5",
120
+ "contentHash": "jUEXmkBUPdOS/MP9areK/sbKhdklq9+tEhvwfxGalZVnmyLUO5rrheNNutUBtvbZ7J8ECkG7/r2KXi/IFC06cA==",
121
+ "dependencies": {
122
+ "Microsoft.Extensions.Caching.Abstractions": "10.0.5",
123
+ "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.5",
124
+ "Microsoft.Extensions.Logging.Abstractions": "10.0.5",
125
+ "Microsoft.Extensions.Options": "10.0.5",
126
+ "Microsoft.Extensions.Primitives": "10.0.5"
127
+ }
128
+ },
129
+ "Microsoft.Extensions.Configuration": {
130
+ "type": "Transitive",
131
+ "resolved": "10.0.15",
132
+ "contentHash": "XwGgzRlryUvICZbY/oNlR1MbFDYg6gePDoW38RyUis8vEAVKIHvMy9di5dujK6dObbUOQJpHtrjUZ2rU0V7RvQ==",
133
+ "dependencies": {
134
+ "Microsoft.Extensions.Configuration.Abstractions": "10.0.15",
135
+ "Microsoft.Extensions.Primitives": "10.0.15"
136
+ }
137
+ },
138
+ "Microsoft.Extensions.Configuration.Abstractions": {
139
+ "type": "Transitive",
140
+ "resolved": "10.0.15",
141
+ "contentHash": "0vztB/x4zuR/KT82cgTiiw9DqigfT1FjBgq9FjEFvJUgeacjTG4e7hWD2PCRXzL2kWWl/vBAwtXddIRV3AIU6Q==",
142
+ "dependencies": {
143
+ "Microsoft.Extensions.Primitives": "10.0.15"
144
+ }
145
+ },
146
+ "Microsoft.Extensions.Configuration.Binder": {
147
+ "type": "Transitive",
148
+ "resolved": "10.0.15",
149
+ "contentHash": "VOjlZlv9b3woS1TcI8Ts1r2IsmsT7hqkx7o2sMSNzE5fPBGKfhJszlEIhtQVzUz4H8Uw/7Jtuj9x7wGlCUsi2A==",
150
+ "dependencies": {
151
+ "Microsoft.Extensions.Configuration": "10.0.15",
152
+ "Microsoft.Extensions.Configuration.Abstractions": "10.0.15"
153
+ }
154
+ },
155
+ "Microsoft.Extensions.Configuration.CommandLine": {
156
+ "type": "Transitive",
157
+ "resolved": "10.0.15",
158
+ "contentHash": "5m3XbJnN5g8WMEIqrqpTK8/Xeq+dnmINaRvDUDCWN/J+v9795p/D9brr7597sBnmp2Hqg7/g9i6KbXSWjHQ1cA==",
159
+ "dependencies": {
160
+ "Microsoft.Extensions.Configuration": "10.0.15",
161
+ "Microsoft.Extensions.Configuration.Abstractions": "10.0.15"
162
+ }
163
+ },
164
+ "Microsoft.Extensions.Configuration.EnvironmentVariables": {
165
+ "type": "Transitive",
166
+ "resolved": "10.0.15",
167
+ "contentHash": "Acq0muvpN1QfTGV5eJiuutef5Gd1nWxa9aE6CzlmAGJdgPooR/glwkjY4cTWWZgXSVpfaOEe8AQjRO6xdDrDyQ==",
168
+ "dependencies": {
169
+ "Microsoft.Extensions.Configuration": "10.0.15",
170
+ "Microsoft.Extensions.Configuration.Abstractions": "10.0.15"
171
+ }
172
+ },
173
+ "Microsoft.Extensions.Configuration.FileExtensions": {
174
+ "type": "Transitive",
175
+ "resolved": "10.0.15",
176
+ "contentHash": "zA3VutAch9qe/WcKfVRXnivIMdAeZsxWVbFDO6Z9P6NQdNH73SXrULoH7PYfdC+CBsLiMKvx6z4oTjAvuKPGpA==",
177
+ "dependencies": {
178
+ "Microsoft.Extensions.Configuration": "10.0.15",
179
+ "Microsoft.Extensions.Configuration.Abstractions": "10.0.15",
180
+ "Microsoft.Extensions.FileProviders.Abstractions": "10.0.15",
181
+ "Microsoft.Extensions.FileProviders.Physical": "10.0.15",
182
+ "Microsoft.Extensions.Primitives": "10.0.15"
183
+ }
184
+ },
185
+ "Microsoft.Extensions.Configuration.Json": {
186
+ "type": "Transitive",
187
+ "resolved": "10.0.15",
188
+ "contentHash": "5+FOOsKnE3Vf/q2yIHL+gljoJbmT8WB9vuXeJfXNav14OBCmn5wuQM6Fs3y0mxmRvQVRQvfzmpSvaXRXJBfDdw==",
189
+ "dependencies": {
190
+ "Microsoft.Extensions.Configuration": "10.0.15",
191
+ "Microsoft.Extensions.Configuration.Abstractions": "10.0.15",
192
+ "Microsoft.Extensions.Configuration.FileExtensions": "10.0.15",
193
+ "Microsoft.Extensions.FileProviders.Abstractions": "10.0.15"
194
+ }
195
+ },
196
+ "Microsoft.Extensions.Configuration.UserSecrets": {
197
+ "type": "Transitive",
198
+ "resolved": "10.0.15",
199
+ "contentHash": "P4gX89ewSSE4UElThXKUmapXpRb3RwaL2jDqvUy9wn30T757mxozGV+xb5H4Fdm01dMHXTzA/Zj1NUInltqCPg==",
200
+ "dependencies": {
201
+ "Microsoft.Extensions.Configuration.Abstractions": "10.0.15",
202
+ "Microsoft.Extensions.Configuration.Json": "10.0.15",
203
+ "Microsoft.Extensions.FileProviders.Abstractions": "10.0.15",
204
+ "Microsoft.Extensions.FileProviders.Physical": "10.0.15"
205
+ }
206
+ },
207
+ "Microsoft.Extensions.DependencyInjection": {
208
+ "type": "Transitive",
209
+ "resolved": "10.0.15",
210
+ "contentHash": "7DUIgHeJpBs1RG0hYwWDOImXP03+uPrV5KtyUPxrZsiWadlg95aYijNKCZ2j2UmufQAmN6XEQfx4B/J6TuE7lg==",
211
+ "dependencies": {
212
+ "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.15"
213
+ }
214
+ },
215
+ "Microsoft.Extensions.DependencyInjection.Abstractions": {
216
+ "type": "Transitive",
217
+ "resolved": "10.0.15",
218
+ "contentHash": "KczR2nV7ooeimtZxAawPht/FJUHnX+uoWy7y4pba+iaRQBwUM2NwtiqZg+/FpZDHUf2RkkzveGkhMTWDNzmDUA=="
219
+ },
220
+ "Microsoft.Extensions.DependencyModel": {
221
+ "type": "Transitive",
222
+ "resolved": "10.0.15",
223
+ "contentHash": "087ome/PndkacsUGmB6OoTyhQ3UwoSRqv9odKbvdD4h3o6TqSIVIK/v1WaNKZ/oiMUVuMcV0l2yXdt55RHRxGw=="
224
+ },
225
+ "Microsoft.Extensions.Diagnostics": {
226
+ "type": "Transitive",
227
+ "resolved": "10.0.15",
228
+ "contentHash": "+FK6gPJDZ+8abQ7WAXUSxK7jgwhATzHlt3TCiO/paVwx9I8VsdHHoapugUUZNHr0Re2UFDoxpPpCp5kp6TX5iQ==",
229
+ "dependencies": {
230
+ "Microsoft.Extensions.Configuration": "10.0.15",
231
+ "Microsoft.Extensions.Diagnostics.Abstractions": "10.0.15",
232
+ "Microsoft.Extensions.Options.ConfigurationExtensions": "10.0.15"
233
+ }
234
+ },
235
+ "Microsoft.Extensions.Diagnostics.Abstractions": {
236
+ "type": "Transitive",
237
+ "resolved": "10.0.15",
238
+ "contentHash": "oD1xl7uoHGnVuCEo3bjj1W3cVTMe55QVdkeis9YYdcnLfU42X58XTx6A1Sl7kiqp5LbzVrUvdy7F/mNKaOnpLg==",
239
+ "dependencies": {
240
+ "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.15",
241
+ "Microsoft.Extensions.Options": "10.0.15"
242
+ }
243
+ },
244
+ "Microsoft.Extensions.FileProviders.Abstractions": {
245
+ "type": "Transitive",
246
+ "resolved": "10.0.15",
247
+ "contentHash": "1RSYAUtJeb76m2T0u5BbvwKY3s+ge9fT3cj1odvo5Bm+frc7pU9RSeXeMIlYHV8GjpqLdv2neeeomz010yYL8A==",
248
+ "dependencies": {
249
+ "Microsoft.Extensions.Primitives": "10.0.15"
250
+ }
251
+ },
252
+ "Microsoft.Extensions.FileProviders.Physical": {
253
+ "type": "Transitive",
254
+ "resolved": "10.0.15",
255
+ "contentHash": "2hOXuMZ//9h6foRT4KzcrooXNpf9GC6uk1p41kRHiSiVNNHwEeLywqafzHv+rjxKcN5+Ld9ZjnZayxRCgp+nAQ==",
256
+ "dependencies": {
257
+ "Microsoft.Extensions.FileProviders.Abstractions": "10.0.15",
258
+ "Microsoft.Extensions.FileSystemGlobbing": "10.0.15",
259
+ "Microsoft.Extensions.Primitives": "10.0.15"
260
+ }
261
+ },
262
+ "Microsoft.Extensions.FileSystemGlobbing": {
263
+ "type": "Transitive",
264
+ "resolved": "10.0.15",
265
+ "contentHash": "2Vc8lDEufnkNpp/8Q3mP77Hz2BnPhuKzj9MiMTINiRJ23gCudPewm3yQdxvBbWpZZw68PxQ61CZMo0rS/WwDAg=="
266
+ },
267
+ "Microsoft.Extensions.Hosting": {
268
+ "type": "Transitive",
269
+ "resolved": "10.0.15",
270
+ "contentHash": "czDs4dncBxTiQ9geO0UWRx6RTfLugUT0WLsIAH7DEw/ebMpkIrLsnfyr7gG9TX0q4qM2GnTtxzxiZvGv+eqvFg==",
271
+ "dependencies": {
272
+ "Microsoft.Extensions.Configuration": "10.0.15",
273
+ "Microsoft.Extensions.Configuration.Abstractions": "10.0.15",
274
+ "Microsoft.Extensions.Configuration.Binder": "10.0.15",
275
+ "Microsoft.Extensions.Configuration.CommandLine": "10.0.15",
276
+ "Microsoft.Extensions.Configuration.EnvironmentVariables": "10.0.15",
277
+ "Microsoft.Extensions.Configuration.FileExtensions": "10.0.15",
278
+ "Microsoft.Extensions.Configuration.Json": "10.0.15",
279
+ "Microsoft.Extensions.Configuration.UserSecrets": "10.0.15",
280
+ "Microsoft.Extensions.DependencyInjection": "10.0.15",
281
+ "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.15",
282
+ "Microsoft.Extensions.Diagnostics": "10.0.15",
283
+ "Microsoft.Extensions.FileProviders.Abstractions": "10.0.15",
284
+ "Microsoft.Extensions.FileProviders.Physical": "10.0.15",
285
+ "Microsoft.Extensions.Hosting.Abstractions": "10.0.15",
286
+ "Microsoft.Extensions.Logging": "10.0.15",
287
+ "Microsoft.Extensions.Logging.Abstractions": "10.0.15",
288
+ "Microsoft.Extensions.Logging.Configuration": "10.0.15",
289
+ "Microsoft.Extensions.Logging.Console": "10.0.15",
290
+ "Microsoft.Extensions.Logging.Debug": "10.0.15",
291
+ "Microsoft.Extensions.Logging.EventLog": "10.0.15",
292
+ "Microsoft.Extensions.Logging.EventSource": "10.0.15",
293
+ "Microsoft.Extensions.Options": "10.0.15"
294
+ }
295
+ },
296
+ "Microsoft.Extensions.Hosting.Abstractions": {
297
+ "type": "Transitive",
298
+ "resolved": "10.0.15",
299
+ "contentHash": "s/JBiwR3F/vKRCbe6WO5b6t6or1ISaZ3rllchcw6/FSWZmK8s2fAzD7elrZo7592FnMD1fhGoG/wuJL1YYtVXw==",
300
+ "dependencies": {
301
+ "Microsoft.Extensions.Configuration.Abstractions": "10.0.15",
302
+ "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.15",
303
+ "Microsoft.Extensions.Diagnostics.Abstractions": "10.0.15",
304
+ "Microsoft.Extensions.FileProviders.Abstractions": "10.0.15",
305
+ "Microsoft.Extensions.Logging.Abstractions": "10.0.15"
306
+ }
307
+ },
308
+ "Microsoft.Extensions.Logging": {
309
+ "type": "Transitive",
310
+ "resolved": "10.0.15",
311
+ "contentHash": "Vya6t+hqCLeoZsbsJo5FzrEoOTK7syIeuaxLl3F8QagD3sXVxAQgmVEq5+jaA4gQ4yreg27fZ8gJH96bWtxoBQ==",
312
+ "dependencies": {
313
+ "Microsoft.Extensions.DependencyInjection": "10.0.15",
314
+ "Microsoft.Extensions.Logging.Abstractions": "10.0.15",
315
+ "Microsoft.Extensions.Options": "10.0.15"
316
+ }
317
+ },
318
+ "Microsoft.Extensions.Logging.Abstractions": {
319
+ "type": "Transitive",
320
+ "resolved": "10.0.15",
321
+ "contentHash": "s5xzXGWqqlmmThMnQ1rnmL7QLwbluB2JqX9NaK8C9HPuHzN7kfmiddTRbwts5rQssATkh2B18RrofjeUD8g14A==",
322
+ "dependencies": {
323
+ "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.15"
324
+ }
325
+ },
326
+ "Microsoft.Extensions.Logging.Configuration": {
327
+ "type": "Transitive",
328
+ "resolved": "10.0.15",
329
+ "contentHash": "Km6Ml1/nu0T/f80Btonxf9CWmP/BXI84aJwC5fUbCxRArJ0lH++0ADQrjErZvsSIrKhusTkTSGBChKy2fYAF4w==",
330
+ "dependencies": {
331
+ "Microsoft.Extensions.Configuration": "10.0.15",
332
+ "Microsoft.Extensions.Configuration.Abstractions": "10.0.15",
333
+ "Microsoft.Extensions.Configuration.Binder": "10.0.15",
334
+ "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.15",
335
+ "Microsoft.Extensions.Logging": "10.0.15",
336
+ "Microsoft.Extensions.Logging.Abstractions": "10.0.15",
337
+ "Microsoft.Extensions.Options": "10.0.15",
338
+ "Microsoft.Extensions.Options.ConfigurationExtensions": "10.0.15"
339
+ }
340
+ },
341
+ "Microsoft.Extensions.Logging.Console": {
342
+ "type": "Transitive",
343
+ "resolved": "10.0.15",
344
+ "contentHash": "3PPlRrxB8B5KH902aSRCm0J9jjbYp24qet1+NZabAPaW0wIxRUL5jyB+8l2b85Y8VfsN2dhnutrdEkVvstVr5A==",
345
+ "dependencies": {
346
+ "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.15",
347
+ "Microsoft.Extensions.Logging": "10.0.15",
348
+ "Microsoft.Extensions.Logging.Abstractions": "10.0.15",
349
+ "Microsoft.Extensions.Logging.Configuration": "10.0.15",
350
+ "Microsoft.Extensions.Options": "10.0.15"
351
+ }
352
+ },
353
+ "Microsoft.Extensions.Logging.Debug": {
354
+ "type": "Transitive",
355
+ "resolved": "10.0.15",
356
+ "contentHash": "vPvEpc4hq9maNykhz/PyCREgm82TvUJTzSM9Y3vXwGYS7AGSXvC7HkXiA9KM1nFhuHP9w4mHS7ZAhIUTxCwigA==",
357
+ "dependencies": {
358
+ "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.15",
359
+ "Microsoft.Extensions.Logging": "10.0.15",
360
+ "Microsoft.Extensions.Logging.Abstractions": "10.0.15"
361
+ }
362
+ },
363
+ "Microsoft.Extensions.Logging.EventLog": {
364
+ "type": "Transitive",
365
+ "resolved": "10.0.15",
366
+ "contentHash": "U1I57mk4Nu4YRttw50tOprKxBGEjfv7Nm51MwnW2rUeVuO6UyFXlF0CV9aC+1npkShmkRYIj9C77KX2+/1jFSQ==",
367
+ "dependencies": {
368
+ "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.15",
369
+ "Microsoft.Extensions.Logging": "10.0.15",
370
+ "Microsoft.Extensions.Logging.Abstractions": "10.0.15",
371
+ "Microsoft.Extensions.Options": "10.0.15",
372
+ "System.Diagnostics.EventLog": "10.0.15"
373
+ }
374
+ },
375
+ "Microsoft.Extensions.Logging.EventSource": {
376
+ "type": "Transitive",
377
+ "resolved": "10.0.15",
378
+ "contentHash": "EbkZxaOGBDXqqdsIh7kGuFxqDoiLDWwgqG/WiqM4kpSJ9T9lSIyE6724R13T0ihhAc9VShTYZvjp9ug+rZ4GBQ==",
379
+ "dependencies": {
380
+ "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.15",
381
+ "Microsoft.Extensions.Logging": "10.0.15",
382
+ "Microsoft.Extensions.Logging.Abstractions": "10.0.15",
383
+ "Microsoft.Extensions.Options": "10.0.15",
384
+ "Microsoft.Extensions.Primitives": "10.0.15"
385
+ }
386
+ },
387
+ "Microsoft.Extensions.Options": {
388
+ "type": "Transitive",
389
+ "resolved": "10.0.15",
390
+ "contentHash": "vF3ghTChyyX3BDqRFUurk+fn8vZySFmWHm4vIeeOQbBrjnPqWrr5ceyCb1jrGpMasYdcK9IuJskLVQZWBfNxCg==",
391
+ "dependencies": {
392
+ "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.15",
393
+ "Microsoft.Extensions.Primitives": "10.0.15"
394
+ }
395
+ },
396
+ "Microsoft.Extensions.Options.ConfigurationExtensions": {
397
+ "type": "Transitive",
398
+ "resolved": "10.0.15",
399
+ "contentHash": "yQX7dFAHm9xKBh2TLiA3aTrmOkgEeMRoS7UM/XuVy3CpddaluUXDIMiM8na5q6JMesH8zt3bKIDu/3hkEhVo7Q==",
400
+ "dependencies": {
401
+ "Microsoft.Extensions.Configuration.Abstractions": "10.0.15",
402
+ "Microsoft.Extensions.Configuration.Binder": "10.0.15",
403
+ "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.15",
404
+ "Microsoft.Extensions.Options": "10.0.15",
405
+ "Microsoft.Extensions.Primitives": "10.0.15"
406
+ }
407
+ },
408
+ "Microsoft.Extensions.Primitives": {
409
+ "type": "Transitive",
410
+ "resolved": "10.0.15",
411
+ "contentHash": "NjzLiZ1/bYx8VwLqHxNnCfDTQQbCY7P1dwT7JM5FiANiaSfI6UKHi0/sla+jyRbOIbPAMJkhWQ/9pH3tMf+IrQ=="
412
+ },
413
+ "Microsoft.OpenApi": {
414
+ "type": "Transitive",
415
+ "resolved": "2.13.0",
416
+ "contentHash": "wavlgBYnn7uU/ZGhFHNl0UlDOlwz+6KRduKkqdCCbfxxANaQ1rmBpETVGdwXanAM6twQOxsvI3CBEGF4OScbeQ=="
417
+ },
418
+ "Microsoft.TestPlatform.ObjectModel": {
419
+ "type": "Transitive",
420
+ "resolved": "17.14.1",
421
+ "contentHash": "xTP1W6Mi6SWmuxd3a+jj9G9UoC850WGwZUps1Wah9r1ZxgXhdJfj1QqDLJkFjHDCvN42qDL2Ps5KjQYWUU0zcQ=="
422
+ },
423
+ "Microsoft.TestPlatform.TestHost": {
424
+ "type": "Transitive",
425
+ "resolved": "17.14.1",
426
+ "contentHash": "d78LPzGKkJwsJXAQwsbJJ7LE7D1wB+rAyhHHAaODF+RDSQ0NgMjDFkSA1Djw18VrxO76GlKAjRUhl+H8NL8Z+Q==",
427
+ "dependencies": {
428
+ "Microsoft.TestPlatform.ObjectModel": "17.14.1",
429
+ "Newtonsoft.Json": "13.0.3"
430
+ }
431
+ },
432
+ "Newtonsoft.Json": {
433
+ "type": "Transitive",
434
+ "resolved": "13.0.3",
435
+ "contentHash": "HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ=="
436
+ },
437
+ "Npgsql": {
438
+ "type": "Transitive",
439
+ "resolved": "10.0.0",
440
+ "contentHash": "xZAYhPOU2rUIFpV48xsqhCx9vXs6Y+0jX2LCoSEfDFYMw9jtAOUk3iQsCnDLrFIv9NT3JGMihn7nnuZsPKqJmA==",
441
+ "dependencies": {
442
+ "Microsoft.Extensions.Logging.Abstractions": "10.0.0"
443
+ }
444
+ },
445
+ "Npgsql.EntityFrameworkCore.PostgreSQL": {
446
+ "type": "Transitive",
447
+ "resolved": "10.0.0",
448
+ "contentHash": "E2+uSWxSB8LdsUVwPaqRWOcGOP92biry2JEwc0KJMdLJF+aZdczeIdEXVwEyv4nSVMQJH0o8tLhyAMiR6VF0lw==",
449
+ "dependencies": {
450
+ "Microsoft.EntityFrameworkCore": "[10.0.0, 11.0.0)",
451
+ "Microsoft.EntityFrameworkCore.Relational": "[10.0.0, 11.0.0)",
452
+ "Npgsql": "10.0.0"
453
+ }
454
+ },
455
+ "System.Diagnostics.EventLog": {
456
+ "type": "Transitive",
457
+ "resolved": "10.0.15",
458
+ "contentHash": "d1ynyO/BvCyi8SI1m6/xASazxrZm20V/mTDJay4qmL947RsA5MEiMk1JWvafw6JX005VVBAHvjWQP1dRRBvYKw=="
459
+ },
460
+ "xunit.abstractions": {
461
+ "type": "Transitive",
462
+ "resolved": "2.0.3",
463
+ "contentHash": "pot1I4YOxlWjIb5jmwvvQNbTrZ3lJQ+jUGkGjWE3hEFM0l5gOnBWS+H3qsex68s5cO52g+44vpGzhAt+42vwKg=="
464
+ },
465
+ "xunit.analyzers": {
466
+ "type": "Transitive",
467
+ "resolved": "1.18.0",
468
+ "contentHash": "OtFMHN8yqIcYP9wcVIgJrq01AfTxijjAqVDy/WeQVSyrDC1RzBWeQPztL49DN2syXRah8TYnfvk035s7L95EZQ=="
469
+ },
470
+ "xunit.assert": {
471
+ "type": "Transitive",
472
+ "resolved": "2.9.3",
473
+ "contentHash": "/Kq28fCE7MjOV42YLVRAJzRF0WmEqsmflm0cfpMjGtzQ2lR5mYVj1/i0Y8uDAOLczkL3/jArrwehfMD0YogMAA=="
474
+ },
475
+ "xunit.core": {
476
+ "type": "Transitive",
477
+ "resolved": "2.9.3",
478
+ "contentHash": "BiAEvqGvyme19wE0wTKdADH+NloYqikiU0mcnmiNyXaF9HyHmE6sr/3DC5vnBkgsWaE6yPyWszKSPSApWdRVeQ==",
479
+ "dependencies": {
480
+ "xunit.extensibility.core": "[2.9.3]",
481
+ "xunit.extensibility.execution": "[2.9.3]"
482
+ }
483
+ },
484
+ "xunit.extensibility.core": {
485
+ "type": "Transitive",
486
+ "resolved": "2.9.3",
487
+ "contentHash": "kf3si0YTn2a8J8eZNb+zFpwfoyvIrQ7ivNk5ZYA5yuYk1bEtMe4DxJ2CF/qsRgmEnDr7MnW1mxylBaHTZ4qErA==",
488
+ "dependencies": {
489
+ "xunit.abstractions": "2.0.3"
490
+ }
491
+ },
492
+ "xunit.extensibility.execution": {
493
+ "type": "Transitive",
494
+ "resolved": "2.9.3",
495
+ "contentHash": "yMb6vMESlSrE3Wfj7V6cjQ3S4TXdXpRqYeNEI3zsX31uTsGMJjEw6oD5F5u1cHnMptjhEECnmZSsPxB6ChZHDQ==",
496
+ "dependencies": {
497
+ "xunit.extensibility.core": "[2.9.3]"
498
+ }
499
+ },
500
+ "app": {
501
+ "type": "Project",
502
+ "dependencies": {
503
+ "Microsoft.AspNetCore.OpenApi": "[10.0.15, )",
504
+ "Microsoft.EntityFrameworkCore": "[10.0.5, )",
505
+ "Microsoft.EntityFrameworkCore.InMemory": "[10.0.5, )",
506
+ "Npgsql.EntityFrameworkCore.PostgreSQL": "[10.0.0, )"
507
+ }
508
+ }
509
+ }
510
+ }
511
+ }
@@ -0,0 +1 @@
1
+ using Microsoft.EntityFrameworkCore;
@@ -0,0 +1,3 @@
1
+ ## Database
2
+
3
+ Postgres through EF Core + Npgsql. Set `DATABASE_URL` (see `.env.example`). Schema for tests uses `EnsureCreated` against a throwaway database; apply real migrations yourself with `dotnet ef` when you add them — the Yosr Seed gate does not run migrations.
@@ -0,0 +1,3 @@
1
+ builder.Services.AddDbContext<AppDbContext>(options =>
2
+ options.UseNpgsql(ToNpgsql(settings.DatabaseUrl)));
3
+ builder.Services.AddScoped<INoteStore, EfNoteStore>();
@@ -0,0 +1,19 @@
1
+ # Build
2
+ FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
3
+ WORKDIR /src
4
+ COPY Directory.Build.props global.json ./
5
+ COPY src/App/App.csproj src/App/
6
+ COPY src/App/packages.lock.json src/App/
7
+ RUN dotnet restore src/App/App.csproj --locked-mode
8
+ COPY src/App/ src/App/
9
+ RUN dotnet publish src/App/App.csproj -c Release -o /app/publish --no-restore
10
+
11
+ # Runtime
12
+ FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS final
13
+ WORKDIR /app
14
+ RUN groupadd --system app && useradd --system --gid app --create-home app
15
+ COPY --from=build /app/publish .
16
+ USER app
17
+ ENV ASPNETCORE_URLS=http://+:8080
18
+ EXPOSE 8080
19
+ ENTRYPOINT ["dotnet", "App.dll"]
@@ -0,0 +1,12 @@
1
+ # @seed:anchor compose-header
2
+ services:
3
+ app:
4
+ build: .
5
+ ports:
6
+ - "8080:8080"
7
+ environment:
8
+ ASPNETCORE_URLS: http://+:8080
9
+ # @seed:anchor app-env
10
+ # @seed:anchor app-config
11
+ # @seed:anchor services
12
+ # @seed:anchor compose-volumes
@@ -0,0 +1,3 @@
1
+ ## Docker
2
+
3
+ Multi-stage image: SDK build → `mcr.microsoft.com/dotnet/aspnet:10.0`, non-root user. `docker compose up --build` starts the API on port 8080.
@@ -0,0 +1,3 @@
1
+ depends_on:
2
+ db:
3
+ condition: service_healthy
@@ -0,0 +1 @@
1
+ DATABASE_URL: postgresql://postgres:postgres@db:5432/postgres
@@ -0,0 +1 @@
1
+ # App + Postgres for local Docker. Gate uses its own throwaway Postgres when needed.
@@ -0,0 +1,2 @@
1
+ volumes:
2
+ pgdata:
@@ -0,0 +1,13 @@
1
+ db:
2
+ image: postgres:18.6-alpine
3
+ environment:
4
+ POSTGRES_PASSWORD: postgres
5
+ ports:
6
+ - "5432:5432"
7
+ volumes:
8
+ - pgdata:/var/lib/postgresql/data
9
+ healthcheck:
10
+ test: ["CMD-SHELL", "pg_isready -U postgres"]
11
+ interval: 5s
12
+ timeout: 5s
13
+ retries: 10
@@ -0,0 +1,3 @@
1
+ ## Docker
2
+
3
+ Multi-stage image: SDK build → `mcr.microsoft.com/dotnet/aspnet:10.0`, non-root user. Compose starts Postgres and the API; `DATABASE_URL` points at the `db` service.
@@ -0,0 +1,52 @@
1
+ using App.Shared;
2
+
3
+ namespace App.Features.Notes;
4
+
5
+ public static class NotesEndpoints
6
+ {
7
+ public static RouteGroupBuilder MapNotesEndpoints(this IEndpointRouteBuilder app)
8
+ {
9
+ var group = app.MapGroup("/notes");
10
+ // @seed:anchor notes-group
11
+
12
+ group.MapGet("/", async (NotesService notes, CancellationToken cancellationToken) =>
13
+ {
14
+ var items = await notes.ListAsync(cancellationToken);
15
+ return Results.Ok(items.Select(NoteResponse.From));
16
+ });
17
+
18
+ group.MapGet("/{id:int}", async (int id, NotesService notes, CancellationToken cancellationToken) =>
19
+ {
20
+ return await notes.GetAsync(id, cancellationToken) switch
21
+ {
22
+ Result<Note>.Ok ok => Results.Ok(NoteResponse.From(ok.Value)),
23
+ Result<Note>.Err err => Results.NotFound(new { detail = err.Message }),
24
+ _ => Results.StatusCode(500),
25
+ };
26
+ });
27
+
28
+ group.MapPost("/", async (NoteCreate body, NotesService notes, CancellationToken cancellationToken) =>
29
+ {
30
+ return await notes.CreateAsync(body, cancellationToken) switch
31
+ {
32
+ Result<Note>.Ok ok => Results.Created($"/notes/{ok.Value.Id}", NoteResponse.From(ok.Value)),
33
+ Result<Note>.Err err => Results.BadRequest(new { detail = err.Message }),
34
+ _ => Results.StatusCode(500),
35
+ };
36
+ });
37
+
38
+ group.MapPatch("/{id:int}", async (int id, NoteUpdate body, NotesService notes, CancellationToken cancellationToken) =>
39
+ {
40
+ return await notes.UpdateAsync(id, body, cancellationToken) switch
41
+ {
42
+ Result<Note>.Ok ok => Results.Ok(NoteResponse.From(ok.Value)),
43
+ Result<Note>.Err err when err.Message.Contains("not found", StringComparison.OrdinalIgnoreCase)
44
+ => Results.NotFound(new { detail = err.Message }),
45
+ Result<Note>.Err err => Results.BadRequest(new { detail = err.Message }),
46
+ _ => Results.StatusCode(500),
47
+ };
48
+ });
49
+
50
+ return group;
51
+ }
52
+ }