@bleedingdev/modern-js-ultramodern-create 3.9.0-ultramodern.2 → 3.9.0-ultramodern.4
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/ultramodern-package-source.cjs +2 -1
- package/dist/cjs/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts-build-identity.cjs +5 -2
- package/dist/cjs/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts-modern-configs.cjs +5 -0
- package/dist/cjs/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts-typescript.cjs +1 -0
- package/dist/cjs/ultramodern-tooling/commands/migrate-strict-effect/package-cohort.cjs +30 -9
- package/dist/cjs/ultramodern-tooling/commands/migrate-strict-effect/pnpm-policy.cjs +9 -13
- package/dist/cjs/ultramodern-tooling/commands/migrate-strict-effect/pnpm-yaml.cjs +30 -7
- package/dist/cjs/ultramodern-tooling/commands/migrate-strict-effect/shared-api-infrastructure.cjs +99 -0
- package/dist/cjs/ultramodern-tooling/commands/migrate-strict-effect/toolchain-pins.cjs +14 -5
- package/dist/cjs/ultramodern-tooling/commands/migrate-strict-effect/workspace-artifact-ownership.cjs +215 -0
- package/dist/cjs/ultramodern-tooling/commands/migrate-strict-effect.cjs +72 -8
- package/dist/cjs/ultramodern-workspace/api/client.cjs +2 -2
- package/dist/cjs/ultramodern-workspace/api/service.cjs +45 -16
- package/dist/cjs/ultramodern-workspace/api/shared.cjs +26 -16
- package/dist/cjs/ultramodern-workspace/mf-validation/inspect.cjs +6 -2
- package/dist/cjs/ultramodern-workspace/module-federation/reexport-module.cjs +18 -51
- package/dist/cjs/ultramodern-workspace/package-json.cjs +13 -1
- package/dist/cjs/ultramodern-workspace/policy.cjs +1 -0
- package/dist/cjs/ultramodern-workspace/tsconfigs.cjs +3 -0
- package/dist/cjs/ultramodern-workspace/workspace-script-plan.cjs +1 -1
- package/dist/cjs/ultramodern-workspace/workspace-scripts.cjs +5 -0
- package/dist/cjs/ultramodern-workspace/workspace-validation-contract.cjs +2 -1
- package/dist/cjs/ultramodern-workspace/write-app.cjs +10 -4
- package/dist/cjs/ultramodern-workspace/write-workspace.cjs +5 -3
- package/dist/esm/ultramodern-package-source.js +2 -1
- package/dist/esm/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts-build-identity.js +5 -2
- package/dist/esm/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts-modern-configs.js +5 -0
- package/dist/esm/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts-typescript.js +1 -0
- package/dist/esm/ultramodern-tooling/commands/migrate-strict-effect/package-cohort.js +30 -9
- package/dist/esm/ultramodern-tooling/commands/migrate-strict-effect/pnpm-policy.js +9 -13
- package/dist/esm/ultramodern-tooling/commands/migrate-strict-effect/pnpm-yaml.js +30 -7
- package/dist/esm/ultramodern-tooling/commands/migrate-strict-effect/shared-api-infrastructure.js +50 -0
- package/dist/esm/ultramodern-tooling/commands/migrate-strict-effect/toolchain-pins.js +14 -5
- package/dist/esm/ultramodern-tooling/commands/migrate-strict-effect/workspace-artifact-ownership.js +166 -0
- package/dist/esm/ultramodern-tooling/commands/migrate-strict-effect.js +73 -9
- package/dist/esm/ultramodern-workspace/api/client.js +2 -2
- package/dist/esm/ultramodern-workspace/api/service.js +45 -16
- package/dist/esm/ultramodern-workspace/api/shared.js +27 -17
- package/dist/esm/ultramodern-workspace/mf-validation/inspect.js +6 -2
- package/dist/esm/ultramodern-workspace/module-federation/reexport-module.js +18 -51
- package/dist/esm/ultramodern-workspace/package-json.js +13 -1
- package/dist/esm/ultramodern-workspace/policy.js +1 -0
- package/dist/esm/ultramodern-workspace/tsconfigs.js +3 -0
- package/dist/esm/ultramodern-workspace/workspace-script-plan.js +1 -1
- package/dist/esm/ultramodern-workspace/workspace-scripts.js +5 -0
- package/dist/esm/ultramodern-workspace/workspace-validation-contract.js +2 -1
- package/dist/esm/ultramodern-workspace/write-app.js +10 -4
- package/dist/esm/ultramodern-workspace/write-workspace.js +6 -4
- package/dist/esm-node/ultramodern-package-source.js +2 -1
- package/dist/esm-node/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts-build-identity.js +5 -2
- package/dist/esm-node/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts-modern-configs.js +5 -0
- package/dist/esm-node/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts-typescript.js +1 -0
- package/dist/esm-node/ultramodern-tooling/commands/migrate-strict-effect/package-cohort.js +30 -9
- package/dist/esm-node/ultramodern-tooling/commands/migrate-strict-effect/pnpm-policy.js +9 -13
- package/dist/esm-node/ultramodern-tooling/commands/migrate-strict-effect/pnpm-yaml.js +30 -7
- package/dist/esm-node/ultramodern-tooling/commands/migrate-strict-effect/shared-api-infrastructure.js +51 -0
- package/dist/esm-node/ultramodern-tooling/commands/migrate-strict-effect/toolchain-pins.js +14 -5
- package/dist/esm-node/ultramodern-tooling/commands/migrate-strict-effect/workspace-artifact-ownership.js +167 -0
- package/dist/esm-node/ultramodern-tooling/commands/migrate-strict-effect.js +73 -9
- package/dist/esm-node/ultramodern-workspace/api/client.js +2 -2
- package/dist/esm-node/ultramodern-workspace/api/service.js +45 -16
- package/dist/esm-node/ultramodern-workspace/api/shared.js +27 -17
- package/dist/esm-node/ultramodern-workspace/mf-validation/inspect.js +6 -2
- package/dist/esm-node/ultramodern-workspace/module-federation/reexport-module.js +18 -51
- package/dist/esm-node/ultramodern-workspace/package-json.js +13 -1
- package/dist/esm-node/ultramodern-workspace/policy.js +1 -0
- package/dist/esm-node/ultramodern-workspace/tsconfigs.js +3 -0
- package/dist/esm-node/ultramodern-workspace/workspace-script-plan.js +1 -1
- package/dist/esm-node/ultramodern-workspace/workspace-scripts.js +5 -0
- package/dist/esm-node/ultramodern-workspace/workspace-validation-contract.js +2 -1
- package/dist/esm-node/ultramodern-workspace/write-app.js +10 -4
- package/dist/esm-node/ultramodern-workspace/write-workspace.js +6 -4
- package/dist/types/ultramodern-package-source.d.ts +1 -1
- package/dist/types/ultramodern-tooling/commands/migrate-strict-effect/package-cohort.d.ts +3 -1
- package/dist/types/ultramodern-tooling/commands/migrate-strict-effect/pnpm-yaml.d.ts +7 -0
- package/dist/types/ultramodern-tooling/commands/migrate-strict-effect/shared-api-infrastructure.d.ts +4 -0
- package/dist/types/ultramodern-tooling/commands/migrate-strict-effect/workspace-artifact-ownership.d.ts +27 -0
- package/dist/types/ultramodern-workspace/api/client.d.ts +3 -1
- package/dist/types/ultramodern-workspace/api/service.d.ts +3 -1
- package/dist/types/ultramodern-workspace/api/shared.d.ts +2 -0
- package/dist/types/ultramodern-workspace/module-federation/reexport-module.d.ts +2 -2
- package/dist/types/ultramodern-workspace/package-json.d.ts +1 -1
- package/dist/types/ultramodern-workspace/policy.d.ts +2 -0
- package/dist/types/ultramodern-workspace/workspace-validation-contract.d.ts +1 -1
- package/package.json +5 -4
- package/template-workspace/.modernjs/release-cohort.json +40 -40
- package/template-workspace/scripts/bootstrap-agent-skills.mjs +4 -4
- package/templates/packages/effect-bff-runtime.ts +50 -0
- package/templates/packages/microvertical-api-baseline.ts +102 -0
- package/templates/workspace-scripts/check-ultramodern-api-boundaries.mts +49 -28
- package/templates/workspace-scripts/microvertical-api-baseline-boundary.mts +944 -0
- package/templates/workspace-scripts/ultramodern-cloudflare-proof.mjs +25 -7
- package/templates/workspace-scripts/validate-ultramodern-workspace.mjs.handlebars +1 -1
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
import node_fs from "node:fs";
|
|
2
2
|
import node_path from "node:path";
|
|
3
|
+
import { semver } from "@modern-js/utils";
|
|
3
4
|
import { ULTRAMODERN_PACKAGE_PINS, ULTRAMODERN_WORKSPACE_POLICY } from "../../../ultramodern-workspace/policy.js";
|
|
4
5
|
const { toolchain: toolchain } = ULTRAMODERN_WORKSPACE_POLICY;
|
|
5
6
|
const retiredRootToolingDependencies = [
|
|
6
7
|
"@typescript/typescript6"
|
|
7
8
|
];
|
|
9
|
+
function migrationPnpmVersion(value) {
|
|
10
|
+
const baseline = toolchain.packageManager.version;
|
|
11
|
+
return 'string' == typeof value && semver.valid(value) && semver.major(value) === semver.major(baseline) && semver.gte(value, baseline) ? value : baseline;
|
|
12
|
+
}
|
|
8
13
|
function isRecord(value) {
|
|
9
14
|
return Boolean(value) && 'object' == typeof value && !Array.isArray(value);
|
|
10
15
|
}
|
|
@@ -22,22 +27,24 @@ function updateUltramodernConfigToolchain(config) {
|
|
|
22
27
|
const workspace = ensureRecord(config, 'workspace', 'workspace');
|
|
23
28
|
const packageManager = ensureRecord(workspace, 'packageManager', 'workspace.packageManager');
|
|
24
29
|
packageManager.name = toolchain.packageManager.name;
|
|
25
|
-
packageManager.version =
|
|
30
|
+
packageManager.version = migrationPnpmVersion(packageManager.version);
|
|
26
31
|
const node = ensureRecord(workspace, 'node', 'workspace.node');
|
|
27
32
|
node.version = toolchain.node.version;
|
|
28
33
|
node.engineRange = toolchain.node.engineRange;
|
|
29
34
|
}
|
|
30
35
|
function updateRootPackageToolchain(packageJson) {
|
|
31
|
-
packageJson.packageManager
|
|
36
|
+
const currentVersion = 'string' == typeof packageJson.packageManager ? packageJson.packageManager.match(/^pnpm@([^+]+)(?:\+.*)?$/u)?.[1] : void 0;
|
|
37
|
+
packageJson.packageManager = `${toolchain.packageManager.name}@${migrationPnpmVersion(currentVersion)}`;
|
|
32
38
|
const engines = ensureRecord(packageJson, 'engines', 'package.json engines');
|
|
33
39
|
engines.node = toolchain.node.engineRange;
|
|
34
40
|
engines.pnpm = toolchain.packageManager.engineRange;
|
|
35
41
|
const devDependencies = ensureRecord(packageJson, 'devDependencies', 'package.json devDependencies');
|
|
36
42
|
for (const packageName of retiredRootToolingDependencies)delete devDependencies[packageName];
|
|
37
43
|
devDependencies["@typescript/native"] = ULTRAMODERN_PACKAGE_PINS.rootDevDependencies["@typescript/native"];
|
|
44
|
+
devDependencies['@types/node'] = ULTRAMODERN_PACKAGE_PINS.rootDevDependencies["@types/node"];
|
|
38
45
|
devDependencies.miniflare = ULTRAMODERN_PACKAGE_PINS.rootDevDependencies.miniflare;
|
|
39
46
|
}
|
|
40
|
-
function updateMiseTools(content) {
|
|
47
|
+
function updateMiseTools(content, pnpmVersion) {
|
|
41
48
|
const lines = content.replace(/\r\n/gu, '\n').split('\n');
|
|
42
49
|
if ('' === lines.at(-1)) lines.pop();
|
|
43
50
|
let toolsStart = lines.findIndex((line)=>/^\s*\[tools\]\s*(?:#.*)?$/u.test(line));
|
|
@@ -64,13 +71,15 @@ function updateMiseTools(content) {
|
|
|
64
71
|
lines[index] = line;
|
|
65
72
|
};
|
|
66
73
|
upsertTool('node', toolchain.node.version);
|
|
67
|
-
upsertTool('pnpm',
|
|
74
|
+
upsertTool('pnpm', pnpmVersion);
|
|
68
75
|
return `${lines.join('\n')}\n`;
|
|
69
76
|
}
|
|
70
77
|
function updateGeneratedToolchainFiles(io) {
|
|
71
78
|
const misePath = node_path.join(io.workspaceRoot, '.mise.toml');
|
|
72
79
|
const miseContent = node_fs.existsSync(misePath) ? node_fs.readFileSync(misePath, 'utf-8') : '';
|
|
73
|
-
io.
|
|
80
|
+
const packageJson = JSON.parse(node_fs.readFileSync(node_path.join(io.workspaceRoot, 'package.json'), 'utf8'));
|
|
81
|
+
const pnpmVersion = migrationPnpmVersion(packageJson.packageManager?.match(/^pnpm@([^+]+)(?:\+.*)?$/u)?.[1]);
|
|
82
|
+
io.write(misePath, updateMiseTools(miseContent, pnpmVersion));
|
|
74
83
|
const workflowPath = node_path.join(io.workspaceRoot, '.github/workflows/ultramodern-workspace-gates.yml');
|
|
75
84
|
if (!node_fs.existsSync(workflowPath)) return;
|
|
76
85
|
const workflow = node_fs.readFileSync(workflowPath, 'utf-8');
|
package/dist/esm/ultramodern-tooling/commands/migrate-strict-effect/workspace-artifact-ownership.js
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import node_fs from "node:fs";
|
|
2
|
+
import node_path from "node:path";
|
|
3
|
+
import { parse } from "@babel/parser";
|
|
4
|
+
import { formatGeneratedSourceCandidates } from "../../../ultramodern-workspace/fs-io.js";
|
|
5
|
+
function isLiteralData(node, literalIdentifiers) {
|
|
6
|
+
if (!node) return false;
|
|
7
|
+
if ('Identifier' === node.type) return literalIdentifiers?.has(node.name) ?? false;
|
|
8
|
+
if ([
|
|
9
|
+
'StringLiteral',
|
|
10
|
+
'NumericLiteral',
|
|
11
|
+
'BooleanLiteral',
|
|
12
|
+
'NullLiteral'
|
|
13
|
+
].includes(node.type)) return true;
|
|
14
|
+
if ('UnaryExpression' === node.type) return '-' === node.operator && 'NumericLiteral' === node.argument.type;
|
|
15
|
+
if ('ArrayExpression' === node.type) return node.elements.every((element)=>isLiteralData(element, literalIdentifiers));
|
|
16
|
+
return 'ObjectExpression' === node.type && node.properties.every((property)=>'ObjectProperty' === property.type && !property.computed && !property.shorthand && [
|
|
17
|
+
'Identifier',
|
|
18
|
+
'StringLiteral',
|
|
19
|
+
'NumericLiteral'
|
|
20
|
+
].includes(property.key.type) && isLiteralData(property.value, literalIdentifiers));
|
|
21
|
+
}
|
|
22
|
+
function withoutGeneratedData(source, binding) {
|
|
23
|
+
if (!binding) return source;
|
|
24
|
+
const parsed = parse(source, {
|
|
25
|
+
sourceType: 'module',
|
|
26
|
+
plugins: [
|
|
27
|
+
"typescript"
|
|
28
|
+
]
|
|
29
|
+
});
|
|
30
|
+
for (const statement of parsed.program.body)if ('VariableDeclaration' === statement.type && 'const' === statement.kind) {
|
|
31
|
+
for (const declaration of statement.declarations)if ('Identifier' === declaration.id.type && declaration.id.name === binding && declaration.init && isLiteralData(declaration.init)) return source.slice(0, declaration.init.start) + '{}' + source.slice(declaration.init.end);
|
|
32
|
+
}
|
|
33
|
+
return source;
|
|
34
|
+
}
|
|
35
|
+
function staticPropertyValue(node, name) {
|
|
36
|
+
if (node?.type !== 'ObjectExpression') return;
|
|
37
|
+
const keys = new Set();
|
|
38
|
+
let value;
|
|
39
|
+
for (const property of node.properties){
|
|
40
|
+
if ('ObjectProperty' !== property.type || property.computed || property.shorthand || ![
|
|
41
|
+
'Identifier',
|
|
42
|
+
'StringLiteral'
|
|
43
|
+
].includes(property.key.type)) return;
|
|
44
|
+
const key = property.key.name ?? property.key.value;
|
|
45
|
+
if (keys.has(key)) return;
|
|
46
|
+
keys.add(key);
|
|
47
|
+
if (key === name) value = property.value;
|
|
48
|
+
}
|
|
49
|
+
return value;
|
|
50
|
+
}
|
|
51
|
+
function refreshValidatorReleaseCohort(source, releaseCohort) {
|
|
52
|
+
try {
|
|
53
|
+
const parsed = parse(source, {
|
|
54
|
+
sourceType: 'module',
|
|
55
|
+
plugins: [
|
|
56
|
+
"typescript"
|
|
57
|
+
],
|
|
58
|
+
tokens: true
|
|
59
|
+
});
|
|
60
|
+
const constants = parsed.program.body.flatMap((statement)=>'VariableDeclaration' === statement.type && 'const' === statement.kind ? statement.declarations : []);
|
|
61
|
+
const matches = [];
|
|
62
|
+
for (const statement of parsed.program.body)if ('VariableDeclaration' === statement.type && 'const' === statement.kind) for (const declaration of statement.declarations){
|
|
63
|
+
const kind = staticPropertyValue(declaration.init, 'kind');
|
|
64
|
+
if (kind?.type !== 'StringLiteral' || 'modernjs.ultramodern-workspace-validation-contract' !== kind.value) continue;
|
|
65
|
+
const cohort = staticPropertyValue(staticPropertyValue(declaration.init, 'cohort'), 'releaseCohort');
|
|
66
|
+
const schema = staticPropertyValue(cohort, 'schema');
|
|
67
|
+
const schemaVersion = staticPropertyValue(cohort, 'schemaVersion');
|
|
68
|
+
const version = staticPropertyValue(staticPropertyValue(cohort, 'release'), 'version');
|
|
69
|
+
const versionConstant = version?.type === 'Identifier' ? constants.find((declaration)=>'Identifier' === declaration.id.type && declaration.id.name === version.name && declaration.init?.type === 'StringLiteral') : void 0;
|
|
70
|
+
if (schema?.type === 'StringLiteral' && 'bleedingdev.ultramodern.release-cohort' === schema.value && schemaVersion?.type === 'NumericLiteral' && 1 === schemaVersion.value && isLiteralData(cohort, versionConstant ? new Set([
|
|
71
|
+
version.name
|
|
72
|
+
]) : void 0)) matches.push({
|
|
73
|
+
cohort,
|
|
74
|
+
version,
|
|
75
|
+
versionConstant
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
if (1 !== matches.length) return source;
|
|
79
|
+
const { cohort, version, versionConstant } = matches[0];
|
|
80
|
+
let content = JSON.stringify(releaseCohort, null, 2);
|
|
81
|
+
const edits = [];
|
|
82
|
+
if (versionConstant && !parsed.tokens?.some((token)=>'name' === token.type.label && token.value === version.name && token.start !== versionConstant.id.start && (token.start < cohort.start || token.end > cohort.end))) {
|
|
83
|
+
content = content.replaceAll(`"version": ${JSON.stringify(releaseCohort.release.version)}`, `"version": ${version.name}`);
|
|
84
|
+
edits.push({
|
|
85
|
+
start: versionConstant.init.start,
|
|
86
|
+
end: versionConstant.init.end,
|
|
87
|
+
content: JSON.stringify(releaseCohort.release.version)
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
edits.push({
|
|
91
|
+
start: cohort.start,
|
|
92
|
+
end: cohort.end,
|
|
93
|
+
content
|
|
94
|
+
});
|
|
95
|
+
for (const edit of edits.toSorted((left, right)=>right.start - left.start))source = source.slice(0, edit.start) + edit.content + source.slice(edit.end);
|
|
96
|
+
return source;
|
|
97
|
+
} catch {
|
|
98
|
+
return source;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
function preserveConsumerWorkspaceArtifacts(io, candidates) {
|
|
102
|
+
const preservedPaths = new Set();
|
|
103
|
+
const physicalRoot = node_fs.realpathSync(io.workspaceRoot);
|
|
104
|
+
const canonicalSources = formatGeneratedSourceCandidates(candidates.map((candidate, index)=>[
|
|
105
|
+
`canonical/${index}/${candidate.relativePath}`,
|
|
106
|
+
withoutGeneratedData(candidate.content, candidate.generatedDataBinding)
|
|
107
|
+
]));
|
|
108
|
+
for (const [index, candidate] of candidates.entries()){
|
|
109
|
+
const paths = [
|
|
110
|
+
candidate.relativePath,
|
|
111
|
+
candidate.legacyPath
|
|
112
|
+
].filter((value)=>void 0 !== value);
|
|
113
|
+
for (const relativePath of paths){
|
|
114
|
+
const filePath = node_path.join(io.workspaceRoot, relativePath);
|
|
115
|
+
if (!node_fs.existsSync(filePath)) continue;
|
|
116
|
+
const physicalRelative = node_path.relative(physicalRoot, node_fs.realpathSync(filePath));
|
|
117
|
+
if ('..' === physicalRelative || physicalRelative.startsWith(`..${node_path.sep}`) || node_path.isAbsolute(physicalRelative)) throw new Error(`Refusing to inspect an artifact outside the workspace: ${relativePath}`);
|
|
118
|
+
const source = node_fs.readFileSync(filePath, 'utf8');
|
|
119
|
+
let recognized = source === candidate.content;
|
|
120
|
+
if (!recognized) try {
|
|
121
|
+
const normalized = withoutGeneratedData(source, candidate.generatedDataBinding);
|
|
122
|
+
const canonical = canonicalSources[index];
|
|
123
|
+
recognized = normalized === canonical || formatGeneratedSourceCandidates([
|
|
124
|
+
[
|
|
125
|
+
relativePath,
|
|
126
|
+
normalized
|
|
127
|
+
]
|
|
128
|
+
])[0] === canonical;
|
|
129
|
+
} catch {}
|
|
130
|
+
if (!recognized) for (const pairedPath of paths)preservedPaths.add(pairedPath);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
const reported = new Set();
|
|
134
|
+
const isPreserved = (filePath)=>{
|
|
135
|
+
const relativePath = node_path.relative(io.workspaceRoot, filePath).split(node_path.sep).join('/');
|
|
136
|
+
if (!preservedPaths.has(relativePath)) return false;
|
|
137
|
+
if (!reported.has(relativePath)) {
|
|
138
|
+
io.log(`${relativePath} preserved consumer-owned artifact: its canonical generated source does not match.`);
|
|
139
|
+
reported.add(relativePath);
|
|
140
|
+
}
|
|
141
|
+
return true;
|
|
142
|
+
};
|
|
143
|
+
return {
|
|
144
|
+
preservedPaths,
|
|
145
|
+
refreshReleaseCohort (releaseCohort) {
|
|
146
|
+
if (!releaseCohort) return;
|
|
147
|
+
for (const candidate of candidates)if ('workspaceValidationContract' === candidate.generatedDataBinding) for (const relativePath of [
|
|
148
|
+
candidate.relativePath,
|
|
149
|
+
candidate.legacyPath
|
|
150
|
+
]){
|
|
151
|
+
if (!relativePath || !preservedPaths.has(relativePath)) continue;
|
|
152
|
+
const filePath = node_path.join(io.workspaceRoot, relativePath);
|
|
153
|
+
if (!node_fs.existsSync(filePath)) continue;
|
|
154
|
+
const source = node_fs.readFileSync(filePath, 'utf8');
|
|
155
|
+
io.write(filePath, refreshValidatorReleaseCohort(source, releaseCohort));
|
|
156
|
+
}
|
|
157
|
+
},
|
|
158
|
+
io: {
|
|
159
|
+
...io,
|
|
160
|
+
write: (filePath, content)=>isPreserved(filePath) ? false : io.write(filePath, content),
|
|
161
|
+
writeGenerated: (filePath, content)=>isPreserved(filePath) ? false : io.writeGenerated(filePath, content),
|
|
162
|
+
remove: (filePath)=>isPreserved(filePath) ? false : io.remove(filePath)
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
export { preserveConsumerWorkspaceArtifacts };
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import node_fs from "node:fs";
|
|
2
2
|
import node_path from "node:path";
|
|
3
3
|
import { RELEASE_COHORT_PROJECTION_PATH, readCreateReleaseCohort } from "../../ultramodern-release-cohort.js";
|
|
4
|
+
import { createAppEnvDts } from "../../ultramodern-workspace/app-files.js";
|
|
4
5
|
import { createDevelopmentOverlay, createTopology, createUltramodernConfig } from "../../ultramodern-workspace/contracts.js";
|
|
5
6
|
import { stampDeliveryUnitIdentity } from "../../ultramodern-workspace/delivery-unit-stamp.js";
|
|
6
7
|
import { ULTRAMODERN_WORKSPACE_POLICY } from "../../ultramodern-workspace/policy.js";
|
|
7
8
|
import { createAdditionalShellConfigEntry } from "../../ultramodern-workspace/shells.js";
|
|
8
|
-
import { createWorkspaceValidationScript, migratedWorkspaceScriptArtifacts } from "../../ultramodern-workspace/workspace-scripts.js";
|
|
9
|
+
import { createWorkspaceValidationScript, createZeropsRuntimeMaterializationScript, migratedWorkspaceScriptArtifacts } from "../../ultramodern-workspace/workspace-scripts.js";
|
|
10
|
+
import { createZeropsYaml } from "../../ultramodern-workspace/zerops.js";
|
|
9
11
|
import { additionalShellsFromToolingConfig, allWorkspaceAppsFromToolingConfig, normalizeCompactUltramodernConfig, synthesizeCompactUltramodernConfig, workspaceAppsFromToolingConfig } from "../config.js";
|
|
10
12
|
import { reconcileCompactPackageSourceMetadata, reconcilePackageSourceMetadata, updateReferenceTopology, updateUltramodernConfig } from "./migrate-strict-effect/api-metadata.js";
|
|
11
13
|
import { ensureGeneratedDeclarationPatches } from "./migrate-strict-effect/declaration-patches.js";
|
|
@@ -18,7 +20,9 @@ import { ensureBffEffectDependencies, updateGeneratedPackageScripts, updateGener
|
|
|
18
20
|
import { createMigrationPackageSource } from "./migrate-strict-effect/package-source.js";
|
|
19
21
|
import { updateGeneratedPnpmWorkspacePolicy, validateGeneratedPnpmLockReleaseAgePolicy } from "./migrate-strict-effect/pnpm-policy.js";
|
|
20
22
|
import { ensureGeneratedModuleFederationBridgeRouterOptOut, preflightModuleFederationBridgeRouter, removeRetiredReactRouterDependency } from "./migrate-strict-effect/react-router-retirement.js";
|
|
23
|
+
import { ensureSharedApiInfrastructure } from "./migrate-strict-effect/shared-api-infrastructure.js";
|
|
21
24
|
import { updateGeneratedToolchainFiles, updateRootPackageToolchain } from "./migrate-strict-effect/toolchain-pins.js";
|
|
25
|
+
import { preserveConsumerWorkspaceArtifacts } from "./migrate-strict-effect/workspace-artifact-ownership.js";
|
|
22
26
|
import { hasFlag } from "./options.js";
|
|
23
27
|
const retiredMetadataPaths = [
|
|
24
28
|
'.modernjs/ultramodern-generated-contract.json',
|
|
@@ -74,10 +78,24 @@ function synchronizeMigrationDeliveryUnitMetadata(io, raw, packageSource) {
|
|
|
74
78
|
'schemaVersion',
|
|
75
79
|
'id',
|
|
76
80
|
"description",
|
|
77
|
-
'preset'
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
+
'preset'
|
|
82
|
+
])if ('id' === key || "description" === key) topology[key] ??= canonicalTopology[key];
|
|
83
|
+
else topology[key] = canonicalTopology[key];
|
|
84
|
+
const existingSharedPackages = Array.isArray(topology.sharedPackages) ? topology.sharedPackages : [];
|
|
85
|
+
topology.sharedPackages = [
|
|
86
|
+
...existingSharedPackages,
|
|
87
|
+
...canonicalTopology.sharedPackages.filter((candidate)=>!existingSharedPackages.some((existing)=>existing.id === candidate.id))
|
|
88
|
+
];
|
|
89
|
+
topology.validation = {
|
|
90
|
+
...canonicalTopology.validation,
|
|
91
|
+
...topology.validation,
|
|
92
|
+
commands: [
|
|
93
|
+
...new Set([
|
|
94
|
+
...topology.validation?.commands ?? [],
|
|
95
|
+
...canonicalTopology.validation.commands
|
|
96
|
+
])
|
|
97
|
+
]
|
|
98
|
+
};
|
|
81
99
|
const canonicalTopologyApps = new Map([
|
|
82
100
|
canonicalTopology.shell,
|
|
83
101
|
...canonicalTopology.verticals ?? []
|
|
@@ -100,8 +118,21 @@ function synchronizeMigrationDeliveryUnitMetadata(io, raw, packageSource) {
|
|
|
100
118
|
'api',
|
|
101
119
|
'cloudflare',
|
|
102
120
|
'ownership'
|
|
103
|
-
])if (Object.hasOwn(canonicalEntry, key))
|
|
104
|
-
|
|
121
|
+
])if (Object.hasOwn(canonicalEntry, key)) {
|
|
122
|
+
const canonicalValue = canonicalEntry[key];
|
|
123
|
+
if ('api' === key && entry.api && canonicalValue && 'object' == typeof canonicalValue) {
|
|
124
|
+
const nextApi = {
|
|
125
|
+
...entry.api,
|
|
126
|
+
...canonicalValue
|
|
127
|
+
};
|
|
128
|
+
if (!Object.hasOwn(entry.api, 'domainOperations')) delete nextApi.domainOperations;
|
|
129
|
+
entry.api = nextApi;
|
|
130
|
+
} else if ('cloudflare' === key && entry.cloudflare && canonicalValue && 'object' == typeof canonicalValue) entry.cloudflare = {
|
|
131
|
+
...canonicalValue,
|
|
132
|
+
...entry.cloudflare
|
|
133
|
+
};
|
|
134
|
+
else entry[key] = canonicalValue;
|
|
135
|
+
} else delete entry[key];
|
|
105
136
|
}
|
|
106
137
|
}
|
|
107
138
|
writeJsonFile(io, topologyPath, topology);
|
|
@@ -122,7 +153,10 @@ function synchronizeMigrationCompactPolicy(io, raw, packageSource) {
|
|
|
122
153
|
const apps = workspaceAppsFromToolingConfig(migrated);
|
|
123
154
|
apps.filter((app)=>'shell' !== app.kind);
|
|
124
155
|
const canonical = requireRecord(createUltramodernConfig(migrated.workspace.packageScope, packageSource.modernPackageVersion, packageSource, apps, migrated.features.tailwind, migrated.bridge, additionalShellsFromToolingConfig(migrated), apps.find((app)=>'shell' === app.kind)), 'Generated compact policy');
|
|
125
|
-
for (const key of compactPolicyKeys)
|
|
156
|
+
for (const key of compactPolicyKeys){
|
|
157
|
+
if ('workspace' === key) canonical.workspace.packageManager.version = raw.workspace.packageManager.version;
|
|
158
|
+
raw[key] = canonical[key];
|
|
159
|
+
}
|
|
126
160
|
writeJsonFile(io, node_path.join(io.workspaceRoot, '.modernjs/ultramodern.json'), raw);
|
|
127
161
|
}
|
|
128
162
|
function reconcileRootPackageSourceMetadata(packageJson, packageSource) {
|
|
@@ -262,6 +296,33 @@ function migrateStrictEffect(args, context, io, dryRun, skipInstall) {
|
|
|
262
296
|
version: packageSource.modernPackageVersion
|
|
263
297
|
});
|
|
264
298
|
const releaseCohort = 'install' === packageSource.strategy ? readCreateReleaseCohort() : void 0;
|
|
299
|
+
const currentApps = workspaceAppsFromToolingConfig(current);
|
|
300
|
+
const allCurrentApps = allWorkspaceAppsFromToolingConfig(current);
|
|
301
|
+
const artifactOwnership = preserveConsumerWorkspaceArtifacts(io, [
|
|
302
|
+
...allCurrentApps.map((app)=>({
|
|
303
|
+
relativePath: `${app.directory}/src/modern-app-env.d.ts`,
|
|
304
|
+
content: createAppEnvDts(app, allCurrentApps.filter((remote)=>'shell' !== remote.kind), current.workspace.packageScope)
|
|
305
|
+
})),
|
|
306
|
+
...migratedWorkspaceScriptArtifacts({
|
|
307
|
+
shellOnly: false,
|
|
308
|
+
hasBackendSurface: true
|
|
309
|
+
}).filter((artifact)=>"scripts/validate-ultramodern-workspace.mts" !== artifact.relativePath),
|
|
310
|
+
{
|
|
311
|
+
relativePath: "scripts/validate-ultramodern-workspace.mts",
|
|
312
|
+
legacyPath: "scripts/validate-ultramodern-workspace.mjs",
|
|
313
|
+
generatedDataBinding: 'workspaceValidationContract',
|
|
314
|
+
content: createWorkspaceValidationScript(current.workspace.packageScope, current.features.tailwind, currentApps.filter((app)=>'shell' !== app.kind), void 0, additionalShellsFromToolingConfig(current), currentApps.find((app)=>'shell' === app.kind))
|
|
315
|
+
},
|
|
316
|
+
{
|
|
317
|
+
relativePath: 'zerops.yaml',
|
|
318
|
+
content: `${createZeropsYaml(current.workspace.packageScope, allWorkspaceAppsFromToolingConfig(current))}\n`
|
|
319
|
+
},
|
|
320
|
+
{
|
|
321
|
+
relativePath: "scripts/materialize-zerops-runtime.mjs",
|
|
322
|
+
content: createZeropsRuntimeMaterializationScript()
|
|
323
|
+
}
|
|
324
|
+
]);
|
|
325
|
+
io = artifactOwnership.io;
|
|
265
326
|
reconcileCompactPackageSourceMetadata(raw.packageSource, packageSource);
|
|
266
327
|
reconcileRootPackageSourceMetadata(readJsonFile(node_path.join(io.workspaceRoot, 'package.json')), packageSource);
|
|
267
328
|
updateGeneratedPnpmWorkspacePolicy(createMigrationIo(io.workspaceRoot, true), packageSource, {
|
|
@@ -282,6 +343,7 @@ function migrateStrictEffect(args, context, io, dryRun, skipInstall) {
|
|
|
282
343
|
const verticalApps = migrated.topology.apps.filter((app)=>'shell' !== app.kind);
|
|
283
344
|
const hasBackendSurface = verticalApps.some((app)=>app.api);
|
|
284
345
|
const shellOnly = 0 === verticalApps.length;
|
|
346
|
+
if (hasBackendSurface) ensureSharedApiInfrastructure(io, migrated.workspace.packageScope, packageSource);
|
|
285
347
|
if (shellOnly) {
|
|
286
348
|
io.log('Shell-only workspace: skipping backend-federation and Zerops runtime stages.');
|
|
287
349
|
for (const relativePath of [
|
|
@@ -312,6 +374,7 @@ function migrateStrictEffect(args, context, io, dryRun, skipInstall) {
|
|
|
312
374
|
io.writeGenerated(node_path.join(io.workspaceRoot, artifact.relativePath), artifact.content);
|
|
313
375
|
}
|
|
314
376
|
io.writeGenerated(node_path.join(io.workspaceRoot, "scripts/validate-ultramodern-workspace.mts"), createWorkspaceValidationScript(validationContractInputs.scope, validationContractInputs.enableTailwind, validationContractInputs.remotes, releaseCohort, validationContractInputs.additionalShells, validationContractInputs.primaryShell, raw, node_fs.existsSync(node_path.join(io.workspaceRoot, 'topology/ownership.json')) ? requireRecord(readJsonFile(node_path.join(io.workspaceRoot, 'topology/ownership.json')), 'Ownership topology') : void 0, developmentOverlay));
|
|
377
|
+
artifactOwnership.refreshReleaseCohort(releaseCohort);
|
|
315
378
|
for (const relativePath of retiredMetadataPaths)io.remove(node_path.join(io.workspaceRoot, relativePath));
|
|
316
379
|
updateGeneratedBuildIdentityModules(io, migrated);
|
|
317
380
|
updateGeneratedTypeScriptSurfaces(io, migrated);
|
|
@@ -326,7 +389,7 @@ function migrateStrictEffect(args, context, io, dryRun, skipInstall) {
|
|
|
326
389
|
reconcileRootPackageSourceMetadata(packageJson, packageSource);
|
|
327
390
|
updateRootPackageToolchain(packageJson);
|
|
328
391
|
}
|
|
329
|
-
updateModernDependencies(packageJson, packageSource);
|
|
392
|
+
updateModernDependencies(packageJson, packageSource, releaseCohort);
|
|
330
393
|
const ownsGeneratedScripts = 'package.json' === relativePackageFile || allMigratedApps.some((app)=>`${app.directory}/package.json` === relativePackageFile);
|
|
331
394
|
updateGeneratedToolingDependencies(packageJson, {
|
|
332
395
|
ownsGeneratedScripts
|
|
@@ -336,6 +399,7 @@ function migrateStrictEffect(args, context, io, dryRun, skipInstall) {
|
|
|
336
399
|
if ('preserved' === retiredReactRouter) io.log(`${relativePackageFile} keeps its react-router dependency: authored source imports React Router directly, so its generated Module Federation config keeps bridge.enableBridgeRouter: true.`);
|
|
337
400
|
else if ('removed' === retiredReactRouter) io.log(`${relativePackageFile} dropped its obsolete react-router dependency: no authored source imports React Router.`);
|
|
338
401
|
updateGeneratedPackageScripts(packageJson, {
|
|
402
|
+
preservedArtifacts: artifactOwnership.preservedPaths,
|
|
339
403
|
relativePackageFile,
|
|
340
404
|
apps: allMigratedApps,
|
|
341
405
|
shellOnly,
|
|
@@ -2,7 +2,7 @@ import { resolveApiProtocol, resolveApiStem, verticalApiApps } from "../descript
|
|
|
2
2
|
import { packageName, toPascalCase } from "../naming.js";
|
|
3
3
|
import { createCheckoutCartClientExports, serviceHasCheckoutCartState } from "./checkout-cart.js";
|
|
4
4
|
import { verticalApiErrorStem, verticalApiExport, verticalApiGroupName, verticalApiNotFoundErrorExport } from "./names.js";
|
|
5
|
-
function createApiClient(service, contractImportPath) {
|
|
5
|
+
function createApiClient(service, contractImportPath, options) {
|
|
6
6
|
const apiExport = verticalApiExport(service);
|
|
7
7
|
const contractExport = verticalApiGroupName(service);
|
|
8
8
|
const stem = resolveApiStem(service);
|
|
@@ -118,7 +118,7 @@ export const ${readinessName} = (
|
|
|
118
118
|
operationContext:
|
|
119
119
|
options.operationContext ?? ${groupName}OperationContexts.readiness,
|
|
120
120
|
}).pipe(
|
|
121
|
-
Effect.flatMap(client => client.${groupName}.readiness({})),
|
|
121
|
+
Effect.flatMap(client => client.${options ? 'foundation' : groupName}.readiness({})),
|
|
122
122
|
);
|
|
123
123
|
|
|
124
124
|
export const ${getName} = (
|
|
@@ -4,23 +4,22 @@ import { packageName } from "../naming.js";
|
|
|
4
4
|
import { createCheckoutCartServerHandlers, createCheckoutCartServerState } from "./checkout-cart.js";
|
|
5
5
|
import { verticalApiExport, verticalApiGroupName, verticalApiNotFoundErrorExport } from "./names.js";
|
|
6
6
|
import { createRpcApiServiceEntry, rpcPath, verticalRpcContractExport, verticalRpcGroupExport } from "./rpc.js";
|
|
7
|
-
function createApiServiceEntry(service, contractImportPath) {
|
|
7
|
+
function createApiServiceEntry(service, contractImportPath, options) {
|
|
8
8
|
if ((service.api?.protocol ?? 'rest') === 'rpc') return createRpcApiServiceEntry(service);
|
|
9
9
|
const apiExport = verticalApiExport(service);
|
|
10
10
|
const groupName = verticalApiGroupName(service);
|
|
11
11
|
const notFoundErrorExport = verticalApiNotFoundErrorExport(service);
|
|
12
12
|
const stem = resolveApiStem(service);
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
const assemblyImport = options ? `import { assembleEffectBffRuntime } from '${packageName(options.scope, 'shared-contracts')}/server/effect-bff-runtime';\n` : '';
|
|
14
|
+
return `${assemblyImport}import {
|
|
15
|
+
${options ? '' : 'defineEffectBff,'}
|
|
15
16
|
Effect,
|
|
16
17
|
HttpApiBuilder,
|
|
17
18
|
Layer,
|
|
18
19
|
} from '@modern-js/plugin-bff/effect-edge';
|
|
19
|
-
import type {
|
|
20
|
-
EffectBffDefinition,
|
|
21
|
-
EffectBffRuntime,
|
|
20
|
+
${options ? '' : `import type {
|
|
22
21
|
EffectRuntimeLayer,
|
|
23
|
-
} from '@modern-js/plugin-bff/effect-edge'
|
|
22
|
+
} from '@modern-js/plugin-bff/effect-edge';`}
|
|
24
23
|
import { ultramodernApiMarker } from '../shared/ultramodern-build.ts';
|
|
25
24
|
import {
|
|
26
25
|
${apiExport},
|
|
@@ -50,6 +49,31 @@ const operationAttributes = (operationContext: OperationContext) => ({
|
|
|
50
49
|
: {}),
|
|
51
50
|
});
|
|
52
51
|
|
|
52
|
+
${options ? `const ${groupName}ReadinessLayer = HttpApiBuilder.group(
|
|
53
|
+
${apiExport},
|
|
54
|
+
'foundation',
|
|
55
|
+
handlers => handlers
|
|
56
|
+
.handle('readiness', () =>
|
|
57
|
+
Effect.succeed({
|
|
58
|
+
checks: {
|
|
59
|
+
api: 'ready' as const,
|
|
60
|
+
moduleFederation: 'ready' as const,
|
|
61
|
+
ssr: 'ready' as const,
|
|
62
|
+
translations: 'ready' as const,
|
|
63
|
+
},
|
|
64
|
+
marker: ultramodernApiMarker,
|
|
65
|
+
status: 'ready' as const,
|
|
66
|
+
versionSkew: 'none' as const,
|
|
67
|
+
}).pipe(
|
|
68
|
+
Effect.withSpan('ultramodern.api.${groupName}.readiness', {
|
|
69
|
+
attributes: operationAttributes(${groupName}OperationContexts.readiness),
|
|
70
|
+
kind: 'server',
|
|
71
|
+
}),
|
|
72
|
+
),
|
|
73
|
+
)
|
|
74
|
+
,
|
|
75
|
+
);` : ''}
|
|
76
|
+
|
|
53
77
|
const ${groupName}Layer = HttpApiBuilder.group(
|
|
54
78
|
${apiExport},
|
|
55
79
|
'${groupName}',
|
|
@@ -68,7 +92,7 @@ const ${groupName}Layer = HttpApiBuilder.group(
|
|
|
68
92
|
}),
|
|
69
93
|
),
|
|
70
94
|
)
|
|
71
|
-
.handle('readiness', () =>
|
|
95
|
+
${options ? '' : ` .handle('readiness', () =>
|
|
72
96
|
Effect.succeed({
|
|
73
97
|
checks: {
|
|
74
98
|
api: 'ready' as const,
|
|
@@ -86,6 +110,7 @@ const ${groupName}Layer = HttpApiBuilder.group(
|
|
|
86
110
|
}),
|
|
87
111
|
),
|
|
88
112
|
)
|
|
113
|
+
`}
|
|
89
114
|
.handle('get', ({ params }) => {
|
|
90
115
|
const matchedItem = ${groupName}Items.find(
|
|
91
116
|
candidate => candidate.id === params.id,
|
|
@@ -125,14 +150,18 @@ const ${groupName}Layer = HttpApiBuilder.group(
|
|
|
125
150
|
)${createCheckoutCartServerHandlers(service)},
|
|
126
151
|
);
|
|
127
152
|
|
|
128
|
-
const
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
layer
|
|
135
|
-
})
|
|
153
|
+
${options ? `const apiHandlersLive = Layer.mergeAll(${groupName}Layer, ${groupName}ReadinessLayer);
|
|
154
|
+
export const make${apiExport[0].toUpperCase()}${apiExport.slice(1)}Runtime = () =>
|
|
155
|
+
assembleEffectBffRuntime({
|
|
156
|
+
api: ${apiExport},
|
|
157
|
+
handlers: apiHandlersLive,
|
|
158
|
+
});` : `export const make${apiExport[0].toUpperCase()}${apiExport.slice(1)}Runtime = () => {
|
|
159
|
+
const layer = HttpApiBuilder.layer(${apiExport}).pipe(
|
|
160
|
+
Layer.provide(${groupName}Layer),
|
|
161
|
+
) satisfies EffectRuntimeLayer;
|
|
162
|
+
return defineEffectBff({ api: ${apiExport}, layer });
|
|
163
|
+
};`}
|
|
164
|
+
const apiRuntime = make${apiExport[0].toUpperCase()}${apiExport.slice(1)}Runtime();
|
|
136
165
|
|
|
137
166
|
export default apiRuntime;
|
|
138
167
|
`;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { resolveApiPrefix, resolveApiStem } from "../descriptors.js";
|
|
2
2
|
import { renderTemplate } from "../fs-io.js";
|
|
3
|
-
import { toPascalCase } from "../naming.js";
|
|
3
|
+
import { packageName, toPascalCase } from "../naming.js";
|
|
4
4
|
import { createCheckoutCartApiContractFields, createCheckoutCartEndpointDefinitions, createCheckoutCartOperationContexts, createCheckoutCartSharedSchemas } from "./checkout-cart.js";
|
|
5
5
|
import { verticalApiCreatePayloadSchemaExport, verticalApiExport, verticalApiGroupName, verticalApiMarkerSchemaExport, verticalApiName, verticalApiNotFoundErrorExport, verticalApiNotFoundSchemaExport, verticalApiReadinessSchemaExport, verticalApiSchemaExport } from "./names.js";
|
|
6
6
|
function createSharedApiImports() {
|
|
@@ -13,7 +13,7 @@ function createSharedApiImports() {
|
|
|
13
13
|
} from '@modern-js/plugin-bff/effect-client';
|
|
14
14
|
`;
|
|
15
15
|
}
|
|
16
|
-
function createSharedApiContract(service) {
|
|
16
|
+
function createSharedApiContract(service, shared = false) {
|
|
17
17
|
const schemaExport = verticalApiSchemaExport(service);
|
|
18
18
|
const markerSchemaExport = verticalApiMarkerSchemaExport(service);
|
|
19
19
|
const readinessSchemaExport = verticalApiReadinessSchemaExport(service);
|
|
@@ -70,7 +70,7 @@ function createSharedApiContract(service) {
|
|
|
70
70
|
listOperationContext,
|
|
71
71
|
readinessOperationContext
|
|
72
72
|
}).trim();
|
|
73
|
-
return `export interface ${markerType} {
|
|
73
|
+
return `${shared ? `export type ${markerType} = MicroVerticalBuildMarker;` : `export interface ${markerType} {
|
|
74
74
|
readonly appId: string;
|
|
75
75
|
readonly build: string;
|
|
76
76
|
readonly buildMarker: string;
|
|
@@ -80,7 +80,7 @@ function createSharedApiContract(service) {
|
|
|
80
80
|
readonly surface: string;
|
|
81
81
|
readonly unitId: string;
|
|
82
82
|
readonly version: string;
|
|
83
|
-
}
|
|
83
|
+
}`}
|
|
84
84
|
|
|
85
85
|
export interface ${itemType} {
|
|
86
86
|
readonly id: string;
|
|
@@ -88,7 +88,7 @@ export interface ${itemType} {
|
|
|
88
88
|
readonly title: string;
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
-
export interface ${readinessType} {
|
|
91
|
+
${shared ? `export type ${readinessType} = MicroVerticalReadiness;` : `export interface ${readinessType} {
|
|
92
92
|
readonly checks: {
|
|
93
93
|
readonly api: 'ready';
|
|
94
94
|
readonly moduleFederation: 'ready';
|
|
@@ -98,7 +98,7 @@ export interface ${readinessType} {
|
|
|
98
98
|
readonly marker: ${markerType};
|
|
99
99
|
readonly status: 'ready';
|
|
100
100
|
readonly versionSkew: 'none';
|
|
101
|
-
}
|
|
101
|
+
}`}
|
|
102
102
|
|
|
103
103
|
export interface ${createPayloadType} {
|
|
104
104
|
readonly title: string;
|
|
@@ -117,7 +117,7 @@ export interface ${notFoundErrorExport} {
|
|
|
117
117
|
readonly id: string;
|
|
118
118
|
}
|
|
119
119
|
|
|
120
|
-
export const ${markerSchemaExport}: Schema.Codec<${markerType}> = Schema.Struct({
|
|
120
|
+
${shared ? `export const ${markerSchemaExport}: Schema.Codec<${markerType}> = MicroVerticalBuildMarkerSchema;` : `export const ${markerSchemaExport}: Schema.Codec<${markerType}> = Schema.Struct({
|
|
121
121
|
appId: Schema.String,
|
|
122
122
|
build: Schema.String,
|
|
123
123
|
buildMarker: Schema.String,
|
|
@@ -127,7 +127,7 @@ export const ${markerSchemaExport}: Schema.Codec<${markerType}> = Schema.Struct(
|
|
|
127
127
|
surface: Schema.String,
|
|
128
128
|
unitId: Schema.String,
|
|
129
129
|
version: Schema.String,
|
|
130
|
-
})
|
|
130
|
+
});`}
|
|
131
131
|
|
|
132
132
|
export const ${schemaExport}: Schema.Codec<${itemType}> = Schema.Struct({
|
|
133
133
|
id: Schema.String,
|
|
@@ -135,7 +135,7 @@ export const ${schemaExport}: Schema.Codec<${itemType}> = Schema.Struct({
|
|
|
135
135
|
title: Schema.String,
|
|
136
136
|
});
|
|
137
137
|
|
|
138
|
-
export const ${readinessSchemaExport}: Schema.Codec<${readinessType}> = Schema.Struct({
|
|
138
|
+
${shared ? `export const ${readinessSchemaExport}: Schema.Codec<${readinessType}> = MicroVerticalReadinessSchema;` : `export const ${readinessSchemaExport}: Schema.Codec<${readinessType}> = Schema.Struct({
|
|
139
139
|
checks: Schema.Struct({
|
|
140
140
|
api: Schema.Literal('ready'),
|
|
141
141
|
moduleFederation: Schema.Literal('ready'),
|
|
@@ -145,7 +145,7 @@ export const ${readinessSchemaExport}: Schema.Codec<${readinessType}> = Schema.S
|
|
|
145
145
|
marker: ${markerSchemaExport},
|
|
146
146
|
status: Schema.Literal('ready'),
|
|
147
147
|
versionSkew: Schema.Literal('none'),
|
|
148
|
-
})
|
|
148
|
+
});`}
|
|
149
149
|
|
|
150
150
|
export const ${createPayloadSchemaExport}: Schema.Codec<${createPayloadType}> = Schema.Struct({
|
|
151
151
|
title: Schema.String,
|
|
@@ -157,7 +157,7 @@ ${checkoutCartSharedSchemaSection}export const ${notFoundSchemaExport}: Schema.C
|
|
|
157
157
|
HttpApiSchema.status(404),
|
|
158
158
|
);
|
|
159
159
|
|
|
160
|
-
export interface OperationContext {
|
|
160
|
+
${shared ? 'export type OperationContext = MicroVerticalOperationContext;' : `export interface OperationContext {
|
|
161
161
|
method: string;
|
|
162
162
|
operationId: string;
|
|
163
163
|
routePath: string;
|
|
@@ -169,9 +169,14 @@ export interface OperationContext {
|
|
|
169
169
|
| 'data-platform'
|
|
170
170
|
| 'unknown';
|
|
171
171
|
traceId?: string;
|
|
172
|
-
}
|
|
172
|
+
}`}
|
|
173
173
|
|
|
174
|
-
export const ${
|
|
174
|
+
${shared ? `export const ${groupName}FoundationApi = HttpApi.make('${apiName}Foundation').add(
|
|
175
|
+
HttpApiGroup.make('foundation').add(
|
|
176
|
+
HttpApiEndpoint.get('readiness', '/${stem}/readiness', { success: ${readinessSchemaExport} }),
|
|
177
|
+
),
|
|
178
|
+
);` : ''}
|
|
179
|
+
export const ${apiExport} = HttpApi.make('${apiName}')${shared ? `.addHttpApi(${groupName}FoundationApi)` : ''}.add(
|
|
175
180
|
HttpApiGroup.make('${groupName}')
|
|
176
181
|
.add(
|
|
177
182
|
HttpApiEndpoint.get('list', '/${stem}', {
|
|
@@ -183,11 +188,12 @@ export const ${apiExport} = HttpApi.make('${apiName}').add(
|
|
|
183
188
|
}),
|
|
184
189
|
}),
|
|
185
190
|
)
|
|
186
|
-
.add(
|
|
191
|
+
${shared ? '' : ` .add(
|
|
187
192
|
HttpApiEndpoint.get('readiness', '/${stem}/readiness', {
|
|
188
193
|
success: ${readinessSchemaExport},
|
|
189
194
|
}),
|
|
190
195
|
)
|
|
196
|
+
`}
|
|
191
197
|
.add(
|
|
192
198
|
HttpApiEndpoint.get('get', '/${stem}/:id', {
|
|
193
199
|
error: ${notFoundSchemaExport},
|
|
@@ -208,7 +214,7 @@ export const ${apiExport} = HttpApi.make('${apiName}').add(
|
|
|
208
214
|
);
|
|
209
215
|
|
|
210
216
|
export const ${groupName}OperationContexts = {
|
|
211
|
-
${operationContextEntries}
|
|
217
|
+
${shared ? operationContextEntries.replaceAll(/(\w+: )\{([\s\S]*?)\},/gu, (_, key, fields)=>`${key}createMicroVerticalOperationContext({${fields.replace(/\n\s*source: 'generated-client',/u, '')}}),`) : operationContextEntries}
|
|
212
218
|
} satisfies Record<string, OperationContext>;
|
|
213
219
|
|
|
214
220
|
export const ${groupName}ApiContract = {
|
|
@@ -219,8 +225,12 @@ ${createCheckoutCartApiContractFields(service)} ownerId: '${service.id}',
|
|
|
219
225
|
} as const;
|
|
220
226
|
`;
|
|
221
227
|
}
|
|
222
|
-
function createSharedApi(service) {
|
|
228
|
+
function createSharedApi(service, options) {
|
|
229
|
+
const imports = options ? `import { MicroVerticalBuildMarkerSchema, MicroVerticalReadinessSchema, createMicroVerticalOperationContext } from '${packageName(options.scope, 'shared-contracts')}/microvertical-api-baseline';
|
|
230
|
+
import type { MicroVerticalBuildMarker, MicroVerticalReadiness, MicroVerticalOperationContext } from '${packageName(options.scope, 'shared-contracts')}/microvertical-api-baseline';
|
|
231
|
+
` : '';
|
|
223
232
|
return `${createSharedApiImports()}
|
|
224
|
-
${
|
|
233
|
+
${imports}
|
|
234
|
+
${createSharedApiContract(service, void 0 !== options)}`;
|
|
225
235
|
}
|
|
226
236
|
export { createSharedApi };
|