@bleedingdev/modern-js-ultramodern-create 3.9.0-ultramodern.2 → 3.9.0-ultramodern.3
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-tooling/commands/migrate-strict-effect/generated-artifacts-typescript.cjs +1 -0
- package/dist/cjs/ultramodern-tooling/commands/migrate-strict-effect/package-cohort.cjs +2 -0
- 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 +1 -0
- package/dist/cjs/ultramodern-tooling/commands/migrate-strict-effect.cjs +2 -0
- 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 +0 -5
- package/dist/cjs/ultramodern-workspace/package-json.cjs +12 -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/write-app.cjs +9 -3
- package/dist/cjs/ultramodern-workspace/write-workspace.cjs +5 -3
- 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 +2 -0
- 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 +1 -0
- package/dist/esm/ultramodern-tooling/commands/migrate-strict-effect.js +2 -0
- 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 +0 -5
- package/dist/esm/ultramodern-workspace/package-json.js +12 -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/write-app.js +9 -3
- package/dist/esm/ultramodern-workspace/write-workspace.js +6 -4
- 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 +2 -0
- 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 +1 -0
- package/dist/esm-node/ultramodern-tooling/commands/migrate-strict-effect.js +2 -0
- 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 +0 -5
- package/dist/esm-node/ultramodern-workspace/package-json.js +12 -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/write-app.js +9 -3
- package/dist/esm-node/ultramodern-workspace/write-workspace.js +6 -4
- package/dist/types/ultramodern-tooling/commands/migrate-strict-effect/shared-api-infrastructure.d.ts +4 -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/package-json.d.ts +1 -1
- package/dist/types/ultramodern-workspace/policy.d.ts +2 -0
- package/package.json +4 -4
- package/template-workspace/.modernjs/release-cohort.json +40 -40
- 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
package/dist/esm/ultramodern-tooling/commands/migrate-strict-effect/shared-api-infrastructure.js
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import node_fs from "node:fs";
|
|
2
|
+
import node_path from "node:path";
|
|
3
|
+
import { readFileTemplate } from "../../../ultramodern-workspace/fs-io.js";
|
|
4
|
+
import { createSharedPackage, createSharedPackageTsConfig } from "../../../ultramodern-workspace/package-json.js";
|
|
5
|
+
import { readJsonFile, writeJsonFile } from "./io.js";
|
|
6
|
+
const directory = 'packages/shared-contracts';
|
|
7
|
+
function record(value, label) {
|
|
8
|
+
if (null === value || 'object' != typeof value || Array.isArray(value)) throw new Error(`${label} must be an object; resolve it before migration.`);
|
|
9
|
+
return value;
|
|
10
|
+
}
|
|
11
|
+
function ensureSharedApiInfrastructure(io, scope, packageSource) {
|
|
12
|
+
for (const relativePath of [
|
|
13
|
+
'packages',
|
|
14
|
+
directory,
|
|
15
|
+
`${directory}/package.json`,
|
|
16
|
+
`${directory}/src`,
|
|
17
|
+
`${directory}/src/index.ts`
|
|
18
|
+
])if (node_fs.lstatSync(node_path.join(io.workspaceRoot, relativePath), {
|
|
19
|
+
throwIfNoEntry: false
|
|
20
|
+
})?.isSymbolicLink()) throw new Error(`Shared API migration cannot write through a symbolic link: ${relativePath}`);
|
|
21
|
+
const manifestPath = node_path.join(io.workspaceRoot, directory, 'package.json');
|
|
22
|
+
const defaults = createSharedPackage(scope, 'shared-contracts', 'Shared workspace contracts', packageSource);
|
|
23
|
+
const manifest = node_fs.existsSync(manifestPath) ? record(readJsonFile(manifestPath), `${directory}/package.json`) : defaults;
|
|
24
|
+
const exports = void 0 === manifest.exports ? {} : 'string' == typeof manifest.exports ? {
|
|
25
|
+
'.': manifest.exports
|
|
26
|
+
} : record(manifest.exports, `${directory}/package.json exports`);
|
|
27
|
+
if (Object.keys(exports).some((key)=>!key.startsWith('.')) || void 0 !== exports['.'] && './src/index.ts' !== exports['.']) throw new Error(`${directory}/package.json exports must expose ./src/index.ts at "." before adding shared API infrastructure; consumer exports were not overwritten.`);
|
|
28
|
+
for (const [subpath, target] of Object.entries(record(defaults.exports, 'Shared contract exports')))if (void 0 !== exports[subpath] && exports[subpath] !== target) throw new Error(`${directory}/package.json ${subpath} must expose ${String(target)}; consumer exports were not overwritten.`);
|
|
29
|
+
const dependencies = record(manifest.dependencies ?? {}, `${directory}/package.json dependencies`);
|
|
30
|
+
manifest.exports = {
|
|
31
|
+
...record(defaults.exports, 'Shared contract exports'),
|
|
32
|
+
...exports
|
|
33
|
+
};
|
|
34
|
+
manifest.dependencies = {
|
|
35
|
+
...record(defaults.dependencies, 'Shared contract dependencies'),
|
|
36
|
+
...dependencies
|
|
37
|
+
};
|
|
38
|
+
const writeMissing = (relativePath, content)=>{
|
|
39
|
+
const filePath = node_path.join(io.workspaceRoot, directory, relativePath);
|
|
40
|
+
if (void 0 === node_fs.lstatSync(filePath, {
|
|
41
|
+
throwIfNoEntry: false
|
|
42
|
+
})) io.writeGenerated(filePath, content);
|
|
43
|
+
};
|
|
44
|
+
writeMissing('src/effect-bff-runtime.ts', readFileTemplate('packages/effect-bff-runtime.ts'));
|
|
45
|
+
writeMissing('src/microvertical-api-baseline.ts', readFileTemplate('packages/microvertical-api-baseline.ts'));
|
|
46
|
+
writeMissing('tsconfig.json', `${JSON.stringify(createSharedPackageTsConfig(directory), null, 2)}\n`);
|
|
47
|
+
writeMissing('src/index.ts', readFileTemplate('packages/shared-contracts-index.ts'));
|
|
48
|
+
writeJsonFile(io, manifestPath, manifest);
|
|
49
|
+
}
|
|
50
|
+
export { ensureSharedApiInfrastructure };
|
|
@@ -35,6 +35,7 @@ function updateRootPackageToolchain(packageJson) {
|
|
|
35
35
|
const devDependencies = ensureRecord(packageJson, 'devDependencies', 'package.json devDependencies');
|
|
36
36
|
for (const packageName of retiredRootToolingDependencies)delete devDependencies[packageName];
|
|
37
37
|
devDependencies["@typescript/native"] = ULTRAMODERN_PACKAGE_PINS.rootDevDependencies["@typescript/native"];
|
|
38
|
+
devDependencies['@types/node'] = ULTRAMODERN_PACKAGE_PINS.rootDevDependencies["@types/node"];
|
|
38
39
|
devDependencies.miniflare = ULTRAMODERN_PACKAGE_PINS.rootDevDependencies.miniflare;
|
|
39
40
|
}
|
|
40
41
|
function updateMiseTools(content) {
|
|
@@ -18,6 +18,7 @@ import { ensureBffEffectDependencies, updateGeneratedPackageScripts, updateGener
|
|
|
18
18
|
import { createMigrationPackageSource } from "./migrate-strict-effect/package-source.js";
|
|
19
19
|
import { updateGeneratedPnpmWorkspacePolicy, validateGeneratedPnpmLockReleaseAgePolicy } from "./migrate-strict-effect/pnpm-policy.js";
|
|
20
20
|
import { ensureGeneratedModuleFederationBridgeRouterOptOut, preflightModuleFederationBridgeRouter, removeRetiredReactRouterDependency } from "./migrate-strict-effect/react-router-retirement.js";
|
|
21
|
+
import { ensureSharedApiInfrastructure } from "./migrate-strict-effect/shared-api-infrastructure.js";
|
|
21
22
|
import { updateGeneratedToolchainFiles, updateRootPackageToolchain } from "./migrate-strict-effect/toolchain-pins.js";
|
|
22
23
|
import { hasFlag } from "./options.js";
|
|
23
24
|
const retiredMetadataPaths = [
|
|
@@ -282,6 +283,7 @@ function migrateStrictEffect(args, context, io, dryRun, skipInstall) {
|
|
|
282
283
|
const verticalApps = migrated.topology.apps.filter((app)=>'shell' !== app.kind);
|
|
283
284
|
const hasBackendSurface = verticalApps.some((app)=>app.api);
|
|
284
285
|
const shellOnly = 0 === verticalApps.length;
|
|
286
|
+
if (hasBackendSurface) ensureSharedApiInfrastructure(io, migrated.workspace.packageScope, packageSource);
|
|
285
287
|
if (shellOnly) {
|
|
286
288
|
io.log('Shell-only workspace: skipping backend-federation and Zerops runtime stages.');
|
|
287
289
|
for (const relativePath of [
|
|
@@ -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 };
|
|
@@ -11,7 +11,11 @@ function extractExposes(configPath, value) {
|
|
|
11
11
|
if (array) return array.sort();
|
|
12
12
|
throw new Error(`Cannot statically extract Module Federation exposes from ${configPath}; use a literal exposes object or string array.`);
|
|
13
13
|
}
|
|
14
|
-
function extractDtsSettings(configPath, value) {
|
|
14
|
+
function extractDtsSettings(configPath, value, exposes) {
|
|
15
|
+
if ('false' === value) {
|
|
16
|
+
if (exposes.length > 0) throw new Error(`Module Federation DTS cannot be disabled for exposed app ${configPath}.`);
|
|
17
|
+
return {};
|
|
18
|
+
}
|
|
15
19
|
if (void 0 === value) return {};
|
|
16
20
|
const dts = parseObjectLiteral(value);
|
|
17
21
|
if (!dts || dts.hasSpread) throw new Error(`Cannot statically extract Module Federation DTS settings from ${configPath}; use a literal dts object.`);
|
|
@@ -38,7 +42,7 @@ function inspectModuleFederationConfigSource(source, appDir, configPath) {
|
|
|
38
42
|
return {
|
|
39
43
|
appDir,
|
|
40
44
|
configPath,
|
|
41
|
-
dts: extractDtsSettings(configPath, properties.properties.get('dts')),
|
|
45
|
+
dts: extractDtsSettings(configPath, properties.properties.get('dts'), exposes),
|
|
42
46
|
exposes,
|
|
43
47
|
hostOnlyNoExposes
|
|
44
48
|
};
|
|
@@ -45,22 +45,17 @@ const ultramodernBuildArtifact = {
|
|
|
45
45
|
},
|
|
46
46
|
} as const;
|
|
47
47
|
|
|
48
|
-
export { ultramodernBuildArtifact };
|
|
49
|
-
|
|
50
48
|
export const ultramodernDeliveryUnit =
|
|
51
49
|
ultramodernBuildArtifact.deliveryUnit;
|
|
52
|
-
export const ultramodernVerticalIdentity = ultramodernDeliveryUnit;
|
|
53
50
|
export const ultramodernUiMarker = ultramodernBuildArtifact.surfaces.ui;
|
|
54
51
|
export const ultramodernApiMarker = ultramodernBuildArtifact.surfaces.api;
|
|
55
52
|
`;
|
|
56
53
|
}
|
|
57
54
|
function createUltramodernBuildReexportModule() {
|
|
58
55
|
return `export {
|
|
59
|
-
ultramodernBuildArtifact,
|
|
60
56
|
ultramodernApiMarker,
|
|
61
57
|
ultramodernDeliveryUnit,
|
|
62
58
|
ultramodernUiMarker,
|
|
63
|
-
ultramodernVerticalIdentity,
|
|
64
59
|
} from '../shared/ultramodern-build';
|
|
65
60
|
`;
|
|
66
61
|
}
|
|
@@ -171,7 +171,7 @@ function createAppPackage(scope, app, packageSource, enableTailwind, remotes = [
|
|
|
171
171
|
if (Object.keys(packageExports).length > 0) packageJson.exports = packageExports;
|
|
172
172
|
return packageJson;
|
|
173
173
|
}
|
|
174
|
-
function createSharedPackage(scope, id, description) {
|
|
174
|
+
function createSharedPackage(scope, id, description, packageSource) {
|
|
175
175
|
const packageJson = {
|
|
176
176
|
private: true,
|
|
177
177
|
name: packageName(scope, id),
|
|
@@ -188,6 +188,17 @@ function createSharedPackage(scope, id, description) {
|
|
|
188
188
|
'@effect/tsgo': ULTRAMODERN_PACKAGE_PINS.appDevDependencies["@effect/tsgo"]
|
|
189
189
|
}
|
|
190
190
|
};
|
|
191
|
+
if ('shared-contracts' === id) {
|
|
192
|
+
packageJson.exports = {
|
|
193
|
+
'.': './src/index.ts',
|
|
194
|
+
'./microvertical-api-baseline': './src/microvertical-api-baseline.ts',
|
|
195
|
+
'./server/effect-bff-runtime': './src/effect-bff-runtime.ts'
|
|
196
|
+
};
|
|
197
|
+
packageJson.dependencies = {
|
|
198
|
+
...ULTRAMODERN_PACKAGE_PINS.bffEffectDependencies,
|
|
199
|
+
'@modern-js/plugin-bff': packageSource ? modernPackageSpecifier('@modern-js/plugin-bff', packageSource) : WORKSPACE_PACKAGE_VERSION
|
|
200
|
+
};
|
|
201
|
+
}
|
|
191
202
|
if ('shared-design-tokens' === id) packageJson.exports = {
|
|
192
203
|
...packageJson.exports,
|
|
193
204
|
'./tokens.css': './src/tokens.css'
|
|
@@ -249,6 +249,7 @@ const ULTRAMODERN_PACKAGE_PINS = {
|
|
|
249
249
|
rootDevDependencies: {
|
|
250
250
|
'@effect/tsgo': EFFECT_TSGO_VERSION,
|
|
251
251
|
"@typescript/native": `npm:typescript@${TYPESCRIPT_VERSION}`,
|
|
252
|
+
'@types/node': TYPES_NODE_VERSION,
|
|
252
253
|
'cross-env': CROSS_ENV_VERSION,
|
|
253
254
|
lefthook: LEFTHOOK_VERSION,
|
|
254
255
|
miniflare: MINIFLARE_VERSION,
|
|
@@ -139,7 +139,7 @@ function createWorkspaceRootScriptPlan(remotes = [], options = {}) {
|
|
|
139
139
|
migrateStrictEffect: rootToolingWrapperCommand('migrateStrictEffect'),
|
|
140
140
|
zeropsMaterialize: "node ./scripts/materialize-zerops-runtime.mjs",
|
|
141
141
|
contractCheck: rootToolingWrapperCommand('validate'),
|
|
142
|
-
typecheck: options.typecheck ?? `${rootToolingWrapperCommand('typecheck')} --
|
|
142
|
+
typecheck: options.typecheck ?? `${rootToolingWrapperCommand('typecheck')} --build tsconfig.json`,
|
|
143
143
|
check: `pnpm format:check && pnpm lint && pnpm typecheck && pnpm skills:check && pnpm i18n:boundaries && pnpm api:check && pnpm contract:check && pnpm performance:readiness${bridgeCheck}`
|
|
144
144
|
};
|
|
145
145
|
}
|
|
@@ -129,6 +129,7 @@ function writeGeneratedWorkspaceScripts(targetDir, scope, enableTailwind, remote
|
|
|
129
129
|
const hasBackendSurface = remotes.some(appHasApi);
|
|
130
130
|
writeWorkspaceOwnedMtsScript(targetDir, 'check-ultramodern-i18n-boundaries', createWorkspaceI18nBoundaryValidationScript());
|
|
131
131
|
writeWorkspaceOwnedMtsScript(targetDir, 'check-ultramodern-api-boundaries', createWorkspaceApiBoundaryValidationScript());
|
|
132
|
+
writeWorkspaceOwnedMtsScript(targetDir, 'microvertical-api-baseline-boundary', external_fs_io_js_readFileTemplate("workspace-scripts/microvertical-api-baseline-boundary.mts"));
|
|
132
133
|
if (!shellOnly) {
|
|
133
134
|
writeFileReplacing(targetDir, "scripts/materialize-zerops-runtime.mjs", createZeropsRuntimeMaterializationScript());
|
|
134
135
|
writeWorkspaceOwnedMtsScript(targetDir, 'proof-workerd-ssr', createWorkerdSsrProofScript());
|
|
@@ -156,6 +157,10 @@ function migratedWorkspaceScriptArtifacts(options) {
|
|
|
156
157
|
content: createWorkspaceI18nBoundaryValidationScript(),
|
|
157
158
|
legacyPath: "scripts/check-ultramodern-i18n-boundaries.mjs"
|
|
158
159
|
},
|
|
160
|
+
{
|
|
161
|
+
relativePath: "scripts/microvertical-api-baseline-boundary.mts",
|
|
162
|
+
content: external_fs_io_js_readFileTemplate("workspace-scripts/microvertical-api-baseline-boundary.mts")
|
|
163
|
+
},
|
|
159
164
|
{
|
|
160
165
|
relativePath: "scripts/check-ultramodern-api-boundaries.mts",
|
|
161
166
|
content: createWorkspaceApiBoundaryValidationScript(),
|
|
@@ -88,11 +88,17 @@ function writeAppRouteAndShellFiles({ targetDir, scope, resolvedApp, emitsUi, re
|
|
|
88
88
|
function writeAppApiAndRemoteExposeFiles({ targetDir, scope, resolvedApp, emitsUi, writeAppFile }) {
|
|
89
89
|
if (appHasApi(resolvedApp)) {
|
|
90
90
|
const rpcProtocol = 'rpc' === resolveApiProtocol(resolvedApp);
|
|
91
|
-
rpcProtocol ? writeFile(targetDir, `${resolvedApp.directory}/shared/rpc.ts`, createRpcContractFile(resolvedApp)) : writeFile(targetDir, `${resolvedApp.directory}/shared/api.ts`, createSharedApi(resolvedApp
|
|
92
|
-
|
|
91
|
+
rpcProtocol ? writeFile(targetDir, `${resolvedApp.directory}/shared/rpc.ts`, createRpcContractFile(resolvedApp)) : writeFile(targetDir, `${resolvedApp.directory}/shared/api.ts`, createSharedApi(resolvedApp, {
|
|
92
|
+
scope
|
|
93
|
+
}));
|
|
94
|
+
writeFile(targetDir, `${resolvedApp.directory}/api/index.ts`, createApiServiceEntry(resolvedApp, rpcProtocol ? '../shared/rpc.ts' : '../shared/api.ts', {
|
|
95
|
+
scope
|
|
96
|
+
}));
|
|
93
97
|
writeFile(targetDir, `${resolvedApp.directory}/api/backend-federation.ts`, createBackendFederationContractFile(resolvedApp));
|
|
94
98
|
writeFile(targetDir, `${resolvedApp.directory}/api/effect-api.ts`, createBackendEffectApiExpose(scope, resolvedApp));
|
|
95
|
-
rpcProtocol ? writeFile(targetDir, `${resolvedApp.directory}/src/api/${resolvedApp.api.stem}-rpc-client.ts`, createRpcClientFile(resolvedApp)) : writeFile(targetDir, `${resolvedApp.directory}/src/api/${resolvedApp.api.stem}-client.ts`, createApiClient(resolvedApp, '../../shared/api'
|
|
99
|
+
rpcProtocol ? writeFile(targetDir, `${resolvedApp.directory}/src/api/${resolvedApp.api.stem}-rpc-client.ts`, createRpcClientFile(resolvedApp)) : writeFile(targetDir, `${resolvedApp.directory}/src/api/${resolvedApp.api.stem}-client.ts`, createApiClient(resolvedApp, '../../shared/api', {
|
|
100
|
+
scope
|
|
101
|
+
}));
|
|
96
102
|
}
|
|
97
103
|
if ('vertical' === resolvedApp.kind && emitsUi) {
|
|
98
104
|
writeAppFile('src/federation-entry.tsx', createRemoteEntry(resolvedApp));
|
|
@@ -5,7 +5,7 @@ import { createSharedDesignTokensCss } from "./app-files.js";
|
|
|
5
5
|
import { normalizeUltramodernBridgeConfig } from "./bridge-config.js";
|
|
6
6
|
import { createDevelopmentOverlay, createOwnership, createTopology, createUltramodernConfig } from "./contracts.js";
|
|
7
7
|
import { ULTRAMODERN_CONFIG_PATH, createShellHost, sharedPackages, shellApp } from "./descriptors.js";
|
|
8
|
-
import { copyRootTemplate, formatGeneratedWorkspaceFiles, writeFile, writeFileReplacing, writeJson } from "./fs-io.js";
|
|
8
|
+
import { copyRootTemplate, formatGeneratedWorkspaceFiles, readFileTemplate, writeFile, writeFileReplacing, writeJson } from "./fs-io.js";
|
|
9
9
|
import { createFileSnapshot, createGenerationResult, diffFileSnapshots } from "./generation-result.js";
|
|
10
10
|
import { assertUniqueTailwindPrefixes, toPackageScope } from "./naming.js";
|
|
11
11
|
import { runCodeSmithOverlays } from "./overlays.js";
|
|
@@ -19,12 +19,14 @@ import { createZeropsYaml } from "./zerops.js";
|
|
|
19
19
|
function hasExplicitInstallRequest(options) {
|
|
20
20
|
return void 0 !== options.packageSource && 'workspace' !== options.packageSource.strategy;
|
|
21
21
|
}
|
|
22
|
-
function writeSharedPackages(targetDir, scope) {
|
|
22
|
+
function writeSharedPackages(targetDir, scope, packageSource) {
|
|
23
23
|
for (const sharedPackage of sharedPackages){
|
|
24
|
-
writeJson(targetDir, `${sharedPackage.directory}/package.json`, createSharedPackage(scope, sharedPackage.id, sharedPackage.description));
|
|
24
|
+
writeJson(targetDir, `${sharedPackage.directory}/package.json`, createSharedPackage(scope, sharedPackage.id, sharedPackage.description, packageSource));
|
|
25
25
|
writeJson(targetDir, `${sharedPackage.directory}/tsconfig.json`, createSharedPackageTsConfig(sharedPackage.directory));
|
|
26
26
|
}
|
|
27
27
|
writeFile(targetDir, 'packages/shared-contracts/src/index.ts', createSharedContractsIndex());
|
|
28
|
+
writeFile(targetDir, 'packages/shared-contracts/src/microvertical-api-baseline.ts', readFileTemplate('packages/microvertical-api-baseline.ts'));
|
|
29
|
+
writeFile(targetDir, 'packages/shared-contracts/src/effect-bff-runtime.ts', readFileTemplate('packages/effect-bff-runtime.ts'));
|
|
28
30
|
writeFile(targetDir, 'packages/shared-design-tokens/src/index.ts', `export const sharedDesignTokens = {
|
|
29
31
|
color: {
|
|
30
32
|
accent: '#2f8f68',
|
|
@@ -144,7 +146,7 @@ function generateUltramodernWorkspaceInPlace(options, logicalWorkspaceRoot = opt
|
|
|
144
146
|
writeJson(options.targetDir, ULTRAMODERN_CONFIG_PATH, createCompactUltramodernConfig(scope, options.modernVersion, packageSource, createdApps, enableTailwind, bridge));
|
|
145
147
|
writeApp(options.targetDir, scope, shellApp, packageSource, enableTailwind, initialVerticals, bridge);
|
|
146
148
|
for (const remote of initialVerticals)writeApp(options.targetDir, scope, remote, packageSource, enableTailwind, initialVerticals, bridge);
|
|
147
|
-
writeSharedPackages(options.targetDir, scope);
|
|
149
|
+
writeSharedPackages(options.targetDir, scope, packageSource);
|
|
148
150
|
writeGeneratedWorkspaceScripts(options.targetDir, scope, enableTailwind, initialVerticals, releaseCohort);
|
|
149
151
|
const preliminaryAfterFiles = createFileSnapshot(options.targetDir);
|
|
150
152
|
const preliminaryDiff = diffFileSnapshots(beforeFiles, preliminaryAfterFiles);
|
|
@@ -71,6 +71,7 @@ function mergeTypeScriptConfig(generated, existing) {
|
|
|
71
71
|
...existingCompilerOptions,
|
|
72
72
|
...generatedCompilerOptions
|
|
73
73
|
};
|
|
74
|
+
if (Array.isArray(generatedCompilerOptions.types)) compilerOptions.types = mergeUniqueJsonValues(generatedCompilerOptions.types, existingCompilerOptions.types);
|
|
74
75
|
if (Array.isArray(generatedCompilerOptions.plugins) || Array.isArray(existingCompilerOptions.plugins)) compilerOptions.plugins = mergeTypeScriptPlugins(generatedCompilerOptions.plugins, existingCompilerOptions.plugins);
|
|
75
76
|
const merged = {
|
|
76
77
|
...existingConfig,
|
|
@@ -136,6 +136,8 @@ function mergeGeneratedScript(consumer, framework, canRetireLegacyOxfmtCliExclus
|
|
|
136
136
|
const frameworkSegmentSet = new Set(frameworkSegments);
|
|
137
137
|
const canonicalFrameworkSegment = (segment)=>{
|
|
138
138
|
if (frameworkSegmentSet.has(segment)) return segment;
|
|
139
|
+
const referenceBuild = segment.replace('--project tsconfig.json', '--build tsconfig.json');
|
|
140
|
+
if (frameworkSegmentSet.has(referenceBuild)) return referenceBuild;
|
|
139
141
|
const portableEnvironmentSegment = `cross-env ${segment}`;
|
|
140
142
|
if (frameworkSegmentSet.has(portableEnvironmentSegment)) return portableEnvironmentSegment;
|
|
141
143
|
const portableOxfmtSegment = segment.replace(/ '!repos\/\*\*'$/u, '');
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import "node:module";
|
|
2
|
+
import node_fs from "node:fs";
|
|
3
|
+
import node_path from "node:path";
|
|
4
|
+
import { readFileTemplate } from "../../../ultramodern-workspace/fs-io.js";
|
|
5
|
+
import { createSharedPackage, createSharedPackageTsConfig } from "../../../ultramodern-workspace/package-json.js";
|
|
6
|
+
import { readJsonFile, writeJsonFile } from "./io.js";
|
|
7
|
+
const directory = 'packages/shared-contracts';
|
|
8
|
+
function record(value, label) {
|
|
9
|
+
if (null === value || 'object' != typeof value || Array.isArray(value)) throw new Error(`${label} must be an object; resolve it before migration.`);
|
|
10
|
+
return value;
|
|
11
|
+
}
|
|
12
|
+
function ensureSharedApiInfrastructure(io, scope, packageSource) {
|
|
13
|
+
for (const relativePath of [
|
|
14
|
+
'packages',
|
|
15
|
+
directory,
|
|
16
|
+
`${directory}/package.json`,
|
|
17
|
+
`${directory}/src`,
|
|
18
|
+
`${directory}/src/index.ts`
|
|
19
|
+
])if (node_fs.lstatSync(node_path.join(io.workspaceRoot, relativePath), {
|
|
20
|
+
throwIfNoEntry: false
|
|
21
|
+
})?.isSymbolicLink()) throw new Error(`Shared API migration cannot write through a symbolic link: ${relativePath}`);
|
|
22
|
+
const manifestPath = node_path.join(io.workspaceRoot, directory, 'package.json');
|
|
23
|
+
const defaults = createSharedPackage(scope, 'shared-contracts', 'Shared workspace contracts', packageSource);
|
|
24
|
+
const manifest = node_fs.existsSync(manifestPath) ? record(readJsonFile(manifestPath), `${directory}/package.json`) : defaults;
|
|
25
|
+
const exports = void 0 === manifest.exports ? {} : 'string' == typeof manifest.exports ? {
|
|
26
|
+
'.': manifest.exports
|
|
27
|
+
} : record(manifest.exports, `${directory}/package.json exports`);
|
|
28
|
+
if (Object.keys(exports).some((key)=>!key.startsWith('.')) || void 0 !== exports['.'] && './src/index.ts' !== exports['.']) throw new Error(`${directory}/package.json exports must expose ./src/index.ts at "." before adding shared API infrastructure; consumer exports were not overwritten.`);
|
|
29
|
+
for (const [subpath, target] of Object.entries(record(defaults.exports, 'Shared contract exports')))if (void 0 !== exports[subpath] && exports[subpath] !== target) throw new Error(`${directory}/package.json ${subpath} must expose ${String(target)}; consumer exports were not overwritten.`);
|
|
30
|
+
const dependencies = record(manifest.dependencies ?? {}, `${directory}/package.json dependencies`);
|
|
31
|
+
manifest.exports = {
|
|
32
|
+
...record(defaults.exports, 'Shared contract exports'),
|
|
33
|
+
...exports
|
|
34
|
+
};
|
|
35
|
+
manifest.dependencies = {
|
|
36
|
+
...record(defaults.dependencies, 'Shared contract dependencies'),
|
|
37
|
+
...dependencies
|
|
38
|
+
};
|
|
39
|
+
const writeMissing = (relativePath, content)=>{
|
|
40
|
+
const filePath = node_path.join(io.workspaceRoot, directory, relativePath);
|
|
41
|
+
if (void 0 === node_fs.lstatSync(filePath, {
|
|
42
|
+
throwIfNoEntry: false
|
|
43
|
+
})) io.writeGenerated(filePath, content);
|
|
44
|
+
};
|
|
45
|
+
writeMissing('src/effect-bff-runtime.ts', readFileTemplate('packages/effect-bff-runtime.ts'));
|
|
46
|
+
writeMissing('src/microvertical-api-baseline.ts', readFileTemplate('packages/microvertical-api-baseline.ts'));
|
|
47
|
+
writeMissing('tsconfig.json', `${JSON.stringify(createSharedPackageTsConfig(directory), null, 2)}\n`);
|
|
48
|
+
writeMissing('src/index.ts', readFileTemplate('packages/shared-contracts-index.ts'));
|
|
49
|
+
writeJsonFile(io, manifestPath, manifest);
|
|
50
|
+
}
|
|
51
|
+
export { ensureSharedApiInfrastructure };
|
|
@@ -36,6 +36,7 @@ function updateRootPackageToolchain(packageJson) {
|
|
|
36
36
|
const devDependencies = ensureRecord(packageJson, 'devDependencies', 'package.json devDependencies');
|
|
37
37
|
for (const packageName of retiredRootToolingDependencies)delete devDependencies[packageName];
|
|
38
38
|
devDependencies["@typescript/native"] = ULTRAMODERN_PACKAGE_PINS.rootDevDependencies["@typescript/native"];
|
|
39
|
+
devDependencies['@types/node'] = ULTRAMODERN_PACKAGE_PINS.rootDevDependencies["@types/node"];
|
|
39
40
|
devDependencies.miniflare = ULTRAMODERN_PACKAGE_PINS.rootDevDependencies.miniflare;
|
|
40
41
|
}
|
|
41
42
|
function updateMiseTools(content) {
|
|
@@ -19,6 +19,7 @@ import { ensureBffEffectDependencies, updateGeneratedPackageScripts, updateGener
|
|
|
19
19
|
import { createMigrationPackageSource } from "./migrate-strict-effect/package-source.js";
|
|
20
20
|
import { updateGeneratedPnpmWorkspacePolicy, validateGeneratedPnpmLockReleaseAgePolicy } from "./migrate-strict-effect/pnpm-policy.js";
|
|
21
21
|
import { ensureGeneratedModuleFederationBridgeRouterOptOut, preflightModuleFederationBridgeRouter, removeRetiredReactRouterDependency } from "./migrate-strict-effect/react-router-retirement.js";
|
|
22
|
+
import { ensureSharedApiInfrastructure } from "./migrate-strict-effect/shared-api-infrastructure.js";
|
|
22
23
|
import { updateGeneratedToolchainFiles, updateRootPackageToolchain } from "./migrate-strict-effect/toolchain-pins.js";
|
|
23
24
|
import { hasFlag } from "./options.js";
|
|
24
25
|
const retiredMetadataPaths = [
|
|
@@ -283,6 +284,7 @@ function migrateStrictEffect(args, context, io, dryRun, skipInstall) {
|
|
|
283
284
|
const verticalApps = migrated.topology.apps.filter((app)=>'shell' !== app.kind);
|
|
284
285
|
const hasBackendSurface = verticalApps.some((app)=>app.api);
|
|
285
286
|
const shellOnly = 0 === verticalApps.length;
|
|
287
|
+
if (hasBackendSurface) ensureSharedApiInfrastructure(io, migrated.workspace.packageScope, packageSource);
|
|
286
288
|
if (shellOnly) {
|
|
287
289
|
io.log('Shell-only workspace: skipping backend-federation and Zerops runtime stages.');
|
|
288
290
|
for (const relativePath of [
|