@bleedingdev/modern-js-create 3.2.0-ultramodern.120 → 3.2.0-ultramodern.122

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 (118) hide show
  1. package/README.md +35 -12
  2. package/dist/cjs/create-package-root.cjs +7 -9
  3. package/dist/cjs/index.cjs +74 -44
  4. package/dist/cjs/locale/en.cjs +6 -7
  5. package/dist/cjs/locale/zh.cjs +6 -7
  6. package/dist/cjs/ultramodern-workspace/add-vertical.cjs +337 -0
  7. package/dist/cjs/ultramodern-workspace/app-files.cjs +223 -0
  8. package/dist/cjs/ultramodern-workspace/contracts.cjs +836 -0
  9. package/dist/cjs/ultramodern-workspace/demo-components.cjs +422 -0
  10. package/dist/cjs/ultramodern-workspace/descriptors.cjs +222 -0
  11. package/dist/cjs/ultramodern-workspace/effect-api.cjs +952 -0
  12. package/dist/cjs/ultramodern-workspace/fs-io.cjs +191 -0
  13. package/dist/cjs/ultramodern-workspace/index.cjs +48 -0
  14. package/dist/cjs/ultramodern-workspace/locales.cjs +173 -0
  15. package/dist/cjs/ultramodern-workspace/module-federation.cjs +487 -0
  16. package/dist/cjs/ultramodern-workspace/naming.cjs +161 -0
  17. package/dist/cjs/ultramodern-workspace/package-json.cjs +406 -0
  18. package/dist/cjs/ultramodern-workspace/package-source.cjs +59 -0
  19. package/dist/cjs/ultramodern-workspace/policy.cjs +248 -0
  20. package/dist/cjs/ultramodern-workspace/public-surface.cjs +268 -0
  21. package/dist/cjs/ultramodern-workspace/routes.cjs +375 -0
  22. package/dist/cjs/ultramodern-workspace/types.cjs +61 -0
  23. package/dist/cjs/ultramodern-workspace/versions.cjs +153 -0
  24. package/dist/cjs/ultramodern-workspace/workspace-scripts.cjs +153 -0
  25. package/dist/cjs/ultramodern-workspace/write-workspace.cjs +175 -0
  26. package/dist/esm/create-package-root.js +7 -9
  27. package/dist/esm/index.js +72 -42
  28. package/dist/esm/locale/en.js +6 -7
  29. package/dist/esm/locale/zh.js +6 -7
  30. package/dist/esm/ultramodern-workspace/add-vertical.js +252 -0
  31. package/dist/esm/ultramodern-workspace/app-files.js +149 -0
  32. package/dist/esm/ultramodern-workspace/contracts.js +741 -0
  33. package/dist/esm/ultramodern-workspace/demo-components.js +363 -0
  34. package/dist/esm/ultramodern-workspace/descriptors.js +133 -0
  35. package/dist/esm/ultramodern-workspace/effect-api.js +854 -0
  36. package/dist/esm/ultramodern-workspace/fs-io.js +90 -0
  37. package/dist/esm/ultramodern-workspace/index.js +3 -0
  38. package/dist/esm/ultramodern-workspace/locales.js +122 -0
  39. package/dist/esm/ultramodern-workspace/module-federation.js +415 -0
  40. package/dist/esm/ultramodern-workspace/naming.js +71 -0
  41. package/dist/esm/ultramodern-workspace/package-json.js +338 -0
  42. package/dist/esm/ultramodern-workspace/package-source.js +21 -0
  43. package/dist/esm/ultramodern-workspace/policy.js +183 -0
  44. package/dist/esm/ultramodern-workspace/public-surface.js +183 -0
  45. package/dist/esm/ultramodern-workspace/routes.js +280 -0
  46. package/dist/esm/ultramodern-workspace/types.js +16 -0
  47. package/dist/esm/ultramodern-workspace/versions.js +34 -0
  48. package/dist/esm/ultramodern-workspace/workspace-scripts.js +91 -0
  49. package/dist/esm/ultramodern-workspace/write-workspace.js +121 -0
  50. package/dist/esm-node/create-package-root.js +7 -9
  51. package/dist/esm-node/index.js +72 -42
  52. package/dist/esm-node/locale/en.js +6 -7
  53. package/dist/esm-node/locale/zh.js +6 -7
  54. package/dist/esm-node/ultramodern-workspace/add-vertical.js +253 -0
  55. package/dist/esm-node/ultramodern-workspace/app-files.js +150 -0
  56. package/dist/esm-node/ultramodern-workspace/contracts.js +742 -0
  57. package/dist/esm-node/ultramodern-workspace/demo-components.js +364 -0
  58. package/dist/esm-node/ultramodern-workspace/descriptors.js +134 -0
  59. package/dist/esm-node/ultramodern-workspace/effect-api.js +855 -0
  60. package/dist/esm-node/ultramodern-workspace/fs-io.js +91 -0
  61. package/dist/esm-node/ultramodern-workspace/index.js +4 -0
  62. package/dist/esm-node/ultramodern-workspace/locales.js +123 -0
  63. package/dist/esm-node/ultramodern-workspace/module-federation.js +416 -0
  64. package/dist/esm-node/ultramodern-workspace/naming.js +72 -0
  65. package/dist/esm-node/ultramodern-workspace/package-json.js +339 -0
  66. package/dist/esm-node/ultramodern-workspace/package-source.js +22 -0
  67. package/dist/esm-node/ultramodern-workspace/policy.js +184 -0
  68. package/dist/esm-node/ultramodern-workspace/public-surface.js +184 -0
  69. package/dist/esm-node/ultramodern-workspace/routes.js +281 -0
  70. package/dist/esm-node/ultramodern-workspace/types.js +17 -0
  71. package/dist/esm-node/ultramodern-workspace/versions.js +35 -0
  72. package/dist/esm-node/ultramodern-workspace/workspace-scripts.js +92 -0
  73. package/dist/esm-node/ultramodern-workspace/write-workspace.js +122 -0
  74. package/dist/types/locale/en.d.ts +4 -5
  75. package/dist/types/locale/index.d.ts +8 -10
  76. package/dist/types/locale/zh.d.ts +4 -5
  77. package/dist/types/ultramodern-workspace/add-vertical.d.ts +19 -0
  78. package/dist/types/ultramodern-workspace/app-files.d.ts +14 -0
  79. package/dist/types/ultramodern-workspace/contracts.d.ts +21 -0
  80. package/dist/types/ultramodern-workspace/demo-components.d.ts +9 -0
  81. package/dist/types/ultramodern-workspace/descriptors.d.ts +39 -0
  82. package/dist/types/ultramodern-workspace/effect-api.d.ts +73 -0
  83. package/dist/types/ultramodern-workspace/fs-io.d.ts +18 -0
  84. package/dist/types/ultramodern-workspace/index.d.ts +4 -0
  85. package/dist/types/ultramodern-workspace/locales.d.ts +183 -0
  86. package/dist/types/ultramodern-workspace/module-federation.d.ts +16 -0
  87. package/dist/types/ultramodern-workspace/naming.d.ts +16 -0
  88. package/dist/types/ultramodern-workspace/package-json.d.ts +12 -0
  89. package/dist/types/ultramodern-workspace/package-source.d.ts +2 -0
  90. package/dist/types/ultramodern-workspace/policy.d.ts +60 -0
  91. package/dist/types/ultramodern-workspace/public-surface.d.ts +37 -0
  92. package/dist/types/ultramodern-workspace/routes.d.ts +25 -0
  93. package/dist/types/ultramodern-workspace/types.d.ts +95 -0
  94. package/dist/types/ultramodern-workspace/versions.d.ts +38 -0
  95. package/dist/types/ultramodern-workspace/workspace-scripts.d.ts +10 -0
  96. package/dist/types/ultramodern-workspace/write-workspace.d.ts +4 -0
  97. package/package.json +4 -3
  98. package/template-workspace/.github/workflows/ultramodern-workspace-gates.yml.handlebars +1 -4
  99. package/template-workspace/.mise.toml.handlebars +1 -0
  100. package/template-workspace/{AGENTS.md → AGENTS.md.handlebars} +12 -7
  101. package/template-workspace/README.md.handlebars +40 -24
  102. package/template-workspace/{pnpm-workspace.yaml → pnpm-workspace.yaml.handlebars} +2 -2
  103. package/template-workspace/scripts/bootstrap-agent-skills.mjs +31 -51
  104. package/templates/app/shell-frame.tsx +49 -0
  105. package/templates/app/ultramodern-route-head.tsx.handlebars +142 -0
  106. package/templates/packages/shared-contracts-index.ts +466 -0
  107. package/templates/workspace-scripts/assert-mf-types.mjs.handlebars +69 -0
  108. package/templates/workspace-scripts/check-ultramodern-i18n-boundaries.mjs +9 -0
  109. package/templates/workspace-scripts/generate-public-surface-assets.mjs +529 -0
  110. package/templates/workspace-scripts/proof-cloudflare-version.mjs +125 -0
  111. package/templates/workspace-scripts/ultramodern-cloudflare-proof.mjs +851 -0
  112. package/templates/workspace-scripts/ultramodern-performance-readiness.config.mjs +7 -0
  113. package/templates/workspace-scripts/ultramodern-performance-readiness.mjs +223 -0
  114. package/templates/workspace-scripts/validate-ultramodern-workspace.mjs.handlebars +656 -0
  115. package/dist/cjs/ultramodern-workspace.cjs +0 -6797
  116. package/dist/esm/ultramodern-workspace.js +0 -6738
  117. package/dist/esm-node/ultramodern-workspace.js +0 -6739
  118. package/dist/types/ultramodern-workspace.d.ts +0 -29
