@bleedingdev/modern-js-create 3.4.0-ultramodern.2 → 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.
Files changed (128) hide show
  1. package/README.md +13 -11
  2. package/dist/cjs/index.cjs +37 -4
  3. package/dist/cjs/locale/en.cjs +12 -2
  4. package/dist/cjs/locale/zh.cjs +12 -2
  5. package/dist/cjs/ultramodern-tooling/commands.cjs +185 -0
  6. package/dist/cjs/ultramodern-tooling/config.cjs +172 -0
  7. package/dist/cjs/ultramodern-workspace/add-vertical.cjs +79 -61
  8. package/dist/cjs/ultramodern-workspace/bridge-config.cjs +327 -0
  9. package/dist/cjs/ultramodern-workspace/codesmith.cjs +1 -0
  10. package/dist/cjs/ultramodern-workspace/contracts.cjs +121 -648
  11. package/dist/cjs/ultramodern-workspace/demo-components.cjs +26 -33
  12. package/dist/cjs/ultramodern-workspace/descriptors.cjs +4 -4
  13. package/dist/cjs/ultramodern-workspace/effect-api.cjs +230 -100
  14. package/dist/cjs/ultramodern-workspace/fs-io.cjs +117 -0
  15. package/dist/cjs/ultramodern-workspace/generation-result.cjs +1 -1
  16. package/dist/cjs/ultramodern-workspace/index.cjs +4 -0
  17. package/dist/cjs/ultramodern-workspace/mf-validation.cjs +571 -0
  18. package/dist/cjs/ultramodern-workspace/module-federation.cjs +51 -20
  19. package/dist/cjs/ultramodern-workspace/package-json.cjs +62 -19
  20. package/dist/cjs/ultramodern-workspace/policy.cjs +2 -2
  21. package/dist/cjs/ultramodern-workspace/public-api.cjs +4 -0
  22. package/dist/cjs/ultramodern-workspace/versions.cjs +5 -0
  23. package/dist/cjs/ultramodern-workspace/workspace-scripts.cjs +80 -43
  24. package/dist/cjs/ultramodern-workspace/write-workspace.cjs +63 -20
  25. package/dist/esm/index.js +37 -4
  26. package/dist/esm/locale/en.js +12 -2
  27. package/dist/esm/locale/zh.js +12 -2
  28. package/dist/esm/ultramodern-tooling/commands.js +132 -0
  29. package/dist/esm/ultramodern-tooling/config.js +120 -0
  30. package/dist/esm/ultramodern-workspace/add-vertical.js +81 -66
  31. package/dist/esm/ultramodern-workspace/bridge-config.js +270 -0
  32. package/dist/esm/ultramodern-workspace/codesmith.js +1 -0
  33. package/dist/esm/ultramodern-workspace/contracts.js +124 -603
  34. package/dist/esm/ultramodern-workspace/demo-components.js +26 -33
  35. package/dist/esm/ultramodern-workspace/descriptors.js +2 -2
  36. package/dist/esm/ultramodern-workspace/effect-api.js +230 -100
  37. package/dist/esm/ultramodern-workspace/fs-io.js +116 -2
  38. package/dist/esm/ultramodern-workspace/generation-result.js +2 -2
  39. package/dist/esm/ultramodern-workspace/index.js +1 -0
  40. package/dist/esm/ultramodern-workspace/mf-validation.js +516 -0
  41. package/dist/esm/ultramodern-workspace/module-federation.js +49 -21
  42. package/dist/esm/ultramodern-workspace/package-json.js +60 -20
  43. package/dist/esm/ultramodern-workspace/policy.js +2 -2
  44. package/dist/esm/ultramodern-workspace/public-api.js +1 -0
  45. package/dist/esm/ultramodern-workspace/versions.js +3 -1
  46. package/dist/esm/ultramodern-workspace/workspace-scripts.js +78 -26
  47. package/dist/esm/ultramodern-workspace/write-workspace.js +67 -27
  48. package/dist/esm-node/index.js +37 -4
  49. package/dist/esm-node/locale/en.js +12 -2
  50. package/dist/esm-node/locale/zh.js +12 -2
  51. package/dist/esm-node/ultramodern-tooling/commands.js +133 -0
  52. package/dist/esm-node/ultramodern-tooling/config.js +121 -0
  53. package/dist/esm-node/ultramodern-workspace/add-vertical.js +81 -66
  54. package/dist/esm-node/ultramodern-workspace/bridge-config.js +271 -0
  55. package/dist/esm-node/ultramodern-workspace/codesmith.js +1 -0
  56. package/dist/esm-node/ultramodern-workspace/contracts.js +124 -603
  57. package/dist/esm-node/ultramodern-workspace/demo-components.js +26 -33
  58. package/dist/esm-node/ultramodern-workspace/descriptors.js +2 -2
  59. package/dist/esm-node/ultramodern-workspace/effect-api.js +230 -100
  60. package/dist/esm-node/ultramodern-workspace/fs-io.js +116 -2
  61. package/dist/esm-node/ultramodern-workspace/generation-result.js +2 -2
  62. package/dist/esm-node/ultramodern-workspace/index.js +1 -0
  63. package/dist/esm-node/ultramodern-workspace/mf-validation.js +517 -0
  64. package/dist/esm-node/ultramodern-workspace/module-federation.js +49 -21
  65. package/dist/esm-node/ultramodern-workspace/package-json.js +60 -20
  66. package/dist/esm-node/ultramodern-workspace/policy.js +2 -2
  67. package/dist/esm-node/ultramodern-workspace/public-api.js +1 -0
  68. package/dist/esm-node/ultramodern-workspace/versions.js +3 -1
  69. package/dist/esm-node/ultramodern-workspace/workspace-scripts.js +78 -26
  70. package/dist/esm-node/ultramodern-workspace/write-workspace.js +67 -27
  71. package/dist/types/locale/en.d.ts +10 -0
  72. package/dist/types/locale/index.d.ts +20 -0
  73. package/dist/types/locale/zh.d.ts +10 -0
  74. package/dist/types/ultramodern-tooling/commands.d.ts +1 -0
  75. package/dist/types/ultramodern-tooling/config.d.ts +43 -0
  76. package/dist/types/ultramodern-workspace/add-vertical.d.ts +5 -2
  77. package/dist/types/ultramodern-workspace/bridge-config.d.ts +64 -0
  78. package/dist/types/ultramodern-workspace/codesmith.d.ts +1 -0
  79. package/dist/types/ultramodern-workspace/contracts.d.ts +2 -17
  80. package/dist/types/ultramodern-workspace/descriptors.d.ts +1 -1
  81. package/dist/types/ultramodern-workspace/fs-io.d.ts +1 -0
  82. package/dist/types/ultramodern-workspace/index.d.ts +2 -0
  83. package/dist/types/ultramodern-workspace/mf-validation.d.ts +27 -0
  84. package/dist/types/ultramodern-workspace/module-federation.d.ts +1 -0
  85. package/dist/types/ultramodern-workspace/package-json.d.ts +5 -3
  86. package/dist/types/ultramodern-workspace/public-api.d.ts +2 -0
  87. package/dist/types/ultramodern-workspace/types.d.ts +2 -0
  88. package/dist/types/ultramodern-workspace/versions.d.ts +2 -0
  89. package/dist/types/ultramodern-workspace/workspace-scripts.d.ts +2 -7
  90. package/dist/types/ultramodern-workspace/write-workspace.d.ts +4 -2
  91. package/package.json +5 -3
  92. package/template-workspace/{.agents → .codex}/skills-lock.json +12 -12
  93. package/template-workspace/.gitignore.handlebars +3 -0
  94. package/template-workspace/AGENTS.md.handlebars +11 -5
  95. package/template-workspace/README.md.handlebars +15 -12
  96. package/template-workspace/oxfmt.config.ts +1 -0
  97. package/template-workspace/oxlint.config.ts +1 -0
  98. package/template-workspace/patches/@tanstack__router-core@1.171.13.patch +51 -0
  99. package/template-workspace/pnpm-workspace.yaml.handlebars +12 -0
  100. package/template-workspace/scripts/bootstrap-agent-skills.mjs +96 -69
  101. package/templates/app/shell-frame.tsx +1 -2
  102. package/templates/app/ultramodern-route-head.tsx.handlebars +5 -6
  103. package/templates/workspace-scripts/assert-mf-types.mjs.handlebars +59 -7
  104. package/templates/workspace-scripts/check-ultramodern-api-boundaries.mjs +339 -0
  105. package/templates/workspace-scripts/generate-public-surface-assets.mjs +245 -8
  106. package/templates/workspace-scripts/proof-cloudflare-version.mjs +276 -8
  107. package/templates/workspace-scripts/ultramodern-cloudflare-proof.mjs +35 -10
  108. package/templates/workspace-scripts/ultramodern-performance-readiness.mjs +164 -1
  109. package/templates/workspace-scripts/ultramodern-typecheck.mjs +4 -2
  110. package/templates/workspace-scripts/validate-ultramodern-workspace.mjs.handlebars +949 -63
  111. /package/template-workspace/{.agents → .codex}/rstackjs-agent-skills-LICENSE +0 -0
  112. /package/template-workspace/{.agents → .codex}/skills/rsbuild-best-practices/SKILL.md +0 -0
  113. /package/template-workspace/{.agents → .codex}/skills/rsdoctor-analysis/SKILL.md +0 -0
  114. /package/template-workspace/{.agents → .codex}/skills/rsdoctor-analysis/references/command-map.md +0 -0
  115. /package/template-workspace/{.agents → .codex}/skills/rsdoctor-analysis/references/common-analysis-patterns.md +0 -0
  116. /package/template-workspace/{.agents → .codex}/skills/rsdoctor-analysis/references/install-rsdoctor-common.md +0 -0
  117. /package/template-workspace/{.agents → .codex}/skills/rsdoctor-analysis/references/install-rsdoctor-rspack.md +0 -0
  118. /package/template-workspace/{.agents → .codex}/skills/rsdoctor-analysis/references/install-rsdoctor-webpack.md +0 -0
  119. /package/template-workspace/{.agents → .codex}/skills/rsdoctor-analysis/references/install-rsdoctor.md +0 -0
  120. /package/template-workspace/{.agents → .codex}/skills/rsdoctor-analysis/references/rsdoctor-data-types.md +0 -0
  121. /package/template-workspace/{.agents → .codex}/skills/rslib-best-practices/SKILL.md +0 -0
  122. /package/template-workspace/{.agents → .codex}/skills/rslib-modern-package/SKILL.md +0 -0
  123. /package/template-workspace/{.agents → .codex}/skills/rspack-best-practices/SKILL.md +0 -0
  124. /package/template-workspace/{.agents → .codex}/skills/rspack-tracing/SKILL.md +0 -0
  125. /package/template-workspace/{.agents → .codex}/skills/rspack-tracing/references/bottlenecks.md +0 -0
  126. /package/template-workspace/{.agents → .codex}/skills/rspack-tracing/references/tracing-guide.md +0 -0
  127. /package/template-workspace/{.agents → .codex}/skills/rspack-tracing/scripts/analyze_trace.js +0 -0
  128. /package/template-workspace/{.agents → .codex}/skills/rstest-best-practices/SKILL.md +0 -0
