@bleedingdev/modern-js-create 3.2.0-ultramodern.12 → 3.2.0-ultramodern.121

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 (165) hide show
  1. package/README.md +167 -72
  2. package/bin/run.js +0 -0
  3. package/dist/cjs/create-package-root.cjs +63 -0
  4. package/dist/cjs/index.cjs +528 -0
  5. package/dist/cjs/locale/en.cjs +93 -0
  6. package/dist/cjs/locale/index.cjs +50 -0
  7. package/dist/cjs/locale/zh.cjs +93 -0
  8. package/dist/cjs/ultramodern-package-source.cjs +135 -0
  9. package/dist/cjs/ultramodern-workspace/add-vertical.cjs +337 -0
  10. package/dist/cjs/ultramodern-workspace/app-files.cjs +223 -0
  11. package/dist/cjs/ultramodern-workspace/contracts.cjs +836 -0
  12. package/dist/cjs/ultramodern-workspace/demo-components.cjs +422 -0
  13. package/dist/cjs/ultramodern-workspace/descriptors.cjs +222 -0
  14. package/dist/cjs/ultramodern-workspace/effect-api.cjs +952 -0
  15. package/dist/cjs/ultramodern-workspace/fs-io.cjs +191 -0
  16. package/dist/cjs/ultramodern-workspace/index.cjs +48 -0
  17. package/dist/cjs/ultramodern-workspace/locales.cjs +173 -0
  18. package/dist/cjs/ultramodern-workspace/module-federation.cjs +487 -0
  19. package/dist/cjs/ultramodern-workspace/naming.cjs +161 -0
  20. package/dist/cjs/ultramodern-workspace/package-json.cjs +406 -0
  21. package/dist/cjs/ultramodern-workspace/package-source.cjs +59 -0
  22. package/dist/cjs/ultramodern-workspace/policy.cjs +248 -0
  23. package/dist/cjs/ultramodern-workspace/public-surface.cjs +268 -0
  24. package/dist/cjs/ultramodern-workspace/routes.cjs +375 -0
  25. package/dist/cjs/ultramodern-workspace/types.cjs +61 -0
  26. package/dist/cjs/ultramodern-workspace/versions.cjs +153 -0
  27. package/dist/cjs/ultramodern-workspace/workspace-scripts.cjs +153 -0
  28. package/dist/cjs/ultramodern-workspace/write-workspace.cjs +175 -0
  29. package/dist/esm/create-package-root.js +14 -0
  30. package/dist/esm/index.js +491 -0
  31. package/dist/esm/locale/en.js +55 -0
  32. package/dist/esm/locale/index.js +9 -0
  33. package/dist/esm/locale/zh.js +55 -0
  34. package/dist/esm/ultramodern-package-source.js +63 -0
  35. package/dist/esm/ultramodern-workspace/add-vertical.js +252 -0
  36. package/dist/esm/ultramodern-workspace/app-files.js +149 -0
  37. package/dist/esm/ultramodern-workspace/contracts.js +741 -0
  38. package/dist/esm/ultramodern-workspace/demo-components.js +363 -0
  39. package/dist/esm/ultramodern-workspace/descriptors.js +133 -0
  40. package/dist/esm/ultramodern-workspace/effect-api.js +854 -0
  41. package/dist/esm/ultramodern-workspace/fs-io.js +90 -0
  42. package/dist/esm/ultramodern-workspace/index.js +3 -0
  43. package/dist/esm/ultramodern-workspace/locales.js +122 -0
  44. package/dist/esm/ultramodern-workspace/module-federation.js +415 -0
  45. package/dist/esm/ultramodern-workspace/naming.js +71 -0
  46. package/dist/esm/ultramodern-workspace/package-json.js +338 -0
  47. package/dist/esm/ultramodern-workspace/package-source.js +21 -0
  48. package/dist/esm/ultramodern-workspace/policy.js +183 -0
  49. package/dist/esm/ultramodern-workspace/public-surface.js +183 -0
  50. package/dist/esm/ultramodern-workspace/routes.js +280 -0
  51. package/dist/esm/ultramodern-workspace/types.js +16 -0
  52. package/dist/esm/ultramodern-workspace/versions.js +34 -0
  53. package/dist/esm/ultramodern-workspace/workspace-scripts.js +91 -0
  54. package/dist/esm/ultramodern-workspace/write-workspace.js +121 -0
  55. package/dist/esm-node/create-package-root.js +15 -0
  56. package/dist/esm-node/index.js +492 -0
  57. package/dist/esm-node/locale/en.js +56 -0
  58. package/dist/esm-node/locale/index.js +10 -0
  59. package/dist/esm-node/locale/zh.js +56 -0
  60. package/dist/esm-node/ultramodern-package-source.js +64 -0
  61. package/dist/esm-node/ultramodern-workspace/add-vertical.js +253 -0
  62. package/dist/esm-node/ultramodern-workspace/app-files.js +150 -0
  63. package/dist/esm-node/ultramodern-workspace/contracts.js +742 -0
  64. package/dist/esm-node/ultramodern-workspace/demo-components.js +364 -0
  65. package/dist/esm-node/ultramodern-workspace/descriptors.js +134 -0
  66. package/dist/esm-node/ultramodern-workspace/effect-api.js +855 -0
  67. package/dist/esm-node/ultramodern-workspace/fs-io.js +91 -0
  68. package/dist/esm-node/ultramodern-workspace/index.js +4 -0
  69. package/dist/esm-node/ultramodern-workspace/locales.js +123 -0
  70. package/dist/esm-node/ultramodern-workspace/module-federation.js +416 -0
  71. package/dist/esm-node/ultramodern-workspace/naming.js +72 -0
  72. package/dist/esm-node/ultramodern-workspace/package-json.js +339 -0
  73. package/dist/esm-node/ultramodern-workspace/package-source.js +22 -0
  74. package/dist/esm-node/ultramodern-workspace/policy.js +184 -0
  75. package/dist/esm-node/ultramodern-workspace/public-surface.js +184 -0
  76. package/dist/esm-node/ultramodern-workspace/routes.js +281 -0
  77. package/dist/esm-node/ultramodern-workspace/types.js +17 -0
  78. package/dist/esm-node/ultramodern-workspace/versions.js +35 -0
  79. package/dist/esm-node/ultramodern-workspace/workspace-scripts.js +92 -0
  80. package/dist/esm-node/ultramodern-workspace/write-workspace.js +122 -0
  81. package/dist/types/create-package-root.d.ts +1 -0
  82. package/dist/types/locale/en.d.ts +8 -9
  83. package/dist/types/locale/index.d.ts +109 -2
  84. package/dist/types/locale/zh.d.ts +8 -9
  85. package/dist/types/ultramodern-package-source.d.ts +28 -0
  86. package/dist/types/ultramodern-workspace/add-vertical.d.ts +19 -0
  87. package/dist/types/ultramodern-workspace/app-files.d.ts +14 -0
  88. package/dist/types/ultramodern-workspace/contracts.d.ts +21 -0
  89. package/dist/types/ultramodern-workspace/demo-components.d.ts +9 -0
  90. package/dist/types/ultramodern-workspace/descriptors.d.ts +39 -0
  91. package/dist/types/ultramodern-workspace/effect-api.d.ts +73 -0
  92. package/dist/types/ultramodern-workspace/fs-io.d.ts +18 -0
  93. package/dist/types/ultramodern-workspace/index.d.ts +4 -0
  94. package/dist/types/ultramodern-workspace/locales.d.ts +183 -0
  95. package/dist/types/ultramodern-workspace/module-federation.d.ts +16 -0
  96. package/dist/types/ultramodern-workspace/naming.d.ts +16 -0
  97. package/dist/types/ultramodern-workspace/package-json.d.ts +12 -0
  98. package/dist/types/ultramodern-workspace/package-source.d.ts +2 -0
  99. package/dist/types/ultramodern-workspace/policy.d.ts +60 -0
  100. package/dist/types/ultramodern-workspace/public-surface.d.ts +37 -0
  101. package/dist/types/ultramodern-workspace/routes.d.ts +25 -0
  102. package/dist/types/ultramodern-workspace/types.d.ts +95 -0
  103. package/dist/types/ultramodern-workspace/versions.d.ts +38 -0
  104. package/dist/types/ultramodern-workspace/workspace-scripts.d.ts +10 -0
  105. package/dist/types/ultramodern-workspace/write-workspace.d.ts +4 -0
  106. package/package.json +34 -15
  107. package/template-workspace/.agents/agent-reference-repos.json +24 -0
  108. package/template-workspace/.agents/skills-lock.json +19 -0
  109. package/template-workspace/.codex/hooks.json +16 -0
  110. package/template-workspace/.github/renovate.json +29 -0
  111. package/template-workspace/.github/workflows/ultramodern-workspace-gates.yml.handlebars +67 -0
  112. package/template-workspace/.gitignore.handlebars +5 -0
  113. package/template-workspace/.mise.toml.handlebars +3 -0
  114. package/template-workspace/AGENTS.md.handlebars +87 -0
  115. package/template-workspace/README.md.handlebars +132 -11
  116. package/template-workspace/lefthook.yml +24 -0
  117. package/template-workspace/oxfmt.config.ts +1 -0
  118. package/template-workspace/oxlint.config.ts +1 -0
  119. package/template-workspace/pnpm-workspace.yaml.handlebars +40 -0
  120. package/template-workspace/scripts/bootstrap-agent-skills.mjs +184 -21
  121. package/template-workspace/scripts/setup-agent-reference-repos.mjs +370 -0
  122. package/templates/app/shell-frame.tsx +49 -0
  123. package/templates/app/ultramodern-route-head.tsx.handlebars +142 -0
  124. package/templates/packages/shared-contracts-index.ts +466 -0
  125. package/templates/workspace-scripts/assert-mf-types.mjs.handlebars +69 -0
  126. package/templates/workspace-scripts/check-ultramodern-i18n-boundaries.mjs +9 -0
  127. package/templates/workspace-scripts/generate-public-surface-assets.mjs +529 -0
  128. package/templates/workspace-scripts/proof-cloudflare-version.mjs +125 -0
  129. package/templates/workspace-scripts/ultramodern-cloudflare-proof.mjs +851 -0
  130. package/templates/workspace-scripts/ultramodern-performance-readiness.config.mjs +7 -0
  131. package/templates/workspace-scripts/ultramodern-performance-readiness.mjs +223 -0
  132. package/templates/workspace-scripts/validate-ultramodern-workspace.mjs.handlebars +593 -0
  133. package/dist/index.js +0 -2626
  134. package/dist/types/ultramodern-workspace.d.ts +0 -20
  135. package/template/.agents/skills-lock.json +0 -34
  136. package/template/.browserslistrc +0 -4
  137. package/template/.github/workflows/ultramodern-gates.yml.handlebars +0 -30
  138. package/template/.gitignore.handlebars +0 -30
  139. package/template/.nvmrc +0 -2
  140. package/template/AGENTS.md +0 -25
  141. package/template/README.md +0 -79
  142. package/template/api/effect/index.ts.handlebars +0 -23
  143. package/template/api/lambda/hello.ts.handlebars +0 -6
  144. package/template/config/public/locales/cs/translation.json +0 -39
  145. package/template/config/public/locales/en/translation.json +0 -39
  146. package/template/modern.config.ts.handlebars +0 -53
  147. package/template/oxfmt.config.ts +0 -8
  148. package/template/oxlint.config.ts +0 -12
  149. package/template/package.json.handlebars +0 -67
  150. package/template/postcss.config.mjs.handlebars +0 -6
  151. package/template/scripts/bootstrap-agent-skills.mjs +0 -95
  152. package/template/scripts/check-i18n-strings.mjs +0 -83
  153. package/template/scripts/validate-ultramodern.mjs.handlebars +0 -178
  154. package/template/shared/effect/api.ts.handlebars +0 -17
  155. package/template/src/modern-app-env.d.ts +0 -1
  156. package/template/src/modern.runtime.ts.handlebars +0 -23
  157. package/template/src/routes/index.css.handlebars +0 -129
  158. package/template/src/routes/layout.tsx.handlebars +0 -9
  159. package/template/src/routes/page.tsx.handlebars +0 -155
  160. package/template/tailwind.config.ts.handlebars +0 -10
  161. package/template/tsconfig.json +0 -120
  162. package/template-workspace/AGENTS.md +0 -50
  163. package/template-workspace/pnpm-workspace.yaml +0 -17
  164. package/template-workspace/scripts/check-i18n-strings.mjs +0 -83
  165. package/template-workspace/scripts/validate-ultramodern-workspace.mjs.handlebars +0 -433
@@ -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 };