@@ -0,0 +1,855 @@
1
+ import "node:module";
2
+ import { appHasEffectApi, effectApiPrefix, effectApiStem, verticalEffectApps } from "./descriptors.js";
3
+ import { packageName, toCamelCase, toPascalCase } from "./naming.js";
4
+ function verticalEffectApiExport(service) {
5
+ return `${toCamelCase(effectApiStem(service))}EffectApi`;
6
+ }
7
+ function verticalEffectGroupName(service) {
8
+ return toCamelCase(effectApiStem(service));
9
+ }
10
+ function verticalEffectApiName(service) {
11
+ return `${toPascalCase(effectApiStem(service))}EffectApi`;
12
+ }
13
+ function verticalEffectSchemaExport(service) {
14
+ return `${toCamelCase(effectApiStem(service))}ItemSchema`;
15
+ }
16
+ function verticalEffectMarkerSchemaExport(service) {
17
+ return `${toCamelCase(effectApiStem(service))}MarkerSchema`;
18
+ }
19
+ function verticalEffectReadinessSchemaExport(service) {
20
+ return `${toCamelCase(effectApiStem(service))}ReadinessSchema`;
21
+ }
22
+ function verticalEffectErrorStem(service) {
23
+ return effectApiStem(service);
24
+ }
25
+ function verticalEffectCreatePayloadSchemaExport(service) {
26
+ return `${toCamelCase(effectApiStem(service))}CreatePayloadSchema`;
27
+ }
28
+ function verticalEffectNotFoundErrorExport(service) {
29
+ return `${toPascalCase(verticalEffectErrorStem(service))}NotFound`;
30
+ }
31
+ function verticalEffectNotFoundSchemaExport(service) {
32
+ return `${toCamelCase(verticalEffectErrorStem(service))}NotFoundSchema`;
33
+ }
34
+ function serviceHasCheckoutCartState(service) {
35
+ return 'checkout' === effectApiStem(service);
36
+ }
37
+ function createCheckoutCartSharedSchemas(service) {
38
+ if (!serviceHasCheckoutCartState(service)) return '';
39
+ return `
40
+ export const checkoutCartLineSchema = Schema.Struct({
41
+ sku: Schema.String,
42
+ name: Schema.String,
43
+ quantity: Schema.Finite,
44
+ unitPriceCents: Schema.Finite,
45
+ });
46
+
47
+ export const checkoutCartSchema = Schema.Struct({
48
+ lines: Schema.Array(checkoutCartLineSchema),
49
+ subtotalCents: Schema.Finite,
50
+ totalQuantity: Schema.Finite,
51
+ });
52
+
53
+ export const checkoutAddCartItemPayloadSchema = Schema.Struct({
54
+ sku: Schema.String,
55
+ name: Schema.optional(Schema.String),
56
+ quantity: Schema.Finite,
57
+ unitPriceCents: Schema.optional(Schema.Finite),
58
+ });
59
+
60
+ export const checkoutRemoveCartItemPayloadSchema = Schema.Struct({
61
+ sku: Schema.String,
62
+ });
63
+ `;
64
+ }
65
+ function createCheckoutCartEndpointDefinitions(service) {
66
+ if (!serviceHasCheckoutCartState(service)) return '';
67
+ return `
68
+ .add(
69
+ HttpApiEndpoint.get('getCart', '/effect/checkout/cart', {
70
+ success: checkoutCartSchema,
71
+ }),
72
+ )
73
+ .add(
74
+ HttpApiEndpoint.post('addCartItem', '/effect/checkout/cart/items', {
75
+ payload: checkoutAddCartItemPayloadSchema,
76
+ success: checkoutCartSchema,
77
+ }),
78
+ )
79
+ .add(
80
+ HttpApiEndpoint.post('removeCartItem', '/effect/checkout/cart/remove', {
81
+ payload: checkoutRemoveCartItemPayloadSchema,
82
+ success: checkoutCartSchema,
83
+ }),
84
+ )
85
+ .add(
86
+ HttpApiEndpoint.post('clearCart', '/effect/checkout/cart/clear', {
87
+ success: checkoutCartSchema,
88
+ }),
89
+ )`;
90
+ }
91
+ function createCheckoutCartOperationContexts(service) {
92
+ const apiName = verticalEffectApiName(service);
93
+ const groupName = verticalEffectGroupName(service);
94
+ if (!serviceHasCheckoutCartState(service)) return '';
95
+ return `
96
+ addCartItem: {
97
+ method: 'POST',
98
+ operationId: '${apiName}:${groupName}:addCartItem',
99
+ routePath: '/effect/checkout/cart/items',
100
+ source: 'generated-client',
101
+ },
102
+ clearCart: {
103
+ method: 'POST',
104
+ operationId: '${apiName}:${groupName}:clearCart',
105
+ routePath: '/effect/checkout/cart/clear',
106
+ source: 'generated-client',
107
+ },
108
+ getCart: {
109
+ method: 'GET',
110
+ operationId: '${apiName}:${groupName}:getCart',
111
+ routePath: '/effect/checkout/cart',
112
+ source: 'generated-client',
113
+ },
114
+ removeCartItem: {
115
+ method: 'POST',
116
+ operationId: '${apiName}:${groupName}:removeCartItem',
117
+ routePath: '/effect/checkout/cart/remove',
118
+ source: 'generated-client',
119
+ },`;
120
+ }
121
+ function createCheckoutCartApiContractFields(service) {
122
+ if (!serviceHasCheckoutCartState(service)) return '';
123
+ return ` checkoutCartPath: '${effectApiPrefix(service)}/effect/checkout/cart',
124
+ `;
125
+ }
126
+ function createCheckoutCartServerState(service) {
127
+ if (!serviceHasCheckoutCartState(service)) return '';
128
+ return `
129
+ type CheckoutCartLine = {
130
+ sku: string;
131
+ name: string;
132
+ quantity: number;
133
+ unitPriceCents: number;
134
+ };
135
+
136
+ const checkoutCartLines = new Map<string, CheckoutCartLine>();
137
+
138
+ const createCheckoutCartSnapshot = () => {
139
+ const lines = [...checkoutCartLines.values()].sort((left, right) =>
140
+ left.sku.localeCompare(right.sku),
141
+ );
142
+ return {
143
+ lines,
144
+ subtotalCents: lines.reduce(
145
+ (total, line) => total + line.quantity * line.unitPriceCents,
146
+ 0,
147
+ ),
148
+ totalQuantity: lines.reduce((total, line) => total + line.quantity, 0),
149
+ };
150
+ };
151
+ `;
152
+ }
153
+ function createCheckoutCartServerHandlers(service) {
154
+ const groupName = verticalEffectGroupName(service);
155
+ if (!serviceHasCheckoutCartState(service)) return '';
156
+ return `
157
+ .handle('getCart', () =>
158
+ Effect.sync(() => createCheckoutCartSnapshot()).pipe(
159
+ Effect.withSpan('ultramodern.effect.${groupName}.checkout.getCart', {
160
+ attributes: operationAttributes(${groupName}OperationContexts.getCart),
161
+ kind: 'server',
162
+ }),
163
+ ),
164
+ )
165
+ .handle('addCartItem', ({ payload }) =>
166
+ Effect.sync(() => {
167
+ const existingLine = checkoutCartLines.get(payload.sku);
168
+ checkoutCartLines.set(payload.sku, {
169
+ sku: payload.sku,
170
+ name: payload.name ?? existingLine?.name ?? payload.sku,
171
+ quantity: (existingLine?.quantity ?? 0) + payload.quantity,
172
+ unitPriceCents:
173
+ payload.unitPriceCents ?? existingLine?.unitPriceCents ?? 0,
174
+ });
175
+ return createCheckoutCartSnapshot();
176
+ }).pipe(
177
+ Effect.withSpan('ultramodern.effect.${groupName}.checkout.addCartItem', {
178
+ attributes: operationAttributes(${groupName}OperationContexts.addCartItem),
179
+ kind: 'server',
180
+ }),
181
+ ),
182
+ )
183
+ .handle('removeCartItem', ({ payload }) =>
184
+ Effect.sync(() => {
185
+ checkoutCartLines.delete(payload.sku);
186
+ return createCheckoutCartSnapshot();
187
+ }).pipe(
188
+ Effect.withSpan('ultramodern.effect.${groupName}.checkout.removeCartItem', {
189
+ attributes: operationAttributes(${groupName}OperationContexts.removeCartItem),
190
+ kind: 'server',
191
+ }),
192
+ ),
193
+ )
194
+ .handle('clearCart', () =>
195
+ Effect.sync(() => {
196
+ checkoutCartLines.clear();
197
+ return createCheckoutCartSnapshot();
198
+ }).pipe(
199
+ Effect.withSpan('ultramodern.effect.${groupName}.checkout.clearCart', {
200
+ attributes: operationAttributes(${groupName}OperationContexts.clearCart),
201
+ kind: 'server',
202
+ }),
203
+ ),
204
+ )`;
205
+ }
206
+ function createCheckoutCartClientExports(service) {
207
+ if (!serviceHasCheckoutCartState(service)) return '';
208
+ const stem = effectApiStem(service);
209
+ const groupName = verticalEffectGroupName(service);
210
+ const pascalStem = toPascalCase(stem);
211
+ const clientOptionsName = `${pascalStem}ClientOptions`;
212
+ const createClientName = `create${pascalStem}Client`;
213
+ return `
214
+ export interface CheckoutCartLine {
215
+ sku: string;
216
+ name: string;
217
+ quantity: number;
218
+ unitPriceCents: number;
219
+ }
220
+
221
+ export interface CheckoutCart {
222
+ lines: readonly CheckoutCartLine[];
223
+ subtotalCents: number;
224
+ totalQuantity: number;
225
+ }
226
+
227
+ export interface CheckoutAddCartItemInput {
228
+ sku: string;
229
+ name?: string;
230
+ quantity: number;
231
+ unitPriceCents?: number;
232
+ }
233
+
234
+ export const getCheckoutCart = (
235
+ options: ${clientOptionsName} = {},
236
+ ) =>
237
+ ${createClientName}({
238
+ ...options,
239
+ operationContext:
240
+ options.operationContext ?? ${groupName}OperationContexts.getCart,
241
+ }).pipe(
242
+ Effect.flatMap(client => client.${groupName}.getCart({})),
243
+ );
244
+
245
+ export const addCheckoutCartItem = (
246
+ payload: CheckoutAddCartItemInput,
247
+ options: ${clientOptionsName} = {},
248
+ ) =>
249
+ ${createClientName}({
250
+ ...options,
251
+ operationContext:
252
+ options.operationContext ?? ${groupName}OperationContexts.addCartItem,
253
+ }).pipe(
254
+ Effect.flatMap(client =>
255
+ client.${groupName}.addCartItem({ payload }),
256
+ ),
257
+ );
258
+
259
+ export const removeCheckoutCartItem = (
260
+ sku: string,
261
+ options: ${clientOptionsName} = {},
262
+ ) =>
263
+ ${createClientName}({
264
+ ...options,
265
+ operationContext:
266
+ options.operationContext ?? ${groupName}OperationContexts.removeCartItem,
267
+ }).pipe(
268
+ Effect.flatMap(client =>
269
+ client.${groupName}.removeCartItem({ payload: { sku } }),
270
+ ),
271
+ );
272
+
273
+ export const clearCheckoutCart = (
274
+ options: ${clientOptionsName} = {},
275
+ ) =>
276
+ ${createClientName}({
277
+ ...options,
278
+ operationContext:
279
+ options.operationContext ?? ${groupName}OperationContexts.clearCart,
280
+ }).pipe(
281
+ Effect.flatMap(client => client.${groupName}.clearCart({})),
282
+ );
283
+ `;
284
+ }
285
+ function createEffectSharedApiImports() {
286
+ return `import {
287
+ HttpApi,
288
+ HttpApiEndpoint,
289
+ HttpApiGroup,
290
+ HttpApiSchema,
291
+ Schema,
292
+ } from '@modern-js/plugin-bff/effect-client';
293
+ `;
294
+ }
295
+ function createEffectSharedApiContract(service) {
296
+ const schemaExport = verticalEffectSchemaExport(service);
297
+ const markerSchemaExport = verticalEffectMarkerSchemaExport(service);
298
+ const readinessSchemaExport = verticalEffectReadinessSchemaExport(service);
299
+ const createPayloadSchemaExport = verticalEffectCreatePayloadSchemaExport(service);
300
+ const notFoundErrorExport = verticalEffectNotFoundErrorExport(service);
301
+ const notFoundSchemaExport = verticalEffectNotFoundSchemaExport(service);
302
+ const apiExport = verticalEffectApiExport(service);
303
+ const apiName = verticalEffectApiName(service);
304
+ const groupName = verticalEffectGroupName(service);
305
+ const stem = effectApiStem(service);
306
+ const apiPrefix = effectApiPrefix(service);
307
+ const checkoutCartSharedSchemas = createCheckoutCartSharedSchemas(service);
308
+ const checkoutCartSharedSchemaSection = '' === checkoutCartSharedSchemas ? '' : `${checkoutCartSharedSchemas}\n`;
309
+ const checkoutCartOperationContexts = createCheckoutCartOperationContexts(service).trimStart();
310
+ const checkoutCartOperationContextEntries = '' === checkoutCartOperationContexts ? '' : `${checkoutCartOperationContexts}\n`;
311
+ return `export const ${markerSchemaExport} = Schema.Struct({
312
+ appId: Schema.String,
313
+ build: Schema.String,
314
+ deployProfile: Schema.String,
315
+ packageName: Schema.String,
316
+ surface: Schema.String,
317
+ version: Schema.String,
318
+ });
319
+
320
+ export const ${schemaExport} = Schema.Struct({
321
+ id: Schema.String,
322
+ marker: ${markerSchemaExport},
323
+ title: Schema.String,
324
+ });
325
+
326
+ export const ${readinessSchemaExport} = Schema.Struct({
327
+ checks: Schema.Struct({
328
+ effectBff: Schema.Literal('ready'),
329
+ moduleFederation: Schema.Literal('ready'),
330
+ ssr: Schema.Literal('ready'),
331
+ translations: Schema.Literal('ready'),
332
+ }),
333
+ marker: ${markerSchemaExport},
334
+ status: Schema.Literal('ready'),
335
+ versionSkew: Schema.Literal('none'),
336
+ });
337
+
338
+ export const ${createPayloadSchemaExport} = Schema.Struct({
339
+ title: Schema.String,
340
+ });
341
+
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(
350
+ HttpApiSchema.status(404),
351
+ );
352
+
353
+ export interface OperationContext {
354
+ method: string;
355
+ operationId: string;
356
+ routePath: string;
357
+ source:
358
+ | 'client'
359
+ | 'server'
360
+ | 'generated-client'
361
+ | 'effect-adapter'
362
+ | 'data-platform'
363
+ | 'unknown';
364
+ traceId?: string;
365
+ }
366
+
367
+ export const ${apiExport} = HttpApi.make('${apiName}').add(
368
+ HttpApiGroup.make('${groupName}')
369
+ .add(
370
+ HttpApiEndpoint.get('list', '/effect/${stem}', {
371
+ query: {
372
+ limit: Schema.optional(Schema.FiniteFromString),
373
+ },
374
+ success: Schema.Struct({
375
+ items: Schema.Array(${schemaExport}),
376
+ }),
377
+ }),
378
+ )
379
+ .add(
380
+ HttpApiEndpoint.get('readiness', '/effect/${stem}/readiness', {
381
+ success: ${readinessSchemaExport},
382
+ }),
383
+ )
384
+ .add(
385
+ HttpApiEndpoint.get('get', '/effect/${stem}/:id', {
386
+ error: ${notFoundSchemaExport},
387
+ params: {
388
+ id: Schema.String,
389
+ },
390
+ success: ${schemaExport},
391
+ }),
392
+ )
393
+ .add(
394
+ HttpApiEndpoint.post('create', '/effect/${stem}', {
395
+ payload: ${createPayloadSchemaExport},
396
+ success: Schema.Struct({
397
+ item: ${schemaExport},
398
+ }),
399
+ }),
400
+ )${createCheckoutCartEndpointDefinitions(service)},
401
+ );
402
+
403
+ export const ${groupName}OperationContexts = {
404
+ ${checkoutCartOperationContextEntries} create: {
405
+ method: 'POST',
406
+ operationId: '${apiName}:${groupName}:create',
407
+ routePath: '/effect/${stem}',
408
+ source: 'generated-client',
409
+ },
410
+ get: {
411
+ method: 'GET',
412
+ operationId: '${apiName}:${groupName}:get',
413
+ routePath: '/effect/${stem}/:id',
414
+ source: 'generated-client',
415
+ },
416
+ list: {
417
+ method: 'GET',
418
+ operationId: '${apiName}:${groupName}:list',
419
+ routePath: '/effect/${stem}',
420
+ source: 'generated-client',
421
+ },
422
+ readiness: {
423
+ method: 'GET',
424
+ operationId: '${apiName}:${groupName}:readiness',
425
+ routePath: '/effect/${stem}/readiness',
426
+ source: 'generated-client',
427
+ },
428
+ } satisfies Record<string, OperationContext>;
429
+
430
+ export const ${groupName}ApiContract = {
431
+ apiPrefix: '${apiPrefix}',
432
+ basePath: '${apiPrefix}/effect/${stem}',
433
+ ${createCheckoutCartApiContractFields(service)} ownerId: '${service.id}',
434
+ readinessPath: '${apiPrefix}/effect/${stem}/readiness',
435
+ } as const;
436
+ `;
437
+ }
438
+ function createEffectSharedApi(service) {
439
+ return `${createEffectSharedApiImports()}
440
+ ${createEffectSharedApiContract(service)}`;
441
+ }
442
+ function createEffectServiceEntry(service, contractImportPath) {
443
+ const apiExport = verticalEffectApiExport(service);
444
+ const groupName = verticalEffectGroupName(service);
445
+ const notFoundErrorExport = verticalEffectNotFoundErrorExport(service);
446
+ const stem = effectApiStem(service);
447
+ return `import {
448
+ defineEffectBff,
449
+ Effect,
450
+ HttpApiBuilder,
451
+ Layer,
452
+ } from '@modern-js/plugin-bff/effect-edge';
453
+ import { ultramodernApiMarker } from '../../src/ultramodern-build.ts';
454
+ import {
455
+ ${apiExport},
456
+ ${groupName}OperationContexts,
457
+ ${notFoundErrorExport},
458
+ } from '${contractImportPath}';
459
+ import type { OperationContext } from '${contractImportPath}';
460
+
461
+ const ${groupName}Items = [
462
+ {
463
+ id: 'starter-${stem}',
464
+ marker: ultramodernApiMarker,
465
+ title: 'Wire a real ${stem} source here',
466
+ },
467
+ ];
468
+ ${createCheckoutCartServerState(service)}
469
+
470
+ const operationAttributes = (operationContext: OperationContext) => ({
471
+ 'modernjs.operation.id': operationContext.operationId,
472
+ 'modernjs.operation.method': operationContext.method,
473
+ 'modernjs.operation.route': operationContext.routePath,
474
+ 'modernjs.operation.source': operationContext.source,
475
+ ...(typeof operationContext.traceId === 'string'
476
+ ? { 'modernjs.trace.id': operationContext.traceId }
477
+ : {}),
478
+ });
479
+
480
+ const ${groupName}Layer = HttpApiBuilder.group(
481
+ ${apiExport},
482
+ '${groupName}',
483
+ (handlers) =>
484
+ handlers
485
+ .handle('list', ({ query }) =>
486
+ Effect.succeed({
487
+ items:
488
+ typeof query.limit === 'number'
489
+ ? ${groupName}Items.slice(0, query.limit)
490
+ : ${groupName}Items,
491
+ }).pipe(
492
+ Effect.withSpan('ultramodern.effect.${groupName}.list', {
493
+ attributes: operationAttributes(${groupName}OperationContexts.list),
494
+ kind: 'server',
495
+ }),
496
+ ),
497
+ )
498
+ .handle('readiness', () =>
499
+ Effect.succeed({
500
+ checks: {
501
+ effectBff: 'ready' as const,
502
+ moduleFederation: 'ready' as const,
503
+ ssr: 'ready' as const,
504
+ translations: 'ready' as const,
505
+ },
506
+ marker: ultramodernApiMarker,
507
+ status: 'ready' as const,
508
+ versionSkew: 'none' as const,
509
+ }).pipe(
510
+ Effect.withSpan('ultramodern.effect.${groupName}.readiness', {
511
+ attributes: operationAttributes(${groupName}OperationContexts.readiness),
512
+ kind: 'server',
513
+ }),
514
+ ),
515
+ )
516
+ .handle('get', ({ params }) => {
517
+ const matchedItem = ${groupName}Items.find(
518
+ candidate => candidate.id === params.id,
519
+ );
520
+ const result =
521
+ matchedItem === undefined
522
+ ? Effect.fail(new ${notFoundErrorExport}({ id: params.id }))
523
+ : Effect.succeed(matchedItem);
524
+
525
+ return result.pipe(
526
+ Effect.withSpan('ultramodern.effect.${groupName}.get', {
527
+ attributes: operationAttributes(${groupName}OperationContexts.get),
528
+ kind: 'server',
529
+ }),
530
+ );
531
+ })
532
+ .handle('create', ({ payload }) =>
533
+ Effect.succeed({
534
+ item: {
535
+ id: \`generated-${stem}-\${payload.title
536
+ .toLowerCase()
537
+ .replaceAll(/[^a-z0-9]+/gu, '-')
538
+ .replaceAll(/^-|-$/gu, '')}\`,
539
+ marker: ultramodernApiMarker,
540
+ title: payload.title,
541
+ },
542
+ }).pipe(
543
+ Effect.withSpan('ultramodern.effect.${groupName}.create', {
544
+ attributes: operationAttributes(${groupName}OperationContexts.create),
545
+ kind: 'server',
546
+ }),
547
+ ),
548
+ )${createCheckoutCartServerHandlers(service)},
549
+ );
550
+
551
+ const layer = HttpApiBuilder.layer(${apiExport}).pipe(
552
+ Layer.provide(${groupName}Layer),
553
+ );
554
+
555
+ export default defineEffectBff({
556
+ api: ${apiExport},
557
+ layer,
558
+ });
559
+ `;
560
+ }
561
+ function createEffectClient(service, contractImportPath) {
562
+ const apiExport = verticalEffectApiExport(service);
563
+ const contractExport = verticalEffectGroupName(service);
564
+ const stem = effectApiStem(service);
565
+ const groupName = verticalEffectGroupName(service);
566
+ const singular = verticalEffectErrorStem(service);
567
+ const clientOptionsName = `${toPascalCase(stem)}ClientOptions`;
568
+ const createClientName = `create${toPascalCase(stem)}Client`;
569
+ const listName = `list${toPascalCase(stem)}`;
570
+ const readinessName = `get${toPascalCase(stem)}Readiness`;
571
+ const getName = `get${toPascalCase(singular)}`;
572
+ const createName = `create${toPascalCase(singular)}`;
573
+ const checkoutCartClientExports = createCheckoutCartClientExports(service);
574
+ return `import {
575
+ Effect,
576
+ makeEffectHttpApiClient,
577
+ runEffectRequest,
578
+ } from '@modern-js/plugin-bff/effect-client';
579
+ import {
580
+ ${contractExport}ApiContract,
581
+ ${apiExport},
582
+ ${groupName}OperationContexts,
583
+ } from '${contractImportPath}';
584
+ import type { OperationContext } from '${contractImportPath}';
585
+
586
+ export { Effect, runEffectRequest };
587
+
588
+ export interface ${clientOptionsName} {
589
+ baseUrl?: string | URL;
590
+ locale?: string;
591
+ operationContext?: OperationContext;
592
+ traceparent?: string;
593
+ }
594
+
595
+ export const ${createClientName} = (
596
+ options: ${clientOptionsName} = {},
597
+ ) =>
598
+ makeEffectHttpApiClient(${apiExport}, {
599
+ baseUrl: options.baseUrl ?? ${contractExport}ApiContract.apiPrefix,
600
+ requestContext: {
601
+ ...(options.locale === undefined ? {} : { locale: options.locale }),
602
+ ...(options.operationContext === undefined
603
+ ? {}
604
+ : { operationContext: options.operationContext }),
605
+ ...(options.traceparent === undefined
606
+ ? {}
607
+ : { traceparent: options.traceparent }),
608
+ },
609
+ });
610
+
611
+ export const ${listName} = (
612
+ options: ${clientOptionsName} & { limit?: number } = {},
613
+ ) =>
614
+ ${createClientName}({
615
+ ...options,
616
+ operationContext:
617
+ options.operationContext ?? ${groupName}OperationContexts.list,
618
+ }).pipe(
619
+ Effect.flatMap(client =>
620
+ client.${groupName}.list({ query: { limit: options.limit } }),
621
+ ),
622
+ );
623
+
624
+ export const ${readinessName} = (
625
+ options: ${clientOptionsName} = {},
626
+ ) =>
627
+ ${createClientName}({
628
+ ...options,
629
+ operationContext:
630
+ options.operationContext ?? ${groupName}OperationContexts.readiness,
631
+ }).pipe(
632
+ Effect.flatMap(client => client.${groupName}.readiness({})),
633
+ );
634
+
635
+ export const ${getName} = (
636
+ id: string,
637
+ options: ${clientOptionsName} = {},
638
+ ) =>
639
+ ${createClientName}({
640
+ ...options,
641
+ operationContext:
642
+ options.operationContext ?? ${groupName}OperationContexts.get,
643
+ }).pipe(
644
+ Effect.flatMap(client => client.${groupName}.get({ params: { id } })),
645
+ );
646
+
647
+ export const ${createName} = (
648
+ title: string,
649
+ options: ${clientOptionsName} = {},
650
+ ) =>
651
+ ${createClientName}({
652
+ ...options,
653
+ operationContext:
654
+ options.operationContext ?? ${groupName}OperationContexts.create,
655
+ }).pipe(
656
+ Effect.flatMap(client =>
657
+ client.${groupName}.create({ payload: { title } }),
658
+ ),
659
+ );${checkoutCartClientExports}
660
+ `;
661
+ }
662
+ function createShellEffectClient(scope, remotes = []) {
663
+ const exports = verticalEffectApps(remotes).map((remote)=>{
664
+ const stem = effectApiStem(remote);
665
+ const pascalStem = toPascalCase(stem);
666
+ const pascalSingular = toPascalCase(verticalEffectErrorStem(remote));
667
+ const checkoutCartExports = serviceHasCheckoutCartState(remote) ? ` addCheckoutCartItem,
668
+ clearCheckoutCart,
669
+ getCheckoutCart,
670
+ removeCheckoutCartItem,
671
+ type CheckoutAddCartItemInput,
672
+ type CheckoutCart,
673
+ type CheckoutCartLine,
674
+ ` : '';
675
+ return `export {
676
+ ${checkoutCartExports} create${pascalSingular},
677
+ create${pascalStem}Client,
678
+ get${pascalSingular},
679
+ get${pascalStem}Readiness,
680
+ list${pascalStem},
681
+ type ${pascalStem}ClientOptions,
682
+ } from '${packageName(scope, remote.packageSuffix)}/effect/client';`;
683
+ }).join('\n\n');
684
+ return exports ? `${exports}\n` : `export const ultramodernVerticalClients = [] as const;
685
+ `;
686
+ }
687
+ function createEffectReadinessContract(app) {
688
+ const stem = effectApiStem(app);
689
+ return {
690
+ endpoint: `/effect/${stem}/readiness`,
691
+ marker: {
692
+ ui: 'ultramodernUiMarker',
693
+ api: 'ultramodernApiMarker',
694
+ skew: 'none'
695
+ },
696
+ checks: [
697
+ 'moduleFederation',
698
+ 'ssr',
699
+ 'translations',
700
+ 'effectBff'
701
+ ]
702
+ };
703
+ }
704
+ function createEffectRequestContextContract() {
705
+ return {
706
+ propagatedHeaders: [
707
+ 'accept-language',
708
+ 'authorization',
709
+ 'traceparent',
710
+ 'x-correlation-id',
711
+ 'x-tenant-id',
712
+ 'x-ultramodern-env',
713
+ 'x-vertical-version-id'
714
+ ],
715
+ source: 'shell-to-vertical-effect-client'
716
+ };
717
+ }
718
+ function createEffectDomainOperations(app) {
719
+ const stem = effectApiStem(app);
720
+ const group = verticalEffectGroupName(app);
721
+ const basePath = `/effect/${stem}`;
722
+ const checkoutCartOperations = serviceHasCheckoutCartState(app) ? {
723
+ checkoutCartAddItem: {
724
+ client: 'addCheckoutCartItem',
725
+ method: 'POST',
726
+ path: '/effect/checkout/cart/items',
727
+ resource: 'checkout-cart',
728
+ owner: app.id
729
+ },
730
+ checkoutCartClear: {
731
+ client: 'clearCheckoutCart',
732
+ method: 'POST',
733
+ path: '/effect/checkout/cart/clear',
734
+ resource: 'checkout-cart',
735
+ owner: app.id
736
+ },
737
+ checkoutCartRead: {
738
+ client: 'getCheckoutCart',
739
+ method: 'GET',
740
+ path: '/effect/checkout/cart',
741
+ resource: 'checkout-cart',
742
+ owner: app.id
743
+ },
744
+ checkoutCartRemoveItem: {
745
+ client: 'removeCheckoutCartItem',
746
+ method: 'POST',
747
+ path: '/effect/checkout/cart/remove',
748
+ resource: 'checkout-cart',
749
+ owner: app.id
750
+ }
751
+ } : {};
752
+ return {
753
+ ...checkoutCartOperations,
754
+ workspaceFeed: {
755
+ client: `list${toPascalCase(stem)}`,
756
+ method: 'GET',
757
+ path: basePath,
758
+ resource: 'workspace-items',
759
+ owner: app.id
760
+ },
761
+ workspaceDetail: {
762
+ client: `get${toPascalCase(verticalEffectErrorStem(app))}`,
763
+ method: 'GET',
764
+ path: `${basePath}/:id`,
765
+ resource: 'workspace-item',
766
+ owner: app.id
767
+ },
768
+ workspaceCreate: {
769
+ client: `create${toPascalCase(verticalEffectErrorStem(app))}`,
770
+ method: 'POST',
771
+ path: basePath,
772
+ resource: group,
773
+ owner: app.id
774
+ }
775
+ };
776
+ }
777
+ function effectApiTopologyMetadata(app) {
778
+ if (!appHasEffectApi(app)) return;
779
+ 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
+ }
801
+ };
802
+ }
803
+ function createEffectOperationContract(target) {
804
+ const stem = effectApiStem(target);
805
+ const checkoutCartOperations = serviceHasCheckoutCartState(target) ? {
806
+ addCartItem: {
807
+ method: 'POST',
808
+ path: '/effect/checkout/cart/items',
809
+ source: 'generated-client'
810
+ },
811
+ clearCart: {
812
+ method: 'POST',
813
+ path: '/effect/checkout/cart/clear',
814
+ source: 'generated-client'
815
+ },
816
+ getCart: {
817
+ method: 'GET',
818
+ path: '/effect/checkout/cart',
819
+ source: 'generated-client'
820
+ },
821
+ removeCartItem: {
822
+ method: 'POST',
823
+ path: '/effect/checkout/cart/remove',
824
+ source: 'generated-client'
825
+ }
826
+ } : {};
827
+ return {
828
+ group: verticalEffectGroupName(target),
829
+ notFound: verticalEffectNotFoundErrorExport(target),
830
+ operations: {
831
+ ...checkoutCartOperations,
832
+ list: {
833
+ method: 'GET',
834
+ path: `/effect/${stem}`,
835
+ source: 'generated-client'
836
+ },
837
+ readiness: {
838
+ method: 'GET',
839
+ path: `/effect/${stem}/readiness`,
840
+ source: 'generated-client'
841
+ },
842
+ get: {
843
+ method: 'GET',
844
+ path: `/effect/${stem}/:id`,
845
+ source: 'generated-client'
846
+ },
847
+ create: {
848
+ method: 'POST',
849
+ path: `/effect/${stem}`,
850
+ source: 'generated-client'
851
+ }
852
+ }
853
+ };
854
+ }
855
+ export { createEffectClient, createEffectDomainOperations, createEffectOperationContract, createEffectReadinessContract, createEffectRequestContextContract, createEffectServiceEntry, createEffectSharedApi, createEffectSharedApiContract, createEffectSharedApiImports, createShellEffectClient, effectApiTopologyMetadata, verticalEffectApiExport, verticalEffectApiName, verticalEffectCreatePayloadSchemaExport, verticalEffectErrorStem, verticalEffectGroupName, verticalEffectMarkerSchemaExport, verticalEffectNotFoundErrorExport, verticalEffectNotFoundSchemaExport, verticalEffectReadinessSchemaExport, verticalEffectSchemaExport };