@@ -2,13 +2,13 @@ import "node:module";
2
2
  import { appHasEffectApi, effectApiPrefix, effectApiStem, verticalEffectApps } from "./descriptors.js";
3
3
  import { packageName, toCamelCase, toPascalCase } from "./naming.js";
4
4
  function verticalEffectApiExport(service) {
5
- return `${toCamelCase(effectApiStem(service))}EffectApi`;
5
+ return `${toCamelCase(effectApiStem(service))}Api`;
6
6
  }
7
7
  function verticalEffectGroupName(service) {
8
8
  return toCamelCase(effectApiStem(service));
9
9
  }
10
10
  function verticalEffectApiName(service) {
11
- return `${toPascalCase(effectApiStem(service))}EffectApi`;
11
+ return `${toPascalCase(effectApiStem(service))}Api`;
12
12
  }
13
13
  function verticalEffectSchemaExport(service) {
14
14
  return `${toCamelCase(effectApiStem(service))}ItemSchema`;
@@ -37,27 +37,51 @@ function serviceHasCheckoutCartState(service) {
37
37
  function createCheckoutCartSharedSchemas(service) {
38
38
  if (!serviceHasCheckoutCartState(service)) return '';
39
39
  return `
40
- export const checkoutCartLineSchema = Schema.Struct({
40
+ export interface CheckoutCartLine {
41
+ readonly sku: string;
42
+ readonly name: string;
43
+ readonly quantity: number;
44
+ readonly unitPriceCents: number;
45
+ }
46
+
47
+ export interface CheckoutCart {
48
+ readonly lines: readonly CheckoutCartLine[];
49
+ readonly subtotalCents: number;
50
+ readonly totalQuantity: number;
51
+ }
52
+
53
+ export interface CheckoutAddCartItemPayload {
54
+ readonly sku: string;
55
+ readonly name?: string;
56
+ readonly quantity: number;
57
+ readonly unitPriceCents?: number;
58
+ }
59
+
60
+ export interface CheckoutRemoveCartItemPayload {
61
+ readonly sku: string;
62
+ }
63
+
64
+ export const checkoutCartLineSchema: Schema.Codec<CheckoutCartLine> = Schema.Struct({
41
65
  sku: Schema.String,
42
66
  name: Schema.String,
43
67
  quantity: Schema.Finite,
44
68
  unitPriceCents: Schema.Finite,
45
69
  });
46
70
 
47
- export const checkoutCartSchema = Schema.Struct({
71
+ export const checkoutCartSchema: Schema.Codec<CheckoutCart> = Schema.Struct({
48
72
  lines: Schema.Array(checkoutCartLineSchema),
49
73
  subtotalCents: Schema.Finite,
50
74
  totalQuantity: Schema.Finite,
51
75
  });
52
76
 
53
- export const checkoutAddCartItemPayloadSchema = Schema.Struct({
77
+ export const checkoutAddCartItemPayloadSchema: Schema.Codec<CheckoutAddCartItemPayload> = Schema.Struct({
54
78
  sku: Schema.String,
55
79
  name: Schema.optional(Schema.String),
56
80
  quantity: Schema.Finite,
57
81
  unitPriceCents: Schema.optional(Schema.Finite),
58
82
  });
59
83
 
60
- export const checkoutRemoveCartItemPayloadSchema = Schema.Struct({
84
+ export const checkoutRemoveCartItemPayloadSchema: Schema.Codec<CheckoutRemoveCartItemPayload> = Schema.Struct({
61
85
  sku: Schema.String,
62
86
  });
63
87
  `;
@@ -66,24 +90,24 @@ function createCheckoutCartEndpointDefinitions(service) {
66
90
  if (!serviceHasCheckoutCartState(service)) return '';
67
91
  return `
68
92
  .add(
69
- HttpApiEndpoint.get('getCart', '/effect/checkout/cart', {
93
+ HttpApiEndpoint.get('getCart', '/checkout/cart', {
70
94
  success: checkoutCartSchema,
71
95
  }),
72
96
  )
73
97
  .add(
74
- HttpApiEndpoint.post('addCartItem', '/effect/checkout/cart/items', {
98
+ HttpApiEndpoint.post('addCartItem', '/checkout/cart/items', {
75
99
  payload: checkoutAddCartItemPayloadSchema,
76
100
  success: checkoutCartSchema,
77
101
  }),
78
102
  )
79
103
  .add(
80
- HttpApiEndpoint.post('removeCartItem', '/effect/checkout/cart/remove', {
104
+ HttpApiEndpoint.post('removeCartItem', '/checkout/cart/remove', {
81
105
  payload: checkoutRemoveCartItemPayloadSchema,
82
106
  success: checkoutCartSchema,
83
107
  }),
84
108
  )
85
109
  .add(
86
- HttpApiEndpoint.post('clearCart', '/effect/checkout/cart/clear', {
110
+ HttpApiEndpoint.post('clearCart', '/checkout/cart/clear', {
87
111
  success: checkoutCartSchema,
88
112
  }),
89
113
  )`;
@@ -96,31 +120,31 @@ function createCheckoutCartOperationContexts(service) {
96
120
  addCartItem: {
97
121
  method: 'POST',
98
122
  operationId: '${apiName}:${groupName}:addCartItem',
99
- routePath: '/effect/checkout/cart/items',
123
+ routePath: '/checkout/cart/items',
100
124
  source: 'generated-client',
101
125
  },
102
126
  clearCart: {
103
127
  method: 'POST',
104
128
  operationId: '${apiName}:${groupName}:clearCart',
105
- routePath: '/effect/checkout/cart/clear',
129
+ routePath: '/checkout/cart/clear',
106
130
  source: 'generated-client',
107
131
  },
108
132
  getCart: {
109
133
  method: 'GET',
110
134
  operationId: '${apiName}:${groupName}:getCart',
111
- routePath: '/effect/checkout/cart',
135
+ routePath: '/checkout/cart',
112
136
  source: 'generated-client',
113
137
  },
114
138
  removeCartItem: {
115
139
  method: 'POST',
116
140
  operationId: '${apiName}:${groupName}:removeCartItem',
117
- routePath: '/effect/checkout/cart/remove',
141
+ routePath: '/checkout/cart/remove',
118
142
  source: 'generated-client',
119
143
  },`;
120
144
  }
121
145
  function createCheckoutCartApiContractFields(service) {
122
146
  if (!serviceHasCheckoutCartState(service)) return '';
123
- return ` checkoutCartPath: '${effectApiPrefix(service)}/effect/checkout/cart',
147
+ return ` checkoutCartPath: '${effectApiPrefix(service)}/checkout/cart',
124
148
  `;
125
149
  }
126
150
  function createCheckoutCartServerState(service) {
@@ -156,7 +180,7 @@ function createCheckoutCartServerHandlers(service) {
156
180
  return `
157
181
  .handle('getCart', () =>
158
182
  Effect.sync(() => createCheckoutCartSnapshot()).pipe(
159
- Effect.withSpan('ultramodern.effect.${groupName}.checkout.getCart', {
183
+ Effect.withSpan('ultramodern.api.${groupName}.checkout.getCart', {
160
184
  attributes: operationAttributes(${groupName}OperationContexts.getCart),
161
185
  kind: 'server',
162
186
  }),
@@ -174,7 +198,7 @@ function createCheckoutCartServerHandlers(service) {
174
198
  });
175
199
  return createCheckoutCartSnapshot();
176
200
  }).pipe(
177
- Effect.withSpan('ultramodern.effect.${groupName}.checkout.addCartItem', {
201
+ Effect.withSpan('ultramodern.api.${groupName}.checkout.addCartItem', {
178
202
  attributes: operationAttributes(${groupName}OperationContexts.addCartItem),
179
203
  kind: 'server',
180
204
  }),
@@ -185,7 +209,7 @@ function createCheckoutCartServerHandlers(service) {
185
209
  checkoutCartLines.delete(payload.sku);
186
210
  return createCheckoutCartSnapshot();
187
211
  }).pipe(
188
- Effect.withSpan('ultramodern.effect.${groupName}.checkout.removeCartItem', {
212
+ Effect.withSpan('ultramodern.api.${groupName}.checkout.removeCartItem', {
189
213
  attributes: operationAttributes(${groupName}OperationContexts.removeCartItem),
190
214
  kind: 'server',
191
215
  }),
@@ -196,7 +220,7 @@ function createCheckoutCartServerHandlers(service) {
196
220
  checkoutCartLines.clear();
197
221
  return createCheckoutCartSnapshot();
198
222
  }).pipe(
199
- Effect.withSpan('ultramodern.effect.${groupName}.checkout.clearCart', {
223
+ Effect.withSpan('ultramodern.api.${groupName}.checkout.clearCart', {
200
224
  attributes: operationAttributes(${groupName}OperationContexts.clearCart),
201
225
  kind: 'server',
202
226
  }),
@@ -210,6 +234,7 @@ function createCheckoutCartClientExports(service) {
210
234
  const pascalStem = toPascalCase(stem);
211
235
  const clientOptionsName = `${pascalStem}ClientOptions`;
212
236
  const createClientName = `create${pascalStem}Client`;
237
+ const clientEffectTypeName = `${pascalStem}ClientEffect`;
213
238
  return `
214
239
  export interface CheckoutCartLine {
215
240
  sku: string;
@@ -233,7 +258,7 @@ export interface CheckoutAddCartItemInput {
233
258
 
234
259
  export const getCheckoutCart = (
235
260
  options: ${clientOptionsName} = {},
236
- ) =>
261
+ ): ${clientEffectTypeName}<CheckoutCart> =>
237
262
  ${createClientName}({
238
263
  ...options,
239
264
  operationContext:
@@ -245,7 +270,7 @@ export const getCheckoutCart = (
245
270
  export const addCheckoutCartItem = (
246
271
  payload: CheckoutAddCartItemInput,
247
272
  options: ${clientOptionsName} = {},
248
- ) =>
273
+ ): ${clientEffectTypeName}<CheckoutCart> =>
249
274
  ${createClientName}({
250
275
  ...options,
251
276
  operationContext:
@@ -259,7 +284,7 @@ export const addCheckoutCartItem = (
259
284
  export const removeCheckoutCartItem = (
260
285
  sku: string,
261
286
  options: ${clientOptionsName} = {},
262
- ) =>
287
+ ): ${clientEffectTypeName}<CheckoutCart> =>
263
288
  ${createClientName}({
264
289
  ...options,
265
290
  operationContext:
@@ -272,7 +297,7 @@ export const removeCheckoutCartItem = (
272
297
 
273
298
  export const clearCheckoutCart = (
274
299
  options: ${clientOptionsName} = {},
275
- ) =>
300
+ ): ${clientEffectTypeName}<CheckoutCart> =>
276
301
  ${createClientName}({
277
302
  ...options,
278
303
  operationContext:
@@ -303,12 +328,63 @@ function createEffectSharedApiContract(service) {
303
328
  const apiName = verticalEffectApiName(service);
304
329
  const groupName = verticalEffectGroupName(service);
305
330
  const stem = effectApiStem(service);
331
+ const pascalStem = toPascalCase(stem);
332
+ const markerType = `${pascalStem}Marker`;
333
+ const itemType = `${pascalStem}Item`;
334
+ const readinessType = `${pascalStem}Readiness`;
335
+ const createPayloadType = `${pascalStem}CreatePayload`;
336
+ const createResponseType = `${pascalStem}CreateResponse`;
337
+ const listResponseType = `${pascalStem}ListResponse`;
306
338
  const apiPrefix = effectApiPrefix(service);
307
339
  const checkoutCartSharedSchemas = createCheckoutCartSharedSchemas(service);
308
340
  const checkoutCartSharedSchemaSection = '' === checkoutCartSharedSchemas ? '' : `${checkoutCartSharedSchemas}\n`;
309
341
  const checkoutCartOperationContexts = createCheckoutCartOperationContexts(service).trimStart();
310
342
  const checkoutCartOperationContextEntries = '' === checkoutCartOperationContexts ? '' : `${checkoutCartOperationContexts}\n`;
311
- return `export const ${markerSchemaExport} = Schema.Struct({
343
+ return `export interface ${markerType} {
344
+ readonly appId: string;
345
+ readonly build: string;
346
+ readonly deployProfile: string;
347
+ readonly packageName: string;
348
+ readonly surface: string;
349
+ readonly version: string;
350
+ }
351
+
352
+ export interface ${itemType} {
353
+ readonly id: string;
354
+ readonly marker: ${markerType};
355
+ readonly title: string;
356
+ }
357
+
358
+ export interface ${readinessType} {
359
+ readonly checks: {
360
+ readonly api: 'ready';
361
+ readonly moduleFederation: 'ready';
362
+ readonly ssr: 'ready';
363
+ readonly translations: 'ready';
364
+ };
365
+ readonly marker: ${markerType};
366
+ readonly status: 'ready';
367
+ readonly versionSkew: 'none';
368
+ }
369
+
370
+ export interface ${createPayloadType} {
371
+ readonly title: string;
372
+ }
373
+
374
+ export interface ${listResponseType} {
375
+ readonly items: readonly ${itemType}[];
376
+ }
377
+
378
+ export interface ${createResponseType} {
379
+ readonly item: ${itemType};
380
+ }
381
+
382
+ export interface ${notFoundErrorExport} {
383
+ readonly _tag: '${notFoundErrorExport}';
384
+ readonly id: string;
385
+ }
386
+
387
+ export const ${markerSchemaExport}: Schema.Codec<${markerType}> = Schema.Struct({
312
388
  appId: Schema.String,
313
389
  build: Schema.String,
314
390
  deployProfile: Schema.String,
@@ -317,15 +393,15 @@ function createEffectSharedApiContract(service) {
317
393
  version: Schema.String,
318
394
  });
319
395
 
320
- export const ${schemaExport} = Schema.Struct({
396
+ export const ${schemaExport}: Schema.Codec<${itemType}> = Schema.Struct({
321
397
  id: Schema.String,
322
398
  marker: ${markerSchemaExport},
323
399
  title: Schema.String,
324
400
  });
325
401
 
326
- export const ${readinessSchemaExport} = Schema.Struct({
402
+ export const ${readinessSchemaExport}: Schema.Codec<${readinessType}> = Schema.Struct({
327
403
  checks: Schema.Struct({
328
- effectBff: Schema.Literal('ready'),
404
+ api: Schema.Literal('ready'),
329
405
  moduleFederation: Schema.Literal('ready'),
330
406
  ssr: Schema.Literal('ready'),
331
407
  translations: Schema.Literal('ready'),
@@ -335,18 +411,13 @@ export const ${readinessSchemaExport} = Schema.Struct({
335
411
  versionSkew: Schema.Literal('none'),
336
412
  });
337
413
 
338
- export const ${createPayloadSchemaExport} = Schema.Struct({
414
+ export const ${createPayloadSchemaExport}: Schema.Codec<${createPayloadType}> = Schema.Struct({
339
415
  title: Schema.String,
340
416
  });
341
417
 
342
- ${checkoutCartSharedSchemaSection}export class ${notFoundErrorExport} extends Schema.TaggedErrorClass<${notFoundErrorExport}>()(
343
- '${notFoundErrorExport}',
344
- {
345
- id: Schema.String,
346
- },
347
- ) {}
348
-
349
- export const ${notFoundSchemaExport} = ${notFoundErrorExport}.pipe(
418
+ ${checkoutCartSharedSchemaSection}export const ${notFoundSchemaExport}: Schema.Codec<${notFoundErrorExport}> = Schema.TaggedStruct('${notFoundErrorExport}', {
419
+ id: Schema.String,
420
+ }).pipe(
350
421
  HttpApiSchema.status(404),
351
422
  );
352
423
 
@@ -367,7 +438,7 @@ export interface OperationContext {
367
438
  export const ${apiExport} = HttpApi.make('${apiName}').add(
368
439
  HttpApiGroup.make('${groupName}')
369
440
  .add(
370
- HttpApiEndpoint.get('list', '/effect/${stem}', {
441
+ HttpApiEndpoint.get('list', '/${stem}', {
371
442
  query: {
372
443
  limit: Schema.optional(Schema.FiniteFromString),
373
444
  },
@@ -377,12 +448,12 @@ export const ${apiExport} = HttpApi.make('${apiName}').add(
377
448
  }),
378
449
  )
379
450
  .add(
380
- HttpApiEndpoint.get('readiness', '/effect/${stem}/readiness', {
451
+ HttpApiEndpoint.get('readiness', '/${stem}/readiness', {
381
452
  success: ${readinessSchemaExport},
382
453
  }),
383
454
  )
384
455
  .add(
385
- HttpApiEndpoint.get('get', '/effect/${stem}/:id', {
456
+ HttpApiEndpoint.get('get', '/${stem}/:id', {
386
457
  error: ${notFoundSchemaExport},
387
458
  params: {
388
459
  id: Schema.String,
@@ -391,7 +462,7 @@ export const ${apiExport} = HttpApi.make('${apiName}').add(
391
462
  }),
392
463
  )
393
464
  .add(
394
- HttpApiEndpoint.post('create', '/effect/${stem}', {
465
+ HttpApiEndpoint.post('create', '/${stem}', {
395
466
  payload: ${createPayloadSchemaExport},
396
467
  success: Schema.Struct({
397
468
  item: ${schemaExport},
@@ -404,34 +475,34 @@ export const ${groupName}OperationContexts = {
404
475
  ${checkoutCartOperationContextEntries} create: {
405
476
  method: 'POST',
406
477
  operationId: '${apiName}:${groupName}:create',
407
- routePath: '/effect/${stem}',
478
+ routePath: '/${stem}',
408
479
  source: 'generated-client',
409
480
  },
410
481
  get: {
411
482
  method: 'GET',
412
483
  operationId: '${apiName}:${groupName}:get',
413
- routePath: '/effect/${stem}/:id',
484
+ routePath: '/${stem}/:id',
414
485
  source: 'generated-client',
415
486
  },
416
487
  list: {
417
488
  method: 'GET',
418
489
  operationId: '${apiName}:${groupName}:list',
419
- routePath: '/effect/${stem}',
490
+ routePath: '/${stem}',
420
491
  source: 'generated-client',
421
492
  },
422
493
  readiness: {
423
494
  method: 'GET',
424
495
  operationId: '${apiName}:${groupName}:readiness',
425
- routePath: '/effect/${stem}/readiness',
496
+ routePath: '/${stem}/readiness',
426
497
  source: 'generated-client',
427
498
  },
428
499
  } satisfies Record<string, OperationContext>;
429
500
 
430
501
  export const ${groupName}ApiContract = {
431
502
  apiPrefix: '${apiPrefix}',
432
- basePath: '${apiPrefix}/effect/${stem}',
503
+ basePath: '${apiPrefix}/${stem}',
433
504
  ${createCheckoutCartApiContractFields(service)} ownerId: '${service.id}',
434
- readinessPath: '${apiPrefix}/effect/${stem}/readiness',
505
+ readinessPath: '${apiPrefix}/${stem}/readiness',
435
506
  } as const;
436
507
  `;
437
508
  }
@@ -450,13 +521,20 @@ function createEffectServiceEntry(service, contractImportPath) {
450
521
  HttpApiBuilder,
451
522
  Layer,
452
523
  } from '@modern-js/plugin-bff/effect-edge';
453
- import { ultramodernApiMarker } from '../../src/ultramodern-build.ts';
524
+ import type {
525
+ EffectBffDefinition,
526
+ EffectBffRuntime,
527
+ EffectRuntimeLayer,
528
+ } from '@modern-js/plugin-bff/effect-edge';
529
+ import { ultramodernApiMarker } from '../shared/ultramodern-build.ts';
454
530
  import {
455
531
  ${apiExport},
456
532
  ${groupName}OperationContexts,
533
+ } from '${contractImportPath}';
534
+ import type {
457
535
  ${notFoundErrorExport},
536
+ OperationContext,
458
537
  } from '${contractImportPath}';
459
- import type { OperationContext } from '${contractImportPath}';
460
538
 
461
539
  const ${groupName}Items = [
462
540
  {
@@ -489,7 +567,7 @@ const ${groupName}Layer = HttpApiBuilder.group(
489
567
  ? ${groupName}Items.slice(0, query.limit)
490
568
  : ${groupName}Items,
491
569
  }).pipe(
492
- Effect.withSpan('ultramodern.effect.${groupName}.list', {
570
+ Effect.withSpan('ultramodern.api.${groupName}.list', {
493
571
  attributes: operationAttributes(${groupName}OperationContexts.list),
494
572
  kind: 'server',
495
573
  }),
@@ -498,7 +576,7 @@ const ${groupName}Layer = HttpApiBuilder.group(
498
576
  .handle('readiness', () =>
499
577
  Effect.succeed({
500
578
  checks: {
501
- effectBff: 'ready' as const,
579
+ api: 'ready' as const,
502
580
  moduleFederation: 'ready' as const,
503
581
  ssr: 'ready' as const,
504
582
  translations: 'ready' as const,
@@ -507,7 +585,7 @@ const ${groupName}Layer = HttpApiBuilder.group(
507
585
  status: 'ready' as const,
508
586
  versionSkew: 'none' as const,
509
587
  }).pipe(
510
- Effect.withSpan('ultramodern.effect.${groupName}.readiness', {
588
+ Effect.withSpan('ultramodern.api.${groupName}.readiness', {
511
589
  attributes: operationAttributes(${groupName}OperationContexts.readiness),
512
590
  kind: 'server',
513
591
  }),
@@ -517,13 +595,17 @@ const ${groupName}Layer = HttpApiBuilder.group(
517
595
  const matchedItem = ${groupName}Items.find(
518
596
  candidate => candidate.id === params.id,
519
597
  );
598
+ const notFound: ${notFoundErrorExport} = {
599
+ _tag: '${notFoundErrorExport}',
600
+ id: params.id,
601
+ };
520
602
  const result =
521
603
  matchedItem === undefined
522
- ? Effect.fail(new ${notFoundErrorExport}({ id: params.id }))
604
+ ? Effect.fail(notFound)
523
605
  : Effect.succeed(matchedItem);
524
606
 
525
607
  return result.pipe(
526
- Effect.withSpan('ultramodern.effect.${groupName}.get', {
608
+ Effect.withSpan('ultramodern.api.${groupName}.get', {
527
609
  attributes: operationAttributes(${groupName}OperationContexts.get),
528
610
  kind: 'server',
529
611
  }),
@@ -540,7 +622,7 @@ const ${groupName}Layer = HttpApiBuilder.group(
540
622
  title: payload.title,
541
623
  },
542
624
  }).pipe(
543
- Effect.withSpan('ultramodern.effect.${groupName}.create', {
625
+ Effect.withSpan('ultramodern.api.${groupName}.create', {
544
626
  attributes: operationAttributes(${groupName}OperationContexts.create),
545
627
  kind: 'server',
546
628
  }),
@@ -550,12 +632,15 @@ const ${groupName}Layer = HttpApiBuilder.group(
550
632
 
551
633
  const layer = HttpApiBuilder.layer(${apiExport}).pipe(
552
634
  Layer.provide(${groupName}Layer),
553
- );
635
+ ) satisfies EffectRuntimeLayer;
554
636
 
555
- export default defineEffectBff({
637
+ const apiRuntime: EffectBffDefinition<typeof ${apiExport}, EffectRuntimeLayer> &
638
+ EffectBffRuntime<typeof ${apiExport}, EffectRuntimeLayer> = defineEffectBff({
556
639
  api: ${apiExport},
557
640
  layer,
558
641
  });
642
+
643
+ export default apiRuntime;
559
644
  `;
560
645
  }
561
646
  function createEffectClient(service, contractImportPath) {
@@ -566,25 +651,71 @@ function createEffectClient(service, contractImportPath) {
566
651
  const singular = verticalEffectErrorStem(service);
567
652
  const clientOptionsName = `${toPascalCase(stem)}ClientOptions`;
568
653
  const createClientName = `create${toPascalCase(stem)}Client`;
654
+ const clientTypeName = `${toPascalCase(stem)}Client`;
655
+ const clientEffectTypeName = `${toPascalCase(stem)}ClientEffect`;
569
656
  const listName = `list${toPascalCase(stem)}`;
570
657
  const readinessName = `get${toPascalCase(stem)}Readiness`;
571
658
  const getName = `get${toPascalCase(singular)}`;
572
659
  const createName = `create${toPascalCase(singular)}`;
660
+ const notFoundErrorExport = verticalEffectNotFoundErrorExport(service);
661
+ const pascalStem = toPascalCase(stem);
662
+ const itemType = `${pascalStem}Item`;
663
+ const readinessType = `${pascalStem}Readiness`;
664
+ const createResponseType = `${pascalStem}CreateResponse`;
665
+ const listResponseType = `${pascalStem}ListResponse`;
573
666
  const checkoutCartClientExports = createCheckoutCartClientExports(service);
574
667
  return `import {
575
668
  Effect,
576
669
  makeEffectHttpApiClient,
577
670
  runEffectRequest,
578
671
  } from '@modern-js/plugin-bff/effect-client';
672
+ import type {
673
+ HttpClientError,
674
+ HttpApi,
675
+ HttpApiClient,
676
+ HttpApiGroup,
677
+ Schema,
678
+ } from '@modern-js/plugin-bff/effect-client';
579
679
  import {
580
680
  ${contractExport}ApiContract,
581
681
  ${apiExport},
582
682
  ${groupName}OperationContexts,
583
683
  } from '${contractImportPath}';
584
- import type { OperationContext } from '${contractImportPath}';
684
+ import type {
685
+ ${createResponseType},
686
+ ${itemType},
687
+ ${listResponseType},
688
+ ${notFoundErrorExport},
689
+ OperationContext,
690
+ ${readinessType},
691
+ } from '${contractImportPath}';
585
692
 
586
693
  export { Effect, runEffectRequest };
587
694
 
695
+ type ${pascalStem}ApiGroups = typeof ${apiExport} extends HttpApi.HttpApi<
696
+ infer _ApiId,
697
+ infer Groups
698
+ >
699
+ ? Groups
700
+ : never;
701
+
702
+ export type ${clientTypeName} = HttpApiClient.Client<
703
+ Extract<${pascalStem}ApiGroups, HttpApiGroup.Any>,
704
+ never,
705
+ never
706
+ >;
707
+
708
+ export type ${pascalStem}ClientError =
709
+ | ${notFoundErrorExport}
710
+ | HttpClientError.HttpClientError
711
+ | Schema.SchemaError;
712
+
713
+ export type ${clientEffectTypeName}<Success> = Effect.Effect<
714
+ Success,
715
+ ${pascalStem}ClientError,
716
+ never
717
+ >;
718
+
588
719
  export interface ${clientOptionsName} {
589
720
  baseUrl?: string | URL;
590
721
  locale?: string;
@@ -594,7 +725,7 @@ export interface ${clientOptionsName} {
594
725
 
595
726
  export const ${createClientName} = (
596
727
  options: ${clientOptionsName} = {},
597
- ) =>
728
+ ): ${clientEffectTypeName}<${clientTypeName}> =>
598
729
  makeEffectHttpApiClient(${apiExport}, {
599
730
  baseUrl: options.baseUrl ?? ${contractExport}ApiContract.apiPrefix,
600
731
  requestContext: {
@@ -610,7 +741,7 @@ export const ${createClientName} = (
610
741
 
611
742
  export const ${listName} = (
612
743
  options: ${clientOptionsName} & { limit?: number } = {},
613
- ) =>
744
+ ): ${clientEffectTypeName}<${listResponseType}> =>
614
745
  ${createClientName}({
615
746
  ...options,
616
747
  operationContext:
@@ -623,7 +754,7 @@ export const ${listName} = (
623
754
 
624
755
  export const ${readinessName} = (
625
756
  options: ${clientOptionsName} = {},
626
- ) =>
757
+ ): ${clientEffectTypeName}<${readinessType}> =>
627
758
  ${createClientName}({
628
759
  ...options,
629
760
  operationContext:
@@ -635,7 +766,7 @@ export const ${readinessName} = (
635
766
  export const ${getName} = (
636
767
  id: string,
637
768
  options: ${clientOptionsName} = {},
638
- ) =>
769
+ ): ${clientEffectTypeName}<${itemType}> =>
639
770
  ${createClientName}({
640
771
  ...options,
641
772
  operationContext:
@@ -647,7 +778,7 @@ export const ${getName} = (
647
778
  export const ${createName} = (
648
779
  title: string,
649
780
  options: ${clientOptionsName} = {},
650
- ) =>
781
+ ): ${clientEffectTypeName}<${createResponseType}> =>
651
782
  ${createClientName}({
652
783
  ...options,
653
784
  operationContext:
@@ -679,7 +810,7 @@ ${checkoutCartExports} create${pascalSingular},
679
810
  get${pascalStem}Readiness,
680
811
  list${pascalStem},
681
812
  type ${pascalStem}ClientOptions,
682
- } from '${packageName(scope, remote.packageSuffix)}/effect/client';`;
813
+ } from '${packageName(scope, remote.packageSuffix)}/api/client';`;
683
814
  }).join('\n\n');
684
815
  return exports ? `${exports}\n` : `export const ultramodernVerticalClients = [] as const;
685
816
  `;
@@ -687,7 +818,7 @@ ${checkoutCartExports} create${pascalSingular},
687
818
  function createEffectReadinessContract(app) {
688
819
  const stem = effectApiStem(app);
689
820
  return {
690
- endpoint: `/effect/${stem}/readiness`,
821
+ endpoint: `/${stem}/readiness`,
691
822
  marker: {
692
823
  ui: 'ultramodernUiMarker',
693
824
  api: 'ultramodernApiMarker',
@@ -697,7 +828,7 @@ function createEffectReadinessContract(app) {
697
828
  'moduleFederation',
698
829
  'ssr',
699
830
  'translations',
700
- 'effectBff'
831
+ 'api'
701
832
  ]
702
833
  };
703
834
  }
@@ -712,39 +843,39 @@ function createEffectRequestContextContract() {
712
843
  'x-ultramodern-env',
713
844
  'x-vertical-version-id'
714
845
  ],
715
- source: 'shell-to-vertical-effect-client'
846
+ source: 'shell-to-vertical-api-client'
716
847
  };
717
848
  }
718
849
  function createEffectDomainOperations(app) {
719
850
  const stem = effectApiStem(app);
720
851
  const group = verticalEffectGroupName(app);
721
- const basePath = `/effect/${stem}`;
852
+ const basePath = `/${stem}`;
722
853
  const checkoutCartOperations = serviceHasCheckoutCartState(app) ? {
723
854
  checkoutCartAddItem: {
724
855
  client: 'addCheckoutCartItem',
725
856
  method: 'POST',
726
- path: '/effect/checkout/cart/items',
857
+ path: '/checkout/cart/items',
727
858
  resource: 'checkout-cart',
728
859
  owner: app.id
729
860
  },
730
861
  checkoutCartClear: {
731
862
  client: 'clearCheckoutCart',
732
863
  method: 'POST',
733
- path: '/effect/checkout/cart/clear',
864
+ path: '/checkout/cart/clear',
734
865
  resource: 'checkout-cart',
735
866
  owner: app.id
736
867
  },
737
868
  checkoutCartRead: {
738
869
  client: 'getCheckoutCart',
739
870
  method: 'GET',
740
- path: '/effect/checkout/cart',
871
+ path: '/checkout/cart',
741
872
  resource: 'checkout-cart',
742
873
  owner: app.id
743
874
  },
744
875
  checkoutCartRemoveItem: {
745
876
  client: 'removeCheckoutCartItem',
746
877
  method: 'POST',
747
- path: '/effect/checkout/cart/remove',
878
+ path: '/checkout/cart/remove',
748
879
  resource: 'checkout-cart',
749
880
  owner: app.id
750
881
  }
@@ -777,27 +908,26 @@ function createEffectDomainOperations(app) {
777
908
  function effectApiTopologyMetadata(app) {
778
909
  if (!appHasEffectApi(app)) return;
779
910
  return {
780
- effect: {
781
- runtime: 'effect',
782
- bff: {
783
- prefix: app.effectApi.prefix,
784
- openapi: '/openapi.json'
785
- },
786
- contract: {
787
- export: './shared/effect/api',
788
- path: `${app.directory}/shared/effect/api.ts`
789
- },
790
- client: {
791
- export: './effect/client',
792
- path: `${app.directory}/src/effect/${app.effectApi.stem}-client.ts`
793
- },
794
- serverEntry: `${app.directory}/api/effect/index.ts`,
795
- basePath: `${app.effectApi.prefix}/effect/${app.effectApi.stem}`,
796
- consumedBy: app.effectApi.consumedBy,
797
- readiness: createEffectReadinessContract(app),
798
- requestContext: createEffectRequestContextContract(),
799
- domainOperations: createEffectDomainOperations(app)
800
- }
911
+ runtime: 'effect',
912
+ bff: {
913
+ prefix: app.effectApi.prefix,
914
+ openapi: '/openapi.json',
915
+ strictEffectApproach: true
916
+ },
917
+ contract: {
918
+ export: './api',
919
+ path: `${app.directory}/shared/api.ts`
920
+ },
921
+ client: {
922
+ export: './api/client',
923
+ path: `${app.directory}/src/api/${app.effectApi.stem}-client.ts`
924
+ },
925
+ serverEntry: `${app.directory}/api/index.ts`,
926
+ basePath: `${app.effectApi.prefix}/${app.effectApi.stem}`,
927
+ consumedBy: app.effectApi.consumedBy,
928
+ readiness: createEffectReadinessContract(app),
929
+ requestContext: createEffectRequestContextContract(),
930
+ domainOperations: createEffectDomainOperations(app)
801
931
  };
802
932
  }
803
933
  function createEffectOperationContract(target) {
@@ -805,22 +935,22 @@ function createEffectOperationContract(target) {
805
935
  const checkoutCartOperations = serviceHasCheckoutCartState(target) ? {
806
936
  addCartItem: {
807
937
  method: 'POST',
808
- path: '/effect/checkout/cart/items',
938
+ path: '/checkout/cart/items',
809
939
  source: 'generated-client'
810
940
  },
811
941
  clearCart: {
812
942
  method: 'POST',
813
- path: '/effect/checkout/cart/clear',
943
+ path: '/checkout/cart/clear',
814
944
  source: 'generated-client'
815
945
  },
816
946
  getCart: {
817
947
  method: 'GET',
818
- path: '/effect/checkout/cart',
948
+ path: '/checkout/cart',
819
949
  source: 'generated-client'
820
950
  },
821
951
  removeCartItem: {
822
952
  method: 'POST',
823
- path: '/effect/checkout/cart/remove',
953
+ path: '/checkout/cart/remove',
824
954
  source: 'generated-client'
825
955
  }
826
956
  } : {};
@@ -831,22 +961,22 @@ function createEffectOperationContract(target) {
831
961
  ...checkoutCartOperations,
832
962
  list: {
833
963
  method: 'GET',
834
- path: `/effect/${stem}`,
964
+ path: `/${stem}`,
835
965
  source: 'generated-client'
836
966
  },
837
967
  readiness: {
838
968
  method: 'GET',
839
- path: `/effect/${stem}/readiness`,
969
+ path: `/${stem}/readiness`,
840
970
  source: 'generated-client'
841
971
  },
842
972
  get: {
843
973
  method: 'GET',
844
- path: `/effect/${stem}/:id`,
974
+ path: `/${stem}/:id`,
845
975
  source: 'generated-client'
846
976
  },
847
977
  create: {
848
978
  method: 'POST',
849
- path: `/effect/${stem}`,
979
+ path: `/${stem}`,
850
980
  source: 'generated-client'
851
981
  }
852
982
  }