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