@foro-sh/foro 0.9.3 → 0.11.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.
- package/README.md +6 -4
- package/dist/_generated-manifest-cases.d.ts +360 -66
- package/dist/_generated-manifest-cases.d.ts.map +1 -1
- package/dist/_generated-manifest-cases.js +386 -66
- package/dist/manifest-cases.d.ts +6 -4
- package/dist/manifest-cases.d.ts.map +1 -1
- package/dist/manifest-cases.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -4,16 +4,18 @@ This package is the TypeScript SDK for Foro.
|
|
|
4
4
|
|
|
5
5
|
## `@foro-sh/foro/manifest-cases`
|
|
6
6
|
|
|
7
|
-
The shared
|
|
7
|
+
The shared project-config validation table, as typed data. Foro's Python
|
|
8
8
|
`_manifest.py` is a port of [foro-sh/platform]'s
|
|
9
9
|
`apps/api/src/services/manifest.ts`, so both implementations run this same
|
|
10
|
-
table to guarantee they never disagree about what a valid
|
|
11
|
-
they did, `foro check` would pass locally and the deploy
|
|
10
|
+
table to guarantee they never disagree about what a valid `pyproject.toml` or
|
|
11
|
+
`package.json` is — if they did, `foro check` would pass locally and the deploy
|
|
12
|
+
would fail.
|
|
12
13
|
|
|
13
14
|
```ts
|
|
14
15
|
import { manifestCases } from '@foro-sh/foro/manifest-cases'
|
|
15
16
|
|
|
16
|
-
for (const { name,
|
|
17
|
+
for (const { name, files, expect } of manifestCases) {
|
|
18
|
+
// write `files` into an empty directory, then validate it
|
|
17
19
|
// expect is { ok: true } or { ok: false, reason: ManifestRejectionReason }
|
|
18
20
|
}
|
|
19
21
|
```
|
|
@@ -1,193 +1,487 @@
|
|
|
1
1
|
export declare const rawManifestCases: readonly [{
|
|
2
|
-
readonly name: "minimal
|
|
3
|
-
readonly
|
|
2
|
+
readonly name: "python-minimal";
|
|
3
|
+
readonly files: {
|
|
4
|
+
readonly "pyproject.toml": "[project]\nname = \"my-server\"\n";
|
|
5
|
+
readonly "server.py": "";
|
|
6
|
+
};
|
|
7
|
+
readonly expect: {
|
|
8
|
+
readonly ok: true;
|
|
9
|
+
};
|
|
10
|
+
}, {
|
|
11
|
+
readonly name: "python-poetry-only-name";
|
|
12
|
+
readonly files: {
|
|
13
|
+
readonly "pyproject.toml": "[tool.poetry]\nname = \"my-server\"\n";
|
|
14
|
+
readonly "main.py": "";
|
|
15
|
+
};
|
|
16
|
+
readonly expect: {
|
|
17
|
+
readonly ok: true;
|
|
18
|
+
};
|
|
19
|
+
}, {
|
|
20
|
+
readonly name: "python-explicit-entrypoint";
|
|
21
|
+
readonly files: {
|
|
22
|
+
readonly "pyproject.toml": "[project]\nname = \"my-server\"\n\n[tool.foro]\nentrypoint = \"src/server.py\"\n";
|
|
23
|
+
};
|
|
4
24
|
readonly expect: {
|
|
5
25
|
readonly ok: true;
|
|
6
26
|
};
|
|
7
27
|
}, {
|
|
8
|
-
readonly name: "
|
|
9
|
-
readonly
|
|
28
|
+
readonly name: "python-all-fields";
|
|
29
|
+
readonly files: {
|
|
30
|
+
readonly "pyproject.toml": "[project]\nname = \"my-server\"\n\n[tool.foro]\nbuild_path = \"app\"\nentrypoint = \"server.py\"\nruntime = \"python\"\nruntime_version = \"3.11\"\nport = 9000\ndependency_manager = \"poetry\"\n";
|
|
31
|
+
};
|
|
10
32
|
readonly expect: {
|
|
11
33
|
readonly ok: true;
|
|
12
34
|
};
|
|
13
35
|
}, {
|
|
14
|
-
readonly name: "
|
|
15
|
-
readonly
|
|
36
|
+
readonly name: "python-requires-python-range";
|
|
37
|
+
readonly files: {
|
|
38
|
+
readonly "pyproject.toml": "[project]\nname = \"my-server\"\nrequires-python = \">=3.11,<3.13\"\n";
|
|
39
|
+
readonly "server.py": "";
|
|
40
|
+
};
|
|
16
41
|
readonly expect: {
|
|
17
42
|
readonly ok: true;
|
|
18
43
|
};
|
|
19
44
|
}, {
|
|
20
|
-
readonly name: "
|
|
21
|
-
readonly
|
|
45
|
+
readonly name: "python-requires-python-exclusion";
|
|
46
|
+
readonly files: {
|
|
47
|
+
readonly "pyproject.toml": "[project]\nname = \"my-server\"\nrequires-python = \">=3.11,!=3.13\"\n";
|
|
48
|
+
readonly "server.py": "";
|
|
49
|
+
};
|
|
22
50
|
readonly expect: {
|
|
23
51
|
readonly ok: true;
|
|
24
52
|
};
|
|
25
53
|
}, {
|
|
26
|
-
readonly name: "
|
|
27
|
-
readonly
|
|
54
|
+
readonly name: "python-requires-python-excludes-every-supported-version-one-by-one";
|
|
55
|
+
readonly files: {
|
|
56
|
+
readonly "pyproject.toml": "[project]\nname = \"my-server\"\nrequires-python = \">=3.11,!=3.11,!=3.12,!=3.13\"\n";
|
|
57
|
+
readonly "server.py": "";
|
|
58
|
+
};
|
|
28
59
|
readonly expect: {
|
|
29
60
|
readonly ok: false;
|
|
30
|
-
readonly reason: "
|
|
61
|
+
readonly reason: "invalid_runtime_version";
|
|
31
62
|
};
|
|
32
63
|
}, {
|
|
33
|
-
readonly name: "
|
|
34
|
-
readonly
|
|
64
|
+
readonly name: "python-hyphen-inside-a-path-segment-is-fine";
|
|
65
|
+
readonly files: {
|
|
66
|
+
readonly "pyproject.toml": "[project]\nname = \"my-server\"\n\n[tool.foro]\nentrypoint = \"my-tools/mcp-server.py\"\n";
|
|
67
|
+
};
|
|
68
|
+
readonly expect: {
|
|
69
|
+
readonly ok: true;
|
|
70
|
+
};
|
|
71
|
+
}, {
|
|
72
|
+
readonly name: "python-scoped-unspellable-name-is-normalised";
|
|
73
|
+
readonly files: {
|
|
74
|
+
readonly "pyproject.toml": "[project]\nname = \"My_Server\"\n";
|
|
75
|
+
readonly "server.py": "";
|
|
76
|
+
};
|
|
77
|
+
readonly expect: {
|
|
78
|
+
readonly ok: true;
|
|
79
|
+
};
|
|
80
|
+
}, {
|
|
81
|
+
readonly name: "python-no-entry-file";
|
|
82
|
+
readonly files: {
|
|
83
|
+
readonly "pyproject.toml": "[project]\nname = \"my-server\"\n";
|
|
84
|
+
};
|
|
35
85
|
readonly expect: {
|
|
36
86
|
readonly ok: false;
|
|
37
|
-
readonly reason: "
|
|
87
|
+
readonly reason: "invalid_entrypoint";
|
|
38
88
|
};
|
|
39
89
|
}, {
|
|
40
|
-
readonly name: "
|
|
41
|
-
readonly
|
|
90
|
+
readonly name: "python-entrypoint-traversal";
|
|
91
|
+
readonly files: {
|
|
92
|
+
readonly "pyproject.toml": "[project]\nname = \"my-server\"\n\n[tool.foro]\nentrypoint = \"../server.py\"\n";
|
|
93
|
+
};
|
|
42
94
|
readonly expect: {
|
|
43
95
|
readonly ok: false;
|
|
44
|
-
readonly reason: "
|
|
96
|
+
readonly reason: "invalid_entrypoint";
|
|
45
97
|
};
|
|
46
98
|
}, {
|
|
47
|
-
readonly name: "
|
|
48
|
-
readonly
|
|
99
|
+
readonly name: "python-entrypoint-absolute";
|
|
100
|
+
readonly files: {
|
|
101
|
+
readonly "pyproject.toml": "[project]\nname = \"my-server\"\n\n[tool.foro]\nentrypoint = \"/server.py\"\n";
|
|
102
|
+
};
|
|
49
103
|
readonly expect: {
|
|
50
104
|
readonly ok: false;
|
|
51
105
|
readonly reason: "invalid_entrypoint";
|
|
52
106
|
};
|
|
53
107
|
}, {
|
|
54
|
-
readonly name: "
|
|
55
|
-
readonly
|
|
108
|
+
readonly name: "python-entrypoint-shell-metachar";
|
|
109
|
+
readonly files: {
|
|
110
|
+
readonly "pyproject.toml": "[project]\nname = \"my-server\"\n\n[tool.foro]\nentrypoint = \"server.py; rm -rf x\"\n";
|
|
111
|
+
};
|
|
56
112
|
readonly expect: {
|
|
57
113
|
readonly ok: false;
|
|
58
114
|
readonly reason: "invalid_entrypoint";
|
|
59
115
|
};
|
|
60
116
|
}, {
|
|
61
|
-
readonly name: "
|
|
62
|
-
readonly
|
|
117
|
+
readonly name: "python-entrypoint-leading-dash";
|
|
118
|
+
readonly files: {
|
|
119
|
+
readonly "pyproject.toml": "[project]\nname = \"my-server\"\n\n[tool.foro]\nentrypoint = \"--isolated\"\n";
|
|
120
|
+
};
|
|
63
121
|
readonly expect: {
|
|
64
122
|
readonly ok: false;
|
|
65
123
|
readonly reason: "invalid_entrypoint";
|
|
66
124
|
};
|
|
67
125
|
}, {
|
|
68
|
-
readonly name: "
|
|
69
|
-
readonly
|
|
126
|
+
readonly name: "python-entrypoint-leading-dash-nested";
|
|
127
|
+
readonly files: {
|
|
128
|
+
readonly "pyproject.toml": "[project]\nname = \"my-server\"\n\n[tool.foro]\nentrypoint = \"src/-rf.py\"\n";
|
|
129
|
+
};
|
|
70
130
|
readonly expect: {
|
|
71
131
|
readonly ok: false;
|
|
72
132
|
readonly reason: "invalid_entrypoint";
|
|
73
133
|
};
|
|
74
134
|
}, {
|
|
75
|
-
readonly name: "
|
|
76
|
-
readonly
|
|
135
|
+
readonly name: "python-build-path-traversal";
|
|
136
|
+
readonly files: {
|
|
137
|
+
readonly "pyproject.toml": "[project]\nname = \"my-server\"\n\n[tool.foro]\nentrypoint = \"server.py\"\nbuild_path = \"../x\"\n";
|
|
138
|
+
};
|
|
139
|
+
readonly expect: {
|
|
140
|
+
readonly ok: false;
|
|
141
|
+
readonly reason: "invalid_build_path";
|
|
142
|
+
};
|
|
143
|
+
}, {
|
|
144
|
+
readonly name: "python-build-path-shell-metachar";
|
|
145
|
+
readonly files: {
|
|
146
|
+
readonly "pyproject.toml": "[project]\nname = \"my-server\"\n\n[tool.foro]\nentrypoint = \"server.py\"\nbuild_path = \". && whoami\"\n";
|
|
147
|
+
};
|
|
77
148
|
readonly expect: {
|
|
78
149
|
readonly ok: false;
|
|
79
150
|
readonly reason: "invalid_build_path";
|
|
80
151
|
};
|
|
81
152
|
}, {
|
|
82
|
-
readonly name: "
|
|
83
|
-
readonly
|
|
153
|
+
readonly name: "python-build-path-space";
|
|
154
|
+
readonly files: {
|
|
155
|
+
readonly "pyproject.toml": "[project]\nname = \"my-server\"\n\n[tool.foro]\nentrypoint = \"server.py\"\nbuild_path = \"my services\"\n";
|
|
156
|
+
};
|
|
84
157
|
readonly expect: {
|
|
85
158
|
readonly ok: false;
|
|
86
159
|
readonly reason: "invalid_build_path";
|
|
87
160
|
};
|
|
88
161
|
}, {
|
|
89
|
-
readonly name: "
|
|
90
|
-
readonly
|
|
162
|
+
readonly name: "python-build-path-newline";
|
|
163
|
+
readonly files: {
|
|
164
|
+
readonly "pyproject.toml": "[project]\nname = \"my-server\"\n\n[tool.foro]\nentrypoint = \"server.py\"\nbuild_path = \"app\\nUSER root\"\n";
|
|
165
|
+
};
|
|
91
166
|
readonly expect: {
|
|
92
167
|
readonly ok: false;
|
|
93
168
|
readonly reason: "invalid_build_path";
|
|
94
169
|
};
|
|
95
170
|
}, {
|
|
96
|
-
readonly name: "
|
|
97
|
-
readonly
|
|
171
|
+
readonly name: "python-build-path-leading-dash";
|
|
172
|
+
readonly files: {
|
|
173
|
+
readonly "pyproject.toml": "[project]\nname = \"my-server\"\n\n[tool.foro]\nentrypoint = \"server.py\"\nbuild_path = \"-rf\"\n";
|
|
174
|
+
};
|
|
98
175
|
readonly expect: {
|
|
99
176
|
readonly ok: false;
|
|
100
177
|
readonly reason: "invalid_build_path";
|
|
101
178
|
};
|
|
102
179
|
}, {
|
|
103
|
-
readonly name: "
|
|
104
|
-
readonly
|
|
180
|
+
readonly name: "python-runtime-version-outside-allowlist";
|
|
181
|
+
readonly files: {
|
|
182
|
+
readonly "pyproject.toml": "[project]\nname = \"my-server\"\n\n[tool.foro]\nentrypoint = \"server.py\"\nruntime_version = \"3.9\"\n";
|
|
183
|
+
};
|
|
184
|
+
readonly expect: {
|
|
185
|
+
readonly ok: false;
|
|
186
|
+
readonly reason: "invalid_runtime_version";
|
|
187
|
+
};
|
|
188
|
+
}, {
|
|
189
|
+
readonly name: "python-requires-python-excludes-every-supported-version";
|
|
190
|
+
readonly files: {
|
|
191
|
+
readonly "pyproject.toml": "[project]\nname = \"my-server\"\nrequires-python = \"==3.9\"\n";
|
|
192
|
+
readonly "server.py": "";
|
|
193
|
+
};
|
|
194
|
+
readonly expect: {
|
|
195
|
+
readonly ok: false;
|
|
196
|
+
readonly reason: "invalid_runtime_version";
|
|
197
|
+
};
|
|
198
|
+
}, {
|
|
199
|
+
readonly name: "python-runtime-version-from-another-runtime";
|
|
200
|
+
readonly files: {
|
|
201
|
+
readonly "pyproject.toml": "[project]\nname = \"my-server\"\n\n[tool.foro]\nentrypoint = \"server.py\"\nruntime_version = \"22\"\n";
|
|
202
|
+
};
|
|
203
|
+
readonly expect: {
|
|
204
|
+
readonly ok: false;
|
|
205
|
+
readonly reason: "invalid_runtime_version";
|
|
206
|
+
};
|
|
207
|
+
}, {
|
|
208
|
+
readonly name: "runtime-outside-allowlist";
|
|
209
|
+
readonly files: {
|
|
210
|
+
readonly "pyproject.toml": "[project]\nname = \"my-server\"\n\n[tool.foro]\nentrypoint = \"server.py\"\nruntime = \"ruby\"\n";
|
|
211
|
+
};
|
|
212
|
+
readonly expect: {
|
|
213
|
+
readonly ok: false;
|
|
214
|
+
readonly reason: "invalid_runtime";
|
|
215
|
+
};
|
|
216
|
+
}, {
|
|
217
|
+
readonly name: "runtime-disagrees-with-the-file-it-was-read-from";
|
|
218
|
+
readonly files: {
|
|
219
|
+
readonly "pyproject.toml": "[project]\nname = \"my-server\"\n\n[tool.foro]\nentrypoint = \"server.py\"\nruntime = \"node\"\n";
|
|
220
|
+
};
|
|
221
|
+
readonly expect: {
|
|
222
|
+
readonly ok: false;
|
|
223
|
+
readonly reason: "invalid_runtime";
|
|
224
|
+
};
|
|
225
|
+
}, {
|
|
226
|
+
readonly name: "both-config-files-and-neither-claims-the-runtime";
|
|
227
|
+
readonly files: {
|
|
228
|
+
readonly "pyproject.toml": "[project]\nname = \"my-server\"\n";
|
|
229
|
+
readonly "package.json": "{\"name\": \"my-server\", \"main\": \"index.js\"}";
|
|
230
|
+
readonly "server.py": "";
|
|
231
|
+
};
|
|
232
|
+
readonly expect: {
|
|
233
|
+
readonly ok: false;
|
|
234
|
+
readonly reason: "invalid_runtime";
|
|
235
|
+
};
|
|
236
|
+
}, {
|
|
237
|
+
readonly name: "both-config-files-and-both-claim-the-runtime";
|
|
238
|
+
readonly files: {
|
|
239
|
+
readonly "pyproject.toml": "[project]\nname = \"my-server\"\n\n[tool.foro]\nruntime = \"python\"\n";
|
|
240
|
+
readonly "package.json": "{\"name\": \"my-server\", \"main\": \"index.js\", \"foro\": {\"runtime\": \"node\"}}";
|
|
241
|
+
readonly "server.py": "";
|
|
242
|
+
};
|
|
243
|
+
readonly expect: {
|
|
244
|
+
readonly ok: false;
|
|
245
|
+
readonly reason: "invalid_runtime";
|
|
246
|
+
};
|
|
247
|
+
}, {
|
|
248
|
+
readonly name: "both-config-files-python-claims-the-runtime";
|
|
249
|
+
readonly files: {
|
|
250
|
+
readonly "pyproject.toml": "[project]\nname = \"my-server\"\n\n[tool.foro]\nruntime = \"python\"\n";
|
|
251
|
+
readonly "package.json": "{\"name\": \"tooling-only\"}";
|
|
252
|
+
readonly "server.py": "";
|
|
253
|
+
};
|
|
254
|
+
readonly expect: {
|
|
255
|
+
readonly ok: true;
|
|
256
|
+
};
|
|
257
|
+
}, {
|
|
258
|
+
readonly name: "both-config-files-node-claims-the-runtime";
|
|
259
|
+
readonly files: {
|
|
260
|
+
readonly "pyproject.toml": "[project]\nname = \"my-server\"\n";
|
|
261
|
+
readonly "package.json": "{\"name\": \"my-server\", \"main\": \"index.js\", \"foro\": {\"runtime\": \"node\"}}";
|
|
262
|
+
readonly "server.py": "";
|
|
263
|
+
};
|
|
264
|
+
readonly expect: {
|
|
265
|
+
readonly ok: true;
|
|
266
|
+
};
|
|
267
|
+
}, {
|
|
268
|
+
readonly name: "no-config-file-only-a-legacy-manifest";
|
|
269
|
+
readonly files: {
|
|
270
|
+
readonly "foro.yaml": "name: my-server\nentrypoint: server.py\n";
|
|
271
|
+
readonly "server.py": "";
|
|
272
|
+
};
|
|
273
|
+
readonly expect: {
|
|
274
|
+
readonly ok: false;
|
|
275
|
+
readonly reason: "missing_manifest";
|
|
276
|
+
};
|
|
277
|
+
}, {
|
|
278
|
+
readonly name: "unknown-field-stale-python-version";
|
|
279
|
+
readonly files: {
|
|
280
|
+
readonly "pyproject.toml": "[project]\nname = \"my-server\"\n\n[tool.foro]\nentrypoint = \"server.py\"\npython_version = \"3.13\"\n";
|
|
281
|
+
};
|
|
282
|
+
readonly expect: {
|
|
283
|
+
readonly ok: false;
|
|
284
|
+
readonly reason: "unknown_field";
|
|
285
|
+
};
|
|
286
|
+
}, {
|
|
287
|
+
readonly name: "unknown-field-typo";
|
|
288
|
+
readonly files: {
|
|
289
|
+
readonly "pyproject.toml": "[project]\nname = \"my-server\"\n\n[tool.foro]\nentrypoint = \"server.py\"\nentrypoints = \"server.py\"\n";
|
|
290
|
+
};
|
|
105
291
|
readonly expect: {
|
|
106
292
|
readonly ok: false;
|
|
107
|
-
readonly reason: "
|
|
293
|
+
readonly reason: "unknown_field";
|
|
108
294
|
};
|
|
109
295
|
}, {
|
|
110
|
-
readonly name: "
|
|
111
|
-
readonly
|
|
296
|
+
readonly name: "unknown-field-name-belongs-to-the-package";
|
|
297
|
+
readonly files: {
|
|
298
|
+
readonly "pyproject.toml": "[project]\nname = \"my-server\"\n\n[tool.foro]\nentrypoint = \"server.py\"\nname = \"other\"\n";
|
|
299
|
+
};
|
|
300
|
+
readonly expect: {
|
|
301
|
+
readonly ok: false;
|
|
302
|
+
readonly reason: "unknown_field";
|
|
303
|
+
};
|
|
304
|
+
}, {
|
|
305
|
+
readonly name: "port-too-low";
|
|
306
|
+
readonly files: {
|
|
307
|
+
readonly "pyproject.toml": "[project]\nname = \"my-server\"\n\n[tool.foro]\nentrypoint = \"server.py\"\nport = 80\n";
|
|
308
|
+
};
|
|
112
309
|
readonly expect: {
|
|
113
310
|
readonly ok: false;
|
|
114
311
|
readonly reason: "invalid_port";
|
|
115
312
|
};
|
|
116
313
|
}, {
|
|
117
|
-
readonly name: "
|
|
118
|
-
readonly
|
|
314
|
+
readonly name: "port-too-high";
|
|
315
|
+
readonly files: {
|
|
316
|
+
readonly "pyproject.toml": "[project]\nname = \"my-server\"\n\n[tool.foro]\nentrypoint = \"server.py\"\nport = 70000\n";
|
|
317
|
+
};
|
|
119
318
|
readonly expect: {
|
|
120
319
|
readonly ok: false;
|
|
121
320
|
readonly reason: "invalid_port";
|
|
122
321
|
};
|
|
123
322
|
}, {
|
|
124
|
-
readonly name: "
|
|
125
|
-
readonly
|
|
323
|
+
readonly name: "port-sidecar-reserved";
|
|
324
|
+
readonly files: {
|
|
325
|
+
readonly "pyproject.toml": "[project]\nname = \"my-server\"\n\n[tool.foro]\nentrypoint = \"server.py\"\nport = 8001\n";
|
|
326
|
+
};
|
|
126
327
|
readonly expect: {
|
|
127
328
|
readonly ok: false;
|
|
128
329
|
readonly reason: "invalid_port";
|
|
129
330
|
};
|
|
130
331
|
}, {
|
|
131
|
-
readonly name: "
|
|
132
|
-
readonly
|
|
332
|
+
readonly name: "port-gate-reserved";
|
|
333
|
+
readonly files: {
|
|
334
|
+
readonly "pyproject.toml": "[project]\nname = \"my-server\"\n\n[tool.foro]\nentrypoint = \"server.py\"\nport = 8002\n";
|
|
335
|
+
};
|
|
133
336
|
readonly expect: {
|
|
134
337
|
readonly ok: false;
|
|
135
338
|
readonly reason: "invalid_port";
|
|
136
339
|
};
|
|
137
340
|
}, {
|
|
138
|
-
readonly name: "
|
|
139
|
-
readonly
|
|
341
|
+
readonly name: "port-non-integer";
|
|
342
|
+
readonly files: {
|
|
343
|
+
readonly "pyproject.toml": "[project]\nname = \"my-server\"\n\n[tool.foro]\nentrypoint = \"server.py\"\nport = \"abc\"\n";
|
|
344
|
+
};
|
|
140
345
|
readonly expect: {
|
|
141
346
|
readonly ok: false;
|
|
142
|
-
readonly reason: "
|
|
347
|
+
readonly reason: "invalid_port";
|
|
143
348
|
};
|
|
144
349
|
}, {
|
|
145
|
-
readonly name: "
|
|
146
|
-
readonly
|
|
350
|
+
readonly name: "dependency-manager-outside-allowlist";
|
|
351
|
+
readonly files: {
|
|
352
|
+
readonly "pyproject.toml": "[project]\nname = \"my-server\"\n\n[tool.foro]\nentrypoint = \"server.py\"\ndependency_manager = \"pipx\"\n";
|
|
353
|
+
};
|
|
147
354
|
readonly expect: {
|
|
148
355
|
readonly ok: false;
|
|
149
|
-
readonly reason: "
|
|
356
|
+
readonly reason: "invalid_dependency_manager";
|
|
150
357
|
};
|
|
151
358
|
}, {
|
|
152
|
-
readonly name: "
|
|
153
|
-
readonly
|
|
359
|
+
readonly name: "node-dependency-manager-on-python";
|
|
360
|
+
readonly files: {
|
|
361
|
+
readonly "pyproject.toml": "[project]\nname = \"my-server\"\n\n[tool.foro]\nentrypoint = \"server.py\"\ndependency_manager = \"npm\"\n";
|
|
362
|
+
};
|
|
154
363
|
readonly expect: {
|
|
155
364
|
readonly ok: false;
|
|
156
|
-
readonly reason: "
|
|
365
|
+
readonly reason: "invalid_dependency_manager";
|
|
157
366
|
};
|
|
158
367
|
}, {
|
|
159
|
-
readonly name: "
|
|
160
|
-
readonly
|
|
368
|
+
readonly name: "unparseable-pyproject";
|
|
369
|
+
readonly files: {
|
|
370
|
+
readonly "pyproject.toml": "[project\nname = \"my-server\"\n";
|
|
371
|
+
};
|
|
161
372
|
readonly expect: {
|
|
162
373
|
readonly ok: false;
|
|
163
374
|
readonly reason: "invalid_shape";
|
|
164
375
|
};
|
|
165
376
|
}, {
|
|
166
|
-
readonly name: "
|
|
167
|
-
readonly
|
|
377
|
+
readonly name: "node-minimal";
|
|
378
|
+
readonly files: {
|
|
379
|
+
readonly "package.json": "{\"name\": \"my-server\", \"main\": \"dist/index.js\"}";
|
|
380
|
+
};
|
|
381
|
+
readonly expect: {
|
|
382
|
+
readonly ok: true;
|
|
383
|
+
};
|
|
384
|
+
}, {
|
|
385
|
+
readonly name: "node-scoped-name";
|
|
386
|
+
readonly files: {
|
|
387
|
+
readonly "package.json": "{\"name\": \"@acme/my-server\", \"main\": \"dist/index.js\"}";
|
|
388
|
+
};
|
|
389
|
+
readonly expect: {
|
|
390
|
+
readonly ok: true;
|
|
391
|
+
};
|
|
392
|
+
}, {
|
|
393
|
+
readonly name: "node-bin-map";
|
|
394
|
+
readonly files: {
|
|
395
|
+
readonly "package.json": "{\"name\": \"my-server\", \"bin\": {\"my-server\": \"dist/cli.js\"}}";
|
|
396
|
+
};
|
|
397
|
+
readonly expect: {
|
|
398
|
+
readonly ok: true;
|
|
399
|
+
};
|
|
400
|
+
}, {
|
|
401
|
+
readonly name: "node-implicit-index";
|
|
402
|
+
readonly files: {
|
|
403
|
+
readonly "package.json": "{\"name\": \"my-server\"}";
|
|
404
|
+
readonly "index.js": "";
|
|
405
|
+
};
|
|
406
|
+
readonly expect: {
|
|
407
|
+
readonly ok: true;
|
|
408
|
+
};
|
|
409
|
+
}, {
|
|
410
|
+
readonly name: "node-engines-range";
|
|
411
|
+
readonly files: {
|
|
412
|
+
readonly "package.json": "{\"name\": \"my-server\", \"main\": \"index.js\", \"engines\": {\"node\": \">=22\"}}";
|
|
413
|
+
};
|
|
414
|
+
readonly expect: {
|
|
415
|
+
readonly ok: true;
|
|
416
|
+
};
|
|
417
|
+
}, {
|
|
418
|
+
readonly name: "node-engines-union-of-every-supported-major";
|
|
419
|
+
readonly files: {
|
|
420
|
+
readonly "package.json": "{\"name\": \"my-server\", \"main\": \"index.js\", \"engines\": {\"node\": \"^22 || ^24\"}}";
|
|
421
|
+
};
|
|
422
|
+
readonly expect: {
|
|
423
|
+
readonly ok: true;
|
|
424
|
+
};
|
|
425
|
+
}, {
|
|
426
|
+
readonly name: "node-engines-union-with-one-unsupported-branch";
|
|
427
|
+
readonly files: {
|
|
428
|
+
readonly "package.json": "{\"name\": \"my-server\", \"main\": \"index.js\", \"engines\": {\"node\": \"^20 || ^22\"}}";
|
|
429
|
+
};
|
|
430
|
+
readonly expect: {
|
|
431
|
+
readonly ok: true;
|
|
432
|
+
};
|
|
433
|
+
}, {
|
|
434
|
+
readonly name: "node-all-fields";
|
|
435
|
+
readonly files: {
|
|
436
|
+
readonly "package.json": "{\"name\": \"my-server\", \"main\": \"dist/index.js\", \"foro\": {\"runtime\": \"node\", \"runtime_version\": \"22\", \"port\": 9000, \"dependency_manager\": \"pnpm\"}}";
|
|
437
|
+
};
|
|
438
|
+
readonly expect: {
|
|
439
|
+
readonly ok: true;
|
|
440
|
+
};
|
|
441
|
+
}, {
|
|
442
|
+
readonly name: "node-no-entry-file";
|
|
443
|
+
readonly files: {
|
|
444
|
+
readonly "package.json": "{\"name\": \"my-server\"}";
|
|
445
|
+
};
|
|
168
446
|
readonly expect: {
|
|
169
447
|
readonly ok: false;
|
|
170
448
|
readonly reason: "invalid_entrypoint";
|
|
171
449
|
};
|
|
172
450
|
}, {
|
|
173
|
-
readonly name: "
|
|
174
|
-
readonly
|
|
451
|
+
readonly name: "node-engines-excludes-every-supported-version";
|
|
452
|
+
readonly files: {
|
|
453
|
+
readonly "package.json": "{\"name\": \"my-server\", \"main\": \"index.js\", \"engines\": {\"node\": \"^20\"}}";
|
|
454
|
+
};
|
|
175
455
|
readonly expect: {
|
|
176
456
|
readonly ok: false;
|
|
177
|
-
readonly reason: "
|
|
457
|
+
readonly reason: "invalid_runtime_version";
|
|
178
458
|
};
|
|
179
459
|
}, {
|
|
180
|
-
readonly name: "
|
|
181
|
-
readonly
|
|
460
|
+
readonly name: "node-runtime-version-from-another-runtime";
|
|
461
|
+
readonly files: {
|
|
462
|
+
readonly "package.json": "{\"name\": \"my-server\", \"main\": \"index.js\", \"foro\": {\"runtime_version\": \"3.12\"}}";
|
|
463
|
+
};
|
|
182
464
|
readonly expect: {
|
|
183
465
|
readonly ok: false;
|
|
184
|
-
readonly reason: "
|
|
466
|
+
readonly reason: "invalid_runtime_version";
|
|
185
467
|
};
|
|
186
468
|
}, {
|
|
187
|
-
readonly name: "
|
|
188
|
-
readonly
|
|
469
|
+
readonly name: "python-dependency-manager-on-node";
|
|
470
|
+
readonly files: {
|
|
471
|
+
readonly "package.json": "{\"name\": \"my-server\", \"main\": \"index.js\", \"foro\": {\"dependency_manager\": \"uv\"}}";
|
|
472
|
+
};
|
|
189
473
|
readonly expect: {
|
|
190
|
-
readonly ok:
|
|
474
|
+
readonly ok: false;
|
|
475
|
+
readonly reason: "invalid_dependency_manager";
|
|
476
|
+
};
|
|
477
|
+
}, {
|
|
478
|
+
readonly name: "unparseable-package-json";
|
|
479
|
+
readonly files: {
|
|
480
|
+
readonly "package.json": "{\"name\": ";
|
|
481
|
+
};
|
|
482
|
+
readonly expect: {
|
|
483
|
+
readonly ok: false;
|
|
484
|
+
readonly reason: "invalid_shape";
|
|
191
485
|
};
|
|
192
486
|
}];
|
|
193
487
|
//# sourceMappingURL=_generated-manifest-cases.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"_generated-manifest-cases.d.ts","sourceRoot":"","sources":["../src/_generated-manifest-cases.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,gBAAgB
|
|
1
|
+
{"version":3,"file":"_generated-manifest-cases.d.ts","sourceRoot":"","sources":["../src/_generated-manifest-cases.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA4hBnB,CAAA"}
|
|
@@ -2,222 +2,542 @@
|
|
|
2
2
|
// manifest-cases.json. Do not edit - edit the JSON and rebuild.
|
|
3
3
|
export const rawManifestCases = [
|
|
4
4
|
{
|
|
5
|
-
"name": "minimal
|
|
6
|
-
"
|
|
5
|
+
"name": "python-minimal",
|
|
6
|
+
"files": {
|
|
7
|
+
"pyproject.toml": "[project]\nname = \"my-server\"\n",
|
|
8
|
+
"server.py": ""
|
|
9
|
+
},
|
|
7
10
|
"expect": {
|
|
8
11
|
"ok": true
|
|
9
12
|
}
|
|
10
13
|
},
|
|
11
14
|
{
|
|
12
|
-
"name": "
|
|
13
|
-
"
|
|
15
|
+
"name": "python-poetry-only-name",
|
|
16
|
+
"files": {
|
|
17
|
+
"pyproject.toml": "[tool.poetry]\nname = \"my-server\"\n",
|
|
18
|
+
"main.py": ""
|
|
19
|
+
},
|
|
14
20
|
"expect": {
|
|
15
21
|
"ok": true
|
|
16
22
|
}
|
|
17
23
|
},
|
|
18
24
|
{
|
|
19
|
-
"name": "
|
|
20
|
-
"
|
|
25
|
+
"name": "python-explicit-entrypoint",
|
|
26
|
+
"files": {
|
|
27
|
+
"pyproject.toml": "[project]\nname = \"my-server\"\n\n[tool.foro]\nentrypoint = \"src/server.py\"\n"
|
|
28
|
+
},
|
|
21
29
|
"expect": {
|
|
22
30
|
"ok": true
|
|
23
31
|
}
|
|
24
32
|
},
|
|
25
33
|
{
|
|
26
|
-
"name": "
|
|
27
|
-
"
|
|
34
|
+
"name": "python-all-fields",
|
|
35
|
+
"files": {
|
|
36
|
+
"pyproject.toml": "[project]\nname = \"my-server\"\n\n[tool.foro]\nbuild_path = \"app\"\nentrypoint = \"server.py\"\nruntime = \"python\"\nruntime_version = \"3.11\"\nport = 9000\ndependency_manager = \"poetry\"\n"
|
|
37
|
+
},
|
|
28
38
|
"expect": {
|
|
29
39
|
"ok": true
|
|
30
40
|
}
|
|
31
41
|
},
|
|
32
42
|
{
|
|
33
|
-
"name": "
|
|
34
|
-
"
|
|
43
|
+
"name": "python-requires-python-range",
|
|
44
|
+
"files": {
|
|
45
|
+
"pyproject.toml": "[project]\nname = \"my-server\"\nrequires-python = \">=3.11,<3.13\"\n",
|
|
46
|
+
"server.py": ""
|
|
47
|
+
},
|
|
48
|
+
"expect": {
|
|
49
|
+
"ok": true
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
"name": "python-requires-python-exclusion",
|
|
54
|
+
"files": {
|
|
55
|
+
"pyproject.toml": "[project]\nname = \"my-server\"\nrequires-python = \">=3.11,!=3.13\"\n",
|
|
56
|
+
"server.py": ""
|
|
57
|
+
},
|
|
58
|
+
"expect": {
|
|
59
|
+
"ok": true
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
"name": "python-requires-python-excludes-every-supported-version-one-by-one",
|
|
64
|
+
"files": {
|
|
65
|
+
"pyproject.toml": "[project]\nname = \"my-server\"\nrequires-python = \">=3.11,!=3.11,!=3.12,!=3.13\"\n",
|
|
66
|
+
"server.py": ""
|
|
67
|
+
},
|
|
35
68
|
"expect": {
|
|
36
69
|
"ok": false,
|
|
37
|
-
"reason": "
|
|
70
|
+
"reason": "invalid_runtime_version"
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
"name": "python-hyphen-inside-a-path-segment-is-fine",
|
|
75
|
+
"files": {
|
|
76
|
+
"pyproject.toml": "[project]\nname = \"my-server\"\n\n[tool.foro]\nentrypoint = \"my-tools/mcp-server.py\"\n"
|
|
77
|
+
},
|
|
78
|
+
"expect": {
|
|
79
|
+
"ok": true
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
"name": "python-scoped-unspellable-name-is-normalised",
|
|
84
|
+
"files": {
|
|
85
|
+
"pyproject.toml": "[project]\nname = \"My_Server\"\n",
|
|
86
|
+
"server.py": ""
|
|
87
|
+
},
|
|
88
|
+
"expect": {
|
|
89
|
+
"ok": true
|
|
38
90
|
}
|
|
39
91
|
},
|
|
40
92
|
{
|
|
41
|
-
"name": "
|
|
42
|
-
"
|
|
93
|
+
"name": "python-no-entry-file",
|
|
94
|
+
"files": {
|
|
95
|
+
"pyproject.toml": "[project]\nname = \"my-server\"\n"
|
|
96
|
+
},
|
|
43
97
|
"expect": {
|
|
44
98
|
"ok": false,
|
|
45
|
-
"reason": "
|
|
99
|
+
"reason": "invalid_entrypoint"
|
|
46
100
|
}
|
|
47
101
|
},
|
|
48
102
|
{
|
|
49
|
-
"name": "
|
|
50
|
-
"
|
|
103
|
+
"name": "python-entrypoint-traversal",
|
|
104
|
+
"files": {
|
|
105
|
+
"pyproject.toml": "[project]\nname = \"my-server\"\n\n[tool.foro]\nentrypoint = \"../server.py\"\n"
|
|
106
|
+
},
|
|
51
107
|
"expect": {
|
|
52
108
|
"ok": false,
|
|
53
|
-
"reason": "
|
|
109
|
+
"reason": "invalid_entrypoint"
|
|
54
110
|
}
|
|
55
111
|
},
|
|
56
112
|
{
|
|
57
|
-
"name": "
|
|
58
|
-
"
|
|
113
|
+
"name": "python-entrypoint-absolute",
|
|
114
|
+
"files": {
|
|
115
|
+
"pyproject.toml": "[project]\nname = \"my-server\"\n\n[tool.foro]\nentrypoint = \"/server.py\"\n"
|
|
116
|
+
},
|
|
59
117
|
"expect": {
|
|
60
118
|
"ok": false,
|
|
61
119
|
"reason": "invalid_entrypoint"
|
|
62
120
|
}
|
|
63
121
|
},
|
|
64
122
|
{
|
|
65
|
-
"name": "
|
|
66
|
-
"
|
|
123
|
+
"name": "python-entrypoint-shell-metachar",
|
|
124
|
+
"files": {
|
|
125
|
+
"pyproject.toml": "[project]\nname = \"my-server\"\n\n[tool.foro]\nentrypoint = \"server.py; rm -rf x\"\n"
|
|
126
|
+
},
|
|
67
127
|
"expect": {
|
|
68
128
|
"ok": false,
|
|
69
129
|
"reason": "invalid_entrypoint"
|
|
70
130
|
}
|
|
71
131
|
},
|
|
72
132
|
{
|
|
73
|
-
"name": "
|
|
74
|
-
"
|
|
133
|
+
"name": "python-entrypoint-leading-dash",
|
|
134
|
+
"files": {
|
|
135
|
+
"pyproject.toml": "[project]\nname = \"my-server\"\n\n[tool.foro]\nentrypoint = \"--isolated\"\n"
|
|
136
|
+
},
|
|
75
137
|
"expect": {
|
|
76
138
|
"ok": false,
|
|
77
139
|
"reason": "invalid_entrypoint"
|
|
78
140
|
}
|
|
79
141
|
},
|
|
80
142
|
{
|
|
81
|
-
"name": "
|
|
82
|
-
"
|
|
143
|
+
"name": "python-entrypoint-leading-dash-nested",
|
|
144
|
+
"files": {
|
|
145
|
+
"pyproject.toml": "[project]\nname = \"my-server\"\n\n[tool.foro]\nentrypoint = \"src/-rf.py\"\n"
|
|
146
|
+
},
|
|
83
147
|
"expect": {
|
|
84
148
|
"ok": false,
|
|
85
149
|
"reason": "invalid_entrypoint"
|
|
86
150
|
}
|
|
87
151
|
},
|
|
88
152
|
{
|
|
89
|
-
"name": "
|
|
90
|
-
"
|
|
153
|
+
"name": "python-build-path-traversal",
|
|
154
|
+
"files": {
|
|
155
|
+
"pyproject.toml": "[project]\nname = \"my-server\"\n\n[tool.foro]\nentrypoint = \"server.py\"\nbuild_path = \"../x\"\n"
|
|
156
|
+
},
|
|
157
|
+
"expect": {
|
|
158
|
+
"ok": false,
|
|
159
|
+
"reason": "invalid_build_path"
|
|
160
|
+
}
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
"name": "python-build-path-shell-metachar",
|
|
164
|
+
"files": {
|
|
165
|
+
"pyproject.toml": "[project]\nname = \"my-server\"\n\n[tool.foro]\nentrypoint = \"server.py\"\nbuild_path = \". && whoami\"\n"
|
|
166
|
+
},
|
|
91
167
|
"expect": {
|
|
92
168
|
"ok": false,
|
|
93
169
|
"reason": "invalid_build_path"
|
|
94
170
|
}
|
|
95
171
|
},
|
|
96
172
|
{
|
|
97
|
-
"name": "
|
|
98
|
-
"
|
|
173
|
+
"name": "python-build-path-space",
|
|
174
|
+
"files": {
|
|
175
|
+
"pyproject.toml": "[project]\nname = \"my-server\"\n\n[tool.foro]\nentrypoint = \"server.py\"\nbuild_path = \"my services\"\n"
|
|
176
|
+
},
|
|
99
177
|
"expect": {
|
|
100
178
|
"ok": false,
|
|
101
179
|
"reason": "invalid_build_path"
|
|
102
180
|
}
|
|
103
181
|
},
|
|
104
182
|
{
|
|
105
|
-
"name": "
|
|
106
|
-
"
|
|
183
|
+
"name": "python-build-path-newline",
|
|
184
|
+
"files": {
|
|
185
|
+
"pyproject.toml": "[project]\nname = \"my-server\"\n\n[tool.foro]\nentrypoint = \"server.py\"\nbuild_path = \"app\\nUSER root\"\n"
|
|
186
|
+
},
|
|
107
187
|
"expect": {
|
|
108
188
|
"ok": false,
|
|
109
189
|
"reason": "invalid_build_path"
|
|
110
190
|
}
|
|
111
191
|
},
|
|
112
192
|
{
|
|
113
|
-
"name": "
|
|
114
|
-
"
|
|
193
|
+
"name": "python-build-path-leading-dash",
|
|
194
|
+
"files": {
|
|
195
|
+
"pyproject.toml": "[project]\nname = \"my-server\"\n\n[tool.foro]\nentrypoint = \"server.py\"\nbuild_path = \"-rf\"\n"
|
|
196
|
+
},
|
|
115
197
|
"expect": {
|
|
116
198
|
"ok": false,
|
|
117
199
|
"reason": "invalid_build_path"
|
|
118
200
|
}
|
|
119
201
|
},
|
|
120
202
|
{
|
|
121
|
-
"name": "
|
|
122
|
-
"
|
|
203
|
+
"name": "python-runtime-version-outside-allowlist",
|
|
204
|
+
"files": {
|
|
205
|
+
"pyproject.toml": "[project]\nname = \"my-server\"\n\n[tool.foro]\nentrypoint = \"server.py\"\nruntime_version = \"3.9\"\n"
|
|
206
|
+
},
|
|
207
|
+
"expect": {
|
|
208
|
+
"ok": false,
|
|
209
|
+
"reason": "invalid_runtime_version"
|
|
210
|
+
}
|
|
211
|
+
},
|
|
212
|
+
{
|
|
213
|
+
"name": "python-requires-python-excludes-every-supported-version",
|
|
214
|
+
"files": {
|
|
215
|
+
"pyproject.toml": "[project]\nname = \"my-server\"\nrequires-python = \"==3.9\"\n",
|
|
216
|
+
"server.py": ""
|
|
217
|
+
},
|
|
218
|
+
"expect": {
|
|
219
|
+
"ok": false,
|
|
220
|
+
"reason": "invalid_runtime_version"
|
|
221
|
+
}
|
|
222
|
+
},
|
|
223
|
+
{
|
|
224
|
+
"name": "python-runtime-version-from-another-runtime",
|
|
225
|
+
"files": {
|
|
226
|
+
"pyproject.toml": "[project]\nname = \"my-server\"\n\n[tool.foro]\nentrypoint = \"server.py\"\nruntime_version = \"22\"\n"
|
|
227
|
+
},
|
|
228
|
+
"expect": {
|
|
229
|
+
"ok": false,
|
|
230
|
+
"reason": "invalid_runtime_version"
|
|
231
|
+
}
|
|
232
|
+
},
|
|
233
|
+
{
|
|
234
|
+
"name": "runtime-outside-allowlist",
|
|
235
|
+
"files": {
|
|
236
|
+
"pyproject.toml": "[project]\nname = \"my-server\"\n\n[tool.foro]\nentrypoint = \"server.py\"\nruntime = \"ruby\"\n"
|
|
237
|
+
},
|
|
238
|
+
"expect": {
|
|
239
|
+
"ok": false,
|
|
240
|
+
"reason": "invalid_runtime"
|
|
241
|
+
}
|
|
242
|
+
},
|
|
243
|
+
{
|
|
244
|
+
"name": "runtime-disagrees-with-the-file-it-was-read-from",
|
|
245
|
+
"files": {
|
|
246
|
+
"pyproject.toml": "[project]\nname = \"my-server\"\n\n[tool.foro]\nentrypoint = \"server.py\"\nruntime = \"node\"\n"
|
|
247
|
+
},
|
|
248
|
+
"expect": {
|
|
249
|
+
"ok": false,
|
|
250
|
+
"reason": "invalid_runtime"
|
|
251
|
+
}
|
|
252
|
+
},
|
|
253
|
+
{
|
|
254
|
+
"name": "both-config-files-and-neither-claims-the-runtime",
|
|
255
|
+
"files": {
|
|
256
|
+
"pyproject.toml": "[project]\nname = \"my-server\"\n",
|
|
257
|
+
"package.json": "{\"name\": \"my-server\", \"main\": \"index.js\"}",
|
|
258
|
+
"server.py": ""
|
|
259
|
+
},
|
|
260
|
+
"expect": {
|
|
261
|
+
"ok": false,
|
|
262
|
+
"reason": "invalid_runtime"
|
|
263
|
+
}
|
|
264
|
+
},
|
|
265
|
+
{
|
|
266
|
+
"name": "both-config-files-and-both-claim-the-runtime",
|
|
267
|
+
"files": {
|
|
268
|
+
"pyproject.toml": "[project]\nname = \"my-server\"\n\n[tool.foro]\nruntime = \"python\"\n",
|
|
269
|
+
"package.json": "{\"name\": \"my-server\", \"main\": \"index.js\", \"foro\": {\"runtime\": \"node\"}}",
|
|
270
|
+
"server.py": ""
|
|
271
|
+
},
|
|
272
|
+
"expect": {
|
|
273
|
+
"ok": false,
|
|
274
|
+
"reason": "invalid_runtime"
|
|
275
|
+
}
|
|
276
|
+
},
|
|
277
|
+
{
|
|
278
|
+
"name": "both-config-files-python-claims-the-runtime",
|
|
279
|
+
"files": {
|
|
280
|
+
"pyproject.toml": "[project]\nname = \"my-server\"\n\n[tool.foro]\nruntime = \"python\"\n",
|
|
281
|
+
"package.json": "{\"name\": \"tooling-only\"}",
|
|
282
|
+
"server.py": ""
|
|
283
|
+
},
|
|
284
|
+
"expect": {
|
|
285
|
+
"ok": true
|
|
286
|
+
}
|
|
287
|
+
},
|
|
288
|
+
{
|
|
289
|
+
"name": "both-config-files-node-claims-the-runtime",
|
|
290
|
+
"files": {
|
|
291
|
+
"pyproject.toml": "[project]\nname = \"my-server\"\n",
|
|
292
|
+
"package.json": "{\"name\": \"my-server\", \"main\": \"index.js\", \"foro\": {\"runtime\": \"node\"}}",
|
|
293
|
+
"server.py": ""
|
|
294
|
+
},
|
|
295
|
+
"expect": {
|
|
296
|
+
"ok": true
|
|
297
|
+
}
|
|
298
|
+
},
|
|
299
|
+
{
|
|
300
|
+
"name": "no-config-file-only-a-legacy-manifest",
|
|
301
|
+
"files": {
|
|
302
|
+
"foro.yaml": "name: my-server\nentrypoint: server.py\n",
|
|
303
|
+
"server.py": ""
|
|
304
|
+
},
|
|
305
|
+
"expect": {
|
|
306
|
+
"ok": false,
|
|
307
|
+
"reason": "missing_manifest"
|
|
308
|
+
}
|
|
309
|
+
},
|
|
310
|
+
{
|
|
311
|
+
"name": "unknown-field-stale-python-version",
|
|
312
|
+
"files": {
|
|
313
|
+
"pyproject.toml": "[project]\nname = \"my-server\"\n\n[tool.foro]\nentrypoint = \"server.py\"\npython_version = \"3.13\"\n"
|
|
314
|
+
},
|
|
315
|
+
"expect": {
|
|
316
|
+
"ok": false,
|
|
317
|
+
"reason": "unknown_field"
|
|
318
|
+
}
|
|
319
|
+
},
|
|
320
|
+
{
|
|
321
|
+
"name": "unknown-field-typo",
|
|
322
|
+
"files": {
|
|
323
|
+
"pyproject.toml": "[project]\nname = \"my-server\"\n\n[tool.foro]\nentrypoint = \"server.py\"\nentrypoints = \"server.py\"\n"
|
|
324
|
+
},
|
|
123
325
|
"expect": {
|
|
124
326
|
"ok": false,
|
|
125
|
-
"reason": "
|
|
327
|
+
"reason": "unknown_field"
|
|
126
328
|
}
|
|
127
329
|
},
|
|
128
330
|
{
|
|
129
|
-
"name": "
|
|
130
|
-
"
|
|
331
|
+
"name": "unknown-field-name-belongs-to-the-package",
|
|
332
|
+
"files": {
|
|
333
|
+
"pyproject.toml": "[project]\nname = \"my-server\"\n\n[tool.foro]\nentrypoint = \"server.py\"\nname = \"other\"\n"
|
|
334
|
+
},
|
|
335
|
+
"expect": {
|
|
336
|
+
"ok": false,
|
|
337
|
+
"reason": "unknown_field"
|
|
338
|
+
}
|
|
339
|
+
},
|
|
340
|
+
{
|
|
341
|
+
"name": "port-too-low",
|
|
342
|
+
"files": {
|
|
343
|
+
"pyproject.toml": "[project]\nname = \"my-server\"\n\n[tool.foro]\nentrypoint = \"server.py\"\nport = 80\n"
|
|
344
|
+
},
|
|
131
345
|
"expect": {
|
|
132
346
|
"ok": false,
|
|
133
347
|
"reason": "invalid_port"
|
|
134
348
|
}
|
|
135
349
|
},
|
|
136
350
|
{
|
|
137
|
-
"name": "
|
|
138
|
-
"
|
|
351
|
+
"name": "port-too-high",
|
|
352
|
+
"files": {
|
|
353
|
+
"pyproject.toml": "[project]\nname = \"my-server\"\n\n[tool.foro]\nentrypoint = \"server.py\"\nport = 70000\n"
|
|
354
|
+
},
|
|
139
355
|
"expect": {
|
|
140
356
|
"ok": false,
|
|
141
357
|
"reason": "invalid_port"
|
|
142
358
|
}
|
|
143
359
|
},
|
|
144
360
|
{
|
|
145
|
-
"name": "
|
|
146
|
-
"
|
|
361
|
+
"name": "port-sidecar-reserved",
|
|
362
|
+
"files": {
|
|
363
|
+
"pyproject.toml": "[project]\nname = \"my-server\"\n\n[tool.foro]\nentrypoint = \"server.py\"\nport = 8001\n"
|
|
364
|
+
},
|
|
147
365
|
"expect": {
|
|
148
366
|
"ok": false,
|
|
149
367
|
"reason": "invalid_port"
|
|
150
368
|
}
|
|
151
369
|
},
|
|
152
370
|
{
|
|
153
|
-
"name": "
|
|
154
|
-
"
|
|
371
|
+
"name": "port-gate-reserved",
|
|
372
|
+
"files": {
|
|
373
|
+
"pyproject.toml": "[project]\nname = \"my-server\"\n\n[tool.foro]\nentrypoint = \"server.py\"\nport = 8002\n"
|
|
374
|
+
},
|
|
155
375
|
"expect": {
|
|
156
376
|
"ok": false,
|
|
157
377
|
"reason": "invalid_port"
|
|
158
378
|
}
|
|
159
379
|
},
|
|
160
380
|
{
|
|
161
|
-
"name": "
|
|
162
|
-
"
|
|
381
|
+
"name": "port-non-integer",
|
|
382
|
+
"files": {
|
|
383
|
+
"pyproject.toml": "[project]\nname = \"my-server\"\n\n[tool.foro]\nentrypoint = \"server.py\"\nport = \"abc\"\n"
|
|
384
|
+
},
|
|
163
385
|
"expect": {
|
|
164
386
|
"ok": false,
|
|
165
|
-
"reason": "
|
|
387
|
+
"reason": "invalid_port"
|
|
166
388
|
}
|
|
167
389
|
},
|
|
168
390
|
{
|
|
169
|
-
"name": "
|
|
170
|
-
"
|
|
391
|
+
"name": "dependency-manager-outside-allowlist",
|
|
392
|
+
"files": {
|
|
393
|
+
"pyproject.toml": "[project]\nname = \"my-server\"\n\n[tool.foro]\nentrypoint = \"server.py\"\ndependency_manager = \"pipx\"\n"
|
|
394
|
+
},
|
|
171
395
|
"expect": {
|
|
172
396
|
"ok": false,
|
|
173
|
-
"reason": "
|
|
397
|
+
"reason": "invalid_dependency_manager"
|
|
174
398
|
}
|
|
175
399
|
},
|
|
176
400
|
{
|
|
177
|
-
"name": "
|
|
178
|
-
"
|
|
401
|
+
"name": "node-dependency-manager-on-python",
|
|
402
|
+
"files": {
|
|
403
|
+
"pyproject.toml": "[project]\nname = \"my-server\"\n\n[tool.foro]\nentrypoint = \"server.py\"\ndependency_manager = \"npm\"\n"
|
|
404
|
+
},
|
|
179
405
|
"expect": {
|
|
180
406
|
"ok": false,
|
|
181
|
-
"reason": "
|
|
407
|
+
"reason": "invalid_dependency_manager"
|
|
182
408
|
}
|
|
183
409
|
},
|
|
184
410
|
{
|
|
185
|
-
"name": "
|
|
186
|
-
"
|
|
411
|
+
"name": "unparseable-pyproject",
|
|
412
|
+
"files": {
|
|
413
|
+
"pyproject.toml": "[project\nname = \"my-server\"\n"
|
|
414
|
+
},
|
|
187
415
|
"expect": {
|
|
188
416
|
"ok": false,
|
|
189
417
|
"reason": "invalid_shape"
|
|
190
418
|
}
|
|
191
419
|
},
|
|
192
420
|
{
|
|
193
|
-
"name": "
|
|
194
|
-
"
|
|
421
|
+
"name": "node-minimal",
|
|
422
|
+
"files": {
|
|
423
|
+
"package.json": "{\"name\": \"my-server\", \"main\": \"dist/index.js\"}"
|
|
424
|
+
},
|
|
425
|
+
"expect": {
|
|
426
|
+
"ok": true
|
|
427
|
+
}
|
|
428
|
+
},
|
|
429
|
+
{
|
|
430
|
+
"name": "node-scoped-name",
|
|
431
|
+
"files": {
|
|
432
|
+
"package.json": "{\"name\": \"@acme/my-server\", \"main\": \"dist/index.js\"}"
|
|
433
|
+
},
|
|
434
|
+
"expect": {
|
|
435
|
+
"ok": true
|
|
436
|
+
}
|
|
437
|
+
},
|
|
438
|
+
{
|
|
439
|
+
"name": "node-bin-map",
|
|
440
|
+
"files": {
|
|
441
|
+
"package.json": "{\"name\": \"my-server\", \"bin\": {\"my-server\": \"dist/cli.js\"}}"
|
|
442
|
+
},
|
|
443
|
+
"expect": {
|
|
444
|
+
"ok": true
|
|
445
|
+
}
|
|
446
|
+
},
|
|
447
|
+
{
|
|
448
|
+
"name": "node-implicit-index",
|
|
449
|
+
"files": {
|
|
450
|
+
"package.json": "{\"name\": \"my-server\"}",
|
|
451
|
+
"index.js": ""
|
|
452
|
+
},
|
|
453
|
+
"expect": {
|
|
454
|
+
"ok": true
|
|
455
|
+
}
|
|
456
|
+
},
|
|
457
|
+
{
|
|
458
|
+
"name": "node-engines-range",
|
|
459
|
+
"files": {
|
|
460
|
+
"package.json": "{\"name\": \"my-server\", \"main\": \"index.js\", \"engines\": {\"node\": \">=22\"}}"
|
|
461
|
+
},
|
|
462
|
+
"expect": {
|
|
463
|
+
"ok": true
|
|
464
|
+
}
|
|
465
|
+
},
|
|
466
|
+
{
|
|
467
|
+
"name": "node-engines-union-of-every-supported-major",
|
|
468
|
+
"files": {
|
|
469
|
+
"package.json": "{\"name\": \"my-server\", \"main\": \"index.js\", \"engines\": {\"node\": \"^22 || ^24\"}}"
|
|
470
|
+
},
|
|
471
|
+
"expect": {
|
|
472
|
+
"ok": true
|
|
473
|
+
}
|
|
474
|
+
},
|
|
475
|
+
{
|
|
476
|
+
"name": "node-engines-union-with-one-unsupported-branch",
|
|
477
|
+
"files": {
|
|
478
|
+
"package.json": "{\"name\": \"my-server\", \"main\": \"index.js\", \"engines\": {\"node\": \"^20 || ^22\"}}"
|
|
479
|
+
},
|
|
480
|
+
"expect": {
|
|
481
|
+
"ok": true
|
|
482
|
+
}
|
|
483
|
+
},
|
|
484
|
+
{
|
|
485
|
+
"name": "node-all-fields",
|
|
486
|
+
"files": {
|
|
487
|
+
"package.json": "{\"name\": \"my-server\", \"main\": \"dist/index.js\", \"foro\": {\"runtime\": \"node\", \"runtime_version\": \"22\", \"port\": 9000, \"dependency_manager\": \"pnpm\"}}"
|
|
488
|
+
},
|
|
489
|
+
"expect": {
|
|
490
|
+
"ok": true
|
|
491
|
+
}
|
|
492
|
+
},
|
|
493
|
+
{
|
|
494
|
+
"name": "node-no-entry-file",
|
|
495
|
+
"files": {
|
|
496
|
+
"package.json": "{\"name\": \"my-server\"}"
|
|
497
|
+
},
|
|
195
498
|
"expect": {
|
|
196
499
|
"ok": false,
|
|
197
500
|
"reason": "invalid_entrypoint"
|
|
198
501
|
}
|
|
199
502
|
},
|
|
200
503
|
{
|
|
201
|
-
"name": "
|
|
202
|
-
"
|
|
504
|
+
"name": "node-engines-excludes-every-supported-version",
|
|
505
|
+
"files": {
|
|
506
|
+
"package.json": "{\"name\": \"my-server\", \"main\": \"index.js\", \"engines\": {\"node\": \"^20\"}}"
|
|
507
|
+
},
|
|
203
508
|
"expect": {
|
|
204
509
|
"ok": false,
|
|
205
|
-
"reason": "
|
|
510
|
+
"reason": "invalid_runtime_version"
|
|
206
511
|
}
|
|
207
512
|
},
|
|
208
513
|
{
|
|
209
|
-
"name": "
|
|
210
|
-
"
|
|
514
|
+
"name": "node-runtime-version-from-another-runtime",
|
|
515
|
+
"files": {
|
|
516
|
+
"package.json": "{\"name\": \"my-server\", \"main\": \"index.js\", \"foro\": {\"runtime_version\": \"3.12\"}}"
|
|
517
|
+
},
|
|
211
518
|
"expect": {
|
|
212
519
|
"ok": false,
|
|
213
|
-
"reason": "
|
|
520
|
+
"reason": "invalid_runtime_version"
|
|
214
521
|
}
|
|
215
522
|
},
|
|
216
523
|
{
|
|
217
|
-
"name": "
|
|
218
|
-
"
|
|
524
|
+
"name": "python-dependency-manager-on-node",
|
|
525
|
+
"files": {
|
|
526
|
+
"package.json": "{\"name\": \"my-server\", \"main\": \"index.js\", \"foro\": {\"dependency_manager\": \"uv\"}}"
|
|
527
|
+
},
|
|
219
528
|
"expect": {
|
|
220
|
-
"ok":
|
|
529
|
+
"ok": false,
|
|
530
|
+
"reason": "invalid_dependency_manager"
|
|
531
|
+
}
|
|
532
|
+
},
|
|
533
|
+
{
|
|
534
|
+
"name": "unparseable-package-json",
|
|
535
|
+
"files": {
|
|
536
|
+
"package.json": "{\"name\": "
|
|
537
|
+
},
|
|
538
|
+
"expect": {
|
|
539
|
+
"ok": false,
|
|
540
|
+
"reason": "invalid_shape"
|
|
221
541
|
}
|
|
222
542
|
}
|
|
223
543
|
];
|
package/dist/manifest-cases.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The shared
|
|
2
|
+
* The shared project-config validation table.
|
|
3
3
|
*
|
|
4
4
|
* foro's `_manifest.py` is a port of foro-sh/platform's
|
|
5
5
|
* `apps/api/src/services/manifest.ts`, and the two can never be allowed to
|
|
@@ -15,12 +15,14 @@
|
|
|
15
15
|
/** Mirrors `ManifestRejectionReason` in foro-sh/platform's `@foro/types`.
|
|
16
16
|
* Kept in sync deliberately: it is what makes a reason added on one side a
|
|
17
17
|
* compile error on the other. */
|
|
18
|
-
export type ManifestRejectionReason = 'missing_manifest' | 'unsupported_language' | 'invalid_yaml' | 'invalid_shape' | 'invalid_name' | 'invalid_entrypoint' | 'invalid_build_path' | '
|
|
18
|
+
export type ManifestRejectionReason = 'missing_manifest' | 'unsupported_language' | 'invalid_yaml' | 'invalid_shape' | 'invalid_name' | 'invalid_entrypoint' | 'invalid_build_path' | 'invalid_runtime' | 'invalid_runtime_version' | 'invalid_port' | 'invalid_dependency_manager' | 'unsupported_project' | 'unknown_field';
|
|
19
19
|
export interface ManifestCase {
|
|
20
20
|
/** Stable identifier, unique across the table - use it as the test name. */
|
|
21
21
|
readonly name: string;
|
|
22
|
-
/**
|
|
23
|
-
|
|
22
|
+
/** Files to write into an empty directory before validating it, keyed by
|
|
23
|
+
* repo-relative path. Always the config file under test, plus whatever the
|
|
24
|
+
* entry-file inference has to find on disk. */
|
|
25
|
+
readonly files: Readonly<Record<string, string>>;
|
|
24
26
|
readonly expect: {
|
|
25
27
|
readonly ok: true;
|
|
26
28
|
} | {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"manifest-cases.d.ts","sourceRoot":"","sources":["../src/manifest-cases.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAIH;;kCAEkC;AAClC,MAAM,MAAM,uBAAuB,GAC/B,kBAAkB,GAClB,sBAAsB,GACtB,cAAc,GACd,eAAe,GACf,cAAc,GACd,oBAAoB,GACpB,oBAAoB,GACpB,
|
|
1
|
+
{"version":3,"file":"manifest-cases.d.ts","sourceRoot":"","sources":["../src/manifest-cases.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAIH;;kCAEkC;AAClC,MAAM,MAAM,uBAAuB,GAC/B,kBAAkB,GAClB,sBAAsB,GACtB,cAAc,GACd,eAAe,GACf,cAAc,GACd,oBAAoB,GACpB,oBAAoB,GACpB,iBAAiB,GACjB,yBAAyB,GACzB,cAAc,GACd,4BAA4B,GAC5B,qBAAqB,GACrB,eAAe,CAAA;AAEnB,MAAM,WAAW,YAAY;IAC3B,4EAA4E;IAC5E,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB;;oDAEgD;IAChD,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;IAChD,QAAQ,CAAC,MAAM,EACX;QAAE,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAA;KAAE,GACrB;QAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;QAAC,QAAQ,CAAC,MAAM,EAAE,uBAAuB,CAAA;KAAE,CAAA;CACrE;AAED,eAAO,MAAM,aAAa,EAAE,SAAS,YAAY,EAAqB,CAAA"}
|
package/dist/manifest-cases.js
CHANGED