@bleedingdev/modern-js-create 3.4.0-ultramodern.19 → 3.4.0-ultramodern.20
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/dist/cjs/index.cjs +1 -1
- package/dist/cjs/locale/en.cjs +2 -2
- package/dist/cjs/locale/zh.cjs +2 -2
- package/dist/cjs/ultramodern-tooling/config.cjs +9 -91
- package/dist/cjs/ultramodern-workspace/add-vertical.cjs +13 -40
- package/dist/cjs/ultramodern-workspace/contracts.cjs +2 -0
- package/dist/cjs/ultramodern-workspace/demo-components.cjs +12 -12
- package/dist/cjs/ultramodern-workspace/descriptors.cjs +0 -4
- package/dist/cjs/ultramodern-workspace/effect-api.cjs +74 -75
- package/dist/cjs/ultramodern-workspace/mf-validation.cjs +1 -2
- package/dist/cjs/ultramodern-workspace/module-federation.cjs +3 -2
- package/dist/cjs/ultramodern-workspace/package-json.cjs +6 -5
- package/dist/cjs/ultramodern-workspace/policy.cjs +1 -1
- package/dist/cjs/ultramodern-workspace/workspace-scripts.cjs +7 -0
- package/dist/cjs/ultramodern-workspace/write-workspace.cjs +4 -4
- package/dist/esm/index.js +1 -1
- package/dist/esm/locale/en.js +2 -2
- package/dist/esm/locale/zh.js +2 -2
- package/dist/esm/ultramodern-tooling/config.js +10 -92
- package/dist/esm/ultramodern-workspace/add-vertical.js +14 -41
- package/dist/esm/ultramodern-workspace/contracts.js +2 -0
- package/dist/esm/ultramodern-workspace/demo-components.js +12 -12
- package/dist/esm/ultramodern-workspace/descriptors.js +1 -2
- package/dist/esm/ultramodern-workspace/effect-api.js +74 -75
- package/dist/esm/ultramodern-workspace/mf-validation.js +1 -2
- package/dist/esm/ultramodern-workspace/module-federation.js +3 -2
- package/dist/esm/ultramodern-workspace/package-json.js +6 -5
- package/dist/esm/ultramodern-workspace/policy.js +1 -1
- package/dist/esm/ultramodern-workspace/workspace-scripts.js +5 -1
- package/dist/esm/ultramodern-workspace/write-workspace.js +4 -4
- package/dist/esm-node/index.js +1 -1
- package/dist/esm-node/locale/en.js +2 -2
- package/dist/esm-node/locale/zh.js +2 -2
- package/dist/esm-node/ultramodern-tooling/config.js +10 -92
- package/dist/esm-node/ultramodern-workspace/add-vertical.js +14 -41
- package/dist/esm-node/ultramodern-workspace/contracts.js +2 -0
- package/dist/esm-node/ultramodern-workspace/demo-components.js +12 -12
- package/dist/esm-node/ultramodern-workspace/descriptors.js +1 -2
- package/dist/esm-node/ultramodern-workspace/effect-api.js +74 -75
- package/dist/esm-node/ultramodern-workspace/mf-validation.js +1 -2
- package/dist/esm-node/ultramodern-workspace/module-federation.js +3 -2
- package/dist/esm-node/ultramodern-workspace/package-json.js +6 -5
- package/dist/esm-node/ultramodern-workspace/policy.js +1 -1
- package/dist/esm-node/ultramodern-workspace/workspace-scripts.js +5 -1
- package/dist/esm-node/ultramodern-workspace/write-workspace.js +4 -4
- package/dist/types/ultramodern-tooling/config.d.ts +1 -2
- package/dist/types/ultramodern-workspace/descriptors.d.ts +0 -1
- package/dist/types/ultramodern-workspace/workspace-scripts.d.ts +1 -0
- package/package.json +3 -3
- package/templates/workspace-scripts/assert-mf-types.mjs.handlebars +1 -11
- package/templates/workspace-scripts/check-ultramodern-api-boundaries.mjs +339 -0
- package/templates/workspace-scripts/generate-public-surface-assets.mjs +14 -12
- package/templates/workspace-scripts/proof-cloudflare-version.mjs +3 -13
- package/templates/workspace-scripts/ultramodern-cloudflare-proof.mjs +3 -3
- package/templates/workspace-scripts/ultramodern-performance-readiness.mjs +1 -9
- package/templates/workspace-scripts/validate-ultramodern-workspace.mjs.handlebars +82 -86
|
@@ -2,10 +2,6 @@ import fs from 'node:fs';
|
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
|
|
4
4
|
const root = process.cwd();
|
|
5
|
-
const generatedContractPath = path.join(
|
|
6
|
-
root,
|
|
7
|
-
'.modernjs/ultramodern-generated-contract.json',
|
|
8
|
-
);
|
|
9
5
|
const compactConfigPath = path.join(root, '.modernjs/ultramodern.json');
|
|
10
6
|
const defaultAppDirs = {{defaultAppDirsJson}};
|
|
11
7
|
|
|
@@ -53,13 +49,7 @@ function readGeneratedContractView() {
|
|
|
53
49
|
: [],
|
|
54
50
|
};
|
|
55
51
|
}
|
|
56
|
-
|
|
57
|
-
return {
|
|
58
|
-
sourcePath: generatedContractPath,
|
|
59
|
-
...readJson(generatedContractPath),
|
|
60
|
-
};
|
|
61
|
-
}
|
|
62
|
-
return undefined;
|
|
52
|
+
throw new Error('Missing UltraModern config. Expected .modernjs/ultramodern.json.');
|
|
63
53
|
}
|
|
64
54
|
|
|
65
55
|
const generatedContract = readGeneratedContractView();
|
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
|
|
5
|
+
const workspaceRoot = process.env.ULTRAMODERN_WORKSPACE_ROOT ?? process.cwd();
|
|
6
|
+
const failures = [];
|
|
7
|
+
|
|
8
|
+
const ignoredDirectories = new Set([
|
|
9
|
+
'.git',
|
|
10
|
+
'.modern',
|
|
11
|
+
'.output',
|
|
12
|
+
'coverage',
|
|
13
|
+
'dist',
|
|
14
|
+
'node_modules',
|
|
15
|
+
'repos',
|
|
16
|
+
]);
|
|
17
|
+
|
|
18
|
+
function normalize(filePath) {
|
|
19
|
+
return filePath.split(path.sep).join('/');
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function relative(filePath) {
|
|
23
|
+
return normalize(path.relative(workspaceRoot, filePath));
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function exists(relativePath) {
|
|
27
|
+
return fs.existsSync(path.join(workspaceRoot, relativePath));
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function readText(relativePath) {
|
|
31
|
+
return fs.readFileSync(path.join(workspaceRoot, relativePath), 'utf8');
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function fail(message) {
|
|
35
|
+
failures.push(message);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function assert(condition, message) {
|
|
39
|
+
if (!condition) {
|
|
40
|
+
fail(message);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function listFiles(startDirectory) {
|
|
45
|
+
const absoluteStart = path.join(workspaceRoot, startDirectory);
|
|
46
|
+
if (!fs.existsSync(absoluteStart)) {
|
|
47
|
+
return [];
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const files = [];
|
|
51
|
+
const visit = directory => {
|
|
52
|
+
for (const entry of fs.readdirSync(directory, { withFileTypes: true })) {
|
|
53
|
+
if (ignoredDirectories.has(entry.name)) {
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const absoluteEntry = path.join(directory, entry.name);
|
|
58
|
+
if (entry.isDirectory()) {
|
|
59
|
+
visit(absoluteEntry);
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (entry.isFile()) {
|
|
64
|
+
files.push(relative(absoluteEntry));
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
visit(absoluteStart);
|
|
70
|
+
return files;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function listDirectories(startDirectory) {
|
|
74
|
+
const absoluteStart = path.join(workspaceRoot, startDirectory);
|
|
75
|
+
if (!fs.existsSync(absoluteStart)) {
|
|
76
|
+
return [];
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return fs
|
|
80
|
+
.readdirSync(absoluteStart, { withFileTypes: true })
|
|
81
|
+
.filter(entry => entry.isDirectory() && !ignoredDirectories.has(entry.name))
|
|
82
|
+
.map(entry => path.posix.join(startDirectory, entry.name));
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function assertNoPath(relativePath, message) {
|
|
86
|
+
if (exists(relativePath)) {
|
|
87
|
+
fail(message);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function assertContains(relativePath, content, pattern, message) {
|
|
92
|
+
assert(pattern.test(content), `${relativePath}: ${message}`);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function assertNotContains(relativePath, content, pattern, message) {
|
|
96
|
+
assert(!pattern.test(content), `${relativePath}: ${message}`);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const generatedFiles = [
|
|
100
|
+
...listFiles('apps'),
|
|
101
|
+
...listFiles('verticals'),
|
|
102
|
+
...listFiles('packages'),
|
|
103
|
+
];
|
|
104
|
+
|
|
105
|
+
for (const forbiddenPath of [
|
|
106
|
+
'apps/shell-super-app/src/effect',
|
|
107
|
+
...listDirectories('verticals').flatMap(verticalPath => [
|
|
108
|
+
`${verticalPath}/api/effect`,
|
|
109
|
+
`${verticalPath}/api/lambda`,
|
|
110
|
+
`${verticalPath}/shared/effect`,
|
|
111
|
+
`${verticalPath}/src/effect`,
|
|
112
|
+
]),
|
|
113
|
+
]) {
|
|
114
|
+
assertNoPath(
|
|
115
|
+
forbiddenPath,
|
|
116
|
+
`${forbiddenPath} is forbidden in UltraModern strictEffectApproach workspaces; use api/index.ts, shared/api.ts and src/api/* instead.`,
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const textFiles = generatedFiles.filter(file =>
|
|
121
|
+
/\.(?:[cm]?[jt]sx?|json|md|mjs|mts|cts)$/u.test(file),
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
for (const file of textFiles) {
|
|
125
|
+
const content = readText(file);
|
|
126
|
+
|
|
127
|
+
if (/\/api\//u.test(file)) {
|
|
128
|
+
assertNotContains(
|
|
129
|
+
file,
|
|
130
|
+
content,
|
|
131
|
+
/\bnew\s+Response\s*\(|\bResponse\.json\s*\(/u,
|
|
132
|
+
'API modules must not hand-build Response objects; model the endpoint through Effect HttpApi and schemas.',
|
|
133
|
+
);
|
|
134
|
+
assertNotContains(
|
|
135
|
+
file,
|
|
136
|
+
content,
|
|
137
|
+
/\brequest\.(?:json|text|formData|arrayBuffer)\s*\(/u,
|
|
138
|
+
'API modules must not manually parse request bodies; use HttpApiEndpoint payload/query/params schemas.',
|
|
139
|
+
);
|
|
140
|
+
assertNotContains(
|
|
141
|
+
file,
|
|
142
|
+
content,
|
|
143
|
+
/\bexport\s+const\s+handler\b|\bexport\s+default\s+async\b/u,
|
|
144
|
+
'API modules must not export raw request handlers; export defineEffectBff(...) from api/index.ts.',
|
|
145
|
+
);
|
|
146
|
+
assertNotContains(
|
|
147
|
+
file,
|
|
148
|
+
content,
|
|
149
|
+
/\bcreateHandler\s*[:=]\s*(?!defineEffectBff\b)/u,
|
|
150
|
+
'API modules must not define unbranded handler factories; use defineEffectBff(...).',
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
assertNotContains(
|
|
155
|
+
file,
|
|
156
|
+
content,
|
|
157
|
+
/@modern-js\/plugin-bff\/hono-server/u,
|
|
158
|
+
'Hono server helpers are not part of generated UltraModern API workspaces.',
|
|
159
|
+
);
|
|
160
|
+
assertNotContains(
|
|
161
|
+
file,
|
|
162
|
+
content,
|
|
163
|
+
/runtimeFramework:\s*['"]hono['"]/u,
|
|
164
|
+
'Generated UltraModern API apps must use the Effect runtime.',
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const shellClient = 'apps/shell-super-app/src/api/vertical-clients.ts';
|
|
169
|
+
if (exists('apps/shell-super-app')) {
|
|
170
|
+
assert(exists(shellClient), `${shellClient} must aggregate vertical API clients.`);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
for (const verticalPath of listDirectories('verticals')) {
|
|
174
|
+
const apiEntry = `${verticalPath}/api/index.ts`;
|
|
175
|
+
const sharedApi = `${verticalPath}/shared/api.ts`;
|
|
176
|
+
const srcApiDirectory = `${verticalPath}/src/api`;
|
|
177
|
+
const modernConfig = `${verticalPath}/modern.config.ts`;
|
|
178
|
+
const packageJsonPath = `${verticalPath}/package.json`;
|
|
179
|
+
|
|
180
|
+
assert(exists(apiEntry), `${apiEntry} is required.`);
|
|
181
|
+
assert(exists(sharedApi), `${sharedApi} is required.`);
|
|
182
|
+
assert(exists(srcApiDirectory), `${srcApiDirectory} is required.`);
|
|
183
|
+
|
|
184
|
+
if (exists(srcApiDirectory)) {
|
|
185
|
+
const clientFiles = listFiles(srcApiDirectory).filter(file =>
|
|
186
|
+
/-client\.ts$/u.test(file),
|
|
187
|
+
);
|
|
188
|
+
assert(
|
|
189
|
+
clientFiles.length > 0,
|
|
190
|
+
`${srcApiDirectory} must contain a generated API client.`,
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (exists(apiEntry)) {
|
|
195
|
+
const entry = readText(apiEntry);
|
|
196
|
+
assertContains(
|
|
197
|
+
apiEntry,
|
|
198
|
+
entry,
|
|
199
|
+
/\bdefineEffectBff\b/u,
|
|
200
|
+
'must export a defineEffectBff(...) runtime definition.',
|
|
201
|
+
);
|
|
202
|
+
assertContains(
|
|
203
|
+
apiEntry,
|
|
204
|
+
entry,
|
|
205
|
+
/\bHttpApiBuilder\b/u,
|
|
206
|
+
'must implement handlers through HttpApiBuilder.',
|
|
207
|
+
);
|
|
208
|
+
assertContains(
|
|
209
|
+
apiEntry,
|
|
210
|
+
entry,
|
|
211
|
+
/\bLayer\b/u,
|
|
212
|
+
'must compose dependencies with Effect Layer.',
|
|
213
|
+
);
|
|
214
|
+
assertContains(
|
|
215
|
+
apiEntry,
|
|
216
|
+
entry,
|
|
217
|
+
/from ['"]\.\.\/shared\/api\.ts['"]/u,
|
|
218
|
+
'must import the contract from ../shared/api.ts.',
|
|
219
|
+
);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
if (exists(sharedApi)) {
|
|
223
|
+
const contract = readText(sharedApi);
|
|
224
|
+
assertContains(
|
|
225
|
+
sharedApi,
|
|
226
|
+
contract,
|
|
227
|
+
/\bHttpApi\.make\b/u,
|
|
228
|
+
'must declare the HttpApi contract.',
|
|
229
|
+
);
|
|
230
|
+
assertContains(
|
|
231
|
+
sharedApi,
|
|
232
|
+
contract,
|
|
233
|
+
/\bHttpApiGroup\.make\b/u,
|
|
234
|
+
'must declare HttpApi groups.',
|
|
235
|
+
);
|
|
236
|
+
assertContains(
|
|
237
|
+
sharedApi,
|
|
238
|
+
contract,
|
|
239
|
+
/\bHttpApiEndpoint\./u,
|
|
240
|
+
'must declare endpoints through HttpApiEndpoint.',
|
|
241
|
+
);
|
|
242
|
+
assertContains(
|
|
243
|
+
sharedApi,
|
|
244
|
+
contract,
|
|
245
|
+
/\bSchema\./u,
|
|
246
|
+
'must use Schema for request, response and error shapes.',
|
|
247
|
+
);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
if (exists(modernConfig)) {
|
|
251
|
+
const config = readText(modernConfig);
|
|
252
|
+
assertContains(
|
|
253
|
+
modernConfig,
|
|
254
|
+
config,
|
|
255
|
+
/runtimeFramework:\s*['"]effect['"]/u,
|
|
256
|
+
'must use bff.runtimeFramework: effect.',
|
|
257
|
+
);
|
|
258
|
+
assertContains(
|
|
259
|
+
modernConfig,
|
|
260
|
+
config,
|
|
261
|
+
/entry:\s*['"]\.\/api\/index['"]/u,
|
|
262
|
+
'must point bff.effect.entry at ./api/index.',
|
|
263
|
+
);
|
|
264
|
+
assertContains(
|
|
265
|
+
modernConfig,
|
|
266
|
+
config,
|
|
267
|
+
/strictEffectApproach:\s*true/u,
|
|
268
|
+
'must enable strictEffectApproach explicitly.',
|
|
269
|
+
);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
if (exists(packageJsonPath)) {
|
|
273
|
+
const packageJson = JSON.parse(readText(packageJsonPath));
|
|
274
|
+
assert(
|
|
275
|
+
packageJson.exports?.['./api'] === './shared/api.ts',
|
|
276
|
+
`${packageJsonPath}: package must export ./api from shared/api.ts.`,
|
|
277
|
+
);
|
|
278
|
+
assert(
|
|
279
|
+
typeof packageJson.exports?.['./api/client'] === 'string' &&
|
|
280
|
+
packageJson.exports['./api/client'].startsWith('./src/api/'),
|
|
281
|
+
`${packageJsonPath}: package must export ./api/client from src/api/*.`,
|
|
282
|
+
);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
if (exists('apps/shell-super-app/package.json')) {
|
|
287
|
+
const shellPackageJson = JSON.parse(
|
|
288
|
+
readText('apps/shell-super-app/package.json'),
|
|
289
|
+
);
|
|
290
|
+
assert(
|
|
291
|
+
shellPackageJson.exports?.['./api/clients'] ===
|
|
292
|
+
'./src/api/vertical-clients.ts',
|
|
293
|
+
'apps/shell-super-app/package.json must export ./api/clients.',
|
|
294
|
+
);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
if (exists('package.json')) {
|
|
298
|
+
const rootPackageJson = JSON.parse(readText('package.json'));
|
|
299
|
+
assert(
|
|
300
|
+
rootPackageJson.scripts?.['api:check'] ===
|
|
301
|
+
'node ./scripts/check-ultramodern-api-boundaries.mjs',
|
|
302
|
+
'Root package.json must expose api:check.',
|
|
303
|
+
);
|
|
304
|
+
assert(
|
|
305
|
+
rootPackageJson.scripts?.check?.includes('pnpm api:check'),
|
|
306
|
+
'Root check script must include pnpm api:check.',
|
|
307
|
+
);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
if (exists('topology/reference-topology.json')) {
|
|
311
|
+
const topology = JSON.parse(readText('topology/reference-topology.json'));
|
|
312
|
+
for (const vertical of topology.verticals ?? []) {
|
|
313
|
+
if (vertical.api?.runtime === 'effect') {
|
|
314
|
+
assert(
|
|
315
|
+
vertical.api.bff?.strictEffectApproach === true,
|
|
316
|
+
`${vertical.id} topology must mark strictEffectApproach as true.`,
|
|
317
|
+
);
|
|
318
|
+
assert(
|
|
319
|
+
typeof vertical.api.serverEntry === 'string' &&
|
|
320
|
+
vertical.api.serverEntry.endsWith('/api/index.ts'),
|
|
321
|
+
`${vertical.id} topology must use api/index.ts as the server entry.`,
|
|
322
|
+
);
|
|
323
|
+
}
|
|
324
|
+
assert(
|
|
325
|
+
!vertical.api?.effect,
|
|
326
|
+
`${vertical.id} topology must describe the API directly, not under api.effect.`,
|
|
327
|
+
);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
if (failures.length > 0) {
|
|
332
|
+
console.error('UltraModern API boundary check failed:');
|
|
333
|
+
for (const failure of failures) {
|
|
334
|
+
console.error(`- ${failure}`);
|
|
335
|
+
}
|
|
336
|
+
process.exit(1);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
console.log('UltraModern API boundary check passed.');
|
|
@@ -7,10 +7,6 @@ const workspaceRoot = path.resolve(
|
|
|
7
7
|
process.env.ULTRAMODERN_WORKSPACE_ROOT ??
|
|
8
8
|
path.join(path.dirname(fileURLToPath(import.meta.url)), '..'),
|
|
9
9
|
);
|
|
10
|
-
const contractPath = path.join(
|
|
11
|
-
workspaceRoot,
|
|
12
|
-
'.modernjs/ultramodern-generated-contract.json',
|
|
13
|
-
);
|
|
14
10
|
const compactConfigPath = path.join(workspaceRoot, '.modernjs/ultramodern.json');
|
|
15
11
|
|
|
16
12
|
function readJson(filePath) {
|
|
@@ -107,6 +103,10 @@ function normalizeCompactApp(rawApp) {
|
|
|
107
103
|
marker: {
|
|
108
104
|
appId: id,
|
|
109
105
|
},
|
|
106
|
+
routes:
|
|
107
|
+
rawApp.routes && typeof rawApp.routes === 'object'
|
|
108
|
+
? rawApp.routes
|
|
109
|
+
: undefined,
|
|
110
110
|
effectApi:
|
|
111
111
|
rawApp.effectApi && typeof rawApp.effectApi === 'object'
|
|
112
112
|
? {
|
|
@@ -165,7 +165,7 @@ function createPublicRoutes(app) {
|
|
|
165
165
|
|
|
166
166
|
function createPublicSurface(app) {
|
|
167
167
|
const publicRoutes = createPublicRoutes(app);
|
|
168
|
-
|
|
168
|
+
const basePublicSurface = {
|
|
169
169
|
authoring: 'colocated-route-meta',
|
|
170
170
|
artifactLifecycle: 'build-and-deploy-output',
|
|
171
171
|
generatedManifest: './src/routes/ultramodern-route-metadata',
|
|
@@ -195,6 +195,14 @@ function createPublicSurface(app) {
|
|
|
195
195
|
routeEntries: [],
|
|
196
196
|
concreteUrlPaths: [],
|
|
197
197
|
};
|
|
198
|
+
|
|
199
|
+
return app.routes?.publicSurface &&
|
|
200
|
+
typeof app.routes.publicSurface === 'object'
|
|
201
|
+
? {
|
|
202
|
+
...basePublicSurface,
|
|
203
|
+
...app.routes.publicSurface,
|
|
204
|
+
}
|
|
205
|
+
: basePublicSurface;
|
|
198
206
|
}
|
|
199
207
|
|
|
200
208
|
function createCloudflareDeploy(config, app) {
|
|
@@ -237,17 +245,11 @@ function readGeneratedContractView() {
|
|
|
237
245
|
if (fs.existsSync(compactConfigPath)) {
|
|
238
246
|
return synthesizeContractFromCompactConfig(readJson(compactConfigPath));
|
|
239
247
|
}
|
|
240
|
-
if (fs.existsSync(contractPath)) {
|
|
241
|
-
return {
|
|
242
|
-
sourcePath: contractPath,
|
|
243
|
-
...readJson(contractPath),
|
|
244
|
-
};
|
|
245
|
-
}
|
|
246
248
|
throw new Error(
|
|
247
249
|
`Missing UltraModern config. Expected ${path.relative(
|
|
248
250
|
workspaceRoot,
|
|
249
251
|
compactConfigPath,
|
|
250
|
-
)}
|
|
252
|
+
)}.`,
|
|
251
253
|
);
|
|
252
254
|
}
|
|
253
255
|
|
|
@@ -9,10 +9,6 @@ const workspaceRoot = path.resolve(
|
|
|
9
9
|
process.env.ULTRAMODERN_WORKSPACE_ROOT ??
|
|
10
10
|
path.join(path.dirname(fileURLToPath(import.meta.url)), '..'),
|
|
11
11
|
);
|
|
12
|
-
const contractPath = path.join(
|
|
13
|
-
workspaceRoot,
|
|
14
|
-
'.modernjs/ultramodern-generated-contract.json',
|
|
15
|
-
);
|
|
16
12
|
const compactConfigPath = path.join(workspaceRoot, '.modernjs/ultramodern.json');
|
|
17
13
|
const defaultOut = path.join(
|
|
18
14
|
workspaceRoot,
|
|
@@ -219,7 +215,7 @@ function createCloudflareRoutes(app) {
|
|
|
219
215
|
locale: `/locales/en/${appNamespace(app)}.json`,
|
|
220
216
|
...(app.effectApi
|
|
221
217
|
? {
|
|
222
|
-
|
|
218
|
+
apiReadiness: `${app.effectApi.prefix}/${app.effectApi.stem}/readiness`,
|
|
223
219
|
}
|
|
224
220
|
: {}),
|
|
225
221
|
};
|
|
@@ -285,17 +281,11 @@ function readGeneratedContractView() {
|
|
|
285
281
|
if (fs.existsSync(compactConfigPath)) {
|
|
286
282
|
return synthesizeContractFromCompactConfig(readJson(compactConfigPath));
|
|
287
283
|
}
|
|
288
|
-
if (fs.existsSync(contractPath)) {
|
|
289
|
-
return {
|
|
290
|
-
sourcePath: contractPath,
|
|
291
|
-
...readJson(contractPath),
|
|
292
|
-
};
|
|
293
|
-
}
|
|
294
284
|
throw new Error(
|
|
295
285
|
`Missing UltraModern config. Expected ${path.relative(
|
|
296
286
|
workspaceRoot,
|
|
297
287
|
compactConfigPath,
|
|
298
|
-
)}
|
|
288
|
+
)}.`,
|
|
299
289
|
);
|
|
300
290
|
}
|
|
301
291
|
|
|
@@ -379,7 +369,7 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
379
369
|
schemaVersion: 1,
|
|
380
370
|
generatedAt: new Date().toISOString(),
|
|
381
371
|
status: results.length > 0 ? 'pass' : 'skipped',
|
|
382
|
-
contractPath: contract.sourcePath ??
|
|
372
|
+
contractPath: contract.sourcePath ?? compactConfigPath,
|
|
383
373
|
results,
|
|
384
374
|
skipped,
|
|
385
375
|
};
|
|
@@ -840,13 +840,13 @@ async function validateI18nEvidence(evidence, app, publicUrl, routes) {
|
|
|
840
840
|
}
|
|
841
841
|
|
|
842
842
|
async function validateReadinessEvidence(evidence, app, publicUrl, routes) {
|
|
843
|
-
if (routes.
|
|
844
|
-
const readiness = await fetchText(joinUrl(publicUrl, routes.
|
|
843
|
+
if (routes.apiReadiness) {
|
|
844
|
+
const readiness = await fetchText(joinUrl(publicUrl, routes.apiReadiness));
|
|
845
845
|
const readinessJson = parseMaybeJson(readiness.body);
|
|
846
846
|
const apiMarker = markerFromJson(readinessJson);
|
|
847
847
|
evidence.assertions.push({
|
|
848
848
|
type: 'api-marker',
|
|
849
|
-
route: routes.
|
|
849
|
+
route: routes.apiReadiness,
|
|
850
850
|
expected: app.marker?.build,
|
|
851
851
|
actual: apiMarker,
|
|
852
852
|
status: readiness.ok && apiMarker === app.marker?.build ? 'pass' : 'fail',
|
|
@@ -7,7 +7,6 @@ const defaultReportPath =
|
|
|
7
7
|
'.codex/reports/performance-readiness/ultramodern-performance-readiness.json';
|
|
8
8
|
const configPath = 'scripts/ultramodern-performance-readiness.config.mjs';
|
|
9
9
|
const compactConfigPath = '.modernjs/ultramodern.json';
|
|
10
|
-
const legacyContractPath = '.modernjs/ultramodern-generated-contract.json';
|
|
11
10
|
const optOutEnv = 'ULTRAMODERN_PERFORMANCE_READINESS_DIAGNOSTICS';
|
|
12
11
|
const signalIds = [
|
|
13
12
|
'bfcache',
|
|
@@ -219,15 +218,8 @@ const readGeneratedContractView = () => {
|
|
|
219
218
|
};
|
|
220
219
|
}
|
|
221
220
|
|
|
222
|
-
if (exists(legacyContractPath)) {
|
|
223
|
-
return {
|
|
224
|
-
sourcePath: legacyContractPath,
|
|
225
|
-
...readJson(legacyContractPath),
|
|
226
|
-
};
|
|
227
|
-
}
|
|
228
|
-
|
|
229
221
|
throw new Error(
|
|
230
|
-
`Missing UltraModern config. Expected ${compactConfigPath}
|
|
222
|
+
`Missing UltraModern config. Expected ${compactConfigPath}.`,
|
|
231
223
|
);
|
|
232
224
|
};
|
|
233
225
|
|